Главная страница «Первого сентября»Главная страница журнала «Информатика»Содержание №2/2009


В мир информатики
Школа программирования

Использование звуковых файлов формата wav в программах на Бейсике

Окончание. Начало см. “В мир информатики”
№ 119 (“Информатика” № 1/2009)

Д.Ю. Усенков,
Москва

В первой части статьи были рассмотрены теоретические вопросы, связанные с генерацией звука в программах на языке Бейсик путем прямого доступа к динамику компьютера. Теперь мы можем написать собственные подпрограммы “побитовой” работы со звуком. Их листинги (вариант языка — QBasic) приведены ниже.

DECLARE SUB SetPeriod (i%)

DECLARE SUB OscillOFF ()

DECLARE SUB OscillON ()

DECLARE SUB SpeakOFF ()

DECLARE SUB SpeakON ()

'&H61 - порт В микросхемы 8255

'&H42 - порт канала 2 таймера (звуковой сигнал)

'&H43 - порт команд установки таймера

'&HB6 - команда установки частоты канала 2

'========================

' ОСНОВНАЯ ПРОГРАММА

'========================

' ПОДПРОГРАММЫ

'----------------

SUB OscillOFF 'Отключить таймер от динамика

OUT &H61, (INP(&H61) AND &HFC)

END SUB

 

SUB OscillON 'Подключить таймер к динамику

OUT &H61, (INP(&H61) OR &H3)

END SUB

 

'Установить период отсчета таймера

SUB SetPeriod (i%) OUT &H43, &HB6

'Загрузить младший байт частоты

OUT &H42, (i% AND &HFF) OUT &H42, (i% \ 256) 'Загрузить старший байт частоты

END SUB

 

SUB SpeakOFF 'Динамик <- 0

OUT &H61, (INP(&H61) AND &HFD)

END SUB

 

SUB SpeakON 'Динамик B <- 1

OUT &H61, (INP(&H61) OR &H2)

END SUB

Примечание. В отличие от предыдущего примера константы в командах AND и OR приведены не в двоичном, а в более коротком шестнадцатеричном формате.

Первые три подпрограммы служат для генерации звука с помощью микросхемы таймера (задали частоту — подключили таймер — подождали нужное время — отключили таймер). Две последние же осуществляют требуемую нам запись в бит управления динамиком логического нуля и единицы соответственно (прежде чем их использовать, нужно отключить таймер, вызвав подпрограмму OscillOFF). Приведем пару несложных примеров, иллюстрирующих работу с подпрограммами (следующие программные строки нужно вставить в предыдущий листинг вместо комментария “ОСНОВНАЯ ПРОГРАММА”):

CLS

PRINT "Проба звука - доступ через таймер"

'Константа периода равна 1190000/частота, Гц

SetPeriod (3000) OscillON

FOR j0% = 0 TO 10: FOR j1% = 0 TO 30000

NEXT j1%: NEXT j0%

OscillOFF

'Ожидание нажатия любой клавиши

DO

LOOP UNTIL INKEY$ <> ""

PRINT "Проба звука - побитный доступ"

OscillOFF

FOR j0% = 0 TO 100

SpeakON

FOR j1% = 0 TO 30000: NEXT j1%

SpeakOFF

FOR j1% = 0 TO 30000: NEXT j1%

NEXT j0%

PRINT "Все сделано!"

END

А теперь нам не составит особого труда написать программу, воспроизводящую на встроенном динамике звук, мелодию или речь, записанные в wav-файлах. Поскольку чтение из файла — операция довольно медленная, будем сначала загружать всю закодированную звуковую последовательность в буферный массив в оперативную память, а затем расшифровывать содержимое его ячеек и генерировать звук. (В приведенном ниже листинге мы будем использовать прямое обращение к порту 61Н с помощью команд INP и OUT, а не написанные ранее подпрограммы, чтобы не терять на их вызове лишние миллисекунды.)

'ДЕМОНСТРАЦИЯ ВОСПРОИЗВЕДЕНИЯ WAV-ФАЙЛОВ

DIM Buf AS STRING * 1 'Буфер длиной 1 байт

DIM Mas%(30000) 'Буферный массив

CONST Slp% = 300 'Задержка между отсчетами

'звука (подбирается)

CLS

'Отключение таймера от динамика (OscillOFF)

OUT &H61, (INP(&H61) AND &HFC)

'Ввод имени wav-файла без расширения 9

INPUT "Введите имя wav-файла"; N$

'Открытие файла в двоичном режиме

OPEN N$ + ".WAV" FOR BINARY AS #1

fl& = LOF(1) 'Определение длины файла

IF fl& > 30000 THEN

PRINT "Файл слишком велик"

ELSE

FOR i& = 1 TO fl& 'Чтение wav-файла

GET #1, i&, Buf

Mas%(i&) = ASC(Buf)

NEXT i&

PRINT "Нажмите любую клавишу

для воспроизведения звука"

'Ожидание нажатия клавиши

DO: LOOP WHILE INKEY$ = ""

FOR i& = 44 TO fl& 'Перебор байтов (с 44-го)

Bt% = Mas%(i&) 'Получить байт как число

'Определение значений громкости

SELECT CASE Bt%

CASE 128 'Байт 128 - нулевой уровень

'Задержка без переключения динамика)

FOR j% = 0 TO Slp%: NEXT j%

CASE IS < 128 'Байт больше 128

'Подать на динамик 1 (SpeakON)

OUT &H61, (INP(&H61) OR &H2)

FOR j% = 0 TO Slp%:

NEXT j% 'Задержка

CASE IS > 128 'Байт меньше 128

'Подать на динамик 0 (SpeakOFF)

OUT &H61, (INP(&H61) AND &HFD)

FOR j% = 0 TO Slp%: NEXT j%

'Задержка

END SELECT

NEXT i&

END IF

CLOSE #1 'Закрыть wav-файл

END

К сожалению, в отличие от “системных” языков программирования типа Си в Бейсике не допускается использование числовых переменных длиной 1 байт и числовых массивов с такими элементами. Поэтому при написании программы пришлось выбирать один из трех “обходных путей”: использование обычного целочисленного массива с двухбайтными ячейками (с двойным перерасходом занимаемого объема памяти), формирование массива символьных переменных длиной 1 байт каждая (с перекодировкой считанных из файла байтов в символы с помощью функции CHR$ и обратной перекодировкой с помощью функции ASC) или попарное объединение считываемых байтов в целые двухбайтные числа с последующим разделением при генерации звука. Желающие могут опробовать все три способа, автором же данной статьи выбран первый из них (с целью упрощения алгоритма). “Расплатой” за это решение стало ограничение допустимой длины воспроизводимого wav-файла: если она больше 30 000 (число десятичное), на экран выводится соответствующее сообщение.

Обратите внимание на присваиваемое переменной Slp% в начале листинга значение 300. Это количество прогонов “пустого” цикла FOR, используемое в качестве задержки между последовательными занесениями в бит динамика 1 и 0. Это число подбирается пользователем по результатам экспериментов в зависимости от имеющегося типа компьютера (точнее, от тактовой частоты процессора). И, наконец, вы можете произвести более тонкую “подгонку” константы задержки для конкретного wav-файла, добиваясь наилучших результатов звучания.

Литература

1. Д.Ю. Усенков. Звук в программах на Бейсике. / “В мир информатики” № 102–103 (“Информатика” № 2–3/2008).


9 Файл должен находиться в том же каталоге (папке), что и программа qbasic.exe.

Дм. Юр. Усенков

TopList