Тон-генератор своими руками

Синтезаторы, звуковые модули, сэмплеры

Сообщение Dmitry_Milk » Ср окт 03, 2007 12:27

Нет, это то, что называется субтрактивным синтезом.

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

Схема звукового тракта в этом смысле практически элементарна, вся фишка, вся "соль" формируемых такой схемой звуков появляется тогда, когда у фильтра достаточно выражен резонанс и в процессе звучания ноты идет постоянное изменение частоты этого резонанса по генератору огибающей и(или) LFO - и тогда идут "вау", "виу", "чав", "тьууййй" и другие звуковые вепендрежи аналоговых синтезаторов 70-х годов.
Аватара пользователя
Dmitry_Milk
Любитель
Любитель
 
Сообщения: 282
Зарегистрирован: Вс июн 03, 2007 11:55

Сообщение Gregory » Ср окт 03, 2007 19:50

А на каком этапе вклинивается зависимость от нотно-частотного ряда (т.е. от нажатой клавиши)?
Obligatus servus
http://www.dubrovenko.ru
Аватара пользователя
Gregory
Почётный участник
Почётный участник
 
Сообщения: 840
Зарегистрирован: Чт янв 20, 2005 11:49
Откуда: Санкт-Ленинград

Сообщение Dmitry_Milk » Ср окт 03, 2007 22:55

Высота ноты, питч (pitch), как бы "сбоку" подходит к этой схеме - управляет частотой генератора, может с некоторым коэффициэнтом влиять на частоту среза фильтра, иногда может использоваться даже в амплитудном модуляторе, образуя громкостной "уклон клавиатуры".
Аватара пользователя
Dmitry_Milk
Любитель
Любитель
 
Сообщения: 282
Зарегистрирован: Вс июн 03, 2007 11:55

Сообщение Gregory » Чт окт 04, 2007 10:50

Dmitry_Milk писал(а):управляет частотой генератора
Так какого?
Obligatus servus
http://www.dubrovenko.ru
Аватара пользователя
Gregory
Почётный участник
Почётный участник
 
Сообщения: 840
Зарегистрирован: Чт янв 20, 2005 11:49
Откуда: Санкт-Ленинград

Сообщение Dmitry_Milk » Чт окт 04, 2007 18:03

Аватара пользователя
Dmitry_Milk
Любитель
Любитель
 
Сообщения: 282
Зарегистрирован: Вс июн 03, 2007 11:55

Сообщение Gregory » Чт окт 04, 2007 23:57

Dmitry_Milk, попробую разобраться на досуге. :wink:
Obligatus servus
http://www.dubrovenko.ru
Аватара пользователя
Gregory
Почётный участник
Почётный участник
 
Сообщения: 840
Зарегистрирован: Чт янв 20, 2005 11:49
Откуда: Санкт-Ленинград

Сообщение Gregory » Пт окт 05, 2007 15:35

Dmitry_Milk, поглядел. Мои худшие предположения подтвердились. Эти "пилы" зависят от нажатой клавиши. В в/у ветке предлагался способ реализации. Довольно громоздкий. Можете предложить более изящное решение?
Obligatus servus
http://www.dubrovenko.ru
Аватара пользователя
Gregory
Почётный участник
Почётный участник
 
Сообщения: 840
Зарегистрирован: Чт янв 20, 2005 11:49
Откуда: Санкт-Ленинград

Сообщение Dmitry_Milk » Сб окт 06, 2007 22:41

Да в общем то не все так громоздко. И с пилами то как раз практически никаких проблем. в Основном самое проблематичное - фильтрация.

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

1. Генерация пилообразного сигнала. Используем 2 переменные. Одна из них - это параметр, характеризующий частоту, f. Вторая переменная gen - это собстенно фаза генератора и она же - его выход. Генерируем элементарно - просто при просчете каждой выборки добавляем f к gen. переменная gen периодически переполняется и gen из положительной области перескакивает в отрицательную, что и приводит к соскоку очередного "зуба" пилы.

Пусть разрядность gen - 16. Чем больше f, тем чаще происходит переполнение, зависимость пропорциональная, причем, если бы f мог быть 2^16, то переполнение происходило бы в каждой выборке, что соответствовало бы чатоте, равной частоте дискретизации. Обозначим Fd - частота дискретизации. Тогда, если нам требуется получить частоту пилы F0, то необходимо взять f=2^16*F0/Fd.

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

