Сразу скажу, что программировать SoundBlaster непросто. Но не всё так плохо, если не касаться программирования ЧМ-синтезатора. Поэтому в данном разделе приведено руководство только по программированию цифрового канала SoundBlaster.
Для начала некоторые сведения. Сердцем SoundBlaster (далее SB) является процессор для обработки цифровых сигналов (DSP - Digital Signal Processor). Этот чип позволяет воспроизводить и записывать звуки, и содержит в себе два основных блока - аналогово-цифровой преобразователь (АЦП) и цифро-аналоговый преобразователь (ЦАП). Первый блок позволяет преобразовывать аналоговый сигнал (например, с микрофона) в цифровой сигнал. Второй блок преобразует цифровой сигнал в аналоговый, благодаря чему можно что-то услышать из колонок. Как и любое другое периферийное устройство, SB имеет IRQ (InteRrupt Request - сигнал запроса прерывания), канал прямого доступа к памяти (DMA - Direct Memory Access) и базовый порт ввода-вывода. IRQ нужен для того, чтобы программа могла реагировать на ответ SB по окончании операции воспроизведения или записи. DMA нужен для быстрой передачи данных между SB и памятью, не загружая при этом процессор. Базовый порт ввода-вывода нужен для того, чтобы программа могла посылать в SB команды (например, команду воспроизведения) и читать состояние SB (типа готов он, например, к записи в него команды или нет).
SB имеет несколько регистров для работы с цифровым каналом:
Доступ к этим регистрам осуществляется посредством чтения или записи в соответствующий порт. BASE означает адрес базового порта ввода-вывода, а выражение BASE + 0xYY означает конкретный порт для данного регистра, относительно базового порта.
Для того, чтобы начать работу с SB нужно сбросить DSP (reset DSP) для этого нужно проделать следующие операции:
Пример, демонстрирующий вышесказанное приведён ниже:
unsigned int RESET_DSP(unsigned port_) { outportb(port_ + 6, 1); delay(10); outportb(port_ + 6, 0); delay(10); if (((inportb(port_ + 0x0E) & 0x80) == 0x80) & (inportb(port_ + 0x0A) == 0xAA)) return port_; return 0; }
В данном примере параметр port_ задаёт базовый порт ввода-вывода.
Для записи команды в DSP можно применить следующий алгоритм:
Следующий пример иллюстрирует эти действия:
void WRITE_DSP(char value) { while (inportb(BASE + 0x0C) & 0x80) {} outportb(BASE + 0x0C, value); }
В данном случае глобальная переменная BASE задаёт базовый адрес порта ввода-вывода, а параметр value определяет нужную команду, для записи в DSP.
И, наконец, SB должен знать, чего от него хотят. Для воспроизведения или записи через SB используются такие действия:
Вот как всё это делается:
void PlayRecord(unsigned size, int mode, unsigned samplerate) { WRITE_DSP(0x40); WRITE_DSP(256 - 1000000 / samplerate); WRITE_DSP(mode); WRITE_DSP(size & 0xFF); WRITE_DSP(size >> 8); }
В этой процедуре параметр size задаёт размер данных для чтения/записи, mode - команда из таблицы 1, samplerate - требуемая частота в герцах.
Этих трёх процедур вполне достаточно для воспроизведения и даже записи звука через SB. Но теперь придётся программировать контроллер DMA, иначе будет невозможно передавать данные в SB или в память из SB. Однако для программирования DMA можно обойтись всего лишь одной процедурой и следующими действиями:
Маска канала нужна для того, чтобы в процессе записи всех этих значений в порты DMA не использовался.
Ниже представлена процедура для выполнения всех этих действий:
void SetupDMA(unsigned channel, void far *buffer, unsigned data_size, int mode) { unsigned page, offs; data_size--; offs = (FP_SEG(buffer) << 4) + FP_OFF(buffer); page = (FP_SEG(buffer) + (FP_OFF(buffer) >> 4)) >> 12; outportb(0x0A, channel | 4); outportb(0x0C, 0); if (mode == 1) outportb(0x0B, 0x45); else outportb(0x0B, 0x49); outportb(0x02, offs & 255); outportb(0x02, offs >> 8); outportb(0x83, page); outportb(0x03, data_size & 255); outportb(0x03, data_size >> 8); outportb(0x0A, channel); }
В этой процедуре channel - номер канала DMA, buffer - указатель на буфер данных, data_size - размер буфера данных и mode - параметр, задающий режим работы (mode = 0 - воспроизведение, mode = 1 - запись).
Примечание. Контроллер DMA должен программироваться до программирования SB.
Таблица 1 - команды воспроизведения / записи SB.
Описание команды SB | Код команды | Частотный диапазон |
Воспроизведение звука
через DMA. 8 бит, моно. Запись звука через DMA. 8 бит, моно. |
0x14 0x24 |
4Кгц - 44Кгц 4Кгц - 23Кгц |
Если удастся найти другие команды (например, для воспроизведения стерео, 16 бит), то таблица будет дополнена.
Примечание. Перед воспроизведением звука необходимо записать в DSP команду включения динамика, иначе ничего не будет слышно.
Таблица 2 - прочие команды SB.
Описание команды SB | Код команды |
Включение динамика. Выключение динамика. Приостановка (пауза) воспроизведения. Продолжение воспроизведения (снятие паузы). Установка константы частоты. |
0xD1 0xD3 0xD0 0xD4 0x40 |
Для автоопределения (autoinitialization) базового порта ввода-вывода SB можно просматривать все порты ввода-вывода с 0x220 по 0x280 и с помощью процедуры RESET_DSP определить, есть ли на конкретном порте SB или нет.
Copyright © 1999 by HackMaster