summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJames McDonnell <jmcdonnell@blackberry.com>2017-03-24 11:34:30 -0400
committerJames McDonnell <jmcdonnell@blackberry.com>2017-03-27 19:04:01 +0000
commit883df8dfda760fdbe850303383ba098887abbf62 (patch)
tree1839f3e9aa41e97217795ac83242a9eb70191da7
parent1221f6a7e3767e14d2afdfc806120953bd6535be (diff)
io-audio based audio management for QNX 7.0.0
Add code to setup and handle io-audio based audio management notifications. Add InterruptedState to QAudio::State. Indicates that this stream is in a suspended state because another higher priority stream currently has control of the audio device. Playback cannot resume until the higher priority stream relinquishes control of the audio device. Adjust the documentation for QAudio::State SuspendedState to reflect the fact that audio management events can also put the stream into this state. Add InterruptedState handling to the spectrum example. Change-Id: Ie8616af03ae4c503de1d0571a222e5853035cc0e Reviewed-by: Yoann Lopes <yoann.lopes@qt.io>
-rw-r--r--examples/multimedia/spectrum/app/mainwidget.cpp7
-rw-r--r--src/multimedia/audio/qaudio.cpp22
-rw-r--r--src/multimedia/audio/qaudio.h2
-rw-r--r--src/plugins/qnx-audio/audio/qnxaudiooutput.cpp123
-rw-r--r--src/plugins/qnx-audio/audio/qnxaudiooutput.h14
5 files changed, 139 insertions, 29 deletions
diff --git a/examples/multimedia/spectrum/app/mainwidget.cpp b/examples/multimedia/spectrum/app/mainwidget.cpp
index a69c85a44..35b92d13e 100644
--- a/examples/multimedia/spectrum/app/mainwidget.cpp
+++ b/examples/multimedia/spectrum/app/mainwidget.cpp
@@ -109,7 +109,9 @@ void MainWidget::stateChanged(QAudio::Mode mode, QAudio::State state)
updateButtonStates();
- if (QAudio::ActiveState != state && QAudio::SuspendedState != state) {
+ if (QAudio::ActiveState != state &&
+ QAudio::SuspendedState != state &&
+ QAudio::InterruptedState != state) {
m_levelMeter->reset();
m_spectrograph->reset();
}
@@ -418,7 +420,8 @@ void MainWidget::updateButtonStates()
const bool playEnabled = (/*m_engine->dataLength() &&*/
(QAudio::AudioOutput != m_engine->mode() ||
(QAudio::ActiveState != m_engine->state() &&
- QAudio::IdleState != m_engine->state())));
+ QAudio::IdleState != m_engine->state() &&
+ QAudio::InterruptedState != m_engine->state())));
m_playButton->setEnabled(playEnabled);
}
diff --git a/src/multimedia/audio/qaudio.cpp b/src/multimedia/audio/qaudio.cpp
index d4f89e898..dea9a05a5 100644
--- a/src/multimedia/audio/qaudio.cpp
+++ b/src/multimedia/audio/qaudio.cpp
@@ -79,13 +79,18 @@ Q_CONSTRUCTOR_FUNCTION(qRegisterAudioMetaTypes)
/*!
\enum QAudio::State
- \value ActiveState Audio data is being processed, this state is set after start() is called
- and while audio data is available to be processed.
- \value SuspendedState The audio device is in a suspended state, this state will only be entered
- after suspend() is called.
- \value StoppedState The audio device is closed, and is not processing any audio data
- \value IdleState The QIODevice passed in has no data and audio system's buffer is empty, this state
- is set after start() is called and while no audio data is available to be processed.
+ \value ActiveState Audio data is being processed, this state is set after start() is called
+ and while audio data is available to be processed.
+ \value SuspendedState The audio stream is in a suspended state. Entered after suspend() is called
+ or when another stream takes control of the audio device. In the later case,
+ a call to resume will return control of the audio device to this stream. This
+ should usually only be done upon user request.
+ \value StoppedState The audio device is closed, and is not processing any audio data
+ \value IdleState The QIODevice passed in has no data and audio system's buffer is empty, this state
+ is set after start() is called and while no audio data is available to be processed.
+ \value InterruptedState This stream is in a suspended state because another higher priority stream currently
+ has control of the audio device. Playback cannot resume until the higher priority
+ stream relinquishes control of the audio device.
*/
/*!
@@ -285,6 +290,9 @@ QDebug operator<<(QDebug dbg, QAudio::State state)
case QAudio::IdleState:
dbg << "IdleState";
break;
+ case QAudio::InterruptedState:
+ dbg << "InterruptedState";
+ break;
}
return dbg;
}
diff --git a/src/multimedia/audio/qaudio.h b/src/multimedia/audio/qaudio.h
index 1c38e9f35..2603d71d1 100644
--- a/src/multimedia/audio/qaudio.h
+++ b/src/multimedia/audio/qaudio.h
@@ -55,7 +55,7 @@ class QString;
namespace QAudio
{
enum Error { NoError, OpenError, IOError, UnderrunError, FatalError };
- enum State { ActiveState, SuspendedState, StoppedState, IdleState };
+ enum State { ActiveState, SuspendedState, StoppedState, IdleState, InterruptedState };
enum Mode { AudioInput, AudioOutput };
enum Role {
diff --git a/src/plugins/qnx-audio/audio/qnxaudiooutput.cpp b/src/plugins/qnx-audio/audio/qnxaudiooutput.cpp
index 084c5b371..c4c09f543 100644
--- a/src/plugins/qnx-audio/audio/qnxaudiooutput.cpp
+++ b/src/plugins/qnx-audio/audio/qnxaudiooutput.cpp
@@ -43,19 +43,24 @@
#include <private/qaudiohelpers_p.h>
+#pragma GCC diagnostic ignored "-Wvla"
+
QT_BEGIN_NAMESPACE
QnxAudioOutput::QnxAudioOutput()
- : m_source(0),
- m_pushSource(false),
- m_notifyInterval(1000),
- m_error(QAudio::NoError),
- m_state(QAudio::StoppedState),
- m_volume(1.0),
- m_periodSize(0),
- m_pcmHandle(0),
- m_bytesWritten(0),
- m_intervalOffset(0)
+ : m_source(0)
+ , m_pushSource(false)
+ , m_notifyInterval(1000)
+ , m_error(QAudio::NoError)
+ , m_state(QAudio::StoppedState)
+ , m_volume(1.0)
+ , m_periodSize(0)
+ , m_pcmHandle(0)
+ , m_bytesWritten(0)
+ , m_intervalOffset(0)
+#if _NTO_VERSION >= 700
+ , m_pcmNotifier(0)
+#endif
{
m_timer.setSingleShot(false);
m_timer.setInterval(20);
@@ -124,20 +129,16 @@ void QnxAudioOutput::reset()
void QnxAudioOutput::suspend()
{
- m_timer.stop();
snd_pcm_playback_pause(m_pcmHandle);
- setState(QAudio::SuspendedState);
+ if (state() != QAudio::InterruptedState)
+ suspendInternal(QAudio::SuspendedState);
}
void QnxAudioOutput::resume()
{
snd_pcm_playback_resume(m_pcmHandle);
- if (m_pushSource)
- setState(QAudio::IdleState);
- else {
- setState(QAudio::ActiveState);
- m_timer.start();
- }
+ if (state() != QAudio::InterruptedState)
+ resumeInternal();
}
int QnxAudioOutput::bytesFree() const
@@ -146,6 +147,7 @@ int QnxAudioOutput::bytesFree() const
return 0;
snd_pcm_channel_status_t status;
+ memset(&status, 0, sizeof(status));
status.channel = SND_PCM_CHANNEL_PLAYBACK;
const int errorCode = snd_pcm_plugin_status(m_pcmHandle, &status);
@@ -226,7 +228,9 @@ QString QnxAudioOutput::category() const
void QnxAudioOutput::pullData()
{
- if (m_state == QAudio::StoppedState || m_state == QAudio::SuspendedState)
+ if (m_state == QAudio::StoppedState
+ || m_state == QAudio::SuspendedState
+ || m_state == QAudio::InterruptedState)
return;
const int bytesAvailable = bytesFree();
@@ -300,6 +304,8 @@ bool QnxAudioOutput::open()
return false;
}
+ addPcmEventFilter();
+
// Necessary so that bytesFree() which uses the "free" member of the status struct works
snd_pcm_plugin_set_disable(m_pcmHandle, PLUGIN_MMAP);
@@ -342,6 +348,8 @@ bool QnxAudioOutput::open()
m_intervalOffset = 0;
m_bytesWritten = 0;
+ createPcmNotifiers();
+
return true;
}
@@ -349,6 +357,8 @@ void QnxAudioOutput::close()
{
m_timer.stop();
+ destroyPcmNotifiers();
+
if (m_pcmHandle) {
snd_pcm_plugin_flush(m_pcmHandle, SND_PCM_CHANNEL_PLAYBACK);
snd_pcm_close(m_pcmHandle);
@@ -411,8 +421,62 @@ qint64 QnxAudioOutput::write(const char *data, qint64 len)
}
}
+void QnxAudioOutput::suspendInternal(QAudio::State suspendState)
+{
+ m_timer.stop();
+ setState(suspendState);
+}
+
+void QnxAudioOutput::resumeInternal()
+{
+ if (m_pushSource) {
+ setState(QAudio::IdleState);
+ } else {
+ setState(QAudio::ActiveState);
+ m_timer.start();
+ }
+}
+
#if _NTO_VERSION >= 700
+QAudio::State suspendState(const snd_pcm_event_t &event)
+{
+ Q_ASSERT(event.type == SND_PCM_EVENT_AUDIOMGMT_STATUS);
+ Q_ASSERT(event.data.audiomgmt_status.new_status == SND_PCM_STATUS_SUSPENDED);
+ return event.data.audiomgmt_status.flags & SND_PCM_STATUS_EVENT_HARD_SUSPEND
+ ? QAudio::InterruptedState : QAudio::SuspendedState;
+}
+
+void QnxAudioOutput::addPcmEventFilter()
+{
+ /* Enable PCM events */
+ snd_pcm_filter_t filter;
+ memset(&filter, 0, sizeof(filter));
+ filter.enable = (1<<SND_PCM_EVENT_AUDIOMGMT_STATUS) |
+ (1<<SND_PCM_EVENT_AUDIOMGMT_MUTE) |
+ (1<<SND_PCM_EVENT_OUTPUTCLASS);
+ snd_pcm_set_filter(m_pcmHandle, SND_PCM_CHANNEL_PLAYBACK, &filter);
+}
+
+void QnxAudioOutput::createPcmNotifiers()
+{
+ // QSocketNotifier::Read for poll based event dispatcher. Exception for
+ // select based event dispatcher.
+ m_pcmNotifier = new QSocketNotifier(snd_pcm_file_descriptor(m_pcmHandle,
+ SND_PCM_CHANNEL_PLAYBACK),
+ QSocketNotifier::Read, this);
+ connect(m_pcmNotifier, &QSocketNotifier::activated,
+ this, &QnxAudioOutput::pcmNotifierActivated);
+}
+
+void QnxAudioOutput::destroyPcmNotifiers()
+{
+ if (m_pcmNotifier) {
+ delete m_pcmNotifier;
+ m_pcmNotifier = 0;
+ }
+}
+
void QnxAudioOutput::setTypeName(snd_pcm_channel_params_t *params)
{
if (m_category.isEmpty())
@@ -433,8 +497,29 @@ void QnxAudioOutput::setTypeName(snd_pcm_channel_params_t *params)
strcpy(params->audio_type_name, latin1Category.constData());
}
+void QnxAudioOutput::pcmNotifierActivated(int socket)
+{
+ Q_UNUSED(socket);
+
+ snd_pcm_event_t pcm_event;
+ memset(&pcm_event, 0, sizeof(pcm_event));
+ while (snd_pcm_channel_read_event(m_pcmHandle, SND_PCM_CHANNEL_PLAYBACK, &pcm_event) == 0) {
+ if (pcm_event.type == SND_PCM_EVENT_AUDIOMGMT_STATUS) {
+ if (pcm_event.data.audiomgmt_status.new_status == SND_PCM_STATUS_SUSPENDED)
+ suspendInternal(suspendState(pcm_event));
+ else if (pcm_event.data.audiomgmt_status.new_status == SND_PCM_STATUS_RUNNING)
+ resumeInternal();
+ else if (pcm_event.data.audiomgmt_status.new_status == SND_PCM_STATUS_PAUSED)
+ suspendInternal(QAudio::SuspendedState);
+ }
+ }
+}
+
#else
+void QnxAudioOutput::addPcmEventFilter() {}
+void QnxAudioOutput::createPcmNotifiers() {}
+void QnxAudioOutput::destroyPcmNotifiers() {}
void QnxAudioOutput::setTypeName(snd_pcm_channel_params_t *) {}
#endif
diff --git a/src/plugins/qnx-audio/audio/qnxaudiooutput.h b/src/plugins/qnx-audio/audio/qnxaudiooutput.h
index c4fe37516..85aadf4b9 100644
--- a/src/plugins/qnx-audio/audio/qnxaudiooutput.h
+++ b/src/plugins/qnx-audio/audio/qnxaudiooutput.h
@@ -45,6 +45,7 @@
#include <QTime>
#include <QTimer>
#include <QIODevice>
+#include <QSocketNotifier>
#include <sys/asoundlib.h>
#include <sys/neutrino.h>
@@ -93,8 +94,14 @@ private:
void setError(QAudio::Error error);
void setState(QAudio::State state);
+ void addPcmEventFilter();
+ void createPcmNotifiers();
+ void destroyPcmNotifiers();
void setTypeName(snd_pcm_channel_params_t *params);
+ void suspendInternal(QAudio::State suspendState);
+ void resumeInternal();
+
friend class QnxPushIODevice;
qint64 write(const char *data, qint64 len);
@@ -115,6 +122,13 @@ private:
QTime m_startTimeStamp;
QTime m_intervalTimeStamp;
qint64 m_intervalOffset;
+
+#if _NTO_VERSION >= 700
+ QSocketNotifier *m_pcmNotifier;
+
+private slots:
+ void pcmNotifierActivated(int socket);
+#endif
};
class QnxPushIODevice : public QIODevice