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 {
|
pub fn load_builtin_boxed(&mut self, instance: Box<dyn OxideModule>, name: &str) -> u32 {
|
||||||
let id = self.next_id;
|
let id = self.next_id;
|
||||||
self.next_id += 1;
|
self.next_id += 1;
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,7 @@ path = "src/main.rs"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
clap = { version = "4.5.48", features = ["derive"] }
|
clap = { version = "4.5.48", features = ["derive"] }
|
||||||
|
crossbeam-channel = "0.5.12"
|
||||||
serde = { version = "1.0.228", features = ["derive"] }
|
serde = { version = "1.0.228", features = ["derive"] }
|
||||||
toml = "0.9.7"
|
toml = "0.9.7"
|
||||||
uuid = { version = "1.18.1", features = ["v4", "serde"] }
|
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 types;
|
||||||
pub mod recording;
|
pub mod recording;
|
||||||
|
|
||||||
|
|
@ -7,6 +8,7 @@ pub use recording::*;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::any::Any;
|
use std::any::Any;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
use std::sync::{Arc, Mutex};
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
pub use serde;
|
pub use serde;
|
||||||
|
|
@ -80,6 +82,8 @@ pub struct GuiElement {
|
||||||
pub controls: String,
|
pub controls: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// --- MIDI types ---
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy)]
|
#[derive(Debug, Clone, Copy)]
|
||||||
pub struct MidiEvent {
|
pub struct MidiEvent {
|
||||||
pub timing: u32,
|
pub timing: u32,
|
||||||
|
|
@ -92,12 +96,45 @@ pub enum MidiMessage {
|
||||||
NoteOff { key: u8, velocity: u8 },
|
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)]
|
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
|
||||||
pub enum TransportState {
|
pub enum TransportState {
|
||||||
Playing,
|
Playing,
|
||||||
#[default]
|
#[default]
|
||||||
Stopped,
|
Stopped,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, Default)]
|
#[derive(Debug, Clone, Copy, Default)]
|
||||||
pub struct MusicalTime {
|
pub struct MusicalTime {
|
||||||
pub sample_pos: u64,
|
pub sample_pos: u64,
|
||||||
|
|
@ -106,8 +143,13 @@ pub struct MusicalTime {
|
||||||
pub time_signature_numerator: u8,
|
pub time_signature_numerator: u8,
|
||||||
pub time_signature_denominator: u8,
|
pub time_signature_denominator: u8,
|
||||||
pub state: TransportState,
|
pub state: TransportState,
|
||||||
|
pub cycle_active: bool,
|
||||||
|
pub cycle_start_sample: u64,
|
||||||
|
pub cycle_end_sample: u64,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// --- GUI messages ---
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub enum ToGuiMessage {
|
pub enum ToGuiMessage {
|
||||||
Log(String),
|
Log(String),
|
||||||
|
|
@ -162,6 +204,8 @@ impl ToGuiQueue {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// --- Global config ---
|
||||||
|
|
||||||
#[derive(Clone, Debug, Default)]
|
#[derive(Clone, Debug, Default)]
|
||||||
pub struct GlobalConfig {
|
pub struct GlobalConfig {
|
||||||
pub instance_id: Uuid,
|
pub instance_id: Uuid,
|
||||||
|
|
@ -169,6 +213,8 @@ pub struct GlobalConfig {
|
||||||
pub buffer_size: u32,
|
pub buffer_size: u32,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// --- Audio I/O types ---
|
||||||
|
|
||||||
pub struct MainAudioInput<'a> { pub buffer: &'a [f32] }
|
pub struct MainAudioInput<'a> { pub buffer: &'a [f32] }
|
||||||
impl<'a> MainAudioInput<'a> {
|
impl<'a> MainAudioInput<'a> {
|
||||||
pub fn iter(&self) -> impl Iterator<Item = &f32> { self.buffer.iter() }
|
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 }
|
pub fn buffer_mut(&mut self) -> &mut [f32] { self.buffer }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// --- Chain types ---
|
||||||
|
|
||||||
pub struct ChainInput<'a> {
|
pub struct ChainInput<'a> {
|
||||||
pub data: &'a (dyn Any + Send),
|
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> {
|
pub struct LaneRef<'a> {
|
||||||
real: &'a [f32],
|
real: &'a [f32],
|
||||||
|
|
@ -311,15 +359,16 @@ pub struct PortDeclaration {
|
||||||
pub struct ModuleContract {
|
pub struct ModuleContract {
|
||||||
pub realtime: bool,
|
pub realtime: bool,
|
||||||
pub min_buffer_samples: Option<usize>,
|
pub min_buffer_samples: Option<usize>,
|
||||||
|
pub latency_samples: u32,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for ModuleContract {
|
impl Default for ModuleContract {
|
||||||
fn default() -> Self {
|
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> {
|
pub struct PortView<'a> {
|
||||||
buses_in: HashMap<String, BusRef<'a>>,
|
buses_in: HashMap<String, BusRef<'a>>,
|
||||||
|
|
@ -375,6 +424,8 @@ pub struct Ports<'a> {
|
||||||
pub chain_in: Option<ChainInput<'a>>,
|
pub chain_in: Option<ChainInput<'a>>,
|
||||||
pub chain_out: Option<ChainOutput<'a>>,
|
pub chain_out: Option<ChainOutput<'a>>,
|
||||||
pub port: PortView<'a>,
|
pub port: PortView<'a>,
|
||||||
|
pub midi_out: Option<MidiOutput>,
|
||||||
|
pub midi_in: Option<MidiInput<'a>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Default for Ports<'a> {
|
impl<'a> Default for Ports<'a> {
|
||||||
|
|
@ -385,20 +436,110 @@ impl<'a> Default for Ports<'a> {
|
||||||
chain_in: None,
|
chain_in: None,
|
||||||
chain_out: None,
|
chain_out: None,
|
||||||
port: PortView::new(),
|
port: PortView::new(),
|
||||||
|
midi_out: None,
|
||||||
|
midi_in: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// --- Process context ---
|
||||||
|
|
||||||
pub struct ProcessContext {
|
pub struct ProcessContext {
|
||||||
pub time: MusicalTime,
|
pub time: MusicalTime,
|
||||||
pub params: HashMap<String, f32>,
|
pub params: HashMap<String, f32>,
|
||||||
pub to_gui: ToGuiQueue,
|
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 {
|
pub trait OxideModule: Send + Sync {
|
||||||
fn new(config: &GlobalConfig) -> Self where Self: Sized;
|
fn new(config: &GlobalConfig) -> Self where Self: Sized;
|
||||||
fn process(&mut self, ports: Ports, context: &ProcessContext);
|
fn process(&mut self, ports: Ports, context: &ProcessContext);
|
||||||
fn contract(&self) -> ModuleContract { ModuleContract::default() }
|
fn contract(&self) -> ModuleContract { ModuleContract::default() }
|
||||||
fn port_declarations(&self) -> Vec<PortDeclaration> { Vec::new() }
|
fn port_declarations(&self) -> Vec<PortDeclaration> { Vec::new() }
|
||||||
fn receive_data(&mut self, _key: &str, _data: Box<dyn Any + Send>) {}
|
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,
|
tempo: f32,
|
||||||
time_sig_num: u8,
|
time_sig_num: u8,
|
||||||
},
|
},
|
||||||
|
CycleBoundary {
|
||||||
|
boundary_sample: u64,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct PlaybackRegion {
|
pub struct PlaybackRegion {
|
||||||
|
|
@ -26,4 +29,23 @@ pub struct PlaybackRegion {
|
||||||
pub start_sample: u64,
|
pub start_sample: u64,
|
||||||
pub audio_l: Vec<f32>,
|
pub audio_l: Vec<f32>,
|
||||||
pub audio_r: 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 {
|
ModuleContract {
|
||||||
realtime: true,
|
realtime: true,
|
||||||
min_buffer_samples: Some(self.fft_size),
|
min_buffer_samples: Some(self.fft_size),
|
||||||
|
latency_samples: 0,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -49,7 +49,7 @@ impl OxideModule for RegionPlayerModule {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn contract(&self) -> ModuleContract {
|
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>) {
|
fn receive_data(&mut self, key: &str, data: Box<dyn Any + Send>) {
|
||||||
|
|
|
||||||
|
|
@ -110,6 +110,6 @@ impl OxideModule for SpiralVisualizer {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn contract(&self) -> ModuleContract {
|
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