Hezu06 commited on
Commit
efa0dd0
·
1 Parent(s): 1905ff7
Files changed (1) hide show
  1. main.py +95 -52
main.py CHANGED
@@ -74,105 +74,148 @@ model.eval() # Chuyển sang chế độ suy luận
74
  with open('notes_corpus.pkl', 'rb') as f:
75
  corpus = pickle.load(f)
76
 
77
- # ==========================================
78
- # 2. HÀM SÁNG TÁC BẰNG NHIỆT ĐỘ (TEMPERATURE)
79
- # ==========================================
80
- def sample_with_top_k(probs, k=10):
81
- idx = np.argsort(probs)[-k:]
82
- probs = probs[idx]
83
- probs = probs / probs.sum()
84
- return np.random.choice(idx, p=probs)
85
-
86
- def generate_ai_midi(bpm: int, output_file: str, num_notes: int = 60):
87
- num_notes = int(120 + bpm * 1.5)
88
- # --- PHẦN THÊM MỚI: ĐỊNH NGHĨA THANG NGŨ CUNG (C Pentatonic) ---
89
- # Các nốt: C, D, E, G, A ở nhiều quãng tám (từ trầm đến trung)
90
- # Chỉ dùng các nốt từ C2 (36) đến C4 (60) - Tuyệt đối không để vượt quá 60
91
- # PENTATONIC_MS = [36, 38, 40, 43, 45, 48, 50, 52, 55, 57, 60]
92
-
93
- # def get_closest_pentatonic(pitch_input):
94
- # """Hàm ép một nốt bất kỳ về nốt Ngũ cung gần nhất"""
95
- # return min(PENTATONIC_MS, key=lambda x: abs(x - pitch_input))
96
- # -----------------------------------------------------------
97
- # Logic phản hồi sinh học
98
  temperature = 0.6 + (bpm - 60) / 100
99
  temperature = max(0.5, min(1.2, temperature))
100
-
101
- # Chọn bừa 100 nốt từ kho dữ liệu để làm "mồi" cho AI
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
102
  start_idx = random.randint(0, len(corpus) - 100)
103
  seed_sequence = corpus[start_idx:start_idx + 100]
104
- pattern = [note_to_int[char] for char in seed_sequence]
105
-
 
 
106
  generated_notes = []
107
-
108
- print(f"Đang sáng tác với Temp={temperature}...")
109
-
110
- # AI bắt đầu dự đoán
 
 
 
 
 
 
 
 
 
 
 
111
  for i in range(num_notes):
112
  sequence_tensor = torch.tensor([pattern], dtype=torch.long).to(device)
113
-
114
  with torch.no_grad():
115
  prediction = model(sequence_tensor)
116
-
117
- # ÁP DỤNG TEMPERATURE
118
  prediction = prediction / temperature
119
- probabilities = torch.softmax(prediction, dim=1).numpy()[0]
120
-
121
- # Chọn nốt dựa trên phân phối xác suất
122
- index = sample_with_top_k(probabilities, k=8)
123
-
124
  result_note = int_to_note[index]
 
125
  generated_notes.append(result_note)
126
-
127
- # Cập nhật cửa sổ trượt (thêm nốt mới, xóa nốt cũ nhất)
128
  pattern.append(index)
129
  pattern = pattern[1:]
130
- print("Generated notes:", generated_notes[:20])
 
 
131
  # ==========================================
132
- # 3. CHUYỂN NGỮ AI THÀNH FILE MIDI
133
  # ==========================================
134
  offset = 0
135
  output_stream = stream.Stream()
136
- output_stream.append(instrument.Piano()) # Chọn nhạc cụ
137
-
138
- for pattern_note in generated_notes:
 
 
 
 
 
139
  try:
140
  parts = pattern_note.split("_")
141
 
 
 
 
 
 
 
 
 
 
 
 
142
  # REST
143
  if parts[0] == "REST":
144
- dur = float(eval(parts[1])) # "33/100" → 0.33
145
  offset += dur
146
  continue
147
 
148
  # NOTE
149
  elif parts[0] == "NOTE":
150
  pitch = parts[1]
151
- dur = float(eval(parts[2]))
 
 
 
 
 
152
 
153
  new_obj = note.Note(pitch)
154
  new_obj.quarterLength = dur
155
 
156
- # CHORD
157
  elif parts[0] == "CHORD":
158
  pitches = parts[1].split(".")
159
- dur = float(eval(parts[2]))
160
 
161
  chord_notes = [note.Note(p) for p in pitches]
162
  new_obj = chord.Chord(chord_notes)
163
  new_obj.quarterLength = dur
164
 
165
- # thêm vào stream
166
- new_obj.offset = offset
 
 
167
  output_stream.append(new_obj)
168
 
169
  offset += dur
170
 
171
  except Exception as e:
172
  print("Skip:", pattern_note, e)
173
- continue
174
- output_stream.write('midi', fp=output_file)
 
 
 
 
 
 
 
 
175
 
 
176
  # ==========================================
177
  # 4. API ENDPOINT
178
  # ==========================================
 
74
  with open('notes_corpus.pkl', 'rb') as f:
75
  corpus = pickle.load(f)
76
 
77
+ def generate_ai_midi(bpm: int, output_file: str, num_notes: int = 200):
78
+ # ==========================================
79
+ # 1. CONFIG CHILL MUSIC
80
+ # ==========================================
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
81
  temperature = 0.6 + (bpm - 60) / 100
