From b5a738fa52c8b0f2212deb5febd2d7f0b8f6544f Mon Sep 17 00:00:00 2001
From: tobigun <tobigun@b956fd51-792f-4845-bead-9b4dfca2ff2c>
Date: Fri, 9 May 2008 19:19:28 +0000
Subject: - input-source selection works now (with bass or portaudio with
 portmixer) - audio-effects (DSP) interface for audio-playback plus a simple
 voice removal example (does not sound that good) - FFMpeg support for BASS -
 audio-clock for FFMpeg for GetPosition and synchronisation - more compatible
 seeking in FFMpeg - clean termination of the audio interfaces/streams
 (especially ffmpeg) - Audio output device enumeration (selection will be
 added later to the sounds option screen) - display of threshold and volume in
 the record-options screen - threshold and volume can be changed with the 'T'
 (threshold) and '+'/'-' (source volume) keys - added a FadeIn() method to the
 IAudioPlayback interface - some minor changes to the audio classes/screens -
 new base-class for audio-playback classes (used by bass, portaudio and sdl)

git-svn-id: svn://svn.code.sf.net/p/ultrastardx/svn/trunk@1078 b956fd51-792f-4845-bead-9b4dfca2ff2c
---
 Game/Code/Classes/UMusic.pas | 162 +++++++++++++++++++++++++++++++++++++------
 1 file changed, 142 insertions(+), 20 deletions(-)

(limited to 'Game/Code/Classes/UMusic.pas')

diff --git a/Game/Code/Classes/UMusic.pas b/Game/Code/Classes/UMusic.pas
index 4e0291cf..9977661f 100644
--- a/Game/Code/Classes/UMusic.pas
+++ b/Game/Code/Classes/UMusic.pas
@@ -80,6 +80,7 @@ type
   TFFTData  = array[0..(FFTSize div 2)-1] of Single;
 
 type
+  PPCMStereoSample = ^TPCMStereoSample;
   TPCMStereoSample = array[0..1] of SmallInt;
   TPCMData  = array[0..511] of TPCMStereoSample;
 
@@ -123,6 +124,18 @@ type
       constructor Create(Channels: byte; SampleRate: double; Format: TAudioSampleFormat);
   end;
 
+type
+  TSoundEffect = class
+    public
+      EngineData: Pointer; // can be used for engine-specific data
+      procedure Callback(Buffer: PChar; BufSize: integer); virtual; abstract;
+  end;
+
+  TVoiceRemoval = class(TSoundEffect)
+    public
+      procedure Callback(Buffer: PChar; BufSize: integer); override;
+  end;
+
 type
   TAudioProcessingStream = class
     public
@@ -131,40 +144,40 @@ type
 
   TAudioPlaybackStream = class(TAudioProcessingStream)
     protected
-      function GetLoop(): boolean;          virtual; abstract;
-      procedure SetLoop(Enabled: boolean);  virtual; abstract;
+      function GetPosition: real;           virtual; abstract;
+      procedure SetPosition(Time: real);    virtual; abstract;
       function GetLength(): real;           virtual; abstract;
       function GetStatus(): TStreamStatus;  virtual; abstract;
       function GetVolume(): integer;        virtual; abstract;
-      procedure SetVolume(volume: integer); virtual; abstract;
+      procedure SetVolume(Volume: integer); virtual; abstract;
+      function GetLoop(): boolean;          virtual; abstract;
+      procedure SetLoop(Enabled: boolean);  virtual; abstract;
     public
       procedure Play();                     virtual; abstract;
       procedure Pause();                    virtual; abstract;
       procedure Stop();                     virtual; abstract;
+      procedure FadeIn(Time: real; TargetVolume: integer);  virtual; abstract;
+
+      procedure GetFFTData(var data: TFFTData);          virtual; abstract;
+      function GetPCMData(var data: TPCMData): Cardinal; virtual; abstract;
+
+      procedure AddSoundEffect(effect: TSoundEffect);    virtual; abstract;
+      procedure RemoveSoundEffect(effect: TSoundEffect); virtual; abstract;
 
