Справочник программиста на персональном компьютере фирмы IBM. Таймеры и звук - Генерация набора тонов

ОГЛАВЛЕНИЕ

     2.2.5 Генерация набора тонов.


   В  этом  подразделе показано как генерировать цепочку  звуков,
когда компьютер ничем другим не занят; в следующем будет показано
как выполнить ту же задачу, когда компьютер занят другой работой.
Когда компьютер ничем другим не занят,  то можно выводить мелодию
или производить специальные звуковые эффекты; когда же  компьютер
занят другой работой, то нельзя производить звуковые эффекты.
   Создание звуковых строк является одной из мощнейших возможнос-
тей, предоставляемых Бейсиком.  Построение же строк звуков в  ас-
семблере требует большой работы.  Может быть использован любой из
двух  методов генерации звука, предложенных в [2.2.2] и  [2.2.3].
Для обоих методов надо  просто  генерировать  один  тон в течении
заданного времени, затем следующий и т.д.  Каждая звуковая строка
формируется из двух строк данных, одна из которых содержит часто-
ты  последовательных тонов, а другая хранит их длительности  (при
условии, что требуются разные  длительности).   Продолжительность
звучания  определяется  с использованием счетчика  времени  суток
BIOS [2.1.6].

   Высокий уровень.


   Опреатор Бейсика PLAY предоставляет большие возможности.  Опе-
ратор  сопровождается строкой нот, перемешанных с  информацией  о
том, как эти ноты должны быть исполнены. Ноты записываются буква-
ми A - G и последующими знаками для диезов и бемолей. Диезы обоз-
начаются знаками # или +, а  бемоли  минусом (-).  Операторы PLAY
"CC#D" и PLAY "CD-D" эквивалентны, но нельзя использовать диезы и
бемоли для обозначения белых  клавиш.   Второй способ задания нот
состоит  в вычислении кодового номера от 0 до 84, причем 0  соот-
ветствует отсутствию звучания,  а  числа от 1 до 84 соответствуют
84 возможным нотам семи октав, начиная снизу. Номеру должна пред-
шествовать буква N: PLAY "N3N72N44".
   Допустимый диапазон -  семь  октав,  внутри  каждой могут быть
ноты от C(до) до B(си).  Октавы пронумерованы от 0 до 6 и нота до
первой октавы соответствует  октаве  3. Текущая октава может быть
изменена  в  любой момент, за счет вставки в строку буквы  O,  за
которой следует номер октавы.  Если  не было начальной установки,
то  используется октава 4.  Оператор PLAY "O3CO4CO5CO6C"  выводит
ноты до последовательных октав  вверх.   Другой  способ изменения
октавы  состоит во включении в строку символов > или  <,  которые
переключают тон вверх и вниз на октаву, соответственно.  Оператор
PLAY "O3C>C>C>C" приводит к тому же результату, что и предыдущий.
   Длительность исполнения нот также может быть изменена за  счет
вставки кодового номера, которому предшествует буква L.  Все пос-
ледующие ноты будут исполняться с этой длительностью до тех  пор,
пока не встретится другой код длины.  Код - это число от 1 до 64,
причем  1 соответствует целой ноте, а 64 - 1/64.  Запись L4 соот-
ветствует четверти.  Темп с которым исполняются ноты регулируется
кодом темпа, который состоит из буквы T, за которой следует число
от 32 до 255, дающее число четвертей, исполняемых в минуту.  Если
эти параметры не указаны, то по умолчанию берется длительность L4
и темп 120.  Для  изменения  длительности  только одной ноты надо
поместить значение длины после ноты и без буквы L.  Оператор PLAY

"L4CDE16FG" исполнит E как шестнадцатую, а все остальные ноты как
четверти.  Длительность пауз берется такой же, как и длительность
нот.  Поместите номер от 1 до 64 после буквы P для паузы.  P1 де-
лает  паузу интервалом в целую, а P64 - в 1/64.  Помещение  точки
после ноты имеет тот же эффект,  какой  он  имеет в обычной музы-
кальной  нотации:  длительность  ноты  увеличивается  наполовину.
Вторая точка продолжит длительность еще наполовину.
   По умолчанию ноты играются 7/8 указанной  длительности.  Чтобы
они исполнялись полную длительность (легато), поместите в  строку
ML. Чтобы они исполнялись 3/4  длительности (стаккато), поместите
в строку MS. Чтобы вернуться к нормальному стилю надо указать MN.
   Обычно,  вся прочая деятельность программы прекращается до тех
пор, пока не будет  сыграна  строка.  Для  того чтобы выполнялись
операторы,  следующие за оператором PLAY, а строка исполнялась  в
фоновом режиме, поместите в  строку  MB.  Для восстановления нор-
мальной ситуации напишите MF.
   Наконец,  оператор  PLAY позволяет исполнять подстроки  внутри
