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: ")
22#define OPENSLES_BUFFERS 10
26#define POSITIONUPDATEPERIOD 40
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);
101 LOG(VB_GENERAL, LOG_ERR,
LOC +
"Error: Failed to load libOpenSLES");
109 LOG(VB_GENERAL, LOG_ERR,
LOC +
"Error: Failed to load symbol slCreateEngine");
114#define OPENSL_DLSYM(dest, name) \
116 const SLInterfaceID *sym = (const SLInterfaceID *)dlsym(m_so_handle, "SL_IID_" name); \
117 if (sym == nullptr) \
119 LOG(VB_GENERAL, LOG_ERR, "AOOSLES Error: Failed to load symbol SL_IID_" name); \
146 const SLboolean req1[] = { SL_BOOLEAN_FALSE };
162 SLDataLocator_AndroidSimpleBufferQueue loc_bufq = {
163 SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE,
167 SLDataFormat_PCM format_pcm;
168 format_pcm.formatType = SL_DATAFORMAT_PCM;
169 format_pcm.numChannels = 2;
170 format_pcm.samplesPerSec = ((SLuint32)
m_sampleRate * 1000) ;
171 format_pcm.endianness = SL_BYTEORDER_LITTLEENDIAN;
174 case FORMAT_U8: format_pcm.bitsPerSample = SL_PCMSAMPLEFORMAT_FIXED_8;
break;
175 case FORMAT_S16: format_pcm.bitsPerSample = SL_PCMSAMPLEFORMAT_FIXED_16;
break;
177 case FORMAT_S24LSB: format_pcm.bitsPerSample = SL_PCMSAMPLEFORMAT_FIXED_24;
break;
179 format_pcm.endianness = SL_BYTEORDER_BIGENDIAN;
180 format_pcm.bitsPerSample = SL_PCMSAMPLEFORMAT_FIXED_24;
182 case FORMAT_S32: format_pcm.bitsPerSample = SL_PCMSAMPLEFORMAT_FIXED_32;
break;
187 QString message {QCoreApplication::translate(
"AudioOutputOpenSLES",
"Unknown sample format: %1")
190 LOG(VB_GENERAL, LOG_ERR, message);
194 format_pcm.containerSize = format_pcm.bitsPerSample;
195 format_pcm.channelMask = SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT;
197 SLDataSource audioSrc = {&loc_bufq, &format_pcm};
200 SLDataLocator_OutputMix loc_outmix = {
201 SL_DATALOCATOR_OUTPUTMIX,
204 SLDataSink audioSnk = {&loc_outmix,
nullptr};
208 static const SLboolean req2[] = { SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE };
212 &audioSnk,
sizeof(ids2) /
sizeof(*ids2),
221 result = SL_RESULT_UNKNOWN_ERROR;
223 if (result != SL_RESULT_SUCCESS) {
226 format_pcm.samplesPerSec = ((SLuint32) 48000 * 1000) ;
228 &audioSnk,
sizeof(ids2) /
sizeof(*ids2),
357 LOG(VB_GENERAL, LOG_INFO,
LOC + QString(
"Native Rate %1").arg(nativeRate));
393 LOG(VB_AUDIO, LOG_INFO,
LOC + QString(
"Buffering %1 fragments of %2 bytes each, total: %3 bytes")
414 LOG(VB_AUDIO, LOG_INFO,
LOC + QString(
"Freed"));
423 SLAndroidSimpleBufferQueueState st;
425 if (res != SL_RESULT_SUCCESS) {
426 LOG(VB_GENERAL, LOG_ERR,
LOC + QString(
"Could not query buffer queue state in %1 (%2)").arg(__func__).arg(res));
429 LOG(VB_AUDIO, LOG_INFO,
LOC + QString(
"Num Queued %1").arg(st.count));
435 LOG(VB_AUDIO, LOG_INFO,
LOC + QString(
"WriteAudio %1").arg(size));
440 if (numBufferesQueued < 0)
466 if (r != SL_RESULT_SUCCESS)
469 LOG(VB_GENERAL, LOG_ERR,
LOC + QString(
"error %1 when writing %2 bytes %3")
472 .arg((r == SL_RESULT_BUFFER_INSUFFICIENT) ?
" (buffer insufficient)" :
""));
487 if (numBufferesQueued < 0)
499 if (r == SL_RESULT_SUCCESS)
501 if (mb <= SL_MILLIBEL_MIN)
507 volume = lroundf(expf(mb / (3*2000.0F)) * 100);
509 LOG(VB_AUDIO, LOG_INFO,
LOC + QString(
"GetVolume(%1) %2 (%3)")
510 .arg(channel).arg(volume).arg(mb));
514 LOG(VB_GENERAL, LOG_ERR,
LOC + QString(
"GetVolume(%1) %2 (%3) : %4")
515 .arg(channel).arg(volume).arg(mb).arg(r));
524 LOG(VB_GENERAL, LOG_ERR,
LOC +
"Android volume only supports stereo!");
529 float vol = volume / 100.f;
534 mb = SL_MILLIBEL_MIN;
539 mb = lroundf(3 * 2000.F * log10f(vol));
540 if (mb < SL_MILLIBEL_MIN)
541 mb = SL_MILLIBEL_MIN;
547 if (r == SL_RESULT_SUCCESS)
549 LOG(VB_AUDIO, LOG_INFO,
LOC + QString(
"SetVolume(%1) %2(%3)")
550 .arg(channel).arg(volume).arg(mb));
554 LOG(VB_GENERAL, LOG_ERR,
LOC + QString(
"SetVolume(%1) %2(%3) : %4")
555 .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.
#define LOG(_MASK_, _LEVEL_, _QSTRING_)