Hezu06 commited on
Commit
754fcda
·
1 Parent(s): 96a341e

refactor music generation by adjusting synth volumes and batching note processing for improved timing

Browse files
Files changed (2) hide show
  1. index.html +11 -11
  2. main.py +48 -45
index.html CHANGED
@@ -523,7 +523,7 @@
523
 
524
  // 🌊 PAD (chord)
525
  const pad = new Tone.PolySynth(Tone.Synth, {
526
- volume: -12,
527
  oscillator: { type: "sine" },
528
  envelope: {
529
  attack: 1,
@@ -535,7 +535,7 @@
535
 
536
  // 🥁 KICK
537
  const kick = new Tone.MembraneSynth({
538
- volume: -8,
539
  pitchDecay: 0.05,
540
  octaves: 6,
541
  envelope: {
@@ -594,30 +594,30 @@
594
  ws.send(JSON.stringify({ bpm: currentBpm }));
595
  };
596
 
 
 
597
  ws.onmessage = (event) => {
598
  const data = JSON.parse(event.data);
599
 
600
  data.notes.forEach(n => {
601
  const note = Tone.Frequency(n.note, "midi").toNote();
602
- const vel = Math.max(0.3, (n.velocity || 80) / 127);
603
-
604
- const t = Tone.now();
605
 
606
  if (n.type === "melody") {
607
- piano.triggerAttackRelease(note, n.duration, t, vel);
608
  }
609
 
610
  else if (n.type === "chord") {
611
- pad.triggerAttackRelease(note, n.duration, t, vel * 0.7);
612
  }
613
 
614
  else if (n.type === "drum") {
615
- if (n.note === 36)
616
- kick.triggerAttackRelease("C1", "8n", t);
617
- if (n.note === 38)
618
- snare.triggerAttackRelease("16n", t);
619
  }
620
  });
 
 
621
  };
622
 
623
  ws.onerror = (err) => {
 
523
 
524
  // 🌊 PAD (chord)
525
  const pad = new Tone.PolySynth(Tone.Synth, {
526
+ volume: -8,
527
  oscillator: { type: "sine" },
528
  envelope: {
529
  attack: 1,
 
535
 
536
  // 🥁 KICK
537
  const kick = new Tone.MembraneSynth({
538
+ volume: -12,
539
  pitchDecay: 0.05,
540
  octaves: 6,
541
  envelope: {
 
594
  ws.send(JSON.stringify({ bpm: currentBpm }));
595
  };
596
 
597
+ let startTime = Tone.now() + 0.1; // Thời gian bắt đầu chơi nhạc, cách hiện tại 100ms để đảm bảo đồng bộ
598
+
599
  ws.onmessage = (event) => {
600
  const data = JSON.parse(event.data);
601
 
602
  data.notes.forEach(n => {
603
  const note = Tone.Frequency(n.note, "midi").toNote();
604
+ const t = startTime + n.time; // 🔥 timeline chuẩn
 
 
605
 
606
  if (n.type === "melody") {
607
+ piano.triggerAttackRelease(note, n.duration, t);
608
  }
609
 
610
  else if (n.type === "chord") {
611
+ pad.triggerAttackRelease(note, n.duration, t);
612
  }
613
 
614
  else if (n.type === "drum") {
615
+ if (n.note === 36) kick.triggerAttackRelease("C1", "8n", t);
616
+ if (n.note === 38) snare.triggerAttackRelease("16n", t);
 
 
617
  }
618
  });
619
+
620
+ startTime += data.step * 16; // advance timeline
621
  };
622
 
623
  ws.onerror = (err) => {
main.py CHANGED
@@ -114,66 +114,69 @@ async def websocket_vibe(websocket: WebSocket):
114
  return min(chord + [c+12 for c in chord], key=lambda x: abs(x - midi))
115
 
116
  try:
 
 
117
  while is_playing:
118
  beat = 60.0 / current_bpm
119
- step = beat / 2 # 🔥 QUAN TRỌNG: nhanh hơn x2
120
 
121
- inp[0] = torch.tensor(pattern)
122
 
123
- with torch.no_grad():
124
- pred = model(inp)
125
- probs = torch.softmax(pred / 0.8, dim=1).numpy()[0]
126
- idx = np.random.choice(len(probs), p=probs)
127
 
128
- tok = int_to_note[idx]
129
- pattern = pattern[1:] + [idx]
 
 
130
 
131
- midi = None
132
- try:
133
- if tok.startswith("NOTE"):
134
- midi = note.Note(tok.split("_")[1]).pitch.midi
135
- except:
136
- pass
137
 
138
- chord = CHORDS[(note_count // 8) % len(CHORDS)]
 
 
 
 
 
139
 
140
- notes = []
141
 
142
- # 🎹 CHORD (pad)
143
- if note_count % 8 == 0:
144
- for c in chord:
145
- notes.append({
146
- "note": c,
147
- "velocity": 40,
148
- "duration": beat * 4,
149
- "type": "chord"
150
- })
151
 
152
- # 🎸 MELODY
153
- if midi:
154
- while midi < 55: midi += 12
155
- while midi > 75: midi -= 12
 
 
 
 
 
 
156
 
157
- if random.random() < 0.7:
158
- midi = snap(midi, chord)
 
 
159
 
160
- notes.append({
161
- "note": midi,
162
- "velocity": random.randint(55, 80),
163
- "duration": step * 1.2,
164
- "type": "melody"
165
- })
 
166
 
167
- # 🥁 DRUM
168
- if note_count % 4 == 0:
169
- notes.append({"note": 36, "type": "drum"})
170
- if note_count % 4 == 2:
171
- notes.append({"note": 38, "type": "drum"})
172
 
173
- await websocket.send_json({"notes": notes})
 
 
 
174
 
175
- note_count += 1
176
- await asyncio.sleep(step)
177
 
178
  except:
179
  pass
 
114
  return min(chord + [c+12 for c in chord], key=lambda x: abs(x - midi))
115
 
116
  try:
117
+ STEPS_PER_BATCH = 16
118
+
119
  while is_playing:
120
  beat = 60.0 / current_bpm
121
+ step = beat / 2
122
 
123
+ batch = []
124
 
125
+ for i in range(STEPS_PER_BATCH):
126
+ inp[0] = torch.tensor(pattern)
 
 
127
 
128
+ with torch.no_grad():
129
+ pred = model(inp)
130
+ probs = torch.softmax(pred / 0.8, dim=1).numpy()[0]
131
+ idx = np.random.choice(len(probs), p=probs)
132
 
133
+ tok = int_to_note[idx]
134
+ pattern = pattern[1:] + [idx]
 
 
 
 
135
 
136
+ midi = None
137
+ try:
138
+ if tok.startswith("NOTE"):
139
+ midi = note.Note(tok.split("_")[1]).pitch.midi
140
+ except:
141
+ pass
142
 
143
+ chord = CHORDS[((note_count + i) // 8) % len(CHORDS)]
144
 
145
+ notes = []
 
 
 
 
 
 
 
 
146
 
147
+ # chord
148
+ if (note_count + i) % 8 == 0:
149
+ for c in chord:
150
+ notes.append({
151
+ "note": c,
152
+ "velocity": 40,
153
+ "duration": beat * 4,
154
+ "type": "chord",
155
+ "time": i * step
156
+ })
157
 
158
+ # melody
159
+ if midi:
160
+ while midi < 55: midi += 12
161
+ while midi > 75: midi -= 12
162
 
163
+ notes.append({
164
+ "note": midi,
165
+ "velocity": random.randint(55, 80),
166
+ "duration": step * 1.2,
167
+ "type": "melody",
168
+ "time": i * step
169
+ })
170
 
171
+ batch.extend(notes)
 
 
 
 
172
 
173
+ await websocket.send_json({
174
+ "notes": batch,
175
+ "step": step
176
+ })
177
 
178
+ note_count += STEPS_PER_BATCH
179
+ await asyncio.sleep(step * STEPS_PER_BATCH * 0.8)
180
 
181
  except:
182
  pass