9#include <QtCore/qcoreapplication.h>
10#include <QtCore/qjniobject.h>
11#include <QtCore/qpermissions.h>
12#include <QtCore/private/qandroidextras_p.h>
15#define MINIMUM_PERIOD_TIME_MS 5
16#define DEFAULT_PERIOD_TIME_MS 50
18#define CheckError(message) if (result != SL_RESULT_SUCCESS) { qWarning(message); return; }
20#define SL_ANDROID_PCM_REPRESENTATION_INVALID 0
27 , m_checkedInputFormats(
false)
31 result = slCreateEngine(&m_engineObject, 0, 0, 0, 0, 0);
34 result = (*m_engineObject)->Realize(m_engineObject, SL_BOOLEAN_FALSE);
37 result = (*m_engineObject)->GetInterface(m_engineObject, SL_IID_ENGINE, &m_engine);
44 (*m_engineObject)->Destroy(m_engineObject);
49 return openslesEngine();
54 switch (channelCount) {
55 case 1:
return SL_SPEAKER_FRONT_CENTER;
56 case 2:
return SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT;
57 case 3:
return SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT | SL_SPEAKER_FRONT_CENTER;
58 case 4:
return SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT
59 | SL_SPEAKER_BACK_LEFT | SL_SPEAKER_BACK_RIGHT;
60 case 5:
return SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT | SL_SPEAKER_BACK_LEFT
61 | SL_SPEAKER_BACK_RIGHT | SL_SPEAKER_FRONT_CENTER;
62 case 6:
return SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT | SL_SPEAKER_BACK_LEFT
63 | SL_SPEAKER_BACK_RIGHT | SL_SPEAKER_FRONT_CENTER | SL_SPEAKER_LOW_FREQUENCY;
70 SLAndroidDataFormat_PCM_EX format_pcm;
71 format_pcm.formatType = SL_ANDROID_DATAFORMAT_PCM_EX;
76 format_pcm.channelMask = getChannelMask(format_pcm.numChannels);
77 format_pcm.endianness = (QSysInfo::ByteOrder == QSysInfo::LittleEndian ?
78 SL_BYTEORDER_LITTLEENDIAN :
79 SL_BYTEORDER_BIGENDIAN);
82 case QAudioFormat::SampleFormat::UInt8:
83 format_pcm.representation = SL_ANDROID_PCM_REPRESENTATION_UNSIGNED_INT;
85 case QAudioFormat::SampleFormat::Int16:
86 case QAudioFormat::SampleFormat::Int32:
87 format_pcm.representation = SL_ANDROID_PCM_REPRESENTATION_SIGNED_INT;
89 case QAudioFormat::SampleFormat::Float:
90 format_pcm.representation = SL_ANDROID_PCM_REPRESENTATION_FLOAT;
103 QList<QAudioDevice> devices;
105 if (mode == QAudioDevice::Input) {
106 devs = QJniObject::callStaticObjectMethod(
107 "org/qtproject/qt/android/multimedia/QtAudioDeviceManager",
108 "getAudioInputDevices",
109 "()[Ljava/lang/String;");
110 }
else if (mode == QAudioDevice::Output) {
111 devs = QJniObject::callStaticObjectMethod(
112 "org/qtproject/qt/android/multimedia/QtAudioDeviceManager",
113 "getAudioOutputDevices",
114 "()[Ljava/lang/String;");
116 if (devs.isValid()) {
118 jobjectArray devsArray =
static_cast<jobjectArray>(devs.object());
119 const jint size = env->GetArrayLength(devsArray);
120 for (
int i = 0; i < size; ++i) {
121 auto devElement = env->GetObjectArrayElement(devsArray, i);
122 QString val = QJniObject(devElement).toString();
123 env->DeleteLocalRef(devElement);
124 int pos = val.indexOf(QStringLiteral(
":"));
126 val.left(pos).toUtf8(), val.mid(pos+1), mode, i == 0))->create();
134 return QJniObject::callStaticMethod<jboolean>(
135 "org/qtproject/qt/android/multimedia/QtAudioDeviceManager",
142 return qApp->checkPermission(QMicrophonePermission{}) == Qt::PermissionStatus::Granted;
147 if (mode == QAudioDevice::Input) {
148 if (!m_checkedInputFormats)
150 return m_supportedInputChannelCounts;
152 return QList<
int>() << 1 << 2;
158 if (mode == QAudioDevice::Input) {
159 if (!m_checkedInputFormats)
161 return m_supportedInputSampleRates;
163 return QList<
int>() << 8000 << 11025 << 12000 << 16000 << 22050 << 24000
164 << 32000 << 44100 << 48000 << 64000 << 88200 << 96000 << 192000;
170 static int sampleRate = 0;
171 static int framesPerBuffer = 0;
174 return framesPerBuffer;
179 QJniObject ctx(QNativeInterface::QAndroidApplication::context());
184 QJniObject audioServiceString = ctx.getStaticObjectField(
"android/content/Context",
186 "Ljava/lang/String;");
187 QJniObject am = ctx.callObjectMethod(
"getSystemService",
188 "(Ljava/lang/String;)Ljava/lang/Object;",
189 audioServiceString.object());
193 auto sampleRateField = QJniObject::getStaticObjectField(
"android/media/AudioManager",
194 "PROPERTY_OUTPUT_SAMPLE_RATE",
195 "Ljava/lang/String;");
196 auto framesPerBufferField = QJniObject::getStaticObjectField(
197 "android/media/AudioManager",
198 "PROPERTY_OUTPUT_FRAMES_PER_BUFFER",
199 "Ljava/lang/String;");
201 auto sampleRateString = am.callObjectMethod(
"getProperty",
202 "(Ljava/lang/String;)Ljava/lang/String;",
203 sampleRateField.object());
204 auto framesPerBufferString = am.callObjectMethod(
"getProperty",
205 "(Ljava/lang/String;)Ljava/lang/String;",
206 framesPerBufferField.object());
208 if (!sampleRateString.isValid() || !framesPerBufferString.isValid())
211 framesPerBuffer = framesPerBufferString.toString().toInt();
212 sampleRate = sampleRateString.toString().toInt();
215 return framesPerBuffer;
228 const int channelConfig = [&format]() ->
int
240 const int audioFormat = [&format]() ->
int
242 const int sdkVersion = QNativeInterface::QAndroidApplication::sdkVersion();
254 const int minBufferSize = QJniObject::callStaticMethod<jint>(
"android/media/AudioTrack",
271 static int isSupported = -1;
273 if (isSupported != -1)
274 return (isSupported == 1);
276 QJniObject ctx(QNativeInterface::QAndroidApplication::context());
280 QJniObject pm = ctx.callObjectMethod(
"getPackageManager",
"()Landroid/content/pm/PackageManager;");
284 QJniObject audioFeatureField = QJniObject::getStaticObjectField(
285 "android/content/pm/PackageManager",
286 "FEATURE_AUDIO_LOW_LATENCY",
287 "Ljava/lang/String;");
288 if (!audioFeatureField.isValid())
291 isSupported = pm.callMethod<jboolean>(
"hasSystemFeature",
292 "(Ljava/lang/String;)Z",
293 audioFeatureField.object());
294 return (isSupported == 1);
299 return qEnvironmentVariableIsSet(
"QT_OPENSL_INFO");
304 m_supportedInputChannelCounts = QList<
int>() << 1;
305 m_supportedInputSampleRates.clear();
307 SLAndroidDataFormat_PCM_EX defaultFormat;
308 defaultFormat.formatType = SL_DATAFORMAT_PCM;
309 defaultFormat.numChannels = 1;
310 defaultFormat.sampleRate = SL_SAMPLINGRATE_44_1;
311 defaultFormat.bitsPerSample = SL_PCMSAMPLEFORMAT_FIXED_32;
312 defaultFormat.containerSize = SL_PCMSAMPLEFORMAT_FIXED_32;
313 defaultFormat.channelMask = SL_ANDROID_MAKE_INDEXED_CHANNEL_MASK(SL_SPEAKER_FRONT_CENTER);
314 defaultFormat.endianness = SL_BYTEORDER_LITTLEENDIAN;
315 defaultFormat.representation = SL_ANDROID_PCM_REPRESENTATION_SIGNED_INT;
317 const SLuint32 rates[13] = { SL_SAMPLINGRATE_8,
318 SL_SAMPLINGRATE_11_025,
321 SL_SAMPLINGRATE_22_05,
324 SL_SAMPLINGRATE_44_1,
327 SL_SAMPLINGRATE_88_2,
329 SL_SAMPLINGRATE_192 };
333 for (size_t i = 0 ; i <
std::size(rates); ++i) {
334 SLAndroidDataFormat_PCM_EX format = defaultFormat;
335 format.sampleRate = rates[i];
337 if (inputFormatIsSupported(format))
338 m_supportedInputSampleRates.append(rates[i] / 1000);
344 SLAndroidDataFormat_PCM_EX format = defaultFormat;
345 format.numChannels = 2;
346 format.channelMask = SL_ANDROID_MAKE_INDEXED_CHANNEL_MASK(SL_SPEAKER_FRONT_LEFT
347 | SL_SPEAKER_FRONT_RIGHT);
348 if (inputFormatIsSupported(format))
349 m_supportedInputChannelCounts.append(2);
352 m_checkedInputFormats =
true;
355bool QOpenSLESEngine::inputFormatIsSupported(SLAndroidDataFormat_PCM_EX format)
358 SLObjectItf recorder = 0;
359 SLDataLocator_IODevice loc_dev = { SL_DATALOCATOR_IODEVICE, SL_IODEVICE_AUDIOINPUT,
360 SL_DEFAULTDEVICEID_AUDIOINPUT, NULL };
361 SLDataSource audioSrc = { &loc_dev, NULL };
363 SLDataLocator_AndroidSimpleBufferQueue loc_bq = { SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE, 1 };
364 SLDataSink audioSnk = { &loc_bq, &format };
370 result = (*m_engine)->CreateAudioRecorder(m_engine, &recorder, &audioSrc, &audioSnk, 0, 0, 0);
371 if (result == SL_RESULT_SUCCESS)
372 result = (*recorder)->Realize(recorder,
false);
374 if (result == SL_RESULT_SUCCESS) {
375 (*recorder)->Destroy(recorder);
static int getLowLatencyBufferSize(const QAudioFormat &format)
static QOpenSLESEngine * instance()
static int getOutputValue(OutputValue type, int defaultValue=0)
static int getDefaultBufferSize(const QAudioFormat &format)
static bool supportsLowLatency()
static bool printDebugInfo()
QList< int > supportedSampleRates(QAudioDevice::Mode mode) const
QList< int > supportedChannelCounts(QAudioDevice::Mode mode) const
#define MINIMUM_PERIOD_TIME_MS
#define DEFAULT_PERIOD_TIME_MS
static bool hasRecordPermission()
static SLuint32 getChannelMask(unsigned channelCount)
Q_GLOBAL_STATIC(QOpenSLESEngine, openslesEngine)
#define CheckError(message)
#define SL_ANDROID_PCM_REPRESENTATION_INVALID