7#if QT_VERSION < QT_VERSION_CHECK(6,0,0)
8#include <QAndroidJniEnvironment>
10#include <QJniEnvironment>
12#define QAndroidJniEnvironment QJniEnvironment
13#define QAndroidJniObject QJniObject
20#define LOC QString("AOSLES: ")
29#define CHECK_OPENSL_ERROR(msg) \
30 if (result != SL_RESULT_SUCCESS) \
32 LOG(VB_GENERAL, LOG_ERR, LOC + QString("Open Error: (%1)").arg(result)); \
37#define CHECK_OPENSL_START_ERROR(msg) \
38 if (result != SL_RESULT_SUCCESS) \
40 LOG(VB_GENERAL, LOG_ERR, LOC + QString("Start Error: (%1)").arg(result)); \
46#define Destroy(a) (*(a))->Destroy(a);
47#define SetPlayState(a, b) (*(a))->SetPlayState(a, b)
48#define RegisterCallback(a, b, c) (*(a))->RegisterCallback(a, b, c)
49#define GetInterface(a, b, c) (*(a))->GetInterface(a, b, c)
50#define Realize(a, b) (*(a))->Realize(a, b)
51#define CreateOutputMix(a, b, c, d, e) (*(a))->CreateOutputMix(a, b, c, d, e)
52#define CreateAudioPlayer(a, b, c, d, e, f, g) \
53 (*(a))->CreateAudioPlayer(a, b, c, d, e, f, g)
54#define Enqueue(a, b, c) (*(a))->Enqueue(a, b, c)
55#define Clear(a) (*(a))->Clear(a)
56#define GetState(a, b) (*(a))->GetState(a, b)
57#define SetPositionUpdatePeriod(a, b) (*(a))->SetPositionUpdatePeriod(a, b)
58#define SetVolumeLevel(a, b) (*(a))->SetVolumeLevel(a, b)
59#define GetVolumeLevel(a, b) (*(a))->GetVolumeLevel(a, b)
60#define SetMute(a, b) (*(a))->SetMute(a, b)
68 jclass cls = jniEnv->FindClass(
"android/media/AudioTrack");
69 jmethodID method = jniEnv->GetStaticMethodID (cls,
"getNativeOutputSampleRate",
"(I)I");
70 int sample_rate = jniEnv->CallStaticIntMethod (cls, method, 3);
74 return QAndroidJniObject::callStaticMethodInt(
"android/media/AudioTrack",
"getNativeOutputSampleRate",
"(I)I", 3);
79int GetNativeOutputFramesPerBuffer(
void)
81#if QT_VERSION < QT_VERSION_CHECK(6,0,0)
84 QJniObject activity = QNativeInterface::QAndroidApplication::context();
87 QAndroidJniObject audioManager = activity.callObjectMethod(
"getSystemService",
"", );
88 jstring sValue = audioManager.callMethod<jstring>(
"getProperty", PROPERTY_OUTPUT_FRAMES_PER_BUFFER);
90 return QAndroidJniObject::callStaticMethodInt(
"android/media/AudioManager",
"getProperty",
"(I)I", 3);
99 LOG(VB_GENERAL, LOG_ERR,
LOC +
"Error: Failed to load libOpenSLES");
107 LOG(VB_GENERAL, LOG_ERR,
LOC +
"Error: Failed to load symbol slCreateEngine");
112#define OPENSL_DLSYM(dest, name) \
114 const SLInterfaceID *sym = (const SLInterfaceID *)dlsym(m_soHandle, "SL_IID_" name); \
115 if (sym == nullptr) \
117 LOG(VB_GENERAL, LOG_ERR, "AOOSLES Error: Failed to load symbol SL_IID_" name); \
145 const std::array<SLboolean,1> req1 { SL_BOOLEAN_FALSE };
147 1, ids1.data(), req1.data());
160 SLDataLocator_AndroidSimpleBufferQueue loc_bufq = {
161 SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE,
165 SLDataFormat_PCM format_pcm;
166 format_pcm.formatType = SL_DATAFORMAT_PCM;
167 format_pcm.numChannels = 2;
168 format_pcm.samplesPerSec = ((SLuint32)
m_sampleRate * 1000) ;
169 format_pcm.endianness = SL_BYTEORDER_LITTLEENDIAN;
172 case FORMAT_U8: format_pcm.bitsPerSample = SL_PCMSAMPLEFORMAT_FIXED_8;
break;
173 case FORMAT_S16: format_pcm.bitsPerSample = SL_PCMSAMPLEFORMAT_FIXED_16;
break;
175 case FORMAT_S24LSB: format_pcm.bitsPerSample = SL_PCMSAMPLEFORMAT_FIXED_24;
break;
177 format_pcm.endianness = SL_BYTEORDER_BIGENDIAN;
178 format_pcm.bitsPerSample = SL_PCMSAMPLEFORMAT_FIXED_24;
180 case FORMAT_S32: format_pcm.bitsPerSample = SL_PCMSAMPLEFORMAT_FIXED_32;
break;
185 QString message {QCoreApplication::translate(
"AudioOutputOpenSLES",
"Unknown sample format: %1")
188 LOG(VB_GENERAL, LOG_ERR, message);
192 format_pcm.containerSize = format_pcm.bitsPerSample;
193 format_pcm.channelMask = SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT;
195 SLDataSource audioSrc = {&loc_bufq, &format_pcm};
198 SLDataLocator_OutputMix loc_outmix = {
199 SL_DATALOCATOR_OUTPUTMIX,
202 SLDataSink audioSnk = {&loc_outmix,
nullptr};
206 static const std::array<SLboolean,2> req2 { SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE };
211 &audioSnk, ids2.size(), ids2.data(), req2.data());
219 result = SL_RESULT_UNKNOWN_ERROR;
221 if (result != SL_RESULT_SUCCESS) {
224 format_pcm.samplesPerSec = ((SLuint32) 48000 * 1000) ;
226 &audioSnk, ids2.size(), ids2.data(), req2.data());
258 }
catch (std::bad_alloc &ex) {
292 m_buf.shrink_to_fit();
352 LOG(VB_GENERAL, LOG_INFO,
LOC + QString(
"Native Rate %1").arg(nativeRate));
354 settings->AddSupportedRate(nativeRate);
366 settings->AddSupportedChannels(2);
368 settings->setPassthrough(0);
388 LOG(VB_AUDIO, LOG_INFO,
LOC + QString(
"Buffering %1 fragments of %2 bytes each, total: %3 bytes")
409 LOG(VB_AUDIO, LOG_INFO,
LOC + QString(
"Freed"));
418 SLAndroidSimpleBufferQueueState st;
420 if (res != SL_RESULT_SUCCESS) {
421 LOG(VB_GENERAL, LOG_ERR,
LOC + QString(
"Could not query buffer queue state in %1 (%2)").arg(__func__).arg(res));
424 LOG(VB_AUDIO, LOG_INFO,
LOC + QString(
"Num Queued %1").arg(st.count));
430 LOG(VB_AUDIO, LOG_INFO,
LOC + QString(
"WriteAudio %1").arg(size));
435 if (numBufferesQueued < 0)
458 if (r != SL_RESULT_SUCCESS)
461 LOG(VB_GENERAL, LOG_ERR,
LOC + QString(
"error %1 when writing %2 bytes %3")
464 .arg((r == SL_RESULT_BUFFER_INSUFFICIENT) ?
" (buffer insufficient)" :
""));
479 if (numBufferesQueued < 0)
491 if (r == SL_RESULT_SUCCESS)
493 if (mb <= SL_MILLIBEL_MIN)
499 volume = lroundf(expf(mb / (3*2000.0F)) * 100);
501 LOG(VB_AUDIO, LOG_INFO,
LOC + QString(
"GetVolume(%1) %2 (%3)")
502 .arg(channel).arg(volume).arg(mb));
506 LOG(VB_GENERAL, LOG_ERR,
LOC + QString(
"GetVolume(%1) %2 (%3) : %4")
507 .arg(channel).arg(volume).arg(mb).arg(r));
516 LOG(VB_GENERAL, LOG_ERR,
LOC +
"Android volume only supports stereo!");
521 float vol = volume / 100.0F;
526 mb = SL_MILLIBEL_MIN;
531 mb = lroundf(3 * 2000.F * log10f(vol));
532 if (mb < SL_MILLIBEL_MIN)
533 mb = SL_MILLIBEL_MIN;
539 if (r == SL_RESULT_SUCCESS)
541 LOG(VB_AUDIO, LOG_INFO,
LOC + QString(
"SetVolume(%1) %2(%3)")
542 .arg(channel).arg(volume).arg(mb));
546 LOG(VB_GENERAL, LOG_ERR,
LOC + QString(
"SetVolume(%1) %2(%3) : %4")
547 .arg(channel).arg(volume).arg(mb).arg(r));
#define QAndroidJniEnvironment
int GetNativeOutputSampleRate(void)
#define SetPositionUpdatePeriod(a, b)
static constexpr size_t OPENSLES_BUFFERS
#define GetInterface(a, b, c)
#define CreateAudioPlayer(a, b, c, d, e, f, g)
#define RegisterCallback(a, b, c)
static constexpr int32_t POSITIONUPDATEPERIOD
#define GetVolumeLevel(a, b)
#define OPENSL_DLSYM(dest, name)
static constexpr std::chrono::milliseconds OPENSLES_BUFLEN
#define CHECK_OPENSL_ERROR(msg)
#define SetVolumeLevel(a, b)
#define QAndroidJniObject
#define SetPlayState(a, b)
#define CreateOutputMix(a, b, c, d, e)
SLresult(*)(SLObjectItf *, SLuint32, const SLEngineOption *, SLuint32, const SLInterfaceID *, const SLboolean *) slCreateEngine_t
void KillAudio(void)
Kill the output thread and cleanup.
void Reconfigure(const AudioSettings &settings) override
(Re)Configure AudioOutputBase
AudioFormat m_outputFormat
int m_outputBytesPerFrame
long m_soundcardBufferSize
void InitSettings(const AudioSettings &settings)
static void SPlayedCallback(SLAndroidSimpleBufferQueueItf caller, void *pContext)
~AudioOutputOpenSLES() override
std::vector< uint8_t > m_buf
SLObjectItf m_engineObject
SLObjectItf m_outputMixObject
void SetVolumeChannel(int channel, int volume) override
void WriteAudio(unsigned char *aubuf, int size) override
slCreateEngine_t m_slCreateEnginePtr
void PlayedCallback(SLAndroidSimpleBufferQueueItf caller)
SLEngineItf m_engineEngine
AudioOutputSettings * GetOutputSettings(bool digital) override
AudioOutputOpenSLES(const AudioSettings &settings)
SLInterfaceID m_slIidEngine
int GetBufferedOnSoundcard(void) const override
Return the size in bytes of frames currently in the audio buffer adjusted with the audio playback lat...
int GetNumberOfBuffersQueued() const
SLAndroidSimpleBufferQueueItf m_playerBufferQueue
SLInterfaceID m_slIidPlay
SLInterfaceID m_slIidVolume
SLInterfaceID m_slIidAndroidSimpleBufferQueue
int GetVolumeChannel(int channel) const override
bool OpenDevice(void) override
SLObjectItf m_playerObject
void CloseDevice(void) override
void dispatchError(const QString &e)
bool m_init
If set to false, AudioOutput instance will not try to initially open the audio device.
#define LOG(_MASK_, _LEVEL_, _QSTRING_)