BlackBerry/QNX: Add a new backend for Blackberry handling audio
authorKevin Ottens <kevin.ottens.qnx@kdab.com>
Thu, 21 Feb 2013 13:28:12 +0000 (14:28 +0100)
committerThe Qt Project <gerrit-noreply@qt-project.org>
Mon, 25 Feb 2013 10:15:53 +0000 (11:15 +0100)
For now it only implements the audio output part.
The plugin can be used on all QNX systems (including BB10) that
provide a libasound version < 1.0.10.

Change-Id: Ifcfd871558d5d2bfb9e8a5f5ef0cfe009c0a111d
Reviewed-by: Thomas McGuire <thomas.mcguire@kdab.com>
12 files changed:
src/plugins/plugins.pro
src/plugins/qnx/audio/audio.pro [new file with mode: 0644]
src/plugins/qnx/audio/qnx_audio.json [new file with mode: 0644]
src/plugins/qnx/audio/qnxaudiodeviceinfo.cpp [new file with mode: 0644]
src/plugins/qnx/audio/qnxaudiodeviceinfo.h [new file with mode: 0644]
src/plugins/qnx/audio/qnxaudiooutput.cpp [new file with mode: 0644]
src/plugins/qnx/audio/qnxaudiooutput.h [new file with mode: 0644]
src/plugins/qnx/audio/qnxaudioplugin.cpp [new file with mode: 0644]
src/plugins/qnx/audio/qnxaudioplugin.h [new file with mode: 0644]
src/plugins/qnx/audio/qnxaudioutils.cpp [new file with mode: 0644]
src/plugins/qnx/audio/qnxaudioutils.h [new file with mode: 0644]
src/plugins/qnx/qnx.pro [new file with mode: 0644]

index 24708e0..c8826a8 100644 (file)
@@ -12,6 +12,10 @@ blackberry {
     SUBDIRS += blackberry
 }
 
+qnx {
+    SUBDIRS += qnx
+}
+
 win32 {
     SUBDIRS += audiocapture
 }
