В. Артамонов

Программирование в системе Express Pascal

В языке Бейсик КУВТ "Корвет" нет специальных средств, позволяющих программировать мелодии. Однако с помощью оператора ВЕЕР этот пробел можно устранить.

После выполнения процедур начальной инициализации в системной области памяти ПК8020 появляются четыре 8-разрядных ячейки, работая с которыми можно при помощи встроенного динамика генерировать широкий по высоте и длительности диапазон звуков. Это ячейки со следующими адресами:

Первоначально в первые две ячейки заносится число 25А0, а во вторые - число 0800, что заставляет динамик генерировать звук частотой 800 Гц и длительностью 0.25 с.

Указанные выше факты позволили установить следующие соотношения: чтобы динамик генерировал звук с частотой F (Гц) и длительностью D (с), необходимо в ячейки с адресами F715, F716 записать число A=9632*800/F, а в ячейки с адресами F717, F718 - число B=2048*4*D. При этом по адресам F715 и F717 записываются младшие разряды указанных чисел.

Полученные формулы носят эмпирический характер, однако прослушивание профессиональными музыкантами мелодий, исполняемых компьютером, подтвердило их правильность.

Хотя соотношения для частоты и длительности уже позволяют программировать музыку, необходимый при этом перевод нот в форму многоразрядных чисел оказывается делом затруднительным. Поэтому возникла идея написать прикладную программу, имитирующую оператор PLAY расширенного Бейсика IBM PC, который предусматривает применение специального музыкального языка для программирования мелодий.

В силу особенностей ПК8020 удалось реализовать не все из 19 подкоманд оператора PLAY, однако наиболее важные были запрограммированы. Кроме того, исполнение мелодий в режиме реального времени, как это делается в операторе PLAY, вызвало затруднения из-за относительно медленной работы интерпретатора языка Бейсик ПК8020. Поэтому были написаны две программы: PLAY и MELOD.

Программа PLAY - своего рода компилятор, который каждую ноту представляет в виде четырех целых десятичных чисел и вместе с названием мелодии записывает на ГМД в файл с именем, выбранным пользователем. Она "понимает" следующие команды:

нота - з вучание указанной (С, D, Е, F, G, А, В) ноты в нужной октаве с диезом (знак +), бемолем (знак -) либо чистой (знак пробела);
O - задание номера от 0 до 7;
N (номер) - звучание ноты с номером от 01 до 17;
L (длительность) - задание длительности всех последующих нот от целой 01 до шестьдесят четвертой 64. Как вариант этот параметр может следовать за конкретной нотой, указывая ее длительность;
Р (длительность) - пауза. Параметр указывается, как и в команде L;
Т (число ударов) - задание темпа произведения от 30 до 260;
MN - исполнение нон легаго (обычное);
ML - исполнение легато;
MS - исполнение стаккато.

Внешне эти команды аналогичны подкомандам оператора PLAY. Однако есть ряд отличий.

Команда "." в явном виде отсутствует. Чтобы увеличить длительность звучания ноты в число раз, кратное 1,5, необходимо показатель кратности поставить в 5-6-ю позиции команды "нота" (например, D+0801); в 6-7-ю позиции подкоманды "N" (например, N050801); в 4-5-ю позиции подкоманд "L", "Р" (например, L0802 или Р1601).

Ноты В- и А+, А- и G+, G- и F+, Е- и D+, D- и С+ в подкоманде "N" имеют разные номера, хотя при исполнении мелодии звучат одинаково.

Мелодии могут записываться в восьми октавах, из которых две лежат ниже первой (номера 0 и 1), а пять - выше.

Программа MELOD озвучивает с помощью оператора ВЕЕР откомпилированные программой PLAY мелодии. Для этого указанный пользователем файл с мелодией считывается в оперативную память и перед каждым использованием оператора ВЕЕР в ячейки F715 - F718 заносятся целые числа, характеризующие очередную ноту.

Некоторую сложность представляет реализация пауз при исполнении мелодии. Специальные средства для этого отсутствуют; однако если в ячейки с адресами F715 и F716 записать число 2, то будут генерироваться импульсы с частотой 3,9 МГц, т. е. не воспроизводимые динамиком и не слышимые ухом. Это обстоятельство и было использовано.

Для иллюстрации приведем команды для исполнения на ПК8020 песни "Тонкая рябина".

    Т76,O2,Е 02,G 04,В 02,O3,С 04,O2,В 04,
    А ,Р01,D+02,F+04,O3,С 02,O2,В 04,А 04, 
    G ,Р01,Е 04,G 04,В 04,ОЗ,Е 02, D 04,
    D 04,C ,P01,D 02,С 04,O2,В 02,А 04,
    В 04,Е ,Р01,Е 04,G 04,В 04,ОЗ,Е 02,
    D 04,D 04,C ,P01,D 02,C 04,O2,В 02,
    А 04,В 04,ОЗ,Е   01

