Транспонирование звука?

Сижу вот, пишу сервер звуковых эффектов для виртурилки, нужен для эмитации звука движка машинки (а также клаксона и прочих звуков). Всё на базе ALSA.


Есть буфер, в который помещаю сэмпл звука двигателя. С микшированием звуков (например, бибикание одновременно со звуком двигателя) разобрался — тупо побайтовое сложение двух буферов сэмплов, двигателя и клаксона (с последующим ограничением по амплитуде чтоб треска не было). А вот сделать транспонирование звука двигателя что-то не получается. Транспонирование — изменение тональности, т.е. чем быстрее машинка едет — тем выше звук. Пробовал оставлять только каждый 2-й (или 3-й, 4-й) байт из буфера сэмпла, но получается какая-то фигня.


Подскажите, плиз, в каком направлении копать. Всё это дело должно производиться на лету. В голову лезут смутные воспоминания о FFT (быстрое преобразование Фурье), но конкретно не пойму как применить.


UPD> Всё, сделали, работает и даже не лагает.
  • Вопрос задан
  • 3810 просмотров
Пригласить эксперта
Ответы на вопрос 2
merlin-vrn
@merlin-vrn
Если не боитесь читать исходники, возьмите libmodplug и посмотрите, как это сделано у них. Принцип вам MTonly уже описал.

То, что вы делали — «это слишком». Если брать каждый второй сэмпл получается примитивный частный случай (но совершенно правильный с точки зрения теории!) ресемплинга «на октаву выше». Не немного, сразу на октаву, т.к. частота вырастает сразу вдвое.

Если нужно не вдвое, ресемплинг нужно делать не так. В частности, брать не каждый второй, а допустим при проигрывании каждых очередных 100 отсчётов получать их из 101 исходного отсчёта. Таким образом вы сделаете звук чуть-чуть выше. Примитивный вариант для вас будет — линейная интерполяция: отсчёты расположены равномерно, но «решётки» не совпадают. Вычисляете, с какими весами должны входить два ближайших отсчёта.

Пример:
Пусть отсчёты PCM будут 15 123 53 234 54 52 35 151… Мы хотим сыграть их на квинту выше, т.е. для каждых трёх исходных мы должны воспроизвести два.
тогда на выходе будет:
15, (125+53)/2, 234, (54+52)/2, 35, и так далее. Я здесь «промежуточные» отсчёты, которые находились в тех точках, в которых в оригинале ничего не было, вычисляю при помощи линейной интерполяции. Поскольку новые отсчёты лежат точно посередине, два «соседних» входят с одинаковыми весами.
Другой пример с этой же последовательностью: мы хотим сделать из этих восьми отсчётов семь. На выходе:
15, (123*5+53*1)/6, (53*4+234*2)/6, (234*3+54*3)/6, (54*2+52*4)/6, (52*1+35*5)/6, 151,…
Или же, сдвиг на a=0.992:
15, 123*(1-a)+53*a, 53*(1-2a)+234*2a,… — осталось только научиться правильно обрабатывать момент, когда n*a становится больше единицы.
Стоит нарисовать «временнУю диаграмму» для того и другого количества отсчётов, а потом посмотреть, что и как вычислять.

Для аккуратной интерполяции есть более продвинутые алгоритмы, в частности, фильтры FIR (finite impulse response, КИХ, конечная импульсная характеристика) — они будут давать меньше «призвуков» при таком изменении тона.
А потом вы забьёте на реализацию этого самостоятельно и начнёте использовать библиотеку rubberband :)
Ответ написан
MTonly
@MTonly
Веб-разработчик с 2002 года
Общий принцип синтеза на основе сэмплов: чем выше нужен тон, тем с большей скоростью следует воспроизводить сэмпл. Увеличение скорости воспроизведение вдвое повышает высоту тона на октаву. Для промежуточных полутонов следует использовать дробный коэффициент.
Ответ написан
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы