fcb_looper/FCB1010_integration.md
2025-05-25 23:53:46 +02:00

9.9 KiB

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

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:

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:

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:

// 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.