# 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 any column starts from all-stopped state
Stop (FCH):  When all columns stop
```

#### 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 <string>
   Values: "menu" | "performance"
   Update frequency: On change only

/looper/tempo <float>
   Range: 50.0 - 200.0 BPM
   Update frequency: On change only

/looper/click/enabled <int>
   Values: 0 (disabled) | 1 (enabled)
   Update frequency: On change only

/looper/click/volume <float>
   Range: 0.0 - 1.0
   Update frequency: On expression pedal change

/looper/master/volume <float>
   Range: 0.0 - 1.0
   Update frequency: On expression pedal change
```

#### 2. Navigation State Messages

```osc
/looper/selected/column <int>
   Range: 1-5
   Update frequency: On change only

/looper/selected/row <int>
   Range: 1-5
   Update frequency: On change only
```

#### 3. Matrix Cell State Messages

```osc
/looper/cell/<column>/<row>/state <string>
   Column: 1-5, Row: 1-5
   Values: "empty" | "loading" | "ready" | "recording" | "playing" | "solo"
   Update frequency: On change only

/looper/cell/<column>/<row>/volume <float>
   Column: 1-5, Row: 1-5
   Range: 0.0 - 1.0
   Update frequency: On change only
```

#### 4. Column State Messages

```osc
/looper/column/<column>/beats <int>
   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/<column>/beat <int>
   Column: 1-5
   Value: Current beat (1 = start of loop, 1+ = beat N)
   Update frequency: On change
```

#### 5. Metronome Messages

```osc
/looper/metronome/position <float>
   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,
  }
}
```