Все функции чтения и записи звука я выделил в отдельный модуль. Он приведен после текста программы.
При нажатии Button1 создается звуковой файл в памяти (то есть в памяти создается заголовок,
затем идут данные - все точно так же, как в обычном wav-файле), сохраняется на диск и одновременно
начинает воспроизводиться. Для этого используется функция playsound.
Остановить воспроизведение можно кнопкой Button2.
При нажатии Button3 открывается файл ex.wav (если Вы уже нажимали Button1, то он существует).
Далее из файла считываются данные и для каждого канала находится средняя громкость.
Не уверен, что это самый правильный способ, но здесь за громкость
я взял просто среднее арифметическое.
Результаты выводятся в заголовок окна. Для каждого канала выводится значение
в процентах от максимально возможной громкости.
Теперь о самой структуре данных. Она очень проста. Если канал один, то данные записаны подряд:
первое значение,
второе значение,
третье значение
...
Если же в файле два канала, то они чередуются:
первое значение первого канала, первое значение второго канала,
второе значение первого канала, второе значение второго канала,
третье значение первого канала, третье значение второго канала,
...
Если файл восьми битный, то каждое значение занимает 1 байт, если шестнадцати битный - 2 байта.
Это соответствует типам shortint и smallint соответственно.
В этой программе данные записываются при помощи процедуры GetData.
SaveSound вызывает ее для каждого значения.
В качестве параметров передаются канал и номер. А возвращаемое значение передается
через нетипизированный параметр res. Такой подход позволяет избежать проблем с типами данных.
При чтении все данные копируются в память, а затем находится сумма всех значений для каждого канала.
При выводе громкости эти суммы делятся на максимально возможные суммы и умножаются на сто.
Скачать все необходимые для компиляции файлы проекта можно на program.dax.ru.
uses MMSystem, wavfile;
procedure TForm1.Button1Click(Sender: TObject);
const
fr = 11025; {Частота в герцах}
len = 1; {Длина звука в секундах}procedure GetData(ch: smallint; index: integer; var res);
var
v: smallint absolute res; // конечное значение
amp: single; // амплитудаbeginif ch = 0
then amp := sin(index * 2 * Pi / (fr * len))
else amp := cos(index * 2 * Pi / (fr * len));
v := round(amp * (random(60000) - 30000));
end;
var
M: TMemoryStream; // поток для хранения информации в памяти
F: TFileStream; // Поток для созранения файлаbegin
M := nil; F := nil;
try
M := TMemoryStream.Create;
randomize;
SaveSound(M {Куда записывать}, round(fr * len) {len секунд},
fr {частота}, 16 {16 бит}, 2 {2 каналла}, @GetData);
// Воспроизведение звука:ifnot playsound(M.Memory, 0, SND_MEMORY or SND_LOOP or SND_ASYNC)
then ShowMessage('Can not play the sound');
F := TFileStream.Create('ex.wav', fmCreate);
M.Position := 0;
F.CopyFrom(M, M.Size);
finally
M.Free; F.Free;
end;
end;
procedure TForm1.Button2Click(Sender: TObject);
begin
playsound(nil, 0, 0); // Остановка воспроизведенияend;
procedure TForm1.Button3Click(Sender: TObject);
var
SampleCount, SamplesPerSec: integer;
BitsPerSample, Channeles: smallint;
F: TFileStream;
Volume: array [0..1] of single;
ToPercent: single;
buf: pointer;
buf8: ^shortint;
buf16: ^smallint;
i, ch: integer;
begin
F := nil; buf := nil;
try
Volume[0] := 0; Volume[1] := 0;
F := TFileStream.Create('ex.wav', fmOpenRead);
ReadWaveHeader(F, SampleCount, SamplesPerSec,
BitsPerSample, Channeles);
// Чтение данных:
GetMem(buf, SampleCount * Channeles * BitsPerSample);
F.Read(buf^, SampleCount * Channeles * BitsPerSample);
if BitsPerSample = 8 thenbegin
buf8 := buf;
for i := 0 to SampleCount - 1 dofor ch := 0 to Channeles - 1 dobegin
Volume[ch] := Volume[ch] + abs(buf8^);
inc(buf8); // Переход к следующему элементуendendelsebegin
buf16 := buf;
for i := 0 to SampleCount - 1 dofor ch := 0 to Channeles - 1 dobegin
Volume[ch] := Volume[ch] + abs(buf16^);
inc(buf16); // Переход к следующему элементуend;
end;
// Вывод результатов:
ToPercent := (1 shl BitsPerSample) / 100 * SampleCount;
if Channeles = 1
then Form1.Caption := Format('volume: %2.2f%%',
[Volume[0] / ToPercent])
else Form1.Caption := Format('left: %2.2f%%, right: %2.2f%%',
[Volume[0] / ToPercent, Volume[1] / ToPercent]);
finally
F.Free;
FreeMem(buf);
end;
end;
--------------------------------------------------------------------------------
unit wavfile;
interfaceuses classes, sysutils;
type
TWaveHeader = record
idRiff: array [0..3] of char;
RiffLen: longint;
idWave: array [0..3] of char;
idFmt: array [0..3] of char;
InfoLen: longint;
WaveType: smallint;
Ch: smallint;
Freq: longint;
BytesPerSec: longint;
align: smallint;
Bits: smallint;
end;
TDataHeader = record
idData: array [0..3] of char;
DataLen: longint;
end;
TGetData = procedure(ch: smallint; index: integer; var res);
TSetData = procedure(ch: smallint; index: integer; data: smallint);
procedure CreateWaveHeader(SampleCount, SamplesPerSec: integer;
BitsPerSample, Channeles: smallint; var WaveHeader: TWaveHeader;
var DataHeader: TDataHeader);
procedure ReadWaveHeader(Stream: TStream;
var SampleCount, SamplesPerSec: integer;
var BitsPerSample, Channeles: smallint);
procedure SaveSound(Stream: TStream; SampleCount, SamplesPerSec: integer;
BitsPerSample, Channeles: smallint; GetData: TGetData);
implementationprocedure Creat
BitsPerSample, Channeles: smallint; var WaveHeader: TWaveHeader;
var DataHeader: TDataHeader);
var
len: integer;
beginif (SampleCount < 0) or (SamplesPerSec < 1) or
(not BitsPerSample in [8, 16]) or
(not Channeles in [1, 2])
thenraise Exception.Create('Wrong params');
len := SampleCount * BitsPerSample div 8 * Channeles;
with WaveHeader dobegin
idRiff := 'RIFF';
RiffLen := len + 38;
idWave := 'WAVE';
idFmt := 'fmt ';
InfoLen := 16;
WaveType := 1;
Ch := Channeles;
Freq := SamplesPerSec;
BytesPerSec := SamplesPerSec * BitsPerSample div 8 * Channeles;
align := Channeles * BitsPerSample div 8;
Bits := BitsPerSample;
end;
with DataHeader dobegin
idData := 'data';
DataLen := len;
end;
end;
procedure ReadWaveHeader(Stream: TStream;
var SampleCount, SamplesPerSec: integer;
var BitsPerSample, Channeles: smallint);
var
WaveHeader: TWaveHeader;
DataHeader: TDataHeader;
begin
Stream.Read(WaveHeader, sizeof(TWaveHeader));
with WaveHeader dobeginif idRiff < > 'RIFF' thenraise EReadError.Create('Wrong idRIFF');
if idWave < > 'WAVE' thenraise EReadError.Create('Wrong idWAVE');
if idFmt < > 'fmt ' thenraise EReadError.Create('Wrong idFmt');
if WaveType < > 1 thenraise EReadError.Create('Unknown format');
Channeles := Ch;
SamplesPerSec := Freq;
BitsPerSample := Bits;
Stream.Seek(InfoLen - 16, soFromCurrent);
end;
Stream.Read(DataHeader, sizeof(TDataHeader));
if DataHeader.idData = 'fact' thenbegin
Stream.Seek(4, soFromCurrent);
Stream.Read(DataHeader, sizeof(TDataHeader));
end;
with DataHeader dobeginif idData < > 'data' thenraise EReadError.Create('Wrong idData');
SampleCount := DataLen div (Channeles * BitsPerSample div 8)
end;
end;
procedure SaveSound(Stream: TStream; SampleCount, SamplesPerSec: integer;
BitsPerSample, Channeles: smallint; GetData: TGetData);
var
WaveHeader: TWaveHeader;
DataHeader: TDataHeader;
buf: smallint;
BytesPerSample: smallint;
i: integer;
begin
CreateWaveHeader(SampleCount, SamplesPerSec, BitsPerSample,
Channeles, WaveHeader, DataHeader);
Stream.Write(WaveHeader, sizeof(TWaveHeader));
Stream.Write(DataHeader, sizeof(TDataHeader));
BytesPerSample := BitsPerSample div 8;
if Channeles = 1
thenfor i := 0 to SampleCount - 1 dobegin
GetData(0, i, buf);
Stream.Write(buf, BytesPerSample);
endelsefor i := 0 to SampleCount - 1 dobegin
GetData(0, i, buf);
Stream.Write(buf, BytesPerSample);
GetData(1, i, buf);
Stream.Write(buf, BytesPerSample);
end;
end;
end.
Here's a translation of the content into Russian:
Это программное обеспечение на языке Delphi, демонстрирующее, как читать и записывать файлы аудио в формате WAV. Программа включает в себя три кнопки: Button1, Button2 и Button3.
Button1 создает новый файл WAV с указанным частотой дискретизации, длиной и количеством каналов (моно или стерео). Она генерирует аудиоданные с помощью процедуры GetData, которая вызывается для каждого образца. Генерируемые аудиоданные записываются в паметный поток, сохраняются в файле и воспроизводятся с помощью функции playsound.
Button2 останавливает воспроизведение звука, передавая null-указатель функции playsound.
Button3 читает существующий файл WAV (например, "ex.wav"), рассчитывает среднее значение громкости для каждого канала и отображает результаты в заголовке формы. Программа использует процедуру ReadWaveHeader, чтобы прочитать заголовок файла WAV, а затем она читает аудиоданные и рассчитывает сумму абсолютных значений для каждого образца. Среднее значение громкости рассчитывается путем деления суммы на максимальное возможное значение (которое зависит от глубины битности аудиоданных).
Вот некоторые предложения по улучшению кода:
Обработка ошибок: Программа выбрасывает исключения, когда передаются недопустимые параметры в CreateWaveHeader или ReadWaveHeader. Рекомендуется добавить более конкретные сообщения об ошибках и механизмы журналирования для обработки ошибок более надежно.
Организация кода: Главная процедура формы очень длинная и выполняет много задач. Рекомендуется разбить ее на меньшие процедуры, каждая из которых выполняет конкретную задачу (например, создание файла WAV, воспроизведение звука, чтение файла WAV).
Безопасность типов: Программа использует неоттипизированные параметры в некоторых процедурах (например, GetData). Рекомендуется использовать оттипизированные параметры или вводить промежуточные типы для улучшения безопасности типов.
Стиль кода: Код форматирования не согласован, некоторые строки превышают 80 символов, а другие очень коротки. Рекомендуется использовать единый стиль кодирования throughout программы.
Вот обновленная версия кода, которая учитывает эти предложения:
unitWaveFile;...
... и т.д.
Чтение и запись звука в программе на Delphi с использованием модуля MMSystem и wavfile.
Комментарии и вопросы
Получайте свежие новости и обновления по Object Pascal, Delphi и Lazarus прямо в свой смартфон. Подпишитесь на наш Telegram-канал delphi_kansoftware и будьте в курсе последних тенденций в разработке под Linux, Windows, Android и iOS
Материалы статей собраны из открытых источников, владелец сайта не претендует на авторство. Там где авторство установить не удалось, материал подаётся без имени автора. В случае если Вы считаете, что Ваши права нарушены, пожалуйста, свяжитесь с владельцем сайта.