Теперь я алгоритм ШИМ-модуляции улучшил, задействовав интервальный таймер.Stan wrote:Здесь мы WAV-файл, представленный отсчетами через нормальный ADC, пытаемся воспроизвести через РС-спикер, используя методы ШИМ-модуляции.
При этом отмечаем, что алгоритм:является довольно грубым, хотя и дает приемлемый на слух результат, но может быть улучшен.Code: Select all
— если разность больше нуля, подаем на встроенный динамик логическую единицу, иначе — логический нуль (если же данный байт содержит именно число 128, не изменяем логический уровень на входе динамика вовсе); — отрабатываем цикл задержки до обработки следующего байта (длительность задержки нужно подобрать в зависимости от используемого языка программирования и тактовой частоты процессора);
Основная идея: амплитуде сэмпла из wav-файла ставим в соответствие длительность
импульса из таблицы.
Code: Select all
'=======================================================
' ДЕМОНСТРАЦИЯ ВОСПРОИЗВЕДЕНИЯ WAV-ФАЙЛОВ МЕТОДОМ ШИМ
'=======================================================
'
'&H61 - порт В микросхемы 8255
'&H42 - порт канала 2 таймера (звуковой сигнал)
'&H43 - порт команд установки таймера
DEFINT A-Z
DIM Mas%(0 TO 32766) '--------- Буферный массив
'--- Таблица длительностей импульсов для канала 2 интервального таймера
DIM SAMP(0 TO 255) AS INTEGER
'--- заполняем таблицу длительностей импульсов
FOR i = 0 TO 255 '--- length of DATA to read
READ R '--- read
SAMP(i) = R '--- put into array
NEXT i '--- until 255
CONST Slp% = 530 '--- Задержка между отсчетами звука (подбирается)
CLS
'--- Ввод имени wav-файла (8 SYMB +"."3)
' INPUT "Введите имя wav-файла"; N$
N$ = "C:\QBASIC\HELLOW.WAV" '--- файл должен быть 8 бит 8000...11025... Гц - взять в этой ветке
'--- Открытие файла в двоичном режиме
OPEN N$ FOR BINARY AS #1 ' fl& > 32766
fl& = LOF(1) '----- Определение длины файла
IF fl& < 2 THEN CLOSE #1: KILL N$: PRINT "File not found": END
IF fl& > 32766 THEN fl& = 32766
FOR i& = 0 TO fl& '--- Чтение wav-файла в Mas%(i&)
GET #1, i& + 1, Mas%(i&)'--- Buf
NEXT i&
PRINT
PRINT " Нажмите любую клавишу для воспроизведения звука "
PRINT
'--- Open Gate Timer_2, Speaker="0"
R = INP(&H61) AND &HFE
OUT &H61, R
'---Ожидание нажатия клавиши
DO: LOOP WHILE INKEY$ = ""
'---- Set up the internal speaker & Timer_2 ---
OUT &H43, &HB0 '--- Set Timer_2 "прерыв.терм.счета, Low, High bytes"
OUT &H42, &H01 '--- Stop
OUT &H42, &H00 '--- Timer_2
OUT &H43, &H90 '--- Set Timer_2 "прерыв.терм.счета, only Low byte"
R = INP(&H61) OR 3 '--- Open Gate Timer_2, Speaker="1"
OUT &H61, R
FOR i& = 44 TO fl& '--- Перебор байтов (с 44-го)
Bt% = Mas%(i&) '--- Получить целое (2 байта)
DEF SEG = VARSEG(Bt%)
bLo% = PEEK(VARPTR(Bt%)) '--- Get Low byte
bHi% = PEEK(VARPTR(Bt%) + 1) '--- Get High byte
DEF SEG
'----- Определение интервала для таймера -------------
Bt% = SAMP(bLo%)
'----- Send Value to the Timer_2 Port
OUT &H42, Bt%
FOR j% = 0 TO Slp% '---Задержка
NEXT j%
'----- Определение интервала для таймера -------------
Bt% = SAMP(bHi%)
'----- Send Value to the Timer_2 Port
OUT &H42, Bt%
FOR j% = 0 TO Slp% '---Задержка
NEXT j%
NEXT i&
'---- Reset the Speaker & Timer_2 ---
OUT &H43, &HB6
R = INP(&H61) AND &HFC
OUT &H61, R
END
'--- Timer Table for WAVE Samples ---------------------------------
'DATA 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2
'DATA 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4
'DATA 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 5
'DATA 5, 5, 5, 5, 5, 5, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6
'DATA 6, 6, 6, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 8, 8
'DATA 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 9, 9, 9, 9, 9
'DATA 9, 9, 10, 10, 10, 10, 11, 11, 12, 12, 13, 14, 14, 15, 16, 17
'DATA 17, 18, 19, 20, 21, 22, 23, 24, 26, 27, 28, 29, 30, 31, 33, 34
'DATA 35, 36, 38, 39, 40, 41, 43, 44, 45, 46, 48, 49, 50, 51, 52, 53
'DATA 54, 55, 57, 58, 58, 59, 60, 61, 62, 63, 64, 64, 65, 66, 66, 67
'DATA 67, 67, 67, 67, 67, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68
'DATA 68, 68, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 69, 70, 70, 70
'DATA 70, 70, 70, 70, 70, 70, 70, 70, 70, 70, 71, 71, 71, 71, 71, 71
'DATA 71, 71, 71, 71, 71, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72, 72
'DATA 72, 72, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 73, 74, 74, 74
'DATA 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 75, 75, 75, 75, 75, 75
DATA 2, 2, 2, 2, 2, 2, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
DATA 4, 4, 4, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 8, 8,
DATA 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 10, 10, 10, 10, 10,
DATA 10, 10, 10, 10, 10, 10, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12,
DATA 12, 12, 12, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 16, 16,
DATA 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 18, 18, 18, 18, 18,
DATA 18, 18, 20, 20, 20, 20, 22, 22, 24, 24, 26, 28, 28, 30, 32, 34,
DATA 34, 36, 38, 40, 42, 44, 46, 48, 52, 54, 56, 58, 60, 62, 66, 68,
DATA 70, 72, 76, 78, 80, 82, 86, 88, 90, 92, 96, 98,100,102,104,106,
DATA 108,110,114,116,116,118,120,122,124,126,128,128,130,132,132,134,
DATA 134,134,134,134,134,136,136,136,136,136,136,136,136,136,136,136,
DATA 136,136,138,138,138,138,138,138,138,138,138,138,138,140,140,140,
DATA 140,140,140,140,140,140,140,140,140,140,142,142,142,142,142,142,
DATA 142,142,142,142,142,144,144,144,144,144,144,144,144,144,144,144,
DATA 144,144,146,146,146,146,146,146,146,146,146,146,146,148,148,148,
DATA 148,148,148,148,148,148,148,148,148,148,150,150,150,150,150,150
'------------------------------------------------------------------
Под Windows (ecли она допускает прямое обращение к портам) звук хуже, появляются щелчки.
Таблиц длительностей импульсов две: первая - закомментированная, во второй - удвоенные значения длительностей из первой, что делает звук громче.
Можно попробовать утроить длительности, но при этом надо помнить про ограничения, связанные с частотой дискретизации:
Частота на входе системного таймера: F=1193182 Hz; при этом период T=0,838e-6S = 0,838uS; ~ 1uS
При частоте дискретизации звука Fq = 8000 Hz; интервал между отсчетами Tq=1/Fq = 1/8000 = 0,000125S = 125uS
Аналогично:
Fq = 11025 Hz; Tq=1/Fq = 1/11025 = 0,0000907S = 90,7uS
Fq = 22050 Hz; Tq=1/Fq = 1/22050 = 0,00004535S = 45,35uS
То есть, для частоты дискретизации звука Fq = 8000 Hz максимальная длительность импульса 150 периодов таймера соответствуют ~124uS, что практически на пределе периода 125uS.
Поэтому для частоты дискретизации Fq = 11025 Hz эти длительности уже не подойдут.
Интервал между сэмплами здесь подбирается эмпирически:
CONST Slp% = 530 '--- Задержка между отсчетами звука (подбирается)
как выяснилось ранее, программный подбор этой задержки всё равно требует эмпирических коэффициентов.
Лучшего результата можно достичь, переписав прерывание INT_8 канала 0 таймера и использовав его для отсчета интервалов между семплами.
Но Windows в ряде случаев "недоволен" таким решением, поэтому задержка реализована циклом.
Lavr: Давайте "битвы оффтопов" прекратим!