Files
SonicForgeStudio/plans/PLAN_VSTI.md

7.0 KiB
Raw Permalink Blame History

Kế hoạch cài đặt SoundFont / VSTi (32_SF_VSTi.md)

Dựa trên hiện trạng codebase: app/core/render_engine.py, app/core/vst_engine.py, tests/test_render_engine.py, app/api/v1/audio.py, requirements.txt (có pedalboard, numpy, soundfile).


1. Server Backend (Python / FastAPI)

1.1 PluginManager (app/core/vst_engine.py — SỬA)

Hiện trạng: vst_engine.pyrender_midi_events_to_audio() synth sine cơ bản. Cần class quản lý VST3.

Công việc:

  • Thêm class PluginManager:
    • __init__(vst_dir="/opt/daw_engine/vst3", sf_dir="/opt/daw_engine/soundfonts")
    • _scan_plugins(): quét .vst3/.so trong vst_dir
    • _scan_soundfonts(): quét .sf2/.sf3 trong sf_dir
    • load_vst(name, preset_data=None): load VST3 bằng pedalboard.VST3Plugin
    • load_soundfont(path): load SF2 bằng pyfluidsynth (in-memory buffer)
    • list_available(): trả về { vst_instruments: [...], soundfonts: [...] }
  • Import pedalboard + pyfluidsynth với try/except
  • requirements.txt: thêm mido>=1.3.0, pyfluidsynth>=1.3.0

File: app/core/vst_engine.py


1.2 Render Engine (app/core/render_engine.py — SỬA)

Hiện trạng: PythonRenderEngine render audio clips + MIDI events. Cần VST3 + SoundFont path.

Optimization 1 — In-memory SoundFont render (thay subprocess CLI):

  • Dùng pyfluidsynth C-bindings thay vì lệnh fluidsynth CLI qua subprocess
  • Luồng render:
    1. fl = pyfluidsynth.FluidSynth(sample_rate=sr, gain=0.5)
    2. fl.sfload(path_to_sf2)fl.program_select(track_id, font_id, bank, preset)
    3. Render từng block MIDI event → numpy array bằng fl.render_midi(midi_events, sr)
    4. Trả về in-memory buffer, không ghi file tạm
  • Loại bỏ hoàn toàn subprocess CLI (tránh I/O disk, race condition, temp file collision)

Optimization 2 — Sample Offset chính xác cho Pedalboard VST3:

  • Công thức quy đổi beat → sample offset:
    SampleOffset = BeatPosition × (60 / BPM) × SampleRate
    
  • Khi gọi vst_plugin(array, sample_rate=sr, midi_messages=midi_list):
    • Mỗi pedalboard.MidiMessage phải kèm sample_offset chính xác
    • Chuyển từng MIDI event: midi_events[].start_beatsample_offset
    • Nếu không quy đổi, VSTi dồn toàn bộ nốt vào sample đầu (0\text{ms}) → sai nhịp
  • Thêm hàm _midi_events_to_messages(midi_events, bpm, sr) → list[MidiMessage]

Công việc:

  • Thêm render_midi_track_with_vst(midi_events, vst_plugin, sr, bpm):
    • Gọi _midi_events_to_messages() → quy đổi sang sample offset
    • Khởi tạo VST3 từ PluginManager.load_vst(), process buffer
    • Fallback về basic synth nếu VST không available
  • Thêm render_soundfont_track(midi_events, sf_path, sr, bpm):
    • Dùng pyfluidsynth.FluidSynth in-memory
    • fl.render_midi() → numpy array
  • Sửa render_session_container() nhận track["instrument"] (VST/SF id) mỗi track

File: app/core/render_engine.py


1.3 API Endpoints (app/api/v1/plugins.py — MỚI + main.py — SỬA)

  • GET /api/v1/plugins/availablePluginManager().list_available()
  • GET /api/v1/plugins/default-soundfonts → scan app/static/soundfonts/
  • POST /api/v1/projects/render → nhận ProjectSchema → render → return URL WAV
  • Mount router trong main.py:
    from app.api.v1.plugins import router
    app.include_router(router, prefix="/api/v1/plugins", tags=["plugins"])
    

