05 июля 2015 |
|
Музыкальный движок Muse 128b evilpaul/Ate Bit Муза - такая штука,которая может внеза─ пно прийти, когда задаёшься сложной зада─ чей. Тут случай был такой: я смотрел 128- байтные звуковые демонстрации gwEm'ана Atari ST и решил - я тоже должен попробо─ вать! Я уже писал несколько музыкальных пле─ йеров для ZX в разные времена. Ещё когда Ate Bit официально не существовал,мы выпу─ скали пару дем группой evilpaul/ЧMat/Eater - и они как раз использовали мои первые попытки в области создания плейеров (я то─ гда даже не знал, что на Спектруме сущест─ вуют трекеры). Шли годы, и мы перешли на обычный плейер от Vortex Tracker'а, но я до сих пор пишу плейеры для наших малень─ ких дем. Первый такой был для mipmap (2007 год) - там надо было реализовать в 1k intro несколько эффектов и музыку, чтобы превзойти все старые 1k intro. Разумеется, места для хранения настоящих музыкальных данных не было, поэтому ноты вытаскивались прямо из ПЗУ. Главная хитрость была в под─ стройке данных перед проигрыванием,и самый простой способ для этого - интерпретиро─ вать случайные данные в рамках конкретной гаммы. Например, так: 0 -> C 1 -> D 2 -> E 3 -> F 4 -> G 5 -> A 6 -> B 7 -> C 8 -> Dи т.д. Так случайные данные окажутся в тональ─ ности до-мажора, и это довольно работоспо─ собный способ для получения "удовлетвори─ тельно" звучащей музыки из рандомной пос─ ледовательности.Всё,что требуется - табли─ ца высот нот в мажорной гамме, которая мо─ жет быть легко сгенерирована из нескольких значений. Получается что-то вроде обычного трекерного плейера, но только мы не зани─ маем место в таблице нот под ноты, которые мы не используем. Можно даже двинуться дальше и использовать 5 нот, так называе─ мую пентатонику. Например: 0 -> C 1 -> D 2 -> E 3 -> G 4 -> A 5 -> C 6 -> D 7 -> E 8 -> Gи т.д. Пентатоника хорошо знакома гитаристам - каждый, кто пытался импровизировать мело─ дию или бас на гитаре, знает, что если за─ жать на грифе пентатонику, то практически всё, что играешь, звучит более чем непло─ хо. То же самое происходит с пропусканием данных из ПЗУ через пентатонику. Если вы реализовали такое дело, то дальше достато─ чно зациклить, например, 16 байт и поис─ кать хорошие 16-байтные последовательности в ПЗУ. Это может быть довольно долгий про─ цесс, но по идее, если зациклить что-то несколько раз, мозг сам воспринимет это как мелодию. Запомните этот факт, мы к не─ му ещё вернёмся. Итак, теперь вы можете написать хорошую мелодию для 1k демы, вообще не храня нот─ ный текст. Или не можете? Есть две вещи, которые вы не достанете из случайных дан─ ных - инструменты и ударные. Инструменты хранятся в виде коротких таблиц с громкос─ тями и сдвигами частоты. Громкости управ─ ляют огибающей звука, а сдвиги частоты мо─ гут дать вибрато или при необходимости другие эффекты типа слайдов или легато.Ли─ нию ударных я обычно пишу вручную. Четыре паттерна по четыре такта из четырёх нот каждый, ритмически повторяющийся в неболь─ шим изменением в конце, занимает 64 байта. Это слишком много для 1k демо, но к счас─ тью, это хорошо пакуется. Я делал пару 1k дем с таким методом, а также 1kdj, который использует то же са─ мое, но с нотами, написанными вручную,а не взятыми из ПЗУ.При этом пришлось пожертво─ вать визуальными эффектами и графикой,зато там много места под плейер и музыкальные данные. А однажды в начале 2014 года я пи─ сал новый маленький плейер для 4k демы. Этот проект развился в демо "Daytrip" (бу─ дет показано на Sundown 2015 ) с большим музыкальным плейером, который никак не влезет в 4k. Но у меня было несколько идей в начале разработки этого демо как раз по поводу совсем крошечного музыкального пле─ йера. Именно тогда я наткнулся на 128-бай─ тные звуковые демонстрации от gwEm. Ведь явно что-то похожее можно сделать и на Спектруме? Ответ был,разумеется, "да",но для этого требовалась ещё пара хитростей. Во-первых, в 128 байтах нет места ни для кода,переко─ дирующего случайные данные, ни для таблицы нот.Но это не проблема,так как мы уже зна─ ем, что если зациклить случайный набор нот несколько раз, мозг решит, что это музыка. Это легко проверить: оцифруйте пару секунд чего угодно и прокрутите в цикле - в 9 случаях из 10 это сойдёт за перкуссию. Muse именно так и делает. Она играет один случайный набор нот, высоких, в качестве мелодии,а другой набор нот, низких и более долгих, в качестве баса. Ударные зашиты в код в виде набора из 4 нот. Эти 4 ноты - первые 4 байта программы,из них первые три (SCF,NOP,NOP) ничего не делают,а четвёртый - это первая настоящая команда в программе (LD DE,nn). Ударные не шедевр, но занимают всего три байта. Другая новая хитрость - в том, что Muse не синхронизирована с прерываниями.Там да─ же нетHALT. Вместо этого она просто кру─ тится в коротком цикле между тиками и в конце концов обновляет регистры AY на час─ тоте выше 50 Гц - в результате звук неско─ лько отличается от плейеров,которые играют на 50 Гц. Плейер работает с 10-байтной структурой данных для каждого канала.На эту структуру указывает регистрIX - плейер обрабатывает один канал за раз. Регистр микшера в AY настраивается в начале программы так,чтобы один из каналов был с включенным шумом - это будут ударные. Каждый канал имеет таймер, поэтому ка─ налы играют с разной скоростью - бас мед─ леннее,ударные быстрее.Из значения таймера ещё вычисляется текущая громкость канала. Мелодии берутся из ПЗУ, но мы указываем только СТАРШИЙ байт адреса в данных кана─ ла. С таким ограничением довольно трудно найти в ПЗУ хорошие паттерны, но опять- таки - это экономит несколько байтов. И наконец, как вы увидите в исходнике, Muse использует несколько байт данных (по три на каждый канал, плюс один для удар─ ных), которые находятся вне области прог─ раммы. Рассчитано на то,что там изначально лежат нули. Вот сам исходник Muse (компилируется ассемблером pasmo ). AYCTRL equ 65533 AYDATA equ 49149 org 32768 start ; паттерн ударных лежит по круглому адресу ;и занимает 4 байта ; три байта мы задаём, 4-й берём ;из команды ld de,nn ; не лучший вариант ударных, но ;оставалось только три байта... drumPattern db 0x37 ; scf db 0 ; nop db 0 ; nop ld de,%110000+(7<<8);настраиваем ;регистр микшера calloutAyByte ; -- главный цикл -- loop ld ix,databass ; указатель на ;данные первого канала call chanplay db Oxdd,0x2e,LOW(datatune) ;ld lx call chanplay db Oxdd,0x2e,LOW(datadrums);ld lx call chanplay synca djnz synca syncb add hl,sp ;хорошая, медленная, ;однобайтная инструкция... djnz syncb jr loop ; -- играть один канал -- chanplay ; обновляем таймер ld a,(ix+CHA_TIMER) sub (ix+CHA_SPEED) ld (ix+CHA_TIMER),a ld h,a ;запоминаем таймер для ;огибающей громкости ниже jr nz,nonoteinc ; следующая нота inc (ix+CHA_LOOPPOS) ld a,(ix+CHA_LOOPPOS) and (ix+CHA_REPEATLENGTHMASK) jr nz,nonewpat ; следующий паттерн ld a,(ix+CHA_PATTERNDATALOW) add a,(ix+CHA_PATTERNDATASTEP) ld (ix+CHA_PATTERNDATALOW),a nonewpat nonoteinc ld d,(ix+CHA_PATTERNDATAHIGH) ld a,(ix+CHA_LOOPPOS) and (ix+CHA_LOOPLENGTHMASK) add a,(ix+CHA_PATTERNDATALOW) ld e,a ld a,(de) ld d,(ix+CHAN_AYCOARSEPITCHCTRL) ld e,a call outAyByte;установим частоту ld b,4 ; h=таймер, >>4, чтобы получить громкость vshift srl h djnz vshift ld d,(ix+CHAN_AYVOLCTRL) ld e,h ; call outAyByte ;установим громкость ; ret ; d = ctrl, e = data outAyByte ld bc,AYCTRL out (c),d ld b,HIGH(AYDATA) out (c),e ret CHAN_AYCOARSEPITCHCTRL equ 0 CHAN_AYVOLCTRL equ 1 CHA_PATTERNDATALOW equ 100 CHA_PATTERNDATAHIGH equ 2 CHA_TIMER equ 101 CHA_SPEED equ 3 CHA_LOOPPOS equ 102 CHA_LOOPLENGTHMASK equ 4 CHA_REPEATLENGTHMASK equ 5 CHA_PATTERNDATASTEP equ 6 databass db 5 db 10 db 5 db 1 db %1 db %111 db 64 datatune db 3 db 9 db 13 db 8 db %1111 db %111111 db 19 datadrums db 1 db 8 db HIGH(drumPattern) db 4 db %11 db %11 ; db 0 end start
Other articles:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Similar articles:
В этот день... 21 November