diff options
Diffstat (limited to 'us_maker_edition/src')
39 files changed, 1980 insertions, 872 deletions
diff --git a/us_maker_edition/src/base/UConfig.pas b/us_maker_edition/src/base/UConfig.pas index ef08827b..74415f4d 100644 --- a/us_maker_edition/src/base/UConfig.pas +++ b/us_maker_edition/src/base/UConfig.pas @@ -130,7 +130,7 @@ const USDX_VERSION_MAJOR = 1; USDX_VERSION_MINOR = 1; USDX_VERSION_RELEASE = 0; - USDX_VERSION_STATE = 'Beta'; + USDX_VERSION_STATE = 'RC'; USDX_STRING = 'UltraStar Deluxe'; (* diff --git a/us_maker_edition/src/base/UDraw.pas b/us_maker_edition/src/base/UDraw.pas index bb9f28ca..77e0940d 100644 --- a/us_maker_edition/src/base/UDraw.pas +++ b/us_maker_edition/src/base/UDraw.pas @@ -1145,6 +1145,9 @@ begin (LyricsState.TotalTime > 0) then begin LyricsProgress := CurLyricsTime / LyricsState.TotalTime; + // avoid that the bar "overflows" for inaccurate song lengths + if (LyricsProgress > 1.0) then + LyricsProgress := 1.0; glTexCoord2f((width * LyricsProgress) / 8, 0); glVertex2f(x + width * LyricsProgress, y); diff --git a/us_maker_edition/src/base/UEditorLyrics.pas b/us_maker_edition/src/base/UEditorLyrics.pas index 0eacd1f9..5030eff5 100644 --- a/us_maker_edition/src/base/UEditorLyrics.pas +++ b/us_maker_edition/src/base/UEditorLyrics.pas @@ -195,6 +195,7 @@ begin Word[WordNum].FontStyle := FontStyleI; SetFontStyle(FontStyleI); SetFontSize(SizeR); + SetFontItalic(Italic); Word[WordNum].Width := glTextWidth(Text); Word[WordNum].Text := Text; Word[WordNum].ColR := ColR; diff --git a/us_maker_edition/src/base/UFiles.pas b/us_maker_edition/src/base/UFiles.pas index 5a258e3e..1a7ca8f8 100644 --- a/us_maker_edition/src/base/UFiles.pas +++ b/us_maker_edition/src/base/UFiles.pas @@ -131,7 +131,9 @@ begin if (Song.Encoding = encUTF8) then SongFile.WriteString(UTF8_BOM); - SongFile.WriteLine('#ENCODING:' + EncodingName(Song.Encoding)); + // do not save "auto" encoding tag + if (Song.Encoding <> encAuto) then + SongFile.WriteLine('#ENCODING:' + EncodingName(Song.Encoding)); SongFile.WriteLine('#TITLE:' + EncodeToken(Song.Title)); SongFile.WriteLine('#ARTIST:' + EncodeToken(Song.Artist)); diff --git a/us_maker_edition/src/base/UIni.pas b/us_maker_edition/src/base/UIni.pas index a4c85a3b..b198f22c 100644 --- a/us_maker_edition/src/base/UIni.pas +++ b/us_maker_edition/src/base/UIni.pas @@ -44,31 +44,34 @@ uses UPath; type - // TInputDeviceConfig stores the configuration for an input device. - // Configurations will be stored in the InputDeviceConfig array. - // Note that not all devices listed in InputDeviceConfig are active devices. - // Some might be unplugged and hence unavailable. - // Available devices are held in TAudioInputProcessor.DeviceList. Each - // TAudioInputDevice listed there has a CfgIndex field which is the index to - // its configuration in the InputDeviceConfig array. - // Name: - // the name of the input device - // Input: - // the index of the input source to use for recording - // ChannelToPlayerMap: - // mapping of recording channels to players, e.g. ChannelToPlayerMap[0] = 2 - // maps the channel 0 (left) to player 2. A player index of 0 means that - // the channel is not assigned to a player. + {** + * TInputDeviceConfig stores the configuration for an input device. + * Configurations will be stored in the InputDeviceConfig array. + * Note that not all devices listed in InputDeviceConfig are active devices. + * Some might be unplugged and hence unavailable. + * Available devices are held in TAudioInputProcessor.DeviceList. Each + * TAudioInputDevice listed there has a CfgIndex field which is the index to + * its configuration in the InputDeviceConfig array. + *} PInputDeviceConfig = ^TInputDeviceConfig; TInputDeviceConfig = record - Name: string; - Input: integer; - Latency: integer; //**< latency in ms, or LATENCY_AUTODETECT for default + Name: string; //**< Name of the input device + Input: integer; //**< Index of the input source to use for recording + Latency: integer; //**< Latency in ms, or LATENCY_AUTODETECT for default + + {** + * Mapping of recording channels to players, e.g. ChannelToPlayerMap[0] = 2 + * maps the channel 0 (left) to player 2. + * A player index of 0 (CHANNEL_OFF) means that the channel is not assigned + * to any player (the channel is off). + *} ChannelToPlayerMap: array of integer; end; +{* Constants for TInputDeviceConfig *} const - LATENCY_AUTODETECT = -1; + CHANNEL_OFF = 0; // for field ChannelToPlayerMap + LATENCY_AUTODETECT = -1; // for field Latency type @@ -87,6 +90,7 @@ type procedure LoadInputDeviceCfg(IniFile: TMemIniFile); procedure SaveInputDeviceCfg(IniFile: TIniFile); procedure LoadThemes(IniFile: TCustomIniFile); + procedure LoadPaths(IniFile: TCustomIniFile); procedure LoadScreenModes(IniFile: TCustomIniFile); @@ -121,6 +125,8 @@ type Spectrum: integer; Spectrograph: integer; MovieSize: integer; + VideoPreview: integer; + VideoEnabled: integer; // Sound MicBoost: integer; @@ -164,6 +170,9 @@ type Joypad: integer; Mouse: integer; + // default encoding for texts (lyrics, song-name, ...) + DefaultEncoding: TEncoding; + procedure Load(); procedure Save(); procedure SaveNames; @@ -214,6 +223,8 @@ const ISpectrum: array[0..1] of UTF8String = ('Off', 'On'); ISpectrograph: array[0..1] of UTF8String = ('Off', 'On'); IMovieSize: array[0..2] of UTF8String = ('Half', 'Full [Vid]', 'Full [BG+Vid]'); + IVideoPreview: array[0..1] of UTF8String = ('Off', 'On'); + IVideoEnabled: array[0..1] of UTF8String = ('Off', 'On'); IClickAssist: array[0..1] of UTF8String = ('Off', 'On'); IBeatClick: array[0..1] of UTF8String = ('Off', 'On'); @@ -295,6 +306,8 @@ var ISpectrumTranslated: array[0..1] of UTF8String = ('Off', 'On'); ISpectrographTranslated: array[0..1] of UTF8String = ('Off', 'On'); IMovieSizeTranslated: array[0..2] of UTF8String = ('Half', 'Full [Vid]', 'Full [BG+Vid]'); + IVideoPreviewTranslated: array[0..1] of UTF8String = ('Off', 'On'); + IVideoEnabledTranslated: array[0..1] of UTF8String = ('Off', 'On'); IClickAssistTranslated: array[0..1] of UTF8String = ('Off', 'On'); IBeatClickTranslated: array[0..1] of UTF8String = ('Off', 'On'); @@ -415,6 +428,12 @@ begin IMovieSizeTranslated[1] := ULanguage.Language.Translate('OPTION_VALUE_FULL_VID'); IMovieSizeTranslated[2] := ULanguage.Language.Translate('OPTION_VALUE_FULL_VID_BG'); + IVideoPreviewTranslated[0] := ULanguage.Language.Translate('OPTION_VALUE_OFF'); + IVideoPreviewTranslated[1] := ULanguage.Language.Translate('OPTION_VALUE_ON'); + + IVideoEnabledTranslated[0] := ULanguage.Language.Translate('OPTION_VALUE_OFF'); + IVideoEnabledTranslated[1] := ULanguage.Language.Translate('OPTION_VALUE_ON'); + IClickAssistTranslated[0] := ULanguage.Language.Translate('OPTION_VALUE_OFF'); IClickAssistTranslated[1] := ULanguage.Language.Translate('OPTION_VALUE_ON'); @@ -658,7 +677,7 @@ begin for ChannelIndex := 0 to High(DeviceCfg.ChannelToPlayerMap) do begin DeviceCfg.ChannelToPlayerMap[ChannelIndex] := - IniFile.ReadInteger('Record', Format('Channel%d[%d]', [ChannelIndex+1, DeviceIndex]), 0); + IniFile.ReadInteger('Record', Format('Channel%d[%d]', [ChannelIndex+1, DeviceIndex]), CHANNEL_OFF); end; end; end; @@ -930,6 +949,12 @@ begin // MovieSize MovieSize := GetArrayIndex(IMovieSize, IniFile.ReadString('Graphics', 'MovieSize', IMovieSize[2])); + // VideoPreview + VideoPreview := GetArrayIndex(IVideoPreview, IniFile.ReadString('Graphics', 'VideoPreview', IVideoPreview[1])); + + // VideoEnabled + VideoEnabled := GetArrayIndex(IVideoEnabled, IniFile.ReadString('Graphics', 'VideoEnabled', IVideoEnabled[1])); + // ClickAssist ClickAssist := GetArrayIndex(IClickAssist, IniFile.ReadString('Sound', 'ClickAssist', 'Off')); @@ -960,6 +985,9 @@ begin // NoteLines NoteLines := GetArrayIndex(INoteLines, IniFile.ReadString('Lyrics', 'NoteLines', INoteLines[1])); + // DefaultEncoding + DefaultEncoding := ParseEncoding(IniFile.ReadString('Lyrics', 'Encoding', ''), encAuto); + LoadThemes(IniFile); LoadInputDeviceCfg(IniFile); @@ -1077,6 +1105,12 @@ begin // Movie Size IniFile.WriteString('Graphics', 'MovieSize', IMovieSize[MovieSize]); + // VideoPreview + IniFile.WriteString('Graphics', 'VideoPreview', IVideoPreview[VideoPreview]); + + // VideoEnabled + IniFile.WriteString('Graphics', 'VideoEnabled', IVideoEnabled[VideoEnabled]); + // ClickAssist IniFile.WriteString('Sound', 'ClickAssist', IClickAssist[ClickAssist]); @@ -1110,6 +1144,9 @@ begin // NoteLines IniFile.WriteString('Lyrics', 'NoteLines', INoteLines[NoteLines]); + //Encoding default + IniFile.WriteString('Lyrics', 'Encoding', EncodingName(DefaultEncoding)); + // Theme IniFile.WriteString('Themes', 'Theme', ITheme[Theme]); diff --git a/us_maker_edition/src/base/UMain.pas b/us_maker_edition/src/base/UMain.pas index 0d479420..174ef162 100644 --- a/us_maker_edition/src/base/UMain.pas +++ b/us_maker_edition/src/base/UMain.pas @@ -39,7 +39,7 @@ uses procedure Main; procedure MainLoop; -function CheckEvents: boolean; +procedure CheckEvents; type TMainThreadExecProc = procedure(Data: Pointer); @@ -98,6 +98,7 @@ uses procedure Main; var WindowTitle: string; + BadPlayer: integer; begin {$IFNDEF Debug} try @@ -304,8 +305,14 @@ begin SoundLib.StartBgMusic; // check microphone settings, goto record options if they are corrupt - if (not AudioInputProcessor.ValidateSettings) then + BadPlayer := AudioInputProcessor.ValidateSettings; + if (BadPlayer <> 0) then + begin + ScreenPopupError.ShowPopup( + Format(Language.Translate('ERROR_PLAYER_DEVICE_ASSIGNMENT'), + [BadPlayer])); Display.CurrentScreen^.FadeTo( @ScreenOptionsRecord ); + end; //------------------------------ // Start Mainloop @@ -347,13 +354,14 @@ var Delay: integer; TicksCurrent: cardinal; TicksBeforeFrame: cardinal; - Continue: boolean; + Done: boolean; begin SDL_EnableKeyRepeat(125, 125); + Done := false; + CountSkipTime(); // JB - for some reason this seems to be needed when we use the SDL Timer functions. - while Continue do - begin + repeat TicksBeforeFrame := SDL_GetTicks; // joypad @@ -361,10 +369,10 @@ begin Joy.Update; // keyboard events - Continue := CheckEvents; + CheckEvents; // display - Continue := Display.Draw; + Done := not Display.Draw; SwapBuffers; // FPS limiter @@ -376,7 +384,7 @@ begin CountSkipTime; - end; + until Done; end; procedure DoQuit; @@ -394,13 +402,14 @@ begin end; end; -function CheckEvents: boolean; +procedure CheckEvents; var Event: TSDL_event; mouseDown: boolean; mouseBtn: integer; + KeepGoing: boolean; begin - Result := true; + KeepGoing := true; while (SDL_PollEvent(@Event) <> 0) do begin case Event.type_ of @@ -445,17 +454,17 @@ begin if not Assigned(Display.NextScreen) then begin //drop input when changing screens if (ScreenPopupError <> nil) and (ScreenPopupError.Visible) then - Result := ScreenPopupError.ParseMouse(mouseBtn, mouseDown, Event.button.x, Event.button.y) + KeepGoing := ScreenPopupError.ParseMouse(mouseBtn, mouseDown, Event.button.x, Event.button.y) else if (ScreenPopupInfo <> nil) and (ScreenPopupInfo.Visible) then - Result := ScreenPopupInfo.ParseMouse(mouseBtn, mouseDown, Event.button.x, Event.button.y) + KeepGoing := ScreenPopupInfo.ParseMouse(mouseBtn, mouseDown, Event.button.x, Event.button.y) else if (ScreenPopupCheck <> nil) and (ScreenPopupCheck.Visible) then - Result := ScreenPopupCheck.ParseMouse(mouseBtn, mouseDown, Event.button.x, Event.button.y) + KeepGoing := ScreenPopupCheck.ParseMouse(mouseBtn, mouseDown, Event.button.x, Event.button.y) else begin - Result := Display.CurrentScreen^.ParseMouse(mouseBtn, mouseDown, Event.button.x, Event.button.y); + KeepGoing := Display.CurrentScreen^.ParseMouse(mouseBtn, mouseDown, Event.button.x, Event.button.y); // if screen wants to exit - if not Result then + if not KeepGoing then DoQuit; end; end; @@ -535,18 +544,18 @@ begin // if there is a visible popup then let it handle input instead of underlying screen // shoud be done in a way to be sure the topmost popup has preference (maybe error, then check) else if (ScreenPopupError <> nil) and (ScreenPopupError.Visible) then - Result := ScreenPopupError.ParseInput(Event.key.keysym.sym, Event.key.keysym.unicode, true) + KeepGoing := ScreenPopupError.ParseInput(Event.key.keysym.sym, Event.key.keysym.unicode, true) else if (ScreenPopupInfo <> nil) and (ScreenPopupInfo.Visible) then - Result := ScreenPopupInfo.ParseInput(Event.key.keysym.sym, Event.key.keysym.unicode, true) + KeepGoing := ScreenPopupInfo.ParseInput(Event.key.keysym.sym, Event.key.keysym.unicode, true) else if (ScreenPopupCheck <> nil) and (ScreenPopupCheck.Visible) then - Result := ScreenPopupCheck.ParseInput(Event.key.keysym.sym, Event.key.keysym.unicode, true) + KeepGoing := ScreenPopupCheck.ParseInput(Event.key.keysym.sym, Event.key.keysym.unicode, true) else begin // check if screen wants to exit - Result := Display.ParseInput(Event.key.keysym.sym, Event.key.keysym.unicode, true); + KeepGoing := Display.ParseInput(Event.key.keysym.sym, Event.key.keysym.unicode, true); // if screen wants to exit - if not Result then + if not KeepGoing then DoQuit; end; diff --git a/us_maker_edition/src/base/UMusic.pas b/us_maker_edition/src/base/UMusic.pas index 7f2b3e30..41d6e80c 100644 --- a/us_maker_edition/src/base/UMusic.pas +++ b/us_maker_edition/src/base/UMusic.pas @@ -43,6 +43,27 @@ uses type TNoteType = (ntFreestyle, ntNormal, ntGolden); + {** + * acoStretch: Stretch to screen width and height + * - ignores aspect + * + no borders + * + no image data loss + * acoCrop: Stretch to screen width or height, crop the other dimension + * + keeps aspect + * + no borders + * - frame borders are cropped (image data loss) + * acoLetterBox: Stretch to screen width, add bars at or crop top and bottom + * + keeps aspect + * - borders at top and bottom + * o top/bottom is cropped if width < height (unusual) + *} + TAspectCorrection = (acoStretch, acoCrop, acoLetterBox); + + TRectCoords = record + Left, Right: double; + Upper, Lower: double; + end; + const // ScoreFactor defines how a notehit of a specified notetype is // measured in comparison to the other types @@ -334,9 +355,49 @@ type procedure SetPosition(Time: real); function GetPosition: real; - procedure GetFrame(Time: Extended); - procedure DrawGL(Screen: integer); + procedure SetScreen(Screen: integer); + function GetScreen(): integer; + + procedure SetScreenPosition(X, Y: double; Z: double = 0.0); + procedure GetScreenPosition(var X, Y, Z: double); + + procedure SetWidth(Width: double); + function GetWidth(): double; + + procedure SetHeight(Height: double); + function GetHeight(): double; + + {** + * Sub-image of the video frame to draw. + * This can be used for zooming or similar purposes. + *} + procedure SetFrameRange(Range: TRectCoords); + function GetFrameRange(): TRectCoords; + + function GetFrameAspect(): real; + + procedure SetAspectCorrection(AspectCorrection: TAspectCorrection); + function GetAspectCorrection(): TAspectCorrection; + + + procedure SetAlpha(Alpha: double); + function GetAlpha(): double; + + procedure SetReflectionSpacing(Spacing: double); + function GetReflectionSpacing(): double; + procedure GetFrame(Time: Extended); + procedure Draw(); + procedure DrawReflection(); + + + property Screen: integer read GetScreen; + property Width: double read GetWidth write SetWidth; + property Height: double read GetHeight write SetHeight; + property Alpha: double read GetAlpha write SetAlpha; + property ReflectionSpacing: double read GetReflectionSpacing write SetReflectionSpacing; + property FrameAspect: real read GetFrameAspect; + property AspectCorrection: TAspectCorrection read GetAspectCorrection write SetAspectCorrection; property Loop: boolean read GetLoop write SetLoop; property Position: real read GetPosition write SetPosition; end; @@ -414,7 +475,15 @@ type (* IVideoDecoder = Interface( IGenericDecoder ) ['{2F184B2B-FE69-44D5-9031-0A2462391DCA}'] - function Open(const Filename: IPath): TVideoDecodeStream; + function Open(const Filename: IPath): TVideoDecodeStream; + + procedure SetPosition(Time: real); + function GetPosition: real; + + procedure UpdateTexture(Texture: glUint); + + property Loop: boolean read GetLoop write SetLoop; + property Position: real read GetPosition write SetPosition; end; *) diff --git a/us_maker_edition/src/base/UNote.pas b/us_maker_edition/src/base/UNote.pas index 6eb99df9..d800d30e 100644 --- a/us_maker_edition/src/base/UNote.pas +++ b/us_maker_edition/src/base/UNote.pas @@ -88,12 +88,23 @@ type Note: array of TPlayerNote; end; +{* Player and music info *} var - - // player and music info - Player: array of TPlayer; + {** + * Player info and state for each player. + * The amount of players is given by PlayersPlay. + *} + Player: array of TPlayer; + + {** + * Number of players or teams playing. + * Possible values: 1 - 6 + *} PlayersPlay: integer; + {** + * Selected song for singing. + *} CurrentSong: TSong; const @@ -340,8 +351,39 @@ begin end; procedure NewBeatDetect(Screen: TScreenSing); + var + SentenceEnd: integer; + I: cardinal; begin NewNote(Screen); + + // check for sentence end + // we check all lines here because a new sentence may + // have been started even before the old one finishes + // due to corrupt lien breaks + // checking only current line works to, but may lead to + // weird ratings for the song files w/ the mentioned + // errors + // To-Do Philipp : check current and last line should + // do it for most corrupt txt and for lines in + // non-corrupt txts that start immediatly after the prev. + // line ends + if (assigned(Screen)) then + begin + for I := 0 to Lines[0].High do + begin + with Lines[0].Line[I] do + begin + if (HighNote > 0) then + begin + SentenceEnd := Note[HighNote].Start + Note[HighNote].Length; + + if (LyricsState.OldBeatD < SentenceEnd) and (LyricsState.CurrentBeatD >= SentenceEnd) then + Screen.OnSentenceEnd(I); + end; + end; + end; + end; end; procedure NewNote(Screen: TScreenSing); @@ -571,20 +613,6 @@ begin end; // for PlayerIndex //Log.LogStatus('EndBeat', 'NewBeat'); - - // on sentence end -> for LineBonus and display of SingBar (rating pop-up) - if (SentenceDetected >= Low(Lines[0].Line)) and - (SentenceDetected <= High(Lines[0].Line)) then - begin - Line := @Lines[0].Line[SentenceDetected]; - CurrentLineFragment := @Line.Note[Line.HighNote]; - if ((CurrentLineFragment.Start + CurrentLineFragment.Length - 1) = LyricsState.CurrentBeatD) then - begin - if assigned(Screen) then - Screen.OnSentenceEnd(SentenceDetected); - end; - end; - end; end. diff --git a/us_maker_edition/src/base/UPathUtils.pas b/us_maker_edition/src/base/UPathUtils.pas index c2bcdd4b..2bfcde42 100644 --- a/us_maker_edition/src/base/UPathUtils.pas +++ b/us_maker_edition/src/base/UPathUtils.pas @@ -185,8 +185,13 @@ begin // Add song paths AddSongPath(Params.SongPath); +{$IF Defined(DARWIN)} + AddSongPath(Platform.GetMusicPath); + AddSongPath(UserPath.Append('songs')); +{$ELSE} AddSongPath(SharedPath.Append('songs')); AddSongPath(UserPath.Append('songs')); +{$IFEND} // Add category cover paths AddCoverPath(SharedPath.Append('covers')); diff --git a/us_maker_edition/src/base/UPlatform.pas b/us_maker_edition/src/base/UPlatform.pas index 11c67fa7..6d884979 100644 --- a/us_maker_edition/src/base/UPlatform.pas +++ b/us_maker_edition/src/base/UPlatform.pas @@ -51,6 +51,7 @@ type procedure Halt; virtual; function GetLogPath: IPath; virtual; abstract; + function GetMusicPath: IPath; virtual; abstract; function GetGameSharedPath: IPath; virtual; abstract; function GetGameUserPath: IPath; virtual; abstract; end; diff --git a/us_maker_edition/src/base/UPlatformMacOSX.pas b/us_maker_edition/src/base/UPlatformMacOSX.pas index d55e8bea..7115a6b0 100644 --- a/us_maker_edition/src/base/UPlatformMacOSX.pas +++ b/us_maker_edition/src/base/UPlatformMacOSX.pas @@ -108,7 +108,10 @@ type *} procedure CreateUserFolders(); - function GetHomeDir(): IPath; + {** + * GetHomeDir returns the path to $HOME. + *} + function GetHomeDir: IPath; public {** @@ -121,13 +124,19 @@ type {** * GetLogPath returns the path for log messages. Currently it is set to - * $HOME/Library/Application Support/UltraStarDeluxe/log. + * $HOME/Library/Logs/UltraStar Deluxe/. *} function GetLogPath: IPath; override; {** + * GetMusicPath returns the path for music. Currently it is set to + * $HOME/Music/UltraStar Deluxe/. + *} + function GetMusicPath: IPath; override; + + {** * GetGameSharedPath returns the path for shared resources. Currently it - * is set to /Library/Application Support/UltraStarDeluxe. + * is also set to $HOME/Library/Application Support/UltraStarDeluxe. * However it is not used. *} function GetGameSharedPath: IPath; override; @@ -135,7 +144,7 @@ type {** * GetGameUserPath returns the path for user resources. Currently it is * set to $HOME/Library/Application Support/UltraStarDeluxe. - * This is where a user can add songs, themes, .... + * This is where a user can add themes, .... *} function GetGameUserPath: IPath; override; end; @@ -251,21 +260,24 @@ begin Result := GetExecutionDir().GetParent().GetParent(); end; -function TPlatformMacOSX.GetApplicationSupportPath: IPath; -const - PathName: string = 'Library/Application Support/UltraStarDeluxe'; +function TPlatformMacOSX.GetHomeDir: IPath; begin - Result := GetHomeDir().Append(PathName, pdAppend); + Result := Path(GetEnvironmentVariable('HOME')); end; -function TPlatformMacOSX.GetHomeDir(): IPath; +function TPlatformMacOSX.GetApplicationSupportPath: IPath; begin - Result := Path(GetEnvironmentVariable('HOME')); + Result := GetHomeDir.Append('Library/Application Support/UltraStarDeluxe', pdAppend); end; function TPlatformMacOSX.GetLogPath: IPath; begin - Result := GetApplicationSupportPath.Append('logs'); + Result := GetHomeDir.Append('Library/Logs/UltraStar Deluxe', pdAppend); +end; + +function TPlatformMacOSX.GetMusicPath: IPath; +begin + Result := GetHomeDir.Append('Music/UltraStar Deluxe', pdAppend); end; function TPlatformMacOSX.GetGameSharedPath: IPath; diff --git a/us_maker_edition/src/base/URecord.pas b/us_maker_edition/src/base/URecord.pas index c183875c..5cddcc77 100644 --- a/us_maker_edition/src/base/URecord.pas +++ b/us_maker_edition/src/base/URecord.pas @@ -124,6 +124,8 @@ type procedure SetVolume(Volume: single); virtual; abstract; end; + TBooleanDynArray = array of boolean; + TAudioInputProcessor = class public Sound: array of TCaptureBuffer; // sound-buffers for every player @@ -133,9 +135,36 @@ type destructor Destroy; override; procedure UpdateInputDeviceConfig; - function ValidateSettings: boolean; - // handle microphone input + {** + * Validates the mic settings. + * If a player was assigned to multiple mics a popup will be displayed + * with the ID of the player. + * The return value is the player number of the first player that is not + * configured correctly or 0 if all players are correct. + *} + function ValidateSettings: integer; + + {** + * Checks if players 1 to PlayerCount are configured correctly. + * A player is configured if a device's channel is assigned to him. + * For each player (up to PlayerCount) the state will be in PlayerState. + * If a player's state is true the player is configured, otherwise not. + * The return value is the player number of the first player that is not + * configured correctly or 0 if all players are correct. + * The PlayerState array is zero based (index 0 for player 1). + *} + function CheckPlayersConfig(PlayerCount: cardinal; + var PlayerState: TBooleanDynArray): integer; overload; + + {** + * Same as the array version but it does not output a state for each player. + *} + function CheckPlayersConfig(PlayerCount: cardinal): integer; overload; + + {** + * Handle microphone input + *} procedure HandleMicrophoneData(Buffer: PByteArray; Size: integer; InputDevice: TAudioInputDevice); end; @@ -163,8 +192,6 @@ implementation uses ULog, - UGraphic, - ULanguage, UNote; var @@ -555,10 +582,10 @@ begin channelIndex := High(deviceCfg.ChannelToPlayerMap); // add missing channels or remove non-existing ones SetLength(deviceCfg.ChannelToPlayerMap, device.AudioFormat.Channels); - // initialize added channels to 0 + // assign added channels to no player for i := channelIndex+1 to High(deviceCfg.ChannelToPlayerMap) do begin - deviceCfg.ChannelToPlayerMap[i] := 0; + deviceCfg.ChannelToPlayerMap[i] := CHANNEL_OFF; end; // associate ini-index with device @@ -587,23 +614,23 @@ begin for channelIndex := 0 to channelCount-1 do begin - // set default at first start of USDX (1st device, 1st channel -> player1) - if ((channelIndex = 0) and (device.CfgIndex = 0)) then - deviceCfg.ChannelToPlayerMap[0] := 1 - else - deviceCfg.ChannelToPlayerMap[channelIndex] := 0; + // Do not set any default on first start of USDX. + // Otherwise most probably the wrong device (internal sound card) + // will be selected. + // It is better to force the user to configure the mics himself. + deviceCfg.ChannelToPlayerMap[channelIndex] := CHANNEL_OFF; end; end; end; end; -function TAudioInputProcessor.ValidateSettings: boolean; +function TAudioInputProcessor.ValidateSettings: integer; const MAX_PLAYER_COUNT = 6; // FIXME: there should be a global variable for this var I, J: integer; PlayerID: integer; - PlayerMap: array [0 .. MAX_PLAYER_COUNT] of boolean; + PlayerMap: array [0 .. MAX_PLAYER_COUNT - 1] of boolean; InputDevice: TAudioInputDevice; InputDeviceCfg: PInputDeviceConfig; begin @@ -621,24 +648,73 @@ begin begin // get player that was mapped to the current device channel PlayerID := InputDeviceCfg.ChannelToPlayerMap[J]; - if (PlayerID <> 0) then + if (PlayerID <> CHANNEL_OFF) then begin // check if player is already assigned to another device/channel - if (PlayerMap[PlayerID]) then + if (PlayerMap[PlayerID - 1]) then begin - ScreenPopupError.ShowPopup( - Format(Language.Translate('ERROR_PLAYER_DEVICE_ASSIGNMENT'), - [PlayerID])); - Result := false; + Result := PlayerID; Exit; end; // mark player as assigned to a device - PlayerMap[PlayerID] := true; + PlayerMap[PlayerID - 1] := true; end; end; end; - Result := true; + Result := 0; +end; + +function TAudioInputProcessor.CheckPlayersConfig(PlayerCount: cardinal; + var PlayerState: TBooleanDynArray): integer; +var + DeviceIndex: integer; + ChannelIndex: integer; + Device: TAudioInputDevice; + DeviceCfg: PInputDeviceConfig; + PlayerIndex: integer; + I: integer; +begin + SetLength(PlayerState, PlayerCount); + // set all entries to "not configured" + for I := 0 to High(PlayerState) do + begin + PlayerState[I] := false; + end; + + // check each used device + for DeviceIndex := 0 to High(AudioInputProcessor.DeviceList) do + begin + Device := AudioInputProcessor.DeviceList[DeviceIndex]; + if not assigned(Device) then + continue; + DeviceCfg := @Ini.InputDeviceConfig[Device.CfgIndex]; + + // check if device is used + for ChannelIndex := 0 to High(DeviceCfg.ChannelToPlayerMap) do + begin + PlayerIndex := DeviceCfg.ChannelToPlayerMap[ChannelIndex] - 1; + if (PlayerIndex >= 0) and (PlayerIndex < PlayerCount) then + PlayerState[PlayerIndex] := true; + end; + end; + + Result := 0; + for I := 0 to High(PlayerState) do + begin + if (PlayerState[I] = false) then + begin + Result := I + 1; + Break; + end; + end; +end; + +function TAudioInputProcessor.CheckPlayersConfig(PlayerCount: cardinal): integer; +var + PlayerState: TBooleanDynArray; +begin + Result := CheckPlayersConfig(PlayerCount, PlayerState); end; {* @@ -737,7 +813,7 @@ begin // check if device is used for ChannelIndex := 0 to High(DeviceCfg.ChannelToPlayerMap) do begin - Player := DeviceCfg.ChannelToPlayerMap[ChannelIndex]-1; + Player := DeviceCfg.ChannelToPlayerMap[ChannelIndex] - 1; if (Player < 0) or (Player >= PlayersPlay) then begin Device.LinkCaptureBuffer(ChannelIndex, nil); diff --git a/us_maker_edition/src/base/USong.pas b/us_maker_edition/src/base/USong.pas index a441fe40..e92c5b45 100644 --- a/us_maker_edition/src/base/USong.pas +++ b/us_maker_edition/src/base/USong.pas @@ -179,9 +179,6 @@ uses UMusic, //needed for Lines UNote; //needed for Player -const - DEFAULT_ENCODING = encAuto; - constructor TSong.Create(); begin inherited; @@ -510,22 +507,25 @@ begin //Check for ZeroNote if Param2 = 0 then + begin Log.LogWarn(Format('"%s" in line %d: %s', - [FileNamePath.ToNative, FileLineNo, 'found note with length zero -> note ignored']), 'TSong.LoadSong') + [FileNamePath.ToNative, FileLineNo, + 'found note with length zero -> converted to FreeStyle']), + 'TSong.LoadSong'); //Log.LogError('Found zero-length note at "'+Param0+' '+IntToStr(Param1)+' '+IntToStr(Param2)+' '+IntToStr(Param3)+ParamLyric+'" -> Note ignored!') + Param0 := 'F'; + end; + + // add notes + if not Both then + // P1 + ParseNote(0, Param0, (Param1+Rel[0]) * Mult, Param2 * Mult, Param3, ParamLyric) else begin - // add notes - if not Both then - // P1 - ParseNote(0, Param0, (Param1+Rel[0]) * Mult, Param2 * Mult, Param3, ParamLyric) - else - begin - // P1 + P2 - ParseNote(0, Param0, (Param1+Rel[0]) * Mult, Param2 * Mult, Param3, ParamLyric); - ParseNote(1, Param0, (Param1+Rel[1]) * Mult, Param2 * Mult, Param3, ParamLyric); - end; - end; //Zeronote check + // P1 + P2 + ParseNote(0, Param0, (Param1+Rel[0]) * Mult, Param2 * Mult, Param3, ParamLyric); + ParseNote(1, Param0, (Param1+Rel[1]) * Mult, Param2 * Mult, Param3, ParamLyric); + end; end // if else if Param0 = '-' then @@ -1087,7 +1087,7 @@ begin // File encoding else if (Identifier = 'ENCODING') then begin - self.Encoding := ParseEncoding(Value, DEFAULT_ENCODING); + self.Encoding := ParseEncoding(Value, Ini.DefaultEncoding); end // unsupported tag @@ -1236,7 +1236,7 @@ begin Year := 0; // set to default encoding - Encoding := DEFAULT_ENCODING; + Encoding := Ini.DefaultEncoding; // clear custom header tags SetLength(CustomTags, 0); diff --git a/us_maker_edition/src/config-darwin.inc b/us_maker_edition/src/config-darwin.inc index 83cadbae..4c128a05 100644 --- a/us_maker_edition/src/config-darwin.inc +++ b/us_maker_edition/src/config-darwin.inc @@ -50,4 +50,9 @@ PORTAUDIO_VERSION_RELEASE = 0; {$IFEND} +{$DEFINE HaveLibPcre} +{$IF Defined(HaveLibPcre) and Defined(IncludeConstants)} + LIBPCRE_LIBDIR = '/sw/lib'; +{$IFEND} + {$UNDEF HavePortmixer} diff --git a/us_maker_edition/src/config-win.inc b/us_maker_edition/src/config-win.inc index 72e00aef..843fdb0e 100644 --- a/us_maker_edition/src/config-win.inc +++ b/us_maker_edition/src/config-win.inc @@ -8,26 +8,26 @@ {$IF Defined(HaveFFmpeg) and Defined(IncludeConstants)} av__codec = 'avcodec-52'; LIBAVCODEC_VERSION_MAJOR = 52; - LIBAVCODEC_VERSION_MINOR = 45; - LIBAVCODEC_VERSION_RELEASE = 0; + LIBAVCODEC_VERSION_MINOR = 67; + LIBAVCODEC_VERSION_RELEASE = 2; av__format = 'avformat-52'; LIBAVFORMAT_VERSION_MAJOR = 52; - LIBAVFORMAT_VERSION_MINOR = 46; + LIBAVFORMAT_VERSION_MINOR = 62; LIBAVFORMAT_VERSION_RELEASE = 0; av__util = 'avutil-50'; LIBAVUTIL_VERSION_MAJOR = 50; - LIBAVUTIL_VERSION_MINOR = 7; - LIBAVUTIL_VERSION_RELEASE = 0; + LIBAVUTIL_VERSION_MINOR = 15; + LIBAVUTIL_VERSION_RELEASE = 2; {$IFEND} {$DEFINE HaveSWScale} {$IF Defined(HaveSWScale) and Defined(IncludeConstants)} sw__scale = 'swscale-0'; LIBSWSCALE_VERSION_MAJOR = 0; - LIBSWSCALE_VERSION_MINOR = 7; - LIBSWSCALE_VERSION_RELEASE = 2; + LIBSWSCALE_VERSION_MINOR = 10; + LIBSWSCALE_VERSION_RELEASE = 0; {$IFEND} {$DEFINE HaveProjectM} diff --git a/us_maker_edition/src/config.inc.in b/us_maker_edition/src/config.inc.in index b5e086f4..d57c7ab4 100644 --- a/us_maker_edition/src/config.inc.in +++ b/us_maker_edition/src/config.inc.in @@ -50,4 +50,9 @@ PORTAUDIO_VERSION_RELEASE = @portaudio_VERSION_RELEASE@; {$IFEND} +{$@DEFINE_HAVE_LIBPCRE@ HaveLibPcre} +{$IF Defined(HaveLibPcre) and Defined(IncludeConstants)} + LIBPCRE_LIBDIR = '@libpcre_LIBDIR@'; +{$IFEND} + {$@DEFINE_HAVE_PORTMIXER@ HavePortmixer} diff --git a/us_maker_edition/src/lib/ffmpeg/avcodec.pas b/us_maker_edition/src/lib/ffmpeg/avcodec.pas index 066910a3..a441232d 100644 --- a/us_maker_edition/src/lib/ffmpeg/avcodec.pas +++ b/us_maker_edition/src/lib/ffmpeg/avcodec.pas @@ -14,20 +14,16 @@ * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - *) - -(* + * * This is a part of Pascal porting of ffmpeg. * - Originally by Victor Zinetz for Delphi and Free Pascal on Windows. * - For Mac OS X, some modifications were made by The Creative CAT, denoted as CAT * in the source codes. * - Changes and updates by the UltraStar Deluxe Team - *) - -(* + * * Conversion of libavcodec/avcodec.h * Min. version: 51.16.0, revision 6577, Sat Oct 7 15:30:46 2006 UTC - * Max. version: 52.67.0, revision 23057, Tue May 11 18:30 2010 CET + * Max. version: 52.72.0, revision 23338, Sun May 30 20:55 2010 CET * *) @@ -86,7 +82,7 @@ const *) (* Max. supported version by this header *) LIBAVCODEC_MAX_VERSION_MAJOR = 52; - LIBAVCODEC_MAX_VERSION_MINOR = 67; + LIBAVCODEC_MAX_VERSION_MINOR = 72; LIBAVCODEC_MAX_VERSION_RELEASE = 0; LIBAVCODEC_MAX_VERSION = (LIBAVCODEC_MAX_VERSION_MAJOR * VERSION_MAJOR) + (LIBAVCODEC_MAX_VERSION_MINOR * VERSION_MINOR) + @@ -307,6 +303,9 @@ type {$IF LIBAVCODEC_VERSION >= 52062000} // >= 52.62.0 CODEC_ID_YOP, {$IFEND} +{$IF LIBAVCODEC_VERSION >= 52067002} // >= 52.67.2 + CODEC_ID_VP8, +{$IFEND} //* various PCM "codecs" */ CODEC_ID_PCM_S16LE= $10000, @@ -850,6 +849,14 @@ const CODEC_CAP_SUBFRAMES = $0100; {$IFEND} + {$IF LIBAVCODEC_VERSION >= 52071000} // >= 52.71.0 + (** + * Codec is experimental and is thus avoided in favor of non experimental + * encoders + *) + CODEC_CAP_EXPERIMENTAL = $0200; + {$IFEND} + //the following defines may change, don't expect compatibility if you use them MB_TYPE_INTRA4x4 = $001; MB_TYPE_INTRA16x16 = $002; //FIXME h264 specific @@ -1256,6 +1263,16 @@ type *) log_level_offset_offset: cint; {$IFEND} + +{$IF LIBAVUTIL_VERSION >= 50015003} // 50.15.3 + (** + * Offset in the structure where a pointer to the parent context for loging is stored. + * for example a decoder that uses eval.c could pass its AVCodecContext to eval as such + * parent context. And a av_log() implementation could then display the parent context + * can be NULL of course + *) + parent_log_context_offset: cint; +{$IFEND} end; {** @@ -3012,7 +3029,11 @@ type *) crf_max: cfloat; {$IFEND} - end; + + {$IF LIBAVCODEC_VERSION >= 52067002} // >= 52.67.2 + log_level_offset: cint; + {$IFEND} + end; {TAVCodecContext} (** * AVCodec. @@ -3852,6 +3873,10 @@ function avcodec_get_edge_width(): cuint; * Modifies width and height values so that they will result in a memory * buffer that is acceptable for the codec if you do not use any horizontal * padding. + * + * May only be used if a codec with CODEC_CAP_DR1 has been opened. + * If CODEC_FLAG_EMU_EDGE is not set, the dimensions must have been increased + * according to avcodec_get_edge_width() before. *) procedure avcodec_align_dimensions(s: PAVCodecContext; width: PCint; height: PCint); cdecl; external av__codec; @@ -3861,6 +3886,10 @@ procedure avcodec_align_dimensions(s: PAVCodecContext; width: PCint; height: PCi * Modifies width and height values so that they will result in a memory * buffer that is acceptable for the codec if you also ensure that all * line sizes are a multiple of the respective linesize_align[i]. + * + * May only be used if a codec with CODEC_CAP_DR1 has been opened. + * If CODEC_FLAG_EMU_EDGE is not set, the dimensions must have been increased + * according to avcodec_get_edge_width() before. *) procedure avcodec_align_dimensions2(s: PAVCodecContext; width: PCint; height: PCint; linesize_align: PQuadIntArray); @@ -4212,6 +4241,9 @@ function av_get_bits_per_sample_format(sample_fmt: TSampleFormat): cint; const AV_PARSER_PTS_NB = 4; PARSER_FLAG_COMPLETE_FRAMES = $0001; +{$IF LIBAVCODEC_VERSION >= 52070000} // 52.70.0 + PARSER_FLAG_ONCE = $0002; +{$IFEND} type {* frame parsing *} @@ -4694,20 +4726,19 @@ const {$ENDIF} (** - * We need the sign of of the error, because some platforms have + * We need the sign of the error, because some platforms have * E* and errno already negated. The previous version failed - * with Delphi, because it needs EINVAL defined. + * with Delphi, because it needed EINVAL defined. * Warning: This code is platform dependent and assumes constants * to be 32 bit. * This version does the following steps: * 1) shr 30: shifts the sign bit to bit position 2 * 2) and $00000002: sets all other bits to zero * positive EINVAL gives 0, negative gives 2 - * 3) not: inverts all bits. This gives -1 and -3 - * 4) + 2: positive EINVAL gives 1, negative -1 + * 3) - 1: positive EINVAL gives -1, negative 1 *) const - AVERROR_SIGN = not((EINVAL shr 30) and $00000002) + 2; + AVERROR_SIGN = (EINVAL shr 30) and $00000002 - 1; (* #if EINVAL > 0 diff --git a/us_maker_edition/src/lib/ffmpeg/avformat.pas b/us_maker_edition/src/lib/ffmpeg/avformat.pas index a217263d..34142125 100644 --- a/us_maker_edition/src/lib/ffmpeg/avformat.pas +++ b/us_maker_edition/src/lib/ffmpeg/avformat.pas @@ -14,20 +14,16 @@ * You should have received a copy of the GNU Lesser General Public * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - *) - -(* + * * This is a part of Pascal porting of ffmpeg. * - Originally by Victor Zinetz for Delphi and Free Pascal on Windows. * - For Mac OS X, some modifications were made by The Creative CAT, denoted as CAT * in the source codes. * - Changes and updates by the UltraStar Deluxe Team - *) - -(* + * * Conversion of libavformat/avformat.h * Min. version: 50.5.0 , revision 6577, Sat Oct 7 15:30:46 2006 UTC - * Max. version: 52.62.0, revision 23102, Thu May 13 1:15:00 2010 CET + * Max. version: 52.67.0, revision 23357, Sun May 30 21:30:00 2010 CET *) unit avformat; @@ -85,7 +81,7 @@ const *) (* Max. supported version by this header *) LIBAVFORMAT_MAX_VERSION_MAJOR = 52; - LIBAVFORMAT_MAX_VERSION_MINOR = 62; + LIBAVFORMAT_MAX_VERSION_MINOR = 67; LIBAVFORMAT_MAX_VERSION_RELEASE = 0; LIBAVFORMAT_MAX_VERSION = (LIBAVFORMAT_MAX_VERSION_MAJOR * VERSION_MAJOR) + (LIBAVFORMAT_MAX_VERSION_MINOR * VERSION_MINOR) + @@ -405,6 +401,9 @@ const AVFMT_FLAG_NOFILLIN = $0010; ///< Do not infer any values from other values, just return what is stored in the container AVFMT_FLAG_NOPARSE = $0020; ///< Do not use AVParsers, you also must set AVFMT_FLAG_NOFILLIN as the fillin code works on frames and no parsing -> no frames. Also seeking to frames can not work if parsing to find frame boundaries has been disabled {$IFEND} +{$IF LIBAVFORMAT_VERSION >= 52063000} // >= 52.63.0 + AVFMT_FLAG_RTP_HINT = $0040; ///< Add RTP hinting to the output file +{$IFEND} // used by AVStream MAX_REORDER_DELAY = 16; @@ -671,6 +670,9 @@ type AVSTREAM_PARSE_FULL, (**< full parsing and repack *) AVSTREAM_PARSE_HEADERS, (**< Only parse headers, do not repack. *) AVSTREAM_PARSE_TIMESTAMPS (**< full parsing and interpolation of timestamps for frames not starting on a packet boundary *) + {$IF LIBAVFORMAT_VERSION >= 52066000} // 52.66.0 + , AVSTREAM_PARSE_FULL_ONCE (**< full parsing and repack of the first frame only, only implemented for H.264 currently *) + {$IFEND} ); TAVIndexEntry = record diff --git a/us_maker_edition/src/lib/ffmpeg/avio.pas b/us_maker_edition/src/lib/ffmpeg/avio.pas index 4863ee39..0ebca5fa 100644 --- a/us_maker_edition/src/lib/ffmpeg/avio.pas +++ b/us_maker_edition/src/lib/ffmpeg/avio.pas @@ -15,24 +15,20 @@ * You should have received a copy of the GNU Lesser General Public * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - *) - -(* + * * This is a part of Pascal porting of ffmpeg. * - Originally by Victor Zinetz for Delphi and Free Pascal on Windows. * - For Mac OS X, some modifications were made by The Creative CAT, denoted as CAT * in the source codes. * - Changes and updates by the UltraStar Deluxe Team - *) - -(* + * * Conversion of libavformat/avio.h * unbuffered I/O operations * @warning This file has to be considered an internal but installed * header, so it should not be directly included in your projects. * * update to - * Max. avformat version: 52.62.0, revision 23004, Tue May 11 19:29:00 2010 CET + * Max. avformat version: 52.67.0, revision 23357, Sun May 30 21:30:00 2010 CET *) unit avio; diff --git a/us_maker_edition/src/lib/ffmpeg/avutil.pas b/us_maker_edition/src/lib/ffmpeg/avutil.pas index 959e8fda..5387a0f1 100644 --- a/us_maker_edition/src/lib/ffmpeg/avutil.pas +++ b/us_maker_edition/src/lib/ffmpeg/avutil.pas @@ -14,22 +14,18 @@ * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - *) - -(* + * * This is a part of Pascal porting of ffmpeg. * - Originally by Victor Zinetz for Delphi and Free Pascal on Windows. * - For Mac OS X, some modifications were made by The Creative CAT, denoted as CAT * in the source codes. * - Changes and updates by the UltraStar Deluxe Team - *) - -(* + * * Conversions of * * libavutil/avutil.h: * Min. version: 49.0.1, revision 6577, Sat Oct 7 15:30:46 2006 UTC - * Max. version: 50.15.2, revision 23059, Tue May 11 22:05:00 2010 CET + * Max. version: 50.16.0, revision 23255, Sun May 30 22:05:00 2010 CET * * libavutil/mem.h: * revision 16590, Tue Jan 13 23:44:16 2009 UTC @@ -96,7 +92,7 @@ const *) (* Max. supported version by this header *) LIBAVUTIL_MAX_VERSION_MAJOR = 50; - LIBAVUTIL_MAX_VERSION_MINOR = 15; + LIBAVUTIL_MAX_VERSION_MINOR = 16; LIBAVUTIL_MAX_VERSION_RELEASE = 0; LIBAVUTIL_MAX_VERSION = (LIBAVUTIL_MAX_VERSION_MAJOR * VERSION_MAJOR) + (LIBAVUTIL_MAX_VERSION_MINOR * VERSION_MINOR) + @@ -124,7 +120,7 @@ const * Returns the LIBAVUTIL_VERSION_INT constant. *) function avutil_version(): cuint; - cdecl; external av__format; + cdecl; external av__util; {$IFEND} {$IF LIBAVUTIL_VERSION >= 50004000} // >= 50.4.0 @@ -132,13 +128,13 @@ function avutil_version(): cuint; * Returns the libavutil build-time configuration. *) function avutil_configuration(): PAnsiChar; - cdecl; external av__format; + cdecl; external av__util; (** * Returns the libavutil license. *) function avutil_license(): PAnsiChar; - cdecl; external av__format; + cdecl; external av__util; {$IFEND} { @@ -158,98 +154,9 @@ type ); } -(* libavutil/error.h *) +{$INCLUDE error.pas} -{$IF LIBAVUTIL_VERSION >= 50012000} // >= 50.12.0 - -{* error handling *} - -const -{$IFDEF UNIX} - ENOENT = ESysENOENT; - EIO = ESysEIO; - ENOMEM = ESysENOMEM; - EINVAL = ESysEINVAL; - EDOM = ESysEDOM; - ENOSYS = ESysENOSYS; - EILSEQ = ESysEILSEQ; - EPIPE = ESysEPIPE; -{$ELSE} - ENOENT = 2; - EIO = 5; - ENOMEM = 12; - EINVAL = 22; - EPIPE = 32; // just an assumption. needs to be checked. - EDOM = 33; - {$IFDEF MSWINDOWS} - // Note: we assume that ffmpeg was compiled with MinGW. - // This must be changed if DLLs were compiled with cygwin. - ENOSYS = 40; // MSVC/MINGW: 40, CYGWIN: 88, LINUX/FPC: 38 - EILSEQ = 42; // MSVC/MINGW: 42, CYGWIN: 138, LINUX/FPC: 84 - {$ENDIF} -{$ENDIF} - -(** - * We need the sign of of the error, because some platforms have - * E* and errno already negated. The previous version failed - * with Delphi, because it needs EINVAL defined. - * Warning: This code is platform dependent and assumes constants - * to be 32 bit. - * This version does the following steps: - * 1) shr 30: shifts the sign bit to bit position 2 - * 2) and $00000002: sets all other bits to zero - * positive EINVAL gives 0, negative gives 2 - * 3) not: inverts all bits. This gives -1 and -3 - * 4) + 2: positive EINVAL gives 1, negative -1 - *) -const - AVERROR_SIGN = not((EINVAL shr 30) and $00000002) + 2; - -(* -#if EINVAL > 0 -#define AVERROR(e) (-(e)) {**< Returns a negative error code from a POSIX error code, to return from library functions. *} -#define AVUNERROR(e) (-(e)) {**< Returns a POSIX error code from a library function error return value. *} -#else -{* Some platforms have E* and errno already negated. *} -#define AVERROR(e) (e) -#define AVUNERROR(e) (e) -#endif -*) - -const - AVERROR_UNKNOWN = AVERROR_SIGN * EINVAL; (**< unknown error *) - AVERROR_IO = AVERROR_SIGN * EIO; (**< I/O error *) - AVERROR_NUMEXPECTED = AVERROR_SIGN * EDOM; (**< Number syntax expected in filename. *) - AVERROR_INVALIDDATA = AVERROR_SIGN * EINVAL; (**< invalid data found *) - AVERROR_NOMEM = AVERROR_SIGN * ENOMEM; (**< not enough memory *) - AVERROR_NOFMT = AVERROR_SIGN * EILSEQ; (**< unknown format *) - AVERROR_NOTSUPP = AVERROR_SIGN * ENOSYS; (**< Operation not supported. *) - AVERROR_NOENT = AVERROR_SIGN * ENOENT; (**< No such file or directory. *) -{$IF LIBAVCODEC_VERSION >= 52017000} // 52.17.0 - AVERROR_EOF = AVERROR_SIGN * EPIPE; (**< End of file. *) -{$IFEND} - // Note: function calls as constant-initializers are invalid - //AVERROR_PATCHWELCOME = -MKTAG('P','A','W','E'); {**< Not yet implemented in FFmpeg. Patches welcome. *} - AVERROR_PATCHWELCOME = -(ord('P') or (ord('A') shl 8) or (ord('W') shl 16) or (ord('E') shl 24)); -{$IFEND} - -{$IF LIBAVUTIL_VERSION >= 50013000} // >= 50.13.0 -(* - * Puts a description of the AVERROR code errnum in errbuf. - * In case of failure the global variable errno is set to indicate the - * error. Even in case of failure av_strerror() will print a generic - * error message indicating the errnum provided to errbuf. - * - * @param errbuf_size the size in bytes of errbuf - * @return 0 on success, a negative value if a description for errnum - * cannot be found - *) - -function av_strerror(errnum: cint; errbuf: Pchar; errbuf_size: cint): cint; - cdecl; external av__util; -{$IFEND} - -(* libavutil/pixfmt.h *) +(* libavutil/pixfmt.h up to revision 23144, May 16 2010 *) type (** @@ -298,8 +205,8 @@ type PIX_FMT_RGB555, ///< packed RGB 5:5:5, 16bpp, (msb)1A 5R 5G 5B(lsb), in CPU endianness, most significant bit to 0 {$IFEND} PIX_FMT_GRAY8, ///< Y , 8bpp - PIX_FMT_MONOWHITE, ///< Y , 1bpp, 0 is white, 1 is black - PIX_FMT_MONOBLACK, ///< Y , 1bpp, 0 is black, 1 is white + PIX_FMT_MONOWHITE, ///< Y , 1bpp, 0 is white, 1 is black, in each byte pixels are ordered from the msb to the lsb + PIX_FMT_MONOBLACK, ///< Y , 1bpp, 0 is black, 1 is white, in each byte pixels are ordered from the msb to the lsb PIX_FMT_PAL8, ///< 8 bit with PIX_FMT_RGB32 palette PIX_FMT_YUVJ420P, ///< planar YUV 4:2:0, 12bpp, full scale (JPEG) PIX_FMT_YUVJ422P, ///< planar YUV 4:2:2, 16bpp, full scale (JPEG) @@ -316,12 +223,12 @@ type PIX_FMT_BGR555, ///< packed RGB 5:5:5, 16bpp, (msb)1A 5B 5G 5R(lsb), in CPU endianness, most significant bit to 1 {$IFEND} PIX_FMT_BGR8, ///< packed RGB 3:3:2, 8bpp, (msb)2B 3G 3R(lsb) - PIX_FMT_BGR4, ///< packed RGB 1:2:1, 4bpp, (msb)1B 2G 1R(lsb) + PIX_FMT_BGR4, ///< packed RGB 1:2:1, bitstream, 4bpp, (msb)1B 2G 1R(lsb), a byte contains two pixels, the first pixel in the byte is the one composed by the 4 msb bits PIX_FMT_BGR4_BYTE, ///< packed RGB 1:2:1, 8bpp, (msb)1B 2G 1R(lsb) PIX_FMT_RGB8, ///< packed RGB 3:3:2, 8bpp, (msb)2R 3G 3B(lsb) - PIX_FMT_RGB4, ///< packed RGB 1:2:1, 4bpp, (msb)1R 2G 1B(lsb) + PIX_FMT_RGB4, ///< packed RGB 1:2:1, bitstream, 4bpp, (msb)1R 2G 1B(lsb), a byte contains two pixels, the first pixel in the byte is the one composed by the 4 msb bits PIX_FMT_RGB4_BYTE, ///< packed RGB 1:2:1, 8bpp, (msb)1R 2G 1B(lsb) - PIX_FMT_NV12, ///< planar YUV 4:2:0, 12bpp, 1 plane for Y and 1 for UV + PIX_FMT_NV12, ///< planar YUV 4:2:0, 12bpp, 1 plane for Y and 1 plane for the UV components, which are interleaved (first byte U and the following byte V) PIX_FMT_NV21, ///< as above, but U and V bytes are swapped {$IF LIBAVUTIL_VERSION <= 50001000} // 50.01.0 PIX_FMT_RGB32_1, ///< packed RGB 8:8:8, 32bpp, (msb)8R 8G 8B 8A(lsb), in CPU endianness @@ -343,8 +250,8 @@ type PIX_FMT_VDPAU_WMV3,///< WMV3 HW decoding with VDPAU, data[0] contains a vdpau_render_state struct which contains the bitstream of the slices as well as various fields extracted from headers PIX_FMT_VDPAU_VC1, ///< VC-1 HW decoding with VDPAU, data[0] contains a vdpau_render_state struct which contains the bitstream of the slices as well as various fields extracted from headers {$IF LIBAVUTIL_VERSION >= 49015000} // 49.15.0 - PIX_FMT_RGB48BE, ///< packed RGB 16:16:16, 48bpp, 16R, 16G, 16B, big-endian - PIX_FMT_RGB48LE, ///< packed RGB 16:16:16, 48bpp, 16R, 16G, 16B, little-endian + PIX_FMT_RGB48BE, ///< packed RGB 16:16:16, 48bpp, 16R, 16G, 16B, the 2-byte value for each R/G/B component is stored as big-endian + PIX_FMT_RGB48LE, ///< packed RGB 16:16:16, 48bpp, 16R, 16G, 16B, the 2-byte value for each R/G/B component is stored as little-endian {$IFEND} {$IF LIBAVUTIL_VERSION >= 50001000} // 50.01.0 PIX_FMT_RGB565BE, ///< packed RGB 5:6:5, 16bpp, (msb) 5R 6G 5B(lsb), big-endian @@ -417,9 +324,10 @@ const PIX_FMT_YUV422 = PIX_FMT_YUYV422; {$IFEND} -(* libavutil/common.h *) // until now MKTAG is all from common.h KMS 9/6/2009 +(* libavutil/common.h *) // until now MKTAG and MKBETAG is all from common.h KMS 19/5/2010 function MKTAG(a, b, c, d: AnsiChar): integer; +function MKBETAG(a, b, c, d: AnsiChar): integer; (* libavutil/mem.h *) @@ -574,6 +482,11 @@ void av_log_set_callback(void (*)(void*, int, const char*, va_list)); void av_log_default_callback(void* ptr, int level, const char* fmt, va_list vl); **} +{$IF LIBAVUTIL_VERSION >= 50015003} // 50.15.3 +function av_default_item_name (ctx: pointer): Pchar; + cdecl; external av__util; +{$IFEND} + implementation (* libavutil/common.h *) @@ -583,4 +496,9 @@ begin Result := (ord(a) or (ord(b) shl 8) or (ord(c) shl 16) or (ord(d) shl 24)); end; +function MKBETAG(a, b, c, d: AnsiChar): integer; +begin + Result := (ord(d) or (ord(c) shl 8) or (ord(b) shl 16) or (ord(a) shl 24)); +end; + end. diff --git a/us_maker_edition/src/lib/ffmpeg/error.pas b/us_maker_edition/src/lib/ffmpeg/error.pas new file mode 100644 index 00000000..c142f6e1 --- /dev/null +++ b/us_maker_edition/src/lib/ffmpeg/error.pas @@ -0,0 +1,113 @@ +(* + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + * This is a part of the Pascal port of ffmpeg. + * - Changes and updates by the UltraStar Deluxe Team + * + * Conversion of libavutil/error.h + * Max. avutil version: 50.16.0, revision 23255, Sun May 30 22:05:00 2010 CET + * + *) + +{$IF LIBAVUTIL_VERSION >= 50012000} // >= 50.12.0 + +{* error handling *} + +const +{$IFDEF UNIX} + ENOENT = ESysENOENT; + EIO = ESysEIO; + ENOMEM = ESysENOMEM; + EINVAL = ESysEINVAL; + EDOM = ESysEDOM; + ENOSYS = ESysENOSYS; + EILSEQ = ESysEILSEQ; + EPIPE = ESysEPIPE; +{$ELSE} + ENOENT = 2; + EIO = 5; + ENOMEM = 12; + EINVAL = 22; + EPIPE = 32; // just an assumption. needs to be checked. + EDOM = 33; + {$IFDEF MSWINDOWS} + // Note: we assume that ffmpeg was compiled with MinGW. + // This must be changed if DLLs were compiled with cygwin. + ENOSYS = 40; // MSVC/MINGW: 40, CYGWIN: 88, LINUX/FPC: 38 + EILSEQ = 42; // MSVC/MINGW: 42, CYGWIN: 138, LINUX/FPC: 84 + {$ENDIF} +{$ENDIF} + +(** + * We need the sign of the error, because some platforms have + * E* and errno already negated. The previous version failed + * with Delphi, because it needed EINVAL defined. + * Warning: This code is platform dependent and assumes constants + * to be 32 bit. + * This version does the following steps: + * 1) shr 30: shifts the sign bit to bit position 2 + * 2) and $00000002: sets all other bits to zero + * positive EINVAL gives 0, negative gives 2 + * 3) not: inverts all bits. This gives -1 and -3 + * 3) - 1: positive EINVAL gives -1, negative 1 + *) +const + AVERROR_SIGN = (EINVAL shr 30) and $00000002 - 1; + +(* +#if EINVAL > 0 +#define AVERROR(e) (-(e)) {**< Returns a negative error code from a POSIX error code, to return from library functions. *} +#define AVUNERROR(e) (-(e)) {**< Returns a POSIX error code from a library function error return value. *} +#else +{* Some platforms have E* and errno already negated. *} +#define AVERROR(e) (e) +#define AVUNERROR(e) (e) +#endif +*) + +const + AVERROR_UNKNOWN = AVERROR_SIGN * EINVAL; (**< unknown error *) + AVERROR_IO = AVERROR_SIGN * EIO; (**< I/O error *) + AVERROR_NUMEXPECTED = AVERROR_SIGN * EDOM; (**< Number syntax expected in filename. *) + AVERROR_INVALIDDATA = AVERROR_SIGN * EINVAL; (**< invalid data found *) + AVERROR_NOMEM = AVERROR_SIGN * ENOMEM; (**< not enough memory *) + AVERROR_NOFMT = AVERROR_SIGN * EILSEQ; (**< unknown format *) + AVERROR_NOTSUPP = AVERROR_SIGN * ENOSYS; (**< Operation not supported. *) + AVERROR_NOENT = AVERROR_SIGN * ENOENT; (**< No such file or directory. *) +{$IF LIBAVCODEC_VERSION >= 52017000} // 52.17.0 + AVERROR_EOF = AVERROR_SIGN * EPIPE; (**< End of file. *) +{$IFEND} + // Note: function calls as constant-initializers are invalid + //AVERROR_PATCHWELCOME = -MKTAG('P','A','W','E'); {**< Not yet implemented in FFmpeg. Patches welcome. *} + AVERROR_PATCHWELCOME = -(ord('P') or (ord('A') shl 8) or (ord('W') shl 16) or (ord('E') shl 24)); +{$IFEND} + +{$IF LIBAVUTIL_VERSION >= 50013000} // >= 50.13.0 +(* + * Puts a description of the AVERROR code errnum in errbuf. + * In case of failure the global variable errno is set to indicate the + * error. Even in case of failure av_strerror() will print a generic + * error message indicating the errnum provided to errbuf. + * + * @param errbuf_size the size in bytes of errbuf + * @return 0 on success, a negative value if a description for errnum + * cannot be found + *) + +function av_strerror(errnum: cint; errbuf: Pchar; errbuf_size: cint): cint; + cdecl; external av__util; +{$IFEND} diff --git a/us_maker_edition/src/lib/ffmpeg/mathematics.pas b/us_maker_edition/src/lib/ffmpeg/mathematics.pas index 3a1f6a2c..a2a59107 100644 --- a/us_maker_edition/src/lib/ffmpeg/mathematics.pas +++ b/us_maker_edition/src/lib/ffmpeg/mathematics.pas @@ -14,19 +14,15 @@ * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - *) - -(* + * * This is a part of Pascal porting of ffmpeg. * - Originally by Victor Zinetz for Delphi and Free Pascal on Windows. * - For Mac OS X, some modifications were made by The Creative CAT, denoted as CAT * in the source codes. * - Changes and updates by the UltraStar Deluxe Team - *) - -(* + * * Conversion of libavutil/mathematics.h - * avutil max. version 50.15.2, revision 23059, Tue May 11 22:10:00 2010 CET + * avutil max. version 50.16.0, revision 23255, Sun May 30 22:05:00 2010 CET * *) diff --git a/us_maker_edition/src/lib/ffmpeg/opt.pas b/us_maker_edition/src/lib/ffmpeg/opt.pas index c755ed35..0e73726f 100644 --- a/us_maker_edition/src/lib/ffmpeg/opt.pas +++ b/us_maker_edition/src/lib/ffmpeg/opt.pas @@ -15,9 +15,7 @@ * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - *) - -(* + * * This is a part of Pascal porting of ffmpeg. * - Originally by Victor Zinetz for Delphi and Free Pascal on Windows. * - For Mac OS X, some modifications were made by The Creative CAT, denoted as CAT @@ -25,7 +23,7 @@ * - Changes and updates by the UltraStar Deluxe Team * * Conversion of libavcodec/opt.h - * Max. avcodec version: 52.67.0, revision 23057, Tue May 11 18:17 2010 CET + * Max. avcodec version: 52.72.0, revision 23338, Sun May 30 20:55 2010 CET * *) diff --git a/us_maker_edition/src/lib/ffmpeg/rational.pas b/us_maker_edition/src/lib/ffmpeg/rational.pas index 6ca9c0d1..e96fccd6 100644 --- a/us_maker_edition/src/lib/ffmpeg/rational.pas +++ b/us_maker_edition/src/lib/ffmpeg/rational.pas @@ -15,19 +15,15 @@ * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - *) - -(* + * * This is a part of Pascal porting of ffmpeg. * - Originally by Victor Zinetz for Delphi and Free Pascal on Windows. * - For Mac OS X, some modifications were made by The Creative CAT, denoted as CAT * in the source codes. * - Changes and updates by the UltraStar Deluxe Team - *) - -(* + * * Conversion of libavutil/rational.h - * avutil max. version 50.15.2, revision 23059, Tue May 11 22:10:00 2010 CET + * avutil max. version 50.16.0, revision 23255, Sun May 30 22:05:00 2010 CET * *) diff --git a/us_maker_edition/src/lib/ffmpeg/swscale.pas b/us_maker_edition/src/lib/ffmpeg/swscale.pas index 4f923f04..f7c11d0d 100644 --- a/us_maker_edition/src/lib/ffmpeg/swscale.pas +++ b/us_maker_edition/src/lib/ffmpeg/swscale.pas @@ -14,16 +14,12 @@ * You should have received a copy of the GNU Lesser General Public * License along with FFmpeg; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - *) - -(* + * * FFmpeg Pascal port * - Ported by the UltraStar Deluxe Team - *) - -(* + * * Conversion of libswscale/swscale.h - * Max. version: 0.10.0, revision 31050, Tue May 11 19:40:00 2010 CET + * Max. version: 0.10.0, revision 31279, Tue May 30 20:25:00 2010 CET *) unit swscale; diff --git a/us_maker_edition/src/lib/pcre/pcre.pas b/us_maker_edition/src/lib/pcre/pcre.pas index 50e3371a..ab04a9d0 100644 --- a/us_maker_edition/src/lib/pcre/pcre.pas +++ b/us_maker_edition/src/lib/pcre/pcre.pas @@ -503,6 +503,9 @@ implementation uses SysUtils, + {$IFDEF DARWIN} + UConfig, + {$ENDIF DARWIN} {$IFDEF MSWINDOWS} Windows; {$ENDIF MSWINDOWS} @@ -536,7 +539,11 @@ const libpcremodulename = 'libpcre.so.0'; {$ENDIF LINUX} {$IFDEF DARWIN} - libpcremodulename = 'libpcre.dylib'; + libpcremodulename = 'libpcre.dylib'; // this is a symlink for example to libpcre.0.0.1.dylib + // the system resolves the symlink + libpcremodulenamefromfink = LIBPCRE_LIBDIR + '/' + libpcremodulename; + // the install command in the Makefile resolves the symlink, when installing libpcre.dylib in the app bundle + libpcremodulenamefromexecutable = '@executable_path/' + libpcremodulename; {$ENDIF DARWIN} PCRECompileExportName = 'pcre_compile'; PCRECompile2ExportName = 'pcre_compile2'; @@ -780,6 +787,14 @@ begin {$IFDEF UNIX} PCRELib := dlopen(PAnsiChar(libpcremodulename), RTLD_NOW); {$ENDIF UNIX} + + {$IFDEF DARWIN} // if libpcre.dylib is not found, first try from the executable path and finally from the fink path + if PCRELib = INVALID_MODULEHANDLE_VALUE then + PCRELib := dlopen(PAnsiChar(libpcremodulenamefromexecutable), RTLD_NOW); + if PCRELib = INVALID_MODULEHANDLE_VALUE then + PCRELib := dlopen(PAnsiChar(libpcremodulenamefromfink), RTLD_NOW); + {$ENDIF DARWIN} + Result := PCRELib <> INVALID_MODULEHANDLE_VALUE; if Result then begin diff --git a/us_maker_edition/src/media/UAudioDecoder_FFmpeg.pas b/us_maker_edition/src/media/UAudioDecoder_FFmpeg.pas index 7ca98885..c64d79c2 100644 --- a/us_maker_edition/src/media/UAudioDecoder_FFmpeg.pas +++ b/us_maker_edition/src/media/UAudioDecoder_FFmpeg.pas @@ -86,60 +86,60 @@ const type TFFmpegDecodeStream = class(TAudioDecodeStream) private - StateLock: PSDL_Mutex; + fStateLock: PSDL_Mutex; - EOFState: boolean; // end-of-stream flag (locked by StateLock) - ErrorState: boolean; // error flag (locked by StateLock) + fEOFState: boolean; // end-of-stream flag (locked by StateLock) + fErrorState: boolean; // error flag (locked by StateLock) - QuitRequest: boolean; // (locked by StateLock) - ParserIdleCond: PSDL_Cond; + fQuitRequest: boolean; // (locked by StateLock) + fParserIdleCond: PSDL_Cond; // parser pause/resume data - ParserLocked: boolean; - ParserPauseRequestCount: integer; - ParserUnlockedCond: PSDL_Cond; - ParserResumeCond: PSDL_Cond; - - SeekRequest: boolean; // (locked by StateLock) - SeekFlags: integer; // (locked by StateLock) - SeekPos: double; // stream position to seek for (in secs) (locked by StateLock) - SeekFlush: boolean; // true if the buffers should be flushed after seeking (locked by StateLock) + fParserLocked: boolean; + fParserPauseRequestCount: integer; + fParserUnlockedCond: PSDL_Cond; + fParserResumeCond: PSDL_Cond; + + fSeekRequest: boolean; // (locked by StateLock) + fSeekFlags: integer; // (locked by StateLock) + fSeekPos: double; // stream position to seek for (in secs) (locked by StateLock) + fSeekFlush: boolean; // true if the buffers should be flushed after seeking (locked by StateLock) SeekFinishedCond: PSDL_Cond; - Loop: boolean; // (locked by StateLock) + fLoop: boolean; // (locked by StateLock) - ParseThread: PSDL_Thread; - PacketQueue: TPacketQueue; + fParseThread: PSDL_Thread; + fPacketQueue: TPacketQueue; - FormatInfo: TAudioFormatInfo; + fFormatInfo: TAudioFormatInfo; // FFmpeg specific data - FormatCtx: PAVFormatContext; - CodecCtx: PAVCodecContext; - Codec: PAVCodec; + fFormatCtx: PAVFormatContext; + fCodecCtx: PAVCodecContext; + fCodec: PAVCodec; - AudioStreamIndex: integer; - AudioStream: PAVStream; - AudioStreamPos: double; // stream position in seconds (locked by DecoderLock) + fAudioStreamIndex: integer; + fAudioStream: PAVStream; + fAudioStreamPos: double; // stream position in seconds (locked by DecoderLock) // decoder pause/resume data - DecoderLocked: boolean; - DecoderPauseRequestCount: integer; - DecoderUnlockedCond: PSDL_Cond; - DecoderResumeCond: PSDL_Cond; + fDecoderLocked: boolean; + fDecoderPauseRequestCount: integer; + fDecoderUnlockedCond: PSDL_Cond; + fDecoderResumeCond: PSDL_Cond; // state-vars for DecodeFrame (locked by DecoderLock) - AudioPaket: TAVPacket; - AudioPaketData: PByteArray; - AudioPaketSize: integer; - AudioPaketSilence: integer; // number of bytes of silence to return + fAudioPaket: TAVPacket; + fAudioPaketData: PByteArray; + fAudioPaketSize: integer; + fAudioPaketSilence: integer; // number of bytes of silence to return // state-vars for AudioCallback (locked by DecoderLock) - AudioBufferPos: integer; - AudioBufferSize: integer; - AudioBuffer: PByteArray; + fAudioBufferPos: integer; + fAudioBufferSize: integer; + fAudioBuffer: PByteArray; - Filename: IPath; + fFilename: IPath; procedure SetPositionIntern(Time: real; Flush: boolean; Blocking: boolean); procedure SetEOF(State: boolean); {$IFDEF HasInline}inline;{$ENDIF} @@ -199,13 +199,13 @@ constructor TFFmpegDecodeStream.Create(); begin inherited Create(); - StateLock := SDL_CreateMutex(); - ParserUnlockedCond := SDL_CreateCond(); - ParserResumeCond := SDL_CreateCond(); - ParserIdleCond := SDL_CreateCond(); + fStateLock := SDL_CreateMutex(); + fParserUnlockedCond := SDL_CreateCond(); + fParserResumeCond := SDL_CreateCond(); + fParserIdleCond := SDL_CreateCond(); SeekFinishedCond := SDL_CreateCond(); - DecoderUnlockedCond := SDL_CreateCond(); - DecoderResumeCond := SDL_CreateCond(); + fDecoderUnlockedCond := SDL_CreateCond(); + fDecoderResumeCond := SDL_CreateCond(); // according to the documentation of avcodec_decode_audio(2), sample-data // should be aligned on a 16 byte boundary. Otherwise internal calls @@ -222,33 +222,33 @@ begin // AudioBuffer was not aligned to a 16 byte boundary. The {$ALIGN x} directive // was not applicable as Delphi in contrast to FPC provides at most 8 byte // alignment ({$ALIGN 16} is not supported) by this directive. - AudioBuffer := GetAlignedMem(AUDIO_BUFFER_SIZE, 16); + fAudioBuffer := GetAlignedMem(AUDIO_BUFFER_SIZE, 16); Reset(); end; procedure TFFmpegDecodeStream.Reset(); begin - ParseThread := nil; + fParseThread := nil; - EOFState := false; - ErrorState := false; - Loop := false; - QuitRequest := false; + fEOFState := false; + fErrorState := false; + fLoop := false; + fQuitRequest := false; - AudioPaketData := nil; - AudioPaketSize := 0; - AudioPaketSilence := 0; + fAudioPaketData := nil; + fAudioPaketSize := 0; + fAudioPaketSilence := 0; - AudioBufferPos := 0; - AudioBufferSize := 0; + fAudioBufferPos := 0; + fAudioBufferSize := 0; - ParserLocked := false; - ParserPauseRequestCount := 0; - DecoderLocked := false; - DecoderPauseRequestCount := 0; + fParserLocked := false; + fParserPauseRequestCount := 0; + fDecoderLocked := false; + fDecoderPauseRequestCount := 0; - FillChar(AudioPaket, SizeOf(TAVPacket), 0); + FillChar(fAudioPaket, SizeOf(TAVPacket), 0); end; {* @@ -258,15 +258,15 @@ destructor TFFmpegDecodeStream.Destroy(); begin Close(); - SDL_DestroyMutex(StateLock); - SDL_DestroyCond(ParserUnlockedCond); - SDL_DestroyCond(ParserResumeCond); - SDL_DestroyCond(ParserIdleCond); + SDL_DestroyMutex(fStateLock); + SDL_DestroyCond(fParserUnlockedCond); + SDL_DestroyCond(fParserResumeCond); + SDL_DestroyCond(fParserIdleCond); SDL_DestroyCond(SeekFinishedCond); - SDL_DestroyCond(DecoderUnlockedCond); - SDL_DestroyCond(DecoderResumeCond); + SDL_DestroyCond(fDecoderUnlockedCond); + SDL_DestroyCond(fDecoderResumeCond); - FreeAlignedMem(AudioBuffer); + FreeAlignedMem(fAudioBuffer); inherited; end; @@ -287,20 +287,20 @@ begin Exit; end; - Self.Filename := Filename; + Self.fFilename := Filename; // use custom 'ufile' protocol for UTF-8 support - if (av_open_input_file(FormatCtx, PAnsiChar('ufile:'+FileName.ToUTF8), nil, 0, nil) <> 0) then + if (av_open_input_file(fFormatCtx, PAnsiChar('ufile:'+FileName.ToUTF8), nil, 0, nil) <> 0) then begin Log.LogError('av_open_input_file failed: "' + Filename.ToNative + '"', 'UAudio_FFmpeg'); Exit; end; // generate PTS values if they do not exist - FormatCtx^.flags := FormatCtx^.flags or AVFMT_FLAG_GENPTS; + fFormatCtx^.flags := fFormatCtx^.flags or AVFMT_FLAG_GENPTS; // retrieve stream information - if (av_find_stream_info(FormatCtx) < 0) then + if (av_find_stream_info(fFormatCtx) < 0) then begin Log.LogError('av_find_stream_info failed: "' + Filename.ToNative + '"', 'UAudio_FFmpeg'); Close(); @@ -308,14 +308,14 @@ begin end; // FIXME: hack used by ffplay. Maybe should not use url_feof() to test for the end - FormatCtx^.pb.eof_reached := 0; + fFormatCtx^.pb.eof_reached := 0; {$IFDEF DebugFFmpegDecode} - dump_format(FormatCtx, 0, PAnsiChar(Filename.ToNative), 0); + dump_format(fFormatCtx, 0, PAnsiChar(Filename.ToNative), 0); {$ENDIF} - AudioStreamIndex := FFmpegCore.FindAudioStreamIndex(FormatCtx); - if (AudioStreamIndex < 0) then + fAudioStreamIndex := FFmpegCore.FindAudioStreamIndex(fFormatCtx); + if (fAudioStreamIndex < 0) then begin Log.LogError('FindAudioStreamIndex: No Audio-stream found "' + Filename.ToNative + '"', 'UAudio_FFmpeg'); Close(); @@ -324,9 +324,9 @@ begin //Log.LogStatus('AudioStreamIndex is: '+ inttostr(ffmpegStreamID), 'UAudio_FFmpeg'); - AudioStream := FormatCtx.streams[AudioStreamIndex]; - AudioStreamPos := 0; - CodecCtx := AudioStream^.codec; + fAudioStream := fFormatCtx.streams[fAudioStreamIndex]; + fAudioStreamPos := 0; + fCodecCtx := fAudioStream^.codec; // TODO: should we use this or not? Should we allow 5.1 channel audio? (* @@ -338,21 +338,21 @@ begin {$IFEND} *) - Codec := avcodec_find_decoder(CodecCtx^.codec_id); - if (Codec = nil) then + fCodec := avcodec_find_decoder(fCodecCtx^.codec_id); + if (fCodec = nil) then begin Log.LogError('Unsupported codec!', 'UAudio_FFmpeg'); - CodecCtx := nil; + fCodecCtx := nil; Close(); Exit; end; // set debug options - CodecCtx^.debug_mv := 0; - CodecCtx^.debug := 0; + fCodecCtx^.debug_mv := 0; + fCodecCtx^.debug := 0; // detect bug-workarounds automatically - CodecCtx^.workaround_bugs := FF_BUG_AUTODETECT; + fCodecCtx^.workaround_bugs := FF_BUG_AUTODETECT; // error resilience strategy (careful/compliant/agressive/very_aggressive) //CodecCtx^.error_resilience := FF_ER_CAREFUL; //FF_ER_COMPLIANT; // allow non spec compliant speedup tricks. @@ -362,7 +362,7 @@ begin // fail if called concurrently by different threads. FFmpegCore.LockAVCodec(); try - AVResult := avcodec_open(CodecCtx, Codec); + AVResult := avcodec_open(fCodecCtx, fCodec); finally FFmpegCore.UnlockAVCodec(); end; @@ -375,23 +375,23 @@ begin // now initialize the audio-format - if (not FFmpegCore.ConvertFFmpegToAudioFormat(CodecCtx^.sample_fmt, SampleFormat)) then + if (not FFmpegCore.ConvertFFmpegToAudioFormat(fCodecCtx^.sample_fmt, SampleFormat)) then begin // try standard format SampleFormat := asfS16; end; - if CodecCtx^.channels > 255 then + if fCodecCtx^.channels > 255 then Log.LogStatus('Error: CodecCtx^.channels > 255', 'TFFmpegDecodeStream.Open'); - FormatInfo := TAudioFormatInfo.Create( - byte(CodecCtx^.channels), - CodecCtx^.sample_rate, + fFormatInfo := TAudioFormatInfo.Create( + byte(fCodecCtx^.channels), + fCodecCtx^.sample_rate, SampleFormat ); - PacketQueue := TPacketQueue.Create(); + fPacketQueue := TPacketQueue.Create(); // finally start the decode thread - ParseThread := SDL_CreateThread(@ParseThreadMain, Self); + fParseThread := SDL_CreateThread(@ParseThreadMain, Self); Result := true; end; @@ -403,47 +403,47 @@ begin // wake threads waiting for packet-queue data // Note: normally, there are no waiting threads. If there were waiting // ones, they would block the audio-callback thread. - if (assigned(PacketQueue)) then - PacketQueue.Abort(); + if (assigned(fPacketQueue)) then + fPacketQueue.Abort(); // send quit request (to parse-thread etc) - SDL_mutexP(StateLock); - QuitRequest := true; - SDL_CondBroadcast(ParserIdleCond); - SDL_mutexV(StateLock); + SDL_mutexP(fStateLock); + fQuitRequest := true; + SDL_CondBroadcast(fParserIdleCond); + SDL_mutexV(fStateLock); // abort parse-thread - if (ParseThread <> nil) then + if (fParseThread <> nil) then begin // and wait until it terminates - SDL_WaitThread(ParseThread, ThreadResult); - ParseThread := nil; + SDL_WaitThread(fParseThread, ThreadResult); + fParseThread := nil; end; // Close the codec - if (CodecCtx <> nil) then + if (fCodecCtx <> nil) then begin // avcodec_close() is not thread-safe FFmpegCore.LockAVCodec(); try - avcodec_close(CodecCtx); + avcodec_close(fCodecCtx); finally FFmpegCore.UnlockAVCodec(); end; - CodecCtx := nil; + fCodecCtx := nil; end; // Close the video file - if (FormatCtx <> nil) then + if (fFormatCtx <> nil) then begin - av_close_input_file(FormatCtx); - FormatCtx := nil; + av_close_input_file(fFormatCtx); + fFormatCtx := nil; end; PerformOnClose(); - FreeAndNil(PacketQueue); - FreeAndNil(FormatInfo); + FreeAndNil(fPacketQueue); + FreeAndNil(fFormatInfo); end; function TFFmpegDecodeStream.GetLength(): real; @@ -451,54 +451,54 @@ begin // do not forget to consider the start_time value here // there is a type size mismatch warnign because start_time and duration are cint64. // So, in principle there could be an overflow when doing the sum. - Result := (FormatCtx^.start_time + FormatCtx^.duration) / AV_TIME_BASE; + Result := (fFormatCtx^.start_time + fFormatCtx^.duration) / AV_TIME_BASE; end; function TFFmpegDecodeStream.GetAudioFormatInfo(): TAudioFormatInfo; begin - Result := FormatInfo; + Result := fFormatInfo; end; function TFFmpegDecodeStream.IsEOF(): boolean; begin - SDL_mutexP(StateLock); - Result := EOFState; - SDL_mutexV(StateLock); + SDL_mutexP(fStateLock); + Result := fEOFState; + SDL_mutexV(fStateLock); end; procedure TFFmpegDecodeStream.SetEOF(State: boolean); begin - SDL_mutexP(StateLock); - EOFState := State; - SDL_mutexV(StateLock); + SDL_mutexP(fStateLock); + fEOFState := State; + SDL_mutexV(fStateLock); end; function TFFmpegDecodeStream.IsError(): boolean; begin - SDL_mutexP(StateLock); - Result := ErrorState; - SDL_mutexV(StateLock); + SDL_mutexP(fStateLock); + Result := fErrorState; + SDL_mutexV(fStateLock); end; procedure TFFmpegDecodeStream.SetError(State: boolean); begin - SDL_mutexP(StateLock); - ErrorState := State; - SDL_mutexV(StateLock); + SDL_mutexP(fStateLock); + fErrorState := State; + SDL_mutexV(fStateLock); end; function TFFmpegDecodeStream.IsSeeking(): boolean; begin - SDL_mutexP(StateLock); - Result := SeekRequest; - SDL_mutexV(StateLock); + SDL_mutexP(fStateLock); + Result := fSeekRequest; + SDL_mutexV(fStateLock); end; function TFFmpegDecodeStream.IsQuit(): boolean; begin - SDL_mutexP(StateLock); - Result := QuitRequest; - SDL_mutexV(StateLock); + SDL_mutexP(fStateLock); + Result := fQuitRequest; + SDL_mutexV(fStateLock); end; function TFFmpegDecodeStream.GetPosition(): real; @@ -509,11 +509,11 @@ begin // ReadData() does not return all of the buffer retrieved by DecodeFrame(). // Determine the size of the unused part of the decode-buffer. - BufferSizeSec := (AudioBufferSize - AudioBufferPos) / - FormatInfo.BytesPerSec; + BufferSizeSec := (fAudioBufferSize - fAudioBufferPos) / + fFormatInfo.BytesPerSec; // subtract the size of unused buffer-data from the audio clock. - Result := AudioStreamPos - BufferSizeSec; + Result := fAudioStreamPos - BufferSizeSec; ResumeDecoder(); end; @@ -525,16 +525,16 @@ end; function TFFmpegDecodeStream.GetLoop(): boolean; begin - SDL_mutexP(StateLock); - Result := Loop; - SDL_mutexV(StateLock); + SDL_mutexP(fStateLock); + Result := fLoop; + SDL_mutexV(fStateLock); end; procedure TFFmpegDecodeStream.SetLoop(Enabled: boolean); begin - SDL_mutexP(StateLock); - Loop := Enabled; - SDL_mutexV(StateLock); + SDL_mutexP(fStateLock); + fLoop := Enabled; + SDL_mutexV(fStateLock); end; @@ -544,25 +544,25 @@ end; procedure TFFmpegDecodeStream.PauseParser(); begin - if (SDL_ThreadID() = ParseThread.threadid) then + if (SDL_ThreadID() = fParseThread.threadid) then Exit; - SDL_mutexP(StateLock); - Inc(ParserPauseRequestCount); - while (ParserLocked) do - SDL_CondWait(ParserUnlockedCond, StateLock); - SDL_mutexV(StateLock); + SDL_mutexP(fStateLock); + Inc(fParserPauseRequestCount); + while (fParserLocked) do + SDL_CondWait(fParserUnlockedCond, fStateLock); + SDL_mutexV(fStateLock); end; procedure TFFmpegDecodeStream.ResumeParser(); begin - if (SDL_ThreadID() = ParseThread.threadid) then + if (SDL_ThreadID() = fParseThread.threadid) then Exit; - SDL_mutexP(StateLock); - Dec(ParserPauseRequestCount); - SDL_CondSignal(ParserResumeCond); - SDL_mutexV(StateLock); + SDL_mutexP(fStateLock); + Dec(fParserPauseRequestCount); + SDL_CondSignal(fParserResumeCond); + SDL_mutexV(fStateLock); end; procedure TFFmpegDecodeStream.SetPositionIntern(Time: real; Flush: boolean; Blocking: boolean); @@ -575,36 +575,36 @@ begin // - Last lock the state lock because we are manipulating some shared state-vars. PauseParser(); PauseDecoder(); - SDL_mutexP(StateLock); + SDL_mutexP(fStateLock); try - EOFState := false; - ErrorState := false; + fEOFState := false; + fErrorState := false; // do not seek if we are already at the correct position. // This is important especially for seeking to position 0 if we already are // at the beginning. Although seeking with AVSEEK_FLAG_BACKWARD for pos 0 works, // it is still a bit choppy (although much better than w/o AVSEEK_FLAG_BACKWARD). - if (Time = AudioStreamPos) then + if (Time = fAudioStreamPos) then Exit; // configure seek parameters - SeekPos := Time; - SeekFlush := Flush; - SeekFlags := AVSEEK_FLAG_ANY; - SeekRequest := true; + fSeekPos := Time; + fSeekFlush := Flush; + fSeekFlags := AVSEEK_FLAG_ANY; + fSeekRequest := true; // Note: the BACKWARD-flag seeks to the first position <= the position // searched for. Otherwise e.g. position 0 might not be seeked correct. // For some reason ffmpeg sometimes doesn't use position 0 but the key-frame // following. In streams with few key-frames (like many flv-files) the next // key-frame after 0 might be 5secs ahead. - if (Time <= AudioStreamPos) then - SeekFlags := SeekFlags or AVSEEK_FLAG_BACKWARD; + if (Time <= fAudioStreamPos) then + fSeekFlags := fSeekFlags or AVSEEK_FLAG_BACKWARD; // send a reuse signal in case the parser was stopped (e.g. because of an EOF) - SDL_CondSignal(ParserIdleCond); + SDL_CondSignal(fParserIdleCond); finally - SDL_mutexV(StateLock); + SDL_mutexV(fStateLock); ResumeDecoder(); ResumeParser(); end; @@ -612,10 +612,10 @@ begin // in blocking mode, wait until seeking is done if (Blocking) then begin - SDL_mutexP(StateLock); - while (SeekRequest) do - SDL_CondWait(SeekFinishedCond, StateLock); - SDL_mutexV(StateLock); + SDL_mutexP(fStateLock); + while (fSeekRequest) do + SDL_CondWait(SeekFinishedCond, fStateLock); + SDL_mutexV(fStateLock); end; end; @@ -635,10 +635,10 @@ begin while (ParseLoop()) do begin // wait for reuse or destruction of stream - SDL_mutexP(StateLock); - while (not (SeekRequest or QuitRequest)) do - SDL_CondWait(ParserIdleCond, StateLock); - SDL_mutexV(StateLock); + SDL_mutexP(fStateLock); + while (not (fSeekRequest or fQuitRequest)) do + SDL_CondWait(fParserIdleCond, fStateLock); + SDL_mutexV(fStateLock); end; end; @@ -669,19 +669,19 @@ var // instead and give priority to the threads requesting the parser to pause. procedure LockParser(); begin - SDL_mutexP(StateLock); - while (ParserPauseRequestCount > 0) do - SDL_CondWait(ParserResumeCond, StateLock); - ParserLocked := true; - SDL_mutexV(StateLock); + SDL_mutexP(fStateLock); + while (fParserPauseRequestCount > 0) do + SDL_CondWait(fParserResumeCond, fStateLock); + fParserLocked := true; + SDL_mutexV(fStateLock); end; procedure UnlockParser(); begin - SDL_mutexP(StateLock); - ParserLocked := false; - SDL_CondBroadcast(ParserUnlockedCond); - SDL_mutexV(StateLock); + SDL_mutexP(fStateLock); + fParserLocked := false; + SDL_CondBroadcast(fParserUnlockedCond); + SDL_mutexV(fStateLock); end; begin @@ -699,92 +699,92 @@ begin end; // handle seek-request (Note: no need to lock SeekRequest here) - if (SeekRequest) then + if (fSeekRequest) then begin // first try: seek on the audio stream - SeekTarget := Round(SeekPos / av_q2d(AudioStream^.time_base)); + SeekTarget := Round(fSeekPos / av_q2d(fAudioStream^.time_base)); StartSilence := 0; - if (SeekTarget < AudioStream^.start_time) then - StartSilence := (AudioStream^.start_time - SeekTarget) * av_q2d(AudioStream^.time_base); - ErrorCode := av_seek_frame(FormatCtx, AudioStreamIndex, SeekTarget, SeekFlags); + if (SeekTarget < fAudioStream^.start_time) then + StartSilence := (fAudioStream^.start_time - SeekTarget) * av_q2d(fAudioStream^.time_base); + ErrorCode := av_seek_frame(fFormatCtx, fAudioStreamIndex, SeekTarget, fSeekFlags); if (ErrorCode < 0) then begin // second try: seek on the default stream (necessary for flv-videos and some ogg-files) - SeekTarget := Round(SeekPos * AV_TIME_BASE); + SeekTarget := Round(fSeekPos * AV_TIME_BASE); StartSilence := 0; - if (SeekTarget < FormatCtx^.start_time) then - StartSilence := (FormatCtx^.start_time - SeekTarget) / AV_TIME_BASE; - ErrorCode := av_seek_frame(FormatCtx, -1, SeekTarget, SeekFlags); + if (SeekTarget < fFormatCtx^.start_time) then + StartSilence := (fFormatCtx^.start_time - SeekTarget) / AV_TIME_BASE; + ErrorCode := av_seek_frame(fFormatCtx, -1, SeekTarget, fSeekFlags); end; // pause decoder and lock state (keep the lock-order to avoid deadlocks). // Note that the decoder does not block in the packet-queue in seeking state, // so locking the decoder here does not cause a dead-lock. PauseDecoder(); - SDL_mutexP(StateLock); + SDL_mutexP(fStateLock); try if (ErrorCode < 0) then begin // seeking failed - ErrorState := true; - Log.LogStatus('Seek Error in "'+FormatCtx^.filename+'"', 'UAudioDecoder_FFmpeg'); + fErrorState := true; + Log.LogError('Seek Error in "'+fFormatCtx^.filename+'"', 'UAudioDecoder_FFmpeg'); end else begin - if (SeekFlush) then + if (fSeekFlush) then begin // flush queue (we will send a Flush-Packet when seeking is finished) - PacketQueue.Flush(); + fPacketQueue.Flush(); // flush the decode buffers - AudioBufferSize := 0; - AudioBufferPos := 0; - AudioPaketSize := 0; - AudioPaketSilence := 0; + fAudioBufferSize := 0; + fAudioBufferPos := 0; + fAudioPaketSize := 0; + fAudioPaketSilence := 0; FlushCodecBuffers(); // Set preliminary stream position. The position will be set to // the correct value as soon as the first packet is decoded. - AudioStreamPos := SeekPos; + fAudioStreamPos := fSeekPos; end else begin // request avcodec buffer flush - PacketQueue.PutStatus(PKT_STATUS_FLAG_FLUSH, nil); + fPacketQueue.PutStatus(PKT_STATUS_FLAG_FLUSH, nil); end; // fill the gap between position 0 and start_time with silence // but not if we are in loop mode - if ((StartSilence > 0) and (not Loop)) then + if ((StartSilence > 0) and (not fLoop)) then begin GetMem(StartSilencePtr, SizeOf(StartSilence)); StartSilencePtr^ := StartSilence; - PacketQueue.PutStatus(PKT_STATUS_FLAG_EMPTY, StartSilencePtr); + fPacketQueue.PutStatus(PKT_STATUS_FLAG_EMPTY, StartSilencePtr); end; end; - SeekRequest := false; + fSeekRequest := false; SDL_CondBroadcast(SeekFinishedCond); finally - SDL_mutexV(StateLock); + SDL_mutexV(fStateLock); ResumeDecoder(); end; end; - if (PacketQueue.GetSize() > MAX_AUDIOQ_SIZE) then + if (fPacketQueue.GetSize() > MAX_AUDIOQ_SIZE) then begin SDL_Delay(10); Continue; end; - if (av_read_frame(FormatCtx, Packet) < 0) then + if (av_read_frame(fFormatCtx, Packet) < 0) then begin // failed to read a frame, check reason {$IF (LIBAVFORMAT_VERSION_MAJOR >= 52)} - ByteIOCtx := FormatCtx^.pb; + ByteIOCtx := fFormatCtx^.pb; {$ELSE} - ByteIOCtx := @FormatCtx^.pb; + ByteIOCtx := @fFormatCtx^.pb; {$IFEND} // check for end-of-file (eof is not an error) @@ -799,7 +799,7 @@ begin else begin // signal end-of-file - PacketQueue.PutStatus(PKT_STATUS_FLAG_EOF, nil); + fPacketQueue.PutStatus(PKT_STATUS_FLAG_EOF, nil); Exit; end; end; @@ -808,17 +808,26 @@ begin if (url_ferror(ByteIOCtx) <> 0) then begin // an error occured -> abort and wait for repositioning or termination - PacketQueue.PutStatus(PKT_STATUS_FLAG_ERROR, nil); + fPacketQueue.PutStatus(PKT_STATUS_FLAG_ERROR, nil); Exit; end; - // no error -> wait for user input - SDL_Delay(100); - Continue; + // url_feof() does not detect an EOF for some files + // so we have to do it this way. + if ((fFormatCtx^.file_size <> 0) and + (ByteIOCtx^.pos >= fFormatCtx^.file_size)) then + begin + fPacketQueue.PutStatus(PKT_STATUS_FLAG_EOF, nil); + Exit; + end; + + // unknown error occured, exit + fPacketQueue.PutStatus(PKT_STATUS_FLAG_ERROR, nil); + Exit; end; - if (Packet.stream_index = AudioStreamIndex) then - PacketQueue.Put(@Packet) + if (Packet.stream_index = fAudioStreamIndex) then + fPacketQueue.Put(@Packet) else av_free_packet(@Packet); @@ -835,28 +844,28 @@ end; procedure TFFmpegDecodeStream.PauseDecoder(); begin - SDL_mutexP(StateLock); - Inc(DecoderPauseRequestCount); - while (DecoderLocked) do - SDL_CondWait(DecoderUnlockedCond, StateLock); - SDL_mutexV(StateLock); + SDL_mutexP(fStateLock); + Inc(fDecoderPauseRequestCount); + while (fDecoderLocked) do + SDL_CondWait(fDecoderUnlockedCond, fStateLock); + SDL_mutexV(fStateLock); end; procedure TFFmpegDecodeStream.ResumeDecoder(); begin - SDL_mutexP(StateLock); - Dec(DecoderPauseRequestCount); - SDL_CondSignal(DecoderResumeCond); - SDL_mutexV(StateLock); + SDL_mutexP(fStateLock); + Dec(fDecoderPauseRequestCount); + SDL_CondSignal(fDecoderResumeCond); + SDL_mutexV(fStateLock); end; procedure TFFmpegDecodeStream.FlushCodecBuffers(); begin // if no flush operation is specified, avcodec_flush_buffers will not do anything. - if (@CodecCtx.codec.flush <> nil) then + if (@fCodecCtx.codec.flush <> nil) then begin // flush buffers used by avcodec_decode_audio, etc. - avcodec_flush_buffers(CodecCtx); + avcodec_flush_buffers(fCodecCtx); end else begin @@ -865,8 +874,8 @@ begin // We will just reopen the codec. FFmpegCore.LockAVCodec(); try - avcodec_close(CodecCtx); - avcodec_open(CodecCtx, Codec); + avcodec_close(fCodecCtx); + avcodec_open(fCodecCtx, fCodec); finally FFmpegCore.UnlockAVCodec(); end; @@ -892,27 +901,27 @@ begin begin // for titles with start_time > 0 we have to generate silence // until we reach the pts of the first data packet. - if (AudioPaketSilence > 0) then + if (fAudioPaketSilence > 0) then begin - DataSize := Min(AudioPaketSilence, BufferSize); + DataSize := Min(fAudioPaketSilence, BufferSize); FillChar(Buffer[0], DataSize, 0); - Dec(AudioPaketSilence, DataSize); - AudioStreamPos := AudioStreamPos + DataSize / FormatInfo.BytesPerSec; + Dec(fAudioPaketSilence, DataSize); + fAudioStreamPos := fAudioStreamPos + DataSize / fFormatInfo.BytesPerSec; Result := DataSize; Exit; end; // read packet data - while (AudioPaketSize > 0) do + while (fAudioPaketSize > 0) do begin DataSize := BufferSize; {$IF LIBAVCODEC_VERSION >= 51030000} // 51.30.0 - PaketDecodedSize := avcodec_decode_audio2(CodecCtx, PSmallint(Buffer), - DataSize, AudioPaketData, AudioPaketSize); + PaketDecodedSize := avcodec_decode_audio2(fCodecCtx, PSmallint(Buffer), + DataSize, fAudioPaketData, fAudioPaketSize); {$ELSE} - PaketDecodedSize := avcodec_decode_audio(CodecCtx, PSmallint(Buffer), - DataSize, AudioPaketData, AudioPaketSize); + PaketDecodedSize := avcodec_decode_audio(fCodecCtx, PSmallint(Buffer), + DataSize, fAudioPaketData, fAudioPaketSize); {$IFEND} if(PaketDecodedSize < 0) then @@ -921,19 +930,19 @@ begin {$IFDEF DebugFFmpegDecode} DebugWriteln('Skip audio frame'); {$ENDIF} - AudioPaketSize := 0; + fAudioPaketSize := 0; Break; end; - Inc(AudioPaketData, PaketDecodedSize); - Dec(AudioPaketSize, PaketDecodedSize); + Inc(fAudioPaketData, PaketDecodedSize); + Dec(fAudioPaketSize, PaketDecodedSize); // check if avcodec_decode_audio returned data, otherwise fetch more frames if (DataSize <= 0) then Continue; // update stream position by the amount of fetched data - AudioStreamPos := AudioStreamPos + DataSize / FormatInfo.BytesPerSec; + fAudioStreamPos := fAudioStreamPos + DataSize / fFormatInfo.BytesPerSec; // we have data, return it and come back for more later Result := DataSize; @@ -941,8 +950,8 @@ begin end; // free old packet data - if (AudioPaket.data <> nil) then - av_free_packet(@AudioPaket); + if (fAudioPaket.data <> nil) then + av_free_packet(@fAudioPaket); // do not block queue on seeking (to avoid deadlocks on the DecoderLock) if (IsSeeking()) then @@ -952,17 +961,17 @@ begin // request a new packet and block if none available. // If this fails, the queue was aborted. - if (PacketQueue.Get(AudioPaket, BlockQueue) <= 0) then + if (fPacketQueue.Get(fAudioPaket, BlockQueue) <= 0) then Exit; // handle Status-packet - if (PAnsiChar(AudioPaket.data) = STATUS_PACKET) then + if (PAnsiChar(fAudioPaket.data) = STATUS_PACKET) then begin - AudioPaket.data := nil; - AudioPaketData := nil; - AudioPaketSize := 0; + fAudioPaket.data := nil; + fAudioPaketData := nil; + fAudioPaketSize := 0; - case (AudioPaket.flags) of + case (fAudioPaket.flags) of PKT_STATUS_FLAG_FLUSH: begin // just used if SetPositionIntern was called without the flush flag. @@ -984,9 +993,9 @@ begin end; PKT_STATUS_FLAG_EMPTY: begin - SilenceDuration := PDouble(PacketQueue.GetStatusInfo(AudioPaket))^; - AudioPaketSilence := Round(SilenceDuration * FormatInfo.SampleRate) * FormatInfo.FrameSize; - PacketQueue.FreeStatusInfo(AudioPaket); + SilenceDuration := PDouble(fPacketQueue.GetStatusInfo(fAudioPaket))^; + fAudioPaketSilence := Round(SilenceDuration * fFormatInfo.SampleRate) * fFormatInfo.FrameSize; + fPacketQueue.FreeStatusInfo(fAudioPaket); end else begin @@ -997,20 +1006,20 @@ begin Continue; end; - AudioPaketData := AudioPaket.data; - AudioPaketSize := AudioPaket.size; + fAudioPaketData := fAudioPaket.data; + fAudioPaketSize := fAudioPaket.size; // if available, update the stream position to the presentation time of this package - if(AudioPaket.pts <> AV_NOPTS_VALUE) then + if(fAudioPaket.pts <> AV_NOPTS_VALUE) then begin {$IFDEF DebugFFmpegDecode} - TmpPos := AudioStreamPos; + TmpPos := fAudioStreamPos; {$ENDIF} - AudioStreamPos := av_q2d(AudioStream^.time_base) * AudioPaket.pts; + fAudioStreamPos := av_q2d(fAudioStream^.time_base) * fAudioPaket.pts; {$IFDEF DebugFFmpegDecode} - DebugWriteln('Timestamp: ' + floattostrf(AudioStreamPos, ffFixed, 15, 3) + ' ' + + DebugWriteln('Timestamp: ' + floattostrf(fAudioStreamPos, ffFixed, 15, 3) + ' ' + '(Calc: ' + floattostrf(TmpPos, ffFixed, 15, 3) + '), ' + - 'Diff: ' + floattostrf(AudioStreamPos-TmpPos, ffFixed, 15, 3)); + 'Diff: ' + floattostrf(fAudioStreamPos-TmpPos, ffFixed, 15, 3)); {$ENDIF} end; end; @@ -1025,19 +1034,19 @@ var // prioritize pause requests procedure LockDecoder(); begin - SDL_mutexP(StateLock); - while (DecoderPauseRequestCount > 0) do - SDL_CondWait(DecoderResumeCond, StateLock); - DecoderLocked := true; - SDL_mutexV(StateLock); + SDL_mutexP(fStateLock); + while (fDecoderPauseRequestCount > 0) do + SDL_CondWait(fDecoderResumeCond, fStateLock); + fDecoderLocked := true; + SDL_mutexV(fStateLock); end; procedure UnlockDecoder(); begin - SDL_mutexP(StateLock); - DecoderLocked := false; - SDL_CondBroadcast(DecoderUnlockedCond); - SDL_mutexV(StateLock); + SDL_mutexP(fStateLock); + fDecoderLocked := false; + SDL_CondBroadcast(fDecoderUnlockedCond); + SDL_mutexV(fStateLock); end; begin @@ -1056,15 +1065,15 @@ begin while (BufferPos < BufferSize) do begin // check if we need more data - if (AudioBufferPos >= AudioBufferSize) then + if (fAudioBufferPos >= fAudioBufferSize) then begin - AudioBufferPos := 0; + fAudioBufferPos := 0; // we have already sent all our data; get more - AudioBufferSize := DecodeFrame(AudioBuffer, AUDIO_BUFFER_SIZE); + fAudioBufferSize := DecodeFrame(fAudioBuffer, AUDIO_BUFFER_SIZE); // check for errors or EOF - if(AudioBufferSize < 0) then + if(fAudioBufferSize < 0) then begin Result := BufferPos; Exit; @@ -1072,16 +1081,16 @@ begin end; // calc number of new bytes in the decode-buffer - CopyByteCount := AudioBufferSize - AudioBufferPos; + CopyByteCount := fAudioBufferSize - fAudioBufferPos; // resize copy-count if more bytes available than needed (remaining bytes are used the next time) RemainByteCount := BufferSize - BufferPos; if (CopyByteCount > RemainByteCount) then CopyByteCount := RemainByteCount; - Move(AudioBuffer[AudioBufferPos], Buffer[BufferPos], CopyByteCount); + Move(fAudioBuffer[fAudioBufferPos], Buffer[BufferPos], CopyByteCount); Inc(BufferPos, CopyByteCount); - Inc(AudioBufferPos, CopyByteCount); + Inc(fAudioBufferPos, CopyByteCount); end; finally UnlockDecoder(); diff --git a/us_maker_edition/src/media/UAudioInput_Bass.pas b/us_maker_edition/src/media/UAudioInput_Bass.pas index b8f914c5..0e79b343 100644 --- a/us_maker_edition/src/media/UAudioInput_Bass.pas +++ b/us_maker_edition/src/media/UAudioInput_Bass.pas @@ -471,9 +471,12 @@ begin Flags := BASS_RecordGetInput(SourceIndex, PSingle(nil)^); if (Flags <> -1) then begin - // is the current source a mic-source? - if ((Flags and BASS_INPUT_TYPE_MIC) <> 0) then + // chech if current source is a mic (and none was set before) + if ((Flags and BASS_INPUT_TYPE_MIC) <> 0) and + (BassDevice.MicSource = -1) then + begin BassDevice.MicSource := SourceIndex; + end; end; Inc(SourceIndex); diff --git a/us_maker_edition/src/media/UMediaCore_FFmpeg.pas b/us_maker_edition/src/media/UMediaCore_FFmpeg.pas index 2d572ff2..eb136995 100644 --- a/us_maker_edition/src/media/UMediaCore_FFmpeg.pas +++ b/us_maker_edition/src/media/UMediaCore_FFmpeg.pas @@ -41,6 +41,7 @@ uses avformat, avutil, avio, + swscale, UMusic, ULog, UPath; @@ -121,9 +122,83 @@ const var Instance: TMediaCore_FFmpeg; +function AV_VERSION_INT(a, b, c: cardinal): cuint; +begin + Result := (a shl 16) or (b shl 8) or c; +end; + +procedure CheckVersions(); +var + libVersion: cuint; + headerVersion: cuint; + + function hexVerToStr(Version: cuint): string; + var + Major, Minor, Release: cardinal; + begin + Major := (Version shr 16) and $FF;; + Minor := (Version shr 8) and $FF; + Release := Version and $FF; + Result := Format('%d.%d.%d', [Major, Minor, Release]); + end; + +begin + libVersion := avcodec_version(); + headerVersion := AV_VERSION_INT( + LIBAVCODEC_VERSION_MAJOR, + LIBAVCODEC_VERSION_MINOR, + LIBAVCODEC_VERSION_RELEASE); + if (libVersion <> headerVersion) then + begin + Log.LogError(Format('%s header (%s) and DLL (%s) versions do not match.', + ['libavcodec', hexVerToStr(headerVersion), hexVerToStr(libVersion)])); + end; + + {$IF LIBAVFORMAT_VERSION >= 52020000} // 52.20.0 + libVersion := avformat_version(); + headerVersion := AV_VERSION_INT( + LIBAVFORMAT_VERSION_MAJOR, + LIBAVFORMAT_VERSION_MINOR, + LIBAVFORMAT_VERSION_RELEASE); + if (libVersion <> headerVersion) then + begin + Log.LogError(Format('%s header (%s) and DLL (%s) versions do not match.', + ['libavformat', hexVerToStr(headerVersion), hexVerToStr(libVersion)])); + end; + {$IFEND} + + {$IF LIBAVUTIL_VERSION >= 49008000} // 49.8.0 + libVersion := avutil_version(); + headerVersion := AV_VERSION_INT( + LIBAVUTIL_VERSION_MAJOR, + LIBAVUTIL_VERSION_MINOR, + LIBAVUTIL_VERSION_RELEASE); + if (libVersion <> headerVersion) then + begin + Log.LogError(Format('%s header (%s) and DLL (%s) versions do not match.', + ['libavutil', hexVerToStr(headerVersion), hexVerToStr(libVersion)])); + end; + {$IFEND} + + {$IF LIBSWSCALE_VERSION >= 000006001} // 0.6.1 + libVersion := swscale_version(); + headerVersion := AV_VERSION_INT( + LIBSWSCALE_VERSION_MAJOR, + LIBSWSCALE_VERSION_MINOR, + LIBSWSCALE_VERSION_RELEASE); + if (libVersion <> headerVersion) then + begin + Log.LogError(Format('%s header (%s) and DLL (%s) versions do not match.', + ['libswscale', hexVerToStr(headerVersion), hexVerToStr(libVersion)])); + end; + {$IFEND} +end; + constructor TMediaCore_FFmpeg.Create(); begin inherited; + + CheckVersions(); av_register_protocol(@UTF8FileProtocol); AVCodecLock := SDL_CreateMutex(); end; diff --git a/us_maker_edition/src/media/UMedia_dummy.pas b/us_maker_edition/src/media/UMedia_dummy.pas index 8ebfd3a9..46cbe6b8 100644 --- a/us_maker_edition/src/media/UMedia_dummy.pas +++ b/us_maker_edition/src/media/UMedia_dummy.pas @@ -112,9 +112,43 @@ type procedure SetPosition(Time: real); function GetPosition: real; - procedure GetFrame(Time: Extended); - procedure DrawGL(Screen: integer); + procedure SetScreen(Screen: integer); + function GetScreen(): integer; + + procedure SetScreenPosition(X, Y, Z: double); + procedure GetScreenPosition(var X, Y, Z: double); + + procedure SetWidth(Width: double); + function GetWidth(): double; + + procedure SetHeight(Height: double); + function GetHeight(): double; + + procedure SetFrameRange(Range: TRectCoords); + function GetFrameRange(): TRectCoords; + + function GetFrameAspect(): real; + + procedure SetAspectCorrection(AspectCorrection: TAspectCorrection); + function GetAspectCorrection(): TAspectCorrection; + + procedure SetAlpha(Alpha: double); + function GetAlpha(): double; + + procedure SetReflectionSpacing(Spacing: double); + function GetReflectionSpacing(): double; + procedure GetFrame(Time: Extended); + procedure Draw(); + procedure DrawReflection(); + + property Screen: integer read GetScreen; + property Width: double read GetWidth write SetWidth; + property Height: double read GetHeight write SetWidth; + property Alpha: double read GetAlpha write SetAlpha; + property ReflectionSpacing: double read GetReflectionSpacing write SetReflectionSpacing; + property FrameAspect: real read GetFrameAspect; + property AspectCorrection: TAspectCorrection read GetAspectCorrection; property Loop: boolean read GetLoop write SetLoop; property Position: real read GetPosition write SetPosition; end; @@ -329,11 +363,97 @@ begin Result := 0; end; +procedure TVideo_Dummy.SetScreen(Screen: integer); +begin +end; + +function TVideo_Dummy.GetScreen(): integer; +begin + Result := 0; +end; + +procedure TVideo_Dummy.SetScreenPosition(X, Y, Z: double); +begin +end; + +procedure TVideo_Dummy.GetScreenPosition(var X, Y, Z: double); +begin + X := 0; + Y := 0; + Z := 0; +end; + +procedure TVideo_Dummy.SetWidth(Width: double); +begin +end; + +function TVideo_Dummy.GetWidth(): double; +begin + Result := 0; +end; + +procedure TVideo_Dummy.SetHeight(Height: double); +begin +end; + +function TVideo_Dummy.GetHeight(): double; +begin + Result := 0; +end; + +procedure TVideo_Dummy.SetFrameRange(Range: TRectCoords); +begin +end; + +function TVideo_Dummy.GetFrameRange(): TRectCoords; +begin + Result.Left := 0; + Result.Right := 0; + Result.Upper := 0; + Result.Lower := 0; +end; + +function TVideo_Dummy.GetFrameAspect(): real; +begin + Result := 0; +end; + +procedure TVideo_Dummy.SetAspectCorrection(AspectCorrection: TAspectCorrection); +begin +end; + +function TVideo_Dummy.GetAspectCorrection(): TAspectCorrection; +begin + Result := acoStretch; +end; + +procedure TVideo_Dummy.SetAlpha(Alpha: double); +begin +end; + +function TVideo_Dummy.GetAlpha(): double; +begin + Result := 0; +end; + +procedure TVideo_Dummy.SetReflectionSpacing(Spacing: double); +begin +end; + +function TVideo_Dummy.GetReflectionSpacing(): double; +begin + Result := 0; +end; + procedure TVideo_Dummy.GetFrame(Time: Extended); begin end; -procedure TVideo_Dummy.DrawGL(Screen: integer); +procedure TVideo_Dummy.Draw(); +begin +end; + +procedure TVideo_Dummy.DrawReflection(); begin end; diff --git a/us_maker_edition/src/media/UVideo.pas b/us_maker_edition/src/media/UVideo.pas index c7d59fc8..add7bdc8 100644 --- a/us_maker_edition/src/media/UVideo.pas +++ b/us_maker_edition/src/media/UVideo.pas @@ -48,24 +48,6 @@ interface {$DEFINE PIXEL_FMT_BGR} {$ENDIF} -type - {** - * vacStretch: Stretch to screen width and height - * - ignores aspect - * + no borders - * + no image data loss - * vacCrop: Stretch to screen width or height, crop the other dimension - * + keeps aspect - * + no borders - * - frame borders are cropped (image data loss) - * vacLetterBox: Stretch to screen width, add bars at or crop top and bottom - * + keeps aspect - * - borders at top and bottom - * o top/bottom is cropped if width < height (unusual) - *} - TAspectCorrection = (acoStretch, acoCrop, acoLetterBox); - - implementation uses @@ -112,12 +94,9 @@ const PIXEL_FMT_SIZE = 3; {$ENDIF} -type - TRectCoords = record - Left, Right: double; - Upper, Lower: double; - end; + ReflectionH = 0.5; //reflection height (50%) +type IVideo_FFmpeg = interface (IVideo) ['{E640E130-C8C0-4399-AF02-67A3569313AB}'] function Open(const FileName: IPath): boolean; @@ -149,11 +128,25 @@ type fSwScaleContext: PSwsContext; {$ENDIF} + fScreen: integer; //actual screen to draw on + + fPosX: double; + fPosY: double; + fPosZ: double; + fWidth: double; + fHeight: double; + + fFrameRange: TRectCoords; + + fAlpha: double; + fReflectionSpacing: double; + + fAspect: real; //**< width/height ratio fAspectCorrection: TAspectCorrection; - fTimeBase: extended; //**< FFmpeg time base per time unit - fFrameTime: extended; //**< video time position (absolute) + fFrameDuration: extended; //**< duration of a video frame in seconds (= 1/fps) + fFrameTime: extended; //**< video time position (absolute) fLoopTime: extended; //**< start time of the current loop fPboEnabled: boolean; @@ -163,6 +156,8 @@ type procedure SynchronizeTime(Frame: PAVFrame; var pts: double); procedure GetVideoRect(var ScreenRect, TexRect: TRectCoords); + procedure DrawBorders(ScreenRect: TRectCoords); + procedure DrawBordersReflected(ScreenRect: TRectCoords; AlphaUpper, AlphaLower: double); procedure ShowDebugInfo(); @@ -183,8 +178,39 @@ type procedure SetPosition(Time: real); function GetPosition: real; - procedure GetFrame(Time: Extended); - procedure DrawGL(Screen: integer); + procedure SetScreen(Screen: integer); + function GetScreen(): integer; + + procedure SetScreenPosition(X, Y, Z: double); + procedure GetScreenPosition(var X, Y, Z: double); + + procedure SetWidth(Width: double); + function GetWidth(): double; + + procedure SetHeight(Height: double); + function GetHeight(): double; + + {** + * Sub-image of the video frame to draw. + * This can be used for zooming or similar purposes. + *} + procedure SetFrameRange(Range: TRectCoords); + function GetFrameRange(): TRectCoords; + + function GetFrameAspect(): real; + + procedure SetAspectCorrection(AspectCorrection: TAspectCorrection); + function GetAspectCorrection(): TAspectCorrection; + + procedure SetAlpha(Alpha: double); + function GetAlpha(): double; + + procedure SetReflectionSpacing(Spacing: double); + function GetReflectionSpacing(): double; + + procedure GetFrame(Time: Extended); + procedure Draw(); + procedure DrawReflection(); end; TVideoPlayback_FFmpeg = class( TInterfacedObject, IVideoPlayback ) @@ -406,19 +432,18 @@ begin fAspect := fAspect * fCodecContext^.width / fCodecContext^.height; - fTimeBase := 1/av_q2d(fStream^.r_frame_rate); + fFrameDuration := 1/av_q2d(fStream^.r_frame_rate); - // hack to get reasonable timebase (for divx and others) - if (fTimeBase < 0.02) then // 0.02 <-> 50 fps + // hack to get reasonable framerate (for divx and others) + if (fFrameDuration < 0.02) then // 0.02 <-> 50 fps begin - fTimeBase := av_q2d(fStream^.r_frame_rate); - while (fTimeBase > 50) do - fTimeBase := fTimeBase/10; - fTimeBase := 1/fTimeBase; + fFrameDuration := av_q2d(fStream^.r_frame_rate); + while (fFrameDuration > 50) do + fFrameDuration := fFrameDuration/10; + fFrameDuration := 1/fFrameDuration; end; - Log.LogInfo('VideoTimeBase: ' + floattostr(fTimeBase), 'TVideoPlayback_ffmpeg.Open'); - Log.LogInfo('Framerate: '+inttostr(floor(1/fTimeBase))+'fps', 'TVideoPlayback_ffmpeg.Open'); + Log.LogInfo('Framerate: '+inttostr(floor(1/fFrameDuration))+'fps', 'TVideoPlayback_ffmpeg.Open'); {$IFDEF UseSWScale} // if available get a SWScale-context -> faster than the deprecated img_convert(). @@ -484,7 +509,7 @@ begin fOpened := False; fPaused := False; - fTimeBase := 0; + fFrameDuration := 0; fFrameTime := 0; fStream := nil; fStreamIndex := -1; @@ -498,6 +523,22 @@ begin fPboId := 0; fAspectCorrection := acoCrop; + + fScreen := 1; + + fPosX := 0; + fPosY := 0; + fPosZ := 0; + fWidth := RenderW; + fHeight := RenderH; + + fFrameRange.Left := 0; + fFrameRange.Right := 1; + fFrameRange.Upper := 0; + fFrameRange.Lower := 1; + + fAlpha := 1; + fReflectionSpacing := 0; end; procedure TVideo_FFmpeg.Close; @@ -686,12 +727,6 @@ begin Exit; {* - * TODO: - * Check if it is correct to assume that fTimeBase is the time of one frame? - * The tutorial and FFPlay do not make this assumption. - *} - - {* * Synchronization - begin *} @@ -710,12 +745,12 @@ begin {$IFDEF DebugDisplay} DebugWriteln('Time: '+inttostr(floor(Time*1000)) + sLineBreak + 'VideoTime: '+inttostr(floor(fFrameTime*1000)) + sLineBreak + - 'TimeBase: '+inttostr(floor(fTimeBase*1000)) + sLineBreak + + 'TimeBase: '+inttostr(floor(fFrameDuration*1000)) + sLineBreak + 'TimeDiff: '+inttostr(floor(TimeDifference*1000))); {$endif} // check if time has reached the next frame - if (TimeDiff < fTimeBase) then + if (TimeDiff < fFrameDuration) then begin {$ifdef DebugFrames} // frame delay debug display @@ -726,7 +761,7 @@ begin DebugWriteln('not getting new frame' + sLineBreak + 'Time: '+inttostr(floor(Time*1000)) + sLineBreak + 'VideoTime: '+inttostr(floor(fFrameTime*1000)) + sLineBreak + - 'TimeBase: '+inttostr(floor(fTimeBase*1000)) + sLineBreak + + 'TimeBase: '+inttostr(floor(fFrameDuration*1000)) + sLineBreak + 'TimeDiff: '+inttostr(floor(TimeDifference*1000))); {$endif} @@ -745,9 +780,9 @@ begin // check if we have to skip frames // Either if we are one frame behind or if the skip threshold has been reached. - // Do not skip if the difference is less than fTimeBase as there is no next frame. - // Note: We assume that fTimeBase is the length of one frame. - if (TimeDiff >= Max(fTimeBase, SKIP_FRAME_DIFF)) then + // Do not skip if the difference is less than fFrameDuration as there is no next frame. + // Note: We assume that fFrameDuration is the length of one frame. + if (TimeDiff >= Max(fFrameDuration, SKIP_FRAME_DIFF)) then begin {$IFDEF DebugFrames} //frame drop debug display @@ -755,13 +790,13 @@ begin {$ENDIF} {$IFDEF DebugDisplay} DebugWriteln('skipping frames' + sLineBreak + - 'TimeBase: '+inttostr(floor(fTimeBase*1000)) + sLineBreak + + 'TimeBase: '+inttostr(floor(fFrameDuration*1000)) + sLineBreak + 'TimeDiff: '+inttostr(floor(TimeDifference*1000))); {$endif} // update video-time - DropFrameCount := Trunc(TimeDiff / fTimeBase); - fFrameTime := fFrameTime + DropFrameCount*fTimeBase; + DropFrameCount := Trunc(TimeDiff / fFrameDuration); + fFrameTime := fFrameTime + DropFrameCount*fFrameDuration; // skip frames for i := 1 to DropFrameCount do @@ -889,72 +924,142 @@ procedure TVideo_FFmpeg.GetVideoRect(var ScreenRect, TexRect: TRectCoords); var ScreenAspect: double; // aspect of screen resolution ScaledVideoWidth, ScaledVideoHeight: double; + begin // Three aspects to take into account: // 1. Screen/display resolution (e.g. 1920x1080 -> 16:9) - // 2. Render aspect (fixed to 800x600 -> 4:3) + // 2. Render aspect (fWidth x fHeight -> variable) // 3. Movie aspect (video frame aspect stored in fAspect) - ScreenAspect := ScreenW / ScreenH; + ScreenAspect := fWidth*((ScreenW/Screens)/RenderW)/(fHeight*(ScreenH/RenderH)); case fAspectCorrection of acoStretch: begin - ScaledVideoWidth := RenderW; - ScaledVideoHeight := RenderH; + ScaledVideoWidth := fWidth; + ScaledVideoHeight := fHeight; end; + acoCrop: begin if (ScreenAspect >= fAspect) then begin - ScaledVideoWidth := RenderW; - ScaledVideoHeight := RenderH * ScreenAspect/fAspect; - end - else + ScaledVideoWidth := fWidth; + ScaledVideoHeight := fHeight * ScreenAspect/fAspect; + end else begin - ScaledVideoHeight := RenderH; - ScaledVideoWidth := RenderW * fAspect/ScreenAspect; + ScaledVideoHeight := fHeight; + ScaledVideoWidth := fWidth * fAspect/ScreenAspect; end; end; + acoLetterBox: begin - ScaledVideoWidth := RenderW; - ScaledVideoHeight := RenderH * ScreenAspect/fAspect; - end - else + if (ScreenAspect <= fAspect) then + begin + ScaledVideoWidth := fWidth; + ScaledVideoHeight := fHeight * ScreenAspect/fAspect; + end else + begin + ScaledVideoHeight := fHeight; + ScaledVideoWidth := fWidth * fAspect/ScreenAspect; + end; + end else raise Exception.Create('Unhandled aspect correction!'); end; - // center video - ScreenRect.Left := (RenderW - ScaledVideoWidth) / 2; + //center video + ScreenRect.Left := (fWidth - ScaledVideoWidth) / 2 + fPosX; ScreenRect.Right := ScreenRect.Left + ScaledVideoWidth; - ScreenRect.Upper := (RenderH - ScaledVideoHeight) / 2; + ScreenRect.Upper := (fHeight - ScaledVideoHeight) / 2 + fPosY; ScreenRect.Lower := ScreenRect.Upper + ScaledVideoHeight; // texture contains right/lower (power-of-2) padding. // Determine the texture coords of the video frame. - TexRect.Left := 0; - TexRect.Right := fCodecContext^.width / fTexWidth; - TexRect.Upper := 0; - TexRect.Lower := fCodecContext^.height / fTexHeight; + TexRect.Left := (fCodecContext^.width / fTexWidth) * fFrameRange.Left; + TexRect.Right := (fCodecContext^.width / fTexWidth) * fFrameRange.Right; + TexRect.Upper := (fCodecContext^.height / fTexHeight) * fFrameRange.Upper; + TexRect.Lower := (fCodecContext^.height / fTexHeight) * fFrameRange.Lower; end; -procedure TVideo_FFmpeg.DrawGL(Screen: integer); -var - ScreenRect: TRectCoords; - TexRect: TRectCoords; +procedure TVideo_FFmpeg.DrawBorders(ScreenRect: TRectCoords); + procedure DrawRect(left, right, upper, lower: double); + begin + glColor4f(0, 0, 0, fAlpha); + glBegin(GL_QUADS); + glVertex3f(left, upper, fPosZ); + glVertex3f(right, upper, fPosZ); + glVertex3f(right, lower, fPosZ); + glVertex3f(left, lower, fPosZ); + glEnd; + end; begin - // have a nice black background to draw on - // (even if there were errors opening the vid) - // TODO: Philipp: IMO TVideoPlayback should not clear the screen at - // all, because clearing is already done by the background class - // at this moment. - if (Screen = 1) then + //upper border + if(ScreenRect.Upper > fPosY) then + DrawRect(fPosX, fPosX+fWidth, fPosY, ScreenRect.Upper); + + //lower border + if(ScreenRect.Lower < fPosY+fHeight) then + DrawRect(fPosX, fPosX+fWidth, ScreenRect.Lower, fPosY+fHeight); + + //left border + if(ScreenRect.Left > fPosX) then + DrawRect(fPosX, ScreenRect.Left, fPosY, fPosY+fHeight); + + //right border + if(ScreenRect.Right < fPosX+fWidth) then + DrawRect(ScreenRect.Right, fPosX+fWidth, fPosY, fPosY+fHeight); +end; + +procedure TVideo_FFmpeg.DrawBordersReflected(ScreenRect: TRectCoords; AlphaUpper, AlphaLower: double); +var + rPosUpper, rPosLower: double; + + procedure DrawRect(left, right, upper, lower: double); + var + AlphaTop: double; + AlphaBottom: double; + begin - // It is important that we just clear once before we start - // drawing the first screen otherwise the first screen - // would be cleared by the drawgl called when the second - // screen is drawn - glClearColor(0, 0, 0, 0); - glClear(GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT); + AlphaTop := AlphaUpper+(AlphaLower-AlphaUpper)*(upper-rPosUpper)/(fHeight*ReflectionH); + AlphaBottom := AlphaLower+(AlphaUpper-AlphaLower)*(rPosLower-lower)/(fHeight*ReflectionH); + + glBegin(GL_QUADS); + glColor4f(0, 0, 0, AlphaTop); + glVertex3f(left, upper, fPosZ); + glVertex3f(right, upper, fPosZ); + + glColor4f(0, 0, 0, AlphaBottom); + glVertex3f(right, lower, fPosZ); + glVertex3f(left, lower, fPosZ); + glEnd; end; +begin + rPosUpper := fPosY+fHeight+fReflectionSpacing; + rPosLower := rPosUpper+fHeight*ReflectionH; + + //upper border + if(ScreenRect.Upper > rPosUpper) then + DrawRect(fPosX, fPosX+fWidth, rPosUpper, ScreenRect.Upper); + //lower border + if(ScreenRect.Lower < rPosLower) then + DrawRect(fPosX, fPosX+fWidth, ScreenRect.Lower, rPosLower); + + //left border + if(ScreenRect.Left > fPosX) then + DrawRect(fPosX, ScreenRect.Left, rPosUpper, rPosLower); + + //right border + if(ScreenRect.Right < fPosX+fWidth) then + DrawRect(ScreenRect.Right, fPosX+fWidth, rPosUpper, rPosLower); +end; + + +procedure TVideo_FFmpeg.Draw(); +var + ScreenRect: TRectCoords; + TexRect: TRectCoords; + HeightFactor: double; + WidthFactor: double; + +begin // exit if there's nothing to draw if (not fOpened) then Exit; @@ -966,31 +1071,53 @@ begin // get texture and screen positions GetVideoRect(ScreenRect, TexRect); - // we could use blending for brightness control, but do we need this? - glDisable(GL_BLEND); + WidthFactor := (ScreenW/Screens) / RenderW; + HeightFactor := ScreenH / RenderH; + + glScissor( + round(fPosX*WidthFactor + (ScreenW/Screens)*(fScreen-1)), + round((RenderH-fPosY-fHeight)*HeightFactor), + round(fWidth*WidthFactor), + round(fHeight*HeightFactor) + ); + + glEnable(GL_SCISSOR_TEST); + glEnable(GL_BLEND); + glDepthRange(0, 10); + glDepthFunc(GL_LEQUAL); + glEnable(GL_DEPTH_TEST); glEnable(GL_TEXTURE_2D); glBindTexture(GL_TEXTURE_2D, fFrameTex); - glColor3f(1, 1, 1); + glColor4f(1, 1, 1, fAlpha); glBegin(GL_QUADS); // upper-left coord glTexCoord2f(TexRect.Left, TexRect.Upper); - glVertex2f(ScreenRect.Left, ScreenRect.Upper); + glVertex3f(ScreenRect.Left, ScreenRect.Upper, fPosZ); // lower-left coord glTexCoord2f(TexRect.Left, TexRect.Lower); - glVertex2f(ScreenRect.Left, ScreenRect.Lower); + glVertex3f(ScreenRect.Left, ScreenRect.Lower, fPosZ); // lower-right coord glTexCoord2f(TexRect.Right, TexRect.Lower); - glVertex2f(ScreenRect.Right, ScreenRect.Lower); + glVertex3f(ScreenRect.Right, ScreenRect.Lower, fPosZ); // upper-right coord glTexCoord2f(TexRect.Right, TexRect.Upper); - glVertex2f(ScreenRect.Right, ScreenRect.Upper); + glVertex3f(ScreenRect.Right, ScreenRect.Upper, fPosZ); glEnd; + glDisable(GL_TEXTURE_2D); + glBindTexture(GL_TEXTURE_2D, 0); + + //draw black borders + DrawBorders(ScreenRect); + + glDisable(GL_DEPTH_TEST); + glDisable(GL_BLEND); + glDisable(GL_SCISSOR_TEST); {$IFDEF VideoBenchmark} Log.BenchmarkEnd(15); - Log.LogBenchmark('DrawGL', 15); + Log.LogBenchmark('Draw', 15); {$ENDIF} {$IF Defined(Info) or Defined(DebugFrames)} @@ -998,10 +1125,98 @@ begin {$IFEND} end; +procedure TVideo_FFmpeg.DrawReflection(); +var + ScreenRect: TRectCoords; + TexRect: TRectCoords; + HeightFactor: double; + WidthFactor: double; + + AlphaTop: double; + AlphaBottom: double; + + AlphaUpper: double; + AlphaLower: double; + +begin + // exit if there's nothing to draw + if (not fOpened) then + Exit; + + // get texture and screen positions + GetVideoRect(ScreenRect, TexRect); + + WidthFactor := (ScreenW/Screens) / RenderW; + HeightFactor := ScreenH / RenderH; + + glScissor( + round(fPosX*WidthFactor + (ScreenW/Screens)*(fScreen-1)), + round((RenderH-fPosY-fHeight-fReflectionSpacing-fHeight*ReflectionH)*HeightFactor), + round(fWidth*WidthFactor), + round(fHeight*HeightFactor*ReflectionH) + ); + + glEnable(GL_SCISSOR_TEST); + glEnable(GL_BLEND); + glDepthRange(0, 10); + glDepthFunc(GL_LEQUAL); + glEnable(GL_DEPTH_TEST); + + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glEnable(GL_TEXTURE_2D); + glBindTexture(GL_TEXTURE_2D, fFrameTex); + + //calculate new ScreenRect coordinates for Reflection + ScreenRect.Lower := fPosY + fHeight + fReflectionSpacing + + (ScreenRect.Upper-fPosY) + (ScreenRect.Lower-ScreenRect.Upper)*ReflectionH; + ScreenRect.Upper := fPosY + fHeight + fReflectionSpacing + + (ScreenRect.Upper-fPosY); + + AlphaUpper := fAlpha-0.3; + AlphaLower := 0; + + AlphaTop := AlphaUpper-(AlphaLower-AlphaUpper)* + (ScreenRect.Upper-fPosY-fHeight-fReflectionSpacing)/fHeight; + AlphaBottom := AlphaLower+(AlphaUpper-AlphaLower)* + (fPosY+fHeight+fReflectionSpacing+fHeight*ReflectionH-ScreenRect.Lower)/fHeight; + + glBegin(GL_QUADS); + //Top Left + glColor4f(1, 1, 1, AlphaTop); + glTexCoord2f(TexRect.Left, TexRect.Lower); + glVertex3f(ScreenRect.Left, ScreenRect.Upper, fPosZ); + + //Bottom Left + glColor4f(1, 1, 1, AlphaBottom); + glTexCoord2f(TexRect.Left, (TexRect.Lower-TexRect.Upper)*(1-ReflectionH)); + glVertex3f(ScreenRect.Left, ScreenRect.Lower, fPosZ); + + //Bottom Right + glColor4f(1, 1, 1, AlphaBottom); + glTexCoord2f(TexRect.Right, (TexRect.Lower-TexRect.Upper)*(1-ReflectionH)); + glVertex3f(ScreenRect.Right, ScreenRect.Lower, fPosZ); + + //Top Right + glColor4f(1, 1, 1, AlphaTop); + glTexCoord2f(TexRect.Right, TexRect.Lower); + glVertex3f(ScreenRect.Right, ScreenRect.Upper, fPosZ); + glEnd; + + glDisable(GL_TEXTURE_2D); + glBindTexture(GL_TEXTURE_2D, 0); + + //draw black borders + DrawBordersReflected(ScreenRect, AlphaUpper, AlphaLower); + + glDisable(GL_DEPTH_TEST); + glDisable(GL_BLEND); + glDisable(GL_SCISSOR_TEST); +end; + procedure TVideo_FFmpeg.ShowDebugInfo(); begin {$IFDEF Info} - if (fFrameTime+fTimeBase < 0) then + if (fFrameTime+fFrameDuration < 0) then begin glColor4f(0.7, 1, 0.3, 1); SetFontStyle (1); @@ -1093,7 +1308,10 @@ begin fEOF := false; fFrameTexValid := false; - if (av_seek_frame(fFormatContext, fStreamIndex, Floor(Time/fTimeBase), SeekFlags) < 0) then + if (av_seek_frame(fFormatContext, + fStreamIndex, + Round(Time / av_q2d(fStream^.time_base)), + SeekFlags) < 0) then begin Log.LogError('av_seek_frame() failed', 'TVideoPlayback_ffmpeg.SetPosition'); Exit; @@ -1107,6 +1325,111 @@ begin Result := fFrameTime; end; +procedure TVideo_FFmpeg.SetScreen(Screen: integer); +begin + fScreen := Screen; +end; + +function TVideo_FFmpeg.GetScreen(): integer; +begin + Result := fScreen; +end; + + +procedure TVideo_FFmpeg.SetScreenPosition(X, Y, Z: double); +begin + fPosX := X; + fPosY := Y; + fPosZ := Z; +end; + +procedure TVideo_FFmpeg.GetScreenPosition(var X, Y, Z: double); +begin + X := fPosX; + Y := fPosY; + Z := fPosZ; +end; + + +procedure TVideo_FFmpeg.SetWidth(Width: double); +begin + fWidth := Width; +end; + +function TVideo_FFmpeg.GetWidth(): double; +begin + Result := fWidth; +end; + + +procedure TVideo_FFmpeg.SetHeight(Height: double); +begin + fHeight := Height; +end; + +function TVideo_FFmpeg.GetHeight(): double; +begin + Result := fHeight; +end; + + +procedure TVideo_FFmpeg.SetFrameRange(Range: TRectCoords); +begin + fFrameRange := Range; +end; + +function TVideo_FFmpeg.GetFrameRange(): TRectCoords; +begin + Result := fFrameRange; +end; + + +function TVideo_FFmpeg.GetFrameAspect(): real; +begin + Result := fAspect; +end; + + +procedure TVideo_FFmpeg.SetAspectCorrection(AspectCorrection: TAspectCorrection); +begin + fAspectCorrection := AspectCorrection; +end; + +function TVideo_FFmpeg.GetAspectCorrection(): TAspectCorrection; +begin + Result := fAspectCorrection; +end; + + + +procedure TVideo_FFmpeg.SetAlpha(Alpha: double); +begin + fAlpha := Alpha; + + if (fAlpha>1) then + fAlpha := 1; + + if (fAlpha<0) then + fAlpha := 0; +end; + +function TVideo_FFmpeg.GetAlpha(): double; +begin + Result := fAlpha; +end; + + +procedure TVideo_FFmpeg.SetReflectionSpacing(Spacing: double); +begin + fReflectionSpacing := Spacing; +end; + +function TVideo_FFmpeg.GetReflectionSpacing(): double; +begin + Result := fReflectionSpacing; +end; + + initialization MediaManager.Add(TVideoPlayback_FFmpeg.Create); diff --git a/us_maker_edition/src/media/UVisualizer.pas b/us_maker_edition/src/media/UVisualizer.pas index 4f553521..1cdc3500 100644 --- a/us_maker_edition/src/media/UVisualizer.pas +++ b/us_maker_edition/src/media/UVisualizer.pas @@ -110,6 +110,8 @@ type fState: TProjectMState; + fScreen: integer; + fVisualTex: GLuint; fPCMData: TPCMData; fRndPCMcount: integer; @@ -144,8 +146,35 @@ type procedure SetLoop(Enable: boolean); function GetLoop(): boolean; + procedure SetScreen(Screen: integer); + function GetScreen(): integer; + + procedure SetScreenPosition(X, Y, Z: double); + procedure GetScreenPosition(var X, Y, Z: double); + + procedure SetWidth(Width: double); + function GetWidth(): double; + + procedure SetHeight(Height: double); + function GetHeight(): double; + + procedure SetFrameRange(Range: TRectCoords); + function GetFrameRange(): TRectCoords; + + function GetFrameAspect(): real; + + procedure SetAspectCorrection(AspectCorrection: TAspectCorrection); + function GetAspectCorrection(): TAspectCorrection; + + procedure SetAlpha(Alpha: double); + function GetAlpha(): double; + + procedure SetReflectionSpacing(Spacing: double); + function GetReflectionSpacing(): double; + procedure GetFrame(Time: Extended); - procedure DrawGL(Screen: integer); + procedure Draw(); + procedure DrawReflection(); end; TVideoPlayback_ProjectM = class( TInterfacedObject, IVideoVisualization ) @@ -262,6 +291,88 @@ begin Result := true; end; +procedure TVideo_ProjectM.SetScreen(Screen: integer); +begin +end; + +function TVideo_ProjectM.GetScreen(): integer; +begin + Result := 0; +end; + +procedure TVideo_ProjectM.SetScreenPosition(X, Y, Z: double); +begin +end; + +procedure TVideo_ProjectM.GetScreenPosition(var X, Y, Z: double); +begin + X := 0; + Y := 0; + Z := 0; +end; + +procedure TVideo_ProjectM.SetWidth(Width: double); +begin +end; + +function TVideo_ProjectM.GetWidth(): double; +begin + Result := 0; +end; + +procedure TVideo_ProjectM.SetHeight(Height: double); +begin +end; + +function TVideo_ProjectM.GetHeight(): double; +begin + Result := 0; +end; + +procedure TVideo_ProjectM.SetFrameRange(Range: TRectCoords); +begin +end; + +function TVideo_ProjectM.GetFrameRange(): TRectCoords; +begin + Result.Left := 0; + Result.Right := 0; + Result.Upper := 0; + Result.Lower := 0; +end; + +function TVideo_ProjectM.GetFrameAspect(): real; +begin + Result := 0; +end; + +procedure TVideo_ProjectM.SetAspectCorrection(AspectCorrection: TAspectCorrection); +begin +end; + +function TVideo_ProjectM.GetAspectCorrection(): TAspectCorrection; +begin + Result := acoStretch; +end; + +procedure TVideo_ProjectM.SetAlpha(Alpha: double); +begin +end; + +function TVideo_ProjectM.GetAlpha(): double; +begin + Result := 1; +end; + +procedure TVideo_ProjectM.SetReflectionSpacing(Spacing: double); +begin +end; + +function TVideo_ProjectM.GetReflectionSpacing(): double; +begin + Result := 0; +end; + {** * Returns the stack depth of the given OpenGL matrix mode stack. *} @@ -485,11 +596,11 @@ end; * Draws the current frame to screen. * TODO: this is not used yet. Data is directly drawn on GetFrame(). *} -procedure TVideo_ProjectM.DrawGL(Screen: integer); +procedure TVideo_ProjectM.Draw(); begin {$IFDEF UseTexture} // have a nice black background to draw on - if (Screen = 1) then + if (fScreen = 1) then begin glClearColor(0, 0, 0, 0); glClear(GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT); @@ -521,10 +632,10 @@ begin // draw projectM frame // Screen is 1 to 2. So current screen is from (Screen - 1) to (Screen) glBegin(GL_QUADS); - glTexCoord2f(0, 0); glVertex2f((Screen - 1), 0); - glTexCoord2f(1, 0); glVertex2f(Screen, 0); - glTexCoord2f(1, 1); glVertex2f(Screen, 1); - glTexCoord2f(0, 1); glVertex2f((Screen - 1), 1); + glTexCoord2f(0, 0); glVertex2f((fScreen - 1), 0); + glTexCoord2f(1, 0); glVertex2f(fScreen, 0); + glTexCoord2f(1, 1); glVertex2f(fScreen, 1); + glTexCoord2f(0, 1); glVertex2f((fScreen - 1), 1); glEnd(); glDisable(GL_TEXTURE_2D); @@ -538,6 +649,10 @@ begin {$ENDIF} end; +procedure TVideo_ProjectM.DrawReflection(); +begin +end; + {** * Produces random "sound"-data in case no audio-data is available. * Otherwise the visualization will look rather boring. diff --git a/us_maker_edition/src/menu/UDisplay.pas b/us_maker_edition/src/menu/UDisplay.pas index e3ec272a..6ec8e2ed 100644 --- a/us_maker_edition/src/menu/UDisplay.pas +++ b/us_maker_edition/src/menu/UDisplay.pas @@ -51,12 +51,12 @@ type ePreDraw: THookableEvent; eDraw: THookableEvent; - //fade-to-black-hack + // fade-to-black BlackScreen: boolean; FadeEnabled: boolean; // true if fading is enabled FadeFailed: boolean; // true if fading is possible (enough memory, etc.) - FadeTime: cardinal; // time when fading starts, 0 means that the fade texture must be initialized + FadeStartTime: cardinal; // time when fading starts, 0 means that the fade texture must be initialized DoneOnShow: boolean; // true if passed onShow after fading FadeTex: array[0..1] of GLuint; @@ -87,7 +87,7 @@ type NextScreen: PMenu; CurrentScreen: PMenu; - //popup data + // popup data NextScreenWithCheck: Pmenu; CheckOK: boolean; @@ -130,12 +130,12 @@ var const { constants for screen transition time in milliseconds } - Transition_Fade_Time = 400; + FADE_DURATION = 400; { constants for software cursor effects time in milliseconds } - Cursor_FadeIn_Time = 500; // seconds the fade in effect lasts - Cursor_FadeOut_Time = 2000; // seconds the fade out effect lasts - Cursor_AutoHide_Time = 5000; // seconds until auto fade out starts if there is no mouse movement + CURSOR_FADE_IN_TIME = 500; // seconds the fade in effect lasts + CURSOR_FADE_OUT_TIME = 2000; // seconds the fade out effect lasts + CURSOR_AUTOHIDE_TIME = 5000; // seconds until auto fade out starts if there is no mouse movement implementation @@ -160,14 +160,14 @@ begin ePreDraw := THookableEvent.Create('Display.PreDraw'); eDraw := THookableEvent.Create('Display.Draw'); - //popup hack + // init popup CheckOK := false; NextScreen := nil; NextScreenWithCheck := nil; BlackScreen := false; - // fade mod - FadeTime := 0; + // init fade + FadeStartTime := 0; FadeEnabled := (Ini.ScreenFade = 1); FadeFailed := false; DoneOnShow := false; @@ -175,7 +175,7 @@ begin glGenTextures(2, PGLuint(@FadeTex)); InitFadeTextures(); - //Set LastError for OSD to No Error + // set LastError for OSD to No Error OSD_LastError := 'No Errors'; // software cursor default values @@ -222,11 +222,6 @@ var begin Result := true; - //We don't need this here anymore, - //Because the background care about cleaning the buffers - //glClearColor(1, 1, 1 , 0); - //glClear(GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT); - for S := 1 to Screens do begin ScreenAct := S; @@ -238,8 +233,7 @@ begin glViewPort((S-1) * ScreenW div Screens, 0, ScreenW div Screens, ScreenH); - // popup hack - // check was successful... move on + // popup check was successful... move on if CheckOK then begin if assigned(NextScreenWithCheck) then @@ -260,7 +254,7 @@ begin ePreDraw.CallHookChain(false); CurrentScreen.Draw; - //popup mod + // popup if (ScreenPopupError <> nil) and ScreenPopupError.Visible then ScreenPopupError.Draw else if (ScreenPopupInfo <> nil) and ScreenPopupInfo.Visible then @@ -268,11 +262,11 @@ begin else if (ScreenPopupCheck <> nil) and ScreenPopupCheck.Visible then ScreenPopupCheck.Draw; - // fade mod - FadeTime := 0; + // fade + FadeStartTime := 0; if ((Ini.ScreenFade = 1) and (not FadeFailed)) then FadeEnabled := true - else if (Ini.ScreenFade = 0) then + else FadeEnabled := false; eDraw.CallHookChain(false); @@ -287,8 +281,8 @@ begin if (FadeEnabled and not FadeFailed) then begin - //Create Fading texture if we're just starting - if FadeTime = 0 then + // create fading texture if we're just starting + if FadeStartTime = 0 then begin // draw screen that will be faded ePreDraw.CallHookChain(false); @@ -319,7 +313,6 @@ begin Log.LogError('Fading disabled: ' + gluErrorString(glError), 'TDisplay.Draw'); end; - // blackscreen-hack if not BlackScreen and (S = 1) and not DoneOnShow then begin NextScreen.OnShow; @@ -329,18 +322,9 @@ begin // set fade time once on second screen (or first if screens = 1) if (Screens = 1) or (S = 2) then - FadeTime := SDL_GetTicks; + FadeStartTime := SDL_GetTicks; end; // end texture creation in first fading step - {//do some time-based fading - currentTime := SDL_GetTicks(); - if (currentTime > LastFadeTime+30) and (S = 1) then - begin - FadeState := FadeState + 5; - LastFadeTime := currentTime; - end; } - - // blackscreen-hack if not BlackScreen then begin ePreDraw.CallHookChain(false); @@ -349,15 +333,16 @@ begin end else if ScreenAct = 1 then begin + // draw black screen glClearColor(0, 0, 0, 1); glClear(GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT); end; // and draw old screen over it... slowly fading out - if (FadeTime = 0) then + if (FadeStartTime = 0) then FadeStateSquare := 0 // for first screen if screens = 2 else - FadeStateSquare := sqr((SDL_GetTicks - FadeTime) / Transition_Fade_Time); + FadeStateSquare := sqr((SDL_GetTicks - FadeStartTime) / FADE_DURATION); if (FadeStateSquare < 1) then begin @@ -392,17 +377,19 @@ begin //glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); end; end - - // blackscreen hack + + // there is no need to init next screen if it is a black screen else if not BlackScreen then begin NextScreen.OnShow; end; - if ((FadeTime + Transition_Fade_Time < SDL_GetTicks) or (not FadeEnabled) or FadeFailed) and ((Screens = 1) or (S = 2)) then + if ((FadeStartTime + FADE_DURATION < SDL_GetTicks) or + (not FadeEnabled) or FadeFailed) and + ((Screens = 1) or (S = 2)) then begin // fade out complete... - FadeTime := 0; + FadeStartTime := 0; DoneOnShow := false; CurrentScreen.onHide; CurrentScreen.ShowFinish := false; @@ -421,7 +408,7 @@ begin end; end; // if -// Draw OSD only on first Screen if Debug Mode is enabled + // Draw OSD only on first Screen if Debug Mode is enabled if ((Ini.Debug = 1) or (Params.Debug)) and (S = 1) then DrawDebugInformation; @@ -485,7 +472,7 @@ begin if (not Cursor_Visible) and (Cursor_LastMove <> 0) then begin if Cursor_Fade then // we use a trick here to consider progress of fade out - Cursor_LastMove := Ticks - round(Cursor_FadeIn_Time * (1 - (Ticks - Cursor_LastMove)/Cursor_FadeOut_Time)) + Cursor_LastMove := Ticks - round(CURSOR_FADE_IN_TIME * (1 - (Ticks - Cursor_LastMove)/CURSOR_FADE_OUT_TIME)) else Cursor_LastMove := Ticks; @@ -533,7 +520,7 @@ begin begin // draw software cursor Ticks := SDL_GetTicks; - if (Cursor_Visible) and (Cursor_LastMove + Cursor_AutoHide_Time <= Ticks) then + if (Cursor_Visible) and (Cursor_LastMove + CURSOR_AUTOHIDE_TIME <= Ticks) then begin // start fade out after 5 secs w/o activity Cursor_Visible := false; Cursor_LastMove := Ticks; @@ -545,17 +532,17 @@ begin begin if Cursor_Visible then begin // fade in - if (Cursor_LastMove + Cursor_FadeIn_Time <= Ticks) then + if (Cursor_LastMove + CURSOR_FADE_IN_TIME <= Ticks) then Cursor_Fade := false else - Alpha := sin((Ticks - Cursor_LastMove) * 0.5 * pi / Cursor_FadeIn_Time) * 0.7; + Alpha := sin((Ticks - Cursor_LastMove) * 0.5 * pi / CURSOR_FADE_IN_TIME) * 0.7; end else begin //fade out - if (Cursor_LastMove + Cursor_FadeOut_Time <= Ticks) then + if (Cursor_LastMove + CURSOR_FADE_OUT_TIME <= Ticks) then Cursor_Fade := false else - Alpha := cos((Ticks - Cursor_LastMove) * 0.5 * pi / Cursor_FadeOut_Time) * 0.7; + Alpha := cos((Ticks - Cursor_LastMove) * 0.5 * pi / CURSOR_FADE_OUT_TIME) * 0.7; end; end; diff --git a/us_maker_edition/src/menu/UMenuBackgroundVideo.pas b/us_maker_edition/src/menu/UMenuBackgroundVideo.pas index bfaee702..9a33e721 100644 --- a/us_maker_edition/src/menu/UMenuBackgroundVideo.pas +++ b/us_maker_edition/src/menu/UMenuBackgroundVideo.pas @@ -151,14 +151,14 @@ begin glClear(GL_DEPTH_BUFFER_BIT); // video failure -> draw blank background if (fBgVideo = nil) then - glClear(GL_COLOR_BUFFER_BIT); + glClear(GL_COLOR_BUFFER_BIT); end; if (fBgVideo <> nil) then begin fBgVideo.GetFrame(VideoBGTimer.GetTime()); - // FIXME: why do we draw on screen 2? Seems to be wrong. - fBgVideo.DrawGL(2); + fBgVideo.SetScreen(ScreenAct); + fBgVideo.Draw(); end; end; diff --git a/us_maker_edition/src/screens/UScreenEditSub.pas b/us_maker_edition/src/screens/UScreenEditSub.pas index 400053c0..51e1df42 100644 --- a/us_maker_edition/src/screens/UScreenEditSub.pas +++ b/us_maker_edition/src/screens/UScreenEditSub.pas @@ -52,6 +52,7 @@ uses gl, {$IFDEF UseMIDIPort} MidiOut, + MidiCons, {$ENDIF} UThemes, UPath; @@ -485,7 +486,11 @@ begin Lines[0].Line[Lines[0].Current].Note[CurrentNote].NoteType := ntNormal else Lines[0].Line[Lines[0].Current].Note[CurrentNote].NoteType := ntFreestyle; - GoldenRec.KillAll; + GoldenRec.KillAll; + + // update lyrics + Lyric.AddLine(Lines[0].Current); + Lyric.Selected := CurrentNote; Exit; end; SDLK_Z: @@ -667,18 +672,35 @@ begin SDLK_SPACE: begin - // Play Sentence - PlaySentenceMidi := false; // stop midi - PlaySentence := true; - Click := false; - AudioPlayback.Stop; - AudioPlayback.Position := GetTimeFromBeat(Lines[0].Line[Lines[0].Current].Note[CurrentNote].Start); - PlayStopTime := (GetTimeFromBeat( - Lines[0].Line[Lines[0].Current].Note[CurrentNote].Start + - Lines[0].Line[Lines[0].Current].Note[CurrentNote].Length)); - AudioPlayback.SetVolume(SelectsS[VolumeAudioSlideId].SelectedOption / 100); - AudioPlayback.Play; - LastClick := -100; + if (SDL_ModState = 0) or (SDL_ModState = KMOD_LSHIFT or KMOD_LCTRL) then + begin + // Play Sentence + PlaySentenceMidi := false; // stop midi + PlaySentence := true; + Click := false; + AudioPlayback.Stop; + AudioPlayback.Position := GetTimeFromBeat(Lines[0].Line[Lines[0].Current].Note[CurrentNote].Start); + PlayStopTime := (GetTimeFromBeat( + Lines[0].Line[Lines[0].Current].Note[CurrentNote].Start + + Lines[0].Line[Lines[0].Current].Note[CurrentNote].Length)); + AudioPlayback.SetVolume(SelectsS[VolumeAudioSlideId].SelectedOption / 100); + AudioPlayback.Play; + LastClick := -100; + end; + + if (SDL_ModState = KMOD_LSHIFT) or (SDL_ModState = KMOD_LSHIFT or KMOD_LCTRL) then + begin + // Play Midi + PlaySentenceMidi := true; + + MidiTime := USTime.GetTime; + MidiStart := GetTimeFromBeat(Lines[0].Line[Lines[0].Current].Note[CurrentNote].Start); + MidiStop := GetTimeFromBeat( + Lines[0].Line[Lines[0].Current].Note[CurrentNote].Start + + Lines[0].Line[Lines[0].Current].Note[CurrentNote].Length); + + LastClick := -100; + end; end; SDLK_RETURN: @@ -718,7 +740,7 @@ begin begin if SDL_ModState = KMOD_LCTRL then begin - // moves text to right in current sentence + // deletes current note CopyToUndo; DeleteNote; GoldenRec.KillAll; @@ -878,7 +900,7 @@ begin begin {$IFDEF UseMIDIPort} MidiOut.PutShort($B1, $7, floor(1.27*SelectsS[VolumeMidiSlideId].SelectedOption)); - MidiOut.PutShort($81, Lines[0].Line[Lines[0].Current].Note[MidiLastNote].Tone + 60, 127); + MidiOut.PutShort(MIDI_NOTEOFF or 1, Lines[0].Line[Lines[0].Current].Note[MidiLastNote].Tone + 60, 127); PlaySentenceMidi := false; {$ENDIF} @@ -915,7 +937,7 @@ begin AudioPlayback.Stop; PlaySentence := false; {$IFDEF UseMIDIPort} - MidiOut.PutShort($B1, $7, floor(1.27*SelectsS[VolumeMidiSlideId].SelectedOption)); + MidiOut.PutShort(MIDI_NOTEOFF or 1, $7, floor(1.27*SelectsS[VolumeMidiSlideId].SelectedOption)); MidiOut.PutShort($81, Lines[0].Line[Lines[0].Current].Note[MidiLastNote].Tone + 60, 127); PlaySentenceMidi := false; {$endif} @@ -1445,13 +1467,21 @@ begin Note[N] := Note[N-1]; end; - // me slightly modify new note - Note[CurrentNote].Length := 1; - Inc(Note[CurrentNote+1].Start); - Dec(Note[CurrentNote+1].Length); - Note[CurrentNote+1].Text := '- '; + // Note[Cur] and Note[Cur + 1] is identical at this point + // modify first note + Note[CurrentNote].Length := Note[CurrentNote+1].Length div 2 + Note[CurrentNote+1].Length mod 2; + + // 2nd note + Note[CurrentNote+1].Start := Note[CurrentNote].Start + Note[CurrentNote].Length; + Note[CurrentNote+1].Length := Note[CurrentNote + 1].Length div 2; + + Note[CurrentNote+1].Text := '~'; Note[CurrentNote+1].Color := 1; end; + + // update lyric display + Lyric.AddLine(Lines[0].Current); + Lyric.Selected := CurrentNote; end; procedure TScreenEditSub.DeleteNote; @@ -1462,9 +1492,8 @@ begin C := Lines[0].Current; //Do Not delete Last Note - if (Lines[0].High > 0) or (Lines[0].Line[C].HighNote > 0) then + if (Lines[0].Line[C].HighNote > 0) then begin - // we copy all notes from the next to the selected one for N := CurrentNote+1 to Lines[0].Line[C].HighNote do begin @@ -1472,37 +1501,47 @@ begin end; Dec(Lines[0].Line[C].HighNote); - if (Lines[0].Line[C].HighNote >= 0) then - begin - SetLength(Lines[0].Line[C].Note, Lines[0].Line[C].HighNote + 1); - // me slightly modify new note - if CurrentNote > Lines[0].Line[C].HighNote then - Dec(CurrentNote); - - Lines[0].Line[C].Note[CurrentNote].Color := 2; - end - //Last Note of current Sentence Deleted - > Delete Sentence - else + SetLength(Lines[0].Line[C].Note, Lines[0].Line[C].HighNote + 1); + + // last note was deleted + if (CurrentNote > Lines[0].Line[C].HighNote) then begin - //Move all Sentences after the current to the Left - for N := C+1 to Lines[0].High do - Lines[0].Line[N-1] := Lines[0].Line[N]; - - //Delete Last Sentence - SetLength(Lines[0].Line, Lines[0].High); - Lines[0].High := High(Lines[0].Line); - Lines[0].Number := Length(Lines[0].Line); - - CurrentNote := 0; - if (C > 0) then - Lines[0].Current := C - 1 - else - Lines[0].Current := 0; - - Lines[0].Line[Lines[0].Current].Note[CurrentNote].Color := 2; + // select new last note + CurrentNote := Lines[0].Line[C].HighNote; + + // correct Line ending + with Lines[0].Line[C] do + End_ := Note[HighNote].Start + Note[HighNote].Length; end; + + Lines[0].Line[C].Note[CurrentNote].Color := 2; + end + // Last Note of current Sentence Deleted - > Delete Sentence + // if there are more than two left + else if (Lines[0].High > 1) then + begin + //Move all Sentences after the current to the Left + for N := C+1 to Lines[0].High do + Lines[0].Line[N-1] := Lines[0].Line[N]; + + //Delete Last Sentence + SetLength(Lines[0].Line, Lines[0].High); + Lines[0].High := High(Lines[0].Line); + Lines[0].Number := Length(Lines[0].Line); + + CurrentNote := 0; + if (C > 0) then + Lines[0].Current := C - 1 + else + Lines[0].Current := 0; + + Lines[0].Line[Lines[0].Current].Note[CurrentNote].Color := 2; end; + + // update lyric display + Lyric.AddLine(Lines[0].Current); + Lyric.Selected := CurrentNote; end; procedure TScreenEditSub.TransposeNote(Transpose: integer); @@ -1896,6 +1935,7 @@ end; procedure TScreenEditSub.DrawInfoBar(x, y, w, h: integer); var start, end_: integer; + SongStart, SongEnd: integer; ww: integer; pos: real; @@ -1910,9 +1950,9 @@ begin if(numLines=0) then Exit; - start := Lines[0].Line[0].Start; - end_ := Lines[0].Line[numLines-1].End_; - ww := end_ - start; + SongStart := Lines[0].Line[0].Note[0].Start; + SongEnd := Lines[0].Line[numLines-1].End_; + ww := SongEnd - SongStart; glColor4f(0, 0, 0, 1); glDisable(GL_BLEND); @@ -1945,7 +1985,7 @@ begin end_ := Lines[0].Line[line].Note[Lines[0].Line[line].HighNote].Start+ Lines[0].Line[line].Note[Lines[0].Line[line].HighNote].Length; - pos := start/ww*w; + pos := (start - SongStart)/ww*w; br := (end_-start)/ww*w; glbegin(gl_quads); @@ -1954,26 +1994,19 @@ begin glVertex2f(x+pos+br, y+h); glVertex2f(x+pos+br, y); glEnd; - { - numNotes := Length(Lines[0].Line[line].Nuta); - - for note := 0 to numNotes - 1 do - begin - - end; } end; if(PlaySentence or PlaySentenceMidi) then begin glColor4f(0, 0, 0, 0.5); pos := 0; - br := AktBeat/ww*w; + br := (AktBeat - SongStart)/ww*w; if (br>w) then br := w; end else begin glColor4f(1, 0, 0, 1); - pos := Lines[0].Line[Lines[0].Current].Note[CurrentNote].Start/ww*w; + pos := (Lines[0].Line[Lines[0].Current].Note[CurrentNote].Start - SongStart)/ww*w; br := Lines[0].Line[Lines[0].Current].Note[CurrentNote].Length/ww*w; if (br<1) then br := 1; @@ -2316,7 +2349,7 @@ begin end; -// Interaction := 0; + //Interaction := 0; TextEditMode := false; TitleEditMode := false; ArtistEditMode := false; @@ -2344,7 +2377,7 @@ begin if (MidiPos > MidiStop) then begin MidiOut.PutShort($B1, $7, floor(1.27*SelectsS[VolumeMidiSlideId].SelectedOption)); - MidiOut.PutShort($81, Lines[0].Line[Lines[0].Current].Note[MidiLastNote].Tone + 60, 127); + MidiOut.PutShort(MIDI_NOTEOFF or 1, Lines[0].Line[Lines[0].Current].Note[MidiLastNote].Tone + 60, 127); PlaySentenceMidi := false; end; {$ENDIF} @@ -2363,7 +2396,7 @@ begin {$IFDEF UseMIDIPort} MidiOut.PutShort($B1, $7, floor(1.27*SelectsS[VolumeMidiSlideId].SelectedOption)); if i > 0 then - MidiOut.PutShort($81, Lines[0].Line[Lines[0].Current].Note[i-1].Tone + 60, 127); + MidiOut.PutShort(MIDI_NOTEOFF or 1, Lines[0].Line[Lines[0].Current].Note[i-1].Tone + 60, 127); MidiOut.PutShort($91, Lines[0].Line[Lines[0].Current].Note[i].Tone + 60, 127); MidiLastNote := i; {$ENDIF} @@ -2421,7 +2454,7 @@ begin // click if (Click) and (PlaySentence) then begin -// AktBeat := Floor(CurrentSong.BPM[0].BPM * (Music.Position - CurrentSong.GAP / 1000) / 60); + //AktBeat := Floor(CurrentSong.BPM[0].BPM * (Music.Position - CurrentSong.GAP / 1000) / 60); AktBeat := Floor(GetMidBeat(AudioPlayback.Position - CurrentSong.GAP / 1000)); Text[TextDebug].Text := IntToStr(AktBeat); if AktBeat <> LastClick then diff --git a/us_maker_edition/src/screens/UScreenOptionsRecord.pas b/us_maker_edition/src/screens/UScreenOptionsRecord.pas index 0f9cd49a..dc4a355f 100644 --- a/us_maker_edition/src/screens/UScreenOptionsRecord.pas +++ b/us_maker_edition/src/screens/UScreenOptionsRecord.pas @@ -88,6 +88,7 @@ type procedure StartPreview; procedure StopPreview; procedure UpdateInputDevice; + function ValidateSettings: boolean; procedure ChangeVolume(VolumeChange: single); procedure DrawVolume(x, y, Width, Height: single); procedure DrawVUMeter(const State: TDrawState; x, y, Width, Height: single); @@ -120,6 +121,7 @@ uses TextGL, UGraphic, UDraw, + ULanguage, UMain, UMenuSelectSlide, UMenuText, @@ -168,7 +170,7 @@ begin SDLK_BACKSPACE: begin // TODO: Show Save/Abort screen - if (AudioInputProcessor.ValidateSettings()) then + if (ValidateSettings()) then begin Ini.Save; AudioPlayback.PlaySound(SoundLib.Back); @@ -179,7 +181,7 @@ begin begin if (SelInteraction = ExitButtonIID) then begin - if (AudioInputProcessor.ValidateSettings()) then + if (ValidateSettings()) then begin Ini.Save; AudioPlayback.PlaySound(SoundLib.Back); @@ -213,6 +215,24 @@ begin end; end; +function TScreenOptionsRecord.ValidateSettings: boolean; +var + BadPlayer: integer; +begin + BadPlayer := AudioInputProcessor.ValidateSettings(); + if (BadPlayer <> 0) then + begin + ScreenPopupError.ShowPopup( + Format(Language.Translate('ERROR_PLAYER_DEVICE_ASSIGNMENT'), + [BadPlayer])); + Result := false; + end + else + begin + Result := true; + end; +end; + constructor TScreenOptionsRecord.Create; var DeviceIndex: integer; @@ -321,12 +341,6 @@ begin end; // add Exit-button - //ButtonTheme := Theme.OptionsRecord.ButtonExit; - // adjust button position - //if (WidgetYPos <> 0) then - // ButtonTheme.Y := WidgetYPos; - //AddButton(ButtonTheme); - // <mog> I uncommented the stuff above, because it's not skinable :X AddButton(Theme.OptionsRecord.ButtonExit); if (Length(Button[0].Text) = 0) then AddButtonText(20, 5, Theme.Options.Description[7]); @@ -783,7 +797,7 @@ begin for ChannelIndex := 0 to High(Device.CaptureChannel) do begin // load player color mapped to current input channel - if (DeviceCfg.ChannelToPlayerMap[ChannelIndex] > 0) then + if (DeviceCfg.ChannelToPlayerMap[ChannelIndex] <> CHANNEL_OFF) then begin // set mapped channel to corresponding player-color LoadColor(State.R, State.G, State.B, 'P'+ IntToStr(DeviceCfg.ChannelToPlayerMap[ChannelIndex]) + 'Dark'); diff --git a/us_maker_edition/src/screens/UScreenOptionsThemes.pas b/us_maker_edition/src/screens/UScreenOptionsThemes.pas index 94475cc7..29d8a9dc 100644 --- a/us_maker_edition/src/screens/UScreenOptionsThemes.pas +++ b/us_maker_edition/src/screens/UScreenOptionsThemes.pas @@ -92,6 +92,10 @@ begin UGraphic.LoadScreens(); AudioPlayback.PlaySound(SoundLib.Back); + + // select theme button in new created options screen + ScreenOptions.Interaction := 4; + FadeTo(@ScreenOptions); end; SDLK_RETURN: @@ -106,6 +110,10 @@ begin UGraphic.LoadScreens(); AudioPlayback.PlaySound(SoundLib.Back); + + // select theme button in new created options screen + ScreenOptions.Interaction := 4; + FadeTo(@ScreenOptions); end; end; diff --git a/us_maker_edition/src/screens/UScreenSing.pas b/us_maker_edition/src/screens/UScreenSing.pas index 233f1e38..3e0d8078 100644 --- a/us_maker_edition/src/screens/UScreenSing.pas +++ b/us_maker_edition/src/screens/UScreenSing.pas @@ -62,6 +62,12 @@ type function GetClock(): real; override; end; + TTimebarMode = ( + tbmCurrent, // current song position + tbmRemaining, // remaining time + tbmTotal // total time + ); + type TScreenSing = class(TMenu) private @@ -70,6 +76,7 @@ type fVideoClip: IVideo; fLyricsSync: TLyricsSyncSource; fMusicSync: TMusicSyncSource; + fTimebarMode: TTimebarMode; protected eSongLoaded: THookableEvent; //< event is called after lyrics of a song are loaded on OnShow Paused: boolean; //pause Mod @@ -178,7 +185,9 @@ begin Result := false; Exit; end; - Ord('V'): // show visualization + + // show visualization + Ord('V'): begin fShowVisualization := not fShowVisualization; @@ -193,11 +202,23 @@ begin end; Exit; end; + + // pause Ord('P'): begin Pause; Exit; end; + + // toggle time display + Ord('T'): + begin + if (fTimebarMode = High(TTimebarMode)) then + fTimebarMode := Low(TTimebarMode) + else + Inc(fTimebarMode); + Exit; + end; end; // check special keys @@ -349,6 +370,7 @@ var Color: TRGB; VideoFile, BgFile: IPath; success: boolean; + BadPlayer: integer; begin inherited; @@ -450,6 +472,8 @@ begin Statics[StaticP3R].Visible := V3R; Text[TextP3R].Visible := V3R; + fTimebarMode := tbmCurrent; + // FIXME: sets path and filename to '' ResetSingTemp; @@ -498,7 +522,7 @@ begin *} fShowVisualization := false; VideoFile := CurrentSong.Path.Append(CurrentSong.Video); - if (CurrentSong.Video.IsSet) and VideoFile.IsFile then + if (Ini.VideoEnabled = 1) and CurrentSong.Video.IsSet() and VideoFile.IsFile then begin fVideoClip := VideoPlayback.Open(VideoFile); fCurrentVideo := fVideoClip; @@ -562,6 +586,14 @@ begin LyricsState.TotalTime := AudioPlayback.Length; LyricsState.UpdateBeats(); + BadPlayer := AudioInputProcessor.CheckPlayersConfig(PlayersPlay); + if (BadPlayer <> 0) then + begin + ScreenPopupError.ShowPopup( + Format(Language.Translate('ERROR_PLAYER_NO_DEVICE_ASSIGNMENT'), + [BadPlayer])); + end; + // prepare and start voice-capture AudioInput.CaptureStart; @@ -725,8 +757,10 @@ end; function TScreenSing.Draw: boolean; var - Min: integer; - Sec: integer; + DisplayTime: real; + DisplayPrefix: string; + DisplayMin: integer; + DisplaySec: integer; T: integer; CurLyricsTime: real; VideoFrameTime: Extended; @@ -769,53 +803,31 @@ begin end; // case end; // if - //// - // dual screen, part 1 - //////////////////////// - - // Note: ScreenX is the offset of the current screen in dual-screen mode so we - // will move the statics and texts to the correct screen here. - // FIXME: clean up this weird stuff. Commenting this stuff out, nothing - // was missing on screen w/ 6 players - so do we even need this stuff? - {Statics[StaticP1].Texture.X := Statics[StaticP1].Texture.X + 10 * ScreenX; - - Text[TextP1].X := Text[TextP1].X + 10 * ScreenX; } - - {Statics[StaticP1ScoreBG].Texture.X := Statics[StaticP1ScoreBG].Texture.X + 10*ScreenX; - Text[TextP1Score].X := Text[TextP1Score].X + 10*ScreenX;} - - {Statics[StaticP2R].Texture.X := Statics[StaticP2R].Texture.X + 10 * ScreenX; - - Text[TextP2R].X := Text[TextP2R].X + 10 * ScreenX; } - - {Statics[StaticP2RScoreBG].Texture.X := Statics[StaticP2RScoreBG].Texture.X + 10*ScreenX; - Text[TextP2RScore].X := Text[TextP2RScore].X + 10*ScreenX;} - - // end of weird stuff - { - Statics[1].Texture.X := Statics[1].Texture.X + 10 * ScreenX; } - - { for T := 0 to 1 do - Text[T].X := Text[T].X + 10 * ScreenX; } - // retrieve current lyrics time, we have to store the value to avoid // that min- and sec-values do not match CurLyricsTime := LyricsState.GetCurrentTime(); - Min := Round(CurLyricsTime) div 60; - Sec := Round(CurLyricsTime) mod 60; + + // retrieve time for timebar text + case (fTimebarMode) of + tbmRemaining: begin + DisplayTime := LyricsState.TotalTime - CurLyricsTime; + DisplayPrefix := '-'; + end; + tbmTotal: begin + DisplayTime := LyricsState.TotalTime; + DisplayPrefix := '#'; + end; + else begin + DisplayTime := CurLyricsTime; + DisplayPrefix := ''; + end; + end; + DisplayMin := Round(DisplayTime) div 60; + DisplaySec := Round(DisplayTime) mod 60; // update static menu with time ... - Text[TextTimeText].Text := ''; - if Min < 10 then - Text[TextTimeText].Text := '0'; - Text[TextTimeText].Text := Text[TextTimeText].Text + IntToStr(Min) + ':'; - if Sec < 10 then - Text[TextTimeText].Text := Text[TextTimeText].Text + '0'; - Text[TextTimeText].Text := Text[TextTimeText].Text + IntToStr(Sec); - - // draw static menu (BG) - // Note: there is no menu and the animated background brakes the video playback - //DrawBG; + Text[TextTimeText].Text := Format('%s%.2d:%.2d', + [DisplayPrefix, DisplayMin, DisplaySec]); //the song was sung to the end? Line := Lyrics.GetUpperLine(); @@ -847,7 +859,8 @@ begin fCurrentVideo.GetFrame(VideoFrameTime); end; - fCurrentVideo.DrawGL(ScreenAct); + fCurrentVideo.SetScreen(ScreenAct); + fCurrentVideo.Draw; end; // draw static menu (FG) @@ -857,8 +870,10 @@ begin //Log.LogError('Check for music finish: ' + BoolToStr(Music.Finished) + ' ' + FloatToStr(LyricsState.CurrentTime*1000) + ' ' + IntToStr(CurrentSong.Finish)); if ShowFinish then begin - if (not AudioPlayback.Finished) and ((CurrentSong.Finish = 0) or - (LyricsState.GetCurrentTime() * 1000 <= CurrentSong.Finish)) and (not Settings.Finish) then + if (not AudioPlayback.Finished) and + ((CurrentSong.Finish = 0) or + (LyricsState.GetCurrentTime() * 1000 <= CurrentSong.Finish)) and + (not Settings.Finish) then begin // analyze song if not paused if (not Paused) then @@ -886,27 +901,6 @@ begin // draw scores Scores.Draw; - //// - // dual screen, part 2 - //////////////////////// - - // Note: ScreenX is the offset of the current screen in dual-screen mode so we - // will move the statics and texts to the correct screen here. - // FIXME: clean up this weird stuff - - {Statics[StaticP1].Texture.X := Statics[StaticP1].Texture.X - 10 * ScreenX; - Text[TextP1].X := Text[TextP1].X - 10 * ScreenX; - - Statics[StaticP2R].Texture.X := Statics[StaticP2R].Texture.X - 10 * ScreenX; - Text[TextP2R].X := Text[TextP2R].X - 10 * ScreenX; - - // end of weird - - Statics[1].Texture.X := Statics[1].Texture.X - 10 * ScreenX; - - for T := 0 to 1 do - Text[T].X := Text[T].X - 10 * ScreenX; } - // draw pausepopup // FIXME: this is a workaround that the static is drawn over the lyrics, lines, scores and effects // maybe someone could find a better solution diff --git a/us_maker_edition/src/screens/UScreenSong.pas b/us_maker_edition/src/screens/UScreenSong.pas index 6b83d522..6fe8d204 100644 --- a/us_maker_edition/src/screens/UScreenSong.pas +++ b/us_maker_edition/src/screens/UScreenSong.pas @@ -62,8 +62,12 @@ type isScrolling: boolean; // true if song flow is about to move + fCurrentVideo: IVideo; + procedure StartMusicPreview(); procedure StopMusicPreview(); + procedure StartVideoPreview(); + procedure StopVideoPreview(); public TextArtist: integer; TextTitle: integer; @@ -128,6 +132,7 @@ type function Draw: boolean; override; procedure GenerateThumbnails(); procedure OnShow; override; + procedure OnShowFinish; override; procedure OnHide; override; procedure SelectNext; procedure SelectPrev; @@ -886,6 +891,8 @@ begin PreviewOpened := -1; isScrolling := false; + + fCurrentVideo := nil; end; procedure TScreenSong.GenerateThumbnails(); @@ -960,6 +967,7 @@ begin if (Ini.PreviewVolume <> 0) then begin StartMusicPreview; + StartVideoPreview; end; // fade in detailed cover @@ -973,6 +981,7 @@ begin UnLoadDetailedCover; StopMusicPreview(); + StopVideoPreview(); PreviewOpened := -1; end; @@ -1506,6 +1515,9 @@ begin AudioPlayback.Stop; PreviewOpened := -1; + // reset video playback engine + fCurrentVideo := nil; + if Ini.Players <= 3 then PlayersPlay := Ini.Players + 1; if Ini.Players = 4 then PlayersPlay := 6; @@ -1544,11 +1556,17 @@ begin end; end; - isScrolling := true; + isScrolling := false; SetJoker; SetStatics; end; +procedure TScreenSong.OnShowFinish; +begin + isScrolling := true; + CoverTime := 10; +end; + procedure TScreenSong.OnHide; begin // turn music volume to 100% @@ -1556,6 +1574,7 @@ begin // stop preview StopMusicPreview(); + StopVideoPreview(); end; procedure TScreenSong.DrawExtensions; @@ -1573,9 +1592,10 @@ end; function TScreenSong.Draw: boolean; var - dx: real; - dt: real; - I: integer; + dx: real; + dt: real; + I: integer; + VideoAlpha: real; begin if isScrolling then begin @@ -1611,7 +1631,7 @@ begin //Log.LogBenchmark('SetScroll4', 5); //Fading Functions, Only if Covertime is under 5 Seconds - if (CoverTime < 5) then + if (CoverTime < 9) then begin // cover fade if (CoverTime < 1) and (CoverTime + TimeSkip >= 1) then @@ -1641,10 +1661,43 @@ begin //Draw BG DrawBG; + VideoAlpha := Button[interaction].Texture.Alpha*(CoverTime-1); //Instead of Draw FG Procedure: //We draw Buttons for our own for I := 0 to Length(Button) - 1 do - Button[I].Draw; + begin + if (I<>Interaction) or not Assigned(fCurrentVideo) or (VideoAlpha<1) or AudioPlayback.Finished then + Button[I].Draw; + end; + + if AudioPlayback.Finished then + StopVideoPreview; + + if Assigned(fCurrentVideo) then + begin + // Just call this once + // when Screens = 2 + if (ScreenAct = 1) then + fCurrentVideo.GetFrame(CatSongs.Song[Interaction].VideoGAP + AudioPlayback.Position); + + fCurrentVideo.SetScreen(ScreenAct); + fCurrentVideo.Alpha := VideoAlpha; + + //set up window + with Button[interaction] do + begin + fCurrentVideo.SetScreenPosition(X, Y, Z); + fCurrentVideo.Width := W; + fCurrentVideo.Height := H; + fCurrentVideo.ReflectionSpacing := Reflectionspacing; + end; + fCurrentVideo.AspectCorrection := acoCrop; + + fCurrentVideo.Draw; + + if Button[interaction].Reflection then + fCurrentVideo.DrawReflection; + end; // Statics for I := 0 to Length(Statics) - 1 do @@ -1738,12 +1791,13 @@ end; procedure TScreenSong.StartMusicPreview(); var Song: TSong; + PreviewPos: real; begin AudioPlayback.Close(); if CatSongs.VisibleSongs = 0 then Exit; - + Song := CatSongs.Song[Interaction]; if not assigned(Song) then Exit; @@ -1755,8 +1809,13 @@ begin if AudioPlayback.Open(Song.Path.Append(Song.Mp3)) then begin PreviewOpened := Interaction; - - AudioPlayback.Position := AudioPlayback.Length / 4; + + PreviewPos := AudioPlayback.Length / 4; + // fix for invalid music file lengths + if (PreviewPos > 60.0) then + PreviewPos := 60.0; + AudioPlayback.Position := PreviewPos; + // set preview volume if (Ini.PreviewFading = 0) then begin @@ -1779,12 +1838,66 @@ begin AudioPlayback.Stop; end; +procedure TScreenSong.StartVideoPreview(); +var + VideoFile: IPath; + Song: TSong; + +begin + if (Ini.VideoPreview=0) then + Exit; + + if Assigned(fCurrentVideo) then + begin + fCurrentVideo.Stop(); + fCurrentVideo := nil; + end; + + //if no audio open => exit + if (PreviewOpened = -1) then + Exit; + + if CatSongs.VisibleSongs = 0 then + Exit; + + Song := CatSongs.Song[Interaction]; + if not assigned(Song) then + Exit; + + //fix: if main cat than there is nothing to play + if Song.main then + Exit; + + VideoFile := Song.Path.Append(Song.Video); + if (Song.Video.IsSet) and VideoFile.IsFile then + begin + fCurrentVideo := VideoPlayback.Open(VideoFile); + if (fCurrentVideo <> nil) then + begin + fCurrentVideo.Position := Song.VideoGAP + AudioPlayback.Position; + fCurrentVideo.Play; + end; + end; +end; + +procedure TScreenSong.StopVideoPreview(); +begin + // Stop video preview of previous song + if Assigned(fCurrentVideo) then + begin + fCurrentVideo.Stop(); + fCurrentVideo := nil; + end; +end; + // Changes previewed song procedure TScreenSong.ChangeMusic; begin StopMusicPreview(); + StopVideoPreview(); PreviewOpened := -1; StartMusicPreview(); + StartVideoPreview(); end; procedure TScreenSong.SkipTo(Target: cardinal); |