414 lines
12 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_audiomix.pas,v $
Revision 1.6 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.4 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.3 2005/09/15 20:59:37 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.3 2005/08/23 19:45:51 z0m3ie
changed to Version 2.31
Revision 1.2 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))
}
{$hints off}
unit acs_audiomix;
{$ifdef fpc}
{$mode delphi}
{$endif}
interface
uses
Classes, SysUtils, ACS_Types, ACS_Classes, ACS_Strings;
const
BUF_SIZE = $100000;
type
TACSAudioMixerMode = (amMix, amConcatenate, amRTMix, amCustomMix);
TACSAudioMixer = class(TACSCustomInput)
private
FInput1, FInput2 : TACSCustomInput;
BufStart, BufEnd : Integer;
ByteCount : Cardinal; // add by leozhang
FVolume1, FVolume2 : Byte;
EndOfInput1, EndOfInput2 : Boolean;
InBuf1, InBuf2 : array[1..BUF_SIZE] of Byte;
Buisy : Boolean;
FMode : TACSAudioMixerMode;
FInput2Start: Cardinal;
FLock : Boolean;
FFgPlaying : Boolean;
function GetBPS : Integer; override;
function GetCh : Integer; override;
function GetSR : Integer; override;
procedure SetInput1(aInput : TACSCustomInput);
procedure SetInput2(aInput : TACSCustomInput);
public
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
function GetData(Buffer : Pointer; BufferSize : Integer): Integer; override;
procedure Init; override;
procedure Flush; override;
property FgPlaying : Boolean read FFgPlaying;
published
property Input1 : TACSCustomInput read FInput1 write SetInput1;
property Input2 : TACSCustomInput read FInput2 write SetInput2;
property Mode : TACSAudioMixerMode read FMode write FMode;
property Input2Start :Cardinal read FInput2Start write FInput2Start;
property Volume1 : Byte read FVolume1 write FVolume1;
property Volume2 : Byte read FVolume2 write FVolume2;
end;
implementation
procedure MixChannels16(Buf1, Buf2 : PACSBuffer16; Vol1, Vol2, InSize : Integer);
var // optimized by leozhang
i, tmp : Integer;
V1,V2: Real;
begin
V1 := Vol1 / 127;
V2 := Vol2 / 127;
for i := 0 to (Insize shr 1) - 1 do
begin
if Buf2[i] = 0 then
begin
Buf2[i] := Round(Buf1[i] * V1);
end else
if Buf1[i] = 0 then
begin
Buf2[i] := Round(Buf2[i] * V2);
end else
Buf2[i] := Round(Buf1[i] * V1) + Round(Buf2[i] * V2);
end;
end;
constructor TACSAudioMixer.Create;
begin
inherited Create(AOwner);
FVolume1 := 127;
FVolume2 := 127;
FInput2Start := 0;
end;
destructor TACSAudioMixer.Destroy;
begin
inherited Destroy;
end;
function TACSAudioMixer.GetBPS;
begin
if not Assigned(FInput1) then
raise EACSException.Create(strInputnotAssigned);
Result := FInput1.BitsPerSample;
end;
function TACSAudioMixer.GetCh;
begin
if not Assigned(FInput1) then
raise EACSException.Create(strInputnotAssigned);
Result:= FInput1.Channels;
end;
function TACSAudioMixer.GetSR;
begin
if not Assigned(FInput1) then
raise EACSException.Create(strInputnotAssigned);
Result := FInput1.SampleRate;
end;
procedure TACSAudioMixer.Init;
var
In2StartByte : Cardinal; // add by zhangl.
begin
Buisy := True;
FPosition := 0;
BufStart := 1;
BufEnd := 0;
EndOfInput1 := False;
EndOfInput2 := False;
if not Assigned(FInput1) then
raise EACSException.Create(strInputnotAssigned);
if FMode = amRTMix then
begin
FInput1.Init;
FSize := FInput1.Size;
if Assigned(FInput2) then
begin
FInput2.Init;
FFgPlaying := True;
end else EndOfInput2 := True;
FLock := False;
end else
begin
if not Assigned(FInput2) then
raise EACSException.Create(strInputnotAssigned);
FInput1.Init;
FInput2.Init;
case FMode of
amMix :
if FInput1.Size > FInput2.Size then FSize := FInput1.Size
else FSize := FInput2.Size;
amConcatenate :
FSize := FInput1.Size + FInput2.Size; //determine the size of the output stream in bytes
amCustomMix:
// add by leozhang
begin
In2StartByte := Round(Int((FInput2Start * FInput2.SampleRate) /1000) *
(FInput2.Channels) * ((FInput2.BitsPerSample) shr 3));
ByteCount := In2StartByte;
if Cardinal(FInput1.Size) > In2StartByte + FInput2.Size then
FSize := FInput1.Size
else
FSize := In2StartByte + FInput2.Size;
FLock := False;
end;
// leozhang
end;
end;
end;
procedure TACSAudioMixer.Flush;
begin
FInput1.Flush;
if (FMode <> amRTMix) or Assigned(FInput2) then
FInput2.Flush;
Buisy := False;
end;
function TACSAudioMixer.GetData;
var
l1, l2 : Integer;
InSize : Integer;
begin
if not Buisy then raise EACSException.Create(strStreamnotopen);
if BufStart > BufEnd then
begin
if EndOfInput1 and EndOfInput2 then
begin
Result := 0;
Exit;
end;
if (FMode = amRTMix) and EndOfInput1 then
begin
Result := 0;
Exit;
end;
BufStart := 1;
case Mode of
amMix :
begin
l1 := 0;
l2 := 0;
FillChar(InBuf1[1], BUF_SIZE, 0);
FillChar(InBuf2[1], BUF_SIZE, 0);
if not EndOfInput1 then
begin
l1 := FInput1.GetData(@InBuf1[1], BUF_SIZE);
InSize := l1;
while (InSize <> 0) and (l1 < BUF_SIZE) do
begin
InSize := FInput1.GetData(@InBuf1[l1+1], BUF_SIZE - l1);
Inc(l1, InSize);
end;
if InSize = 0 then EndOfInput1 := True;
end;
if not EndOfInput2 then
begin
l2 := FInput2.GetData(@InBuf2[1], BUF_SIZE);
InSize := l2;
while (InSize <> 0) and (l2 < BUF_SIZE) do
begin
InSize := FInput2.GetData(@InBuf2[l2+1], BUF_SIZE - l2);
Inc(l2, InSize);
end;
if InSize = 0 then EndOfInput2 := True;
end;
if (l1 = 0) and (l2 = 0) then
begin
Result := 0;
Exit;
end;
if l1 > l2 then BufEnd := l1 else BufEnd := l2;
MixChannels16(@InBuf1[1], @InBuf2[1], FVolume1, FVolume2, BufEnd);
end;
amConcatenate :
begin
if not EndOfInput1 then
begin
l1 := FInput1.GetData(@InBuf2[1], BUF_SIZE);
if l1 = 0 then EndOfInput1 := True
else BufEnd := l1;
end;
if EndOfInput1 then
begin
l2 := FInput2.GetData(@InBuf2[1], BUF_SIZE);
if l2 = 0 then
begin
Result := 0;
Exit;
end
else BufEnd := l2;
end;
end;
// add by leo.zhang
amCustomMix:
begin
l1 := 0;
l2 := 0;
FillChar(InBuf1[1], BUF_SIZE, 0);
FillChar(InBuf2[1], BUF_SIZE, 0);
if not EndOfInput1 then
begin
l1 := FInput1.GetData(@InBuf1[1], BUF_SIZE);
InSize := l1;
while (InSize <> 0) and (l1 < BUF_SIZE) do
begin
InSize := FInput1.GetData(@InBuf1[l1+1], BUF_SIZE - l1);
Inc(l1, InSize);
end;
if InSize = 0 then EndOfInput1 := True;
end;
if not (FLock or EndOfInput2) then
begin
FLock := True;
if ByteCount > BUF_SIZE then
begin
ByteCount := ByteCount - BUF_SIZE;
l2 := BUF_SIZE; InSize := l2;
end else
begin
l2 := FInput2.GetData(@InBuf2[ByteCount+1],BUF_SIZE - ByteCount);
InSize := l2;
if ByteCount <> 0 then
begin
Inc(l2,ByteCount);
InSize := l2;
ByteCount := 0;
end;
while (InSize <> 0) and (l2 < BUF_SIZE) do
begin
InSize := FInput2.GetData(@InBuf2[l2+1], BUF_SIZE - l2);
Inc(l2, InSize);
end;
end;
if InSize = 0 then EndOfInput2 := True;
FLock := False;
end;
if (l1 = 0) and (l2 = 0) then
begin
Result := 0;
Exit;
end;
if l1 > l2 then BufEnd := l1 else BufEnd := l2;
MixChannels16(@InBuf1[1], @InBuf2[1], FVolume1, FVolume2, BufEnd);
end;
// leo.zhang.
amRTMix :
begin
l1 := 0;
l2 := 0;
FillChar(InBuf1[1], BUF_SIZE, 0);
FillChar(InBuf2[1], BUF_SIZE, 0);
if not EndOfInput1 then
begin
l1 := FInput1.GetData(@InBuf1[1], BUF_SIZE);
InSize := l1;
while (InSize <> 0) and (l1 < BUF_SIZE) do
begin
InSize := FInput1.GetData(@InBuf1[l1+1], BUF_SIZE - l1);
Inc(l1, InSize);
end;
if InSize = 0 then EndOfInput1 := True;
end;
if not (FLock or EndOfInput2) then
begin
FLock := True;
l2 := FInput2.GetData(@InBuf2[1], BUF_SIZE);
InSize := l2;
while (InSize <> 0) and (l2 < BUF_SIZE) do
begin
InSize := FInput2.GetData(@InBuf2[l2+1], BUF_SIZE - l2);
Inc(l2, InSize);
end;
if InSize = 0 then
begin
EndOfInput2 := True;
FFGPlaying := False;
FInput2.Flush;
FInput2 := nil;
end;
FLock := False;
end;
if (l1 = 0) and (l2 = 0) then
begin
Result := 0;
Exit;
end;
if l1 > l2 then BufEnd := l1 else BufEnd := l2;
MixChannels16(@InBuf1[1], @InBuf2[1], FVolume1, FVolume2, BufEnd);
end;
end; // case end.
end; // endif.
if BufferSize < (BufEnd - BufStart + 1)
then Result := BufferSize
else Result := BufEnd - BufStart + 1;
Move(InBuf2[BufStart], Buffer^, Result);
Inc(BufStart, Result);
Inc(FPosition, Result);
end; // procedure end.
procedure TACSAudioMixer.SetInput1;
begin
if Buisy then
raise EACSException.Create(strBusy);
FInput1 := aInput;
end;
procedure TACSAudioMixer.SetInput2;
begin
if not Buisy then FInput2 := aInput
else
if FMode = amRTMix then
begin
if FFgPlaying then
begin
while Flock do;
FLock := True;
Input2.Flush;
end;
FInput2 := aInput;
Finput2.Init;
Flock := False;
FFgPlaying := True;
EndOfInput2 := False;
end else
raise EACSException.Create(strNotinFBMode);
end;
end.