2. Нормализация/масштабирование перед суммированием.
При суммировании возможно переполнение. Чтобы избежать переполнения, умножим выходной сигнал каждого из генераторов на масштабирующий коэффициэнт (дробный). Масштабирование также необходимо для того, чтоб избежать последующего переполнения в фильтре, поскольку при фильтрации амплитуда сигнала может вырасти в количество раз, соответствующее добротности.

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

X(i+1)=X(i)+V(i)
V(i+1)=A*V(i)+B*(in(i)-X(i)),

где X и V - переменные состояния фильтра (X- выход НЧ-фильтрации), а коэффициэнты

A=1-2*PI*Fres/(Q*Fd)
B=(2*PI*Fres/Fd)^2,

где Q - добротность фильтра, Fres - его резонансная частота.

Коэффициэнты A и B будем представлять в виде беззнакового(положительного) дробного числа с фиксированной запятой, состоящего только из дробной части. При таком выборе формата коэффициэнтов операция умножения будет автоматически сохранять положение запятой умножаемого числа.

Считаем, что параметры A и B подготавливаются для фильтра управляющим контроллером.

4. Амплитудная манипуляция. Собственно, только отметим, что для простоты опять же будем представлять амплитуду в виде беззнакового(положительного) дробного числа с фиксированной запятой, состоящего только из дробной части, но уже однобайтового, поскольку здесь не требуется такой диапазон значений, как при задании коэффициэнтов фильтра.

Опять же считаем, что амплитуда подготавливается управляющим контроллером.

---------------------------------------------------------------------------
Алгоритм:

//управляющие параметры
signed word f1; // частотный фактор генератора 1
signed word f2; // частотный фактор генератора 2
unsigned fractional byte scale1; // масштабирующий коэффициэнт генератора 1
unsigned fractional byte scale2; // масштабирующий коэффициэнт генератора 2
unsigned fractional word A; // коэффициэнт А фильтра
unsigned fractional word B; // коэффициэнт B фильтра
unsigned fractional byte amp; // параметр амплитудной манипуляции

//рабочие переменные
signed word tmp1,tmp2; // переменные для промежуточных вычислений

signed word gen1; // текущее состояние генератора 1
signed word gen2; // текущее состояние генератора 2

//переменные состояния фильтра
signed word X;
signed word V;

void ProcessSample()
{
gen1=gen1+f1; // следующее состояние генератора 1
gen2=gen2+f2; // следующее состояние генератора 2

// масштабирование и суммирование выходных сигналов генераторов
tmp1=gen1*scale1;
tmp1=tmp1+gen2*scale2;

//алгоритм фильтрации
tmp2=A*V;
tmp1=tmp1-X;
tmp2=tmp2+B*tmp1;
X=X+V;
V=V+tmp2;

// амплитудная модуляция
tmp1=X*amp;

// вывод результата
out (tmp1);
}
Аватара пользователя
Dmitry_Milk
Любитель
Любитель
 
Сообщения: 282
Зарегистрирован: Вс июн 03, 2007 11:55

Сообщение Dmitry_Milk » Сб окт 06, 2007 22:48

Теперь реализация на ассемблере.
Для фильтров приведены по два варианта операции умножения, точная и упрощенная. Упрощенная реализация короче примерно на 10 операций, но дает погрешность в самом младшем бите (недоучитывается возможный перенос от суммирования трех чисел в дополнительном младшем отбрасываемом байте).

-----------------------------------------------------------------
переменные

f1L equ 0x80 ;signed word f1
f1H equ 0x81
f2L equ 0x82 ;signed word f2
f2H equ 0x83
scale1 equ 0x84 ;unsigned fractional byte scale1
scale2 equ 0x85 ;unsigned fractional byte scale2
AL equ 0x86 ;unsigned fractional word A
AH equ 0x87
BL equ 0x88 ;unsigned fractional word B
BH equ 0x89
amp equ 0x8A ;unsigned fractional byte amp

tmp1L equ 0x70 ;signed word tmp1
tmp1H equ 0x71
tmp2L equ 0x72 ;signed word tmp2
tmp2H equ 0x73
gen1L equ 0x74 ;signed word gen1
gen1H equ 0x75
gen2L equ 0x76 ;signed word gen2
gen2H equ 0x77
XL equ 0x78 ;signed word X
XH equ 0x79
VL equ 0x7A ;signed word V
VH equ 0x7B
prod1 equ 0x7C ;дополнительный младший байт, использующийся для осуществелния умножения с соблюдением точности.

