aboutsummaryrefslogtreecommitdiffstats
path: root/Game/Code/Classes
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--Game/Code/Classes/UAudio_FFMpeg_Pa.pas969
1 files changed, 969 insertions, 0 deletions
diff --git a/Game/Code/Classes/UAudio_FFMpeg_Pa.pas b/Game/Code/Classes/UAudio_FFMpeg_Pa.pas
new file mode 100644
index 00000000..a1682e78
--- /dev/null
+++ b/Game/Code/Classes/UAudio_FFMpeg_Pa.pas
@@ -0,0 +1,969 @@
+unit UAudio_FFMpeg;
+
+(*******************************************************************************
+
+This unit is primarily based upon -
+ http://www.dranger.com/ffmpeg/ffmpegtutorial_all.html
+
+ and tutorial03.c
+
+ http://www.inb.uni-luebeck.de/~boehme/using_libavcodec.html
+
+*******************************************************************************)
+
+interface
+
+{$IFDEF FPC}
+ {$MODE Delphi}
+{$ENDIF}
+
+{$I switches.inc}
+
+
+uses Classes,
+ {$IFDEF win32}
+ windows,
+ {$ENDIF}
+ Messages,
+ SysUtils,
+ {$IFNDEF FPC}
+ Forms,
+ {$ENDIF}
+ SDL, // Used for Audio output Interface
+ avcodec, // FFMpeg Audio file decoding
+ avformat,
+ avutil,
+ ULog,
+ UMusic;
+
+implementation
+
+uses
+ {$IFDEF LAZARUS}
+ lclintf,
+ //libc, // not available in win32
+ {$ENDIF}
+ portaudio,
+ UIni,
+ UMain,
+ UThemes;
+
+
+type
+ PPacketQueue = ^TPacketQueue;
+ TPacketQueue = class
+ private
+ firstPkt,
+ lastPkt : PAVPacketList;
+ nbPackets : integer;
+ size : integer;
+ mutex : PSDL_Mutex;
+ cond : PSDL_Cond;
+ quit : boolean;
+
+ public
+ constructor Create();
+
+ function Put(pkt : PAVPacket): integer;
+ function Get(var pkt: TAVPacket; block: boolean): integer;
+ end;
+
+type
+ TStreamStatus = (sNotReady, sStopped, sPlaying, sSeeking, sPaused, sOpen);
+
+const
+ StreamStatusStr: array[TStreamStatus] of string = ('Not ready', 'Stopped', 'Playing', 'Seeking', 'Paused', 'Open');
+
+type
+ PAudioBuffer = ^TAudioBuffer;
+ TAudioBuffer = array[0 .. (AVCODEC_MAX_AUDIO_FRAME_SIZE * 3 div 2)-1] of byte;
+
+const
+ SDL_AUDIO_BUFFER_SIZE = 1024;
+
+type
+ TFFMpegOutputStream = class(TAudioOutputStream)
+ private
+ parseThread: PSDL_Thread;
+ packetQueue: TPacketQueue;
+
+ status: TStreamStatus;
+
+ // FFMpeg internal data
+ pFormatCtx : PAVFormatContext;
+ pCodecCtx : PAVCodecContext;
+ pCodec : PAVCodec;
+ ffmpegStreamID : Integer;
+ ffmpegStream : PAVStream;
+
+ // static vars for AudioDecodeFrame
+ pkt : TAVPacket;
+ audio_pkt_data : PChar;
+ audio_pkt_size : integer;
+
+ // static vars for AudioCallback
+ audio_buf_index : cardinal;
+ audio_buf_size : cardinal;
+ audio_buf : TAudioBuffer;
+
+ channel: PPaStream;
+ public
+ constructor Create(); overload;
+ constructor Create(pFormatCtx: PAVFormatContext;
+ pCodecCtx: PAVCodecContext; pCodec: PAVCodec;
+ ffmpegStreamID : Integer; ffmpegStream: PAVStream); overload;
+
+ procedure Play();
+ procedure Pause();
+ procedure Stop();
+ procedure Close();
+
+ function AudioDecodeFrame(buffer : PUInt8; bufSize: integer): integer;
+ end;
+
+type
+ TAudio_FFMpeg = class( TInterfacedObject, IAudioPlayback )
+ private
+ MusicStream: TFFMpegOutputStream;
+
+ StartSoundStream: TFFMpegOutputStream;
+ BackSoundStream: TFFMpegOutputStream;
+ SwooshSoundStream: TFFMpegOutputStream;
+ ChangeSoundStream: TFFMpegOutputStream;
+ OptionSoundStream: TFFMpegOutputStream;
+ ClickSoundStream: TFFMpegOutputStream;
+ DrumSoundStream: TFFMpegOutputStream;
+ HihatSoundStream: TFFMpegOutputStream;
+ ClapSoundStream: TFFMpegOutputStream;
+ ShuffleSoundStream: TFFMpegOutputStream;
+
+ //Custom Sounds
+ CustomSounds: array of TCustomSoundEntry;
+ Loaded: boolean;
+ Loop: boolean;
+
+ function FindAudioStreamID(pFormatCtx : PAVFormatContext): integer;
+ public
+ function GetName: String;
+ procedure InitializePlayback;
+ procedure SetVolume(Volume: integer);
+ procedure SetMusicVolume(Volume: integer);
+ procedure SetLoop(Enabled: boolean);
+ function Open(Name: string): boolean; // true if succeed
+ procedure Rewind;
+ procedure MoveTo(Time: real);
+ procedure Play;
+ procedure Pause; //Pause Mod
+ procedure Stop;
+ procedure Close;
+ function Finished: boolean;
+ function Length: real;
+ function getPosition: real;
+ procedure PlayStart;
+ procedure PlayBack;
+ procedure PlaySwoosh;
+ procedure PlayChange;
+ procedure PlayOption;
+ procedure PlayClick;
+ procedure PlayDrum;
+ procedure PlayHihat;
+ procedure PlayClap;
+ procedure PlayShuffle;
+ procedure StopShuffle;
+
+ function LoadSoundFromFile(Name: string): TFFMpegOutputStream;
+
+ //Equalizer
+ function GetFFTData: TFFTData;
+
+ // Interface for Visualizer
+ function GetPCMData(var data: TPCMData): Cardinal;
+
+ //Custom Sounds
+ function LoadCustomSound(const Filename: String): Cardinal;
+ procedure PlayCustomSound(const Index: Cardinal );
+ end;
+
+var
+ test: TFFMpegOutputStream;
+ it: integer;
+
+var
+ singleton_MusicFFMpeg : IAudioPlayback = nil;
+
+
+function ParseAudio(data: Pointer): integer; cdecl; forward;
+procedure SDL_AudioCallback( userdata: Pointer; stream: PUInt8; len: Integer ); cdecl; forward;
+function Pa_AudioCallback(input: Pointer; output: Pointer; frameCount: Longword;
+ timeInfo: PPaStreamCallbackTimeInfo; statusFlags: TPaStreamCallbackFlags;
+ userData: Pointer): Integer; cdecl; forward;
+
+constructor TFFMpegOutputStream.Create();
+begin
+ inherited;
+
+ packetQueue := TPacketQueue.Create();
+
+ FillChar(pkt, sizeof(TAVPacket), #0);
+
+ status := sStopped;
+
+ audio_pkt_data := nil;
+ audio_pkt_size := 0;
+
+ audio_buf_index := 0;
+ audio_buf_size := 0;
+end;
+
+constructor TFFMpegOutputStream.Create(pFormatCtx: PAVFormatContext;
+ pCodecCtx: PAVCodecContext; pCodec: PAVCodec;
+ ffmpegStreamID : Integer; ffmpegStream: PAVStream);
+begin
+ Create();
+
+ Self.pFormatCtx := pFormatCtx;
+ Self.pCodecCtx := pCodecCtx;
+ Self.pCodec := pCodec;
+ Self.ffmpegStreamID := ffmpegStreamID;
+ Self.ffmpegStream := ffmpegStream;
+
+ test := Self;
+ it:=0;
+end;
+
+procedure TFFMpegOutputStream.Play();
+var
+ err: TPaError;
+begin
+ writeln('Play request');
+ if(status = sStopped) then
+ begin
+ writeln('Play ok');
+ status := sPlaying;
+ Self.parseThread := SDL_CreateThread(@ParseAudio, Self);
+ //SDL_PauseAudio(0);
+
+ err := Pa_StartStream(Self.channel);
+ end;
+end;
+
+procedure TFFMpegOutputStream.Pause();
+begin
+end;
+
+procedure TFFMpegOutputStream.Stop();
+begin
+end;
+
+procedure TFFMpegOutputStream.Close();
+begin
+ // Close the codec
+ avcodec_close(pCodecCtx);
+
+ // Close the video file
+ av_close_input_file(pFormatCtx);
+end;
+
+
+function TAudio_FFMpeg.GetName: String;
+begin
+ result := 'FFMpeg';
+end;
+
+procedure TAudio_FFMpeg.InitializePlayback;
+begin
+ Log.LogStatus('InitializePlayback', 'UAudio_FFMpeg');
+
+ Loaded := false;
+ Loop := false;
+
+ av_register_all();
+ //SDL_Init(SDL_INIT_AUDIO);
+ Pa_Initialize();
+
+ StartSoundStream := LoadSoundFromFile(SoundPath + 'Common Start.mp3');
+ {
+ BackSoundStream := LoadSoundFromFile(SoundPath + 'Common Back.mp3');
+ SwooshSoundStream := LoadSoundFromFile(SoundPath + 'menu swoosh.mp3');
+ ChangeSoundStream := LoadSoundFromFile(SoundPath + 'select music change music 50.mp3');
+ OptionSoundStream := LoadSoundFromFile(SoundPath + 'option change col.mp3');
+ ClickSoundStream := LoadSoundFromFile(SoundPath + 'rimshot022b.mp3');
+ }
+// DrumSoundStream := LoadSoundFromFile(SoundPath + 'bassdrumhard076b.mp3');
+// HihatSoundStream := LoadSoundFromFile(SoundPath + 'hihatclosed068b.mp3');
+// ClapSoundStream := LoadSoundFromFile(SoundPath + 'claps050b.mp3');
+
+// ShuffleSoundStream := LoadSoundFromFile(SoundPath + 'Shuffle.mp3');
+end;
+
+
+procedure TAudio_FFMpeg.SetVolume(Volume: integer);
+begin
+ //New: Sets Volume only for this Application
+(*
+ BASS_SetConfig(BASS_CONFIG_GVOL_SAMPLE, Volume);
+ BASS_SetConfig(BASS_CONFIG_GVOL_STREAM, Volume);
+ BASS_SetConfig(BASS_CONFIG_GVOL_MUSIC, Volume);
+*)
+end;
+
+procedure TAudio_FFMpeg.SetMusicVolume(Volume: Integer);
+begin
+ //Max Volume Prevention
+ if Volume > 100 then
+ Volume := 100;
+
+ if Volume < 0 then
+ Volume := 0;
+
+
+ //Set Volume
+// BASS_ChannelSetAttributes (Bass, -1, Volume, -101);
+end;
+
+procedure TAudio_FFMpeg.SetLoop(Enabled: boolean);
+begin
+ Loop := Enabled;
+end;
+
+function TAudio_FFMpeg.Open(Name: string): boolean;
+begin
+ Loaded := false;
+ if FileExists(Name) then
+ begin
+// Bass := Bass_StreamCreateFile(false, pchar(Name), 0, 0, 0);
+
+ Loaded := true;
+ //Set Max Volume
+ SetMusicVolume (100);
+ end;
+
+ Result := Loaded;
+end;
+
+procedure TAudio_FFMpeg.Rewind;
+begin
+ if Loaded then
+ begin
+ end;
+end;
+
+procedure TAudio_FFMpeg.MoveTo(Time: real);
+var
+ bytes: integer;
+begin
+// bytes := BASS_ChannelSeconds2Bytes(Bass, Time);
+// BASS_ChannelSetPosition(Bass, bytes);
+end;
+
+procedure TAudio_FFMpeg.Play;
+begin
+ if MusicStream <> nil then
+ if Loaded then
+ begin
+ if Loop then
+ begin
+ end;
+ // start from beginning...
+ // actually bass itself does not loop, nor does this TAudio_FFMpeg Class
+ MusicStream.Play();
+ end;
+end;
+
+procedure TAudio_FFMpeg.Pause; //Pause Mod
+begin
+ if MusicStream <> nil then
+ if Loaded then begin
+ MusicStream.Pause(); // Pauses Song
+ end;
+end;
+
+procedure TAudio_FFMpeg.Stop;
+begin
+ if MusicStream <> nil then
+ MusicStream.Stop();
+end;
+
+procedure TAudio_FFMpeg.Close;
+begin
+ if MusicStream <> nil then
+ MusicStream.Close();
+end;
+
+function TAudio_FFMpeg.Length: real;
+var
+ bytes: integer;
+begin
+ Result := MusicStream.pFormatCtx^.duration / AV_TIME_BASE;
+end;
+
+function TAudio_FFMpeg.getPosition: real;
+var
+ bytes: integer;
+begin
+ Result := 0;
+
+(*
+ bytes := BASS_ChannelGetPosition(BASS);
+ Result := BASS_ChannelBytes2Seconds(BASS, bytes);
+*)
+end;
+
+function TAudio_FFMpeg.Finished: boolean;
+begin
+ Result := false;
+
+(*
+ if BASS_ChannelIsActive(BASS) = BASS_ACTIVE_STOPPED then
+ begin
+ Result := true;
+ end;
+*)
+end;
+
+procedure TAudio_FFMpeg.PlayStart;
+begin
+ if StartSoundStream <> nil then
+ StartSoundStream.Play();
+end;
+
+procedure TAudio_FFMpeg.PlayBack;
+begin
+ if BackSoundStream <> nil then
+ BackSoundStream.Play();
+end;
+
+procedure TAudio_FFMpeg.PlaySwoosh;
+begin
+ if SwooshSoundStream <> nil then
+ SwooshSoundStream.Play();
+end;
+
+procedure TAudio_FFMpeg.PlayChange;
+begin
+ if ChangeSoundStream <> nil then
+ ChangeSoundStream.Play();
+end;
+
+procedure TAudio_FFMpeg.PlayOption;
+begin
+ if OptionSoundStream <> nil then
+ OptionSoundStream.Play();
+end;
+
+procedure TAudio_FFMpeg.PlayClick;
+begin
+ if ClickSoundStream <> nil then
+ ClickSoundStream.Play();
+end;
+
+procedure TAudio_FFMpeg.PlayDrum;
+begin
+ if DrumSoundStream <> nil then
+ DrumSoundStream.Play();
+end;
+
+procedure TAudio_FFMpeg.PlayHihat;
+begin
+ if HihatSoundStream <> nil then
+ HihatSoundStream.Play();
+end;
+
+procedure TAudio_FFMpeg.PlayClap;
+begin
+ if ClapSoundStream <> nil then
+ ClapSoundStream.Play();
+end;
+
+procedure TAudio_FFMpeg.PlayShuffle;
+begin
+ if ShuffleSoundStream <> nil then
+ ShuffleSoundStream.Play();
+end;
+
+procedure TAudio_FFMpeg.StopShuffle;
+begin
+ if ShuffleSoundStream <> nil then
+ ShuffleSoundStream.Stop();
+end;
+
+
+function TFFMpegOutputStream.AudioDecodeFrame(buffer : PUInt8; bufSize: integer): integer;
+var
+ len1,
+ data_size: integer;
+begin
+ result := -1;
+
+ if (buffer = nil) then
+ exit;
+
+ while true do
+ begin
+ while (audio_pkt_size > 0) do
+ begin
+// writeln( 'got audio packet' );
+ data_size := bufSize;
+
+ if(pCodecCtx = nil) then begin
+ writeln('Das wars');
+ exit;
+ end;
+
+ // TODO: should be avcodec_decode_audio2 but this wont link on my ubuntu box.
+ len1 := avcodec_decode_audio(pCodecCtx, Pointer(buffer),
+ data_size, audio_pkt_data, audio_pkt_size);
+
+ //writeln('avcodec_decode_audio : ' + inttostr( len1 ));
+
+ if(len1 < 0) then
+ begin
+ // if error, skip frame
+// writeln( 'Skip audio frame' );
+ audio_pkt_size := 0;
+ break;
+ end;
+
+ audio_pkt_data := audio_pkt_data + len1;
+ audio_pkt_size := audio_pkt_size + len1;
+
+ if (data_size <= 0) then
+ begin
+ // No data yet, get more frames
+ continue;
+ end;
+
+ // We have data, return it and come back for more later
+ result := data_size;
+ exit;
+ end;
+
+ inc(it);
+ if (pkt.data <> nil) then
+ begin
+ av_free_packet(pkt);
+ end;
+
+ if (packetQueue.quit) then
+ begin
+ result := -1;
+ exit;
+ end;
+ writeln(it);
+ if (packetQueue.Get(pkt, true) < 0) then
+ begin
+ result := -1;
+ exit;
+ end;
+
+ audio_pkt_data := PChar(pkt.data);
+ audio_pkt_size := pkt.size;
+// writeln( 'Audio Packet Size - ' + inttostr(audio_pkt_size) );
+ end;
+end;
+
+procedure SDL_AudioCallback(userdata: Pointer; stream: PUInt8; len: Integer); cdecl;
+var
+ outStream : TFFMpegOutputStream;
+ len1,
+ audio_size : integer;
+ pSrc : Pointer;
+begin
+ outStream := TFFMpegOutputStream(userdata);
+
+ while (len > 0) do
+ with outStream do begin
+ if (audio_buf_index >= audio_buf_size) then
+ begin
+ // We have already sent all our data; get more
+ audio_size := AudioDecodeFrame(@audio_buf, sizeof(TAudioBuffer));
+ //writeln('audio_decode_frame : '+ inttostr(audio_size));
+
+ if(audio_size < 0) then
+ begin
+ // If error, output silence
+ audio_buf_size := 1024;
+ FillChar(audio_buf, audio_buf_size, #0);
+ //writeln( 'Silence' );
+ end
+ else
+ begin
+ audio_buf_size := audio_size;
+ end;
+ audio_buf_index := 0; // Todo : jb - SegFault ?
+ end;
+
+ len1 := audio_buf_size - audio_buf_index;
+ if (len1 > len) then
+ len1 := len;
+
+ pSrc := PChar(@audio_buf) + audio_buf_index;
+ {$ifdef WIN32}
+ CopyMemory(stream, pSrc , len1);
+ {$else}
+ memcpy(stream, pSrc , len1);
+ {$endif}
+
+ Dec(len, len1);
+ Inc(stream, len1);
+ Inc(audio_buf_index, len1);
+ end;
+end;
+
+function Pa_AudioCallback(input: Pointer; output: Pointer; frameCount: Longword;
+ timeInfo: PPaStreamCallbackTimeInfo; statusFlags: TPaStreamCallbackFlags;
+ userData: Pointer): Integer; cdecl;
+var
+ outStream : TFFMpegOutputStream;
+ len1,
+ audio_size : integer;
+ pSrc : Pointer;
+ len : integer;
+begin
+ outStream := TFFMpegOutputStream(userData);
+ len := frameCount * 4; // use *2 for mono-files
+
+ while (len > 0) do
+ with outStream do begin
+ if (audio_buf_index >= audio_buf_size) then
+ begin
+ // We have already sent all our data; get more
+ audio_size := AudioDecodeFrame(@audio_buf, sizeof(TAudioBuffer));
+ //writeln('audio_decode_frame : '+ inttostr(audio_size));
+
+ if(audio_size < 0) then
+ begin
+ // If error, output silence
+ audio_buf_size := 1024;
+ FillChar(audio_buf, audio_buf_size, #0);
+ //writeln( 'Silence' );
+ end
+ else
+ begin
+ audio_buf_size := audio_size;
+ end;
+ audio_buf_index := 0; // Todo : jb - SegFault ?
+ end;
+
+ len1 := audio_buf_size - audio_buf_index;
+ if (len1 > len) then
+ len1 := len;
+
+ pSrc := PChar(@audio_buf) + audio_buf_index;
+ {$ifdef WIN32}
+ CopyMemory(output, pSrc , len1);
+ {$else}
+ memcpy(output, pSrc , len1);
+ {$endif}
+
+ Dec(len, len1);
+ Inc(PChar(output), len1);
+ Inc(audio_buf_index, len1);
+ end;
+end;
+
+function TAudio_FFMpeg.FindAudioStreamID(pFormatCtx : PAVFormatContext): integer;
+var
+ i : integer;
+ streamID: integer;
+ stream : PAVStream;
+begin
+ // Find the first audio stream
+ streamID := -1;
+
+ for i := 0 to pFormatCtx^.nb_streams-1 do
+ begin
+ //Log.LogStatus('aFormatCtx.streams[i] : ' + inttostr(i), 'UAudio_FFMpeg');
+ stream := pFormatCtx^.streams[i];
+
+ if ( stream.codec^.codec_type = CODEC_TYPE_AUDIO ) then
+ begin
+ Log.LogStatus('Found Audio Stream', 'UAudio_FFMpeg');
+ streamID := i;
+ break;
+ end;
+ end;
+
+ result := streamID;
+end;
+
+function ParseAudio(data: Pointer): integer; cdecl;
+var
+ packet: TAVPacket;
+ stream: TFFMpegOutputStream;
+begin
+ stream := TFFMpegOutputStream(data);
+
+ while (av_read_frame(stream.pFormatCtx, packet) >= 0) do
+ begin
+ //writeln( 'ffmpeg - av_read_frame' );
+
+ if (packet.stream_index = stream.ffmpegStreamID) then
+ begin
+ //writeln( 'packet_queue_put' );
+ stream.packetQueue.put(@packet);
+ end
+ else
+ begin
+ av_free_packet(packet);
+ end;
+ end;
+
+ //Writeln('Done: ' + inttostr(stream.packetQueue.nbPackets));
+
+ result := 0;
+end;
+
+
+function TAudio_FFMpeg.LoadSoundFromFile(Name: string): TFFMpegOutputStream;
+var
+ pFormatCtx : PAVFormatContext;
+ pCodecCtx : PAVCodecContext;
+ pCodec : PAVCodec;
+ ffmpegStreamID : Integer;
+ ffmpegStream : PAVStream;
+ wanted_spec,
+ spec : TSDL_AudioSpec;
+ csIndex : integer;
+ stream : TFFMpegOutputStream;
+ err : TPaError;
+begin
+ result := nil;
+
+ if (not FileExists(Name)) then
+ begin
+ Log.LogStatus('LoadSoundFromFile: Sound not found "' + Name + '"', 'UAudio_FFMpeg');
+ exit;
+ end;
+
+ // Open audio file
+ if (av_open_input_file(pFormatCtx, PChar(Name), nil, 0, nil) > 0) then
+ exit;
+
+ // Retrieve stream information
+ if (av_find_stream_info(pFormatCtx) < 0) then
+ exit;
+
+ dump_format(pFormatCtx, 0, pchar(Name), 0);
+
+ ffmpegStreamID := FindAudioStreamID(pFormatCtx);
+ if (ffmpegStreamID < 0) then
+ exit;
+
+ //Log.LogStatus('Audio Stream ID is : '+ inttostr(ffmpegStreamID), 'UAudio_FFMpeg');
+
+ ffmpegStream := pFormatCtx.streams[ffmpegStreamID];
+ pCodecCtx := ffmpegStream^.codec;
+
+ pCodec := avcodec_find_decoder(pCodecCtx^.codec_id);
+ if (pCodec = nil) then
+ begin
+ Log.LogStatus('Unsupported codec!', 'UAudio_FFMpeg');
+ exit;
+ end;
+
+ avcodec_open(pCodecCtx, pCodec);
+ //writeln( 'Opened the codec' );
+
+ stream := TFFMpegOutputStream.Create(pFormatCtx, pCodecCtx, pCodec,
+ ffmpegStreamID, ffmpegStream);
+
+ {
+ // Set SDL audio settings from codec info
+ wanted_spec.freq := pCodecCtx^.sample_rate;
+ wanted_spec.format := AUDIO_S16SYS;
+ wanted_spec.channels := pCodecCtx^.channels;
+ wanted_spec.silence := 0;
+ wanted_spec.samples := SDL_AUDIO_BUFFER_SIZE;
+ wanted_spec.callback := AudioCallback;
+ wanted_spec.userdata := stream;
+
+ // TODO: this works only one time (?)
+ if (SDL_OpenAudio(@wanted_spec, @spec) < 0) then
+ begin
+ Log.LogStatus('SDL_OpenAudio: '+SDL_GetError(), 'UAudio_FFMpeg');
+ stream.Free();
+ exit;
+ end;
+ }
+
+ err := Pa_OpenDefaultStream(stream.channel, 0, pCodecCtx^.channels, paInt16,
+ pCodecCtx^.sample_rate, SDL_AUDIO_BUFFER_SIZE div 4, //paFramesPerBufferUnspecified,
+ @PA_AudioCallback, stream);
+ if(err <> paNoError) then begin
+ Log.LogStatus('Pa_OpenDefaultStream: '+Pa_GetErrorText(err), 'UAudio_FFMpeg');
+ stream.Free();
+ exit;
+ end;
+
+ Log.LogStatus('SDL opened audio device', 'UAudio_FFMpeg');
+
+ //Add CustomSound
+ csIndex := High(CustomSounds) + 1;
+ SetLength (CustomSounds, csIndex + 1);
+ CustomSounds[csIndex].Filename := Name;
+ CustomSounds[csIndex].Stream := stream;
+
+ result := stream;
+end;
+
+//Equalizer
+function TAudio_FFMpeg.GetFFTData: TFFTData;
+var
+ data: TFFTData;
+begin
+ //Get Channel Data Mono and 256 Values
+// BASS_ChannelGetData(Bass, @Result, BASS_DATA_FFT512);
+ result := data;
+end;
+
+// Interface for Visualizer
+function TAudio_FFMpeg.GetPCMData(var data: TPCMData): Cardinal;
+begin
+ result := 0;
+end;
+
+function TAudio_FFMpeg.LoadCustomSound(const Filename: String): Cardinal;
+var
+ S: TFFMpegOutputStream;
+ I: Integer;
+ F: String;
+begin
+ //Search for Sound in already loaded Sounds
+ F := UpperCase(SoundPath + FileName);
+ For I := 0 to High(CustomSounds) do
+ begin
+ if (UpperCase(CustomSounds[I].Filename) = F) then
+ begin
+ Result := I;
+ Exit;
+ end;
+ end;
+
+ S := LoadSoundFromFile(SoundPath + Filename);
+ if (S <> nil) then
+ Result := High(CustomSounds)
+ else
+ Result := 0;
+end;
+
+procedure TAudio_FFMpeg.PlayCustomSound(const Index: Cardinal );
+begin
+ if Index <= High(CustomSounds) then
+ (CustomSounds[Index].Stream as TFFMpegOutputStream).Play();
+end;
+
+
+constructor TPacketQueue.Create();
+begin
+ inherited;
+
+ firstPkt := nil;
+ lastPkt := nil;
+ nbPackets := 0;
+ size := 0;
+
+ mutex := SDL_CreateMutex();
+ cond := SDL_CreateCond();
+end;
+
+function TPacketQueue.Put(pkt : PAVPacket): integer;
+var
+ pkt1 : PAVPacketList;
+begin
+ result := -1;
+
+ if (av_dup_packet(pkt) < 0) then
+ exit;
+
+ pkt1 := av_malloc(sizeof(TAVPacketList));
+ if (pkt1 = nil) then
+ exit;
+
+ pkt1^.pkt := pkt^;
+ pkt1^.next := nil;
+
+
+ SDL_LockMutex(Self.mutex);
+ try
+
+ if (Self.lastPkt = nil) then
+ Self.firstPkt := pkt1
+ else
+ Self.lastPkt^.next := pkt1;
+
+ Self.lastPkt := pkt1;
+ inc(Self.nbPackets);
+
+ Writeln('Put: ' + inttostr(nbPackets));
+
+ Self.size := Self.size + pkt1^.pkt.size;
+ SDL_CondSignal(Self.cond);
+
+ finally
+ SDL_UnlockMutex(Self.mutex);
+ end;
+
+ result := 0;
+end;
+
+function TPacketQueue.Get(var pkt: TAVPacket; block: boolean): integer;
+var
+ pkt1 : PAVPacketList;
+begin
+ result := -1;
+
+ SDL_LockMutex(Self.mutex);
+ try
+ while true do
+ begin
+ if (quit) then
+ exit;
+
+ pkt1 := Self.firstPkt;
+
+ if (pkt1 <> nil) then
+ begin
+ Self.firstPkt := pkt1.next;
+ if (Self.firstPkt = nil) then
+ Self.lastPkt := nil;
+ dec(Self.nbPackets);
+
+ Writeln('Get: ' + inttostr(nbPackets));
+
+ Self.size := Self.size - pkt1^.pkt.size;
+ pkt := pkt1^.pkt;
+ av_free(pkt1);
+
+ result := 1;
+ break;
+ end
+ else
+ if (not block) then
+ begin
+ result := 0;
+ break;
+ end
+ else
+ begin
+ SDL_CondWait(Self.cond, Self.mutex);
+ end;
+ end;
+ finally
+ SDL_UnlockMutex(Self.mutex);
+ end;
+end;
+
+
+
+initialization
+ singleton_MusicFFMpeg := TAudio_FFMpeg.create();
+
+ writeln( 'UAudio_FFMpeg - Register Playback' );
+ AudioManager.add( IAudioPlayback( singleton_MusicFFMpeg ) );
+
+finalization
+ AudioManager.Remove( IAudioPlayback( singleton_MusicFFMpeg ) );
+
+
+end.