Алгоритм для микширования звука

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

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

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

-Адам

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

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

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

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

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

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

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

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

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

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

Большинство приложений для микширования звука будут смешивать их с номерами с плавающей запятой (32 бит достаточно хорош для микширования небольшого количества streamов). Переведите 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) 

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

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

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

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

  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 и применять возврат к целым числам в конце с ранее выбранным коэффициентом мощности. Надеюсь, я не ошибся сейчас, потому что я тороплюсь.

Вы также можете купить себе запас высоты с помощью алгоритма, такого как 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 и разгладить клип до хорошей кривой. Производит гармонические искажения, которые звучат лучше, чем отсечение, и могут быть желательными в некоторых случаях.

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

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

дерзость

GStreamer

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

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

 out = (s1 + s2) - (s1 * s2); 

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

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

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

out = (s1 + s2) – (s1 * s2);

Введет сильное искажение, когда | s1 + s2 | подход 1.0 (по крайней мере, когда я пробовал его при смешивании простых синусоидальных волн). Я прочитал эту рекомендацию в нескольких местах, но, по моему скромному мнению, это бесполезный подход.

То, что происходит физически, когда «смесь волн» заключается в том, что их усилители добавляют, как и многие из предложенных здесь плакатов. Или

  • клип (искажает результат) или
  • суммируйте свои 16-битные значения в 32-битное число, а затем разделите на количество ваших источников (это то, что я хотел бы предложить, поскольку это единственный способ, который мне известен, избегая искажений)

Я сделал это следующим образом: я использовал float (образцы от -1 до 1), и я инициализировал переменную autoGain со значением 1. Затем я бы добавил все образцы вместе (также может быть больше 2). Затем я бы умножал исходящий сигнал на autoGain. Если абсолютное значение суммы сигналов до умножения будет больше 1, я бы присвоил значение 1 / этой суммы. Это позволило бы сделать автогайн меньшим, чем 1, скажем, 0,7 и будет эквивалентен некоторому оператору, быстро отменяющему основной объем, как только он увидит, что общий звук становится слишком громким. Затем я бы над регулируемым периодом времени добавлял к автогейну, пока он, наконец, не вернется к «1» (наш оператор оправился от шока и медленно проворачивает громкость :-)).

 // #include  // short ileft, nleft; ... // short iright, nright; ... // Mix float hiL = ileft + nleft; float hiR = iright + nright; // Clipping short left = std::max(-32768.0f, std::min(hiL, 32767.0f)); short right = std::max(-32768.0f, std::min(hiR, 32767.0f)); 

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

 > So what's the correct method to add these sounds together in my software mixer? 

Как вы догадались, добавление и обрезка – правильный путь, если вы не хотите потерять объем источников. С образцами, которые являются int16_t , вам нужно int16_t сумму int32_t , а затем ограничить и преобразовать обратно в int16_t .

 > Am I wrong and the correct method is to lower the volume of each by half? 

Да. Половина объема несколько субъективна, но то, что вы можете видеть здесь, состоит в том, что уменьшение объема (громкости) на половину означает уменьшение примерно на 10 дБ (деление мощности на 10 или выборки на 3.16). Но вы имеете в виду, очевидно, чтобы уменьшить значения выборки наполовину. Это уменьшение на 6 дБ, заметное уменьшение, но не так много, как уменьшение вдвое объема (таблица громкости очень полезна).

При этом уменьшении на 6 дБ вы избегаете всех отсечений. Но что происходит, когда вы хотите больше входных каналов? Для четырех каналов вам необходимо разделить входные значения на 4, что понижается на 12 дБ, таким образом, будет меньше половины громкости для каждого канала.

 > Do I need to add a compressor/limiter or some other processing stage to get the volume and mixing effect I'm trying for? 

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

Как предложил Марк Рэнсом, решение избежать обрезки, а не потерять до 6 дБ на канал, должно ударить где-то между «добавлением и обрезкой» и «усреднением».

То есть для двух источников: добавление, разделение где-то между 1 и 2 (уменьшить диапазон от [-65536, 65534] до чего-то меньшего), а затем ограничить.

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

Я сделал следующее:

 MAX_VAL = Full 8 or 16 or whatever value dst_val = your base audio sample src_val = sample to add to base Res = (((MAX_VAL - dst_val) * src_val) / MAX_VAL) + dst_val 

Умножьте левый запас src на значение MAX_VAL с нормализованным назначением и добавьте его. Он никогда не будет зажиматься, никогда не будет менее громким и абсолютно естественным.

Пример:

 250.5882 = (((255 - 180) * 240) / 255) + 180 

И это звучит хорошо 🙂

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

F (X) = - \ гидроразрыва {х} {| х | -1}

