More concrete project plan

This commit is contained in:
Geens 2025-06-01 14:17:33 +02:00
parent 5ab36a7d02
commit 1bc1224441
3 changed files with 250 additions and 268 deletions

View File

@ -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<FcbCommand>) {
// 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<Vec<u8>>
) {
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.

240
interface_spec.md Normal file
View File

@ -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 <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" | "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>/bars <int>
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/<column>/bar <int>
Column: 1-5
Value: Current bar (1 = start of loop, 1+ = bar N)
Update frequency: On change
```
#### 5. Metronome Messages
```osc
/looper/metronome/position <float>
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]
}
}
```

View File

@ -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)