414 lines
12 KiB
ObjectPascal
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.
|