Optimization 3 — Validation Magic Bytes cho Upload SoundFont:

  • Endpoint upload .sf2 không chỉ kiểm tra đuôi file
  • Thêm FastAPI Dependency kiểm tra Header/Magic Bytes:
    RIFF header (4 bytes: 0x52 0x49 0x46 0x46) + sfbk (4 bytes: 0x73 0x66 0x62 0x6B)
    
  • Logic validate:
    def validate_sf2_header(data: bytes):
        if len(data) < 12: return False
        if data[0:4] != b'RIFF': return False
        if data[8:12] != b'sfbk': return False
        return True
    
  • Từ chối upload nếu magic bytes không khớp → chặn file độc hại ngay từ API layer

File mới: app/api/v1/plugins.py


1.4 Dockerfile / Dependencies

  • requirements.txt: thêm mido>=1.3.0, pyfluidsynth>=1.3.0
  • Dockerfile: cài fluidsynth (shared lib cho pyfluidsynth), libfluidsynth-dev, build-essential
  • Tạo /opt/daw_engine/vst3//opt/daw_engine/soundfonts/
  • Tạo app/static/soundfonts/ cho default SF

2. Client Frontend (JavaScript)

2.1 API Client (app/static/js/services/api.js — SỬA)

Thêm methods:

  • listPlugins()GET /v1/plugins/available
  • listDefaultSoundfonts()GET /v1/plugins/default-soundfonts
  • renderProject(data)POST /v1/projects/render
  • uploadSoundFont(file)POST /v1/audio/upload-soundfont (FormData)

2.2 SoundFont Player (app/static/js/services/soundfontPlayer.js — MỚI)

  • loadSoundFont(url) → fetch .sf2 → ArrayBuffer
  • initFluidSynth() → khởi tạo Was m engine (fallback Web Audio API basic synth)
  • playNote(note, velocity, duration) → play MIDI note
  • Lưu user SF vào IndexedDB qua window.SonicStorage
  • Global: window.SonicSF

2.3 Synth / Instrument Panel UI (index.html)

  • Instrument Selector: dropdown chọn VST/SoundFont từ listPlugins()
  • SoundFont Upload: drag & drop .sf2 → IndexedDB
  • Preset Browser: load .vital/.fxp/.syx cho VST đang chọn
  • Render to WAV button: gọi renderProject() → progress → play

2.4 MIDI Track Support (index.html)

  • Track model thêm: type: 'audio' | 'midi', midiEvents: [], instrumentId
  • WaveformLane vẽ MIDI notes (rects) thay waveform
  • Double-click → piano roll editor (tham khảo md/Piano_roll_UX.md)

3. Tests

Test File Mới/Sửa
PluginManager init + scan + load tests/test_vst_engine.py MỚI
Render MIDI track with VST (sample offset chính xác) tests/test_render_engine.py SỬA
Render MIDI track with SoundFont (pyfluidsynth in-memory) tests/test_render_engine.py SỬA
Render project mixed audio + MIDI tests/test_render_engine.py SỬA
API plugins/available/render tests/test_plugin_api.py MỚI
SF2 Magic Bytes validation (RIFF+sfbk) tests/test_plugin_api.py MỚI

4. Thứ tự thực hiện

Bước Mô tả Phụ thuộc
B1 Sửa vst_engine.py: PluginManager class
B2 Thêm mido, pyfluidsynth vào requirements.txt
B3 Tạo app/api/v1/plugins.py: 3 endpoints + SF2 magic bytes validate B1
B4 Mount router trong main.py B3
B5 Sửa render_engine.py: VST3 (sample offset) + SF (pyfluidsynth in-memory) B1
B6 Tạo soundfontPlayer.js
B7 Sửa api.js: plugin/SF methods
B8 UI Synth Panel + MIDI track (index.html) B5-B7
B9 Tests B1-B5
B10 Dockerfile: fluidsynth lib + VST dirs