На главную Наши проекты:
Журнал   ·   Discuz!ML   ·   Wiki   ·   DRKB   ·   Помощь проекту
ПРАВИЛА FAQ Помощь Участники Календарь Избранное RSS
msm.ru
! Перед отправкой сообщения внимательно прочтите правила раздела!!!
1. Все статьи должны быть оформлены согласно Правил оформления статей.
2. Любые обсуждения должны происходить в специальной теме, обсуждение в любых других темах раздела запрещены.
3. Запрещается писать статьи о создании и распространении вирусов, троянов и других вредоносных программ!
4. За грамотно написанные и правильно оформленные статьи авторы награждаются DigiMoney.

Дополнительные ссылки:
Желаю творческих успехов! ;)
Модераторы: Jin X
  
    > Воспроизведение звука из PC Speaker'а , DOS, Общие вопросы
       
      Воспроизведение звука из системного динамика (PC Speaker'а)

      Для того чтобы заставить встроенный системный динамик издать какой-либо сигнал, необходимо задействовать две микросхемы:
      • Программируемый таймер i8253/i8254.
      • Программируемый периферийный интерфейс (ППИ) i8255.
      Системный таймер представляет собой генератор импульсов, работающий с частотой 1193181 Гц (~ 1.19 МГц). Он подаёт импульсы на три канала: канал 0 (генерирующий прерывание 8, т.е. IRQ0), канал 1 (предназначенный для регенерации памяти) и канал 2 (связанный с системным динамиком). Принцип работы всех каналов одинаковый. Каждый из них имеет несколько регистров: регистр управляющего слова (CWR, 8 бит), регистр состояния счётчика, регистр-счётчик (Counting Element (CE), 16 бит), регистр, хранящий значение коэффициента пересчёта (CR, 16 бит) и регистр-защёлку (OL, 16 бит), в который периодически передаётся значение регистра-счётчика. При каждом поступлении импульса от таймера происходит декремент (уменьшение на единицу) значения регистра-счётчика. В режиме работы 2 и 3 (см. ниже) при достижении нулевого значения в регистр-счётчик загружается значение коэффициента пересчёта, которое может быть установлено при помощи записи в соответствующий порт ввода-вывода (40h для канала 0, 41h для канала 1 и 42h для канала 2) и весь процесс повторяется заново. Т.о, чем меньше коэффициент пересчёта, тем короче цикл счёта (и длится он CR/1193181 секунды, т.е. повторяется с частотой 1193181/CR Гц). Каждый канал имеет шесть режимов работы, но нам больше всего подходит режим 3 (генератор меандра), т.к. именно в этом режиме положительный и отрицательный выходной сигнал занимают по половине периода счёта1.
      На вход звукогенератора поступает логическое "И" двух сигналов: выхода второго канала таймера и содержимого бита 1 порта PB ППИ (порт 61h). Счёт же второго канала разрешается только в том случае, если установлен бит 0 порта PB ППИ. Т.о, всё, что нам необходимо сделать - это инициализировать таймер и установить биты 0 и 1 порта PB ППИ. Для инициализации таймера нужно записать некоторое значение в регистр управляющего слова (доступ к которому осуществляется посредством записи в порт 43h), а затем поочерёдно младший и старший байты коэффициента пересчёта, т.е. значения (1193181/частота) в порт ввода-вывода канала 2 (порт 42h). Регистр управляющего слова имеет следующую структуру:
      • бит 0 определяет тип константы, загружаемой в CR: 0 - двоичное число, 1 - двоично-десятичное (BCD), обычно = 0;
      • биты 1-3 определяют режим работы канала: для режима 3 = 011b;
      • биты 4-5 определяют тип операции: 00b - "защёлкивание" - зафиксировать значение регистра-защёлки для (и до) последующего чтения через порт 40h, 41h или 42h (сначала читается младший байт, затем старший), 01b - запись младшего байта регистра пересчёта, 10b - запись старшего байта регистра пересчёта, 11b - запись сначала младшего, затем старшего байта регистра пересчёта;
      • биты 6-7 определяют номер канала: для канала 2 = 10h.
      Т.е. в нашем случае необходимо записать значение 10110110b (0B6h).

      Для отключения звука достаточно сбросить биты 0 и 1 порта PB ППИ.

      А вот, собственно, и сам код (SOUND.INC):
      ExpandedWrap disabled
        ; Подключка для генерации звука
         
        ; Процедура Sound: включить звук
        ; Вход: AX = частота звука (Гц)
        Sound       proc
                mov dx,12h
                cmp ax,dx          ; Частота <= 18 Гц ?
                jbe @@Exit         ; Да, на выход, чтобы избежать переполнения
                xchg    cx,ax          ; Сохраняем частоту в СX
                mov al,10110110b   ; Упр.сл.таймера: канал 2, режим 3, дв.слово
                out 43h,al         ; Выводим в регистр режима
                mov ax,34DDh       ; DX:AX = 1193181
                div cx             ; AX = (DX:AX) / СX
                out 42h,al         ; Записываем младший байт счетчика
                mov al,ah
                out 42h,al         ; Записываем старший байт счетчика
                in  al,61h         ; Порт PB
                or  al,11b         ; Устанавливаем биты 0-1
                out 61h,al         ; Записываем обратно в PB
        @@Exit:     ret                    ; Выходим из процедуры
        Sound       endp
         
        ; Процедура NoSound: отключить звук
        NoSound     proc
                in  al,61h         ; Порт PB
                and al,not 11b     ; Сбрасываем биты 0-1
                out 61h,al         ; Записываем обратно в PB
                ret                    ; Выходим из процедуры
        NoSound     endp
      1 Кстати, при начальной загрузке BIOS инициализирует канал 0 для работы в режиме 3, а в регистр коэффициента пересчёта записывается 0, т.е. 65536 декрементов на цикл счёта. При этом IRQ0 (int 08h) генерируется каждый раз при достижении регистром-счётчиком нуля, т.е. ~ 18.2 раза в секунду.

      Теперь я хочу рассказать немного о нотах. Как известно, в природе существует 12 нот (7 основных и 5 с производными названиями), также известно, что изменение высоты на октаву - это изменение частоты в два раза, а частота ноты "ля" первой октавы = 440 Гц. Отсюда получаем, что изменение высоты на полутон - это изменение в корень 12-й степени из 2 раз (~ 1.059463). Вычисляем частоту ноты "до" самой низкой октавы фортепиано (субконтроктавы) и получаем ~ 16.351598 Гц. Таким образом, вычисление частоты ноты сводится к простейшей процедуре:
      • Умножаем значение 16.351598 на 2 столько раз, на сколько нужная нам октава выше субконтроктавы.
      • Умножаем получившееся значение на 1.059463 столько раз, на сколько полутонов нужная нам нота выше ноты "до".
      P.S. Кстати, Вы не забыли, что от перестановки мест множителей произведение не меняется? :)
      Для вычислений можно, конечно, привлечь математический сопроцессор, но я нашёл более, как мне кажется, короткий и быстрый путь (NOTEFREQ.INC):
      ExpandedWrap disabled
        ; Подключка для получения частоты ноты
         
        ; Процедура GetNoteFreq: получение частоты ноты
        ; Вход:   AH = номер октавы, AL = номер ноты
        ; Выход:  AX = частота (Гц)
        ; Октавы: 0-субконтроктава, 1-контроктава, 2-большая, 3-малая, 4-первая, 5-вторая... 9-шестая
        ; Ноты:   0-до, 1-до#, 2-ре, 3-ре#, 4-ми, 5-фа, 6-фа#, 7-соль, 8-соль#, 9-ля, 10-ля#, 11-си
        GetNoteFreq     proc
                xchg    cx,ax
                cmp cl,11
                jbe @@OkNote
                mov cl,11
        @@OkNote:   mov ax,7
                mov dx,0B78Ah+23
                mov bx,61858
        @@1:        xchg    dx,ax
                div bx
                push    ax
                div bx
                xchg    ax,dx
                pop ax
                dec cl
                jns @@1
         
                cmp ch,9
                jbe @@2
                mov ch,9
        @@2:        shl dx,1
                rcl ax,1
                dec ch
                jns @@2
                shl dx,1
                adc ax,0
                ret
        GetNoteFreq     endp
      0 пользователей читают эту тему (0 гостей и 0 скрытых пользователей)
      0 пользователей:


      Рейтинг@Mail.ru
      [ Script execution time: 0,0337 ]   [ 15 queries used ]   [ Generated: 25.04.24, 02:28 GMT ]