Android OpenSL ES - 以44.1Khz采样的.wav文件问题

时间:2014-11-21 16:38:39

标签: android-ndk java-native-interface opensl

我尝试将部分 OpenAL 代码转换为 OpenSL ES 以用于我的Android使用(Kitkat 4.4.4) Genymotion 上遇到了在 44.1Khz 采样的.wav文件的问题。我的应用程序是 native one(glue)。

我已经关注 Android NDK 示例的 / native-audio 示例以及优秀书籍 Android NDK初学者指南中的片段,所以我的代码在大多数wav / PCM数据上都能正常运行,除了那些以44.1Khz采样的数据。我的具体代码如下:

引擎初始

  // create OpenSL ES engine
  SLEngineOption EngineOption[] = {(SLuint32) SL_ENGINEOPTION_THREADSAFE, (SLuint32) SL_BOOLEAN_TRUE};
  const SLInterfaceID lEngineMixIIDs[] = {SL_IID_ENGINE};
  const SLboolean lEngineMixReqs[] = {SL_BOOLEAN_TRUE};
  SLresult res = slCreateEngine(&mEngineObj, 1, EngineOption, 1, lEngineMixIIDs, lEngineMixReqs);
  res = (*mEngineObj)->Realize(mEngineObj, SL_BOOLEAN_FALSE);
  res = (*mEngineObj)->GetInterface(mEngineObj, SL_IID_ENGINE, &mEngine);   // get 'engine' interface
  // create output mix (AKA playback; this represents speakers, headset etc.)
  res = (*mEngine)->CreateOutputMix(mEngine, &mOutputMixObj, 0,NULL, NULL);
  res = (*mOutputMixObj)->Realize(mOutputMixObj, SL_BOOLEAN_FALSE);

播放器初始化

  SLresult lRes;
  // Set-up sound audio source.
  SLDataLocator_AndroidSimpleBufferQueue lDataLocatorIn;
  lDataLocatorIn.locatorType = SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE;
  lDataLocatorIn.numBuffers = 1;   // 1 buffer for a one-time load
  // analyze and set correct PCM format
  SLDataFormat_PCM lDataFormat;
  lDataFormat.formatType = SL_DATAFORMAT_PCM;

  lDataFormat.numChannels = audio->wav.channels;     // etc. 1,2
  lDataFormat.samplesPerSec = audio->wav.sampleRate * 1000;   // etc. 44100 * 1000
  lDataFormat.bitsPerSample = audio->wav.bitsPerSample;   // etc. 16
  lDataFormat.containerSize = audio->wav.bitsPerSample;
  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;

  const SLInterfaceID lSoundPlayerIIDs[] = { SL_IID_PLAY, SL_IID_ANDROIDSIMPLEBUFFERQUEUE };
  const SLboolean lSoundPlayerReqs[] = { SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE };
  lRes = (*mEngine)->CreateAudioPlayer(mEngine, &mPlayerObj, &lDataSource, &lDataSink, 2, lSoundPlayerIIDs, lSoundPlayerReqs);
  if (lRes != SL_RESULT_SUCCESS) { return; }
  lRes = (*mPlayerObj)->Realize(mPlayerObj, SL_BOOLEAN_FALSE);
  if (lRes != SL_RESULT_SUCCESS) { return; }
  lRes = (*mPlayerObj)->GetInterface(mPlayerObj, SL_IID_PLAY, &mPlayer);
  if (lRes != SL_RESULT_SUCCESS) { return; }

  lRes = (*mPlayerObj)->GetInterface(mPlayerObj, SL_IID_ANDROIDSIMPLEBUFFERQUEUE, &mPlayerQueue);
  if (lRes != SL_RESULT_SUCCESS) { return; }
  // register callback on the buffer queue
  lRes = (*mPlayerQueue)->RegisterCallback(mPlayerQueue, bqPlayerQueueCallback, NULL);
  if (lRes != SL_RESULT_SUCCESS) { return; }
  lRes = (*mPlayer)->SetCallbackEventsMask(mPlayer, SL_PLAYEVENT_HEADATEND);
  if (lRes != SL_RESULT_SUCCESS) { return; }

  // ..fetch the data in 'audio->data' from opened FILE* stream and set 'datasize'

  // feed the buffer with data
  lRes = (*mPlayerQueue)->Clear(mPlayerQueue);   // remove any sound from buffer
  lRes = (*mPlayerQueue)->Enqueue(mPlayerQueue, audio->data, datasize);

上述效果适用于 8000 22050 32000 样本/秒,但 41100 样本,5次中有4次重复播放第一次播放时有很多时间。它就像是一个敲门音效,它实际上以单->SetPlayState(..SL_PLAYSTATE_PLAYING);和速度循环多次(约50次)。我的代码有任何明显错误吗?这些抽样的多线程问题?其他人有这种问题吗?我应该对41.1Khz案件进行下采样吗?它可能是 Genymotion 问题吗? TX

1 个答案:

答案 0 :(得分:0)

我通过从 44Khz 22Khz 的下采样解决了这个问题。有趣的是,这只发生在包含1个通道和44,100个样本的声音上;在所有其他情况下,没有问题。