From fb6e49f33fab1046aa234be4994219719a3963a8 Mon Sep 17 00:00:00 2001 From: Kevin Ottens Date: Thu, 21 Feb 2013 14:28:12 +0100 Subject: [PATCH] BlackBerry/QNX: Add a new backend for Blackberry handling audio 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 --- src/plugins/plugins.pro | 4 + src/plugins/qnx/audio/audio.pro | 24 ++ src/plugins/qnx/audio/qnx_audio.json | 3 + src/plugins/qnx/audio/qnxaudiodeviceinfo.cpp | 150 +++++++++ src/plugins/qnx/audio/qnxaudiodeviceinfo.h | 74 +++++ src/plugins/qnx/audio/qnxaudiooutput.cpp | 443 +++++++++++++++++++++++++++ src/plugins/qnx/audio/qnxaudiooutput.h | 131 ++++++++ src/plugins/qnx/audio/qnxaudioplugin.cpp | 85 +++++ src/plugins/qnx/audio/qnxaudioplugin.h | 66 ++++ src/plugins/qnx/audio/qnxaudioutils.cpp | 130 ++++++++ src/plugins/qnx/audio/qnxaudioutils.h | 57 ++++ src/plugins/qnx/qnx.pro | 3 + 12 files changed, 1170 insertions(+) create mode 100644 src/plugins/qnx/audio/audio.pro create mode 100644 src/plugins/qnx/audio/qnx_audio.json create mode 100644 src/plugins/qnx/audio/qnxaudiodeviceinfo.cpp create mode 100644 src/plugins/qnx/audio/qnxaudiodeviceinfo.h create mode 100644 src/plugins/qnx/audio/qnxaudiooutput.cpp create mode 100644 src/plugins/qnx/audio/qnxaudiooutput.h create mode 100644 src/plugins/qnx/audio/qnxaudioplugin.cpp create mode 100644 src/plugins/qnx/audio/qnxaudioplugin.h create mode 100644 src/plugins/qnx/audio/qnxaudioutils.cpp create mode 100644 src/plugins/qnx/audio/qnxaudioutils.h create mode 100644 src/plugins/qnx/qnx.pro diff --git a/src/plugins/plugins.pro b/src/plugins/plugins.pro index 24708e0..c8826a8 100644 --- a/src/plugins/plugins.pro +++ b/src/plugins/plugins.pro @@ -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 index 0000000..3b67fbf --- /dev/null +++ b/src/plugins/qnx/audio/audio.pro @@ -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 index 0000000..a31d521 --- /dev/null +++ b/src/plugins/qnx/audio/qnx_audio.json @@ -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 index 0000000..ce80835 --- /dev/null +++ b/src/plugins/qnx/audio/qnxaudiodeviceinfo.cpp @@ -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 + +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, ¶ms); + snd_pcm_close(handle); + + return errorCode == 0; +} + +QString QnxAudioDeviceInfo::deviceName() const +{ + return m_name; +} + +QStringList QnxAudioDeviceInfo::supportedCodecs() +{ + return QStringList() << QLatin1String("audio/pcm"); +} + +QList QnxAudioDeviceInfo::supportedSampleRates() +{ + return QList() << 8000 << 11025 << 22050 << 44100 << 48000; +} + +QList QnxAudioDeviceInfo::supportedChannelCounts() +{ + return QList() << 1 << 2; +} + +QList QnxAudioDeviceInfo::supportedSampleSizes() +{ + return QList() << 8 << 16 << 32; +} + +QList QnxAudioDeviceInfo::supportedByteOrders() +{ + return QList() << QAudioFormat::LittleEndian << QAudioFormat::BigEndian; +} + +QList QnxAudioDeviceInfo::supportedSampleTypes() +{ + return QList() << 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 index 0000000..72c10cc --- /dev/null +++ b/src/plugins/qnx/audio/qnxaudiodeviceinfo.h @@ -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 supportedSampleRates() Q_DECL_OVERRIDE; + QList supportedChannelCounts() Q_DECL_OVERRIDE; + QList supportedSampleSizes() Q_DECL_OVERRIDE; + QList supportedByteOrders() Q_DECL_OVERRIDE; + QList 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 index 0000000..4a82e93 --- /dev/null +++ b/src/plugins/qnx/audio/qnxaudiooutput.cpp @@ -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 + +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, ¶ms)) < 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 index 0000000..77c15e4 --- /dev/null +++ b/src/plugins/qnx/audio/qnxaudiooutput.h @@ -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 +#include + +#include + +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 index 0000000..e24374a --- /dev/null +++ b/src/plugins/qnx/audio/qnxaudioplugin.cpp @@ -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 + +static const char *OUTPUT_ID = "QnxAudioOutput"; + +QT_BEGIN_NAMESPACE + +QnxAudioPlugin::QnxAudioPlugin(QObject *parent) + : QAudioSystemPlugin(parent) +{ +} + +QList QnxAudioPlugin::availableDevices(QAudio::Mode mode) const +{ + if (mode == QAudio::AudioOutput) + return QList() << OUTPUT_ID; + else + return QList(); +} + +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 index 0000000..1886057 --- /dev/null +++ b/src/plugins/qnx/audio/qnxaudioplugin.h @@ -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 + +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 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 index 0000000..d6400c2 --- /dev/null +++ b/src/plugins/qnx/audio/qnxaudioutils.cpp @@ -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(¶ms, 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 index 0000000..ddd30b1 --- /dev/null +++ b/src/plugins/qnx/audio/qnxaudioutils.h @@ -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 + +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 index 0000000..3049729 --- /dev/null +++ b/src/plugins/qnx/qnx.pro @@ -0,0 +1,3 @@ +TEMPLATE = subdirs + +SUBDIRS = audio -- 2.7.4