Reject older versions of PipeWire than built against
[openal-soft.git] / alc / backends / opensl.cpp
blob3a2100b5bb95e05e6b3f6323fd8f9ab897aaf6e7
1 /*
2 * Copyright (C) 2011 The Android Open Source Project
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
17 /* This is an OpenAL backend for Android using the native audio APIs based on
18 * OpenSL ES 1.0.1. It is based on source code for the native-audio sample app
19 * bundled with NDK.
22 #include "config.h"
24 #include "opensl.h"
26 #include <stdlib.h>
27 #include <jni.h>
29 #include <new>
30 #include <array>
31 #include <cstring>
32 #include <thread>
33 #include <functional>
35 #include "albit.h"
36 #include "alnumeric.h"
37 #include "core/device.h"
38 #include "core/helpers.h"
39 #include "core/logging.h"
40 #include "opthelpers.h"
41 #include "ringbuffer.h"
42 #include "threads.h"
44 #include <SLES/OpenSLES.h>
45 #include <SLES/OpenSLES_Android.h>
46 #include <SLES/OpenSLES_AndroidConfiguration.h>
49 namespace {
51 /* Helper macros */
52 #define EXTRACT_VCALL_ARGS(...) __VA_ARGS__))
53 #define VCALL(obj, func) ((*(obj))->func((obj), EXTRACT_VCALL_ARGS
54 #define VCALL0(obj, func) ((*(obj))->func((obj) EXTRACT_VCALL_ARGS
57 constexpr char opensl_device[] = "OpenSL";
60 constexpr SLuint32 GetChannelMask(DevFmtChannels chans) noexcept
62 switch(chans)
64 case DevFmtMono: return SL_SPEAKER_FRONT_CENTER;
65 case DevFmtStereo: return SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT;
66 case DevFmtQuad: return SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT |
67 SL_SPEAKER_BACK_LEFT | SL_SPEAKER_BACK_RIGHT;
68 case DevFmtX51: return SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT |
69 SL_SPEAKER_FRONT_CENTER | SL_SPEAKER_LOW_FREQUENCY | SL_SPEAKER_SIDE_LEFT |
70 SL_SPEAKER_SIDE_RIGHT;
71 case DevFmtX61: return SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT |
72 SL_SPEAKER_FRONT_CENTER | SL_SPEAKER_LOW_FREQUENCY | SL_SPEAKER_BACK_CENTER |
73 SL_SPEAKER_SIDE_LEFT | SL_SPEAKER_SIDE_RIGHT;
74 case DevFmtX71:
75 case DevFmtX3D71: return SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT |
76 SL_SPEAKER_FRONT_CENTER | SL_SPEAKER_LOW_FREQUENCY | SL_SPEAKER_BACK_LEFT |
77 SL_SPEAKER_BACK_RIGHT | SL_SPEAKER_SIDE_LEFT | SL_SPEAKER_SIDE_RIGHT;
78 case DevFmtAmbi3D:
79 break;
81 return 0;
84 #ifdef SL_ANDROID_DATAFORMAT_PCM_EX
85 constexpr SLuint32 GetTypeRepresentation(DevFmtType type) noexcept
87 switch(type)
89 case DevFmtUByte:
90 case DevFmtUShort:
91 case DevFmtUInt:
92 return SL_ANDROID_PCM_REPRESENTATION_UNSIGNED_INT;
93 case DevFmtByte:
94 case DevFmtShort:
95 case DevFmtInt:
96 return SL_ANDROID_PCM_REPRESENTATION_SIGNED_INT;
97 case DevFmtFloat:
98 return SL_ANDROID_PCM_REPRESENTATION_FLOAT;
100 return 0;
102 #endif
104 constexpr SLuint32 GetByteOrderEndianness() noexcept
106 if(al::endian::native == al::endian::little)
107 return SL_BYTEORDER_LITTLEENDIAN;
108 return SL_BYTEORDER_BIGENDIAN;
111 const char *res_str(SLresult result) noexcept
113 switch(result)
115 case SL_RESULT_SUCCESS: return "Success";
116 case SL_RESULT_PRECONDITIONS_VIOLATED: return "Preconditions violated";
117 case SL_RESULT_PARAMETER_INVALID: return "Parameter invalid";
118 case SL_RESULT_MEMORY_FAILURE: return "Memory failure";
119 case SL_RESULT_RESOURCE_ERROR: return "Resource error";
120 case SL_RESULT_RESOURCE_LOST: return "Resource lost";
121 case SL_RESULT_IO_ERROR: return "I/O error";
122 case SL_RESULT_BUFFER_INSUFFICIENT: return "Buffer insufficient";
123 case SL_RESULT_CONTENT_CORRUPTED: return "Content corrupted";
124 case SL_RESULT_CONTENT_UNSUPPORTED: return "Content unsupported";
125 case SL_RESULT_CONTENT_NOT_FOUND: return "Content not found";
126 case SL_RESULT_PERMISSION_DENIED: return "Permission denied";
127 case SL_RESULT_FEATURE_UNSUPPORTED: return "Feature unsupported";
128 case SL_RESULT_INTERNAL_ERROR: return "Internal error";
129 case SL_RESULT_UNKNOWN_ERROR: return "Unknown error";
130 case SL_RESULT_OPERATION_ABORTED: return "Operation aborted";
131 case SL_RESULT_CONTROL_LOST: return "Control lost";
132 #ifdef SL_RESULT_READONLY
133 case SL_RESULT_READONLY: return "ReadOnly";
134 #endif
135 #ifdef SL_RESULT_ENGINEOPTION_UNSUPPORTED
136 case SL_RESULT_ENGINEOPTION_UNSUPPORTED: return "Engine option unsupported";
137 #endif
138 #ifdef SL_RESULT_SOURCE_SINK_INCOMPATIBLE
139 case SL_RESULT_SOURCE_SINK_INCOMPATIBLE: return "Source/Sink incompatible";
140 #endif
142 return "Unknown error code";
145 #define PRINTERR(x, s) do { \
146 if UNLIKELY((x) != SL_RESULT_SUCCESS) \
147 ERR("%s: %s\n", (s), res_str((x))); \
148 } while(0)
151 struct OpenSLPlayback final : public BackendBase {
152 OpenSLPlayback(DeviceBase *device) noexcept : BackendBase{device} { }
153 ~OpenSLPlayback() override;
155 void process(SLAndroidSimpleBufferQueueItf bq) noexcept;
156 static void processC(SLAndroidSimpleBufferQueueItf bq, void *context) noexcept
157 { static_cast<OpenSLPlayback*>(context)->process(bq); }
159 int mixerProc();
161 void open(const char *name) override;
162 bool reset() override;
163 void start() override;
164 void stop() override;
165 ClockLatency getClockLatency() override;
167 /* engine interfaces */
168 SLObjectItf mEngineObj{nullptr};
169 SLEngineItf mEngine{nullptr};
171 /* output mix interfaces */
172 SLObjectItf mOutputMix{nullptr};
174 /* buffer queue player interfaces */
175 SLObjectItf mBufferQueueObj{nullptr};
177 RingBufferPtr mRing{nullptr};
178 al::semaphore mSem;
180 std::mutex mMutex;
182 uint mFrameSize{0};
184 std::atomic<bool> mKillNow{true};
185 std::thread mThread;
187 DEF_NEWDEL(OpenSLPlayback)
190 OpenSLPlayback::~OpenSLPlayback()
192 if(mBufferQueueObj)
193 VCALL0(mBufferQueueObj,Destroy)();
194 mBufferQueueObj = nullptr;
196 if(mOutputMix)
197 VCALL0(mOutputMix,Destroy)();
198 mOutputMix = nullptr;
200 if(mEngineObj)
201 VCALL0(mEngineObj,Destroy)();
202 mEngineObj = nullptr;
203 mEngine = nullptr;
207 /* this callback handler is called every time a buffer finishes playing */
208 void OpenSLPlayback::process(SLAndroidSimpleBufferQueueItf) noexcept
210 /* A note on the ringbuffer usage: The buffer queue seems to hold on to the
211 * pointer passed to the Enqueue method, rather than copying the audio.
212 * Consequently, the ringbuffer contains the audio that is currently queued
213 * and waiting to play. This process() callback is called when a buffer is
214 * finished, so we simply move the read pointer up to indicate the space is
215 * available for writing again, and wake up the mixer thread to mix and
216 * queue more audio.
218 mRing->readAdvance(1);
220 mSem.post();
223 int OpenSLPlayback::mixerProc()
225 SetRTPriority();
226 althrd_setname(MIXER_THREAD_NAME);
228 SLPlayItf player;
229 SLAndroidSimpleBufferQueueItf bufferQueue;
230 SLresult result{VCALL(mBufferQueueObj,GetInterface)(SL_IID_ANDROIDSIMPLEBUFFERQUEUE,
231 &bufferQueue)};
232 PRINTERR(result, "bufferQueue->GetInterface SL_IID_ANDROIDSIMPLEBUFFERQUEUE");
233 if(SL_RESULT_SUCCESS == result)
235 result = VCALL(mBufferQueueObj,GetInterface)(SL_IID_PLAY, &player);
236 PRINTERR(result, "bufferQueue->GetInterface SL_IID_PLAY");
239 const size_t frame_step{mDevice->channelsFromFmt()};
241 if(SL_RESULT_SUCCESS != result)
242 mDevice->handleDisconnect("Failed to get playback buffer: 0x%08x", result);
244 while(SL_RESULT_SUCCESS == result && !mKillNow.load(std::memory_order_acquire)
245 && mDevice->Connected.load(std::memory_order_acquire))
247 if(mRing->writeSpace() == 0)
249 SLuint32 state{0};
251 result = VCALL(player,GetPlayState)(&state);
252 PRINTERR(result, "player->GetPlayState");
253 if(SL_RESULT_SUCCESS == result && state != SL_PLAYSTATE_PLAYING)
255 result = VCALL(player,SetPlayState)(SL_PLAYSTATE_PLAYING);
256 PRINTERR(result, "player->SetPlayState");
258 if(SL_RESULT_SUCCESS != result)
260 mDevice->handleDisconnect("Failed to start playback: 0x%08x", result);
261 break;
264 if(mRing->writeSpace() == 0)
266 mSem.wait();
267 continue;
271 std::unique_lock<std::mutex> dlock{mMutex};
272 auto data = mRing->getWriteVector();
273 mDevice->renderSamples(data.first.buf,
274 static_cast<uint>(data.first.len)*mDevice->UpdateSize, frame_step);
275 if(data.second.len > 0)
276 mDevice->renderSamples(data.second.buf,
277 static_cast<uint>(data.second.len)*mDevice->UpdateSize, frame_step);
279 size_t todo{data.first.len + data.second.len};
280 mRing->writeAdvance(todo);
281 dlock.unlock();
283 for(size_t i{0};i < todo;i++)
285 if(!data.first.len)
287 data.first = data.second;
288 data.second.buf = nullptr;
289 data.second.len = 0;
292 result = VCALL(bufferQueue,Enqueue)(data.first.buf, mDevice->UpdateSize*mFrameSize);
293 PRINTERR(result, "bufferQueue->Enqueue");
294 if(SL_RESULT_SUCCESS != result)
296 mDevice->handleDisconnect("Failed to queue audio: 0x%08x", result);
297 break;
300 data.first.len--;
301 data.first.buf += mDevice->UpdateSize*mFrameSize;
305 return 0;
309 void OpenSLPlayback::open(const char *name)
311 if(!name)
312 name = opensl_device;
313 else if(strcmp(name, opensl_device) != 0)
314 throw al::backend_exception{al::backend_error::NoDevice, "Device name \"%s\" not found",
315 name};
317 /* There's only one device, so if it's already open, there's nothing to do. */
318 if(mEngineObj) return;
320 // create engine
321 SLresult result{slCreateEngine(&mEngineObj, 0, nullptr, 0, nullptr, nullptr)};
322 PRINTERR(result, "slCreateEngine");
323 if(SL_RESULT_SUCCESS == result)
325 result = VCALL(mEngineObj,Realize)(SL_BOOLEAN_FALSE);
326 PRINTERR(result, "engine->Realize");
328 if(SL_RESULT_SUCCESS == result)
330 result = VCALL(mEngineObj,GetInterface)(SL_IID_ENGINE, &mEngine);
331 PRINTERR(result, "engine->GetInterface");
333 if(SL_RESULT_SUCCESS == result)
335 result = VCALL(mEngine,CreateOutputMix)(&mOutputMix, 0, nullptr, nullptr);
336 PRINTERR(result, "engine->CreateOutputMix");
338 if(SL_RESULT_SUCCESS == result)
340 result = VCALL(mOutputMix,Realize)(SL_BOOLEAN_FALSE);
341 PRINTERR(result, "outputMix->Realize");
344 if(SL_RESULT_SUCCESS != result)
346 if(mOutputMix)
347 VCALL0(mOutputMix,Destroy)();
348 mOutputMix = nullptr;
350 if(mEngineObj)
351 VCALL0(mEngineObj,Destroy)();
352 mEngineObj = nullptr;
353 mEngine = nullptr;
355 throw al::backend_exception{al::backend_error::DeviceError,
356 "Failed to initialize OpenSL device: 0x%08x", result};
359 mDevice->DeviceName = name;
362 bool OpenSLPlayback::reset()
364 SLresult result;
366 if(mBufferQueueObj)
367 VCALL0(mBufferQueueObj,Destroy)();
368 mBufferQueueObj = nullptr;
370 mRing = nullptr;
372 #if 0
373 if(!mDevice->Flags.get<FrequencyRequest>())
375 /* FIXME: Disabled until I figure out how to get the Context needed for
376 * the getSystemService call.
378 JNIEnv *env = Android_GetJNIEnv();
379 jobject jctx = Android_GetContext();
381 /* Get necessary stuff for using java.lang.Integer,
382 * android.content.Context, and android.media.AudioManager.
384 jclass int_cls = JCALL(env,FindClass)("java/lang/Integer");
385 jmethodID int_parseint = JCALL(env,GetStaticMethodID)(int_cls,
386 "parseInt", "(Ljava/lang/String;)I"
388 TRACE("Integer: %p, parseInt: %p\n", int_cls, int_parseint);
390 jclass ctx_cls = JCALL(env,FindClass)("android/content/Context");
391 jfieldID ctx_audsvc = JCALL(env,GetStaticFieldID)(ctx_cls,
392 "AUDIO_SERVICE", "Ljava/lang/String;"
394 jmethodID ctx_getSysSvc = JCALL(env,GetMethodID)(ctx_cls,
395 "getSystemService", "(Ljava/lang/String;)Ljava/lang/Object;"
397 TRACE("Context: %p, AUDIO_SERVICE: %p, getSystemService: %p\n",
398 ctx_cls, ctx_audsvc, ctx_getSysSvc);
400 jclass audmgr_cls = JCALL(env,FindClass)("android/media/AudioManager");
401 jfieldID audmgr_prop_out_srate = JCALL(env,GetStaticFieldID)(audmgr_cls,
402 "PROPERTY_OUTPUT_SAMPLE_RATE", "Ljava/lang/String;"
404 jmethodID audmgr_getproperty = JCALL(env,GetMethodID)(audmgr_cls,
405 "getProperty", "(Ljava/lang/String;)Ljava/lang/String;"
407 TRACE("AudioManager: %p, PROPERTY_OUTPUT_SAMPLE_RATE: %p, getProperty: %p\n",
408 audmgr_cls, audmgr_prop_out_srate, audmgr_getproperty);
410 const char *strchars;
411 jstring strobj;
413 /* Now make the calls. */
414 //AudioManager audMgr = (AudioManager)getSystemService(Context.AUDIO_SERVICE);
415 strobj = JCALL(env,GetStaticObjectField)(ctx_cls, ctx_audsvc);
416 jobject audMgr = JCALL(env,CallObjectMethod)(jctx, ctx_getSysSvc, strobj);
417 strchars = JCALL(env,GetStringUTFChars)(strobj, nullptr);
418 TRACE("Context.getSystemService(%s) = %p\n", strchars, audMgr);
419 JCALL(env,ReleaseStringUTFChars)(strobj, strchars);
421 //String srateStr = audMgr.getProperty(AudioManager.PROPERTY_OUTPUT_SAMPLE_RATE);
422 strobj = JCALL(env,GetStaticObjectField)(audmgr_cls, audmgr_prop_out_srate);
423 jstring srateStr = JCALL(env,CallObjectMethod)(audMgr, audmgr_getproperty, strobj);
424 strchars = JCALL(env,GetStringUTFChars)(strobj, nullptr);
425 TRACE("audMgr.getProperty(%s) = %p\n", strchars, srateStr);
426 JCALL(env,ReleaseStringUTFChars)(strobj, strchars);
428 //int sampleRate = Integer.parseInt(srateStr);
429 sampleRate = JCALL(env,CallStaticIntMethod)(int_cls, int_parseint, srateStr);
431 strchars = JCALL(env,GetStringUTFChars)(srateStr, nullptr);
432 TRACE("Got system sample rate %uhz (%s)\n", sampleRate, strchars);
433 JCALL(env,ReleaseStringUTFChars)(srateStr, strchars);
435 if(!sampleRate) sampleRate = device->Frequency;
436 else sampleRate = maxu(sampleRate, MIN_OUTPUT_RATE);
438 #endif
440 mDevice->FmtChans = DevFmtStereo;
441 mDevice->FmtType = DevFmtShort;
443 setDefaultWFXChannelOrder();
444 mFrameSize = mDevice->frameSizeFromFmt();
447 const std::array<SLInterfaceID,2> ids{{ SL_IID_ANDROIDSIMPLEBUFFERQUEUE, SL_IID_ANDROIDCONFIGURATION }};
448 const std::array<SLboolean,2> reqs{{ SL_BOOLEAN_TRUE, SL_BOOLEAN_FALSE }};
450 SLDataLocator_OutputMix loc_outmix{};
451 loc_outmix.locatorType = SL_DATALOCATOR_OUTPUTMIX;
452 loc_outmix.outputMix = mOutputMix;
454 SLDataSink audioSnk{};
455 audioSnk.pLocator = &loc_outmix;
456 audioSnk.pFormat = nullptr;
458 SLDataLocator_AndroidSimpleBufferQueue loc_bufq{};
459 loc_bufq.locatorType = SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE;
460 loc_bufq.numBuffers = mDevice->BufferSize / mDevice->UpdateSize;
462 SLDataSource audioSrc{};
463 #ifdef SL_ANDROID_DATAFORMAT_PCM_EX
464 SLAndroidDataFormat_PCM_EX format_pcm_ex{};
465 format_pcm_ex.formatType = SL_ANDROID_DATAFORMAT_PCM_EX;
466 format_pcm_ex.numChannels = mDevice->channelsFromFmt();
467 format_pcm_ex.sampleRate = mDevice->Frequency * 1000;
468 format_pcm_ex.bitsPerSample = mDevice->bytesFromFmt() * 8;
469 format_pcm_ex.containerSize = format_pcm_ex.bitsPerSample;
470 format_pcm_ex.channelMask = GetChannelMask(mDevice->FmtChans);
471 format_pcm_ex.endianness = GetByteOrderEndianness();
472 format_pcm_ex.representation = GetTypeRepresentation(mDevice->FmtType);
474 audioSrc.pLocator = &loc_bufq;
475 audioSrc.pFormat = &format_pcm_ex;
477 result = VCALL(mEngine,CreateAudioPlayer)(&mBufferQueueObj, &audioSrc, &audioSnk, ids.size(),
478 ids.data(), reqs.data());
479 if(SL_RESULT_SUCCESS != result)
480 #endif
482 /* Alter sample type according to what SLDataFormat_PCM can support. */
483 switch(mDevice->FmtType)
485 case DevFmtByte: mDevice->FmtType = DevFmtUByte; break;
486 case DevFmtUInt: mDevice->FmtType = DevFmtInt; break;
487 case DevFmtFloat:
488 case DevFmtUShort: mDevice->FmtType = DevFmtShort; break;
489 case DevFmtUByte:
490 case DevFmtShort:
491 case DevFmtInt:
492 break;
495 SLDataFormat_PCM format_pcm{};
496 format_pcm.formatType = SL_DATAFORMAT_PCM;
497 format_pcm.numChannels = mDevice->channelsFromFmt();
498 format_pcm.samplesPerSec = mDevice->Frequency * 1000;
499 format_pcm.bitsPerSample = mDevice->bytesFromFmt() * 8;
500 format_pcm.containerSize = format_pcm.bitsPerSample;
501 format_pcm.channelMask = GetChannelMask(mDevice->FmtChans);
502 format_pcm.endianness = GetByteOrderEndianness();
504 audioSrc.pLocator = &loc_bufq;
505 audioSrc.pFormat = &format_pcm;
507 result = VCALL(mEngine,CreateAudioPlayer)(&mBufferQueueObj, &audioSrc, &audioSnk, ids.size(),
508 ids.data(), reqs.data());
509 PRINTERR(result, "engine->CreateAudioPlayer");
511 if(SL_RESULT_SUCCESS == result)
513 /* Set the stream type to "media" (games, music, etc), if possible. */
514 SLAndroidConfigurationItf config;
515 result = VCALL(mBufferQueueObj,GetInterface)(SL_IID_ANDROIDCONFIGURATION, &config);
516 PRINTERR(result, "bufferQueue->GetInterface SL_IID_ANDROIDCONFIGURATION");
517 if(SL_RESULT_SUCCESS == result)
519 SLint32 streamType = SL_ANDROID_STREAM_MEDIA;
520 result = VCALL(config,SetConfiguration)(SL_ANDROID_KEY_STREAM_TYPE, &streamType,
521 sizeof(streamType));
522 PRINTERR(result, "config->SetConfiguration");
525 /* Clear any error since this was optional. */
526 result = SL_RESULT_SUCCESS;
528 if(SL_RESULT_SUCCESS == result)
530 result = VCALL(mBufferQueueObj,Realize)(SL_BOOLEAN_FALSE);
531 PRINTERR(result, "bufferQueue->Realize");
533 if(SL_RESULT_SUCCESS == result)
535 const uint num_updates{mDevice->BufferSize / mDevice->UpdateSize};
536 mRing = RingBuffer::Create(num_updates, mFrameSize*mDevice->UpdateSize, true);
539 if(SL_RESULT_SUCCESS != result)
541 if(mBufferQueueObj)
542 VCALL0(mBufferQueueObj,Destroy)();
543 mBufferQueueObj = nullptr;
545 return false;
548 return true;
551 void OpenSLPlayback::start()
553 mRing->reset();
555 SLAndroidSimpleBufferQueueItf bufferQueue;
556 SLresult result{VCALL(mBufferQueueObj,GetInterface)(SL_IID_ANDROIDSIMPLEBUFFERQUEUE,
557 &bufferQueue)};
558 PRINTERR(result, "bufferQueue->GetInterface");
559 if(SL_RESULT_SUCCESS == result)
561 result = VCALL(bufferQueue,RegisterCallback)(&OpenSLPlayback::processC, this);
562 PRINTERR(result, "bufferQueue->RegisterCallback");
564 if(SL_RESULT_SUCCESS != result)
565 throw al::backend_exception{al::backend_error::DeviceError,
566 "Failed to register callback: 0x%08x", result};
568 try {
569 mKillNow.store(false, std::memory_order_release);
570 mThread = std::thread(std::mem_fn(&OpenSLPlayback::mixerProc), this);
572 catch(std::exception& e) {
573 throw al::backend_exception{al::backend_error::DeviceError,
574 "Failed to start mixing thread: %s", e.what()};
578 void OpenSLPlayback::stop()
580 if(mKillNow.exchange(true, std::memory_order_acq_rel) || !mThread.joinable())
581 return;
583 mSem.post();
584 mThread.join();
586 SLPlayItf player;
587 SLresult result{VCALL(mBufferQueueObj,GetInterface)(SL_IID_PLAY, &player)};
588 PRINTERR(result, "bufferQueue->GetInterface");
589 if(SL_RESULT_SUCCESS == result)
591 result = VCALL(player,SetPlayState)(SL_PLAYSTATE_STOPPED);
592 PRINTERR(result, "player->SetPlayState");
595 SLAndroidSimpleBufferQueueItf bufferQueue;
596 result = VCALL(mBufferQueueObj,GetInterface)(SL_IID_ANDROIDSIMPLEBUFFERQUEUE, &bufferQueue);
597 PRINTERR(result, "bufferQueue->GetInterface");
598 if(SL_RESULT_SUCCESS == result)
600 result = VCALL0(bufferQueue,Clear)();
601 PRINTERR(result, "bufferQueue->Clear");
603 if(SL_RESULT_SUCCESS == result)
605 result = VCALL(bufferQueue,RegisterCallback)(nullptr, nullptr);
606 PRINTERR(result, "bufferQueue->RegisterCallback");
608 if(SL_RESULT_SUCCESS == result)
610 SLAndroidSimpleBufferQueueState state;
611 do {
612 std::this_thread::yield();
613 result = VCALL(bufferQueue,GetState)(&state);
614 } while(SL_RESULT_SUCCESS == result && state.count > 0);
615 PRINTERR(result, "bufferQueue->GetState");
617 mRing.reset();
621 ClockLatency OpenSLPlayback::getClockLatency()
623 ClockLatency ret;
625 std::lock_guard<std::mutex> _{mMutex};
626 ret.ClockTime = GetDeviceClockTime(mDevice);
627 ret.Latency = std::chrono::seconds{mRing->readSpace() * mDevice->UpdateSize};
628 ret.Latency /= mDevice->Frequency;
630 return ret;
634 struct OpenSLCapture final : public BackendBase {
635 OpenSLCapture(DeviceBase *device) noexcept : BackendBase{device} { }
636 ~OpenSLCapture() override;
638 void process(SLAndroidSimpleBufferQueueItf bq) noexcept;
639 static void processC(SLAndroidSimpleBufferQueueItf bq, void *context) noexcept
640 { static_cast<OpenSLCapture*>(context)->process(bq); }
642 void open(const char *name) override;
643 void start() override;
644 void stop() override;
645 void captureSamples(al::byte *buffer, uint samples) override;
646 uint availableSamples() override;
648 /* engine interfaces */
649 SLObjectItf mEngineObj{nullptr};
650 SLEngineItf mEngine;
652 /* recording interfaces */
653 SLObjectItf mRecordObj{nullptr};
655 RingBufferPtr mRing{nullptr};
656 uint mSplOffset{0u};
658 uint mFrameSize{0};
660 DEF_NEWDEL(OpenSLCapture)
663 OpenSLCapture::~OpenSLCapture()
665 if(mRecordObj)
666 VCALL0(mRecordObj,Destroy)();
667 mRecordObj = nullptr;
669 if(mEngineObj)
670 VCALL0(mEngineObj,Destroy)();
671 mEngineObj = nullptr;
672 mEngine = nullptr;
676 void OpenSLCapture::process(SLAndroidSimpleBufferQueueItf) noexcept
678 /* A new chunk has been written into the ring buffer, advance it. */
679 mRing->writeAdvance(1);
683 void OpenSLCapture::open(const char* name)
685 if(!name)
686 name = opensl_device;
687 else if(strcmp(name, opensl_device) != 0)
688 throw al::backend_exception{al::backend_error::NoDevice, "Device name \"%s\" not found",
689 name};
691 SLresult result{slCreateEngine(&mEngineObj, 0, nullptr, 0, nullptr, nullptr)};
692 PRINTERR(result, "slCreateEngine");
693 if(SL_RESULT_SUCCESS == result)
695 result = VCALL(mEngineObj,Realize)(SL_BOOLEAN_FALSE);
696 PRINTERR(result, "engine->Realize");
698 if(SL_RESULT_SUCCESS == result)
700 result = VCALL(mEngineObj,GetInterface)(SL_IID_ENGINE, &mEngine);
701 PRINTERR(result, "engine->GetInterface");
703 if(SL_RESULT_SUCCESS == result)
705 mFrameSize = mDevice->frameSizeFromFmt();
706 /* Ensure the total length is at least 100ms */
707 uint length{maxu(mDevice->BufferSize, mDevice->Frequency/10)};
708 /* Ensure the per-chunk length is at least 10ms, and no more than 50ms. */
709 uint update_len{clampu(mDevice->BufferSize/3, mDevice->Frequency/100,
710 mDevice->Frequency/100*5)};
711 uint num_updates{(length+update_len-1) / update_len};
713 mRing = RingBuffer::Create(num_updates, update_len*mFrameSize, false);
715 mDevice->UpdateSize = update_len;
716 mDevice->BufferSize = static_cast<uint>(mRing->writeSpace() * update_len);
718 if(SL_RESULT_SUCCESS == result)
720 const std::array<SLInterfaceID,2> ids{{ SL_IID_ANDROIDSIMPLEBUFFERQUEUE, SL_IID_ANDROIDCONFIGURATION }};
721 const std::array<SLboolean,2> reqs{{ SL_BOOLEAN_TRUE, SL_BOOLEAN_FALSE }};
723 SLDataLocator_IODevice loc_dev{};
724 loc_dev.locatorType = SL_DATALOCATOR_IODEVICE;
725 loc_dev.deviceType = SL_IODEVICE_AUDIOINPUT;
726 loc_dev.deviceID = SL_DEFAULTDEVICEID_AUDIOINPUT;
727 loc_dev.device = nullptr;
729 SLDataSource audioSrc{};
730 audioSrc.pLocator = &loc_dev;
731 audioSrc.pFormat = nullptr;
733 SLDataLocator_AndroidSimpleBufferQueue loc_bq{};
734 loc_bq.locatorType = SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE;
735 loc_bq.numBuffers = mDevice->BufferSize / mDevice->UpdateSize;
737 SLDataSink audioSnk{};
738 #ifdef SL_ANDROID_DATAFORMAT_PCM_EX
739 SLAndroidDataFormat_PCM_EX format_pcm_ex{};
740 format_pcm_ex.formatType = SL_ANDROID_DATAFORMAT_PCM_EX;
741 format_pcm_ex.numChannels = mDevice->channelsFromFmt();
742 format_pcm_ex.sampleRate = mDevice->Frequency * 1000;
743 format_pcm_ex.bitsPerSample = mDevice->bytesFromFmt() * 8;
744 format_pcm_ex.containerSize = format_pcm_ex.bitsPerSample;
745 format_pcm_ex.channelMask = GetChannelMask(mDevice->FmtChans);
746 format_pcm_ex.endianness = GetByteOrderEndianness();
747 format_pcm_ex.representation = GetTypeRepresentation(mDevice->FmtType);
749 audioSnk.pLocator = &loc_bq;
750 audioSnk.pFormat = &format_pcm_ex;
751 result = VCALL(mEngine,CreateAudioRecorder)(&mRecordObj, &audioSrc, &audioSnk,
752 ids.size(), ids.data(), reqs.data());
753 if(SL_RESULT_SUCCESS != result)
754 #endif
756 /* Fallback to SLDataFormat_PCM only if it supports the desired
757 * sample type.
759 if(mDevice->FmtType == DevFmtUByte || mDevice->FmtType == DevFmtShort
760 || mDevice->FmtType == DevFmtInt)
762 SLDataFormat_PCM format_pcm{};
763 format_pcm.formatType = SL_DATAFORMAT_PCM;
764 format_pcm.numChannels = mDevice->channelsFromFmt();
765 format_pcm.samplesPerSec = mDevice->Frequency * 1000;
766 format_pcm.bitsPerSample = mDevice->bytesFromFmt() * 8;
767 format_pcm.containerSize = format_pcm.bitsPerSample;
768 format_pcm.channelMask = GetChannelMask(mDevice->FmtChans);
769 format_pcm.endianness = GetByteOrderEndianness();
771 audioSnk.pLocator = &loc_bq;
772 audioSnk.pFormat = &format_pcm;
773 result = VCALL(mEngine,CreateAudioRecorder)(&mRecordObj, &audioSrc, &audioSnk,
774 ids.size(), ids.data(), reqs.data());
776 PRINTERR(result, "engine->CreateAudioRecorder");
779 if(SL_RESULT_SUCCESS == result)
781 /* Set the record preset to "generic", if possible. */
782 SLAndroidConfigurationItf config;
783 result = VCALL(mRecordObj,GetInterface)(SL_IID_ANDROIDCONFIGURATION, &config);
784 PRINTERR(result, "recordObj->GetInterface SL_IID_ANDROIDCONFIGURATION");
785 if(SL_RESULT_SUCCESS == result)
787 SLuint32 preset = SL_ANDROID_RECORDING_PRESET_GENERIC;
788 result = VCALL(config,SetConfiguration)(SL_ANDROID_KEY_RECORDING_PRESET, &preset,
789 sizeof(preset));
790 PRINTERR(result, "config->SetConfiguration");
793 /* Clear any error since this was optional. */
794 result = SL_RESULT_SUCCESS;
796 if(SL_RESULT_SUCCESS == result)
798 result = VCALL(mRecordObj,Realize)(SL_BOOLEAN_FALSE);
799 PRINTERR(result, "recordObj->Realize");
802 SLAndroidSimpleBufferQueueItf bufferQueue;
803 if(SL_RESULT_SUCCESS == result)
805 result = VCALL(mRecordObj,GetInterface)(SL_IID_ANDROIDSIMPLEBUFFERQUEUE, &bufferQueue);
806 PRINTERR(result, "recordObj->GetInterface");
808 if(SL_RESULT_SUCCESS == result)
810 result = VCALL(bufferQueue,RegisterCallback)(&OpenSLCapture::processC, this);
811 PRINTERR(result, "bufferQueue->RegisterCallback");
813 if(SL_RESULT_SUCCESS == result)
815 const uint chunk_size{mDevice->UpdateSize * mFrameSize};
816 const auto silence = (mDevice->FmtType == DevFmtUByte) ? al::byte{0x80} : al::byte{0};
818 auto data = mRing->getWriteVector();
819 std::fill_n(data.first.buf, data.first.len*chunk_size, silence);
820 std::fill_n(data.second.buf, data.second.len*chunk_size, silence);
821 for(size_t i{0u};i < data.first.len && SL_RESULT_SUCCESS == result;i++)
823 result = VCALL(bufferQueue,Enqueue)(data.first.buf + chunk_size*i, chunk_size);
824 PRINTERR(result, "bufferQueue->Enqueue");
826 for(size_t i{0u};i < data.second.len && SL_RESULT_SUCCESS == result;i++)
828 result = VCALL(bufferQueue,Enqueue)(data.second.buf + chunk_size*i, chunk_size);
829 PRINTERR(result, "bufferQueue->Enqueue");
833 if(SL_RESULT_SUCCESS != result)
835 if(mRecordObj)
836 VCALL0(mRecordObj,Destroy)();
837 mRecordObj = nullptr;
839 if(mEngineObj)
840 VCALL0(mEngineObj,Destroy)();
841 mEngineObj = nullptr;
842 mEngine = nullptr;
844 throw al::backend_exception{al::backend_error::DeviceError,
845 "Failed to initialize OpenSL device: 0x%08x", result};
848 mDevice->DeviceName = name;
851 void OpenSLCapture::start()
853 SLRecordItf record;
854 SLresult result{VCALL(mRecordObj,GetInterface)(SL_IID_RECORD, &record)};
855 PRINTERR(result, "recordObj->GetInterface");
857 if(SL_RESULT_SUCCESS == result)
859 result = VCALL(record,SetRecordState)(SL_RECORDSTATE_RECORDING);
860 PRINTERR(result, "record->SetRecordState");
862 if(SL_RESULT_SUCCESS != result)
863 throw al::backend_exception{al::backend_error::DeviceError,
864 "Failed to start capture: 0x%08x", result};
867 void OpenSLCapture::stop()
869 SLRecordItf record;
870 SLresult result{VCALL(mRecordObj,GetInterface)(SL_IID_RECORD, &record)};
871 PRINTERR(result, "recordObj->GetInterface");
873 if(SL_RESULT_SUCCESS == result)
875 result = VCALL(record,SetRecordState)(SL_RECORDSTATE_PAUSED);
876 PRINTERR(result, "record->SetRecordState");
880 void OpenSLCapture::captureSamples(al::byte *buffer, uint samples)
882 const uint update_size{mDevice->UpdateSize};
883 const uint chunk_size{update_size * mFrameSize};
884 const auto silence = (mDevice->FmtType == DevFmtUByte) ? al::byte{0x80} : al::byte{0};
886 /* Read the desired samples from the ring buffer then advance its read
887 * pointer.
889 size_t adv_count{0};
890 auto rdata = mRing->getReadVector();
891 for(uint i{0};i < samples;)
893 const uint rem{minu(samples - i, update_size - mSplOffset)};
894 std::copy_n(rdata.first.buf + mSplOffset*size_t{mFrameSize}, rem*size_t{mFrameSize},
895 buffer + i*size_t{mFrameSize});
897 mSplOffset += rem;
898 if(mSplOffset == update_size)
900 /* Finished a chunk, reset the offset and advance the read pointer. */
901 mSplOffset = 0;
903 ++adv_count;
904 rdata.first.len -= 1;
905 if(!rdata.first.len)
906 rdata.first = rdata.second;
907 else
908 rdata.first.buf += chunk_size;
911 i += rem;
913 mRing->readAdvance(adv_count);
915 SLAndroidSimpleBufferQueueItf bufferQueue{};
916 if LIKELY(mDevice->Connected.load(std::memory_order_acquire))
918 const SLresult result{VCALL(mRecordObj,GetInterface)(SL_IID_ANDROIDSIMPLEBUFFERQUEUE,
919 &bufferQueue)};
920 PRINTERR(result, "recordObj->GetInterface");
921 if UNLIKELY(SL_RESULT_SUCCESS != result)
923 mDevice->handleDisconnect("Failed to get capture buffer queue: 0x%08x", result);
924 bufferQueue = nullptr;
928 if LIKELY(bufferQueue)
930 SLresult result{SL_RESULT_SUCCESS};
931 auto wdata = mRing->getWriteVector();
932 std::fill_n(wdata.first.buf, wdata.first.len*chunk_size, silence);
933 for(size_t i{0u};i < wdata.first.len && SL_RESULT_SUCCESS == result;i++)
935 result = VCALL(bufferQueue,Enqueue)(wdata.first.buf + chunk_size*i, chunk_size);
936 PRINTERR(result, "bufferQueue->Enqueue");
938 if(wdata.second.len > 0)
940 std::fill_n(wdata.second.buf, wdata.second.len*chunk_size, silence);
941 for(size_t i{0u};i < wdata.second.len && SL_RESULT_SUCCESS == result;i++)
943 result = VCALL(bufferQueue,Enqueue)(wdata.second.buf + chunk_size*i, chunk_size);
944 PRINTERR(result, "bufferQueue->Enqueue");
950 uint OpenSLCapture::availableSamples()
951 { return static_cast<uint>(mRing->readSpace()*mDevice->UpdateSize - mSplOffset); }
953 } // namespace
955 bool OSLBackendFactory::init() { return true; }
957 bool OSLBackendFactory::querySupport(BackendType type)
958 { return (type == BackendType::Playback || type == BackendType::Capture); }
960 std::string OSLBackendFactory::probe(BackendType type)
962 std::string outnames;
963 switch(type)
965 case BackendType::Playback:
966 case BackendType::Capture:
967 /* Includes null char. */
968 outnames.append(opensl_device, sizeof(opensl_device));
969 break;
971 return outnames;
974 BackendPtr OSLBackendFactory::createBackend(DeviceBase *device, BackendType type)
976 if(type == BackendType::Playback)
977 return BackendPtr{new OpenSLPlayback{device}};
978 if(type == BackendType::Capture)
979 return BackendPtr{new OpenSLCapture{device}};
980 return nullptr;
983 BackendFactory &OSLBackendFactory::getFactory()
985 static OSLBackendFactory factory{};
986 return factory;