
git-svn-id: https://svn.code.sf.net/p/lazarus-ccr/svn@2265 8e941d3f-bd1b-0410-a28a-d453659cc2b4
215 lines
5.9 KiB
ObjectPascal
215 lines
5.9 KiB
ObjectPascal
{
|
|
WAV format reader for the fpSound library
|
|
|
|
License: The same modified LGPL as the LCL
|
|
|
|
Authors:
|
|
|
|
JiXian Yang
|
|
Felipe Monteiro de Carvalho
|
|
|
|
Canonical WAV file description here:
|
|
|
|
https://ccrma.stanford.edu/courses/422/projects/WaveFormat/
|
|
}
|
|
unit fpsound_wav;
|
|
|
|
{$mode objfpc}
|
|
|
|
interface
|
|
|
|
uses
|
|
Classes, SysUtils, Math,
|
|
fpsound;
|
|
|
|
// WAVE UTILS
|
|
|
|
type
|
|
// WAV is formed by the following structures in this order
|
|
// All items are in little endian order, except the char arrays
|
|
// Items might be in big endian order if the RIFF identifier is RIFX
|
|
|
|
TRiffHeader = packed record
|
|
ID : array [0..3] of char; // should be RIFF
|
|
Size : LongWord; // 4 + (8 + SubChunk1Size) + (8 + SubChunk2Size). The entire file size excluding TRiffHeader.ID and .Size
|
|
Format : array [0..3] of char; // should be WAVE
|
|
end;
|
|
|
|
TWaveFormat = packed record
|
|
ID : array [0..3] of char; // Should be "fmt "
|
|
Size : LongWord; // SubChunk1Size
|
|
Format : Word; // PCM = 1 (Linear quantization), values > 1 indicate a compressed format
|
|
Channels : Word; // Mono = 1, Stereo = 2, etc
|
|
SampleRate : LongWord; // 8000, 44100, etc
|
|
ByteRate : LongWord; // = SampleRate * NumChannels * BitsPerSample/8
|
|
BlockAlign : Word; // = NumChannels * BitsPerSample/8
|
|
BitsPerSample : Word; // examples: 8 bits, 16 bits, etc
|
|
end;
|
|
// If the format is not PCM then there will also be:
|
|
// TWaveFormatExtension = packed record
|
|
// ExtraParamSize: Word;
|
|
// ExtraParams...
|
|
// end;
|
|
|
|
TDataChunk = packed record
|
|
Id : array [0..3] of char; // should be "data"
|
|
Size : LongWord; // == NumSamples * NumChannels * BitsPerSample/8
|
|
end;
|
|
// And after this header the actual data comes, which is an array of samples
|
|
|
|
{ TWaveReader }
|
|
|
|
TWaveReader = class(TSoundReader)
|
|
public
|
|
fmt: TWaveFormat;
|
|
datachunk: TDataChunk;
|
|
NumSamples: Integer;
|
|
procedure ReadFromStream(AStream: TStream; ADest: TSoundDocument); override;
|
|
procedure ReadHeaders(AStream: TStream; ADest: TSoundDocument);
|
|
procedure ReadAllSamples(AStream: TStream; ADest: TSoundDocument);
|
|
procedure ReadSample(AStream: TStream; ADest: TSoundDocument);
|
|
//function ReadBuf(var Buffer; BufferSize: Integer): Integer;
|
|
end;
|
|
|
|
implementation
|
|
|
|
const
|
|
ID_RIFF = 'RIFF';
|
|
ID_WAVE ='WAVE';
|
|
fD_fmt = 'fmt ';
|
|
ID_data = 'data';
|
|
|
|
{ TWaveReader }
|
|
|
|
procedure TWaveReader.ReadFromStream(AStream:TStream; ADest: TSoundDocument);
|
|
begin
|
|
ReadHeaders(AStream, ADest);
|
|
ReadAllSamples(AStream, ADest);
|
|
end;
|
|
|
|
procedure TWaveReader.ReadHeaders(AStream: TStream; ADest: TSoundDocument);
|
|
var
|
|
riff : TRiffHeader;
|
|
lKeyElement: TSoundKeyElement;
|
|
begin
|
|
AStream.Read(riff, sizeof(riff));//=sizeof(riff);
|
|
riff.Size:=LEtoN(riff.Size);
|
|
//Result:=Result and (riff.ID=ID_RIFF) and (riff.Format=ID_WAVE);
|
|
//if not Result then Exit;
|
|
|
|
AStream.Read(fmt, sizeof(fmt));//=sizeof(fmt);
|
|
fmt.Size:=LEtoN(fmt.Size);
|
|
fmt.Format:=LEtoN(fmt.Format);
|
|
fmt.Channels:=LEtoN(fmt.Channels);
|
|
fmt.SampleRate:=LEtoN(fmt.SampleRate);
|
|
fmt.ByteRate:=LEtoN(fmt.ByteRate);
|
|
fmt.BlockAlign:=LEtoN(fmt.BlockAlign);
|
|
fmt.BitsPerSample:=LEtoN(fmt.BitsPerSample);
|
|
|
|
AStream.Read(datachunk, sizeof(datachunk));
|
|
datachunk.Size := LEtoN(datachunk.Size);
|
|
|
|
NumSamples := fmt.BlockAlign div datachunk.size;
|
|
|
|
//Result:=fmt.ID=ID_fmt;
|
|
// pos:=-1;
|
|
|
|
// Store the data in the document
|
|
lKeyElement := TSoundKeyElement.Create;
|
|
lKeyElement.SampleRate := fmt.SampleRate;
|
|
lKeyElement.BitsPerSample := fmt.BitsPerSample;
|
|
lKeyElement.Channels := fmt.Channels;
|
|
ADest.AddSoundElement(lKeyElement);
|
|
end;
|
|
|
|
procedure TWaveReader.ReadAllSamples(AStream: TStream; ADest: TSoundDocument);
|
|
var
|
|
i: Integer;
|
|
begin
|
|
for i := 0 to NumSamples - 1 do
|
|
ReadSample(AStream, ADest);
|
|
end;
|
|
|
|
procedure TWaveReader.ReadSample(AStream: TStream; ADest: TSoundDocument);
|
|
var
|
|
lSoundSample8: TSoundSample8;
|
|
lSoundSample16: TSoundSample16;
|
|
i: Integer;
|
|
lByteData: Byte;
|
|
lWordData: SmallInt;
|
|
begin
|
|
if fmt.BitsPerSample = 8 then
|
|
begin
|
|
lSoundSample8 := TSoundSample8.Create;
|
|
SetLength(lSoundSample8.ChannelValues, fmt.Channels);
|
|
for i := 0 to fmt.Channels - 1 do
|
|
begin
|
|
lByteData := AStream.ReadByte();
|
|
lSoundSample8.ChannelValues[i] := lByteData;
|
|
end;
|
|
|
|
ADest.AddSoundElement(lSoundSample8);
|
|
end
|
|
else if fmt.BitsPerSample = 16 then
|
|
begin
|
|
lSoundSample16 := TSoundSample16.Create;
|
|
SetLength(lSoundSample16.ChannelValues, fmt.Channels);
|
|
for i := 0 to fmt.Channels - 1 do
|
|
begin
|
|
AStream.Read(lWordData, 2);
|
|
lSoundSample16.ChannelValues[i] := lWordData;
|
|
end;
|
|
|
|
ADest.AddSoundElement(lSoundSample16);
|
|
end
|
|
else
|
|
raise Exception.Create(Format('[TWaveReader.ReadSample] Invalid number of bits per sample: %d', [fmt.BitsPerSample]));
|
|
end;
|
|
|
|
{function TWaveReader.ReadBuf(var Buffer;BufferSize:Integer):Integer;
|
|
var
|
|
sz : Integer;
|
|
p : PByteArray;
|
|
i : Integer;
|
|
begin
|
|
FillChar(Buffer, BufferSize, 0);
|
|
Result:=0;
|
|
// all data read
|
|
if eof then Exit;
|
|
|
|
p:=@Buffer;
|
|
i:=0;
|
|
while (not eof) and (i<bufferSize) do begin
|
|
if chunkpos>=chunkdata.Size then begin
|
|
if pos<0 then
|
|
fstream.Position:=sizeof(TRiffHeader)+Int64(fmt.Size)+sizeof(TDataChunk)
|
|
else
|
|
fstream.Position:=pos+chunkdata.size+SizeOf(chunkdata);
|
|
|
|
eof:=pos>=fStream.Size;
|
|
if not eof then begin
|
|
pos:=fStream.Position;
|
|
sz:=fstream.Read(chunkdata, sizeof(chunkdata));
|
|
chunkdata.Size:=LEtoN(chunkdata.Size);
|
|
if (sz<>sizeof(chunkdata)) or (chunkdata.Id<>ID_data) then
|
|
chunkpos:=chunkdata.Size
|
|
else
|
|
chunkpos:=0;
|
|
end;
|
|
end else begin
|
|
sz:=Min(BufferSize, chunkdata.Size-chunkpos);
|
|
fStream.Position:=pos+sizeof(chunkdata)+chunkpos;
|
|
sz:=fStream.Read(p[i], sz);
|
|
if sz<0 then Exit;
|
|
inc(chunkpos, sz);
|
|
inc(i, sz);
|
|
end;
|
|
end;
|
|
Result:=i;
|
|
end;}
|
|
|
|
initialization
|
|
RegisterSoundReader(TWaveReader.Create, sfWav);
|
|
end.
|
|
|