243 lines
6.9 KiB
ObjectPascal
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.
|