Мир звуков Спектрума - глава 4.5: Программирование звуковых эффектов - Многоголосые мелодии (полифония).

ZX Forum #04
        4.5. Многоголосые мелодии

   Хотя в предыдущей главе  я  старательно
доказывал Вам тщетность попыток программи-
рования музыки на ассемблере, Вы  все-таки
должны знать основные принципы этого заня-
тия.
   Сейчас я  опишу  создание  многоголосой
музыки, то есть  музыки, в  которой  может
звучать две или больше ноты одновременно.
   Первым  примером  будет   подпрограмма,
имитирующая двухголосое звучание:
1415.
 10            DI                 ; запрет прерываний
 20            LD      D,200      ; D=частота 1
 30            LD      E,50       ; E=частота 2
 40            LD      H,150      ; H=длительность
 50            XOR     A          ; A=бордюр для голоса 1 (0)
 60            EX      AF,AF'     ; смена регистров A и F на
                                    альтернативные
 70            XOR     A          ; A=бордюр для голоса 2 (0)
 80            LD      C,E        ; C=счетчик 1
 90            LD      B,D        ; B=счетчик 2
100  BEEP      EX      AF,AF'     ; смена регистров A и F
                                    (смена голоса)
110            DEC     C          ; C=C-1
120            JR      NZ,CONT    ; если C<>0, то перейти на
                                    CONT
130            LD      C,E        ; восстановить счетчик 1
140            XOR     16         ; инвертировать бит D4 голоса
                                    1
150  CONT      OUT     (254),A    ; вывести A в порт 254
160            EX      AF,AF'     ; смена регистров A и F
                                    (смена голоса)
170            DEC     B          ; B=B-1
180            JR      NZ,CONT2   ; если B<>0, то перейти на
                                    CONT2
190            LD      B,D        ; восстановить счетчик 2
200            XOR     16         ; инвертировать бит D4 голоса
                                    2
210  CONT2     OUT     (254),A    ; вывести A в порт 254
220            INC     L          ; L=L+1
230            JR      NZ,BEEP    ; если L<>0, то перейти на
                                    BEEP
240            NOP                ; резерв
250            NOP                ; резерв
260            DEC     H          ; H=H-1
270            JR      NZ,BEEP    ; если H<>0, то перейти на
                                    BEEP
280            EI                 ; разрешение прерываний
290            RET                ; возврат
2
Теперь поясню некоторые детали.
   Два альтернативных регистра A использу-
ются первым и вторым голосами для хранения
цвета бордюра и состояния динамика.
   Странная операция с регистром  L  перед
завершением цикла предназначена для  "рас-
тягивания" длительности. Если  ее  убрать,
то звучание этой подпрограммы станет  нас-
только кратковременным, что Вы можете  его
даже не услышать.
   Два байта резерва (операции NOP) Вы мо-
жете использовать  для  изменения  частоты
обоих голосов  во  время  воспроизведения.
Для этого вместо "NOP" нужно вставить  INC
D или DEC D для первого голоса и INC E или
DEC E для второго.
   Эту подпрограмму можно использовать  на
ассемблере (см. предыдущую главу)  или  на
бейсике (в данном  случае  параметры  сле-
дует заносить оператором POKE в  следующие

ячейки : ADDR+2 - частота 1, ADDR+4 - час-
тота 2, ADDR+6 - длительность, где  ADDR -
адрес расположения подпрограммы в памяти).
   Следующие две многоголосые  подпрограм-
мы очень похожи  друг на друга, но  первая
звучит значительно тише и  несколько  чище
второй.
   Вот они:
1415.
 10            DI                 ; запрет прерываний
 20            LD      HL,2000    ; HL=длительность
 30  LOOP1     LD      IX,FRQS    ; IX=адрес таблицы частот
 40            LD      B,5        ; B=число голосов
 50  LOOP2     DEC     (IX+0)     ; байт по адресу IX+0
                                    уменьшить на 1
 60            JR      NZ,NOSIGN  ; если не 0, то перейти на
                                    NOSIGN
 70            LD      A,24       ; A=цвет бордюра + 24
 80            OUT     (254),A    ; вывод A в порт 254
 90            LD      A,(IX+5)   ; обновление счетчика