;---------------------------

movf f1L,w ;gen1=gen1+f1
addwf gen1L,f
movf f1H,w
addwfc gen1H,f

movf f2L,w ;gen2=gen2+f2
addwf gen2L,f
movf f2H,w
addwfc gen2H,f

movf scale1,w ;tmp1=gen1 * scale
mulwf gen1H
movff PRODL,tmp1L
movff PRODH,tmp1H
btfsc gen1H,7
subwf tmp1H,f
mulwf gen1L
movf PRODH,w
addwf tmp1L,f
clrf WREG
addwfc tmp1H,f

movf scale2,w ;tmp1=tmp1+gen2*scale2
mulwf gen2H
movf PRODL,w
addwf tmp1L,f
movf PRODH,w
addwfc tmp1H,f
movf scale2,w
btfsc gen2H,7
subwf tmp2H,f
mulwf gen2L
movf PRODH,w
addwf tmp1L,f
clrf WREG
addwfc tmp2H,f

;movf AL,w ;tmp2=A*V - точное умножение
;mulwf VL
;movff PRODH,prod1
;movf AH,w
;mulwf VH
;movff PRODL,tmp2L
;movff PRODH,tmp2H
;mulwf VL
;movf PRODL,w
;addwf prod1,f
;movf PRODH,w
;addwfc tmp2L,f
;clrf WREG
;addwfc tmp2H,f
;movf AL,w
;mulwf VH
;movf PRODL,w
;addwf prod1,f
;movf PRODH,w
;addwfc tmp2L,f
;clrf WREG
;addwfc tmp2H,f
;movf VH,w ;знаковая коррекция
;bnn filt1
;movf AL,w
;subwf tmp2L,f
;movf AH,w
;subwfb tmp2H,f
;filt1:

movf AH,w ;tmp2=A*V - упрощенное умножение
mulwf VH
movff PRODL,tmp2L
movff PRODH,tmp2H
mulwf VL
movf PRODH,w
addwf tmp2L,f
clrf WREG
addwfc tmp2H,f
movf AL,w
mulwf VH
movf PRODH,w
addwf tmp2L,f
clrf WREG
addwfc tmp2H,f
movf VH,w ;знаковая коррекция
bnn filt1
movf AL,w
subwf tmp2L,f
movf AH,w
subwfb tmp2H,f
filt1:

movf XL,w ;tmp1=tmp1-X
subwf tmp1L,f
movf XH,w
subwfb tmp1H,f

;movf BL,w ;tmp2=tmp2+B*tmp1 - точное умножение
;mulwf tmp1L
;movf PRODH,w
;addwf prod1,f
;clrf WREG
;addwfc tmp2L,f
;addwfc tmp2H,f
;movf BH,w
;mulwf tmp1H
;movf PRODL,w
;addwf tmp2L,f
;movf PRODH,w
;addwfc tmp2H,f
;movf BH,w
;mulwf tmp1L
;movf PRODL,w
;addwf prod1,f
;movf PRODH,w
;addwfc tmp2L,f
;clrf WREG
;addwfc tmp2H,f
;movf BL,w
;mulwf tmp1H
;movf PRODL,w
;addwf prod1,f
;movf PRODH,w
;addwfc tmp2L,f
;clrf WREG
;addwfc tmp2H,f
;movf tmp1H,w ;знаковая коррекция
;bnn filt2
;movf BL,w
;subwf tmp2L,f
;movf BH,w
;subwfb tmp2H,f
;filt2:

movf BH,w ;tmp2=tmp2+B*tmp1 - упрощенное умножение
mulwf tmp1H
movf PRODL,w
addwf tmp2L,f
movf PRODH,w
addwfc tmp2H,f
movf BH,w
mulwf tmp1L
movf PRODH,w
addwf tmp2L,f
clrf WREG
addwfc tmp2H,f
movf BL,w
mulwf tmp1H
movf PRODH,w
addwf tmp2L,f
clrf WREG
addwfc tmp2H,f
movf tmp1H,w ;знаковая коррекция
bnn filt2
movf BL,w
subwf tmp2L,f
movf BH,w
subwfb tmp2H,f
filt2:

