aboutsummaryrefslogtreecommitdiffstats
path: root/Game/Code/Classes/UAudioPlayback_SoftMixer.pas
diff options
context:
space:
mode:
authortobigun <tobigun@b956fd51-792f-4845-bead-9b4dfca2ff2c>2008-05-09 19:19:28 +0000
committertobigun <tobigun@b956fd51-792f-4845-bead-9b4dfca2ff2c>2008-05-09 19:19:28 +0000
commitb5a738fa52c8b0f2212deb5febd2d7f0b8f6544f (patch)
tree3c2812cffdd035b385d5b0f0f8f5ea0702973739 /Game/Code/Classes/UAudioPlayback_SoftMixer.pas
parent37744cee627605db0675efd3a6e0c42bd51c48d6 (diff)
downloadusdx-b5a738fa52c8b0f2212deb5febd2d7f0b8f6544f.tar.gz
usdx-b5a738fa52c8b0f2212deb5febd2d7f0b8f6544f.tar.xz
usdx-b5a738fa52c8b0f2212deb5febd2d7f0b8f6544f.zip
- 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
Diffstat (limited to '')
-rw-r--r--Game/Code/Classes/UAudioPlayback_SoftMixer.pas370
1 files changed, 164 insertions, 206 deletions
diff --git a/Game/Code/Classes/UAudioPlayback_SoftMixer.pas b/Game/Code/Classes/UAudioPlayback_SoftMixer.pas
index f65b3d9a..431653d0 100644
--- a/Game/Code/Classes/UAudioPlayback_SoftMixer.pas
+++ b/Game/Code/Classes/UAudioPlayback_SoftMixer.pas
@@ -13,7 +13,8 @@ uses
Classes,
SysUtils,
sdl,
- UMusic;
+ UMusic,
+ UAudioPlaybackBase;
type
TAudioPlayback_SoftMixer = class;
@@ -35,9 +36,14 @@ type
InternalLock: PSDL_Mutex;
+ SoundEffects: TList;
+
+ FadeInStartTime, FadeInTime: cardinal;
+ FadeInStartVolume, FadeInTargetVolume: integer;
+
procedure Reset();
- class function ConvertAudioFormatToSDL(fmt: TAudioSampleFormat): UInt16;
+ class function ConvertAudioFormatToSDL(Format: TAudioSampleFormat; out SDLFormat: UInt16): boolean;
function InitFormatConversion(): boolean;
procedure Lock(); {$IFDEF HasInline}inline;{$ENDIF}
@@ -51,24 +57,26 @@ type
procedure Play(); override;
procedure Pause(); override;
procedure Stop(); override;
+ procedure FadeIn(Time: real; TargetVolume: integer); override;
+
procedure Close(); override;
- function GetLoop(): boolean; override;
- procedure SetLoop(Enabled: boolean); override;
+
function GetLength(): real; override;
function GetStatus(): TStreamStatus; override;
-
- function IsLoaded(): boolean;
-
function GetVolume(): integer; override;
procedure SetVolume(Volume: integer); override;
+ function GetLoop(): boolean; override;
+ procedure SetLoop(Enabled: boolean); override;
+ function GetPosition: real; override;
+ procedure SetPosition(Time: real); override;
- // functions delegated to the decode stream
- function GetPosition: real;
- procedure SetPosition(Time: real);
function ReadData(Buffer: PChar; BufSize: integer): integer;
- function GetPCMData(var data: TPCMData): Cardinal;
- procedure GetFFTData(var data: TFFTData);
+ function GetPCMData(var data: TPCMData): Cardinal; override;
+ procedure GetFFTData(var data: TFFTData); override;
+
+ procedure AddSoundEffect(effect: TSoundEffect); override;
+ procedure RemoveSoundEffect(effect: TSoundEffect); override;
end;
TAudioMixerStream = class
@@ -79,7 +87,7 @@ type
mixerBuffer: PChar;
internalLock: PSDL_Mutex;
- _volume: integer;
+ appVolume: integer;
procedure Lock(); {$IFDEF HasInline}inline;{$ENDIF}
procedure Unlock(); {$IFDEF HasInline}inline;{$ENDIF}
@@ -96,9 +104,8 @@ type
property Volume: integer READ GetVolume WRITE SetVolume;
end;
- TAudioPlayback_SoftMixer = class( TInterfacedObject, IAudioPlayback )
+ TAudioPlayback_SoftMixer = class(TAudioPlaybackBase)
private
- MusicStream: TGenericPlaybackStream;
MixerStream: TAudioMixerStream;
protected
FormatInfo: TAudioFormatInfo;
@@ -106,45 +113,21 @@ type
function InitializeAudioPlaybackEngine(): boolean; virtual; abstract;
function StartAudioPlaybackEngine(): boolean; virtual; abstract;
procedure StopAudioPlaybackEngine(); virtual; abstract;
+ function FinalizeAudioPlaybackEngine(): boolean; virtual; abstract;
procedure AudioCallback(buffer: PChar; size: integer); {$IFDEF HasInline}inline;{$ENDIF}
- public
- function GetName: String; virtual; abstract;
-
- function InitializePlayback(): boolean;
- destructor Destroy; override;
- function Load(const Filename: String): TGenericPlaybackStream;
-
- procedure SetVolume(Volume: integer);
- procedure SetMusicVolume(Volume: integer);
- procedure SetLoop(Enabled: boolean);
- function Open(const Filename: string): boolean; // true if succeed
- procedure Rewind;
- procedure SetPosition(Time: real);
- procedure Play;
- procedure Pause;
-
- procedure Stop;
- procedure Close;
- function Finished: boolean;
- function Length: real;
- function GetPosition: real;
-
- // Equalizer
- procedure GetFFTData(var data: TFFTData);
+ function OpenStream(const Filename: String): TAudioPlaybackStream; override;
+ public
+ function GetName: String; override; abstract;
+ function InitializePlayback(): boolean; override;
+ function FinalizePlayback: boolean; override;
- // Interface for Visualizer
- function GetPCMData(var data: TPCMData): Cardinal;
+ procedure SetAppVolume(Volume: integer); override;
function GetMixer(): TAudioMixerStream; {$IFDEF HasInline}inline;{$ENDIF}
function GetAudioFormatInfo(): TAudioFormatInfo;
procedure MixBuffers(dst, src: PChar; size: Cardinal; volume: Integer); virtual;
-
- // Sounds
- function OpenSound(const Filename: String): TAudioPlaybackStream;
- procedure PlaySound(stream: TAudioPlaybackStream);
- procedure StopSound(stream: TAudioPlaybackStream);
end;
implementation
@@ -161,11 +144,13 @@ uses
constructor TAudioMixerStream.Create(Engine: TAudioPlayback_SoftMixer);
begin
+ inherited Create();
+
Self.Engine := Engine;
activeStreams := TList.Create;
internalLock := SDL_CreateMutex();
- _volume := 100;
+ appVolume := 100;
end;
destructor TAudioMixerStream.Destroy();
@@ -174,6 +159,7 @@ begin
Freemem(mixerBuffer);
activeStreams.Free;
SDL_DestroyMutex(internalLock);
+ inherited;
end;
procedure TAudioMixerStream.Lock();
@@ -189,14 +175,14 @@ end;
function TAudioMixerStream.GetVolume(): integer;
begin
Lock();
- result := _volume;
+ result := appVolume;
Unlock();
end;
procedure TAudioMixerStream.SetVolume(volume: integer);
begin
Lock();
- _volume := volume;
+ appVolume := volume;
Unlock();
end;
@@ -270,8 +256,8 @@ begin
if (size > 0) then
begin
// mix stream-data with mixer-buffer
- // Note: use _volume (Application-Volume) instead of Volume to prevent recursive locking
- Engine.MixBuffers(Buffer, mixerBuffer, size, _volume * stream.Volume div 100);
+ // Note: use Self.appVolume instead of Self.Volume to prevent recursive locking
+ Engine.MixBuffers(Buffer, mixerBuffer, size, appVolume * stream.Volume div 100);
end;
end;
@@ -292,6 +278,8 @@ begin
inherited Create();
Self.Engine := Engine;
internalLock := SDL_CreateMutex();
+ SoundEffects := TList.Create;
+ Status := ssStopped;
Reset();
end;
@@ -299,20 +287,34 @@ destructor TGenericPlaybackStream.Destroy();
begin
Close();
SDL_DestroyMutex(internalLock);
- inherited Destroy();
+ FreeAndNil(SoundEffects);
+ inherited;
end;
procedure TGenericPlaybackStream.Reset();
begin
+ // wake-up sleeping audio-callback threads in the ReadData()-function
+ if assigned(decodeStream) then
+ decodeStream.Close();
+
+ // stop audio-callback on this stream
Stop();
+
+ // reset and/or free data
+
Loop := false;
+
// TODO: use DecodeStream.Unref() instead of Free();
FreeAndNil(DecodeStream);
+
FreeMem(SampleBuffer);
SampleBuffer := nil;
SampleBufferPos := 0;
BytesAvail := 0;
+
_volume := 0;
+ SoundEffects.Clear;
+ FadeInTime := 0;
end;
procedure TGenericPlaybackStream.Lock();
@@ -325,24 +327,27 @@ begin
SDL_mutexV(internalLock);
end;
-class function TGenericPlaybackStream.ConvertAudioFormatToSDL(fmt: TAudioSampleFormat): UInt16;
+class function TGenericPlaybackStream.ConvertAudioFormatToSDL(Format: TAudioSampleFormat; out SDLFormat: UInt16): boolean;
begin
- case fmt of
- asfU8: Result := AUDIO_U8;
- asfS8: Result := AUDIO_S8;
- asfU16LSB: Result := AUDIO_U16LSB;
- asfS16LSB: Result := AUDIO_S16LSB;
- asfU16MSB: Result := AUDIO_U16MSB;
- asfS16MSB: Result := AUDIO_S16MSB;
- asfU16: Result := AUDIO_U16;
- asfS16: Result := AUDIO_S16;
- else Result := 0;
+ case Format of
+ asfU8: SDLFormat := AUDIO_U8;
+ asfS8: SDLFormat := AUDIO_S8;
+ asfU16LSB: SDLFormat := AUDIO_U16LSB;
+ asfS16LSB: SDLFormat := AUDIO_S16LSB;
+ asfU16MSB: SDLFormat := AUDIO_U16MSB;
+ asfS16MSB: SDLFormat := AUDIO_S16MSB;
+ asfU16: SDLFormat := AUDIO_U16;
+ asfS16: SDLFormat := AUDIO_S16;
+ else begin
+ Result := false;
+ Exit;
+ end;
end;
+ Result := true;
end;
function TGenericPlaybackStream.InitFormatConversion(): boolean;
var
- //err: integer;
srcFormat: UInt16;
dstFormat: UInt16;
srcFormatInfo: TAudioFormatInfo;
@@ -353,10 +358,8 @@ begin
srcFormatInfo := DecodeStream.GetAudioFormatInfo();
dstFormatInfo := Engine.GetAudioFormatInfo();
- srcFormat := ConvertAudioFormatToSDL(srcFormatInfo.Format);
- dstFormat := ConvertAudioFormatToSDL(dstFormatInfo.Format);
-
- if ((srcFormat = 0) or (dstFormat = 0)) then
+ if (not ConvertAudioFormatToSDL(srcFormatInfo.Format, srcFormat) or
+ not ConvertAudioFormatToSDL(dstFormatInfo.Format, dstFormat)) then
begin
Log.LogError('Audio-format not supported by SDL', 'TSoftMixerPlaybackStream.InitFormatConversion');
Exit;
@@ -399,7 +402,7 @@ procedure TGenericPlaybackStream.Play();
var
mixer: TAudioMixerStream;
begin
- if (status <> ssPaused) then
+ if (status = ssPlaying) then
begin
// rewind
if assigned(DecodeStream) then
@@ -412,6 +415,15 @@ begin
mixer.AddStream(Self);
end;
+procedure TGenericPlaybackStream.FadeIn(Time: real; TargetVolume: integer);
+begin
+ FadeInTime := Trunc(Time * 1000);
+ FadeInStartTime := SDL_GetTicks();
+ FadeInStartVolume := _volume;
+ FadeInTargetVolume := TargetVolume;
+ Play();
+end;
+
procedure TGenericPlaybackStream.Pause();
var
mixer: TAudioMixerStream;
@@ -427,16 +439,18 @@ procedure TGenericPlaybackStream.Stop();
var
mixer: TAudioMixerStream;
begin
+ if (status = ssStopped) then
+ Exit;
+
status := ssStopped;
mixer := Engine.GetMixer();
if (mixer <> nil) then
mixer.RemoveStream(Self);
-end;
-function TGenericPlaybackStream.IsLoaded(): boolean;
-begin
- result := assigned(DecodeStream);
+ // rewind (note: DecodeStream might be closed already, but this is not a problem)
+ if assigned(DecodeStream) then
+ DecodeStream.Position := 0;
end;
function TGenericPlaybackStream.GetLoop(): boolean;
@@ -480,6 +494,7 @@ var
remFrameBytes: integer;
copyCnt: integer;
BytesNeeded: integer;
+ i: integer;
begin
Result := -1;
@@ -543,7 +558,9 @@ begin
// end-of-file reached -> stop playback
if (DecodeStream.EOF) then
+ begin
Stop();
+ end;
// resample decoded data
cvt.buf := PUint8(SampleBuffer);
@@ -552,6 +569,15 @@ begin
Exit;
SampleBufferCount := cvt.len_cvt;
+
+ // apply effects
+ for i := 0 to SoundEffects.Count-1 do
+ begin
+ if (SoundEffects[i] <> nil) then
+ begin
+ TSoundEffect(SoundEffects[i]).Callback(SampleBuffer, SampleBufferCount);
+ end;
+ end;
finally
Unlock();
end;
@@ -664,11 +690,28 @@ begin
// resize data to a 0..1 range
for i := 0 to High(TFFTData) do
begin
- // TODO: this might need some work
data[i] := Sqrt(data[i]) / 100;
end;
end;
+procedure TGenericPlaybackStream.AddSoundEffect(effect: TSoundEffect);
+begin
+ if (not assigned(effect)) then
+ Exit;
+ Lock();
+ // check if effect is already in list to avoid duplicates
+ if (SoundEffects.IndexOf(Pointer(effect)) = -1) then
+ SoundEffects.Add(Pointer(effect));
+ Unlock();
+end;
+
+procedure TGenericPlaybackStream.RemoveSoundEffect(effect: TSoundEffect);
+begin
+ Lock();
+ SoundEffects.Remove(effect);
+ Unlock();
+end;
+
function TGenericPlaybackStream.GetPosition: real;
begin
if assigned(DecodeStream) then
@@ -684,12 +727,37 @@ begin
end;
function TGenericPlaybackStream.GetVolume(): integer;
+var
+ FadeAmount: Single;
begin
- result := _volume;
+ Lock();
+ // adjust volume if fading is enabled
+ if (FadeInTime > 0) then
+ begin
+ FadeAmount := (SDL_GetTicks() - FadeInStartTime) / FadeInTime;
+ // check if fade-target is reached
+ if (FadeAmount >= 1) then
+ begin
+ // target reached -> stop fading
+ FadeInTime := 0;
+ _volume := FadeInTargetVolume;
+ end
+ else
+ begin
+ // fading in progress
+ _volume := Trunc(FadeAmount*FadeInTargetVolume + (1-FadeAmount)*FadeInStartVolume);
+ end;
+ end;
+ // return current volume
+ Result := _volume;
+ Unlock();
end;
procedure TGenericPlaybackStream.SetVolume(volume: integer);
begin
+ Lock();
+ // stop fading
+ FadeInTime := 0;
// clamp volume
if (volume > 100) then
_volume := 100
@@ -697,6 +765,7 @@ begin
_volume := 0
else
_volume := volume;
+ Unlock();
end;
@@ -719,15 +788,17 @@ begin
result := true;
end;
-destructor TAudioPlayback_SoftMixer.Destroy;
+function TAudioPlayback_SoftMixer.FinalizePlayback: boolean;
begin
+ Close;
StopAudioPlaybackEngine();
- FreeAndNil(MusicStream);
FreeAndNil(MixerStream);
FreeAndNil(FormatInfo);
- inherited Destroy();
+ FinalizeAudioPlaybackEngine();
+ inherited FinalizePlayback;
+ Result := true;
end;
procedure TAudioPlayback_SoftMixer.AudioCallback(buffer: PChar; size: integer);
@@ -745,13 +816,16 @@ begin
Result := FormatInfo;
end;
-function TAudioPlayback_SoftMixer.Load(const Filename: String): TGenericPlaybackStream;
+function TAudioPlayback_SoftMixer.OpenStream(const Filename: String): TAudioPlaybackStream;
var
decodeStream: TAudioDecodeStream;
playbackStream: TGenericPlaybackStream;
begin
Result := nil;
+ if (AudioDecoder = nil) then
+ Exit;
+
decodeStream := AudioDecoder.Open(Filename);
if not assigned(decodeStream) then
begin
@@ -760,110 +834,28 @@ begin
end;
playbackStream := TGenericPlaybackStream.Create(Self);
+ if (not assigned(playbackStream)) then
+ begin
+ FreeAndNil(decodeStream);
+ Exit;
+ end;
+
if (not playbackStream.SetDecodeStream(decodeStream)) then
+ begin
+ FreeAndNil(playbackStream);
+ FreeAndNil(decodeStream);
Exit;
+ end;
result := playbackStream;
end;
-procedure TAudioPlayback_SoftMixer.SetVolume(Volume: integer);
+procedure TAudioPlayback_SoftMixer.SetAppVolume(Volume: integer);
begin
// sets volume only for this application
MixerStream.Volume := Volume;
end;
-procedure TAudioPlayback_SoftMixer.SetMusicVolume(Volume: Integer);
-begin
- if assigned(MusicStream) then
- MusicStream.Volume := Volume;
-end;
-
-procedure TAudioPlayback_SoftMixer.SetLoop(Enabled: boolean);
-begin
- if assigned(MusicStream) then
- MusicStream.SetLoop(Enabled);
-end;
-
-function TAudioPlayback_SoftMixer.Open(const Filename: string): boolean;
-//var
-// decodeStream: TAudioDecodeStream;
-begin
- Result := false;
-
- // free old MusicStream
- MusicStream.Free();
- // and load new one
- MusicStream := Load(Filename);
- if not assigned(MusicStream) then
- Exit;
-
- //Set Max Volume
- SetMusicVolume(100);
-
- Result := true;
-end;
-
-procedure TAudioPlayback_SoftMixer.Rewind;
-begin
- SetPosition(0);
-end;
-
-procedure TAudioPlayback_SoftMixer.SetPosition(Time: real);
-begin
- if assigned(MusicStream) then
- MusicStream.SetPosition(Time);
-end;
-
-function TAudioPlayback_SoftMixer.GetPosition: real;
-begin
- if assigned(MusicStream) then
- Result := MusicStream.GetPosition()
- else
- Result := -1;
-end;
-
-function TAudioPlayback_SoftMixer.Length: real;
-begin
- if assigned(MusicStream) then
- Result := MusicStream.GetLength()
- else
- Result := -1;
-end;
-
-procedure TAudioPlayback_SoftMixer.Play;
-begin
- if assigned(MusicStream) then
- MusicStream.Play();
-end;
-
-procedure TAudioPlayback_SoftMixer.Pause;
-begin
- if assigned(MusicStream) then
- MusicStream.Pause();
-end;
-
-procedure TAudioPlayback_SoftMixer.Stop;
-begin
- if assigned(MusicStream) then
- MusicStream.Stop();
-end;
-
-procedure TAudioPlayback_SoftMixer.Close;
-begin
- if assigned(MusicStream) then
- begin
- MusicStream.Close();
- end;
-end;
-
-function TAudioPlayback_SoftMixer.Finished: boolean;
-begin
- if assigned(MusicStream) then
- Result := (MusicStream.GetStatus() = ssStopped)
- else
- Result := true;
-end;
-
procedure TAudioPlayback_SoftMixer.MixBuffers(dst, src: PChar; size: Cardinal; volume: Integer);
var
SampleIndex: Cardinal;
@@ -916,38 +908,4 @@ begin
end;
end;
-//Equalizer
-procedure TAudioPlayback_SoftMixer.GetFFTData(var data: TFFTData);
-begin
- if assigned(MusicStream) then
- MusicStream.GetFFTData(data);
-end;
-
-// Interface for Visualizer
-function TAudioPlayback_SoftMixer.GetPCMData(var data: TPCMData): Cardinal;
-begin
- if assigned(MusicStream) then
- Result := MusicStream.GetPCMData(data)
- else
- Result := 0;
-end;
-
-function TAudioPlayback_SoftMixer.OpenSound(const Filename: String): TAudioPlaybackStream;
-begin
- Result := Load(Filename);
-end;
-
-procedure TAudioPlayback_SoftMixer.PlaySound(stream: TAudioPlaybackStream);
-begin
- if assigned(stream) then
- stream.Play();
-end;
-
-procedure TAudioPlayback_SoftMixer.StopSound(stream: TAudioPlaybackStream);
-begin
- if assigned(stream) then
- stream.Stop();
-end;
-
-
end.