Android, C : как преобразовать частоту дискретизации звука с помощью ресэмплера oboe

#android #c #audio #resampling #oboe

#Android #c #Аудио #повторная дискретизация #гобой

Вопрос:

Я использую oboe для воспроизведения звуковых файлов на Android. У меня есть файлы с частотой 44,1 кГц и 48 кГц, которые я хочу иметь возможность воспроизводить в одном и том же аудиопотоке, поэтому мне нужно выполнить повторную выборку.

Декодирование и воспроизведение файлов работает нормально, но поскольку у меня две разные частоты дискретизации, мне нужно выполнить повторную дискретизацию (в настоящее время я пытаюсь выполнить с 44,1 до 48, поскольку мой аудиопоток составляет 48 кГц.)

Итак, я пытаюсь выполнить передискретизацию с помощью передискретизатора oboe, но я не совсем понимаю, как это сделать. Следуя руководству readme по преобразованию фиксированного количества входных кадров (я предполагаю, что это то, что я должен сделать?), Я попытался реализовать следующим образом. Первая часть кода получает декодированное и возвращает, если частоты дискретизации равны (эта часть работает так, как задумано), во второй части я пытаюсь выполнить повторную выборку при необходимости:

 StorageDataSource *StorageDataSource::newFromStorageAsset(AMediaExtractor amp;extractor,
                                                          const char *fileName,
                                                          AudioProperties targetProperties) {

    std::ifstream stream;
    stream.open(fileName, std::ifstream::in | std::ifstream::binary);
    stream.seekg(0, std::ios::end);
    long size = stream.tellg();
    stream.close();

    constexpr int kMaxCompressionRatio{12};
    const long maximumDataSizeInBytes =
            kMaxCompressionRatio * (size) * sizeof(int16_t);
    auto decodedData = new uint8_t[maximumDataSizeInBytes];

    int32_t rate = NDKExtractor::getSampleRate(extractor);
    int32_t *inputSampleRate = amp;rate;

    int64_t bytesDecoded = NDKExtractor::decode(extractor, decodedData, targetProperties);
    auto numSamples = bytesDecoded / sizeof(int16_t);

    auto outputBuffer = std::make_unique<float[]>(numSamples);

    // The NDK decoder can only decode to int16, we need to convert to floats
    oboe::convertPcm16ToFloat(
            reinterpret_cast<int16_t *>(decodedData),
            outputBuffer.get(),
            bytesDecoded / sizeof(int16_t));

 if (*inputSampleRate == targetProperties.sampleRate) {
        return new StorageDataSource(std::move(outputBuffer),
                                     numSamples,
                                     targetProperties);
    } else {

        // this is where I try to convert the sample rate

        float *inputBuffer;
        inputBuffer = reinterpret_cast<float *>(decodedData); // is this correct?

        float *outputBuffer2;    // multi-channel buffer to be filled, TODO improve name
        int numInputFrames;  // number of frames of input

        // TODO is this correct?
        numInputFrames = numSamples / 2;

        int numOutputFrames = 0;
        int channelCount = 2;  

        resampler::MultiChannelResampler *mResampler = resampler::MultiChannelResampler::make(
                2, // channel count
                44100, // input sampleRate
                48000, // output sampleRate
                resampler::MultiChannelResampler::Quality::Best); // conversion quality

        int inputFramesLeft = numInputFrames;

        while (inputFramesLeft > 0) {

            if (mResampler->isWriteNeeded()) {
                mResampler->writeNextFrame(inputBuffer);
                inputBuffer  = channelCount;
                inputFramesLeft--;
            } else {
                mResampler->readNextFrame(outputBuffer2);
                outputBuffer2  = channelCount;
                numOutputFrames  ;
            }
        }
        delete mResampler;

// return is missing!
    }

// returning the original data since above code doesn't work properly yet
 return new StorageDataSource(std::move(outputBuffer),
                                     numSamples,
                                     targetProperties);
}
  

Повторная дискретизация завершается сбоем с SIGSEV :

 A: signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr 0x7fe69c7000