-      property Loop: boolean READ GetLoop WRITE SetLoop;
       property Length: real READ GetLength;
+      property Position: real READ GetPosition WRITE SetPosition;
       property Status: TStreamStatus READ GetStatus;
       property Volume: integer READ GetVolume WRITE SetVolume;
+      property Loop: boolean READ GetLoop WRITE SetLoop;
   end;
 
-  (*
-  TAudioMixerStream = class(TAudioProcessingStream)
-    procedure AddStream(stream: TAudioProcessingStream);
-    procedure RemoveStream(stream: TAudioProcessingStream);
-    procedure SetMasterVolume(volume: cardinal);
-    function GetMasterVolume(): cardinal;
-    procedure SetStreamVolume(stream: TAudioProcessingStream; volume: cardinal);
-    function GetStreamVolume(stream: TAudioProcessingStream): cardinal;
-  end;
-  *)
-
   TAudioDecodeStream = class(TAudioProcessingStream)
     protected
       function GetLength(): real;           virtual; abstract;
       function GetPosition(): real;         virtual; abstract;
       procedure SetPosition(Time: real);    virtual; abstract;
       function IsEOF(): boolean;            virtual; abstract;
+      function IsError(): boolean;          virtual; abstract;
     public
       function ReadData(Buffer: PChar; BufSize: integer): integer; virtual; abstract;
       function GetAudioFormatInfo(): TAudioFormatInfo; virtual; abstract;
@@ -174,6 +187,19 @@ type
       property EOF: boolean READ IsEOF;
   end;
 
+type
+  TAudioVoiceStream = class(TAudioProcessingStream)
+    public
+      function ReadData(Buffer: PChar; BufSize: integer): integer; virtual; abstract;
+      function GetAudioFormatInfo(): TAudioFormatInfo; virtual; abstract;
+  end;
+  // soundcard output-devices information
+  TAudioOutputDevice = class
+    public
+      Name: string; // soundcard name
+  end;
+  TAudioOutputDeviceList = array of TAudioOutputDevice;
+
 type
   IGenericPlayback = Interface
   ['{63A5EBC3-3F4D-4F23-8DFB-B5165FCE33DD}']
@@ -208,9 +234,13 @@ type
   IAudioPlayback = Interface( IGenericPlayback )
   ['{E4AE0B40-3C21-4DC5-847C-20A87E0DFB96}']
       function InitializePlayback: boolean;
+      function FinalizePlayback: boolean;
+      
+      function GetOutputDeviceList(): TAudioOutputDeviceList;
+      procedure SetAppVolume(Volume: integer);
       procedure SetVolume(Volume: integer);
-      procedure SetMusicVolume(Volume: integer);
       procedure SetLoop(Enabled: boolean);
+      procedure FadeIn(Time: real; TargetVolume: integer);
 
       procedure Rewind;
       function  Finished: boolean;
@@ -232,6 +262,7 @@ type
   ['{557B0E9A-604D-47E4-B826-13769F3E10B7}']
       function GetName(): String;
       function InitializeDecoder(): boolean;
+      function FinalizeDecoder(): boolean;
       //function IsSupported(const Filename: string): boolean;
   end;
 
@@ -251,6 +282,7 @@ type
   ['{A5C8DA92-2A0C-4AB2-849B-2F7448C6003A}']
       function GetName: String;
       function InitializeRecord: boolean;
+      function FinalizeRecord(): boolean;
 
       procedure CaptureStart;
       procedure CaptureStop;
@@ -288,6 +320,7 @@ var // TODO : JB --- THESE SHOULD NOT BE GLOBAL
 
 
 procedure InitializeSound;
+procedure FinalizeSound;
 
 function  Visualization(): IVideoPlayback;
 function  VideoPlayback(): IVideoPlayback;
@@ -360,14 +393,32 @@ begin
   result := singleton_AudioDecoder;
 end;
 
