Как смешать 2 или более каналов цифрового звука? — Полагаю, такой вопрос возникает довольно часто, если вы не пользуетесь каким-либо API/DLL/и прочим. Итак начнем.
Теория
Возьмем два звука(сэмпла), которые нам нужно смешать.
Но-но-но. У нас ведь цифровой звук. Значит, на выходе будет стоять ЦАП, предположим, — 8бит ЦАП, частота дискретизации — 16KHz. То, естественно, это сказывается на форме сигнала. Осциллограмма двух каналов будет примерно такой:
И теперь затык. Как смешать звук, состоящий из чисел? Первое, что приходит в голову — сложение цифровых амплитуд, что и является самым правильным способом. «+«! Максимальная амплитуда каждого звука: 256, а ЦАП у нас — 8бит, соответственно, при сложении будет такая картина:
Этого следовало ожидать. Все значения амплитуд, которые выше половины возможности ЦАП-а — назовем их — Пик-ами.
Тут несколько возможных путей:
- Делить на 2, перед смешиванием
- Нормализовать «пики»
- Увеличить запас ЦАП, т.е. повысить разрядность.
Делить на 2 — это самое простое, но это уменьшает качество звука. Если два канала, то разрядность каждого уменьшится до 7бит. А если звуков > 2? Противоречие налицо. С одной стороны нужно понизить уровень каналов во избежание переполнения ЦАП-а, с другой — иметь максимально возможный уровень в канале для полного использования всех разрядов.
Нормализация и т.п. — небольшая потеря качества, но вариант хороший.
Повысить разрядность — ну, это крайние меры.
Дело в том, что время «пиков» может совпадать у двух каналов, поэтому происходит переполнение ЦАП-а. Почему в «природе» этого не происходит?
Пример: хор из 100 человек. Если бы они пели полностью синхронно, то у нас давно бы взорвались перепонки. Если звук в природе был бы цифровой, то частота дискретизации стремилась к бесконечности. А разрядность «природного ЦАП-а» была бы то же не маленькой.
Снижая частоту дискретизации — совпадений «пиков» становится еще больше:
Здесь сразу видно, что переполнение ЦАП-а будет очень часто, чтобы уменьшить этот шанс — нужно увеличить частоту дискретизации, либо уменьшить разрядность.
Есть еще один способ смешивания звука: поднять общую частоту дискретизации в 2 раза и выводить сэмплы(значения амплитуд) последовательно. Четные — звук1, нечетные — звук2. Потерь качества — 0%.
Ну, а если вы считаете каждый такт вашей программы, то можно выводить каждый звук на отдельный ЦАП, а смешивать их уже аналоговым микшером или просто резистором.
Программные решения
Среднее арифметическое.
Z = (A + B) / 2
Теряем качество на один бит, но этот способ имеет право на жизнь. Также все типы данных должны быть знаковыми.
1 2 3 4 5 6 | int8_t SoundBuf1[xxx]; int8_t SoundBuf2[xxx]; void tranferDAC() { DAC = (SoundBuf1[countBuf_1]+SoundBuf2[countBuf_2])>>1; } |
Последовательный вывод.
Z = A, … Z = B, …
Условие: частота дискретизации должна быть умножена на 2.
1 2 3 4 5 6 7 8 9 10 11 12 | boolean flag = false; void tranferDAC() { if (flag) { DAC = SoundBuf1[countBuf_1]; countBuf_1++; } else { DAC = SoundBuf2[countBuf_2]; countBuf_2++; } flag=!flag; } |
Нормализация.
Z = 2(A + B) — (A * B)/128 — 256
via (для знаковых чисел)
1 2 3 4 5 6 | int8_t SoundBuf1[xxx]; int8_t SoundBuf2[xxx]; void transferDAC() { DAC = (((SoundBuf1[countBuf_1] + SoundBuf2[countBuf_2])<<1) — ((SoundBuf1[countBuf_1]*SoundBuf2[countBuf_2])>>7) — 256); } |
Z = A + B — (A * B)/256
(для беззнаковых)
1 2 3 4 5 6 | uint8_t SoundBuf1[xxx]; uint8_t SoundBuf2[xxx]; void transferDAC() { DAC = (SoundBuf1[countBuf_1] + SoundBuf2[countBuf_2] — ((SoundBuf1[countBuf_1]*SoundBuf2[countBuf_2])>>8)); } |
Аппаратные решения
Питание — двуполярное, естественно. (можно использовать преобразователь из этой статьи)
То же очень простой микшер. (транзистор можно брать с любой буквой)
Автор микшеров — Vadim.
Отличная статья! 😉
Спасибо 😉