-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathMain.cs
288 lines (235 loc) · 10.5 KB
/
Main.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
using MelonLoader;
using UnityEngine;
using Il2CppRhythm;
namespace LyricsCinema
{
public class Main : MelonMod
{
// Preferences category
private MelonPreferences_Category prefsCategory;
// variables
private AudioSource currentSong;
private MusicController musicController;
private string currentSongId;
private static string _currentLyric = "";
private static string _nextLyric = "";
private static bool _showLyric = false;
private static int _currentLineIndex = -1;
// Lyrics - text properties
private static GUIStyle _textStyle;
private static GUIStyle _nextLineStyle;
private MelonPreferences_Entry<int> fontSize;
// Lyrics - top line
private MelonPreferences_Entry<float> topLineY;
private MelonPreferences_Entry<float> topLineX;
private MelonPreferences_Entry<float> topLineWidthMargin;
private MelonPreferences_Entry<float> topLineHeight;
// Lyrics - bottom line
private MelonPreferences_Entry<float> bottomLineY;
private MelonPreferences_Entry<float> bottomLineX;
private MelonPreferences_Entry<float> bottomLineWidthMargin;
private MelonPreferences_Entry<float> bottomLineHeight;
// Dict to hold the lyrics and timestamps
public static Dictionary<string, (float[] timings, string[] lines)> LyricsData = new Dictionary<string, (float[] timings, string[] lines)>();
public override void OnInitializeMelon()
{
base.OnInitializeMelon();
// Initialize preferences and set custom file path
prefsCategory = MelonPreferences.CreateCategory("LyricsCinema", "Lyrics Cinema Settings");
prefsCategory.SetFilePath("UserData/LyricsCinema.cfg");
// Create entries for text properties
fontSize = prefsCategory.CreateEntry("FontSize", 65, "Font Size");
// Create entries for top line properties
topLineY = prefsCategory.CreateEntry("TopLineY", 250f, "Top Line Y Position");
topLineX = prefsCategory.CreateEntry("TopLineX", 20f, "Top Line X Position");
topLineWidthMargin = prefsCategory.CreateEntry("TopLineWidthMargin", 40f, "Top Line Width Margin");
topLineHeight = prefsCategory.CreateEntry("TopLineHeight", 100f, "Top Line Height");
// Create entries for bottom line properties
bottomLineY = prefsCategory.CreateEntry("BottomLineY", 320f, "Bottom Line Y Position");
bottomLineX = prefsCategory.CreateEntry("BottomLineX", 20f, "Bottom Line X Position");
bottomLineWidthMargin = prefsCategory.CreateEntry("BottomLineWidthMargin", 40f, "Bottom Line Width Margin");
bottomLineHeight = prefsCategory.CreateEntry("BottomLineHeight", 100f, "Bottom Line Height");
}
public override void OnLateInitializeMelon()
{
// Register Songs
BrainPower.RegisterLyrics();
Chandelier.RegisterLyrics();
GiraGira.RegisterLyrics();
CheapThrills.RegisterLyrics();
DontStartNow.RegisterLyrics();
StarryEyed.RegisterLyrics();
Levitating.RegisterLyrics();
// add more songs here
foreach (var songId in LyricsData.Keys)
{
CustomLogger.Msg("Lyrics loaded for ID: " + songId);
}
InitializeGUIStyles();
MelonEvents.OnGUI.Subscribe(DrawLyrics);
}
public override void OnUpdate()
{
// Find the MusicController GameObject
if (musicController == null)
{
GameObject musicControllerGameObject = GameObject.Find("MusicController");
if (musicControllerGameObject != null)
{
musicController = musicControllerGameObject.GetComponent<MusicController>();
CustomLogger.Debug("MusicController GameObject found.");
}
else
{
CustomLogger.Debug("MusicController GameObject NOT found. Probably in a menu....");
}
}
if (musicController != null && musicController.musicInfo != null)
{
// find currently playing song
string newSongId = musicController.musicInfo.id;
if (newSongId != currentSongId)
{
currentSongId = newSongId;
CustomLogger.Msg($"Current song ID changed to: {currentSongId}");
}
// update Lyrics
currentSong = musicController.GetComponent<AudioSource>();
if (currentSong != null && currentSong.isPlaying && LyricsData.ContainsKey(currentSongId))
{
_showLyric = true;
UpdateCurrentLyric(currentSong.time);
}
else
{
_showLyric = false;
if (!currentSong.isPlaying)
CustomLogger.Debug("AudioSource is not playing any music.");
if (!LyricsData.ContainsKey(currentSongId))
CustomLogger.Debug("No lyrics available for this song ID.");
}
}
}
private void UpdateCurrentLyric(float currentTime)
{
if (!LyricsData.TryGetValue(currentSongId, out var data))
{
CustomLogger.Debug("No lyrics data found for the current song ID.");
return;
}
float[] timings = data.timings;
string[] lines = data.lines;
// If timings are not explicitly provided, calculate them evenly based on the song length
if (timings.Length == 0 && currentSong.clip != null)
{
float totalLength = currentSong.clip.length;
int numberOfLines = lines.Length;
float timePerLine = totalLength / numberOfLines;
timings = new float[numberOfLines];
for (int i = 0; i < numberOfLines; i++)
{
timings[i] = i * timePerLine;
}
}
bool lyricFound = false;
float tolerance = 0.5f; // 500 ms tolerance for matching the current time to a lyric timing
for (int i = 0; i < timings.Length; i++)
{
if (currentTime < timings[i] + tolerance) // Adding tolerance
{
if (i > 0 && currentTime + tolerance >= timings[i - 1]) // Ensure the current time is within the current timing range
{
_currentLyric = lines[i - 1];
_currentLineIndex = i - 1;
lyricFound = true;
}
_nextLyric = i < lines.Length - 1 ? lines[i] : "";
break;
}
}
if (!lyricFound)
{
_showLyric = false;
CustomLogger.Debug("No matching lyric timing found. Hiding lyrics.");
}
else
{
_showLyric = true;
CustomLogger.Debug($"Current Time: {currentTime}, Lyric displayed: {_currentLyric}");
}
}
public void InitializeGUIStyles()
{
_textStyle = new GUIStyle()
{
fontSize = fontSize.Value,
alignment = TextAnchor.MiddleCenter,
normal = { textColor = Color.cyan }
};
_nextLineStyle = new GUIStyle(_textStyle)
{
normal = { textColor = new Color(168, 173, 173, 0.3f) } // Cyan with 50% opacity
};
}
public void DrawLyrics()
{
if (_showLyric)
{
if (_currentLineIndex >= 0)
{
//to place at bottom:
//GUI.Label(new Rect(20, Screen.height - 140, Screen.width - 40, 100), _currentLyric, _textStyle);
// top place at top:
GUI.Label(new Rect(topLineX.Value, topLineY.Value, Screen.width - topLineWidthMargin.Value, topLineHeight.Value), _currentLyric, _textStyle);
// GUI.Label(new Rect(20, 250, Screen.width - 40, 90), _currentLyric, _textStyle);
if (!string.IsNullOrEmpty(_nextLyric))
{
//to place at bottom:
//GUI.Label(new Rect(20, Screen.height - 70, Screen.width - 40, 100), _nextLyric, _nextLineStyle);
//to place at top
GUI.Label(new Rect(bottomLineX.Value, bottomLineY.Value, Screen.width - bottomLineWidthMargin.Value, bottomLineHeight.Value), _nextLyric, _nextLineStyle);
//GUI.Label(new Rect(20, 320, Screen.width - 40, 90), _nextLyric, _nextLineStyle);
}
}
}
}
public override void OnApplicationQuit()
{
MelonEvents.OnGUI.Unsubscribe(DrawLyrics);
}
public static void RegisterSong(string songId, (float[] timings, string[] lines) data)
{
LyricsData.Add(songId, data);
}
public static float ParseLRCTime(string lrcTimestamp)
{
// Expected format: [mm:ss.ff]
string[] parts = lrcTimestamp.Split(':');
if (parts.Length != 2)
return 0;
string[] secondParts = parts[1].Split('.');
float minutes = float.Parse(parts[0]);
float seconds = float.Parse(secondParts[0]);
float fraction = secondParts.Length > 1 ? float.Parse("0." + secondParts[1]) : 0;
return minutes * 60 + seconds;
// Quickfix: discard ms because just adding them leads them to be interpreted as seconds
//return minutes * 60 + seconds + fraction;
}
public static class CustomLogger
{
// Debug message logging method
// Compile with Debug to enable debug logging output
public static void Debug(string message)
{
#if DEBUG_LOGGING
MelonLogger.Msg("[DEBUG] " + message);
#endif
}
// Regular message logging method
public static void Msg(string message)
{
MelonLogger.Msg(message);
}
}
}
}