diff options
Diffstat (limited to 'src/classes/UMusic.pas')
-rw-r--r-- | src/classes/UMusic.pas | 1233 |
1 files changed, 0 insertions, 1233 deletions
diff --git a/src/classes/UMusic.pas b/src/classes/UMusic.pas deleted file mode 100644 index 6476f629..00000000 --- a/src/classes/UMusic.pas +++ /dev/null @@ -1,1233 +0,0 @@ -unit UMusic; - -interface - -{$IFDEF FPC} - {$MODE Delphi} -{$ENDIF} - -{$I switches.inc} - -uses - UTime, - Classes; - -type - TNoteType = (ntFreestyle, ntNormal, ntGolden); - - (** - * TLineFragment represents a fragment of a lyrics line. - * This is a text-fragment (e.g. a syllable) assigned to a note pitch, - * represented by a bar in the sing-screen. - *) - PLineFragment = ^TLineFragment; - TLineFragment = record - Color: integer; - Start: integer; // beat the fragment starts at - Length: integer; // length in beats - Tone: integer; // full range tone - Text: string; // text assigned to this fragment (a syllable, word, etc.) - NoteType: TNoteType; // note-type: golden-note/freestyle etc. - end; - - (** - * TLine represents one lyrics line and consists of multiple - * notes. - *) - PLine = ^TLine; - TLine = record - Start: integer; // the start beat of this line (<> start beat of the first note of this line) - Lyric: string; - LyricWidth: real; // @deprecated: width of the line in pixels. - // Do not use this as the width is not correct. - // Use TLyricsEngine.GetUpperLine().Width instead. - End_: integer; - BaseNote: integer; - HighNote: integer; // index of last note in line (= High(Note)?) - TotalNotes: integer; // value of all notes in the line - LastLine: boolean; - Note: array of TLineFragment; - end; - - (** - * TLines stores sets of lyric lines and information on them. - * Normally just one set is defined but in duet mode it might for example - * contain two sets. - *) - TLines = record - Current: integer; // for drawing of current line - High: integer; // (= High(Line)?) - Number: integer; - Resolution: integer; - NotesGAP: integer; - ScoreValue: integer; - Line: array of TLine; - end; - - (** - * TLyricsState contains all information concerning the - * state of the lyrics, e.g. the current beat or duration of the lyrics. - *) - TLyricsState = class - private - Timer: TRelativeTimer; // keeps track of the current time - public - OldBeat: integer; // previous discovered beat - CurrentBeat: integer; // current beat (rounded) - MidBeat: real; // current beat (float) - - // now we use this for super synchronization! - // only used when analyzing voice - // TODO: change ...D to ...Detect(ed) - OldBeatD: integer; // previous discovered beat - CurrentBeatD: integer; // current discovered beat (rounded) - MidBeatD: real; // current discovered beat (float) - - // we use this for audible clicks - // TODO: Change ...C to ...Click - OldBeatC: integer; // previous discovered beat - CurrentBeatC: integer; - MidBeatC: real; // like CurrentBeatC - - OldLine: integer; // previous displayed sentence - - StartTime: real; // time till start of lyrics (= Gap) - TotalTime: real; // total song time - - constructor Create(); - procedure Pause(); - procedure Resume(); - - procedure Reset(); - procedure UpdateBeats(); - - (** - * current song time (in seconds) used as base-timer for lyrics etc. - *) - function GetCurrentTime(): real; - procedure SetCurrentTime(Time: real); - end; - - -const - FFTSize = 512; // size of FFT data (output: FFTSize/2 values) -type - TFFTData = array[0..(FFTSize div 2)-1] of Single; - -type - PPCMStereoSample = ^TPCMStereoSample; - TPCMStereoSample = array[0..1] of SmallInt; - TPCMData = array[0..511] of TPCMStereoSample; - -type - TStreamStatus = (ssStopped, ssPlaying, ssPaused); -const - StreamStatusStr: array[TStreamStatus] of string = - ('Stopped', 'Playing', 'Paused'); - -type - TAudioSampleFormat = ( - asfU8, asfS8, // unsigned/signed 8 bits - asfU16LSB, asfS16LSB, // unsigned/signed 16 bits (endianness: LSB) - asfU16MSB, asfS16MSB, // unsigned/signed 16 bits (endianness: MSB) - asfU16, asfS16, // unsigned/signed 16 bits (endianness: System) - asfS24, // signed 24 bits (endianness: System) - asfS32, // signed 32 bits (endianness: System) - asfFloat // float - ); - -const - // Size of one sample (one channel only) in bytes - AudioSampleSize: array[TAudioSampleFormat] of integer = ( - 1, 1, // asfU8, asfS8 - 2, 2, // asfU16LSB, asfS16LSB - 2, 2, // asfU16MSB, asfS16MSB - 2, 2, // asfU16, asfS16 - 3, // asfS24 - 4, // asfS32 - 4 // asfFloat - ); - -const - CHANNELMAP_LEFT = 1; - CHANNELMAP_RIGHT = 2; - CHANNELMAP_FRONT = CHANNELMAP_LEFT or CHANNELMAP_RIGHT; - -type - TAudioFormatInfo = class - private - fSampleRate : double; - fChannels : byte; - fFormat : TAudioSampleFormat; - fFrameSize : integer; - - procedure SetChannels(Channels: byte); - procedure SetFormat(Format: TAudioSampleFormat); - procedure UpdateFrameSize(); - function GetBytesPerSec(): double; - public - constructor Create(Channels: byte; SampleRate: double; Format: TAudioSampleFormat); - function Copy(): TAudioFormatInfo; - - (** - * Returns the inverse ratio of the size of data in this format to its - * size in a given target format. - * Example: SrcSize*SrcInfo.GetRatio(TgtInfo) = TgtSize - *) - function GetRatio(TargetInfo: TAudioFormatInfo): double; - - property SampleRate: double read fSampleRate write fSampleRate; - property Channels: byte read fChannels write SetChannels; - property Format: TAudioSampleFormat read fFormat write SetFormat; - property FrameSize: integer read fFrameSize; - property BytesPerSec: double read GetBytesPerSec; - end; - -type - TSoundEffect = class - public - EngineData: Pointer; // can be used for engine-specific data - procedure Callback(Buffer: PChar; BufSize: integer); virtual; abstract; - end; - - TVoiceRemoval = class(TSoundEffect) - public - procedure Callback(Buffer: PChar; BufSize: integer); override; - end; - -type - TSyncSource = class - function GetClock(): real; virtual; abstract; - end; - - TAudioProcessingStream = class; - TOnCloseHandler = procedure(Stream: TAudioProcessingStream); - - TAudioProcessingStream = class - protected - OnCloseHandlers: array of TOnCloseHandler; - - function GetLength(): real; virtual; abstract; - function GetPosition(): real; virtual; abstract; - procedure SetPosition(Time: real); virtual; abstract; - function GetLoop(): boolean; virtual; abstract; - procedure SetLoop(Enabled: boolean); virtual; abstract; - - procedure PerformOnClose(); - public - function GetAudioFormatInfo(): TAudioFormatInfo; virtual; abstract; - procedure Close(); virtual; abstract; - - (** - * Adds a new OnClose action handler. - * The handlers are performed in the order they were added. - * If not stated explicitely, member-variables might have been invalidated - * already. So do not use any member (variable/method/...) if you are not - * sure it is valid. - *) - procedure AddOnCloseHandler(Handler: TOnCloseHandler); - - property Length: real read GetLength; - property Position: real read GetPosition write SetPosition; - property Loop: boolean read GetLoop write SetLoop; - end; - - TAudioSourceStream = class(TAudioProcessingStream) - protected - function IsEOF(): boolean; virtual; abstract; - function IsError(): boolean; virtual; abstract; - public - function ReadData(Buffer: PChar; BufferSize: integer): integer; virtual; abstract; - - property EOF: boolean read IsEOF; - property Error: boolean read IsError; - end; - - (* - * State-Chart for playback-stream state transitions - * []: Transition, (): State - * - * /---[Play/FadeIn]--->-\ /-------[Pause]----->-\ - * -[Create]->(Stop) (Play) (Pause) - * \\-<-[Stop/EOF*/Error]-/ \-<---[Play/FadeIn]--// - * \-<------------[Stop/EOF*/Error]--------------/ - * - * *: if not looped, otherwise stream is repeated - * Note: SetPosition() does not change the state. - *) - - TAudioPlaybackStream = class(TAudioProcessingStream) - protected - SyncSource: TSyncSource; - AvgSyncDiff: double; - SourceStream: TAudioSourceStream; - - function GetLatency(): double; virtual; abstract; - function GetStatus(): TStreamStatus; virtual; abstract; - function GetVolume(): single; virtual; abstract; - procedure SetVolume(Volume: single); virtual; abstract; - function Synchronize(BufferSize: integer; FormatInfo: TAudioFormatInfo): integer; - procedure FillBufferWithFrame(Buffer: PChar; BufferSize: integer; Frame: PChar; FrameSize: integer); - public - (** - * Opens a SourceStream for playback. - * Note that the caller (not the TAudioPlaybackStream) is responsible to - * free the SourceStream after the Playback-Stream is closed. - * You may use an OnClose-handler to achieve this. GetSourceStream() - * guarantees to deliver this method's SourceStream parameter to - * the OnClose-handler. Freeing SourceStream at OnClose is allowed. - *) - function Open(SourceStream: TAudioSourceStream): boolean; virtual; abstract; - - procedure Play(); virtual; abstract; - procedure Pause(); virtual; abstract; - procedure Stop(); virtual; abstract; - procedure FadeIn(Time: real; TargetVolume: single); virtual; abstract; - - procedure GetFFTData(var data: TFFTData); virtual; abstract; - function GetPCMData(var data: TPCMData): Cardinal; virtual; abstract; - - procedure AddSoundEffect(Effect: TSoundEffect); virtual; abstract; - procedure RemoveSoundEffect(Effect: TSoundEffect); virtual; abstract; - - procedure SetSyncSource(SyncSource: TSyncSource); - function GetSourceStream(): TAudioSourceStream; - - property Status: TStreamStatus read GetStatus; - property Volume: single read GetVolume write SetVolume; - end; - - TAudioDecodeStream = class(TAudioSourceStream) - end; - - TAudioVoiceStream = class(TAudioSourceStream) - protected - FormatInfo: TAudioFormatInfo; - ChannelMap: integer; - public - destructor Destroy; override; - - function Open(ChannelMap: integer; FormatInfo: TAudioFormatInfo): boolean; virtual; - procedure Close(); override; - - procedure WriteData(Buffer: PChar; BufferSize: integer); virtual; abstract; - function GetAudioFormatInfo(): TAudioFormatInfo; override; - - function GetLength(): real; override; - function GetPosition(): real; override; - procedure SetPosition(Time: real); override; - function GetLoop(): boolean; override; - procedure SetLoop(Enabled: boolean); override; - end; - -type - // soundcard output-devices information - TAudioOutputDevice = class - public - Name: string; // soundcard name - end; - TAudioOutputDeviceList = array of TAudioOutputDevice; - -type - IGenericPlayback = Interface - ['{63A5EBC3-3F4D-4F23-8DFB-B5165FCE33DD}'] - function GetName: String; - - function Open(const Filename: string): boolean; // true if succeed - procedure Close; - - procedure Play; - procedure Pause; - procedure Stop; - - procedure SetPosition(Time: real); - function GetPosition: real; - - property Position: real read GetPosition write SetPosition; - end; - - IVideoPlayback = Interface( IGenericPlayback ) - ['{3574C40C-28AE-4201-B3D1-3D1F0759B131}'] - function Init(): boolean; - function Finalize: boolean; - - procedure GetFrame(Time: Extended); // WANT TO RENAME THESE TO BE MORE GENERIC - procedure DrawGL(Screen: integer); // WANT TO RENAME THESE TO BE MORE GENERIC - - end; - - IVideoVisualization = Interface( IVideoPlayback ) - ['{5AC17D60-B34D-478D-B632-EB00D4078017}'] - end; - - IAudioPlayback = Interface( IGenericPlayback ) - ['{E4AE0B40-3C21-4DC5-847C-20A87E0DFB96}'] - function InitializePlayback: boolean; - function FinalizePlayback: boolean; - - function GetOutputDeviceList(): TAudioOutputDeviceList; - - procedure SetAppVolume(Volume: single); - procedure SetVolume(Volume: single); - procedure SetLoop(Enabled: boolean); - - procedure FadeIn(Time: real; TargetVolume: single); - procedure SetSyncSource(SyncSource: TSyncSource); - - procedure Rewind; - function Finished: boolean; - function Length: real; - - // Sounds - // TODO: - // add a TMediaDummyPlaybackStream implementation that will - // be used by the TSoundLib whenever OpenSound() fails, so checking for - // nil-pointers is not neccessary anymore. - // PlaySound/StopSound will be removed then, OpenSound will be renamed to - // CreateSound. - function OpenSound(const Filename: String): TAudioPlaybackStream; - procedure PlaySound(Stream: TAudioPlaybackStream); - procedure StopSound(Stream: TAudioPlaybackStream); - - // Equalizer - procedure GetFFTData(var Data: TFFTData); - - // Interface for Visualizer - function GetPCMData(var Data: TPCMData): Cardinal; - - function CreateVoiceStream(ChannelMap: integer; FormatInfo: TAudioFormatInfo): TAudioVoiceStream; - end; - - IGenericDecoder = Interface - ['{557B0E9A-604D-47E4-B826-13769F3E10B7}'] - function GetName(): String; - function InitializeDecoder(): boolean; - function FinalizeDecoder(): 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; - function InitializeRecord: boolean; - function FinalizeRecord(): boolean; - - procedure CaptureStart; - procedure CaptureStop; - end; - -type - TAudioConverter = class - protected - fSrcFormatInfo: TAudioFormatInfo; - fDstFormatInfo: TAudioFormatInfo; - public - function Init(SrcFormatInfo: TAudioFormatInfo; DstFormatInfo: TAudioFormatInfo): boolean; virtual; - destructor Destroy(); override; - - (** - * Converts the InputBuffer and stores the result in OutputBuffer. - * If the result is not -1, InputSize will be set to the actual number of - * input-buffer bytes used. - * Returns the number of bytes written to the output-buffer or -1 if an error occured. - *) - function Convert(InputBuffer: PChar; OutputBuffer: PChar; var InputSize: integer): integer; virtual; abstract; - - (** - * Destination/Source size ratio - *) - function GetRatio(): double; virtual; abstract; - - function GetOutputBufferSize(InputSize: integer): integer; virtual; abstract; - property SrcFormatInfo: TAudioFormatInfo read fSrcFormatInfo; - property DstFormatInfo: TAudioFormatInfo read fDstFormatInfo; - end; - -(* TODO -const - SOUNDID_START = 0; - SOUNDID_BACK = 1; - SOUNDID_SWOOSH = 2; - SOUNDID_CHANGE = 3; - SOUNDID_OPTION = 4; - SOUNDID_CLICK = 5; - LAST_SOUNDID = SOUNDID_CLICK; - - BaseSoundFilenames: array[0..LAST_SOUNDID] of string = ( - '%SOUNDPATH%/Common start.mp3', // Start - '%SOUNDPATH%/Common back.mp3', // Back - '%SOUNDPATH%/menu swoosh.mp3', // Swoosh - '%SOUNDPATH%/select music change music 50.mp3', // Change - '%SOUNDPATH%/option change col.mp3', // Option - '%SOUNDPATH%/rimshot022b.mp3' // Click - { - '%SOUNDPATH%/bassdrumhard076b.mp3', // Drum (unused) - '%SOUNDPATH%/hihatclosed068b.mp3', // Hihat (unused) - '%SOUNDPATH%/claps050b.mp3', // Clap (unused) - '%SOUNDPATH%/Shuffle.mp3' // Shuffle (unused) - } - ); -*) - -type - TSoundLibrary = class - private - // TODO - //Sounds: array of TAudioPlaybackStream; - public - // TODO: move sounds to the private section - // and provide IDs instead. - Start: TAudioPlaybackStream; - Back: TAudioPlaybackStream; - Swoosh: TAudioPlaybackStream; - Change: TAudioPlaybackStream; - Option: TAudioPlaybackStream; - Click: TAudioPlaybackStream; - BGMusic: TAudioPlaybackStream; - - constructor Create(); - destructor Destroy(); override; - - procedure LoadSounds(); - procedure UnloadSounds(); - - procedure StartBgMusic(); - procedure PauseBgMusic(); - // TODO - //function AddSound(Filename: string): integer; - //procedure RemoveSound(ID: integer); - //function GetSound(ID: integer): TAudioPlaybackStream; - //property Sound[ID: integer]: TAudioPlaybackStream read GetSound; default; - end; - -var - // TODO: JB --- THESE SHOULD NOT BE GLOBAL - Lines: array of TLines; - LyricsState: TLyricsState; - SoundLib: TSoundLibrary; - - -procedure InitializeSound; -procedure InitializeVideo; -procedure FinalizeMedia; - -function Visualization(): IVideoPlayback; -function VideoPlayback(): IVideoPlayback; -function AudioPlayback(): IAudioPlayback; -function AudioInput(): IAudioInput; -function AudioDecoders(): TInterfaceList; - -function MediaManager: TInterfaceList; - -procedure DumpMediaInterfaces(); - -implementation - -uses - sysutils, - math, - UIni, - UMain, - UCommandLine, - URecord, - ULog; - -var - DefaultVideoPlayback : IVideoPlayback; - DefaultVisualization : IVideoPlayback; - DefaultAudioPlayback : IAudioPlayback; - DefaultAudioInput : IAudioInput; - AudioDecoderList : TInterfaceList; - MediaInterfaceList : TInterfaceList; - - -constructor TAudioFormatInfo.Create(Channels: byte; SampleRate: double; Format: TAudioSampleFormat); -begin - inherited Create(); - fChannels := Channels; - fSampleRate := SampleRate; - fFormat := Format; - UpdateFrameSize(); -end; - -procedure TAudioFormatInfo.SetChannels(Channels: byte); -begin - fChannels := Channels; - UpdateFrameSize(); -end; - -procedure TAudioFormatInfo.SetFormat(Format: TAudioSampleFormat); -begin - fFormat := Format; - UpdateFrameSize(); -end; - -function TAudioFormatInfo.GetBytesPerSec(): double; -begin - Result := FrameSize * SampleRate; -end; - -procedure TAudioFormatInfo.UpdateFrameSize(); -begin - fFrameSize := AudioSampleSize[fFormat] * fChannels; -end; - -function TAudioFormatInfo.Copy(): TAudioFormatInfo; -begin - Result := TAudioFormatInfo.Create(Self.Channels, Self.SampleRate, Self.Format); -end; - -function TAudioFormatInfo.GetRatio(TargetInfo: TAudioFormatInfo): double; -begin - Result := (TargetInfo.FrameSize / Self.FrameSize) * - (TargetInfo.SampleRate / Self.SampleRate) -end; - - -function MediaManager: TInterfaceList; -begin - if (not assigned(MediaInterfaceList)) then - MediaInterfaceList := TInterfaceList.Create(); - Result := MediaInterfaceList; -end; - -function VideoPlayback(): IVideoPlayback; -begin - Result := DefaultVideoPlayback; -end; - -function Visualization(): IVideoPlayback; -begin - Result := DefaultVisualization; -end; - -function AudioPlayback(): IAudioPlayback; -begin - Result := DefaultAudioPlayback; -end; - -function AudioInput(): IAudioInput; -begin - Result := DefaultAudioInput; -end; - -function AudioDecoders(): TInterfaceList; -begin - Result := AudioDecoderList; -end; - -procedure FilterInterfaceList(const IID: TGUID; InList, OutList: TInterfaceList); -var - i: integer; - obj: IInterface; -begin - if (not assigned(OutList)) then - Exit; - - OutList.Clear; - for i := 0 to InList.Count-1 do - begin - if assigned(InList[i]) then - begin - // add object to list if it implements the interface searched for - if (InList[i].QueryInterface(IID, obj) = 0) then - OutList.Add(obj); - end; - end; -end; - -procedure InitializeSound; -var - i: integer; - InterfaceList: TInterfaceList; - CurrentAudioDecoder: IAudioDecoder; - CurrentAudioPlayback: IAudioPlayback; - CurrentAudioInput: IAudioInput; -begin - // create a temporary list for interface enumeration - InterfaceList := TInterfaceList.Create(); - - // initialize all audio-decoders first - FilterInterfaceList(IAudioDecoder, MediaManager, InterfaceList); - for i := 0 to InterfaceList.Count-1 do - begin - CurrentAudioDecoder := IAudioDecoder(InterfaceList[i]); - if (not CurrentAudioDecoder.InitializeDecoder()) then - begin - Log.LogError('Initialize failed, Removing - '+ CurrentAudioDecoder.GetName); - MediaManager.Remove(CurrentAudioDecoder); - end; - end; - - // create and setup decoder-list (see AudioDecoders()) - AudioDecoderList := TInterfaceList.Create; - FilterInterfaceList(IAudioDecoder, MediaManager, AudioDecoders); - - // find and initialize playback interface - DefaultAudioPlayback := nil; - FilterInterfaceList(IAudioPlayback, MediaManager, InterfaceList); - for i := 0 to InterfaceList.Count-1 do - begin - CurrentAudioPlayback := IAudioPlayback(InterfaceList[i]); - if (CurrentAudioPlayback.InitializePlayback()) then - begin - DefaultAudioPlayback := CurrentAudioPlayback; - break; - end; - Log.LogError('Initialize failed, Removing - '+ CurrentAudioPlayback.GetName); - MediaManager.Remove(CurrentAudioPlayback); - end; - - // find and initialize input interface - DefaultAudioInput := nil; - FilterInterfaceList(IAudioInput, MediaManager, InterfaceList); - for i := 0 to InterfaceList.Count-1 do - begin - CurrentAudioInput := IAudioInput(InterfaceList[i]); - if (CurrentAudioInput.InitializeRecord()) then - begin - DefaultAudioInput := CurrentAudioInput; - break; - end; - Log.LogError('Initialize failed, Removing - '+ CurrentAudioInput.GetName); - MediaManager.Remove(CurrentAudioInput); - end; - - InterfaceList.Free; - - // Update input-device list with registered devices - AudioInputProcessor.UpdateInputDeviceConfig(); - - // Load in-game sounds - SoundLib := TSoundLibrary.Create; -end; - -procedure InitializeVideo(); -var - i: integer; - InterfaceList: TInterfaceList; - VideoInterface: IVideoPlayback; - VisualInterface: IVideoVisualization; -begin - InterfaceList := TInterfaceList.Create; - - // initialize and set video-playback singleton - DefaultVideoPlayback := nil; - FilterInterfaceList(IVideoPlayback, MediaManager, InterfaceList); - for i := 0 to InterfaceList.Count-1 do - begin - VideoInterface := IVideoPlayback(InterfaceList[i]); - if (VideoInterface.Init()) then - begin - DefaultVideoPlayback := VideoInterface; - break; - end; - Log.LogError('Initialize failed, Removing - '+ VideoInterface.GetName); - MediaManager.Remove(VideoInterface); - end; - - // initialize and set visualization singleton - DefaultVisualization := nil; - FilterInterfaceList(IVideoVisualization, MediaManager, InterfaceList); - for i := 0 to InterfaceList.Count-1 do - begin - VisualInterface := IVideoVisualization(InterfaceList[i]); - if (VisualInterface.Init()) then - begin - DefaultVisualization := VisualInterface; - break; - end; - Log.LogError('Initialize failed, Removing - '+ VisualInterface.GetName); - MediaManager.Remove(VisualInterface); - end; - - InterfaceList.Free; - - // now that we have all interfaces, we can dump them - // TODO: move this to another place - if FindCmdLineSwitch( cMediaInterfaces ) then - begin - DumpMediaInterfaces(); - halt; - end; -end; - -procedure UnloadMediaModules; -var - i: integer; - InterfaceList: TInterfaceList; -begin - FreeAndNil(AudioDecoderList); - DefaultAudioPlayback := nil; - DefaultAudioInput := nil; - DefaultVideoPlayback := nil; - DefaultVisualization := nil; - - // create temporary interface list - InterfaceList := TInterfaceList.Create(); - - // finalize audio playback interfaces (should be done before the decoders) - FilterInterfaceList(IAudioPlayback, MediaManager, InterfaceList); - for i := 0 to InterfaceList.Count-1 do - IAudioPlayback(InterfaceList[i]).FinalizePlayback(); - - // finalize audio input interfaces - FilterInterfaceList(IAudioInput, MediaManager, InterfaceList); - for i := 0 to InterfaceList.Count-1 do - IAudioInput(InterfaceList[i]).FinalizeRecord(); - - // finalize audio decoder interfaces - FilterInterfaceList(IAudioDecoder, MediaManager, InterfaceList); - for i := 0 to InterfaceList.Count-1 do - IAudioDecoder(InterfaceList[i]).FinalizeDecoder(); - - // finalize video interfaces - FilterInterfaceList(IVideoPlayback, MediaManager, InterfaceList); - for i := 0 to InterfaceList.Count-1 do - IVideoPlayback(InterfaceList[i]).Finalize(); - - // finalize audio decoder interfaces - FilterInterfaceList(IVideoVisualization, MediaManager, InterfaceList); - for i := 0 to InterfaceList.Count-1 do - IVideoVisualization(InterfaceList[i]).Finalize(); - - InterfaceList.Free; - - // finally free interfaces (by removing all references to them) - FreeAndNil(MediaInterfaceList); -end; - -procedure FinalizeMedia; -begin - // stop, close and free sounds - SoundLib.Free; - - // stop and close music stream - if (AudioPlayback <> nil) then - AudioPlayback.Close; - - // stop any active captures - if (AudioInput <> nil) then - AudioInput.CaptureStop; - - if (VideoPlayback <> nil) then - VideoPlayback.Close; - - if (Visualization <> nil) then - Visualization.Close; - - UnloadMediaModules(); -end; - -procedure DumpMediaInterfaces(); -begin - writeln( '' ); - writeln( '--------------------------------------------------------------' ); - writeln( ' In-use Media Interfaces ' ); - writeln( '--------------------------------------------------------------' ); - writeln( 'Registered Audio Playback Interface : ' + AudioPlayback.GetName ); - writeln( 'Registered Audio Input Interface : ' + AudioInput.GetName ); - writeln( 'Registered Video Playback Interface : ' + VideoPlayback.GetName ); - writeln( 'Registered Visualization Interface : ' + Visualization.GetName ); - writeln( '--------------------------------------------------------------' ); - writeln( '' ); -end; - - -{ TSoundLibrary } - -constructor TSoundLibrary.Create(); -begin - inherited; - LoadSounds(); -end; - -destructor TSoundLibrary.Destroy(); -begin - UnloadSounds(); - inherited; -end; - -procedure TSoundLibrary.LoadSounds(); -begin - UnloadSounds(); - - Start := AudioPlayback.OpenSound(SoundPath + 'Common start.mp3'); - Back := AudioPlayback.OpenSound(SoundPath + 'Common back.mp3'); - Swoosh := AudioPlayback.OpenSound(SoundPath + 'menu swoosh.mp3'); - Change := AudioPlayback.OpenSound(SoundPath + 'select music change music 50.mp3'); - Option := AudioPlayback.OpenSound(SoundPath + 'option change col.mp3'); - Click := AudioPlayback.OpenSound(SoundPath + 'rimshot022b.mp3'); - - BGMusic := AudioPlayback.OpenSound(SoundPath + 'Bebeto_-_Loop010.mp3'); - - if (BGMusic <> nil) then - BGMusic.Loop := True; -end; - -procedure TSoundLibrary.UnloadSounds(); -begin - FreeAndNil(Start); - FreeAndNil(Back); - FreeAndNil(Swoosh); - FreeAndNil(Change); - FreeAndNil(Option); - FreeAndNil(Click); - FreeAndNil(BGMusic); -end; - -(* TODO -function TSoundLibrary.GetSound(ID: integer): TAudioPlaybackStream; -begin - if ((ID >= 0) and (ID < Length(Sounds))) then - Result := Sounds[ID] - else - Result := nil; -end; -*) - -procedure TSoundLibrary.StartBgMusic(); -begin - if (TBackgroundMusicOption(Ini.BackgroundMusicOption) = bmoOn) and - (Soundlib.BGMusic <> nil) and not (Soundlib.BGMusic.Status = ssPlaying) then - begin - AudioPlayback.PlaySound(Soundlib.BGMusic); - end; -end; - -procedure TSoundLibrary.PauseBgMusic(); -begin - If (Soundlib.BGMusic <> nil) then - begin - Soundlib.BGMusic.Pause; - end; -end; - -{ TVoiceRemoval } - -procedure TVoiceRemoval.Callback(Buffer: PChar; BufSize: integer); -var - FrameIndex, FrameSize: integer; - Value: integer; - Sample: PPCMStereoSample; -begin - FrameSize := 2 * SizeOf(SmallInt); - for FrameIndex := 0 to (BufSize div FrameSize)-1 do - begin - Sample := PPCMStereoSample(Buffer); - // channel difference - Value := Sample[0] - Sample[1]; - // clip - if (Value > High(SmallInt)) then - Value := High(SmallInt) - else if (Value < Low(SmallInt)) then - Value := Low(SmallInt); - // assign result - Sample[0] := Value; - Sample[1] := Value; - // increase to next frame - Inc(Buffer, FrameSize); - end; -end; - - -{ TVoiceRemoval } - -constructor TLyricsState.Create(); -begin - // create a triggered timer, so we can Pause() it, set the time - // and Resume() it afterwards for better synching. - Timer := TRelativeTimer.Create(true); - - // reset state - Reset(); -end; - -procedure TLyricsState.Pause(); -begin - Timer.Pause(); -end; - -procedure TLyricsState.Resume(); -begin - Timer.Resume(); -end; - -procedure TLyricsState.SetCurrentTime(Time: real); -begin - // do not start the timer (if not started already), - // after setting the current time - Timer.SetTime(Time, false); -end; - -function TLyricsState.GetCurrentTime(): real; -begin - Result := Timer.GetTime(); -end; - -(** - * Resets the timer and state of the lyrics. - * The timer will be stopped afterwards so you have to call Resume() - * to start the lyrics timer. - *) -procedure TLyricsState.Reset(); -begin - Pause(); - SetCurrentTime(0); - - StartTime := 0; - TotalTime := 0; - - OldBeat := -1; - MidBeat := -1; - CurrentBeat := -1; - - OldBeatC := -1; - MidBeatC := -1; - CurrentBeatC := -1; - - OldBeatD := -1; - MidBeatD := -1; - CurrentBeatD := -1; -end; - -(** - * Updates the beat information (CurrentBeat/MidBeat/...) according to the - * current lyric time. - *) -procedure TLyricsState.UpdateBeats(); -var - CurLyricsTime: real; -begin - CurLyricsTime := GetCurrentTime(); - - OldBeat := CurrentBeat; - MidBeat := GetMidBeat(CurLyricsTime - StartTime / 1000); - CurrentBeat := Floor(MidBeat); - - OldBeatC := CurrentBeatC; - MidBeatC := GetMidBeat(CurLyricsTime - StartTime / 1000); - CurrentBeatC := Floor(MidBeatC); - - OldBeatD := CurrentBeatD; - // MidBeatD = MidBeat with additional GAP - MidBeatD := -0.5 + GetMidBeat(CurLyricsTime - (StartTime + 120 + 20) / 1000); - CurrentBeatD := Floor(MidBeatD); -end; - - -{ TAudioConverter } - -function TAudioConverter.Init(SrcFormatInfo: TAudioFormatInfo; DstFormatInfo: TAudioFormatInfo): boolean; -begin - fSrcFormatInfo := SrcFormatInfo.Copy(); - fDstFormatInfo := DstFormatInfo.Copy(); - Result := true; -end; - -destructor TAudioConverter.Destroy(); -begin - FreeAndNil(fSrcFormatInfo); - FreeAndNil(fDstFormatInfo); -end; - - -{ TAudioProcessingStream } - -procedure TAudioProcessingStream.AddOnCloseHandler(Handler: TOnCloseHandler); -begin - if (@Handler <> nil) then - begin - SetLength(OnCloseHandlers, System.Length(OnCloseHandlers)+1); - OnCloseHandlers[High(OnCloseHandlers)] := @Handler; - end; -end; - -procedure TAudioProcessingStream.PerformOnClose(); -var i: integer; -begin - for i := 0 to High(OnCloseHandlers) do - begin - OnCloseHandlers[i](Self); - end; -end; - - -{ TAudioPlaybackStream } - -function TAudioPlaybackStream.GetSourceStream(): TAudioSourceStream; -begin - Result := SourceStream; -end; - -procedure TAudioPlaybackStream.SetSyncSource(SyncSource: TSyncSource); -begin - Self.SyncSource := SyncSource; - AvgSyncDiff := -1; -end; - -(* - * Results an adjusted size of the input buffer size to keep the stream in sync - * with the SyncSource. If no SyncSource was assigned to this stream, the - * input buffer size will be returned, so this method will have no effect. - * - * These are the possible cases: - * - Result > BufferSize: stream is behind the sync-source (stream is too slow), - * (Result-BufferSize) bytes of the buffer must be skipped. - * - Result = BufferSize: stream is in sync, - * there is nothing to do. - * - Result < BufferSize: stream is ahead of the sync-source (stream is too fast), - * (BufferSize-Result) bytes of the buffer must be padded. - *) -function TAudioPlaybackStream.Synchronize(BufferSize: integer; FormatInfo: TAudioFormatInfo): integer; -var - TimeDiff: double; - TimeCorrectionFactor: double; -const - AVG_HISTORY_FACTOR = 0.9; - SYNC_THRESHOLD = 0.045; - MAX_SYNC_DIFF_TIME = 0.002; -begin - Result := BufferSize; - - if (not assigned(SyncSource)) then - Exit; - - if (BufferSize <= 0) then - Exit; - - // difference between sync-source and stream position - // (negative if the music-stream's position is ahead of the master clock) - TimeDiff := SyncSource.GetClock() - (Position - GetLatency()); - - // calculate average time difference (some sort of weighted mean). - // The bigger AVG_HISTORY_FACTOR is, the smoother is the average diff. - // This means that older diffs are weighted more with a higher history factor - // than with a lower. Do not use a too low history factor. FFmpeg produces - // very instable timestamps (pts) for ogg due to some bugs. They may differ - // +-50ms from the real stream position. Without filtering those glitches we - // would synch without any need, resulting in ugly plopping sounds. - if (AvgSyncDiff = -1) then - AvgSyncDiff := TimeDiff - else - AvgSyncDiff := TimeDiff * (1-AVG_HISTORY_FACTOR) + - AvgSyncDiff * AVG_HISTORY_FACTOR; - - // check if sync needed - if (Abs(AvgSyncDiff) >= SYNC_THRESHOLD) then - begin - // TODO: use SetPosition if diff is too large (>5s) - if (TimeDiff < 1) then - TimeCorrectionFactor := Sign(TimeDiff)*TimeDiff*TimeDiff - else - TimeCorrectionFactor := TimeDiff; - - // calculate adapted buffer size - // reduce size of data to fetch if music is ahead, increase otherwise - Result := BufferSize + Round(TimeCorrectionFactor * FormatInfo.SampleRate) * FormatInfo.FrameSize; - if (Result < 0) then - Result := 0; - - // reset average - AvgSyncDiff := -1; - end; - - (* - DebugWriteln('Diff: ' + floattostrf(TimeDiff, ffFixed, 15, 3) + - '| SyS: ' + floattostrf(SyncSource.GetClock(), ffFixed, 15, 3) + - '| Pos: ' + floattostrf(Position, ffFixed, 15, 3) + - '| Avg: ' + floattostrf(AvgSyncDiff, ffFixed, 15, 3)); - *) -end; - -(* - * Fills a buffer with copies of the given frame or with 0 if frame. - *) -procedure TAudioPlaybackStream.FillBufferWithFrame(Buffer: PChar; BufferSize: integer; Frame: PChar; FrameSize: integer); -var - i: integer; - FrameCopyCount: integer; -begin - // the buffer must at least contain place for one copy of the frame. - if ((Buffer = nil) or (BufferSize <= 0) or (BufferSize < FrameSize)) then - Exit; - - // no valid frame -> fill with 0 - if ((Frame = nil) or (FrameSize <= 0)) then - begin - FillChar(Buffer[0], BufferSize, 0); - Exit; - end; - - // number of frames to copy - FrameCopyCount := BufferSize div FrameSize; - // insert as many copies of frame into the buffer as possible - for i := 0 to FrameCopyCount-1 do - Move(Frame[0], Buffer[i*FrameSize], FrameSize); -end; - -{ TAudioVoiceStream } - -function TAudioVoiceStream.Open(ChannelMap: integer; FormatInfo: TAudioFormatInfo): boolean; -begin - Self.ChannelMap := ChannelMap; - Self.FormatInfo := FormatInfo.Copy(); - // a voice stream is always mono, reassure the the format is correct - Self.FormatInfo.Channels := 1; - Result := true; -end; - -destructor TAudioVoiceStream.Destroy; -begin - Close(); - inherited; -end; - -procedure TAudioVoiceStream.Close(); -begin - PerformOnClose(); - FreeAndNil(FormatInfo); -end; - -function TAudioVoiceStream.GetAudioFormatInfo(): TAudioFormatInfo; -begin - Result := FormatInfo; -end; - -function TAudioVoiceStream.GetLength(): real; -begin - Result := -1; -end; - -function TAudioVoiceStream.GetPosition(): real; -begin - Result := -1; -end; - -procedure TAudioVoiceStream.SetPosition(Time: real); -begin -end; - -function TAudioVoiceStream.GetLoop(): boolean; -begin - Result := false; -end; - -procedure TAudioVoiceStream.SetLoop(Enabled: boolean); -begin -end; - - -end. |