длинной строки.  Имеется  в  виду,  что  часть исполняемой строки
может быть введена как обычная строковая переменная, а затем  эта
переменная может быть вызвана из строки сформированной в операто-
ре  PLAY.   Например,  если S$ =  "EEEEE",  то в  операторе  PLAY
"CDXS$;FG" нота E будет повторена 5 раз. Отметим, что имени пере-
менной должна предшествовать буква X, а за именем следовать точка
с запятой (;).  (Для  компилируемых  программ  применяется другой
метод, использующий переменную VARPTR$ - детали см. в руководстве
по Бейсику).

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

   100 PLAY "ML T40 O3 ECD<G P32 G>DEC"

   Благодаря наличию генератора звука PCjr добавляет к  оператору
PLAY две возможности. Во-первых, допускается параметр V, устанав-
ливающий  громкость.  Выражение V5 устанавливает  (или  изменяет)
громкость на уровень 5. Допустимый диапазон от 0 до 15, причем по
умолчанию  берется 8.  0 полностью подавляет звук.  Во-вторых,  с
помощью оператора PLAY можно  одновременно исполнять три звуковых
строки.  Поместите все три строки в одну программную строку, раз-
деляя их запятыми.  Для того чтобы иметь возможность использовать
эти  специальные  свойства,  Вы должны  предварительно  разрешить
внешний динамик с помощью оператора SOUND ON.

   100 SOUND ON
   110 PLAY "...........","..........","............"

   Низкий уровень.


   В примере для генерации звука используется  микросхема таймера
8253.   Здесь просто исполняются 8 нот, но небольшая  модификация
может сильно расширить  возможности  этой процедуры.  Имеется три
строки данных. Первая устанавливает длительность каждой ноты, как
кратное произвольного периода  задержки  (изменяя этот период за-

держки,  можно  изменять темп).  Вторая строка  содержит  частоты
каждой из 8 нот;  эти  значения  должны  быть  помещены в регистр
задвижки  канала 2 микросхемы 8253 для исполнения желаемых тонов.
Третья строка содержит мелодию в виде  кодовых номеров от 1 до 8,
которые  соответствуют  восьми частотам.  Эта строка  завершается
кодом 0FFH, который  служит  признаком  конца  мелодии. Процедура
просто  читает  очередную ноту мелодии,  находит  соответствующую
частоту и помещает ее в канал 2. Затем длительность для этой ноты
помещается  в счетчик цикла задержки, который использует  счетчик
времени суток, а когда задержка  кончается,  то переходим к обра-
ботке следующей ноты. На рис. 2-5 показана работа этой процедуры.

;---в сегменте данных
BEAT        DB   10,9,8,7,6,5,4,3,2    ;длительность нот
FREQUENCY   DW   2280,2031,1809,1709   ;таблица частот
            DW   1521,1353,1207,1139   ;
MELODY      DB   1,2,3,4,5,6,7,8,0FFH  ;номер частоты ноты

;---инициализация
PORT_B      EQU  61H
COMMAND_REG EQU  43H
LATCH2      EQU  42H
            IN   AL,PORT_B      ;получаем текущий статус
            OR   AL,00000011B   ;разрешаем динамик и таймер
            OUT  PORT_B,AL      ;заменяем байт
            MOV  SI,0           ;инициализируем указатель
            MOV  AL,0B6H        ;установка для канала 2
            OUT  COMMAND_REG,AL ;посылаем в командный регистр
;---смотрим ноту, получаем ее частоту и помещаем в канал 2
NEXT_NOTE:  LEA  BX,MELODY      ;берем смещение для мелодии
            MOV  AL,[BX][SI]    ;берем код n-ной ноты строки
            CMP  AL,0FFH        ;проверка на конец строки
            JE   NO_MORE        ;если конец, то на выход
            CBW                 ;переводим в слово
   ;получение частоты
            MOV  BX,OFFSET FREQUENCY  ;смещение таблицы частот
            DEC  AX             ;начинаем отсчет с 0
            SHL  AX,1           ;умножаем на 2, т.к. слова
            MOV  DI,AX          ;адресуем через DI
            MOV  DX,[BX][DI]    ;получаем частоту из таблицы
   ;начинаем исполнение ноты
            MOV  AL,DL          ;готовим младший байт частоты
            OUT  LATCH2,AL      ;посылаем его
            MOV  AL,DH          ;готовим старший байт частоты
            OUT  LATCH2,AL      ;посылаем его
;---создание цмкла задержки
            MOV  AH,0           ;номер функции чтения счетчика
            INT  1AH            ;получаем значение счетчика
            MOV  BX,OFFSET BEAT ;смещение таблицы длин
            MOV  CL,[BX][SI]    ;берем длину очередной ноты
            MOV  CH,0           ;
            MOV  BX,DX          ;берем младшее слово счетчика
            ADD  BX,CX          ;определяем момент окончания

STILL_SOUND: INT 1AH            ;берем значение счетчика
            CMP  DX,BX          ;сравниваем с окончанием
            JNE  STILL_SOUND    ;неравны - продолжаем звук
            INC  SI             ;переходим к следующей ноте
            JMP  NEXT_NOTE      ;
;---завершение
NO_MORE:    IN   AL,PORT_B      ;получаем статус порта B
            AND  AL,0FCH        ;выключаем динамик
            OUT  61H,AL         ;заменяем байт