Р '(х) = \ гидроразрыва {х} {| х | + 1}

o = f '(\ sum f (s))

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

 #include  #include  #include  #include  #include  #include  #include  #include  // fabs wasn't accurate enough long double ldabs(long double x){ return x < 0 ? -x : x; } // -Inf 1. ) ret = 1.; return ret; } // -1<=input<=+1, -Inf 1. ) sample = 1.; if( sample < -1. ) sample = -1.; long double res = -( sample / ( ldabs(sample) - 1. ) ); // sample was too close to 1 or -1, return largest long double if( isinf(res) ) return sample < 0 ? -LDBL_MAX : LDBL_MAX; return res; } // -1 max_read ) max_read = read_count; } long double insamples[argc-2]; for( size_t j=0; j 

Спасибо всем за то, что поделились своими идеями, в последнее время я также занимаюсь некоторой работой, связанной с микшированием звука. Я также экспериментировал с этой проблемой, может, это поможет вам, ребята :).

Обратите внимание, что я использую 8Khz сэмпл и 16 бит (SInt16) звука в ios RemoteIO AudioUnit.

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

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

Но какой должен быть лучший способ добавления без переполнения / недостаточного streamа?

Основная идея :: У вас есть две звуковые волны: A & B, а результирующая волна C – суперпозиция двух волн A & B. Образец в ограниченном диапазоне бит может привести к переполнению. Итак, теперь мы можем вычислить максимальный предельный крест на верхнем и минимальном предельном кресте на обратной стороне формы волны наложения. Теперь мы вычитаем максимальный верхний предел креста в верхнюю часть формы волны наложения и добавим минимальный предел перехода вниз в нижнюю часть формы волны наложения. ВОИЛА … все готово.

шаги:

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

в следующем коде будет показана реализация.

 static unsigned long upSideDownValue = 0; static unsigned long downSideUpValue = 0; #define SINT16_MIN -32768 #define SINT16_MAX 32767 SInt16* mixTwoVoice (SInt16* RecordedVoiceData, SInt16* RealTimeData, SInt16 *OutputData, unsigned int dataLength){ unsigned long tempDownUpSideValue = 0; unsigned long tempUpSideDownValue = 0; //calibrate maker loop for(unsigned int i=0;i 0) { OutputData[i] = summedValue - upSideDownValue; } else { OutputData[i] = summedValue; } } return OutputData; } 

он отлично работает для меня, у меня есть намерение постепенно изменить значение upSideDownValue & downSideUpValue, чтобы получить более плавный результат.

Я бы сказал, просто добавьте их вместе. Если вы переполняете свое 16-битное пространство PCM, то звуки, которые вы используете, уже невероятно громкие, и вы должны их смягчить. Если это может привести к тому, что они будут слишком мягкими, попробуйте еще один способ увеличить общий объем вывода, например настройку ОС или поворот ручки на динамиках.

  • android: определение уровня звука
  • Как включить элементы управления iPod в фоновом режиме для управления музыкой без iPod в iOS 4?
  • Возможно ли воспроизводить streamи интернет-радиосигналов shoutcast с помощью html5?
  • HTML5 Сафари в прямом эфире против нет
  • Почему для MediaPlayer для Android требуется много времени для подготовки некоторых живых streamов для воспроизведения?
  • Отключить определенные разделы аудиофайла с помощью ffmpeg
  • Обнаруживать тишину при записи
  • Как наложить два аудиофайла с помощью ffmpeg
  • Обрезать аудио с iOS
  • Как остановить музыкальный клип на Java?
  • Android OpenAL?
  • Interesting Posts

    spark submit добавить несколько банок в classpath

    Каковы истинные преимущества ExpandoObject?

    Хешируйте произвольное значение точности (boost :: multiprecision :: cpp_int)

    ASP.NET MVC / EF4 / POCO / Репозиторий – как обновить отношения?

    Есть ли простой способ нанести удар по тексту в виджетах приложения?

    Определить тип файла изображения

    Вызов скрипта python со входом в скрипт python с использованием subprocessа

    Когда один и тот же идентификатор пользователя пытается войти на несколько устройств, как мне убить сеанс на другом устройстве?

    Как использовать getJSON, отправляя данные с помощью метода post?

    Mac OS DVD Burning Software

    Лучший способ проверить, существует ли объект в Entity Framework?

    Каковы кеши первого и второго уровней в спящем режиме?

    Есть ли какой-либо инструмент командной строки, который можно использовать для редактирования переменных среды в Windows?

    Как предотвратить длительное копирование имени файла с внешнего диска?

    Как установить Windows вместе с Ubuntu?

    Давайте будем гением компьютера.