Сырые файлы не воспроизводятся или воспроизводятся некорректно - Oboe (Android-ndk) - PullRequest
1 голос
/ 24 марта 2019

Я пытаюсь воспроизвести аудиофайл в формате Raw (int16 PCM) в моем приложении для Android.Я следил и читал документацию / образцы Oboe, чтобы попытаться воспроизвести один из моих собственных аудиофайлов.

Размер аудиофайла, который мне нужно воспроизвести, составляет примерно 6 КБ или 1592 кадра (стерео).

Либо не воспроизводится звук, либо воспроизводится звук / джиттер при запуске (с различным выходным сигналом - см. Ниже)

Устранение неполадок

обновление Я переключился на число с плавающей точкой для очередей в буфере, вместо того, чтобы сохранять все в int16_t (и преобразовать обратно в int16_t, когда закончил), хотя теперь я вернулся к отсутствию звука.

Звук, кажется, либоиграть или играть при запуске (что неправильно).Звук должен воспроизводиться после нажатия кнопки «Пуск».

  • Когда приложение было реализовано только с int16_t, преждевременное звучание было относительно размера буфера.Если размер буфера меньше, чем аудиофайл, звук будет очень быстрым и обрезанным (более похожим на дрон при меньших размерах буфера).Кажется, что он больше размера необработанного аудио, он воспроизводится в цикле и становится тише при больших размерах буфера.Звук также станет «мягче» при нажатии кнопки запуска.Я даже не совсем уверен, что это означает, что воспроизводился необработанный звук, это могли быть случайные бессмысленные дрожания от Android.

  • При заполнении буферов поплавками и последующем преобразовании в int16_t,звук не воспроизводится.

(я пробовал запустить systrace, но, честно говоря, не знаю, что ищу)

  • Поток открываетсяотлично.
  • Размер буфера не может быть скорректирован в createPlaybackStream() (хотя каким-то образом он все равно устанавливает его в два раза больше размера пакета)
  • Поток запускается нормально.
  • Сырые ресурсы загружаются нормально.

Реализация

Что я сейчас пытаюсь в сборщике:

  • Настройка обратного вызована this или onAudioReady()
  • Установка режима производительности на LowLatency
  • Установка режима совместного использования на Exclusive
  • Установка емкости буфера на (что угоднобольше, чем количество кадров в моем аудиофайле)
  • Установка размера пакета (количество кадров на вызов back) to (что-либо равное или меньшее, чем емкость буфера / 2)

Я использую класс Player и класс AAssetManager из образца Rhythm Game здесь: 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);
}

1 Ответ

1 голос
/ 27 марта 2019

Установка емкости буфера (больше, чем количество кадров моего аудиофайла)

Вам не нужно устанавливать емкость буфера. Это будет установлено автоматически на разумном для вас уровне. Обычно ~ 3000 кадров. Обратите внимание, что буфер емкость отличается от буфера размер , который по умолчанию равен 2 * framesPerBurst.

Установка размера пакета (кадров на обратный вызов) равным (равным или меньшим, чем емкость буфера / 2)

Опять же, не делайте этого. onAudioReady будет вызываться каждый раз, когда потоку требуется больше аудиоданных, а numFrames указывает, сколько кадров вы должны предоставить. Если вы переопределите это значение значением, которое не является точным отношением собственного размера пакета аудиоустройства (типичные значения составляют 128, 192 и 240 кадров в зависимости от используемого оборудования), то вы можете получить глюки звука.

Я переключился на поплавки для очередей в буфере

Формат, в котором необходимо предоставить данные, определяется аудиопотоком, и он известен только после того, как поток был открыт. Вы можете получить его, позвонив по номеру stream->getFormat().

В примере RhythmGame (по крайней мере, версия, на которую вы ссылаетесь ), вот как работают форматы:

  1. Исходный файл преобразуется из 16-разрядного в число с плавающей точкой внутри AAssetDataSource::newFromAssetManager (числа с плавающей запятой являются предпочтительным форматом для любого вида обработки сигналов)
  2. Если формат потока 16-битный, преобразуйте его обратно в onAudioReady

1592 кадра (стерео).

Вы сказали, что ваш источник стерео, но вы указываете его как моно здесь:

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

Без сомнения, это вызовет проблемы со звуком, поскольку AAssetDataSource будет иметь значение для numFrames, что вдвое больше правильного значения. Это вызовет сбои звука, потому что половину времени вы будете воспроизводить случайные части системной памяти.

...