Смешивание 16-битных линейных потоков PCM и предотвращение отсечения / переполнения
Я пытаюсь смешать 2 16-битных линейных аудиопотока PCM, и я не могу преодолеть проблемы с шумом. Я думаю, что они возникают из-за переполнения при смешении образцов.
У меня есть следующая функция...
short int mix_sample(short int sample1, short int sample2)
{
return #mixing_algorithm#;
}
... и вот что я пробовал как # смешивающий алгоритм #
sample1/2 + sample2/2
2*(sample1 + sample2) - 2*(sample1*sample2) - 65535
(sample1 + sample2) - sample1*sample2
(sample1 + sample2) - sample1*sample2 - 65535
(sample1 + sample2) - ((sample1*sample2) >> 0x10) // same as divide by 65535
Некоторые из них дали лучшие результаты, чем другие, но даже лучший результат содержал довольно много шума.
Есть идеи как это решить?
5 ответов
Вот описательная реализация:
short int mix_sample(short int sample1, short int sample2) {
const int32_t result(static_cast<int32_t>(sample1) + static_cast<int32_t>(sample2));
typedef std::numeric_limits<short int> Range;
if (Range::max() < result)
return Range::max();
else if (Range::min() > result)
return Range::min();
else
return result;
}
чтобы смешать, это просто добавить и клип!
чтобы избежать артефактов отсечения, вы захотите использовать насыщенность или ограничитель. в идеале у вас будет маленький int32_t
буфер с небольшим количеством lookahead. это приведет к задержке.
более распространенным, чем ограничение повсеместно, является то, что в вашем сигнале стоит несколько битов "запаса".
Лучшее решение, которое я нашел, дано Виктором Тотом. Он предлагает решение для 8-битной неподписанной PCM, а изменение его для 16-битной подписанной PCM дает следующее:
int a = 111; // first sample (-32768..32767)
int b = 222; // second sample
int m; // mixed result will go here
// Make both samples unsigned (0..65535)
a += 32768;
b += 32768;
// Pick the equation
if ((a < 32768) || (b < 32768)) {
// Viktor's first equation when both sources are "quiet"
// (i.e. less than middle of the dynamic range)
m = a * b / 32768;
} else {
// Viktor's second equation when one or both sources are loud
m = 2 * (a + b) - (a * b) / 32768 - 65536;
}
// Output is unsigned (0..65536) so convert back to signed (-32768..32767)
if (m == 65536) m = 65535;
m -= 32768;
Использование этого алгоритма означает, что почти нет необходимости обрезать выходные данные, так как это только одно значение за пределами диапазона. В отличие от прямого усреднения, громкость одного источника не уменьшается, даже когда другой источник молчит.
Вот что я сделал в своем недавнем проекте синтезатора.
int* unfiltered = (int *)malloc(lengthOfLongPcmInShorts*4);
int i;
for(i = 0; i < lengthOfShortPcmInShorts; i++){
unfiltered[i] = shortPcm[i] + longPcm[i];
}
for(; i < lengthOfLongPcmInShorts; i++){
unfiltered[i] = longPcm[i];
}
int max = 0;
for(int i = 0; i < lengthOfLongPcmInShorts; i++){
int val = unfiltered[i];
if(abs(val) > max)
max = val;
}
short int *newPcm = (short int *)malloc(lengthOfLongPcmInShorts*2);
for(int i = 0; i < lengthOfLongPcmInShorts; i++){
newPcm[i] = (unfilted[i]/max) * MAX_SHRT;
}
Я добавил все данные PCM в целочисленный массив, чтобы получить все данные без фильтрации.
После этого я искал абсолютное максимальное значение в целочисленном массиве.
Наконец, я взял целочисленный массив и поместил его в короткий массив int, взяв каждый элемент, деленный на это максимальное значение, а затем умножив его на максимальное короткое значение int.
Таким образом, вы получите минимальное количество "запаса", необходимого для размещения данных.
Вы могли бы сделать некоторую статистику по целочисленному массиву и интегрировать некоторое отсечение, но для того, что мне было нужно, минимальный объем запаса был достаточно хорош для меня.
Здесь обсуждается: https://dsp.stackexchange.com/questions/3581/algorithms-to-mix-audio-signals-without-clipping, почему решение A+B - A*B не идеально. В одном из комментариев к этому обсуждению скрыто предложение суммировать значения и разделить их на квадратный корень из числа сигналов. И дополнительная проверка на обрезку не помешала. Это кажется разумной (простой и быстрой) золотой серединой.
Я думаю, что они должны быть функциями отображения [MIN_SHORT, MAX_SHORT] -> [MIN_SHORT, MAX_SHORT]
и они явно нет (кроме первого), поэтому происходит переполнение.
Если предложение не работает, вы также можете попробовать:
((long int)(sample1) + sample2) / 2
Поскольку вы находитесь во временной области, информация о частоте находится в разнице между последовательными выборками, когда вы делите на два, вы повреждаете эту информацию. Вот почему добавление и отсечение работают лучше. Ограничение, конечно, добавит очень высокочастотный шум, который, вероятно, отфильтрован.