# FCB1010 Looper Pedal - Interface Specification ## System Architecture Overview ```mermaid graph TD FCB1010[FCB1010 MIDI Controller] UMC404HD[UMC404HD Audio Interface] JACK_MIDI[JACK MIDI] JACK_AUDIO[JACK Audio] AudioEngine[Audio Engine Process] Display[Display Process] FCB1010 <-->|MIDI DIN| UMC404HD UMC404HD <-->|MIDI USB| JACK_MIDI UMC404HD <-->|Audio USB| JACK_AUDIO JACK_MIDI -->|CC messages| AudioEngine AudioEngine -->|MIDI Clock/Click| JACK_MIDI JACK_AUDIO <-->|Audio Data| AudioEngine AudioEngine -->|OSC Unix Socket| Display subgraph "Hardware" FCB1010 UMC404HD end subgraph "Software" JACK_MIDI JACK_AUDIO AudioEngine Display end ``` ## MIDI Interface ### Input: FCB1010 → Audio Engine #### Button Mappings (State-Independent) ``` Button 1: CC #20 (Record/Arm / Tap Tempo) Button 2: CC #21 (Play/Mute Track / Click Toggle) Button 3: CC #22 (Solo Track) Button 4: CC #23 (Overdub) Button 5: CC #24 (Clear Track / Clear Column) Button 6: CC #25 (Column 1 Select) Button 7: CC #26 (Column 2 Select) Button 8: CC #27 (Column 3 Select) Button 9: CC #28 (Column 4 Select) Button 10: CC #29 (Column 5 Select) UP: CC #30 (Row Up / Mode Switch) DOWN: CC #31 (Row Down / Mode Switch) ``` #### Expression Pedals ``` Expression A: CC #1 (Track Volume / Click Volume) Expression B: CC #7 (Master Volume) ``` **Note**: FCB1010 sends identical CC messages regardless of current system state. The Audio Engine interprets these based on its internal mode (Menu vs Performance). ### Output: Audio Engine → External Devices #### MIDI Clock (Continuous) ``` Clock (F8H): 24 PPQN, sent continuously while application running ``` #### Transport Control ``` Start (FAH): When application starts or BPM changes Stop (FCH): When application stops or BPM changes ``` #### Song Position Pointer ``` SPP (F2H): Reset to 0 on Start message Increments during playback Stops incrementing on Stop message 1 MIDI beat = 6 MIDI clocks = 1/4 quarter note ``` ## OSC Interface (Audio Engine → Display) ### Transport: Unix Domain Socket - **Protocol**: OSC 1.0 over SLIP-encoded stream - **Direction**: Unidirectional (Audio Engine → Display only) ### Message Categories #### 1. System State Messages ```osc /looper/mode Values: "menu" | "performance" Update frequency: On change only /looper/tempo Range: 50.0 - 200.0 BPM Update frequency: On change only /looper/click/enabled Values: 0 (disabled) | 1 (enabled) Update frequency: On change only /looper/click/volume Range: 0.0 - 1.0 Update frequency: On expression pedal change /looper/master/volume Range: 0.0 - 1.0 Update frequency: On expression pedal change ``` #### 2. Navigation State Messages ```osc /looper/selected/column Range: 1-5 Update frequency: On change only /looper/selected/row Range: 1-5 Update frequency: On change only ``` #### 3. Matrix Cell State Messages ```osc /looper/cell///state Column: 1-5, Row: 1-5 Values: "empty" | "loading" | "ready" | "recording" | "playing" | "solo" Update frequency: On change only /looper/cell///volume Column: 1-5, Row: 1-5 Range: 0.0 - 1.0 Update frequency: On change only ``` #### 4. Column State Messages ```osc /looper/column//beats Column: 1-5 Value: Number of beats in column (set by first recording, 0 = not set) Update frequency: On first recording in column /looper/column//beat Column: 1-5 Value: Current beat (1 = start of loop, 1+ = beat N) Update frequency: On change ``` #### 5. Metronome Messages ```osc /looper/metronome/position Range: 0.0 - 1.0 (position within current beat) Update frequency: same as PPQN midi signal ``` ### OSC Implementation Details #### Initial State Synchronization OSC is stateless - there's no "subscription" mechanism. When the display process connects: 1. **Display connects** to Unix socket 2. **Audio engine detects** new connection 3. **Complete state dump** sent immediately: 4. **Regular updates** begin (position updates, state changes) #### OSC Message Bundling For efficiency, column-beat messages are bundled: ## Session Persistence **Single Source of Truth** - **Timing authority**: `samples_per_beat` in state.json - **Track existence**: WAV file presence in filesystem - **Tempo**: Derived from `(sample_rate × 60) ÷ samples_per_beat` - **Column beats**: Derived from `wav_length_samples ÷ samples_per_beat` - **Delete files**: - If it's not a multiple of `samples_per_beat` - If it doesn't match the sample rate - When a track is cleared ## Auto-Save Triggers - Track data changes (record/clear) - Tempo/timing changes - Volume adjustments (when constant for 5 seconds) - Application shutdown ## Directory Structure ``` ~/.fcb_looper/ ├── state.json # System state and timing authority ├── col_1_row_1.wav # Audio tracks ├── col_1_row_2.wav └── ... ``` ## State File Format ```json { "version": "1.0", "connections": { "midi_in": [ "sendmidi:midi_out" ], "audio_out": [ ], "audio_in": [ ] }, "ui_state": { "selected_column": 3, "selected_row": 2 }, "user_preferences": { "click_enabled": true, "click_volume": 0.5, "master_volume": 0.8 }, "track_volumes": { "col_1_row_1": 0.75, "col_2_row_3": 0.9 }, "timing": { "sample_rate": 44100, "samples_per_beat": 105840, } } ```