Как выглядит чередующийся стереозвук PCM linear Int16 с прямым порядком байтов?

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

Я понимаю, что у моего аудио будет два канала, и, следовательно, сэмплы будут сохраняться в формате [слева][справа][слева][справа]... Что я не понимаю, так это то, что именно это означает. Я также читал, что каждый сэмпл хранится в формате [MSB слева][LSB слева][MSB справа][LSB справа]. Означает ли это, что каждое 16-разрядное целое число фактически кодирует два 8-разрядных кадра, или каждое 16-разрядное целое число является собственным кадром, предназначенным для левого или правого канала?

Всем спасибо. Любая помощь приветствуется.

Изменить: Если вы решили привести примеры, пожалуйста, обратитесь к следующему.

Контекст метода

В частности, я должен преобразовать перемеженный short[] в два числа с плавающей точкой [], каждый из которых представляет левый или правый канал. Я буду реализовывать это на Java.

public static float[][] deinterleaveAudioData(short[] interleavedData) {
    //initialize the channel arrays
    float[] left = new float[interleavedData.length / 2];
    float[] right = new float[interleavedData.length / 2];
    //iterate through the buffer
    for (int i = 0; i < interleavedData.length; i++) {
        //THIS IS WHERE I DON'T KNOW WHAT TO DO
    }
    //return the separated left and right channels
    return new float[][]{left, right};
}

Моя текущая реализация

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

public static float[][] deinterleaveAudioData(short[] interleavedData) {
    //initialize the channel arrays
    float[] left = new float[interleavedData.length / 2];
    float[] right = new float[interleavedData.length / 2];
    //iterate through the buffer
    for (int i = 0; i < left.length; i++) {
        left[i] = (float) interleavedData[2 * i];
        right[i] = (float) interleavedData[2 * i + 1];
    }
    //return the separated left and right channels
    return new float[][]{left, right};
}

Формат

Если кому-то нужна дополнительная информация о формате аудио, то все, что у меня есть, - это следующее.

  • Формат PCM 2-канальный с чередованием
  • Частота дискретизации составляет 44100
  • Количество шорт в буфере коротких [] 2048
  • Количество кадров в коротком буфере [] равно 1024
  • Количество кадров в пакете равно 1

4 ответа

Я понимаю, что у моего аудио будет два канала, и, следовательно, сэмплы будут сохраняться в формате [слева][справа][слева][справа]... Что я не понимаю, так это то, что именно это означает.

Чередующиеся данные PCM сохраняются по одной выборке на канал в порядке следования каналов, прежде чем перейти к следующей выборке. Кадр PCM состоит из группы выборок для каждого канала. Если у вас есть стереозвук с левым и правым каналами, тогда один сэмпл из каждого вместе создает кадр.

  • Кадр 0: [левый образец][правый образец]
  • Кадр 1: [левый образец][правый образец]
  • Кадр 2: [левый образец][правый образец]
  • Кадр 3: [левый образец][правый образец]
  • так далее...

Каждый образец представляет собой измерение и цифровое квантование давления в определенный момент времени. То есть, если у вас есть 8 битов на выборку, у вас есть 256 возможных уровней точности, на которых можно измерять давление. Зная, что звуковые волны - это... волны... с пиками и долинами, мы захотим измерить расстояние от центра. Таким образом, мы можем определить центр в 127 или около того, вычесть и добавить оттуда (от 0 до 255, без знака), или мы можем рассматривать эти 8 бит как со знаком (одинаковые значения, просто различную их интерпретацию) и переходить от -128 до 127.

Используя 8 бит на семпл с одноканальным (моно) звуком, мы используем один байт на семпл, что означает, что одна секунда аудио, сэмплированного с частотой 44,1 кГц, использует ровно 44,100 байт памяти.

Теперь давайте предположим 8 бит на семпл, но в стереофоническом режиме с частотой 44,1 кГц. Любой другой байт будет для левого, и каждый другой будет для R.

LRLRLRLRLRLRLRLRLRLRLR...

Увеличьте его до 16 бит, и у вас будет два байта на выборку (выборки устанавливаются в скобках [ а также ] пробелы указывают границы кадра)

[LL][RR] [LL][RR] [LL][RR] [LL][RR] [LL][RR] [LL][RR]...

