Смешивание 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

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

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