Refactor chord voicings and add pentatonic snapping function for improved melody generation and tonal consistency
Browse files
main.py
CHANGED
|
@@ -60,24 +60,39 @@ SEQ_LEN = 128
|
|
| 60 |
BARS_PER_BATCH = 2
|
| 61 |
|
| 62 |
# Chord progressions — 2 màu sắc luân phiên để tránh lặp
|
| 63 |
-
#
|
| 64 |
-
#
|
| 65 |
-
#
|
|
|
|
| 66 |
CHORD_SETS = {
|
| 67 |
-
"calm": [ # BPM cao — tông mềm,
|
| 68 |
-
[60, 64, 67, 71], # Cmaj7
|
| 69 |
-
[57, 60, 64, 67], # Am7
|
| 70 |
-
[53, 57, 60, 64], # Fmaj7
|
| 71 |
-
[55, 59, 62, 67], # G9
|
| 72 |
],
|
| 73 |
-
"groove": [ # BPM thấp — tông rõ hơn
|
| 74 |
-
[60, 64, 67, 71], # Cmaj7
|
| 75 |
-
[
|
| 76 |
-
[
|
| 77 |
-
[53,
|
| 78 |
],
|
| 79 |
}
|
| 80 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 81 |
# ==========================================
|
| 82 |
# ENTRAINMENT HELPERS
|
| 83 |
# ==========================================
|
|
@@ -115,8 +130,10 @@ def get_melody_params(bpm: int, music_beat: float) -> dict:
|
|
| 115 |
dur_factor = 0.72 + t * 0.48 # 0.72 -> 1.20 (legato khi BPM cao)
|
| 116 |
duration = round(music_beat * dur_factor, 4)
|
| 117 |
|
| 118 |
-
|
| 119 |
-
|
|
|
|
|
|
|
| 120 |
|
| 121 |
return {
|
| 122 |
"vel_low": max(40, vel_low),
|
|
@@ -221,9 +238,14 @@ async def websocket_vibe(websocket: WebSocket):
|
|
| 221 |
while midi < lo: midi += 12
|
| 222 |
while midi > hi: midi -= 12
|
| 223 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 224 |
if midi == last_midi and random.random() < 0.40:
|
| 225 |
step = random.choice([-2, 2, 3, -3, 5])
|
| 226 |
-
midi =
|
|
|
|
| 227 |
|
| 228 |
last_midi = midi
|
| 229 |
velocity = random.randint(mel_params["vel_low"],
|
|
|
|
| 60 |
BARS_PER_BATCH = 2
|
| 61 |
|
| 62 |
# Chord progressions — 2 màu sắc luân phiên để tránh lặp
|
| 63 |
+
# ── CHORD VOICINGS ───────────────────────────────────────────────────────
|
| 64 |
+
# Tất cả trong vùng G3–B4 (MIDI 55–71) — không quá trầm, không quá cao.
|
| 65 |
+
# Chỉ dùng chord diatonic trong C major để melody pentatonic luôn fit.
|
| 66 |
+
# Fm7 (borrowed chord) đã bỏ vì Ab/Eb xung đột với C major pentatonic.
|
| 67 |
CHORD_SETS = {
|
| 68 |
+
"calm": [ # BPM cao (tim nhanh, cần calming) — tông mềm, mơ màng
|
| 69 |
+
[60, 64, 67, 71], # Cmaj7 C4 E4 G4 B4
|
| 70 |
+
[57, 60, 64, 67], # Am7 A3 C4 E4 G4
|
| 71 |
+
[53, 57, 60, 64], # Fmaj7 F3 A3 C4 E4
|
| 72 |
+
[55, 59, 62, 67], # G9 G3 B3 D4 G4
|
| 73 |
],
|
| 74 |
+
"groove": [ # BPM thấp (tim chậm, ok groove) — tông rõ hơn
|
| 75 |
+
[60, 64, 67, 71], # Cmaj7 C4 E4 G4 B4
|
| 76 |
+
[57, 60, 64, 67], # Am7 A3 C4 E4 G4
|
| 77 |
+
[55, 59, 62, 65], # G7 G3 B3 D4 F4
|
| 78 |
+
[53, 57, 60, 64], # Fmaj7 F3 A3 C4 E4
|
| 79 |
],
|
| 80 |
}
|
| 81 |
|
| 82 |
+
# ── C MAJOR PENTATONIC SNAP ───────────────────────────────────────────────
|
| 83 |
+
# Pitch classes: C=0 D=2 E=4 G=7 A=9
|
| 84 |
+
# Snap mỗi nốt AI về pentatonic gần nhất → luôn consonant với mọi chord trên.
|
| 85 |
+
_PENTA = [0, 2, 4, 7, 9]
|
| 86 |
+
|
| 87 |
+
def snap_pentatonic(midi: int) -> int:
|
| 88 |
+
"""Snap MIDI note về C major pentatonic gần nhất, giữ nguyên octave."""
|
| 89 |
+
pc = midi % 12
|
| 90 |
+
best = min(_PENTA, key=lambda p: min(abs(p - pc), 12 - abs(p - pc)))
|
| 91 |
+
diff = best - pc
|
| 92 |
+
if diff > 6: diff -= 12
|
| 93 |
+
if diff < -6: diff += 12
|
| 94 |
+
return midi + diff
|
| 95 |
+
|
| 96 |
# ==========================================
|
| 97 |
# ENTRAINMENT HELPERS
|
| 98 |
# ==========================================
|
|
|
|
| 130 |
dur_factor = 0.72 + t * 0.48 # 0.72 -> 1.20 (legato khi BPM cao)
|
| 131 |
duration = round(music_beat * dur_factor, 4)
|
| 132 |
|
| 133 |
+
# Hạ range xuống ~1 quart so với trước: G3–G4 thay vì B3–E5
|
| 134 |
+
# Lofi melody nghe tốt nhất ở vùng mid (G3–A4, MIDI 55–69)
|
| 135 |
+
midi_low = int(55 + t * 3) # 55 (G3) → 58 (Bb3)
|
| 136 |
+
midi_high = int(67 + t * 3) # 67 (G4) → 70 (Bb4)
|
| 137 |
|
| 138 |
return {
|
| 139 |
"vel_low": max(40, vel_low),
|
|
|
|
| 238 |
while midi < lo: midi += 12
|
| 239 |
while midi > hi: midi -= 12
|
| 240 |
|
| 241 |
+
# Snap về C major pentatonic trước
|
| 242 |
+
midi = snap_pentatonic(midi)
|
| 243 |
+
|
| 244 |
+
# Tránh lặp nốt — nhảy sang pentatonic liền kề
|
| 245 |
if midi == last_midi and random.random() < 0.40:
|
| 246 |
step = random.choice([-2, 2, 3, -3, 5])
|
| 247 |
+
midi = snap_pentatonic(
|
| 248 |
+
max(lo - 2, min(hi + 2, midi + step)))
|
| 249 |
|
| 250 |
last_midi = midi
|
| 251 |
velocity = random.randint(mel_params["vel_low"],
|