100            LD      (IX+0),A   ;   текущего канала
110            XOR     A          ; A=цвет бордюра (0)
120            OUT     (254),A    ; вывод A в порт 254
130  NOSIGN    INC     IX         ; IX=IX+1
140            DJNZ    LOOP2      ; цикл
150            DEC     HL         ; HL=HL-1
160            LD      A,H        ; HL=
170            OR      L          ;    0 ?
180            JR      NZ,LOOP1   ; если нет, то цикл
190            EI                 ; разрешение прерываний
200            RET                ; возврат
210  FRQS      DEFB    1,1,1,1,1  ; счетчики для 5 каналов
220            DEFB    10,20,30   ; частоты для
230            DEFB    40,50      ;    5 каналов

 10            DI                 ; запрет прерываний
 20            LD      HL,2000    ; HL=длительность
 30  LOOP1     LD      IX,FRQS    ; IX=адрес таблицы частот
 40            LD      B,5        ; B=число голосов
 50  LOOP2     DEC     (IX+0)     ; байт по адресу IX+0
                                    уменьшить на 1
 60            JR      NZ,NOSIGN  ; если не 0, то перейти на
                                    NOSIGN
 70            LD      A,(IX+5)   ; A=содержимое IX+5
 80            XOR     1          ; инвертирование бита D0
 90            LD      (IX+5),A   ; содержимое IX+5=A
100            LD      A,0        ; A=цвет бордюра
110            JR      Z,NOSIGN   ; если D0 содер. IX+5=0, то
                                    перейти на NOSIGN
120            OR      24         ; установить в 1 биты D3 и
                                    D4 регистра A
130            OUT     (254),A    ; вывод A в порт 254
140            LD      A,(IX+10)  ; обновление счетчика
150            LD      (IX+0),A   ;   текущего канала
160  NOSIGN    INC     IX         ; IX=IX+1
170            DJNZ    LOOP2      ; цикл
180            DEC     HL         ; HL=HL-1
190            LD      A,H        ; HL=
200            OR      L          ;    0 ?
210            JR      NZ,LOOP1   ; если нет, то цикл
220            EI                 ; разрешение прерываний
230            RET                ; возврат
240  FRQS      DEFB    1,1,1,1,1  ; счетчики для 5 каналов
250            DEFB    0,0,0,0,0  ; буферы для 5 каналов
260            DEFB    10,20,30   ; частоты для
270            DEFB    40,50      ;  5 каналов
2
   Эти подпрограммы заводят  счетчики  для
каждого из каналов  и  последовательно  их
уменьшают. Если  какой-либо  из  счетчиков
становится равным 0, на динамик  выводится
сигнал, а в данный счетчик заносится  час-
тота из таблицы.
   В  данных  примерах  подпрограммы  вос-
производят по пять  голосов, но  Вы  може-
те легко изменить их  число, модифицировав
значения в строках 40 и 90 для первой под-
программы или 40,70,90 и 140 (в строку 140
надо вставить число голосов, умноженное на
2) - для второй. При этом надо также скор-
ректировать таблицы частот.
   Хотя голосов может быть  довольно  мно-
го (до 85), увлекаться  их количеством  не
стоит, так как чем их больше, тем хуже ка-
чество. Впрочем, вряд ли  найдется  гений,
способный написать мелодию на 85 голосов.
   Если Вы хотите использовать  многоголо-
сие, но в конкретный  момент  времени  Вам
надо воспроизвести меньшее число  голосов,
чем задано, то неиспользуемым голосам  на-
до присвоить частоты, совпадающие с часто-
тами используемых голосов.