awacke1 commited on
Commit
faac71f
·
verified ·
1 Parent(s): 699f4c3

Update index.html

Browse files
Files changed (1) hide show
  1. index.html +521 -19
index.html CHANGED
@@ -1,19 +1,521 @@
1
- <!doctype html>
2
- <html>
3
- <head>
4
- <meta charset="utf-8" />
5
- <meta name="viewport" content="width=device-width" />
6
- <title>My static Space</title>
7
- <link rel="stylesheet" href="style.css" />
8
- </head>
9
- <body>
10
- <div class="card">
11
- <h1>Welcome to your static Space!</h1>
12
- <p>You can modify this app directly by editing <i>index.html</i> in the Files and versions tab.</p>
13
- <p>
14
- Also don't forget to check the
15
- <a href="https://huggingface.co/docs/hub/spaces" target="_blank">Spaces documentation</a>.
16
- </p>
17
- </div>
18
- </body>
19
- </html>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import React, { useState, useRef, useEffect, useCallback } from 'react';
2
+ import * as Tone from 'tone'; // Import Tone.js
3
+
4
+ // Song data parsed from your previous responses
5
+ const songsData = [
6
+ {
7
+ title: "Talkin' 'bout a Revolution",
8
+ artist: "Tracy Chapman",
9
+ sections: [
10
+ { type: "Intro", lines: [{ chords: "G C D G", lyrics: "" }] },
11
+ { type: "Verse 1", lines: [
12
+ { chords: "G C D G", lyrics: "While outside a revolution's talking..." },
13
+ { chords: "G C D G", lyrics: "It's gonna come, it's gonna come..." }
14
+ ]},
15
+ { type: "Chorus 1", lines: [
16
+ { chords: "G C D G", lyrics: "Talkin' 'bout a revolution, oh, no" },
17
+ { chords: "G C D G", lyrics: "Talkin' 'bout a revolution..." }
18
+ ]},
19
+ { type: "Verse 2", lines: [
20
+ { chords: "G C D G", lyrics: "While outside a revolution's talking..." },
21
+ { chords: "G C D G", lyrics: "It's gonna come, it's gonna come..." }
22
+ ]},
23
+ { type: "Chorus 2", lines: [
24
+ { chords: "G C D G", lyrics: "Talkin' 'bout a revolution, oh, no" },
25
+ { chords: "G C D G", lyrics: "Talkin' 'bout a revolution..." }
26
+ ]},
27
+ { type: "Outro", lines: [{ chords: "G C D G", lyrics: "(repeat and fade)" }] }
28
+ ]
29
+ },
30
+ {
31
+ title: "Fast Car",
32
+ artist: "Tracy Chapman",
33
+ sections: [
34
+ { type: "Intro", lines: [{ chords: "C G Am F", lyrics: "" }] },
35
+ { type: "Verse 1", lines: [
36
+ { chords: "C G Am F", lyrics: "You got a fast car, I want a ticket to anywhere..." },
37
+ { chords: "C G Am F", lyrics: "We go driving in it, anywhere, maybe we'll make a deal..." }
38
+ ]},
39
+ { type: "Chorus 1", lines: [
40
+ { chords: "C G Am F", lyrics: "So remember when we were driving, driving in your car..." },
41
+ { chords: "C G Am F", lyrics: "The speed of light, we gotta go, go, go, go, go..." }
42
+ ]},
43
+ { type: "Verse 2", lines: [
44
+ { chords: "C G Am F", lyrics: "You got a fast car, I got a plan to get us out of here..." },
45
+ { chords: "C G Am F", lyrics: "I been working at the convenience store, so slow..." }
46
+ ]},
47
+ { type: "Chorus 2", lines: [
48
+ { chords: "C G Am F", lyrics: "So remember when we were driving, driving in your car..." },
49
+ { chords: "C G Am F", lyrics: "The speed of light, we gotta go, go, go, go, go..." }
50
+ ]},
51
+ { type: "Outro", lines: [{ chords: "C G Am F", lyrics: "(repeat and fade)" }] }
52
+ ]
53
+ },
54
+ {
55
+ title: "Cult of Personality",
56
+ artist: "Living Colour",
57
+ sections: [
58
+ { type: "Intro", lines: [{ chords: "Em G C D", lyrics: "" }] },
59
+ { type: "Verse 1", lines: [
60
+ { chords: "Em G C D", lyrics: "Look in my eyes, what do you see?" },
61
+ { chords: "Em G C D", lyrics: "The cult of personality." }
62
+ ]},
63
+ { type: "Chorus 1", lines: [
64
+ { chords: "Em G C D", lyrics: "Cult of Personality! Cult of Personality!" }
65
+ ]},
66
+ { type: "Verse 2", lines: [
67
+ { chords: "Em G C D", lyrics: "I look in your eyes, what do I see?" },
68
+ { chords: "Em G C D", lyrics: "The cult of personality." }
69
+ ]},
70
+ { type: "Chorus 2", lines: [
71
+ { chords: "Em G C D", lyrics: "Cult of Personality! Cult of Personality!" }
72
+ ]},
73
+ { type: "Outro", lines: [{ chords: "Em G C D", lyrics: "(repeated, building to a final hit on Em)" }] }
74
+ ]
75
+ },
76
+ {
77
+ title: "Glamour Boys",
78
+ artist: "Living Colour",
79
+ sections: [
80
+ { type: "Intro", lines: [{ chords: "E B A E", lyrics: "" }] },
81
+ { type: "Verse 1", lines: [
82
+ { chords: "E B", lyrics: "The Glamour Boys, they got the girls" },
83
+ { chords: "A E", lyrics: "They got the cars, they got the pearls" }
84
+ ]},
85
+ { type: "Chorus 1", lines: [
86
+ { chords: "E B A E", lyrics: "Glamour Boys, oh, the Glamour Boys, what makes you think you're so cool?" }
87
+ ]},
88
+ { type: "Verse 2", lines: [
89
+ { chords: "E B", lyrics: "The Glamour Boys, they're so hip" },
90
+ { chords: "A E", lyrics: "They got the style, they got the trip" }
91
+ ]},
92
+ { type: "Chorus 2", lines: [
93
+ { chords: "E B A E", lyrics: "Glamour Boys, oh, the Glamour Boys, what makes you think you're so cool?" }
94
+ ]},
95
+ { type: "Outro", lines: [{ chords: "E B A E", lyrics: "(repeat and fade with guitar licks)" }] }
96
+ ]
97
+ },
98
+ {
99
+ title: "Ocean Size",
100
+ artist: "Jane's Addiction",
101
+ sections: [
102
+ { type: "Intro", lines: [{ chords: "C G Am F", lyrics: "" }] },
103
+ { type: "Verse 1", lines: [
104
+ { chords: "C G Am F", lyrics: "Day by day, as the weeks turn to months, I watch you..." },
105
+ { chords: "C G Am F", lyrics: "Growing taller, growing wiser, growing stronger..." }
106
+ ]},
107
+ { type: "Chorus 1", lines: [
108
+ { chords: "C G D F", lyrics: "Ocean size, it's the ocean size" },
109
+ { chords: "C G D F", lyrics: "The ocean size, it's the ocean size" }
110
+ ]},
111
+ { type: "Verse 2", lines: [
112
+ { chords: "C G Am F", lyrics: "Day by day, as the weeks turn to months, I watch you..." },
113
+ { chords: "C G Am F", lyrics: "Growing stronger, growing wiser, growing taller..." }
114
+ ]},
115
+ { type: "Chorus 2", lines: [
116
+ { chords: "C G D F", lyrics: "Ocean size, it's the ocean size" },
117
+ { chords: "C G D F", lyrics: "The ocean size, it's the ocean size" }
118
+ ]},
119
+ { type: "Outro", lines: [{ chords: "C G Am F", lyrics: "(repeat and fade)" }] }
120
+ ]
121
+ },
122
+ {
123
+ title: "Mountain Song",
124
+ artist: "Jane's Addiction",
125
+ sections: [
126
+ { type: "Intro", lines: [{ chords: "Em C G D", lyrics: "" }] },
127
+ { type: "Verse 1", lines: [
128
+ { chords: "Em C G D", lyrics: "Coming down the mountain, I saw a girl" },
129
+ { chords: "Em C G D", lyrics: "She was looking at me, in my world" }
130
+ ]},
131
+ { type: "Chorus 1", lines: [
132
+ { chords: "Em C G D", lyrics: "Mountain Song! Mountain Song!" }
133
+ ]},
134
+ { type: "Verse 2", lines: [
135
+ { chords: "Em C G D", lyrics: "She said, \"Where'd you come from? Where'd you go?\"" },
136
+ { chords: "Em C G D", lyrics: "\"I came from the mountain, don't you know?\"" }
137
+ ]},
138
+ { type: "Chorus 2", lines: [
139
+ { chords: "Em C G D", lyrics: "Mountain Song! Mountain Song!" }
140
+ ]},
141
+ { type: "Outro", lines: [{ chords: "Em C G D", lyrics: "(repeat and fade)" }] }
142
+ ]
143
+ },
144
+ {
145
+ title: "Where Is My Mind?",
146
+ artist: "Pixies",
147
+ sections: [
148
+ { type: "Intro", lines: [{ chords: "C G Am F", lyrics: "" }] },
149
+ { type: "Verse 1", lines: [
150
+ { chords: "C G Am F", lyrics: "With your feet on the air and your head on the ground..." },
151
+ { chords: "C G Am F", lyrics: "Try this trick and spin it, yeah..." }
152
+ ]},
153
+ { type: "Chorus 1", lines: [
154
+ { chords: "C G Am F", lyrics: "Where is my mind? Where is my mind?" },
155
+ { chords: "C G Am F", lyrics: "Where is my mind? Way out in the water, see it swimming..." }
156
+ ]},
157
+ { type: "Verse 2", lines: [
158
+ { chords: "C G Am F", lyrics: "I was thinking about you and the things we've done..." },
159
+ { chords: "C G Am F", lyrics: "And all the places we've been to, oh yeah..." }
160
+ ]},
161
+ { type: "Chorus 2", lines: [
162
+ { chords: "C G Am F", lyrics: "Where is my mind? Where is my mind?" },
163
+ { chords: "C G Am F", lyrics: "Where is my mind? Way out in the water, see it swimming..." }
164
+ ]},
165
+ { type: "Outro", lines: [{ chords: "C G Am F", lyrics: "(repeat and fade)" }] }
166
+ ]
167
+ },
168
+ {
169
+ title: "Fisherman's Blues",
170
+ artist: "The Waterboys",
171
+ sections: [
172
+ { type: "Intro", lines: [{ chords: "G C D G", lyrics: "" }] },
173
+ { type: "Verse 1", lines: [
174
+ { chords: "G C D G", lyrics: "I wish I was a fisherman, tumbling on the sea" },
175
+ { chords: "G C D G", lyrics: "Far away from dry land, and its bitter misery" }
176
+ ]},
177
+ { type: "Chorus 1", lines: [
178
+ { chords: "G C D G", lyrics: "I'm gonna make a record, a record of my dreams" },
179
+ { chords: "G C D G", lyrics: "And let the wind and the waves sing along to the themes" }
180
+ ]},
181
+ { type: "Verse 2", lines: [
182
+ { chords: "G C D G", lyrics: "I wish I was a fisherman, out on the rolling deep" },
183
+ { chords: "G C D G", lyrics: "With nothing but the stars to guide me, while the city sleeps" }
184
+ ]},
185
+ { type: "Chorus 2", lines: [
186
+ { chords: "G C D G", lyrics: "I'm gonna make a record, a record of my dreams" },
187
+ { chords: "G C D G", lyrics: "And let the wind and the waves sing along to the themes" }
188
+ ]},
189
+ { type: "Outro", lines: [{ chords: "G C D G", lyrics: "(repeat and fade)" }] }
190
+ ]
191
+ },
192
+ {
193
+ title: "Express Yourself",
194
+ artist: "N.W.A.",
195
+ sections: [
196
+ { type: "Intro", lines: [{ chords: "F Am Dm C", lyrics: "" }] },
197
+ { type: "Verse 1", lines: [
198
+ { chords: "F Am Dm C", lyrics: "I'm expressing with my full capabilities, and now I'm living in reality..." },
199
+ { chords: "F Am Dm C", lyrics: "The only solution is to get involved and move it..." }
200
+ ]},
201
+ { type: "Chorus 1", lines: [
202
+ { chords: "F Am Dm C", lyrics: "Express yourself! Express yourself!" },
203
+ { chords: "F Am Dm C", lyrics: "Express yourself! It's a brand new thing..." }
204
+ ]},
205
+ { type: "Verse 2", lines: [
206
+ { chords: "F Am Dm C", lyrics: "Now I'm the one, I'm the one, I'm the one that you know..." },
207
+ { chords: "F Am Dm C", lyrics: "Coming to get you, coming to get you, coming to get you, watch me go..." }
208
+ ]},
209
+ { type: "Chorus 2", lines: [
210
+ { chords: "F Am Dm C", lyrics: "Express yourself! Express yourself!" },
211
+ { chords: "F Am Dm C", lyrics: "Express yourself! It's a brand new thing..." }
212
+ ]},
213
+ { type: "Outro", lines: [{ chords: "F Am Dm C", lyrics: "(repeat and fade)" }] }
214
+ ]
215
+ },
216
+ {
217
+ title: "One",
218
+ artist: "Metallica",
219
+ sections: [
220
+ { type: "Intro", lines: [{ chords: "Am G C F", lyrics: "(Acoustic Intro/Verse Part)" }] },
221
+ { type: "Verse 1", lines: [
222
+ { chords: "Am G C F", lyrics: "I can't remember anything, can't tell if this is true or dream..." },
223
+ { chords: "Am G C F", lyrics: "Deep down inside I feel the scream..." }
224
+ ]},
225
+ { type: "Chorus 1", lines: [ // Note: This is not a traditional chorus for this song, but the repeated progression
226
+ { chords: "Am G C F", lyrics: "Hold my breath as I wish for death..." }
227
+ ]},
228
+ { type: "Verse 2", lines: [
229
+ { chords: "Am G C F", lyrics: "Life it seems will fade away, drifting further every day..." },
230
+ { chords: "Am G C F", lyrics: "Getting lost within myself, nothing matters, no one else..." }
231
+ ]},
232
+ { type: "Chorus 2", lines: [ // Note: This is not a traditional chorus for this song, but the repeated progression
233
+ { chords: "Am G C F", lyrics: "All the thoughts that I have now..." }
234
+ ]},
235
+ { type: "Outro", lines: [{ chords: "E5 - D5 - C5 - A5", lyrics: "(heavy riffing, fast tempo, ends on E5)" }] }
236
+ ]
237
+ },
238
+ {
239
+ title: "Handle with Care",
240
+ artist: "Traveling Wilburys",
241
+ sections: [
242
+ { type: "Intro", lines: [{ chords: "G D Em C", lyrics: "" }] },
243
+ { type: "Verse 1", lines: [
244
+ { chords: "G D Em C", lyrics: "Been beat up and battered 'round, been sent up, and I've been shot down..." },
245
+ { chords: "G D Em C", lyrics: "You're the best thing that I've found, handle me with care." }
246
+ ]},
247
+ { type: "Chorus 1", lines: [
248
+ { chords: "G D Em C", lyrics: "Handle me with care, handle me with care" },
249
+ { chords: "G D Em C", lyrics: "Handle me with care, handle me with care" }
250
+ ]},
251
+ { type: "Verse 2", lines: [
252
+ { chords: "G D Em C", lyrics: "I been stuck in so much traffic, I'm a mess, a nervous wreck..." },
253
+ { chords: "G D Em C", lyrics: "I could use some tender love and care, handle me with care." }
254
+ ]},
255
+ { type: "Chorus 2", lines: [
256
+ { chords: "G D Em C", lyrics: "Handle me with care, handle me with care" },
257
+ { chords: "G D Em C", lyrics: "Handle me with care, handle me with care" }
258
+ ]},
259
+ { type: "Outro", lines: [{ chords: "G D Em C", lyrics: "(repeat and fade)" }] }
260
+ ]
261
+ }
262
+ ];
263
+
264
+
265
+ // Simplified chord to MIDI note mapping
266
+ // Maps chord root to MIDI note number (C4 = 60) and then adds intervals for Major/Minor triad
267
+ const chordToMidi = (chordName) => {
268
+ const rootMap = {
269
+ 'C': 60, 'C#': 61, 'Db': 61, 'D': 62, 'D#': 63, 'Eb': 63, 'E': 64, 'F': 65,
270
+ 'F#': 66, 'Gb': 66, 'G': 67, 'G#': 68, 'Ab': 68, 'A': 69, 'A#': 70, 'Bb': 70, 'B': 71
271
+ };
272
+
273
+ let root = null;
274
+ let type = 'major'; // Default to major
275
+ let octaveOffset = 0; // Default to 4th octave for root
276
+
277
+ // Parse root and accidental (e.g., C#, Eb)
278
+ let baseChord = chordName.trim().replace('5', ''); // Handle power chords as root
279
+ const match = baseChord.match(/^([A-G][b#]?)/);
280
+ if (match) {
281
+ root = rootMap[match[1]];
282
+ baseChord = baseChord.substring(match[1].length); // Remove root for type parsing
283
+ } else {
284
+ // Fallback for unparseable roots, or just return empty
285
+ return [];
286
+ }
287
+
288
+ // Determine chord type (simple major/minor/power for now)
289
+ if (baseChord.includes('m') || baseChord.includes('min')) {
290
+ type = 'minor';
291
+ } else if (baseChord.includes('5')) { // Explicit power chord
292
+ type = 'power';
293
+ }
294
+
295
+ if (root === null) return []; // Should not happen with match check above
296
+
297
+ const notes = [root + octaveOffset];
298
+ if (type === 'major') {
299
+ notes.push(root + 4 + octaveOffset); // Major third
300
+ notes.push(root + 7 + octaveOffset); // Perfect fifth
301
+ } else if (type === 'minor') {
302
+ notes.push(root + 3 + octaveOffset); // Minor third
303
+ notes.push(root + 7 + octaveOffset); // Perfect fifth
304
+ } else if (type === 'power') {
305
+ notes.push(root + 7 + octaveOffset); // Perfect fifth
306
+ }
307
+
308
+ // Add higher octave for fuller sound, if it makes sense
309
+ if (notes.length > 0) {
310
+ notes.push(notes[0] + 12); // Add root an octave higher
311
+ }
312
+
313
+ return notes;
314
+ };
315
+
316
+
317
+ // Main App Component
318
+ const App = () => {
319
+ const [currentSongIndex, setCurrentSongIndex] = useState(0);
320
+ const [currentTempo, setCurrentTempo] = useState(90); // Default to Medium tempo
321
+ const synthRef = useRef(null);
322
+ const playSequenceRef = useRef(null);
323
+
324
+ // Define tempo presets
325
+ const tempos = {
326
+ slow: 30,
327
+ medium: 90, // Changed from previous default for distinct steps
328
+ fast: 128
329
+ };
330
+
331
+ // Initialize Tone.js synth once
332
+ useEffect(() => {
333
+ // Only create synth if it doesn't exist
334
+ if (!synthRef.current) {
335
+ synthRef.current = new Tone.PolySynth(Tone.Synth, {
336
+ envelope: {
337
+ attack: 0.02,
338
+ decay: 0.1,
339
+ sustain: 0.3,
340
+ release: 1
341
+ }
342
+ }).toDestination();
343
+ }
344
+
345
+ // Cleanup function
346
+ return () => {
347
+ if (synthRef.current) {
348
+ synthRef.current.dispose();
349
+ synthRef.current = null;
350
+ }
351
+ if (playSequenceRef.current) {
352
+ playSequenceRef.current.stop();
353
+ playSequenceRef.current.dispose();
354
+ playSequenceRef.current = null;
355
+ }
356
+ Tone.Transport.stop(); // Stop transport on unmount
357
+ };
358
+ }, []); // Run once on mount
359
+
360
+ const currentSong = songsData[currentSongIndex];
361
+
362
+ const handleNextSong = () => {
363
+ if (playSequenceRef.current) {
364
+ playSequenceRef.current.stop(); // Stop any current playback
365
+ }
366
+ setCurrentSongIndex((prevIndex) => (prevIndex + 1) % songsData.length);
367
+ };
368
+
369
+ const handlePrevSong = () => {
370
+ if (playSequenceRef.current) {
371
+ playSequenceRef.current.stop(); // Stop any current playback
372
+ }
373
+ setCurrentSongIndex((prevIndex) => (prevIndex - 1 + songsData.length) % songsData.length);
374
+ };
375
+
376
+ const setTempo = (tempoValue) => {
377
+ setCurrentTempo(tempoValue);
378
+ Tone.Transport.bpm.value = tempoValue; // Update Tone.js Transport BPM
379
+ if (playSequenceRef.current && Tone.Transport.state === 'started') {
380
+ // If playing, restart the sequence to apply tempo immediately
381
+ playChords(); // This will stop and restart with new tempo
382
+ }
383
+ };
384
+
385
+
386
+ const playChords = useCallback(async () => {
387
+ if (!synthRef.current) {
388
+ console.error("Synth not initialized.");
389
+ return;
390
+ }
391
+
392
+ // Start Tone.js audio context if not already started
393
+ if (Tone.context.state !== 'running') {
394
+ await Tone.start();
395
+ console.log('Audio context started.');
396
+ }
397
+
398
+ // Stop any existing sequence and transport
399
+ if (playSequenceRef.current) {
400
+ playSequenceRef.current.stop();
401
+ playSequenceRef.current.dispose();
402
+ playSequenceRef.current = null;
403
+ }
404
+ Tone.Transport.stop();
405
+ Tone.Transport.cancel(); // Clear any scheduled events
406
+
407
+ // Set the tempo before starting the transport
408
+ Tone.Transport.bpm.value = currentTempo;
409
+
410
+ const allChords = [];
411
+ currentSong.sections.forEach(section => {
412
+ section.lines.forEach(line => {
413
+ // Simple regex to extract individual chord names, assume space-separated
414
+ const chordsInLine = line.chords.split(/\s+/).filter(c => c.length > 0);
415
+ chordsInLine.forEach(chord => allChords.push(chord));
416
+ });
417
+ });
418
+
419
+ if (allChords.length === 0) {
420
+ console.warn("No chords found for this song.");
421
+ return;
422
+ }
423
+
424
+ // Create a new sequence for playback
425
+ // The interval '1n' means one whole note per chord in this sequence
426
+ // Tone.js Transport BPM controls the actual duration of '1n'
427
+ playSequenceRef.current = new Tone.Sequence((time, chordName) => {
428
+ const midiNotes = chordToMidi(chordName);
429
+ if (midiNotes.length > 0) {
430
+ synthRef.current.triggerAttackRelease(midiNotes.map(n => Tone.Midi(n).toNote()), "0.8n", time); // hold for 0.8 of a whole note
431
+ }
432
+ }, allChords, "1n").start(0); // Each chord plays for 1 whole note duration
433
+
434
+ Tone.Transport.start();
435
+
436
+ }, [currentSong, currentTempo]); // Recreate sequence if current song or tempo changes
437
+
438
+ // Stop playback when song index changes
439
+ useEffect(() => {
440
+ if (playSequenceRef.current) {
441
+ playSequenceRef.current.stop();
442
+ Tone.Transport.stop();
443
+ }
444
+ }, [currentSongIndex]);
445
+
446
+
447
+ return (
448
+ <div className="flex flex-col h-screen bg-gray-900 text-gray-100 font-inter">
449
+ {/* Header with song title and artist */}
450
+ <header className="p-4 bg-gray-800 shadow-md text-center rounded-b-lg">
451
+ <h1 className="text-3xl font-bold text-blue-400">
452
+ {currentSong.title}
453
+ </h1>
454
+ <p className="text-xl text-gray-300">{currentSong.artist}</p>
455
+ </header>
456
+
457
+ {/* Main content area - lyrics and chords */}
458
+ <main className="flex-1 overflow-y-auto p-6 text-2xl leading-relaxed">
459
+ {currentSong.sections.map((section, secIndex) => (
460
+ <div key={secIndex} className="mb-8">
461
+ <h2 className="text-3xl font-semibold text-yellow-300 mb-4 sticky top-0 bg-gray-900 py-2 z-10">
462
+ {section.type}
463
+ </h2>
464
+ {section.lines.map((line, lineIndex) => (
465
+ <div key={lineIndex} className="mb-4">
466
+ <p className="font-bold text-green-300 whitespace-pre">
467
+ {line.chords}
468
+ </p>
469
+ <p className="text-gray-100">
470
+ {line.lyrics}
471
+ </p>
472
+ </div>
473
+ ))}
474
+ </div>
475
+ ))}
476
+ </main>
477
+
478
+ {/* Navigation and MIDI controls */}
479
+ <footer className="p-4 bg-gray-800 shadow-t-md flex justify-center items-center space-x-4 rounded-t-lg flex-wrap">
480
+ <button
481
+ onClick={handlePrevSong}
482
+ className="bg-blue-600 hover:bg-blue-700 text-white font-bold py-3 px-6 rounded-lg shadow-lg transition duration-300 ease-in-out transform hover:scale-105 my-2"
483
+ >
484
+ Previous Song
485
+ </button>
486
+ <button
487
+ onClick={() => setTempo(tempos.slow)}
488
+ className={`py-3 px-6 rounded-lg shadow-lg transition duration-300 ease-in-out transform hover:scale-105 my-2 ${currentTempo === tempos.slow ? 'bg-purple-700' : 'bg-purple-500 hover:bg-purple-600'} text-white font-bold`}
489
+ >
490
+ Slow ({tempos.slow} BPM)
491
+ </button>
492
+ <button
493
+ onClick={() => setTempo(tempos.medium)}
494
+ className={`py-3 px-6 rounded-lg shadow-lg transition duration-300 ease-in-out transform hover:scale-105 my-2 ${currentTempo === tempos.medium ? 'bg-purple-700' : 'bg-purple-500 hover:bg-purple-600'} text-white font-bold`}
495
+ >
496
+ Medium ({tempos.medium} BPM)
497
+ </button>
498
+ <button
499
+ onClick={() => setTempo(tempos.fast)}
500
+ className={`py-3 px-6 rounded-lg shadow-lg transition duration-300 ease-in-out transform hover:scale-105 my-2 ${currentTempo === tempos.fast ? 'bg-purple-700' : 'bg-purple-500 hover:bg-purple-600'} text-white font-bold`}
501
+ >
502
+ Fast ({tempos.fast} BPM)
503
+ </button>
504
+ <button
505
+ onClick={playChords}
506
+ className="bg-green-600 hover:bg-green-700 text-white font-bold py-3 px-6 rounded-lg shadow-lg transition duration-300 ease-in-out transform hover:scale-105 my-2"
507
+ >
508
+ Play Chords
509
+ </button>
510
+ <button
511
+ onClick={handleNextSong}
512
+ className="bg-blue-600 hover:bg-blue-700 text-white font-bold py-3 px-6 rounded-lg shadow-lg transition duration-300 ease-in-out transform hover:scale-105 my-2"
513
+ >
514
+ Next Song
515
+ </button>
516
+ </footer>
517
+ </div>
518
+ );
519
+ };
520
+
521
+ export default App;