From ff1dc556936cef441e105a19aa9d1d3b9e0cc833 Mon Sep 17 00:00:00 2001 From: tobigun Date: Fri, 11 Jan 2008 13:52:44 +0000 Subject: changed from ffmpeg-free (audio) to ffmpeg-enabled (if turned on in switches.inc) version. BASS is used by default. git-svn-id: svn://svn.code.sf.net/p/ultrastardx/svn/trunk@786 b956fd51-792f-4845-bead-9b4dfca2ff2c --- Game/Code/Classes/UAudio_FFMpeg.pas | 1005 -------------------------------- Game/Code/Classes/UAudio_bass.pas | 703 ---------------------- Game/Code/Classes/UAudio_portaudio.pas | 427 -------------- Game/Code/Classes/UMedia_dummy.pas | 13 +- Game/Code/Classes/UMusic.pas | 112 +++- Game/Code/Classes/UVideo.pas | 12 +- Game/Code/Classes/UVisualizer.pas | 15 +- 7 files changed, 117 insertions(+), 2170 deletions(-) delete mode 100644 Game/Code/Classes/UAudio_FFMpeg.pas delete mode 100644 Game/Code/Classes/UAudio_bass.pas delete mode 100644 Game/Code/Classes/UAudio_portaudio.pas (limited to 'Game/Code/Classes') diff --git a/Game/Code/Classes/UAudio_FFMpeg.pas b/Game/Code/Classes/UAudio_FFMpeg.pas deleted file mode 100644 index fb25b0f9..00000000 --- a/Game/Code/Classes/UAudio_FFMpeg.pas +++ /dev/null @@ -1,1005 +0,0 @@ -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, - {$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; - - paStream : PPaStream; - paFrameSize : integer; - 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.paStream); - if(err <> paNoError) then - begin - Writeln('Play: '+ Pa_GetErrorText(err)); - end; - 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; - - Inc(audio_pkt_data, len1); - Dec(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; - 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 * outStream.paFrameSize; - - 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; - // move this to a portaudio specific section - paOutParams : TPaStreamParameters; - paApiInfo : PPaHostApiInfo; - paApi : TPaHostApiIndex; - paOutDevice : TPaDeviceIndex; - paOutDeviceInfo : PPaDeviceInfo; -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; - } - - paApi := Pa_HostApiTypeIdToHostApiIndex(paALSA); - paApiInfo := Pa_GetHostApiInfo(paApi); - paOutDevice := paApiInfo^.defaultOutputDevice; - paOutDeviceInfo := Pa_GetDeviceInfo(paOutDevice); - - with paOutParams do begin - device := paOutDevice; - channelCount := pCodecCtx^.channels; - sampleFormat := paInt16; - suggestedLatency := paOutDeviceInfo^.defaultHighOutputLatency; - hostApiSpecificStreamInfo := nil; - end; - - stream.paFrameSize := sizeof(Smallint) * pCodecCtx^.channels; - - err := Pa_OpenStream(stream.paStream, nil, @paOutParams, pCodecCtx^.sample_rate, - paFramesPerBufferUnspecified, //SDL_AUDIO_BUFFER_SIZE div stream.paFrameSize - paNoFlag, @PA_AudioCallback, stream); - if(err <> paNoError) then begin - Log.LogStatus('Pa_OpenDefaultStream: '+Pa_GetErrorText(err), 'UAudio_FFMpeg'); - stream.Free(); - exit; - end; - - Log.LogStatus('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. diff --git a/Game/Code/Classes/UAudio_bass.pas b/Game/Code/Classes/UAudio_bass.pas deleted file mode 100644 index 972d6df0..00000000 --- a/Game/Code/Classes/UAudio_bass.pas +++ /dev/null @@ -1,703 +0,0 @@ -unit UAudio_bass; - -interface - -{$IFDEF FPC} - {$MODE Delphi} -{$ENDIF} - -{$I switches.inc} - - -uses Classes, - {$IFDEF win32} - windows, - {$ENDIF} - Messages, - SysUtils, - {$IFNDEF FPC} - Forms, - {$ENDIF} - bass, - ULog, - UMusic; - -implementation - -uses - {$IFDEF LAZARUS} - lclintf, - {$ENDIF} - URecord, - UIni, - UMain, - UCommon, - UThemes; - -type - TMPModes = (mpNotReady, mpStopped, mpPlaying, mpRecording, mpSeeking, - mpPaused, mpOpen); - -const - ModeStr: array[TMPModes] of string = ('Not ready', 'Stopped', 'Playing', 'Recording', 'Seeking', 'Paused', 'Open'); - -type - TBassOutputStream = class(TAudioOutputStream) - Handle: HSTREAM; - - constructor Create(); overload; - constructor Create(stream: HSTREAM); overload; - end; - -type -{$IFDEF UseBASSInput} - TAudio_bass = class( TInterfacedObject, IAudioPlayback, IAudioInput) -{$ELSE} - TAudio_bass = class( TInterfacedObject, IAudioPlayback) -{$ENDIF} - private - MusicStream: HSTREAM; - - StartSoundStream: HSTREAM; - BackSoundStream: HSTREAM; - SwooshSoundStream: HSTREAM; - ChangeSoundStream: HSTREAM; - OptionSoundStream: HSTREAM; - ClickSoundStream: HSTREAM; - DrumSoundStream: HSTREAM; - HihatSoundStream: HSTREAM; - ClapSoundStream: HSTREAM; - ShuffleSoundStream: HSTREAM; - - //Custom Sounds - CustomSounds: array of TCustomSoundEntry; - Loaded: boolean; - Loop: boolean; - - public - function GetName: String; - - {IAudioOutput interface} - - 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(var stream: HSTREAM; Name: string): boolean; - - //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 ); - - {IAudioInput interface} - {$IFDEF UseBASSInput} - procedure InitializeRecord; - - procedure CaptureStart; - procedure CaptureStop; - procedure CaptureCard(Card: byte; CaptureSoundLeft, CaptureSoundRight: TSound); - procedure StopCard(Card: byte); - {$ENDIF} - end; - -{$IFDEF UseBASSInput} - TBassSoundCard = class(TGenericSoundCard) - RecordStream: HSTREAM; - end; -{$ENDIF} - - -var - singleton_MusicBass : IAudioPlayback; - - -constructor TBassOutputStream.Create(); -begin - inherited; -end; - -constructor TBassOutputStream.Create(stream: HSTREAM); -begin - Create(); - Handle := stream; -end; - - -function TAudio_bass.GetName: String; -begin - result := 'BASS'; -end; - -procedure TAudio_bass.InitializePlayback; -var - Pet: integer; - S: integer; -begin -// Log.BenchmarkStart(4); -// Log.LogStatus('Initializing Playback Subsystem', 'Music Initialize'); - - Loaded := false; - Loop := false; - - if not BASS_Init(1, 44100, 0, 0, nil) then - begin - Log.LogError('Could not initialize BASS', 'Error'); - Exit; - end; - -// Log.BenchmarkEnd(4); Log.LogBenchmark('--> Bass Init', 4); - - // config playing buffer -// BASS_SetConfig(BASS_CONFIG_UPDATEPERIOD, 10); -// BASS_SetConfig(BASS_CONFIG_BUFFER, 100); - -// Log.LogStatus('Loading Sounds', 'Music Initialize'); - -// Log.BenchmarkStart(4); - LoadSoundFromFile(StartSoundStream, SoundPath + 'Common Start.mp3'); - LoadSoundFromFile(BackSoundStream, SoundPath + 'Common Back.mp3'); - LoadSoundFromFile(SwooshSoundStream, SoundPath + 'menu swoosh.mp3'); - LoadSoundFromFile(ChangeSoundStream, SoundPath + 'select music change music 50.mp3'); - LoadSoundFromFile(OptionSoundStream, SoundPath + 'option change col.mp3'); - LoadSoundFromFile(ClickSoundStream, SoundPath + 'rimshot022b.mp3'); - -// LoadSoundFromFile(DrumSoundStream, SoundPath + 'bassdrumhard076b.mp3'); -// LoadSoundFromFile(HihatSoundStream, SoundPath + 'hihatclosed068b.mp3'); -// LoadSoundFromFile(ClapSoundStream, SoundPath + 'claps050b.mp3'); - -// LoadSoundFromFile(ShuffleSoundStream, SoundPath + 'Shuffle.mp3'); - -// Log.BenchmarkEnd(4); -// Log.LogBenchmark('--> Loading Sounds', 4); -end; - -procedure TAudio_bass.SetVolume(Volume: integer); -begin - //Old Sets Wave Volume - //BASS_SetVolume(Volume); - //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_bass.SetMusicVolume(Volume: Integer); -begin - //Max Volume Prevention - if Volume > 100 then - Volume := 100; - - if Volume < 0 then - Volume := 0; - - //Set Volume - BASS_ChannelSetAttributes (MusicStream, -1, Volume, -101); -end; - -procedure TAudio_bass.SetLoop(Enabled: boolean); -begin - Loop := Enabled; -end; - -function TAudio_bass.Open(Name: string): boolean; -begin - Loaded := false; - if FileExists(Name) then - begin - MusicStream := Bass_StreamCreateFile(false, pchar(Name), 0, 0, 0); - - Loaded := true; - //Set Max Volume - SetMusicVolume (100); - end; - - Result := Loaded; -end; - -procedure TAudio_bass.Rewind; -begin - if Loaded then begin - end; -end; - -procedure TAudio_bass.MoveTo(Time: real); -var - bytes: integer; -begin - bytes := BASS_ChannelSeconds2Bytes(MusicStream, Time); - BASS_ChannelSetPosition(MusicStream, bytes); -end; - -procedure TAudio_bass.Play; -begin - if Loaded then - begin - if Loop then - BASS_ChannelPlay(MusicStream, True); // start from beginning... actually bass itself does not loop, nor does this TAudio_bass Class - - BASS_ChannelPlay(MusicStream, False); // for setting position before playing - end; -end; - -procedure TAudio_bass.Pause; //Pause Mod -begin - if Loaded then begin - BASS_ChannelPause(MusicStream); // Pauses Song - end; -end; - -procedure TAudio_bass.Stop; -begin - Bass_ChannelStop(MusicStream); -end; - -procedure TAudio_bass.Close; -begin - Bass_StreamFree(MusicStream); -end; - -function TAudio_bass.Length: real; -var - bytes: integer; -begin - Result := 60; - - bytes := BASS_ChannelGetLength(MusicStream); - Result := BASS_ChannelBytes2Seconds(MusicStream, bytes); -end; - -function TAudio_bass.getPosition: real; -var - bytes: integer; -begin - Result := 0; - - bytes := BASS_ChannelGetPosition(MusicStream); - Result := BASS_ChannelBytes2Seconds(MusicStream, bytes); -end; - -function TAudio_bass.Finished: boolean; -begin - Result := false; - - if BASS_ChannelIsActive(MusicStream) = BASS_ACTIVE_STOPPED then - begin - Result := true; - end; -end; - -procedure TAudio_bass.PlayStart; -begin - BASS_ChannelPlay(StartSoundStream, True); -end; - -procedure TAudio_bass.PlayBack; -begin - BASS_ChannelPlay(BackSoundStream, True);// then -end; - -procedure TAudio_bass.PlaySwoosh; -begin - BASS_ChannelPlay(SwooshSoundStream, True); -end; - -procedure TAudio_bass.PlayChange; -begin - BASS_ChannelPlay(ChangeSoundStream, True); -end; - -procedure TAudio_bass.PlayOption; -begin - BASS_ChannelPlay(OptionSoundStream, True); -end; - -procedure TAudio_bass.PlayClick; -begin - BASS_ChannelPlay(ClickSoundStream, True); -end; - -procedure TAudio_bass.PlayDrum; -begin - BASS_ChannelPlay(DrumSoundStream, True); -end; - -procedure TAudio_bass.PlayHihat; -begin - BASS_ChannelPlay(HihatSoundStream, True); -end; - -procedure TAudio_bass.PlayClap; -begin - BASS_ChannelPlay(ClapSoundStream, True); -end; - -procedure TAudio_bass.PlayShuffle; -begin - BASS_ChannelPlay(ShuffleSoundStream, True); -end; - -procedure TAudio_bass.StopShuffle; -begin - BASS_ChannelStop(ShuffleSoundStream); -end; - -function TAudio_bass.LoadSoundFromFile(var stream: HSTREAM; Name: string): boolean; -var - L: Integer; -begin - if FileExists(Name) then - begin - Log.LogStatus('Loading Sound: "' + Name + '"', 'LoadSoundFromFile'); - try - stream := BASS_StreamCreateFile(False, pchar(Name), 0, 0, 0); - - //Add CustomSound - L := High(CustomSounds) + 1; - SetLength (CustomSounds, L + 1); - CustomSounds[L].Filename := Name; - CustomSounds[L].Stream := TBassOutputStream.Create(stream); - except - Log.LogError('Failed to open using BASS', 'LoadSoundFromFile'); - end; - end - else - begin - Log.LogError('Sound not found: "' + Name + '"', 'LoadSoundFromFile'); - exit; - end; -end; - -//Equalizer -function TAudio_bass.GetFFTData: TFFTData; -var - Data: TFFTData; -begin - //Get Channel Data Mono and 256 Values - BASS_ChannelGetData(MusicStream, @Result, BASS_DATA_FFT512); -end; - -{* - * Copies interleaved PCM 16bit uint (maybe fake) stereo samples into data. - * Returns the number of frames (= stereo/mono sample) - *} -function TAudio_bass.GetPCMData(var data: TPCMData): Cardinal; -var - info: BASS_CHANNELINFO; - nBytes: DWORD; -begin - //Get Channel Data Mono and 256 Values - BASS_ChannelGetInfo(MusicStream, info); - ZeroMemory(@data, sizeof(TPCMData)); - - if (info.chans = 1) then - begin - // mono file -> add stereo channel - { - nBytes := BASS_ChannelGetData(Bass, @data[0], samples*sizeof(Smallint)); - // interleave data - //CopyMemory(@data[1], @data[0], samples*sizeof(Smallint)); - } - result := 0; - end - else - begin - // stereo file - nBytes := BASS_ChannelGetData(MusicStream, @data, sizeof(TPCMData)); - end; - if(nBytes <= 0) then - result := 0 - else - result := nBytes div sizeof(TPCMStereoSample); -end; - -function TAudio_bass.LoadCustomSound(const Filename: String): Cardinal; -var - S: hStream; - 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; - - if LoadSoundFromFile(S, SoundPath + Filename) then - Result := High(CustomSounds) - else - Result := 0; -end; - -procedure TAudio_bass.PlayCustomSound(const Index: Cardinal ); -begin - if Index <= High(CustomSounds) then - with CustomSounds[Index].Stream as TBassOutputStream do - begin - BASS_ChannelPlay(Handle, True); - end; -end; - -{$IFDEF UseBASSInput} - -procedure TAudio_bass.InitializeRecord; -var - device: integer; - Descr: string; - input: integer; - input2: integer; - InputName: PChar; - Flags: integer; - mic: array[0..15] of integer; - SC: integer; // soundcard - SCI: integer; // soundcard input - No: integer; - -function isDuplicate(Desc: String): Boolean; -var - I: Integer; -begin - Result := False; - //Check for Soundcard with same Description - For I := 0 to SC-1 do - begin - if (Recording.SoundCard[I].Description = Desc) then - begin - Result := True; - Break; - end; - end; -end; - -begin - with Recording do - begin - // checks for recording devices and puts them into an array - SetLength(SoundCard, 0); - - SC := 0; - Descr := BASS_RecordGetDeviceDescription(SC); - - while (Descr <> '') do - begin - //If there is another SoundCard with the Same ID, Search an available Name - if (IsDuplicate(Descr)) then - begin - No:= 1; //Count of SoundCards with same Name - Repeat - Inc(No) - Until not IsDuplicate(Descr + ' (' + InttoStr(No) + ')'); - - //Set Description - Descr := Descr + ' (' + InttoStr(No) + ')'; - end; - - SetLength(SoundCard, SC+1); - - // TODO: free object on termination - SoundCard[SC] := TBassSoundCard.Create(); - SoundCard[SC].Description := Descr; - - //Get Recording Inputs - SCI := 0; - BASS_RecordInit(SC); - - InputName := BASS_RecordGetInputName(SCI); - - {$IFDEF DARWIN} - // Under MacOSX the SingStar Mics have an empty - // InputName. So, we have to add a hard coded - // Workaround for this problem - if (InputName = nil) and (Pos( 'USBMIC Serial#', Descr) > 0) then - begin - InputName := 'Microphone'; - end; - {$ENDIF} - - SetLength(SoundCard[SC].Input, 1); - SoundCard[SC].Input[SCI].Name := InputName; - - // process each input - while (InputName <> nil) do - begin - Flags := BASS_RecordGetInput(SCI); - if (SCI >= 1) {AND (Flags AND BASS_INPUT_OFF = 0)} then - begin - SetLength(SoundCard[SC].Input, SCI+1); - SoundCard[SC].Input[SCI].Name := InputName; - end; - - //Set Mic Index - if ((Flags and BASS_INPUT_TYPE_MIC) = 1) then - SoundCard[SC].MicInput := SCI; - - Inc(SCI); - InputName := BASS_RecordGetInputName(SCI); - end; - - BASS_RecordFree; - - Inc(SC); - Descr := BASS_RecordGetDeviceDescription(SC); - end; // while - end; // with Recording -end; - -// TODO: code is used by all IAudioInput implementors -// -> move to a common superclass (TAudioInput_Generic?) -procedure TAudio_bass.CaptureStart; -var - S: integer; - SC: integer; - PlayerLeft, PlayerRight: integer; - CaptureSoundLeft, CaptureSoundRight: TSound; -begin - for S := 0 to High(Recording.Sound) do - Recording.Sound[S].BufferLong[0].Clear; - - for SC := 0 to High(Ini.CardList) do begin - PlayerLeft := Ini.CardList[SC].ChannelL-1; - PlayerRight := Ini.CardList[SC].ChannelR-1; - if PlayerLeft >= PlayersPlay then PlayerLeft := -1; - if PlayerRight >= PlayersPlay then PlayerRight := -1; - if (PlayerLeft > -1) or (PlayerRight > -1) then begin - if (PlayerLeft > -1) then - CaptureSoundLeft := Recording.Sound[PlayerLeft] - else - CaptureSoundLeft := nil; - if (PlayerRight > -1) then - CaptureSoundRight := Recording.Sound[PlayerRight] - else - CaptureSoundRight := nil; - - CaptureCard(SC, CaptureSoundLeft, CaptureSoundRight); - end; - end; -end; - -// TODO: code is used by all IAudioInput implementors -// -> move to a common superclass (TAudioInput_Generic?) -procedure TAudio_bass.CaptureStop; -var - SC: integer; - PlayerLeft: integer; - PlayerRight: integer; -begin - - for SC := 0 to High(Ini.CardList) do begin - PlayerLeft := Ini.CardList[SC].ChannelL-1; - PlayerRight := Ini.CardList[SC].ChannelR-1; - if PlayerLeft >= PlayersPlay then PlayerLeft := -1; - if PlayerRight >= PlayersPlay then PlayerRight := -1; - if (PlayerLeft > -1) or (PlayerRight > -1) then - StopCard(SC); - end; - -end; - -{* - * Bass input capture callback. - * Params: - * stream - BASS input stream - * buffer - buffer of captured samples - * len - size of buffer in bytes - * user - players associated with left/right channels - *} -function MicrophoneCallback(stream: HSTREAM; buffer: Pointer; - len: Cardinal; Card: Cardinal): boolean; stdcall; -begin - Recording.HandleMicrophoneData(buffer, len, Recording.SoundCard[Card]); - Result := true; -end; - -{* - * Start input-capturing on Soundcard specified by Card. - * Params: - * Card - soundcard index in Recording.SoundCard array - * CaptureSoundLeft - sound(-buffer) used for left channel capture data - * CaptureSoundRight - sound(-buffer) used for right channel capture data - *} -procedure TAudio_bass.CaptureCard(Card: byte; CaptureSoundLeft, CaptureSoundRight: TSound); -var - Error: integer; - ErrorMsg: string; - bassSoundCard: TBassSoundCard; -begin - if not BASS_RecordInit(Card) then - begin - Error := BASS_ErrorGetCode; - ErrorMsg := IntToStr(Error); - if Error = BASS_ERROR_DX then ErrorMsg := 'No DX5'; - if Error = BASS_ERROR_ALREADY then ErrorMsg := 'The device has already been initialized'; - if Error = BASS_ERROR_DEVICE then ErrorMsg := 'The device number specified is invalid'; - if Error = BASS_ERROR_DRIVER then ErrorMsg := 'There is no available device driver'; - Log.LogError('Error initializing record [' + IntToStr(Card) + ']'); - Log.LogError('TAudio_bass.CaptureCard: Error initializing record: ' + ErrorMsg); - end - else - begin - bassSoundCard := TBassSoundCard(Recording.SoundCard[Card]); - bassSoundCard.CaptureSoundLeft := CaptureSoundLeft; - bassSoundCard.CaptureSoundRight := CaptureSoundRight; - - // capture in 44.1kHz/stereo/16bit and a 20ms callback period - bassSoundCard.RecordStream := - BASS_RecordStart(44100, 2, MakeLong(0, 20) , @MicrophoneCallback, Card); - end; -end; - -{* - * Stop input-capturing on Soundcard specified by Card. - * Params: - * Card - soundcard index in Recording.SoundCard array - *} -procedure TAudio_bass.StopCard(Card: byte); -begin - BASS_RecordSetDevice(Card); - BASS_RecordFree; -end; - -{$ENDIF} - - -initialization - singleton_MusicBass := TAudio_bass.create(); - AudioManager.add( singleton_MusicBass ); - -finalization - AudioManager.Remove( singleton_MusicBass ); - -end. diff --git a/Game/Code/Classes/UAudio_portaudio.pas b/Game/Code/Classes/UAudio_portaudio.pas deleted file mode 100644 index 47e54981..00000000 --- a/Game/Code/Classes/UAudio_portaudio.pas +++ /dev/null @@ -1,427 +0,0 @@ -unit UAudio_Portaudio; - -interface - -{$IFDEF FPC} - {$MODE Delphi} -{$ENDIF} - -{$I switches.inc} - - -uses Classes, - {$IFDEF win32} - windows, - {$ENDIF} - Messages, - SysUtils, - {$IFNDEF FPC} - Forms, - {$ENDIF} - portaudio, - {$IFDEF UsePortmixer} - portmixer, - {$ENDIF} - ULog, - UMusic; - -implementation - -uses - {$IFDEF LAZARUS} - lclintf, - {$ENDIF} - URecord, - UIni, - UMain, - UCommon, - UThemes; -{ -type - TPaHostApiIndex = PaHostApiIndex; - TPaDeviceIndex = PaDeviceIndex; - PPaStream = ^PaStreamPtr; - PPaStreamCallbackTimeInfo = ^PaStreamCallbackTimeInfo; - TPaStreamCallbackFlags = PaStreamCallbackFlags; - TPaHostApiTypeId = PaHostApiTypeId; - PPaHostApiInfo = ^PaHostApiInfo; - PPaDeviceInfo = ^PaDeviceInfo; - TPaError = PaError; - TPaStreamParameters = PaStreamParameters; -} -type - TAudio_Portaudio = class( TInterfacedObject, IAudioInput ) - private - function GetPreferredApiIndex(): TPaHostApiIndex; - public - function GetName: String; - procedure InitializeRecord; - - procedure CaptureStart; - procedure CaptureStop; - - procedure CaptureCard(Card: byte; CaptureSoundLeft, CaptureSoundRight: TSound); - procedure StopCard(Card: byte); - end; - - TPortaudioSoundCard = class(TGenericSoundCard) - RecordStream: PPaStream; - DeviceIndex: TPaDeviceIndex; - end; - -function MicrophoneCallback(input: Pointer; output: Pointer; frameCount: Longword; - timeInfo: PPaStreamCallbackTimeInfo; statusFlags: TPaStreamCallbackFlags; - inputDevice: Pointer): Integer; cdecl; forward; - -var - singleton_MusicPortaudio : IAudioInput; - -const - sampleRate: Double = 44100.; - -{* the default API used by Portaudio is the least common denominator - * and might lack efficiency. ApiPreferenceOrder defines the order of - * preferred APIs to use. The first API-type in the list is tried first. If it's - * not available the next is tried, ... - * If none of the preferred APIs was found the default API is used. - * Pascal doesn't permit zero-length static arrays, so you can use paDefaultApi - * as an array's only member if you do not have any preferences. - * paDefaultApi also terminates a preferences list but this is optional. - *} -const - paDefaultApi = -1; -var - ApiPreferenceOrder: -{$IF Defined(WIN32)} - // Note1: Portmixer has no mixer support for paASIO and paWASAPI at the moment - // Note2: Windows Default-API is MME - //array[0..0] of TPaHostApiTypeId = ( paDirectSound, paMME ); - array[0..0] of TPaHostApiTypeId = ( paDirectSound ); -{$ELSEIF Defined(LINUX)} - // Note1: Portmixer has no mixer support for paJACK at the moment - // Note2: Not tested, but ALSA might be better than OSS. - array[0..1] of TPaHostApiTypeId = ( paALSA, paOSS ); -{$ELSEIF Defined(DARWIN)} - // Note: Not tested. - //array[0..0] of TPaHostApiTypeId = ( paCoreAudio ); - array[0..0] of TPaHostApiTypeId = ( paDefaultApi ); -{$ELSE} - array[0..0] of TPaHostApiTypeId = ( paDefaultApi ); -{$IFEND} - -function TAudio_Portaudio.GetName: String; -begin - result := 'Portaudio'; -end; - -function TAudio_Portaudio.GetPreferredApiIndex(): TPaHostApiIndex; -var - i: integer; -begin - result := -1; - - // select preferred sound-API - for i:= 0 to High(ApiPreferenceOrder) do - begin - if(ApiPreferenceOrder[i] <> paDefaultApi) then begin - // check if API is available - result := Pa_HostApiTypeIdToHostApiIndex(ApiPreferenceOrder[i]); - if(result >= 0) then - break; - end; - end; - - // None of the preferred APIs is available -> use default - if(result < 0) then begin - result := Pa_GetDefaultHostApi(); - end; -end; - -// TODO: should be a function with boolean return type -procedure TAudio_Portaudio.InitializeRecord; -var - i: integer; - apiIndex: TPaHostApiIndex; - apiInfo: PPaHostApiInfo; - deviceName: string; - deviceIndex: TPaDeviceIndex; - deviceInfo: PPaDeviceInfo; - inputCnt: integer; - inputName: string; - SC: integer; // soundcard - SCI: integer; // soundcard input - err: TPaError; - errMsg: string; - paSoundCard: TPortaudioSoundCard; - inputParams: TPaStreamParameters; - stream: PPaStream; - {$IFDEF UsePortmixer} - mixer: PPxMixer; - {$ENDIF} -begin - // TODO: call Pa_Terminate() on termination - err := Pa_Initialize(); - if(err <> paNoError) then begin - Log.CriticalError('Portaudio.InitializeRecord: ' + Pa_GetErrorText(err)); - //Log.LogError('Portaudio.InitializeRecord: ' + Pa_GetErrorText(err)); - // result := false; - Exit; - end; - - apiIndex := GetPreferredApiIndex(); - apiInfo := Pa_GetHostApiInfo(apiIndex); - - SC := 0; - - // init array-size to max. input-devices count - SetLength(Recording.SoundCard, apiInfo^.deviceCount); // fix deviceCountL - for i:= 0 to High(Recording.SoundCard) do - begin - // convert API-specific device-index to global index - deviceIndex := Pa_HostApiDeviceIndexToDeviceIndex(apiIndex, i); - deviceInfo := Pa_GetDeviceInfo(deviceIndex); - - // current device is no input device -> skip - if(deviceInfo^.maxInputChannels <= 0) then - continue; - - // TODO: free object on termination - paSoundCard := TPortaudioSoundCard.Create(); - Recording.SoundCard[SC] := paSoundCard; - - // retrieve device-name - deviceName := deviceInfo^.name; - paSoundCard.Description := deviceName; - paSoundCard.DeviceIndex := deviceIndex; - - // setup desired input parameters - with inputParams do begin - device := deviceIndex; - channelCount := 2; - sampleFormat := paInt16; - suggestedLatency := deviceInfo^.defaultLowInputLatency; - hostApiSpecificStreamInfo := nil; - end; - - // check if device supports our input-format - err := Pa_IsFormatSupported(@inputParams, nil, sampleRate); - if(err <> 0) then begin - // format not supported -> skip - errMsg := Pa_GetErrorText(err); - Log.LogError('Portaudio.InitializeRecord, device: "'+ deviceName +'" ' - + '('+ errMsg +')'); - paSoundCard.Free(); - continue; - end; - - // TODO: retry with mono if stereo is not supported - // TODO: retry with input-latency set to 20ms (defaultLowInputLatency might - // not be set correctly in OSS) - - err := Pa_OpenStream(stream, @inputParams, nil, sampleRate, - paFramesPerBufferUnspecified, paNoFlag, @MicrophoneCallback, nil); - if(err <> paNoError) then begin - // unable to open device -> skip - errMsg := Pa_GetErrorText(err); - Log.LogError('Portaudio.InitializeRecord, device: "'+ deviceName +'" ' - + '('+ errMsg +')'); - paSoundCard.Free(); - continue; - end; - - - {$IFDEF UsePortmixer} - - // use default mixer - mixer := Px_OpenMixer(stream, 0); - - // get input count - inputCnt := Px_GetNumInputSources(mixer); - SetLength(paSoundCard.Input, inputCnt); - - // get input names - for SCI := 0 to inputCnt-1 do - begin - inputName := Px_GetInputSourceName(mixer, SCI); - paSoundCard.Input[SCI].Name := inputName; - end; - - Px_CloseMixer(mixer); - - {$ELSE} // !UsePortmixer - - //Pa_StartStream(stream); - // TODO: check if callback was called (this problem may occur on some devices) - //Pa_StopStream(stream); - - Pa_CloseStream(stream); - - // create a standard input source - SetLength(paSoundCard.Input, 1); - paSoundCard.Input[0].Name := 'Standard'; - - {$ENDIF} - - // use default input source - paSoundCard.InputSelected := 0; - - Inc(SC); - end; - - // adjust size to actual input-device count - SetLength(Recording.SoundCard, SC); - - Log.LogStatus('#Soundcards: ' + inttostr(SC), 'Portaudio'); - - { - SoundCard[SC].InputSelected := Mic[Device]; - } -end; - -// TODO: code is used by all IAudioInput implementors -// -> move to a common superclass (TAudioInput_Generic?) -procedure TAudio_Portaudio.CaptureStart; -var - S: integer; - SC: integer; - PlayerLeft, PlayerRight: integer; - CaptureSoundLeft, CaptureSoundRight: TSound; -begin - for S := 0 to High(Recording.Sound) do - Recording.Sound[S].BufferLong[0].Clear; - - for SC := 0 to High(Ini.CardList) do begin - PlayerLeft := Ini.CardList[SC].ChannelL-1; - PlayerRight := Ini.CardList[SC].ChannelR-1; - if PlayerLeft >= PlayersPlay then PlayerLeft := -1; - if PlayerRight >= PlayersPlay then PlayerRight := -1; - if (PlayerLeft > -1) or (PlayerRight > -1) then begin - if (PlayerLeft > -1) then - CaptureSoundLeft := Recording.Sound[PlayerLeft] - else - CaptureSoundLeft := nil; - if (PlayerRight > -1) then - CaptureSoundRight := Recording.Sound[PlayerRight] - else - CaptureSoundRight := nil; - - CaptureCard(SC, CaptureSoundLeft, CaptureSoundRight); - end; - end; -end; - -// TODO: code is used by all IAudioInput implementors -// -> move to a common superclass (TAudioInput_Generic?) -procedure TAudio_Portaudio.CaptureStop; -var - SC: integer; - PlayerLeft: integer; - PlayerRight: integer; -begin - - for SC := 0 to High(Ini.CardList) do begin - PlayerLeft := Ini.CardList[SC].ChannelL-1; - PlayerRight := Ini.CardList[SC].ChannelR-1; - if PlayerLeft >= PlayersPlay then PlayerLeft := -1; - if PlayerRight >= PlayersPlay then PlayerRight := -1; - if (PlayerLeft > -1) or (PlayerRight > -1) then - StopCard(SC); - end; - -end; - -{* - * Portaudio input capture callback. - *} -function MicrophoneCallback(input: Pointer; output: Pointer; frameCount: Longword; - timeInfo: PPaStreamCallbackTimeInfo; statusFlags: TPaStreamCallbackFlags; - inputDevice: Pointer): Integer; cdecl; -begin - Recording.HandleMicrophoneData(input, frameCount*4, inputDevice); - result := paContinue; -end; - -{* - * Start input-capturing on Soundcard specified by Card. - * Params: - * Card - soundcard index in Recording.SoundCard array - * CaptureSoundLeft - sound(-buffer) used for left channel capture data - * CaptureSoundRight - sound(-buffer) used for right channel capture data - *} -procedure TAudio_Portaudio.CaptureCard(Card: byte; CaptureSoundLeft, CaptureSoundRight: TSound); -var - Error: TPaError; - ErrorMsg: string; - inputParams: TPaStreamParameters; - deviceInfo: PPaDeviceInfo; - stream: PPaStream; - paSoundCard: TPortaudioSoundCard; -begin - paSoundCard := TPortaudioSoundCard(Recording.SoundCard[Card]); - paSoundCard.CaptureSoundLeft := CaptureSoundLeft; - paSoundCard.CaptureSoundRight := CaptureSoundRight; - - // get input latency info - deviceInfo := Pa_GetDeviceInfo(paSoundCard.DeviceIndex); - - // set input stream parameters - with inputParams do begin - device := paSoundCard.DeviceIndex; - channelCount := 2; - sampleFormat := paInt16; - suggestedLatency := deviceInfo^.defaultLowInputLatency; - hostApiSpecificStreamInfo := nil; - end; - - Log.LogStatus(inttostr(paSoundCard.DeviceIndex), 'Portaudio'); - Log.LogStatus(floattostr(deviceInfo^.defaultLowInputLatency), 'Portaudio'); - - // open input stream - Error := Pa_OpenStream(stream, @inputParams, nil, sampleRate, - paFramesPerBufferUnspecified, paNoFlag, - @MicrophoneCallback, Pointer(paSoundCard)); - if(Error <> paNoError) then begin - ErrorMsg := Pa_GetErrorText(Error); - Log.CriticalError('TAudio_Portaudio.CaptureCard('+ IntToStr(Card) +'): Error opening stream: ' + ErrorMsg); - //Halt; - end; - - paSoundCard.RecordStream := stream; - - // start capture - Error := Pa_StartStream(stream); - if(Error <> paNoError) then begin - Pa_CloseStream(stream); - ErrorMsg := Pa_GetErrorText(Error); - Log.CriticalError('TAudio_Portaudio.CaptureCard('+ IntToStr(Card) +'): Error starting stream: ' + ErrorMsg); - //Halt; - end; -end; - -{* - * Stop input-capturing on Soundcard specified by Card. - * Params: - * Card - soundcard index in Recording.SoundCard array - *} -procedure TAudio_Portaudio.StopCard(Card: byte); -var - stream: PPaStream; - paSoundCard: TPortaudioSoundCard; -begin - paSoundCard := TPortaudioSoundCard(Recording.SoundCard[Card]); - stream := paSoundCard.RecordStream; - if(stream <> nil) then begin - Pa_StopStream(stream); - Pa_CloseStream(stream); - end; -end; - - -initialization - singleton_MusicPortaudio := TAudio_Portaudio.create(); - AudioManager.add( singleton_MusicPortaudio ); - -finalization - AudioManager.Remove( singleton_MusicPortaudio ); - -end. diff --git a/Game/Code/Classes/UMedia_dummy.pas b/Game/Code/Classes/UMedia_dummy.pas index 52e3434e..c973512d 100644 --- a/Game/Code/Classes/UMedia_dummy.pas +++ b/Game/Code/Classes/UMedia_dummy.pas @@ -45,8 +45,8 @@ type procedure Pause; procedure Stop; - procedure MoveTo(Time: real); - function getPosition: real; + procedure SetPosition(Time: real); + function GetPosition: real; procedure GetFrame(Time: Extended); procedure DrawGL(Screen: integer); @@ -82,8 +82,6 @@ type procedure PlayShuffle; procedure StopShuffle; - function LoadSoundFromFile(var stream: TAudioOutputStream; Name: string): boolean; - function LoadCustomSound(const Filename: String): Cardinal; procedure PlayCustomSound(const Index: Cardinal ); @@ -135,7 +133,7 @@ procedure Tmedia_dummy.Stop; begin end; -procedure Tmedia_dummy.MoveTo(Time: real); +procedure Tmedia_dummy.SetPosition(Time: real); begin end; @@ -251,11 +249,6 @@ procedure Tmedia_dummy.StopShuffle; begin end; -function Tmedia_dummy.LoadSoundFromFile(var stream: TAudioOutputStream; Name: string): boolean; -begin - result := false; -end; - function Tmedia_dummy.LoadCustomSound(const Filename: String): Cardinal; begin result := 0; diff --git a/Game/Code/Classes/UMusic.pas b/Game/Code/Classes/UMusic.pas index c2d616ec..bf366130 100644 --- a/Game/Code/Classes/UMusic.pas +++ b/Game/Code/Classes/UMusic.pas @@ -83,17 +83,72 @@ type Source: array of string; end; +type TFFTData = array[0..256] of Single; TPCMStereoSample = array[0..1] of Smallint; TPCMData = array[0..511] of TPCMStereoSample; - TAudioOutputStream = class +type + TStreamStatus = (sStopped, sPlaying, sPaused); +const + StreamStatusStr: array[TStreamStatus] of string = ('Stopped', 'Playing', 'Paused'); + +type + TAudioProcessingStream = class + public + procedure Close(); virtual; abstract; + end; + + TAudioPlaybackStream = class(TAudioProcessingStream) + protected + function GetLoop(): boolean; virtual; abstract; + procedure SetLoop(Enabled: boolean); virtual; abstract; + function GetLength(): real; virtual; abstract; + function GetStatus(): TStreamStatus; virtual; abstract; + public + procedure Play(); virtual; abstract; + procedure Pause(); virtual; abstract; + procedure Stop(); virtual; abstract; + + property Loop: boolean READ GetLoop WRITE SetLoop; + property Length: real READ GetLength; + property Status: TStreamStatus READ GetStatus; end; + (* + TAudioMixerStream = class(TAudioProcessingStream) + procedure AddStream(stream: TAudioProcessingStream); + procedure RemoveStream(stream: TAudioProcessingStream); + procedure SetMasterVolume(volume: cardinal); + function GetMasterVolume(): cardinal; + procedure SetStreamVolume(stream: TAudioProcessingStream; volume: cardinal); + function GetStreamVolume(stream: TAudioProcessingStream): cardinal; + end; + *) + + TAudioDecodeStream = class(TAudioProcessingStream) + protected + function GetLength(): real; virtual; abstract; + function GetChannelCount(): cardinal; virtual; abstract; + function GetSampleRate(): cardinal; virtual; abstract; + function GetPosition(): real; virtual; abstract; + procedure SetPosition(Time: real); virtual; abstract; + function IsEOF(): boolean; virtual; abstract; + public + function ReadData(Buffer: PChar; BufSize: integer): integer; virtual; abstract; + + property Length: real READ GetLength; + property ChannelCount: cardinal READ GetChannelCount; + property SampleRate: cardinal READ GetSampleRate; + property Position: real READ GetPosition WRITE SetPosition; + property EOF: boolean READ IsEOF; + end; + +type TCustomSoundEntry = record Filename : String; - Stream : TAudioOutputStream; + Stream : TAudioPlaybackStream; end; type @@ -101,23 +156,23 @@ type ['{63A5EBC3-3F4D-4F23-8DFB-B5165FCE33DD}'] function GetName: String; - function Open(Name: string): boolean; // true if succeed + function Open(Filename: string): boolean; // true if succeed procedure Close; procedure Play; procedure Pause; procedure Stop; - procedure MoveTo(Time: real); - function getPosition: real; - - property position : real READ getPosition WRITE MoveTo; + procedure SetPosition(Time: real); + function GetPosition: real; + + property Position : real READ GetPosition WRITE SetPosition; end; IVideoPlayback = Interface( IGenericPlayback ) ['{3574C40C-28AE-4201-B3D1-3D1F0759B131}'] procedure init(); - + procedure GetFrame(Time: Extended); // WANT TO RENAME THESE TO BE MORE GENERIC procedure DrawGL(Screen: integer); // WANT TO RENAME THESE TO BE MORE GENERIC @@ -150,9 +205,6 @@ type procedure PlayShuffle; procedure StopShuffle; - // TODO - //function LoadSoundFromFile(var stream: TAudioOutputStream; Name: string): boolean; - //Custom Sounds function LoadCustomSound(const Filename: String): Cardinal; procedure PlayCustomSound(const Index: Cardinal ); @@ -164,6 +216,24 @@ type function GetPCMData(var data: TPCMData): Cardinal; end; + IGenericDecoder = Interface + ['{557B0E9A-604D-47E4-B826-13769F3E10B7}'] + function InitializeDecoder(): boolean; + //function IsSupported(const Filename: string): boolean; + end; + + (* + IVideoDecoder = Interface( IGenericDecoder ) + ['{2F184B2B-FE69-44D5-9031-0A2462391DCA}'] + function Open(const Filename: string): TVideoDecodeStream; + end; + *) + + IAudioDecoder = Interface( IGenericDecoder ) + ['{AB47B1B6-2AA9-4410-BF8C-EC79561B5478}'] + function Open(const Filename: string): TAudioDecodeStream; + end; + IAudioInput = Interface ['{A5C8DA92-2A0C-4AB2-849B-2F7448C6003A}'] function GetName: String; @@ -191,6 +261,7 @@ function Visualization(): IVideoPlayback; function VideoPlayback(): IVideoPlayback; function AudioPlayback(): IAudioPlayback; function AudioInput(): IAudioInput; +function AudioDecoder(): IAudioDecoder; function AudioManager: TInterfaceList; @@ -207,6 +278,7 @@ var singleton_Visualization : IVideoPlayback = nil; singleton_AudioPlayback : IAudioPlayback = nil; singleton_AudioInput : IAudioInput = nil; + singleton_AudioDecoder : IAudioDecoder = nil; singleton_AudioManager : TInterfaceList = nil; @@ -240,6 +312,11 @@ begin result := singleton_AudioInput; end; +function AudioDecoder(): IAudioDecoder; +begin + result := singleton_AudioDecoder; +end; + procedure InitializeSound; var lTmpInterface : IInterface; @@ -249,6 +326,7 @@ begin singleton_AudioPlayback := nil; singleton_AudioInput := nil; + singleton_AudioDecoder := nil; singleton_VideoPlayback := nil; singleton_Visualization := nil; @@ -271,6 +349,13 @@ begin singleton_AudioInput := IAudioInput( lTmpInterface ); end; + // if this interface is a Decoder, then set it as the default used + if ( AudioManager[iCount].QueryInterface( IAudioDecoder, lTmpInterface ) = 0 ) AND + ( true ) then + begin + singleton_AudioDecoder := IAudioDecoder( lTmpInterface ); + end; + // if this interface is a Input, then set it as the default used if ( AudioManager[iCount].QueryInterface( IVideoPlayback, lTmpInterface ) = 0 ) AND ( true ) then @@ -293,6 +378,11 @@ begin begin end; + if AudioDecoder <> nil then + begin + AudioDecoder.InitializeDecoder; + end; + if AudioPlayback <> nil then begin AudioPlayback.InitializePlayback; diff --git a/Game/Code/Classes/UVideo.pas b/Game/Code/Classes/UVideo.pas index 4f6b566a..bbd03d84 100644 --- a/Game/Code/Classes/UVideo.pas +++ b/Game/Code/Classes/UVideo.pas @@ -45,9 +45,11 @@ uses SDL, dialogs, {$endif} {$ENDIF} + (* FIXME {$ifdef UseFFMpegAudio} - UAudio_FFMpeg, + UAudioDecoder_FFMpeg, {$endif} + *) UIni, UMusic, UGraphic; @@ -101,8 +103,8 @@ type procedure Pause; procedure Stop; - procedure MoveTo(Time: real); - function getPosition: real; + procedure SetPosition(Time: real); + function GetPosition: real; procedure GetFrame(Time: Extended); procedure DrawGL(Screen: integer); @@ -626,7 +628,7 @@ procedure TVideoPlayback_ffmpeg.Stop; begin end; -procedure TVideoPlayback_ffmpeg.MoveTo(Time: real); +procedure TVideoPlayback_ffmpeg.SetPosition(Time: real); begin fVideoSkipTime := Time; @@ -639,7 +641,7 @@ begin end; // what is this supposed to do? return VideoTime? -function TVideoPlayback_ffmpeg.getPosition: real; +function TVideoPlayback_ffmpeg.GetPosition: real; begin result := 0; end; diff --git a/Game/Code/Classes/UVisualizer.pas b/Game/Code/Classes/UVisualizer.pas index 5fc4bb82..22c168a2 100644 --- a/Game/Code/Classes/UVisualizer.pas +++ b/Game/Code/Classes/UVisualizer.pas @@ -61,9 +61,7 @@ type VisualTex : glUint; PCMData : TPCMData; - hRC : Integer; - hDC : Integer; - + RndPCMcount : integer; projMatrix: array[0..3, 0..3] of GLdouble; @@ -91,8 +89,8 @@ type procedure Pause; procedure Stop; - procedure MoveTo(Time: real); - function getPosition: real; + procedure SetPosition(Time: real); + function GetPosition: real; procedure GetFrame(Time: Extended); procedure DrawGL(Screen: integer); @@ -150,12 +148,12 @@ begin VisualizerStop(); end; -procedure TVideoPlayback_ProjectM.MoveTo(Time: real); +procedure TVideoPlayback_ProjectM.SetPosition(Time: real); begin pm.RandomPreset(); end; -function TVideoPlayback_ProjectM.getPosition: real; +function TVideoPlayback_ProjectM.GetPosition: real; begin result := 0; end; @@ -240,7 +238,6 @@ end; procedure TVideoPlayback_ProjectM.GetFrame(Time: Extended); var - i: integer; nSamples: cardinal; stackDepth: Integer; begin @@ -269,7 +266,7 @@ begin // this may happen with some presets ( on linux ) if there is a div by zero // in projectM's getBeatVals() function (file: beat_detect.cc) Log.LogStatus('Div by zero!', 'Visualizer'); - MoveTo( now ); + SetPosition( now ); end; //glGetIntegerv(GL_PROJECTION_STACK_DEPTH, @stackDepth); -- cgit v1.2.3