82
  temperature = max(0.5, min(1.2, temperature))
83
+
84
+ # 🎼 C Major scale (safe, chill)
85
+ SCALE = ["C", "D", "E", "F", "G", "A", "B"]
86
+
87
+ # 🎹 Chill chord progression (lofi classic)
88
+ CHORD_PROGRESSION = [
89
+ ["C4", "E4", "G4"],
90
+ ["A3", "C4", "E4"],
91
+ ["D4", "F4", "A4"],
92
+ ["G3", "B3", "D4"]
93
+ ]
94
+
95
+ # 🥁 Rhythm pattern (groove)
96
+ RHYTHM_PATTERN = [0.5, 0.5, 1.0, 0.5, 0.5, 1.5]
97
+
98
+ # ==========================================
99
+ # 2. SEED (random mạnh hơn)
100
+ # ==========================================
101
  start_idx = random.randint(0, len(corpus) - 100)
102
  seed_sequence = corpus[start_idx:start_idx + 100]
103
+ random.shuffle(seed_sequence)
104
+
105
+ pattern = [note_to_int[n] for n in seed_sequence]
106
+
107
  generated_notes = []
108
+
109
+ print(f"🎧 Generating chill music | Temp={temperature:.2f}")
110
+
111
+ # ==========================================
112
+ # 3. TOP-K SAMPLING
113
+ # ==========================================
114
+ def sample_with_top_k(probs, k=8):
115
+ idx = np.argsort(probs)[-k:]
116
+ probs = probs[idx]
117
+ probs = probs / probs.sum()
118
+ return np.random.choice(idx, p=probs)
119
+
120
+ # ==========================================
121
+ # 4. GENERATE NOTES
122
+ # ==========================================
123
  for i in range(num_notes):
124
  sequence_tensor = torch.tensor([pattern], dtype=torch.long).to(device)
125
+
126
  with torch.no_grad():
127
  prediction = model(sequence_tensor)
128
+
 
129
  prediction = prediction / temperature
130
+ probs = torch.softmax(prediction, dim=1).cpu().numpy()[0]
131
+
132
+ index = sample_with_top_k(probs)
 
 
133
  result_note = int_to_note[index]
134
+
135
  generated_notes.append(result_note)
136
+
 
137
  pattern.append(index)
138
  pattern = pattern[1:]
139
+
140
+ print("Sample notes:", generated_notes[:10])
141
+
142
  # ==========================================
143
+ # 5. BUILD MIDI STREAM
144
  # ==========================================
145
  offset = 0
146
  output_stream = stream.Stream()
147
+
148
+ inst = instrument.Piano()
149
+ inst.midiProgram = 0
150
+ output_stream.insert(0, inst)
151
+
152
+ chord_idx = 0
153
+
154
+ for i, pattern_note in enumerate(generated_notes):
155
  try:
156
  parts = pattern_note.split("_")
157
 
158
+ # 🎹 ADD CHORD (every 8 notes)
159
+ if i % 8 == 0:
160
+ chord_notes = [note.Note(p) for p in CHORD_PROGRESSION[chord_idx]]
161
+ ch = chord.Chord(chord_notes)
162
+ ch.quarterLength = 4
163
+ ch.offset = offset
164
+ ch.volume.velocity = random.randint(50, 80)
165
+ output_stream.append(ch)
166
+
167
+ chord_idx = (chord_idx + 1) % len(CHORD_PROGRESSION)
168
+
169
  # REST
170
  if parts[0] == "REST":
171
+ dur = RHYTHM_PATTERN[i % len(RHYTHM_PATTERN)]
172
  offset += dur
173
  continue
174
 
175
  # NOTE
176
  elif parts[0] == "NOTE":
177
  pitch = parts[1]
178
+
179
+ # 🎼 Scale filter
180
+ if pitch[0] not in SCALE:
181
+ continue
182
+
183
+ dur = RHYTHM_PATTERN[i % len(RHYTHM_PATTERN)]
184
 
185
  new_obj = note.Note(pitch)
186
  new_obj.quarterLength = dur
187
 
188
+ # CHORD (AI generated)
189
  elif parts[0] == "CHORD":
190
  pitches = parts[1].split(".")
191
+ dur = RHYTHM_PATTERN[i % len(RHYTHM_PATTERN)]
192
 
193
  chord_notes = [note.Note(p) for p in pitches]
194
  new_obj = chord.Chord(chord_notes)
195
  new_obj.quarterLength = dur
196
 
197
+ # 🎧 HUMANIZATION
198
+ new_obj.offset = offset + random.uniform(-0.02, 0.02)
199
+ new_obj.volume.velocity = random.randint(60, 90)
200
+
201
  output_stream.append(new_obj)
202
 
203
  offset += dur
204
 
205
  except Exception as e:
206
  print("Skip:", pattern_note, e)
207
+ continue
208
+ final_stream = stream.Stream()
209
+
210
+ for i in range(3): # loop 3 lần
211
+ for el in output_stream:
212
+ new_el = el
213
+ new_el.offset += i * offset
214
+ final_stream.append(new_el)
215
+
216
+ print("Total notes:", len(final_stream))
217
 
218
+ final_stream.write('midi', fp=output_file)
219
  # ==========================================
220
  # 4. API ENDPOINT
221
  # ==========================================