# CLIENT-SIDE EXECUTION FLOW (SF3 + SPESSASYNTH + INDEXEDDB) This document describes the step-by-step processing chain that takes place inside the Client Browser, from launching the Web DAW application, downloading and buffering `.sf3` instrument files, and setting up MIDI channels, to outputting real-time audio. --- ## 1. SEQUENCE DIAGRAM ```text [ USER / UI ] [ APP / CLIENT ] [ INDEXEDDB ] [ SERVER API ] [ SPESSASYNTH ENGINE ] | | | | | 1. Open Web Page ---------> | Initial AudioCtx | | | | | Init SpessaSynth -----------------------------------------------> | Connect Destination | | Fetch Catalog --------------------------->| Get /catalog | | | | | | 2. Select Instrument -----> | Read (sf_id, bank, prog) | | (e.g., Pipa) | Query SF3 Buffer --->| Check Key (sf_id) | | | | | -- (Miss) -------->| Fetch /download/sf_id | | | | | Return .sf3 (~4MB) | | | <--------------------| Save ArrayBuffer --| | | | Load SF3 Memory ------------------------------------------------> | addSoundFont(buffer) | | | | | 3. Channel Router --------> | Switch Bank/Program -------------------------------------------> | controllerChange(ch, 0, bank) | | | | | programChange(ch, prog) | | | | | 4. Trigger MIDI Key ------> | Raw MIDI Event | | | (or Timeline Play) | (noteOn: pitch, vel) -------------------------------------------> | noteOn(ch, pitch, vel) | | | | | AudioWorklet Synthesis | | <------------------------------------------------------------------ | Audio Out (User Speakers) ``` --- ## 2. DETAILED PROCESSING PHASES ### PHASE 1: BOOTSTRAPPING & ENGINE INIT * **Web Audio Context Initialization:** Upon the user's first interaction with the web page (Mouse Click/Keypress), the application initializes the `AudioContext`. * **SpessaSynth Synthesizer Initialization:** The `soundfontPlayer.js` module instantiates `SpessaSynthClass` and connects its output directly to `audioCtx.destination`: ```javascript this.synthInstance = new window.SpessaSynthClass(this.audioCtx.destination); ``` * **Instrument Catalog Load (Catalog Context):** The Frontend dispatches a `GET /api/v1/plugins/soundfonts/catalog` request to load the `condensed_catalog`, which contains lookup tables for `sf_id`, `bank`, and `program`. --- ### PHASE 2: `.SF3` ASSET LOADING & CACHING Triggered when a user selects an instrument via the Synth UI button (or when the AI Copilot spawns a new Track with a designated instrument, e.g., `dsk_asian_dreamz`): * **Query Browser Cache (IndexedDB):** The Client calls `sfStorage.getBuffer(sfId)` to search for the `.sf3` file's `ArrayBuffer` inside the `DAW_SoundFont_Cache` database. * **Handling Cache Hit vs Cache Miss:** * **Cache Hit ($0\text{ms}$):** Retrieves the `ArrayBuffer` directly from the browser's RAM/Storage. * **Cache Miss:** 1. Sends a `GET /api/v1/plugins/soundfonts/download/{sf_id}` request to the Server. 2. Downloads the compressed, optimized `.sf3` asset (ultra-lightweight size $\sim 3.5 - 5.5\text{ MB}$). 3. Invokes `sfStorage.saveBuffer(sfId, arrayBuffer)` to store it inside IndexedDB for subsequent visits. * **Load Data into SpessaSynth Wasm/JS Memory:** Passes the `ArrayBuffer` to SpessaSynth Engine's `SoundFontManager`: ```javascript await this.synthInstance.soundFontManager.addSoundFont(buffer); ``` --- ### PHASE 3: BANK/PROGRAM ROUTING & MIDI CHANNEL SETUP This is the most critical phase to resolve `No preset found for 0:0:0` errors. * **MIDI Channel Assignment:** * **Melodic Instruments (Piano, Pipa, Strings, Brass, etc.):** Allocated to Channels 0 through 8. * **Percussion / Drum Kits (Bank 128):** Mandatory allocation to Channel 9 (GM Standard Channel 10). * **Dispatch Bank Select & Program Change to SpessaSynth Engine:** Prior to scheduling any note events, the Client triggers two simultaneous control events: ```javascript // 1. Select Bank (Control Change 0) this.synthInstance.controllerChange(channel, 0, bank); // 2. Select Program (Program Change) this.synthInstance.programChange(channel, program); ``` *Example for Pipa (`dsk_asian_dreamz`):* Calls `controllerChange(0, 0, 0)` and `programChange(0, 0)`. SpessaSynth switches Channel 0's state to the Pipa instrument patch. --- ### PHASE 4: REALTIME SYNTHESIS & AUDIO OUTPUT Triggered when receiving note-control signals (from a Hardware MIDI Keyboard or Timeline Transport Playback): * **Scenario A: User plays a Hardware MIDI Keyboard (e.g., Nektar SE49)** 1. The browser receives a Raw MIDI Event: Web MIDI API captures message `[144, 65, 78]` (`NoteOn`, `Pitch 65`, `Velocity 78`). 2. **Latency Compensation:** Calculates real-time offsets and issues `NoteOn` to SpessaSynth: ```javascript const midiPitch = pitch; const midiVelocity = Math.floor(velocity * 127); this.synthInstance.noteOn(channel, midiPitch, midiVelocity); ``` 3. **Key Release:** Triggers a `NoteOff` event: ```javascript this.synthInstance.noteOff(channel, midiPitch); ``` * **Scenario B: User triggers Play on Timeline / Piano Roll** 1. **Transport Controller & Scheduler (`PrecisionAudioScheduler`):** Scans for MIDI notes located within the moving Playhead window. 2. **Note Scheduling:** * Converts beat positions to precise audio timing based on BPM tempo (`exactAudioTime`). * Dispatches `noteOn(channel, pitch, velocity)` at the exact timestamp $T_{\text{start}}$. * Dispatches `noteOff(channel, pitch)` at timestamp $T_{\text{start}} + T_{\text{duration}}$. * **Audio Worklet Audio Rendering:** SpessaSynth Engine reads Ogg/WAV sample data inside the `.sf3` asset, applies Envelopes (ADSR), Modulators, and Gain Control parameters on the designated Channel, and pushes PCM audio data directly to user speakers with $0\text{ms}$ latency. --- ## 3. 100% RELIABILITY VERIFICATION CHECKLIST * [ ] `.sf3` files loaded into the browser open without triggering `Corrupted File` errors. * [ ] The `sfStorage.getBuffer` function successfully stores and retrieves `ArrayBuffer` data from IndexedDB. * [ ] Both `controllerChange(channel, 0, bank)` and `programChange(channel, program)` are invoked immediately upon changing instruments on the UI. * [ ] Percussion/Drum instruments are persistently allocated to Channel 9. * [ ] Console logs confirm: `[SonicSF] Switched Channel X -> Bank: B, Program: P`.