From d7dc423fe3632a0b75005ded77977ad92c595a8e Mon Sep 17 00:00:00 2001 From: Skyler Lehmkuhl Date: Tue, 28 Oct 2025 20:19:08 -0400 Subject: [PATCH] Remove old SimpleSynth and effect system --- daw-backend/src/audio/engine.rs | 355 ++++++------------ daw-backend/src/audio/node_graph/graph.rs | 5 + .../src/audio/node_graph/node_trait.rs | 6 + .../audio/node_graph/nodes/multi_sampler.rs | 55 --- .../audio/node_graph/nodes/oscilloscope.rs | 55 +-- daw-backend/src/audio/node_graph/nodes/pan.rs | 2 +- .../src/audio/node_graph/nodes/splitter.rs | 2 +- daw-backend/src/audio/project.rs | 87 +---- daw-backend/src/audio/track.rs | 128 ++----- daw-backend/src/command/mod.rs | 2 +- daw-backend/src/command/types.rs | 21 +- daw-backend/src/lib.rs | 3 +- src-tauri/src/audio.rs | 26 +- src-tauri/src/lib.rs | 1 - 14 files changed, 224 insertions(+), 524 deletions(-) diff --git a/daw-backend/src/audio/engine.rs b/daw-backend/src/audio/engine.rs index 06815c2..3be4e77 100644 --- a/daw-backend/src/audio/engine.rs +++ b/daw-backend/src/audio/engine.rs @@ -7,7 +7,6 @@ use crate::audio::project::Project; use crate::audio::recording::RecordingState; use crate::audio::track::{Track, TrackId, TrackNode}; use crate::command::{AudioEvent, Command, Query, QueryResponse}; -use crate::effects::{Effect, GainEffect, PanEffect, SimpleEQ}; use petgraph::stable_graph::NodeIndex; use std::sync::atomic::{AtomicU64, Ordering}; use std::sync::Arc; @@ -102,7 +101,6 @@ impl Engine { if let Some(node) = self.project.get_track_mut(id) { if let crate::audio::track::TrackNode::Audio(audio_track) = node { audio_track.clips = track.clips; - audio_track.effects = track.effects; audio_track.volume = track.volume; audio_track.muted = track.muted; audio_track.solo = track.solo; @@ -325,106 +323,6 @@ impl Engine { } } } - Command::AddGainEffect(track_id, gain_db) => { - // Get the track node and handle audio tracks, MIDI tracks, and groups - match self.project.get_track_mut(track_id) { - Some(crate::audio::track::TrackNode::Audio(track)) => { - if let Some(effect) = track.effects.iter_mut().find(|e| e.name() == "Gain") { - effect.set_parameter(0, gain_db); - } else { - track.add_effect(Box::new(GainEffect::with_gain_db(gain_db))); - } - } - Some(crate::audio::track::TrackNode::Midi(track)) => { - if let Some(effect) = track.effects.iter_mut().find(|e| e.name() == "Gain") { - effect.set_parameter(0, gain_db); - } else { - track.add_effect(Box::new(GainEffect::with_gain_db(gain_db))); - } - } - Some(crate::audio::track::TrackNode::Group(group)) => { - if let Some(effect) = group.effects.iter_mut().find(|e| e.name() == "Gain") { - effect.set_parameter(0, gain_db); - } else { - group.add_effect(Box::new(GainEffect::with_gain_db(gain_db))); - } - } - None => {} - } - } - Command::AddPanEffect(track_id, pan) => { - match self.project.get_track_mut(track_id) { - Some(crate::audio::track::TrackNode::Audio(track)) => { - if let Some(effect) = track.effects.iter_mut().find(|e| e.name() == "Pan") { - effect.set_parameter(0, pan); - } else { - track.add_effect(Box::new(PanEffect::with_pan(pan))); - } - } - Some(crate::audio::track::TrackNode::Midi(track)) => { - if let Some(effect) = track.effects.iter_mut().find(|e| e.name() == "Pan") { - effect.set_parameter(0, pan); - } else { - track.add_effect(Box::new(PanEffect::with_pan(pan))); - } - } - Some(crate::audio::track::TrackNode::Group(group)) => { - if let Some(effect) = group.effects.iter_mut().find(|e| e.name() == "Pan") { - effect.set_parameter(0, pan); - } else { - group.add_effect(Box::new(PanEffect::with_pan(pan))); - } - } - None => {} - } - } - Command::AddEQEffect(track_id, low_db, mid_db, high_db) => { - match self.project.get_track_mut(track_id) { - Some(crate::audio::track::TrackNode::Audio(track)) => { - if let Some(effect) = track.effects.iter_mut().find(|e| e.name() == "SimpleEQ") { - effect.set_parameter(0, low_db); - effect.set_parameter(1, mid_db); - effect.set_parameter(2, high_db); - } else { - let mut eq = SimpleEQ::new(); - eq.set_parameter(0, low_db); - eq.set_parameter(1, mid_db); - eq.set_parameter(2, high_db); - track.add_effect(Box::new(eq)); - } - } - Some(crate::audio::track::TrackNode::Midi(track)) => { - if let Some(effect) = track.effects.iter_mut().find(|e| e.name() == "SimpleEQ") { - effect.set_parameter(0, low_db); - effect.set_parameter(1, mid_db); - effect.set_parameter(2, high_db); - } else { - let mut eq = SimpleEQ::new(); - eq.set_parameter(0, low_db); - eq.set_parameter(1, mid_db); - eq.set_parameter(2, high_db); - track.add_effect(Box::new(eq)); - } - } - Some(crate::audio::track::TrackNode::Group(group)) => { - if let Some(effect) = group.effects.iter_mut().find(|e| e.name() == "SimpleEQ") { - effect.set_parameter(0, low_db); - effect.set_parameter(1, mid_db); - effect.set_parameter(2, high_db); - } else { - let mut eq = SimpleEQ::new(); - eq.set_parameter(0, low_db); - eq.set_parameter(1, mid_db); - eq.set_parameter(2, high_db); - group.add_effect(Box::new(eq)); - } - } - None => {} - } - } - Command::ClearEffects(track_id) => { - let _ = self.project.clear_effects(track_id); - } Command::CreateMetatrack(name) => { let track_id = self.project.add_group_track(name.clone(), None); // Notify UI about the new metatrack @@ -743,13 +641,8 @@ impl Engine { Command::GraphAddNode(track_id, node_type, x, y) => { // Get MIDI track (graphs are only for MIDI tracks currently) if let Some(TrackNode::Midi(track)) = self.project.get_track_mut(track_id) { - // Create graph if it doesn't exist - if track.instrument_graph.is_none() { - // Use large buffer to accommodate any audio callback size - track.instrument_graph = Some(InstrumentGraph::new(self.sample_rate, 8192)); - } - - if let Some(graph) = &mut track.instrument_graph { + let graph = &mut track.instrument_graph; + { // Create the node based on type let node: Box = match node_type.as_str() { "Oscillator" => Box::new(OscillatorNode::new("Oscillator".to_string())), @@ -810,7 +703,8 @@ impl Engine { Command::GraphAddNodeToTemplate(track_id, voice_allocator_id, node_type, _x, _y) => { if let Some(TrackNode::Midi(track)) = self.project.get_track_mut(track_id) { - if let Some(graph) = &mut track.instrument_graph { + let graph = &mut track.instrument_graph; + { let va_idx = NodeIndex::new(voice_allocator_id as usize); // Create the node @@ -871,7 +765,8 @@ impl Engine { Command::GraphRemoveNode(track_id, node_index) => { if let Some(TrackNode::Midi(track)) = self.project.get_track_mut(track_id) { - if let Some(graph) = &mut track.instrument_graph { + let graph = &mut track.instrument_graph; + { let node_idx = NodeIndex::new(node_index as usize); graph.remove_node(node_idx); let _ = self.event_tx.push(AudioEvent::GraphStateChanged(track_id)); @@ -881,7 +776,8 @@ impl Engine { Command::GraphConnect(track_id, from, from_port, to, to_port) => { if let Some(TrackNode::Midi(track)) = self.project.get_track_mut(track_id) { - if let Some(graph) = &mut track.instrument_graph { + let graph = &mut track.instrument_graph; + { let from_idx = NodeIndex::new(from as usize); let to_idx = NodeIndex::new(to as usize); @@ -902,7 +798,8 @@ impl Engine { Command::GraphConnectInTemplate(track_id, voice_allocator_id, from, from_port, to, to_port) => { if let Some(TrackNode::Midi(track)) = self.project.get_track_mut(track_id) { - if let Some(graph) = &mut track.instrument_graph { + let graph = &mut track.instrument_graph; + { let va_idx = NodeIndex::new(voice_allocator_id as usize); match graph.connect_in_voice_allocator_template(va_idx, from, from_port, to, to_port) { @@ -923,7 +820,8 @@ impl Engine { Command::GraphDisconnect(track_id, from, from_port, to, to_port) => { if let Some(TrackNode::Midi(track)) = self.project.get_track_mut(track_id) { - if let Some(graph) = &mut track.instrument_graph { + let graph = &mut track.instrument_graph; + { let from_idx = NodeIndex::new(from as usize); let to_idx = NodeIndex::new(to as usize); graph.disconnect(from_idx, from_port, to_idx, to_port); @@ -934,7 +832,8 @@ impl Engine { Command::GraphSetParameter(track_id, node_index, param_id, value) => { if let Some(TrackNode::Midi(track)) = self.project.get_track_mut(track_id) { - if let Some(graph) = &mut track.instrument_graph { + let graph = &mut track.instrument_graph; + { let node_idx = NodeIndex::new(node_index as usize); if let Some(graph_node) = graph.get_graph_node_mut(node_idx) { graph_node.node.set_parameter(param_id, value); @@ -945,7 +844,8 @@ impl Engine { Command::GraphSetMidiTarget(track_id, node_index, enabled) => { if let Some(TrackNode::Midi(track)) = self.project.get_track_mut(track_id) { - if let Some(graph) = &mut track.instrument_graph { + let graph = &mut track.instrument_graph; + { let node_idx = NodeIndex::new(node_index as usize); graph.set_midi_target(node_idx, enabled); } @@ -954,7 +854,8 @@ impl Engine { Command::GraphSetOutputNode(track_id, node_index) => { if let Some(TrackNode::Midi(track)) = self.project.get_track_mut(track_id) { - if let Some(graph) = &mut track.instrument_graph { + let graph = &mut track.instrument_graph; + { let node_idx = NodeIndex::new(node_index as usize); graph.set_output_node(Some(node_idx)); } @@ -963,27 +864,26 @@ impl Engine { Command::GraphSavePreset(track_id, preset_path, preset_name, description, tags) => { if let Some(TrackNode::Midi(track)) = self.project.get_track_mut(track_id) { - if let Some(ref graph) = track.instrument_graph { - // Serialize the graph to a preset - let mut preset = graph.to_preset(&preset_name); - preset.metadata.description = description; - preset.metadata.tags = tags; - preset.metadata.author = String::from("User"); + let graph = &track.instrument_graph; + // Serialize the graph to a preset + let mut preset = graph.to_preset(&preset_name); + preset.metadata.description = description; + preset.metadata.tags = tags; + preset.metadata.author = String::from("User"); - // Write to file - if let Ok(json) = preset.to_json() { - if let Err(e) = std::fs::write(&preset_path, json) { - let _ = self.event_tx.push(AudioEvent::GraphConnectionError( - track_id, - format!("Failed to save preset: {}", e) - )); - } - } else { + // Write to file + if let Ok(json) = preset.to_json() { + if let Err(e) = std::fs::write(&preset_path, json) { let _ = self.event_tx.push(AudioEvent::GraphConnectionError( track_id, - "Failed to serialize preset".to_string() + format!("Failed to save preset: {}", e) )); } + } else { + let _ = self.event_tx.push(AudioEvent::GraphConnectionError( + track_id, + "Failed to serialize preset".to_string() + )); } } } @@ -1001,7 +901,7 @@ impl Engine { Ok(graph) => { // Replace the track's graph if let Some(TrackNode::Midi(track)) = self.project.get_track_mut(track_id) { - track.instrument_graph = Some(graph); + track.instrument_graph = graph; let _ = self.event_tx.push(AudioEvent::GraphStateChanged(track_id)); // Emit preset loaded event after everything is loaded let _ = self.event_tx.push(AudioEvent::GraphPresetLoaded(track_id)); @@ -1036,24 +936,23 @@ impl Engine { use crate::audio::node_graph::nodes::VoiceAllocatorNode; if let Some(TrackNode::Midi(track)) = self.project.get_track_mut(track_id) { - if let Some(ref graph) = track.instrument_graph { - let va_idx = NodeIndex::new(voice_allocator_id as usize); + let graph = &track.instrument_graph; + let va_idx = NodeIndex::new(voice_allocator_id as usize); - // Get the VoiceAllocator node and serialize its template - if let Some(node) = graph.get_node(va_idx) { - // Downcast to VoiceAllocatorNode - let node_ptr = node as *const dyn crate::audio::node_graph::AudioNode; - let node_ptr = node_ptr as *const VoiceAllocatorNode; + // Get the VoiceAllocator node and serialize its template + if let Some(node) = graph.get_node(va_idx) { + // Downcast to VoiceAllocatorNode + let node_ptr = node as *const dyn crate::audio::node_graph::AudioNode; + let node_ptr = node_ptr as *const VoiceAllocatorNode; - unsafe { - let va_node = &*node_ptr; - let template_preset = va_node.template_graph().to_preset(&preset_name); + unsafe { + let va_node = &*node_ptr; + let template_preset = va_node.template_graph().to_preset(&preset_name); - // Write to file - if let Ok(json) = template_preset.to_json() { - if let Err(e) = std::fs::write(&preset_path, json) { - eprintln!("Failed to save template preset: {}", e); - } + // Write to file + if let Ok(json) = template_preset.to_json() { + if let Err(e) = std::fs::write(&preset_path, json) { + eprintln!("Failed to save template preset: {}", e); } } } @@ -1065,19 +964,18 @@ impl Engine { use crate::audio::node_graph::nodes::SimpleSamplerNode; if let Some(TrackNode::Midi(track)) = self.project.get_track_mut(track_id) { - if let Some(ref mut graph) = track.instrument_graph { - let node_idx = NodeIndex::new(node_id as usize); + let graph = &mut track.instrument_graph; + let node_idx = NodeIndex::new(node_id as usize); - if let Some(graph_node) = graph.get_graph_node_mut(node_idx) { - // Downcast to SimpleSamplerNode - let node_ptr = &mut *graph_node.node as *mut dyn crate::audio::node_graph::AudioNode; - let node_ptr = node_ptr as *mut SimpleSamplerNode; + if let Some(graph_node) = graph.get_graph_node_mut(node_idx) { + // Downcast to SimpleSamplerNode + let node_ptr = &mut *graph_node.node as *mut dyn crate::audio::node_graph::AudioNode; + let node_ptr = node_ptr as *mut SimpleSamplerNode; - unsafe { - let sampler_node = &mut *node_ptr; - if let Err(e) = sampler_node.load_sample_from_file(&file_path) { - eprintln!("Failed to load sample: {}", e); - } + unsafe { + let sampler_node = &mut *node_ptr; + if let Err(e) = sampler_node.load_sample_from_file(&file_path) { + eprintln!("Failed to load sample: {}", e); } } } @@ -1088,19 +986,18 @@ impl Engine { use crate::audio::node_graph::nodes::MultiSamplerNode; if let Some(TrackNode::Midi(track)) = self.project.get_track_mut(track_id) { - if let Some(ref mut graph) = track.instrument_graph { - let node_idx = NodeIndex::new(node_id as usize); + let graph = &mut track.instrument_graph; + let node_idx = NodeIndex::new(node_id as usize); - if let Some(graph_node) = graph.get_graph_node_mut(node_idx) { - // Downcast to MultiSamplerNode - let node_ptr = &mut *graph_node.node as *mut dyn crate::audio::node_graph::AudioNode; - let node_ptr = node_ptr as *mut MultiSamplerNode; + if let Some(graph_node) = graph.get_graph_node_mut(node_idx) { + // Downcast to MultiSamplerNode + let node_ptr = &mut *graph_node.node as *mut dyn crate::audio::node_graph::AudioNode; + let node_ptr = node_ptr as *mut MultiSamplerNode; - unsafe { - let multi_sampler_node = &mut *node_ptr; - if let Err(e) = multi_sampler_node.load_layer_from_file(&file_path, key_min, key_max, root_key, velocity_min, velocity_max) { - eprintln!("Failed to add sample layer: {}", e); - } + unsafe { + let multi_sampler_node = &mut *node_ptr; + if let Err(e) = multi_sampler_node.load_layer_from_file(&file_path, key_min, key_max, root_key, velocity_min, velocity_max) { + eprintln!("Failed to add sample layer: {}", e); } } } @@ -1111,19 +1008,18 @@ impl Engine { use crate::audio::node_graph::nodes::MultiSamplerNode; if let Some(TrackNode::Midi(track)) = self.project.get_track_mut(track_id) { - if let Some(ref mut graph) = track.instrument_graph { - let node_idx = NodeIndex::new(node_id as usize); + let graph = &mut track.instrument_graph; + let node_idx = NodeIndex::new(node_id as usize); - if let Some(graph_node) = graph.get_graph_node_mut(node_idx) { - // Downcast to MultiSamplerNode - let node_ptr = &mut *graph_node.node as *mut dyn crate::audio::node_graph::AudioNode; - let node_ptr = node_ptr as *mut MultiSamplerNode; + if let Some(graph_node) = graph.get_graph_node_mut(node_idx) { + // Downcast to MultiSamplerNode + let node_ptr = &mut *graph_node.node as *mut dyn crate::audio::node_graph::AudioNode; + let node_ptr = node_ptr as *mut MultiSamplerNode; - unsafe { - let multi_sampler_node = &mut *node_ptr; - if let Err(e) = multi_sampler_node.update_layer(layer_index, key_min, key_max, root_key, velocity_min, velocity_max) { - eprintln!("Failed to update sample layer: {}", e); - } + unsafe { + let multi_sampler_node = &mut *node_ptr; + if let Err(e) = multi_sampler_node.update_layer(layer_index, key_min, key_max, root_key, velocity_min, velocity_max) { + eprintln!("Failed to update sample layer: {}", e); } } } @@ -1134,19 +1030,18 @@ impl Engine { use crate::audio::node_graph::nodes::MultiSamplerNode; if let Some(TrackNode::Midi(track)) = self.project.get_track_mut(track_id) { - if let Some(ref mut graph) = track.instrument_graph { - let node_idx = NodeIndex::new(node_id as usize); + let graph = &mut track.instrument_graph; + let node_idx = NodeIndex::new(node_id as usize); - if let Some(graph_node) = graph.get_graph_node_mut(node_idx) { - // Downcast to MultiSamplerNode - let node_ptr = &mut *graph_node.node as *mut dyn crate::audio::node_graph::AudioNode; - let node_ptr = node_ptr as *mut MultiSamplerNode; + if let Some(graph_node) = graph.get_graph_node_mut(node_idx) { + // Downcast to MultiSamplerNode + let node_ptr = &mut *graph_node.node as *mut dyn crate::audio::node_graph::AudioNode; + let node_ptr = node_ptr as *mut MultiSamplerNode; - unsafe { - let multi_sampler_node = &mut *node_ptr; - if let Err(e) = multi_sampler_node.remove_layer(layer_index) { - eprintln!("Failed to remove sample layer: {}", e); - } + unsafe { + let multi_sampler_node = &mut *node_ptr; + if let Err(e) = multi_sampler_node.remove_layer(layer_index) { + eprintln!("Failed to remove sample layer: {}", e); } } } @@ -1160,19 +1055,11 @@ impl Engine { let response = match query { Query::GetGraphState(track_id) => { if let Some(TrackNode::Midi(track)) = self.project.get_track(track_id) { - if let Some(ref graph) = track.instrument_graph { - let preset = graph.to_preset("temp"); - match preset.to_json() { - Ok(json) => QueryResponse::GraphState(Ok(json)), - Err(e) => QueryResponse::GraphState(Err(format!("Failed to serialize graph: {:?}", e))), - } - } else { - // Empty graph - let empty_preset = crate::audio::node_graph::preset::GraphPreset::new("empty"); - match empty_preset.to_json() { - Ok(json) => QueryResponse::GraphState(Ok(json)), - Err(_) => QueryResponse::GraphState(Err("Failed to serialize empty graph".to_string())), - } + let graph = &track.instrument_graph; + let preset = graph.to_preset("temp"); + match preset.to_json() { + Ok(json) => QueryResponse::GraphState(Ok(json)), + Err(e) => QueryResponse::GraphState(Err(format!("Failed to serialize graph: {:?}", e))), } } else { QueryResponse::GraphState(Err(format!("Track {} not found or is not a MIDI track", track_id))) @@ -1180,25 +1067,22 @@ impl Engine { } Query::GetTemplateState(track_id, voice_allocator_id) => { if let Some(TrackNode::Midi(track)) = self.project.get_track_mut(track_id) { - if let Some(ref mut graph) = track.instrument_graph { - let node_idx = NodeIndex::new(voice_allocator_id as usize); - if let Some(graph_node) = graph.get_graph_node_mut(node_idx) { - // Downcast to VoiceAllocatorNode - let node_ptr = &*graph_node.node as *const dyn crate::audio::node_graph::AudioNode; - let node_ptr = node_ptr as *const VoiceAllocatorNode; - unsafe { - let va_node = &*node_ptr; - let template_preset = va_node.template_graph().to_preset("template"); - match template_preset.to_json() { - Ok(json) => QueryResponse::GraphState(Ok(json)), - Err(e) => QueryResponse::GraphState(Err(format!("Failed to serialize template: {:?}", e))), - } + let graph = &mut track.instrument_graph; + let node_idx = NodeIndex::new(voice_allocator_id as usize); + if let Some(graph_node) = graph.get_graph_node_mut(node_idx) { + // Downcast to VoiceAllocatorNode + let node_ptr = &*graph_node.node as *const dyn crate::audio::node_graph::AudioNode; + let node_ptr = node_ptr as *const VoiceAllocatorNode; + unsafe { + let va_node = &*node_ptr; + let template_preset = va_node.template_graph().to_preset("template"); + match template_preset.to_json() { + Ok(json) => QueryResponse::GraphState(Ok(json)), + Err(e) => QueryResponse::GraphState(Err(format!("Failed to serialize template: {:?}", e))), } - } else { - QueryResponse::GraphState(Err("Voice allocator node not found".to_string())) } } else { - QueryResponse::GraphState(Err("Graph not found".to_string())) + QueryResponse::GraphState(Err("Voice allocator node not found".to_string())) } } else { QueryResponse::GraphState(Err(format!("Track {} not found or is not a MIDI track", track_id))) @@ -1206,7 +1090,10 @@ impl Engine { } Query::GetOscilloscopeData(track_id, node_id, sample_count) => { match self.project.get_oscilloscope_data(track_id, node_id, sample_count) { - Some(data) => QueryResponse::OscilloscopeData(Ok(data)), + Some((audio, cv)) => { + use crate::command::OscilloscopeData; + QueryResponse::OscilloscopeData(Ok(OscilloscopeData { audio, cv })) + } None => QueryResponse::OscilloscopeData(Err(format!( "Failed to get oscilloscope data from track {} node {}", track_id, node_id @@ -1437,26 +1324,6 @@ impl EngineController { let _ = self.command_tx.push(Command::MoveClip(track_id, clip_id, new_start_time)); } - /// Add or update gain effect on track - pub fn add_gain_effect(&mut self, track_id: TrackId, gain_db: f32) { - let _ = self.command_tx.push(Command::AddGainEffect(track_id, gain_db)); - } - - /// Add or update pan effect on track - pub fn add_pan_effect(&mut self, track_id: TrackId, pan: f32) { - let _ = self.command_tx.push(Command::AddPanEffect(track_id, pan)); - } - - /// Add or update EQ effect on track - pub fn add_eq_effect(&mut self, track_id: TrackId, low_db: f32, mid_db: f32, high_db: f32) { - let _ = self.command_tx.push(Command::AddEQEffect(track_id, low_db, mid_db, high_db)); - } - - /// Clear all effects from a track - pub fn clear_effects(&mut self, track_id: TrackId) { - let _ = self.command_tx.push(Command::ClearEffects(track_id)); - } - /// Get current playhead position in samples pub fn get_playhead_samples(&self) -> u64 { self.playhead.load(Ordering::Relaxed) @@ -1770,7 +1637,7 @@ impl EngineController { } /// Query oscilloscope data from a node - pub fn query_oscilloscope_data(&mut self, track_id: TrackId, node_id: u32, sample_count: usize) -> Result, String> { + pub fn query_oscilloscope_data(&mut self, track_id: TrackId, node_id: u32, sample_count: usize) -> Result { // Send query if let Err(_) = self.query_tx.push(Query::GetOscilloscopeData(track_id, node_id, sample_count)) { return Err("Failed to send query - queue full".to_string()); diff --git a/daw-backend/src/audio/node_graph/graph.rs b/daw-backend/src/audio/node_graph/graph.rs index 43a07da..3101c9d 100644 --- a/daw-backend/src/audio/node_graph/graph.rs +++ b/daw-backend/src/audio/node_graph/graph.rs @@ -499,6 +499,11 @@ impl InstrumentGraph { self.get_node(idx).and_then(|node| node.get_oscilloscope_data(sample_count)) } + /// Get oscilloscope CV data from a specific node + pub fn get_oscilloscope_cv_data(&self, idx: NodeIndex, sample_count: usize) -> Option> { + self.get_node(idx).and_then(|node| node.get_oscilloscope_cv_data(sample_count)) + } + /// Get node mutably by index /// Note: Due to lifetime constraints with trait objects, this returns a mutable reference /// to the GraphNode, from which you can access the node diff --git a/daw-backend/src/audio/node_graph/node_trait.rs b/daw-backend/src/audio/node_graph/node_trait.rs index 6eecdb0..ec4a3c7 100644 --- a/daw-backend/src/audio/node_graph/node_trait.rs +++ b/daw-backend/src/audio/node_graph/node_trait.rs @@ -64,4 +64,10 @@ pub trait AudioNode: Send { fn get_oscilloscope_data(&self, _sample_count: usize) -> Option> { None } + + /// Get oscilloscope CV data if this is an oscilloscope node + /// Returns None for non-oscilloscope nodes + fn get_oscilloscope_cv_data(&self, _sample_count: usize) -> Option> { + None + } } diff --git a/daw-backend/src/audio/node_graph/nodes/multi_sampler.rs b/daw-backend/src/audio/node_graph/nodes/multi_sampler.rs index 77ea06b..88b51e5 100644 --- a/daw-backend/src/audio/node_graph/nodes/multi_sampler.rs +++ b/daw-backend/src/audio/node_graph/nodes/multi_sampler.rs @@ -330,61 +330,6 @@ impl MultiSamplerNode { } } } - - /// Calculate playback speed from pitch difference - fn calculate_speed(&self, layer: &SampleLayer, note: u8) -> f32 { - let semitone_diff = note as i16 - layer.root_key as i16; - 2.0_f32.powf(semitone_diff as f32 / 12.0) - } - - /// Read sample at playhead with linear interpolation - fn read_sample(&self, playhead: f32, sample: &[f32]) -> f32 { - if sample.is_empty() || playhead < 0.0 { - return 0.0; - } - - let index = playhead.floor() as usize; - if index >= sample.len() { - return 0.0; - } - - let frac = playhead - playhead.floor(); - let sample1 = sample[index]; - let sample2 = if index + 1 < sample.len() { - sample[index + 1] - } else { - 0.0 - }; - - sample1 + (sample2 - sample1) * frac - } - - /// Process envelope for a voice - fn process_envelope(&self, voice: &mut Voice, sample_rate: f32) -> f32 { - match voice.envelope_phase { - EnvelopePhase::Attack => { - let attack_samples = self.attack_time * sample_rate; - voice.envelope_value += 1.0 / attack_samples; - if voice.envelope_value >= 1.0 { - voice.envelope_value = 1.0; - voice.envelope_phase = EnvelopePhase::Sustain; - } - } - EnvelopePhase::Sustain => { - voice.envelope_value = 1.0; - } - EnvelopePhase::Release => { - let release_samples = self.release_time * sample_rate; - voice.envelope_value -= 1.0 / release_samples; - if voice.envelope_value <= 0.0 { - voice.envelope_value = 0.0; - voice.is_active = false; - } - } - } - - voice.envelope_value.clamp(0.0, 1.0) - } } impl AudioNode for MultiSamplerNode { diff --git a/daw-backend/src/audio/node_graph/nodes/oscilloscope.rs b/daw-backend/src/audio/node_graph/nodes/oscilloscope.rs index 5dc533e..64488cc 100644 --- a/daw-backend/src/audio/node_graph/nodes/oscilloscope.rs +++ b/daw-backend/src/audio/node_graph/nodes/oscilloscope.rs @@ -28,7 +28,7 @@ impl TriggerMode { } /// Circular buffer for storing audio samples -struct CircularBuffer { +pub struct CircularBuffer { buffer: Vec, write_pos: usize, capacity: usize, @@ -75,7 +75,7 @@ impl CircularBuffer { } } -/// Oscilloscope node for visualizing audio signals +/// Oscilloscope node for visualizing audio and CV signals pub struct OscilloscopeNode { name: String, time_scale: f32, // Milliseconds to display (10-1000ms) @@ -86,8 +86,9 @@ pub struct OscilloscopeNode { sample_counter: usize, // Counter for V/oct triggering trigger_period: usize, // Period in samples for V/oct triggering - // Shared buffer for reading from Tauri commands - buffer: Arc>, + // Shared buffers for reading from Tauri commands + buffer: Arc>, // Audio buffer + cv_buffer: Arc>, // CV buffer inputs: Vec, outputs: Vec, @@ -101,6 +102,7 @@ impl OscilloscopeNode { let inputs = vec![ NodePort::new("Audio In", SignalType::Audio, 0), NodePort::new("V/oct", SignalType::CV, 1), + NodePort::new("CV In", SignalType::CV, 2), ]; let outputs = vec![ @@ -123,6 +125,7 @@ impl OscilloscopeNode { sample_counter: 0, trigger_period: 480, // Default to ~100Hz at 48kHz buffer: Arc::new(Mutex::new(CircularBuffer::new(BUFFER_SIZE))), + cv_buffer: Arc::new(Mutex::new(CircularBuffer::new(BUFFER_SIZE))), inputs, outputs, parameters, @@ -143,11 +146,23 @@ impl OscilloscopeNode { } } + /// Read CV samples from the CV buffer (for Tauri commands) + pub fn read_cv_samples(&self, count: usize) -> Vec { + if let Ok(buffer) = self.cv_buffer.lock() { + buffer.read(count) + } else { + vec![0.0; count] + } + } + /// Clear the buffer pub fn clear_buffer(&self) { if let Ok(mut buffer) = self.buffer.lock() { buffer.clear(); } + if let Ok(mut cv_buffer) = self.cv_buffer.lock() { + cv_buffer.clear(); + } } /// Convert V/oct to frequency in Hz (matches oscillator convention) @@ -155,23 +170,6 @@ impl OscilloscopeNode { fn voct_to_frequency(voct: f32) -> f32 { 440.0 * 2.0_f32.powf(voct) } - - /// Check if trigger condition is met - fn is_triggered(&self, current_sample: f32) -> bool { - match self.trigger_mode { - TriggerMode::FreeRunning => true, - TriggerMode::RisingEdge => { - self.last_sample <= self.trigger_level && current_sample > self.trigger_level - } - TriggerMode::FallingEdge => { - self.last_sample >= self.trigger_level && current_sample < self.trigger_level - } - TriggerMode::VoltPerOctave => { - // Trigger at the start of each period - self.sample_counter == 0 - } - } - } } impl AudioNode for OscilloscopeNode { @@ -242,11 +240,19 @@ impl AudioNode for OscilloscopeNode { // Pass through audio (copy input to output) output[..len].copy_from_slice(&input[..len]); - // Capture samples to buffer + // Capture audio samples to buffer if let Ok(mut buffer) = self.buffer.lock() { buffer.write(&input[..len]); } + // Capture CV samples if CV input is connected (input 2) + if inputs.len() > 2 && !inputs[2].is_empty() { + let cv_input = inputs[2]; + if let Ok(mut cv_buffer) = self.cv_buffer.lock() { + cv_buffer.write(&cv_input[..len.min(cv_input.len())]); + } + } + // Update last sample for trigger detection (use left channel, frame 0) if !input.is_empty() { self.last_sample = input[0]; @@ -279,6 +285,7 @@ impl AudioNode for OscilloscopeNode { sample_counter: 0, trigger_period: 480, buffer: Arc::new(Mutex::new(CircularBuffer::new(BUFFER_SIZE))), + cv_buffer: Arc::new(Mutex::new(CircularBuffer::new(BUFFER_SIZE))), inputs: self.inputs.clone(), outputs: self.outputs.clone(), parameters: self.parameters.clone(), @@ -288,4 +295,8 @@ impl AudioNode for OscilloscopeNode { fn get_oscilloscope_data(&self, sample_count: usize) -> Option> { Some(self.read_samples(sample_count)) } + + fn get_oscilloscope_cv_data(&self, sample_count: usize) -> Option> { + Some(self.read_cv_samples(sample_count)) + } } diff --git a/daw-backend/src/audio/node_graph/nodes/pan.rs b/daw-backend/src/audio/node_graph/nodes/pan.rs index 455646d..c2db78b 100644 --- a/daw-backend/src/audio/node_graph/nodes/pan.rs +++ b/daw-backend/src/audio/node_graph/nodes/pan.rs @@ -120,7 +120,7 @@ impl AudioNode for PanNode { if inputs.len() > 1 && frame < inputs[1].len() { let cv = inputs[1][frame]; // CV is mono // CV is 0-1, map to -1 to +1 range - pan += (cv * 2.0 - 1.0); + pan += cv * 2.0 - 1.0; pan = pan.clamp(-1.0, 1.0); } diff --git a/daw-backend/src/audio/node_graph/nodes/splitter.rs b/daw-backend/src/audio/node_graph/nodes/splitter.rs index 22b3c7e..ff6b168 100644 --- a/daw-backend/src/audio/node_graph/nodes/splitter.rs +++ b/daw-backend/src/audio/node_graph/nodes/splitter.rs @@ -1,4 +1,4 @@ -use crate::audio::node_graph::{AudioNode, NodeCategory, NodePort, Parameter, ParameterUnit, SignalType}; +use crate::audio::node_graph::{AudioNode, NodeCategory, NodePort, Parameter, SignalType}; use crate::audio::midi::MidiEvent; /// Splitter node - copies input to multiple outputs for parallel routing diff --git a/daw-backend/src/audio/project.rs b/daw-backend/src/audio/project.rs index 452182e..57403fb 100644 --- a/daw-backend/src/audio/project.rs +++ b/daw-backend/src/audio/project.rs @@ -1,9 +1,8 @@ use super::buffer_pool::BufferPool; use super::clip::Clip; -use super::midi::MidiClip; +use super::midi::{MidiClip, MidiEvent}; use super::pool::AudioPool; use super::track::{AudioTrack, Metatrack, MidiTrack, RenderContext, TrackId, TrackNode}; -use crate::effects::Effect; use std::collections::HashMap; /// Project manages the hierarchical track structure @@ -199,12 +198,18 @@ impl Project { } /// Get oscilloscope data from a node in a track's graph - pub fn get_oscilloscope_data(&self, track_id: TrackId, node_id: u32, sample_count: usize) -> Option> { + pub fn get_oscilloscope_data(&self, track_id: TrackId, node_id: u32, sample_count: usize) -> Option<(Vec, Vec)> { if let Some(TrackNode::Midi(track)) = self.tracks.get(&track_id) { - if let Some(ref graph) = track.instrument_graph { - let node_idx = petgraph::stable_graph::NodeIndex::new(node_id as usize); - return graph.get_oscilloscope_data(node_idx, sample_count); - } + let graph = &track.instrument_graph; + let node_idx = petgraph::stable_graph::NodeIndex::new(node_id as usize); + + // Get audio data + let audio = graph.get_oscilloscope_data(node_idx, sample_count)?; + + // Get CV data (may be empty if no CV input or not an oscilloscope node) + let cv = graph.get_oscilloscope_cv_data(node_idx, sample_count).unwrap_or_default(); + + return Some((audio, cv)); } None } @@ -244,44 +249,6 @@ impl Project { } } - /// Add an effect to a track (audio, MIDI, or group) - pub fn add_effect(&mut self, track_id: TrackId, effect: Box) -> Result<(), &'static str> { - match self.tracks.get_mut(&track_id) { - Some(TrackNode::Audio(track)) => { - track.add_effect(effect); - Ok(()) - } - Some(TrackNode::Midi(track)) => { - track.add_effect(effect); - Ok(()) - } - Some(TrackNode::Group(group)) => { - group.add_effect(effect); - Ok(()) - } - None => Err("Track not found"), - } - } - - /// Clear effects from a track - pub fn clear_effects(&mut self, track_id: TrackId) -> Result<(), &'static str> { - match self.tracks.get_mut(&track_id) { - Some(TrackNode::Audio(track)) => { - track.clear_effects(); - Ok(()) - } - Some(TrackNode::Midi(track)) => { - track.clear_effects(); - Ok(()) - } - Some(TrackNode::Group(group)) => { - group.clear_effects(); - Ok(()) - } - None => Err("Track not found"), - } - } - /// Render all root tracks into the output buffer pub fn render( &mut self, @@ -399,13 +366,8 @@ impl Project { ); } - // Apply group effects + // Apply group volume and mix into output if let Some(TrackNode::Group(group)) = self.tracks.get_mut(&track_id) { - for effect in &mut group.effects { - effect.process(&mut group_buffer, ctx.channels as usize, ctx.sample_rate); - } - - // Apply group volume and mix into output for (out_sample, group_sample) in output.iter_mut().zip(group_buffer.iter()) { *out_sample += group_sample * group.volume; } @@ -439,30 +401,21 @@ impl Project { } /// Send a live MIDI note on event to a track's instrument + /// Note: With node-based instruments, MIDI events are handled during the process() call pub fn send_midi_note_on(&mut self, track_id: TrackId, note: u8, velocity: u8) { + // Queue the MIDI note-on event to the track's live MIDI queue if let Some(TrackNode::Midi(track)) = self.tracks.get_mut(&track_id) { - // Create a MIDI event and queue it to the instrument - let event = crate::audio::midi::MidiEvent { - timestamp: 0, // Immediate playback - status: 0x90, // Note on - data1: note, - data2: velocity, - }; - track.instrument.queue_event(event); + let event = MidiEvent::note_on(0, 0, note, velocity); + track.queue_live_midi(event); } } /// Send a live MIDI note off event to a track's instrument pub fn send_midi_note_off(&mut self, track_id: TrackId, note: u8) { + // Queue the MIDI note-off event to the track's live MIDI queue if let Some(TrackNode::Midi(track)) = self.tracks.get_mut(&track_id) { - // Create a MIDI event and queue it to the instrument - let event = crate::audio::midi::MidiEvent { - timestamp: 0, // Immediate playback - status: 0x80, // Note off - data1: note, - data2: 0, - }; - track.instrument.queue_event(event); + let event = MidiEvent::note_off(0, 0, note, 0); + track.queue_live_midi(event); } } } diff --git a/daw-backend/src/audio/track.rs b/daw-backend/src/audio/track.rs index d71093a..6dbf31c 100644 --- a/daw-backend/src/audio/track.rs +++ b/daw-backend/src/audio/track.rs @@ -1,9 +1,8 @@ use super::automation::{AutomationLane, AutomationLaneId, ParameterId}; use super::clip::Clip; -use super::midi::MidiClip; +use super::midi::{MidiClip, MidiEvent}; use super::node_graph::InstrumentGraph; use super::pool::AudioPool; -use crate::effects::{Effect, SimpleSynth}; use std::collections::HashMap; /// Track ID type @@ -134,7 +133,6 @@ pub struct Metatrack { pub id: TrackId, pub name: String, pub children: Vec, - pub effects: Vec>, pub volume: f32, pub muted: bool, pub solo: bool, @@ -156,7 +154,6 @@ impl Metatrack { id, name, children: Vec::new(), - effects: Vec::new(), volume: 1.0, muted: false, solo: false, @@ -240,16 +237,6 @@ impl Metatrack { self.children.retain(|&id| id != track_id); } - /// Add an effect to the group's effect chain - pub fn add_effect(&mut self, effect: Box) { - self.effects.push(effect); - } - - /// Clear all effects from the group - pub fn clear_effects(&mut self) { - self.effects.clear(); - } - /// Set group volume pub fn set_volume(&mut self, volume: f32) { self.volume = volume.max(0.0); @@ -297,38 +284,40 @@ impl Metatrack { } } -/// MIDI track with MIDI clips and a virtual instrument +/// MIDI track with MIDI clips and a node-based instrument pub struct MidiTrack { pub id: TrackId, pub name: String, pub clips: Vec, - pub instrument: SimpleSynth, - pub effects: Vec>, + pub instrument_graph: InstrumentGraph, pub volume: f32, pub muted: bool, pub solo: bool, /// Automation lanes for this track pub automation_lanes: HashMap, next_automation_id: AutomationLaneId, - /// Optional instrument graph (replaces SimpleSynth when present) - pub instrument_graph: Option, + /// Queue for live MIDI input (virtual keyboard, MIDI controllers) + live_midi_queue: Vec, } impl MidiTrack { /// Create a new MIDI track with default settings pub fn new(id: TrackId, name: String) -> Self { + // Use default sample rate and a large buffer size that can accommodate any callback + let default_sample_rate = 48000; + let default_buffer_size = 8192; + Self { id, name, clips: Vec::new(), - instrument: SimpleSynth::new(), - effects: Vec::new(), + instrument_graph: InstrumentGraph::new(default_sample_rate, default_buffer_size), volume: 1.0, muted: false, solo: false, automation_lanes: HashMap::new(), next_automation_id: 0, - instrument_graph: None, + live_midi_queue: Vec::new(), } } @@ -357,16 +346,6 @@ impl MidiTrack { self.automation_lanes.remove(&lane_id).is_some() } - /// Add an effect to the track's effect chain - pub fn add_effect(&mut self, effect: Box) { - self.effects.push(effect); - } - - /// Clear all effects from the track - pub fn clear_effects(&mut self) { - self.effects.clear(); - } - /// Add a MIDI clip to this track pub fn add_clip(&mut self, clip: MidiClip) { self.clips.push(clip); @@ -393,8 +372,20 @@ impl MidiTrack { } /// Stop all currently playing notes on this track's instrument + /// Note: With node-based instruments, stopping is handled by ceasing MIDI input pub fn stop_all_notes(&mut self) { - self.instrument.all_notes_off(); + // No-op: Node-based instruments stop when they receive no MIDI input + // Individual synthesizer nodes handle note-off events appropriately + } + + /// Queue a live MIDI event (from virtual keyboard or MIDI controller) + pub fn queue_live_midi(&mut self, event: MidiEvent) { + self.live_midi_queue.push(event); + } + + /// Clear the live MIDI queue + pub fn clear_live_midi_queue(&mut self) { + self.live_midi_queue.clear(); } /// Process only live MIDI input (queued events) without rendering clips @@ -402,27 +393,14 @@ impl MidiTrack { pub fn process_live_input( &mut self, output: &mut [f32], - sample_rate: u32, - channels: u32, + _sample_rate: u32, + _channels: u32, ) { - // Generate audio - use instrument graph if available, otherwise SimpleSynth - if let Some(graph) = &mut self.instrument_graph { - // Get pending MIDI events from SimpleSynth (they're queued there by send_midi_note_on/off) - // We need to drain them so they're not processed again - let events: Vec = - self.instrument.pending_events.drain(..).collect(); + // Generate audio using instrument graph with live MIDI events + self.instrument_graph.process(output, &self.live_midi_queue); - // Process graph with MIDI events - graph.process(output, &events); - } else { - // Fallback to SimpleSynth (which processes queued events) - self.instrument.process(output, channels as usize, sample_rate); - } - - // Apply effect chain - for effect in &mut self.effects { - effect.process(output, channels as usize, sample_rate); - } + // Clear the queue after processing + self.live_midi_queue.clear(); // Apply track volume (no automation during live input) for sample in output.iter_mut() { @@ -455,22 +433,8 @@ impl MidiTrack { } } - // Generate audio - use instrument graph if available, otherwise SimpleSynth - if let Some(graph) = &mut self.instrument_graph { - // Use node graph for audio generation - graph.process(output, &midi_events); - } else { - // Fallback to SimpleSynth - for event in &midi_events { - self.instrument.queue_event(*event); - } - self.instrument.process(output, channels as usize, sample_rate); - } - - // Apply effect chain - for effect in &mut self.effects { - effect.process(output, channels as usize, sample_rate); - } + // Generate audio using instrument graph + self.instrument_graph.process(output, &midi_events); // Evaluate and apply automation let effective_volume = self.evaluate_automation_at_time(playhead_seconds); @@ -505,12 +469,11 @@ impl MidiTrack { } } -/// Audio track with clips and effect chain +/// Audio track with clips pub struct AudioTrack { pub id: TrackId, pub name: String, pub clips: Vec, - pub effects: Vec>, pub volume: f32, pub muted: bool, pub solo: bool, @@ -526,7 +489,6 @@ impl AudioTrack { id, name, clips: Vec::new(), - effects: Vec::new(), volume: 1.0, muted: false, solo: false, @@ -560,25 +522,6 @@ impl AudioTrack { self.automation_lanes.remove(&lane_id).is_some() } - /// Add an effect to the track's effect chain - pub fn add_effect(&mut self, effect: Box) { - self.effects.push(effect); - } - - /// Remove an effect from the chain by index - pub fn remove_effect(&mut self, index: usize) -> Option> { - if index < self.effects.len() { - Some(self.effects.remove(index)) - } else { - None - } - } - - /// Clear all effects from the track - pub fn clear_effects(&mut self) { - self.effects.clear(); - } - /// Add a clip to this track pub fn add_clip(&mut self, clip: Clip) { self.clips.push(clip); @@ -634,11 +577,6 @@ impl AudioTrack { } } - // Apply effect chain - for effect in &mut self.effects { - effect.process(output, channels as usize, sample_rate); - } - // Evaluate and apply automation let effective_volume = self.evaluate_automation_at_time(playhead_seconds); diff --git a/daw-backend/src/command/mod.rs b/daw-backend/src/command/mod.rs index 4143198..4dd1b68 100644 --- a/daw-backend/src/command/mod.rs +++ b/daw-backend/src/command/mod.rs @@ -1,3 +1,3 @@ pub mod types; -pub use types::{AudioEvent, Command, Query, QueryResponse}; +pub use types::{AudioEvent, Command, OscilloscopeData, Query, QueryResponse}; diff --git a/daw-backend/src/command/types.rs b/daw-backend/src/command/types.rs index 411194d..e7774ed 100644 --- a/daw-backend/src/command/types.rs +++ b/daw-backend/src/command/types.rs @@ -30,16 +30,6 @@ pub enum Command { /// Move a clip to a new timeline position MoveClip(TrackId, ClipId, f64), - // Effect management commands - /// Add or update gain effect on track (gain in dB) - AddGainEffect(TrackId, f32), - /// Add or update pan effect on track (-1.0 = left, 0.0 = center, 1.0 = right) - AddPanEffect(TrackId, f32), - /// Add or update EQ effect on track (low_db, mid_db, high_db) - AddEQEffect(TrackId, f32, f32, f32), - /// Clear all effects from a track - ClearEffects(TrackId), - // Metatrack management commands /// Create a new metatrack with a name CreateMetatrack(String), @@ -211,11 +201,20 @@ pub enum Query { GetOscilloscopeData(TrackId, u32, usize), } +/// Oscilloscope data from a node +#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] +pub struct OscilloscopeData { + /// Audio samples + pub audio: Vec, + /// CV samples (may be empty if no CV input) + pub cv: Vec, +} + /// Responses to synchronous queries #[derive(Debug)] pub enum QueryResponse { /// Graph state as JSON string GraphState(Result), /// Oscilloscope data samples - OscilloscopeData(Result, String>), + OscilloscopeData(Result), } diff --git a/daw-backend/src/lib.rs b/daw-backend/src/lib.rs index f7973bb..b0858d4 100644 --- a/daw-backend/src/lib.rs +++ b/daw-backend/src/lib.rs @@ -18,8 +18,7 @@ pub use audio::{ TrackNode, }; pub use audio::node_graph::{GraphPreset, InstrumentGraph, PresetMetadata, SerializedConnection, SerializedNode}; -pub use command::{AudioEvent, Command}; -pub use effects::{Effect, GainEffect, PanEffect, SimpleEQ, SimpleSynth}; +pub use command::{AudioEvent, Command, OscilloscopeData}; pub use io::{load_midi_file, AudioFile, WaveformPeak, WavWriter}; use cpal::traits::{DeviceTrait, HostTrait, StreamTrait}; diff --git a/src-tauri/src/audio.rs b/src-tauri/src/audio.rs index 2857eb0..86ba8a3 100644 --- a/src-tauri/src/audio.rs +++ b/src-tauri/src/audio.rs @@ -216,13 +216,6 @@ pub async fn audio_set_track_parameter( "volume" => controller.set_track_volume(track_id, value), "mute" => controller.set_track_mute(track_id, value > 0.5), "solo" => controller.set_track_solo(track_id, value > 0.5), - "pan" => { - // Pan effect - would need to add this via effects system - controller.add_pan_effect(track_id, value); - } - "gain_db" => { - controller.add_gain_effect(track_id, value); - } _ => return Err(format!("Unknown parameter: {}", parameter)), } Ok(()) @@ -231,19 +224,11 @@ pub async fn audio_set_track_parameter( } } -#[tauri::command] -pub async fn audio_get_available_instruments() -> Result, String> { - // Return list of available instruments - // For now, only SimpleSynth is available - Ok(vec!["SimpleSynth".to_string()]) -} - #[tauri::command] pub async fn audio_create_track( state: tauri::State<'_, Arc>>, name: String, track_type: String, - instrument: Option, ) -> Result { let mut audio_state = state.lock().unwrap(); @@ -254,14 +239,7 @@ pub async fn audio_create_track( if let Some(controller) = &mut audio_state.controller { match track_type.as_str() { "audio" => controller.create_audio_track(name), - "midi" => { - // Validate instrument for MIDI tracks - let inst = instrument.unwrap_or_else(|| "SimpleSynth".to_string()); - if inst != "SimpleSynth" { - return Err(format!("Unknown instrument: {}", inst)); - } - controller.create_midi_track(name) - }, + "midi" => controller.create_midi_track(name), _ => return Err(format!("Unknown track type: {}", track_type)), } Ok(track_id) @@ -1137,7 +1115,7 @@ pub async fn get_oscilloscope_data( track_id: u32, node_id: u32, sample_count: usize, -) -> Result, String> { +) -> Result { let mut audio_state = state.lock().unwrap(); if let Some(controller) = &mut audio_state.controller { diff --git a/src-tauri/src/lib.rs b/src-tauri/src/lib.rs index 98b02ff..d082f78 100644 --- a/src-tauri/src/lib.rs +++ b/src-tauri/src/lib.rs @@ -199,7 +199,6 @@ pub fn run() { audio::audio_seek, audio::audio_test_beep, audio::audio_set_track_parameter, - audio::audio_get_available_instruments, audio::audio_create_track, audio::audio_load_file, audio::audio_add_clip,