原始文件未播放或播放不正确 - 双簧管 (Android-ndk)
Raw files aren't playing, or are playing incorrectly - Oboe (Android-ndk)
我正在尝试在我的 android 应用程序中播放原始 (int16 PCM) 编码的音频文件。我一直在关注和通读双簧管 documentation/samples 以尝试播放我自己的音频文件之一。
我需要播放的音频文件大约是 6kb,或 1592 帧(立体声)。
没有声音播放,或者 sound/jitter 在启动时播放(输出不同 - 见下文)
疑难解答
更新
我已经切换到缓冲区队列的浮点数,而不是将所有内容都保留为 int16_t(并在完成后转换回 int16_t),尽管现在我回到了无声状态。
音频似乎没有播放,或者在启动时播放(这是错误的)。我按 'start' 后应该会播放声音。
当仅使用 int16_t 实现应用程序时,过早的声音与缓冲区大小有关。如果缓冲区大小小于音频文件,则声音会非常快且被削波(在较小的缓冲区大小下更像无人机)。比原始音频大小更大,它看起来像是在循环播放,并且在更大的缓冲区大小下变得更安静。当按下开始按钮时,声音也会变成 "softer"。我什至不完全确定这意味着原始音频正在播放,它可能只是来自 Android.
的随机无意义的抖动
当用浮点数填充缓冲区,然后转换为int16_t时,没有播放音频。
(我试过 运行 systrace,但老实说我不知道我在找什么)
- 流打开正常。
- 在
createPlaybackStream()
中无法调整缓冲区大小(尽管它仍然以某种方式将其设置为突发大小的两倍)
- 流开始正常。
- 原始资源加载正常。
实施
我目前在构建器中尝试的内容:
- 正在将回调设置为
this
,或 onAudioReady()
- 正在将性能模式设置为
LowLatency
- 正在将共享模式设置为
Exclusive
- 将缓冲区容量设置为(大于我的音频文件帧数的任何值)
- 将突发大小(每次回调的帧数)设置为(任何等于或小于缓冲区容量/2)
我在这里使用 Rhythm Game 示例中的 Player
class 和 AAssetManager
class:https://github.com/google/oboe/blob/master/samples/RhythmGame。我正在使用这些 classes 来加载我的资源并播放声音。 Player.renderAudio
将音频数据写入输出缓冲区。
以下是我的音频引擎的相关方法:
void AudioEngine::createPlaybackStream() {
// // Load the RAW PCM data files into memory
std::shared_ptr<AAssetDataSource> soundSource(AAssetDataSource::newFromAssetManager(assetManager, "sound.raw", ChannelCount::Mono));
if (soundSource == nullptr) {
LOGE("Could not load source data for sound");
return;
}
sound = std::make_shared<Player>(soundSource);
AudioStreamBuilder builder;
builder.setCallback(this);
builder.setPerformanceMode(PerformanceMode::LowLatency);
builder.setSharingMode(SharingMode::Exclusive);
builder.setChannelCount(mChannelCount);
Result result = builder.openStream(&stream);
if (result == Result::OK && stream != nullptr) {
mSampleRate = stream->getSampleRate();
mFramesPerBurst = stream->getFramesPerBurst();
int channelCount = stream->getChannelCount();
if (channelCount != mChannelCount) {
LOGW("Requested %d channels but received %d", mChannelCount, channelCount);
}
// Set the buffer size to (burst size * 2) - this will give us the minimum possible latency while minimizing underruns
stream->setBufferSizeInFrames(mFramesPerBurst * 2);
if (setBufferSizeResult != Result::OK) {
LOGW("Failed to set buffer size. Error: %s", convertToText(setBufferSizeResult.error()));
}
// Start the stream - the dataCallback function will start being called
result = stream->requestStart();
if (result != Result::OK) {
LOGE("Error starting stream. %s", convertToText(result));
}
} else {
LOGE("Failed to create stream. Error: %s", convertToText(result));
}
}
DataCallbackResult AudioEngine::onAudioReady(AudioStream *audioStream, void *audioData, int32_t numFrames) {
int16_t *outputBuffer = static_cast<int16_t *>(audioData);
sound->renderAudio(outputBuffer, numFrames);
return DataCallbackResult::Continue;
}
// When the 'start' button is pressed, it calls this method with true
// There should be no sound on app start-up until this button is pressed
// Sound stops when 'stop' is pressed
setPlaying(bool isPlaying) {
sound->setPlaying(isPlaying);
}
Setting the buffer capacity to (anything bigger than my audio file frame count)
您不需要设置缓冲容量。这将自动设置为适合您的合理水平。通常约 3000 帧。请注意,缓冲区 capacity 与缓冲区 size 不同,后者默认为 2*framesPerBurst.
Setting the burst size (frames per call back) to (anything equal to or lower than the buffer capacity / 2)
再次强调,不要这样做。每次流需要更多音频数据时都会调用 onAudioReady
,numFrames
表示您应该提供多少帧。如果您用一个不是音频设备本机突发大小的精确比率的值覆盖此值(典型值为 128、192 和 240 帧,具体取决于底层硬件),那么您可能会遇到音频故障。
I have switched to floats for buffer queuing
您需要提供数据的格式由音频流决定,只有在流打开后才知道。调用stream->getFormat()
即可获取。
在 RhythmGame
示例中(至少 the version you're referring to)格式的工作原理如下:
- 源文件从 16 位转换为内部浮点数
AAssetDataSource::newFromAssetManager
(浮点数是任何类型信号处理的首选格式)
- 如果流格式为 16 位,则在
onAudioReady
中将其转换回
1592 frames (stereo).
您说您的来源是立体声,但您在此处将其指定为单声道:
std::shared_ptr soundSource(AAssetDataSource::newFromAssetManager(assetManager, "sound.raw", ChannelCount::Mono));
毫无疑问,这会导致音频问题,因为 AAssetDataSource
的 numFrames
值是正确值的两倍。这将导致音频故障,因为有一半时间您将播放系统内存的随机部分。
我正在尝试在我的 android 应用程序中播放原始 (int16 PCM) 编码的音频文件。我一直在关注和通读双簧管 documentation/samples 以尝试播放我自己的音频文件之一。
我需要播放的音频文件大约是 6kb,或 1592 帧(立体声)。
没有声音播放,或者 sound/jitter 在启动时播放(输出不同 - 见下文)
疑难解答
更新 我已经切换到缓冲区队列的浮点数,而不是将所有内容都保留为 int16_t(并在完成后转换回 int16_t),尽管现在我回到了无声状态。
音频似乎没有播放,或者在启动时播放(这是错误的)。我按 'start' 后应该会播放声音。
当仅使用 int16_t 实现应用程序时,过早的声音与缓冲区大小有关。如果缓冲区大小小于音频文件,则声音会非常快且被削波(在较小的缓冲区大小下更像无人机)。比原始音频大小更大,它看起来像是在循环播放,并且在更大的缓冲区大小下变得更安静。当按下开始按钮时,声音也会变成 "softer"。我什至不完全确定这意味着原始音频正在播放,它可能只是来自 Android.
的随机无意义的抖动
当用浮点数填充缓冲区,然后转换为int16_t时,没有播放音频。
(我试过 运行 systrace,但老实说我不知道我在找什么)
- 流打开正常。
- 在
createPlaybackStream()
中无法调整缓冲区大小(尽管它仍然以某种方式将其设置为突发大小的两倍) - 流开始正常。
- 原始资源加载正常。
实施
我目前在构建器中尝试的内容:
- 正在将回调设置为
this
,或onAudioReady()
- 正在将性能模式设置为
LowLatency
- 正在将共享模式设置为
Exclusive
- 将缓冲区容量设置为(大于我的音频文件帧数的任何值)
- 将突发大小(每次回调的帧数)设置为(任何等于或小于缓冲区容量/2)
我在这里使用 Rhythm Game 示例中的 Player
class 和 AAssetManager
class:https://github.com/google/oboe/blob/master/samples/RhythmGame。我正在使用这些 classes 来加载我的资源并播放声音。 Player.renderAudio
将音频数据写入输出缓冲区。
以下是我的音频引擎的相关方法:
void AudioEngine::createPlaybackStream() {
// // Load the RAW PCM data files into memory
std::shared_ptr<AAssetDataSource> soundSource(AAssetDataSource::newFromAssetManager(assetManager, "sound.raw", ChannelCount::Mono));
if (soundSource == nullptr) {
LOGE("Could not load source data for sound");
return;
}
sound = std::make_shared<Player>(soundSource);
AudioStreamBuilder builder;
builder.setCallback(this);
builder.setPerformanceMode(PerformanceMode::LowLatency);
builder.setSharingMode(SharingMode::Exclusive);
builder.setChannelCount(mChannelCount);
Result result = builder.openStream(&stream);
if (result == Result::OK && stream != nullptr) {
mSampleRate = stream->getSampleRate();
mFramesPerBurst = stream->getFramesPerBurst();
int channelCount = stream->getChannelCount();
if (channelCount != mChannelCount) {
LOGW("Requested %d channels but received %d", mChannelCount, channelCount);
}
// Set the buffer size to (burst size * 2) - this will give us the minimum possible latency while minimizing underruns
stream->setBufferSizeInFrames(mFramesPerBurst * 2);
if (setBufferSizeResult != Result::OK) {
LOGW("Failed to set buffer size. Error: %s", convertToText(setBufferSizeResult.error()));
}
// Start the stream - the dataCallback function will start being called
result = stream->requestStart();
if (result != Result::OK) {
LOGE("Error starting stream. %s", convertToText(result));
}
} else {
LOGE("Failed to create stream. Error: %s", convertToText(result));
}
}
DataCallbackResult AudioEngine::onAudioReady(AudioStream *audioStream, void *audioData, int32_t numFrames) {
int16_t *outputBuffer = static_cast<int16_t *>(audioData);
sound->renderAudio(outputBuffer, numFrames);
return DataCallbackResult::Continue;
}
// When the 'start' button is pressed, it calls this method with true
// There should be no sound on app start-up until this button is pressed
// Sound stops when 'stop' is pressed
setPlaying(bool isPlaying) {
sound->setPlaying(isPlaying);
}
Setting the buffer capacity to (anything bigger than my audio file frame count)
您不需要设置缓冲容量。这将自动设置为适合您的合理水平。通常约 3000 帧。请注意,缓冲区 capacity 与缓冲区 size 不同,后者默认为 2*framesPerBurst.
Setting the burst size (frames per call back) to (anything equal to or lower than the buffer capacity / 2)
再次强调,不要这样做。每次流需要更多音频数据时都会调用 onAudioReady
,numFrames
表示您应该提供多少帧。如果您用一个不是音频设备本机突发大小的精确比率的值覆盖此值(典型值为 128、192 和 240 帧,具体取决于底层硬件),那么您可能会遇到音频故障。
I have switched to floats for buffer queuing
您需要提供数据的格式由音频流决定,只有在流打开后才知道。调用stream->getFormat()
即可获取。
在 RhythmGame
示例中(至少 the version you're referring to)格式的工作原理如下:
- 源文件从 16 位转换为内部浮点数
AAssetDataSource::newFromAssetManager
(浮点数是任何类型信号处理的首选格式) - 如果流格式为 16 位,则在
onAudioReady
中将其转换回
1592 frames (stereo).
您说您的来源是立体声,但您在此处将其指定为单声道:
std::shared_ptr soundSource(AAssetDataSource::newFromAssetManager(assetManager, "sound.raw", ChannelCount::Mono));
毫无疑问,这会导致音频问题,因为 AAssetDataSource
的 numFrames
值是正确值的两倍。这将导致音频故障,因为有一半时间您将播放系统内存的随机部分。