原始文件无法播放或播放不正确 - 双簧管(Android-ndk)

问题描述 投票:1回答:1

我正在尝试在我的Android应用程序中播放Raw(int16 PCM)编码的音频文件。我一直关注并阅读Oboe文档/示例,试图获取我自己的音频文件。

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

要么没有声音播放,要么声音/抖动在启动时播放(输出变化不同 - 见下文)

故障排除

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

音频似乎要么没有播放,要么在启动时播放(这是错误的)。按'开始'后声音应播放。

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

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

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

履行

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

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

我在这里使用来自Rhythm Game样本的Exclusive类和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;
}
c++ android-ndk audio-player oboe
1个回答
1
投票

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

您无需设置缓冲区容量。这将自动设置在合理的水平。通常~3000帧。请注意,缓冲区容量与缓冲区大小不同,默认为2 * framesPerBurst。

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

再说一次,不要这样做。每次流需要更多音频数据时都会调用// 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); } onAudioReady表示你应该提供多少帧。如果使用的值不是音频设备的本机突发大小(典型值为128,192和240帧,具体取决于底层硬件)的精确比率,则覆盖此值,则可能会出现音频故障。

我已切换到浮动缓冲区排队

提供数据所需的格式由音频流决定,只有在打开流后才能知道。你可以通过调用numFrames来获得它。

stream->getFormat()样本(至少RhythmGame)中,格式如何工作:

  1. 源文件在the version you're referring to中从16位转换为float(浮点数是任何类型信号处理的首选格式)
  2. 如果流格式为16位,则将其转换回AAssetDataSource::newFromAssetManager内部

1592帧(立体声)。

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

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

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

© www.soinside.com 2019 - 2024. All rights reserved.