From 680c9cab916491cdc4310df5f87c946a50f606b4 Mon Sep 17 00:00:00 2001 From: Yoann Lopes Date: Thu, 28 Feb 2013 18:05:57 +0100 Subject: OpenSL ES audio plugin. Adds support for QAudioOutput, QAudioInput and QAudioDeviceInfo using OpenSL ES 1.0.1. This plugin is used on Android. Change-Id: Idf2c22a861e067196f6c5139e51393b086f64183 Reviewed-by: Christian Stromme --- config.tests/opensles/main.cpp | 47 ++ config.tests/opensles/opensles.pro | 2 + qtmultimedia.pro | 1 + src/plugins/opensles/opensles.json | 3 + src/plugins/opensles/opensles.pro | 25 + src/plugins/opensles/qopenslesaudioinput.cpp | 505 +++++++++++++++++++++ src/plugins/opensles/qopenslesaudioinput.h | 134 ++++++ src/plugins/opensles/qopenslesaudiooutput.cpp | 628 ++++++++++++++++++++++++++ src/plugins/opensles/qopenslesaudiooutput.h | 155 +++++++ src/plugins/opensles/qopenslesdeviceinfo.cpp | 115 +++++ src/plugins/opensles/qopenslesdeviceinfo.h | 77 ++++ src/plugins/opensles/qopenslesengine.cpp | 207 +++++++++ src/plugins/opensles/qopenslesengine.h | 82 ++++ src/plugins/opensles/qopenslesplugin.cpp | 78 ++++ src/plugins/opensles/qopenslesplugin.h | 72 +++ src/plugins/plugins.pro | 3 + 16 files changed, 2134 insertions(+) create mode 100644 config.tests/opensles/main.cpp create mode 100644 config.tests/opensles/opensles.pro create mode 100644 src/plugins/opensles/opensles.json create mode 100644 src/plugins/opensles/opensles.pro create mode 100644 src/plugins/opensles/qopenslesaudioinput.cpp create mode 100644 src/plugins/opensles/qopenslesaudioinput.h create mode 100644 src/plugins/opensles/qopenslesaudiooutput.cpp create mode 100644 src/plugins/opensles/qopenslesaudiooutput.h create mode 100644 src/plugins/opensles/qopenslesdeviceinfo.cpp create mode 100644 src/plugins/opensles/qopenslesdeviceinfo.h create mode 100644 src/plugins/opensles/qopenslesengine.cpp create mode 100644 src/plugins/opensles/qopenslesengine.h create mode 100644 src/plugins/opensles/qopenslesplugin.cpp create mode 100644 src/plugins/opensles/qopenslesplugin.h diff --git a/config.tests/opensles/main.cpp b/config.tests/opensles/main.cpp new file mode 100644 index 000000000..45fd2f05d --- /dev/null +++ b/config.tests/opensles/main.cpp @@ -0,0 +1,47 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** 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 + +int main() +{ + return 0; +} diff --git a/config.tests/opensles/opensles.pro b/config.tests/opensles/opensles.pro new file mode 100644 index 000000000..def49c7ac --- /dev/null +++ b/config.tests/opensles/opensles.pro @@ -0,0 +1,2 @@ +LIBS += -lOpenSLES +SOURCES += main.cpp diff --git a/qtmultimedia.pro b/qtmultimedia.pro index ed4a1bfc9..593dff24f 100644 --- a/qtmultimedia.pro +++ b/qtmultimedia.pro @@ -2,6 +2,7 @@ requires(qtHaveModule(gui)) load(configure) qtCompileTest(openal) +qtCompileTest(opensles) win32 { qtCompileTest(directshow) qtCompileTest(wmsdk) diff --git a/src/plugins/opensles/opensles.json b/src/plugins/opensles/opensles.json new file mode 100644 index 000000000..a31d52107 --- /dev/null +++ b/src/plugins/opensles/opensles.json @@ -0,0 +1,3 @@ +{ + "Keys": ["default"] +} diff --git a/src/plugins/opensles/opensles.pro b/src/plugins/opensles/opensles.pro new file mode 100644 index 000000000..53c5a120b --- /dev/null +++ b/src/plugins/opensles/opensles.pro @@ -0,0 +1,25 @@ +TARGET = qtaudio_opensles +QT += multimedia-private + +PLUGIN_TYPE = audio +PLUGIN_CLASS_NAME = QOpenSLESPlugin +load(qt_plugin) + +LIBS += -lOpenSLES + +HEADERS += \ + qopenslesplugin.h \ + qopenslesengine.h \ + qopenslesdeviceinfo.h \ + qopenslesaudioinput.h \ + qopenslesaudiooutput.h + +SOURCES += \ + qopenslesplugin.cpp \ + qopenslesengine.cpp \ + qopenslesdeviceinfo.cpp \ + qopenslesaudioinput.cpp \ + qopenslesaudiooutput.cpp + +OTHER_FILES += \ + opensles.json diff --git a/src/plugins/opensles/qopenslesaudioinput.cpp b/src/plugins/opensles/qopenslesaudioinput.cpp new file mode 100644 index 000000000..d07421f0e --- /dev/null +++ b/src/plugins/opensles/qopenslesaudioinput.cpp @@ -0,0 +1,505 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** 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 "qopenslesaudioinput.h" + +#include "qopenslesengine.h" +#include +#include + +#ifdef ANDROID +#include +#endif + +QT_BEGIN_NAMESPACE + +#define NUM_BUFFERS 2 +#define DEFAULT_PERIOD_TIME_MS 50 +#define MINIMUM_PERIOD_TIME_MS 5 + +#ifdef ANDROID +static void bufferQueueCallback(SLAndroidSimpleBufferQueueItf, void *context) +#else +static void bufferQueueCallback(SLBufferQueueItf, void *context) +#endif +{ + // Process buffer in main thread + QMetaObject::invokeMethod(reinterpret_cast(context), "processBuffer"); +} + +QOpenSLESAudioInput::QOpenSLESAudioInput(const QByteArray &device) + : m_device(device) + , m_engine(QOpenSLESEngine::instance()) + , m_recorderObject(0) + , m_recorder(0) + , m_bufferQueue(0) + , m_pullMode(true) + , m_processedBytes(0) + , m_audioSource(0) + , m_bufferIODevice(0) + , m_errorState(QAudio::NoError) + , m_deviceState(QAudio::StoppedState) + , m_lastNotifyTime(0) + , m_volume(1) + , m_bufferSize(0) + , m_periodSize(0) + , m_intervalTime(1000) + , m_buffers(new QByteArray[NUM_BUFFERS]) + , m_currentBuffer(0) +{ +#ifdef ANDROID + if (qstrcmp(device, QT_ANDROID_PRESET_CAMCORDER) == 0) + m_recorderPreset = SL_ANDROID_RECORDING_PRESET_CAMCORDER; + else if (qstrcmp(device, QT_ANDROID_PRESET_VOICE_RECOGNITION) == 0) + m_recorderPreset = SL_ANDROID_RECORDING_PRESET_VOICE_RECOGNITION; + else + m_recorderPreset = SL_ANDROID_RECORDING_PRESET_GENERIC; +#endif +} + +QOpenSLESAudioInput::~QOpenSLESAudioInput() +{ + if (m_recorderObject) + (*m_recorderObject)->Destroy(m_recorderObject); + delete[] m_buffers; +} + +QAudio::Error QOpenSLESAudioInput::error() const +{ + return m_errorState; +} + +QAudio::State QOpenSLESAudioInput::state() const +{ + return m_deviceState; +} + +void QOpenSLESAudioInput::setFormat(const QAudioFormat &format) +{ + if (m_deviceState == QAudio::StoppedState) + m_format = format; +} + +QAudioFormat QOpenSLESAudioInput::format() const +{ + return m_format; +} + +void QOpenSLESAudioInput::start(QIODevice *device) +{ + if (m_deviceState != QAudio::StoppedState) + stopRecording(); + + if (!m_pullMode && m_bufferIODevice) { + m_bufferIODevice->close(); + delete m_bufferIODevice; + m_bufferIODevice = 0; + } + + m_pullMode = true; + m_audioSource = device; + + if (startRecording()) { + m_deviceState = QAudio::ActiveState; + } else { + m_deviceState = QAudio::StoppedState; + Q_EMIT errorChanged(m_errorState); + } + + Q_EMIT stateChanged(m_deviceState); +} + +QIODevice *QOpenSLESAudioInput::start() +{ + if (m_deviceState != QAudio::StoppedState) + stopRecording(); + + m_audioSource = 0; + + if (!m_pullMode && m_bufferIODevice) { + m_bufferIODevice->close(); + delete m_bufferIODevice; + } + + m_pullMode = false; + m_pushBuffer.clear(); + m_bufferIODevice = new QBuffer(&m_pushBuffer); + m_bufferIODevice->open(QIODevice::ReadOnly); + + if (startRecording()) { + m_deviceState = QAudio::IdleState; + } else { + m_deviceState = QAudio::StoppedState; + Q_EMIT errorChanged(m_errorState); + m_bufferIODevice->close(); + delete m_bufferIODevice; + m_bufferIODevice = 0; + } + + Q_EMIT stateChanged(m_deviceState); + return m_bufferIODevice; +} + +bool QOpenSLESAudioInput::startRecording() +{ + m_processedBytes = 0; + m_clockStamp.restart(); + m_lastNotifyTime = 0; + + SLresult result; + + // configure audio source + SLDataLocator_IODevice loc_dev = { SL_DATALOCATOR_IODEVICE, SL_IODEVICE_AUDIOINPUT, + SL_DEFAULTDEVICEID_AUDIOINPUT, NULL }; + SLDataSource audioSrc = { &loc_dev, NULL }; + + // configure audio sink +#ifdef ANDROID + SLDataLocator_AndroidSimpleBufferQueue loc_bq = { SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE, + NUM_BUFFERS }; +#else + SLDataLocator_BufferQueue loc_bq = { SL_DATALOCATOR_BUFFERQUEUE, NUM_BUFFERS }; +#endif + + SLDataFormat_PCM format_pcm = QOpenSLESEngine::audioFormatToSLFormatPCM(m_format); + SLDataSink audioSnk = { &loc_bq, &format_pcm }; + + // create audio recorder + // (requires the RECORD_AUDIO permission) +#ifdef ANDROID + const SLInterfaceID id[2] = { SL_IID_ANDROIDSIMPLEBUFFERQUEUE, SL_IID_ANDROIDCONFIGURATION }; + const SLboolean req[2] = { SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE }; +#else + const SLInterfaceID id[1] = { SL_IID_BUFFERQUEUE }; + const SLboolean req[1] = { SL_BOOLEAN_TRUE }; +#endif + + result = (*m_engine->slEngine())->CreateAudioRecorder(m_engine->slEngine(), &m_recorderObject, + &audioSrc, &audioSnk, + sizeof(req) / sizeof(SLboolean), id, req); + if (result != SL_RESULT_SUCCESS) { + m_errorState = QAudio::OpenError; + return false; + } + +#ifdef ANDROID + // configure recorder source + SLAndroidConfigurationItf configItf; + result = (*m_recorderObject)->GetInterface(m_recorderObject, SL_IID_ANDROIDCONFIGURATION, + &configItf); + if (result != SL_RESULT_SUCCESS) { + m_errorState = QAudio::OpenError; + return false; + } + + result = (*configItf)->SetConfiguration(configItf, SL_ANDROID_KEY_RECORDING_PRESET, + &m_recorderPreset, sizeof(SLuint32)); + + SLuint32 presetValue = SL_ANDROID_RECORDING_PRESET_NONE; + SLuint32 presetSize = 2*sizeof(SLuint32); // intentionally too big + result = (*configItf)->GetConfiguration(configItf, SL_ANDROID_KEY_RECORDING_PRESET, + &presetSize, (void*)&presetValue); + + if (result != SL_RESULT_SUCCESS || presetValue == SL_ANDROID_RECORDING_PRESET_NONE) { + m_errorState = QAudio::OpenError; + return false; + } +#endif + + // realize the audio recorder + result = (*m_recorderObject)->Realize(m_recorderObject, SL_BOOLEAN_FALSE); + if (result != SL_RESULT_SUCCESS) { + m_errorState = QAudio::OpenError; + return false; + } + + // get the record interface + result = (*m_recorderObject)->GetInterface(m_recorderObject, SL_IID_RECORD, &m_recorder); + if (result != SL_RESULT_SUCCESS) { + m_errorState = QAudio::FatalError; + return false; + } + + // get the buffer queue interface +#ifdef ANDROID + SLInterfaceID bufferqueueItfID = SL_IID_ANDROIDSIMPLEBUFFERQUEUE; +#else + SLInterfaceID bufferqueueItfID = SL_IID_BUFFERQUEUE; +#endif + result = (*m_recorderObject)->GetInterface(m_recorderObject, bufferqueueItfID, &m_bufferQueue); + if (result != SL_RESULT_SUCCESS) { + m_errorState = QAudio::FatalError; + return false; + } + + // register callback on the buffer queue + result = (*m_bufferQueue)->RegisterCallback(m_bufferQueue, bufferQueueCallback, this); + if (result != SL_RESULT_SUCCESS) { + m_errorState = QAudio::FatalError; + return false; + } + + if (m_bufferSize <= 0) { + m_bufferSize = m_format.bytesForDuration(DEFAULT_PERIOD_TIME_MS * 1000); + } else { + int minimumBufSize = m_format.bytesForDuration(MINIMUM_PERIOD_TIME_MS * 1000); + if (m_bufferSize < minimumBufSize) + m_bufferSize = minimumBufSize; + } + + m_periodSize = m_bufferSize; + + // enqueue empty buffers to be filled by the recorder + for (int i = 0; i < NUM_BUFFERS; ++i) { + m_buffers[i].resize(m_periodSize); + + result = (*m_bufferQueue)->Enqueue(m_bufferQueue, m_buffers[i].data(), m_periodSize); + if (result != SL_RESULT_SUCCESS) { + m_errorState = QAudio::FatalError; + return false; + } + } + + // start recording + result = (*m_recorder)->SetRecordState(m_recorder, SL_RECORDSTATE_RECORDING); + if (result != SL_RESULT_SUCCESS) { + m_errorState = QAudio::FatalError; + return false; + } + + m_errorState = QAudio::NoError; + + return true; +} + +void QOpenSLESAudioInput::stop() +{ + if (m_deviceState == QAudio::StoppedState) + return; + + m_deviceState = QAudio::StoppedState; + + stopRecording(); + + m_errorState = QAudio::NoError; + Q_EMIT stateChanged(m_deviceState); +} + +void QOpenSLESAudioInput::stopRecording() +{ + flushBuffers(); + + SLresult result; + result = (*m_recorder)->SetRecordState(m_recorder, SL_RECORDSTATE_STOPPED); + result = (*m_bufferQueue)->Clear(m_bufferQueue); + + (*m_recorderObject)->Destroy(m_recorderObject); + m_recorderObject = 0; + + for (int i = 0; i < NUM_BUFFERS; ++i) + m_buffers[i].clear(); + m_currentBuffer = 0; + + if (!m_pullMode && m_bufferIODevice) { + m_bufferIODevice->close(); + delete m_bufferIODevice; + m_bufferIODevice = 0; + m_pushBuffer.clear(); + } +} + +void QOpenSLESAudioInput::suspend() +{ + if (m_deviceState == QAudio::ActiveState) { + m_deviceState = QAudio::SuspendedState; + emit stateChanged(m_deviceState); + + (*m_recorder)->SetRecordState(m_recorder, SL_RECORDSTATE_PAUSED); + } +} + +void QOpenSLESAudioInput::resume() +{ + if (m_deviceState == QAudio::SuspendedState || m_deviceState == QAudio::IdleState) { + (*m_recorder)->SetRecordState(m_recorder, SL_RECORDSTATE_RECORDING); + + m_deviceState = QAudio::ActiveState; + emit stateChanged(m_deviceState); + } +} + +void QOpenSLESAudioInput::processBuffer() +{ + if (m_deviceState == QAudio::StoppedState || m_deviceState == QAudio::SuspendedState) + return; + + if (m_deviceState != QAudio::ActiveState) { + m_errorState = QAudio::NoError; + m_deviceState = QAudio::ActiveState; + emit stateChanged(m_deviceState); + } + + QByteArray *processedBuffer = &m_buffers[m_currentBuffer]; + writeDataToDevice(processedBuffer->constData(), processedBuffer->size()); + + // Re-enqueue the buffer + SLresult result = (*m_bufferQueue)->Enqueue(m_bufferQueue, + processedBuffer->data(), + processedBuffer->size()); + + m_currentBuffer = (m_currentBuffer + 1) % NUM_BUFFERS; + + // If the buffer queue is empty (shouldn't happen), stop recording. +#ifdef ANDROID + SLAndroidSimpleBufferQueueState state; +#else + SLBufferQueueState state; +#endif + result = (*m_bufferQueue)->GetState(m_bufferQueue, &state); + if (result != SL_RESULT_SUCCESS || state.count == 0) { + stop(); + m_errorState = QAudio::FatalError; + Q_EMIT errorChanged(m_errorState); + } +} + +void QOpenSLESAudioInput::writeDataToDevice(const char *data, int size) +{ + m_processedBytes += size; + + if (m_pullMode) { + // write buffer to the QIODevice + if (m_audioSource->write(data, size) < 0) { + stop(); + m_errorState = QAudio::IOError; + Q_EMIT errorChanged(m_errorState); + } + } else { + // emits readyRead() so user will call read() on QIODevice to get some audio data + if (m_bufferIODevice != 0) { + m_pushBuffer.append(data, size); + Q_EMIT m_bufferIODevice->readyRead(); + } + } + + // Send notify signal if needed + qint64 processedMsecs = processedUSecs() / 1000; + if (m_intervalTime && (processedMsecs - m_lastNotifyTime) >= m_intervalTime) { + Q_EMIT notify(); + m_lastNotifyTime = processedMsecs; + } +} + +void QOpenSLESAudioInput::flushBuffers() +{ + SLmillisecond recorderPos; + (*m_recorder)->GetPosition(m_recorder, &recorderPos); + qint64 devicePos = processedUSecs(); + + qint64 delta = recorderPos * 1000 - devicePos; + + if (delta > 0) + writeDataToDevice(m_buffers[m_currentBuffer].constData(), m_format.bytesForDuration(delta)); +} + +int QOpenSLESAudioInput::bytesReady() const +{ + if (m_deviceState == QAudio::ActiveState || m_deviceState == QAudio::SuspendedState) + return m_bufferIODevice ? m_bufferIODevice->bytesAvailable() : m_periodSize; + + return 0; +} + +void QOpenSLESAudioInput::setBufferSize(int value) +{ + m_bufferSize = value; +} + +int QOpenSLESAudioInput::bufferSize() const +{ + return m_bufferSize; +} + +int QOpenSLESAudioInput::periodSize() const +{ + return m_periodSize; +} + +void QOpenSLESAudioInput::setNotifyInterval(int ms) +{ + m_intervalTime = qMax(0, ms); +} + +int QOpenSLESAudioInput::notifyInterval() const +{ + return m_intervalTime; +} + +qint64 QOpenSLESAudioInput::processedUSecs() const +{ + return m_format.durationForBytes(m_processedBytes); +} + +qint64 QOpenSLESAudioInput::elapsedUSecs() const +{ + if (m_deviceState == QAudio::StoppedState) + return 0; + + return m_clockStamp.elapsed() * 1000; +} + +void QOpenSLESAudioInput::setVolume(qreal vol) +{ + // Volume interface is not available for the recorder on Android + m_volume = vol; +} + +qreal QOpenSLESAudioInput::volume() const +{ + return m_volume; +} + +void QOpenSLESAudioInput::reset() +{ + stop(); +} + +QT_END_NAMESPACE diff --git a/src/plugins/opensles/qopenslesaudioinput.h b/src/plugins/opensles/qopenslesaudioinput.h new file mode 100644 index 000000000..c71963087 --- /dev/null +++ b/src/plugins/opensles/qopenslesaudioinput.h @@ -0,0 +1,134 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** 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 QOPENSLESAUDIOINPUT_H +#define QOPENSLESAUDIOINPUT_H + +#include +#include +#include + +#ifdef ANDROID +#include + +#define QT_ANDROID_PRESET_MIC "mic" +#define QT_ANDROID_PRESET_CAMCORDER "camcorder" +#define QT_ANDROID_PRESET_VOICE_RECOGNITION "voicerecognition" + +#endif + +QT_BEGIN_NAMESPACE + +class QOpenSLESEngine; +class QIODevice; +class QBuffer; + +class QOpenSLESAudioInput : public QAbstractAudioInput +{ + Q_OBJECT + +public: + QOpenSLESAudioInput(const QByteArray &device); + ~QOpenSLESAudioInput(); + + void start(QIODevice *device); + QIODevice *start(); + void stop(); + void reset(); + void suspend(); + void resume(); + int bytesReady() const; + int periodSize() const; + void setBufferSize(int value); + int bufferSize() const; + void setNotifyInterval(int milliSeconds); + int notifyInterval() const; + qint64 processedUSecs() const; + qint64 elapsedUSecs() const; + QAudio::Error error() const; + QAudio::State state() const; + void setFormat(const QAudioFormat &format); + QAudioFormat format() const; + + void setVolume(qreal volume); + qreal volume() const; + +public Q_SLOTS: + void processBuffer(); + +private: + bool startRecording(); + void stopRecording(); + void writeDataToDevice(const char *data, int size); + void flushBuffers(); + + QByteArray m_device; + QOpenSLESEngine *m_engine; + SLObjectItf m_recorderObject; + SLRecordItf m_recorder; +#ifdef ANDROID + SLuint32 m_recorderPreset; + SLAndroidSimpleBufferQueueItf m_bufferQueue; +#else + SLBufferQueueItf m_bufferQueue; +#endif + + bool m_pullMode; + qint64 m_processedBytes; + QIODevice *m_audioSource; + QBuffer *m_bufferIODevice; + QByteArray m_pushBuffer; + QAudioFormat m_format; + QAudio::Error m_errorState; + QAudio::State m_deviceState; + QTime m_clockStamp; + qint64 m_lastNotifyTime; + qreal m_volume; + int m_bufferSize; + int m_periodSize; + int m_intervalTime; + QByteArray *m_buffers; + int m_currentBuffer; +}; + +QT_END_NAMESPACE + +#endif // QOPENSLESAUDIOINPUT_H diff --git a/src/plugins/opensles/qopenslesaudiooutput.cpp b/src/plugins/opensles/qopenslesaudiooutput.cpp new file mode 100644 index 000000000..908e299c1 --- /dev/null +++ b/src/plugins/opensles/qopenslesaudiooutput.cpp @@ -0,0 +1,628 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** 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 "qopenslesaudiooutput.h" +#include "qopenslesengine.h" +#include +#include + +#ifdef ANDROID +#include +#include +#endif // ANDROID + +#define BUFFER_COUNT 2 +#define DEFAULT_PERIOD_TIME_MS 50 +#define MINIMUM_PERIOD_TIME_MS 5 + +QT_BEGIN_NAMESPACE + +QMap QOpenSLESAudioOutput::m_categories; + +QOpenSLESAudioOutput::QOpenSLESAudioOutput(const QByteArray &device) + : m_deviceName(device), + m_state(QAudio::StoppedState), + m_error(QAudio::NoError), + m_outputMixObject(Q_NULLPTR), + m_playerObject(Q_NULLPTR), + m_playItf(Q_NULLPTR), + m_volumeItf(Q_NULLPTR), + m_bufferQueueItf(Q_NULLPTR), + m_audioSource(Q_NULLPTR), + m_buffers(Q_NULLPTR), + m_volume(1.0), + m_pullMode(false), + m_nextBuffer(0), + m_bufferSize(0), + m_notifyInterval(1000), + m_periodSize(0), + m_elapsedTime(0), + m_processedBytes(0), + m_availableBuffers(BUFFER_COUNT) +{ +#ifndef ANDROID + m_streamType = -1; +#else + m_streamType = SL_ANDROID_STREAM_MEDIA; + m_category = QLatin1String("media"); +#endif // ANDROID +} + +QOpenSLESAudioOutput::~QOpenSLESAudioOutput() +{ + destroyPlayer(); +} + +QAudio::Error QOpenSLESAudioOutput::error() const +{ + return m_error; +} + +QAudio::State QOpenSLESAudioOutput::state() const +{ + return m_state; +} + +void QOpenSLESAudioOutput::start(QIODevice *device) +{ + Q_ASSERT(device); + destroyPlayer(); + + m_pullMode = true; + + if (!preparePlayer()) + return; + + m_audioSource = device; + setState(QAudio::ActiveState); + setError(QAudio::NoError); + + // Attempt to fill buffers first. + for (int i = 0; i != BUFFER_COUNT; ++i) { + const int index = i * m_bufferSize; + const qint64 readSize = m_audioSource->read(m_buffers + index, m_bufferSize); + if (readSize && SL_RESULT_SUCCESS != (*m_bufferQueueItf)->Enqueue(m_bufferQueueItf, + m_buffers + index, + readSize)) { + setError(QAudio::FatalError); + destroyPlayer(); + return; + } + m_processedBytes += readSize; + } + + // Change to state to playing. + // We need to do this after filling the buffers or processedBytes might get corrupted. + if (SL_RESULT_SUCCESS != (*m_playItf)->SetPlayState(m_playItf, SL_PLAYSTATE_PLAYING)) { + setError(QAudio::FatalError); + destroyPlayer(); + } +} + +QIODevice *QOpenSLESAudioOutput::start() +{ + destroyPlayer(); + + m_pullMode = false; + + if (!preparePlayer()) + return Q_NULLPTR; + + m_audioSource = new SLIODevicePrivate(this); + m_audioSource->open(QIODevice::WriteOnly | QIODevice::Unbuffered); + + // Change to state to playing + if (SL_RESULT_SUCCESS != (*m_playItf)->SetPlayState(m_playItf, SL_PLAYSTATE_PLAYING)) { + setError(QAudio::FatalError); + destroyPlayer(); + } + + setState(QAudio::IdleState); + return m_audioSource; +} + +void QOpenSLESAudioOutput::stop() +{ + if (m_state == QAudio::StoppedState) + return; + + destroyPlayer(); + setError(QAudio::NoError); +} + +int QOpenSLESAudioOutput::bytesFree() const +{ + if (m_state != QAudio::ActiveState && m_state != QAudio::IdleState) + return 0; + + return m_availableBuffers.load() ? m_bufferSize : 0; +} + +int QOpenSLESAudioOutput::periodSize() const +{ + return m_periodSize; +} + +void QOpenSLESAudioOutput::setBufferSize(int value) +{ + if (m_state != QAudio::StoppedState) + return; + + m_bufferSize = value; +} + +int QOpenSLESAudioOutput::bufferSize() const +{ + return m_bufferSize; +} + +void QOpenSLESAudioOutput::setNotifyInterval(int ms) +{ + m_notifyInterval = ms > 0 ? ms : 0; +} + +int QOpenSLESAudioOutput::notifyInterval() const +{ + return m_notifyInterval; +} + +qint64 QOpenSLESAudioOutput::processedUSecs() const +{ + if (m_state == QAudio::IdleState || m_state == QAudio::SuspendedState) + return m_format.durationForBytes(m_processedBytes); + + SLmillisecond processMSec = 0; + if (m_playItf) + (*m_playItf)->GetPosition(m_playItf, &processMSec); + + return processMSec * 1000; +} + +void QOpenSLESAudioOutput::resume() +{ + if (m_state != QAudio::SuspendedState) + return; + + if (SL_RESULT_SUCCESS != (*m_playItf)->SetPlayState(m_playItf, SL_PLAYSTATE_PLAYING)) { + setError(QAudio::FatalError); + destroyPlayer(); + return; + } + + setState(QAudio::ActiveState); + setError(QAudio::NoError); +} + +void QOpenSLESAudioOutput::setFormat(const QAudioFormat &format) +{ + m_format = format; +} + +QAudioFormat QOpenSLESAudioOutput::format() const +{ + return m_format; +} + +void QOpenSLESAudioOutput::suspend() +{ + if (m_state != QAudio::ActiveState && m_state != QAudio::IdleState) + return; + + if (SL_RESULT_SUCCESS != (*m_playItf)->SetPlayState(m_playItf, SL_PLAYSTATE_PAUSED)) { + setError(QAudio::FatalError); + destroyPlayer(); + return; + } + + setState(QAudio::SuspendedState); + setError(QAudio::NoError); +} + +qint64 QOpenSLESAudioOutput::elapsedUSecs() const +{ + if (m_state == QAudio::StoppedState) + return 0; + + return m_clockStamp.elapsed() * 1000; +} + +void QOpenSLESAudioOutput::reset() +{ + destroyPlayer(); +} + +void QOpenSLESAudioOutput::setVolume(qreal vol) +{ + m_volume = qBound(qreal(0.0), vol, qreal(1.0)); + const SLmillibel newVolume = adjustVolume(m_volume); + if (m_volumeItf && SL_RESULT_SUCCESS != (*m_volumeItf)->SetVolumeLevel(m_volumeItf, newVolume)) + qWarning() << "Unable to change volume"; +} + +qreal QOpenSLESAudioOutput::volume() const +{ + return m_volume; +} + +void QOpenSLESAudioOutput::setCategory(const QString &category) +{ +#ifndef ANDROID + Q_UNUSED(category); +#else + if (m_categories.isEmpty()) { + m_categories.insert(QLatin1String("voice"), SL_ANDROID_STREAM_VOICE); + m_categories.insert(QLatin1String("system"), SL_ANDROID_STREAM_SYSTEM); + m_categories.insert(QLatin1String("ring"), SL_ANDROID_STREAM_RING); + m_categories.insert(QLatin1String("media"), SL_ANDROID_STREAM_MEDIA); + m_categories.insert(QLatin1String("alarm"), SL_ANDROID_STREAM_ALARM); + m_categories.insert(QLatin1String("notification"), SL_ANDROID_STREAM_NOTIFICATION); + } + + const SLint32 streamType = m_categories.value(category, -1); + if (streamType == -1) { + qWarning() << "Unknown category" << category + << ", available categories are:" << m_categories.keys() + << ". Defaulting to category \"media\""; + return; + } + + m_streamType = streamType; + m_category = category; +#endif // ANDROID +} + +QString QOpenSLESAudioOutput::category() const +{ + return m_category; +} + +void QOpenSLESAudioOutput::onEOSEvent() +{ + if (m_state != QAudio::ActiveState) + return; + + SLBufferQueueState state; + if (SL_RESULT_SUCCESS != (*m_bufferQueueItf)->GetState(m_bufferQueueItf, &state)) + return; + + if (state.count > 0) + return; + + setState(QAudio::IdleState); + setError(QAudio::UnderrunError); +} + +void QOpenSLESAudioOutput::bufferAvailable(quint32 count, quint32 playIndex) +{ + Q_UNUSED(count); + Q_UNUSED(playIndex); + + if (m_state == QAudio::StoppedState) + return; + + if (!m_pullMode) { + m_availableBuffers.fetchAndAddRelaxed(1); + return; + } + + const int index = m_nextBuffer * m_bufferSize; + const qint64 readSize = m_audioSource->read(m_buffers + index, m_bufferSize); + + if (1 > readSize) + return; + + if (SL_RESULT_SUCCESS != (*m_bufferQueueItf)->Enqueue(m_bufferQueueItf, + m_buffers + index, + readSize)) { + setError(QAudio::FatalError); + destroyPlayer(); + return; + } + + m_processedBytes += readSize; + m_nextBuffer = (m_nextBuffer + 1) % BUFFER_COUNT; +} + +void QOpenSLESAudioOutput::playCallback(SLPlayItf player, void *ctx, SLuint32 event) +{ + Q_UNUSED(player); + QOpenSLESAudioOutput *audioOutput = reinterpret_cast(ctx); + if (event & SL_PLAYEVENT_HEADATEND) + QMetaObject::invokeMethod(audioOutput, "onEOSEvent", Qt::QueuedConnection); + if (event & SL_PLAYEVENT_HEADATNEWPOS) + Q_EMIT audioOutput->notify(); + +} + +void QOpenSLESAudioOutput::bufferQueueCallback(SLBufferQueueItf bufferQueue, void *ctx) +{ + SLBufferQueueState state; + (*bufferQueue)->GetState(bufferQueue, &state); + QOpenSLESAudioOutput *audioOutput = reinterpret_cast(ctx); + audioOutput->bufferAvailable(state.count, state.playIndex); +} + +bool QOpenSLESAudioOutput::preparePlayer() +{ + SLEngineItf engine = QOpenSLESEngine::instance()->slEngine(); + if (!engine) { + qWarning() << "No engine"; + setError(QAudio::FatalError); + return false; + } + + SLDataLocator_BufferQueue bufferQueueLocator = { SL_DATALOCATOR_BUFFERQUEUE, BUFFER_COUNT }; + SLDataFormat_PCM pcmFormat = QOpenSLESEngine::audioFormatToSLFormatPCM(m_format); + + SLDataSource audioSrc = { &bufferQueueLocator, &pcmFormat }; + + // OutputMix + if (SL_RESULT_SUCCESS != (*engine)->CreateOutputMix(engine, + &m_outputMixObject, + 0, + Q_NULLPTR, + Q_NULLPTR)) { + qWarning() << "Unable to create output mix"; + setError(QAudio::FatalError); + return false; + } + + if (SL_RESULT_SUCCESS != (*m_outputMixObject)->Realize(m_outputMixObject, SL_BOOLEAN_FALSE)) { + qWarning() << "Unable to initialize output mix"; + setError(QAudio::FatalError); + return false; + } + + SLDataLocator_OutputMix outputMixLocator = { SL_DATALOCATOR_OUTPUTMIX, m_outputMixObject }; + SLDataSink audioSink = { &outputMixLocator, Q_NULLPTR }; + +#ifndef ANDROID + const int iids = 2; + const SLInterfaceID ids[iids] = { SL_IID_BUFFERQUEUE, SL_IID_VOLUME }; + const SLboolean req[iids] = { SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE }; +#else + const int iids = 3; + const SLInterfaceID ids[iids] = { SL_IID_BUFFERQUEUE, + SL_IID_VOLUME, + SL_IID_ANDROIDCONFIGURATION }; + const SLboolean req[iids] = { SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE }; +#endif // ANDROID + + // AudioPlayer + if (SL_RESULT_SUCCESS != (*engine)->CreateAudioPlayer(engine, + &m_playerObject, + &audioSrc, + &audioSink, + iids, + ids, + req)) { + qWarning() << "Unable to create AudioPlayer"; + setError(QAudio::OpenError); + return false; + } + +#ifdef ANDROID + // Set profile/category + SLAndroidConfigurationItf playerConfig; + if (SL_RESULT_SUCCESS == (*m_playerObject)->GetInterface(m_playerObject, + SL_IID_ANDROIDCONFIGURATION, + &playerConfig)) { + (*playerConfig)->SetConfiguration(playerConfig, + SL_ANDROID_KEY_STREAM_TYPE, + &m_streamType, + sizeof(SLint32)); + } +#endif // ANDROID + + if (SL_RESULT_SUCCESS != (*m_playerObject)->Realize(m_playerObject, SL_BOOLEAN_FALSE)) { + qWarning() << "Unable to initialize AudioPlayer"; + setError(QAudio::OpenError); + return false; + } + + // Buffer interface + if (SL_RESULT_SUCCESS != (*m_playerObject)->GetInterface(m_playerObject, + SL_IID_BUFFERQUEUE, + &m_bufferQueueItf)) { + setError(QAudio::FatalError); + return false; + } + + if (SL_RESULT_SUCCESS != (*m_bufferQueueItf)->RegisterCallback(m_bufferQueueItf, + bufferQueueCallback, + this)) { + setError(QAudio::FatalError); + return false; + } + + // Play interface + if (SL_RESULT_SUCCESS != (*m_playerObject)->GetInterface(m_playerObject, + SL_IID_PLAY, + &m_playItf)) { + setError(QAudio::FatalError); + return false; + } + + if (SL_RESULT_SUCCESS != (*m_playItf)->RegisterCallback(m_playItf, playCallback, this)) { + setError(QAudio::FatalError); + return false; + } + + SLuint32 mask = SL_PLAYEVENT_HEADATEND; + if (m_notifyInterval && SL_RESULT_SUCCESS == (*m_playItf)->SetPositionUpdatePeriod(m_playItf, + m_notifyInterval)) { + mask |= SL_PLAYEVENT_HEADATNEWPOS; + } + + if (SL_RESULT_SUCCESS != (*m_playItf)->SetCallbackEventsMask(m_playItf, mask)) { + setError(QAudio::FatalError); + return false; + } + + // Volume interface + if (SL_RESULT_SUCCESS != (*m_playerObject)->GetInterface(m_playerObject, + SL_IID_VOLUME, + &m_volumeItf)) { + setError(QAudio::FatalError); + return false; + } + + setVolume(m_volume); + + // Buffer size + if (m_bufferSize <= 0) { + m_bufferSize = m_format.bytesForDuration(DEFAULT_PERIOD_TIME_MS * 1000); + } else { + const int minimumBufSize = m_format.bytesForDuration(MINIMUM_PERIOD_TIME_MS * 1000); + if (m_bufferSize < minimumBufSize) + m_bufferSize = minimumBufSize; + } + + m_periodSize = m_bufferSize; + + if (!m_buffers) + m_buffers = new char[BUFFER_COUNT * m_bufferSize]; + + m_clockStamp.restart(); + setError(QAudio::NoError); + + return true; +} + +void QOpenSLESAudioOutput::destroyPlayer() +{ + setState(QAudio::StoppedState); + + // We need to change the state manually... + if (m_playItf) + (*m_playItf)->SetPlayState(m_playItf, SL_PLAYSTATE_STOPPED); + + if (m_bufferQueueItf && SL_RESULT_SUCCESS != (*m_bufferQueueItf)->Clear(m_bufferQueueItf)) + qWarning() << "Unable to clear buffer"; + + if (m_playerObject) { + (*m_playerObject)->Destroy(m_playerObject); + m_playerObject = Q_NULLPTR; + } + + if (m_outputMixObject) { + (*m_outputMixObject)->Destroy(m_outputMixObject); + m_outputMixObject = Q_NULLPTR; + } + + if (!m_pullMode && m_audioSource) { + m_audioSource->close(); + delete m_audioSource; + m_audioSource = Q_NULLPTR; + } + + delete [] m_buffers; + m_buffers = Q_NULLPTR; + m_processedBytes = 0; + m_nextBuffer = 0; + m_availableBuffers = BUFFER_COUNT; + m_playItf = Q_NULLPTR; + m_volumeItf = Q_NULLPTR; + m_bufferQueueItf = Q_NULLPTR; +} + +qint64 QOpenSLESAudioOutput::writeData(const char *data, qint64 len) +{ + if (!len) + return 0; + + if (len > m_bufferSize) + len = m_bufferSize; + + const int index = m_nextBuffer * m_bufferSize; + ::memcpy(m_buffers + index, data, len); + const SLuint32 res = (*m_bufferQueueItf)->Enqueue(m_bufferQueueItf, + m_buffers + index, + len); + + if (res == SL_RESULT_BUFFER_INSUFFICIENT) + return 0; + + if (res != SL_RESULT_SUCCESS) { + setError(QAudio::FatalError); + destroyPlayer(); + return -1; + } + + m_processedBytes += len; + m_availableBuffers.fetchAndAddRelaxed(-1); + setState(QAudio::ActiveState); + setError(QAudio::NoError); + m_nextBuffer = (m_nextBuffer + 1) % BUFFER_COUNT; + + return len; +} + +inline void QOpenSLESAudioOutput::setState(QAudio::State state) +{ + if (m_state == state) + return; + + m_state = state; + Q_EMIT stateChanged(m_state); +} + +inline void QOpenSLESAudioOutput::setError(QAudio::Error error) +{ + if (m_error == error) + return; + + m_error = error; + Q_EMIT errorChanged(m_error); +} + +inline SLmillibel QOpenSLESAudioOutput::adjustVolume(qreal vol) +{ + if (qFuzzyIsNull(vol)) + return SL_MILLIBEL_MIN; + + if (qFuzzyCompare(vol, qreal(1.0))) + return 0; + + return SL_MILLIBEL_MIN + ((1 - (qLn(10 - (vol * 10)) / qLn(10))) * SL_MILLIBEL_MAX); +} + +QT_END_NAMESPACE diff --git a/src/plugins/opensles/qopenslesaudiooutput.h b/src/plugins/opensles/qopenslesaudiooutput.h new file mode 100644 index 000000000..b0f01fa22 --- /dev/null +++ b/src/plugins/opensles/qopenslesaudiooutput.h @@ -0,0 +1,155 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** 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 QOPENSLESAUDIOOUTPUT_H +#define QOPENSLESAUDIOOUTPUT_H + +#include +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +class QOpenSLESAudioOutput : public QAbstractAudioOutput +{ + Q_OBJECT + +public: + QOpenSLESAudioOutput(const QByteArray &device); + ~QOpenSLESAudioOutput(); + + void start(QIODevice *device) 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 value) Q_DECL_OVERRIDE; + int bufferSize() const Q_DECL_OVERRIDE; + void setNotifyInterval(int milliSeconds) 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; + + void setCategory(const QString &category) Q_DECL_OVERRIDE; + QString category() const Q_DECL_OVERRIDE; + +private: + friend class SLIODevicePrivate; + + Q_INVOKABLE void onEOSEvent(); + void bufferAvailable(quint32 count, quint32 playIndex); + + static void playCallback(SLPlayItf playItf, void *ctx, SLuint32 event); + static void bufferQueueCallback(SLBufferQueueItf bufferQueue, void *ctx); + + bool preparePlayer(); + void destroyPlayer(); + qint64 writeData(const char *data, qint64 len); + + void setState(QAudio::State state); + void setError(QAudio::Error error); + + SLmillibel adjustVolume(qreal vol); + + QByteArray m_deviceName; + QAudio::State m_state; + QAudio::Error m_error; + SLObjectItf m_outputMixObject; + SLObjectItf m_playerObject; + SLPlayItf m_playItf; + SLVolumeItf m_volumeItf; + SLBufferQueueItf m_bufferQueueItf; + QIODevice *m_audioSource; + char *m_buffers; + qreal m_volume; + bool m_pullMode; + int m_nextBuffer; + int m_bufferSize; + int m_notifyInterval; + int m_periodSize; + qint64 m_elapsedTime; + qint64 m_processedBytes; + QAtomicInt m_availableBuffers; + + qint32 m_streamType; + QTime m_clockStamp; + QAudioFormat m_format; + QString m_category; + static QMap m_categories; +}; + +class SLIODevicePrivate : public QIODevice +{ + Q_OBJECT + +public: + inline SLIODevicePrivate(QOpenSLESAudioOutput *audio) : m_audioDevice(audio) {} + inline ~SLIODevicePrivate() Q_DECL_OVERRIDE {} + +protected: + inline qint64 readData(char *, qint64) Q_DECL_OVERRIDE { return 0; } + inline qint64 writeData(const char *data, qint64 len) Q_DECL_OVERRIDE; + +private: + QOpenSLESAudioOutput *m_audioDevice; +}; + +qint64 SLIODevicePrivate::writeData(const char *data, qint64 len) +{ + Q_ASSERT(m_audioDevice); + return m_audioDevice->writeData(data, len); +} + +QT_END_NAMESPACE + +#endif // QOPENSLESAUDIOOUTPUT_H diff --git a/src/plugins/opensles/qopenslesdeviceinfo.cpp b/src/plugins/opensles/qopenslesdeviceinfo.cpp new file mode 100644 index 000000000..8301beaa5 --- /dev/null +++ b/src/plugins/opensles/qopenslesdeviceinfo.cpp @@ -0,0 +1,115 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** 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 "qopenslesdeviceinfo.h" + +#include "qopenslesengine.h" + +QT_BEGIN_NAMESPACE + +QOpenSLESDeviceInfo::QOpenSLESDeviceInfo(const QByteArray &device, QAudio::Mode mode) + : m_engine(QOpenSLESEngine::instance()) + , m_device(device) + , m_mode(mode) +{ +} + +bool QOpenSLESDeviceInfo::isFormatSupported(const QAudioFormat &format) const +{ + QOpenSLESDeviceInfo *that = const_cast(this); + return that->supportedCodecs().contains(format.codec()) + && that->supportedSampleRates().contains(format.sampleRate()) + && that->supportedChannelCounts().contains(format.channelCount()) + && that->supportedSampleSizes().contains(format.sampleSize()) + && that->supportedByteOrders().contains(format.byteOrder()) + && that->supportedSampleTypes().contains(format.sampleType()); +} + +QAudioFormat QOpenSLESDeviceInfo::preferredFormat() const +{ + QAudioFormat format; + format.setCodec(QStringLiteral("audio/pcm")); + format.setSampleSize(16); + format.setSampleType(QAudioFormat::SignedInt); + format.setSampleRate(44100); + format.setChannelCount(m_mode == QAudio::AudioInput ? 1 : 2); + return format; +} + +QString QOpenSLESDeviceInfo::deviceName() const +{ + return m_device; +} + +QStringList QOpenSLESDeviceInfo::supportedCodecs() +{ + return QStringList() << QStringLiteral("audio/pcm"); +} + +QList QOpenSLESDeviceInfo::supportedSampleRates() +{ + return m_engine->supportedSampleRates(m_mode); +} + +QList QOpenSLESDeviceInfo::supportedChannelCounts() +{ + return m_engine->supportedChannelCounts(m_mode); +} + +QList QOpenSLESDeviceInfo::supportedSampleSizes() +{ + if (m_mode == QAudio::AudioInput) + return QList() << 16; + else + return QList() << 8 << 16; +} + +QList QOpenSLESDeviceInfo::supportedByteOrders() +{ + return QList() << QAudioFormat::LittleEndian; +} + +QList QOpenSLESDeviceInfo::supportedSampleTypes() +{ + return QList() << QAudioFormat::SignedInt; +} + +QT_END_NAMESPACE diff --git a/src/plugins/opensles/qopenslesdeviceinfo.h b/src/plugins/opensles/qopenslesdeviceinfo.h new file mode 100644 index 000000000..15a5c52f3 --- /dev/null +++ b/src/plugins/opensles/qopenslesdeviceinfo.h @@ -0,0 +1,77 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** 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 QOPENSLESDEVICEINFO_H +#define QOPENSLESDEVICEINFO_H + +#include + +QT_BEGIN_NAMESPACE + +class QOpenSLESEngine; + +class QOpenSLESDeviceInfo : public QAbstractAudioDeviceInfo +{ + Q_OBJECT + +public: + QOpenSLESDeviceInfo(const QByteArray &device, QAudio::Mode mode); + ~QOpenSLESDeviceInfo() {} + + QAudioFormat preferredFormat() const; + bool isFormatSupported(const QAudioFormat &format) const; + QString deviceName() const; + QStringList supportedCodecs(); + QList supportedSampleRates(); + QList supportedChannelCounts(); + QList supportedSampleSizes(); + QList supportedByteOrders(); + QList supportedSampleTypes(); + +private: + QOpenSLESEngine *m_engine; + QByteArray m_device; + QAudio::Mode m_mode; +}; + +QT_END_NAMESPACE + +#endif // QOPENSLESDEVICEINFO_H diff --git a/src/plugins/opensles/qopenslesengine.cpp b/src/plugins/opensles/qopenslesengine.cpp new file mode 100644 index 000000000..056b51e26 --- /dev/null +++ b/src/plugins/opensles/qopenslesengine.cpp @@ -0,0 +1,207 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** 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 "qopenslesengine.h" + +#include "qopenslesaudioinput.h" +#include + +#ifdef ANDROID +#include +#endif + +#define CheckError(message) if (result != SL_RESULT_SUCCESS) { qWarning(message); return; } + +Q_GLOBAL_STATIC(QOpenSLESEngine, openslesEngine); + +QOpenSLESEngine::QOpenSLESEngine() + : m_engineObject(0) + , m_engine(0) +{ + SLresult result; + + result = slCreateEngine(&m_engineObject, 0, 0, 0, 0, 0); + CheckError("Failed to create engine"); + + result = (*m_engineObject)->Realize(m_engineObject, SL_BOOLEAN_FALSE); + CheckError("Failed to realize engine"); + + result = (*m_engineObject)->GetInterface(m_engineObject, SL_IID_ENGINE, &m_engine); + CheckError("Failed to get engine interface"); + + checkSupportedInputFormats(); +} + +QOpenSLESEngine::~QOpenSLESEngine() +{ + if (m_engineObject) + (*m_engineObject)->Destroy(m_engineObject); +} + +QOpenSLESEngine *QOpenSLESEngine::instance() +{ + return openslesEngine(); +} + +SLDataFormat_PCM QOpenSLESEngine::audioFormatToSLFormatPCM(const QAudioFormat &format) +{ + SLDataFormat_PCM format_pcm; + format_pcm.formatType = SL_DATAFORMAT_PCM; + format_pcm.numChannels = format.channelCount(); + format_pcm.samplesPerSec = format.sampleRate() * 1000; + format_pcm.bitsPerSample = format.sampleSize(); + format_pcm.containerSize = format.sampleSize(); + format_pcm.channelMask = (format.channelCount() == 1 ? + SL_SPEAKER_FRONT_CENTER : + SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT); + format_pcm.endianness = (format.byteOrder() == QAudioFormat::LittleEndian ? + SL_BYTEORDER_LITTLEENDIAN : + SL_BYTEORDER_BIGENDIAN); + return format_pcm; + +} + +QList QOpenSLESEngine::availableDevices(QAudio::Mode mode) const +{ + QList devices; + if (mode == QAudio::AudioInput) { +#ifdef ANDROID + devices << QT_ANDROID_PRESET_MIC + << QT_ANDROID_PRESET_CAMCORDER + << QT_ANDROID_PRESET_VOICE_RECOGNITION; +#else + devices << "default"; +#endif + } else { + devices << "default"; + } + return devices; +} + +QList QOpenSLESEngine::supportedChannelCounts(QAudio::Mode mode) const +{ + if (mode == QAudio::AudioInput) + return m_supportedInputChannelCounts; + else + return QList() << 1 << 2; +} + +QList QOpenSLESEngine::supportedSampleRates(QAudio::Mode mode) const +{ + if (mode == QAudio::AudioInput) { + return m_supportedInputSampleRates; + } else { + return QList() << 8000 << 11025 << 12000 << 16000 << 22050 + << 24000 << 32000 << 44100 << 48000; + } +} + +void QOpenSLESEngine::checkSupportedInputFormats() +{ + m_supportedInputChannelCounts = QList() << 1; + m_supportedInputSampleRates.clear(); + + SLDataFormat_PCM defaultFormat; + defaultFormat.formatType = SL_DATAFORMAT_PCM; + defaultFormat.numChannels = 1; + defaultFormat.samplesPerSec = SL_SAMPLINGRATE_44_1; + defaultFormat.bitsPerSample = SL_PCMSAMPLEFORMAT_FIXED_16; + defaultFormat.containerSize = SL_PCMSAMPLEFORMAT_FIXED_16; + defaultFormat.channelMask = SL_SPEAKER_FRONT_CENTER; + defaultFormat.endianness = SL_BYTEORDER_LITTLEENDIAN; + + const SLuint32 rates[9] = { SL_SAMPLINGRATE_8, + SL_SAMPLINGRATE_11_025, + SL_SAMPLINGRATE_12, + SL_SAMPLINGRATE_16, + SL_SAMPLINGRATE_22_05, + SL_SAMPLINGRATE_24, + SL_SAMPLINGRATE_32, + SL_SAMPLINGRATE_44_1, + SL_SAMPLINGRATE_48 }; + + + // Test sampling rates + for (int i = 0 ; i < 9; ++i) { + SLDataFormat_PCM format = defaultFormat; + format.samplesPerSec = rates[i]; + + if (inputFormatIsSupported(format)) + m_supportedInputSampleRates.append(rates[i] / 1000); + + } + + // Test if stereo is supported + { + SLDataFormat_PCM format = defaultFormat; + format.numChannels = 2; + format.channelMask = 0; + if (inputFormatIsSupported(format)) + m_supportedInputChannelCounts.append(2); + } +} + +bool QOpenSLESEngine::inputFormatIsSupported(SLDataFormat_PCM format) +{ + SLresult result; + SLObjectItf recorder = 0; + SLDataLocator_IODevice loc_dev = { SL_DATALOCATOR_IODEVICE, SL_IODEVICE_AUDIOINPUT, + SL_DEFAULTDEVICEID_AUDIOINPUT, NULL }; + SLDataSource audioSrc = { &loc_dev, NULL }; + +#ifdef ANDROID + SLDataLocator_AndroidSimpleBufferQueue loc_bq = { SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE, 1 }; +#else + SLDataLocator_BufferQueue loc_bq = { SL_DATALOCATOR_BUFFERQUEUE, 1 }; +#endif + SLDataSink audioSnk = { &loc_bq, &format }; + + result = (*m_engine)->CreateAudioRecorder(m_engine, &recorder, &audioSrc, &audioSnk, 0, 0, 0); + if (result == SL_RESULT_SUCCESS) + result = (*recorder)->Realize(recorder, false); + + if (result == SL_RESULT_SUCCESS) { + (*recorder)->Destroy(recorder); + return true; + } + + return false; +} diff --git a/src/plugins/opensles/qopenslesengine.h b/src/plugins/opensles/qopenslesengine.h new file mode 100644 index 000000000..9f12ac65d --- /dev/null +++ b/src/plugins/opensles/qopenslesengine.h @@ -0,0 +1,82 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** 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 QOPENSLESENGINE_H +#define QOPENSLESENGINE_H + +#include +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE + +class QOpenSLESEngine +{ +public: + QOpenSLESEngine(); + ~QOpenSLESEngine(); + + static QOpenSLESEngine *instance(); + + SLEngineItf slEngine() const { return m_engine; } + + static SLDataFormat_PCM audioFormatToSLFormatPCM(const QAudioFormat &format); + + QList availableDevices(QAudio::Mode mode) const; + QList supportedChannelCounts(QAudio::Mode mode) const; + QList supportedSampleRates(QAudio::Mode mode) const; + +private: + void checkSupportedInputFormats(); + bool inputFormatIsSupported(SLDataFormat_PCM format); + + SLObjectItf m_engineObject; + SLEngineItf m_engine; + + QList m_supportedInputChannelCounts; + QList m_supportedInputSampleRates; +}; + +QT_END_NAMESPACE + +#endif // QOPENSLESENGINE_H diff --git a/src/plugins/opensles/qopenslesplugin.cpp b/src/plugins/opensles/qopenslesplugin.cpp new file mode 100644 index 000000000..a7fdb9bd4 --- /dev/null +++ b/src/plugins/opensles/qopenslesplugin.cpp @@ -0,0 +1,78 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** 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 "qopenslesplugin.h" + +#include "qopenslesengine.h" +#include "qopenslesdeviceinfo.h" +#include "qopenslesaudioinput.h" +#include "qopenslesaudiooutput.h" + +QT_BEGIN_NAMESPACE + +QOpenSLESPlugin::QOpenSLESPlugin(QObject *parent) + : QAudioSystemPlugin(parent) + , m_engine(QOpenSLESEngine::instance()) +{ +} + +QList QOpenSLESPlugin::availableDevices(QAudio::Mode mode) const +{ + return m_engine->availableDevices(mode); +} + +QAbstractAudioInput *QOpenSLESPlugin::createInput(const QByteArray &device) +{ + return new QOpenSLESAudioInput(device); +} + +QAbstractAudioOutput *QOpenSLESPlugin::createOutput(const QByteArray &device) +{ + return new QOpenSLESAudioOutput(device); +} + +QAbstractAudioDeviceInfo *QOpenSLESPlugin::createDeviceInfo(const QByteArray &device, QAudio::Mode mode) +{ + return new QOpenSLESDeviceInfo(device, mode); +} + +QT_END_NAMESPACE + diff --git a/src/plugins/opensles/qopenslesplugin.h b/src/plugins/opensles/qopenslesplugin.h new file mode 100644 index 000000000..46144abe2 --- /dev/null +++ b/src/plugins/opensles/qopenslesplugin.h @@ -0,0 +1,72 @@ +/**************************************************************************** +** +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** 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 QOPENSLESPLUGIN_H +#define QOPENSLESPLUGIN_H + +#include + +QT_BEGIN_NAMESPACE + +class QOpenSLESEngine; + +class QOpenSLESPlugin : public QAudioSystemPlugin +{ + Q_OBJECT + + Q_PLUGIN_METADATA(IID "org.qt-project.qt.audiosystemfactory/5.0" FILE "opensles.json") + +public: + QOpenSLESPlugin(QObject *parent = 0); + ~QOpenSLESPlugin() {} + + QList availableDevices(QAudio::Mode mode) const; + QAbstractAudioInput *createInput(const QByteArray &device); + QAbstractAudioOutput *createOutput(const QByteArray &device); + QAbstractAudioDeviceInfo *createDeviceInfo(const QByteArray &device, QAudio::Mode mode); + +private: + QOpenSLESEngine *m_engine; +}; + +QT_END_NAMESPACE + +#endif // QOPENSLESPLUGIN_H diff --git a/src/plugins/plugins.pro b/src/plugins/plugins.pro index 8f751ae3b..4ef472dcd 100644 --- a/src/plugins/plugins.pro +++ b/src/plugins/plugins.pro @@ -53,3 +53,6 @@ mac:!simulator { } } +config_opensles { + SUBDIRS += opensles +} -- cgit v1.2.3