diff options
Diffstat (limited to 'us_maker_edition/src/base')
-rw-r--r-- | us_maker_edition/src/base/UConfig.pas | 2 | ||||
-rw-r--r-- | us_maker_edition/src/base/UDraw.pas | 3 | ||||
-rw-r--r-- | us_maker_edition/src/base/UEditorLyrics.pas | 1 | ||||
-rw-r--r-- | us_maker_edition/src/base/UFiles.pas | 4 | ||||
-rw-r--r-- | us_maker_edition/src/base/UIni.pas | 77 | ||||
-rw-r--r-- | us_maker_edition/src/base/UMain.pas | 49 | ||||
-rw-r--r-- | us_maker_edition/src/base/UMusic.pas | 75 | ||||
-rw-r--r-- | us_maker_edition/src/base/UNote.pas | 62 | ||||
-rw-r--r-- | us_maker_edition/src/base/UPathUtils.pas | 5 | ||||
-rw-r--r-- | us_maker_edition/src/base/UPlatform.pas | 1 | ||||
-rw-r--r-- | us_maker_edition/src/base/UPlatformMacOSX.pas | 34 | ||||
-rw-r--r-- | us_maker_edition/src/base/URecord.pas | 120 | ||||
-rw-r--r-- | us_maker_edition/src/base/USong.pas | 34 |
13 files changed, 355 insertions, 112 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); |