Уменьшение битовой глубины выборки путем усечения

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

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

4 ответа

Решение

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

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

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- Битовое слово, которое можно обернуть, чтобы получить пиковое отрицательное значение.

С педантами можно просверлить предположения относительно правого сдвига и поведения деления, но я пропускаю их для ясности.

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

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

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

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

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

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