6#if QT_VERSION < QT_VERSION_CHECK(6,0,0)
7#include <QAndroidJniEnvironment>
9#include <QJniEnvironment>
11#define QAndroidJniEnvironment QJniEnvironment
12#define QAndroidJniObject QJniObject
19#define LOC QString("AOSLES: ")
21#define OPENSLES_BUFFERS 10
25#define POSITIONUPDATEPERIOD 40
28#define CHECK_OPENSL_ERROR(msg) \
29 if (result != SL_RESULT_SUCCESS) \
31 LOG(VB_GENERAL, LOG_ERR, LOC + QString("Open Error: (%1)").arg(result)); \
36#define CHECK_OPENSL_START_ERROR(msg) \
37 if (result != SL_RESULT_SUCCESS) \
39 LOG(VB_GENERAL, LOG_ERR, LOC + QString("Start Error: (%1)").arg(result)); \
45#define Destroy(a) (*(a))->Destroy(a);
46#define SetPlayState(a, b) (*(a))->SetPlayState(a, b)
47#define RegisterCallback(a, b, c) (*(a))->RegisterCallback(a, b, c)
48#define GetInterface(a, b, c) (*(a))->GetInterface(a, b, c)
49#define Realize(a, b) (*(a))->Realize(a, b)
50#define CreateOutputMix(a, b, c, d, e) (*(a))->CreateOutputMix(a, b, c, d, e)
51#define CreateAudioPlayer(a, b, c, d, e, f, g) \
52 (*(a))->CreateAudioPlayer(a, b, c, d, e, f, g)
53#define Enqueue(a, b, c) (*(a))->Enqueue(a, b, c)
54#define Clear(a) (*(a))->Clear(a)
55#define GetState(a, b) (*(a))->GetState(a, b)
56#define SetPositionUpdatePeriod(a, b) (*(a))->SetPositionUpdatePeriod(a, b)
57#define SetVolumeLevel(a, b) (*(a))->SetVolumeLevel(a, b)
58#define GetVolumeLevel(a, b) (*(a))->GetVolumeLevel(a, b)
59#define SetMute(a, b) (*(a))->SetMute(a, b)
67 jclass cls = jniEnv->FindClass(
"android/media/AudioTrack");
68 jmethodID method = jniEnv->GetStaticMethodID (cls,
"getNativeOutputSampleRate",
"(I)I");
69 int sample_rate = jniEnv->CallStaticIntMethod (cls, method, 3);
73 return QAndroidJniObject::callStaticMethodInt(
"android/media/AudioTrack",
"getNativeOutputSampleRate",
"(I)I", 3);
78int GetNativeOutputFramesPerBuffer(
void)
80#if QT_VERSION < QT_VERSION_CHECK(6,0,0)
83 QJniObject activity = QNativeInterface::QAndroidApplication::context();
86 QAndroidJniObject audioManager = activity.callObjectMethod(
"getSystemService",
"", );
87 jstring sValue = audioManager.callMethod<jstring>(
"getProperty", PROPERTY_OUTPUT_FRAMES_PER_BUFFER);
89 return QAndroidJniObject::callStaticMethodInt(
"android/media/AudioManager",
"getProperty",
"(I)I", 3);
100 LOG(VB_GENERAL, LOG_ERR,
LOC +
"Error: Failed to load libOpenSLES");
108 LOG(VB_GENERAL, LOG_ERR,
LOC +
"Error: Failed to load symbol slCreateEngine");
113#define OPENSL_DLSYM(dest, name) \
115 const SLInterfaceID *sym = (const SLInterfaceID *)dlsym(m_so_handle, "SL_IID_" name); \
116 if (sym == nullptr) \
118 LOG(VB_GENERAL, LOG_ERR, "AOOSLES Error: Failed to load symbol SL_IID_" name); \
145 const SLboolean req1[] = { SL_BOOLEAN_FALSE };
161 SLDataLocator_AndroidSimpleBufferQueue loc_bufq = {
162 SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE,
166 SLDataFormat_PCM format_pcm;
167 format_pcm.formatType = SL_DATAFORMAT_PCM;
168 format_pcm.numChannels = 2;
169 format_pcm.samplesPerSec = ((SLuint32)
m_sampleRate * 1000) ;
170 format_pcm.endianness = SL_BYTEORDER_LITTLEENDIAN;
173 case FORMAT_U8: format_pcm.bitsPerSample = SL_PCMSAMPLEFORMAT_FIXED_8;
break;
174 case FORMAT_S16: format_pcm.bitsPerSample = SL_PCMSAMPLEFORMAT_FIXED_16;
break;
176 case FORMAT_S24LSB: format_pcm.bitsPerSample = SL_PCMSAMPLEFORMAT_FIXED_24;
break;
178 format_pcm.endianness = SL_BYTEORDER_BIGENDIAN;
179 format_pcm.bitsPerSample = SL_PCMSAMPLEFORMAT_FIXED_24;
181 case FORMAT_S32: format_pcm.bitsPerSample = SL_PCMSAMPLEFORMAT_FIXED_32;
break;
186 QString message {QCoreApplication::translate(
"AudioOutputOpenSLES",
"Unknown sample format: %1")
189 LOG(VB_GENERAL, LOG_ERR, message);
193 format_pcm.containerSize = format_pcm.bitsPerSample;
194 format_pcm.channelMask = SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT;
196 SLDataSource audioSrc = {&loc_bufq, &format_pcm};
199 SLDataLocator_OutputMix loc_outmix = {
200 SL_DATALOCATOR_OUTPUTMIX,
203 SLDataSink audioSnk = {&loc_outmix,
nullptr};
207 static const SLboolean req2[] = { SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE };
211 &audioSnk,
sizeof(ids2) /
sizeof(*ids2),
220 result = SL_RESULT_UNKNOWN_ERROR;
222 if (result != SL_RESULT_SUCCESS) {
225 format_pcm.samplesPerSec = ((SLuint32) 48000 * 1000) ;
227 &audioSnk,
sizeof(ids2) /
sizeof(*ids2),
356 LOG(VB_GENERAL, LOG_INFO,
LOC + QString(
"Native Rate %1").arg(nativeRate));
392 LOG(VB_AUDIO, LOG_INFO,
LOC + QString(
"Buffering %1 fragments of %2 bytes each, total: %3 bytes")
413 LOG(VB_AUDIO, LOG_INFO,
LOC + QString(
"Freed"));
422 SLAndroidSimpleBufferQueueState st;
424 if (res != SL_RESULT_SUCCESS) {
425 LOG(VB_GENERAL, LOG_ERR,
LOC + QString(
"Could not query buffer queue state in %1 (%2)").arg(__func__).arg(res));
428 LOG(VB_AUDIO, LOG_INFO,
LOC + QString(
"Num Queued %1").arg(st.count));
434 LOG(VB_AUDIO, LOG_INFO,
LOC + QString(
"WriteAudio %1").arg(size));
439 if (numBufferesQueued < 0)
465 if (r != SL_RESULT_SUCCESS)
468 LOG(VB_GENERAL, LOG_ERR,
LOC + QString(
"error %1 when writing %2 bytes %3")
471 .arg((r == SL_RESULT_BUFFER_INSUFFICIENT) ?
" (buffer insufficient)" :
""));
486 if (numBufferesQueued < 0)
498 if (r == SL_RESULT_SUCCESS)
500 if (mb <= SL_MILLIBEL_MIN)
506 volume = lroundf(expf(mb / (3*2000.0F)) * 100);
508 LOG(VB_AUDIO, LOG_INFO,
LOC + QString(
"GetVolume(%1) %2 (%3)")
509 .arg(channel).arg(volume).arg(mb));
513 LOG(VB_GENERAL, LOG_ERR,
LOC + QString(
"GetVolume(%1) %2 (%3) : %4")
514 .arg(channel).arg(volume).arg(mb).arg(r));
523 LOG(VB_GENERAL, LOG_ERR,
LOC +
"Android volume only supports stereo!");
528 float vol = volume / 100.f;
533 mb = SL_MILLIBEL_MIN;
538 mb = lroundf(3 * 2000.F * log10f(vol));
539 if (mb < SL_MILLIBEL_MIN)
540 mb = SL_MILLIBEL_MIN;
546 if (r == SL_RESULT_SUCCESS)
548 LOG(VB_AUDIO, LOG_INFO,
LOC + QString(
"SetVolume(%1) %2(%3)")
549 .arg(channel).arg(volume).arg(mb));
553 LOG(VB_GENERAL, LOG_ERR,
LOC + QString(
"SetVolume(%1) %2(%3) : %4")
554 .arg(channel).arg(volume).arg(mb).arg(r));
#define QAndroidJniEnvironment
int GetNativeOutputSampleRate(void)
#define SetPositionUpdatePeriod(a, b)
#define GetInterface(a, b, c)
#define CreateAudioPlayer(a, b, c, d, e, f, g)
#define RegisterCallback(a, b, c)
#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 POSITIONUPDATEPERIOD
#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)
SLObjectItf m_engineObject
SLObjectItf m_outputMixObject
void SetVolumeChannel(int channel, int volume) override
void WriteAudio(unsigned char *aubuf, int size) override
SLInterfaceID m_SL_IID_PLAY
slCreateEngine_t m_slCreateEnginePtr
SLInterfaceID m_SL_IID_VOLUME
void PlayedCallback(SLAndroidSimpleBufferQueueItf caller)
SLEngineItf m_engineEngine
AudioOutputSettings * GetOutputSettings(bool digital) override
AudioOutputOpenSLES(const AudioSettings &settings)
SLInterfaceID m_SL_IID_ANDROIDSIMPLEBUFFERQUEUE
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
virtual ~AudioOutputOpenSLES()
SLAndroidSimpleBufferQueueItf m_playerBufferQueue
SLInterfaceID m_SL_IID_ENGINE
int GetVolumeChannel(int channel) const override
bool OpenDevice(void) override
SLObjectItf m_playerObject
void CloseDevice(void) override
void AddSupportedRate(int rate)
void setPassthrough(int val)
void AddSupportedChannels(int channels)
void AddSupportedFormat(AudioFormat format)
void dispatchError(const QString &e)
bool m_init
If set to false, AudioOutput instance will not try to initially open the audio device.
static void usleep(std::chrono::microseconds time)
#define LOG(_MASK_, _LEVEL_, _QSTRING_)