(* 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.