

Mimo upływu lat w dalszym ciągu lubię od czasu do czasu odświeżyć sobie w pamięci elektroniczne brzmienia lat 80tych, kiedy to po raz pierwszy miałem okazję usłyszeć „syntetyczną” muzykę pochodzącą z wielkich drewnianych „szaf” (automaty arcade) lub trochę mniejszych plastikowych pudełek zwanych komputerami osobistymi. W tamtym czasie jeszcze nie miałem większego pojęcia o technice i sposobie kreowania dźwięków na drodze elektronicznej, ale to nie przeszkadzało w delektowaniu się przeróżnymi aranżacjami, wiele z nich do tej pory uważam za prawdziwe arcydzieła tak pod względem muzycznym jak i programistycznym (np. AGENTX2 Tim Follin wersja na ZX spectrum, nie C64 http://z80.i-demo.pl/Tim_Follin-Agent_X_2.mp3 ), ale nie o sztuce będzie tym razem.
Po latach przyjemność z muzyki nie minęła, dodatkowo wiedzy o jej tworzeniu i przetwarzaniu trochę przybyło. Wcześniej lub później musiał więc nadejść czas na 'syntezę' zainteresowań

http://www.swinkels.tvtom.pl/swinsid/
Przy okazji przypomniałem sobie budowę uświęconego wręcz generatora, i powody z jakich emulacja programowa tego „instrumentu” jest nieco problematyczna

Wracając jednak do tego niezwykle niepraktycznego projektu


Takie rozwiązanie umożliwia generowanie przeróżnych efektów z użyciem minimalnej liczby danych wysyłanych do syntezatora, oraz użycie popularnych samplerów midi, żeby wydobyć z układu jak najszybciej jakie kolwiek dźwięki.

Krótko o programowaniu..
Generator każdego kanału to DDS operujący na 16bitowym słowie które bezpośrednio przekłada się na częstotliwość. Nie będę tutaj opisywał zasady działania DDS, bo jest o tym mnogość informacji. Po pobraniu próbki z tablicy (można wybrać numer tablicy z rom lub ram) każdy kanał umożliwia dodatkowe operacje w celu wzbogacenia brzmienia surowego przebiegu. Pierwsza możliwość modyfikacji, to regulacja przesunięcia fazy pobierania próbek z tablicy ustalana w CC04. Następnym elementem jest PWM który umożliwia pominięcie próbek z WaveTable. Na miejsce próbki ładowana jest wartość z rejestru PWS1 (PhaseWidthSampel) - CC03. Następne urozmaicenie, to możliwość modulacji amplitudowej próbki generatora próbką z poprzedniego generatora. Wybór takiej opcji ustawia się bitem nr 1 rejestru CC00. Pomysł ten zaczerpnąłem z działania wcześniej wspomnianych sprzętowych generatorów, które w swojej konstrukcji posiadają właśnie taką możliwość, podobnie z generatorem szumu. Istnieje również możliwość „XORowania” wartości próbek dwóch generatorów bit.5 CC00 (jest to już mój oryginalny pomysł na który wpadłem przy pracy z MEGAwave). Ustawienie bitu 3 CC00 powoduje modulowanie obwiedni generatora prosto z generatora szumu. Bit.4 CC00 zmniejsza modulacje szumem o połowę. Bit 2 mnoży próbkę x2 co podnosi poziom sygnału oraz „ukwadratowia” przebiegi. Na końcu syntezy kanału znajduje się modulacja obwiedni i równoczesna regulacja amplitudy, umożliwiająca zmianę głośności, oraz umiejscowienie generatora na panoramie stereo. W kodzie można też odnaleźć fragment odpowiedzialny za synchronizowanie zmian amplitudy do zera fali (W praktyce do początku tablicy, ale najczęściej znajdują się tam próbki o najniższej wartości).
Poniżej fragment programu dla generatora CH1 (ogólnie cały program jest napisany bardzo nieefektywnie rozmiarowo, każdy generator ma swój osobny powtarzający się kod, dodatkowo można natknąć się na nieco niekonsekwencji spowodowanej nieciągłością czasową pisania programu

Code: armasm
W celu uzyskania zadowalającej jakości dźwięku przy wydajności umożliwiającej obsługę 8 kanałów okazało się konieczne napędzenie procesora z nieco większą niż zwykle częstotliwością. Pierwotnie było to 34.560Mhz, później zmniejszone do 32MHz. Wysoka częstotliwość zegara przekłada się na wyższą częstotliwość sprzętowego PWM procesora oraz mniejszy poziom szumów na wyjściu pseudo 16bitowego daca, przede wszystkim DDS wymaga jak najwyższego zegara w celu uniknięcia aliasingu dla wyższych częstotliwości. Synteza pracująca z 40kHz zegarem dostarcza akceptowalnej jakości sygnał do mniej więcej 2kHz.
Generowany dźwięk jest pobierany z 4 portów PWM. Pierwszy dostarcza sygnału MSB drugi PWM dostarcza LSB. Sygnały zsumowane w odpowiednich proporcjach zapewniają pseudo 16 bitową rozdzielczość przy częstotliwości „nośnej” 125kHz. We wcześniejszej wersji z zegarem 34MHz częstotliwość odniesienia była uzyskiwana z preskalera przetwornika ADC, który dostarczał koniecznego do równej pracy programu sygnału upłynięcia 832 cykli zegarowych, co odpowiadało częstotliwości próbkowania oraz głównej pętli programu 41.53kHz (dla 25MHz=30,04kHz). Później częstotliwość odniesienia została uzyskiwana z jednego z timerów . (32MHz/(3X256)=41,666kHz SR). W programie zrezygnowałem z używania przerwań, również stos przed dopisaniem obsługi odczytu karty SD oraz przetwarzania pliku midi był nieużywany.
Bloki przetwarzające są wywoływane poprzez odpowiedni podział czasu z wykorzystaniem rozkazu IJMP.
Przykład podziału czasu procesora dla obsługi jednego kanału (1/8 głównej pętli):
ijmpvectors: ;(8x16=128 wektorow)
rjmp Usart_receive ;midi read!
rjmp CH1Umodulator ;Universal modulator
rjmp Ch1_ornamentator
rjmp Ch1FRQmodulator
rjmp Ch1PITmodulator ;PITch koniecznie cykl za FRQ
rjmp Ch1ENVmodulator
rjmp Ch1PORTAM
rjmp Ch1BUFMOD
rjmp Usart_receive ;midi read!
rjmp nul ;reserved
rjmp nul
rjmp deltatimer
rjmp file
rjmp file2
rjmp nul
rjmp Ch1BUFMOD
...
Poglądowy opis poszczególnych bloków programu „syntezatora”
1.ATMEGA_8CH_synth.asm - program główny, inicjalizacja rejestrów, zarządzanie pętlą główną, przydział czasu procesora na bloki syntezatora oraz synchronizacja z zegarem.
2.table.inc- zawiera tablice fali, nuta/częstotliwość, ornamenty oraz inne tablice LUT.
3.usart_midi.inc- Program odpowiedzialny, za cykliczne sprawdzanie i pobieranie odebranego bajtu z usart lub pliku (program wywoływany 1/8 czasu procesora – czas procesora umownie cały obieg programu dokoła pętli głównej mainloop, program usarata wykonywany jest zawsze w nieparzystym numerze cyklu timera dzielącego czas procesora-8 bitowy licznik w zależności od jego wartości wykonywane są programy usart_midi oraz modulators i reg_buf ).Program zawiera rozpoznawanie komunikatów midi, i odpowiednie ładowanie danych (częstotliwość -nuta, wartości kontrolerów CC) bezpośrednio lub pośrednio przez zmienne _buf do syntezatora lub programu modulatorów.
4.synth.inc- Program zawierający syntezę DDS oraz dodatkowe obliczenia związane z modulacją amplitudy. Synth wykonuje się za każdym razem wywołania petli głównej. Warto tutaj zwrócić uwagę na „nie symetryczna” generacje fali (dobrze to widać na początku odtwarzania- składowa stała wędruje z 0V do ½ VCC, na końcu wraca na 0V), oraz ładowanie zbuforowanych wartości obwiedni podczas przejścia napięcia fali przez zero (faktyczne zero a nie ½) gdyż na próbkach bez znaku są prowadzone obliczenia.
5.modulators.inc- Procedury związane z modulacja dźwięku/zmiana parametrów zmiennych ładowanych już bezpośrednio do synth. Znajdują się tutaj podprogramy odczytujące wartości z tablic oraz rejestrów i w zależności od czasu modyfikujące amplitudę lub częstotliwość dla danego kanału.
6.ornamentators.inc- Program generujący charakterystyczne ornamenty dźwięku – modulowanie częstotliwości w równych odstępach czasu przesunięciami o zaprogramowaną liczbę półtonów. Ornamenty są odczytywane z osobnych tablic i mają długość 64B każdy. (W standardowej nomenklaturze muzycznej zwykło się nazywać takie ozdobniki arpeggio).
7.univmod.inc- Program uniwersalnego modulatora. Modulator który można zaprogramować na zmienianie wartości dowolnego kontrolera CC w dowolnego kanału. Modulator korzysta z tych samych tablic w rom co reszta generatorów.
8.portamento.inc- Program umożliwiający płynne przejścia między kolejnymi tonami. Działanie ze względu na komplikacje dopasowania do standardu odbiega nieco od rasowego portamento używanego w syntezatorach.
9.reg_buf.inc- programy przepisujące zmienne buforowane w celu ograniczenia zakłóceń dźwięku podczas zmiany wartości parametrów związanych z obwiednią - VOL PAN PH PW PWS.
*dodane po roku bo tyle sobie leżał syntezatorek w poczekalni, w międzyczasie doczekał się procedur obsługi karty SD i odczytu midi z pliku: *
10.M816_SD.inc- obsługa karty SD z poziomu sprzętu, inicjalizacja, adresowanie itd.
11.file_parser.inc- uproszczone odczytywanie nut midi z pliku (pomijane parametry związane z tempem).
W skrócie o działaniu programu:
Na początku sprawdzane jest, czy można zainicjować kartę SD. Jeśli tak, program szuka nagłówka MThd i uruchamia odczyt z karty SD (adresowanie 65536 bloków tylko). Jeśli brak karty, oczekuje na strumień danych poprzez usart. Dioda zaświeca się w przypadku wykrycia mniej niż 20 cykli pozostałych do sygnału synchronizacji- wskaźnik przeładowania procesora, przydatny podczas debuggingu i testu równomiernego rozłożenia czasu procesora na bloki modulatorów. Niektóre bloki syntezatora, jeśli użyte w wielu kanałach równocześnie mogą przedłużyć ponad normę czas wykonywania pętli programowej (np. portamento). Podczas prawidłowej pracy dioda powinna być zgaszona, ewentualnie sporadycznie sobie mrugnąć

Nieco o sprzęcie:
Jak widać na schemacie projekt HW jest minimalistyczny, nie użyłem nawet kondensatorów na wyjściu pwm. Potencjometry służą do dokładnego „odważenia” bajtów MSB LSB pwmowego daca. Procesor powinien bez problemu działać przy 3.7V, szczerze mówiąc to u mnie większość czasu był zasilany 4.5V, również po włożeniu karty SD (nie udało się jej uszkodzić). Nie próbowałem używać generatora kwarcowego procesora, dostawał zegar z osobnego układu. Niestety 32MHz to szczyt możliwości stabilnej pracy dla M16, dla M32 sprawa wyglądała minimalnie gorzej w trakcie testów maksymalnego zegara przy jakim procesor jeszcze działa jakkolwiek poprawnie, ale myślę, że z 32MHz M32 poradzi sobie równie dobrze jak M16. Układ po zablokowaniu zasilania tantalami o niskim ESR umożliwia osiągnięcie odstępu sygnał szum na poziomie nie gorszym niż 60dB. Do wyjścia można podłączyć 32omowe słuchawki i głośność będzie w sam raz. W celu rejestracji „sampli” podłączyłem wyjście bezpośrednio z wejściem karty SB220 bez zauważalnych szkód w obrazie audio spowodowanych nieodfiltrowanym 125kHz pwm.
Jak już wspomniałem przedstawiony projekt jest urządzeniem mało przydatnym, zamieszczonym głównie w celach edukacyjnych i archiwizacyjnych. Jedynym praktycznym zastosowaniem wydaje się możliwość odtwarzania muzyki bezpośrednio z karty SD i retransmisja strumienia midi na wyjściu TX procesora.
W celu bardziej intuicyjnej analizy przepływu danych w programie spróbowałem sporządzić jakikolwiek schemat blokowy syntezatora. Mam nadzieje, że chociaż w części wyjaśnia zasadę działania programu oraz ilość czasu jaką procesor musi poświecić na poszczególne jego elementy.
Zamieszczam też źródło całego programu, nieco rozlazłe, ale chciałem jak najbardziej oszczędzić na czasie, bo tego nigdy za wiele w takim przypadku. Podsumowując dołączam fragmenty audio kilku transkrypcji jako efekt końcowy całego projektu. Nie są one najlepszym „demo” wykorzystującym w pełni możliwości procesora, ale pozwalają na przybliżone zapoznanie się z jego możliwościami. Niestety używanie popularnych programów do edycji midi jest tu dość niewygodnie. Aby w pełni zapanować nad wszystkimi możliwościami generatorów należało by napisać osobny specjalizowany tracker umożliwiający szybki i odpowiednio dostosowany dostęp do rejestrów CC które są tu najczęściej wykorzystane w niestandardowy sposób.
Zainteresowanym tematem życzę udanych eksperymentów dźwiękowych.
Ponieważ nie mogłem załadować na elektrodę plików nie filmowych audio, załączam niżej spakowane zipem.
Cool? Ranking DIY