Use exceptions for backend open failures
[openal-soft.git] / alc / backends / opensl.cpp
blob4e4ceb5b865b7e11d48245ad0072c12e3f440c1b
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 "backends/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 "alcmain.h"
36 #include "alexcpt.h"
37 #include "alu.h"
38 #include "compat.h"
39 #include "endiantest.h"
40 #include "ringbuffer.h"
41 #include "threads.h"
43 #include <SLES/OpenSLES.h>
44 #include <SLES/OpenSLES_Android.h>
45 #include <SLES/OpenSLES_AndroidConfiguration.h>
48 namespace {
50 /* Helper macros */
51 #define EXTRACT_VCALL_ARGS(...) __VA_ARGS__))
52 #define VCALL(obj, func) ((*(obj))->func((obj), EXTRACT_VCALL_ARGS
53 #define VCALL0(obj, func) ((*(obj))->func((obj) EXTRACT_VCALL_ARGS
56 constexpr ALCchar opensl_device[] = "OpenSL";
59 SLuint32 GetChannelMask(DevFmtChannels chans)
61 switch(chans)
63 case DevFmtMono: return SL_SPEAKER_FRONT_CENTER;
64 case DevFmtStereo: return SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT;
65 case DevFmtQuad: return SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT |
66 SL_SPEAKER_BACK_LEFT | SL_SPEAKER_BACK_RIGHT;
67 case DevFmtX51: return SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT |
68 SL_SPEAKER_FRONT_CENTER | SL_SPEAKER_LOW_FREQUENCY | SL_SPEAKER_SIDE_LEFT |
69 SL_SPEAKER_SIDE_RIGHT;
70 case DevFmtX51Rear: return SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT |
71 SL_SPEAKER_FRONT_CENTER | SL_SPEAKER_LOW_FREQUENCY | SL_SPEAKER_BACK_LEFT |
72 SL_SPEAKER_BACK_RIGHT;
73 case DevFmtX61: return SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT |
74 SL_SPEAKER_FRONT_CENTER | SL_SPEAKER_LOW_FREQUENCY | SL_SPEAKER_BACK_CENTER |
75 SL_SPEAKER_SIDE_LEFT | SL_SPEAKER_SIDE_RIGHT;
76 case DevFmtX71: return SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT |
77 SL_SPEAKER_FRONT_CENTER | SL_SPEAKER_LOW_FREQUENCY | SL_SPEAKER_BACK_LEFT |
78 SL_SPEAKER_BACK_RIGHT | SL_SPEAKER_SIDE_LEFT | SL_SPEAKER_SIDE_RIGHT;
79 case DevFmtAmbi3D:
80 break;
82 return 0;
85 #ifdef SL_ANDROID_DATAFORMAT_PCM_EX
86 SLuint32 GetTypeRepresentation(DevFmtType type)
88 switch(type)
90 case DevFmtUByte:
91 case DevFmtUShort:
92 case DevFmtUInt:
93 return SL_ANDROID_PCM_REPRESENTATION_UNSIGNED_INT;
94 case DevFmtByte:
95 case DevFmtShort:
96 case DevFmtInt:
97 return SL_ANDROID_PCM_REPRESENTATION_SIGNED_INT;
98 case DevFmtFloat:
99 return SL_ANDROID_PCM_REPRESENTATION_FLOAT;
101 return 0;
103 #endif
105 const char *res_str(SLresult result)
107 switch(result)
109 case SL_RESULT_SUCCESS: return "Success";
110 case SL_RESULT_PRECONDITIONS_VIOLATED: return "Preconditions violated";
111 case SL_RESULT_PARAMETER_INVALID: return "Parameter invalid";
112 case SL_RESULT_MEMORY_FAILURE: return "Memory failure";
113 case SL_RESULT_RESOURCE_ERROR: return "Resource error";
114 case SL_RESULT_RESOURCE_LOST: return "Resource lost";
115 case SL_RESULT_IO_ERROR: return "I/O error";
116 case SL_RESULT_BUFFER_INSUFFICIENT: return "Buffer insufficient";
117 case SL_RESULT_CONTENT_CORRUPTED: return "Content corrupted";
118 case SL_RESULT_CONTENT_UNSUPPORTED: return "Content unsupported";
119 case SL_RESULT_CONTENT_NOT_FOUND: return "Content not found";
120 case SL_RESULT_PERMISSION_DENIED: return "Permission denied";
121 case SL_RESULT_FEATURE_UNSUPPORTED: return "Feature unsupported";
122 case SL_RESULT_INTERNAL_ERROR: return "Internal error";
123 case SL_RESULT_UNKNOWN_ERROR: return "Unknown error";
124 case SL_RESULT_OPERATION_ABORTED: return "Operation aborted";
125 case SL_RESULT_CONTROL_LOST: return "Control lost";
126 #ifdef SL_RESULT_READONLY
127 case SL_RESULT_READONLY: return "ReadOnly";
128 #endif
129 #ifdef SL_RESULT_ENGINEOPTION_UNSUPPORTED
130 case SL_RESULT_ENGINEOPTION_UNSUPPORTED: return "Engine option unsupported";
131 #endif
132 #ifdef SL_RESULT_SOURCE_SINK_INCOMPATIBLE
133 case SL_RESULT_SOURCE_SINK_INCOMPATIBLE: return "Source/Sink incompatible";
134 #endif
136 return "Unknown error code";
139 #define PRINTERR(x, s) do { \
140 if UNLIKELY((x) != SL_RESULT_SUCCESS) \
141 ERR("%s: %s\n", (s), res_str((x))); \
142 } while(0)
145 struct OpenSLPlayback final : public BackendBase {
146 OpenSLPlayback(ALCdevice *device) noexcept : BackendBase{device} { }
147 ~OpenSLPlayback() override;
149 static void processC(SLAndroidSimpleBufferQueueItf bq, void *context)
150 { static_cast<OpenSLPlayback*>(context)->process(bq); }
151 void process(SLAndroidSimpleBufferQueueItf bq);
153 int mixerProc();
155 void open(const ALCchar *name) override;
156 bool reset() override;
157 bool start() override;
158 void stop() override;
159 ClockLatency getClockLatency() override;
161 /* engine interfaces */
162 SLObjectItf mEngineObj{nullptr};
163 SLEngineItf mEngine{nullptr};
165 /* output mix interfaces */
166 SLObjectItf mOutputMix{nullptr};
168 /* buffer queue player interfaces */
169 SLObjectItf mBufferQueueObj{nullptr};
171 RingBufferPtr mRing{nullptr};
172 al::semaphore mSem;
174 ALuint mFrameSize{0};
176 std::atomic<bool> mKillNow{true};
177 std::thread mThread;
179 DEF_NEWDEL(OpenSLPlayback)
182 OpenSLPlayback::~OpenSLPlayback()
184 if(mBufferQueueObj)
185 VCALL0(mBufferQueueObj,Destroy)();
186 mBufferQueueObj = nullptr;
188 if(mOutputMix)
189 VCALL0(mOutputMix,Destroy)();
190 mOutputMix = nullptr;
192 if(mEngineObj)
193 VCALL0(mEngineObj,Destroy)();
194 mEngineObj = nullptr;
195 mEngine = nullptr;
199 /* this callback handler is called every time a buffer finishes playing */
200 void OpenSLPlayback::process(SLAndroidSimpleBufferQueueItf)
202 /* A note on the ringbuffer usage: The buffer queue seems to hold on to the
203 * pointer passed to the Enqueue method, rather than copying the audio.
204 * Consequently, the ringbuffer contains the audio that is currently queued
205 * and waiting to play. This process() callback is called when a buffer is
206 * finished, so we simply move the read pointer up to indicate the space is
207 * available for writing again, and wake up the mixer thread to mix and
208 * queue more audio.
210 mRing->readAdvance(1);
212 mSem.post();
215 int OpenSLPlayback::mixerProc()
217 SetRTPriority();
218 althrd_setname(MIXER_THREAD_NAME);
220 SLPlayItf player;
221 SLAndroidSimpleBufferQueueItf bufferQueue;
222 SLresult result{VCALL(mBufferQueueObj,GetInterface)(SL_IID_ANDROIDSIMPLEBUFFERQUEUE,
223 &bufferQueue)};
224 PRINTERR(result, "bufferQueue->GetInterface SL_IID_ANDROIDSIMPLEBUFFERQUEUE");
225 if(SL_RESULT_SUCCESS == result)
227 result = VCALL(mBufferQueueObj,GetInterface)(SL_IID_PLAY, &player);
228 PRINTERR(result, "bufferQueue->GetInterface SL_IID_PLAY");
231 lock();
232 if(SL_RESULT_SUCCESS != result)
233 aluHandleDisconnect(mDevice, "Failed to get playback buffer: 0x%08x", result);
235 while(SL_RESULT_SUCCESS == result && !mKillNow.load(std::memory_order_acquire) &&
236 mDevice->Connected.load(std::memory_order_acquire))
238 if(mRing->writeSpace() == 0)
240 SLuint32 state{0};
242 result = VCALL(player,GetPlayState)(&state);
243 PRINTERR(result, "player->GetPlayState");
244 if(SL_RESULT_SUCCESS == result && state != SL_PLAYSTATE_PLAYING)
246 result = VCALL(player,SetPlayState)(SL_PLAYSTATE_PLAYING);
247 PRINTERR(result, "player->SetPlayState");
249 if(SL_RESULT_SUCCESS != result)
251 aluHandleDisconnect(mDevice, "Failed to start platback: 0x%08x", result);
252 break;
255 if(mRing->writeSpace() == 0)
257 unlock();
258 mSem.wait();
259 lock();
260 continue;
264 auto data = mRing->getWriteVector();
265 aluMixData(mDevice, data.first.buf,
266 static_cast<ALuint>(data.first.len*mDevice->UpdateSize));
267 if(data.second.len > 0)
268 aluMixData(mDevice, data.second.buf,
269 static_cast<ALuint>(data.second.len*mDevice->UpdateSize));
271 size_t todo{data.first.len + data.second.len};
272 mRing->writeAdvance(todo);
274 for(size_t i{0};i < todo;i++)
276 if(!data.first.len)
278 data.first = data.second;
279 data.second.buf = nullptr;
280 data.second.len = 0;
283 result = VCALL(bufferQueue,Enqueue)(data.first.buf, mDevice->UpdateSize*mFrameSize);
284 PRINTERR(result, "bufferQueue->Enqueue");
285 if(SL_RESULT_SUCCESS != result)
287 aluHandleDisconnect(mDevice, "Failed to queue audio: 0x%08x", result);
288 break;
291 data.first.len--;
292 data.first.buf += mDevice->UpdateSize*mFrameSize;
295 unlock();
297 return 0;
301 void OpenSLPlayback::open(const ALCchar *name)
303 if(!name)
304 name = opensl_device;
305 else if(strcmp(name, opensl_device) != 0)
306 throw al::backend_exception{ALC_INVALID_VALUE, "Device name \"%s\" not found", name};
308 // create engine
309 SLresult result{slCreateEngine(&mEngineObj, 0, nullptr, 0, nullptr, nullptr)};
310 PRINTERR(result, "slCreateEngine");
311 if(SL_RESULT_SUCCESS == result)
313 result = VCALL(mEngineObj,Realize)(SL_BOOLEAN_FALSE);
314 PRINTERR(result, "engine->Realize");
316 if(SL_RESULT_SUCCESS == result)
318 result = VCALL(mEngineObj,GetInterface)(SL_IID_ENGINE, &mEngine);
319 PRINTERR(result, "engine->GetInterface");
321 if(SL_RESULT_SUCCESS == result)
323 result = VCALL(mEngine,CreateOutputMix)(&mOutputMix, 0, nullptr, nullptr);
324 PRINTERR(result, "engine->CreateOutputMix");
326 if(SL_RESULT_SUCCESS == result)
328 result = VCALL(mOutputMix,Realize)(SL_BOOLEAN_FALSE);
329 PRINTERR(result, "outputMix->Realize");
332 if(SL_RESULT_SUCCESS != result)
334 if(mOutputMix)
335 VCALL0(mOutputMix,Destroy)();
336 mOutputMix = nullptr;
338 if(mEngineObj)
339 VCALL0(mEngineObj,Destroy)();
340 mEngineObj = nullptr;
341 mEngine = nullptr;
343 throw al::backend_exception{ALC_INVALID_VALUE, "Failed to initialize OpenSL: 0x%08x",
344 result};
347 mDevice->DeviceName = name;
350 bool OpenSLPlayback::reset()
352 SLresult result;
354 if(mBufferQueueObj)
355 VCALL0(mBufferQueueObj,Destroy)();
356 mBufferQueueObj = nullptr;
358 mRing = nullptr;
360 #if 0
361 if(!mDevice->Flags.get<FrequencyRequest>())
363 /* FIXME: Disabled until I figure out how to get the Context needed for
364 * the getSystemService call.
366 JNIEnv *env = Android_GetJNIEnv();
367 jobject jctx = Android_GetContext();
369 /* Get necessary stuff for using java.lang.Integer,
370 * android.content.Context, and android.media.AudioManager.
372 jclass int_cls = JCALL(env,FindClass)("java/lang/Integer");
373 jmethodID int_parseint = JCALL(env,GetStaticMethodID)(int_cls,
374 "parseInt", "(Ljava/lang/String;)I"
376 TRACE("Integer: %p, parseInt: %p\n", int_cls, int_parseint);
378 jclass ctx_cls = JCALL(env,FindClass)("android/content/Context");
379 jfieldID ctx_audsvc = JCALL(env,GetStaticFieldID)(ctx_cls,
380 "AUDIO_SERVICE", "Ljava/lang/String;"
382 jmethodID ctx_getSysSvc = JCALL(env,GetMethodID)(ctx_cls,
383 "getSystemService", "(Ljava/lang/String;)Ljava/lang/Object;"
385 TRACE("Context: %p, AUDIO_SERVICE: %p, getSystemService: %p\n",
386 ctx_cls, ctx_audsvc, ctx_getSysSvc);
388 jclass audmgr_cls = JCALL(env,FindClass)("android/media/AudioManager");
389 jfieldID audmgr_prop_out_srate = JCALL(env,GetStaticFieldID)(audmgr_cls,
390 "PROPERTY_OUTPUT_SAMPLE_RATE", "Ljava/lang/String;"
392 jmethodID audmgr_getproperty = JCALL(env,GetMethodID)(audmgr_cls,
393 "getProperty", "(Ljava/lang/String;)Ljava/lang/String;"
395 TRACE("AudioManager: %p, PROPERTY_OUTPUT_SAMPLE_RATE: %p, getProperty: %p\n",
396 audmgr_cls, audmgr_prop_out_srate, audmgr_getproperty);
398 const char *strchars;
399 jstring strobj;
401 /* Now make the calls. */
402 //AudioManager audMgr = (AudioManager)getSystemService(Context.AUDIO_SERVICE);
403 strobj = JCALL(env,GetStaticObjectField)(ctx_cls, ctx_audsvc);
404 jobject audMgr = JCALL(env,CallObjectMethod)(jctx, ctx_getSysSvc, strobj);
405 strchars = JCALL(env,GetStringUTFChars)(strobj, nullptr);
406 TRACE("Context.getSystemService(%s) = %p\n", strchars, audMgr);
407 JCALL(env,ReleaseStringUTFChars)(strobj, strchars);
409 //String srateStr = audMgr.getProperty(AudioManager.PROPERTY_OUTPUT_SAMPLE_RATE);
410 strobj = JCALL(env,GetStaticObjectField)(audmgr_cls, audmgr_prop_out_srate);
411 jstring srateStr = JCALL(env,CallObjectMethod)(audMgr, audmgr_getproperty, strobj);
412 strchars = JCALL(env,GetStringUTFChars)(strobj, nullptr);
413 TRACE("audMgr.getProperty(%s) = %p\n", strchars, srateStr);
414 JCALL(env,ReleaseStringUTFChars)(strobj, strchars);
416 //int sampleRate = Integer.parseInt(srateStr);
417 sampleRate = JCALL(env,CallStaticIntMethod)(int_cls, int_parseint, srateStr);
419 strchars = JCALL(env,GetStringUTFChars)(srateStr, nullptr);
420 TRACE("Got system sample rate %uhz (%s)\n", sampleRate, strchars);
421 JCALL(env,ReleaseStringUTFChars)(srateStr, strchars);
423 if(!sampleRate) sampleRate = device->Frequency;
424 else sampleRate = maxu(sampleRate, MIN_OUTPUT_RATE);
426 #endif
428 mDevice->FmtChans = DevFmtStereo;
429 mDevice->FmtType = DevFmtShort;
431 SetDefaultWFXChannelOrder(mDevice);
432 mFrameSize = mDevice->frameSizeFromFmt();
435 const std::array<SLInterfaceID,2> ids{{ SL_IID_ANDROIDSIMPLEBUFFERQUEUE, SL_IID_ANDROIDCONFIGURATION }};
436 const std::array<SLboolean,2> reqs{{ SL_BOOLEAN_TRUE, SL_BOOLEAN_FALSE }};
438 SLDataLocator_OutputMix loc_outmix{};
439 loc_outmix.locatorType = SL_DATALOCATOR_OUTPUTMIX;
440 loc_outmix.outputMix = mOutputMix;
442 SLDataSink audioSnk{};
443 audioSnk.pLocator = &loc_outmix;
444 audioSnk.pFormat = nullptr;
446 SLDataLocator_AndroidSimpleBufferQueue loc_bufq{};
447 loc_bufq.locatorType = SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE;
448 loc_bufq.numBuffers = mDevice->BufferSize / mDevice->UpdateSize;
450 SLDataSource audioSrc{};
451 #ifdef SL_ANDROID_DATAFORMAT_PCM_EX
452 SLAndroidDataFormat_PCM_EX format_pcm_ex{};
453 format_pcm_ex.formatType = SL_ANDROID_DATAFORMAT_PCM_EX;
454 format_pcm_ex.numChannels = mDevice->channelsFromFmt();
455 format_pcm_ex.sampleRate = mDevice->Frequency * 1000;
456 format_pcm_ex.bitsPerSample = mDevice->bytesFromFmt() * 8;
457 format_pcm_ex.containerSize = format_pcm_ex.bitsPerSample;
458 format_pcm_ex.channelMask = GetChannelMask(mDevice->FmtChans);
459 format_pcm_ex.endianness = IS_LITTLE_ENDIAN ? SL_BYTEORDER_LITTLEENDIAN : SL_BYTEORDER_BIGENDIAN;
460 format_pcm_ex.representation = GetTypeRepresentation(mDevice->FmtType);
462 audioSrc.pLocator = &loc_bufq;
463 audioSrc.pFormat = &format_pcm_ex;
465 result = VCALL(mEngine,CreateAudioPlayer)(&mBufferQueueObj, &audioSrc, &audioSnk, ids.size(),
466 ids.data(), reqs.data());
467 if(SL_RESULT_SUCCESS != result)
468 #endif
470 /* Alter sample type according to what SLDataFormat_PCM can support. */
471 switch(mDevice->FmtType)
473 case DevFmtByte: mDevice->FmtType = DevFmtUByte; break;
474 case DevFmtUInt: mDevice->FmtType = DevFmtInt; break;
475 case DevFmtFloat:
476 case DevFmtUShort: mDevice->FmtType = DevFmtShort; break;
477 case DevFmtUByte:
478 case DevFmtShort:
479 case DevFmtInt:
480 break;
483 SLDataFormat_PCM format_pcm{};
484 format_pcm.formatType = SL_DATAFORMAT_PCM;
485 format_pcm.numChannels = mDevice->channelsFromFmt();
486 format_pcm.samplesPerSec = mDevice->Frequency * 1000;
487 format_pcm.bitsPerSample = mDevice->bytesFromFmt() * 8;
488 format_pcm.containerSize = format_pcm.bitsPerSample;
489 format_pcm.channelMask = GetChannelMask(mDevice->FmtChans);
490 format_pcm.endianness = IS_LITTLE_ENDIAN ? SL_BYTEORDER_LITTLEENDIAN :
491 SL_BYTEORDER_BIGENDIAN;
493 audioSrc.pLocator = &loc_bufq;
494 audioSrc.pFormat = &format_pcm;
496 result = VCALL(mEngine,CreateAudioPlayer)(&mBufferQueueObj, &audioSrc, &audioSnk, ids.size(),
497 ids.data(), reqs.data());
498 PRINTERR(result, "engine->CreateAudioPlayer");
500 if(SL_RESULT_SUCCESS == result)
502 /* Set the stream type to "media" (games, music, etc), if possible. */
503 SLAndroidConfigurationItf config;
504 result = VCALL(mBufferQueueObj,GetInterface)(SL_IID_ANDROIDCONFIGURATION, &config);
505 PRINTERR(result, "bufferQueue->GetInterface SL_IID_ANDROIDCONFIGURATION");
506 if(SL_RESULT_SUCCESS == result)
508 SLint32 streamType = SL_ANDROID_STREAM_MEDIA;
509 result = VCALL(config,SetConfiguration)(SL_ANDROID_KEY_STREAM_TYPE, &streamType,
510 sizeof(streamType));
511 PRINTERR(result, "config->SetConfiguration");
514 /* Clear any error since this was optional. */
515 result = SL_RESULT_SUCCESS;
517 if(SL_RESULT_SUCCESS == result)
519 result = VCALL(mBufferQueueObj,Realize)(SL_BOOLEAN_FALSE);
520 PRINTERR(result, "bufferQueue->Realize");
522 if(SL_RESULT_SUCCESS == result)
524 const ALuint num_updates{mDevice->BufferSize / mDevice->UpdateSize};
525 try {
526 mRing = CreateRingBuffer(num_updates, mFrameSize*mDevice->UpdateSize, true);
528 catch(std::exception& e) {
529 ERR("Failed allocating ring buffer %ux%ux%u: %s\n", mDevice->UpdateSize,
530 num_updates, mFrameSize, e.what());
531 result = SL_RESULT_MEMORY_FAILURE;
535 if(SL_RESULT_SUCCESS != result)
537 if(mBufferQueueObj)
538 VCALL0(mBufferQueueObj,Destroy)();
539 mBufferQueueObj = nullptr;
541 return false;
544 return true;
547 bool OpenSLPlayback::start()
549 mRing->reset();
551 SLAndroidSimpleBufferQueueItf bufferQueue;
552 SLresult result{VCALL(mBufferQueueObj,GetInterface)(SL_IID_ANDROIDSIMPLEBUFFERQUEUE,
553 &bufferQueue)};
554 PRINTERR(result, "bufferQueue->GetInterface");
555 if(SL_RESULT_SUCCESS != result)
556 return false;
558 result = VCALL(bufferQueue,RegisterCallback)(&OpenSLPlayback::processC, this);
559 PRINTERR(result, "bufferQueue->RegisterCallback");
560 if(SL_RESULT_SUCCESS != result) return false;
562 try {
563 mKillNow.store(false, std::memory_order_release);
564 mThread = std::thread(std::mem_fn(&OpenSLPlayback::mixerProc), this);
565 return true;
567 catch(std::exception& e) {
568 ERR("Could not create playback thread: %s\n", e.what());
570 catch(...) {
572 return false;
575 void OpenSLPlayback::stop()
577 if(mKillNow.exchange(true, std::memory_order_acq_rel) || !mThread.joinable())
578 return;
580 mSem.post();
581 mThread.join();
583 SLPlayItf player;
584 SLresult result{VCALL(mBufferQueueObj,GetInterface)(SL_IID_PLAY, &player)};
585 PRINTERR(result, "bufferQueue->GetInterface");
586 if(SL_RESULT_SUCCESS == result)
588 result = VCALL(player,SetPlayState)(SL_PLAYSTATE_STOPPED);
589 PRINTERR(result, "player->SetPlayState");
592 SLAndroidSimpleBufferQueueItf bufferQueue;
593 result = VCALL(mBufferQueueObj,GetInterface)(SL_IID_ANDROIDSIMPLEBUFFERQUEUE, &bufferQueue);
594 PRINTERR(result, "bufferQueue->GetInterface");
595 if(SL_RESULT_SUCCESS == result)
597 result = VCALL0(bufferQueue,Clear)();
598 PRINTERR(result, "bufferQueue->Clear");
600 if(SL_RESULT_SUCCESS == result)
602 result = VCALL(bufferQueue,RegisterCallback)(nullptr, nullptr);
603 PRINTERR(result, "bufferQueue->RegisterCallback");
605 if(SL_RESULT_SUCCESS == result)
607 SLAndroidSimpleBufferQueueState state;
608 do {
609 std::this_thread::yield();
610 result = VCALL(bufferQueue,GetState)(&state);
611 } while(SL_RESULT_SUCCESS == result && state.count > 0);
612 PRINTERR(result, "bufferQueue->GetState");
616 ClockLatency OpenSLPlayback::getClockLatency()
618 ClockLatency ret;
620 lock();
621 ret.ClockTime = GetDeviceClockTime(mDevice);
622 ret.Latency = std::chrono::seconds{mRing->readSpace() * mDevice->UpdateSize};
623 ret.Latency /= mDevice->Frequency;
624 unlock();
626 return ret;
630 struct OpenSLCapture final : public BackendBase {
631 OpenSLCapture(ALCdevice *device) noexcept : BackendBase{device} { }
632 ~OpenSLCapture() override;
634 static void processC(SLAndroidSimpleBufferQueueItf bq, void *context)
635 { static_cast<OpenSLCapture*>(context)->process(bq); }
636 void process(SLAndroidSimpleBufferQueueItf bq);
638 void open(const ALCchar *name) override;
639 bool start() override;
640 void stop() override;
641 ALCenum captureSamples(al::byte *buffer, ALCuint samples) override;
642 ALCuint availableSamples() override;
644 /* engine interfaces */
645 SLObjectItf mEngineObj{nullptr};
646 SLEngineItf mEngine;
648 /* recording interfaces */
649 SLObjectItf mRecordObj{nullptr};
651 RingBufferPtr mRing{nullptr};
652 ALCuint mSplOffset{0u};
654 ALuint mFrameSize{0};
656 DEF_NEWDEL(OpenSLCapture)
659 OpenSLCapture::~OpenSLCapture()
661 if(mRecordObj)
662 VCALL0(mRecordObj,Destroy)();
663 mRecordObj = nullptr;
665 if(mEngineObj)
666 VCALL0(mEngineObj,Destroy)();
667 mEngineObj = nullptr;
668 mEngine = nullptr;
672 void OpenSLCapture::process(SLAndroidSimpleBufferQueueItf)
674 /* A new chunk has been written into the ring buffer, advance it. */
675 mRing->writeAdvance(1);
679 void OpenSLCapture::open(const ALCchar* name)
681 if(!name)
682 name = opensl_device;
683 else if(strcmp(name, opensl_device) != 0)
684 throw al::backend_exception{ALC_INVALID_VALUE, "Device name \"%s\" not found", name};
686 SLresult result{slCreateEngine(&mEngineObj, 0, nullptr, 0, nullptr, nullptr)};
687 PRINTERR(result, "slCreateEngine");
688 if(SL_RESULT_SUCCESS == result)
690 result = VCALL(mEngineObj,Realize)(SL_BOOLEAN_FALSE);
691 PRINTERR(result, "engine->Realize");
693 if(SL_RESULT_SUCCESS == result)
695 result = VCALL(mEngineObj,GetInterface)(SL_IID_ENGINE, &mEngine);
696 PRINTERR(result, "engine->GetInterface");
698 if(SL_RESULT_SUCCESS == result)
700 mFrameSize = mDevice->frameSizeFromFmt();
701 /* Ensure the total length is at least 100ms */
702 ALuint length{maxu(mDevice->BufferSize, mDevice->Frequency/10)};
703 /* Ensure the per-chunk length is at least 10ms, and no more than 50ms. */
704 ALuint update_len{clampu(mDevice->BufferSize/3, mDevice->Frequency/100,
705 mDevice->Frequency/100*5)};
706 ALuint num_updates{(length+update_len-1) / update_len};
708 try {
709 mRing = CreateRingBuffer(num_updates, update_len*mFrameSize, false);
711 mDevice->UpdateSize = update_len;
712 mDevice->BufferSize = static_cast<ALuint>(mRing->writeSpace() * update_len);
714 catch(std::exception& e) {
715 ERR("Failed to allocate ring buffer: %s\n", e.what());
716 result = SL_RESULT_MEMORY_FAILURE;
719 if(SL_RESULT_SUCCESS == result)
721 const std::array<SLInterfaceID,2> ids{{ SL_IID_ANDROIDSIMPLEBUFFERQUEUE, SL_IID_ANDROIDCONFIGURATION }};
722 const std::array<SLboolean,2> reqs{{ SL_BOOLEAN_TRUE, SL_BOOLEAN_FALSE }};
724 SLDataLocator_IODevice loc_dev{};
725 loc_dev.locatorType = SL_DATALOCATOR_IODEVICE;
726 loc_dev.deviceType = SL_IODEVICE_AUDIOINPUT;
727 loc_dev.deviceID = SL_DEFAULTDEVICEID_AUDIOINPUT;
728 loc_dev.device = nullptr;
730 SLDataSource audioSrc{};
731 audioSrc.pLocator = &loc_dev;
732 audioSrc.pFormat = nullptr;
734 SLDataLocator_AndroidSimpleBufferQueue loc_bq{};
735 loc_bq.locatorType = SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE;
736 loc_bq.numBuffers = mDevice->BufferSize / mDevice->UpdateSize;
738 SLDataSink audioSnk{};
739 #ifdef SL_ANDROID_DATAFORMAT_PCM_EX
740 SLAndroidDataFormat_PCM_EX format_pcm_ex{};
741 format_pcm_ex.formatType = SL_ANDROID_DATAFORMAT_PCM_EX;
742 format_pcm_ex.numChannels = mDevice->channelsFromFmt();
743 format_pcm_ex.sampleRate = mDevice->Frequency * 1000;
744 format_pcm_ex.bitsPerSample = mDevice->bytesFromFmt() * 8;
745 format_pcm_ex.containerSize = format_pcm_ex.bitsPerSample;
746 format_pcm_ex.channelMask = GetChannelMask(mDevice->FmtChans);
747 format_pcm_ex.endianness = IS_LITTLE_ENDIAN ? SL_BYTEORDER_LITTLEENDIAN :
748 SL_BYTEORDER_BIGENDIAN;
749 format_pcm_ex.representation = GetTypeRepresentation(mDevice->FmtType);
751 audioSnk.pLocator = &loc_bq;
752 audioSnk.pFormat = &format_pcm_ex;
753 result = VCALL(mEngine,CreateAudioRecorder)(&mRecordObj, &audioSrc, &audioSnk,
754 ids.size(), ids.data(), reqs.data());
755 if(SL_RESULT_SUCCESS != result)
756 #endif
758 /* Fallback to SLDataFormat_PCM only if it supports the desired
759 * sample type.
761 if(mDevice->FmtType == DevFmtUByte || mDevice->FmtType == DevFmtShort
762 || mDevice->FmtType == DevFmtInt)
764 SLDataFormat_PCM format_pcm{};
765 format_pcm.formatType = SL_DATAFORMAT_PCM;
766 format_pcm.numChannels = mDevice->channelsFromFmt();
767 format_pcm.samplesPerSec = mDevice->Frequency * 1000;
768 format_pcm.bitsPerSample = mDevice->bytesFromFmt() * 8;
769 format_pcm.containerSize = format_pcm.bitsPerSample;
770 format_pcm.channelMask = GetChannelMask(mDevice->FmtChans);
771 format_pcm.endianness = IS_LITTLE_ENDIAN ? SL_BYTEORDER_LITTLEENDIAN :
772 SL_BYTEORDER_BIGENDIAN;
774 audioSnk.pLocator = &loc_bq;
775 audioSnk.pFormat = &format_pcm;
776 result = VCALL(mEngine,CreateAudioRecorder)(&mRecordObj, &audioSrc, &audioSnk,
777 ids.size(), ids.data(), reqs.data());
779 PRINTERR(result, "engine->CreateAudioRecorder");
782 if(SL_RESULT_SUCCESS == result)
784 /* Set the record preset to "generic", if possible. */
785 SLAndroidConfigurationItf config;
786 result = VCALL(mRecordObj,GetInterface)(SL_IID_ANDROIDCONFIGURATION, &config);
787 PRINTERR(result, "recordObj->GetInterface SL_IID_ANDROIDCONFIGURATION");
788 if(SL_RESULT_SUCCESS == result)
790 SLuint32 preset = SL_ANDROID_RECORDING_PRESET_GENERIC;
791 result = VCALL(config,SetConfiguration)(SL_ANDROID_KEY_RECORDING_PRESET, &preset,
792 sizeof(preset));
793 PRINTERR(result, "config->SetConfiguration");
796 /* Clear any error since this was optional. */
797 result = SL_RESULT_SUCCESS;
799 if(SL_RESULT_SUCCESS == result)
801 result = VCALL(mRecordObj,Realize)(SL_BOOLEAN_FALSE);
802 PRINTERR(result, "recordObj->Realize");
805 SLAndroidSimpleBufferQueueItf bufferQueue;
806 if(SL_RESULT_SUCCESS == result)
808 result = VCALL(mRecordObj,GetInterface)(SL_IID_ANDROIDSIMPLEBUFFERQUEUE, &bufferQueue);
809 PRINTERR(result, "recordObj->GetInterface");
811 if(SL_RESULT_SUCCESS == result)
813 result = VCALL(bufferQueue,RegisterCallback)(&OpenSLCapture::processC, this);
814 PRINTERR(result, "bufferQueue->RegisterCallback");
816 if(SL_RESULT_SUCCESS == result)
818 const ALuint chunk_size{mDevice->UpdateSize * mFrameSize};
820 auto data = mRing->getWriteVector();
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{ALC_INVALID_VALUE, "Failed to initialize OpenSL: 0x%08x",
845 result};
848 mDevice->DeviceName = name;
851 bool 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");
863 if(SL_RESULT_SUCCESS != result)
865 aluHandleDisconnect(mDevice, "Failed to start capture: 0x%08x", result);
866 return false;
869 return true;
872 void OpenSLCapture::stop()
874 SLRecordItf record;
875 SLresult result{VCALL(mRecordObj,GetInterface)(SL_IID_RECORD, &record)};
876 PRINTERR(result, "recordObj->GetInterface");
878 if(SL_RESULT_SUCCESS == result)
880 result = VCALL(record,SetRecordState)(SL_RECORDSTATE_PAUSED);
881 PRINTERR(result, "record->SetRecordState");
885 ALCenum OpenSLCapture::captureSamples(al::byte *buffer, ALCuint samples)
887 SLAndroidSimpleBufferQueueItf bufferQueue;
888 SLresult result{VCALL(mRecordObj,GetInterface)(SL_IID_ANDROIDSIMPLEBUFFERQUEUE, &bufferQueue)};
889 PRINTERR(result, "recordObj->GetInterface");
891 const ALuint update_size{mDevice->UpdateSize};
892 const ALuint chunk_size{update_size * mFrameSize};
894 /* Read the desired samples from the ring buffer then advance its read
895 * pointer.
897 auto data = mRing->getReadVector();
898 for(ALCuint i{0};i < samples;)
900 const ALCuint rem{minu(samples - i, update_size - mSplOffset)};
901 std::copy_n(data.first.buf + mSplOffset*mFrameSize, rem*mFrameSize, buffer + i*mFrameSize);
903 mSplOffset += rem;
904 if(mSplOffset == update_size)
906 /* Finished a chunk, reset the offset and advance the read pointer. */
907 mSplOffset = 0;
909 mRing->readAdvance(1);
910 result = VCALL(bufferQueue,Enqueue)(data.first.buf, chunk_size);
911 PRINTERR(result, "bufferQueue->Enqueue");
912 if(SL_RESULT_SUCCESS != result) break;
914 data.first.len--;
915 if(!data.first.len)
916 data.first = data.second;
917 else
918 data.first.buf += chunk_size;
921 i += rem;
924 if UNLIKELY(SL_RESULT_SUCCESS != result)
926 aluHandleDisconnect(mDevice, "Failed to update capture buffer: 0x%08x", result);
927 return ALC_INVALID_DEVICE;
930 return ALC_NO_ERROR;
933 ALCuint OpenSLCapture::availableSamples()
934 { return static_cast<ALuint>(mRing->readSpace()*mDevice->UpdateSize - mSplOffset); }
936 } // namespace
938 bool OSLBackendFactory::init() { return true; }
940 bool OSLBackendFactory::querySupport(BackendType type)
941 { return (type == BackendType::Playback || type == BackendType::Capture); }
943 void OSLBackendFactory::probe(DevProbe type, std::string *outnames)
945 switch(type)
947 case DevProbe::Playback:
948 case DevProbe::Capture:
949 /* Includes null char. */
950 outnames->append(opensl_device, sizeof(opensl_device));
951 break;
955 BackendPtr OSLBackendFactory::createBackend(ALCdevice *device, BackendType type)
957 if(type == BackendType::Playback)
958 return BackendPtr{new OpenSLPlayback{device}};
959 if(type == BackendType::Capture)
960 return BackendPtr{new OpenSLCapture{device}};
961 return nullptr;
964 BackendFactory &OSLBackendFactory::getFactory()
966 static OSLBackendFactory factory{};
967 return factory;