Уменьшение битовой глубины выборки путем усечения - PullRequest
0 голосов
/ 26 октября 2010

Мне нужно уменьшить разрядность цифрового аудиосигнала с 24 до 16 бит.

Взятие только 16 старших значащих битов (т.е. усечение) каждого образца эквивалентно выполнению пропорционального вычисления (out = in * 0xFFFF / 0xFFFFFF)?

Ответы [ 4 ]

3 голосов
/ 26 октября 2010

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

1 голос
/ 26 октября 2010

Дизеринг добавлением шума в целом даст вам лучшие результаты. Ключ к этому - форма шума. Алгоритмы сглаживания pow-r имеют особую форму, которая очень популярна во многих приложениях для цифровых звуковых рабочих станций (SONAR, Logic и т. Д.) Cakewalk.

Если вам не нужна полная точность воспроизведения Pow-R, вы можете просто сгенерировать шум с довольно низкой амплитудой и смешать его с вашим сигналом. Вы найдете это маскирует некоторые эффекты квантования.

1 голос
/ 26 октября 2010

Полагаю, вы имеете в виду (in * 0xFFFF) / 0xFFFFFF, в этом случае да.

0 голосов
/ 14 апреля 2014

x * 0xffff / 0xffffff чрезмерно педантичен, но не в хорошем смысле, если ваши сэмплы подписаны - и, вероятно, в целом не очень хорошо.

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

Если образцы подписаны, то пиковые положительные значения будут 0x7fff и 0x7fffff, тогда как пиковые отрицательные значения будут -0x8000 и -0x800000.Ваша первая проблема - решить, равно ли +1 0x7fff или -1 равно -0x8000.Если вы выберете последнее, то это простая операция смены.Если вы попытаетесь получить оба, то ноль перестанет быть нулевым.

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

Если вы хотите масштабировать в соответствии с пиковыми положительными значениями, правильная форма будет такой:

out = rint((float)in * 0x7fff / 0x7fffff);

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

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

Большинство людей предпочитают:

out = (in + 128) >> 8;
if (out > 0x7fff) out = 0x7fff;

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

Вы добавляете 128, потому что смещение вправо округляет в сторону отрицательной бесконечности.Средняя ошибка равна -128, и вы добавляете 128, чтобы исправить это, чтобы поддерживать 0 равным 0. Проверка на переполнение необходима, потому что в противном случае входное значение 0x7fffff даст результат 0x8000, а когда высохраните это в 16-битном слове, которое обернулось бы, давая пиковое отрицательное значение.

Педанты C могут пробить допущения в предположениях о поведении смещения вправо и деления, но я пропускаю их для ясности.

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

out = (in + (rand() & 255) - (rand() & 255)) >> 8;
if (out < -0x8000) out = -0x8000;
if (out > 0x7fff) out = 0x7fff;

Опять большие проблемы с использованием rand(), которые я собираюсь пропустить для ясности.

...