86 lines
4.3 KiB
Markdown
86 lines
4.3 KiB
Markdown
Nguyên nhân xuất hiện thông báo lỗi từ SpessaSynth Core:
|
|
`basic_synthesizer_core.ts:169 No preset found for 0:0:0! Did you forget to add a sound bank?`
|
|
|
|
Thông số `0:0:0` trong thông báo đại diện cho `Bank MSB : Bank LSB : Program Number` (cấu hình mặc định ban đầu của kênh MIDI). Lỗi này xảy ra do **3 nguyên nhân chính** sau:
|
|
|
|
---
|
|
|
|
### 1. Phân tích nguyên nhân kỹ thuật
|
|
|
|
1. **Chưa gửi lệnh `programChange` & `controllerChange` sang SpessaSynth khi chọn "Pipa"**:
|
|
* Khi chọn nhạc cụ "Pipa" trên UI, ứng dụng mới chỉ cập nhật giao diện mà **chưa kích hoạt lệnh đổi tiếng** (`synthInstance.programChange(channel, program)`) sang kênh MIDI tương ứng của SpessaSynth.
|
|
* Khi gõ phím trên `SE49` (nhận dữ liệu Raw MIDI `[144, 65, 78]`), SpessaSynth vẫn truy xuất bộ tiếng ở vị trí mặc định là `Bank 0 : Program 0` (thường là Grand Piano theo chuẩn General MIDI).
|
|
* Trong SoundFont `DSK_Asian_DreamZ`, vị trí `0:0:0` không tồn tại hoặc không chứa thông tin nốt, khiến SpessaSynth thông báo không tìm thấy preset.
|
|
|
|
|
|
2. **Sai chỉ số Bank / Program của nhạc cụ "Pipa"**:
|
|
* Tệp SoundFont `DSK_Asian_DreamZ` là bộ tiếng tùy chỉnh (Non-General MIDI). 8 nhạc cụ bên trong (*Pipa, Pipa Tremolo, Luan, Guzhen, Erhu, Koto, Ban Di, Percussion*) được đánh số `Program` riêng biệt.
|
|
* Nếu UI gửi `program = 0` nhưng thực tế trong file SoundFont, tiếng Pipa nằm ở `program = 1` hoặc `bank = 1`, SpessaSynth sẽ không tìm thấy preset tương ứng.
|
|
|
|
|
|
3. **Lỗi Header/Mapping khi convert từ `.sf2` sang `.sf3**`:
|
|
* Một số công cụ nén (như `mscore` CLI) khi convert các SoundFont tùy chỉnh có cấu trúc phi chuẩn có thể làm mất hoặc biến đổi bảng thông tin Preset Header.
|
|
|
|
|
|
|
|
---
|
|
|
|
### 2. Các bước khắc phục
|
|
|
|
#### **Bước 1: Gọi `programChange` & `controllerChange` ngay khi chọn nhạc cụ trên UI**
|
|
|
|
Đảm bảo khi người dùng chọn nhạc cụ trên giao diện, hàm chọn Bank/Program được kích hoạt trên kênh MIDI phát tiếng:
|
|
|
|
```javascript
|
|
// Khi người dùng chọn "Pipa" trên Dropdown Synth UI
|
|
async function onSelectTrackInstrument(trackChannel, sfId, bank, program) {
|
|
// 1. Nạp file .sf3 vào SpessaSynth (nếu chưa nạp)
|
|
await soundFontPlayerInstance.loadSoundFont(sfId);
|
|
|
|
// 2. BẮT BUỘC: Gửi lệnh đổi Bank (CC 0) và Program Change sang SpessaSynth
|
|
if (soundFontPlayerInstance.synthInstance) {
|
|
soundFontPlayerInstance.synthInstance.controllerChange(trackChannel, 0, bank);
|
|
soundFontPlayerInstance.synthInstance.programChange(trackChannel, program);
|
|
console.log(`[SonicSF] Switched Channel ${trackChannel} -> Bank: ${bank}, Program: ${program}`);
|
|
}
|
|
}
|
|
|
|
```
|
|
|
|
#### **Bước 2: Kiểm tra chính xác chỉ số Bank & Program của Pipa từ Catalog API**
|
|
|
|
Sử dụng API `GET /api/v1/plugins/soundfonts/catalog` (từ mô-đun `SoundFontInspector` đã xây dựng) để tra cứu vị trí chính xác của "Pipa":
|
|
|
|
```json
|
|
"dsk_asian_dreamz": {
|
|
"soundfont_id": "dsk_asian_dreamz",
|
|
"instruments": [
|
|
{ "bank": 0, "program": 0, "name": "Pipa" },
|
|
{ "bank": 0, "program": 1, "name": "Pipa Tremolo" },
|
|
{ "bank": 0, "program": 6, "name": "Ban Di" }
|
|
]
|
|
}
|
|
|
|
```
|
|
|
|
*Lưu ý:* Nếu kết quả trả về tiếng Pipa nằm ở `program: 1` hoặc `bank: 1`, hãy truyền đúng thông số này vào hàm `programChange`.
|
|
|
|
#### **Bước 3: Kiểm tra danh sách Presets mà SpessaSynth đọc được từ file `.sf3**`
|
|
|
|
Để đảm bảo quá trình convert `.sf3` không làm hỏng dữ liệu Preset Header, bạn hãy log danh sách preset sau khi nạp tệp:
|
|
|
|
```javascript
|
|
// Thêm log kiểm tra sau khi addSoundFont vào SpessaSynth
|
|
try {
|
|
await this.synthInstance.soundFontManager.addSoundFont(buffer);
|
|
|
|
// In danh sách các preset đọc được ra console để kiểm tra
|
|
const loadedSF = this.synthInstance.soundFontManager.soundFonts[0];
|
|
console.log("[SonicSF] Loaded Presets in SF3:", loadedSF.presets);
|
|
} catch (e) {
|
|
console.error("[SonicSF] Error parsing SF3:", e);
|
|
}
|
|
|
|
```
|
|
|
|
* Nếu `loadedSF.presets` rỗng (`[]`), file `.sf3` đã bị hỏng khi nén. Bạn hãy thử nạp lại file `.sf2` gốc chưa nén để đối chiếu. |