-procedure AssignSingletonObjects(); 
+procedure FilterInterfaceList(const IID: TGUID; InList, OutList: TInterfaceList);
+var
+  i: integer;
+  obj: IInterface;
+begin
+  if (not assigned(OutList)) then
+    Exit;
+
+  OutList.Clear;
+  for i := 0 to InList.Count-1 do
+  begin
+    if assigned(InList[i]) then
+    begin
+      // add object to list if it implements the interface searched for
+      if (InList[i].QueryInterface(IID, obj) = 0) then
+        OutList.Add(obj);
+    end;
+  end;
+end;
+
+procedure AssignSingletonObjects();
 var
   lTmpInterface : IInterface;
   iCount        : Integer;
 begin
   lTmpInterface := nil;
-  
-
 
   for iCount := 0 to AudioManager.Count - 1 do
   begin
@@ -410,7 +461,6 @@ begin
 
     end;
   end;
-
 end;
 
 procedure InitializeSound;
@@ -460,6 +510,8 @@ begin
     end;    
   end;
 
+  // Update input-device list with registered devices
+  AudioInputProcessor.UpdateInputDeviceConfig();
   // Load in-game sounds
   SoundLib := TSoundLibrary.Create;
 
@@ -480,6 +532,50 @@ begin
   end;
 end;
 
+procedure FinalizeSound;
+var
+  i: integer;
+  AudioIntfList: TInterfaceList;
+begin
+  // stop, close and free sounds
+  SoundLib.Free;
+
+  // stop and close music stream
+  if (AudioPlayback <> nil) then
+    AudioPlayback.Close;
+
+  // stop any active captures
+  if (AudioInput <> nil) then
+    AudioInput.CaptureStop;
+
+  singleton_AudioPlayback := nil;
+  singleton_AudioDecoder := nil;
+  singleton_AudioInput := nil;
+
+  // create temporary interface list
+  AudioIntfList := TInterfaceList.Create();
+
+  // finalize audio playback interfaces (should be done before the decoders) 
+  FilterInterfaceList(IAudioPlayback, AudioManager, AudioIntfList);
+  for i := 0 to AudioIntfList.Count-1 do
+    IAudioPlayback(AudioIntfList[i]).FinalizePlayback();
+
+  // finalize audio input interfaces
+  FilterInterfaceList(IAudioInput, AudioManager, AudioIntfList);
+  for i := 0 to AudioIntfList.Count-1 do
+    IAudioInput(AudioIntfList[i]).FinalizeRecord();
+
+  // finalize audio decoder interfaces
+  FilterInterfaceList(IAudioDecoder, AudioManager, AudioIntfList);
+  for i := 0 to AudioIntfList.Count-1 do
+    IAudioDecoder(AudioIntfList[i]).FinalizeDecoder();
+
+  AudioIntfList.Free;
+
+  // free audio interfaces
+  while (AudioManager.Count > 0) do
+    AudioManager.Delete(0);
+end;
 
 { TSoundLibrary }
 
@@ -535,6 +631,32 @@ begin
   //Shuffle.Free;
 end;
 
+procedure TVoiceRemoval.Callback(Buffer: PChar; BufSize: integer);
+var
+  FrameIndex, FrameSize: integer;
+  Value: integer;
+  Sample: PPCMStereoSample;
+begin
+  FrameSize := 2 * SizeOf(SmallInt);
+  for FrameIndex := 0 to (BufSize div FrameSize)-1 do
+  begin
+    Sample := PPCMStereoSample(Buffer);
+    // channel difference
+    Value := Sample[0] - Sample[1];
+    // clip
+    if (Value > High(SmallInt)) then
+      Value := High(SmallInt)
+    else if (Value < Low(SmallInt)) then
+      Value := Low(SmallInt);
+    // assign result
+    Sample[0] := Value;
+    Sample[1] := Value;
+    // increase to next frame
+    Inc(Buffer, FrameSize);
+  end;
+end;
+
+
 initialization
 begin
   singleton_AudioManager := TInterfaceList.Create();
-- 
cgit v1.2.3