aboutsummaryrefslogtreecommitdiffstats
path: root/Game/Code/Classes/UVisualizer.pas
diff options
context:
space:
mode:
authortobigun <tobigun@b956fd51-792f-4845-bead-9b4dfca2ff2c>2007-12-17 16:40:13 +0000
committertobigun <tobigun@b956fd51-792f-4845-bead-9b4dfca2ff2c>2007-12-17 16:40:13 +0000
commit65cd744909a51e2c9adcfbd3bbbeee096b35b111 (patch)
tree0ccdadc411563a0ecc410bb15ac1c18597ee1c48 /Game/Code/Classes/UVisualizer.pas
parent69182994753c4dd7f16ba7f6ac2e406a2d71d3a6 (diff)
downloadusdx-65cd744909a51e2c9adcfbd3bbbeee096b35b111.tar.gz
usdx-65cd744909a51e2c9adcfbd3bbbeee096b35b111.tar.xz
usdx-65cd744909a51e2c9adcfbd3bbbeee096b35b111.zip
- faster preset switching
- added projectM 1.0 support git-svn-id: svn://svn.code.sf.net/p/ultrastardx/svn/trunk@715 b956fd51-792f-4845-bead-9b4dfca2ff2c
Diffstat (limited to '')
-rw-r--r--Game/Code/Classes/UVisualizer.pas761
1 files changed, 378 insertions, 383 deletions
diff --git a/Game/Code/Classes/UVisualizer.pas b/Game/Code/Classes/UVisualizer.pas
index 6548e2b7..5fc4bb82 100644
--- a/Game/Code/Classes/UVisualizer.pas
+++ b/Game/Code/Classes/UVisualizer.pas
@@ -1,383 +1,378 @@
-{############################################################################
-# Visualizer support for UltraStar deluxe #
-# #
-# Created by hennymcc #
-# Slight modifications by Jay Binks #
-# based on UVideo.pas #
-#############################################################################}
-
-unit UVisualizer;
-
-interface
-
-{$IFDEF FPC}
- {$MODE DELPHI}
-{$ENDIF}
-
-uses SDL,
- UGraphicClasses,
- textgl,
- math,
- OpenGL12,
- SysUtils,
- UIni,
- {$ifdef DebugDisplay}
- dialogs,
- {$ENDIF}
- projectM,
- UMusic;
-
-implementation
-
-uses
- UGraphic;
-
-var
- singleton_VideoProjectM : IVideoPlayback;
-
-const
- gx = 32;
- gy = 24;
- fps = 30;
- texsize = 512;
- visuals_Dir = 'Visuals'; // TODO: move this to a place common for all visualizers
- projectM_Dir = visuals_Dir+'/projectM';
-
-type
- TVideoPlayback_ProjectM = class( TInterfacedObject, IVideoPlayback, IVideoVisualization )
-
- pm : PProjectM;
-
- VisualizerStarted ,
- VisualizerPaused : Boolean;
-
- VisualTex : glUint;
- PCMData : TPCMData;
- hRC : Integer;
- hDC : Integer;
-
- RndPCMcount : integer;
-
- procedure VisualizerStart;
- procedure VisualizerStop;
-
- procedure VisualizerTogglePause;
-
- function GetRandomPCMData(var data: TPCMData): Cardinal;
- public
- constructor create();
- procedure init();
- function GetName: String;
-
- function Open( aFileName : string): boolean; // true if succeed
- procedure Close;
-
- procedure Play;
- procedure Pause;
- procedure Stop;
-
- procedure MoveTo(Time: real);
- function getPosition: real;
-
- procedure GetFrame(Time: Extended);
- procedure DrawGL(Screen: integer);
- end;
-
-
-constructor TVideoPlayback_ProjectM.create();
-begin
- RndPCMcount := 0;
-end;
-
-
-procedure TVideoPlayback_ProjectM.init();
-begin
- VisualizerStarted := False;
- VisualizerPaused := False;
-
- glGenTextures(1, PglUint(@VisualTex));
- glBindTexture(GL_TEXTURE_2D, VisualTex);
-
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
- glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
-end;
-
-function TVideoPlayback_ProjectM.GetName: String;
-begin
- result := 'ProjectM';
-end;
-
-
-function TVideoPlayback_ProjectM.Open( aFileName : string): boolean; // true if succeed
-begin
- VisualizerStart();
- result := true;
-end;
-
-procedure TVideoPlayback_ProjectM.Close;
-begin
-end;
-
-procedure TVideoPlayback_ProjectM.Play;
-begin
- VisualizerStart();
-end;
-
-procedure TVideoPlayback_ProjectM.Pause;
-begin
- VisualizerTogglePause();
-end;
-
-procedure TVideoPlayback_ProjectM.Stop;
-begin
- VisualizerStop();
-end;
-
-procedure TVideoPlayback_ProjectM.MoveTo(Time: real);
-begin
- // this code MAY be able to be cut down... but Im not 100% sure..
- // in here, we cant realy move to a specific time, since its all random generated
- // but a call to this function will change the preset, which changes the pattern
- projectM_reset(pm);
-
- pm^.fullscreen := 0;
- pm^.renderTarget^.texsize := texsize;
- pm^.gx := gx;
- pm^.gy := gy;
- pm^.fps := fps;
- pm^.renderTarget^.usePbuffers := 0;
-
- pm^.fontURL := PChar(projectM_Dir+'/fonts');
- pm^.presetURL := PChar(projectM_Dir+'/presets');
-
- glPushAttrib(GL_ALL_ATTRIB_BITS);
- projectM_init(pm);
- glMatrixMode(GL_PROJECTION);
- glPushMatrix();
- glMatrixMode(GL_MODELVIEW);
- glPushMatrix();
- glMatrixMode(GL_TEXTURE);
- glPushMatrix();
-
- projectM_resetGL(pm, ScreenW, ScreenH);
- glMatrixMode(GL_PROJECTION);
- glPopMatrix();
- glMatrixMode(GL_MODELVIEW);
- glPopMatrix();
- glMatrixMode(GL_TEXTURE);
- glPopMatrix();
- glPopAttrib();
-
-end;
-
-function TVideoPlayback_ProjectM.getPosition: real;
-begin
- result := 0;
-end;
-
-procedure TVideoPlayback_ProjectM.VisualizerStart;
-begin
- VisualizerStarted := True;
-
- New(pm);
- projectM_reset(pm);
-
- pm^.fullscreen := 0;
- pm^.renderTarget^.texsize := texsize;
- pm^.gx := gx;
- pm^.gy := gy;
- pm^.fps := fps;
- pm^.renderTarget^.usePbuffers := 0;
-
- pm^.fontURL := PChar(projectM_Dir+'/fonts');
- pm^.presetURL := PChar(projectM_Dir+'/presets');
-
- glPushAttrib(GL_ALL_ATTRIB_BITS);
- projectM_init(pm);
- glMatrixMode(GL_PROJECTION);
- glPushMatrix();
- glMatrixMode(GL_MODELVIEW);
- glPushMatrix();
- glMatrixMode(GL_TEXTURE);
- glPushMatrix();
-
- projectM_resetGL(pm, ScreenW, ScreenH);
-
- glMatrixMode(GL_PROJECTION);
- glPopMatrix();
- glMatrixMode(GL_MODELVIEW);
- glPopMatrix();
- glMatrixMode(GL_TEXTURE);
- glPopMatrix();
- glPopAttrib();
-end;
-
-procedure TVideoPlayback_ProjectM.VisualizerStop;
-begin
- if VisualizerStarted then begin
- VisualizerStarted := False;
- Dispose(pm);
- end;
-end;
-
-procedure TVideoPlayback_ProjectM.VisualizerTogglePause;
-begin
- VisualizerPaused := not VisualizerPaused;
-end;
-
-procedure TVideoPlayback_ProjectM.GetFrame(Time: Extended);
-var
- i: integer;
- nSamples: cardinal;
-begin
- if not VisualizerStarted then Exit;
- if VisualizerPaused then Exit;
-
- // get audio data
- nSamples := AudioPlayback.GetPCMData(PcmData);
-
- if nSamples = 0 then
- nSamples := GetRandomPCMData(PcmData);
-
- addPCM16Data(PPCM16Data(@PcmData), nSamples);
-
- // store OpenGL state (might be messed up otherwise)
- glPushAttrib(GL_ALL_ATTRIB_BITS);
- glMatrixMode(GL_PROJECTION);
- glPushMatrix();
- glMatrixMode(GL_MODELVIEW);
- glPushMatrix();
- glMatrixMode(GL_TEXTURE);
- glPushMatrix();
-
- // let projectM render a frame
- try
- renderFrame(pm);
- except
- // This happens with some presets... ( and only some times also .. moreso on linux )
- // if we have an "Invalid Floating Point Error" while rendering a frame... then change preset.
- MoveTo( now );
-
- // hmm have to be careful, that we dont get to many here... coz it could keep failing.. and trying again.
- end;
- glFlush();
-
- {$IFDEF UseTexture}
- glBindTexture(GL_TEXTURE_2D, VisualTex);
- glCopyTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 0, 0, VisualWidth, VisualHeight, 0);
- {$ENDIF}
-
- // restore USDX OpenGL state
- glMatrixMode(GL_PROJECTION);
- glPopMatrix();
- glMatrixMode(GL_MODELVIEW);
- glPopMatrix();
- glMatrixMode(GL_TEXTURE);
- glPopMatrix();
- glPopAttrib();
-
- // discard projectM's depth buffer information (avoid overlay)
- glClear(GL_DEPTH_BUFFER_BIT);
-end;
-
-procedure TVideoPlayback_ProjectM.DrawGL(Screen: integer);
-begin
- {$IFDEF UseTexture}
- // have a nice black background to draw on (even if there were errors opening the vid)
- if Screen=1 then begin
- glClearColor(0, 0, 0, 0);
- glClear(GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT);
- end;
- // exit if there's nothing to draw
- if not VisualizerStarted then Exit;
-
- // setup display
- glMatrixMode(GL_PROJECTION);
- glPushMatrix();
- glLoadIdentity();
- gluOrtho2D(0, 1, 0, 1);
- glMatrixMode(GL_MODELVIEW);
- glPushMatrix();
- glLoadIdentity();
-
- glEnable(GL_BLEND);
- glEnable(GL_TEXTURE_2D);
- glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
- glBindTexture(GL_TEXTURE_2D, VisualTex);
- glColor4f(1, 1, 1, 1);
-
- // draw projectM frame
- glBegin(GL_QUADS);
- glTexCoord2f(0, 0); glVertex2f(0, 0);
- glTexCoord2f(1, 0); glVertex2f(1, 0);
- glTexCoord2f(1, 1); glVertex2f(1, 1);
- glTexCoord2f(0, 1); glVertex2f(0, 1);
- glEnd();
-
- {
- glbegin(GL_QUADS);
- glTexCoord2f(0, 0);
- glVertex2f(400-VisualWidth/2, 300-VisualHeight/2);
- glTexCoord2f(0, 1);
- glVertex2f(400-VisualWidth/2, 300+VisualHeight/2);
- glTexCoord2f(1, 1);
- glVertex2f(400+VisualWidth/2, 300+VisualHeight/2);
- glTexCoord2f(1, 0);
- glVertex2f(400+VisualWidth/2, 300-VisualHeight/2);
- glEnd;
- }
-
- glDisable(GL_TEXTURE_2D);
- glDisable(GL_BLEND);
-
- // restore state
- glMatrixMode(GL_PROJECTION);
- glPopMatrix();
- glMatrixMode(GL_MODELVIEW);
- glPopMatrix();
- {$ENDIF}
-end;
-
-function TVideoPlayback_ProjectM.GetRandomPCMData(var data: TPCMData): Cardinal;
-var
- i: integer;
-begin
- // Produce some fake PCM data
- if ( RndPCMcount mod 500 = 0 ) then
- begin
- for i := 0 to 511 do begin
- data[0][i] := 0;
- data[1][i] := 0;
- end;
- end
- else begin
- for i := 0 to 511 do begin
- if ( i mod 2 = 0 ) then begin
- data[0][i] := floor(Random * power(2.,14));
- data[1][i] := floor(Random * power(2.,14));
- end
- else begin;
- data[0][i] := floor(Random * power(2.,14));
- data[1][i] := floor(Random * power(2.,14));
- end;
- if ( i mod 2 = 1 ) then begin
- data[0][i] := -data[0][i];
- data[1][i] := -data[1][i];
- end;
- end;
- end;
- Inc( RndPCMcount );
- result := 512;
-end;
-
-
-initialization
- singleton_VideoProjectM := TVideoPlayback_ProjectM.create();
- AudioManager.add( singleton_VideoProjectM );
-
-finalization
- AudioManager.Remove( singleton_VideoProjectM );
-
-
-
-end.
+{############################################################################
+# Visualizer support for UltraStar deluxe #
+# #
+# Created by hennymcc #
+# Slight modifications by Jay Binks #
+# based on UVideo.pas #
+#############################################################################}
+
+unit UVisualizer;
+
+interface
+
+{$IFDEF FPC}
+ {$MODE DELPHI}
+{$ENDIF}
+
+{$I switches.inc}
+
+uses
+ SDL,
+ UGraphicClasses,
+ textgl,
+ math,
+ OpenGL12,
+ SysUtils,
+ UIni,
+ {$ifdef DebugDisplay}
+ {$ifdef win32}
+ dialogs,
+ {$endif}
+ {$endif}
+ projectM,
+ UMusic;
+
+implementation
+
+uses
+ UGraphic,
+ ULog;
+
+var
+ singleton_VideoProjectM : IVideoPlayback;
+
+const
+ gx = 32;
+ gy = 24;
+ fps = 30;
+ texsize = 512;
+ visualsDir = 'Visuals'; // TODO: move this to a place common for all visualizers
+ projectMDir = visualsDir+'/projectM';
+ presetsDir = projectMDir+'/presets';
+ fontsDir = projectMDir+'/fonts';
+
+type
+ TVideoPlayback_ProjectM = class( TInterfacedObject, IVideoPlayback, IVideoVisualization )
+ private
+ pm : TProjectM;
+
+ VisualizerStarted ,
+ VisualizerPaused : Boolean;
+
+ VisualTex : glUint;
+ PCMData : TPCMData;
+ hRC : Integer;
+ hDC : Integer;
+
+ RndPCMcount : integer;
+
+ projMatrix: array[0..3, 0..3] of GLdouble;
+ texMatrix: array[0..3, 0..3] of GLdouble;
+
+ procedure VisualizerStart;
+ procedure VisualizerStop;
+
+ procedure VisualizerTogglePause;
+
+ function GetRandomPCMData(var data: TPCMData): Cardinal;
+
+ procedure SaveOpenGLState();
+ procedure RestoreOpenGLState();
+
+ public
+ constructor create();
+ procedure init();
+ function GetName: String;
+
+ function Open( aFileName : string): boolean; // true if succeed
+ procedure Close;
+
+ procedure Play;
+ procedure Pause;
+ procedure Stop;
+
+ procedure MoveTo(Time: real);
+ function getPosition: real;
+
+ procedure GetFrame(Time: Extended);
+ procedure DrawGL(Screen: integer);
+ end;
+
+
+constructor TVideoPlayback_ProjectM.create();
+begin
+ RndPCMcount := 0;
+end;
+
+
+procedure TVideoPlayback_ProjectM.init();
+begin
+ VisualizerStarted := False;
+ VisualizerPaused := False;
+
+ {$IFDEF UseTexture}
+ glGenTextures(1, PglUint(@VisualTex));
+ glBindTexture(GL_TEXTURE_2D, VisualTex);
+
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+ {$ENDIF}
+end;
+
+function TVideoPlayback_ProjectM.GetName: String;
+begin
+ result := 'ProjectM';
+end;
+
+
+function TVideoPlayback_ProjectM.Open( aFileName : string): boolean; // true if succeed
+begin
+ VisualizerStart();
+ result := true;
+end;
+
+procedure TVideoPlayback_ProjectM.Close;
+begin
+end;
+
+procedure TVideoPlayback_ProjectM.Play;
+begin
+ VisualizerStart();
+end;
+
+procedure TVideoPlayback_ProjectM.Pause;
+begin
+ VisualizerTogglePause();
+end;
+
+procedure TVideoPlayback_ProjectM.Stop;
+begin
+ VisualizerStop();
+end;
+
+procedure TVideoPlayback_ProjectM.MoveTo(Time: real);
+begin
+ pm.RandomPreset();
+end;
+
+function TVideoPlayback_ProjectM.getPosition: real;
+begin
+ result := 0;
+end;
+
+procedure TVideoPlayback_ProjectM.SaveOpenGLState();
+begin
+ // save all OpenGL state-machine attributes
+ glPushAttrib(GL_ALL_ATTRIB_BITS);
+
+ // save projection-matrix
+ glMatrixMode(GL_PROJECTION);
+ // - WARNING: projection-matrix stack-depth is only 2!
+ // -> overflow might occur if glPopMatrix() is used for this matrix
+ // -> use glGet() instead of glPushMatrix()
+ glPushMatrix();
+ //glGetDoublev(GL_PROJECTION_MATRIX, @projMatrix);
+
+ // save texture-matrix
+ glMatrixMode(GL_TEXTURE);
+ // - WARNING: texture-matrix stack-depth is only 2!
+ // -> overflow might occur if glPopMatrix() is used for this matrix
+ // -> use glGet() instead of glPushMatrix() if problems appear
+ glPushMatrix();
+ //glGetDoublev(GL_TEXTURE_MATRIX, @texMatrix);
+
+ // save modelview-matrix
+ glMatrixMode(GL_MODELVIEW);
+ glPushMatrix();
+end;
+
+procedure TVideoPlayback_ProjectM.RestoreOpenGLState();
+begin
+ // restore projection-matrix
+ glMatrixMode(GL_PROJECTION);
+ // - WARNING: projection-matrix stack-depth is only 2!
+ // -> overflow _occurs_ if glPopMatrix() is used for this matrix
+ // -> use glLoadMatrix() instead of glPopMatrix()
+ glPopMatrix();
+ //glLoadMatrixd(@projMatrix);
+
+ // restore texture-matrix
+ // -> overflow might occur if glPopMatrix() is used for this matrix
+ glMatrixMode(GL_TEXTURE);
+ glPopMatrix();
+ //glLoadMatrixd(@texMatrix);
+
+ // restore modelview-matrix
+ glMatrixMode(GL_MODELVIEW);
+ glPopMatrix();
+
+ // restore all OpenGL state-machine attributes
+ glPopAttrib();
+end;
+
+procedure TVideoPlayback_ProjectM.VisualizerStart;
+var
+ initResult: Cardinal;
+begin
+ VisualizerStarted := True;
+
+ pm := TProjectM.Create(gx, gy, fps, texsize, ScreenW, ScreenH,
+ presetsDir, fontsDir);
+ //initResult := projectM_initRenderToTexture(pm);
+
+ SaveOpenGLState();
+ pm.ResetGL(ScreenW, ScreenH);
+ RestoreOpenGLState();
+end;
+
+procedure TVideoPlayback_ProjectM.VisualizerStop;
+begin
+ if VisualizerStarted then begin
+ VisualizerStarted := False;
+ pm.Free();
+ end;
+end;
+
+procedure TVideoPlayback_ProjectM.VisualizerTogglePause;
+begin
+ VisualizerPaused := not VisualizerPaused;
+end;
+
+procedure TVideoPlayback_ProjectM.GetFrame(Time: Extended);
+var
+ i: integer;
+ nSamples: cardinal;
+ stackDepth: Integer;
+begin
+ if not VisualizerStarted then Exit;
+ if VisualizerPaused then Exit;
+
+ // get audio data
+ nSamples := AudioPlayback.GetPCMData(PcmData);
+
+ if nSamples = 0 then
+ nSamples := GetRandomPCMData(PcmData);
+
+ pm.AddPCM16Data(PSmallint(@PcmData), nSamples);
+
+ // store OpenGL state (might be messed up otherwise)
+ SaveOpenGLState();
+ pm.ResetGL(ScreenW, ScreenH);
+
+ //glGetIntegerv(GL_PROJECTION_STACK_DEPTH, @stackDepth);
+ //writeln('StackDepth0: ' + inttostr(stackDepth));
+
+ // let projectM render a frame
+ try
+ pm.RenderFrame();
+ except
+ // 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 );
+ end;
+
+ //glGetIntegerv(GL_PROJECTION_STACK_DEPTH, @stackDepth);
+ //writeln('StackDepth1: ' + inttostr(stackDepth));
+
+ {$IFDEF UseTexture}
+ glBindTexture(GL_TEXTURE_2D, VisualTex);
+ glFlush();
+ glCopyTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 0, 0, VisualWidth, VisualHeight, 0);
+ {$ENDIF}
+
+ // restore USDX OpenGL state
+ RestoreOpenGLState();
+
+ // discard projectM's depth buffer information (avoid overlay)
+ glClear(GL_DEPTH_BUFFER_BIT);
+end;
+
+procedure TVideoPlayback_ProjectM.DrawGL(Screen: integer);
+begin
+ {$IFDEF UseTexture}
+ // have a nice black background to draw on (even if there were errors opening the vid)
+ if Screen=1 then begin
+ glClearColor(0, 0, 0, 0);
+ glClear(GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT);
+ end;
+ // exit if there's nothing to draw
+ if not VisualizerStarted then Exit;
+
+ // setup display
+ glMatrixMode(GL_PROJECTION);
+ glPushMatrix();
+ glLoadIdentity();
+ gluOrtho2D(0, 1, 0, 1);
+ glMatrixMode(GL_MODELVIEW);
+ glPushMatrix();
+ glLoadIdentity();
+
+ glEnable(GL_BLEND);
+ glEnable(GL_TEXTURE_2D);
+ glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
+ glBindTexture(GL_TEXTURE_2D, VisualTex);
+ glColor4f(1, 1, 1, 1);
+
+ // draw projectM frame
+ glBegin(GL_QUADS);
+ glTexCoord2f(0, 0); glVertex2f(0, 0);
+ glTexCoord2f(1, 0); glVertex2f(1, 0);
+ glTexCoord2f(1, 1); glVertex2f(1, 1);
+ glTexCoord2f(0, 1); glVertex2f(0, 1);
+ glEnd();
+
+ glDisable(GL_TEXTURE_2D);
+ glDisable(GL_BLEND);
+
+ // restore state
+ glMatrixMode(GL_PROJECTION);
+ glPopMatrix();
+ glMatrixMode(GL_MODELVIEW);
+ glPopMatrix();
+ {$ENDIF}
+end;
+
+function TVideoPlayback_ProjectM.GetRandomPCMData(var data: TPCMData): Cardinal;
+var
+ i: integer;
+begin
+ // Produce some fake PCM data
+ if ( RndPCMcount mod 500 = 0 ) then
+ begin
+ for i := 0 to 511 do begin
+ data[0][i] := 0;
+ data[1][i] := 0;
+ end;
+ end
+ else begin
+ for i := 0 to 511 do begin
+ if ( i mod 2 = 0 ) then begin
+ data[0][i] := floor(Random * power(2.,14));
+ data[1][i] := floor(Random * power(2.,14));
+ end
+ else begin;
+ data[0][i] := floor(Random * power(2.,14));
+ data[1][i] := floor(Random * power(2.,14));
+ end;
+ if ( i mod 2 = 1 ) then begin
+ data[0][i] := -data[0][i];
+ data[1][i] := -data[1][i];
+ end;
+ end;
+ end;
+ Inc( RndPCMcount );
+ result := 512;
+end;
+
+
+initialization
+ singleton_VideoProjectM := TVideoPlayback_ProjectM.create();
+ AudioManager.add( singleton_VideoProjectM );
+
+finalization
+ AudioManager.Remove( singleton_VideoProjectM );
+
+
+
+end.