7#include <private/qaudiohelpers_p.h>
13#include <SLES/OpenSLES_AndroidConfiguration.h>
14#include <QtCore/qcoreapplication.h>
15#include <QtCore/qpermissions.h>
23#define DEFAULT_PERIOD_TIME_MS 50
24#define MINIMUM_PERIOD_TIME_MS 5
27static bool hasRecordingPermission()
33 qCWarning(qLcAndroidAudioSource,
"Missing microphone permission!");
38static void bufferQueueCallback(SLAndroidSimpleBufferQueueItf,
void *
context)
40static void bufferQueueCallback(SLBufferQueueItf,
void *
context)
59 , m_deviceState(
QAudio::StoppedState)
68 m_recorderPreset = SL_ANDROID_RECORDING_PRESET_CAMCORDER;
69 else if (
qstrcmp(
device, QT_ANDROID_PRESET_VOICE_RECOGNITION) == 0)
70 m_recorderPreset = SL_ANDROID_RECORDING_PRESET_VOICE_RECOGNITION;
71 else if (
qstrcmp(
device, QT_ANDROID_PRESET_VOICE_COMMUNICATION) == 0)
72 m_recorderPreset = SL_ANDROID_RECORDING_PRESET_VOICE_COMMUNICATION;
74 m_recorderPreset = SL_ANDROID_RECORDING_PRESET_GENERIC;
81 (*m_recorderObject)->Destroy(m_recorderObject);
111 if (!m_pullMode && m_bufferIODevice) {
112 m_bufferIODevice->
close();
113 delete m_bufferIODevice;
114 m_bufferIODevice = 0;
120 if (startRecording()) {
137 if (!m_pullMode && m_bufferIODevice) {
138 m_bufferIODevice->
close();
139 delete m_bufferIODevice;
143 m_pushBuffer.
clear();
144 m_bufferIODevice =
new QBuffer(&m_pushBuffer,
this);
147 if (startRecording()) {
152 m_bufferIODevice->
close();
153 delete m_bufferIODevice;
154 m_bufferIODevice = 0;
158 return m_bufferIODevice;
161bool QAndroidAudioSource::startRecording()
163 if (!hasRecordingPermission())
166 m_processedBytes = 0;
167 m_lastNotifyTime = 0;
172 SLDataLocator_IODevice loc_dev = { SL_DATALOCATOR_IODEVICE, SL_IODEVICE_AUDIOINPUT,
173 SL_DEFAULTDEVICEID_AUDIOINPUT, NULL };
174 SLDataSource audioSrc = { &loc_dev, NULL };
178 SLDataLocator_AndroidSimpleBufferQueue loc_bq = { SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE,
181 SLDataLocator_BufferQueue loc_bq = { SL_DATALOCATOR_BUFFERQUEUE,
NUM_BUFFERS };
185 SLDataSink audioSnk = { &loc_bq, &format_pcm };
190 const SLInterfaceID
id[2] = { SL_IID_ANDROIDSIMPLEBUFFERQUEUE, SL_IID_ANDROIDCONFIGURATION };
191 const SLboolean req[2] = { SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE };
193 const SLInterfaceID
id[1] = { SL_IID_BUFFERQUEUE };
194 const SLboolean req[1] = { SL_BOOLEAN_TRUE };
198 &audioSrc, &audioSnk,
199 sizeof(req) /
sizeof(SLboolean),
id, req);
200 if (
result != SL_RESULT_SUCCESS) {
207 SLAndroidConfigurationItf configItf;
208 result = (*m_recorderObject)->GetInterface(m_recorderObject, SL_IID_ANDROIDCONFIGURATION,
210 if (
result != SL_RESULT_SUCCESS) {
215 result = (*configItf)->SetConfiguration(configItf, SL_ANDROID_KEY_RECORDING_PRESET,
216 &m_recorderPreset,
sizeof(SLuint32));
218 SLuint32 presetValue = SL_ANDROID_RECORDING_PRESET_NONE;
219 SLuint32 presetSize = 2*
sizeof(SLuint32);
220 result = (*configItf)->GetConfiguration(configItf, SL_ANDROID_KEY_RECORDING_PRESET,
221 &presetSize, (
void*)&presetValue);
223 if (
result != SL_RESULT_SUCCESS || presetValue == SL_ANDROID_RECORDING_PRESET_NONE) {
230 result = (*m_recorderObject)->Realize(m_recorderObject, SL_BOOLEAN_FALSE);
231 if (
result != SL_RESULT_SUCCESS) {
237 result = (*m_recorderObject)->GetInterface(m_recorderObject, SL_IID_RECORD, &m_recorder);
238 if (
result != SL_RESULT_SUCCESS) {
245 SLInterfaceID bufferqueueItfID = SL_IID_ANDROIDSIMPLEBUFFERQUEUE;
247 SLInterfaceID bufferqueueItfID = SL_IID_BUFFERQUEUE;
249 result = (*m_recorderObject)->GetInterface(m_recorderObject, bufferqueueItfID, &m_bufferQueue);
250 if (
result != SL_RESULT_SUCCESS) {
256 result = (*m_bufferQueue)->RegisterCallback(m_bufferQueue, bufferQueueCallback,
this);
257 if (
result != SL_RESULT_SUCCESS) {
262 if (m_bufferSize <= 0) {
266 if (m_bufferSize < minimumBufSize)
267 m_bufferSize = minimumBufSize;
272 m_buffers[
i].
resize(m_bufferSize);
274 result = (*m_bufferQueue)->Enqueue(m_bufferQueue, m_buffers[
i].
data(), m_bufferSize);
275 if (
result != SL_RESULT_SUCCESS) {
282 result = (*m_recorder)->SetRecordState(m_recorder, SL_RECORDSTATE_RECORDING);
283 if (
result != SL_RESULT_SUCCESS) {
306void QAndroidAudioSource::stopRecording()
310 (*m_recorder)->SetRecordState(m_recorder, SL_RECORDSTATE_STOPPED);
311 (*m_bufferQueue)->Clear(m_bufferQueue);
313 (*m_recorderObject)->Destroy(m_recorderObject);
314 m_recorderObject = 0;
320 if (!m_pullMode && m_bufferIODevice) {
321 m_bufferIODevice->
close();
322 delete m_bufferIODevice;
323 m_bufferIODevice = 0;
324 m_pushBuffer.
clear();
334 (*m_recorder)->SetRecordState(m_recorder, SL_RECORDSTATE_PAUSED);
341 (*m_recorder)->SetRecordState(m_recorder, SL_RECORDSTATE_RECORDING);
359 QByteArray *processedBuffer = &m_buffers[m_currentBuffer];
360 writeDataToDevice(processedBuffer->
constData(), processedBuffer->
size());
363 SLresult
result = (*m_bufferQueue)->Enqueue(m_bufferQueue,
364 processedBuffer->
data(),
365 processedBuffer->
size());
367 m_currentBuffer = (m_currentBuffer + 1) %
NUM_BUFFERS;
371 SLAndroidSimpleBufferQueueState
state;
373 SLBufferQueueState
state;
375 result = (*m_bufferQueue)->GetState(m_bufferQueue, &
state);
376 if (
result != SL_RESULT_SUCCESS ||
state.count == 0) {
383void QAndroidAudioSource::writeDataToDevice(
const char *
data,
int size)
385 m_processedBytes +=
size;
390 if (m_volume < 1.0f) {
399 if (m_audioSource->
write(outData) < 0) {
406 if (m_bufferIODevice != 0) {
407 m_pushBuffer.
append(outData);
413void QAndroidAudioSource::flushBuffers()
415 SLmillisecond recorderPos;
416 (*m_recorder)->GetPosition(m_recorder, &recorderPos);
419 qint64 delta = recorderPos * 1000 - devicePos;
422 const int writeSize =
qMin(m_buffers[m_currentBuffer].
size(),
424 writeDataToDevice(m_buffers[m_currentBuffer].constData(), writeSize);
431 return m_bufferIODevice ? m_bufferIODevice->
bytesAvailable() : m_bufferSize;
438 m_bufferSize =
value;
IOBluetoothDevice * device
void setBufferSize(qsizetype value)
void setFormat(const QAudioFormat &format)
QAudioFormat format() const
QAudio::Error error() const
qsizetype bufferSize() const
void setVolume(qreal volume)
qsizetype bytesReady() const
qint64 processedUSecs() const
QAndroidAudioSource(const QByteArray &device, QObject *parent)
QAudio::State state() const
\inmodule QtCore \reentrant
bool open(OpenMode openMode) override
\reimp
void close() override
\reimp
char * data()
\macro QT_NO_CAST_FROM_BYTEARRAY
qsizetype size() const noexcept
Returns the number of bytes in this byte array.
const char * constData() const noexcept
Returns a pointer to the const data stored in the byte array.
void clear()
Clears the contents of the byte array and makes it null.
void resize(qsizetype size)
Sets the size of the byte array to size bytes.
QByteArray & append(char c)
This is an overloaded member function, provided for convenience. It differs from the above function o...
\inmodule QtCore \reentrant
void readyRead()
This signal is emitted once every time new data is available for reading from the device's current re...
qint64 write(const char *data, qint64 len)
Writes at most maxSize bytes of data from data to the device.
virtual qint64 bytesAvailable() const
Returns the number of bytes that are available for reading.
Access the microphone for monitoring or recording sound.
SLEngineItf slEngine() const
static SLAndroidDataFormat_PCM_EX audioFormatToSLFormatPCM(const QAudioFormat &format)
void qMultiplySamples(qreal factor, const QAudioFormat &format, const void *src, void *dest, int len)
The QAudio namespace contains enums used by the audio classes.
State
\value ActiveState Audio data is being processed, this state is set after start() is called and while...
Error
\value NoError No errors have occurred \value OpenError An error occurred opening the audio device \v...
Combined button and popup list for selecting options.
#define MINIMUM_PERIOD_TIME_MS
#define DEFAULT_PERIOD_TIME_MS
Q_CORE_EXPORT int qstrcmp(const char *str1, const char *str2)
EGLOutputLayerEXT EGLint EGLAttrib value
[5]
#define Q_LOGGING_CATEGORY(name,...)
#define qCWarning(category,...)
constexpr const T & qMin(const T &a, const T &b)
GLenum GLuint GLintptr GLsizeiptr size
[1]
GLint GLsizei GLsizei GLenum GLenum GLsizei void * data
GLint GLsizei GLsizei GLenum format
IUIAutomationTreeWalker __RPC__deref_out_opt IUIAutomationElement ** parent