movf VL,w ;X=X+V
addwf XL,f
movf VH,w
addwfc XH,f

movf tmp2L,w ;V=V+tmp2
addwf VL,f
movf tmp2H,w
addwfv VH,f

movf amp,w ;tmp1=amp*X
mulwf XH
movff PRODL,tmp1L
movff PRODH,tmp1H
btfsc XH,7
subwf tmp1H,f
mulwf XL
movf PRODH,w
addwf tmp1L,f
clrf WREG
addwfc tmp1H,f

movff tmp1L,LATB ;out (tmp1)
movff tmp1H,LATC


Итого 103 операции, с учетом восьми 2х-цикловых операций movff в наихудшем случае, когда во всех умножениях выполняются знаковые коррекции, имеем 111 машинных циклов на расчет одной выборки одного голоса.

При частоте кварца 40 мГц имеем 10 млн машинных циклов в секунду. Значит, при частоте дискретизации 40кГц имеем по 250 машинных циклов на выборку. Если предположить, что вся низкочастотная часть (на схеме ниже пунктирной линии) вынесена на отдельный управляющий контроллер, то контроллер, занимающийся исключительно звукочастотной частью, сможет просчитывать два голоса.

В общем, мои предположения (по 5-6 голосов на контроллер) оказались завышенными, но не сильно, все равно мы гуляем на грани возможностей контроллера, а не далеко за его пределами :) В основном я просчитался с умножениями - они потребовали гораздо больше операций, чем я ожидал.

Зато, если вышеуказанные вычисления точны, то остается резерв около 25-30 машинных циклов на одну выборку, то есть, порядка миллиона машинных циклов в секунду - этого может вполне хватить на то, чтоб в этом же контроллере реализовать и "медленную" часть - прием миди-сообщений, генерация огибающих и вибрато с дискретизацией порядка 100Гц и запись параметров для "быстрой" части. Например, быструю часть можно реализовать в виде основного цикла, а медленную часть обрабатывать по прерываниям от UART и от таймера 100Гц. И если двухголосия достаточно - то может оказаться, что вся электроника обходится одним контроллером и ЦАП-ом.
Аватара пользователя
Dmitry_Milk
Любитель
Любитель
 
Сообщения: 282
Зарегистрирован: Вс июн 03, 2007 11:55

Сообщение Роман Петелин » Вс окт 07, 2007 13:59

Мужики, это все очень прикольно. Но звучать в результате будет как простейший бесплатный VSTi. И нет никакой гарантии, что он не будет зависать, глючить и т.п. Вот если бы он был каким-нибудь ламповым с 4 осцилляторами для сочных лидов, микропроцессорным управлением и MIDI... Вот это было бы круто.
Аватара пользователя
Роман Петелин
Администратор
Администратор
 
Сообщения: 2163
Зарегистрирован: Сб янв 15, 2005 22:22
Откуда: Санкт-Петербург

Сообщение Gregory » Вс окт 07, 2007 14:20

Роман Петелин писал(а):это все очень прикольно
Не только прикольно, но и интересно. :wink:

Dmitry_Milk писал(а):значение параметра f задается другим контроллером
Вот то-то и оно. :lol:
На досуге поизучаю Ваши программы. Кстати, для какого МК они написаны?

Код: Выделить всё
P.S.: Тэг Code          ; позволяет вставлять форматированный текст
Obligatus servus
http://www.dubrovenko.ru
Аватара пользователя
Gregory
Почётный участник
Почётный участник
 
Сообщения: 840
Зарегистрирован: Чт янв 20, 2005 11:49
Откуда: Санкт-Ленинград

Сообщение Dmitry_Milk » Вс окт 07, 2007 18:34

Роман Петелин писал(а):Мужики, это все очень прикольно. Но звучать в результате будет как простейший бесплатный VSTi.

Я в общем то поэтому и не делаю этого. Я беру комп, запускаю Реактор или SynC Modular - и радуюсь жизни.

Но вопрос стоял о принципиальной возможности сделать на МК что-то посерьезнее меандровой пищалки. И я говорю - возможно. И если правильно управлять параметрами во времени - звук можно получить очень даже интересный, в основном за счет эффектов управления - портаменто, огибающие с зависимостью от велосити, использование сообщений от педали Экспрешшн и от афтертача для модуляций питча и частоты среза фильтра - все эти вещи низкочастотные и здесь надо только подрудиться над их программированием. По любому звук будет интереснее синтеза на основе PCM. Только вот голосов слишком мало.

