121 lines
5.7 KiB
Markdown
121 lines
5.7 KiB
Markdown
# GUI Architecture
|
|
|
|
This document defines the high-level architectural decisions for the real-time display system.
|
|
The display must provide smooth visual feedback for metronome position, column progress, and track states
|
|
while maintaining responsive updates from OSC messages without blocking the rendering thread.
|
|
|
|
## Overview
|
|
|
|
The GUI architecture uses two threads to maintain responsive visual updates.
|
|
The OSC receiver thread handles incoming state messages from the audio engine and updates shared state.
|
|
The GUI thread runs the egui rendering loop and interpolates time-based animations independently.
|
|
|
|
These subsystems communicate through Tokio watch channels for efficient state synchronization.
|
|
The audio engine acts as an OSC server, with the display connecting as a client and automatically reconnecting on failures.
|
|
|
|
```mermaid
|
|
graph LR
|
|
AudioEngine[Audio Engine<br/>OSC Server<br/>Unix Socket]
|
|
OSCThread[OSC Receiver Thread<br/>tokio + rosc<br/>State Updates]
|
|
GUIThread[GUI Thread<br/>egui Rendering<br/>Animation Interpolation]
|
|
|
|
AudioEngine -->|OSC Messages| OSCThread
|
|
OSCThread -->|Watch Channel| GUIThread
|
|
|
|
subgraph "Display Process"
|
|
OSCThread
|
|
GUIThread
|
|
end
|
|
```
|
|
|
|
The system receives state updates through standardized OSC messages and renders them using a hierarchical state structure.
|
|
Time-critical animations like metronome movement and progress bars use local interpolation to maintain smooth 60fps updates
|
|
independent of the OSC message frequency.
|
|
|
|
## Technology Stack
|
|
|
|
The display uses egui as the immediate mode GUI framework,
|
|
providing excellent performance for real-time updates and straightforward integration with Rust's async ecosystem.
|
|
|
|
**Core Libraries:**
|
|
- **egui**: Immediate mode GUI framework for rendering
|
|
- **tokio**: Async runtime for OSC message handling
|
|
- **rosc**: OSC message parsing and Unix socket communication
|
|
- **tokio::sync::watch**: Lock-free state sharing between threads
|
|
|
|
## Threading Architecture
|
|
|
|
### OSC Receiver Thread
|
|
The OSC receiver thread manages the Unix socket connection to the audio engine and processes incoming state messages.
|
|
This thread runs a tokio async loop that handles connection establishment, message parsing, and state updates.
|
|
|
|
### GUI Thread
|
|
The GUI thread owns the egui context and handles all rendering operations.
|
|
This thread polls the watch channel receiver for state changes and triggers repaints when updates arrive.
|
|
Time interpolation for smooth animations happens during the render loop using stored timestamps and elapsed time calculations.
|
|
|
|
## State Management
|
|
|
|
### Hierarchical State Structure
|
|
The display state mirrors the audio engine's organization to maintain consistency and simplify message parsing.
|
|
|
|
```
|
|
DisplayState
|
|
├── global
|
|
│ ├── mode: OperationMode (Menu | Performance)
|
|
│ ├── tempo: f32 (BPM)
|
|
│ ├── selected_cell: (usize, usize)
|
|
│ ├── click_enabled: bool
|
|
│ ├── click_volume: f32
|
|
│ └── master_volume: f32
|
|
├── columns: [ColumnState; 5]
|
|
│ ├── beats: usize (Total beats, 0 = not set)
|
|
│ ├── current_beat: usize (1-based position)
|
|
│ └── tracks: [TrackState; 5]
|
|
│ ├── state: TrackDisplayState (Empty | Ready | Recording | Playing | Solo)
|
|
│ └── volume: f32
|
|
└── metronome
|
|
├── position: f32 (0.0-1.0 within current beat)
|
|
└── timestamp: Instant (When received)
|
|
```
|
|
|
|
### State Updates
|
|
The OSC receiver thread modifies the state directly based on incoming messages and sends the complete updated state through the watch channel.
|
|
This approach ensures atomic updates and eliminates complex synchronization logic.
|
|
|
|
## OSC Message Processing
|
|
|
|
### Message Parsing Flow
|
|
Incoming OSC messages map directly to state field updates using a straightforward parsing pipeline.
|
|
Initial connection triggers a complete state dump from the audio engine using the same message format as regular updates.
|
|
|
|
### Time-Sensitive Messages
|
|
Metronome position messages include timestamp capture to enable smooth interpolation in the GUI thread.
|
|
This allows the display to show fluid metronome movement despite receiving position updates at MIDI PPQN rate rather than display refresh rate.
|
|
|
|
## Time Interpolation Strategy
|
|
|
|
### Metronome Animation
|
|
The metronome requires smooth movement between received position updates to maintain visual continuity.
|
|
Time interpolation happens during the render loop by calculating elapsed time since the last position update and advancing the visual position accordingly.
|
|
This maintains frame-rate independence and ensures smooth animations regardless of system load.
|
|
|
|
### Column Progress Animation
|
|
Column progress bars use similar interpolation to show smooth advancement between beat boundaries.
|
|
The current beat position provides the discrete timing reference, with interpolation filling the gaps for fluid visual feedback.
|
|
|
|
## Connection Management
|
|
|
|
### Client Connection Pattern
|
|
The display connects to the audio engine's Unix socket server and handles connection lifecycle automatically.
|
|
Connection failures trigger retry attempts to avoid overwhelming the system during startup sequences.
|
|
|
|
### Error States and Recovery
|
|
The GUI displays connection status to provide user feedback during system startup or audio engine restarts.
|
|
Connection recovery happens transparently in the background, with the GUI resuming normal operation once the OSC stream resumes.
|
|
|
|
## Process Lifecycle
|
|
|
|
The display process operates independently of the audio engine, connecting as needed and handling temporary disconnections gracefully.
|
|
External process management tools handle startup coordination and service lifecycle,
|
|
keeping the display implementation focused on its core visualization responsibilities. |