243 lines
6.9 KiB
ObjectPascal

(*
this file is a part of audio components suite v 2.3.
copyright (c) 2002-2005 andrei borovsky. all rights reserved.
see the license file for more details.
you can contact me at mail@z0m3ie.de
*)
{
$Log: acs_mad.pas,v $
Revision 1.1 2005/12/19 18:36:38 z0m3ie
*** empty log message ***
Revision 1.2 2005/12/04 16:54:34 z0m3ie
All classes are renamed, Style TACS... than T... to avoid conflicts with other components (eg TMixer is TACSMixer now)
Revision 1.1 2005/09/12 22:04:52 z0m3ie
modified structure again, fileformats are now in an sperat folder.
all File In/Out classes are capsulated from TFileIn and TFileOut
Revision 1.3 2005/08/25 20:18:00 z0m3ie
Version 2.4 restructure
TCDPlayer removed (fits not in component structure)
TMP3ToWavConverter removed (fits not in component structure)
Revision 1.2 2005/08/22 20:17:01 z0m3ie
changed Headers to log
changed mail adress
}
unit acs_mad;
interface
uses
ACS_Types, Classes, SysUtils, Math, MAD;
type
TRawPCMWaveHeader = record
RIFF: array [0..3] of Char;
FileSize: Integer;
RIFFType: array [0..3] of Char;
FmtChunkId: array [0..3] of Char;
FmtChunkSize: Integer;
FormatTag: Word;
Channels: Word;
SampleRate: Integer;
BytesPerSecond: Integer;
BlockAlign: Word;
BitsPerSample: Word;
DataChunkId: array [0..3] of Char;
DataSize: Integer;
end;
TMADProgressEvent = procedure(Sender : TComponent) of object;
TMADDoneEvent = procedure(Sender : TComponent; Success : Boolean) of object;
TMADThread = class(TThread)
private
_Free : Boolean;
Progr : Integer;
Owner : TComponent;
FDecoder : mad_decoder;
FInputStream : TStream;
FOutputStream : TStream;
HasFirstFrame : Boolean;
FSR : Integer;
FChan : Integer;
FBitrate : Integer;
FValid : Boolean;
Data : PACSBuffer8;
InputDone : Boolean;
WaveHdr : TRawPCMWaveHeader;
FSize : Integer;
FMADProgress : TMADProgressEvent;
FMADDone : TMADDoneEvent;
WhenDone : procedure of object;
protected
procedure Execute; override;
public
constructor Create(AOwner : TComponent; InputStream, OutputStream : TStream);
destructor Destroy; override;
end;
implementation
function InputFunc(CData : Pointer; Stream : p_mad_stream) : Integer; cdecl;
var
MT : TMADThread;
Len : Integer;
begin
MT := TMADThread(CData);
if MT.InputDone then
begin
Result := MAD_FLOW_STOP;
Exit;
end;
MT.InputDone := True;
// Len := MT.FInputStream.Read(Data^, MT.FInputStream.Size);
Len := MT.FInputStream.Size;
mad_stream_buffer(Stream, MT.Data, Len);
if not MT.Terminated then Result := MAD_FLOW_CONTINUE
else Result := MAD_FLOW_STOP;
end;
function OutputFunc(CData : Pointer; Header : p_mad_header; pcm : p_mad_pcm) : Integer; cdecl;
var
MT : TMADThread;
i, framesize : Integer;
outsamples : array[0..2303] of SmallInt;
text : array[0..4] of Char;
CProgr : Integer;
begin
MT := TMADThread(CData);
if not MT.HasFirstFrame then
begin
MT.FSR := pcm.samplerate;
MT.FChan := pcm.channels;
MT.FBitrate := Header.bitrate;
framesize := Ceil(144*MT.FBitrate/MT.FSR);
MT.FSize := Round(MT.FInputStream.Size/framesize*1152)*MT.FChan*2;
MT.FValid := True;
text := 'RIFF';
Move(text[0], MT.WaveHdr.RIFF[0], 4);
MT.WaveHdr.FileSize := MT.FSize + 44;
text := 'WAVE';
Move(text[0], MT.WaveHdr.RIFFType[0], 4);
text := 'fmt ';
Move(text[0], MT.WaveHdr.FmtChunkId[0], 4);
MT.WaveHdr.FmtChunkSize := 16;
MT.WaveHdr.FormatTag := 1;
MT.WaveHdr.Channels := MT.FChan;
MT.WaveHdr.SampleRate := MT.FSR;
MT.WaveHdr.BitsPerSample := 16;
MT.WaveHdr.BlockAlign := 2*MT.FChan;
MT.WaveHdr.BytesPerSecond := MT.FSR * MT.WaveHdr.BlockAlign;
text := 'data';
Move(text[0], MT.WaveHdr.DataChunkId[0], 4);
MT.WaveHdr.DataSize := MT.FSize;
if MT.FOutputStream is TMemoryStream then
begin
MT.FOutputStream.Size :=MT.FSize + 44;
MT.FOutputStream.Seek(0, soFromBeginning);
end;
MT.FOutputStream.Write(MT.WaveHdr, 44);
MT.HasFirstFrame := True;
end;
if pcm.channels = 2 then
begin
for i := 0 to pcm.length -1 do
begin
if pcm.samples[0][i] >= MAD_F_ONE then
pcm.samples[0][i] := MAD_F_ONE - 1;
if pcm.samples[0][i] < -MAD_F_ONE then
pcm.samples[0][i] := -MAD_F_ONE;
pcm.samples[0][i] := pcm.samples[0][i] shr (MAD_F_FRACBITS + 1 - 16);
outsamples[i shl 1] := pcm.samples[0][i];
if pcm.samples[1][i] >= MAD_F_ONE then
pcm.samples[1][i] := MAD_F_ONE - 1;
if pcm.samples[1][i] < -MAD_F_ONE then
pcm.samples[1][i] := -MAD_F_ONE;
pcm.samples[1][i] := pcm.samples[1][i] shr (MAD_F_FRACBITS + 1 - 16);
outsamples[(i shl 1)+1] := pcm.samples[1][i];
end;
MT.FOutputStream.Write(outsamples[0], pcm.length*4);
end else
begin
for i := 0 to pcm.length -1 do
begin
if pcm.samples[0][i] >= MAD_F_ONE then
pcm.samples[0][i] := MAD_F_ONE - 1;
if pcm.samples[0][i] < -MAD_F_ONE then
pcm.samples[0][i] := -MAD_F_ONE;
pcm.samples[0][i] := pcm.samples[0][i] shr (MAD_F_FRACBITS + 1 - 16);
outsamples[i] := pcm.samples[0][i];
end;
MT.FOutputStream.Write(outsamples[0], pcm.length*2);
end;
if MT.FSize <> 0 then
begin
CProgr := Round(MT.FOutputStream.Position/MT.FSize*100);
if MT.Progr <> CProgr then
begin
MT.Progr := CProgr;
if Assigned(MT.FMADProgress) then
MT.FMADProgress(MT.Owner);
end;
end;
if not MT.Terminated then Result := MAD_FLOW_CONTINUE
else Result := MAD_FLOW_STOP;
end;
function ErrorFunc(CData : Pointer; Stream : p_mad_stream; Frame : p_mad_frame) : Integer; cdecl;
begin
Result := MAD_FLOW_CONTINUE;
end;
constructor TMADThread.Create;
begin
inherited Create(True);
Owner := AOwner;
FInputStream := InputStream;
FOutputStream := OutputStream;
FreeOnTerminate := False;
end;
destructor TMADThread.Destroy;
begin
if not _Free then
begin
Terminate;
{$IFDEF WIN32}
while not _Free do;
{$ENDIF}
end;
inherited Destroy;
end;
procedure TMADThread.Execute;
begin
try
GetMem(Data, FInputStream.Size);
FInputStream.Read(Data[0], FInputStream.Size);
mad_decoder_init(@FDecoder, Self, InputFunc, nil, nil, OutputFunc, ErrorFunc, nil);
mad_decoder_run(@FDecoder, MAD_DECODER_MODE_SYNC);
mad_decoder_finish(@FDecoder);
FreeMem(Data);
WhenDone;
if Assigned(FMADDone) then FMADDone(Owner, FValid);
_Free := True;
except
FreeMem(Data);
WhenDone;
_Free := True;
end;
end;
end.