Я также читал, что каждый сэмпл хранится в формате [MSB слева][LSB слева][MSB справа][LSB справа].

Не обязательно. Аудио может быть сохранено в любой последовательности. Little endian является наиболее распространенным, но это не волшебное правило. Тем не менее, я думаю, что все каналы идут по порядку всегда, и передний левый канал будет в большинстве случаев каналом 0.

Означает ли это, что каждое 16-разрядное целое число фактически кодирует два 8-разрядных кадра, или каждое 16-разрядное целое число является собственным кадром, предназначенным для левого или правого канала?

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

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

Давайте начнем с получения некоторой терминологии с пути

  • Канал - это монофонический поток сэмплов. Этот термин не обязательно означает, что выборки являются непрерывными в потоке данных.
  • Фрейм - это набор сопутствующих образцов. Для стереозвука (например, каналов L & R) кадр содержит две выборки.
  • Пакет состоит из 1 или более кадров и обычно представляет собой минимальное количество кадров, которые могут быть обработаны системой за один раз. Для PCM Audio пакет часто содержит 1 кадр, но для сжатого аудио он будет больше.
  • Чередование - это термин, обычно используемый для стереозвука, в котором поток данных состоит из последовательных кадров звука. Поэтому поток выглядит как L1R1L2R2L3R3......LnRn

Существуют форматы аудио как с прямым, так и с прямым порядком байтов, которые зависят от варианта использования. Однако при обмене данными между системами обычно возникает проблема - вы всегда будете использовать собственный порядок байтов при обработке или взаимодействии со звуковыми компонентами операционной системы.

Вы не говорите, используете ли вы систему с прямым или обратным порядком байтов, но я подозреваю, что это, вероятно, первая. В этом случае вам нужно повернуть образцы в обратном порядке.

Хотя это не в камне, при использовании образцов с плавающей точкой обычно находятся в диапазоне -1.0<x<+1.0так что вы хотите разделить образцы на 1<<15, Когда используются 16-битные линейные типы, они обычно подписаны.

Забота о замене байтов и преобразовании формата:

int s = (int) interleavedData[2 * i];
short revS = (short) (((s & 0xff) << 8) | ((s >> 8) & 0xff)) 
left[i] = ((float) revS) / 32767.0f;

На самом деле вы имеете дело с почти типичным WAVE-файлом с качеством Audio CD, то есть:

  • 2 канала
  • частота дискретизации 44100 кГц
  • каждая выборка амплитуды квантована на 16-разрядное целое число со знаком

Я сказал почти, потому что big-endianness обычно используется в файлах AIFF (мир Mac), а не в файлах WAVE (мир PC). И я не знаю без поиска, как бороться с порядком байтов в Java, поэтому я оставлю эту часть вам.

О том, как хранятся образцы, довольно просто:

  • каждая выборка занимает 16 бит (целое число от -32768 до +32767)
  • если каналы чередуются: (L,1),(R,1),(L,2),(R,2),...,(L,n),(R,n)
  • если каналы не являются: (L,1),(L,2),...,(L,n),(R,1),(R,2),...,(R,n)

Затем для подачи звукового обратного вызова обычно требуется предоставить 32-битную плавающую точку в диапазоне от -1 до +1. И, возможно, именно здесь что-то может отсутствовать в вашем аглорите. Разделив ваши целые числа на 32768 (2^(16-1)), вы получите правильное звучание.

Я столкнулся с аналогичной проблемой с де-чередование short[] frames которые пришли через Spotify Android SDK onAudioDataDelivered().

Документация для onAudioDelivered был плохо написан год назад. Смотрите выпуск Github. Они обновили документы с улучшенным описанием и более точными именами параметров:

onAudioDataDelivered(short[] samples, int sampleCount, int sampleRate, int channels)

Что может смутить то, что samples.length может быть 4096. Однако, он содержит только sampleCount действительные образцы. Если вы получаете стереозвук, и sampleCount = 2048 есть только 1024 кадра (каждый кадр имеет два образца) аудио в samples массив!

Поэтому вам нужно обновить свою реализацию, чтобы убедиться, что вы работаете с sampleCount и не samples.length,

Другие вопросы по тегам