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:
Prohodilka - Homer Simpson in Russia.

В этот день...   21 November