И нет никакой гарантии, что он не будет зависать, глючить и т.п.


В данном случае наверное все таки можно достаточно сильно гарантировать отсутствие глюков, поскольку программы для МК очень малы по сравнению с тем, что пишется для современных компов, и их буквально с начала до конца можно "отладить", выполняя программу прямо в уме по бумажке.

с 4 осцилляторами для сочных лидов


В общем то, можно и 4 осциллятора сделать. Сам осциллятор "съедает" всего лишь 4 машинных цикла на одну выборку.

Но ламп тут не предвиделось :)

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

Но вот если б это был полноценный инструмент с клавиатурой... Вот сколько может стоить клавиатурная механика и корпус? Если все это в одном корпусе могло бы стоить, скажем, в пределах 5 тыс. рублей, то такие синты, малоголосные, но с интересным звуком, могли бы пользоваться спросом у начинающих подростково-школьных музыкальных групп, для которых синт за штуку баксов нереален. Интересно, сохранились ли еще живыми на территории России предприятия, которые в советское время выпускали синты?
Последний раз редактировалось Dmitry_Milk Вс окт 07, 2007 19:38, всего редактировалось 2 раз(а).
Аватара пользователя
Dmitry_Milk
Любитель
Любитель
 
Сообщения: 282
Зарегистрирован: Вс июн 03, 2007 11:55

Сообщение Dmitry_Milk » Вс окт 07, 2007 19:04

Gregory писал(а):
Dmitry_Milk писал(а):значение параметра f задается другим контроллером
Вот то-то и оно. :lol:


А что оно? для межконтроллерного взаимодействия использовать либо CAN, либо UART, только уже не миди передавать, а блоки параметров.

Да и опять же, прочитайте дописку внизу про свободный остаток машинного времени в миллион машинных циклов в секунду - это не так уж мало, у популярного советского микропроцессора КР580ВМ80А было раза в 2-4 меньше, однако на этом проце когда-то выпускался цифро-аналоговый 8-голосный синтезатор (звукочастотная часть аналоговая по вышеприведенной схеме, а управление - цифровое от КР580ВМ80А). Название синта к сожалению не помню, его реклама была на задней обложке одного из журналов "Радио" второй половины 80-х.

для какого МК они написаны?


Да все для того же PIC от Microchip, по крайней мере систему команд брал для серии PIC18F. Но насколько я понимаю, практически у всех серий PIC базовая система команд и наборы управляющих регистров одинаковы. Исключением, может быть, является операция умножения mulwf - в 18 серии она реализуется аппаратным умножителем 8х8 за один машинный цикл, есть ли эта команда в других сериях, например, в часто используемой вами PIC16F - не знаю.

...P.S.: Тэг Code...


Ок, спасибо, теперь буду знать :)
Аватара пользователя
Dmitry_Milk
Любитель
Любитель
 
Сообщения: 282
Зарегистрирован: Вс июн 03, 2007 11:55

Сообщение Gregory » Вс окт 07, 2007 22:21

Dmitry_Milk писал(а):А что оно?
Оно, это - то, что МК будет уже не один. :wink:

Dmitry_Milk писал(а):для серии PIC18F
Я так и подумал. :lol: 18-я, превосходит 16-ю, если не на порядок, то в несколько раз. И по стоимости, по-моему, тоже.
Obligatus servus
http://www.dubrovenko.ru
Аватара пользователя
Gregory
Почётный участник
Почётный участник
 
Сообщения: 840
Зарегистрирован: Чт янв 20, 2005 11:49
Откуда: Санкт-Ленинград

Сообщение Dmitry_Milk » Вс окт 07, 2007 23:09

http://www.chip-dip.ru

PIC18F258-I/SP, 40MHz, 16KFlash, DIP28 Есть
от 1 шт. 317,20
от 2 шт. 253,00
Аватара пользователя
Dmitry_Milk
Любитель
Любитель
 
Сообщения: 282
Зарегистрирован: Вс июн 03, 2007 11:55

Пред.След.

Вернуться в Синтезаторы



@Mail.ru

x

#{title}

#{text}