- Fix CDN URL: spessasynth_lib@4.3.1/dist/index.js (was @latest with wrong path)
- Use WorkletSynthesizer instead of Synthesizer (correct class name)
- Add audioWorklet.addModule() for processor CDN URL
- Await synth.isReady before use
- Use soundBankManager.addSoundBank() (correct API)
- Fallback to WorkerSynthesizer if AudioWorklet fails
- getAudioContext() calls SonicSF.init on every call (not just first)
- soundfontPlayer.init retries after 1s if SpessaSynth module not loaded yet
- Auto-loads sgm_v2.01 SF3 on successful init
- Fixes race condition: module script loads after regular scripts
- Remove small Synth button between ARM and Monitor (duplicate)
- Remove empty try/catch that swallowed all MIDI handler errors
- Ensure MIDI keyboard passes synth_engine context to playNote
- Direct fallback to armed main track when no PIANO_ROLL sub-tab armed
When MIDI note-on received and no PIANO_ROLL sub-tab is armed,
fallback to armed track in main session. Read its synth_engine and
instrumentProgram to route audio through correct instrument.
Route to track gainNode if available.
Add sfPresetSearchQuery state + search input in instrument selector
modal. Filters sfPresets by name (case-insensitive) when user types.
Clear search on SoundFont switch / modal close.
- toggleTrackDrum: sets is_percussion + soundfont_bank=128
- 🥁 button in track control panel (between Solo and Record)
- Shows 'D' badge when active, routes to MIDI channel 9
- Instrument preset list shows 🥁 prefix for bank=128 presets
Export installed pyfluidsynth is actually the low-level ctypes
wrapper (fluidsynth), not the high-level FluidSynth class. The
broken call fluidsynth.FluidSynth() caused silent fallback to
oscillator synth for ALL SoundFont renders. Replace with:
new_fluid_settings / new_fluid_synth / fluid_synth_sfload /
fluid_synth_program_select / fluid_synth_write_s16_stereo.
Also fix synth time advancement: call write_s16_stereo for
silence gaps instead of just advancing a cursor.
- _find_sf2_path now case-insensitive to match catalog IDs
- serializeTracksList includes synth_engine, soundfont_id, instrument_source
- deserializeTracksList restores synth_engine and soundfont_id
- Add seekPlaybackTo(time) that stops, updates refs, reschedules
MIDI, and restarts playback for both sub-tab and main timeline
- 'Quay lại đầu' and 'Đầu vùng chọn' buttons use seekPlaybackTo
- Sub-tab selection loop only active when st.isLooping is true
- End-of-playback loop uses st.isLooping, not global isLoopingSelection
- SonicSF.stopAll() sets gain 0 and stops oscillators immediately
- Piano roll ruler click during playback now seeks and continues playing
- ADSR envelope uses linearRampToValueAtTime for crack-free release
- stopAll ramps gain to 0 in 20ms before stopping oscillators