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()
, которые я собираюсь пропустить для ясности.