Наши проекты:
Журнал · Discuz!ML · Wiki · DRKB · Помощь проекту |
||
ПРАВИЛА | FAQ | Помощь | Поиск | Участники | Календарь | Избранное | RSS |
[18.97.14.84] |
|
Сообщ.
#1
,
|
|
|
Да именно «писка», я не ошибся и опишу как сохранить в звуковом файле формата WAV (стандарт OS Windows) монотонный моно звук с прямоугольным импульсом, частотой n и длительностью звучания в микросекундах L.
Для начала уясним, что собой представляют звуковые данные. Процесс цифровой записи звука можно абстрактно представить таким образом: под действием движения воздуха, в цепи микрофона возникают изменения силы тока, а компьютер с определенной частотой, именуемой частотой дискретизации, производит запись значения силы тока в относительных единицах от (в нашем случае от 0 до 255). Воспроизведение звука происходит в обратном порядке – с частотой дискретизации на звуковоспроизводящее устройство подается напряжение, сила которого зависит от значения, записанного в звуковом файле. Немного формальностей. WAV файла состоит из двух частей заголовка и области данных. Заголовок в нашем простейшем случае будет состоять из трех последовательно вложенных друг в друга заголовков (chunks). Первый — свойственный для файлов формата RIFF, представляет собой запись из трех полей: символов «RIFF», размера области данных в байтах (без учета размера самого заголовка), и второго «чунка». tSoundFile=record Caption:array[0..3]of char; Size:longint; Data:tSoundFormat; end; Второй «чунк», элемент WAV-файлов, состоит из четырех частей: • слова «WAVE», • слова «fmt »(три буквы и пробел в конце), • 32 битной переменной, хранящей размер следующего поля, • переменной типа tpcmWaveFormat, описанной в файле MMSystem.pas • третього „чунка”. tSoundFormat=record Caption1,Caption2:array[0..3]of char; Size:longint; Format:tpcmWaveFormat; Data:tSoundData; end; тип tpcmWaveFormat представляет собой record из двух записей wf: TwaveFormat и wBitsPerSample: Word. Вторая указывает, сколько бит занимает одна выборка сигнала (в нашем случае 8). Первая – тип record, описывающий формат звуковых данных: TWaveFormat = record wFormatTag: Word; nChannels: Word; nSamplesPerSec: Longint; nAvgBytesPerSec: longint; nBlockAlign: Word; end; wFormatTag – тип звуковых данных должен быть равен константе WAVE_FORMAT_PCM nChannels – количество каналов, для моно звука – 1. NsamplesPerSec — частота дискретизации. NavgBytesPerSec — количество байт передаваемое на воспроизведение в секунду. NblockAlign – размер одной выборки в байтах. Третий «чунк» тоже имеет тип record и содержит слово “data”, размер области данных и массив данных. tSoundData=record Caption:array [0..3]of char; Size:longint; Data:array[0..0]of byte; end; В написании кода будем использовать следующие константы: FDiscretisation=44100; – частота дискретизации (рекомендуется брать только стандартеые значения: 8 000 Гц, 11 025 Гц, 12 000 Гц, 16 000 Гц, 22 050 Гц, 24 000 Гц, 32 000 Гц, 44 100 Гц, 48 000 Гц) Canals=1;– количесво каналов (у нас монозвук) Discret=8; – количество бит, занимаемых одной выборкой сигнала для одного канала Переменные: SoundFile:pSoundFile; – создадим наш файл сперва в динамической памяти. SizeOfData:longint; – размер массива данных SizeOfFile: longint; – размер файла. I: longint; – переменная-счетчик для цикла. По-е-ха-ли. Размер области данных у нас будет: SizeOfData:=Duration*fDiscretisation*(Discret shr 3) div 1000; Размер файла – размер заголовка + длина массива данных-1 выборка. SizeOfFile:=sizeOf(tSoundFile)+ SizeOfData-Discret shr 3; Выделяем память под файл: GetMem(SoundFile,SizeOfFile); Заполняем заголовок SoundFile^.Size:=SizeOfFile-SizeOf(SoundFile^.Caption)-SizeOf(SoundFile^.Size); SoundFile^.Data.Size:=SizeOf(tpcmWaveFormat); SoundFile^.Data.Data.Size:=SizeOfData; SoundFile^.Caption:='RIFF'; SoundFile^.Data.Caption1:='WAVE'; SoundFile^.Data.Caption2:='fmt '; SoundFile^.Data.Format.wBitsPerSample:=Discret; SoundFile^.Data.Format.wf.wFormatTag:=WAVE_FORMAT_PCM; SoundFile^.Data.Format.wf.nChannels:=Canals; SoundFile^.Data.Format.wf.nSamplesPerSec:=FDiscretisation; SoundFile^.Data.Format.wf.nAvgBytesPerSec:=FDiscretisation*Canals*(Discret shr 3); SoundFile^.Data.Format.wf.nBlockAlign:=(Canals*Discret)shr 3; SoundFile^.Data.Data.Caption:='data'; Итак, с заголовком закончили и переходим к заполнению сегмента данных. Волна нашего звука на осциллографе имела бы прямоугольный вид, другими словами, один промежуток времени длительностью 1/n на звуковоспроизводящее устройство будет подаваться максимальный сигнал, а следующий равный по длительности промежуток нулевое напряжение. Итак, заполнение сегмента данных будим производить следующим образом: если конкретная выборка была бы сделана в четный промежуток времени 1/n, то оно равно 255, иначе оно равно 0: for i:=0 to SoundFile^.Data.Data.Size-1 do if Round(Frequenz*i/fDiscretisation) mod 2=0 then SoundFile^.Data.Data.Data[i]:=255 else SoundFile^.Data.Data.Data[i]:=0; Осталось лишь самым банальным образом переписать наш звуковой файл из динамической памяти в файл и освободить динамическую память. Assign(F,FileName); ReWrite(F,1); BlockWrite(F,SoundFile^,SizeOfData); Close(F); FreeMem(SoundFile); Полный листинг выглядит так: program BeepGenerator; {$R-}{$A-} const WAVE_FORMAT_PCM = 1; type TWaveFormat = record wFormatTag: Word; nChannels: Word; nSamplesPerSec: Longint; nAvgBytesPerSec: longint; nBlockAlign: Word; end; TPCMWaveFormat=record wf: TWaveFormat; wBitsPerSample: Word; end; tSoundData=record Caption:array [0..3]of char; Size:longint; Data:array[0..0]of byte; end; tSoundFormat=record Caption1,Caption2:array[0..3]of char; Size:longint; Format:tpcmWaveFormat; Data:tSoundData; end; pSoundFile=^tSoundFile; tSoundFile=record Caption:array[0..3]of char; Size:longint; Data:tSoundFormat; end; const FDiscretisation=44100; Canals=1; Discret=8; procedure SaveBeep(FileName:string;Frequenz,Duration:integer); var F:File; SoundFile:pSoundFile; SizeOfData:longint; I: longint; SizeOfFile:longint; begin SizeOfData:=Duration*fDiscretisation*(Discret shr 3) div 1000; SizeOfFile:=sizeOf(tSoundFile)+ SizeOfData-Discret shr 3; GetMem(SoundFile,SizeOfFile); SoundFile^.Size:=SizeOfFile-SizeOf(SoundFile^.Caption)-SizeOf(SoundFile^.Size); SoundFile^.Data.Size:=SizeOf(tpcmWaveFormat); SoundFile^.Data.Data.Size:=SizeOfData; SoundFile^.Caption:='RIFF'; SoundFile^.Data.Caption1:='WAVE'; SoundFile^.Data.Caption2:='fmt '; SoundFile^.Data.Format.wBitsPerSample:=Discret; SoundFile^.Data.Format.wf.wFormatTag:=WAVE_FORMAT_PCM; SoundFile^.Data.Format.wf.nChannels:=Canals; SoundFile^.Data.Format.wf.nSamplesPerSec:=FDiscretisation; SoundFile^.Data.Format.wf.nAvgBytesPerSec:=FDiscretisation*Canals*(Discret shr 3); SoundFile^.Data.Format.wf.nBlockAlign:=(Canals*Discret)shr 3; SoundFile^.Data.Data.Caption:='data'; Assign(F,FileName); ReWrite(F,1); for I:=0 to SoundFile^.Data.Data.Size-1 do if Round(Frequenz*i/fDiscretisation) mod 2=0 then SoundFile^.Data.Data.Data[i]:=255 else SoundFile^.Data.Data.Data[i]:=0; BlockWrite(F,SoundFile^,SizeOfFile); FreeMem(SoundFile); Close(F); end; begin {Создадим файл Dash.wav, частотой 900 Гц и длительностью 0,75 с} SaveBeep('Dash.wav',900,750); end. |
Сообщ.
#2
,
|
|
|
Возможно, лучше бы область данных не размещать в динамическом массиве, а писать сразу в файл. Таким образом отпадет необходимость в пояснениие относительно динамических массивов и ограничение в размере файла для 16-битных систем.
|
Сообщ.
#3
,
|
|
|
Эту статью стоит оформить по таким правилам:
Возможно, со мной не согласятся, но мне именно так видится интересная статья. |
Сообщ.
#4
,
|
|
|
Romtek, хороший научный подход.
Особенно ценно указание источников пояснения логики действий. Соглашусь с тобой. |