1377 lines
40 KiB
ObjectPascal
1377 lines
40 KiB
ObjectPascal
(*
|
||
this file is a part of audio components suite.
|
||
see the license file for more details.
|
||
you can contact me at mail@z0m3ie.de
|
||
|
||
$Log: acs_classes.pas,v $
|
||
Revision 1.22 2006/09/10 18:26:02 z0m3ie
|
||
*** empty log message ***
|
||
|
||
Revision 1.21 2006/09/04 14:40:15 z0m3ie
|
||
*** empty log message ***
|
||
|
||
Revision 1.20 2006/08/31 20:10:54 z0m3ie
|
||
*** empty log message ***
|
||
|
||
Revision 1.19 2006/07/09 16:40:34 z0m3ie
|
||
*** empty log message ***
|
||
|
||
Revision 1.18 2006/07/04 17:12:45 z0m3ie
|
||
ACS 2.4 alt wiederhergestellt (unterschiedliche Sampleformate ...)
|
||
|
||
Revision 1.7 2006/01/01 18:46:40 z0m3ie
|
||
*** empty log message ***
|
||
|
||
Revision 1.6 2005/12/30 13:24:27 z0m3ie
|
||
waveformat corrections, localizing
|
||
|
||
Revision 1.5 2005/12/30 11:10:57 z0m3ie
|
||
some corrections to lazarus-linux depending things
|
||
|
||
Revision 1.4 2005/12/27 05:43:03 z0m3ie
|
||
fixed little bug with TDoneThread
|
||
|
||
Revision 1.3 2005/12/26 17:31:38 z0m3ie
|
||
fixed some problems in acs_dsfiles
|
||
fixed some problems in acs_vorbis
|
||
reworked all buffers
|
||
|
||
Revision 1.2 2005/12/20 20:11:52 z0m3ie
|
||
updated package
|
||
|
||
Revision 1.1 2005/12/19 18:34:35 z0m3ie
|
||
*** empty log message ***
|
||
|
||
Revision 1.8 2005/12/04 16:54:33 z0m3ie
|
||
All classes are renamed, Style TACS... than T... to avoid conflicts with other components (eg TMixer is TACSMixer now)
|
||
|
||
Revision 1.7 2005/11/28 21:57:24 z0m3ie
|
||
mostly FileOut fixes
|
||
moved PBuffer to PBuffer8
|
||
set all to dynamically Buffering
|
||
|
||
Revision 1.6 2005/11/28 19:10:14 z0m3ie
|
||
*** empty log message ***
|
||
|
||
Revision 1.5 2005/10/02 16:51:46 z0m3ie
|
||
*** empty log message ***
|
||
|
||
Revision 1.4 2005/09/16 17:34:29 z0m3ie
|
||
*** empty log message ***
|
||
|
||
Revision 1.3 2005/09/15 20:59:38 z0m3ie
|
||
start translate the documentation in the source for pasdoc
|
||
|
||
Revision 1.2 2005/09/13 21:54:11 z0m3ie
|
||
acs is localizeable now (ACS_Strings)
|
||
|
||
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.8 2005/09/09 21:33:42 z0m3ie
|
||
linux corrections
|
||
|
||
Revision 1.7 2005/09/04 17:59:37 z0m3ie
|
||
moving CDIn support to AKRip mostly
|
||
begon to add mpegin support for Win with mpg123
|
||
|
||
Revision 1.6 2005/09/02 16:27:49 z0m3ie
|
||
*** empty log message ***
|
||
|
||
Revision 1.5 2005/08/28 20:31:18 z0m3ie
|
||
linux restructuring for 2.4
|
||
|
||
Revision 1.4 2005/08/28 18:35:53 z0m3ie
|
||
created Delphi package for 2.4
|
||
more Mixer stuff
|
||
updated some things for Delphi
|
||
|
||
Revision 1.3 2005/08/22 20:17:01 z0m3ie
|
||
changed Headers to log
|
||
changed mail adress
|
||
*)
|
||
|
||
{
|
||
@abstract(this unit introduces the base classes for acs)
|
||
@author(Andrei Borovsky (2003-2005))
|
||
@author(Christian Ulrich (2005))
|
||
}
|
||
|
||
unit acs_classes;
|
||
|
||
{$ifdef fpc}
|
||
{$mode delphi}
|
||
{$endif}
|
||
|
||
interface
|
||
|
||
uses
|
||
|
||
{$IFDEF MSWINDOWS}
|
||
Windows,Dialogs,
|
||
{$ENDIF}
|
||
ACS_Strings,Classes, SysUtils,ACS_Types;
|
||
|
||
type
|
||
|
||
TACSOutputStatus = (tosUndefined,tosPlaying, tosPaused, tosIdle);
|
||
TACSFileOutputMode = (foRewrite = 0, foAppend);
|
||
TACSOutputFunc = function(Abort : Boolean):Boolean of object;
|
||
|
||
//TThreadDoneEvent = procedure of object;
|
||
|
||
TACSThreadExceptionEvent = procedure(Sender : TComponent; E : Exception) of object;
|
||
TACSHandleThreadException = procedure(E : Exception) of object;
|
||
TACSOutputDoneEvent = procedure(Sender : TComponent) of object;
|
||
TACSOutputProgressEvent = procedure(Sender : TComponent) of object;
|
||
|
||
{$IFDEF LINUX}
|
||
// File access mask constants
|
||
const
|
||
famUserRead = 64;
|
||
famUserWrite = 128;
|
||
famGroupRead = 8;
|
||
famGroupWrite = 16;
|
||
famOthersRead = 1;
|
||
famOthersWrite = 2;
|
||
{$ENDIF}
|
||
|
||
type
|
||
{$IFNDEF FPC}
|
||
{$IFDEF LINUX}
|
||
TTPriority = Integer;
|
||
{$ENDIF}
|
||
{$IFDEF MSWINDOWS}
|
||
TTPriority = TThreadPriority;
|
||
{$ENDIF}
|
||
{$ELSE}
|
||
TTPriority = TThreadPriority;
|
||
{$ENDIF}
|
||
{Basic exception class for ACS}
|
||
EACSException = class(Exception)
|
||
end;
|
||
|
||
{Basic Thread class for ACS}
|
||
|
||
{ TACSThread }
|
||
|
||
TACSThread = class(TThread)
|
||
private
|
||
procedure CallOnProgress;
|
||
public
|
||
Parent : TObject;
|
||
Terminating : Boolean;
|
||
Stop : Boolean;
|
||
HandleException : TACSHandleThreadException;
|
||
Delay : Integer;
|
||
CS : TRTLCriticalSection;
|
||
constructor Create;
|
||
procedure DoPause;
|
||
procedure DoResume;
|
||
procedure Execute; override;
|
||
end;
|
||
|
||
TACSVerySmallThread = class(TThread)
|
||
public
|
||
FOnDone : TACSOutputDoneEvent;
|
||
Sender : TComponent;
|
||
procedure Execute; override;
|
||
procedure CallOnDone;
|
||
end;
|
||
|
||
{ TACSInput is the base class for all input and converter components.
|
||
}
|
||
|
||
{ TACSInput }
|
||
|
||
{ TACSCustomInput }
|
||
|
||
TACSCustomInput = class(TComponent)
|
||
protected
|
||
FPosition : Integer;
|
||
FSize : Integer;
|
||
FBusy : Boolean;
|
||
BufStart, BufEnd : Integer;
|
||
FBuffer : array of byte;
|
||
(* We don't declare the buffer variable here
|
||
because different descendants may need different buffer sizes *)
|
||
function GetBPS : Integer; virtual;
|
||
function GetCh : Integer; virtual;
|
||
function GetSR : Integer; virtual;
|
||
function GetTotalTime : real; virtual;
|
||
procedure SetBufferSize(S : Integer);virtual;
|
||
function GetBufferSize : Integer;
|
||
public
|
||
constructor Create(AOwner: TComponent); override;
|
||
destructor Destroy; override;
|
||
{ This is an abstract method.
|
||
In TACSInput descendands this function reads the audio stream data into the Buffer.
|
||
Returns the number of bytes actually read.
|
||
Returns zero at the end of stream.
|
||
|
||
Note: usually this method is called internally by the output or converter component
|
||
to which the input component is assigned. You can call this method if you want to
|
||
get direct access to the audio stream. In such a case the sequence of calls should
|
||
look like this:
|
||
|
||
InputComponent.Init;
|
||
|
||
InputComponent.GetData(...); // in a loop
|
||
|
||
InputComponent.Flush;
|
||
}
|
||
function GetData(Buffer : Pointer; BufferSize : Integer): Integer; virtual; abstract;
|
||
{ This function is called from output component when the buffer must be filled.
|
||
}
|
||
procedure Reset; virtual;
|
||
{ This is an abstract method. In TACSInput descendands it prepares input
|
||
component for reading data.
|
||
}
|
||
procedure Init; virtual; abstract;
|
||
{ This is an abstract method. In TACSInput descendands it closes the current input,
|
||
clearing up all temporary structures alocated during data transfer.
|
||
|
||
Note: usually this method is called internally by the output or converter component to
|
||
which the input component is assigned. You can call this method if you want to get direct
|
||
access to the audio stream.
|
||
}
|
||
procedure Flush; virtual; abstract;
|
||
{ Read this property to determine the number of bits per sample in the input audio stream.
|
||
Possible values are 8 and 16.
|
||
}
|
||
property BitsPerSample : Integer read GetBPS;
|
||
property Position : Integer read FPosition;
|
||
{ Read this property to get the sample rate (sampling frequency) in Hz
|
||
for the input audio stream.
|
||
}
|
||
property SampleRate : Integer read GetSR;
|
||
{ Read Channles to determine the number of channels in the input audio stream.
|
||
Possible values are 1 (mono) and 2 (stereo).
|
||
}
|
||
property Channels : Integer read GetCh;
|
||
{ Read Size to determine the size of the input stream in bytes.
|
||
}
|
||
property Size : Integer read FSize;
|
||
{ Read this property to get the total time of the input stream in seconds.
|
||
If the total time cannot be determined this property contains 0.
|
||
}
|
||
property TotalTime : Real read GetTotalTime;
|
||
{ This property sets the buffersize of the component
|
||
}
|
||
property BufferSize : Integer read GetBufferSize write SetBufferSize;
|
||
property Busy : Boolean read FBusy;
|
||
end;
|
||
|
||
{ TACSOutput is the base class for all ACS output components.
|
||
}
|
||
|
||
{ TACSOutput }
|
||
|
||
TACSCustomOutput = class(TComponent)
|
||
protected
|
||
CanOutput : Boolean;
|
||
CurProgr : real;
|
||
Thread : TACSThread;
|
||
FInput : TACSCustomInput;
|
||
FOnDone : TACSOutputDoneEvent;
|
||
FOnProgress : TACSOutputProgressEvent;
|
||
Busy : Boolean; // Set to true by Run and to False by WhenDone.
|
||
FOnThreadException : TACSThreadExceptionEvent;
|
||
InputLock : Boolean;
|
||
FBufferSize : Integer;
|
||
FBuffer : PACSBuffer8;
|
||
function GetPriority : TTPriority;
|
||
function GetSuspend : Boolean;
|
||
function GetProgress : real;
|
||
procedure SetInput(vInput : TACSCustomInput); virtual;
|
||
procedure SetPriority(Priority : TTPriority);
|
||
procedure SetSuspend(v : Boolean);
|
||
procedure WhenDone;
|
||
function GetTE : Integer;
|
||
function GetStatus : TACSOutputStatus;
|
||
function GetDelay : Integer;
|
||
procedure SetDelay(Value : Integer);
|
||
procedure Notification(AComponent: TComponent; Operation: TOperation); override;
|
||
procedure HandleThreadException(E : Exception);
|
||
procedure SetBufferSize(S : Integer);
|
||
public
|
||
procedure Prepare; virtual; abstract; // Calls FInput.init
|
||
function DoOutput(Abort : Boolean):Boolean; virtual; abstract;
|
||
procedure Done; virtual; abstract; // Calls FInput.Flush
|
||
constructor Create(AOwner: TComponent); override;
|
||
destructor Destroy; override;
|
||
{$IFDEF MSWINDOWS}
|
||
procedure Abort;
|
||
{$ENDIF}
|
||
{ pauses the output (the output thread is suspended).
|
||
}
|
||
procedure Pause;virtual;
|
||
{ Resumes previously paused output.
|
||
}
|
||
procedure Resume;virtual;
|
||
{ This is the most important method in the output components.
|
||
After an input component has been assigned, call Run to start audio-processing chain.
|
||
}
|
||
procedure Run;
|
||
{ Stops the running output process.
|
||
}
|
||
procedure Stop;
|
||
{ Use this property to set the delay (in milliseconds) in output thread.
|
||
This property allows the user to reduce the stress the output thread puts
|
||
on the CPU (especially under Windows).
|
||
Be careful with this property when using TAudioOut component.
|
||
Assigning too large values to it can cause dropouts in audio playback.
|
||
}
|
||
property Delay : Integer read GetDelay write SetDelay;
|
||
{ Output components perform output in their own threads.
|
||
Use this property to set the priority for the thread.
|
||
}
|
||
property ThreadPriority : TTPriority read GetPriority write SetPriority;
|
||
{ Read Progress to get the output progress in percents.
|
||
This value is meaningful only after the input component has been set
|
||
and only if the input component can tell the size of its stream.
|
||
}
|
||
property Progress : real read GetProgress;
|
||
{ This property indicates the output component's current status. Possible values are:
|
||
|
||
tosPlaying: the component is working;
|
||
|
||
tosPaused: the component is paused (the Pause method was called);
|
||
|
||
tosIdle: the component is idle;
|
||
}
|
||
property Status : TACSOutputStatus read GetStatus;
|
||
property TimeElapsed : Integer read GetTE;
|
||
published
|
||
{ This property allows you to set the input component for the output component.
|
||
The valid input components must be descendants of TACSInput.
|
||
}
|
||
property Input : TACSCustomInput read Finput write SetInput;
|
||
{ If this property is set to True the output thread suspends every time the
|
||
output component becomes inactive, otherwise the thread executes permanently.
|
||
This property is introduced to simplify the debugging process ander
|
||
Kylix IDE. Output components perform output in their own threads.
|
||
The signals received by multi-threaded applications under IDE can cause problems.
|
||
Set this property to False when debugging your application
|
||
and to True in the final release.
|
||
}
|
||
property SuspendWhenIdle : Boolean read GetSuspend write SetSuspend;
|
||
{ This event is invoked when the output is finished.
|
||
|
||
Note: don't try to start other output job before OnDone event from the
|
||
previous job is triggered. An attempt to do so will cause the
|
||
"Component is buisy" exception.
|
||
It is a good practice to desable output-starting controls in the
|
||
method where the output component's Run method is called and to enable
|
||
them in the output component's OnDone event handler.
|
||
}
|
||
property OnDone : TACSOutputDoneEvent read FOnDone write FOndone;
|
||
{ This event is invoked every time the Progress property value changes.
|
||
Be careful when referencing GUI interface components in OnProgress event's handler.
|
||
}
|
||
property OnProgress : TACSOutputProgressEvent read FOnProgress write FOnProgress;
|
||
{ Thread procedure should never terminate.
|
||
For this reason all the exceptions that might arise in the thread
|
||
procedure are caught within the procedure itself.
|
||
The thread generates OnThreadException event to inform the application
|
||
that an exception has occurred.
|
||
Avoid any potential exception-rising actions in this event<6E>s handler!
|
||
Use the handler to reset application controls (if needed) and call the
|
||
input component<6E>s Reset method.
|
||
See the CD ripper demo for an example of handling thread exceptions.
|
||
}
|
||
property OnThreadException : TACSThreadExceptionEvent read FOnThreadException write FOnThreadException;
|
||
{ This Property sets the Buffersize of the component
|
||
}
|
||
property BufferSize : Integer read FBufferSize write SetBufferSize;
|
||
end;
|
||
|
||
{ TACSStreamedInput introduces Stream property.
|
||
This property allows input components that descend from TACSStreamedInput
|
||
to get data not only from files on disc but from any kind of stream.
|
||
The descendats of this class are TACSFileIn (and all its descendats except TMACIn),
|
||
and TStreamIn component that is designed to read raw audio data from streams.
|
||
}
|
||
TACSStreamedInput = class(TACSCustomInput)
|
||
protected
|
||
FStream : TStream;
|
||
FStreamAssigned : Boolean;
|
||
FSeekable : Boolean;
|
||
procedure SetStream(aStream : TStream);
|
||
public
|
||
{ If you write network applications with ACS, you are encouraged to use custom
|
||
streams as it is described in programmer's introduction
|
||
(see also OggStream demo).
|
||
|
||
Usually these custom streams will not be seekable.
|
||
Unfortunately TStream class has no way to indicate it is not seekable
|
||
(all standart TStream descendats are seekable).
|
||
So this property has been added to allow you to specify if the stream is seekable or not.
|
||
The default value of this property is True, and if the stream you're planning
|
||
to use with this component is not seekable, you have to set Seekable to False.
|
||
Note that if the stream is not seekable, such properties as Channels, SampleRate,
|
||
BitsPerSample, and Valid will not return correct values until the input
|
||
component starts performing the actual playback.
|
||
}
|
||
property Seekable : Boolean read FSeekable write FSeekable;
|
||
{ Use this property to set the input stream for the input component.
|
||
Remember that you have to create, destroy and position the input stream explicitly.
|
||
In TACSFileIn descendants the stream assigned to this property takes over
|
||
the FileName property, i. e. if both Stream and FileName property are assigned,
|
||
the stream and not the file will be used for the actual input.
|
||
To unassign this property set it to nil.
|
||
If the stream is seekable it will be reset to the beginning at the end of the playback.
|
||
}
|
||
property Stream : TStream read FStream write SetStream;
|
||
constructor Create(AOwner: TComponent); override;
|
||
end;
|
||
|
||
{ TACSStreamedOutput introduces Stream property.
|
||
This property allows output components that descend from TACSStreamedOutput
|
||
to store data not only to files on disc but to any kind of stream as well.
|
||
}
|
||
TACSStreamedOutput = class(TACSCustomOutput)
|
||
protected
|
||
{ Use this property to set the output stream for the corresponding output component.
|
||
Remember that you have to create, destroy and position the input stream explicitly.
|
||
In file-handling TACSStreamedOutput descendants the stream assigned to this property
|
||
takes over the FileName property, i. e. if both Stream and FileName property are assigned,
|
||
the stream and not the file will be used for the actual output.
|
||
To unassign this property set it to nil.
|
||
}
|
||
FStream : TStream;
|
||
FStreamAssigned : Boolean;
|
||
procedure SetStream(aStream : TStream);
|
||
public
|
||
property Stream : TStream read FStream write SetStream;
|
||
end;
|
||
|
||
{ This class introduces an simple handler for an file tag it can hold any streamable
|
||
piece of data or an string
|
||
}
|
||
TACSFileTag = CLASS
|
||
private
|
||
function GetName : string;virtual;
|
||
public
|
||
property Name : string read GetName;
|
||
function AsString : string;virtual;
|
||
function Streamable : Boolean;virtual;
|
||
procedure SaveToStream(Stream : TStream);virtual;
|
||
procedure LoadFromStream(Stream : TStream);virtual;
|
||
end;
|
||
|
||
{ This class introduces an base class for file tag lists
|
||
}
|
||
|
||
{ TACSFileInfo }
|
||
|
||
TACSFileInfo = CLASS
|
||
private
|
||
procedure ReadFromFile;virtual;
|
||
procedure SetStringTag(Idx : string; const AValue: TACSFileTag);
|
||
function GetStringTag(Idx : string): TACSFileTag;
|
||
public
|
||
procedure SaveToFile;virtual;
|
||
property Tags[Idx : string] : TACSFileTag read GetStringTag write SetStringTag;
|
||
end;
|
||
|
||
{ TACSFileIn is the base class for all input components that read data from files
|
||
(or from other streams in the corresponding file format).
|
||
It introduces such properties as FileName, StartSample, EndSample, Loop,
|
||
and Valid and Jump, Seek, SetStartTime, SetEndTime methods.
|
||
}
|
||
TACSCustomFileIn = class(TACSStreamedInput)
|
||
protected
|
||
FFileName : TFileName;
|
||
FOffset : real;
|
||
FOpened : Integer;
|
||
FValid : Boolean;
|
||
FBPS, FSR, FChan : Integer;
|
||
FTime : Integer;
|
||
FLoop : Boolean;
|
||
FStartSample, FEndSample : Integer;
|
||
FTotalSamples : Integer;
|
||
function GetBPS : Integer; override;
|
||
function GetCh : Integer; override;
|
||
function GetSR : Integer; override;
|
||
function GetTime : Integer;
|
||
function GetValid : Boolean;
|
||
(* Note on FSize calculation:
|
||
FSize is calculated in OpenFile method as the FULL file size.
|
||
More precise calculations regarding StartSample/EndSample are done in Init. *)
|
||
procedure OpenFile; virtual; abstract;
|
||
procedure CloseFile; virtual; abstract;
|
||
function GetTotalTime : real; override;
|
||
public
|
||
constructor Create(AOwner: TComponent); override;
|
||
procedure Reset; override;
|
||
procedure Flush; override;
|
||
procedure Init; override;
|
||
function Seek(SampleNum : Integer) : Boolean; virtual; abstract;
|
||
function SetStartTime(Minutes, Seconds : Integer) : Boolean;
|
||
function SetEndTime(Minutes, Seconds : Integer) : Boolean;
|
||
procedure Jump(Offs : real);
|
||
{ Read this property to determine the total playing time of the file in seconds.
|
||
}
|
||
property Time : Integer read GetTime;
|
||
{ Use this property to read the total number of samples in the file/stream assigned
|
||
to the component. The sample in this context means the minimum playable
|
||
portion of audio data. For mono 8-bit sound 1 sample = 1 byte,
|
||
while for stereo 16-bit sound 1 sample = 4 bytes.
|
||
}
|
||
property TotalSamples : Integer read FTotalSamples;
|
||
{ Read this property to determine if the file is valid.
|
||
It is a good practice to check this property before performing other
|
||
operations on audio stream.
|
||
}
|
||
property Valid : Boolean read GetValid;
|
||
published
|
||
{ Use this property to set the number of the end sample in the file that
|
||
the playback should stop at.
|
||
The value of -1 (default) tells the component to play the file up to the end.
|
||
The sample in this context means the minimum playable portion of audio data.
|
||
For mono 8-bit sound 1 sample = 1 byte,
|
||
while for stereo 16-bit sound 1 sample = 4 bytes.
|
||
You can get the total number of samples in the file by reading TotalSamples property.
|
||
|
||
Note that in some cases (for example, with ADPCM files) sample positioning is not quite precise.
|
||
}
|
||
property EndSample : Integer read FEndSample write FEndSample;
|
||
{ Use this property to set the name of the file to read data from.
|
||
}
|
||
property FileName : TFileName read FFileName write FFileName stored True;
|
||
{ The default value of Loop is False. If this property is set to True,
|
||
the file playback will be looped, in other words the file will start playing
|
||
again right after it is finished. Note, that if Loop is set to True, it will not
|
||
present end conditions to the corresponding output component.
|
||
You can stop the looped input component either by setting Loop to False,
|
||
or by calling Stop method of the corresponding output component.
|
||
}
|
||
property Loop : Boolean read FLoop write FLoop;
|
||
{ Use this property to set the number of the sample in the file that the
|
||
playback should start from. The value of 0 (default) tells the component
|
||
to play the file from the beginning.
|
||
The sample in this context means the minimum playable portion of audio data.
|
||
For mono 8-bit sound 1 sample = 1 byte, while for stereo 16-bit sound 1 sample = 4 bytes.
|
||
You can get the total number of samples in the file by reading TotalSamples property.
|
||
|
||
Note that in some cases (for example, with ADPCM files) sample positioning is not quite precise.
|
||
}
|
||
property StartSample : Integer read FStartSample write FStartSample;
|
||
end;
|
||
|
||
TACSCustomFileOut = class(TACSStreamedOutput)
|
||
protected
|
||
FFileName : TFileName;
|
||
FFileMode : TACSFileOutputMode;
|
||
FAccessMask : Integer;
|
||
procedure SetFileMode(aMode : TACSFileOutputMode); virtual;
|
||
public
|
||
constructor Create(AOwner: TComponent); override;
|
||
{$IFDEF LINUX}
|
||
{ Use this property to set the file access mask. The default value is $1B6,
|
||
which corresponds to rw-rw-rw- access mask.
|
||
}
|
||
property AccessMask : Integer read FAccessMask write FAccessMask;
|
||
{$ENDIF}
|
||
published
|
||
{ There are two possible values for this property: foRewrite and foAppend.
|
||
When foRewrite (default value) is set and the file with the specified
|
||
name already exists (or a stream is assigned) the contents of the file
|
||
(stream) is rewritten.
|
||
|
||
In the foAppend mode the new data is appended to the end of the existing
|
||
file (stream).
|
||
Currently only TWaveOut and TVorbisOut components support foAppend mode.
|
||
}
|
||
property FileMode : TACSFileOutputMode read FFileMode write SetFileMode;
|
||
{ Use this property to set the name of the file to save data to.
|
||
}
|
||
property FileName : TFileName read FFileName write FFileName;
|
||
end;
|
||
|
||
TACSCustomConverter = class(TACSCustomInput)
|
||
protected
|
||
InputLock : Boolean;
|
||
FInput : TACSCustomInput;
|
||
procedure Notification(AComponent: TComponent; Operation: TOperation); override;
|
||
procedure SetInput(aInput : TACSCustomInput); virtual;
|
||
public
|
||
procedure UnlockInput;
|
||
published
|
||
property Input : TACSCustomInput read FInput write SetInput;
|
||
end;
|
||
|
||
const
|
||
|
||
STREAM_BUFFER_SIZE = $80000;
|
||
|
||
type
|
||
|
||
// Circular buffer with TStream interface
|
||
|
||
TACSBufferMode = (bmBlock, bmReport);
|
||
|
||
TACSBufferStream = class(TStream)
|
||
private
|
||
Buff : array[0..STREAM_BUFFER_SIZE-1] of Byte;
|
||
ReadCur, WriteCur : Integer;
|
||
FBufferMode : TACSBufferMode;
|
||
// fBreak : Boolean;
|
||
FBytesInBuffer : Integer;
|
||
BlockEventName : String;
|
||
// LockEventName : String;
|
||
FBytesRead : Integer;
|
||
{$IFDEF MSWINDOWS}
|
||
BlockEvent : THandle;
|
||
CS : TRTLCriticalSection;
|
||
{$ENDIF}
|
||
public
|
||
constructor Create;
|
||
destructor Destroy; override;
|
||
function Read(var Buffer; Count: Longint): Longint; override;
|
||
procedure Reset;
|
||
function Seek(Offset: Longint; Origin: Word): Longint; overload; override;
|
||
function Seek(const Offset: Int64; Origin: TSeekOrigin): Int64; overload; override;
|
||
function Write(const Buffer; Count: Longint): Longint; override;
|
||
property BytesInBuffer : Integer read FBytesInBuffer;
|
||
property BufferMode : TACSBufferMode read FBufferMode write FBufferMode;
|
||
end;
|
||
|
||
|
||
implementation
|
||
|
||
constructor TACSCustomInput.Create(AOwner: TComponent);
|
||
begin
|
||
inherited Create(AOwner);
|
||
Fposition := 0;
|
||
end;
|
||
|
||
destructor TACSCustomInput.Destroy;
|
||
begin
|
||
inherited Destroy;
|
||
end;
|
||
|
||
procedure TACSThread.Execute;
|
||
var
|
||
DoneThread : TACSVerySmallThread;
|
||
ParentComponent : TACSCustomOutput;
|
||
Res : Boolean;
|
||
begin
|
||
ParentComponent := TACSCustomOutput(Parent);
|
||
while not Terminating do
|
||
// if not bSuspend then
|
||
begin
|
||
EnterCriticalSection(CS);
|
||
if Delay > 5 then sleep(Delay);
|
||
try
|
||
if ParentComponent.Progress <> ParentComponent.CurProgr then
|
||
begin
|
||
ParentComponent.CurProgr := ParentComponent.Progress;
|
||
if Assigned(ParentComponent.FOnProgress) then Synchronize(CallOnProgress);
|
||
end;
|
||
Res := ParentComponent.DoOutput(Stop);
|
||
if Stop or (not Res) then
|
||
begin
|
||
Stop := False;
|
||
ParentComponent.WhenDone;
|
||
// This ensures that OnDone event is called outside this thread
|
||
DoneThread := TACSVerySmallThread.Create(True);
|
||
DoneThread.Sender := ParentComponent;
|
||
DoneThread.FOnDone := ParentComponent.FOnDone;
|
||
DoneThread.FreeOnTerminate := True;
|
||
Stop := True;
|
||
Res := True;
|
||
DoneThread.Resume;
|
||
//Dont Suspend the thread maybe Stop has been set to false during OnDone
|
||
//check this first
|
||
if (Stop or (not Res)) then
|
||
Self.Suspend;
|
||
end;
|
||
except
|
||
on E : Exception do
|
||
begin
|
||
Stop := False;
|
||
HandleException(E);
|
||
// if bSuspend then
|
||
Self.Suspend;
|
||
end;
|
||
end;
|
||
LeaveCriticalSection(CS);
|
||
end;
|
||
//DoOutput(True); // Why I'm doing this? I don't remember :-)
|
||
Terminating := False;
|
||
end;
|
||
|
||
constructor TACSCustomOutput.Create(AOwner: TComponent);
|
||
begin
|
||
inherited Create(AOwner);
|
||
Thread := TACSThread.Create;
|
||
Thread.Parent := Self;
|
||
// Thread.DoOutput := Self.DoOutput;
|
||
// Thread.FOnDone := Self.WhenDone;
|
||
Thread.FreeOnTerminate := True;
|
||
Thread.HandleException := HandleThreadException;
|
||
{$IFDEF MSWINDOWS}
|
||
SetSuspend(True);
|
||
InitializeCriticalSection(Thread.CS);
|
||
{$ENDIF}
|
||
end;
|
||
|
||
destructor TACSCustomOutput.Destroy;
|
||
begin
|
||
// if Thread.Suspended then
|
||
// Thread.Resume;
|
||
if not Thread.Suspended then
|
||
Thread.Terminating := True;
|
||
while Thread.Terminating do;
|
||
{$IFDEF MSWINDOWS}
|
||
DeleteCriticalSection(Thread.CS);
|
||
{$ENDIF}
|
||
inherited Destroy;
|
||
end;
|
||
|
||
procedure TACSCustomOutput.WhenDone;
|
||
begin
|
||
if not Busy then Exit;
|
||
CanOutput := False;
|
||
Done;
|
||
Busy := False;
|
||
end;
|
||
|
||
procedure TACSCustomOutput.Run;
|
||
begin
|
||
if Busy then raise EACSException.Create(strBusy);
|
||
if not Assigned(FInput) then raise EACSException.Create(strInputnotAssigned);
|
||
InputLock := False;
|
||
if not Thread.Suspended then Thread.Suspend;
|
||
try
|
||
Prepare;
|
||
Busy := True;
|
||
Thread.Stop := False;
|
||
CanOutput := True;
|
||
if Thread.Suspended then Thread.Resume;
|
||
except
|
||
on E : Exception do HandleThreadException(E);
|
||
end;
|
||
end;
|
||
|
||
procedure TACSCustomOutput.Stop;
|
||
begin
|
||
Thread.Stop := True;
|
||
end;
|
||
|
||
function TACSCustomOutput.GetStatus : TACSOutputStatus;
|
||
begin
|
||
if Busy then
|
||
begin
|
||
if Self.Thread.Suspended then Result := tosPaused
|
||
else Result := tosPlaying;
|
||
end else Result := tosIdle;
|
||
end;
|
||
|
||
procedure TACSCustomOutput.SetPriority(Priority : TTPriority);
|
||
begin
|
||
Thread.Priority := Priority;
|
||
end;
|
||
|
||
function TACSCustomOutput.GetPriority:TTPriority;
|
||
begin
|
||
Result := Thread.Priority;
|
||
end;
|
||
|
||
procedure TACSCustomOutput.SetInput(vInput : TACSCustomInput);
|
||
var
|
||
OldInput, NewInput : TACSCustomInput;
|
||
begin
|
||
if Busy then
|
||
begin
|
||
NewInput := vInput;
|
||
NewInput.Init;
|
||
OldInput := FInput;
|
||
while InputLock do;
|
||
InputLock := True;
|
||
FInput := NewInput;
|
||
InputLock := False;
|
||
OldInput.Flush;
|
||
end else
|
||
FInput := vInput;
|
||
end;
|
||
|
||
function TACSCustomOutput.GetProgress : real;
|
||
begin
|
||
if not Assigned(Finput) then
|
||
begin
|
||
Result := 0;
|
||
Exit;
|
||
end;
|
||
case Finput.Size of
|
||
0: Result := 0;
|
||
-1: Result := -1;
|
||
else Result := (FInput.Position/FInput.Size)*100;
|
||
end;
|
||
end;
|
||
|
||
procedure TACSCustomOutput.Pause;
|
||
begin
|
||
Thread.DoPause;
|
||
end;
|
||
|
||
procedure TACSCustomOutput.Resume;
|
||
begin
|
||
Thread.DoResume;
|
||
end;
|
||
|
||
function TACSCustomOutput.GetSuspend : Boolean;
|
||
begin
|
||
Result := Thread.Suspended;
|
||
end;
|
||
|
||
procedure TACSCustomOutput.SetSuspend(v : Boolean);
|
||
begin
|
||
Thread.Suspended:=v;
|
||
end;
|
||
|
||
constructor TACSStreamedInput.Create;
|
||
begin
|
||
inherited Create(AOwner);
|
||
FSeekable := True;
|
||
end;
|
||
|
||
function TACSCustomFileIn.GetBPS : Integer;
|
||
begin
|
||
if FSeekable then
|
||
begin
|
||
OpenFile;
|
||
Result := FBPS;
|
||
CloseFile;
|
||
end else Result := FBPS;
|
||
end;
|
||
|
||
function TACSCustomFileIn.GetCh : Integer;
|
||
begin
|
||
if FSeekable then
|
||
begin
|
||
OpenFile;
|
||
Result := FChan;
|
||
CloseFile;
|
||
end else Result := FChan;
|
||
end;
|
||
|
||
function TACSCustomFileIn.GetSR : Integer;
|
||
begin
|
||
if FSeekable then
|
||
begin
|
||
OpenFile;
|
||
Result := FSR;
|
||
CloseFile;
|
||
end else Result := FSR;
|
||
end;
|
||
|
||
function TACSCustomFileIn.GetTime : Integer;
|
||
begin
|
||
if FSeekable then
|
||
begin
|
||
OpenFile;
|
||
Result := FTime;
|
||
CloseFile;
|
||
end else Result := FTime;
|
||
end;
|
||
|
||
function TACSCustomFileIn.GetValid : Boolean;
|
||
begin
|
||
if (not FStreamAssigned) and (FileName = '') then
|
||
begin
|
||
Result := False;
|
||
end else
|
||
if FSeekable then
|
||
begin
|
||
OpenFile;
|
||
Result := FValid;
|
||
CloseFile;
|
||
end else Result := FValid;
|
||
end;
|
||
|
||
procedure TACSCustomFileIn.Init;
|
||
begin
|
||
if Busy then raise EACSException.Create(strBusy);
|
||
if not FStreamAssigned then
|
||
if FFileName = '' then raise EACSException.Create(strFilenamenotassigned);
|
||
OpenFile;
|
||
if StartSample <> 0 then Seek(StartSample);
|
||
if (StartSample <> 0) or (FEndSample <> -1) then
|
||
begin
|
||
FSize := FEndSample - FStartSample;
|
||
if FEndSample = -1 then FSize := FSize + FTotalSamples + 1;
|
||
FSize := FSize*(BitsPerSample shr 3)*FChan;
|
||
end;
|
||
FBusy := True;
|
||
BufStart := 1;
|
||
BufEnd := 0;
|
||
FPosition := 0;
|
||
end;
|
||
|
||
procedure TACSCustomFileIn.Flush;
|
||
begin
|
||
CloseFile;
|
||
FBusy := False;
|
||
end;
|
||
|
||
procedure TACSCustomFileIn.Jump(Offs : real);
|
||
begin
|
||
FOffset := Offs;
|
||
end;
|
||
|
||
function TACSCustomOutput.GetTE : Integer;
|
||
begin
|
||
if not Assigned(FInput) then
|
||
Result := 0
|
||
else
|
||
Result := Round(FInput.Position/((FInput.BitsPerSample shr 3) *FInput.Channels*FInput.SampleRate));
|
||
end;
|
||
|
||
function TACSCustomOutput.GetDelay : Integer;
|
||
begin
|
||
if Assigned(Thread) then Result := Thread.Delay
|
||
else Result := 0;
|
||
end;
|
||
|
||
procedure TACSCustomOutput.SetDelay(Value : Integer);
|
||
begin
|
||
if Assigned(Thread) then
|
||
if Value <= 100 then Thread.Delay := Value;
|
||
end;
|
||
|
||
function TACSCustomInput.GetBPS: Integer;
|
||
begin
|
||
Result := -1;
|
||
end;
|
||
|
||
function TACSCustomInput.GetCh: Integer;
|
||
begin
|
||
Result := -1;
|
||
end;
|
||
|
||
function TACSCustomInput.GetSR: Integer;
|
||
begin
|
||
Result := -1;
|
||
end;
|
||
|
||
function TACSCustomInput.GetTotalTime : real;
|
||
begin
|
||
Result := 0; // Default result for the streams.
|
||
end;
|
||
|
||
procedure TACSCustomInput.SetBufferSize(S: Integer);
|
||
begin
|
||
if Busy then
|
||
raise EACSException.Create(strBusy);
|
||
setlength(FBuffer,S);
|
||
end;
|
||
|
||
function TACSCustomInput.GetBufferSize: Integer;
|
||
begin
|
||
Result := length(FBuffer);
|
||
end;
|
||
|
||
function TACSCustomFileIn.GetTotalTime : real;
|
||
begin
|
||
OpenFile;
|
||
Result := 0;
|
||
if (SampleRate = 0) or (Channels = 0) or (BitsPerSample = 0) then Exit;
|
||
Result := Size/(SampleRate*Channels*(BitsPerSample shr 3));
|
||
CloseFile;
|
||
end;
|
||
|
||
procedure TACSStreamedInput.SetStream;
|
||
begin
|
||
FStream := aStream;
|
||
if FStream <> nil then FStreamAssigned := True
|
||
else FStreamAssigned := False;
|
||
end;
|
||
|
||
procedure TACSStreamedOutput.SetStream;
|
||
begin
|
||
FStream := aStream;
|
||
if FStream <> nil then FStreamAssigned := True
|
||
else FStreamAssigned := False;
|
||
end;
|
||
|
||
procedure TACSCustomOutput.Notification(AComponent: TComponent; Operation: TOperation);
|
||
begin
|
||
// Remove the following two lines if they cause troubles in your IDE
|
||
if (AComponent = FInput) and (Operation = opRemove )
|
||
then Input := nil;
|
||
inherited Notification(AComponent, Operation);
|
||
end;
|
||
|
||
procedure TACSCustomInput.Reset;
|
||
begin
|
||
try
|
||
Flush;
|
||
except
|
||
end;
|
||
FBusy := False;
|
||
end;
|
||
|
||
procedure TACSCustomOutput.HandleThreadException(E : Exception);
|
||
var
|
||
Conv : TACSCustomConverter;
|
||
begin
|
||
InputLock := False;
|
||
if Status <> tosIdle then
|
||
begin
|
||
try
|
||
if FInput is TACSCustomConverter then
|
||
begin
|
||
Conv := FInput as TACSCustomConverter;
|
||
Conv.UnlockInput;
|
||
end;
|
||
except
|
||
end;
|
||
try
|
||
Done;
|
||
except
|
||
end;
|
||
end;
|
||
CanOutput := False;
|
||
Busy := False;
|
||
if Assigned(FOnThreadException) then FOnThreadException(Self, E);
|
||
end;
|
||
|
||
procedure TACSCustomOutput.SetBufferSize(S : Integer);
|
||
begin
|
||
if Busy then
|
||
raise EACSException.Create(strBusy);
|
||
FBufferSize := S;
|
||
end;
|
||
|
||
|
||
procedure TACSCustomFileIn.Reset;
|
||
begin
|
||
inherited Reset;
|
||
FOpened := 0;
|
||
end;
|
||
|
||
|
||
constructor TACSCustomFileOut.Create;
|
||
begin
|
||
inherited Create(AOwner);
|
||
{$IFDEF LINUX}
|
||
FAccessMask := $1B6; // rw-rw-rw-
|
||
{$ENDIF}
|
||
end;
|
||
|
||
procedure TACSCustomFileOut.SetFileMode;
|
||
begin
|
||
FFileMode := foRewrite;
|
||
end;
|
||
|
||
procedure TACSCustomConverter.Notification;
|
||
begin
|
||
// Remove the following two lines if they cause troubles in your IDE
|
||
if (AComponent = FInput) and (Operation = opRemove )
|
||
then Input := nil;
|
||
inherited Notification(AComponent, Operation);
|
||
end;
|
||
|
||
procedure TACSCustomConverter.SetInput(aInput : TACSCustomInput);
|
||
var
|
||
OldInput, NewInput : TACSCustomInput;
|
||
begin
|
||
if aInput = Self then Exit;
|
||
if Busy then
|
||
begin
|
||
NewInput := aInput;
|
||
NewInput.Init;
|
||
OldInput := FInput;
|
||
while InputLock do;
|
||
InputLock := True;
|
||
FInput := NewInput;
|
||
InputLock := False;
|
||
OldInput.Flush;
|
||
end else
|
||
FInput := aInput;
|
||
end;
|
||
|
||
procedure TACSCustomConverter.UnlockInput;
|
||
var
|
||
Conv : TACSCustomConverter;
|
||
begin
|
||
InputLock := False;
|
||
if Assigned(FInput) then
|
||
if FInput is TACSCustomConverter then
|
||
begin
|
||
Conv := FInput as TACSCustomConverter;
|
||
Conv.UnlockInput;
|
||
end;
|
||
end;
|
||
|
||
function TACSCustomFileIn.SetStartTime(Minutes, Seconds : Integer) : Boolean;
|
||
var
|
||
Sample : Integer;
|
||
begin
|
||
Result := False;
|
||
if not FSeekable then Exit;
|
||
OpenFile;
|
||
CloseFile;
|
||
Sample := (Minutes*60+Seconds)*FSR;
|
||
if Sample > FTotalSamples then Exit;
|
||
FStartSample := Sample;
|
||
Result := True;
|
||
end;
|
||
|
||
function TACSCustomFileIn.SetEndTime(Minutes, Seconds : Integer) : Boolean;
|
||
var
|
||
Sample : Integer;
|
||
begin
|
||
Result := False;
|
||
if not FSeekable then Exit;
|
||
OpenFile;
|
||
CloseFile;
|
||
Sample := (Minutes*60+Seconds)*FSR;
|
||
if Sample > FTotalSamples then Exit;
|
||
FEndSample := Sample;
|
||
Result := True;
|
||
end;
|
||
|
||
constructor TACSCustomFileIn.Create(AOwner: TComponent);
|
||
begin
|
||
inherited Create(AOwner);
|
||
FStartSample := 0;
|
||
FEndSample := -1;
|
||
end;
|
||
|
||
procedure TACSVerySmallThread.Execute;
|
||
begin
|
||
Synchronize(CallOnDone);
|
||
end;
|
||
|
||
procedure TACSVerySmallThread.CallOnDone;
|
||
begin
|
||
if Assigned(FOnDone) then FOnDone(Sender);
|
||
end;
|
||
|
||
procedure TACSThread.CallOnProgress;
|
||
var
|
||
ParentComponent : TACSCustomOutput;
|
||
begin
|
||
ParentComponent := TACSCustomOutput(Parent);
|
||
ParentComponent.FOnProgress(ParentComponent);
|
||
end;
|
||
|
||
constructor TACSThread.Create;
|
||
begin
|
||
inherited Create(True);
|
||
end;
|
||
|
||
procedure TACSThread.DoPause;
|
||
begin
|
||
If not Suspended then Suspended := True;
|
||
end;
|
||
|
||
procedure TACSThread.DoResume;
|
||
begin
|
||
If Suspended then Suspended := False;
|
||
end;
|
||
|
||
{$IFDEF MSWINDOWS}
|
||
procedure TACSCustomOutput.Abort;
|
||
begin
|
||
TerminateThread(Thread.Handle, 0);
|
||
WhenDone;
|
||
end;
|
||
{$ENDIF}
|
||
|
||
constructor TACSBufferStream.Create;
|
||
begin
|
||
inherited Create;
|
||
{$IFDEF MSWINDOWS}
|
||
BlockEventName := 'Block' + IntToStr(LongWord(Self)); // this way we guarantee that the name is unique
|
||
BlockEvent := CreateEvent(nil, True, False, @BlockEventName[1]);
|
||
InitializeCriticalSection(CS);
|
||
{$ENDIF}
|
||
end;
|
||
|
||
destructor TACSBufferStream.Destroy;
|
||
begin
|
||
{$IFDEF MSWINDOWS}
|
||
DeleteCriticalSection(CS);
|
||
CloseHandle(BlockEvent);
|
||
{$ENDIF}
|
||
inherited Destroy;
|
||
end;
|
||
|
||
function TACSBufferStream.Write(const Buffer; Count: Longint): Longint;
|
||
var
|
||
addr : Pointer;
|
||
S1, S2 : Integer;
|
||
begin
|
||
if WriteCur >= ReadCur then
|
||
begin
|
||
{$IFDEF MSWINDOWS}
|
||
EnterCriticalSection(CS);
|
||
{$ENDIF}
|
||
S1 := STREAM_BUFFER_SIZE-WriteCur;
|
||
if (Count <= S1) then
|
||
begin
|
||
Move(Buffer, Buff[WriteCur], Count);
|
||
Inc(WriteCur, Count);
|
||
Inc(FBytesInBuffer, Count);
|
||
Result := Count;
|
||
end else
|
||
begin
|
||
Move(Buffer, Buff[WriteCur], S1);
|
||
addr := Pointer(Integer(@Buffer) + S1);
|
||
S2 := Count - S1;
|
||
if S2 > ReadCur then
|
||
S2 := ReadCur;
|
||
Move(addr^, Buff[0], S2);
|
||
WriteCur := S2;
|
||
Inc(FBytesInBuffer, S1+S2);
|
||
Result := S1+S2;
|
||
end;
|
||
end else
|
||
begin
|
||
S2 := ReadCur-WriteCur;
|
||
if Count < S2 then
|
||
S2 := Count;
|
||
Move(Buffer, Buff[WriteCur], S2);
|
||
Inc(WriteCur, S2);
|
||
Inc(FBytesInBuffer, S2);
|
||
Result := S2;
|
||
end;
|
||
{$IFDEF MSWINDOWS}
|
||
if Result <> 0 then
|
||
if FBufferMode = bmBlock then PulseEvent(BlockEvent);
|
||
LeaveCriticalSection(CS);
|
||
{$ENDIF}
|
||
end;
|
||
|
||
function TACSBufferStream.Read(var Buffer; Count : Integer) : Integer;
|
||
var
|
||
addr : Pointer;
|
||
S1, S2 : Integer;
|
||
begin
|
||
{$IFDEF MSWINDOWS}
|
||
if ReadCur = WriteCur then
|
||
if FBufferMode = bmBlock then
|
||
begin
|
||
WaitForSingleObject(BlockEvent, INFINITE);
|
||
ResetEvent(BlockEvent);
|
||
end;
|
||
EnterCriticalSection(CS);
|
||
{$ENDIF}
|
||
if ReadCur <= WriteCur then
|
||
begin
|
||
S2 := WriteCur - ReadCur;
|
||
if Count < S2 then S2 := Count;
|
||
Move(Buff[ReadCur], Buffer, S2);
|
||
Inc(ReadCur, S2);
|
||
Dec(FBytesInBuffer, S2);
|
||
Result := S2;
|
||
end else
|
||
begin
|
||
S1 := STREAM_BUFFER_SIZE-ReadCur;
|
||
if Count <= S1 then
|
||
begin
|
||
Move(Buff[ReadCur], Buffer, Count);
|
||
Inc(ReadCur, Count);
|
||
Dec(FBytesInBuffer, Count);
|
||
Result := Count;
|
||
end else
|
||
begin
|
||
S2 := WriteCur;
|
||
if Count - S1 < S2 then S2 := Count;
|
||
Move(Buff[ReadCur], Buffer, S1);
|
||
addr := Pointer(Integer(@Buffer) + S1);
|
||
Move(Buff[0], addr^, S2);
|
||
ReadCur := S2;
|
||
Dec(FBytesInBuffer, S1+S2);
|
||
Result := S1+S2;
|
||
end;
|
||
end;
|
||
Inc(FBytesRead, Result);
|
||
{$IFDEF MSWINDOWS}
|
||
LeaveCriticalSection(CS);
|
||
{$ENDIF}
|
||
end;
|
||
|
||
procedure TACSBufferStream.Reset;
|
||
begin
|
||
{$IFDEF MSWINDOWS}
|
||
EnterCriticalSection(CS);
|
||
{$ENDIF}
|
||
ReadCur := 0;
|
||
WriteCur := 0;
|
||
FBytesRead := 0;
|
||
{$IFDEF MSWINDOWS}
|
||
SetEvent(BlockEvent);
|
||
LeaveCriticalSection(CS);
|
||
{$ENDIF}
|
||
end;
|
||
|
||
// the following property is implemented 'cause tstreams position property uses them
|
||
|
||
function TACSBufferStream.Seek(Offset: Longint; Origin: Word) : Integer;
|
||
begin
|
||
if (Offset = 0) and (Origin = 0) then
|
||
Result := FBytesRead
|
||
else
|
||
raise EACSException.Create(strSeeknotimplemented);
|
||
end;
|
||
|
||
function TACSBufferStream.Seek(const Offset: Int64; Origin: TSeekOrigin): Int64;
|
||
begin
|
||
if (Offset = 0) and (Origin = soCurrent) then
|
||
Result := FBytesRead
|
||
else
|
||
raise EACSException.Create(strSeeknotimplemented);
|
||
end;
|
||
|
||
{ TACSFileInfo }
|
||
|
||
function TACSFileInfo.GetStringTag(Idx : string): TACSFileTag;
|
||
begin
|
||
end;
|
||
|
||
procedure TACSFileInfo.ReadFromFile;
|
||
begin
|
||
end;
|
||
|
||
procedure TACSFileInfo.SetStringTag(Idx : string; const AValue: TACSFileTag);
|
||
begin
|
||
end;
|
||
|
||
procedure TACSFileInfo.SaveToFile;
|
||
begin
|
||
end;
|
||
|
||
{ TACSFileTag }
|
||
|
||
function TACSFileTag.GetName: string;
|
||
begin
|
||
end;
|
||
|
||
function TACSFileTag.AsString: string;
|
||
begin
|
||
end;
|
||
|
||
function TACSFileTag.Streamable: Boolean;
|
||
begin
|
||
Result := False;
|
||
end;
|
||
|
||
procedure TACSFileTag.SaveToStream(Stream: TStream);
|
||
begin
|
||
end;
|
||
|
||
procedure TACSFileTag.LoadFromStream(Stream: TStream);
|
||
begin
|
||
end;
|
||
|
||
end.
|
||
|
||
|