diff --git a/Output/CreditsRessourcesDB.sqlite b/Output/CreditsRessourcesDB.sqlite new file mode 100644 index 00000000..dbfe78bb Binary files /dev/null and b/Output/CreditsRessourcesDB.sqlite differ diff --git a/Output/Graphics/Logo.png b/Output/Graphics/Logo.png index 2ad40d2f..0977ea93 100644 Binary files a/Output/Graphics/Logo.png and b/Output/Graphics/Logo.png differ diff --git a/Output/Languages/Spanish.xml b/Output/Languages/Spanish.xml index 8787eca0..cb106774 100644 --- a/Output/Languages/Spanish.xml +++ b/Output/Languages/Spanish.xml @@ -55,7 +55,7 @@ Lista Dreidel - TileBoard + Cuadrícula Libro Según Sean Necesarias diff --git a/Output/Readme.txt b/Output/Readme.txt new file mode 100644 index 00000000..4f05511a --- /dev/null +++ b/Output/Readme.txt @@ -0,0 +1,126 @@ +Vocaluxe 0.1 README +---------------------------- + +================================= += 1. About = += 2. Release Notes = += 3. Command-Line Parameters = += 4. Controls = += 5. Help & Support = += 6. Source Code = +================================= + + +================================= += 1. About = +================================= + +Vocaluxe is a free and open source singing game. It allows up to three players (currently) to sing +along with music using microphones in order to score points, depending on the pitch of the voice +and the rhythm of singing. + +Vocaluxe is an entirely new development in C#, inspired by the original UltraStar (developed by corvus5) +and the great Ultrastar Deluxe project. + +Supported Operating Systems / Requirements: +- WinXP, Vista, Windows 7 with .NET 2.0 +- 1 GHz CPU, 512 MB RAM, Graphics card with OpenGL 2.1 or DirectX 9.0 support + + +================================= += 2. Release Notes = +================================= + +- The program supports 32bit and 64bit (Vocaluxe_x64.exe) operating systems. + +- The program uses OpenGL as the default renderer. If your graphics card driver does not or not + properly support OpenGL, you can try the Direct3D renderer by editing your Config.xml in the + application's main directory: + + + + TR_CONFIG_DIRECT3D + +- To add your existing song collection to Vocaluxe, put your songs into the 'Songs' subfolder. + + Alternatively, you can add multiple song paths to Vocaluxe by editing your Config.xml: + + + + C:\Vocaluxe\Songs + D:\EvenMore\Songs + +- To use your highscore database from UltraStar deluxe 1.0.1, UltraStar deluxe CMD Mod or + UltraStar deluxe 1.1, simply copy your Ultrastar.db (rename if necessary) into the main + directory. Your highscore database will be imported into the HighscoreDB.sqlite file when + Vocaluxe is started. Upon successful import, the old database is deleted. + +- To add your own player avatars, simply add some picture files to the 'Profiles' subfolder. + Supported file types: png, jpg (jpeg), bmp. + Images should have square proportions, e.g. 400x400 px. + + +================================= += 3. Command-Line Parameters = +================================= +The following command-line parameters may be passed to the game when starting it through a shortcut +or from the console: + +-ConfigFile [Path\File]: Use [Path\File] as configuration file instead of Config.xml in the main folder. + The path to the file has to exist and needs to be an absolute path. + If only a filename is specified, the file is assumed to be in the current directory. + Examples: Vocaluxe.exe -ConfigFile MyConfig.xml + Vocaluxe.exe -ConfigFile C:\Vocaluxe\Configs\MyConfig.xml + +-ScoreFile [Path\File] : Use [File] as score database instead of HighscoreDB.sqlite in the application's + main folder. The path to the file has to exist and needs to be an absolute path. + If only a filename is specified, the file is assumed to be in the current directory. + Examples: Vocaluxe.exe -ScoreFile MyHighscoreDB.sqlite + Vocaluxe.exe -Scorefile C:\Vocaluxe\Highscores\MyHighscoreDB.sqlite + +-SongFolder [Path] : Use [Path] as song folder. The path has to exist and needs to be an absolute path. + Note that any song folders set in the config file will be ignored. + This parameter may be used multiple times to use different song folders. + Examples: Vocaluxe.exe -SongFolder D:\MySongCollection + Vocaluxe.exe -SongFolder D:\MySongCollection -SongFolder E:\MoreSongs + +-SongPath [Path] : Deprecated, see SongFolder [Path]. This alias is provided for convenience. + +Complete example: +Vocaluxe.exe -ConfigFile MyConfig.xml -ScoreFile C:\Vocaluxe\Highscores\MyHighscoreDB.sqlite -SongFolder D:\MySongCollection + + +================================= += 4. Controls = +================================= +[Mouse] to navigate through the screens: Left button: select/manipulate elements, + Right button: go to previous screen. +[Cursor] to navigate through the screens. +[Enter] to confirm +[Escape] or [Back] to go to the previous screen. + +[ALT] + [P] to take a ScreenShot. Screenshots are saved in the directory "Screenshots". +[ALT] + [ENTER] to toggle full screen mode. +[SHIFT] + [F1] to toggle theme edit mode. The theme edit mode is experimental. It allows you + to change the size and position of the element on the screens. You can save + your changes with [S] before leaving the theme edit mode. +[TAB] to open the background music controls (not on all screens). + +Songscreen +[R] to select a random song +[F3] or [CTRL]+[F] to open/close the song search menu +[A] to sing all songs from a category + + +================================= += 5. Help & Suppport = +================================= +Support Forum (German): http://www.ultra-star.de +Song-DataBase (USDB): http://usdb.animux.de/ + + +================================= += 6. Source Code = +================================= +Source Code (GitHub): https://github.com/Vocaluxe/Vocaluxe +SF.Net Page: http://sourceforge.net/projects/vocaluxe/ \ No newline at end of file diff --git a/Output/Themes/Ambient/Screens/ScreenOptionsSound.xml b/Output/Themes/Ambient/Screens/ScreenOptionsSound.xml index 5d3d0e9e..259915bd 100644 --- a/Output/Themes/Ambient/Screens/ScreenOptionsSound.xml +++ b/Output/Themes/Ambient/Screens/ScreenOptionsSound.xml @@ -1,7 +1,7 @@  - 1 + 2 Video @@ -394,4 +394,4 @@ 1 - \ No newline at end of file + diff --git a/Output/Themes/Changelog/Changelog_ScreenOptionsSound.txt b/Output/Themes/Changelog/Changelog_ScreenOptionsSound.txt new file mode 100644 index 00000000..5bdf4bfd --- /dev/null +++ b/Output/Themes/Changelog/Changelog_ScreenOptionsSound.txt @@ -0,0 +1,18 @@ +ScreenOptionsSound + +++++++++++ +Version 2: + +Add: + + + + + + + +and some texts for description (Text2, Text3, Text4) +"TR_SCREENOSOUND_BACKGROUNDMUSIC" +"TR_SCREENOSOUND_BACKGROUNDMUSICVOLUME" +"TR_SCREENOSOUND_BACKGROUNDMUSICSOURCE" +++++++++++ diff --git a/Vocaluxe/Base/CConfig.cs b/Vocaluxe/Base/CConfig.cs index 9bbafce3..00ced7f0 100644 --- a/Vocaluxe/Base/CConfig.cs +++ b/Vocaluxe/Base/CConfig.cs @@ -66,6 +66,19 @@ namespace Vocaluxe.Base TR_CONFIG_LEVEL_MAX //all debug infos } + public enum EBufferSize + { + b0 = 0, + b512 = 512, + b1024 = 1024, + b1536 = 1536, + b2048 = 2048, + b2560 = 2560, + b3072 = 3072, + b3584 = 3584, + b4096 = 4096 + } + public enum EPlaybackLib { PortAudio, @@ -177,7 +190,8 @@ namespace Vocaluxe.Base // Sound public static EPlaybackLib PlayBackLib = EPlaybackLib.PortAudio; public static ERecordLib RecordLib = ERecordLib.PortAudio; - public static uint PortAudioBufferSize = 1024; + public static EBufferSize AudioBufferSize = EBufferSize.b2048; + public static int AudioLatency = 125; public static int BackgroundMusicVolume = 50; public static EOffOn BackgroundMusic = EOffOn.TR_CONFIG_ON; public static EBackgroundMusicSource BackgroundMusicSource = EBackgroundMusicSource.TR_CONFIG_NO_OWN_MUSIC; @@ -287,6 +301,14 @@ namespace Vocaluxe.Base #region Sound CHelper.TryGetEnumValueFromXML("//root/Sound/PlayBackLib", navigator, ref PlayBackLib); CHelper.TryGetEnumValueFromXML("//root/Sound/RecordLib", navigator, ref RecordLib); + CHelper.TryGetEnumValueFromXML("//root/Sound/AudioBufferSize", navigator, ref AudioBufferSize); + + CHelper.TryGetIntValueFromXML("//root/Sound/AudioLatency", navigator, ref AudioLatency); + if (AudioLatency < -500) + AudioLatency = -500; + if (AudioLatency > 500) + AudioLatency = 500; + CHelper.TryGetEnumValueFromXML("//root/Sound/BackgroundMusic", navigator, ref BackgroundMusic); CHelper.TryGetIntValueFromXML("//root/Sound/BackgroundMusicVolume", navigator, ref BackgroundMusicVolume); CHelper.TryGetEnumValueFromXML("//root/Sound/BackgroundMusicSource", navigator, ref BackgroundMusicSource); @@ -482,8 +504,14 @@ namespace Vocaluxe.Base writer.WriteComment("RecordLib: " + ListStrings(Enum.GetNames(typeof(ERecordLib)))); writer.WriteElementString("RecordLib", Enum.GetName(typeof(ERecordLib), RecordLib)); + writer.WriteComment("AudioBufferSize: " + ListStrings(Enum.GetNames(typeof(EBufferSize)))); + writer.WriteElementString("AudioBufferSize", Enum.GetName(typeof(EBufferSize), AudioBufferSize)); + + writer.WriteComment("AudioLatency from -500 to 500 ms"); + writer.WriteElementString("AudioLatency", AudioLatency.ToString()); + writer.WriteComment("Background Music"); - writer.WriteElementString("BackgroundMusicEnabled", Enum.GetName(typeof(EOffOn), BackgroundMusic)); + writer.WriteElementString("BackgroundMusic", Enum.GetName(typeof(EOffOn), BackgroundMusic)); writer.WriteComment("Background Music Volume from 0 to 100"); writer.WriteElementString("BackgroundMusicVolume", BackgroundMusicVolume.ToString()); diff --git a/Vocaluxe/Base/CDataBase.cs b/Vocaluxe/Base/CDataBase.cs index 90b52a93..15c6f6f2 100644 --- a/Vocaluxe/Base/CDataBase.cs +++ b/Vocaluxe/Base/CDataBase.cs @@ -27,21 +27,59 @@ namespace Vocaluxe.Base private static string _HighscoreFilePath; private static string _CoverFilePath; + private static string _CreditsRessourcesFilePath; public static void Init() { _HighscoreFilePath = Path.Combine(System.Environment.CurrentDirectory, CSettings.sFileHighscoreDB); _CoverFilePath = Path.Combine(System.Environment.CurrentDirectory, CSettings.sFileCoverDB); + _CreditsRessourcesFilePath = Path.Combine(System.Environment.CurrentDirectory, CSettings.sFileCreditsRessourcesDB); InitHighscoreDB(); InitCoverDB(); + InitCreditsRessourcesDB(); } #region Highscores + public static int AddScore(string PlayerName, int Score, int LineNr, long Date, int Medley, int Duet, int Diff, + string Artist, string Title, int NumPlayed, string FilePath) + { + SPlayer player = new SPlayer(); + player.Name = PlayerName; + player.Points = Score; + player.LineNr = LineNr; + player.DateTicks = Date; + player.Medley = (Medley == 1); + player.Duet = (Duet == 1); + player.Difficulty = (EGameDifficulty)Diff; + + SQLiteConnection connection = new SQLiteConnection(); + connection.ConnectionString = "Data Source=" + FilePath; + SQLiteCommand command; + + try + { + connection.Open(); + } + catch (Exception) + { + return -1; + } + + command = new SQLiteCommand(connection); + + int DataBaseSongID = GetDataBaseSongID(Artist, Title, NumPlayed, command); + int result = AddScore(player, command, DataBaseSongID); + + command.Dispose(); + connection.Close(); + connection.Dispose(); + + return result; + } + public static int AddScore(SPlayer player) { - int lastInsertID = -1; - SQLiteConnection connection = new SQLiteConnection(); connection.ConnectionString = "Data Source=" + _HighscoreFilePath; SQLiteCommand command; @@ -58,6 +96,18 @@ namespace Vocaluxe.Base command = new SQLiteCommand(connection); int DataBaseSongID = GetDataBaseSongID(player, command); + int result = AddScore(player, command, DataBaseSongID); + + connection.Close(); + connection.Dispose(); + + return result; + } + + private static int AddScore(SPlayer player, SQLiteCommand command, int DataBaseSongID) + { + int lastInsertID = -1; + if (DataBaseSongID >= 0) { @@ -68,7 +118,38 @@ namespace Vocaluxe.Base int Duet = 0; if (player.Duet) Duet = 1; - + + command.CommandText = "SELECT id FROM Scores WHERE SongID = @SongID AND PlayerName = @PlayerName AND Score = @Score AND " + + "LineNr = @LineNr AND Date = @Date AND Medley = @Medley AND Duet = @Duet AND Difficulty = @Difficulty"; + command.Parameters.Add("@SongID", System.Data.DbType.Int32, 0).Value = DataBaseSongID; + command.Parameters.Add("@PlayerName", System.Data.DbType.String, 0).Value = player.Name; + command.Parameters.Add("@Score", System.Data.DbType.Int32, 0).Value = (int)Math.Round(player.Points); + command.Parameters.Add("@LineNr", System.Data.DbType.Int32, 0).Value = (int)player.LineNr; + command.Parameters.Add("@Date", System.Data.DbType.Int64, 0).Value = player.DateTicks; + command.Parameters.Add("@Medley", System.Data.DbType.Int32, 0).Value = Medley; + command.Parameters.Add("@Duet", System.Data.DbType.Int32, 0).Value = Duet; + command.Parameters.Add("@Difficulty", System.Data.DbType.Int32, 0).Value = (int)player.Difficulty; + + SQLiteDataReader reader = null; + try + { + reader = command.ExecuteReader(); + } + catch (Exception) + { + ; + } + + if (reader != null && reader.HasRows) + { + if (reader.Read()) + return reader.GetInt32(0); + } + + if (reader != null) + reader.Close(); + + command.CommandText = "INSERT INTO Scores (SongID, PlayerName, Score, LineNr, Date, Medley, Duet, Difficulty) " + "VALUES (@SongID, @PlayerName, @Score, @LineNr, @Date, @Medley, @Duet, @Difficulty)"; command.Parameters.Add("@SongID", System.Data.DbType.Int32, 0).Value = DataBaseSongID; @@ -84,7 +165,7 @@ namespace Vocaluxe.Base //Read last insert line command.CommandText = "SELECT id FROM Scores ORDER BY Date DESC LIMIT 0, 1"; - SQLiteDataReader reader = null; + reader = null; try { reader = command.ExecuteReader(); @@ -106,10 +187,6 @@ namespace Vocaluxe.Base } } - command.Dispose(); - connection.Close(); - connection.Dispose(); - return lastInsertID; } @@ -120,7 +197,7 @@ namespace Vocaluxe.Base SQLiteCommand command; Score = new List(); - + try { connection.Open(); @@ -149,7 +226,7 @@ namespace Vocaluxe.Base command.Parameters.Add("@SongID", System.Data.DbType.Int32, 0).Value = DataBaseSongID; command.Parameters.Add("@Medley", System.Data.DbType.Int32, 0).Value = Medley; command.Parameters.Add("@Duet", System.Data.DbType.Int32, 0).Value = Duet; - + SQLiteDataReader reader = null; try { @@ -174,13 +251,13 @@ namespace Vocaluxe.Base Score.Add(score); } - + reader.Close(); - reader.Dispose(); + reader.Dispose(); } - + } - + command.Dispose(); connection.Close(); @@ -194,9 +271,38 @@ namespace Vocaluxe.Base if (song == null) return -1; + return GetDataBaseSongID(song.Artist, song.Title, 0, command); + } + + private static int GetDataBaseSongID(string Artist, string Title, string FilePath, int DefNumPlayed) + { + SQLiteConnection connection = new SQLiteConnection(); + connection.ConnectionString = "Data Source=" + FilePath; + SQLiteCommand command; + + try + { + connection.Open(); + } + catch (Exception) + { + return -1; + } + + command = new SQLiteCommand(connection); + int id = GetDataBaseSongID(Artist, Title, DefNumPlayed, command); + command.Dispose(); + connection.Close(); + connection.Dispose(); + + return id; + } + + private static int GetDataBaseSongID(string Artist, string Title, int DefNumPlayed, SQLiteCommand command) + { command.CommandText = "SELECT id FROM Songs WHERE [Title] = @title AND [Artist] = @artist"; - command.Parameters.Add("@title", System.Data.DbType.String, 0).Value = song.Title; - command.Parameters.Add("@artist", System.Data.DbType.String, 0).Value = song.Artist; + command.Parameters.Add("@title", System.Data.DbType.String, 0).Value = Title; + command.Parameters.Add("@artist", System.Data.DbType.String, 0).Value = Artist; SQLiteDataReader reader = null; try @@ -222,14 +328,15 @@ namespace Vocaluxe.Base reader.Close(); command.CommandText = "INSERT INTO Songs (Title, Artist, NumPlayed) " + - "VALUES (@title, @artist, 0)"; - command.Parameters.Add("@title", System.Data.DbType.String, 0).Value = song.Title; - command.Parameters.Add("@artist", System.Data.DbType.String, 0).Value = song.Artist; + "VALUES (@title, @artist, @numplayed)"; + command.Parameters.Add("@title", System.Data.DbType.String, 0).Value = Title; + command.Parameters.Add("@artist", System.Data.DbType.String, 0).Value = Artist; + command.Parameters.Add("@numplayed", System.Data.DbType.Int32, 0).Value = DefNumPlayed; command.ExecuteNonQuery(); command.CommandText = "SELECT id FROM Songs WHERE [Title] = @title AND [Artist] = @artist"; - command.Parameters.Add("@title", System.Data.DbType.String, 0).Value = song.Title; - command.Parameters.Add("@artist", System.Data.DbType.String, 0).Value = song.Artist; + command.Parameters.Add("@title", System.Data.DbType.String, 0).Value = Title; + command.Parameters.Add("@artist", System.Data.DbType.String, 0).Value = Artist; reader = null; try @@ -256,14 +363,140 @@ namespace Vocaluxe.Base reader.Close(); reader.Dispose(); } - + return -1; } - private static bool InitHighscoreDB() + private static bool GetDataBaseSongInfos(int SongID, out string Artist, out string Title, out int NumPlayed, string FilePath) + { + Artist = String.Empty; + Title = String.Empty; + NumPlayed = 0; + + SQLiteConnection connection = new SQLiteConnection(); + connection.ConnectionString = "Data Source=" + FilePath; + SQLiteCommand command; + + try + { + connection.Open(); + } + catch (Exception) + { + return false; + } + + command = new SQLiteCommand(connection); + command.CommandText = "SELECT Artist, Title, NumPlayed FROM Songs WHERE [id] = @id"; + command.Parameters.Add("@id", System.Data.DbType.String, 0).Value = SongID; + + SQLiteDataReader reader = null; + try + { + reader = command.ExecuteReader(); + } + catch (Exception) + { + return false; + } + + if (reader != null && reader.HasRows) + { + reader.Read(); + Artist = reader.GetString(0); + Title = reader.GetString(1); + NumPlayed = reader.GetInt32(2); + reader.Close(); + reader.Dispose(); + + command.Dispose(); + connection.Close(); + connection.Dispose(); + return true; + } + else + { + if (reader != null) + reader.Close(); + } + + command.Dispose(); + connection.Close(); + connection.Dispose(); + + return false; + } + + private static void InitHighscoreDB() + { + string OldDBFilePath = Path.Combine(Environment.CurrentDirectory, CSettings.sFileOldHighscoreDB); + if (File.Exists(OldDBFilePath)) + { + if (File.Exists(_HighscoreFilePath)) + { + CreateOrConvert(OldDBFilePath); + CreateOrConvert(_HighscoreFilePath); + ImportData(OldDBFilePath, _HighscoreFilePath); + + File.Delete(OldDBFilePath); + } + else + { + File.Copy(OldDBFilePath, _HighscoreFilePath); + CreateOrConvert(_HighscoreFilePath); + File.Delete(OldDBFilePath); + } + } + else + CreateOrConvert(_HighscoreFilePath); + } + + private static void CreateHighscoreDB(string FilePath) { SQLiteConnection connection = new SQLiteConnection(); - connection.ConnectionString = "Data Source=" + _HighscoreFilePath; + connection.ConnectionString = "Data Source=" + FilePath; + SQLiteCommand command; + + try + { + connection.Open(); + } + catch (Exception) + { + return; + } + + command = new SQLiteCommand(connection); + + command.CommandText = "CREATE TABLE IF NOT EXISTS Version ( id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, Value INTEGER NOT NULL);"; + command.ExecuteNonQuery(); + + command.CommandText = "INSERT INTO Version (id, Value) VALUES(NULL, " + CSettings.iDatabaseHighscoreVersion.ToString() + ")"; + command.ExecuteNonQuery(); + + command.CommandText = "CREATE TABLE IF NOT EXISTS Songs ( id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, " + + "Artist TEXT NOT NULL, Title TEXT NOT NULL, NumPlayed INTEGER);"; + command.ExecuteNonQuery(); + + command.CommandText = "CREATE TABLE IF NOT EXISTS Scores ( id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, " + + "SongID INTEGER NOT NULL, PlayerName TEXT NOT NULL, Score INTEGER NOT NULL, LineNr INTEGER NOT NULL, Date BIGINT NOT NULL, " + + "Medley INTEGER NOT NULL, Duet INTEGER NOT NULL, Difficulty INTEGER NOT NULL);"; + command.ExecuteNonQuery(); + + command.Dispose(); + connection.Close(); + connection.Dispose(); + } + + /// + /// Creates a new Vocaluxe Database if no file exists. Converts an existing old Ultrastar Deluxe highscore database into vocaluxe format. + /// + /// Database file path + /// + private static bool CreateOrConvert(string FilePath) + { + SQLiteConnection connection = new SQLiteConnection(); + connection.ConnectionString = "Data Source=" + FilePath; SQLiteCommand command; try @@ -292,12 +525,12 @@ namespace Vocaluxe.Base if (reader == null) { // create new database/tables - CreateHighscoreDB(); + CreateHighscoreDB(FilePath); } else if (reader.FieldCount == 0) { // create new database/tables - CreateHighscoreDB(); + CreateHighscoreDB(FilePath); } else { @@ -335,10 +568,10 @@ namespace Vocaluxe.Base //Check for USDX 1.1 DB if (version == 1) - ConvertFrom110(); + ConvertFrom110(FilePath); //Check for USDX 1.01 or CMD Mod DB else if (version == 0 && scoresTableExists) - ConvertFrom101(); + ConvertFrom101(FilePath); command.Dispose(); @@ -348,61 +581,15 @@ namespace Vocaluxe.Base return true; } - private static void CreateHighscoreDB() - { - SQLiteConnection connection = new SQLiteConnection(); - connection.ConnectionString = "Data Source=" + _HighscoreFilePath; - SQLiteCommand command; - - try - { - connection.Open(); - } - catch (Exception) - { - return; - } - - command = new SQLiteCommand(connection); - - command.CommandText = "CREATE TABLE IF NOT EXISTS Version ( id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, Value INTEGER NOT NULL);"; - command.ExecuteNonQuery(); - - command.CommandText = "INSERT INTO Version (id, Value) VALUES(NULL, " + CSettings.iDatabaseHighscoreVersion.ToString() + ")"; - command.ExecuteNonQuery(); - - command.CommandText = "CREATE TABLE IF NOT EXISTS Songs ( id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, " + - "Artist TEXT NOT NULL, Title TEXT NOT NULL, NumPlayed INTEGER);"; - command.ExecuteNonQuery(); - - command.CommandText = "CREATE TABLE IF NOT EXISTS Scores ( id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, " + - "SongID INTEGER NOT NULL, PlayerName TEXT NOT NULL, Score INTEGER NOT NULL, LineNr INTEGER NOT NULL, Date BIGINT NOT NULL, " + - "Medley INTEGER NOT NULL, Duet INTEGER NOT NULL, Difficulty INTEGER NOT NULL);"; - command.ExecuteNonQuery(); - - command.Dispose(); - connection.Close(); - connection.Dispose(); - - /* - // Auslesen des zuletzt eingefügten Datensatzes. - command.CommandText = "SELECT id, name FROM beispiel ORDER BY id DESC LIMIT 0, 1"; - - while (reader.Read()) - { - Console.WriteLine("Dies ist der {0}. eingefügte Datensatz mit dem Wert: \"{1}\"", reader[0].ToString(), reader[1].ToString()); - } - */ - } - /// /// Converts a USDX 1.1 database into the Vocaluxe format /// + /// Database file path /// True if succeeded - private static bool ConvertFrom110() + private static bool ConvertFrom110(string FilePath) { SQLiteConnection connection = new SQLiteConnection(); - connection.ConnectionString = "Data Source=" + _HighscoreFilePath; + connection.ConnectionString = "Data Source=" + FilePath; SQLiteCommand command; try @@ -495,7 +682,7 @@ namespace Vocaluxe.Base command.Parameters.Add("@id", System.Data.DbType.Int32, 0).Value = data.id; command.ExecuteNonQuery(); } - + //Delete old tables after conversion command.CommandText = "DROP TABLE US_Scores;"; command.ExecuteNonQuery(); @@ -520,11 +707,12 @@ namespace Vocaluxe.Base /// /// Converts a USDX 1.01 or CMD 1.01 database to Vocaluxe format /// + /// Database file path /// True if succeeded - private static bool ConvertFrom101() + private static bool ConvertFrom101(string FilePath) { SQLiteConnection connection = new SQLiteConnection(); - connection.ConnectionString = "Data Source=" + _HighscoreFilePath; + connection.ConnectionString = "Data Source=" + FilePath; SQLiteCommand command; SQLiteDataReader reader = null; @@ -541,7 +729,7 @@ namespace Vocaluxe.Base command.CommandText = "PRAGMA table_info(US_Scores);"; reader = command.ExecuteReader(); - + bool dateExists = false; @@ -558,12 +746,12 @@ namespace Vocaluxe.Base } } } - + reader.Close(); //This is a USDX 1.01 DB - if(!dateExists) + if (!dateExists) command.CommandText = "INSERT INTO Scores (SongID, PlayerName, Score, LineNr, Date, Medley, Duet, Difficulty) SELECT SongID, Player, Score, '0', '0', '0', '0', Difficulty from US_Scores"; else // This is a CMD 1.01 DB command.CommandText = "INSERT INTO Scores (SongID, PlayerName, Score, LineNr, Date, Medley, Duet, Difficulty) SELECT SongID, Player, Score, '0', Date, '0', '0', Difficulty from US_Scores"; @@ -577,11 +765,11 @@ namespace Vocaluxe.Base List songs = new List(); Sqlite3.sqlite3 OldDB; - int res = Sqlite3.sqlite3_open(_HighscoreFilePath, out OldDB); + int res = Sqlite3.sqlite3_open(FilePath, out OldDB); if (res != Sqlite3.SQLITE_OK) { - CLog.LogError("Error opening Database: " + _HighscoreFilePath + " (" + Sqlite3.sqlite3_errmsg(OldDB) + ")"); + CLog.LogError("Error opening Database: " + FilePath + " (" + Sqlite3.sqlite3_errmsg(OldDB) + ")"); } else { @@ -590,11 +778,11 @@ namespace Vocaluxe.Base if (res != Sqlite3.SQLITE_OK) { - CLog.LogError("Error query Database: " + _HighscoreFilePath + " (" + Sqlite3.sqlite3_errmsg(OldDB) + ")"); + CLog.LogError("Error query Database: " + FilePath + " (" + Sqlite3.sqlite3_errmsg(OldDB) + ")"); } else { - Sqlite3.sqlite3_step(Stmt); + //Sqlite3.sqlite3_step(Stmt); Encoding UTF8 = Encoding.UTF8; Encoding CP1252 = Encoding.GetEncoding(1252); @@ -605,9 +793,9 @@ namespace Vocaluxe.Base data.id = Sqlite3.sqlite3_column_int(Stmt, 0); - byte[] bytes = Sqlite3.sqlite3_column_rawbytes(Stmt, 1); + byte[] bytes = Sqlite3.sqlite3_column_rawbytes(Stmt, 1); data.str1 = UTF8.GetString(Encoding.Convert(CP1252, UTF8, bytes)); - + bytes = Sqlite3.sqlite3_column_rawbytes(Stmt, 2); data.str2 = UTF8.GetString(Encoding.Convert(CP1252, UTF8, bytes)); @@ -617,7 +805,7 @@ namespace Vocaluxe.Base } Stmt = new Sqlite3.Vdbe(); - + if (!dateExists) res = Sqlite3.sqlite3_prepare_v2(OldDB, "SELECT rowid, Player FROM US_Scores", -1, ref Stmt, 0); else @@ -625,11 +813,11 @@ namespace Vocaluxe.Base if (res != Sqlite3.SQLITE_OK) { - CLog.LogError("Error query Database: " + _HighscoreFilePath + " (" + Sqlite3.sqlite3_errmsg(OldDB) + ")"); + CLog.LogError("Error query Database: " + FilePath + " (" + Sqlite3.sqlite3_errmsg(OldDB) + ")"); } else { - Sqlite3.sqlite3_step(Stmt); + //Sqlite3.sqlite3_step(Stmt); Encoding UTF8 = Encoding.UTF8; Encoding CP1252 = Encoding.GetEncoding(1252); @@ -645,7 +833,7 @@ namespace Vocaluxe.Base if (dateExists) data.ticks = UnixTimeToTicks(Sqlite3.sqlite3_column_int(Stmt, 2)); - + scores.Add(data); } Sqlite3.sqlite3_finalize(Stmt); @@ -678,14 +866,14 @@ namespace Vocaluxe.Base command.Parameters.Add("@id", System.Data.DbType.Int32, 0).Value = data.id; command.ExecuteNonQuery(); } - + //Delete old tables after conversion command.CommandText = "DROP TABLE US_Scores;"; command.ExecuteNonQuery(); command.CommandText = "DROP TABLE US_Songs;"; command.ExecuteNonQuery(); - + reader.Dispose(); command.Dispose(); connection.Close(); @@ -693,6 +881,71 @@ namespace Vocaluxe.Base return true; } + + private static bool ImportData(string SourceDBPath, string DestinationDBPath) + { + #region open db + SQLiteConnection connSource = new SQLiteConnection(); + connSource.ConnectionString = "Data Source=" + SourceDBPath; + + try + { + connSource.Open(); + } + catch (Exception e) + { + CLog.LogError("Error on import high score data. Can't open source database \"" + SourceDBPath + "\" (" + e.Message + ")"); + return false; + } + #endregion open db + + SQLiteCommand cmdSource = new SQLiteCommand(connSource); + SQLiteDataReader rSource; + + #region import table scores + cmdSource.CommandText = "SELECT SongID, PlayerName, Score, LineNr, Date, Medley, Duet, Difficulty FROM Scores"; + rSource = cmdSource.ExecuteReader(); + if (rSource == null) + { + cmdSource.Dispose(); + connSource.Close(); + return false; + } + + if (rSource.FieldCount == 0) + { + rSource.Close(); + cmdSource.Dispose(); + connSource.Close(); + return true; + } + + while (rSource.Read()) + { + int songid = rSource.GetInt32(0); + string player = rSource.GetString(1); + int score = rSource.GetInt32(2); + int linenr = rSource.GetInt32(3); + long date = rSource.GetInt64(4); + int medley = rSource.GetInt32(5); + int duet = rSource.GetInt32(6); + int diff = rSource.GetInt32(7); + + string artist, title; + int numplayed; + if (GetDataBaseSongInfos(songid, out artist, out title, out numplayed, SourceDBPath)) + { + AddScore(player, score, linenr, date, medley, duet, diff, artist, title, numplayed, _HighscoreFilePath); + } + } + #endregion import table scores + + rSource.Close(); + cmdSource.Dispose(); + connSource.Close(); + + return true; + } #endregion Highscores #region Cover @@ -784,7 +1037,7 @@ namespace Vocaluxe.Base return false; } - + int w = MaxSize; int h = MaxSize; @@ -801,7 +1054,7 @@ namespace Vocaluxe.Base byte[] data = new byte[w * h * 4]; BitmapData bmp_data = bmp.LockBits(new Rectangle(0, 0, bmp.Width, bmp.Height), ImageLockMode.ReadOnly, System.Drawing.Imaging.PixelFormat.Format32bppArgb); - Marshal.Copy(bmp_data.Scan0, data, 0, w*h*4); + Marshal.Copy(bmp_data.Scan0, data, 0, w * h * 4); bmp.UnlockBits(bmp_data); bmp.Dispose(); @@ -947,6 +1200,273 @@ namespace Vocaluxe.Base } #endregion Cover + #region CreditsRessources + public static bool GetCreditsRessource(string FileName, ref STexture tex) + { + bool result = false; + + SQLiteConnection connection = new SQLiteConnection(); + connection.ConnectionString = "Data Source=" + _CreditsRessourcesFilePath; + SQLiteCommand command; + try + { + connection.Open(); + } + catch (Exception) + { + return false; + } + command = new SQLiteCommand(connection); + + command.CommandText = "SELECT id, width, height FROM Images WHERE [Path] = @path"; + command.Parameters.Add("@path", System.Data.DbType.String, 0).Value = FileName; + + SQLiteDataReader reader = null; + try + { + reader = command.ExecuteReader(); + } + catch (Exception) + { + throw; + } + + if (reader != null && reader.HasRows) + { + reader.Read(); + int id = reader.GetInt32(0); + int w = reader.GetInt32(1); + int h = reader.GetInt32(2); + reader.Close(); + + command.CommandText = "SELECT Data FROM ImageData WHERE ImageID = " + id.ToString(); + try + { + reader = command.ExecuteReader(); + } + catch (Exception) + { + throw; + } + + if (reader.HasRows) + { + result = true; + reader.Read(); + byte[] data = GetBytes(reader); + tex = CDraw.AddTexture(w, h, ref data); + } + } + + if (reader != null) + { + reader.Close(); + reader.Dispose(); + } + command.Dispose(); + connection.Close(); + connection.Dispose(); + + return result; + } + + //If you want to add an image to db, call this method! + private static bool AddImageToCreditsDB(String ImagePath) + { + bool result = false; + STexture tex; + + if (File.Exists(ImagePath)) + { + + SQLiteConnection connection = new SQLiteConnection(); + connection.ConnectionString = "Data Source=" + _CreditsRessourcesFilePath; + SQLiteCommand command; + try + { + connection.Open(); + } + catch (Exception) + { + return false; + } + command = new SQLiteCommand(connection); + + SQLiteDataReader reader = null; + + if (reader != null) + reader.Close(); + + Bitmap origin; + try + { + origin = new Bitmap(ImagePath); + } + catch (Exception) + { + CLog.LogError("Error loading Texture: " + ImagePath); + tex = new STexture(-1); + + if (reader != null) + { + reader.Close(); + reader.Dispose(); + } + command.Dispose(); + connection.Close(); + connection.Dispose(); + + return false; + } + + int w = origin.Width; + int h = origin.Height; + + Bitmap bmp = new Bitmap(w, h); + Graphics g = Graphics.FromImage(bmp); + g.DrawImage(origin, new Rectangle(0, 0, w, h)); + g.Dispose(); + tex = CDraw.AddTexture(bmp); + byte[] data = new byte[w * h * 4]; + + BitmapData bmp_data = bmp.LockBits(new Rectangle(0, 0, bmp.Width, bmp.Height), ImageLockMode.ReadOnly, System.Drawing.Imaging.PixelFormat.Format32bppArgb); + Marshal.Copy(bmp_data.Scan0, data, 0, w * h * 4); + bmp.UnlockBits(bmp_data); + bmp.Dispose(); + + command.CommandText = "INSERT INTO Images (Path, width, height) " + + "VALUES (@path, " + w.ToString() + ", " + h.ToString() + ")"; + command.Parameters.Add("@path", System.Data.DbType.String, 0).Value = Path.GetFileName(ImagePath); + command.ExecuteNonQuery(); + + command.CommandText = "SELECT id FROM Images WHERE [Path] = @path"; + command.Parameters.Add("@path", System.Data.DbType.String, 0).Value = Path.GetFileName(ImagePath); + reader = null; + try + { + reader = command.ExecuteReader(); + } + catch (Exception) + { + throw; + } + + if (reader != null) + { + reader.Read(); + int id = reader.GetInt32(0); + reader.Close(); + command.CommandText = "INSERT INTO ImageData (ImageID, Data) " + + "VALUES ('" + id.ToString() + "', @data)"; + command.Parameters.Add("@data", System.Data.DbType.Binary, 20).Value = data; + command.ExecuteReader(); + result = true; + } + } + + return result; + } + + private static bool InitCreditsRessourcesDB() + { + SQLiteConnection connection = new SQLiteConnection(); + connection.ConnectionString = "Data Source=" + _CreditsRessourcesFilePath; + SQLiteCommand command; + + try + { + connection.Open(); + } + catch (Exception) + { + return false; + } + + command = new SQLiteCommand(connection); + command.CommandText = "SELECT Value FROM Version"; + + SQLiteDataReader reader = null; + + try + { + reader = command.ExecuteReader(); + } + catch (Exception) + { + ; + } + + if (reader == null) + { + // Log error + CLog.LogError("Can't find Credits-DB!"); + } + else if (reader.FieldCount == 0) + { + // Log error + CLog.LogError("Can't find Credits-DB! Field-Count = 0"); + } + else + { + reader.Read(); + + if (reader.GetInt32(0) < CSettings.iDatabaseHighscoreVersion) + { + // update database + } + } + + if (reader != null) + { + reader.Close(); + reader.Dispose(); + } + + command.Dispose(); + + connection.Close(); + connection.Dispose(); + + return true; + } + + private static void CreateCreditsRessourcesDB() + { + SQLiteConnection connection = new SQLiteConnection(); + connection.ConnectionString = "Data Source=" + _CreditsRessourcesFilePath; + SQLiteCommand command; + + try + { + connection.Open(); + } + catch (Exception) + { + return; + } + + command = new SQLiteCommand(connection); + + command.CommandText = "CREATE TABLE IF NOT EXISTS Version ( id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, Value INTEGER NOT NULL);"; + command.ExecuteNonQuery(); + + command.CommandText = "INSERT INTO Version (id, Value) VALUES(NULL, " + CSettings.iDatabaseCreditsRessourcesVersion.ToString() + ")"; + command.ExecuteNonQuery(); + + command.CommandText = "CREATE TABLE IF NOT EXISTS Images ( id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, " + + "Path TEXT NOT NULL, width INTEGER NOT NULL, height INTEGER NOT NULL);"; + command.ExecuteNonQuery(); + + command.CommandText = "CREATE TABLE IF NOT EXISTS ImageData ( id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, " + + "ImageID INTEGER NOT NULL, Data BLOB NOT NULL);"; + command.ExecuteNonQuery(); + + command.Dispose(); + connection.Close(); + connection.Dispose(); + } + #endregion CreditsRessources + private static byte[] GetBytes(SQLiteDataReader reader) { const int CHUNK_SIZE = 2 * 1024; @@ -973,4 +1493,4 @@ namespace Vocaluxe.Base return t70.Ticks; } } -} +} \ No newline at end of file diff --git a/Vocaluxe/Base/CDraw.cs b/Vocaluxe/Base/CDraw.cs index 5c1129dd..6b00f4b9 100644 --- a/Vocaluxe/Base/CDraw.cs +++ b/Vocaluxe/Base/CDraw.cs @@ -332,6 +332,7 @@ namespace Vocaluxe.Base { #region private vars private string _TextureName; + private STexture _Texture; private SRectF _Rect; private float _Size; private SColorF _Color; @@ -346,7 +347,7 @@ namespace Vocaluxe.Base private float _Vsize; //size changing speed: period [s] private float _LastTime; private EParticeType _Type; - + private Stopwatch _Timer; #endregion private vars @@ -399,6 +400,7 @@ namespace Vocaluxe.Base public CParticle(string textureName, SColorF color, float x, float y, float size, float maxage, float z, float vx, float vy, float vr, float vsize, EParticeType type) { _TextureName = textureName; + _Texture = new STexture(-1); _Color = color; _Rect = new SRectF(x, y, size, size, z); _Size = size; @@ -416,6 +418,29 @@ namespace Vocaluxe.Base _MaxAge = maxage; _Rotation = (float)(CGame.Rand.NextDouble() * 360.0); } + + public CParticle(STexture texture, SColorF color, float x, float y, float size, float maxage, float z, float vx, float vy, float vr, float vsize, EParticeType type) + { + _TextureName = String.Empty; + _Texture = texture; + _Color = color; + _Rect = new SRectF(x, y, size, size, z); + _Size = size; + _Alpha = 1f; + _Angle = 0f; + _Vx = vx; + _Vy = vy; + _Vr = vr; + _Vsize = vsize; + _LastTime = 0f; + _Type = type; + + _Timer = new Stopwatch(); + _Age = 0f; + _MaxAge = maxage; + _Rotation = (float)(CGame.Rand.NextDouble() * 360.0); + } + #endregion Constructors public void Update() @@ -566,7 +591,10 @@ namespace Vocaluxe.Base public void Draw() { - CDraw.DrawTexture(CTheme.GetSkinTexture(_TextureName), _Rect, new SColorF(_Color.R, _Color.G, _Color.B, _Color.A * Alpha2 * _Alpha)); + if (_TextureName != String.Empty) + CDraw.DrawTexture(CTheme.GetSkinTexture(_TextureName), _Rect, new SColorF(_Color.R, _Color.G, _Color.B, _Color.A * Alpha2 * _Alpha)); + else + CDraw.DrawTexture(_Texture, _Rect, new SColorF(_Color.R, _Color.G, _Color.B, _Color.A * Alpha2 * _Alpha)); } } @@ -574,6 +602,7 @@ namespace Vocaluxe.Base { private List _Stars; private string _TextureName; + private STexture _Texture; private int _MaxNumber; private SRectF _Area; private SColorF _Color; @@ -592,12 +621,33 @@ namespace Vocaluxe.Base } } + public SRectF Area + { + get { return _Area; } + set { _Area = value; } + } + public CParticleEffect(int MaxNumber, SColorF Color, SRectF Area, string TextureName, float Size, EParticeType Type) { _Stars = new List(); _Area = Area; _Color = Color; _TextureName = TextureName; + _Texture = new STexture(-1); + _MaxNumber = MaxNumber; + _Size = Size; + _Type = Type; + _SpawnTimer = new Stopwatch(); + _NextSpawnTime = 0f; + } + + public CParticleEffect(int MaxNumber, SColorF Color, SRectF Area, STexture texture, float Size, EParticeType Type) + { + _Stars = new List(); + _Area = Area; + _Color = Color; + _TextureName = String.Empty; + _Texture = texture; _MaxNumber = MaxNumber; _Size = Size; _Type = Type; @@ -695,10 +745,21 @@ namespace Vocaluxe.Base if (h < 0) h = 0; - CParticle star = new CParticle(_TextureName, _Color, - CGame.Rand.Next(w) + _Area.X - size / 4f, - CGame.Rand.Next(h) + _Area.Y - size / 4f, - size, lifetime, _Area.Z, vx, vy, vr, vsize, _Type); + CParticle star; + if (_TextureName != String.Empty) + { + star = new CParticle(_TextureName, _Color, + CGame.Rand.Next(w) + _Area.X - size / 4f, + CGame.Rand.Next(h) + _Area.Y - size / 4f, + size, lifetime, _Area.Z, vx, vy, vr, vsize, _Type); + } + else + { + star = new CParticle(_Texture, _Color, + CGame.Rand.Next(w) + _Area.X - size / 4f, + CGame.Rand.Next(h) + _Area.Y - size / 4f, + size, lifetime, _Area.Z, vx, vy, vr, vsize, _Type); + } _Stars.Add(star); } diff --git a/Vocaluxe/Base/CGame.cs b/Vocaluxe/Base/CGame.cs index 07ea2d2d..9c2b6c7d 100644 --- a/Vocaluxe/Base/CGame.cs +++ b/Vocaluxe/Base/CGame.cs @@ -247,72 +247,60 @@ namespace Vocaluxe.Base for (int p = 0; p < _NumPlayer; p++) { - CSound.AnalyzeBuffer(p); - - if (CSound.RecordToneValid(p) || DEBUG_HIT) + + for (int beat = _OldBeatD + 1; beat <= _CurrentBeatD; beat++) { - for (int beat = _OldBeatD + 1; beat <= _CurrentBeatD; beat++) - { - CLine[] lines = song.Notes.GetLines(_Player[p].LineNr).Line; - int Line = -1; + CLine[] lines = song.Notes.GetLines(_Player[p].LineNr).Line; + int Line = -1; - for (int j = 0; j < lines.Length; j++) + for (int j = 0; j < lines.Length; j++) + { + if (beat >= lines[j].StartBeat && beat <= lines[j].EndBeat) { - if (beat >= lines[j].StartBeat && beat <= lines[j].EndBeat) - { - Line = j; - break; - } + Line = j; + break; + } + } + + if (Line >= 0) + { + if (Line != _Player[p].CurrentLine) + _Player[p].CurrentNote = -1; + + _Player[p].CurrentLine = Line; + + while (_Player[p].SingLine.Count <= Line) + { + _Player[p].SingLine.Add(new CLine()); } - if (Line >= 0) + CNote[] notes = lines[Line].Notes; + int Note = -1; + for (int j = 0; j < notes.Length; j++) { - if (Line != _Player[p].CurrentLine) - _Player[p].CurrentNote = -1; - - _Player[p].CurrentLine = Line; - - while (_Player[p].SingLine.Count <= Line) + if (beat >= notes[j].StartBeat && beat <= notes[j].EndBeat) { - _Player[p].SingLine.Add(new CLine()); + Note = j; + break; } + } - CNote[] notes = lines[Line].Notes; - int Note = -1; - for (int j = 0; j < notes.Length; j++) - { - if (beat >= notes[j].StartBeat && beat <= notes[j].EndBeat) - { - if (notes[j].PointsForBeat > 0) - Note = j; - break; - } - } + if (Note >= 0) + { + _Player[p].CurrentNote = Note; - if (Note >= 0) + if (Line == lines.Length - 1) { - if (Line == lines.Length - 1) + if (Note == lines[Line].NoteCount - 1) { - if (Note == lines[Line].NoteCount - 1) - { + if (notes[Note].EndBeat == beat) _Player[p].SongFinished = true; - } } } - if (Note >= 0 && notes[Note].PointsForBeat > 0) + CSound.AnalyzeBuffer(p); + if (notes[Note].PointsForBeat > 0 && (CSound.RecordToneValid(p) || DEBUG_HIT)) { - _Player[p].CurrentNote = Note; - - if (Line == lines.Length - 1) - { - if (Note == lines[Line].NoteCount - 1) - { - if (notes[Note].EndBeat == beat) - _Player[p].SongFinished = true; - } - } - int Tone = notes[Note].Tone; int TonePlayer = CSound.RecordGetTone(p); @@ -383,8 +371,8 @@ namespace Vocaluxe.Base } } } - } + } } _OldBeatD = _CurrentBeatD; diff --git a/Vocaluxe/Base/CSettings.cs b/Vocaluxe/Base/CSettings.cs index 24ffda63..2f751e83 100644 --- a/Vocaluxe/Base/CSettings.cs +++ b/Vocaluxe/Base/CSettings.cs @@ -47,10 +47,11 @@ namespace Vocaluxe.Base public const int iVersionSub = 0; // steps public const ERevision VersionRevision = ERevision.Beta; - public const int iBuild = 53; // Increase on every published version! Never Reset! + public const int iBuild = 54; // Increase on every published version! Never Reset! public const int iDatabaseHighscoreVersion = 1; public const int iDatabaseCoverVersion = 1; + public const int iDatabaseCreditsRessourcesVersion = 1; public static int iRenderW = 1280; @@ -69,8 +70,10 @@ namespace Vocaluxe.Base public const string sFileCover = "Cover.xml"; public const string sFileFonts = "Fonts.xml"; + public const string sFileOldHighscoreDB = "Ultrastar.db"; public static string sFileHighscoreDB = "HighscoreDB.sqlite"; public const string sFileCoverDB = "CoverDB.sqlite"; + public const string sFileCreditsRessourcesDB = "CreditsRessourcesDB.sqlite"; public const string sFilePerformanceLog = "Performance.log"; public const string sFileErrorLog = "Error.log"; public const string sFileBenchmarkLog = "Benchmark.log"; diff --git a/Vocaluxe/Base/CSound.cs b/Vocaluxe/Base/CSound.cs index b201e6fb..2a2f9d0f 100644 --- a/Vocaluxe/Base/CSound.cs +++ b/Vocaluxe/Base/CSound.cs @@ -501,7 +501,7 @@ namespace Vocaluxe.Base long ticks = _Timer.ElapsedTicks; float dt = _Timer.ElapsedMilliseconds / 1000f; - if (nanosecPerTick > 0L) + if (Stopwatch.IsHighResolution) dt = (float)(ticks * nanosecPerTick / 1000000000.0); return _SetValue + dt; @@ -581,7 +581,7 @@ namespace Vocaluxe.Base long ticks = _STimer.ElapsedTicks; float dt = _STimer.ElapsedMilliseconds / 1000f; - if (nanosecPerTick > 0L) + if (Stopwatch.IsHighResolution) dt = (float)(ticks * nanosecPerTick / 1000000000.0); return _CurrentTime + dt; @@ -617,9 +617,9 @@ namespace Vocaluxe.Base long ticks = _STimer.ElapsedTicks; float dt = _STimer.ElapsedMilliseconds / 1000f; - if (nanosecPerTick > 0L) + if (Stopwatch.IsHighResolution) dt = (float)(ticks * nanosecPerTick / 1000000000.0); - + float Ts = 0f; if (dt > 0) Ts = 1 / (_T / dt + 1); diff --git a/Vocaluxe/Doc/Roadmap.txt b/Vocaluxe/Doc/Roadmap.txt index ed4f88e1..66024f04 100644 --- a/Vocaluxe/Doc/Roadmap.txt +++ b/Vocaluxe/Doc/Roadmap.txt @@ -79,7 +79,8 @@ + v0.2.0: - Medley - WiiMote Support - - Jukebox light + - Jukebox light (bg music controls) + - Show Avatars and Player Name in Sing Screen (and perhaps some other screens) + v0.3.0: - Song Editor diff --git a/Vocaluxe/Lib/Song/CSong.cs b/Vocaluxe/Lib/Song/CSong.cs index bfb8e7b2..37147e7c 100644 --- a/Vocaluxe/Lib/Song/CSong.cs +++ b/Vocaluxe/Lib/Song/CSong.cs @@ -242,6 +242,11 @@ namespace Vocaluxe.Lib.Song this.MP3FileName = Value; HeaderFlags |= EHeaderFlags.MP3; } + else + { + CLog.LogError("Can't find audio file: " + Path.Combine(this.Folder, Value)); + return false; + } break; case "BPM": if (CHelper.TryParse(Value, out this.BPM)) @@ -288,6 +293,9 @@ namespace Vocaluxe.Lib.Song case "VIDEO": if (File.Exists(Path.Combine(this.Folder, Value))) this.VideoFileName = Value; + else + CLog.LogError("Can't find video file: " + Path.Combine(this.Folder, Value)); + break; case "VIDEOGAP": CHelper.TryParse(Value, out this.VideoGap); diff --git a/Vocaluxe/Lib/Sound/CPortAudioPlay.cs b/Vocaluxe/Lib/Sound/CPortAudioPlay.cs index d39c6942..dc23bc91 100644 --- a/Vocaluxe/Lib/Sound/CPortAudioPlay.cs +++ b/Vocaluxe/Lib/Sound/CPortAudioPlay.cs @@ -620,7 +620,7 @@ namespace Vocaluxe.Lib.Sound IntPtr.Zero, ref outputParams, format.SamplesPerSecond, - 0,//CConfig.PortAudioBufferSize, + (uint)CConfig.AudioBufferSize, PortAudio.PaStreamFlags.paNoFlag, _paStreamCallback, data)); @@ -677,6 +677,21 @@ namespace Vocaluxe.Lib.Sound { if (EventDecode.WaitOne(10)) { + lock (MutexSyncSignals) + { + if (_SetSkip) + { + _skip = true; + EventDecode.Set(); + _waiting = true; + } + + _SetSkip = false; + + _Start = _SetStart; + _Loop = _SetLoop; + } + if (_skip) { DoSkip(); @@ -689,25 +704,6 @@ namespace Vocaluxe.Lib.Sound Update(); } } - - if (!_terminated) - { - lock (MutexSyncSignals) - { - if (_SetSkip) - { - _skip = true; - EventDecode.Set(); - _waiting = true; - } - - _SetSkip = false; - - _Start = _SetStart; - _Loop = _SetLoop; - } - } - } DoFree(); @@ -844,10 +840,10 @@ namespace Vocaluxe.Lib.Sound else _waiting = true; - float latency = buf.Length / _BytesPerSecond; + float latency = buf.Length / _BytesPerSecond + CConfig.AudioLatency/1000f; float time = _TimeCode - _data.BytesNotRead / _BytesPerSecond - latency; - _CurrentTime = _SyncTimer.Update(time); + _CurrentTime = time; // _SyncTimer.Update(time); } try @@ -856,7 +852,7 @@ namespace Vocaluxe.Lib.Sound } catch (Exception e) { - Console.WriteLine(e.ToString()); + CLog.LogError("Error PortAudio.StreamCallback: " + e.Message); } return PortAudio.PaStreamCallbackResult.paContinue; diff --git a/Vocaluxe/Lib/Sound/CPortAudioRecord.cs b/Vocaluxe/Lib/Sound/CPortAudioRecord.cs index 04096ab9..87bff1ab 100644 --- a/Vocaluxe/Lib/Sound/CPortAudioRecord.cs +++ b/Vocaluxe/Lib/Sound/CPortAudioRecord.cs @@ -245,6 +245,9 @@ namespace Vocaluxe.Lib.Sound if (!_initialized) return null; + if (_Devices.Count == 0) + return null; + return _Devices.ToArray(); } diff --git a/Vocaluxe/Lib/Video/CVideoDecoderFFmpeg.cs b/Vocaluxe/Lib/Video/CVideoDecoderFFmpeg.cs index e6031786..b8c38f49 100644 --- a/Vocaluxe/Lib/Video/CVideoDecoderFFmpeg.cs +++ b/Vocaluxe/Lib/Video/CVideoDecoderFFmpeg.cs @@ -214,33 +214,34 @@ namespace Vocaluxe.Lib.Video class Decoder { - private IntPtr _instance = IntPtr.Zero; - private IntPtr _videodecoder = IntPtr.Zero; - private IntPtr _package = IntPtr.Zero; - private Stopwatch _Timer = new Stopwatch(); - private CLOSEPROC _Closeproc; - private int _StreamID; - private string _FileName; + private IntPtr _instance = IntPtr.Zero; // acinerella instance + private IntPtr _videodecoder = IntPtr.Zero; // acinerella video decoder instance + + private Stopwatch _LoopTimer = new Stopwatch(); + private CLOSEPROC _Closeproc; // delegate for stream closing + private int _StreamID; // stream ID for stream closing + private string _FileName; // current video file name - private FileStream _fs; - private TAc_read_callback _rc; - private TAc_seek_callback _sc; + private FileStream _fs; // video file stream + private TAc_read_callback _rc; // read callback for acinerella + private TAc_seek_callback _sc; // seek callback for acinerella private bool _FileOpened = false; - private float _VideoTimeBase = 0f; - private float _VideoTime = 0f; - private float _ActualVideoTime = 0f; - private bool _waiting = false; - private bool _skip = false; + private float _VideoTimeBase = 0f; // frame time + private float _VideoDecoderTime = 0f; // time of last decoded frame + private float _CurrentVideoTime = 0f; // current video position + private bool _BufferFull = false; // buffer is full, waiting for free frame slot + + private bool _skip = false; // do skip private float _Time = 0f; private float _Gap = 0f; private float _Start = 0f; private bool _Loop = false; private float _Duration = 0f; - private float _VideoSkipTime = 0f; - private float _NegativeSkipTime = 0f; - private float _SkipTime = 0f; + private float _VideoSkipTime = 0f; // = VideoGap + private float _SkipTime = 0f; // = Start + VideoGap + private bool _Paused = false; private bool _NoMoreFrames = false; private bool _Finished = false; @@ -260,7 +261,6 @@ namespace Vocaluxe.Lib.Video AutoResetEvent EventDecode = new AutoResetEvent(false); SFrameBuffer[] _FrameBuffer = new SFrameBuffer[5]; Object MutexFramebuffer = new Object(); - Object MutexFrame = new Object(); Object MutexSyncSignals = new Object(); public Decoder() @@ -293,15 +293,16 @@ namespace Vocaluxe.Lib.Video get { return _Paused; } set { - _Paused = value; lock (MutexSyncSignals) { + _Paused = value; if (_Paused) - _Timer.Stop(); + { + _LoopTimer.Stop(); + } else { - _Timer.Start(); - EventDecode.Set(); + _LoopTimer.Start(); } } } @@ -330,7 +331,7 @@ namespace Vocaluxe.Lib.Video return false; _FileName = FileName; - _thread.Priority = ThreadPriority.Highest; + _thread.Priority = ThreadPriority.Normal; _thread.Name = Path.GetFileName(FileName); _thread.Start(); return true; @@ -348,12 +349,12 @@ namespace Vocaluxe.Lib.Video { lock (MutexSyncSignals) { - _SetTime += _Timer.ElapsedMilliseconds / 1000f; + _SetTime += _LoopTimer.ElapsedMilliseconds / 1000f; VideoTime = _SetTime; - _Timer.Stop(); - _Timer.Reset(); - _Timer.Start(); + _LoopTimer.Stop(); + _LoopTimer.Reset(); + _LoopTimer.Start(); } @@ -362,12 +363,12 @@ namespace Vocaluxe.Lib.Video return true; } - if (_Time - _SetTime >= 0 || _SetTime > Time) + if (_SetTime != Time) { lock (MutexSyncSignals) { _SetTime = Time; - VideoTime = _VideoTime; + VideoTime = _CurrentVideoTime; } UploadNewFrame(ref frame); EventDecode.Set(); @@ -394,17 +395,15 @@ namespace Vocaluxe.Lib.Video #region Threading private void DoSkip() { - lock (MutexSyncSignals) - { - _VideoSkipTime = _Gap; - _NegativeSkipTime = _Start + _Gap; - _SkipTime = _Start + _Gap; - _BeforeLoop = false; - } + string extension = Path.GetExtension(_FileName); //just a workaround for the mkv-search-bug in ffmpeg - if (_SkipTime > 0) + _VideoSkipTime = _Gap; + _SkipTime = _Start + _Gap; + _BeforeLoop = false; + + if (_SkipTime > 0 && extension != ".mkv") { - _VideoTime = _SkipTime; + _VideoDecoderTime = _SkipTime; try { CAcinerella.ac_seek(_videodecoder, -1, (Int64)(_SkipTime * 1000f)); @@ -417,10 +416,11 @@ namespace Vocaluxe.Lib.Video } else { - _VideoTime = 0f; + _VideoDecoderTime = 0f; try { - CAcinerella.ac_seek(_videodecoder, -1, (Int64)0); + if (extension != ".mkv") + CAcinerella.ac_seek(_videodecoder, -1, (Int64)0); } catch (Exception e) { @@ -428,7 +428,10 @@ namespace Vocaluxe.Lib.Video } } - _ActualVideoTime = _VideoTime; + lock (MutexSyncSignals) + { + _CurrentVideoTime = _VideoDecoderTime; + } lock (MutexFramebuffer) { @@ -439,46 +442,41 @@ namespace Vocaluxe.Lib.Video } } - _waiting = false; + _BufferFull = false; + _skip = false; + EventDecode.Set(); } private void Execute() { DoOpen(); - + EventDecode.Set(); + while (!_terminated) { - if (EventDecode.WaitOne(1) || !_waiting) - { - if (_skip) - { - DoSkip(); - _skip = false; - } - - if (!_waiting) - { - DoDecode(); - } - - if (_waiting) - Copy(); - } - if (!_terminated) + if (EventDecode.WaitOne(10)) { lock (MutexSyncSignals) { _Time = _SetTime; if (_SetSkip) _skip = true; - + _SetSkip = false; _Gap = _SetGap; _Start = _SetStart; _Loop = _SetLoop; } + + if (_skip) + DoSkip(); + + if (!_BufferFull) + DoDecode(); + + if (_BufferFull) + Copy(); } - } DoFree(); @@ -544,7 +542,7 @@ namespace Vocaluxe.Lib.Video if (Videodecoder.stream_info.video_info.frames_per_second > 0) _VideoTimeBase = 1f / (float)Videodecoder.stream_info.video_info.frames_per_second; - _VideoTime = 0f; + _VideoDecoderTime = 0f; _Time = 0f; for (int i = 0; i < _FrameBuffer.Length; i++) @@ -563,14 +561,14 @@ namespace Vocaluxe.Lib.Video if (!_FileOpened) return; - if (_Paused || _NoMoreFrames) + if (_Paused || _NoMoreFrames || _BufferFull) return; - if ((_NegativeSkipTime < 0f) && (_Time + _NegativeSkipTime >= 0f)) - _NegativeSkipTime = 0f; + if ((_SkipTime < 0f) && (_Time + _SkipTime >= 0f)) + _SkipTime = 0f; float myTime = _Time + _VideoSkipTime; - float TimeDifference = myTime - _VideoTime; + float TimeDifference = myTime - _VideoDecoderTime; bool DropFrame = false; if (TimeDifference >= (FRAMEDROPCOUNT - 1) * _VideoTimeBase) @@ -595,17 +593,13 @@ namespace Vocaluxe.Lib.Video if (!_BeforeLoop && (!DropFrame || FrameFinished != 0)) { - lock (MutexFrame) + try { - try - { - FrameFinished = CAcinerella.ac_get_frame(_instance, _videodecoder); - } - catch (Exception) - { - CLog.LogError("Error AcGetFrame " + _FileName); - } - + FrameFinished = CAcinerella.ac_get_frame(_instance, _videodecoder); + } + catch (Exception) + { + CLog.LogError("Error AcGetFrame " + _FileName); } } @@ -637,7 +631,6 @@ namespace Vocaluxe.Lib.Video lock (MutexSyncSignals) { - _ActualVideoTime = 0f; _Start = 0f; _Gap = 0f; _SetTime = 0f; @@ -658,6 +651,7 @@ namespace Vocaluxe.Lib.Video private void Copy() { int num = -1; + lock (MutexFramebuffer) { for (int i = 0; i < _FrameBuffer.Length; i++) @@ -670,29 +664,38 @@ namespace Vocaluxe.Lib.Video } if (num == -1) - _waiting = true; + { + _BufferFull = true; + EventDecode.Reset(); + } else { + TAc_decoder Videodecoder = (TAc_decoder)Marshal.PtrToStructure(_videodecoder, typeof(TAc_decoder)); - lock (MutexFrame) + _VideoDecoderTime = (float)Videodecoder.timecode; + _FrameBuffer[num].time = _VideoDecoderTime; + + if (Videodecoder.buffer != IntPtr.Zero) { - TAc_decoder Videodecoder = (TAc_decoder)Marshal.PtrToStructure(_videodecoder, typeof(TAc_decoder)); + Marshal.Copy(Videodecoder.buffer, _FrameBuffer[num].data, 0, _Width * _Height * 4); - _FrameBuffer[num].time = _VideoTime; - _VideoTime = (float)Videodecoder.timecode; - - if (Videodecoder.buffer != IntPtr.Zero) - { - Marshal.Copy(Videodecoder.buffer, _FrameBuffer[num].data, 0, _Width * _Height * 4); - - _FrameBuffer[num].displayed = false; - } + _FrameBuffer[num].displayed = false; } - - _waiting = false; + int numfull = 0; + for (int i = 0; i < _FrameBuffer.Length; i++) + { + if (!_FrameBuffer[i].displayed) + numfull++; + } + + if (numfull < _FrameBuffer.Length) + _BufferFull = false; } } + + if (!_BufferFull) + EventDecode.Set(); } private void UploadNewFrame(ref STexture frame) @@ -715,8 +718,13 @@ namespace Vocaluxe.Lib.Video { CDraw.UpdateTexture(ref frame, ref _FrameBuffer[num].data); } - _ActualVideoTime = _FrameBuffer[num].time; + + lock (MutexSyncSignals) + { + _CurrentVideoTime = _FrameBuffer[num].time; + } _Finished = false; + EventDecode.Set(); } else { @@ -724,7 +732,6 @@ namespace Vocaluxe.Lib.Video _Finished = true; } } - EventDecode.Set(); } private int FindFrame() @@ -736,27 +743,28 @@ namespace Vocaluxe.Lib.Video { float td = _SetTime + _VideoSkipTime - _FrameBuffer[i].time; + if (td > _VideoTimeBase * 2 || td < -1f * _VideoTimeBase * (_FrameBuffer.Length + 1)) + { + _FrameBuffer[i].displayed = true; + } + if (!_FrameBuffer[i].displayed && (td < diff) && (td > _VideoTimeBase)) { diff = Math.Abs(_FrameBuffer[i].time - _SetTime - _VideoSkipTime); Result = i; } - - if (td > _VideoTimeBase * 2) - _FrameBuffer[i].displayed = true; } if (Result != -1) + { _FrameBuffer[Result].displayed = true; + } return Result; } private void DoFree() { - if (_package != IntPtr.Zero) - CAcinerella.ac_free_package(_package); - if (_videodecoder != IntPtr.Zero) CAcinerella.ac_free_decoder(_videodecoder); diff --git a/Vocaluxe/Menu/CGraphics.cs b/Vocaluxe/Menu/CGraphics.cs index 91908275..d64691f1 100644 --- a/Vocaluxe/Menu/CGraphics.cs +++ b/Vocaluxe/Menu/CGraphics.cs @@ -31,6 +31,8 @@ namespace Vocaluxe.Menu ScreenNames = 14, + ScreenCredits = 15, + ScreenNull = -1 } @@ -212,6 +214,7 @@ namespace Vocaluxe.Menu { // Add Screens, must be the same order as in EScreens! CLog.StartBenchmark(1, "Build Screen List"); + _Screens.Add(new CScreenTest()); _Screens.Add(new CScreenLoad()); _Screens.Add(new CScreenMain()); @@ -227,8 +230,10 @@ namespace Vocaluxe.Menu _Screens.Add(new CScreenOptionsVideo()); _Screens.Add(new CScreenOptionsTheme()); _Screens.Add(new CScreenNames()); + _Screens.Add(new CScreenCredits()); _PopupScreens.Add(new CPopupScreenPlayerControl()); + CLog.StopBenchmark(1, "Build Screen List"); _CurrentScreen = EScreens.ScreenLoad; @@ -255,9 +260,12 @@ namespace Vocaluxe.Menu for (int i = 0; i < _Screens.Count; i++) { - CLog.StartBenchmark(1, "Load Theme " + Enum.GetNames(typeof(EScreens))[i]); - _Screens[i].LoadTheme(); - CLog.StopBenchmark(1, "Load Theme " + Enum.GetNames(typeof(EScreens))[i]); + if (Enum.GetNames(typeof(EScreens))[i] != Enum.GetName(typeof(EScreens), (int)EScreens.ScreenCredits)) + { + CLog.StartBenchmark(1, "Load Theme " + Enum.GetNames(typeof(EScreens))[i]); + _Screens[i].LoadTheme(); + CLog.StopBenchmark(1, "Load Theme " + Enum.GetNames(typeof(EScreens))[i]); + } } for (int i = 0; i < _PopupScreens.Count; i++) @@ -271,7 +279,10 @@ namespace Vocaluxe.Menu ReloadCursor(); for (int i = 0; i < _Screens.Count; i++) { - _Screens[i].ReloadTheme(); + if (Enum.GetNames(typeof(EScreens))[i] != Enum.GetName(typeof(EScreens), (int)EScreens.ScreenCredits)) + { + _Screens[i].ReloadTheme(); + } } for (int i = 0; i < _PopupScreens.Count; i++) @@ -285,7 +296,10 @@ namespace Vocaluxe.Menu ReloadCursor(); for (int i = 0; i < _Screens.Count; i++) { - _Screens[i].ReloadTextures(); + if (Enum.GetNames(typeof(EScreens))[i] != Enum.GetName(typeof(EScreens), (int)EScreens.ScreenCredits)) + { + _Screens[i].ReloadTextures(); + } } for (int i = 0; i < _PopupScreens.Count; i++) @@ -299,7 +313,10 @@ namespace Vocaluxe.Menu CTheme.SaveTheme(); for (int i = 0; i < _Screens.Count; i++) { - _Screens[i].SaveTheme(); + if (Enum.GetNames(typeof(EScreens))[i] != Enum.GetName(typeof(EScreens), (int)EScreens.ScreenCredits)) + { + _Screens[i].SaveTheme(); + } } for (int i = 0; i < _PopupScreens.Count; i++) diff --git a/Vocaluxe/Menu/CLyric.cs b/Vocaluxe/Menu/CLyric.cs index 24651cc3..8a70e0ab 100644 --- a/Vocaluxe/Menu/CLyric.cs +++ b/Vocaluxe/Menu/CLyric.cs @@ -15,7 +15,8 @@ namespace Vocaluxe.Menu { Fill, Jump, - Slide + Slide, + Zoom } struct SNote @@ -23,6 +24,7 @@ namespace Vocaluxe.Menu public string Text; public int StartBeat; public int EndBeat; + public int Duration; public ENoteType Type; } @@ -205,6 +207,7 @@ namespace Vocaluxe.Menu n.Text = note.Text; n.StartBeat = note.StartBeat; n.EndBeat = note.EndBeat; + n.Duration = note.Duration; n.Type = note.NoteType; _Text.Text = note.Text; @@ -229,6 +232,7 @@ namespace Vocaluxe.Menu return _X - _width / 2; } + #region draw public void Draw(float ActualBeat) { if (Visible || CSettings.GameState == EGameState.EditTheme) @@ -236,12 +240,17 @@ namespace Vocaluxe.Menu switch (_Style) { case ELyricStyle.Fill: + DrawFill(ActualBeat); break; case ELyricStyle.Jump: + DrawJump(ActualBeat); break; case ELyricStyle.Slide: DrawSlide(ActualBeat); break; + case ELyricStyle.Zoom: + DrawZoom(ActualBeat); + break; default: DrawSlide(ActualBeat); break; @@ -257,6 +266,8 @@ namespace Vocaluxe.Menu { _Text.X = x; _Text.Style = EStyle.Bold; + _Text.Text = note.Text; + RectangleF rect = CDraw.GetTextBounds(_Text); if (note.Type == ENoteType.Freestyle) _Text.Style = EStyle.BoldItalic; @@ -266,7 +277,7 @@ namespace Vocaluxe.Menu if (CurrentBeat <= note.EndBeat) { _Text.Color = ColorProcessed; - _Text.Text = note.Text; + float diff = note.EndBeat - note.StartBeat; if (diff == 0) @@ -280,23 +291,202 @@ namespace Vocaluxe.Menu } else { - _Text.Color = ColorProcessed; - _Text.Text = note.Text; + _Text.Color = ColorProcessed; _Text.Draw(); } } else { - _Text.Color = Color; - _Text.Text = note.Text; + _Text.Color = Color; _Text.Draw(); } - RectangleF rect = CDraw.GetTextBounds(_Text); + x += rect.Width; } } + private void DrawZoom(float CurrentBeat) + { + float x = _X - _width / 2; // most left position + + //find last active note + int last_note = -1; + for (int i = 0; i < _Notes.Count; i++) + { + if (CurrentBeat >= _Notes[i].StartBeat) + last_note = i; + } + + int zoom_note = -1; + float zoomx = 0f; + + int current_note = -1; + foreach (SNote note in _Notes) + { + current_note++; + + _Text.X = x; + _Text.Style = EStyle.Bold; + _Text.Text = note.Text; + RectangleF rect = CDraw.GetTextBounds(_Text); + + if (note.Type == ENoteType.Freestyle) + _Text.Style = EStyle.BoldItalic; + + if (CurrentBeat >= note.StartBeat) + { + if (CurrentBeat <= note.EndBeat) + { + zoom_note = current_note; + zoomx = _Text.X; + } + else + { + // already passed + if (current_note == last_note) + _Text.Color = ColorProcessed; + else + _Text.Color = Color; + + _Text.Draw(); + } + + } + else + { + // not passed + _Text.Color = Color; + _Text.Draw(); + } + + x += rect.Width; + } + + if (zoom_note > -1) + { + if (_Notes[zoom_note].Duration == 0) + return; + + _Text.X = zoomx; + _Text.Text = _Notes[zoom_note].Text; + _Text.Color = ColorProcessed; + _Text.Style = EStyle.Bold; + + if (_Notes[zoom_note].Type == ENoteType.Freestyle) + _Text.Style = EStyle.BoldItalic; + + RectangleF rect = CDraw.GetTextBounds(_Text); + + float diff = _Notes[zoom_note].EndBeat - _Notes[zoom_note].StartBeat; + + float p = (CurrentBeat - _Notes[zoom_note].StartBeat) / _Notes[zoom_note].Duration; + if (p > 1f) + p = 1f; + + p = 1f - p; + + float ty = _Text.Y; + float tx = _Text.X; + float th = _Text.Height; + float tz = _Text.Z; + + _Text.Height += _Text.Height * p * 0.4f; + RectangleF rectz = CDraw.GetTextBounds(_Text); + _Text.X -= (rectz.Width - rect.Width) / 2f; + _Text.Y -= (rectz.Height - rect.Height) / 2f; + _Text.Z -= 0.1f; + + _Text.Draw(); + + _Text.Y = ty; + _Text.X = tx; + _Text.Height = th; + _Text.Z = tz; + } + } + + private void DrawFill(float CurrentBeat) + { + float x = _X - _width / 2; // most left position + + foreach (SNote note in _Notes) + { + _Text.X = x; + _Text.Style = EStyle.Bold; + _Text.Text = note.Text; + RectangleF rect = CDraw.GetTextBounds(_Text); + + if (note.Type == ENoteType.Freestyle) + _Text.Style = EStyle.BoldItalic; + + if (CurrentBeat >= note.StartBeat) + { + _Text.Color = ColorProcessed; + _Text.Draw(); + } + else + { + // not passed + _Text.Color = Color; + _Text.Draw(); + } + + x += rect.Width; + } + } + + private void DrawJump(float CurrentBeat) + { + float x = _X - _width / 2; // most left position + + foreach (SNote note in _Notes) + { + _Text.X = x; + _Text.Style = EStyle.Bold; + _Text.Text = note.Text; + RectangleF rect = CDraw.GetTextBounds(_Text); + + if (note.Type == ENoteType.Freestyle) + _Text.Style = EStyle.BoldItalic; + + if (CurrentBeat >= note.StartBeat) + { + if (CurrentBeat <= note.EndBeat) + { + _Text.Color = ColorProcessed; + + float diff = note.EndBeat - note.StartBeat; + if (diff == 0) + _Text.Draw(); + else + { + float y = _Text.Y; + _Text.Y -= _Text.Height * 0.1f; + _Text.Draw(); + _Text.Y = y; + } + } + else + { + // already passed + _Text.Color = Color; + _Text.Draw(); + } + + } + else + { + // not passed + _Text.Color = Color; + _Text.Draw(); + } + + x += rect.Width; + } + } + #endregion draw + public void UnloadTextures() { } diff --git a/Vocaluxe/Program.cs b/Vocaluxe/Program.cs index 2239d6ff..32cde09c 100644 --- a/Vocaluxe/Program.cs +++ b/Vocaluxe/Program.cs @@ -141,7 +141,7 @@ namespace Vocaluxe CLog.StopBenchmark(0, "Init Game"); Application.DoEvents(); - + System.Threading.Thread.Sleep(10000); // Start Main Loop _SplashScreen.Close(); CDraw.MainLoop(); @@ -192,6 +192,8 @@ namespace Vocaluxe class SplashScreen : Form { + Bitmap logo; + public SplashScreen() { string path = Path.Combine(Environment.CurrentDirectory, Path.Combine(CSettings.sFolderGraphics, CSettings.sLogo)); @@ -199,9 +201,8 @@ namespace Vocaluxe { try { - Bitmap logo = new Bitmap(path); + logo = new Bitmap(path); this.ClientSize = new Size(logo.Width, logo.Height); - this.BackgroundImage = logo; } catch (Exception e) { @@ -226,11 +227,27 @@ namespace Vocaluxe } else CLog.LogError("Can't find " + path); - + + SetStyle(ControlStyles.SupportsTransparentBackColor, true); + this.BackColor = Color.Transparent; + this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.None; this.Text = CSettings.sProgramName; this.CenterToScreen(); this.Show(); } + + protected override void OnPaint(System.Windows.Forms.PaintEventArgs e) + { + } + + protected override void OnPaintBackground(System.Windows.Forms.PaintEventArgs e) + { + if (logo == null) + return; + + Graphics g = e.Graphics; + g.DrawImage(logo, new Rectangle(0, 0, this.Width, this.Height)); + } } } diff --git a/Vocaluxe/Screens/CScreenCredits.cs b/Vocaluxe/Screens/CScreenCredits.cs new file mode 100644 index 00000000..278b4720 --- /dev/null +++ b/Vocaluxe/Screens/CScreenCredits.cs @@ -0,0 +1,367 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Text; +using System.Windows.Forms; + +using Vocaluxe.Base; +using Vocaluxe.Lib.Draw; +using Vocaluxe.Menu; + +enum EDirection +{ + Left, + Right, + Up +} + +class CCreditName +{ + public CStatic image; + public CParticleEffect particle; + public SRectF particleRect; + public EDirection direction; + public float StartTimeUp; + public bool active; +} + +namespace Vocaluxe.Screens +{ + class CScreenCredits :CMenu + { + // Version number for theme files. Increment it, if you've changed something on the theme files! + const int ScreenVersion = 1; + + + private CStatic logo; + private CParticleEffect starsRed; + private CParticleEffect starsBlue; + private List _CreditNames; + + private Stopwatch LogoTimer; + private Stopwatch CreditsTimer; + + public CScreenCredits() + { + Init(); + } + + protected override void Init() + { + base.Init(); + + _ThemeName = "ScreenCredits"; + _ScreenVersion = ScreenVersion; + + LogoTimer = new Stopwatch(); + CreditsTimer = new Stopwatch(); + } + + public override bool HandleInput(KeyEvent KeyEvent) + { + if (KeyEvent.KeyPressed && !Char.IsControl(KeyEvent.Unicode)) + { + + } + else + { + switch (KeyEvent.Key) + { + case Keys.Escape: + case Keys.Back: + CGraphics.FadeTo(EScreens.ScreenMain); + break; + + case Keys.Enter: + CGraphics.FadeTo(EScreens.ScreenMain); + break; + } + } + + return true; + } + + public override bool HandleMouse(MouseEvent MouseEvent) + { + if (MouseEvent.LB && IsMouseOver(MouseEvent)) + { + + } + + if (MouseEvent.LB) + { + CGraphics.FadeTo(EScreens.ScreenMain); + } + + if (MouseEvent.RB) + { + CGraphics.FadeTo(EScreens.ScreenMain); + } + return true; + } + + public override bool UpdateGame() + { + if (!animation()) + { + CGraphics.FadeTo(EScreens.ScreenMain); + } + return true; + } + + public override void OnShow() + { + STexture tex = new STexture(); + STexture particleTex = new STexture(); + + //Vocaluxe-Logo + CDataBase.GetCreditsRessource("Logo_voc.png", ref tex); + logo = new CStatic(tex, new SColorF(1, 1, 1, 1), new SRectF((CSettings.iRenderW - tex.width)/2, -270 , tex.width, tex.height, -2)); + + //Little stars for logo + CDataBase.GetCreditsRessource("PerfectNoteStar.png", ref particleTex); + int numstars = (int)(logo.Rect.W * 0.25f /2f); + starsRed = new CParticleEffect(numstars, new SColorF(1, 0, 0, 1), new SRectF(logo.Rect.X, logo.Rect.Y, logo.Rect.W, logo.Rect.H, -1), particleTex, 35, EParticeType.Star); + starsBlue = new CParticleEffect(numstars, new SColorF(0.149f, 0.415f, 0.819f, 1), new SRectF(logo.Rect.X, logo.Rect.Y, logo.Rect.W, logo.Rect.H, -1), particleTex, 35, EParticeType.Star); + + //Credit names + _CreditNames = new List(); + + CCreditName CreditEntry = new CCreditName(); + + //brunzel + CDataBase.GetCreditsRessource("brunzel.png", ref tex); + CCreditName CreditEntryBrunzel = new CCreditName(); + CreditEntryBrunzel.image = new CStatic(tex, new SColorF(1,1,1,1), new SRectF(-450, 580, 400, 120, -4)); + CreditEntryBrunzel.particleRect = new SRectF(CreditEntryBrunzel.image.Rect.X + 342, CreditEntryBrunzel.image.Rect.Y + 4, 30, 30, -5); + CreditEntryBrunzel.particle = new CParticleEffect(8, new SColorF(1, 0, 0, 1), CreditEntryBrunzel.particleRect, particleTex, 35, EParticeType.Star); + CreditEntryBrunzel.active = true; + CreditEntryBrunzel.direction = EDirection.Left; + _CreditNames.Add(CreditEntryBrunzel); + + //Darkice + CDataBase.GetCreditsRessource("Darkice.png", ref tex); + CCreditName CreditEntryDarkice = new CCreditName(); + CreditEntryDarkice.image = new CStatic(tex, new SColorF(1, 1, 1, 1), new SRectF(CSettings.iRenderW, 580, 400, 120, -4)); + CreditEntryDarkice.particleRect = new SRectF(CreditEntryDarkice.image.Rect.X + 242, CreditEntryDarkice.image.Rect.Y + 23, 30, 30, -5); + CreditEntryDarkice.particle = new CParticleEffect(8, new SColorF(0.149f, 0.415f ,0.819f, 1), CreditEntryDarkice.particleRect, particleTex, 35, EParticeType.Star); + CreditEntryDarkice.active = true; + CreditEntryDarkice.direction = EDirection.Right; + _CreditNames.Add(CreditEntryDarkice); + + //flokuep + CDataBase.GetCreditsRessource("flokuep.png", ref tex); + CCreditName CreditEntryFlokuep = new CCreditName(); + CreditEntryFlokuep.image = new CStatic(tex, new SColorF(1, 1, 1, 1), new SRectF(-450, 580, 400, 120, -4)); + CreditEntryFlokuep.particleRect = new SRectF(CreditEntryFlokuep.image.Rect.X + 141, CreditEntryFlokuep.image.Rect.Y-2, 30, 30, -5); + CreditEntryFlokuep.particle = new CParticleEffect(8, new SColorF(1, 0, 0, 1), CreditEntryFlokuep.particleRect, particleTex, 35, EParticeType.Star); + CreditEntryFlokuep.active = true; + CreditEntryFlokuep.direction = EDirection.Left; + _CreditNames.Add(CreditEntryFlokuep); + + //bohning + CDataBase.GetCreditsRessource("bohning.png", ref tex); + CCreditName CreditEntryBohning = new CCreditName(); + CreditEntryBohning.image = new CStatic(tex, new SColorF(1, 1, 1, 1), new SRectF(CSettings.iRenderW, 580, 350, 110, -4)); + CreditEntryBohning.particleRect = new SRectF(CreditEntryBohning.image.Rect.X + 172, CreditEntryBohning.image.Rect.Y + 16, 10, 10, -5); + CreditEntryBohning.particle = new CParticleEffect(4, new SColorF(0.149f, 0.415f ,0.819f, 1), CreditEntryBohning.particleRect, particleTex, 25, EParticeType.Star); + CreditEntryBohning.active = true; + CreditEntryBohning.direction = EDirection.Right; + _CreditNames.Add(CreditEntryBohning); + + //mesand + CDataBase.GetCreditsRessource("mesand.png", ref tex); + CCreditName CreditEntryMesand = new CCreditName(); + CreditEntryMesand.image = new CStatic(tex, new SColorF(1, 1, 1, 1), new SRectF(-450, 580, 350, 110, -4)); + CreditEntryMesand.particleRect = new SRectF(CreditEntryMesand.image.Rect.X + 240, CreditEntryMesand.image.Rect.Y - 2, 10, 10, -5); + CreditEntryMesand.particle = new CParticleEffect(4, new SColorF(1, 0, 0, 1), CreditEntryMesand.particleRect, particleTex, 25, EParticeType.Star); + CreditEntryMesand.active = true; + CreditEntryMesand.direction = EDirection.Left; + _CreditNames.Add(CreditEntryMesand); + + //babene03 + CDataBase.GetCreditsRessource("babene03.png", ref tex); + CCreditName CreditEntryBabene03 = new CCreditName(); + CreditEntryBabene03.image = new CStatic(tex, new SColorF(1, 1, 1, 1), new SRectF(CSettings.iRenderW, 580, 350, 110, -4)); + CreditEntryBabene03.particleRect = new SRectF(CreditEntryBabene03.image.Rect.X + 7, CreditEntryBabene03.image.Rect.Y + 4, 10, 10, -5); + CreditEntryBabene03.particle = new CParticleEffect(4, new SColorF(0.149f, 0.415f ,0.819f, 1), CreditEntryBabene03.particleRect, particleTex, 25, EParticeType.Star); + CreditEntryBabene03.active = true; + CreditEntryBabene03.direction = EDirection.Right; + _CreditNames.Add(CreditEntryBabene03); + + //pantero + CDataBase.GetCreditsRessource("pantero.png", ref tex); + CCreditName CreditEntrypantero = new CCreditName(); + CreditEntrypantero.image = new CStatic(tex, new SColorF(1, 1, 1, 1), new SRectF(-450, 580, 350, 110, -4)); + CreditEntrypantero.particleRect = new SRectF(CreditEntrypantero.image.Rect.X + 140, CreditEntrypantero.image.Rect.Y + 15, 10, 10, -5); + CreditEntrypantero.particle = new CParticleEffect(4, new SColorF(1, 0, 0, 1), CreditEntrypantero.particleRect, particleTex, 25, EParticeType.Star); + CreditEntrypantero.active = true; + CreditEntrypantero.direction = EDirection.Left; + _CreditNames.Add(CreditEntrypantero); + + //Pinky007 + CDataBase.GetCreditsRessource("Pinky007.png", ref tex); + CCreditName CreditEntryPinky007 = new CCreditName(); + CreditEntryPinky007.image = new CStatic(tex, new SColorF(1, 1, 1, 1), new SRectF(CSettings.iRenderW, 580, 350, 110, -4)); + CreditEntryPinky007.particleRect = new SRectF(CreditEntryPinky007.image.Rect.X + 42, CreditEntryPinky007.image.Rect.Y + 15, 10, 10, -5); + CreditEntryPinky007.particle = new CParticleEffect(4, new SColorF(0.149f, 0.415f ,0.819f, 1), CreditEntryPinky007.particleRect, particleTex, 25, EParticeType.Star); + CreditEntryPinky007.active = true; + CreditEntryPinky007.direction = EDirection.Right; + _CreditNames.Add(CreditEntryPinky007); + + + //CreditEntry.image = new CStatic(tex, new SColorF(1, 1, 1, 1), new SRectF(0, 0, 350, 110, -4)); + + } + + public override void OnShowFinish() + { + base.OnShowFinish(); + + LogoTimer.Start(); + } + + public override bool Draw() + { + base.Draw(); + + //Draw background + CDraw.DrawColor(new SColorF(0, 0.18f, 0.474f, 1), new SRectF(0, 0, CSettings.iRenderW, CSettings.iRenderH, 0)); + + //Draw stars + starsBlue.Update(); + starsBlue.Draw(); + starsRed.Update(); + starsRed.Draw(); + + logo.Draw(); + + //Draw credit-entries + for (int i = 0; i < _CreditNames.Count; i++) + { + if (_CreditNames[i].active) + { + _CreditNames[i].image.Draw(); + _CreditNames[i].particle.Update(); + _CreditNames[i].particle.Draw(); + } + } + return true; + } + + private bool animation() + { + bool active = false; + if (LogoTimer.IsRunning) + { + active = true; + + logo.Rect.Y = -270 + (270f / 3000f) * LogoTimer.ElapsedMilliseconds; + SRectF rect = new SRectF(logo.Rect.X, logo.Rect.Y, logo.Rect.W, logo.Rect.H, -1); + starsRed.Area = rect; + starsBlue.Area = rect; + if (LogoTimer.ElapsedMilliseconds >= 2000 && !CreditsTimer.IsRunning) + { + CreditsTimer.Start(); + } + if (LogoTimer.ElapsedMilliseconds >= 3000) + { + LogoTimer.Stop(); + LogoTimer.Reset(); + } + } + + if (CreditsTimer.IsRunning) + { + active = true; + + for (int i = 0; i < _CreditNames.Count; i++) + { + if (_CreditNames[i].active) + { + switch (_CreditNames[i].direction) + { + case EDirection.Left: + if (i * 4000f <= CreditsTimer.ElapsedMilliseconds) + { + float xOld = _CreditNames[i].image.Rect.X; + _CreditNames[i].image.Rect.X = -450 + (((CSettings.iRenderW - _CreditNames[i].image.Rect.W) / 2)/3000f) * (CreditsTimer.ElapsedMilliseconds-(i*4000f)); + _CreditNames[i].particleRect.X += (_CreditNames[i].image.Rect.X - xOld); + _CreditNames[i].particle.Area = _CreditNames[i].particleRect; + + //Check if name is in middle of screen and should go up + if (_CreditNames[i].image.Rect.X >= (CSettings.iRenderW - _CreditNames[i].image.Rect.W) / 2) + { + _CreditNames[i].direction = EDirection.Up; + _CreditNames[i].StartTimeUp = CreditsTimer.ElapsedMilliseconds; + } + + } + break; + + case EDirection.Right: + if (i * 4000f <= CreditsTimer.ElapsedMilliseconds) + { + float xOld = _CreditNames[i].image.Rect.X; + _CreditNames[i].image.Rect.X = CSettings.iRenderW - (((CSettings.iRenderW - _CreditNames[i].image.Rect.W) / 2) / 3000f) * (CreditsTimer.ElapsedMilliseconds - (i * 4000f)); + _CreditNames[i].particleRect.X -= (xOld - _CreditNames[i].image.Rect.X); + _CreditNames[i].particle.Area = _CreditNames[i].particleRect; + + //Check if name is in middle of screen and should go up + if (_CreditNames[i].image.Rect.X <= (CSettings.iRenderW - _CreditNames[i].image.Rect.W) / 2 ) + { + _CreditNames[i].direction = EDirection.Up; + _CreditNames[i].StartTimeUp = CreditsTimer.ElapsedMilliseconds; + } + + } + break; + + case EDirection.Up: + float yOld = _CreditNames[i].image.Rect.Y; + _CreditNames[i].image.Rect.Y = 580 - (430f / 3000f) * (CreditsTimer.ElapsedMilliseconds - _CreditNames[i].StartTimeUp); + _CreditNames[i].particleRect.Y -= (yOld - _CreditNames[i].image.Rect.Y); + _CreditNames[i].particle.Area = _CreditNames[i].particleRect; + + //Fade names out + if (_CreditNames[i].image.Rect.Y <= 350f) + { + //Catch some bad alpha-values + float alpha = ((250 - _CreditNames[i].image.Rect.Y) / 20); + if(alpha > 1) + { + alpha = 1; + } + else if(alpha < 0) + { + alpha = 0; + } + _CreditNames[i].image.Alpha = 1 - alpha; + } + + //Set name inactive + if (_CreditNames[i].image.Rect.Y <= 150f) + { + _CreditNames[i].active = false; + //Check, if last name is set to false + if (i == _CreditNames.Count - 1) + { + //Will later fade to Main-Screen + active = false; + + //Stop and reset timer for next time + CreditsTimer.Stop(); + CreditsTimer.Reset(); + } + } + break; + } + } + } + } + + return active; + } + } +} diff --git a/Vocaluxe/Screens/CScreenMain.cs b/Vocaluxe/Screens/CScreenMain.cs index 99c5e824..703c313a 100644 --- a/Vocaluxe/Screens/CScreenMain.cs +++ b/Vocaluxe/Screens/CScreenMain.cs @@ -65,6 +65,10 @@ namespace Vocaluxe.Screens CGraphics.FadeTo(EScreens.ScreenSong); break; + case Keys.C: + CGraphics.FadeTo(EScreens.ScreenCredits); + break; + case Keys.Enter: if (Buttons[htButtons(ButtonSing)].Selected) CGraphics.FadeTo(EScreens.ScreenSong); diff --git a/Vocaluxe/Screens/CScreenOptionsSound.cs b/Vocaluxe/Screens/CScreenOptionsSound.cs index a3fd049e..2e8207f7 100644 --- a/Vocaluxe/Screens/CScreenOptionsSound.cs +++ b/Vocaluxe/Screens/CScreenOptionsSound.cs @@ -12,7 +12,7 @@ namespace Vocaluxe.Screens class CScreenOptionsSound : CMenu { // Version number for theme files. Increment it, if you've changed something on the theme files! - const int ScreenVersion = 1; + const int ScreenVersion = 2; private const string SelectSlideBackgroundMusic = "SelectSlideBackgroundMusic"; private const string SelectSlideBackgroundMusicVolume = "SelectSlideBackgroundMusicVolume"; diff --git a/Vocaluxe/Screens/CScreenSing.cs b/Vocaluxe/Screens/CScreenSing.cs index 7616d49c..bf38ac6b 100644 --- a/Vocaluxe/Screens/CScreenSing.cs +++ b/Vocaluxe/Screens/CScreenSing.cs @@ -382,16 +382,16 @@ namespace Vocaluxe.Screens } break; } - - Lyrics[htLyrics(LyricMain)].Draw(CGame.Beat); Lyrics[htLyrics(LyricSub)].Draw(-100); + Lyrics[htLyrics(LyricMain)].Draw(CGame.Beat); - Lyrics[htLyrics(LyricMainDuet)].Draw(CGame.Beat); Lyrics[htLyrics(LyricSubDuet)].Draw(-100); + Lyrics[htLyrics(LyricMainDuet)].Draw(CGame.Beat); - Lyrics[htLyrics(LyricMainTop)].Draw(CGame.Beat); Lyrics[htLyrics(LyricSubTop)].Draw(-100); + Lyrics[htLyrics(LyricMainTop)].Draw(CGame.Beat); + for (int i = 0; i < CGame.NumPlayer; i++) { @@ -582,7 +582,8 @@ namespace Vocaluxe.Screens lines[i] = song.Notes.GetLines(i); CLine[] line = lines[i].Line; - + + // find current line (it must be the same as in CalcFadingAlpha) int nr = -1; for (int j = 0; j < line.Length; j++) { @@ -851,6 +852,20 @@ namespace Vocaluxe.Screens lines[i] = Song.Notes.GetLines(i); CLine[] line = lines[i].Line; + // find current line for lyric sub fading (it must be the same as in UpdateLyrics) + int CurrentLineSub = 0; + for (int j = 0; j < line.Length; j++) + { + if (line[j].StartBeat <= _CurrentBeat) + { + if (CGame.GetTimeFromBeats(line[j].FirstBeat, Song.BPM) <= _CurrentTime - Song.Gap + 10f) + { + CurrentLineSub = j; + } + } + } + + // find current line for lyric main fading int CurrentLine = 0; for (int j = 0; j < line.Length; j++) { @@ -864,9 +879,11 @@ namespace Vocaluxe.Screens Alpha[i * 2] = 1f; Alpha[i * 2 + 1] = 1f; - // main + // main line alpha if (CurrentLine == 0 && CurrentTime < CGame.GetTimeFromBeats(line[CurrentLine].FirstBeat, Song.BPM)) { + // first main line and fist note is not reached + // => fade in float diff = CGame.GetTimeFromBeats(line[CurrentLine].FirstBeat, Song.BPM) - CurrentTime; if (diff > dt) { @@ -876,20 +893,29 @@ namespace Vocaluxe.Screens else if (CurrentLine < line.Length - 1 && CGame.GetTimeFromBeats(line[CurrentLine].LastBeat, Song.BPM) < CurrentTime && CGame.GetTimeFromBeats(line[CurrentLine + 1].FirstBeat, Song.BPM) > CurrentTime) { + // current position is between two lines + + // time between the to lines float diff = CGame.GetTimeFromBeats(line[CurrentLine + 1].FirstBeat, Song.BPM) - CGame.GetTimeFromBeats(line[CurrentLine].LastBeat, Song.BPM); + // fade only if there is enough time for fading if (diff > 3.3f * dt) { + // time elapsed since last line float last = CurrentTime - CGame.GetTimeFromBeats(line[CurrentLine].LastBeat, Song.BPM); + + // time to next line float next = CGame.GetTimeFromBeats(line[CurrentLine + 1].FirstBeat, Song.BPM) - CurrentTime; if (last < next) { + // fade out Alpha[i * 2] = 1f - last / rt; } else { + // fade in if it is time for if (next > dt) Alpha[i * 2] = 1f - (next - dt) / rt; } @@ -897,14 +923,18 @@ namespace Vocaluxe.Screens } else if (CurrentLine == line.Length - 1 && CGame.GetTimeFromBeats(line[CurrentLine].LastBeat, Song.BPM) < CurrentTime) { + // last main line and last note was reached + // => fade out float diff = CurrentTime - CGame.GetTimeFromBeats(line[CurrentLine].LastBeat, Song.BPM); Alpha[i * 2] = 1f - diff / rt; } // sub - if (CurrentLine < line.Length - 2) + if (CurrentLineSub < line.Length - 2) { - float diff = CGame.GetTimeFromBeats(line[CurrentLine + 1].FirstBeat, Song.BPM) - CurrentTime; + float diff = 0f; + diff = CGame.GetTimeFromBeats(line[CurrentLineSub + 1].FirstBeat, Song.BPM) - CurrentTime; + if (diff > dt) { Alpha[i * 2 + 1] = 1f - (diff - dt) / rt; diff --git a/Vocaluxe/Vocaluxe.csproj b/Vocaluxe/Vocaluxe.csproj index a62154d3..6f08fdce 100644 --- a/Vocaluxe/Vocaluxe.csproj +++ b/Vocaluxe/Vocaluxe.csproj @@ -188,6 +188,7 @@ Form +