diff --git a/src/plugins/qnx/audio/audio.pro b/src/plugins/qnx/audio/audio.pro
new file mode 100644 (file)
index 0000000..3b67fbf
--- /dev/null
@@ -0,0 +1,24 @@
+TARGET = qtmedia_qnx_audio
+QT += multimedia-private
+CONFIG += no_private_qt_headers_warning
+
+PLUGIN_TYPE = audio
+
+load(qt_plugin)
+DESTDIR = $$QT.multimedia.plugins/$${PLUGIN_TYPE}
+LIBS += -lasound
+
+HEADERS += qnxaudioplugin.h \
+           qnxaudiodeviceinfo.h \
+           qnxaudiooutput.h \
+           qnxaudioutils.h
+
+SOURCES += qnxaudioplugin.cpp \
+           qnxaudiodeviceinfo.cpp \
+           qnxaudiooutput.cpp \
+           qnxaudioutils.cpp
+
+OTHER_FILES += qnx_audio.json
+
+target.path += $$[QT_INSTALL_PLUGINS]/$${PLUGIN_TYPE}
+INSTALLS += target
diff --git a/src/plugins/qnx/audio/qnx_audio.json b/src/plugins/qnx/audio/qnx_audio.json
new file mode 100644 (file)
index 0000000..a31d521
--- /dev/null
@@ -0,0 +1,3 @@
+{
+    "Keys": ["default"]
+}
diff --git a/src/plugins/qnx/audio/qnxaudiodeviceinfo.cpp b/src/plugins/qnx/audio/qnxaudiodeviceinfo.cpp
new file mode 100644 (file)
index 0000000..ce80835
--- /dev/null
@@ -0,0 +1,150 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Research In Motion
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia.  For licensing terms and
+** conditions see http://qt.digia.com/licensing.  For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file.  Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights.  These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file.  Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qnxaudiodeviceinfo.h"
+
+#include "qnxaudioutils.h"
+
+#include <sys/asoundlib.h>
+
+QT_BEGIN_NAMESPACE
+
+QnxAudioDeviceInfo::QnxAudioDeviceInfo(const QString &deviceName, QAudio::Mode mode)
+    : m_name(deviceName),
+      m_mode(mode)
+{
+}
+
+QnxAudioDeviceInfo::~QnxAudioDeviceInfo()
+{
+}
+
+QAudioFormat QnxAudioDeviceInfo::preferredFormat() const
+{
+    QAudioFormat format;
+    if (m_mode == QAudio::AudioOutput) {
+        format.setSampleRate(44100);
+        format.setChannelCount(2);
+        format.setByteOrder(QAudioFormat::LittleEndian);
+        format.setSampleType(QAudioFormat::SignedInt);
+        format.setSampleSize(16);
+        format.setCodec(QLatin1String("audio/pcm"));
+    } else {
+        format.setSampleRate(8000);
+        format.setChannelCount(1);
+        format.setSampleType(QAudioFormat::UnSignedInt);
+        format.setSampleSize(8);
+        format.setCodec(QLatin1String("audio/pcm"));
+        if (!isFormatSupported(format)) {
+            format.setChannelCount(2);
+            format.setSampleSize(16);
+            format.setSampleType(QAudioFormat::SignedInt);
+        }
+    }
+    return format;
+}
+
+bool QnxAudioDeviceInfo::isFormatSupported(const QAudioFormat &format) const
+{
+    if (!format.codec().startsWith(QLatin1String("audio/pcm")))
+        return false;
+
+    const int pcmMode = (m_mode == QAudio::AudioOutput) ? SND_PCM_OPEN_PLAYBACK : SND_PCM_OPEN_CAPTURE;
+    snd_pcm_t *handle;
+
+    int card = 0;
+    int device = 0;
+    if (snd_pcm_open_preferred(&handle, &card, &device, pcmMode) < 0)
+        return false;
+
+    snd_pcm_channel_info_t info;
+    memset (&info, 0, sizeof(info));
+    info.channel = (m_mode == QAudio::AudioOutput) ? SND_PCM_CHANNEL_PLAYBACK : SND_PCM_CHANNEL_CAPTURE;
+
+    if (snd_pcm_plugin_info(handle, &info) < 0) {
+        qWarning("QAudioDeviceInfo: couldn't get channel info");
+        snd_pcm_close(handle);
+        return false;
+    }
+
+    snd_pcm_channel_params_t params = QnxAudioUtils::formatToChannelParams(format, m_mode, info.max_fragment_size);
+    const int errorCode = snd_pcm_plugin_params(handle, &params);
+    snd_pcm_close(handle);
+
+    return errorCode == 0;
+}
+
+QString QnxAudioDeviceInfo::deviceName() const
+{
+    return m_name;
+}
+
+QStringList QnxAudioDeviceInfo::supportedCodecs()
+{
+    return QStringList() << QLatin1String("audio/pcm");
+}
+
+QList<int> QnxAudioDeviceInfo::supportedSampleRates()
+{
+    return QList<int>() << 8000 << 11025 << 22050 << 44100 << 48000;
+}
+
+QList<int> QnxAudioDeviceInfo::supportedChannelCounts()
+{
+    return QList<int>() << 1 << 2;
+}
+
+QList<int> QnxAudioDeviceInfo::supportedSampleSizes()
+{
+    return QList<int>() << 8 << 16 << 32;
+}
+
+QList<QAudioFormat::Endian> QnxAudioDeviceInfo::supportedByteOrders()
+{
+    return QList<QAudioFormat::Endian>() << QAudioFormat::LittleEndian << QAudioFormat::BigEndian;
+}
+
+QList<QAudioFormat::SampleType> QnxAudioDeviceInfo::supportedSampleTypes()
+{
+    return QList<QAudioFormat::SampleType>() << QAudioFormat::SignedInt << QAudioFormat::UnSignedInt << QAudioFormat::Float;
+}
+
+QT_END_NAMESPACE
diff --git a/src/plugins/qnx/audio/qnxaudiodeviceinfo.h b/src/plugins/qnx/audio/qnxaudiodeviceinfo.h
new file mode 100644 (file)
index 0000000..72c10cc
--- /dev/null
@@ -0,0 +1,74 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Research In Motion
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia.  For licensing terms and
+** conditions see http://qt.digia.com/licensing.  For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file.  Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights.  These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file.  Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QNXAUDIODEVICEINFO_H
+#define QNXAUDIODEVICEINFO_H
+
+#include "qaudiosystem.h"
+
+QT_BEGIN_NAMESPACE
+
+class QnxAudioDeviceInfo : public QAbstractAudioDeviceInfo
+{
+    Q_OBJECT
+
+public:
+    QnxAudioDeviceInfo(const QString &deviceName, QAudio::Mode mode);
+    ~QnxAudioDeviceInfo();
+
+    QAudioFormat preferredFormat() const Q_DECL_OVERRIDE;
+    bool isFormatSupported(const QAudioFormat &format) const Q_DECL_OVERRIDE;
+    QString deviceName() const Q_DECL_OVERRIDE;
+    QStringList supportedCodecs() Q_DECL_OVERRIDE;
+    QList<int> supportedSampleRates() Q_DECL_OVERRIDE;
+    QList<int> supportedChannelCounts() Q_DECL_OVERRIDE;
+    QList<int> supportedSampleSizes() Q_DECL_OVERRIDE;
+    QList<QAudioFormat::Endian> supportedByteOrders() Q_DECL_OVERRIDE;
+    QList<QAudioFormat::SampleType> supportedSampleTypes() Q_DECL_OVERRIDE;
+
+private:
+    const QString m_name;
+    const QAudio::Mode m_mode;
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/plugins/qnx/audio/qnxaudiooutput.cpp b/src/plugins/qnx/audio/qnxaudiooutput.cpp
new file mode 100644 (file)
index 0000000..4a82e93
--- /dev/null
@@ -0,0 +1,443 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Research In Motion
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia.  For licensing terms and
+** conditions see http://qt.digia.com/licensing.  For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file.  Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights.  These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file.  Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qnxaudiooutput.h"
+
+#include "qnxaudioutils.h"
+
+#include <private/qaudiohelpers_p.h>
+
+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_timer.setSingleShot(false);
+    m_timer.setInterval(20);
+    connect(&m_timer, SIGNAL(timeout()), this, SLOT(pullData()));
+}
+
+QnxAudioOutput::~QnxAudioOutput()
+{
+    stop();
+}
+
+void QnxAudioOutput::start(QIODevice *source)
+{
+    if (m_state != QAudio::StoppedState)
+        stop();
+
+    m_error = QAudio::NoError;
+    m_source = source;
+    m_pushSource = false;
+
+    if (open()) {
+        setState(QAudio::ActiveState);
+        m_timer.start();
+    } else {
+        setError(QAudio::OpenError);
+        setState(QAudio::StoppedState);
+    }
+}
+
+QIODevice *QnxAudioOutput::start()
+{
+    if (m_state != QAudio::StoppedState)
+        stop();
+
+    m_error = QAudio::NoError;
+    m_source = new QnxPushIODevice(this);
+    m_source->open(QIODevice::WriteOnly|QIODevice::Unbuffered);
+    m_pushSource = true;
+
+    if (open())
+        setState(QAudio::IdleState);
+    else {
+        setError(QAudio::OpenError);
+        setState(QAudio::StoppedState);
+    }
+
+    return m_source;
+}
+
+void QnxAudioOutput::stop()
+{
+    if (m_state == QAudio::StoppedState)
+        return;
+
+    setError(QAudio::NoError);
+    setState(QAudio::StoppedState);
+    close();
+}
+
+void QnxAudioOutput::reset()
+{
+    if (m_pcmHandle)
+        snd_pcm_playback_drain(m_pcmHandle);
+    stop();
+}
+
+void QnxAudioOutput::suspend()
+{
+    m_timer.stop();
+    setState(QAudio::SuspendedState);
+}
+
+void QnxAudioOutput::resume()
+{
+    if (m_pushSource)
+        setState(QAudio::IdleState);
+    else {
+        setState(QAudio::ActiveState);
+        m_timer.start();
+    }
+}
+
+int QnxAudioOutput::bytesFree() const
+{
+    if (m_state != QAudio::ActiveState && m_state != QAudio::IdleState)
+        return 0;
+
+    snd_pcm_channel_status_t status;
+    status.channel = SND_PCM_CHANNEL_PLAYBACK;
+    const int errorCode = snd_pcm_plugin_status(m_pcmHandle, &status);
+
+    if (errorCode)
+        return 0;
+    else
+        return status.free;
+}
+
+int QnxAudioOutput::periodSize() const
+{
+     return m_periodSize;
+}
+
+void QnxAudioOutput::setNotifyInterval(int ms)
+{
+    m_notifyInterval = ms;
+}
+
+int QnxAudioOutput::notifyInterval() const
+{
+    return m_notifyInterval;
+}
+
+qint64 QnxAudioOutput::processedUSecs() const
+{
+    return qint64(1000000) * m_format.framesForBytes(m_bytesWritten) / m_format.sampleRate();
+}
+
+qint64 QnxAudioOutput::elapsedUSecs() const
+{
+    if (m_state == QAudio::StoppedState)
+        return 0;
+    else
+        return m_startTimeStamp.elapsed() * 1000;
+}
+
+QAudio::Error QnxAudioOutput::error() const
+{
+    return m_error;
+}
+
+QAudio::State QnxAudioOutput::state() const
+{
+    return m_state;
+}
+
+void QnxAudioOutput::setFormat(const QAudioFormat &format)
+{
+    if (m_state == QAudio::StoppedState)
+        m_format = format;
+}
+
+QAudioFormat QnxAudioOutput::format() const
+{
+    return m_format;
+}
+
+void QnxAudioOutput::setVolume(qreal volume)
+{
+    m_volume = qBound(qreal(0.0), volume, qreal(1.0));
+}
+
+qreal QnxAudioOutput::volume() const
+{
+    return m_volume;
+}
+
+void QnxAudioOutput::pullData()
+{
+    if (m_state == QAudio::StoppedState || m_state == QAudio::SuspendedState)
+        return;
+
+    const int bytesAvailable = bytesFree();
+    const int frames = m_format.framesForBytes(bytesAvailable);
+
+    if (frames == 0 || bytesAvailable < periodSize())
+        return;
+
+    const int bytesRequested = m_format.bytesForFrames(frames);
+
+    char buffer[bytesRequested];
+    const int bytesRead = m_source->read(buffer, bytesRequested);
+
+    // reading can take a while and stream may have been stopped
+    if (!m_pcmHandle)
+        return;
+
+    if (bytesRead > 0) {
+        // Got some data to output
+        if (m_state != QAudio::ActiveState)
+            return;
+
+        const qint64 bytesWritten = write(buffer, bytesRead);
+        if (bytesWritten != bytesRead)
+            m_source->seek(m_source->pos()-(bytesRead-bytesWritten));
+
+    } else {
+        // We're done
+        close();
+        if (bytesRead != 0)
+            setError(QAudio::IOError);
+        setState(QAudio::StoppedState);
+    }
+
+    if (m_state != QAudio::ActiveState)
+        return;
+
+    if (m_notifyInterval > 0 && (m_intervalTimeStamp.elapsed() + m_intervalOffset) > m_notifyInterval) {
+        emit notify();
+        m_intervalOffset = m_intervalTimeStamp.elapsed() + m_intervalOffset - m_notifyInterval;
+        m_intervalTimeStamp.restart();
+    }
+}
+
+bool QnxAudioOutput::open()
+{
+    if (!m_format.isValid() || m_format.sampleRate() <= 0) {
+        if (!m_format.isValid())
+            qWarning("QnxAudioOutput: open error, invalid format.");
+        else
+            qWarning("QnxAudioOutput: open error, invalid sample rate (%d).", m_format.sampleRate());
+
+        return false;
+    }
+
+    int errorCode = 0;
+
+    int card = 0;
+    int device = 0;
+    if ((errorCode = snd_pcm_open_preferred(&m_pcmHandle, &card, &device, SND_PCM_OPEN_PLAYBACK)) < 0) {
+        qWarning("QnxAudioOutput: open error, couldn't open card (0x%x)", -errorCode);
+        return false;
+    }
+
+    if ((errorCode = snd_pcm_nonblock_mode(m_pcmHandle, 0)) < 0) {
+        qWarning("QnxAudioOutput: open error, couldn't set non block mode (0x%x)", -errorCode);
+        close();
+        return false;
+    }
+
+    // Necessary so that bytesFree() which uses the "free" member of the status struct works
+    snd_pcm_plugin_set_disable(m_pcmHandle, PLUGIN_DISABLE_MMAP);
+
+    snd_pcm_channel_info_t info;
+    memset(&info, 0, sizeof(info));
+    info.channel = SND_PCM_CHANNEL_PLAYBACK;
+    if ((errorCode = snd_pcm_plugin_info(m_pcmHandle, &info)) < 0) {
+        qWarning("QnxAudioOutput: open error, couldn't get channel info (0x%x)", -errorCode);
+        close();
+        return false;
+    }
+
+    snd_pcm_channel_params_t params = QnxAudioUtils::formatToChannelParams(m_format, QAudio::AudioOutput, info.max_fragment_size);
+
+    if ((errorCode = snd_pcm_plugin_params(m_pcmHandle, &params)) < 0) {
+        qWarning("QnxAudioOutput: open error, couldn't set channel params (0x%x)", -errorCode);
+        close();
+        return false;
+    }
+
+    if ((errorCode = snd_pcm_plugin_prepare(m_pcmHandle, SND_PCM_CHANNEL_PLAYBACK)) < 0) {
+        qWarning("QnxAudioOutput: open error, couldn't prepare channel (0x%x)", -errorCode);
+        close();
+        return false;
+    }
+
+    snd_pcm_channel_setup_t setup;
+    memset(&setup, 0, sizeof(setup));
+    setup.channel = SND_PCM_CHANNEL_PLAYBACK;
+    if ((errorCode = snd_pcm_plugin_setup(m_pcmHandle, &setup)) < 0) {
+        qWarning("QnxAudioOutput: open error, couldn't get channel setup (0x%x)", -errorCode);
+        close();
+        return false;
+    }
+
+    m_periodSize = qMin(2048, setup.buf.block.frag_size);
+    m_startTimeStamp.restart();
+    m_intervalTimeStamp.restart();
+    m_intervalOffset = 0;
+    m_bytesWritten = 0;
+
+    return true;
+}
+
+void QnxAudioOutput::close()
+{
+    m_timer.stop();
+
+    if (m_pcmHandle) {
+        snd_pcm_plugin_flush(m_pcmHandle, SND_PCM_CHANNEL_PLAYBACK);
+        snd_pcm_close(m_pcmHandle);
+        m_pcmHandle = 0;
+    }
+
+    if (m_pushSource) {
+        delete m_source;
+        m_source = 0;
+    }
+}
+
+void QnxAudioOutput::setError(QAudio::Error error)
+{
+    if (m_error != error) {
+        m_error = error;
+        emit errorChanged(error);
+    }
+}
+
+void QnxAudioOutput::setState(QAudio::State state)
+{
+    if (m_state != state) {
+        m_state = state;
+        emit stateChanged(state);
+    }
+}
+
+qint64 QnxAudioOutput::write(const char *data, qint64 len)
+{
+    if (!m_pcmHandle)
+        return 0;
+
+    // Make sure we're writing (N * frame) worth of bytes
+    const int size = m_format.bytesForFrames(qBound(qint64(0), qint64(bytesFree()), len) / m_format.bytesPerFrame());
+
+    if (size == 0)
+        return 0;
+
+    int written = 0;
+
+    if (m_volume < 1.0f) {
+        char out[size];
+        QAudioHelperInternal::qMultiplySamples(m_volume, m_format, data, out, size);
+        written = snd_pcm_plugin_write(m_pcmHandle, out, size);
+    } else {
+        written = snd_pcm_plugin_write(m_pcmHandle, data, size);
+    }
+
+    if (written > 0) {
+        m_bytesWritten += written;
+        setError(QAudio::NoError);
+        setState(QAudio::ActiveState);
+        return written;
+    } else {
+        close();
+        setError(QAudio::FatalError);
+        setState(QAudio::StoppedState);
+        return 0;
+    }
+}
+
+QnxPushIODevice::QnxPushIODevice(QnxAudioOutput *output)
+    : QIODevice(output),
+      m_output(output)
+{
+}
+
+QnxPushIODevice::~QnxPushIODevice()
+{
+}
+
+qint64 QnxPushIODevice::readData(char *data, qint64 len)
+{
+    Q_UNUSED(data);
+    Q_UNUSED(len);
+    return 0;
+}
+
+qint64 QnxPushIODevice::writeData(const char *data, qint64 len)
+{
+    int retry = 0;
+    qint64 written = 0;
+
+    if (m_output->state() == QAudio::ActiveState
+     || m_output->state() == QAudio::IdleState) {
+        while (written < len) {
+            const int writeSize = m_output->write(data + written, len - written);
+
+            if (writeSize <= 0) {
+                retry++;
+                if (retry > 10)
+                    return written;
+                else
+                    continue;
+            }
+
+            retry = 0;
+            written += writeSize;
+        }
+    }
+
+    return written;
+}
+
+QT_END_NAMESPACE
diff --git a/src/plugins/qnx/audio/qnxaudiooutput.h b/src/plugins/qnx/audio/qnxaudiooutput.h
new file mode 100644 (file)
index 0000000..77c15e4
--- /dev/null
@@ -0,0 +1,131 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Research In Motion
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia.  For licensing terms and
+** conditions see http://qt.digia.com/licensing.  For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file.  Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights.  These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file.  Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QNXAUDIOOUTPUT_H
+#define QNXAUDIOOUTPUT_H
+
+#include "qaudiosystem.h"
+
+#include <QTime>
+#include <QTimer>
+
+#include <sys/asoundlib.h>
+
+QT_BEGIN_NAMESPACE
+
+class QnxPushIODevice;
+
+class QnxAudioOutput : public QAbstractAudioOutput
+{
+    Q_OBJECT
+
+public:
+    QnxAudioOutput();
+    ~QnxAudioOutput();
+
+    void start(QIODevice *source) Q_DECL_OVERRIDE;
+    QIODevice *start() Q_DECL_OVERRIDE;
+    void stop() Q_DECL_OVERRIDE;
+    void reset() Q_DECL_OVERRIDE;
+    void suspend() Q_DECL_OVERRIDE;
+    void resume() Q_DECL_OVERRIDE;
+    int bytesFree() const Q_DECL_OVERRIDE;
+    int periodSize() const Q_DECL_OVERRIDE;
+    void setBufferSize(int) Q_DECL_OVERRIDE {}
+    int bufferSize() const Q_DECL_OVERRIDE { return 0; }
+    void setNotifyInterval(int ms) Q_DECL_OVERRIDE;
+    int notifyInterval() const Q_DECL_OVERRIDE;
+    qint64 processedUSecs() const Q_DECL_OVERRIDE;
+    qint64 elapsedUSecs() const Q_DECL_OVERRIDE;
+    QAudio::Error error() const Q_DECL_OVERRIDE;
+    QAudio::State state() const Q_DECL_OVERRIDE;
+    void setFormat(const QAudioFormat &format) Q_DECL_OVERRIDE;
+    QAudioFormat format() const Q_DECL_OVERRIDE;
+    void setVolume(qreal volume) Q_DECL_OVERRIDE;
+    qreal volume() const Q_DECL_OVERRIDE;
+
+private slots:
+    void pullData();
+
+private:
+    bool open();
+    void close();
+    void setError(QAudio::Error error);
+    void setState(QAudio::State state);
+
+    friend class QnxPushIODevice;
+    qint64 write(const char *data, qint64 len);
+
+    QIODevice *m_source;
+    bool m_pushSource;
+    QTimer m_timer;
+
+    int m_notifyInterval;
+    QAudio::Error m_error;
+    QAudio::State m_state;
+    QAudioFormat m_format;
+    qreal m_volume;
+    int m_periodSize;
+
+    snd_pcm_t *m_pcmHandle;
+    qint64 m_bytesWritten;
+    QTime m_startTimeStamp;
+    QTime m_intervalTimeStamp;
+    qint64 m_intervalOffset;
+};
+
+class QnxPushIODevice : public QIODevice
+{
+    Q_OBJECT
+public:
+    explicit QnxPushIODevice(QnxAudioOutput *output);
+    ~QnxPushIODevice();
+
+    qint64 readData(char *data, qint64 len);
+    qint64 writeData(const char *data, qint64 len);
+
+private:
+    QnxAudioOutput *m_output;
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/plugins/qnx/audio/qnxaudioplugin.cpp b/src/plugins/qnx/audio/qnxaudioplugin.cpp
new file mode 100644 (file)
index 0000000..e24374a
--- /dev/null
@@ -0,0 +1,85 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Research In Motion
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia.  For licensing terms and
+** conditions see http://qt.digia.com/licensing.  For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file.  Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights.  These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file.  Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qnxaudioplugin.h"
+
+#include "qnxaudiodeviceinfo.h"
+#include "qnxaudiooutput.h"
+
+#include <sys/asoundlib.h>
+
+static const char *OUTPUT_ID = "QnxAudioOutput";
+
+QT_BEGIN_NAMESPACE
+
+QnxAudioPlugin::QnxAudioPlugin(QObject *parent)
+    : QAudioSystemPlugin(parent)
+{
+}
+
+QList<QByteArray> QnxAudioPlugin::availableDevices(QAudio::Mode mode) const
+{
+    if (mode == QAudio::AudioOutput)
+        return QList<QByteArray>() << OUTPUT_ID;
+    else
+        return QList<QByteArray>();
+}
+
+QAbstractAudioInput *QnxAudioPlugin::createInput(const QByteArray &device)
+{
+    Q_UNUSED(device);
+    return 0;
+}
+
+QAbstractAudioOutput *QnxAudioPlugin::createOutput(const QByteArray &device)
+{
+    Q_ASSERT(device == OUTPUT_ID);
+    Q_UNUSED(device);
+    return new QnxAudioOutput();
+}
+
+QAbstractAudioDeviceInfo *QnxAudioPlugin::createDeviceInfo(const QByteArray &device, QAudio::Mode mode)
+{
+    Q_ASSERT(device == OUTPUT_ID || device == INPUT_ID);
+    return new QnxAudioDeviceInfo(device, mode);
+}
+
+QT_END_NAMESPACE
diff --git a/src/plugins/qnx/audio/qnxaudioplugin.h b/src/plugins/qnx/audio/qnxaudioplugin.h
new file mode 100644 (file)
index 0000000..1886057
--- /dev/null
@@ -0,0 +1,66 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Research In Motion
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia.  For licensing terms and
+** conditions see http://qt.digia.com/licensing.  For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file.  Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights.  These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file.  Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QNXAUDIOPLUGIN_H
+#define QNXAUDIOPLUGIN_H
+
+#include <qaudiosystemplugin.h>
+
+QT_BEGIN_NAMESPACE
+
+class QnxAudioPlugin : public QAudioSystemPlugin
+{
+    Q_OBJECT
+    Q_PLUGIN_METADATA(IID "org.qt-project.qt.audiosystemfactory/5.0" FILE "qnx_audio.json")
+
+public:
+    explicit QnxAudioPlugin(QObject *parent = 0);
+    ~QnxAudioPlugin() {}
+
+    QList<QByteArray> availableDevices(QAudio::Mode mode) const Q_DECL_OVERRIDE;
+    QAbstractAudioInput *createInput(const QByteArray &device) Q_DECL_OVERRIDE;
+    QAbstractAudioOutput *createOutput(const QByteArray &device) Q_DECL_OVERRIDE;
+    QAbstractAudioDeviceInfo *createDeviceInfo(const QByteArray &device, QAudio::Mode mode) Q_DECL_OVERRIDE;
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/plugins/qnx/audio/qnxaudioutils.cpp b/src/plugins/qnx/audio/qnxaudioutils.cpp
new file mode 100644 (file)
index 0000000..d6400c2
--- /dev/null
@@ -0,0 +1,130 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Research In Motion
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia.  For licensing terms and
+** conditions see http://qt.digia.com/licensing.  For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file.  Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights.  These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file.  Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qnxaudioutils.h"
+
+QT_BEGIN_NAMESPACE
+
+snd_pcm_channel_params_t QnxAudioUtils::formatToChannelParams(const QAudioFormat &format, QAudio::Mode mode, int fragmentSize)
+{
+    snd_pcm_channel_params_t params;
+    memset(&params, 0, sizeof(params));
+    params.channel = (mode == QAudio::AudioOutput) ? SND_PCM_CHANNEL_PLAYBACK : SND_PCM_CHANNEL_CAPTURE;
+    params.mode = SND_PCM_MODE_BLOCK;
+    params.start_mode = SND_PCM_START_DATA;
+    params.stop_mode = SND_PCM_STOP_ROLLOVER;
+    params.buf.block.frag_size = fragmentSize;
+    params.buf.block.frags_min = 1;
+    params.buf.block.frags_max = 1;
+    strcpy(params.sw_mixer_subchn_name, "QAudio Channel");
+
+    params.format.interleave = 1;
+    params.format.rate = format.sampleRate();
+    params.format.voices = format.channelCount();
+
+    switch (format.sampleSize()) {
+    case 8:
+        switch (format.sampleType()) {
+        case QAudioFormat::SignedInt:
+            params.format.format = SND_PCM_SFMT_S8;
+            break;
+        case QAudioFormat::UnSignedInt:
+            params.format.format = SND_PCM_SFMT_U8;
+            break;
+        default:
+            break;
+        }
+        break;
+
+    case 16:
+        switch (format.sampleType()) {
+        case QAudioFormat::SignedInt:
+            if (format.byteOrder() == QAudioFormat::LittleEndian) {
+                params.format.format = SND_PCM_SFMT_S16_LE;
+            } else {
+                params.format.format = SND_PCM_SFMT_S16_BE;
+            }
+            break;
+        case QAudioFormat::UnSignedInt:
+            if (format.byteOrder() == QAudioFormat::LittleEndian) {
+                params.format.format = SND_PCM_SFMT_U16_LE;
+            } else {
+                params.format.format = SND_PCM_SFMT_U16_BE;
+            }
+            break;
+        default:
+            break;
+        }
+        break;
+
+    case 32:
+        switch (format.sampleType()) {
+        case QAudioFormat::SignedInt:
+            if (format.byteOrder() == QAudioFormat::LittleEndian) {
+                params.format.format = SND_PCM_SFMT_S32_LE;
+            } else {
+                params.format.format = SND_PCM_SFMT_S32_BE;
+            }
+            break;
+        case QAudioFormat::UnSignedInt:
+            if (format.byteOrder() == QAudioFormat::LittleEndian) {
+                params.format.format = SND_PCM_SFMT_U32_LE;
+            } else {
+                params.format.format = SND_PCM_SFMT_U32_BE;
+            }
+            break;
+        case QAudioFormat::Float:
+            if (format.byteOrder() == QAudioFormat::LittleEndian) {
+                params.format.format = SND_PCM_SFMT_FLOAT_LE;
+            } else {
+                params.format.format = SND_PCM_SFMT_FLOAT_BE;
+            }
+            break;
+        default:
+            break;
+        }
+        break;
+    }
+
+    return params;
+}
+
+QT_END_NAMESPACE
diff --git a/src/plugins/qnx/audio/qnxaudioutils.h b/src/plugins/qnx/audio/qnxaudioutils.h
new file mode 100644 (file)
index 0000000..ddd30b1
--- /dev/null
@@ -0,0 +1,57 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Research In Motion
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia.  For licensing terms and
+** conditions see http://qt.digia.com/licensing.  For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file.  Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights.  These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file.  Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QNXAUDIOUTILS_H
+#define QNXAUDIOUTILS_H
+
+#include "qaudiosystem.h"
+#include <sys/asoundlib.h>
+
+QT_BEGIN_NAMESPACE
+
+namespace QnxAudioUtils
+{
+    snd_pcm_channel_params_t formatToChannelParams(const QAudioFormat &format, QAudio::Mode mode, int fragmentSize);
+}
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/plugins/qnx/qnx.pro b/src/plugins/qnx/qnx.pro
new file mode 100644 (file)
index 0000000..3049729
--- /dev/null
@@ -0,0 +1,3 @@
+TEMPLATE = subdirs
+
+SUBDIRS = audio