300 lines
7.3 KiB
ObjectPascal
300 lines
7.3 KiB
ObjectPascal
(*
|
|
this file is a part of audio components suite,
|
|
copyright (c) 2005 ross levis. all rights reserved.
|
|
see the license file for more details.
|
|
|
|
TMultiMixer provides for an unlimited number of inputs (channels)
|
|
to be mixed into one audio buffer. Only supports 44100/16/2
|
|
|
|
TMultiMixer
|
|
- property TotalChannels: Integer; // get/set the number of channels
|
|
- property Channel[Index: Integer]: TChannel; default
|
|
TChannel
|
|
- procedure Preload; // runs Input.Init to make starting faster (optional)
|
|
- procedure Start; // Start channel
|
|
- procedure Stop; // Stop channel
|
|
- property Input: TACSInput
|
|
- property Volume: Word; // 0 = silent, 32768 = 100%
|
|
|
|
eg.
|
|
MultiMixer.TotalChannels := 1;
|
|
MultiMixer[0].Input := VorbisIn1;
|
|
AudioOut1.Run;
|
|
MultiMixer.TotalChannels := 2; // Channels can be added or removed while playing
|
|
MultiMixer[1].Input := WAVEIn1;
|
|
MultiMixer[1].Volume := 16384; // 50% volume
|
|
MultiMixer[1].Start; // while at least 1 mixer is playing, others
|
|
MultiMixer[0].Stop; // can be started and stopped individually.
|
|
|
|
*)
|
|
|
|
{
|
|
$Log: acs_multimix.pas,v $
|
|
Revision 1.3 2006/07/04 17:12:45 z0m3ie
|
|
ACS 2.4 alt wiederhergestellt (unterschiedliche Sampleformate ...)
|
|
|
|
Revision 1.1 2005/12/19 18:34:35 z0m3ie
|
|
*** empty log message ***
|
|
|
|
Revision 1.3 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.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.1 2005/08/25 21:02:31 z0m3ie
|
|
TMultiMixer by Ross Levis added
|
|
|
|
}
|
|
|
|
{$hints off}
|
|
unit acs_multimix;
|
|
|
|
{$ifdef fpc}
|
|
{$mode delphi}
|
|
{$endif}
|
|
|
|
interface
|
|
|
|
uses
|
|
Classes, SysUtils, ACS_Types, ACS_Classes, ACS_Strings;
|
|
|
|
const
|
|
BUF_SIZE = 8820;
|
|
|
|
type
|
|
TACSMultiMixer = class;
|
|
|
|
TACSChannel = class
|
|
private
|
|
FOwner: TACSMultiMixer;
|
|
FInput: TACSCustomInput;
|
|
FVolume: Word;
|
|
EndOfInput: Boolean;
|
|
Preloaded: Boolean;
|
|
InBuf: array[1..BUF_SIZE] of Byte;
|
|
public
|
|
constructor Create(AOwner: TACSMultiMixer); virtual;
|
|
destructor Destroy; override;
|
|
procedure Preload;
|
|
procedure Start;
|
|
procedure Stop;
|
|
property Input: TACSCustomInput read FInput write FInput;
|
|
property Volume: Word read FVolume write FVolume;
|
|
end;
|
|
|
|
TACSMultiMixer = class(TACSCustomInput)
|
|
private
|
|
FChannel: array of TACSChannel;
|
|
FTotalChannels: Integer;
|
|
OutBuf: array[1..BUF_SIZE] of Byte;
|
|
Buisy : Boolean;
|
|
FLock: Boolean;
|
|
function GetChannel(Index: Integer): TACSChannel;
|
|
procedure SetTotalChannels(Num: Integer);
|
|
function GetBPS : Integer; override;
|
|
function GetCh : Integer; override;
|
|
function GetSR : Integer; override;
|
|
public
|
|
constructor Create(AOwner: TComponent); override;
|
|
destructor Destroy; override;
|
|
property Channel[Index: Integer]: TACSChannel read GetChannel; default;
|
|
function GetData(Buffer: Pointer; BufferSize: Integer): Integer; override;
|
|
procedure Init; override;
|
|
procedure Flush; override;
|
|
published
|
|
property TotalChannels: Integer read FTotalChannels write SetTotalChannels;
|
|
end;
|
|
|
|
implementation
|
|
|
|
// TChannel
|
|
|
|
constructor TACSChannel.Create;
|
|
begin
|
|
inherited Create;
|
|
FOwner := AOwner;
|
|
FVolume := 32768;
|
|
EndOfInput := True;
|
|
Preloaded := False;
|
|
end;
|
|
|
|
destructor TACSChannel.Destroy;
|
|
begin
|
|
inherited Destroy;
|
|
end;
|
|
|
|
procedure TACSChannel.Preload;
|
|
begin
|
|
if EndOfInput and not Preloaded and Assigned(FInput) then
|
|
begin
|
|
FInput.Init;
|
|
Preloaded := True;
|
|
end;
|
|
end;
|
|
|
|
procedure TACSChannel.Start;
|
|
begin
|
|
if FOwner.Buisy and EndOfInput and Assigned(FInput) then
|
|
begin
|
|
if not Preloaded then Preload;
|
|
EndOfInput := False;
|
|
end;
|
|
end;
|
|
|
|
procedure TACSChannel.Stop;
|
|
begin
|
|
if not EndOfInput then
|
|
begin
|
|
EndOfInput := True;
|
|
Preloaded := False;
|
|
while FOwner.Flock do;
|
|
FOwner.FLock := True;
|
|
FInput.Flush;
|
|
FOwner.Flock := False;
|
|
end;
|
|
end;
|
|
|
|
// TACSMultiMixer
|
|
|
|
constructor TACSMultiMixer.Create;
|
|
begin
|
|
inherited Create(AOwner);
|
|
FLock := False;
|
|
end;
|
|
|
|
destructor TACSMultiMixer.Destroy;
|
|
begin
|
|
SetTotalChannels(0); // free channels
|
|
inherited Destroy;
|
|
end;
|
|
|
|
procedure TACSMultiMixer.SetTotalChannels(Num: Integer);
|
|
var
|
|
chan: Integer;
|
|
begin
|
|
if (Num >= 0) and (Num <> FTotalChannels) then
|
|
begin
|
|
while Flock do;
|
|
FLock := True;
|
|
if Num < FTotalChannels then // remove channels
|
|
begin
|
|
for chan := FTotalChannels-1 downto Num do
|
|
with FChannel[chan] do
|
|
begin
|
|
if not EndOfInput then FInput.Flush;
|
|
Free;
|
|
end;
|
|
SetLength(FChannel,Num);
|
|
end
|
|
else begin // add channels
|
|
SetLength(FChannel,Num);
|
|
for chan := FTotalChannels to Num-1 do
|
|
FChannel[chan] := TACSChannel.Create(Self);
|
|
end;
|
|
FTotalChannels := Num;
|
|
FLock := False;
|
|
end;
|
|
end;
|
|
|
|
function TACSMultiMixer.GetBPS;
|
|
begin
|
|
Result := 16;
|
|
end;
|
|
|
|
function TACSMultiMixer.GetCh;
|
|
begin
|
|
Result:= 2;
|
|
end;
|
|
|
|
function TACSMultiMixer.GetSR;
|
|
begin
|
|
Result := 44100;
|
|
end;
|
|
|
|
procedure TACSMultiMixer.Init;
|
|
var
|
|
chan: Integer;
|
|
begin
|
|
Buisy := True;
|
|
FPosition := 0;
|
|
for chan := 0 to FTotalChannels-1 do
|
|
FChannel[chan].Start;
|
|
FSize := 0;
|
|
FLock := False;
|
|
end;
|
|
|
|
procedure TACSMultiMixer.Flush;
|
|
var
|
|
chan: Integer;
|
|
begin
|
|
for chan := 0 to FTotalChannels-1 do
|
|
with FChannel[chan] do
|
|
begin
|
|
if Assigned(FInput) then FInput.Flush;
|
|
EndOfInput := True;
|
|
Preloaded := False;
|
|
end;
|
|
Buisy := False;
|
|
end;
|
|
|
|
function TACSMultiMixer.GetData;
|
|
var
|
|
i, chan, ReadSize, BufSize: Integer;
|
|
InBuf16, OutBuf16: PACSBuffer16;
|
|
begin
|
|
if not Buisy then raise EACSException.Create(strStreamnotopen);
|
|
begin
|
|
while Flock do sleep(0);
|
|
Flock := True;
|
|
BufSize := 0;
|
|
if BufferSize > BUF_SIZE then BufferSize := BUF_SIZE;
|
|
for chan := 0 to FTotalChannels-1 do
|
|
with FChannel[chan] do
|
|
if not EndOfInput then
|
|
begin
|
|
ReadSize := FInput.GetData(@InBuf[1], BufferSize);
|
|
while (ReadSize < BufferSize) and (ReadSize <> 0) do
|
|
begin
|
|
Result := FInput.GetData(@InBuf[ReadSize+1], BufferSize-ReadSize);
|
|
Inc(ReadSize, Result);
|
|
end;
|
|
FillChar(InBuf[ReadSize+1], BufferSize-ReadSize, 0); // zero rest of buffer
|
|
if ReadSize = 0 then EndOfInput := True
|
|
else if ReadSize > BufSize then BufSize := ReadSize;
|
|
end;
|
|
if BufSize = 0 then
|
|
begin
|
|
Flock := False;
|
|
Result := 0;
|
|
Exit;
|
|
end;
|
|
// mix
|
|
FillChar(OutBuf[1], BufferSize, 0);
|
|
OutBuf16 := @OutBuf;
|
|
for chan := 0 to FTotalChannels-1 do
|
|
with FChannel[chan] do
|
|
if not EndOfInput then
|
|
begin
|
|
InBuf16 := @InBuf;
|
|
for i := 0 to (BufSize shr 1) - 1 do
|
|
OutBuf16[i] := OutBuf16[i] + (InBuf16[i] * FVolume div 32768);
|
|
end;
|
|
Flock := False;
|
|
end;
|
|
Result := BufSize;
|
|
Move(OutBuf[1], Buffer^, Result);
|
|
Inc(FPosition, Result);
|
|
end;
|
|
|
|
function TACSMultiMixer.GetChannel(Index: Integer): TACSChannel;
|
|
begin
|
|
Result := FChannel[Index];
|
|
end;
|
|
|
|
end.
|