OpenSL SL_IID_RATEPITCH и SL_IID_VOLUME для очередей буферов PCM на Android
Я создал многоканальную аудиосистему в OpenSL ES на Android NDK с использованием буферных очередей PCM. Я не могу заставить ОС поддерживать SL_IID_RATEPITCH и SL_IID_VOLUME, несмотря на то, что в документации для Android говорится, что эти два интерфейса поддерживаются. Ниже мой код инициализации. Я делаю что-то неправильно?
static SLresult InitChannel(int i)
{
SLresult lRes;
OpenSLChannel *channel = &sndc[i];
// Initialize stuff for playing PCM channels
// Set-up sound audio source.
SLDataLocator_AndroidSimpleBufferQueue lDataLocatorIn;
lDataLocatorIn.locatorType = SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE;
// At most one buffer in the queue.
lDataLocatorIn.numBuffers = 1;
SLDataFormat_PCM lDataFormat;
lDataFormat.formatType = SL_DATAFORMAT_PCM;
lDataFormat.numChannels = 1; // Mono sound.
lDataFormat.samplesPerSec = SL_SAMPLINGRATE_22_05; // BASE_FREQUENCY
lDataFormat.bitsPerSample = SL_PCMSAMPLEFORMAT_FIXED_16;
lDataFormat.containerSize = SL_PCMSAMPLEFORMAT_FIXED_16;
lDataFormat.channelMask = SL_SPEAKER_FRONT_CENTER;
lDataFormat.endianness = SL_BYTEORDER_LITTLEENDIAN;
SLDataSource lDataSource;
lDataSource.pLocator = &lDataLocatorIn;
lDataSource.pFormat = &lDataFormat;
SLDataLocator_OutputMix lDataLocatorOut;
lDataLocatorOut.locatorType = SL_DATALOCATOR_OUTPUTMIX;
lDataLocatorOut.outputMix = mOutputMixObj;
SLDataSink lDataSink;
lDataSink.pLocator = &lDataLocatorOut;
lDataSink.pFormat = NULL;
// Create and realize the sound player.
// We are going to need its SL_IID_PLAY and also SL_IID_BUFFERQUEUE interface
// now available thanks to the data locator configured in the previous step.
const SLuint32 lSoundPlayerIIDCount = 4;
const SLInterfaceID lSoundPlayerIIDs[] = { SL_IID_PLAY, SL_IID_BUFFERQUEUE, SL_IID_VOLUME, SL_IID_RATEPITCH };
const SLboolean lSoundPlayerReqs[] = { SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE, SL_BOOLEAN_FALSE, SL_BOOLEAN_FALSE };
lRes = (*mEngine)->CreateAudioPlayer(mEngine, &channel->mPlayerObj, &lDataSource, &lDataSink, lSoundPlayerIIDCount, lSoundPlayerIIDs, lSoundPlayerReqs);
if (lRes != SL_RESULT_SUCCESS)
return lRes;
lRes = (*channel->mPlayerObj)->Realize(channel->mPlayerObj, SL_BOOLEAN_FALSE);
if (lRes != SL_RESULT_SUCCESS)
return lRes;
lRes = (*channel->mPlayerObj)->GetInterface(channel->mPlayerObj, SL_IID_PLAY, &channel->mPlayer);
if (lRes != SL_RESULT_SUCCESS)
return lRes;
lRes = (*channel->mPlayerObj)->GetInterface(channel->mPlayerObj, SL_IID_BUFFERQUEUE, &channel->mPlayerQueue);
if (lRes != SL_RESULT_SUCCESS)
return lRes;
// Get Volume Interface
lRes = (*channel->mPlayerObj)->GetInterface(channel->mPlayerObj, SL_IID_VOLUME, &channel->mVolume);
if (lRes != SL_RESULT_SUCCESS)
{
Err_Printf("Volume interface not supported.\n");
// return lRes;
}
lRes = (*channel->mPlayerObj)->GetInterface(channel->mPlayerObj, SL_IID_RATEPITCH, &channel->mRatePitch);
if (lRes != SL_RESULT_SUCCESS)
{
Err_Printf("RatePitch interface not supported.\n");
// return lRes;
}
lRes = (*channel->mPlayerQueue)->RegisterCallback(channel->mPlayerQueue, SoundFinished, channel);
// slCheckErrorWithStatus(lRes, "Problem registering player callback (Error %d).", lRes);
lRes = (*channel->mPlayer)->SetCallbackEventsMask(channel->mPlayer, SL_PLAYEVENT_HEADATEND);
// slCheckErrorWithStatus(lRes, "Problem registering player callback mask (Error %d).", lRes);
}
//
// SystemInit
//
// Initialization for
// the sound subsystem.
//
void SystemInit(void)
{
mEngineObj = NULL;
mEngine = NULL;
mOutputMixObj = NULL;
Err_Printf("Starting OpenSL ES...\n");
SLresult lRes;
const SLuint32 lEngineMixIIDCount = 1;
const SLInterfaceID lEngineMixIIDs[] = { SL_IID_ENGINE };
const SLboolean lEngineMixReqs[] = { SL_BOOLEAN_TRUE };
const SLuint32 lOutputMixIIDCount = 2;
const SLInterfaceID lOutputMixIIDs[] = { SL_IID_VOLUME, SL_IID_RATEPITCH };
const SLboolean lOutputMixReqs[] = { SL_BOOLEAN_FALSE, SL_BOOLEAN_FALSE };
lRes = slCreateEngine(&mEngineObj, 0, NULL, lEngineMixIIDCount, lEngineMixIIDs, lEngineMixReqs);
if (lRes != SL_RESULT_SUCCESS)
goto ERROR; // lolwut?
lRes = (*mEngineObj)->Realize(mEngineObj, SL_BOOLEAN_FALSE);
if (lRes != SL_RESULT_SUCCESS)
goto ERROR;
lRes = (*mEngineObj)->GetInterface(mEngineObj, SL_IID_ENGINE, &mEngine);
if (lRes != SL_RESULT_SUCCESS)
goto ERROR;
lRes = (*mEngine)->CreateOutputMix(mEngine, &mOutputMixObj, lOutputMixIIDCount, lOutputMixIIDs, lOutputMixReqs);
lRes = (*mOutputMixObj)->Realize(mOutputMixObj, SL_BOOLEAN_FALSE);
int i;
for (i = 0; i < NUMCHANNELS; i++)
{
lRes = InitChannel(i);
if (lRes != SL_RESULT_SUCCESS)
goto ERROR;
}
return;
ERROR:
Err_Printf("Error while starting OpenSL ES.");
SystemShutdown();
}
1 ответ
Решение
SL_IID_VOLUME поддерживается, однако он допускает только ослабление (максимальное усиление равно 0). Хорошая аппроксимация OpenAL 0,0-1,0:
float attenuation = 1.0f / 1024.0f + gain * 1023.0f / 1024.0f;
float db = 3 * log10(attenuation) / log10(2);
SLmillibel setGain = (SLmillibel)(db * 1000);
SL_IID_RATEPITCH не поддерживается, но SL_IID_PLAYBACKRATE есть. К сожалению, в данный момент он позволяет устанавливать скорость только от 0,5 до 2,0.