diff options
Diffstat (limited to 'Game/Code/Classes/UAudioPlayback_Portaudio.pas')
-rw-r--r-- | Game/Code/Classes/UAudioPlayback_Portaudio.pas | 881 |
1 files changed, 153 insertions, 728 deletions
diff --git a/Game/Code/Classes/UAudioPlayback_Portaudio.pas b/Game/Code/Classes/UAudioPlayback_Portaudio.pas index 59571d3d..96fff957 100644 --- a/Game/Code/Classes/UAudioPlayback_Portaudio.pas +++ b/Game/Code/Classes/UAudioPlayback_Portaudio.pas @@ -1,728 +1,153 @@ -unit UAudioPlayback_Portaudio; - -interface - -{$IFDEF FPC} - {$MODE Delphi} -{$ENDIF} - -{$I switches.inc} - - -uses - Classes, - SysUtils, - UMusic; - -implementation - -uses - {$IFNDEF Win32} - libc, - {$ENDIF} - sdl, - portaudio, - ULog, - UIni, - UMain; - -type - TPortaudioPlaybackStream = class(TAudioPlaybackStream) - private - Status: TStreamStatus; - Loop: boolean; - - _volume: integer; - - procedure Reset(); - public - DecodeStream: TAudioDecodeStream; - - constructor Create(); - destructor Destroy(); override; - - function SetDecodeStream(decodeStream: TAudioDecodeStream): boolean; - - procedure Play(); override; - procedure Pause(); override; - procedure Stop(); 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; - - // functions delegated to the decode stream - function GetPosition: real; - procedure SetPosition(Time: real); - function ReadData(Buffer: PChar; BufSize: integer): integer; - end; - -type - TAudioMixerStream = class - private - activeStreams: TList; - mixerBuffer: PChar; - internalLock: PSDL_Mutex; - - _volume: integer; - - procedure Lock(); inline; - procedure Unlock(); inline; - - function GetVolume(): integer; - procedure SetVolume(volume: integer); - public - constructor Create(); - destructor Destroy(); override; - procedure AddStream(stream: TAudioPlaybackStream); - procedure RemoveStream(stream: TAudioPlaybackStream); - function ReadData(Buffer: PChar; BufSize: integer): integer; - - property Volume: integer READ GetVolume WRITE SetVolume; - end; - -type - TAudioPlayback_Portaudio = class( TInterfacedObject, IAudioPlayback ) - private - MusicStream: TPortaudioPlaybackStream; - - MixerStream: TAudioMixerStream; - paStream: PPaStream; - - FrameSize: integer; - - function InitializePortaudio(): boolean; - function StartPortaudioStream(): boolean; - - function InitializeSDLAudio(): boolean; - function StartSDLAudioStream(): boolean; - procedure StopSDLAudioStream(); - public - function GetName: String; - - function InitializePlayback(): boolean; - destructor Destroy; override; - - function Load(const Filename: String): TPortaudioPlaybackStream; - - procedure SetVolume(Volume: integer); - procedure SetMusicVolume(Volume: integer); - procedure SetLoop(Enabled: boolean); - function Open(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); - - // Interface for Visualizer - function GetPCMData(var data: TPCMData): Cardinal; - - // Sounds - function OpenSound(const Filename: String): TAudioPlaybackStream; - procedure PlaySound(stream: TAudioPlaybackStream); - procedure StopSound(stream: TAudioPlaybackStream); - end; - - -function AudioCallback(input: Pointer; output: Pointer; frameCount: Longword; - timeInfo: PPaStreamCallbackTimeInfo; statusFlags: TPaStreamCallbackFlags; - userData: Pointer): Integer; cdecl; forward; - -var - singleton_AudioPlaybackPortaudio : IAudioPlayback; - - -{ TAudioMixerStream } - -constructor TAudioMixerStream.Create(); -begin - activeStreams := TList.Create; - internalLock := SDL_CreateMutex(); - _volume := 100; -end; - -destructor TAudioMixerStream.Destroy(); -begin - if assigned(mixerBuffer) then - Freemem(mixerBuffer); - activeStreams.Free; - SDL_DestroyMutex(internalLock); -end; - -procedure TAudioMixerStream.Lock(); -begin - SDL_mutexP(internalLock); -end; - -procedure TAudioMixerStream.Unlock(); -begin - SDL_mutexV(internalLock); -end; - -function TAudioMixerStream.GetVolume(): integer; -begin - Lock(); - result := _volume; - Unlock(); -end; - -procedure TAudioMixerStream.SetVolume(volume: integer); -begin - Lock(); - _volume := volume; - Unlock(); -end; - -procedure TAudioMixerStream.AddStream(stream: TAudioPlaybackStream); -begin - if not assigned(stream) then - Exit; - - Lock(); - // check if stream is already in list to avoid duplicates - if (activeStreams.IndexOf(Pointer(stream)) = -1) then - activeStreams.Add(Pointer(stream)); - Unlock(); -end; - -procedure TAudioMixerStream.RemoveStream(stream: TAudioPlaybackStream); -begin - Lock(); - activeStreams.Remove(Pointer(stream)); - Unlock(); -end; - -function TAudioMixerStream.ReadData(Buffer: PChar; BufSize: integer): integer; -var - i: integer; - size: integer; - stream: TPortaudioPlaybackStream; - appVolume: single; -begin - result := BufSize; - - // zero target-buffer (silence) - FillChar(Buffer^, BufSize, 0); - - // resize mixer-buffer if necessary - ReallocMem(mixerBuffer, BufSize); - if not assigned(mixerBuffer) then - Exit; - - Lock(); - - //writeln('Mix: ' + inttostr(activeStreams.Count)); - - // use _volume instead of Volume to prevent recursive locking - appVolume := _volume / 100 * SDL_MIX_MAXVOLUME; - - for i := 0 to activeStreams.Count-1 do - begin - stream := TPortaudioPlaybackStream(activeStreams[i]); - if (stream.GetStatus() = ssPlaying) then - begin - // fetch data from current stream - size := stream.ReadData(mixerBuffer, BufSize); - if (size > 0) then - begin - SDL_MixAudio(PUInt8(Buffer), PUInt8(mixerBuffer), size, - Trunc(appVolume * stream.Volume / 100)); - end; - end; - end; - - Unlock(); -end; - - -{ TPortaudioPlaybackStream } - -constructor TPortaudioPlaybackStream.Create(); -begin - inherited Create(); - Reset(); -end; - -destructor TPortaudioPlaybackStream.Destroy(); -begin - Close(); - inherited Destroy(); -end; - -procedure TPortaudioPlaybackStream.Reset(); -begin - Status := ssStopped; - Loop := false; - DecodeStream := nil; - _volume := 0; -end; - -function TPortaudioPlaybackStream.SetDecodeStream(decodeStream: TAudioDecodeStream): boolean; -begin - result := false; - - Reset(); - - if not assigned(decodeStream) then - Exit; - Self.DecodeStream := decodeStream; - - _volume := 100; - - result := true; -end; - -procedure TPortaudioPlaybackStream.Close(); -begin - Reset(); -end; - -procedure TPortaudioPlaybackStream.Play(); -begin - if (status <> ssPaused) then - begin - // rewind - if assigned(DecodeStream) then - DecodeStream.Position := 0; - end; - status := ssPlaying; - //MixerStream.AddStream(Self); -end; - -procedure TPortaudioPlaybackStream.Pause(); -begin - status := ssPaused; -end; - -procedure TPortaudioPlaybackStream.Stop(); -begin - status := ssStopped; -end; - -function TPortaudioPlaybackStream.IsLoaded(): boolean; -begin - result := assigned(DecodeStream); -end; - -function TPortaudioPlaybackStream.GetLoop(): boolean; -begin - result := Loop; -end; - -procedure TPortaudioPlaybackStream.SetLoop(Enabled: boolean); -begin - Loop := Enabled; -end; - -function TPortaudioPlaybackStream.GetLength(): real; -begin - if assigned(DecodeStream) then - result := DecodeStream.Length - else - result := -1; -end; - -function TPortaudioPlaybackStream.GetStatus(): TStreamStatus; -begin - result := status; -end; - -function TPortaudioPlaybackStream.ReadData(Buffer: PChar; BufSize: integer): integer; -begin - if not assigned(DecodeStream) then - begin - result := -1; - Exit; - end; - result := DecodeStream.ReadData(Buffer, BufSize); - // end-of-file reached -> stop playback - if (DecodeStream.EOF) then - begin - status := ssStopped; - end; -end; - -function TPortaudioPlaybackStream.GetPosition: real; -begin - if assigned(DecodeStream) then - result := DecodeStream.Position - else - result := -1; -end; - -procedure TPortaudioPlaybackStream.SetPosition(Time: real); -begin - if assigned(DecodeStream) then - DecodeStream.Position := Time; -end; - -function TPortaudioPlaybackStream.GetVolume(): integer; -begin - result := _volume; -end; - -procedure TPortaudioPlaybackStream.SetVolume(volume: integer); -begin - // clamp volume - if (volume > 100) then - _volume := 100 - else if (volume < 0) then - _volume := 0 - else - _volume := volume; -end; - - -{ TAudioPlayback_Portaudio } - -function AudioCallback(input: Pointer; output: Pointer; frameCount: Longword; - timeInfo: PPaStreamCallbackTimeInfo; statusFlags: TPaStreamCallbackFlags; - userData: Pointer): Integer; cdecl; -var - playback: TAudioPlayback_Portaudio; -begin - playback := TAudioPlayback_Portaudio(userData); - with playback do - begin - MixerStream.ReadData(output, frameCount * FrameSize); - end; - result := paContinue; -end; - -procedure SDLAudioCallback(userdata: Pointer; stream: PChar; len: integer); cdecl; -var - playback: TAudioPlayback_Portaudio; -begin - playback := TAudioPlayback_Portaudio(userdata); - with playback do - begin - MixerStream.ReadData(stream, len); - end; -end; - -function TAudioPlayback_Portaudio.GetName: String; -begin - result := 'Portaudio_Playback'; -end; - -function TAudioPlayback_Portaudio.InitializePortaudio(): boolean; -var - paApi : TPaHostApiIndex; - paApiInfo : PPaHostApiInfo; - paOutParams : TPaStreamParameters; - paOutDevice : TPaDeviceIndex; - paOutDeviceInfo : PPaDeviceInfo; - err : TPaError; -begin - result := false; - - Pa_Initialize(); - - // FIXME: determine automatically - {$IFDEF WIN32} - paApi := Pa_HostApiTypeIdToHostApiIndex(paDirectSound); - {$ELSE} - paApi := Pa_HostApiTypeIdToHostApiIndex(paALSA); - {$ENDIF} - if (paApi < 0) then - begin - Log.LogStatus('Pa_HostApiTypeIdToHostApiIndex: '+Pa_GetErrorText(paApi), 'UAudioPlayback_Portaudio'); - exit; - end; - - paApiInfo := Pa_GetHostApiInfo(paApi); - paOutDevice := paApiInfo^.defaultOutputDevice; - paOutDeviceInfo := Pa_GetDeviceInfo(paOutDevice); - - with paOutParams do begin - device := paOutDevice; - channelCount := 2; - sampleFormat := paInt16; - suggestedLatency := paOutDeviceInfo^.defaultHighOutputLatency; - hostApiSpecificStreamInfo := nil; - end; - - // set the size of one audio frame (2channel 16bit uint sample) - FrameSize := 2 * sizeof(Smallint); - - err := Pa_OpenStream(paStream, nil, @paOutParams, 44100, - paFramesPerBufferUnspecified, - paNoFlag, @AudioCallback, Self); - if(err <> paNoError) then begin - Log.LogStatus('Pa_OpenStream: '+Pa_GetErrorText(err), 'UAudioPlayback_Portaudio'); - exit; - end; - - Log.LogStatus('Opened audio device', 'UAudioPlayback_Portaudio'); - - result := true; -end; - -function TAudioPlayback_Portaudio.StartPortaudioStream(): boolean; -var - err: TPaError; -begin - result := false; - - err := Pa_StartStream(paStream); - if(err <> paNoError) then - begin - Log.LogStatus('Pa_StartStream: '+Pa_GetErrorText(err), 'UAudioPlayback_Portaudio'); - exit; - end; - - result := true; -end; - -function TAudioPlayback_Portaudio.InitializeSDLAudio(): boolean; -var - desiredAudioSpec, obtainedAudioSpec: TSDL_AudioSpec; - err: integer; -begin - result := false; - - SDL_InitSubSystem(SDL_INIT_AUDIO); - - FillChar(desiredAudioSpec, sizeof(desiredAudioSpec), 0); - with desiredAudioSpec do - begin - freq := 44100; - format := AUDIO_S16SYS; - channels := 2; - samples := 1024; // latency: 23 ms - callback := @SDLAudioCallback; - userdata := Self; - end; - - // set the size of one audio frame (2channel 16bit uint sample) - FrameSize := 2 * sizeof(Smallint); - - if(SDL_OpenAudio(@desiredAudioSpec, @obtainedAudioSpec) = -1) then - begin - Log.LogStatus('SDL_OpenAudio: ' + SDL_GetError(), 'UAudioPlayback_SDL'); - exit; - end; - - Log.LogStatus('Opened audio device', 'UAudioPlayback_SDL'); - - result := true; -end; - -function TAudioPlayback_Portaudio.StartSDLAudioStream(): boolean; -begin - SDL_PauseAudio(0); - result := true; -end; - -procedure TAudioPlayback_Portaudio.StopSDLAudioStream(); -begin - SDL_CloseAudio(); -end; - -function TAudioPlayback_Portaudio.InitializePlayback: boolean; -begin - result := false; - - //Log.LogStatus('InitializePlayback', 'UAudioPlayback_Portaudio'); - - //if(not InitializePortaudio()) then - if(not InitializeSDLAudio()) then - Exit; - - MixerStream := TAudioMixerStream.Create; - - //if(not StartPortaudioStream()) then; - if(not StartSDLAudioStream()) then - Exit; - - result := true; -end; - -destructor TAudioPlayback_Portaudio.Destroy; -begin - StopSDLAudioStream(); - - MixerStream.Free(); - MusicStream.Free(); - - inherited Destroy(); -end; - -function TAudioPlayback_Portaudio.Load(const Filename: String): TPortaudioPlaybackStream; -var - decodeStream: TAudioDecodeStream; - playbackStream: TPortaudioPlaybackStream; -begin - Result := nil; - - decodeStream := AudioDecoder.Open(Filename); - if not assigned(decodeStream) then - begin - Log.LogStatus('LoadSoundFromFile: Sound not found "' + Filename + '"', 'UAudioPlayback_Portaudio'); - Exit; - end; - - playbackStream := TPortaudioPlaybackStream.Create(); - if (not playbackStream.SetDecodeStream(decodeStream)) then - Exit; - - // FIXME: remove this line - MixerStream.AddStream(playbackStream); - - result := playbackStream; -end; - -procedure TAudioPlayback_Portaudio.SetVolume(Volume: integer); -begin - // sets volume only for this application - MixerStream.Volume := Volume; -end; - -procedure TAudioPlayback_Portaudio.SetMusicVolume(Volume: Integer); -begin - if assigned(MusicStream) then - MusicStream.Volume := Volume; -end; - -procedure TAudioPlayback_Portaudio.SetLoop(Enabled: boolean); -begin - if assigned(MusicStream) then - MusicStream.SetLoop(Enabled); -end; - -function TAudioPlayback_Portaudio.Open(Filename: string): boolean; -var - decodeStream: TAudioDecodeStream; -begin - Result := false; - - // free old MusicStream - MusicStream.Free(); - - MusicStream := Load(Filename); - if not assigned(MusicStream) then - Exit; - - //Set Max Volume - SetMusicVolume(100); - - Result := true; -end; - -procedure TAudioPlayback_Portaudio.Rewind; -begin - SetPosition(0); -end; - -procedure TAudioPlayback_Portaudio.SetPosition(Time: real); -begin - if assigned(MusicStream) then - MusicStream.SetPosition(Time); -end; - -function TAudioPlayback_Portaudio.GetPosition: real; -begin - if assigned(MusicStream) then - Result := MusicStream.GetPosition() - else - Result := -1; -end; - -function TAudioPlayback_Portaudio.Length: real; -begin - if assigned(MusicStream) then - Result := MusicStream.GetLength() - else - Result := -1; -end; - -procedure TAudioPlayback_Portaudio.Play; -begin - if assigned(MusicStream) then - MusicStream.Play(); -end; - -procedure TAudioPlayback_Portaudio.Pause; -begin - if assigned(MusicStream) then - MusicStream.Pause(); -end; - -procedure TAudioPlayback_Portaudio.Stop; -begin - if assigned(MusicStream) then - MusicStream.Stop(); -end; - -procedure TAudioPlayback_Portaudio.Close; -begin - if assigned(MusicStream) then - begin - MixerStream.RemoveStream(MusicStream); - MusicStream.Close(); - end; -end; - -function TAudioPlayback_Portaudio.Finished: boolean; -begin - if assigned(MusicStream) then - Result := (MusicStream.GetStatus() = ssStopped) - else - Result := true; -end; - -//Equalizer -procedure TAudioPlayback_Portaudio.GetFFTData(var data: TFFTData); -begin - //Get Channel Data Mono and 256 Values -// BASS_ChannelGetData(Bass, @Result, BASS_DATA_FFT512); -end; - -// Interface for Visualizer -function TAudioPlayback_Portaudio.GetPCMData(var data: TPCMData): Cardinal; -begin - result := 0; -end; - -function TAudioPlayback_Portaudio.OpenSound(const Filename: String): TAudioPlaybackStream; -begin - result := Load(Filename); -end; - -procedure TAudioPlayback_Portaudio.PlaySound(stream: TAudioPlaybackStream); -begin - if assigned(stream) then - stream.Play(); -end; - -procedure TAudioPlayback_Portaudio.StopSound(stream: TAudioPlaybackStream); -begin - if assigned(stream) then - stream.Stop(); -end; - - -initialization - singleton_AudioPlaybackPortaudio := TAudioPlayback_Portaudio.create(); - AudioManager.add( singleton_AudioPlaybackPortaudio ); - -finalization - AudioManager.Remove( singleton_AudioPlaybackPortaudio ); - - -end. +unit UAudioPlayback_Portaudio;
+
+interface
+
+{$IFDEF FPC}
+ {$MODE Delphi}
+{$ENDIF}
+
+{$I switches.inc}
+
+
+uses
+ Classes,
+ SysUtils,
+ UMusic;
+
+implementation
+
+uses
+ portaudio,
+ UAudioPlayback_SoftMixer,
+ ULog,
+ UIni,
+ UMain;
+
+type
+ TAudioPlayback_Portaudio = class(TAudioPlayback_SoftMixer)
+ private
+ paStream: PPaStream;
+ protected
+ function InitializeAudioPlaybackEngine(): boolean; override;
+ function StartAudioPlaybackEngine(): boolean; override;
+ procedure StopAudioPlaybackEngine(); override;
+ public
+ function GetName: String; override;
+ end;
+
+var
+ singleton_AudioPlaybackPortaudio : IAudioPlayback;
+
+
+{ TAudioPlayback_Portaudio }
+
+function PortaudioAudioCallback(input: Pointer; output: Pointer; frameCount: Longword;
+ timeInfo: PPaStreamCallbackTimeInfo; statusFlags: TPaStreamCallbackFlags;
+ userData: Pointer): Integer; cdecl;
+var
+ engine: TAudioPlayback_Portaudio;
+begin
+ engine := TAudioPlayback_Portaudio(userData);
+ engine.AudioCallback(output, frameCount * engine.FormatInfo.FrameSize);
+ result := paContinue;
+end;
+
+function TAudioPlayback_Portaudio.GetName: String;
+begin
+ result := 'Portaudio_Playback';
+end;
+
+function TAudioPlayback_Portaudio.InitializeAudioPlaybackEngine(): boolean;
+var
+ paApi : TPaHostApiIndex;
+ paApiInfo : PPaHostApiInfo;
+ paOutParams : TPaStreamParameters;
+ paOutDevice : TPaDeviceIndex;
+ paOutDeviceInfo : PPaDeviceInfo;
+ err : TPaError;
+const
+ sampleFreq = 44100;
+begin
+ result := false;
+
+ Pa_Initialize();
+
+ // FIXME: determine automatically
+ {$IFDEF WIN32}
+ paApi := Pa_HostApiTypeIdToHostApiIndex(paDirectSound);
+ {$ELSE}
+ paApi := Pa_HostApiTypeIdToHostApiIndex(paALSA);
+ {$ENDIF}
+ if (paApi < 0) then
+ begin
+ Log.LogStatus('Pa_HostApiTypeIdToHostApiIndex: '+Pa_GetErrorText(paApi), 'UAudioPlayback_Portaudio');
+ exit;
+ end;
+
+ paApiInfo := Pa_GetHostApiInfo(paApi);
+ paOutDevice := paApiInfo^.defaultOutputDevice;
+ paOutDeviceInfo := Pa_GetDeviceInfo(paOutDevice);
+
+ with paOutParams do begin
+ device := paOutDevice;
+ channelCount := 2;
+ sampleFormat := paInt16;
+ suggestedLatency := paOutDeviceInfo^.defaultHighOutputLatency;
+ hostApiSpecificStreamInfo := nil;
+ end;
+
+ err := Pa_OpenStream(paStream, nil, @paOutParams, sampleFreq,
+ paFramesPerBufferUnspecified,
+ paNoFlag, @PortaudioAudioCallback, Self);
+ if(err <> paNoError) then begin
+ Log.LogStatus('Pa_OpenStream: '+Pa_GetErrorText(err), 'UAudioPlayback_Portaudio');
+ exit;
+ end;
+
+ FormatInfo := TAudioFormatInfo.Create(
+ paOutParams.channelCount,
+ sampleFreq,
+ asfS16 // FIXME: is paInt16 system-dependant or -independant?
+ );
+
+ Log.LogStatus('Opened audio device', 'UAudioPlayback_Portaudio');
+
+ result := true;
+end;
+
+function TAudioPlayback_Portaudio.StartAudioPlaybackEngine(): boolean;
+var
+ err: TPaError;
+begin
+ result := false;
+
+ if (paStream = nil) then
+ Exit;
+
+ err := Pa_StartStream(paStream);
+ if(err <> paNoError) then
+ begin
+ Log.LogStatus('Pa_StartStream: '+Pa_GetErrorText(err), 'UAudioPlayback_Portaudio');
+ exit;
+ end;
+
+ result := true;
+end;
+
+procedure TAudioPlayback_Portaudio.StopAudioPlaybackEngine();
+begin
+ if (paStream <> nil) then
+ Pa_StopStream(paStream);
+end;
+
+
+
+initialization
+ singleton_AudioPlaybackPortaudio := TAudioPlayback_Portaudio.create();
+ AudioManager.add( singleton_AudioPlaybackPortaudio );
+
+finalization
+ AudioManager.Remove( singleton_AudioPlaybackPortaudio );
+
+
+end.
|