Как выглядит чередующийся стереозвук 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
,