我已經寫了一個簡單的代碼來讀取Wav文件的頭文件,然後開始播放它。這是我的代碼:在Delphi中播放PCM Wav文件
unit Unit1;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, Generics.collections,
Vcl.ExtCtrls, MMSystem;
type
TForm1 = class(TForm)
Button1: TButton;
OpenDialog1: TOpenDialog;
Label1: TLabel;
Label2: TLabel;
Shape1: TShape;
Image1: TImage;
procedure Button1Click(Sender: TObject);
procedure FormCreate(Sender: TObject);
procedure FormClose(Sender: TObject; var Action: TCloseAction);
private
{ Private declarations }
public
{ Public declarations }
end;
type
TWaveformSample = integer; // signed 32-bit; -2147483648..2147483647
TWaveformSamples = packed array of TWaveformSample; // one channel
var
Form1: TForm1;
myWavFile: file;
DataBlock: array[0..3] of byte;
Count: integer;
NumOfChannels: integer;
SampleRate: integer;
BytesPerSecond: integer;
ByesPerSample: integer;
BitsPerSample: integer;
CompressionCode: integer;
CompressionDesc: string;
BlockAlign: integer;
ExtraFormatBytes: integer;
CompressionCodes: TDictionary<integer, string>;
BytesRead: integer;
Samples: TWaveformSamples;
fmt: TWaveFormatEx;
PacketIsPlaying: Boolean;
implementation
{$R *.dfm}
procedure InitAudioSys;
begin
with fmt do
begin
wFormatTag := WAVE_FORMAT_PCM;
nChannels := NumOfChannels;
nSamplesPerSec := SampleRate;
wBitsPerSample := BitsPerSample;
nAvgBytesPerSec := nChannels * nSamplesPerSec * wBitsPerSample div 8;
nBlockAlign := nChannels * wBitsPerSample div 8;
cbSize := 0;
end;
end;
procedure PlaySound;
var
wo: integer;
hdr: TWaveHdr;
begin
if Length(samples) = 0 then
begin
Writeln('Error: No audio has been created yet.');
Exit;
end;
if waveOutOpen(@wo, WAVE_MAPPER, @fmt, 0, 0, CALLBACK_NULL) = MMSYSERR_NOERROR then
try
PacketIsPlaying := True;
ZeroMemory(@hdr, sizeof(hdr));
with hdr do
begin
lpData := @samples[0];
dwBufferLength := fmt.nChannels * Length(Samples) * sizeof(TWaveformSample);
dwFlags := 0;
end;
waveOutPrepareHeader(wo, @hdr, sizeof(hdr));
waveOutWrite(wo, @hdr, sizeof(hdr));
//sleep(450);
//while waveOutUnprepareHeader(wo, @hdr, sizeof(hdr)) = WAVERR_STILLPLAYING do
//sleep(100);
finally
waveOutClose(wo);
PacketIsPlaying := False;
end;
end;
function ReadDataBlock(Size: integer): Boolean;
begin
try
BlockRead(myWavFile, DataBlock, Size, Count);
INC(BytesRead, Size);
Result := True;
except
Result := False;
end;
end;
function OpenWav(FileName: string): Boolean;
begin
try
Assignfile(myWavFile, filename);
Reset(myWavFile, 1);
Result := True;
except
Result := False;
end;
end;
function CloseWav: Boolean;
begin
try
CloseFile(myWavFile);
Result := True;
except
Result := False;
end;
end;
function ValidateWav: Boolean;
const
RIFF: array[0..3] of byte = (82, 73, 70, 70);
WAVE: array[0..3] of byte = (87, 65, 86, 69);
_FMT: array[0..3] of byte = (102, 109, 116, 32);
FACT: array[0..3] of byte = (102, 97, 99, 116);
DATA: array[0..3] of byte = (100, 97, 116, 97);
_DATA: array[0..3] of byte = (64, 61, 74, 61);
var
RiffChunkSize, FmtChunkSize, FactChunkSize, DataChunkSize, i, j, tmp, Freq: integer;
omega,
dt, t: double;
vol: double;
begin
BytesRead := 0;
//Check "RIFF"
ReadDataBlock(4);
if not CompareMem(@DataBlock, @RIFF, SizeOf(DataBlock)) then
begin
Result := False;
Exit;
end;
//Get "RIFF" Chunk Data Size
ReadDataBlock(4);
Move(DataBlock, RiffChunkSize, 4);
//Check "WAVE"
ReadDataBlock(4);
if not CompareMem(@DataBlock, @WAVE, SizeOf(DataBlock)) then
begin
Result := False;
Exit;
end;
{FMT ---------------------------------------------------------------------}
//Check "FMT"
ReadDataBlock(4);
if not CompareMem(@DataBlock, @_FMT, SizeOf(DataBlock)) then
begin
Result := False;
Exit;
end;
//Get "FMT" Chunk Data Size
ReadDataBlock(4);
Move(DataBlock, FmtChunkSize, 4);
BytesRead := 0;
//Get Wav Compression Code
ReadDataBlock(2);
Move(DataBlock, CompressionCode, 2);
if not CompressionCodes.TryGetValue(CompressionCode, CompressionDesc) then
CompressionDesc := 'File Error!';
//Get Number of Channels
ReadDataBlock(2);
Move(DataBlock, NumOfChannels, 2);
//Get Sample Rate
ReadDataBlock(4);
Move(DataBlock, SampleRate, 4);
//Get Average Bytes Per Second
ReadDataBlock(4);
Move(DataBlock, BytesPerSecond, 4);
//Get Block Align
ReadDataBlock(2);
Move(DataBlock, BlockAlign, 2);
//Get Bits Per Sample
ReadDataBlock(2);
Move(DataBlock, BitsPerSample, 2);
//Extra Format Bytes
if BytesRead <= FmtChunkSize - 2 then
begin
ReadDataBlock(2);
Move(DataBlock, ExtraFormatBytes, 2);
end;
//If it's not Uncompressed/PCM File, then we have Extra Format Bytes
if CompressionCode <> 1 then
begin
//Skip Compression Data
for i := 0 to FmtChunkSize - BytesRead - 1 do
ReadDataBlock(1);
Result := False;
Exit;
end;
{FACT --------------------------------------------------------------------}
{FactChunkSize := 0;
//Check "FACT"
ReadDataBlock(4);
if CompareMem(@DataBlock, @FACT, SizeOf(DataBlock)) then
begin
//Get "FMT" Chunk Data Size
ReadDataBlock(4);
Move(DataBlock, FactChunkSize, 4);
BytesRead := 0;
for i := 0 to FactChunkSize - BytesRead - 1 do
ReadDataBlock(1);
end; }
{DATA ------------------------------------------------------------------}
while BytesRead < FmtChunkSize do
ReadDataBlock(1);
BytesRead := 0;
//Skip bytes until "data" shows up
while (not CompareMem(@DataBlock, @DATA, SizeOf(DataBlock))) and (not CompareMem(@DataBlock, @_DATA, SizeOf(DataBlock))) do
begin
ReadDataBlock(4);
end;
ReadDataBlock(4);
Move(DataBlock, DataChunkSize, 4);
Form1.Label1.Caption := 'Compression Code: ' + IntToStr(CompressionCode) + #10#13 +
'Compression Description: ' + CompressionDesc + #10#13 +
'Number of Channels: ' + IntToStr(NumOfChannels) + #10#13 +
'Sample Rate: ' + IntToStr(SampleRate) + #10#13 +
'Byes per Sample: ' + IntToStr(ByesPerSample) + #10#13 +
'Byes per Second: ' + IntToStr(BytesPerSecond) + #10#13 +
'Bits per Second: ' + IntToStr(BitsPerSample);
tmp := FileSize(myWavFile) - DataChunkSize;
{ j := 0;
Form1.Image1.Canvas.Rectangle(0, 0, Form1.Image1.Width, Form1.Image1.Height);
for i := 0 to (DataChunkSize div 20) do
begin
//BlockRead(myWavFile, DataBlock, 76, Count);
tmp := tmp + 76;
Seek(myWavFile, tmp);
ReadDataBlock(4);
Move(DataBlock, Freq, 4);
if i mod ((DataChunkSize div 80) div Form1.Image1.Width) = 0 then
begin
INC(J);
Form1.Image1.Canvas.MoveTo(j, 121 div 2);
Form1.Image1.Canvas.LineTo(j, (121 div 2) - Trunc((Freq/High(Integer)) * (121 div 2)));
end;
Application.ProcessMessages;
end;
Seek(myWavFile, FileSize(myWavFile) - DataChunkSize); }
InitAudioSys;
PacketIsPlaying := False;
SetLength(Samples, fmt.nSamplesPerSec);
while PacketIsPlaying = false do
begin
for i := 0 to fmt.nSamplesPerSec do
begin
ReadDataBlock(4);
Move(DataBlock, Freq, 4);
Samples[i] := Freq;
end;
PlaySound;
Sleep(2000);
Application.ProcessMessages;
end;
Result := True;
end;
procedure TForm1.Button1Click(Sender: TObject);
var
f: file;
b: array[0..3] of byte;
count: integer;
begin
with opendialog1 do
if execute then
begin
Form1.Image1.Canvas.Rectangle(0, 0, Form1.Image1.Width, Form1.Image1.Height);
Label1.Font.Color := clBlack;
OpenWav(FileName);
if ValidateWav = False then
begin
Label1.Caption := 'Invalid File Data!';
Label1.Font.Color := clRed;
Exit;
end;
CloseWav;
end;
end;
procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
begin
CompressionCodes.Destroy;
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
Image1.Canvas.Rectangle(0, 0, Image1.Width, Image1.Height);
CompressionCodes := TDictionary<integer, string>.Create;
CompressionCodes.Add(0, 'Unknown');
CompressionCodes.Add(1, 'PCM/Uncompressed');
CompressionCodes.Add(2, 'Microsoft ADPCM');
CompressionCodes.Add(6, 'ITU G.711 a-law');
CompressionCodes.Add(7, 'ITU G.711 µ-law');
CompressionCodes.Add(17, 'IMA ADPCM');
CompressionCodes.Add(20, 'ITU G.723 ADPCM (Yamaha)');
CompressionCodes.Add(49, 'GSM 6.10');
CompressionCodes.Add(64, 'ITU G.721 ADPCM');
CompressionCodes.Add(80, 'MPEG');
CompressionCodes.Add(85, 'ISO/MPEG');
CompressionCodes.Add(65536, 'Experimental');
end;
end.
的代碼需要的TLabel,一個TButton和打開文件對話框的形式。
我有文件播放問題。目前,我創建了長度爲SamplesPerSecond
的樣本陣列,並在2000年的延遲中一個接一個地播放它們(延遲少於2000毫秒會引發錯誤)。 我現在想要的是我怎樣才能順利閱讀樣品並連續播放,毫不拖延。另外,我希望能夠在文件正在播放時將圖形中的每個樣本都可視化。
+1這是一個非常棒的答案,值得許多upvotes。 –
感謝您的詳細解答。正如你所提到的,我嘗試了在我的代碼中使用3個緩衝區,它聽起來非常好。這是我第一次編寫處理音頻文件的代碼,這只是一個開始。我還有很長的路要走。我正在處理您現在提供的代碼。 – Agha