first commit
This commit is contained in:
465
project/fm_viewer/fav/AudioOutputOpenSL.cpp
Normal file
465
project/fm_viewer/fav/AudioOutputOpenSL.cpp
Normal file
@@ -0,0 +1,465 @@
|
||||
/******************************************************************************
|
||||
QtAV: Multimedia framework based on Qt and FFmpeg
|
||||
Copyright (C) 2012-2016 Wang Bin <wbsecg1@gmail.com>
|
||||
|
||||
* This file is part of QtAV (from 2014)
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this library; if not, write to the Free Software
|
||||
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
******************************************************************************/
|
||||
|
||||
#define REMOVE_AUDIO_OPENSL 1
|
||||
#if !(REMOVE_AUDIO_OPENSL)
|
||||
|
||||
#include "AudioOutputBackend.h"
|
||||
#include <QtCore/QSemaphore>
|
||||
#include <QtCore/QThread>
|
||||
#include <SLES/OpenSLES.h>
|
||||
#ifdef Q_OS_ANDROID
|
||||
#include <SLES/OpenSLES_Android.h>
|
||||
#include <SLES/OpenSLES_AndroidConfiguration.h>
|
||||
#include <sys/system_properties.h>
|
||||
#endif
|
||||
#include "mkid.h"
|
||||
#include "factory.h"
|
||||
#include "Logger.h"
|
||||
|
||||
// TODO: native sample rate, so AUDIO_OUTPUT_FLAG_FAST is enabled
|
||||
namespace FAV {
|
||||
|
||||
// OpenSL 1.1, or __ANDROID_API__ >= 21
|
||||
#ifndef SL_DATAFORMAT_PCM_EX
|
||||
#define SL_PCM_REPRESENTATION_SIGNED_INT ((SLuint32) 0x00000001)
|
||||
#define SL_PCM_REPRESENTATION_UNSIGNED_INT ((SLuint32) 0x00000002)
|
||||
#define SL_PCM_REPRESENTATION_FLOAT ((SLuint32) 0x00000003)
|
||||
#define SL_DATAFORMAT_PCM_EX ((SLuint32) 0x00000004)
|
||||
struct SLDataFormat_PCM_EX : SLDataFormat_PCM {
|
||||
SLuint32 representation;
|
||||
};
|
||||
#endif
|
||||
|
||||
static const char kName[] = "OpenSL";
|
||||
class AudioOutputOpenSL Q_DECL_FINAL: public AudioOutputBackend
|
||||
{
|
||||
public:
|
||||
AudioOutputOpenSL(QObject *parent = 0);
|
||||
~AudioOutputOpenSL();
|
||||
|
||||
int engineVersion() const { return m_sl_major*100+m_sl_minor*10+m_sl_step;}
|
||||
QString name() const Q_DECL_OVERRIDE { return QLatin1String(kName);}
|
||||
QString status() const Q_DECL_OVERRIDE {return "";};
|
||||
bool isSupported(const AudioFormat& format) const Q_DECL_OVERRIDE;
|
||||
bool isSupported(AudioFormat::SampleFormat sampleFormat) const Q_DECL_OVERRIDE;
|
||||
bool isSupported(AudioFormat::ChannelLayout channelLayout) const Q_DECL_OVERRIDE;
|
||||
bool open() Q_DECL_OVERRIDE;
|
||||
bool close() Q_DECL_OVERRIDE;
|
||||
BufferControl bufferControl() const Q_DECL_OVERRIDE;
|
||||
void onCallback() Q_DECL_OVERRIDE;
|
||||
void acquireNextBuffer() Q_DECL_OVERRIDE;
|
||||
bool write(const QByteArray& data) Q_DECL_OVERRIDE;
|
||||
bool play() Q_DECL_OVERRIDE;
|
||||
//default return -1. means not the control
|
||||
int getPlayedCount() Q_DECL_OVERRIDE;
|
||||
bool setVolume(qreal value) Q_DECL_OVERRIDE;
|
||||
qreal getVolume() const Q_DECL_OVERRIDE;
|
||||
bool setMute(bool value = true) Q_DECL_OVERRIDE;
|
||||
|
||||
#ifdef Q_OS_ANDROID
|
||||
static void bufferQueueCallbackAndroid(SLAndroidSimpleBufferQueueItf bufferQueue, void *context);
|
||||
#endif
|
||||
static void bufferQueueCallback(SLBufferQueueItf bufferQueue, void *context);
|
||||
static void playCallback(SLPlayItf player, void *ctx, SLuint32 event);
|
||||
private:
|
||||
SLDataFormat_PCM_EX audioFormatToSL(const AudioFormat &format);
|
||||
|
||||
SLObjectItf engineObject;
|
||||
SLEngineItf engine;
|
||||
SLEngineCapabilitiesItf m_cap;
|
||||
SLObjectItf m_outputMixObject;
|
||||
SLObjectItf m_playerObject;
|
||||
SLPlayItf m_playItf;
|
||||
SLVolumeItf m_volumeItf;
|
||||
SLBufferQueueItf m_bufferQueueItf;
|
||||
#ifdef Q_OS_ANDROID
|
||||
SLAndroidSimpleBufferQueueItf m_bufferQueueItf_android; // supports decoding, recording
|
||||
#endif
|
||||
bool m_android;
|
||||
int m_android_api_level;
|
||||
SLint16 m_sl_major, m_sl_minor, m_sl_step;
|
||||
SLint32 m_streamType;
|
||||
quint32 buffers_queued;
|
||||
QSemaphore sem;
|
||||
|
||||
// Enqueue does not copy data. We MUST keep the data until it is played out
|
||||
int queue_data_write;
|
||||
QByteArray queue_data;
|
||||
};
|
||||
|
||||
typedef AudioOutputOpenSL AudioOutputBackendOpenSL;
|
||||
static const AudioOutputBackendId AudioOutputBackendId_OpenSL = mkid::id32base36_6<'O', 'p', 'e', 'n', 'S', 'L'>::value;
|
||||
FACTORY_REGISTER(AudioOutputBackend, OpenSL, kName)
|
||||
|
||||
#define SL_ENSURE(FUNC, ...) \
|
||||
do { \
|
||||
SLresult ret = FUNC; \
|
||||
if (ret != SL_RESULT_SUCCESS) { \
|
||||
qWarning("AudioOutputOpenSL Error>>> " #FUNC " (%lu)", ret); \
|
||||
return __VA_ARGS__; \
|
||||
} \
|
||||
} while(0)
|
||||
|
||||
SLDataFormat_PCM_EX AudioOutputOpenSL::audioFormatToSL(const AudioFormat &format)
|
||||
{
|
||||
SLDataFormat_PCM_EX format_pcm;
|
||||
if (format.isFloat()) {
|
||||
if (format.sampleSize() == sizeof(float))
|
||||
format_pcm.representation = SL_PCM_REPRESENTATION_FLOAT;
|
||||
} else if (format.isUnsigned()) {
|
||||
format_pcm.representation = SL_PCM_REPRESENTATION_UNSIGNED_INT;
|
||||
} else {
|
||||
format_pcm.representation = SL_PCM_REPRESENTATION_SIGNED_INT;
|
||||
}
|
||||
format_pcm.formatType = m_android_api_level >= 21 || engineVersion() >= 110 ? SL_DATAFORMAT_PCM_EX : SL_DATAFORMAT_PCM;
|
||||
format_pcm.numChannels = format.channels();
|
||||
format_pcm.samplesPerSec = format.sampleRate() * 1000;
|
||||
format_pcm.bitsPerSample = format.bytesPerSample()*8; //raw sample size, e.g. s24 is 24. currently we do not support such formats
|
||||
format_pcm.containerSize = format_pcm.bitsPerSample;
|
||||
// TODO: more layouts
|
||||
format_pcm.channelMask = format.channels() == 1 ? SL_SPEAKER_FRONT_CENTER : SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT;
|
||||
#ifdef SL_BYTEORDER_NATIVE
|
||||
format_pcm.endianness = SL_BYTEORDER_NATIVE;
|
||||
#else
|
||||
union { unsigned short num; char buf[sizeof(unsigned short)]; } endianness;
|
||||
endianness.num = 1;
|
||||
format_pcm.endianness = endianness.buf[0] ? SL_BYTEORDER_LITTLEENDIAN : SL_BYTEORDER_BIGENDIAN;
|
||||
#endif
|
||||
return format_pcm;
|
||||
}
|
||||
|
||||
#ifdef Q_OS_ANDROID
|
||||
void AudioOutputOpenSL::bufferQueueCallbackAndroid(SLAndroidSimpleBufferQueueItf bufferQueue, void *context)
|
||||
{
|
||||
#if 0
|
||||
SLAndroidSimpleBufferQueueState state;
|
||||
(*bufferQueue)->GetState(bufferQueue, &state);
|
||||
qDebug(">>>>>>>>>>>>>>bufferQueueCallback state.count=%lu .playIndex=%lu", state.count, state.playIndex);
|
||||
#endif
|
||||
AudioOutputOpenSL *ao = reinterpret_cast<AudioOutputOpenSL*>(context);
|
||||
if (ao->bufferControl() & AudioOutputBackend::CountCallback) {
|
||||
ao->onCallback();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
void AudioOutputOpenSL::bufferQueueCallback(SLBufferQueueItf bufferQueue, void *context)
|
||||
{
|
||||
#if 0
|
||||
SLBufferQueueState state;
|
||||
(*bufferQueue)->GetState(bufferQueue, &state);
|
||||
qDebug(">>>>>>>>>>>>>>bufferQueueCallback state.count=%lu .playIndex=%lu", state.count, state.playIndex);
|
||||
#endif
|
||||
AudioOutputOpenSL *ao = reinterpret_cast<AudioOutputOpenSL*>(context);
|
||||
if (ao->bufferControl() & AudioOutputBackend::CountCallback) {
|
||||
ao->onCallback();
|
||||
}
|
||||
}
|
||||
|
||||
void AudioOutputOpenSL::playCallback(SLPlayItf player, void *ctx, SLuint32 event)
|
||||
{
|
||||
Q_UNUSED(player);
|
||||
Q_UNUSED(ctx);
|
||||
Q_UNUSED(event);
|
||||
//qDebug("---------%s event=%lu", __FUNCTION__, event);
|
||||
}
|
||||
|
||||
AudioOutputOpenSL::AudioOutputOpenSL(QObject *parent)
|
||||
: AudioOutputBackend(AudioOutput::DeviceFeatures()
|
||||
|AudioOutput::SetVolume
|
||||
|AudioOutput::SetMute, parent)
|
||||
, m_outputMixObject(0)
|
||||
, m_playerObject(0)
|
||||
, m_playItf(0)
|
||||
, m_volumeItf(0)
|
||||
, m_bufferQueueItf(0)
|
||||
, m_bufferQueueItf_android(0)
|
||||
, m_android(false)
|
||||
, m_android_api_level(0)
|
||||
, m_sl_major(0)
|
||||
, m_sl_minor(0)
|
||||
, m_sl_step(0)
|
||||
, m_streamType(-1)
|
||||
, buffers_queued(0)
|
||||
, queue_data_write(0)
|
||||
{
|
||||
#ifdef Q_OS_ANDROID
|
||||
char v[PROP_VALUE_MAX+1];
|
||||
__system_property_get("ro.build.version.sdk", v);
|
||||
m_android_api_level = atoi(v);
|
||||
#endif
|
||||
available = false;
|
||||
SLEngineOption opt[] = {(SLuint32) SL_ENGINEOPTION_THREADSAFE, (SLuint32) SL_BOOLEAN_TRUE};
|
||||
SL_ENSURE(slCreateEngine(&engineObject, 1, opt, 0, NULL, NULL)); // SLEngineOption is ignored by android
|
||||
SL_ENSURE((*engineObject)->Realize(engineObject, SL_BOOLEAN_FALSE));
|
||||
SL_ENSURE((*engineObject)->GetInterface(engineObject, SL_IID_ENGINE, &engine));
|
||||
available = true;
|
||||
|
||||
if ((*engineObject)->GetInterface(engineObject, SL_IID_ENGINECAPABILITIES, &m_cap) == SL_RESULT_SUCCESS) { // SL_RESULT_FEATURE_UNSUPPORTED
|
||||
if ((*m_cap)->QueryAPIVersion(m_cap, &m_sl_major, &m_sl_minor, &m_sl_step) == SL_RESULT_SUCCESS) {
|
||||
printf("OpenSL version: %d.%d.%d\n", m_sl_major, m_sl_minor, m_sl_step);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
AudioOutputOpenSL::~AudioOutputOpenSL()
|
||||
{
|
||||
if (engineObject)
|
||||
(*engineObject)->Destroy(engineObject);
|
||||
}
|
||||
|
||||
bool AudioOutputOpenSL::isSupported(const AudioFormat& format) const
|
||||
{
|
||||
return isSupported(format.sampleFormat()) && isSupported(format.channelLayout());
|
||||
}
|
||||
|
||||
bool AudioOutputOpenSL::isSupported(AudioFormat::SampleFormat sampleFormat) const
|
||||
{
|
||||
if (sampleFormat == AudioFormat::SampleFormat_Unsigned8 || sampleFormat == AudioFormat::SampleFormat_Signed16) // TODO: android api 21 supports s32?
|
||||
return true;
|
||||
if (m_android_api_level < 21 || (engineVersion() > 0 && engineVersion() < 110))
|
||||
return false;
|
||||
if (!IsFloat(sampleFormat) || IsPlanar(sampleFormat))
|
||||
return false;
|
||||
return RawSampleSize(sampleFormat) == sizeof(float); // TODO: test s32 etc
|
||||
}
|
||||
|
||||
bool AudioOutputOpenSL::isSupported(AudioFormat::ChannelLayout channelLayout) const
|
||||
{
|
||||
return channelLayout == AudioFormat::ChannelLayout_Mono || channelLayout == AudioFormat::ChannelLayout_Stereo;
|
||||
}
|
||||
|
||||
AudioOutputBackend::BufferControl AudioOutputOpenSL::bufferControl() const
|
||||
{
|
||||
return CountCallback;//BufferControl(Callback | PlayedCount);
|
||||
}
|
||||
|
||||
void AudioOutputOpenSL::onCallback()
|
||||
{
|
||||
if (bufferControl() & CountCallback)
|
||||
sem.release();
|
||||
}
|
||||
|
||||
void AudioOutputOpenSL::acquireNextBuffer()
|
||||
{
|
||||
if (bufferControl() & CountCallback)
|
||||
sem.acquire();
|
||||
}
|
||||
|
||||
bool AudioOutputOpenSL::open()
|
||||
{
|
||||
queue_data.resize(buffer_size*buffer_count);
|
||||
SLDataLocator_BufferQueue bufferQueueLocator = { SL_DATALOCATOR_BUFFERQUEUE, (SLuint32)buffer_count };
|
||||
SLDataFormat_PCM_EX pcmFormat = audioFormatToSL(format);
|
||||
SLDataSource audioSrc = { &bufferQueueLocator, &pcmFormat };
|
||||
#ifdef Q_OS_ANDROID
|
||||
SLDataLocator_AndroidSimpleBufferQueue bufferQueueLocator_android = { SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE, (SLuint32)buffer_count };
|
||||
if (m_android)
|
||||
audioSrc.pLocator = &bufferQueueLocator_android;
|
||||
#endif
|
||||
// OutputMix
|
||||
SL_ENSURE((*engine)->CreateOutputMix(engine, &m_outputMixObject, 0, NULL, NULL), false);
|
||||
SL_ENSURE((*m_outputMixObject)->Realize(m_outputMixObject, SL_BOOLEAN_FALSE), false);
|
||||
SLDataLocator_OutputMix outputMixLocator = { SL_DATALOCATOR_OUTPUTMIX, m_outputMixObject };
|
||||
SLDataSink audioSink = { &outputMixLocator, NULL };
|
||||
|
||||
const SLInterfaceID ids[] = { SL_IID_BUFFERQUEUE, SL_IID_VOLUME
|
||||
#ifdef Q_OS_ANDROID
|
||||
, SL_IID_ANDROIDCONFIGURATION
|
||||
#endif
|
||||
};
|
||||
const SLboolean req[] = { SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE
|
||||
#ifdef Q_OS_ANDROID
|
||||
, SL_BOOLEAN_TRUE
|
||||
#endif
|
||||
};
|
||||
// AudioPlayer
|
||||
SL_ENSURE((*engine)->CreateAudioPlayer(engine, &m_playerObject, &audioSrc, &audioSink, sizeof(ids)/sizeof(ids[0]), ids, req), false);
|
||||
#ifdef Q_OS_ANDROID
|
||||
if (m_android) {
|
||||
m_streamType = SL_ANDROID_STREAM_MEDIA;
|
||||
SLAndroidConfigurationItf cfg;
|
||||
if ((*m_playerObject)->GetInterface(m_playerObject, SL_IID_ANDROIDCONFIGURATION, &cfg)) {
|
||||
(*cfg)->SetConfiguration(cfg, SL_ANDROID_KEY_STREAM_TYPE, &m_streamType, sizeof(SLint32));
|
||||
}
|
||||
}
|
||||
#endif
|
||||
SL_ENSURE((*m_playerObject)->Realize(m_playerObject, SL_BOOLEAN_FALSE), false);
|
||||
// Buffer interface
|
||||
#ifdef Q_OS_ANDROID
|
||||
if (m_android) {
|
||||
SL_ENSURE((*m_playerObject)->GetInterface(m_playerObject, SL_IID_ANDROIDSIMPLEBUFFERQUEUE, &m_bufferQueueItf_android), false);
|
||||
SL_ENSURE((*m_bufferQueueItf_android)->RegisterCallback(m_bufferQueueItf_android, AudioOutputOpenSL::bufferQueueCallbackAndroid, this), false);
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
SL_ENSURE((*m_playerObject)->GetInterface(m_playerObject, SL_IID_BUFFERQUEUE, &m_bufferQueueItf), false);
|
||||
SL_ENSURE((*m_bufferQueueItf)->RegisterCallback(m_bufferQueueItf, AudioOutputOpenSL::bufferQueueCallback, this), false);
|
||||
}
|
||||
// Play interface
|
||||
SL_ENSURE((*m_playerObject)->GetInterface(m_playerObject, SL_IID_PLAY, &m_playItf), false);
|
||||
// call when SL_PLAYSTATE_STOPPED
|
||||
SL_ENSURE((*m_playItf)->RegisterCallback(m_playItf, AudioOutputOpenSL::playCallback, this), false);
|
||||
SL_ENSURE((*m_playerObject)->GetInterface(m_playerObject, SL_IID_VOLUME, &m_volumeItf), false);
|
||||
#if 0
|
||||
SLuint32 mask = SL_PLAYEVENT_HEADATEND;
|
||||
// TODO: what does this do?
|
||||
SL_ENSURE((*m_playItf)->SetPositionUpdatePeriod(m_playItf, 100), false);
|
||||
SL_ENSURE((*m_playItf)->SetCallbackEventsMask(m_playItf, mask), false);
|
||||
#endif
|
||||
// Volume interface
|
||||
//SL_ENSURE((*m_playerObject)->GetInterface(m_playerObject, SL_IID_VOLUME, &m_volumeItf), false);
|
||||
|
||||
/*
|
||||
* DO NOT call sem.release(buffer_count - sem.available()) because playInitialData() will enqueue buffers and then sem.release() is called in callback.
|
||||
* Otherwise, Enqueue() the 1st(or more) real buffer may report SL_RESULT_BUFFER_INSUFFICIENT and noise will be played.
|
||||
* Can not Enqueue() internally here because buffers are managed in AudioOutput to ensure 0-copy
|
||||
*/
|
||||
// sem.release(buffer_count - sem.available());
|
||||
return true;
|
||||
}
|
||||
|
||||
bool AudioOutputOpenSL::close()
|
||||
{
|
||||
if (m_playItf)
|
||||
(*m_playItf)->SetPlayState(m_playItf, SL_PLAYSTATE_STOPPED);
|
||||
|
||||
#ifdef Q_OS_ANDROID
|
||||
if (m_android) {
|
||||
if (m_bufferQueueItf_android && SL_RESULT_SUCCESS != (*m_bufferQueueItf_android)->Clear(m_bufferQueueItf_android))
|
||||
qWarning("Unable to clear buffer");
|
||||
m_bufferQueueItf_android = NULL;
|
||||
}
|
||||
#endif
|
||||
if (m_bufferQueueItf && SL_RESULT_SUCCESS != (*m_bufferQueueItf)->Clear(m_bufferQueueItf))
|
||||
qWarning("Unable to clear buffer");
|
||||
|
||||
if (m_playerObject) {
|
||||
(*m_playerObject)->Destroy(m_playerObject);
|
||||
m_playerObject = NULL;
|
||||
}
|
||||
if (m_outputMixObject) {
|
||||
(*m_outputMixObject)->Destroy(m_outputMixObject);
|
||||
m_outputMixObject = NULL;
|
||||
}
|
||||
|
||||
m_playItf = NULL;
|
||||
m_volumeItf = NULL;
|
||||
m_bufferQueueItf = NULL;
|
||||
queue_data.clear();
|
||||
queue_data_write = 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool AudioOutputOpenSL::write(const QByteArray& data)
|
||||
{
|
||||
// assume data.size() <= buffer_size. It's true in QtAV
|
||||
const int s = qMin(queue_data.size() - queue_data_write, data.size());
|
||||
// assume data.size() <= buffer_size. It's true in QtAV
|
||||
if (s < data.size())
|
||||
queue_data_write = 0;
|
||||
memcpy((char*)queue_data.constData() + queue_data_write, data.constData(), data.size());
|
||||
//qDebug("enqueue %p, queue_data_write: %d/%d available:%d", data.constData(), queue_data_write, queue_data.size(), sem.available());
|
||||
#ifdef Q_OS_ANDROID
|
||||
if (m_android)
|
||||
SL_ENSURE((*m_bufferQueueItf_android)->Enqueue(m_bufferQueueItf_android, queue_data.constData() + queue_data_write, data.size()), false);
|
||||
else
|
||||
#endif
|
||||
SL_ENSURE((*m_bufferQueueItf)->Enqueue(m_bufferQueueItf, queue_data.constData() + queue_data_write, data.size()), false);
|
||||
buffers_queued++;
|
||||
queue_data_write += data.size();
|
||||
if (queue_data_write == queue_data.size())
|
||||
queue_data_write = 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool AudioOutputOpenSL::play()
|
||||
{
|
||||
SLuint32 state = SL_PLAYSTATE_PLAYING;
|
||||
(*m_playItf)->GetPlayState(m_playItf, &state);
|
||||
if (state == SL_PLAYSTATE_PLAYING)
|
||||
return true;
|
||||
SL_ENSURE((*m_playItf)->SetPlayState(m_playItf, SL_PLAYSTATE_PLAYING), false);
|
||||
return true;
|
||||
}
|
||||
|
||||
int AudioOutputOpenSL::getPlayedCount()
|
||||
{
|
||||
int processed = buffers_queued;
|
||||
SLuint32 count = 0;
|
||||
#ifdef Q_OS_ANDROID
|
||||
if (m_android) {
|
||||
SLAndroidSimpleBufferQueueState state;
|
||||
(*m_bufferQueueItf_android)->GetState(m_bufferQueueItf_android, &state);
|
||||
count = state.count;
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
SLBufferQueueState state;
|
||||
(*m_bufferQueueItf)->GetState(m_bufferQueueItf, &state);
|
||||
count = state.count;
|
||||
}
|
||||
buffers_queued = count;
|
||||
processed -= count;
|
||||
return processed;
|
||||
}
|
||||
|
||||
bool AudioOutputOpenSL::setVolume(qreal value)
|
||||
{
|
||||
if (!m_volumeItf)
|
||||
return false;
|
||||
SLmillibel v = 0;
|
||||
if (qFuzzyIsNull(value))
|
||||
v = SL_MILLIBEL_MIN;
|
||||
else if (!qFuzzyCompare(value, 1.0))
|
||||
v = 20.0*log10(value)*100.0;
|
||||
SLmillibel vmax = SL_MILLIBEL_MAX;
|
||||
SL_ENSURE((*m_volumeItf)->GetMaxVolumeLevel(m_volumeItf, &vmax), false);
|
||||
if (vmax < v) {
|
||||
qDebug("OpenSL does not support volume: %f %d/%d. sw scale will be used", value, v, vmax);
|
||||
return false;
|
||||
}
|
||||
SL_ENSURE((*m_volumeItf)->SetVolumeLevel(m_volumeItf, v), false);
|
||||
return true;
|
||||
}
|
||||
|
||||
qreal AudioOutputOpenSL::getVolume() const
|
||||
{
|
||||
if (!m_volumeItf)
|
||||
return false;
|
||||
SLmillibel v = 0;
|
||||
SL_ENSURE((*m_volumeItf)->GetVolumeLevel(m_volumeItf, &v), 1.0);
|
||||
if (v == SL_MILLIBEL_MIN)
|
||||
return 0;
|
||||
return pow(10.0, qreal(v)/2000.0);
|
||||
}
|
||||
|
||||
bool AudioOutputOpenSL::setMute(bool value)
|
||||
{
|
||||
if (!m_volumeItf)
|
||||
return false;
|
||||
SL_ENSURE((*m_volumeItf)->SetMute(m_volumeItf, value), false);
|
||||
return true;
|
||||
}
|
||||
|
||||
} //namespace FAV
|
||||
|
||||
#endif // #if !(REMOVE_AUDIO_OPENSL)
|
||||
Reference in New Issue
Block a user