From 544c0084ffc22a794b7a36e52c85fab8fa1e18b8 Mon Sep 17 00:00:00 2001 From: Weird Constructor Date: Mon, 25 Jul 2022 06:36:07 +0200 Subject: [PATCH] Get rid of the weird thingie... --- src/dsp/node_scope.rs | 28 ++++---- src/lib.rs | 4 +- src/matrix.rs | 6 +- src/nodes/node_conf.rs | 24 +++---- src/scope_handle.rs | 51 ++++++++++++++ src/unsync_float_buf.rs | 149 ---------------------------------------- tests/node_scope.rs | 8 +-- 7 files changed, 83 insertions(+), 187 deletions(-) create mode 100644 src/scope_handle.rs delete mode 100644 src/unsync_float_buf.rs diff --git a/src/dsp/node_scope.rs b/src/dsp/node_scope.rs index 0a31119..5c31070 100644 --- a/src/dsp/node_scope.rs +++ b/src/dsp/node_scope.rs @@ -3,26 +3,22 @@ // See README.md and COPYING for details. //use super::helpers::{sqrt4_to_pow4, TrigSignal, Trigger}; -use crate::nodes::SCOPE_SAMPLES; use crate::dsp::{DspNode, LedPhaseVals, NodeContext, NodeId, ProcBuf, SAtom}; +use crate::nodes::SCOPE_SAMPLES; use crate::nodes::{NodeAudioContext, NodeExecContext}; -use crate::UnsyncFloatBuf; +use crate::ScopeHandle; +use std::sync::Arc; /// A simple signal scope #[derive(Debug, Clone)] pub struct Scope { - buf: [UnsyncFloatBuf; 3], + handle: Arc, idx: usize, } impl Scope { pub fn new(_nid: &NodeId) -> Self { - let buf = [ - UnsyncFloatBuf::new_with_len(1), - UnsyncFloatBuf::new_with_len(1), - UnsyncFloatBuf::new_with_len(1), - ]; - Self { buf, idx: 0 } + Self { handle: ScopeHandle::new_shared(), idx: 0 } } pub const in1: &'static str = "Scope in1\nSignal input 1.\nRange: (-1..1)\n"; pub const in2: &'static str = "Scope in2\nSignal input 2.\nRange: (-1..1)\n"; @@ -38,8 +34,8 @@ record up to 24 signals. The received signal will be forwarded to the GUI and you can inspect the waveform there. "#; - pub fn set_scope_buffers(&mut self, buf: [UnsyncFloatBuf; 3]) { - self.buf = buf; + pub fn set_scope_handle(&mut self, handle: Arc) { + self.handle = handle; } } @@ -57,23 +53,25 @@ impl DspNode for Scope { &mut self, ctx: &mut T, _ectx: &mut NodeExecContext, - _nctx: &NodeContext, + nctx: &NodeContext, _atoms: &[SAtom], inputs: &[ProcBuf], - outputs: &mut [ProcBuf], + _outputs: &mut [ProcBuf], ctx_vals: LedPhaseVals, ) { - use crate::dsp::{inp, out}; + use crate::dsp::inp; let in1 = inp::Scope::in1(inputs); let in2 = inp::Scope::in2(inputs); let in3 = inp::Scope::in3(inputs); let inputs = [in1, in2, in3]; + self.handle.set_active_from_mask(nctx.in_connected); + for frame in 0..ctx.nframes() { for (i, input) in inputs.iter().enumerate() { let in_val = input.read(frame); - self.buf[i].write(self.idx, in_val); + self.handle.write(i, self.idx, in_val); } self.idx = (self.idx + 1) % SCOPE_SAMPLES; diff --git a/src/lib.rs b/src/lib.rs index fb81ad0..dd03d86 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -312,7 +312,7 @@ pub mod matrix_repr; pub mod monitor; pub mod nodes; pub mod sample_lib; -pub mod unsync_float_buf; +pub mod scope_handle; mod util; pub use cell_dir::CellDir; @@ -324,7 +324,7 @@ pub use matrix_repr::load_patch_from_file; pub use matrix_repr::save_patch_to_file; pub use nodes::{new_node_engine, NodeConfigurator, NodeExecutor}; pub use sample_lib::{SampleLibrary, SampleLoadError}; -pub use unsync_float_buf::UnsyncFloatBuf; +pub use scope_handle::ScopeHandle; pub struct Context<'a, 'b, 'c, 'd> { pub nframes: usize, diff --git a/src/matrix.rs b/src/matrix.rs index 6e30476..1e1a408 100644 --- a/src/matrix.rs +++ b/src/matrix.rs @@ -9,7 +9,7 @@ pub use crate::monitor::MON_SIG_CNT; pub use crate::nodes::MinMaxMonitorSamples; use crate::nodes::{NodeConfigurator, NodeGraphOrdering, NodeProg, MAX_ALLOCATED_NODES}; pub use crate::CellDir; -use crate::UnsyncFloatBuf; +use crate::ScopeHandle; use std::collections::{HashMap, HashSet}; @@ -582,8 +582,8 @@ impl Matrix { self.config.get_pattern_data(tracker_id) } - pub fn get_scope_buffers(&self, scope: usize) -> Option<[UnsyncFloatBuf; 3]> { - self.config.get_scope_buffers(scope) + pub fn get_scope_handle(&self, scope: usize) -> Option> { + self.config.get_scope_handle(scope) } /// Checks if pattern data updates need to be sent to the diff --git a/src/nodes/node_conf.rs b/src/nodes/node_conf.rs index 25bfff6..b9c8dad 100644 --- a/src/nodes/node_conf.rs +++ b/src/nodes/node_conf.rs @@ -4,15 +4,15 @@ use super::{ FeedbackFilter, GraphMessage, NodeOp, NodeProg, MAX_ALLOCATED_NODES, MAX_AVAIL_TRACKERS, - MAX_INPUTS, MAX_SCOPES, SCOPE_SAMPLES, UNUSED_MONITOR_IDX, + MAX_INPUTS, MAX_SCOPES, UNUSED_MONITOR_IDX, }; use crate::dsp::tracker::{PatternData, Tracker}; use crate::dsp::{node_factory, Node, NodeId, NodeInfo, ParamId, SAtom}; use crate::monitor::{new_monitor_processor, MinMaxMonitorSamples, Monitor, MON_SIG_CNT}; use crate::nodes::drop_thread::DropThread; -use crate::unsync_float_buf::UnsyncFloatBuf; use crate::util::AtomicFloat; use crate::SampleLibrary; +use crate::ScopeHandle; use ringbuf::{Producer, RingBuffer}; use std::collections::HashMap; @@ -179,7 +179,7 @@ pub struct NodeConfigurator { /// Holding the tracker sequencers pub(crate) trackers: Vec, /// Holding the scope buffers: - pub(crate) scopes: Vec<[UnsyncFloatBuf; 3]>, + pub(crate) scopes: Vec>, /// The shared parts of the [NodeConfigurator] /// and the [crate::nodes::NodeExecutor]. pub(crate) shared: SharedNodeConf, @@ -266,6 +266,9 @@ impl NodeConfigurator { let (shared, shared_exec) = SharedNodeConf::new(); + let mut scopes = vec![]; + scopes.resize_with(MAX_SCOPES, || ScopeHandle::new_shared()); + ( NodeConfigurator { nodes, @@ -282,14 +285,7 @@ impl NodeConfigurator { atom_values: std::collections::HashMap::new(), node2idx: HashMap::new(), trackers: vec![Tracker::new(); MAX_AVAIL_TRACKERS], - scopes: vec![ - [ - UnsyncFloatBuf::new_with_len(SCOPE_SAMPLES), - UnsyncFloatBuf::new_with_len(SCOPE_SAMPLES), - UnsyncFloatBuf::new_with_len(SCOPE_SAMPLES) - ]; - MAX_SCOPES - ], + scopes, }, shared_exec, ) @@ -649,7 +645,7 @@ impl NodeConfigurator { } } - pub fn get_scope_buffers(&self, scope: usize) -> Option<[UnsyncFloatBuf; 3]> { + pub fn get_scope_handle(&self, scope: usize) -> Option> { self.scopes.get(scope).cloned() } @@ -693,8 +689,8 @@ impl NodeConfigurator { } if let Node::Scope { node } = &mut node { - if let Some(buf) = self.scopes.get(ni.instance()) { - node.set_scope_buffers(buf.clone()); + if let Some(handle) = self.scopes.get(ni.instance()) { + node.set_scope_handle(handle.clone()); } } diff --git a/src/scope_handle.rs b/src/scope_handle.rs new file mode 100644 index 0000000..fbc322b --- /dev/null +++ b/src/scope_handle.rs @@ -0,0 +1,51 @@ +// Copyright (c) 2022 Weird Constructor +// This file is a part of HexoDSP. Released under GPL-3.0-or-later. +// See README.md and COPYING for details. + +use crate::nodes::SCOPE_SAMPLES; +use crate::util::AtomicFloat; +use std::sync::atomic::{AtomicBool, Ordering}; +use std::sync::Arc; + +#[derive(Debug)] +pub struct ScopeHandle { + bufs: [Vec; 3], + active: [AtomicBool; 3], +} + +impl ScopeHandle { + pub fn new_shared() -> Arc { + let mut v1 = vec![]; + v1.resize_with(SCOPE_SAMPLES, || AtomicFloat::new(0.0)); + let mut v2 = vec![]; + v2.resize_with(SCOPE_SAMPLES, || AtomicFloat::new(0.0)); + let mut v3 = vec![]; + v3.resize_with(SCOPE_SAMPLES, || AtomicFloat::new(0.0)); + Arc::new(Self { + bufs: [v1, v2, v3], + active: [AtomicBool::new(false), AtomicBool::new(false), AtomicBool::new(false)], + }) + } + + pub fn write(&self, buf_idx: usize, idx: usize, v: f32) { + self.bufs[buf_idx % 3][idx % SCOPE_SAMPLES].set(v); + } + + pub fn read(&self, buf_idx: usize, idx: usize) -> f32 { + self.bufs[buf_idx % 3][idx % SCOPE_SAMPLES].get() + } + + pub fn set_active_from_mask(&self, mask: u64) { + self.active[0].store(mask & 0x1 > 0x0, Ordering::Relaxed); + self.active[1].store(mask & 0x2 > 0x0, Ordering::Relaxed); + self.active[2].store(mask & 0x4 > 0x0, Ordering::Relaxed); + } + + pub fn is_active(&self, idx: usize) -> bool { + self.active[idx % 3].load(Ordering::Relaxed) + } + + pub fn len(&self) -> usize { + SCOPE_SAMPLES + } +} diff --git a/src/unsync_float_buf.rs b/src/unsync_float_buf.rs deleted file mode 100644 index 8449921..0000000 --- a/src/unsync_float_buf.rs +++ /dev/null @@ -1,149 +0,0 @@ -// Copyright (c) 2022 Weird Constructor -// This file is a part of HexoDSP. Released under GPL-3.0-or-later. -// See README.md and COPYING for details. - -use crate::util::AtomicFloat; -use std::sync::Arc; - -/// A float buffer that can be written to and read from in an unsynchronized manner. -/// -/// One use case is writing samples to this buffer in the audio thread while -/// a GUI thread reads from this buffer. Mostly useful for an oscilloscope. -/// -///``` -/// use hexodsp::UnsyncFloatBuf; -/// -/// let handle1 = UnsyncFloatBuf::new_with_len(10); -/// let handle2 = handle1.clone(); -/// -/// std::thread::spawn(move || { -/// handle1.write(9, 2032.0); -/// }).join().unwrap(); -/// -/// std::thread::spawn(move || { -/// assert_eq!(handle2.read(9), 2032.0); -/// assert_eq!(handle2.read(20), 0.0); // out of range! -/// }).join().unwrap(); -///``` -#[derive(Debug, Clone)] -pub struct UnsyncFloatBuf(Arc); - -impl UnsyncFloatBuf { - /// Creates a new unsynchronized float buffer with the given length. - pub fn new_with_len(len: usize) -> Self { - Self(UnsyncFloatBufImpl::new_shared(len)) - } - - /// Write float to the given index. - /// - /// If index is out of range, nothing will be written. - pub fn write(&self, idx: usize, v: f32) { - self.0.write(idx, v) - } - - /// Reads a float from the given index. - /// - /// If index is out of range, 0.0 will be returned. - pub fn read(&self, idx: usize) -> f32 { - self.0.read(idx) - } - - /// Length of the buffer. - pub fn len(&self) -> usize { - self.0.len() - } -} - -/// Private implementation detail for [UnsyncFloatBuf]. -/// -/// This mostly allows [UnsyncFloatBuf] to wrap UnsyncFloatBufImpl into an [std::sync::Arc], -/// to make sure the `data_store` Vector is not moved accidentally. -#[derive(Debug)] -struct UnsyncFloatBufImpl { - data_store: Vec, - len: usize, - ptr: *mut AtomicFloat, -} - -unsafe impl Sync for UnsyncFloatBuf {} -unsafe impl Send for UnsyncFloatBuf {} - -impl UnsyncFloatBufImpl { - /// Create a new shared reference of this. You must not create - /// an UnsyncFloatBufImpl that can move! Otherwise the internal pointer - /// would be invalidated. - fn new_shared(len: usize) -> Arc { - let mut rc = Arc::new(Self { data_store: Vec::new(), len, ptr: std::ptr::null_mut() }); - - let mut unsync_buf = Arc::get_mut(&mut rc).expect("No other reference to this Arc"); - unsync_buf.data_store.resize_with(len, || AtomicFloat::new(0.0)); - - // XXX: Taking the pointer to the Vec data buffer is fine, - // because it will not be moved when inside the Arc. - unsync_buf.ptr = unsync_buf.data_store.as_mut_ptr(); - - rc - } - - /// Write a sample. - fn write(&self, idx: usize, v: f32) { - if idx < self.len { - unsafe { - (*self.ptr.add(idx)).set(v); - } - } - } - - /// Read a sample. - fn read(&self, idx: usize) -> f32 { - if idx < self.len { - unsafe { (*self.ptr.add(idx)).get() } - } else { - 0.0 - } - } - - /// Return the length of this buffer. - fn len(&self) -> usize { - self.len - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn check_unsync_float_buf_working() { - let handle1 = UnsyncFloatBuf::new_with_len(512); - for i in 0..512 { - handle1.write(i, i as f32); - } - let handle2 = handle1.clone(); - for i in 0..512 { - assert_eq!(handle2.read(i), i as f32); - } - } - - #[test] - fn check_unsync_float_buf_thread() { - let handle1 = UnsyncFloatBuf::new_with_len(512); - let handle2 = handle1.clone(); - - std::thread::spawn(move || { - for i in 0..512 { - handle1.write(i, i as f32); - } - }) - .join() - .unwrap(); - - std::thread::spawn(move || { - for i in 0..512 { - assert_eq!(handle2.read(i), i as f32); - } - }) - .join() - .unwrap(); - } -} diff --git a/tests/node_scope.rs b/tests/node_scope.rs index 6523050..b2a1769 100644 --- a/tests/node_scope.rs +++ b/tests/node_scope.rs @@ -26,22 +26,22 @@ fn check_node_scope_1() { matrix.set_param(in3_p, SAtom::param(1.0)); let _res = run_for_ms(&mut node_exec, 11.0); - let scope = matrix.get_scope_buffers(0).unwrap(); + let scope = matrix.get_scope_handle(0).unwrap(); let mut v = vec![]; for x in 0..SCOPE_SAMPLES { - v.push(scope[0].read(x)); + v.push(scope.read(0, x)); } assert_decimated_feq!(v, 80, vec![0.0022, 0.1836, 0.3650, 0.5464, 0.7278, 0.9093, 1.0]); let mut v = vec![]; for x in 0..SCOPE_SAMPLES { - v.push(scope[1].read(x)); + v.push(scope.read(1, x)); } assert_decimated_feq!(v, 80, vec![0.0022, 0.1836, 0.3650, 0.5464, 0.7278, 0.9093, 1.0]); let mut v = vec![]; for x in 0..SCOPE_SAMPLES { - v.push(scope[2].read(x)); + v.push(scope.read(2, x)); } assert_decimated_feq!(v, 80, vec![0.0022, 0.1836, 0.3650, 0.5464, 0.7278, 0.9093, 1.0]); }