awacke1 commited on
Commit
bd8ee36
1 Parent(s): c9933ce

Update index.html

Browse files
Files changed (1) hide show
  1. index.html +462 -18
index.html CHANGED
@@ -1,19 +1,463 @@
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
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>Multi-Instrument Synthesizer</title>
7
+ <style>
8
+ body {
9
+ font-family: Arial, sans-serif;
10
+ background-color: #f5f5f5;
11
+ display: flex;
12
+ justify-content: center;
13
+ align-items: center;
14
+ height: 100vh;
15
+ margin: 0;
16
+ }
17
+
18
+ #root {
19
+ width: 100%;
20
+ max-width: 600px;
21
+ background-color: #fff;
22
+ box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
23
+ border-radius: 10px;
24
+ padding: 20px;
25
+ }
26
+
27
+ .button {
28
+ padding: 10px;
29
+ margin: 5px;
30
+ border: none;
31
+ border-radius: 5px;
32
+ cursor: pointer;
33
+ }
34
+
35
+ .bg-blue-500 {
36
+ background-color: #4299e1;
37
+ }
38
+
39
+ .bg-gray-300 {
40
+ background-color: #e2e8f0;
41
+ }
42
+
43
+ .bg-green-500 {
44
+ background-color: #48bb78;
45
+ }
46
+
47
+ .bg-red-500 {
48
+ background-color: #f56565;
49
+ }
50
+
51
+ .text-white {
52
+ color: white;
53
+ }
54
+
55
+ .p-4 {
56
+ padding: 16px;
57
+ }
58
+
59
+ .space-y-4 > * + * {
60
+ margin-top: 16px;
61
+ }
62
+
63
+ .bg-gray-100 {
64
+ background-color: #f7fafc;
65
+ }
66
+
67
+ .rounded-xl {
68
+ border-radius: 12px;
69
+ }
70
+
71
+ .text-2xl {
72
+ font-size: 1.5rem;
73
+ }
74
+
75
+ .font-bold {
76
+ font-weight: 700;
77
+ }
78
+
79
+ .text-center {
80
+ text-align: center;
81
+ }
82
+
83
+ .mb-6 {
84
+ margin-bottom: 24px;
85
+ }
86
+
87
+ .block {
88
+ display: block;
89
+ }
90
+
91
+ .text-sm {
92
+ font-size: 0.875rem;
93
+ }
94
+
95
+ .font-medium {
96
+ font-weight: 500;
97
+ }
98
+
99
+ .text-gray-700 {
100
+ color: #4a5568;
101
+ }
102
+
103
+ .mb-2 {
104
+ margin-bottom: 8px;
105
+ }
106
+
107
+ .grid {
108
+ display: grid;
109
+ }
110
+
111
+ .grid-cols-4 {
112
+ grid-template-columns: repeat(4, minmax(0, 1fr));
113
+ }
114
+
115
+ .grid-cols-2 {
116
+ grid-template-columns: repeat(2, minmax(0, 1fr));
117
+ }
118
+
119
+ .gap-2 {
120
+ gap: 8px;
121
+ }
122
+
123
+ .w-full {
124
+ width: 100%;
125
+ }
126
+
127
+ .px-4 {
128
+ padding-left: 16px;
129
+ padding-right: 16px;
130
+ }
131
+
132
+ .py-2 {
133
+ padding-top: 8px;
134
+ padding-bottom: 8px;
135
+ }
136
+
137
+ .rounded {
138
+ border-radius: 4px;
139
+ }
140
+ </style>
141
+ </head>
142
+ <body>
143
+ <div id="root"></div>
144
+ <script src="https://unpkg.com/react/umd/react.production.min.js"></script>
145
+ <script src="https://unpkg.com/react-dom/umd/react-dom.production.min.js"></script>
146
+ <script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
147
+ <script type="text/babel">
148
+ const { useState, useEffect, useCallback, useRef } = React;
149
+
150
+ const MultiInstrumentSynthesizer = () => {
151
+ const [audioContext, setAudioContext] = useState(null);
152
+ const [isPlaying, setIsPlaying] = useState(false);
153
+ const [tempo, setTempo] = useState(120);
154
+ const [rootNote, setRootNote] = useState(261.63); // C4
155
+ const [selectedProgression, setSelectedProgression] = useState(0);
156
+ const [selectedInstrument, setSelectedInstrument] = useState('fm');
157
+
158
+ const sequencerIntervalRef = useRef(null);
159
+ const currentBarRef = useRef(0);
160
+ const currentBeatRef = useRef(0);
161
+
162
+ useEffect(() => {
163
+ const context = new (window.AudioContext || window.webkitAudioContext)();
164
+ setAudioContext(context);
165
+
166
+ return () => {
167
+ context.close();
168
+ };
169
+ }, []);
170
+
171
+ const noteFrequencies = {
172
+ 'C': 261.63, 'C#': 277.18, 'D': 293.66, 'D#': 311.13, 'E': 329.63, 'F': 349.23,
173
+ 'F#': 369.99, 'G': 392.00, 'G#': 415.30, 'A': 440.00, 'A#': 466.16, 'B': 493.88
174
+ };
175
+
176
+ const progressions = [
177
+ { name: "Blues I", progression: [1, 1, 1, 1, 4, 4, 1, 1, 5, 4, 1, 5] },
178
+ { name: "Blues II", progression: [1, 4, 1, 1, 4, 4, 1, 1, 5, 4, 1, 5] },
179
+ { name: "Rock", progression: [1, 5, 6, 4, 1, 5, 6, 4, 1, 5, 6, 4] },
180
+ { name: "Electronica", progression: [1, 6, 4, 5, 1, 6, 4, 5, 1, 6, 4, 5] },
181
+ { name: "Chillwave", progression: [1, 5, 6, 4, 1, 5, 6, 4, 1, 5, 6, 4] },
182
+ { name: "EDM", progression: [1, 4, 5, 6, 1, 4, 5, 6, 1, 4, 5, 6] }
183
+ ];
184
+
185
+ const generateChord = (root, chordType) => {
186
+ const majorScale = [0, 2, 4, 5, 7, 9, 11];
187
+ let chordIntervals;
188
+
189
+ switch (chordType) {
190
+ case 1: case 4: case 5: // Major (or dominant 7th for blues)
191
+ chordIntervals = [0, 4, 7, 10]; // Dominant 7th
192
+ break;
193
+ case 2: case 3: case 6: // Minor
194
+ chordIntervals = [0, 3, 7, 10]; // Minor 7th
195
+ break;
196
+ default:
197
+ chordIntervals = [0, 4, 7, 10]; // Default to dominant 7th
198
+ }
199
+
200
+ return chordIntervals.map(interval => {
201
+ const scaleStep = (majorScale[chordType - 1] + interval) % 12;
202
+ return root * Math.pow(2, scaleStep / 12);
203
+ });
204
+ };
205
+
206
+ const playFMSynth = useCallback((frequency, duration) => {
207
+ const osc = audioContext.createOscillator();
208
+ const mod = audioContext.createOscillator();
209
+ const modGain = audioContext.createGain();
210
+ const env = audioContext.createGain();
211
+
212
+ mod.type = 'sine';
213
+ osc.type = 'sine';
214
+
215
+ mod.connect(modGain);
216
+ modGain.connect(osc.frequency);
217
+ osc.connect(env);
218
+ env.connect(audioContext.destination);
219
+
220
+ const now = audioContext.currentTime;
221
+ const modFreq = frequency * 2;
222
+ const modIndex = 100;
223
+
224
+ mod.frequency.setValueAtTime(modFreq, now);
225
+ modGain.gain.setValueAtTime(modIndex, now);
226
+ osc.frequency.setValueAtTime(frequency, now);
227
+
228
+ env.gain.setValueAtTime(0, now);
229
+ env.gain.linearRampToValueAtTime(0.5, now + 0.01);
230
+ env.gain.exponentialRampToValueAtTime(0.01, now + duration - 0.1);
231
+ env.gain.linearRampToValueAtTime(0, now + duration);
232
+
233
+ mod.start(now);
234
+ osc.start(now);
235
+ osc.stop(now + duration);
236
+ }, [audioContext]);
237
+
238
+ const playPiano = useCallback((frequency, duration) => {
239
+ const osc = audioContext.createOscillator();
240
+ const gainNode = audioContext.createGain();
241
+ osc.type = 'triangle';
242
+ osc.frequency.setValueAtTime(frequency, audioContext.currentTime);
243
+ osc.connect(gainNode);
244
+ gainNode.connect(audioContext.destination);
245
+
246
+ gainNode.gain.setValueAtTime(0, audioContext.currentTime);
247
+ gainNode.gain.linearRampToValueAtTime(0.5, audioContext.currentTime + 0.01);
248
+ gainNode.gain.exponentialRampToValueAtTime(0.01, audioContext.currentTime + duration - 0.1);
249
+
250
+ osc.start();
251
+ osc.stop(audioContext.currentTime + duration);
252
+ }, [audioContext]);
253
+
254
+
255
+ const playGuitar = useCallback((frequency, duration) => {
256
+ const osc = audioContext.createOscillator();
257
+ const gainNode = audioContext.createGain();
258
+ osc.type = 'sawtooth';
259
+ osc.frequency.setValueAtTime(frequency, audioContext.currentTime);
260
+ osc.connect(gainNode);
261
+ gainNode.connect(audioContext.destination);
262
+
263
+ gainNode.gain.setValueAtTime(0, audioContext.currentTime);
264
+ gainNode.gain.linearRampToValueAtTime(0.5, audioContext.currentTime + 0.005);
265
+ gainNode.gain.exponentialRampToValueAtTime(0.01, audioContext.currentTime + duration - 0.05);
266
+
267
+ osc.start();
268
+ osc.stop(audioContext.currentTime + duration);
269
+ }, [audioContext]);
270
+
271
+ const playDrum = useCallback((type) => {
272
+ const osc = audioContext.createOscillator();
273
+ const gainNode = audioContext.createGain();
274
+ osc.connect(gainNode);
275
+ gainNode.connect(audioContext.destination);
276
+
277
+ if (type === 'kick') {
278
+ osc.frequency.setValueAtTime(150, audioContext.currentTime);
279
+ osc.frequency.exponentialRampToValueAtTime(0.01, audioContext.currentTime + 0.5);
280
+ gainNode.gain.setValueAtTime(1, audioContext.currentTime);
281
+ gainNode.gain.exponentialRampToValueAtTime(0.01, audioContext.currentTime + 0.5);
282
+ } else if (type === 'snare') {
283
+ osc.type = 'triangle';
284
+ osc.frequency.setValueAtTime(100, audioContext.currentTime);
285
+ gainNode.gain.setValueAtTime(1, audioContext.currentTime);
286
+ gainNode.gain.exponentialRampToValueAtTime(0.01, audioContext.currentTime + 0.2);
287
+ } else if (type === 'hihat') {
288
+ osc.type = 'square';
289
+ osc.frequency.setValueAtTime(1000, audioContext.currentTime);
290
+ gainNode.gain.setValueAtTime(0.2, audioContext.currentTime);
291
+ gainNode.gain.exponentialRampToValueAtTime(0.01, audioContext.currentTime + 0.05);
292
+ }
293
+
294
+ osc.start(audioContext.currentTime);
295
+ osc.stop(audioContext.currentTime + 0.5);
296
+ }, [audioContext]);
297
+
298
+ const playChord = useCallback((frequencies) => {
299
+ const noteDuration = 60 / tempo; // Duration of one beat
300
+
301
+ frequencies.forEach((frequency, index) => {
302
+ const playTime = audioContext.currentTime + index * 0.05; // Slight arpeggio effect
303
+ switch (selectedInstrument) {
304
+ case 'fm':
305
+ playFMSynth(frequency, noteDuration);
306
+ break;
307
+ case 'piano':
308
+ playPiano(frequency, noteDuration);
309
+ break;
310
+ case 'guitar':
311
+ playGuitar(frequency, noteDuration);
312
+ break;
313
+ }
314
+ });
315
+ }, [audioContext, tempo, selectedInstrument, playFMSynth, playPiano, playGuitar]);
316
+
317
+ const startSequencer = useCallback(() => {
318
+ if (sequencerIntervalRef.current) {
319
+ clearInterval(sequencerIntervalRef.current);
320
+ }
321
+
322
+ currentBarRef.current = 0;
323
+ currentBeatRef.current = 0;
324
+
325
+ const beatDuration = 60 / tempo / 4; // Duration of a 16th note
326
+
327
+ sequencerIntervalRef.current = setInterval(() => {
328
+ if (currentBeatRef.current % 4 === 0) { // On each quarter note
329
+ const chordType = progressions[selectedProgression].progression[currentBarRef.current];
330
+ const chord = generateChord(rootNote, chordType);
331
+ playChord(chord);
332
+
333
+ // Play kick drum on the 1 and 3
334
+ if (currentBeatRef.current % 8 === 0) {
335
+ playDrum('kick');
336
+ }
337
+ }
338
+
339
+ // Play snare on the 2 and 4
340
+ if (currentBeatRef.current % 8 === 4) {
341
+ playDrum('snare');
342
+ }
343
+
344
+ // Play hihat on every 16th note
345
+ playDrum('hihat');
346
+
347
+ currentBeatRef.current = (currentBeatRef.current + 1) % 16;
348
+ if (currentBeatRef.current === 0) {
349
+ currentBarRef.current = (currentBarRef.current + 1) % 12;
350
+ }
351
+ }, beatDuration * 1000);
352
+
353
+ setIsPlaying(true);
354
+ }, [tempo, playChord, playDrum, selectedProgression, rootNote]);
355
+
356
+ const stopSequencer = useCallback(() => {
357
+ if (sequencerIntervalRef.current) {
358
+ clearInterval(sequencerIntervalRef.current);
359
+ }
360
+ setIsPlaying(false);
361
+ }, []);
362
+
363
+ const togglePlay = () => {
364
+ if (isPlaying) {
365
+ stopSequencer();
366
+ } else {
367
+ startSequencer();
368
+ }
369
+ };
370
+
371
+ const handleRootNoteChange = (note) => {
372
+ setRootNote(noteFrequencies[note]);
373
+ if (isPlaying) {
374
+ stopSequencer();
375
+ setTimeout(startSequencer, 100);
376
+ }
377
+ };
378
+
379
+ const handleProgressionChange = (index) => {
380
+ setSelectedProgression(index);
381
+ if (isPlaying) {
382
+ stopSequencer();
383
+ setTimeout(startSequencer, 100);
384
+ }
385
+ };
386
+
387
+ const handleInstrumentChange = (instrument) => {
388
+ setSelectedInstrument(instrument);
389
+ };
390
+
391
+ const handleTempoChange = (value) => {
392
+ setTempo(value[0]);
393
+ if (isPlaying) {
394
+ stopSequencer();
395
+ setTimeout(startSequencer, 100);
396
+ }
397
+ };
398
+
399
+ return (
400
+ <div className="p-4 space-y-4 bg-gray-100 rounded-xl">
401
+ <h2 className="text-2xl font-bold text-center mb-6">Multi-Instrument Twelve-Bar Synthesizer with Drums</h2>
402
+ <div className="space-y-4">
403
+ <div>
404
+ <label className="block text-sm font-medium text-gray-700 mb-2">Root Note</label>
405
+ <div className="grid grid-cols-4 gap-2">
406
+ {Object.keys(noteFrequencies).map((note) => (
407
+ <button
408
+ key={note}
409
+ onClick={() => handleRootNoteChange(note)}
410
+ className={`button ${note === Object.keys(noteFrequencies).find(key => noteFrequencies[key] === rootNote) ? 'bg-blue-500' : 'bg-gray-300'} text-white`}
411
+ >
412
+ {note}
413
+ </button>
414
+ ))}
415
+ </div>
416
+ </div>
417
+ <div>
418
+ <label className="block text-sm font-medium text-gray-700 mb-2">Progression</label>
419
+ <div className="grid grid-cols-2 gap-2">
420
+ {progressions.map((prog, index) => (
421
+ <button
422
+ key={index}
423
+ onClick={() => handleProgressionChange(index)}
424
+ className={`button ${index === selectedProgression ? 'bg-green-500' : 'bg-gray-300'} text-white text-xs`}
425
+ >
426
+ {prog.name}
427
+ </button>
428
+ ))}
429
+ </div>
430
+ </div>
431
+ <div>
432
+ <label className="block text-sm font-medium text-gray-700 mb-2">Instrument</label>
433
+ <select onChange={(e) => handleInstrumentChange(e.target.value)} value={selectedInstrument} className="w-full">
434
+ <option value="fm">FM Synth</option>
435
+ <option value="piano">Piano</option>
436
+ <option value="guitar">Guitar</option>
437
+ </select>
438
+ </div>
439
+ <div>
440
+ <label className="block text-sm font-medium text-gray-700">Tempo: {tempo} BPM</label>
441
+ <input
442
+ type="range"
443
+ min="60"
444
+ max="180"
445
+ step="1"
446
+ value={tempo}
447
+ onChange={(e) => handleTempoChange([parseInt(e.target.value)])}
448
+ className="w-full"
449
+ />
450
+ </div>
451
+ <button onClick={togglePlay} className={`button w-full ${isPlaying ? 'bg-red-500' : 'bg-green-500'} text-white px-4 py-2 rounded`}>
452
+ {isPlaying ? 'Stop' : 'Play'}
453
+ </button>
454
+ </div>
455
+ </div>
456
+ );
457
+ };
458
+
459
+ const rootElement = document.getElementById('root');
460
+ ReactDOM.render(<MultiInstrumentSynthesizer />, rootElement);
461
+ </script>
462
+ </body>
463
  </html>