kolaslab commited on
Commit
4e9cf78
1 Parent(s): 4cadfa7

Update index.html

Browse files
Files changed (1) hide show
  1. index.html +193 -182
index.html CHANGED
@@ -110,6 +110,7 @@
110
  height: 500px;
111
  position: relative;
112
  overflow: hidden;
 
113
  }
114
 
115
  .grid {
@@ -288,196 +289,206 @@
288
 
289
  <script>
290
  class MelodyGenerator {
291
- constructor() {
292
- this.synth = new Tone.PolySynth(Tone.Synth).toDestination();
293
- this.sequence = [];
294
- this.isPlaying = false;
295
- this.currentWaveform = 'sine';
296
-
297
- this.initUI();
298
- this.setupEventListeners();
299
- }
300
-
301
- initUI() {
302
- // Initialize grid
303
- const grid = document.getElementById('grid');
304
- for (let i = 0; i < 88; i++) {
305
- for (let j = 0; j < 32; j++) {
306
- const cell = document.createElement('div');
307
- cell.className = 'cell';
308
- cell.dataset.note = i;
309
- cell.dataset.time = j;
310
- cell.onclick = () => this.toggleCell(cell);
311
- grid.appendChild(cell);
312
- }
313
- }
314
-
315
- // Initialize value displays
316
- document.querySelectorAll('input[type="range"]').forEach(input => {
317
- const display = document.getElementById(`${input.id}-value`);
318
- if (display) display.textContent = input.value;
319
- });
320
- }
321
-
322
- setupEventListeners() {
323
- document.getElementById('generate').onclick = () => this.generateMelody();
324
- document.getElementById('play').onclick = () => this.togglePlay();
325
- document.getElementById('stop').onclick = () => this.stop();
326
- document.getElementById('download').onclick = () => this.downloadMIDI();
327
-
328
- // Waveform selection
329
- document.querySelectorAll('.wave-btn').forEach(btn => {
330
- btn.onclick = (e) => {
331
- document.querySelectorAll('.wave-btn').forEach(b => b.classList.remove('active'));
332
- e.target.classList.add('active');
333
- this.currentWaveform = e.target.dataset.wave;
334
- this.updateSynthSettings();
335
- };
336
- });
337
-
338
- // Range input updates
339
- document.querySelectorAll('input[type="range"]').forEach(input => {
340
- input.oninput = (e) => {
341
- const display = document.getElementById(`${input.id}-value`);
342
- if (display) {
343
- display.textContent = input.id === 'tempo' ?
344
- `${e.target.value} BPM` : e.target.value;
345
- }
346
- };
347
- });
348
- }
349
-
350
- updateSynthSettings() {
351
- this.synth.set({
352
- oscillator: { type: this.currentWaveform }
353
- });
354
- }
355
-
356
- generateMelody() {
357
- this.clearGrid();
358
- const key = document.getElementById('key').value;
359
- const scale = document.getElementById('scale').value;
360
- const complexity = parseInt(document.getElementById('complexity').value);
361
- const baseOctave = parseInt(document.getElementById('octave').value);
362
-
363
- this.sequence = this.createMelodySequence(key, scale, complexity, baseOctave);
364
- this.visualizeSequence();
365
- }
366
-
367
- createMelodySequence(key, scale, complexity, baseOctave) {
368
- const noteCount = Math.floor(complexity * 4);
369
- const sequence = [];
370
- const scaleNotes = this.getScaleNotes(key, scale);
371
-
372
- for (let i = 0; i < noteCount; i++) {
373
- const time = Math.floor(Math.random() * 32);
374
- const note = scaleNotes[Math.floor(Math.random() * scaleNotes.length)];
375
- sequence.push({
376
- note: `${note}${baseOctave}`,
377
- time: time,
378
- duration: 0.25
379
- });
380
- }
381
-
382
- return sequence;
383
- }
384
-
385
- getScaleNotes(key, scale) {
386
- const notes = ['C', 'C#', 'D', 'D#', 'E', 'F', 'F#', 'G', 'G#', 'A', 'A#', 'B'];
387
- const scales = {
388
- major: [0, 2, 4, 5, 7, 9, 11],
389
- minor: [0, 2, 3, 5, 7, 8, 10],
390
- harmonicMinor: [0, 2, 3, 5, 7, 8, 11],
391
- dorian: [0, 2, 3, 5, 7, 9, 10],
392
- phrygian: [0, 1, 3, 5, 7, 8, 10],
393
- lydian: [0, 2, 4, 6, 7, 9, 11],
394
- mixolydian: [0, 2, 4, 5, 7, 9, 10]
395
- };
396
-
397
- const keyIndex = notes.indexOf(key);
398
- const scalePattern = scales[scale];
399
- return scalePattern.map(interval => notes[(keyIndex + interval) % 12]);
400
- }
401
-
402
- visualizeSequence() {
403
- this.sequence.forEach(note => {
404
- const noteIndex = this.getNoteIndex(note.note);
405
- const cell = document.querySelector(
406
- `.cell[data-note="${noteIndex}"][data-time="${note.time}"]`
407
- );
408
- if (cell) cell.classList.add('active');
409
- });
410
- }
411
-
412
- getNoteIndex(note) {
413
- const noteName = note.slice(0, -1);
414
- const octave = parseInt(note.slice(-1));
415
- const notes = ['C', 'C#', 'D', 'D#', 'E', 'F', 'F#', 'G', 'G#', 'A', 'A#', 'B'];
416
- return (octave * 12) + notes.indexOf(noteName);
417
  }
 
418
 
419
- async togglePlay() {
420
- if (this.isPlaying) {
421
- this.stop();
422
- } else {
423
- await Tone.start();
424
- this.play();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
425
  }
426
- }
427
-
428
- play() {
429
- this.isPlaying = true;
430
- const tempo = document.getElementById('tempo').value;
431
- Tone.Transport.bpm.value = tempo;
432
-
433
- const part = new Tone.Part(((time, note) => {
434
- this.synth.triggerAttackRelease(note.note, note.duration, time);
435
- }), this.sequence.map(note => ({
436
- time: note.time * 0.25,
437
- note: note.note,
438
- duration: note.duration
439
- }))).start(0);
440
-
441
- Tone.Transport.start();
442
- }
443
-
444
- stop() {
445
- this.isPlaying = false;
446
- Tone.Transport.stop();
447
- Tone.Transport.position = 0;
448
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
449
 
450
- toggleCell(cell) {
451
- cell.classList.toggle('active');
452
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
453
 
454
- clearGrid() {
455
- document.querySelectorAll('.cell').forEach(cell => {
456
- cell.classList.remove('active');
457
- });
458
- }
459
 
460
- downloadMIDI() {
461
- // Basic MIDI file structure
462
- const midiData = [
463
- 0x4D, 0x54, 0x68, 0x64, // MThd
464
- 0x00, 0x00, 0x00, 0x06, // Header size
465
- 0x00, 0x01, // Format
466
- 0x00, 0x01, // Tracks
467
- 0x01, 0x80 // Division
468
- ];
469
-
470
- const blob = new Blob([new Uint8Array(midiData)], { type: 'audio/midi' });
471
- const url = window.URL.createObjectURL(blob);
472
- const a = document.createElement('a');
473
- a.href = url;
474
- a.download = 'melody.mid';
475
- a.click();
476
- window.URL.revokeObjectURL(url);
477
- }
 
 
478
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
479
 
480
  const generator = new MelodyGenerator();
481
  </script>
482
  </body>
483
- </html><script async data-explicit-opt-in="true" data-cookie-opt-in="true" src="https://vercel.live/_next-live/feedback/feedback.js"></script>
 
110
  height: 500px;
111
  position: relative;
112
  overflow: hidden;
113
+ transform: scaleY(-1);
114
  }
115
 
116
  .grid {
 
289
 
290
  <script>
291
  class MelodyGenerator {
292
+ constructor() {
293
+ this.synth = new Tone.PolySynth(Tone.Synth).toDestination();
294
+ this.sequence = [];
295
+ this.isPlaying = false;
296
+ this.currentWaveform = 'sine';
297
+ this.currentPart = null; // Store the current Tone.Part
298
+ this.initUI();
299
+ this.setupEventListeners();
300
+ }
301
+
302
+ initUI() {
303
+ // Initialize grid
304
+ const grid = document.getElementById('grid');
305
+ for (let i = 0; i < 88; i++) {
306
+ for (let j = 0; j < 32; j++) {
307
+ const cell = document.createElement('div');
308
+ cell.className = 'cell';
309
+ cell.dataset.note = i;
310
+ cell.dataset.time = j;
311
+ cell.onclick = () => this.toggleCell(cell);
312
+ grid.appendChild(cell);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
313
  }
314
+ }
315
 
316
+ // Initialize value displays
317
+ document.querySelectorAll('input[type="range"]').forEach(input => {
318
+ const display = document.getElementById(`${input.id}-value`);
319
+ if (display) display.textContent = input.value;
320
+ });
321
+ }
322
+
323
+ setupEventListeners() {
324
+ document.getElementById('generate').onclick = () => this.generateMelody();
325
+ document.getElementById('play').onclick = () => this.togglePlay();
326
+ document.getElementById('stop').onclick = () => this.stop();
327
+ document.getElementById('download').onclick = () => this.downloadMIDI();
328
+
329
+ // Waveform selection
330
+ document.querySelectorAll('.wave-btn').forEach(btn => {
331
+ btn.onclick = (e) => {
332
+ document.querySelectorAll('.wave-btn').forEach(b => b.classList.remove('active'));
333
+ e.target.classList.add('active');
334
+ this.currentWaveform = e.target.dataset.wave;
335
+ this.updateSynthSettings();
336
+ };
337
+ });
338
+
339
+ // Range input updates
340
+ document.querySelectorAll('input[type="range"]').forEach(input => {
341
+ input.oninput = (e) => {
342
+ const display = document.getElementById(`${input.id}-value`);
343
+ if (display) {
344
+ display.textContent = input.id === 'tempo' ?
345
+ `${e.target.value} BPM` : e.target.value;
346
  }
347
+ };
348
+ });
349
+ }
350
+
351
+ updateSynthSettings() {
352
+ this.synth.set({
353
+ oscillator: { type: this.currentWaveform }
354
+ });
355
+ }
356
+
357
+ generateMelody() {
358
+ this.stop(); // Stop any existing playback
359
+ this.clearGrid();
360
+ const key = document.getElementById('key').value;
361
+ const scale = document.getElementById('scale').value;
362
+ const complexity = parseInt(document.getElementById('complexity').value);
363
+ const baseOctave = parseInt(document.getElementById('octave').value);
364
+
365
+ this.sequence = this.createMelodySequence(key, scale, complexity, baseOctave);
366
+ this.visualizeSequence();
367
+ }
368
+
369
+ createMelodySequence(key, scale, complexity, baseOctave) {
370
+ const noteCount = Math.floor(complexity * 4);
371
+ const sequence = [];
372
+ const scaleNotes = this.getScaleNotes(key, scale);
373
+
374
+ for (let i = 0; i < noteCount; i++) {
375
+ const time = Math.floor(Math.random() * 32);
376
+ const note = scaleNotes[Math.floor(Math.random() * scaleNotes.length)];
377
+ sequence.push({
378
+ note: `${note}${baseOctave}`,
379
+ time: time,
380
+ duration: 0.25
381
+ });
382
+ }
383
 
384
+ return sequence;
385
+ }
386
+
387
+ getScaleNotes(key, scale) {
388
+ const notes = ['C', 'C#', 'D', 'D#', 'E', 'F', 'F#', 'G', 'G#', 'A', 'A#', 'B'];
389
+ const scales = {
390
+ major: [0, 2, 4, 5, 7, 9, 11],
391
+ minor: [0, 2, 3, 5, 7, 8, 10],
392
+ harmonicMinor: [0, 2, 3, 5, 7, 8, 11],
393
+ dorian: [0, 2, 3, 5, 7, 9, 10],
394
+ phrygian: [0, 1, 3, 5, 7, 8, 10],
395
+ lydian: [0, 2, 4, 6, 7, 9, 11],
396
+ mixolydian: [0, 2, 4, 5, 7, 9, 10]
397
+ };
398
+
399
+ const keyIndex = notes.indexOf(key);
400
+ const scalePattern = scales[scale];
401
+ return scalePattern.map(interval => notes[(keyIndex + interval) % 12]);
402
+ }
403
+
404
+ visualizeSequence() {
405
+ this.sequence.forEach(note => {
406
+ const noteIndex = this.getNoteIndex(note.note);
407
+ const cell = document.querySelector(
408
+ `.cell[data-note="${noteIndex}"][data-time="${note.time}"]`
409
+ );
410
+ if (cell) cell.classList.add('active');
411
+ });
412
+ }
413
+
414
+ getNoteIndex(note) {
415
+ const noteName = note.slice(0, -1);
416
+ const octave = parseInt(note.slice(-1));
417
+ const notes = ['C', 'C#', 'D', 'D#', 'E', 'F', 'F#', 'G', 'G#', 'A', 'A#', 'B'];
418
+ return (octave * 12) + notes.indexOf(noteName);
419
+ }
420
+
421
+ async togglePlay() {
422
+ if (this.isPlaying) {
423
+ this.stop();
424
+ } else {
425
+ await Tone.start();
426
+ this.play();
427
+ }
428
+ }
429
 
430
+ play() {
431
+ if (this.currentPart) {
432
+ this.currentPart.stop();
433
+ this.currentPart.dispose();
434
+ }
435
 
436
+ this.isPlaying = true;
437
+ const tempo = document.getElementById('tempo').value;
438
+ Tone.Transport.bpm.value = tempo;
439
+
440
+ this.currentPart = new Tone.Part(((time, note) => {
441
+ this.synth.triggerAttackRelease(note.note, note.duration, time);
442
+ }), this.sequence.map(note => ({
443
+ time: note.time * 0.25,
444
+ note: note.note,
445
+ duration: note.duration
446
+ }))).start(0);
447
+
448
+ Tone.Transport.start();
449
+ }
450
+
451
+ stop() {
452
+ this.isPlaying = false;
453
+ if (this.currentPart) {
454
+ this.currentPart.stop();
455
+ this.currentPart.dispose();
456
  }
457
+ Tone.Transport.stop();
458
+ Tone.Transport.position = 0;
459
+ }
460
+
461
+ toggleCell(cell) {
462
+ cell.classList.toggle('active');
463
+ }
464
+
465
+ clearGrid() {
466
+ document.querySelectorAll('.cell').forEach(cell => {
467
+ cell.classList.remove('active');
468
+ });
469
+ }
470
+
471
+ downloadMIDI() {
472
+ // Basic MIDI file structure
473
+ const midiData = [
474
+ 0x4D, 0x54, 0x68, 0x64, // MThd
475
+ 0x00, 0x00, 0x00, 0x06, // Header size
476
+ 0x00, 0x01, // Format
477
+ 0x00, 0x01, // Tracks
478
+ 0x01, 0x80 // Division
479
+ ];
480
+
481
+ const blob = new Blob([new Uint8Array(midiData)], { type: 'audio/midi' });
482
+ const url = window.URL.createObjectURL(blob);
483
+ const a = document.createElement('a');
484
+ a.href = url;
485
+ a.download = 'melody.mid';
486
+ a.click();
487
+ window.URL.revokeObjectURL(url);
488
+ }
489
+ }
490
 
491
  const generator = new MelodyGenerator();
492
  </script>
493
  </body>
494
+ </html>