A:     x0  0000007c0e3d1e00  x1  0000007fe69c7000  x2  0000007bb77dd198  x3  0000007bf5432140
A:     x4  0000000000000021  x5  8080800000000000  x6  fefeff7b976e0667  x7  7f7f7f7fff7f7f7f
A:     x8  0000000000000660  x9  0000000000000660  x10 0000000000000000  x11 0000007bf5435840
A:     x12 0000007bb77dd118  x13 0000000000000008  x14 0000007bf54321c0  x15 0000000000000008
A:     x16 0000007bf5432200  x17 0000000000000000  x18 0000007fe69bf7ba  x19 0000007c14e14c00
A:     x20 0000000000000000  x21 0000007c14e14c00  x22 0000007fe69c0d70  x23 0000007bfc6e5dc7
A:     x24 0000000000000008  x25 0000007c9b7705f8  x26 0000007c14e14ca0  x27 0000000000000002
A:     x28 0000007fe69c0aa0  x29 0000007fe69c0420
A:     sp  0000007fe69c0400  lr  0000007bf94f61f0  pc  0000007bf9501b5c
A: backtrace:
A:     #00 pc 0000000000078b5c  /data/app/myapp-G-GmPWmPgOGfffk-qHsQxw==/lib/arm64/libnative-lib.so (resampler::PolyphaseResamplerStereo::readFrame(float*) 684)
A:     #01 pc 000000000006d1ec  /data/app/myapp-G-GmPWmPgOGfffk-qHsQxw==/lib/arm64/libnative-lib.so (resampler::MultiChannelResampler::readNextFrame(float*) 44)
A:     #02 pc 000000000006c84c  /data/app/myapp-G-GmPWmPgOGfffk-qHsQxw==/lib/arm64/libnative-lib.so (StorageDataSource::newFromStorageAsset(AMediaExtractoramp;, char const*, AudioProperties) 1316)
A:     #03 pc 78bbcdd7f9b20dbe  <unknown>
  

Вот мои основные проблемы:
Во-первых, как мне правильно определить количество кадров на моем входе? Как именно фреймы работают с аудиоданными? Я исследовал это, и я все еще не уверен, что понял это? Является ли это постоянным числом? Как я могу рассчитать количество кадров. Как это соотносится с сэмплами, частотой дискретизации и скоростью передачи битов?

Во-вторых, использую ли я вообще правильные входные данные? Я использую свое decodedData значение, поскольку это то, что я получаю обратно от декодера, и только reinterpret_cast это float* означает.

Поскольку у меня довольно мало опыта работы с C , я не уверен, правильно ли то, что я делаю, и, возможно, я привожу несколько ошибок в этот фрагмент кода.

Редактировать: Поскольку я пытаюсь выполнить повторную выборку моего декодированного вывода, я предполагаю, что эта часть информации об PCM из here объясняет, что здесь подразумевается под фреймами:

 For encodings like PCM, a frame consists of the set of samples for all channels at a given point in time, and so the size of a frame (in bytes) is always equal to the size of a sample (in bytes) times the number of channels.
  

Правильно ли это в моем случае? Это означало бы, что я могу вычесть количество кадров из количества сэмплов, длины моего звукового бита и количества каналов?

Комментарии:

1. К сожалению, я не могу ответить на все вопросы, но я хочу попытаться немного помочь с пониманием кадров. «Как я могу рассчитать количество кадров». — для формата PCM (*.wav) общее количество кадров равно «длительности в секундах * частоте кадров». Допустим, у вас есть аудио с частотой кадров 44100 (44,1 кГц) и длительностью 5 секунд. Это означает, что количество кадров будет равно 44100 * 5.

2. То есть это означает, что кадр содержит информацию для всех каналов этого файла за определенный момент времени, как указано выше? Итак, если у меня есть файл PCM 44100Hz и я знаю его размер, могу ли я также каким-то образом узнать продолжительность? Какова связь между размером файла и длительностью?

3. @michpohl ты заставил это работать? Мне нужна похожая вещь

4. К сожалению, нет, я этого не делал. Поскольку это было более удобно для меня, я в конечном итоге пропустил это.