aboutsummaryrefslogtreecommitdiffstats
path: root/us_maker_edition/src/base
diff options
context:
space:
mode:
authorwhiteshark0 <whiteshark0@b956fd51-792f-4845-bead-9b4dfca2ff2c>2010-06-15 21:28:52 +0000
committerwhiteshark0 <whiteshark0@b956fd51-792f-4845-bead-9b4dfca2ff2c>2010-06-15 21:28:52 +0000
commit115255f6a3c2be422680710b5d6ba4226c3383a6 (patch)
tree3bbe81786e12808220efa589adb799e0412d20a2 /us_maker_edition/src/base
parentcf141f470e4cb45b8c886e1536846a8254cdd302 (diff)
downloadusdx-115255f6a3c2be422680710b5d6ba4226c3383a6.tar.gz
usdx-115255f6a3c2be422680710b5d6ba4226c3383a6.tar.xz
usdx-115255f6a3c2be422680710b5d6ba4226c3383a6.zip
merged trunk r2528 into us_maker branch
git-svn-id: svn://svn.code.sf.net/p/ultrastardx/svn/branches/experimental@2530 b956fd51-792f-4845-bead-9b4dfca2ff2c
Diffstat (limited to 'us_maker_edition/src/base')
-rw-r--r--us_maker_edition/src/base/UConfig.pas2
-rw-r--r--us_maker_edition/src/base/UDraw.pas3
-rw-r--r--us_maker_edition/src/base/UEditorLyrics.pas1
-rw-r--r--us_maker_edition/src/base/UFiles.pas4
-rw-r--r--us_maker_edition/src/base/UIni.pas77
-rw-r--r--us_maker_edition/src/base/UMain.pas49
-rw-r--r--us_maker_edition/src/base/UMusic.pas75
-rw-r--r--us_maker_edition/src/base/UNote.pas62
-rw-r--r--us_maker_edition/src/base/UPathUtils.pas5
-rw-r--r--us_maker_edition/src/base/UPlatform.pas1
-rw-r--r--us_maker_edition/src/base/UPlatformMacOSX.pas34
-rw-r--r--us_maker_edition/src/base/URecord.pas120
-rw-r--r--us_maker_edition/src/base/USong.pas34
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);