476 lines
12 KiB
ObjectPascal

(*
the original version of this file is written by thomas la cour,
http://www.top-house.dk/~nr161/delphi/
This file is a part of Audio Components Suite v 2.4
Copyright (c) 2002, 2003 Andrei Borovsky. All rights reserved.
See the LICENSE file for more details.
You can contact me at acs@compiler4.net
*)
{
$Log: acs_mac.pas,v $
Revision 1.7 2006/07/04 17:12:45 z0m3ie
ACS 2.4 alt wiederhergestellt (unterschiedliche Sampleformate ...)
Revision 1.3 2005/12/30 11:10:57 z0m3ie
some corrections to lazarus-linux depending things
Revision 1.2 2005/12/26 17:31:39 z0m3ie
fixed some problems in acs_dsfiles
fixed some problems in acs_vorbis
reworked all buffers
Revision 1.1 2005/12/19 18:36:38 z0m3ie
*** empty log message ***
Revision 1.6 2005/12/18 17:01:54 z0m3ie
delphi compatibility
Revision 1.5 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.4 2005/11/29 18:32:51 z0m3ie
bugfixes for win32 version
Revision 1.3 2005/11/28 21:57:24 z0m3ie
mostly FileOut fixes
moved PBuffer to PBuffer8
set all to dynamically Buffering
Revision 1.2 2005/09/13 04:04:50 z0m3ie
First release without Components for Fileformats
only TFileIn and TFileOut are Visible
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.1 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)
}
unit acs_mac;
{$ifdef linux}{$message error 'unit not supported'}{$endif linux}
interface
uses
ACS_File,Classes, SysUtils, Windows, ACS_Classes, MACDll;
type
// Note by A.B.: It seems that APE compressor supports file output only.
TMACOut = class(TACSCustomFileOut)
private
APECompress: TAPECompress;
WaveFormatEx: TWaveFormatEx;
EndOfStream: Boolean;
FCompressionLevel: Integer;
FMaxAudioBytes: Integer;
procedure SetCompressionLevel(Value: Integer);
protected
procedure Done; override;
function DoOutput(Abort: Boolean): Boolean; override;
procedure Prepare; override;
public
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
published
property CompressionLevel: LongInt read FCompressionLevel write SetCompressionLevel stored True;
property MaxAudioBytes: Integer read FMaxAudioBytes write FMaxAudioBytes;
end;
(* Note by A.B.: Due to the reasons described above this component
ignores streamed input *)
TMACIn = class(TACSCustomFileIn)
private
APEDecompress: TAPEDecompress;
EndOfStream: Boolean;
function GetAverageBitrate: Integer;
function GetCurrentBitrate: Integer;
function GetCurrentBlock: Integer;
function GetCurrentMS: Integer;
function GetLengthMS: Integer;
function GetTotalBlocks: Integer;
protected
function GetBPS: Integer; override;
function GetCh: Integer; override;
function GetSR: Integer; override;
function GetTotalTime: real; override;
procedure OpenFile; override;
procedure CloseFile; override;
public
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
function GetData(Buffer: Pointer; BufferSize: Integer): Integer; override;
function Seek(Sample : Integer) : Boolean; override;
procedure Flush; override;
procedure Init; override;
property AverageBitrate: Integer read GetAverageBitrate;
property CurrentBitrate: Integer read GetCurrentBitrate;
property CurrentBlock: Integer read GetCurrentBlock;
property CurrentMS: Integer read GetCurrentMS;
property LengthMS: Integer read GetLengthMS;
property TotalBlocks: Integer read GetTotalBlocks;
end;
implementation
constructor TMACOut.Create;
begin
inherited Create(AOwner);
FBufferSize := $10000;
FCompressionLevel := COMPRESSION_LEVEL_NORMAL;
FMaxAudioBytes := MAX_AUDIO_BYTES_UNKNOWN;
if not (csDesigning in ComponentState) then
begin
if not MACLoaded then
raise EACSException.Create(MACPath + ' library could not be loaded.');
end;
end;
destructor TMACOut.Destroy;
begin
if Assigned(APECompress) then
APECompress.Free;
inherited Destroy;
end;
procedure TMACOut.Prepare;
var
r: Integer;
begin
GetMem(FBuffer,FBufferSize);
if FFileName = '' then raise EACSException.Create('File name is not assigned.');
FInput.Init;
EndOfStream := False;
APECompress := TAPECompress.Create;
macFillWaveFormatEx(WaveFormatEx, FInput.SampleRate, FInput.BitsPerSample, FInput.Channels);
r := APECompress.Start(
PChar(FFileName),
@WaveFormatEx,
FMaxAudioBytes,
FCompressionLevel,
nil,
CREATE_WAV_HEADER_ON_DECOMPRESSION);
CanOutput := (r = 0);
if r <> 0 then
raise EACSException.Create('Error starting APECompress.' + #13#10 +
macErrorExplanation(r));
end;
procedure TMACOut.Done;
begin
APECompress.Finish(nil, 0, 0);
APECompress.Free;
APECompress := nil;
FInput.Flush;
FreeMem(FBuffer);
end;
function TMACOut.DoOutput(Abort: Boolean): Boolean;
var
Len, i, x, z: Integer;
pBuffer: PByteArray;
nAudioBytesLeft, nBufferBytesAvailable, nNoiseBytes, nRetVal: Integer;
begin
// No exceptions Here
Result := True;
if not CanOutput then Exit;
if Abort or EndOfStream then
begin
(* We don't close file here to avoide exceptions
if output componenet's Stop method is called *)
Result := False;
Exit;
end;
Len := Finput.GetData(@FBuffer[0], FBufferSize);
x := 0;
if Len <> 0 then
begin
nAudioBytesLeft := Len;
while (nAudioBytesLeft > 0) do
begin
nBufferBytesAvailable := 0;
pBuffer := APECompress.LockBuffer(nBufferBytesAvailable);
nNoiseBytes := nBufferBytesAvailable;
if nNoiseBytes > nAudioBytesLeft then
nNoiseBytes := nAudioBytesLeft;
//whats this ? schoult System.Move not be faster ?
for z := 0 to nNoiseBytes - 1 do
begin
pBuffer[z] := FBuffer[x];
inc(x);
end;
nRetVal := APECompress.UnlockBuffer(nNoiseBytes, TRUE);
if (nRetVal <> 0) then
raise EACSException.Create('APECompress.UnlockBuffer Error: ' + inttostr(nRetVal));
dec(nAudioBytesLeft, nNoiseBytes);
end
end
else
EndOfStream := True;
end;
constructor TMACIn.Create;
begin
inherited Create(AOwner);
BufferSize := $2000;
if not (csDesigning in ComponentState) then
begin
if not MACLoaded then
raise EACSException.Create(MACPath + ' library could not be loaded.');
end;
end;
destructor TMACIn.Destroy;
begin
if Assigned(APEDecompress) then
APEDecompress.Free;
inherited Destroy;
end;
procedure TMACIn.OpenFile;
begin
FValid := True;
if FOpened = 0 then
begin
EndOfStream := False;
APEDecompress := TAPEDecompress.Create(FileName);
if APEDecompress.Handle <> 0 then
begin
FSize := APEDecompress.InfoWavTotalBytes;
FSR := APEDecompress.InfoSampleRate;
FBPS := APEDecompress.InfoBitsPerSample;
FChan := APEDecompress.InfoChannels;
FTime := APEDecompress.InfoLengthMS div 1000; // Round(ov_time_total(VFile, 0));
FTotalSamples := (FSize div (FBPS shr 3)) div FChan;
end
else
begin
FValid := False;
FOpened := -1;
end;
end;
Inc(FOpened);
end;
procedure TMACIn.CloseFile;
begin
if FOpened = 1 then
begin
if Assigned(APEDecompress) then
APEDecompress.Free;
APEDecompress := nil;
end;
if FOpened > 0 then Dec(FOpened);
end;
function TMACIn.GetData(Buffer: Pointer; BufferSize: Integer): Integer;
var
l, csize, offs: Integer;
blocks: Integer;
tmp: Double;
begin
if not Busy then raise EACSException.Create('The Stream is not opened');
if BufStart > BufEnd then
begin
if FOffset <> 0 then
begin
offs := Round((FOffset / 100) * FSize);
FPosition := FPosition + offs;
if FPosition < 0 then FPosition := 0
else if FPosition > FSize then FPosition := FSize;
APEDecompress.Seek(FPosition shr 2);
FOffset := 0;
end;
BufStart := 1;
BufEnd := 0;
if not EndOfStream then
begin
while BufEnd < BufferSize do
begin
//l := ov_read(VFile, @buf[BufEnd + 1], BUF_SIZE - BufEnd, 0, 2, 1, @cursec);
blocks := (BufferSize - BufEnd) div 4;
APEDecompress.GetData(@FBuffer[BufEnd], blocks, l);
l := l * 4;
if l <= 0 then
begin
EndOfStream := True;
Break;
end;
Inc(BufEnd, l);
if (FEndSample <> -1) then
begin
csize := (FEndSample-FStartSample)*(FBPS shr 3)*FChan;
if (csize - FPosition) <= 0 then
begin
EndOfStream := True;
Break;
end;
if (csize - FPosition) < BufEnd then
begin
BufEnd := csize - FPosition;
Break;
end;
end;
end;
end;
if EndOfStream and FLoop then
begin
Flush;
Init;
EndOfStream := False;
while BufEnd < BufferSize do
begin
//l := ov_read(VFile, @buf[BufEnd + 1], BUF_SIZE - BufEnd, 0, 2, 1, @cursec);
blocks := (BufferSize - BufEnd) div 4;
APEDecompress.GetData(@FBuffer[BufEnd], blocks, l);
l := l * 4;
if l <= 0 then
begin
EndOfStream := True;
Break;
end;
Inc(BufEnd, l);
end;
end;
end;
if BufferSize < (BufEnd - BufStart + 1) then
Result := BufferSize
else
Result := BufEnd - BufStart + 1;
Move(FBuffer[BufStart - 1], Buffer^, Result);
Inc(BufStart, Result);
Inc(FPosition, Result);
end;
function TMACIn.GetTotalTime: real;
begin
OpenFile;
if Assigned(APEDecompress) then
Result := APEDecompress.LengthMS / 1000;
CloseFile;
end;
function TMACIn.GetAverageBitrate: Integer;
begin
if Assigned(APEDecompress) then
Result := APEDecompress.AverageBitrate;
end;
function TMACIn.GetCurrentBitrate: Integer;
begin
if Assigned(APEDecompress) then
Result := APEDecompress.CurrentBitrate;
end;
function TMACIn.GetCurrentBlock: Integer;
begin
if Assigned(APEDecompress) then
Result := APEDecompress.CurrentBlock;
end;
function TMACIn.GetCurrentMS: Integer;
begin
if Assigned(APEDecompress) then
Result := APEDecompress.CurrentMS;
end;
function TMACIn.GetLengthMS: Integer;
begin
if Assigned(APEDecompress) then
Result := APEDecompress.LengthMS;
end;
function TMACIn.GetTotalBlocks: Integer;
begin
if Assigned(APEDecompress) then
Result := APEDecompress.TotalBlocks;
end;
function TMACIn.GetBPS: Integer;
begin
OpenFile;
Result := FBPS;
CloseFile;
end;
function TMACIn.GetCh: Integer;
begin
OpenFile;
Result := FChan;
CloseFile;
end;
function TMACIn.GetSR: Integer;
begin
OpenFile;
Result := FSR;
CloseFile;
end;
procedure TMACOut.SetCompressionLevel(Value: Integer);
begin
case Value of
COMPRESSION_LEVEL_FAST,
COMPRESSION_LEVEL_NORMAL,
COMPRESSION_LEVEL_HIGH,
COMPRESSION_LEVEL_EXTRA_HIGH: FCompressionLevel := Value;
else
FCompressionLevel := COMPRESSION_LEVEL_NORMAL;
end;
end;
procedure TMACIn.Flush;
begin
inherited Flush;
end;
procedure TMACIn.Init;
begin
inherited Init;
BufStart := 1;
BufEnd := 0;
end;
function TMACIn.Seek(Sample : Integer) : Boolean;
begin
Result := False;
if not FSeekable then Exit;
Result := True;
OpenFile;
APEDecompress.Seek(Sample);
CloseFile;
end;
initialization
FileFormats.Add('mac','Monkey Audio',TMACOut);
FileFormats.Add('mac','Monkey Audio',TMACIn);
end.