diff --git a/FCB1010_integration.md b/FCB1010_integration.md deleted file mode 100644 index d82ef92..0000000 --- a/FCB1010_integration.md +++ /dev/null @@ -1,261 +0,0 @@ -# FCB1010 MIDI Integration Technical Reference - -## 1. MIDI Controller Configuration - -The FCB1010 will be configured with a consistent set of Control Change (CC) messages for all buttons and pedals: - -| Control | CC Number | Value | Description | -|---------|-----------|-------|-------------| -| **Button 1** | CC 1 | 127/0 | Value 127 when pressed, 0 when released | -| **Button 2** | CC 2 | 127/0 | Value 127 when pressed, 0 when released | -| **Button 3** | CC 3 | 127/0 | Value 127 when pressed, 0 when released | -| **Button 4** | CC 4 | 127/0 | Value 127 when pressed, 0 when released | -| **Button 5** | CC 5 | 127/0 | Value 127 when pressed, 0 when released | -| **Button 6** | CC 6 | 127/0 | Value 127 when pressed, 0 when released | -| **Button 7** | CC 7 | 127/0 | Value 127 when pressed, 0 when released | -| **Button 8** | CC 8 | 127/0 | Value 127 when pressed, 0 when released | -| **Button 9** | CC 9 | 127/0 | Value 127 when pressed, 0 when released | -| **Button 10** | CC 10 | 127/0 | Value 127 when pressed, 0 when released | -| **UP** | CC 11 | 127/0 | Value 127 when pressed, 0 when released | -| **DOWN** | CC 12 | 127/0 | Value 127 when pressed, 0 when released | -| **Expression A** | CC 16 | 0-127 | Continuous controller | -| **Expression B** | CC 17 | 0-127 | Continuous controller | -| **MIDI Channel** | All on Channel 1 | | Single channel for simplicity | - -## 2. System Architecture - -### 2.1 Integration Diagram - -``` -+-------------+ +----------------+ +-------------+ +--------------+ -| JACK Thread | --> | Lock-free MIDI | --> | MIDI Thread | --> | Application | -| (real-time) | | Input Queue | | | | Logic Thread | -+-------------+ +----------------+ +-------------+ +--------------+ - ^ | - | v -+----------------+ +----------------+ -| Lock-free MIDI | | Configuration | -| Output Queue | <----------------------| Commands | -+----------------+ +----------------+ -``` - -### 2.2 Communication Channels - -The system uses Crossbeam channels for thread communication: - -1. **MIDI Input Channel**: Transfers raw MIDI data from JACK thread to MIDI processing thread -2. **MIDI Output Channel**: Transfers SysEx configuration messages from MIDI thread to JACK thread -3. **Event Channel**: Delivers controller events from MIDI thread to application logic -4. **Command Channel**: Sends configuration commands from application to MIDI thread - -### 2.3 MIDI Message Flow - -1. **JACK Callback (Real-time Thread)**: - - Receives MIDI data from FCB1010 - - Copies data to `midi_input_sender` - - Reads from `midi_output_receiver` for SysEx configuration - -2. **MIDI Processing Thread**: - - Reads from `midi_input_receiver` - - Translates MIDI messages to `ControllerEvent` instances - - Sends events via `event_sender` - - Processes configuration commands from `command_receiver` - - Sends SysEx messages via `midi_output_sender` - -3. **Application Logic Thread**: - - Reads from `event_receiver` - - Sends configuration commands via `command_sender` - -## 3. Message Translation - -### 3.1 MIDI to ControllerEvent Translation - -The MIDI processing thread translates FCB1010 MIDI messages to controller-independent events according to the following mapping: - -| MIDI Message | Condition | Translated Event | -|--------------|-----------|------------------| -| CC 1, Value 127 | Channel 1 | ButtonPress(1) | -| CC 2, Value 127 | Channel 1 | ButtonPress(2) | -| CC 3, Value 127 | Channel 1 | ButtonPress(3) | -| CC 4, Value 127 | Channel 1 | ButtonPress(4) | -| CC 5, Value 127 | Channel 1 | ButtonPress(5) | -| CC 6, Value 127 | Channel 1 | ButtonPress(6) | -| CC 7, Value 127 | Channel 1 | ButtonPress(7) | -| CC 8, Value 127 | Channel 1 | ButtonPress(8) | -| CC 9, Value 127 | Channel 1 | ButtonPress(9) | -| CC 10, Value 127 | Channel 1 | ButtonPress(10) | -| CC 11, Value 127 | Channel 1 | Navigation(NavigationButton::Up) | -| CC 12, Value 127 | Channel 1 | Navigation(NavigationButton::Down) | -| CC 16, Any Value | Channel 1 | ExpressionPedal(ExpressionPedal::A, value) | -| CC 17, Any Value | Channel 1 | ExpressionPedal(ExpressionPedal::B, value) | - -Notes on translation: -- Only button press events (value 127) are translated for footswitches and navigation buttons -- Button release events (value 0) are ignored -- All expression pedal value changes (0-127) are translated -- Messages on MIDI channels other than Channel 1 are ignored -- Other MIDI message types (Program Change, Note On/Off, etc.) are ignored - -### 3.2 Event Types - -```rust -pub enum ControllerEvent { - /// Footswitch button press (1-10) - ButtonPress(u8), - - /// Navigation button press - Navigation(NavigationButton), - - /// Expression pedal value change - ExpressionPedal(ExpressionPedal, u8), // value 0-127 -} - -pub enum NavigationButton { - Up, - Down, -} - -pub enum ExpressionPedal { - A, - B, -} - -pub enum FcbCommand { - /// Configure the FCB1010 for our application - Initialize, -} -``` - -## 4. FCB1010 Initialization - -The FCB1010 is configured at startup using SysEx messages: - -```rust -fn initialize_fcb1010(command_sender: &crossbeam::channel::Sender) { - // Send initialization command to MIDI processing thread - command_sender.send(FcbCommand::Initialize).unwrap_or_else(|e| { - error!("Failed to send FCB1010 initialization command: {}", e); - }); -} -``` - -The MIDI processing thread handles the initialization by sending SysEx messages: - -```rust -fn handle_fcb_command( - command: FcbCommand, - midi_output_sender: &crossbeam::channel::Sender> -) { - match command { - FcbCommand::Initialize => { - info!("Initializing FCB1010"); - - // Get SysEx messages from FCB1010 SysEx generator - let sysex_messages = generate_fcb1010_configuration(); - - // Send each SysEx message with a delay to avoid buffer overruns - for message in sysex_messages { - midi_output_sender.send(message).unwrap_or_else(|e| { - error!("Failed to send SysEx message: {}", e); - }); - - // Allow time for the message to be processed - std::thread::sleep(std::time::Duration::from_millis(50)); - } - - info!("FCB1010 initialization complete"); - } - } -} -``` - -## 5. JACK Integration - -### 5.1 JACK Process Callback - -The JACK process callback runs in a real-time thread with strict timing constraints. To maintain real-time safety: - -1. **No Memory Allocations**: The callback must never allocate memory (no `Vec`, `String`, `Box`, etc.) -2. **No Blocking Operations**: The callback must not block (no file I/O, mutex locks, etc.) -3. **Minimal Processing**: Only the bare minimum work should be done in this thread - -For FCB1010 integration, the JACK callback should: - -1. **Read MIDI Input**: Copy raw MIDI data bytes to a pre-allocated lock-free ring buffer -2. **Write MIDI Output**: Read from a lock-free buffer and send to the MIDI output port - -#### Avoiding Allocations - -To avoid allocations while handling MIDI data: - -1. **Pre-allocated Buffers**: Use fixed-size arrays or pre-allocated ring buffers -2. **Zero-Copy Access**: Access MIDI data directly without copying when possible -3. **Message Passing**: Use lock-free data structures that don't require allocation -4. **Byte References**: Pass references to MIDI data rather than copying the data - -### 5.2 MIDI Processing Thread - -The MIDI processing thread runs continuously outside of JACK's real-time constraints, allowing for: - -1. Receiving MIDI data from the lock-free input buffer -2. Translating MIDI messages to controller events -3. Sending events to the application logic -4. Processing configuration commands -5. Sending SysEx messages to the FCB1010 - -This thread can safely perform memory allocations and more complex processing since it's not subject to real-time constraints. - -## 6. Testing Approach - -### 6.1 Mock FCB Implementation - -For testing without hardware, a mock FCB implementation will use the same MIDI message format as the physical FCB1010: - -1. **JACK-Based MIDI Device**: The mock will appear as a standard MIDI device in the JACK system -2. **Message Format Consistency**: It will generate identical CC messages following the table in section 1 -3. **Bidirectional Communication**: It will receive and verify SysEx configuration messages -4. **Programmatic Control**: It can be triggered by test code to simulate button presses and pedal movements - -This approach allows testing the complete integration without requiring the physical FCB1010 hardware, while ensuring message format compatibility. - -The mock FCB will translate controller-independent events back to MIDI messages, effectively doing the reverse of the translation described in section 3.1. This symmetrical translation enables comprehensive testing of the entire message pipeline. - -### 6.2 Integration Testing - -Integration tests will verify the complete flow from MIDI messages to application events: - -1. Create a mock FCB instance -2. Start the MIDI processing thread -3. Generate MIDI messages from the mock FCB -4. Verify correct `ControllerEvent` instances are produced -5. Test error conditions and recovery - -### 6.3 SysEx Testing - -SysEx testing will verify the FCB1010 configuration messages: - -1. Create a mock FCB that captures SysEx messages -2. Send the initialization command -3. Verify the SysEx messages match the expected format -4. Test with corrupted or incomplete SysEx responses - -## 7. Logging - -The integration will use the Rust `log` crate for detailed logging: - -```rust -// MIDI message logging -debug!("Received MIDI message: {:?}", data); - -// Controller event logging -info!("Translated to controller event: {:?}", event); - -// SysEx logging -info!("Sending FCB1010 configuration SysEx"); -debug!("SysEx message: {:?}", sysex_message); - -// Error logging -error!("Failed to process MIDI message: {}", error); -``` - -Initial logs will go to standard output, with a future extension to log to a UI window. \ No newline at end of file diff --git a/interface_spec.md b/interface_spec.md new file mode 100644 index 0000000..a010024 --- /dev/null +++ b/interface_spec.md @@ -0,0 +1,240 @@ +# 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) +SysEx Test: CC #127 (Trigger FCB1010 reconfiguration) +``` + +#### 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 + 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" | "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//bars + Column: 1-5 + Value: Number of bars in column (set by first recording, 0 = not set) + Update frequency: On first recording in column + +/looper/column//bar + Column: 1-5 + Value: Current bar (1 = start of loop, 1+ = bar N) + Update frequency: On change +``` + +#### 5. Metronome Messages + +```osc +/looper/metronome/position + Range: 0.0 - 1.0 (position within current bar) + Update frequency: 60 FPS +``` + +### 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-bar messages are bundled: + +## Session Persistence + +**Single Source of Truth** + +- **Timing authority**: `samples_per_bar` in state.json +- **Track existence**: WAV file presence in filesystem +- **Tempo**: Derived from `(sample_rate × 60) ÷ (samples_per_bar × beats_per_bar)` +- **Column bars**: Derived from `wav_length_samples ÷ samples_per_bar` +- **Delete files**: + - If it's not a multiple of `samples_per_bar` + - 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", + "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_bar": 105840, + "time_signature": [4, 4] + } +} +``` \ No newline at end of file diff --git a/project_spec.md b/project_spec.md index e96fac1..3e2edb2 100644 --- a/project_spec.md +++ b/project_spec.md @@ -1,4 +1,4 @@ -# FCB1010 Looper Pedal - Complete Project Specification +# FCB1010 Looper Pedal - Project Specification ## System Overview @@ -28,6 +28,10 @@ The system provides a 5x5 matrix of loop tracks organized in columns and rows, w ## Interface Design - **Metronome**: Moving indicator that pulses when in center - **Progress Bars**: Show current position in each column with bar divisions + + First recording behavior: + - Progress bar fills during bar 1, then stays full + - Additional bars appear as sliding segments (right-to-left) - **Active Row**: Increased border with - **Selected Cell**: Switched border and background color - **Track States**: @@ -43,10 +47,10 @@ The system provides a 5x5 matrix of loop tracks organized in columns and rows, w ### Menu State - **Button 1**: Tap Tempo - **Button 2**: Enable/Disable Click Track -- **Button 3**: Clear selected Column -- **Button 4**: Quantization Settings (Off, 1/4, 1/8, 1/16 note) -- **Button 5**: Save/Load Project (use buttons 6-10 for slot selection) -- **Buttons 6-10**: Column selection (for clear/save/load operations) +- **Button 3**: Not used +- **Button 4**: Not used +- **Button 5**: Clear current column +- **Buttons 6-10**: Column selection (1-5) - **UP/DOWN**: Switch to Performance Mode - **Expression Pedal A**: Click track volume - **Expression Pedal B**: Master volume @@ -93,7 +97,6 @@ The system provides a 5x5 matrix of loop tracks organized in columns and rows, w ### State Management -- **Save in Numbered Slots**: 5 project slots accessible via buttons 6-10 in save/load mode -- **Startup Behavior**: Load last used project and configure FCB1010 +- **Startup Behavior**: Load last audio tracks and configure FCB1010 - **Column Consistency**: Recordings automatically match column bar count - **State Validation**: Prevent invalid operations (e.g., switching columns while recording) \ No newline at end of file