【问题标题】:Raw files aren't playing, or are playing incorrectly - Oboe (Android-ndk)原始文件未播放或播放不正确 - 双簧管 (Android-ndk)
【发布时间】:2019-03-24 01:35:07
【问题描述】:

我正在尝试在我的 android 应用程序中播放原始 (int16 PCM) 编码的音频文件。我一直在关注并阅读双簧管文档/示例,以尝试播放我自己的音频文件之一。

我需要播放的音频文件大约为 6kb,即 1592 帧(立体声)。

启动时不播放声音,或播放声音/抖动(输出不同 - 见下文)

疑难解答

更新 我已经切换到浮点数进行缓冲区排队,而不是将所有内容都保留为 int16_t(并在完成后转换回 int16_t),尽管现在我又没有声音了。

音频似乎没有播放,或者在启动时播放(这是错误的)。我按“开始”后应该播放声音。

  • 当应用程序仅使用 int16_t 实现时,过早的声音与缓冲区大小有关。如果缓冲区大小小于音频文件,则声音非常快且被削波(在缓冲区大小较小时更像无人机)。比原始音频大小更大,它似乎在循环播放,并且在更高的缓冲区大小下变得更安静。按下开始按钮时,声音也会变得“更柔和”。我什至不完全确定这是否意味着正在播放原始音频,它可能只是来自 Android 的随机无意义的抖动。

  • 用浮点数填充缓冲区,然后转换为 int16_t 时,不播放音频。

(我尝试过运行 systrace,但老实说我不知道​​我在寻找什么)

  • 流打开正常。
  • 缓冲区大小无法在 createPlaybackStream() 中调整(尽管它仍以某种方式将其设置为突发大小的两倍)
  • 流开始正常。
  • 原始资源加载正常。

实施

我目前在构建器中尝试的内容:

  • 将回调设置为this,或onAudioReady()
  • 将性能模式设置为LowLatency
  • 设置分享模式为Exclusive
  • 将缓冲区容量设置为(大于我的音频文件帧数)
  • 将突发大小(每次回调的帧数)设置为(等于或小于缓冲区容量 / 2 的任意值)

我在这里使用节奏游戏示例中的Player 类和AAssetManager 类:https://github.com/google/oboe/blob/master/samples/RhythmGame。我正在使用这些类来加载我的资源并播放声音。 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);
}

【问题讨论】:

    标签: c++ android-ndk audio-player oboe


    【解决方案1】:

    将缓冲区容量设置为(大于我的音频文件帧数)

    您无需设置缓冲容量。这将自动为您设置在合理的水平。通常约为 3000 帧。请注意,缓冲区 capacity 不同于缓冲区 size,后者默认为 2*framesPerBurst。

    将突发大小(每次回调的帧数)设置为(等于或小于缓冲区容量 / 2 的任意值)

    再一次,不要这样做。每次流需要更多音频数据时都会调用onAudioReadynumFrames 指示您应该提供多少帧。如果您使用与音频设备的本机突发大小(典型值为 128、192 和 240 帧,具体取决于底层硬件)不完全比率的值覆盖此值,则可能会出现音频故障。

    我已经切换到浮动缓冲区队列

    您需要提供数据的格式由音频流决定,只有在打开流后才能知道。您可以致电stream-&gt;getFormat() 获取。

    RhythmGame 示例(至少the version you're referring to)中,格式的工作原理如下:

    1. 源文件从 16 位转换为 AAssetDataSource::newFromAssetManager 内的浮点数(浮点数是任何类型信号处理的首选格式)
    2. 如果流格式是 16 位,则将其转换回 onAudioReady

    1592 帧(立体声)。

    您说您的源是立体声,但您在这里将其指定为单声道:

    std::shared_ptr soundSource(AAssetDataSource::newFromAssetManager(assetManager, "sound.raw", ChannelCount::Mono));

    毫无疑问,这会导致音频问题,因为AAssetDataSource 的值是numFrames,是正确值的两倍。这将导致音频故障,因为您将有一半时间播放系统内存的随机部分。

    【讨论】:

    • 谢谢!这成功了。我还重新导出了我的音频文件,以确保它们实际上在正确的通道数中。在我的困惑中,我可能认为它们是立体声的,但它们可能是单声道音频样本。即便如此,它也可能来自导出前的文件,因为我注意到大胆地将音轨列为“32 位立体声”。我仔细检查了它们都是 48000 赫兹、16 位、立体声,然后重新导出为无头原始 Pcm 16 位。
    • 很高兴它成功了。您可能想查看新版本的 RhythmGame 示例,它使用压缩资源 (MP3) 来节省应用程序的大小。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-08-29
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多