Ниже приведены также полные тексты программ PLAY и MELOD.

    5  REM *******"PLAY"*******
    10 REM ПРЕПРОЦЕССОР МЕЛОДИЙ
    15 REM АВТОР АРТАМОНОВ В.С.
    17 REM ********************
    20 CLS:PCLS:LOCATE 20,2,1:PRINT "ПРЕПРОЦЕССОР МЕЛОДИЙ":CLEAR 2000
    22 PRINT "ВВЕДИТЕ УНИКАЛЬНОЕ ИМЯ ФАЙЛА ДЛЯ ХРАНЕНИЯ МЕЛОДИИ,"
    24 PRINT "ПОСЛЕ ПОЯВЛЕНИЯ НА ЭКРАНЕ '0k' НАБЕРИТЕ:GOTO 27<ВК>:"
    25 INPUT NM¤:OPEN "О",#1,"FILE.BAS":NM¤="52 OPEN "+CHR¤(34)+"O"+CHR¤(34)+",#1,"+CHR¤(34)+NM¤+CHR¤(34):PRINT #1,NM¤:CLOSE 1
    26 MERGE "FILE.BAS"
    27 PRINT "НАЗВАНИЕ МЕЛОДИИ:":INPUT M¤:PRINT "АВТОР":INPUT A¤
    29 DIM B(17)
    30 B(1)=65.405:B(2)=69.41:B(3)=69.41:B(4)=73.415:B(5)=77.91
    35 B(6)=77.91:B(7)=82.405:B(8)=87.305:B(9)=92.6525:B(10)=92.6525
    40 B(11)=98!:B(12)=104!:B(13)=104!:B(14)=110!:B(15)=116.735:B(16)=116.735:B(17)=123.47
    45 DEF FNH(A¤,S¤)=(INSTR(S¤,LEFT¤(A¤,1))-1)*16+INSTR(S¤,RIGHTS(A¤,1))-1
    50 S¤="0123456789ABCDEF"
    52 OPEN "O",#1,"AAAAAA""
    54 PRINT #1,М¤,A¤
    55 O=2:T=2048*24:REM ПО УМОЛЧАНИЮ 2-Я ОКТАВА.ТЕМП-LARGHISSIMO
    57 KF=1:REM ПО УМОЛЧАНИЮ ДЛИТЕЛЬНОСТЬ РАВНА 1
    58 Z=1:REM ПО УМОЛЧАНИЮ ПРИЕМ- LEGATO. 
    59 GOSUB 1000:NT=0
    60 LOCATE 1,15,0:PRINT SPC(60):L0CATE 1,15,0:INPUT "КОМАНДА"; AG¤:IF AG¤="***" OR AG¤="" THEN 999
    65 G¤=LEFT¤(AG¤,1)
    70 IF G¤="O" THEN GOSUB 100:GOTO 60
    72 IF G¤="T" THEN GOSUB 200:GOTO 60
    74 IF G¤="N" THEN GOSUB 300:GOTO 60
    76 IF G¤="L" THEN GOSUB 400:GOTO 60
    78 IF G¤="P" THEN GOSUB 600:GOTO 60
    80 IF G¤="M" THEN GOSUB 500:GOTO 60
    85 GOSUB 2000:GOTO 60
    100 REM П/П ВЫЧИСЛЕНИЯ ОКТАВЫ.
    110 O=LEN(AG¤)-1:O¤=RIGHT¤(AG¤,O):O=VAL(O¤)
    120 IF O<0 THEN O=0 
    130 IF O>7 THEN O=7 
    140 RETURN
    200 REM П/П ВЫЧИСЛЕНИЯ ТАКТА.
    210 T=LEN(AG¤)-1:T¤=RIGHT¤3(AG¤,T):T=VAL(T¤) 
    220 IF T<=0 THEN T=40 
    230 T=2048*240/T 
    235 T=T*4 
    240 RETURN
    300 REM П/П ОЗВУЧИВАНИЯ HOT.
    310 W=LEN(AG¤)-1:W¤=RIGHT¤(AG¤,W):N=VAL(LEFT¤(W¤,2))
    320 KN=VAL(MID¤(W¤,3,2))
    321 IF KN=0 THEN 330
    322 IF KN<1 THEN KN=1
    324 IF KN>>64 THEN KN=64
    330 IF N<1 THEN N=1
    340 IF N>>17 THEN N=17
    350 KT=VAL(MID¤(W¤,5,2))*1.5
    360 IF KN>0 THEN DL=T*Z/KN:PUZ=T*(1-Z)/KN:GOTO 380
    370 DL=T*Z*KF:PUZ=T*(1-Z)*KF
    380 IF KT>0 THEN DL=DL*KT
    382 DL¤=HEX¤(DL):IF LEN(DL3)<4 THEN DL¤="0"+DL¤
    384 IF LEN(DL¤)<4 THEN DL¤="0"+DL¤
    386 PRINT #1,STR¤(FNH(LEFT¤(DL¤,2),S¤)):PRINT #1,STR¤(FNH(RIGHT¤(DL¤,2),S¤))
    388 F=9632*800/(B(N)*(2^(O+1))):F¤=HEX¤(F):IF LEN(F¤)<4 THEN F¤="0"+F¤
    389 IF LEN(F¤)<4 THEN F¤="0"+F¤
    390 PRINT #1,STR¤(FNH(LEFT¤(F¤,2),S¤)):PRINT #1,STR¤(FNH(RIGHT¤(F¤,2),S¤))
    391 IF PUZ=0 THEN NT=NT+4:G0T0 396
    392 PUZ¤=HEX¤(PUZ):IF LEN(PUZ¤)<4 THEN PUZ¤="0"+PUZ¤
    393 IF LEN(PUZ¤)<4 THEN PUZ¤="0"+PUZ¤
    394 PRINT #1,STR¤(FNH(LEFT¤(PUZ¤,2),S¤)):PRINT #1,STR¤(FNH(RIGHT¤(PUZ¤,2),S¤))
    395 PRINT #1,STR¤(0):PRINT #1,STR¤(2):NT=NT+8
    396 LOCATE 40,15:PRINT SPC(5):LOCATE 40,15 PRINT NT04:FOR IN=1 TO 99:NEXT IN:RETURN
    400 REM П/П ОПРЕДЕЛЕНИЯ ДЛИТ.
    410 L=LEN(AG¤)-1:L¤=RIGHT¤(AG¤,L):L=VAL(LEFT¤(L¤,2))
    420 IF L<1 THEN L=1
    440 IF L>64 THEN L=64
    445 LT=VAL(MID¤(L¤,3,2))*1.5
    450 KF=1/L:IF LT>0 THEN KF=KF*LT
    455 RETURN
    500 REM П/П ПРИЕМА ИЗВЛ. ЗВУКА.
    505 REM STACCATO
    510 IF AG¤="MS" THEN Z=3/4:RETURN
    515 REM LEGATO
    520 IF AG¤="ML" THEN Z=1:RETURN 525 
    REM NON LEGATO
    530 IF AG¤="MN" THEN Z=7/8:RETURN
    540 Z=1:PRINT "НЕВЕРНАЯ КОМАНДА !" 
    550 RETURN
    600 REM П/П ОПРЕД. ПАУЗ В СЕК.
    610 P=LEN(AG¤)-1:P¤=RIGHT¤(AG¤,P) :P=VAL(LEFT¤(P¤,2))
    620 IF P<1 THEN P=1 
    630 IF P>64 THEN P=64 
    640 PZ=T/P
    642 PT=VAL(MID¤(P¤,3,2))*1.5:IF PT>0 THEN PZ=PZ*PT
    643 PZ¤=HEX¤(PZ):IF LEN(PZ¤)<4 THEN PZ¤="0"+PZ¤ 
    645 IF LEN(PZ¤)<4 THEN PZ¤="0"+PZ¤
    650 PRINT #1,STR¤(FNH(LEFT¤(PZ¤,2),S¤)):PRINT #1,STR¤(FNH(RIGHT¤(PZ¤,2),S¤))
    655 PRINT #1,STR¤(0):PRINT #1,STR¤(2):NT=NT+4
    660 LOCATE 40,15:PRINT SPC(5):LOCATE 40,15:PRINT NTO4:FOR IN=1 TO 99:NEXT IN:RETURN 
    999 CLOSE 1:END 
    1000 REM П/П ИНСТРУКЦИЙ. 
    1005 CLS:LOCATE 25,1,1 PRINT "ИНСТРУКЦИЯ":PRINT "ВЫ МОЖETE ВВОДИТЬ СЛЕДУЮЩИЕ КОМАНДЫ:"
    1010 PRINT "1. ОХ,ГДЕ Х-НОМЕР ОКТАВЫ;PRINT "2. ТХХХ.ГДЕ ХХХ-ТЕМ П МЕЛОДИИ;"
    1015 PRINT "3. NXXYYZZ,ГДЕ XX-НОМЕР НОТЫ, Yf-ДЛИТЕЛЬНОСТЬ, ZZ-УВЕЛИЧЕНИЕ ДЛИТЕЛЬНОСТИ В ZZ*1.5 РАЗ;"
    1020 PRINT "4. LXXZZ,ГДЕ XX-ДЛИТЕЛЬНОСТЬ ПО УМОЛЧАНИЮ,ZZ-УВЕЛИЧЕНИЕ ДЛИТЕЛЬНОСТИ В ZZ*1.5 РАЗ;" 
    1025 PRINT "5. PXXZZ,ГДЕ XX-ДЛИТЕЛЬНОСТЬ ПАУЗ,ZZ-УВЕЛИЧЕНИЕ ДЛИТЕЛЬНОСТИ В ZZ*1.5 РАЗ;"
    1030 PRINT "6. MS,ML,MN-ПРИЕМЫ ИЗВЛЕЧЕНИЯ ЗВУКА(STACCATO, LEGATO, NON LEGATO);" 
    1032 PRINT "7. YXZZUU,ГДЕ Y-ОБОЗНАЧЕНИЕ НОТЫ,X(+,-,ПРОБЕЛ), ZZ-ДЛИТЕЛЬНОСТЬ,UU-УВЕЛИЧЕНИЕ ДЛИТЕЛЬНОСТИ В UU*1.5 РАЗ." 
    1040 RETURN 
    2000 REM П/П ОПРЕД. НОМЕРА НОТЫ. 
    2010 Q¤="C C+D-D D+E-E F F+G-G G+A-A А+В-В "
    2015 G¤=G¤+MID¤(AG¤,2,1):NN=INSTR(Q¤,G¤)
    2020 IF NN=0 THEN LOCATE 20,15:PRINT "НЕВЕРНАЯ КОМАНДА !":FOR IN=1 TO 50:NEXT IN:RETURN
    2025 NN=(NN+1)/2:NN¤=STR¤(NN):NN¤=MID¤(NN¤,2,LEN(NN¤)-1):IF LEN(NN¤)<2 THEN NN¤="0"+NN¤ 
    2030 G¤=MID¤(AG¤,3,2):GG=VAL(G¤):G¤=STR¤(GG):G¤=MID¤(G¤,2,LEN(G¤)-1):IF LEN(G¤)<2 THEN G¤="0"+G¤
    2035 DD¤=MID¤(AG¤,5,2):DD=VAL(DD¤):DD¤=STR¤(DD):DD¤=MID¤(DD¤,2,LEN(DD¤)-1):IF LEN(DD¤)<2 THEN DD¤="0"+DD¤ 
    2040 AG¤="N"+NN¤+G¤+DD¤:G¤="N"
    2045 LOCATE 20,15:PRINT AG¤:FOR IN=1 TO 50:NEXT IN 
    2050 GOSUB 300 
    2060 RETURN
    3 REM ******"MELOD"*******
    4 REM ПРОГРАММА ПРОИГРЫВАНИЯ МЕЛОДИЙ.
    5 REM АВТОР АРТАМОНОВ В.С.
    6 REM ********************
    7 GOSUB 100:MERGE "FILE.BAS"
    9 CLEAR 2000
    17 Z=1:PRINT "КОЭФФИЦИЕНТ ДЛИТЕЛЬНОСТИ (1 ИЛИ ЧИСЛО МЕНЬШЕЕ 15)":INPUT Z
    20 I=1:DIM М(5000):INPUT #1,M¤
    22 PCLS:CLS:PRINT M¤
    24 IF EOF(1) THEN 30
    26 INPUT #1,M(I)
    28 I=I+1:GOTO 24
    30 I=I-1:FOR J=1 TO I STEP 4
    40 A=M(J):B=M(J+l):C=M(J+2):D=M(J+3)
    45 A=A*Z
    47 IF A>>255 THEN A=255
    50 POKE &HF718,A
    60 POKE &HF717,B
    70 POKE &HF716,C
    80 POKE &HF715,D
    90 BEEP
    95 NEXT
    99 CLOSE 1:END
    100 REM ПОДПРОГРАММА С ИНСТРУКЦИЯМИ.
    110 PCLS:CLS:LOCATE 20,2,1:PRINT "ЭЛЕКТРОННЫЙ ОРКЕСТР"
    120 PRINT "ВВЕДИТЕ ИМЯ ФАЙЛА С MEЛОДИЕЙ,:PRINT "ПОСЛЕ ПОЯВЛЕНИЯ НА ЭКРАНЕ 'Ок' ВВЕДИТЕ:GOTO9<BK>"
    130 INPUT NM¤
    140 NM¤="10 OPEN "+CHR¤(34)+"I"+CHR¤(34)+",#1,"+CHR¤(34)+NM¤+CHR¤(34)
    150 OPEN "O",#1,"FILE.BAS"
    155 PRINT #1,NM¤:CLOSE 1 
    160 RETURN