rtphone/src/engine/audio/Audio_Android.cpp

598 lines
17 KiB
C++

#include "Audio_Android.h"
#include "../helper/HL_Sync.h"
#include "../helper/HL_Log.h"
#include <mutex>
#include <iostream>
#include "../helper/HL_String.h"
#ifdef TARGET_ANDROID
#define LOG_SUBSYSTEM "Audio"
using namespace Audio;
// -------------------- AndroidEnumerator -----------------------------
AndroidEnumerator::AndroidEnumerator()
{}
AndroidEnumerator::~AndroidEnumerator()
{}
int AndroidEnumerator::indexOfDefaultDevice()
{
return 0;
}
int AndroidEnumerator::count()
{
return 1;
}
int AndroidEnumerator::idAt(int index)
{
return 0;
}
std::string AndroidEnumerator::nameAt(int index)
{
return "Audio";
}
void AndroidEnumerator::open(int direction)
{}
void AndroidEnumerator::close()
{}
// -----------------------
OpenSLEngine::OpenSLEngine()
{}
OpenSLEngine::~OpenSLEngine()
{}
void OpenSLEngine::open()
{
std::unique_lock<std::mutex> l(mMutex);
if (++mUsageCounter == 1)
internalOpen();
}
void OpenSLEngine::close()
{
std::unique_lock<std::mutex> l(mMutex);
if (mUsageCounter == 0)
return;
if (--mUsageCounter == 0)
internalClose();
}
#define CHECK_OPENSLES_ERROR if (resultCode != SL_RESULT_SUCCESS) throw Exception(ERR_OPENSLES, (int)resultCode)
void OpenSLEngine::internalOpen()
{
SLresult resultCode;
// Instantiate OpenSL ES engine object
resultCode = slCreateEngine(&mEngineObject, 0, nullptr, 0, nullptr, nullptr);
CHECK_OPENSLES_ERROR;
// Bring it online (realize)
resultCode = (*mEngineObject)->Realize(mEngineObject, SL_BOOLEAN_FALSE);
CHECK_OPENSLES_ERROR;
// Get interface finally
resultCode = (*mEngineObject)->GetInterface(mEngineObject, SL_IID_ENGINE, &mEngineInterface);
CHECK_OPENSLES_ERROR;
ICELogInfo(<< "OpenSL engine object created.");
}
void OpenSLEngine::internalClose()
{
if (mEngineObject != nullptr)
{
ICELogInfo(<< "Destroy OpenSL engine object.");
(*mEngineObject)->Destroy(mEngineObject);
mEngineObject = nullptr;
mEngineInterface = nullptr;
}
}
SLEngineItf OpenSLEngine::getNativeEngine() const
{
return mEngineInterface;
}
static OpenSLEngine OpenSLEngineInstance;
OpenSLEngine& OpenSLEngine::instance()
{
return OpenSLEngineInstance;
}
// --------------- Input implementation ----------------
AndroidInputDevice::AndroidInputDevice(int devId)
{}
AndroidInputDevice::~AndroidInputDevice()
{}
static int RateToProbe[12][2] = {
{ SL_SAMPLINGRATE_16, 16000 },
{ SL_SAMPLINGRATE_8, 8000 },
{ SL_SAMPLINGRATE_32, 32000 },
{ SL_SAMPLINGRATE_44_1, 44100 },
{ SL_SAMPLINGRATE_11_025, 10025 },
{ SL_SAMPLINGRATE_22_05, 22050 },
{ SL_SAMPLINGRATE_24, 24000 },
{ SL_SAMPLINGRATE_48, 48000 },
{ SL_SAMPLINGRATE_64, 64000 },
{ SL_SAMPLINGRATE_88_2, 88200 },
{ SL_SAMPLINGRATE_96, 96000 },
{ SL_SAMPLINGRATE_192, 192000} };
bool AndroidInputDevice::open()
{
if (active())
return true;
OpenSLEngine::instance().open();
// Probe few sampling rates
bool opened = false;
for (int rateIndex = 0; rateIndex < 12 && !opened; rateIndex++)
{
try
{
internalOpen(RateToProbe[rateIndex][0], RateToProbe[rateIndex][1]);
mDeviceRate = RateToProbe[rateIndex][1];
ICELogInfo(<< "Input Opened with rate " << mDeviceRate << " and rate index " << rateIndex);
opened = mDeviceRate != 0;
if (!opened)
internalClose();
}
catch(...)
{
opened = false;
internalClose();
}
}
mActive = opened;
return opened;
}
void AndroidInputDevice::close()
{
// There is no check for active() value because close() can be called to cleanup after bad open() call.
internalClose();
OpenSLEngine::instance().close();
mActive = false;
}
Format AndroidInputDevice::getFormat()
{
return Format(mDeviceRate, 1);
}
bool AndroidInputDevice::active() const
{
return mActive;
}
bool AndroidInputDevice::fakeMode()
{
return false;
}
void AndroidInputDevice::setFakeMode(bool fakemode)
{}
int AndroidInputDevice::readBuffer(void* buffer)
{
std::unique_lock<std::mutex> l(mMutex);
while (mSdkRateCache.filled() < AUDIO_MIC_BUFFER_SIZE)
{
mDataCondVar.wait(l);
}
return mSdkRateCache.read(buffer, AUDIO_MIC_BUFFER_SIZE);
}
#define CHECK_SL_INTERFACE(INTF, ERR) {if (!INTF) throw Exception(ERR_OPENSLES, ERR); if (!(*INTF)) throw Exception(ERR_OPENSLES, ERR);}
void AndroidInputDevice::internalOpen(int rateCode, int rate)
{
SLresult resultCode = 0;
SLuint32 nrOfChannels = 1;
// Prepare audio source
SLDataLocator_IODevice devDescription = { SL_DATALOCATOR_IODEVICE, SL_IODEVICE_AUDIOINPUT, SL_DEFAULTDEVICEID_AUDIOINPUT, NULL};
SLDataSource audioSource = { &devDescription, NULL };
// Source flags
SLuint32 speakersFlags = nrOfChannels > 1 ? (SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT) : SL_SPEAKER_FRONT_CENTER;
// Buffer queue
SLDataLocator_AndroidSimpleBufferQueue queueDescription = { SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE, 2 };
// Audio format
SLDataFormat_PCM formatDescription = { SL_DATAFORMAT_PCM, nrOfChannels, (SLuint32)rateCode, SL_PCMSAMPLEFORMAT_FIXED_16,
SL_PCMSAMPLEFORMAT_FIXED_16, (SLuint32)speakersFlags, SL_BYTEORDER_LITTLEENDIAN };
SLDataSink audioSink = { &queueDescription, &formatDescription };
// Create recorder
// Do not forget about RECORD_AUDIO permission
const SLInterfaceID interfacesList[2] = { SL_IID_ANDROIDSIMPLEBUFFERQUEUE, SL_IID_ANDROIDCONFIGURATION };
const SLboolean interfacesRequirements[2] = { SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE };
// Get access to OpenSL engine
SLEngineItf engine_interface = OpenSLEngine::instance().getNativeEngine();
CHECK_SL_INTERFACE(engine_interface, -1);
resultCode = (*engine_interface)->CreateAudioRecorder(
OpenSLEngine::instance().getNativeEngine(),
&mRecorderObject, &audioSource, &audioSink, 2, interfacesList, interfacesRequirements);
CHECK_OPENSLES_ERROR;
CHECK_SL_INTERFACE(mRecorderObject, -2);
// Obtain stream type
resultCode = (*mRecorderObject)->GetInterface(mRecorderObject, SL_IID_ANDROIDCONFIGURATION, &mAndroidCfg);
CHECK_OPENSLES_ERROR;
// Now audio recorder goes to real world
resultCode = (*mRecorderObject)->Realize(mRecorderObject, SL_BOOLEAN_FALSE);
CHECK_OPENSLES_ERROR;
// Get recorder interface
resultCode = (*mRecorderObject)->GetInterface(mRecorderObject, SL_IID_RECORD, &mRecorderInterface);
CHECK_OPENSLES_ERROR;
CHECK_SL_INTERFACE(mRecorderInterface, -3);
// Now buffer queue interface...
resultCode = (*mRecorderObject)->GetInterface(mRecorderObject, SL_IID_ANDROIDSIMPLEBUFFERQUEUE, &mRecorderBufferInterface);
CHECK_OPENSLES_ERROR;
CHECK_SL_INTERFACE(mRecorderBufferInterface, -4);
// Resampler is needed to provide SDK's rate
mResampler = std::make_shared<Resampler>();
mResampler->start(nrOfChannels, rate, AUDIO_SAMPLERATE);
// Allocate recorder buffer size
mBufferSize = (AUDIO_MIC_BUFFER_LENGTH / 10) * (rate / 100) * 2;
mRecorderBuffer.setCapacity(mBufferSize * AUDIO_MIC_BUFFER_COUNT);
mRecorderBufferIndex = 0;
// Setup data consuming callback
resultCode = (*mRecorderBufferInterface)->RegisterCallback(mRecorderBufferInterface, DeviceCallback, (void*)this);
CHECK_OPENSLES_ERROR;
// Setup buffers
for (int i=0; i<AUDIO_MIC_BUFFER_COUNT; i++)
(*mRecorderBufferInterface)->Enqueue(mRecorderBufferInterface, mRecorderBuffer.data() + i * mBufferSize, mBufferSize);
// Start finally
resultCode = (*mRecorderInterface)->SetRecordState(mRecorderInterface, SL_RECORDSTATE_RECORDING);
CHECK_OPENSLES_ERROR;
}
void AndroidInputDevice::internalClose()
{
if (!mRecorderObject)
return;
if (*mRecorderObject)
{
if (active())
{
// Stop recording
(*mRecorderInterface)->SetRecordState(mRecorderInterface, SL_RECORDSTATE_STOPPED);
// Wait until recording will not stop really
SLuint32 state = SL_RECORDSTATE_STOPPED;
do
{
(*mRecorderInterface)->GetRecordState(mRecorderInterface, &state);
SyncHelper::delay(1);
}
while (state == SL_RECORDSTATE_RECORDING);
}
(*mRecorderObject)->Destroy(mRecorderObject);
}
mRecorderObject = nullptr;
mRecorderInterface = nullptr;
mRecorderBufferInterface = nullptr;
mAndroidCfg = nullptr;
}
void AndroidInputDevice::handleCallback(SLAndroidSimpleBufferQueueItf bq)
{
std::unique_lock<std::mutex> l(mMutex);
// Send data to AudioPair
if (mConnection)
mConnection->onMicData(getFormat(), mRecorderBuffer.data() + mRecorderBufferIndex * mBufferSize, mBufferSize);
/*
// Send audio to cache with native sample rate
mDeviceRateCache.add(mRecorderBuffer.data() + mRecorderBufferIndex * mBufferSize, mBufferSize);
// Check if there is enough data (10 ms) to send
int tenMsSize = (int)Format(mDeviceRate, 1).sizeFromTime(10);
while (mDeviceRateCache.filled() >= tenMsSize)
{
char* resampled = (char*)alloca(Format().sizeFromTime(10));
int processed = 0;
int outlen = mResampler->processBuffer(mDeviceRateCache.data(), tenMsSize, processed, resampled, Format().sizeFromTime(10));
if (outlen > 0)
mSdkRateCache.add(resampled, (int)Format().sizeFromTime(10));
mDeviceRateCache.erase(tenMsSize);
}
// Tell about data
while (mSdkRateCache.filled() >= AUDIO_MIC_BUFFER_SIZE)
{
if (mConnection)
mConnection->onMicData(Format(), mSdkRateCache.data(), AUDIO_MIC_BUFFER_SIZE);
mSdkRateCache.erase(AUDIO_MIC_BUFFER_SIZE);
}
*/
// Re-enqueue used buffer
(*mRecorderBufferInterface)->Enqueue(mRecorderBufferInterface, mRecorderBuffer.data() + mRecorderBufferIndex * mBufferSize, mBufferSize);
mRecorderBufferIndex++;
mRecorderBufferIndex %= AUDIO_MIC_BUFFER_COUNT;
}
void AndroidInputDevice::DeviceCallback(SLAndroidSimpleBufferQueueItf bq, void *context)
{
try
{
if (context)
reinterpret_cast<AndroidInputDevice*>(context)->handleCallback(bq);
}
catch(...)
{}
}
// ------------ AndroidOutputDevice -----------------
AndroidOutputDevice::AndroidOutputDevice(int devId)
{
ICELogDebug(<< "Creating AndroidOutputDevice. This is: " << StringHelper::toHex(this));
}
AndroidOutputDevice::~AndroidOutputDevice()
{
ICELogDebug(<< "Deleting AndroidOutputDevice.");
close();
}
bool AndroidOutputDevice::open()
{
std::unique_lock<std::mutex> l(mMutex);
bool opened = false;
for (int rateIndex = 0; rateIndex < 12 && !opened; rateIndex++)
{
try
{
internalOpen(RateToProbe[rateIndex][0], RateToProbe[rateIndex][1], true);
opened = true;
mDeviceRate = RateToProbe[rateIndex][1];
ICELogCritical(<< "Output opened with rate " << mDeviceRate << " and index " << rateIndex);
}
catch(...)
{
opened = false;
}
}
if (opened)
ICELogInfo(<< "Speaker opened on rate " << mDeviceRate);
return opened;
}
void AndroidOutputDevice::close()
{
std::unique_lock<std::mutex> l(mMutex);
internalClose();
}
Format AndroidOutputDevice::getFormat()
{
return Format(mDeviceRate, 1);
}
bool AndroidOutputDevice::fakeMode()
{
return false;
}
void AndroidOutputDevice::setFakeMode(bool fakemode)
{
}
void AndroidOutputDevice::internalOpen(int rateId, int rate, bool voice)
{
mInShutdown = false;
SLresult resultCode;
SLuint32 channels = 1;
// Configure audio source
SLDataLocator_AndroidSimpleBufferQueue queue_desc = { SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE, 2 };
const SLInterfaceID interfacesList[] = { SL_IID_VOLUME };
const SLboolean interfaceRequirements[] = { SL_BOOLEAN_FALSE };
resultCode = (*OpenSLEngine::instance().getNativeEngine())->CreateOutputMix(
OpenSLEngine::instance().getNativeEngine(), &mMixer, 1, interfacesList,
interfaceRequirements);
CHECK_OPENSLES_ERROR;
// Bring mixer online
resultCode = (*mMixer)->Realize(mMixer, SL_BOOLEAN_FALSE);
CHECK_OPENSLES_ERROR;
// Prepare mixer configuration
SLuint32 speakers =
channels > 1 ? (SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT) : SL_SPEAKER_FRONT_CENTER;
// Describe audio format
SLDataFormat_PCM pcm_format = {SL_DATAFORMAT_PCM, channels, (SLuint32) rateId,
SL_PCMSAMPLEFORMAT_FIXED_16, SL_PCMSAMPLEFORMAT_FIXED_16,
speakers, SL_BYTEORDER_LITTLEENDIAN};
// Describe audio source - buffers + audio format
SLDataSource audio_source = { &queue_desc, &pcm_format };
// Describe audio sink
SLDataLocator_OutputMix mixer_desc = { SL_DATALOCATOR_OUTPUTMIX, mMixer };
SLDataSink audio_sink = { &mixer_desc, NULL };
// Create player instance
const SLInterfaceID playerInterfaces[] = { SL_IID_ANDROIDSIMPLEBUFFERQUEUE,
SL_IID_VOLUME,
SL_IID_ANDROIDCONFIGURATION };
const SLboolean playerInterfacesReqs[] = { SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE };
resultCode = (*OpenSLEngine::instance().getNativeEngine())->CreateAudioPlayer(
OpenSLEngine::instance().getNativeEngine(), &mPlayer,
&audio_source, &audio_sink, 3, playerInterfaces, playerInterfacesReqs);
CHECK_OPENSLES_ERROR;
// Get android config interface
resultCode = (*mPlayer)->GetInterface(mPlayer, SL_IID_ANDROIDCONFIGURATION, &mAndroidConfig);
if (resultCode == SL_RESULT_SUCCESS)
{
SLint32 streamType = voice ? SL_ANDROID_STREAM_VOICE : SL_ANDROID_STREAM_MEDIA;
resultCode = (*mAndroidConfig)->SetConfiguration(mAndroidConfig, SL_ANDROID_KEY_STREAM_TYPE,
&streamType, sizeof(SLint32));
if (resultCode != SL_RESULT_SUCCESS)
ICELogCritical(<< "Failed to set audio destination with error " << (unsigned)resultCode);
}
else
ICELogCritical(<< "Failed to obtain android cfg audio interface with error " << (unsigned)resultCode);
// Bring player online
resultCode = (*mPlayer)->Realize(mPlayer, SL_BOOLEAN_FALSE);
CHECK_OPENSLES_ERROR;
// Obtain player control
resultCode = (*mPlayer)->GetInterface(mPlayer, SL_IID_PLAY, &mPlayerControl);
CHECK_OPENSLES_ERROR;
// Get the buffer queue interface
resultCode = (*mPlayer)->GetInterface(mPlayer, SL_IID_ANDROIDSIMPLEBUFFERQUEUE,
&mBufferQueue);
CHECK_OPENSLES_ERROR;
// Setup callback
resultCode = (*mBufferQueue)->RegisterCallback(mBufferQueue, DeviceCallback, this);
CHECK_OPENSLES_ERROR;
// Enqueue buffers
mBufferSize = (int)Format(rate, channels).sizeFromTime(AUDIO_SPK_BUFFER_LENGTH);
mPlayBuffer.setCapacity(AUDIO_SPK_BUFFER_COUNT * mBufferSize);
mBufferIndex = 0;
for (int i = 0; i < AUDIO_SPK_BUFFER_COUNT; i++)
(*mBufferQueue)->Enqueue(mBufferQueue, mPlayBuffer.data() + i * mBufferSize,
(SLuint32)mBufferSize);
// Set the player's state to playing
resultCode = (*mPlayerControl)->SetPlayState(mPlayerControl, SL_PLAYSTATE_PLAYING);
CHECK_OPENSLES_ERROR;
ICELogInfo(<< "Android audio output is opened and playing.");
}
void AndroidOutputDevice::internalClose()
{
if (mPlayer)
{
if (*mPlayer)
{
mInShutdown = true;
ICELogInfo(<< "Stop player");
if (mPlayerControl) {
if (*mPlayerControl) {
SLuint32 state = SL_PLAYSTATE_PLAYING;
(*mPlayerControl)->SetPlayState(mPlayerControl, SL_PLAYSTATE_STOPPED);
while (state != SL_PLAYSTATE_STOPPED) {
(*mPlayerControl)->GetPlayState(mPlayerControl, &state);
SyncHelper::delay(1);
}
}
}
// Clear buffer queue
ICELogInfo(<< "Clear player buffer queue");
(*mBufferQueue)->Clear(mBufferQueue);
ICELogInfo(<< "Destroy player object");
// Destroy player object
(*mPlayer)->Destroy(mPlayer);
ICELogInfo(<< "Android audio output closed.");
mPlayer = nullptr;
mPlayerControl = nullptr;
mBufferQueue = nullptr;
mEffect = nullptr;
mAndroidConfig = nullptr;
}
}
if (mMixer)
{
if (*mMixer)
(*mMixer)->Destroy(mMixer);
mMixer = nullptr;
}
}
void AndroidOutputDevice::handleCallback(SLAndroidSimpleBufferQueueItf bq)
{
if (mInShutdown)
return;
/*{
char silence[mBufferSize]; memset(silence, 0, mBufferSize);
(*mBufferQueue)->Enqueue(mBufferQueue, silence, mBufferSize);
return;
}*/
// Ask producer about data
char* buffer = mPlayBuffer.mutableData() + mBufferIndex * mBufferSize;
if (mConnection)
{
Format f = getFormat();
if (f.mRate != 0)
mConnection->onSpkData(f, buffer, mBufferSize);
}
(*mBufferQueue)->Enqueue(mBufferQueue, buffer, (SLuint32)mBufferSize);
mBufferIndex++;
mBufferIndex %= AUDIO_SPK_BUFFER_COUNT;
}
void AndroidOutputDevice::DeviceCallback(SLAndroidSimpleBufferQueueItf bq, void* context)
{
if (!context)
return;
try
{
reinterpret_cast<AndroidOutputDevice*>(context)->handleCallback(bq);
}
catch(...)
{}
}
#endif // TARGET_ANDROID