rewrite unsafe code in voice allocator

This commit is contained in:
Skyler Lehmkuhl 2026-02-15 23:22:36 -05:00
parent 72f10db64d
commit 06c5342724
2 changed files with 90 additions and 181 deletions

View File

@ -1531,12 +1531,8 @@ impl Engine {
// 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;
// Downcast to VoiceAllocatorNode using safe Any trait
if let Some(va_node) = node.as_any().downcast_ref::<VoiceAllocatorNode>() {
let template_preset = va_node.template_graph().to_preset(&preset_name);
// Write to file
@ -1558,12 +1554,8 @@ impl Engine {
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;
unsafe {
let sampler_node = &mut *node_ptr;
// Downcast to SimpleSamplerNode using safe Any trait
if let Some(sampler_node) = graph_node.node.as_any_mut().downcast_mut::<SimpleSamplerNode>() {
if let Err(e) = sampler_node.load_sample_from_file(&file_path) {
eprintln!("Failed to load sample: {}", e);
}
@ -1580,12 +1572,8 @@ impl Engine {
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;
unsafe {
let multi_sampler_node = &mut *node_ptr;
// Downcast to MultiSamplerNode using safe Any trait
if let Some(multi_sampler_node) = graph_node.node.as_any_mut().downcast_mut::<MultiSamplerNode>() {
if let Err(e) = multi_sampler_node.load_layer_from_file(&file_path, key_min, key_max, root_key, velocity_min, velocity_max, loop_start, loop_end, loop_mode) {
eprintln!("Failed to add sample layer: {}", e);
}
@ -1602,12 +1590,8 @@ impl Engine {
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;
unsafe {
let multi_sampler_node = &mut *node_ptr;
// Downcast to MultiSamplerNode using safe Any trait
if let Some(multi_sampler_node) = graph_node.node.as_any_mut().downcast_mut::<MultiSamplerNode>() {
if let Err(e) = multi_sampler_node.update_layer(layer_index, key_min, key_max, root_key, velocity_min, velocity_max, loop_start, loop_end, loop_mode) {
eprintln!("Failed to update sample layer: {}", e);
}
@ -1624,12 +1608,8 @@ impl Engine {
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;
unsafe {
let multi_sampler_node = &mut *node_ptr;
// Downcast to MultiSamplerNode using safe Any trait
if let Some(multi_sampler_node) = graph_node.node.as_any_mut().downcast_mut::<MultiSamplerNode>() {
if let Err(e) = multi_sampler_node.remove_layer(layer_index) {
eprintln!("Failed to remove sample layer: {}", e);
}
@ -1939,16 +1919,15 @@ impl Engine {
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;
// Downcast to VoiceAllocatorNode using safe Any trait
if let Some(va_node) = graph_node.node.as_any().downcast_ref::<VoiceAllocatorNode>() {
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("Node is not a VoiceAllocatorNode".to_string()))
}
} else {
QueryResponse::GraphState(Err("Voice allocator node not found".to_string()))

View File

@ -281,19 +281,10 @@ impl AudioGraph {
// This is tricky with trait objects, so we'll need to use Any
// For now, let's use a different approach - store the node pointer temporarily
// Check node type first
if graph_node.node.node_type() != "VoiceAllocator" {
return Err("Node is not a VoiceAllocator".to_string());
}
// Get mutable reference and downcast using raw pointers
let node_ptr = &mut *graph_node.node as *mut dyn AudioNode;
// SAFETY: We just checked that this is a VoiceAllocator
// This is safe because we know the concrete type
unsafe {
let va_ptr = node_ptr as *mut VoiceAllocatorNode;
let va = &mut *va_ptr;
// Downcast to VoiceAllocatorNode using safe Any trait
let va = graph_node.node.as_any_mut()
.downcast_mut::<VoiceAllocatorNode>()
.ok_or_else(|| "Node is not a VoiceAllocator".to_string())?;
// Add node to template graph
let node_idx = va.template_graph_mut().add_node(node);
@ -304,7 +295,6 @@ impl AudioGraph {
return Ok(node_id);
}
}
Err("VoiceAllocator node not found".to_string())
}
@ -322,18 +312,10 @@ impl AudioGraph {
// Get the VoiceAllocator node
if let Some(graph_node) = self.graph.node_weight_mut(voice_allocator_idx) {
// Check node type first
if graph_node.node.node_type() != "VoiceAllocator" {
return Err("Node is not a VoiceAllocator".to_string());
}
// Get mutable reference and downcast using raw pointers
let node_ptr = &mut *graph_node.node as *mut dyn AudioNode;
// SAFETY: We just checked that this is a VoiceAllocator
unsafe {
let va_ptr = node_ptr as *mut VoiceAllocatorNode;
let va = &mut *va_ptr;
// Downcast to VoiceAllocatorNode using safe Any trait
let va = graph_node.node.as_any_mut()
.downcast_mut::<VoiceAllocatorNode>()
.ok_or_else(|| "Node is not a VoiceAllocator".to_string())?;
// Connect in template graph
let from_idx = NodeIndex::new(from_node as usize);
@ -347,7 +329,6 @@ impl AudioGraph {
return Ok(());
}
}
Err("VoiceAllocator node not found".to_string())
}
@ -364,16 +345,10 @@ impl AudioGraph {
use crate::audio::node_graph::nodes::VoiceAllocatorNode;
if let Some(graph_node) = self.graph.node_weight_mut(voice_allocator_idx) {
if graph_node.node.node_type() != "VoiceAllocator" {
return Err("Node is not a VoiceAllocator".to_string());
}
let node_ptr = &mut *graph_node.node as *mut dyn AudioNode;
// SAFETY: We just checked that this is a VoiceAllocator
unsafe {
let va_ptr = node_ptr as *mut VoiceAllocatorNode;
let va = &mut *va_ptr;
// Downcast to VoiceAllocatorNode using safe Any trait
let va = graph_node.node.as_any_mut()
.downcast_mut::<VoiceAllocatorNode>()
.ok_or_else(|| "Node is not a VoiceAllocator".to_string())?;
let from_idx = NodeIndex::new(from_node as usize);
let to_idx = NodeIndex::new(to_node as usize);
@ -383,7 +358,6 @@ impl AudioGraph {
return Ok(());
}
}
Err("VoiceAllocator node not found".to_string())
}
@ -397,16 +371,10 @@ impl AudioGraph {
use crate::audio::node_graph::nodes::VoiceAllocatorNode;
if let Some(graph_node) = self.graph.node_weight_mut(voice_allocator_idx) {
if graph_node.node.node_type() != "VoiceAllocator" {
return Err("Node is not a VoiceAllocator".to_string());
}
let node_ptr = &mut *graph_node.node as *mut dyn AudioNode;
// SAFETY: We just checked that this is a VoiceAllocator
unsafe {
let va_ptr = node_ptr as *mut VoiceAllocatorNode;
let va = &mut *va_ptr;
// Downcast to VoiceAllocatorNode using safe Any trait
let va = graph_node.node.as_any_mut()
.downcast_mut::<VoiceAllocatorNode>()
.ok_or_else(|| "Node is not a VoiceAllocator".to_string())?;
let node_idx = NodeIndex::new(node_id as usize);
va.template_graph_mut().remove_node(node_idx);
@ -414,7 +382,6 @@ impl AudioGraph {
return Ok(());
}
}
Err("VoiceAllocator node not found".to_string())
}
@ -430,16 +397,10 @@ impl AudioGraph {
use crate::audio::node_graph::nodes::VoiceAllocatorNode;
if let Some(graph_node) = self.graph.node_weight_mut(voice_allocator_idx) {
if graph_node.node.node_type() != "VoiceAllocator" {
return Err("Node is not a VoiceAllocator".to_string());
}
let node_ptr = &mut *graph_node.node as *mut dyn AudioNode;
// SAFETY: We just checked that this is a VoiceAllocator
unsafe {
let va_ptr = node_ptr as *mut VoiceAllocatorNode;
let va = &mut *va_ptr;
// Downcast to VoiceAllocatorNode using safe Any trait
let va = graph_node.node.as_any_mut()
.downcast_mut::<VoiceAllocatorNode>()
.ok_or_else(|| "Node is not a VoiceAllocator".to_string())?;
let node_idx = NodeIndex::new(node_id as usize);
if let Some(template_node) = va.template_graph_mut().get_graph_node_mut(node_idx) {
@ -452,7 +413,6 @@ impl AudioGraph {
return Ok(());
}
}
Err("VoiceAllocator node not found".to_string())
}
@ -468,16 +428,8 @@ impl AudioGraph {
use crate::audio::node_graph::nodes::VoiceAllocatorNode;
if let Some(graph_node) = self.graph.node_weight_mut(voice_allocator_idx) {
if graph_node.node.node_type() != "VoiceAllocator" {
return;
}
let node_ptr = &mut *graph_node.node as *mut dyn AudioNode;
// SAFETY: We just checked that this is a VoiceAllocator
unsafe {
let va_ptr = node_ptr as *mut VoiceAllocatorNode;
let va = &mut *va_ptr;
// Downcast to VoiceAllocatorNode using safe Any trait
if let Some(va) = graph_node.node.as_any_mut().downcast_mut::<VoiceAllocatorNode>() {
let node_idx = NodeIndex::new(node_id as usize);
va.template_graph_mut().set_node_position(node_idx, x, y);
}
@ -655,30 +607,21 @@ impl AudioGraph {
let num_midi_outputs = outputs.iter().filter(|p| p.signal_type == SignalType::Midi).count();
// Create mutable slices for audio/CV outputs
let mut output_slices: Vec<&mut [f32]> = Vec::with_capacity(num_audio_cv_outputs);
for i in 0..num_audio_cv_outputs {
if i < node.output_buffers.len() {
// Safety: We need to work around borrowing rules here
// This is safe because each output buffer is independent
let buffer = &mut node.output_buffers[i] as *mut Vec<f32>;
unsafe {
let slice = &mut (&mut *buffer)[..process_size.min((*buffer).len())];
output_slices.push(slice);
}
}
}
// Each buffer is independent, so this is safe
let mut output_slices: Vec<&mut [f32]> = node.output_buffers
.iter_mut()
.take(num_audio_cv_outputs)
.map(|buf| {
let len = buf.len();
&mut buf[..process_size.min(len)]
})
.collect();
// Create mutable references for MIDI outputs
let mut midi_output_refs: Vec<&mut Vec<MidiEvent>> = Vec::with_capacity(num_midi_outputs);
for i in 0..num_midi_outputs {
if i < node.midi_output_buffers.len() {
// Safety: Similar to above
let buffer = &mut node.midi_output_buffers[i] as *mut Vec<MidiEvent>;
unsafe {
midi_output_refs.push(&mut *buffer);
}
}
}
let mut midi_output_refs: Vec<&mut Vec<MidiEvent>> = node.midi_output_buffers
.iter_mut()
.take(num_midi_outputs)
.collect();
// Process the node with both audio/CV and MIDI
node.node.process(&input_slices, &mut output_slices, &midi_input_slices, &mut midi_output_refs, self.sample_rate);
@ -811,14 +754,9 @@ impl AudioGraph {
}
// For VoiceAllocator nodes, serialize the template graph
// We need to downcast to access template_graph()
// This is safe because we know the node type
if node.node_type() == "VoiceAllocator" {
// Use Any to downcast
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;
// Downcast using safe Any trait
if let Some(va_node) = node.as_any().downcast_ref::<VoiceAllocatorNode>() {
let template_preset = va_node.template_graph().to_preset("template");
serialized.template_graph = Some(Box::new(template_preset));
}
@ -830,10 +768,8 @@ impl AudioGraph {
use crate::audio::node_graph::preset::{EmbeddedSampleData, SampleData};
use base64::{Engine as _, engine::general_purpose};
let node_ptr = &**node as *const dyn crate::audio::node_graph::AudioNode;
let node_ptr = node_ptr as *const SimpleSamplerNode;
unsafe {
let sampler_node = &*node_ptr;
// Downcast using safe Any trait
if let Some(sampler_node) = node.as_any().downcast_ref::<SimpleSamplerNode>() {
if let Some(sample_path) = sampler_node.get_sample_path() {
// Check file size
let should_embed = std::fs::metadata(sample_path)
@ -877,10 +813,8 @@ impl AudioGraph {
use crate::audio::node_graph::preset::{EmbeddedSampleData, LayerData, SampleData};
use base64::{Engine as _, engine::general_purpose};
let node_ptr = &**node as *const dyn crate::audio::node_graph::AudioNode;
let node_ptr = node_ptr as *const MultiSamplerNode;
unsafe {
let multi_sampler_node = &*node_ptr;
// Downcast using safe Any trait
if let Some(multi_sampler_node) = node.as_any().downcast_ref::<MultiSamplerNode>() {
let layers_info = multi_sampler_node.get_layers_info();
if !layers_info.is_empty() {
let layers: Vec<LayerData> = layers_info
@ -1070,10 +1004,8 @@ impl AudioGraph {
crate::audio::node_graph::preset::SampleData::SimpleSampler { file_path, embedded_data } => {
// Load sample into SimpleSampler
if let Some(graph_node) = graph.graph.node_weight_mut(node_idx) {
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;
// Downcast using safe Any trait
if let Some(sampler_node) = graph_node.node.as_any_mut().downcast_mut::<SimpleSamplerNode>() {
// Try embedded data first, then fall back to file path
if let Some(ref embedded) = embedded_data {
@ -1104,10 +1036,8 @@ impl AudioGraph {
crate::audio::node_graph::preset::SampleData::MultiSampler { layers } => {
// Load layers into MultiSampler
if let Some(graph_node) = graph.graph.node_weight_mut(node_idx) {
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;
// Downcast using safe Any trait
if let Some(multi_sampler_node) = graph_node.node.as_any_mut().downcast_mut::<MultiSamplerNode>() {
for layer in layers {
// Try embedded data first, then fall back to file path
if let Some(ref embedded) = layer.embedded_data {