From 868ce765441473e7d1fec9b3ad22a707f121a637 Mon Sep 17 00:00:00 2001 From: tobigun Date: Wed, 21 Apr 2010 18:27:36 +0000 Subject: - add video loop option - allow multiple instances of a video git-svn-id: svn://svn.code.sf.net/p/ultrastardx/svn/trunk@2260 b956fd51-792f-4845-bead-9b4dfca2ff2c --- src/base/UMusic.pas | 39 ++++--- src/media/UMedia_dummy.pas | 200 +++++++++++++++++++++++++-------- src/media/UVideo.pas | 162 +++++++++++++++++---------- src/media/UVisualizer.pas | 230 ++++++++++++++++++++------------------ src/menu/UMenuBackgroundVideo.pas | 39 ++++--- src/screens/UScreenSing.pas | 69 ++++++------ 6 files changed, 459 insertions(+), 280 deletions(-) (limited to 'src') diff --git a/src/base/UMusic.pas b/src/base/UMusic.pas index 5d816c9a..e349bd1f 100644 --- a/src/base/UMusic.pas +++ b/src/base/UMusic.pas @@ -324,28 +324,33 @@ type IGenericPlayback = Interface ['{63A5EBC3-3F4D-4F23-8DFB-B5165FCE33DD}'] function GetName: String; + end; - function Open(const Filename: IPath): boolean; // true if succeed - procedure Close; - + IVideo = interface + ['{58DFC674-9168-41EA-B59D-A61307242B80}'] procedure Play; procedure Pause; procedure Stop; + procedure SetLoop(Enable: boolean); + function GetLoop(): boolean; + procedure SetPosition(Time: real); function GetPosition: real; + procedure GetFrame(Time: Extended); + procedure DrawGL(Screen: integer); + + property Loop: boolean read GetLoop write SetLoop; 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 + function Init(): boolean; + function Finalize: boolean; + function Open(const FileName : IPath): IVideo; end; IVideoVisualization = Interface( IVideoPlayback ) @@ -370,6 +375,18 @@ type function Finished: boolean; function Length: real; + function Open(const Filename: IPath): 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; + // Sounds // TODO: // add a TMediaDummyPlaybackStream implementation that will @@ -814,12 +831,6 @@ begin if (AudioInput <> nil) then AudioInput.CaptureStop; - if (VideoPlayback <> nil) then - VideoPlayback.Close; - - if (Visualization <> nil) then - Visualization.Close; - UnloadMediaModules(); end; diff --git a/src/media/UMedia_dummy.pas b/src/media/UMedia_dummy.pas index 25e94724..35b8bd70 100644 --- a/src/media/UMedia_dummy.pas +++ b/src/media/UMedia_dummy.pas @@ -42,17 +42,17 @@ uses UPath; type - TMedia_dummy = class( TInterfacedObject, IVideoPlayback, IVideoVisualization, IAudioPlayback, IAudioInput ) + TAudio_Dummy = class( TInterfacedObject, IAudioPlayback, IAudioInput ) private DummyOutputDeviceList: TAudioOutputDeviceList; public constructor Create(); - function GetName: string; + function GetName: string; function Init(): boolean; function Finalize(): boolean; - function Open(const aFileName: IPath): boolean; // true if succeed + function Open(const aFileName: IPath): boolean; // true if succeed procedure Close; procedure Play; @@ -64,9 +64,6 @@ type procedure SetSyncSource(SyncSource: TSyncSource); - procedure GetFrame(Time: Extended); - procedure DrawGL(Screen: integer); - // IAudioInput function InitializeRecord: boolean; function FinalizeRecord: boolean; @@ -83,9 +80,11 @@ type procedure FadeIn(Time: real; TargetVolume: single); procedure SetAppVolume(Volume: single); procedure SetVolume(Volume: single); - procedure SetLoop(Enabled: boolean); procedure Rewind; + procedure SetLoop(Enabled: boolean); + function GetLoop(): boolean; + function Finished: boolean; function Length: real; @@ -98,98 +97,122 @@ type procedure CloseVoiceStream(var VoiceStream: TAudioVoiceStream); end; -function TMedia_dummy.GetName: string; -begin - Result := 'dummy'; -end; + TVideo_Dummy = class( TInterfacedObject, IVideo ) + public + procedure Close; -procedure TMedia_dummy.GetFrame(Time: Extended); -begin -end; + procedure Play; + procedure Pause; + procedure Stop; + + procedure SetLoop(Enable: boolean); + function GetLoop(): boolean; + + procedure SetPosition(Time: real); + function GetPosition: real; + + procedure GetFrame(Time: Extended); + procedure DrawGL(Screen: integer); + + property Loop: boolean read GetLoop write SetLoop; + property Position: real read GetPosition write SetPosition; + end; -procedure TMedia_dummy.DrawGL(Screen: integer); + TVideoPlayback_Dummy = class( TInterfacedObject, IVideoPlayback, IVideoVisualization ) + public + constructor Create(); + function GetName: string; + + function Init(): boolean; + function Finalize(): boolean; + + function Open(const FileName: IPath): IVideo; + end; + +function TAudio_Dummy.GetName: string; begin + Result := 'AudioDummy'; end; -constructor TMedia_dummy.Create(); +constructor TAudio_Dummy.Create(); begin inherited; end; -function TMedia_dummy.Init(): boolean; +function TAudio_Dummy.Init(): boolean; begin Result := true; end; -function TMedia_dummy.Finalize(): boolean; +function TAudio_Dummy.Finalize(): boolean; begin Result := true; end; -function TMedia_dummy.Open(const aFileName : IPath): boolean; // true if succeed +function TAudio_Dummy.Open(const aFileName : IPath): boolean; // true if succeed begin Result := false; end; -procedure TMedia_dummy.Close; +procedure TAudio_Dummy.Close; begin end; -procedure TMedia_dummy.Play; +procedure TAudio_Dummy.Play; begin end; -procedure TMedia_dummy.Pause; +procedure TAudio_Dummy.Pause; begin end; -procedure TMedia_dummy.Stop; +procedure TAudio_Dummy.Stop; begin end; -procedure TMedia_dummy.SetPosition(Time: real); +procedure TAudio_Dummy.SetPosition(Time: real); begin end; -function TMedia_dummy.GetPosition: real; +function TAudio_Dummy.GetPosition: real; begin Result := 0; end; -procedure TMedia_dummy.SetSyncSource(SyncSource: TSyncSource); +procedure TAudio_Dummy.SetSyncSource(SyncSource: TSyncSource); begin end; // IAudioInput -function TMedia_dummy.InitializeRecord: boolean; +function TAudio_Dummy.InitializeRecord: boolean; begin Result := true; end; -function TMedia_dummy.FinalizeRecord: boolean; +function TAudio_Dummy.FinalizeRecord: boolean; begin Result := true; end; -procedure TMedia_dummy.CaptureStart; +procedure TAudio_Dummy.CaptureStart; begin end; -procedure TMedia_dummy.CaptureStop; +procedure TAudio_Dummy.CaptureStop; begin end; -procedure TMedia_dummy.GetFFTData(var data: TFFTData); +procedure TAudio_Dummy.GetFFTData(var data: TFFTData); begin end; -function TMedia_dummy.GetPCMData(var data: TPCMData): Cardinal; +function TAudio_Dummy.GetPCMData(var data: TPCMData): Cardinal; begin Result := 0; end; // IAudioPlayback -function TMedia_dummy.InitializePlayback: boolean; +function TAudio_Dummy.InitializePlayback: boolean; begin SetLength(DummyOutputDeviceList, 1); DummyOutputDeviceList[0] := TAudioOutputDevice.Create(); @@ -197,73 +220,152 @@ begin Result := true; end; -function TMedia_dummy.FinalizePlayback: boolean; +function TAudio_Dummy.FinalizePlayback: boolean; begin Result := true; end; -function TMedia_dummy.GetOutputDeviceList(): TAudioOutputDeviceList; +function TAudio_Dummy.GetOutputDeviceList(): TAudioOutputDeviceList; begin Result := DummyOutputDeviceList; end; -procedure TMedia_dummy.SetAppVolume(Volume: single); +procedure TAudio_Dummy.SetAppVolume(Volume: single); +begin +end; + +procedure TAudio_Dummy.SetVolume(Volume: single); begin end; -procedure TMedia_dummy.SetVolume(Volume: single); +procedure TAudio_Dummy.SetLoop(Enabled: boolean); begin end; -procedure TMedia_dummy.SetLoop(Enabled: boolean); +function TAudio_Dummy.GetLoop(): boolean; begin + Result := false; end; -procedure TMedia_dummy.FadeIn(Time: real; TargetVolume: single); +procedure TAudio_Dummy.FadeIn(Time: real; TargetVolume: single); begin end; -procedure TMedia_dummy.Rewind; +procedure TAudio_Dummy.Rewind; begin end; -function TMedia_dummy.Finished: boolean; +function TAudio_Dummy.Finished: boolean; begin Result := false; end; -function TMedia_dummy.Length: real; +function TAudio_Dummy.Length: real; begin Result := 60; end; -function TMedia_dummy.OpenSound(const Filename: IPath): TAudioPlaybackStream; +function TAudio_Dummy.OpenSound(const Filename: IPath): TAudioPlaybackStream; begin Result := nil; end; -procedure TMedia_dummy.CloseSound(var PlaybackStream: TAudioPlaybackStream); +procedure TAudio_Dummy.CloseSound(var PlaybackStream: TAudioPlaybackStream); begin end; -procedure TMedia_dummy.PlaySound(stream: TAudioPlaybackStream); +procedure TAudio_Dummy.PlaySound(stream: TAudioPlaybackStream); begin end; -procedure TMedia_dummy.StopSound(stream: TAudioPlaybackStream); +procedure TAudio_Dummy.StopSound(stream: TAudioPlaybackStream); begin end; -function TMedia_dummy.CreateVoiceStream(Channel: integer; FormatInfo: TAudioFormatInfo): TAudioVoiceStream; +function TAudio_Dummy.CreateVoiceStream(Channel: integer; FormatInfo: TAudioFormatInfo): TAudioVoiceStream; begin Result := nil; end; -procedure TMedia_dummy.CloseVoiceStream(var VoiceStream: TAudioVoiceStream); +procedure TAudio_Dummy.CloseVoiceStream(var VoiceStream: TAudioVoiceStream); +begin +end; + + +{ TVideoPlayback_Dummy } + +procedure TVideo_Dummy.Close; begin end; +procedure TVideo_Dummy.Play; +begin +end; + +procedure TVideo_Dummy.Pause; +begin +end; + +procedure TVideo_Dummy.Stop; +begin +end; + +procedure TVideo_Dummy.SetLoop(Enable: boolean); +begin +end; + +function TVideo_Dummy.GetLoop(): boolean; +begin + Result := false; +end; + +procedure TVideo_Dummy.SetPosition(Time: real); +begin +end; + +function TVideo_Dummy.GetPosition: real; +begin + Result := 0; +end; + +procedure TVideo_Dummy.GetFrame(Time: Extended); +begin +end; + +procedure TVideo_Dummy.DrawGL(Screen: integer); +begin +end; + + +{ TVideoPlayback_Dummy } + +constructor TVideoPlayback_Dummy.Create(); +begin +end; + +function TVideoPlayback_Dummy.GetName: string; +begin + Result := 'VideoDummy'; +end; + +function TVideoPlayback_Dummy.Init(): boolean; +begin + Result := true; +end; + +function TVideoPlayback_Dummy.Finalize(): boolean; +begin + Result := true; +end; + +function TVideoPlayback_Dummy.Open(const FileName: IPath): IVideo; +begin + Result := TVideo_Dummy.Create; +end; + + initialization - MediaManager.Add(TMedia_dummy.Create); + MediaManager.Add(TAudio_Dummy.Create); + MediaManager.Add(TVideoPlayback_Dummy.Create); end. diff --git a/src/media/UVideo.pas b/src/media/UVideo.pas index 27f29f7e..fd3c7e2a 100644 --- a/src/media/UVideo.pas +++ b/src/media/UVideo.pas @@ -107,11 +107,15 @@ type Upper, Lower: double; end; - TVideoPlayback_FFmpeg = class( TInterfacedObject, IVideoPlayback ) + IVideo_FFmpeg = interface (IVideo) + ['{E640E130-C8C0-4399-AF02-67A3569313AB}'] + function Open(const FileName: IPath): boolean; + end; + + TVideo_FFmpeg = class( TInterfacedObject, IVideo_FFmpeg ) private fOpened: boolean; //**< stream successfully opened fPaused: boolean; //**< stream paused - fInitialized: boolean; fEOF: boolean; //**< end-of-file state fLoop: boolean; //**< looping enabled @@ -150,23 +154,37 @@ type procedure ShowDebugInfo(); public - function GetName: String; + constructor Create; + destructor Destroy; override; + + function Open(const FileName: IPath): boolean; + procedure Close; - function Init(): boolean; - function Finalize: boolean; + procedure Play; + procedure Pause; + procedure Stop; + + procedure SetLoop(Enable: boolean); + function GetLoop(): boolean; + + procedure SetPosition(Time: real); + function GetPosition: real; + + procedure GetFrame(Time: Extended); + procedure DrawGL(Screen: integer); + end; - function Open(const FileName : IPath): boolean; // true if succeed - procedure Close; + TVideoPlayback_FFmpeg = class( TInterfacedObject, IVideoPlayback ) + private + fInitialized: boolean; - procedure Play; - procedure Pause; - procedure Stop; + public + function GetName: String; - procedure SetPosition(Time: real); - function GetPosition: real; + function Init(): boolean; + function Finalize: boolean; - procedure GetFrame(Time: Extended); - procedure DrawGL(Screen: integer); + function Open(const FileName : IPath): IVideo; end; var @@ -219,47 +237,46 @@ begin FFmpegCore := TMediaCore_FFmpeg.GetInstance(); - Reset(); av_register_all(); - glGenTextures(1, PGLuint(@fFrameTex)); end; function TVideoPlayback_FFmpeg.Finalize(): boolean; begin - Close(); - glDeleteTextures(1, PGLuint(@fFrameTex)); Result := true; end; -procedure TVideoPlayback_FFmpeg.Reset(); +function TVideoPlayback_FFmpeg.Open(const FileName : IPath): IVideo; +var + Video: IVideo_FFmpeg; begin - // close previously opened video - Close(); + Video := TVideo_FFmpeg.Create; + if Video.Open(FileName) then + Result := Video + else + Result := nil; +end; - fOpened := False; - fPaused := False; - fTimeBase := 0; - fTime := 0; - fStream := nil; - fStreamIndex := -1; - fFrameTexValid := false; - fEOF := false; +{* TVideo_FFmpeg *} - // TODO: do we really want this by default? - fLoop := true; - fLoopTime := 0; +constructor TVideo_FFmpeg.Create; +begin + glGenTextures(1, PGLuint(@fFrameTex)); + Reset(); +end; - fAspectCorrection := acoCrop; +destructor TVideo_FFmpeg.Destroy; +begin + Close(); + glDeleteTextures(1, PGLuint(@fFrameTex)); end; -function TVideoPlayback_FFmpeg.Open(const FileName : IPath): boolean; // true if succeed +function TVideo_FFmpeg.Open(const FileName : IPath): boolean; var errnum: Integer; AudioStreamIndex: integer; begin Result := false; - Reset(); // use custom 'ufile' protocol for UTF-8 support @@ -408,6 +425,7 @@ begin end; {$ENDIF} + fTexWidth := Round(Power(2, Ceil(Log2(fCodecContext^.width)))); fTexHeight := Round(Power(2, Ceil(Log2(fCodecContext^.height)))); @@ -420,11 +438,32 @@ begin glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - fOpened := True; + fOpened := true; Result := true; end; -procedure TVideoPlayback_FFmpeg.Close; +procedure TVideo_FFmpeg.Reset(); +begin + // close previously opened video + Close(); + + fOpened := False; + fPaused := False; + fTimeBase := 0; + fTime := 0; + fStream := nil; + fStreamIndex := -1; + fFrameTexValid := false; + + fEOF := false; + + fLoop := false; + fLoopTime := 0; + + fAspectCorrection := acoCrop; +end; + +procedure TVideo_FFmpeg.Close; begin if (fFrameBuffer <> nil) then av_free(fFrameBuffer); @@ -457,7 +496,7 @@ begin fOpened := False; end; -procedure TVideoPlayback_FFmpeg.SynchronizeTime(Frame: PAVFrame; var pts: double); +procedure TVideo_FFmpeg.SynchronizeTime(Frame: PAVFrame; var pts: double); var FrameDelay: double; begin @@ -484,7 +523,7 @@ end; * @param pts will be updated to the presentation time of the decoded frame. * returns true if a frame could be decoded. False if an error or EOF occured. *} -function TVideoPlayback_FFmpeg.DecodeFrame(): boolean; +function TVideo_FFmpeg.DecodeFrame(): boolean; var FrameFinished: Integer; VideoPktPts: int64; @@ -522,7 +561,10 @@ begin // check for errors if (url_ferror(pbIOCtx) <> 0) then + begin + Log.LogError('Video decoding file error', 'TVideoPlayback_FFmpeg.DecodeFrame'); Exit; + end; // url_feof() does not detect an EOF for some mov-files (e.g. deluxe.mov) // so we have to do it this way. @@ -533,18 +575,9 @@ begin Exit; end; - // no error -> wait for user input -{ - SDL_Delay(100); // initial version, left for documentation - continue; -} - - // Patch by Hawkear: - // Why should this function loop in an endless loop if there is an error? - // This runs in the main thread, so it halts the whole program - // Therefore, it is better to exit when an error occurs + // error occured, log and exit + Log.LogError('Video decoding error', 'TVideoPlayback_FFmpeg.DecodeFrame'); Exit; - end; // if we got a packet from the video stream, then decode it @@ -593,7 +626,7 @@ begin Result := true; end; -procedure TVideoPlayback_FFmpeg.GetFrame(Time: Extended); +procedure TVideo_FFmpeg.GetFrame(Time: Extended); var errnum: Integer; NewTime: Extended; @@ -749,7 +782,7 @@ begin {$ENDIF} end; -procedure TVideoPlayback_FFmpeg.GetVideoRect(var ScreenRect, TexRect: TRectCoords); +procedure TVideo_FFmpeg.GetVideoRect(var ScreenRect, TexRect: TRectCoords); var ScreenAspect: double; // aspect of screen resolution ScaledVideoWidth, ScaledVideoHeight: double; @@ -799,7 +832,7 @@ begin TexRect.Lower := fCodecContext^.height / fTexHeight; end; -procedure TVideoPlayback_FFmpeg.DrawGL(Screen: integer); +procedure TVideo_FFmpeg.DrawGL(Screen: integer); var ScreenRect: TRectCoords; TexRect: TRectCoords; @@ -862,7 +895,7 @@ begin {$IFEND} end; -procedure TVideoPlayback_FFmpeg.ShowDebugInfo(); +procedure TVideo_FFmpeg.ShowDebugInfo(); begin {$IFDEF Info} if (fTime+fTimeBase < 0) then @@ -899,17 +932,28 @@ begin {$ENDIF} end; -procedure TVideoPlayback_FFmpeg.Play; +procedure TVideo_FFmpeg.Play; begin end; -procedure TVideoPlayback_FFmpeg.Pause; +procedure TVideo_FFmpeg.Pause; begin fPaused := not fPaused; end; -procedure TVideoPlayback_FFmpeg.Stop; +procedure TVideo_FFmpeg.Stop; +begin +end; + +procedure TVideo_FFmpeg.SetLoop(Enable: boolean); +begin + fLoop := Enable; + fLoopTime := 0; +end; + +function TVideo_FFmpeg.GetLoop(): boolean; begin + Result := fLoop; end; {** @@ -920,7 +964,7 @@ end; * actual frame time when GetFrame() is called the next time. * @param Time new position in seconds *} -procedure TVideoPlayback_FFmpeg.SetPosition(Time: real); +procedure TVideo_FFmpeg.SetPosition(Time: real); var SeekFlags: integer; begin @@ -955,7 +999,7 @@ begin avcodec_flush_buffers(fCodecContext); end; -function TVideoPlayback_FFmpeg.GetPosition: real; +function TVideo_FFmpeg.GetPosition: real; begin Result := fTime; end; diff --git a/src/media/UVisualizer.pas b/src/media/UVisualizer.pas index b25d68a9..4f553521 100644 --- a/src/media/UVisualizer.pas +++ b/src/media/UVisualizer.pas @@ -60,12 +60,17 @@ interface {$I switches.inc} +{.$DEFINE UseTexture} + uses SDL, UGraphicClasses, textgl, math, gl, + {$IFDEF UseTexture} + glu, + {$ENDIF} SysUtils, UIni, projectM, @@ -90,32 +95,30 @@ const textureSize = 512; {$IFEND} +type + TProjectMState = ( pmPlay, pmStop, pmPause ); + type TGLMatrix = array[0..3, 0..3] of GLdouble; TGLMatrixStack = array of TGLMatrix; type - TVideoPlayback_ProjectM = class( TInterfacedObject, IVideoPlayback, IVideoVisualization ) + TVideo_ProjectM = class( TInterfacedObject, IVideo ) private - pm: TProjectM; - ProjectMPath : string; - Initialized: boolean; - - VisualizerStarted: boolean; - VisualizerPaused: boolean; + fPm: TProjectM; + fProjectMPath : string; - VisualTex: GLuint; - PCMData: TPCMData; - RndPCMcount: integer; + fState: TProjectMState; - ModelviewMatrixStack: TGLMatrixStack; - ProjectionMatrixStack: TGLMatrixStack; - TextureMatrixStack: TGLMatrixStack; + fVisualTex: GLuint; + fPCMData: TPCMData; + fRndPCMcount: integer; - procedure VisualizerStart; - procedure VisualizerStop; + fModelviewMatrixStack: TGLMatrixStack; + fProjectionMatrixStack: TGLMatrixStack; + fTextureMatrixStack: TGLMatrixStack; - procedure VisualizerTogglePause; + procedure InitProjectM; function GetRandomPCMData(var Data: TPCMData): Cardinal; @@ -126,12 +129,9 @@ type procedure RestoreOpenGLState(); public - function GetName: String; + constructor Create; + destructor Destroy; override; - function Init(): boolean; - function Finalize(): boolean; - - function Open(const aFileName: IPath): boolean; // true if succeed procedure Close; procedure Play; @@ -141,10 +141,28 @@ type procedure SetPosition(Time: real); function GetPosition: real; + procedure SetLoop(Enable: boolean); + function GetLoop(): boolean; + procedure GetFrame(Time: Extended); procedure DrawGL(Screen: integer); end; + TVideoPlayback_ProjectM = class( TInterfacedObject, IVideoVisualization ) + private + fInitialized: boolean; + + public + function GetName: String; + + function Init(): boolean; + function Finalize(): boolean; + + function Open(const aFileName: IPath): IVideo; + end; + + +{ TVideoPlayback_ProjectM } function TVideoPlayback_ProjectM.GetName: String; begin @@ -154,76 +172,100 @@ end; function TVideoPlayback_ProjectM.Init(): boolean; begin Result := true; - - if (Initialized) then + if (fInitialized) then Exit; - Initialized := true; + fInitialized := true; +end; - RndPCMcount := 0; +function TVideoPlayback_ProjectM.Finalize(): boolean; +begin + Result := true; +end; + +function TVideoPlayback_ProjectM.Open(const aFileName: IPath): IVideo; +begin + Result := TVideo_ProjectM.Create; +end; - ProjectMPath := ProjectM_DataDir + PathDelim; - VisualizerStarted := False; - VisualizerPaused := False; +{ TVideo_ProjectM } + +constructor TVideo_ProjectM.Create; +begin + fRndPCMcount := 0; + + fProjectMPath := ProjectM_DataDir + PathDelim; + + fState := pmStop; {$IFDEF UseTexture} - glGenTextures(1, PglUint(@VisualTex)); - glBindTexture(GL_TEXTURE_2D, VisualTex); + glGenTextures(1, PglUint(@fVisualTex)); + glBindTexture(GL_TEXTURE_2D, fVisualTex); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); {$ENDIF} + + InitProjectM(); end; -function TVideoPlayback_ProjectM.Finalize(): boolean; +destructor TVideo_ProjectM.Destroy; begin - VisualizerStop(); + Close(); {$IFDEF UseTexture} - glDeleteTextures(1, PglUint(@VisualTex)); + glDeleteTextures(1, PglUint(@fVisualTex)); {$ENDIF} - Result := true; end; -function TVideoPlayback_ProjectM.Open(const aFileName: IPath): boolean; // true if succeed +procedure TVideo_ProjectM.Close; begin - Result := false; + FreeAndNil(fPm); end; -procedure TVideoPlayback_ProjectM.Close; +procedure TVideo_ProjectM.Play; begin - VisualizerStop(); + if (fState = pmStop) and (assigned(fPm)) then + fPm.RandomPreset(); + fState := pmPlay; end; -procedure TVideoPlayback_ProjectM.Play; +procedure TVideo_ProjectM.Pause; begin - VisualizerStart(); + if (fState = pmPlay) then + fState := pmPause + else if (fState = pmPause) then + fState := pmPlay; end; -procedure TVideoPlayback_ProjectM.Pause; +procedure TVideo_ProjectM.Stop; begin - VisualizerTogglePause(); + fState := pmStop; end; -procedure TVideoPlayback_ProjectM.Stop; +procedure TVideo_ProjectM.SetPosition(Time: real); begin - VisualizerStop(); + if assigned(fPm) then + fPm.RandomPreset(); end; -procedure TVideoPlayback_ProjectM.SetPosition(Time: real); +function TVideo_ProjectM.GetPosition: real; begin - if assigned(pm) then - pm.RandomPreset(); + Result := 0; end; -function TVideoPlayback_ProjectM.GetPosition: real; +procedure TVideo_ProjectM.SetLoop(Enable: boolean); begin - Result := 0; +end; + +function TVideo_ProjectM.GetLoop(): boolean; +begin + Result := true; end; {** * Returns the stack depth of the given OpenGL matrix mode stack. *} -function TVideoPlayback_ProjectM.GetMatrixStackDepth(MatrixMode: GLenum): GLint; +function TVideo_ProjectM.GetMatrixStackDepth(MatrixMode: GLenum): GLint; begin // get number of matrices on stack case (MatrixMode) of @@ -253,7 +295,7 @@ end; * By saving the whole stack we are on the safe side, so a nasty bug in the * visualizer does not corrupt USDX. *} -procedure TVideoPlayback_ProjectM.SaveMatrixStack(MatrixMode: GLenum; +procedure TVideo_ProjectM.SaveMatrixStack(MatrixMode: GLenum; var MatrixStack: TGLMatrixStack); var I: integer; @@ -289,7 +331,7 @@ end; {** * Restores the OpenGL matrix stack stored with SaveMatrixStack. *} -procedure TVideoPlayback_ProjectM.RestoreMatrixStack(MatrixMode: GLenum; +procedure TVideo_ProjectM.RestoreMatrixStack(MatrixMode: GLenum; var MatrixStack: TGLMatrixStack); var I: integer; @@ -325,15 +367,15 @@ end; * - Modelview-matrix is pushed to the Modelview-stack * - the OpenGL error-state (glGetError) is cleared *} -procedure TVideoPlayback_ProjectM.SaveOpenGLState(); +procedure TVideo_ProjectM.SaveOpenGLState(); begin // save all OpenGL state-machine attributes glPushAttrib(GL_ALL_ATTRIB_BITS); glPushClientAttrib(GL_CLIENT_ALL_ATTRIB_BITS); - SaveMatrixStack(GL_PROJECTION, ProjectionMatrixStack); - SaveMatrixStack(GL_MODELVIEW, ModelviewMatrixStack); - SaveMatrixStack(GL_TEXTURE, TextureMatrixStack); + SaveMatrixStack(GL_PROJECTION, fProjectionMatrixStack); + SaveMatrixStack(GL_MODELVIEW, fModelviewMatrixStack); + SaveMatrixStack(GL_TEXTURE, fTextureMatrixStack); glMatrixMode(GL_MODELVIEW); @@ -345,15 +387,15 @@ end; * Restores the OpenGL state saved by SaveOpenGLState() * and resets the error-state. *} -procedure TVideoPlayback_ProjectM.RestoreOpenGLState(); +procedure TVideo_ProjectM.RestoreOpenGLState(); begin // reset OpenGL error-state glGetError(); // restore matrix stacks - RestoreMatrixStack(GL_PROJECTION, ProjectionMatrixStack); - RestoreMatrixStack(GL_MODELVIEW, ModelviewMatrixStack); - RestoreMatrixStack(GL_TEXTURE, TextureMatrixStack); + RestoreMatrixStack(GL_PROJECTION, fProjectionMatrixStack); + RestoreMatrixStack(GL_MODELVIEW, fModelviewMatrixStack); + RestoreMatrixStack(GL_TEXTURE, fTextureMatrixStack); // restore all OpenGL state-machine attributes // (also restores the matrix mode) @@ -361,22 +403,19 @@ begin glPopAttrib(); end; -procedure TVideoPlayback_ProjectM.VisualizerStart; +procedure TVideo_ProjectM.InitProjectM; begin - if VisualizerStarted then - Exit; - // the OpenGL state must be saved before TProjectM.Create is called SaveOpenGLState(); try try {$IF PROJECTM_VERSION >= 1000000} // >= 1.0 - pm := TProjectM.Create(ProjectMPath + 'config.inp'); + fPm := TProjectM.Create(fProjectMPath + 'config.inp'); {$ELSE} - pm := TProjectM.Create( + fPm := TProjectM.Create( meshX, meshY, fps, textureSize, ScreenW, ScreenH, - ProjectMPath + 'presets', ProjectMPath + 'fonts'); + fProjectMPath + 'presets', fProjectMPath + 'fonts'); {$IFEND} except on E: Exception do begin @@ -387,72 +426,51 @@ begin end; // initialize OpenGL - pm.ResetGL(ScreenW, ScreenH); + fPm.ResetGL(ScreenW, ScreenH); // skip projectM default-preset - pm.RandomPreset(); + fPm.RandomPreset(); // projectM >= 1.0 uses the OpenGL FramebufferObject (FBO) extension. // Unfortunately it does NOT reset the framebuffer-context after // TProjectM.Create. Either glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0) for // a manual reset or TProjectM.RenderFrame() must be called. // We use the latter so we do not need to load the FBO extension in USDX. - pm.RenderFrame(); - - VisualizerPaused := false; - VisualizerStarted := true; + fPm.RenderFrame(); finally RestoreOpenGLState(); end; end; -procedure TVideoPlayback_ProjectM.VisualizerStop; -begin - if VisualizerStarted then - begin - VisualizerPaused := false; - VisualizerStarted := false; - FreeAndNil(pm); - end; -end; - -procedure TVideoPlayback_ProjectM.VisualizerTogglePause; -begin - VisualizerPaused := not VisualizerPaused; -end; - -procedure TVideoPlayback_ProjectM.GetFrame(Time: Extended); +procedure TVideo_ProjectM.GetFrame(Time: Extended); var nSamples: cardinal; begin - if not VisualizerStarted then - Exit; - - if VisualizerPaused then + if (fState <> pmPlay) then Exit; // get audio data - nSamples := AudioPlayback.GetPCMData(PcmData); + nSamples := AudioPlayback.GetPCMData(fPCMData); // generate some data if non is available if (nSamples = 0) then - nSamples := GetRandomPCMData(PcmData); + nSamples := GetRandomPCMData(fPCMData); // send audio-data to projectM if (nSamples > 0) then - pm.AddPCM16Data(PSmallInt(@PcmData), nSamples); + fPm.AddPCM16Data(PSmallInt(@fPCMData), nSamples); // store OpenGL state (might be messed up otherwise) SaveOpenGLState(); try // setup projectM's OpenGL state - pm.ResetGL(ScreenW, ScreenH); + fPm.ResetGL(ScreenW, ScreenH); // let projectM render a frame - pm.RenderFrame(); + fPm.RenderFrame(); {$IFDEF UseTexture} - glBindTexture(GL_TEXTURE_2D, VisualTex); + glBindTexture(GL_TEXTURE_2D, fVisualTex); glFlush(); - glCopyTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 0, 0, VisualWidth, VisualHeight, 0); + glCopyTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 0, 0, fVisualWidth, fVisualHeight, 0); {$ENDIF} finally // restore USDX OpenGL state @@ -467,7 +485,7 @@ end; * Draws the current frame to screen. * TODO: this is not used yet. Data is directly drawn on GetFrame(). *} -procedure TVideoPlayback_ProjectM.DrawGL(Screen: integer); +procedure TVideo_ProjectM.DrawGL(Screen: integer); begin {$IFDEF UseTexture} // have a nice black background to draw on @@ -478,7 +496,7 @@ begin end; // exit if there's nothing to draw - if not VisualizerStarted then + if (fState <> pmPlay) then Exit; // setup display @@ -497,7 +515,7 @@ begin glEnable(GL_BLEND); glEnable(GL_TEXTURE_2D); glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); - glBindTexture(GL_TEXTURE_2D, VisualTex); + glBindTexture(GL_TEXTURE_2D, fVisualTex); glColor4f(1, 1, 1, 1); // draw projectM frame @@ -524,12 +542,12 @@ end; * Produces random "sound"-data in case no audio-data is available. * Otherwise the visualization will look rather boring. *} -function TVideoPlayback_ProjectM.GetRandomPCMData(var Data: TPCMData): Cardinal; +function TVideo_ProjectM.GetRandomPCMData(var Data: TPCMData): Cardinal; var i: integer; begin // Produce some fake PCM data - if (RndPCMcount mod 500 = 0) then + if (fRndPCMcount mod 500 = 0) then begin FillChar(Data, SizeOf(TPCMData), 0); end @@ -541,7 +559,7 @@ begin Data[i][1] := Random(High(Word)+1); end; end; - Inc(RndPCMcount); + Inc(fRndPCMcount); Result := 512; end; diff --git a/src/menu/UMenuBackgroundVideo.pas b/src/menu/UMenuBackgroundVideo.pas index 9d265764..006c45e0 100644 --- a/src/menu/UMenuBackgroundVideo.pas +++ b/src/menu/UMenuBackgroundVideo.pas @@ -36,6 +36,7 @@ interface uses UThemes, UMenuBackground, + UMusic, UVideo, UPath; @@ -84,6 +85,7 @@ type } TMenuBackgroundVideo = class (TMenuBackground) private fFilename: IPath; + fBgVideo: IVideo; public constructor Create(const ThemedSettings: TThemeBackground); override; procedure OnShow; override; @@ -102,7 +104,6 @@ implementation uses gl, glext, - UMusic, SysUtils, UTime, USkins, @@ -116,42 +117,48 @@ begin raise EMenuBackgroundError.Create('TMenuBackgroundVideo: No video filename present'); fFileName := Skin.GetTextureFileName(ThemedSettings.Tex); - if fFilename.IsFile and VideoPlayback.Open(fFileName) then - begin - VideoBGTimer.SetTime(0); - VideoPlayback.Play; - end - else + if (not fFilename.IsFile) then raise EMenuBackgroundError.Create('TMenuBackgroundVideo: Can''t load background video: ' + fFilename.ToNative); end; destructor TMenuBackgroundVideo.Destroy; begin - end; -procedure TMenuBackgroundVideo.OnShow; +procedure TMenuBackgroundVideo.OnShow; begin - if VideoPlayback.Open( fFileName ) then + fBgVideo := VideoPlayback.Open(fFileName); + if (fBgVideo <> nil) then begin VideoBGTimer.SetTime(0); - VideoPlayback.Play; + fBgVideo.Loop := true; + fBgVideo.Play; end; end; procedure TMenuBackgroundVideo.OnFinish; begin - + // unload video + fBgVideo := nil; end; -procedure TMenuBackgroundVideo.Draw; +procedure TMenuBackgroundVideo.Draw; begin - If (ScreenAct = 1) then //Clear just once when in dual screen mode + // clear just once when in dual screen mode + if (ScreenAct = 1) then + begin glClear(GL_DEPTH_BUFFER_BIT); + // video failure -> draw blank background + if (fBgVideo = nil) then + glClear(GL_COLOR_BUFFER_BIT); + end; - VideoPlayback.GetFrame(VideoBGTimer.GetTime()); + if (fBgVideo <> nil) then + begin + fBgVideo.GetFrame(VideoBGTimer.GetTime()); // FIXME: why do we draw on screen 2? Seems to be wrong. - VideoPlayback.DrawGL(2); + fBgVideo.DrawGL(2); + end; end; // Implementation of TBGVideo diff --git a/src/screens/UScreenSing.pas b/src/screens/UScreenSing.pas index 74c09b4f..5f39ec49 100644 --- a/src/screens/UScreenSing.pas +++ b/src/screens/UScreenSing.pas @@ -105,7 +105,8 @@ type SungToEnd: boolean; fShowVisualization: boolean; - fCurrentVideoPlaybackEngine: IVideoPlayback; + fCurrentVideo: IVideo; + fVideoClip: IVideo; // some settings to be set by plugins settings: record @@ -179,13 +180,14 @@ begin fShowVisualization := not fShowVisualization; if fShowVisualization then - fCurrentVideoPlaybackEngine := Visualization + begin + fCurrentVideo := Visualization.Open(PATH_NONE); + fCurrentVideo.play; + end else - fCurrentVideoPlaybackEngine := VideoPlayback; - - if fShowVisualization then - fCurrentVideoPlaybackEngine.play; - + begin + fCurrentVideo := fVideoClip; + end; Exit; end; Ord('P'): @@ -216,7 +218,7 @@ begin SDLK_TAB: // change visualization preset begin if fShowVisualization then - fCurrentVideoPlaybackEngine.Position := now; // move to a random position + fCurrentVideo.Position := now; // move to a random position end; SDLK_RETURN: @@ -254,7 +256,7 @@ begin // pause video VideoFile := CurrentSong.Path.Append(CurrentSong.Video); if (CurrentSong.Video.IsSet) and VideoFile.Exists then - fCurrentVideoPlaybackEngine.Pause; + fCurrentVideo.Pause; end else // disable pause @@ -267,7 +269,7 @@ begin // video VideoFile := CurrentSong.Path.Append(CurrentSong.Video); if (CurrentSong.Video.IsSet) and VideoFile.Exists then - fCurrentVideoPlaybackEngine.Pause; + fCurrentVideo.Pause; Paused := false; end; @@ -283,7 +285,7 @@ begin fShowVisualization := false; - fCurrentVideoPlaybackEngine := VideoPlayback; + fCurrentVideo := nil; // create score class Scores := TSingScores.Create; @@ -358,8 +360,8 @@ begin ClearSettings; Party.CallBeforeSing; - // reset video playback engine, to play Video Clip... - fCurrentVideoPlaybackEngine := VideoPlayback; + // reset video playback engine + fCurrentVideo := nil; // setup score manager Scores.ClearPlayers; // clear old player values @@ -480,10 +482,6 @@ begin Exit; end; - // reset video playback engine, to play video clip ... - fCurrentVideoPlaybackEngine.Close; - fCurrentVideoPlaybackEngine := VideoPlayback; - {* * == Background == * We have four types of backgrounds: @@ -503,12 +501,13 @@ begin VideoFile := CurrentSong.Path.Append(CurrentSong.Video); if (CurrentSong.Video.IsSet) and VideoFile.IsFile then begin - if (fCurrentVideoPlaybackEngine.Open(VideoFile)) then + fVideoClip := VideoPlayback.Open(VideoFile); + fCurrentVideo := fVideoClip; + if (fVideoClip <> nil) then begin fShowVisualization := false; - fCurrentVideoPlaybackEngine := VideoPlayback; - fCurrentVideoPlaybackEngine.Position := CurrentSong.VideoGAP + CurrentSong.Start; - fCurrentVideoPlaybackEngine.Play; + fCurrentVideo.Position := CurrentSong.VideoGAP + CurrentSong.Start; + fCurrentVideo.Play; VideoLoaded := true; end; end; @@ -538,9 +537,9 @@ begin if (TVisualizerOption(Ini.VisualizerOption) in [voOn]) then begin fShowVisualization := true; - fCurrentVideoPlaybackEngine := Visualization; - if (fCurrentVideoPlaybackEngine <> nil) then - fCurrentVideoPlaybackEngine.Play; + fCurrentVideo := Visualization.Open(PATH_NONE); + if (fCurrentVideo <> nil) then + fCurrentVideo.Play; end; {* @@ -550,9 +549,9 @@ begin (VideoLoaded = false)) then begin fShowVisualization := true; - fCurrentVideoPlaybackEngine := Visualization; - if (fCurrentVideoPlaybackEngine <> nil) then - fCurrentVideoPlaybackEngine.Play; + fCurrentVideo := Visualization.Open(PATH_NONE); + if (fCurrentVideo <> nil) then + fCurrentVideo.Play; end; // prepare lyrics timer @@ -815,14 +814,14 @@ begin // update and draw movie if (ShowFinish and (VideoLoaded or fShowVisualization)) then begin - if assigned(fCurrentVideoPlaybackEngine) then + if assigned(fCurrentVideo) then begin // Just call this once // when Screens = 2 if (ScreenAct = 1) then - fCurrentVideoPlaybackEngine.GetFrame(CurrentSong.VideoGAP + LyricsState.GetCurrentTime()); + fCurrentVideo.GetFrame(CurrentSong.VideoGAP + LyricsState.GetCurrentTime()); - fCurrentVideoPlaybackEngine.DrawGL(ScreenAct); + fCurrentVideo.DrawGL(ScreenAct); end; end; @@ -902,15 +901,13 @@ begin AudioPlayback.Stop; AudioPlayback.SetSyncSource(nil); - if (VideoPlayback <> nil) then - VideoPlayback.Close; - - if (Visualization <> nil) then - Visualization.Close; - // to prevent drawing closed video VideoLoaded := false; + // close video files + fVideoClip := nil; + fCurrentVideo := nil; + // kill all stars and effects GoldenRec.KillAll; -- cgit v1.2.3