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.