Алгоритм микширования звука - PullRequest
56 голосов
/ 18 декабря 2008

У меня есть два необработанных звуковых потока, которые мне нужно добавить вместе. Для целей этого вопроса мы можем предположить, что они имеют одинаковый битрейт и битовую глубину (скажем, 16-битная выборка, частота выборки 44,1 кГц).

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

  • Итак, как правильно добавить эти звуки в мой программный микшер?
  • Я ошибаюсь, и правильный метод - уменьшить громкость каждого из них вдвое?
  • Нужно ли мне добавлять компрессор / лимитер или какой-либо другой этап обработки, чтобы получить объем и эффект микширования, который я пробую?

-Adam

Ответы [ 20 ]

30 голосов
/ 18 декабря 2008

Вы должны сложить их вместе, но обрезать результат в допустимом диапазоне, чтобы предотвратить переполнение.

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

Вы могли бы реализовать более «правильный» компрессор / лимитер, но, не зная вашего точного применения, трудно сказать, будет ли оно того стоить.

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

28 голосов
/ 05 апреля 2012

Я бы предпочел прокомментировать один из двух высоко оцененных ответов, но из-за моей скудной репутации (полагаю) я не могу.

«Отмеченный» ответ: сложите вместе, и клип будет правильным, но не если вы хотите избежать отсечения.

Ответ со ссылкой начинается с работоспособного алгоритма вуду для двух положительных сигналов в [0,1], но затем применяется некоторая очень неисправная алгебра, чтобы вывести совершенно неверный алгоритм для знаковых и 8-битных значений. Алгоритм также не масштабируется до трех или более входов (произведение сигналов будет уменьшаться, а сумма увеличивается).

Итак - преобразуйте входные сигналы в плавающие, масштабируйте их до [0,1] (например, 16-битное значение со знаком станет
float v = ( s + 32767.0 ) / 65536.0 (close enough...))
, а затем сложите их.

Чтобы масштабировать входные сигналы, вы, вероятно, должны выполнить некоторую фактическую работу, а не умножать или вычитать значение вуду. Я бы посоветовал сохранить средний рабочий объем, а затем, если он начнет дрейфовать высоко (выше 0,25, скажем) или низко (ниже 0,01, скажем), начать применять масштабное значение, основанное на объеме. По сути, это становится реализацией автоматического уровня и масштабируется с любым количеством входов. Лучше всего то, что в большинстве случаев он вообще не будет связываться с вашим сигналом.

27 голосов
/ 05 мая 2009

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

17 голосов
/ 18 декабря 2008

Большинство приложений микширования звука будут микшировать с числами с плавающей запятой (32-битных достаточно для смешивания небольшого количества потоков). Переведите 16-битные выборки в числа с плавающей запятой в диапазоне от -1,0 до 1,0, представляющие полную шкалу в 16-битном мире. Затем суммируйте образцы вместе - у вас теперь есть много свободного места. Наконец, если вы получите какие-либо сэмплы, значение которых превышает полную шкалу, вы можете ослабить весь сигнал или использовать жесткое ограничение (отсечение значений до 1,0).

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

short sample1 = ...;
short sample2 = ...;
float samplef1 = sample1 / 32768.0f;
float samplef2 = sample2 / 32768.0f;
float mixed = samplef1 + sample2f;
// reduce the volume a bit:
mixed *= 0.8;
// hard clipping
if (mixed > 1.0f) mixed = 1.0f;
if (mixed < -1.0f) mixed = -1.0f;
short outputSample = (short)(mixed * 32768.0f)
9 голосов
/ 18 декабря 2008

«Тише на половину» не совсем правильно. Из-за логарифмической реакции уха деление выборок пополам сделает его на 6 дБ тише - безусловно, заметным, но не катастрофическим.

Возможно, вы захотите пойти на компромисс, умножив на 0,75. Это сделает его на 3 дБ тише, но уменьшит вероятность переполнения, а также уменьшит искажения, когда это произойдет.

8 голосов
/ 14 декабря 2012

Я не могу поверить, что никто не знает правильный ответ. Все достаточно близко, но все же чистая философия. Ближайшим, то есть лучшим было: (s1 + s2) - (s1 * s2). Это отличный подход, особенно для микроконтроллеров.

Итак, алгоритм идет:

  1. Узнайте громкость, с которой вы хотите, чтобы выходной звук был. Это может быть среднее значение или максимум одного из сигналов.
    factor = average(s1) Вы предполагаете, что оба сигнала в порядке, не переполняет 32767.0
  2. Нормализуйте оба сигнала с этим коэффициентом:
    s1 = (s1/max(s1))*factor
    s2 = (s2/max(s2))*factor
  3. Сложите их вместе и нормализуйте результат с тем же коэффициентом.
    output = ((s1+s2)/max(s1+s2))*factor

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

6 голосов
/ 05 февраля 2011

Вы также можете купить себе запас по алгоритму, подобному y = 1.1x - 0.2x ^ 3 для кривой, с крышкой сверху и снизу. Я использовал это в Hexaphone , когда игрок играет несколько нот вместе (до 6).

float waveshape_distort( float in ) {
  if(in <= -1.25f) {
    return -0.984375;
  } else if(in >= 1.25f) {
    return 0.984375;
  } else {    
    return 1.1f * in - 0.2f * in * in * in;
  }
}

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

4 голосов
/ 08 декабря 2009

преобразовать выборки в значения с плавающей запятой в диапазоне от -1,0 до +1,0, затем:

out = (s1 + s2) - (s1 * s2);
4 голосов
/ 18 декабря 2008

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

Некоторые ссылки:

Audacity

GStreamer

На самом деле вы, вероятно, должны использовать библиотеку.

3 голосов
/ 18 декабря 2008

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...