diff options
-rw-r--r-- | Game/Code/Classes/UAudio_FFMpeg_Pa.pas | 1951 |
1 files changed, 980 insertions, 971 deletions
diff --git a/Game/Code/Classes/UAudio_FFMpeg_Pa.pas b/Game/Code/Classes/UAudio_FFMpeg_Pa.pas index 55ef347a..400af7ce 100644 --- a/Game/Code/Classes/UAudio_FFMpeg_Pa.pas +++ b/Game/Code/Classes/UAudio_FFMpeg_Pa.pas @@ -1,971 +1,980 @@ -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;
-
- result := paContinue;
-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.
+unit UAudio_FFMpeg_Pa; + +(******************************************************************************* + +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, + {$ifndef win32} + libc, // not available in win32 + {$endif} + {$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 := 0; + // Todo : why is Music stream always nil !? + + if assigned( MusicStream ) then + begin + Result := MusicStream.pFormatCtx^.duration / AV_TIME_BASE; + end; +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 * 2; // 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; + + result := paContinue; +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'); + writeln('ERROR : 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 2, //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. |