expand MDK with missing types and trait methods
This commit is contained in:
parent
cf29a6d26b
commit
c9443d5877
|
|
@ -52,6 +52,11 @@ impl ModuleHost {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn load_builtin<T: OxideModule + 'static>(&mut self, name: &str, config: &GlobalConfig) -> u32 {
|
||||
let instance = T::new(config);
|
||||
self.load_builtin_boxed(Box::new(instance), name)
|
||||
}
|
||||
|
||||
pub fn load_builtin_boxed(&mut self, instance: Box<dyn OxideModule>, name: &str) -> u32 {
|
||||
let id = self.next_id;
|
||||
self.next_id += 1;
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ path = "src/main.rs"
|
|||
|
||||
[dependencies]
|
||||
clap = { version = "4.5.48", features = ["derive"] }
|
||||
crossbeam-channel = "0.5.12"
|
||||
serde = { version = "1.0.228", features = ["derive"] }
|
||||
toml = "0.9.7"
|
||||
uuid = { version = "1.18.1", features = ["v4", "serde"] }
|
||||
|
|
|
|||
|
|
@ -0,0 +1,43 @@
|
|||
use std::collections::HashMap;
|
||||
use std::sync::{Arc, Mutex};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct AudioFenceHandle {
|
||||
gui_to_audio: Arc<Mutex<Vec<(String, f32)>>>,
|
||||
audio_to_gui: Arc<Mutex<HashMap<String, f32>>>,
|
||||
}
|
||||
|
||||
impl AudioFenceHandle {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
gui_to_audio: Arc::new(Mutex::new(Vec::new())),
|
||||
audio_to_gui: Arc::new(Mutex::new(HashMap::new())),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn drain_changes(&mut self) -> Vec<(String, f32)> {
|
||||
let mut lock = self.gui_to_audio.lock().unwrap();
|
||||
lock.drain(..).collect()
|
||||
}
|
||||
|
||||
pub fn write_readback(&self, key: &str, value: f32) {
|
||||
let mut lock = self.audio_to_gui.lock().unwrap();
|
||||
lock.insert(key.to_string(), value);
|
||||
}
|
||||
|
||||
pub fn push_change(&self, key: String, value: f32) {
|
||||
let mut lock = self.gui_to_audio.lock().unwrap();
|
||||
lock.push((key, value));
|
||||
}
|
||||
|
||||
pub fn read_current(&self) -> HashMap<String, f32> {
|
||||
let lock = self.audio_to_gui.lock().unwrap();
|
||||
lock.clone()
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for AudioFenceHandle {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
|
@ -1,3 +1,4 @@
|
|||
pub mod gui;
|
||||
pub mod types;
|
||||
pub mod recording;
|
||||
|
||||
|
|
@ -7,6 +8,7 @@ pub use recording::*;
|
|||
use serde::{Deserialize, Serialize};
|
||||
use std::any::Any;
|
||||
use std::collections::HashMap;
|
||||
use std::sync::{Arc, Mutex};
|
||||
use uuid::Uuid;
|
||||
|
||||
pub use serde;
|
||||
|
|
@ -80,6 +82,8 @@ pub struct GuiElement {
|
|||
pub controls: String,
|
||||
}
|
||||
|
||||
// --- MIDI types ---
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct MidiEvent {
|
||||
pub timing: u32,
|
||||
|
|
@ -92,12 +96,45 @@ pub enum MidiMessage {
|
|||
NoteOff { key: u8, velocity: u8 },
|
||||
}
|
||||
|
||||
pub struct MidiInput<'a> {
|
||||
pub events: &'a [MidiEvent],
|
||||
}
|
||||
|
||||
impl<'a> MidiInput<'a> {
|
||||
pub fn new(events: &'a [MidiEvent]) -> Self {
|
||||
Self { events }
|
||||
}
|
||||
|
||||
pub fn events(&self) -> &[MidiEvent] {
|
||||
self.events
|
||||
}
|
||||
}
|
||||
|
||||
pub struct MidiOutput {
|
||||
pub port_name: String,
|
||||
pub buffer: Arc<Mutex<Vec<MidiEvent>>>,
|
||||
}
|
||||
|
||||
impl MidiOutput {
|
||||
pub fn new(port_name: String, buffer: Arc<Mutex<Vec<MidiEvent>>>) -> Self {
|
||||
Self { port_name, buffer }
|
||||
}
|
||||
|
||||
pub fn send(&self, event: MidiEvent) {
|
||||
let mut lock = self.buffer.lock().unwrap();
|
||||
lock.push(event);
|
||||
}
|
||||
}
|
||||
|
||||
// --- Transport / timing ---
|
||||
|
||||
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
|
||||
pub enum TransportState {
|
||||
Playing,
|
||||
#[default]
|
||||
Stopped,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, Default)]
|
||||
pub struct MusicalTime {
|
||||
pub sample_pos: u64,
|
||||
|
|
@ -106,8 +143,13 @@ pub struct MusicalTime {
|
|||
pub time_signature_numerator: u8,
|
||||
pub time_signature_denominator: u8,
|
||||
pub state: TransportState,
|
||||
pub cycle_active: bool,
|
||||
pub cycle_start_sample: u64,
|
||||
pub cycle_end_sample: u64,
|
||||
}
|
||||
|
||||
// --- GUI messages ---
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum ToGuiMessage {
|
||||
Log(String),
|
||||
|
|
@ -162,6 +204,8 @@ impl ToGuiQueue {
|
|||
}
|
||||
}
|
||||
|
||||
// --- Global config ---
|
||||
|
||||
#[derive(Clone, Debug, Default)]
|
||||
pub struct GlobalConfig {
|
||||
pub instance_id: Uuid,
|
||||
|
|
@ -169,6 +213,8 @@ pub struct GlobalConfig {
|
|||
pub buffer_size: u32,
|
||||
}
|
||||
|
||||
// --- Audio I/O types ---
|
||||
|
||||
pub struct MainAudioInput<'a> { pub buffer: &'a [f32] }
|
||||
impl<'a> MainAudioInput<'a> {
|
||||
pub fn iter(&self) -> impl Iterator<Item = &f32> { self.buffer.iter() }
|
||||
|
|
@ -181,6 +227,8 @@ impl<'a> MainAudioOutput<'a> {
|
|||
pub fn buffer_mut(&mut self) -> &mut [f32] { self.buffer }
|
||||
}
|
||||
|
||||
// --- Chain types ---
|
||||
|
||||
pub struct ChainInput<'a> {
|
||||
pub data: &'a (dyn Any + Send),
|
||||
}
|
||||
|
|
@ -199,7 +247,7 @@ impl<'a> ChainOutput<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
// --- Lane/Bus view types for module port access ---
|
||||
// --- Lane/Bus view types ---
|
||||
|
||||
pub struct LaneRef<'a> {
|
||||
real: &'a [f32],
|
||||
|
|
@ -311,15 +359,16 @@ pub struct PortDeclaration {
|
|||
pub struct ModuleContract {
|
||||
pub realtime: bool,
|
||||
pub min_buffer_samples: Option<usize>,
|
||||
pub latency_samples: u32,
|
||||
}
|
||||
|
||||
impl Default for ModuleContract {
|
||||
fn default() -> Self {
|
||||
Self { realtime: true, min_buffer_samples: None }
|
||||
Self { realtime: true, min_buffer_samples: None, latency_samples: 0 }
|
||||
}
|
||||
}
|
||||
|
||||
// --- Port view (runtime data passed to process) ---
|
||||
// --- Port view ---
|
||||
|
||||
pub struct PortView<'a> {
|
||||
buses_in: HashMap<String, BusRef<'a>>,
|
||||
|
|
@ -375,6 +424,8 @@ pub struct Ports<'a> {
|
|||
pub chain_in: Option<ChainInput<'a>>,
|
||||
pub chain_out: Option<ChainOutput<'a>>,
|
||||
pub port: PortView<'a>,
|
||||
pub midi_out: Option<MidiOutput>,
|
||||
pub midi_in: Option<MidiInput<'a>>,
|
||||
}
|
||||
|
||||
impl<'a> Default for Ports<'a> {
|
||||
|
|
@ -385,20 +436,110 @@ impl<'a> Default for Ports<'a> {
|
|||
chain_in: None,
|
||||
chain_out: None,
|
||||
port: PortView::new(),
|
||||
midi_out: None,
|
||||
midi_in: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// --- Process context ---
|
||||
|
||||
pub struct ProcessContext {
|
||||
pub time: MusicalTime,
|
||||
pub params: HashMap<String, f32>,
|
||||
pub to_gui: ToGuiQueue,
|
||||
pub sample_rate: u32,
|
||||
pub error_log: ErrorChannel,
|
||||
pub error_report: ErrorChannel,
|
||||
}
|
||||
|
||||
// --- Parameter descriptors ---
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ParameterDescriptor {
|
||||
pub key: String,
|
||||
pub label: String,
|
||||
pub kind: ParamKind,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum ParamKind {
|
||||
Float { min: f32, max: f32, default: f32, step: Option<f32> },
|
||||
Bool { default: bool },
|
||||
Choice { options: Vec<String>, default: usize },
|
||||
Int { min: i32, max: i32, default: i32 },
|
||||
}
|
||||
|
||||
// --- Module GUI descriptor ---
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum GuiToolkit {
|
||||
Framebuffer,
|
||||
Native,
|
||||
}
|
||||
|
||||
impl Default for GuiToolkit {
|
||||
fn default() -> Self {
|
||||
Self::Native
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ModuleGuiDescriptor {
|
||||
pub width: u32,
|
||||
pub height: u32,
|
||||
pub title: String,
|
||||
pub toolkit: GuiToolkit,
|
||||
}
|
||||
|
||||
// --- Error types ---
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum ErrorKind {
|
||||
Panic,
|
||||
ProcessError,
|
||||
PortError,
|
||||
ContractViolation,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ModuleError {
|
||||
pub module_id: u32,
|
||||
pub module_name: String,
|
||||
pub error_kind: ErrorKind,
|
||||
pub message: String,
|
||||
}
|
||||
|
||||
impl ModuleError {
|
||||
pub fn new(module_id: u32, module_name: String, error_kind: ErrorKind, message: String) -> Self {
|
||||
Self { module_id, module_name, error_kind, message }
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct ErrorChannel {
|
||||
tx: crossbeam_channel::Sender<ModuleError>,
|
||||
}
|
||||
|
||||
impl ErrorChannel {
|
||||
pub fn new(tx: crossbeam_channel::Sender<ModuleError>) -> Self {
|
||||
Self { tx }
|
||||
}
|
||||
|
||||
pub fn send(&self, error: ModuleError) {
|
||||
let _ = self.tx.send(error);
|
||||
}
|
||||
}
|
||||
|
||||
// --- OxideModule trait ---
|
||||
|
||||
pub trait OxideModule: Send + Sync {
|
||||
fn new(config: &GlobalConfig) -> Self where Self: Sized;
|
||||
fn process(&mut self, ports: Ports, context: &ProcessContext);
|
||||
fn contract(&self) -> ModuleContract { ModuleContract::default() }
|
||||
fn port_declarations(&self) -> Vec<PortDeclaration> { Vec::new() }
|
||||
fn receive_data(&mut self, _key: &str, _data: Box<dyn Any + Send>) {}
|
||||
fn param_descriptors(&self) -> Vec<ParameterDescriptor> { Vec::new() }
|
||||
fn gui_descriptor(&self) -> Option<ModuleGuiDescriptor> { None }
|
||||
fn has_gui(&self) -> bool { false }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,6 +18,9 @@ pub enum RecorderMessage {
|
|||
tempo: f32,
|
||||
time_sig_num: u8,
|
||||
},
|
||||
CycleBoundary {
|
||||
boundary_sample: u64,
|
||||
},
|
||||
}
|
||||
|
||||
pub struct PlaybackRegion {
|
||||
|
|
@ -26,4 +29,23 @@ pub struct PlaybackRegion {
|
|||
pub start_sample: u64,
|
||||
pub audio_l: Vec<f32>,
|
||||
pub audio_r: Vec<f32>,
|
||||
pub fade_in_samples: u64,
|
||||
pub fade_out_samples: u64,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct MidiPlaybackNote {
|
||||
pub start_tick: u64,
|
||||
pub duration_ticks: u64,
|
||||
pub note: u8,
|
||||
pub velocity: u8,
|
||||
pub channel: u8,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct MidiPlaybackRegion {
|
||||
pub bus_name: String,
|
||||
pub region_id: Uuid,
|
||||
pub start_beat: f64,
|
||||
pub notes: Vec<MidiPlaybackNote>,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -40,6 +40,7 @@ impl OxideModule for HilbertModule {
|
|||
ModuleContract {
|
||||
realtime: true,
|
||||
min_buffer_samples: Some(self.fft_size),
|
||||
latency_samples: 0,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -49,7 +49,7 @@ impl OxideModule for RegionPlayerModule {
|
|||
}
|
||||
|
||||
fn contract(&self) -> ModuleContract {
|
||||
ModuleContract { realtime: true, min_buffer_samples: None }
|
||||
ModuleContract { realtime: true, min_buffer_samples: None, latency_samples: 0 }
|
||||
}
|
||||
|
||||
fn receive_data(&mut self, key: &str, data: Box<dyn Any + Send>) {
|
||||
|
|
|
|||
|
|
@ -110,6 +110,6 @@ impl OxideModule for SpiralVisualizer {
|
|||
}
|
||||
|
||||
fn contract(&self) -> ModuleContract {
|
||||
ModuleContract { realtime: true, min_buffer_samples: None }
|
||||
ModuleContract { realtime: true, min_buffer_samples: None, latency_samples: 0 }
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue