capture 3 signals at once

This commit is contained in:
Weird Constructor 2022-07-25 06:15:48 +02:00
parent 64ece501ad
commit 8d0dbe797c
7 changed files with 82 additions and 38 deletions

View file

@ -1407,8 +1407,9 @@ macro_rules! node_list {
(0 atv n_id d_id r_id f_def stp_d -1.0, 1.0, 1.0)
[0 sig],
scope => Scope UIType::Generic UICategory::IOUtil
(0 inp n_id d_id r_id f_def stp_d -1.0, 1.0, 0.0)
[0 sig],
(0 in1 n_id d_id r_id f_def stp_d -1.0, 1.0, 0.0)
(0 in2 n_id d_id r_id f_def stp_d -1.0, 1.0, 0.0)
(0 in3 n_id d_id r_id f_def stp_d -1.0, 1.0, 0.0),
ad => Ad UIType::Generic UICategory::Mod
(0 inp n_id d_id r_id f_def stp_d -1.0, 1.0, 1.0)
(1 trig n_id d_id r_id f_def stp_d -1.0, 1.0, 0.0)

View file

@ -3,38 +3,42 @@
// See README.md and COPYING for details.
//use super::helpers::{sqrt4_to_pow4, TrigSignal, Trigger};
use crate::dsp::{
DspNode, LedPhaseVals, NodeContext, NodeId, ProcBuf, SAtom,
};
use crate::nodes::SCOPE_SAMPLES;
use crate::dsp::{DspNode, LedPhaseVals, NodeContext, NodeId, ProcBuf, SAtom};
use crate::nodes::{NodeAudioContext, NodeExecContext};
use crate::UnsyncFloatBuf;
/// A simple signal scope
#[derive(Debug, Clone)]
pub struct Scope {
buf: UnsyncFloatBuf,
buf: [UnsyncFloatBuf; 3],
idx: usize,
}
impl Scope {
pub fn new(_nid: &NodeId) -> Self {
Self { buf: UnsyncFloatBuf::new_with_len(1), idx: 0 }
let buf = [
UnsyncFloatBuf::new_with_len(1),
UnsyncFloatBuf::new_with_len(1),
UnsyncFloatBuf::new_with_len(1),
];
Self { buf, idx: 0 }
}
pub const inp: &'static str = "Scope inp\nSignal input.\nRange: (-1..1)\n";
pub const sig: &'static str =
"Scope sig\nSignal output. The exact same signal that was received on 'inp'!\n";
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";
pub const in3: &'static str = "Scope in3\nSignal input 3.\nRange: (-1..1)\n";
pub const DESC: &'static str = r#"Signal Oscilloscope Probe
This is a signal oscilloscope probe node. You can have up to 16 of these,
which will be displayed in the GUI.
This is a signal oscilloscope probe node, you can capture up to 3 signals.
"#;
pub const HELP: &'static str = r#"Scope - Signal Oscilloscope Probe
You can have up to 16 of these probes in your patch. The received signal will be
forwarded to the GUI and you can inspect the waveform there.
You can have up to 8 different scopes in your patch. That means you can in theory
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_buffer(&mut self, buf: UnsyncFloatBuf) {
pub fn set_scope_buffers(&mut self, buf: [UnsyncFloatBuf; 3]) {
self.buf = buf;
}
}
@ -61,17 +65,23 @@ impl DspNode for Scope {
) {
use crate::dsp::{inp, out};
let inp = inp::Ad::inp(inputs);
let out = out::Scope::sig(outputs);
let in1 = inp::Scope::in1(inputs);
let in2 = inp::Scope::in2(inputs);
let in3 = inp::Scope::in3(inputs);
let inputs = [in1, in2, in3];
for frame in 0..ctx.nframes() {
let in_val = inp.read(frame);
self.buf.write(self.idx, in_val);
self.idx = (self.idx + 1) % self.buf.len();
out.write(frame, in_val);
for (i, input) in inputs.iter().enumerate() {
let in_val = input.read(frame);
self.buf[i].write(self.idx, in_val);
}
self.idx = (self.idx + 1) % SCOPE_SAMPLES;
}
let last_frame = ctx.nframes() - 1;
ctx_vals[0].set(out.read(last_frame));
ctx_vals[0].set(
(in1.read(last_frame) + in2.read(last_frame) + in3.read(last_frame)).clamp(-1.0, 1.0),
);
}
}

View file

@ -582,8 +582,8 @@ impl Matrix {
self.config.get_pattern_data(tracker_id)
}
pub fn get_scope_buffer(&self, scope: usize) -> Option<UnsyncFloatBuf> {
self.config.get_scope_buffer(scope)
pub fn get_scope_buffers(&self, scope: usize) -> Option<[UnsyncFloatBuf; 3]> {
self.config.get_scope_buffers(scope)
}
/// Checks if pattern data updates need to be sent to the

View file

@ -3,7 +3,7 @@
// See README.md and COPYING for details.
pub const MAX_ALLOCATED_NODES: usize = 256;
pub const MAX_SCOPES: usize = 16;
pub const MAX_SCOPES: usize = 8;
pub const SCOPE_SAMPLES: usize = 512;
pub const MAX_INPUTS: usize = 32;
pub const MAX_SMOOTHERS: usize = 36 + 4; // 6 * 6 modulator inputs + 4 UI Knobs

View file

@ -3,15 +3,15 @@
// See README.md and COPYING for details.
use super::{
FeedbackFilter, GraphMessage, NodeOp, NodeProg, MAX_ALLOCATED_NODES,
MAX_AVAIL_TRACKERS, MAX_INPUTS, UNUSED_MONITOR_IDX, MAX_SCOPES, SCOPE_SAMPLES
FeedbackFilter, GraphMessage, NodeOp, NodeProg, MAX_ALLOCATED_NODES, MAX_AVAIL_TRACKERS,
MAX_INPUTS, MAX_SCOPES, SCOPE_SAMPLES, 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::util::AtomicFloat;
use crate::unsync_float_buf::UnsyncFloatBuf;
use crate::util::AtomicFloat;
use crate::SampleLibrary;
use ringbuf::{Producer, RingBuffer};
@ -179,7 +179,7 @@ pub struct NodeConfigurator {
/// Holding the tracker sequencers
pub(crate) trackers: Vec<Tracker>,
/// Holding the scope buffers:
pub(crate) scopes: Vec<UnsyncFloatBuf>,
pub(crate) scopes: Vec<[UnsyncFloatBuf; 3]>,
/// The shared parts of the [NodeConfigurator]
/// and the [crate::nodes::NodeExecutor].
pub(crate) shared: SharedNodeConf,
@ -282,7 +282,14 @@ 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); MAX_SCOPES],
scopes: vec![
[
UnsyncFloatBuf::new_with_len(SCOPE_SAMPLES),
UnsyncFloatBuf::new_with_len(SCOPE_SAMPLES),
UnsyncFloatBuf::new_with_len(SCOPE_SAMPLES)
];
MAX_SCOPES
],
},
shared_exec,
)
@ -642,7 +649,7 @@ impl NodeConfigurator {
}
}
pub fn get_scope_buffer(&self, scope: usize) -> Option<UnsyncFloatBuf> {
pub fn get_scope_buffers(&self, scope: usize) -> Option<[UnsyncFloatBuf; 3]> {
self.scopes.get(scope).cloned()
}
@ -687,7 +694,7 @@ impl NodeConfigurator {
if let Node::Scope { node } = &mut node {
if let Some(buf) = self.scopes.get(ni.instance()) {
node.set_scope_buffer(buf.clone());
node.set_scope_buffers(buf.clone());
}
}

View file

@ -54,6 +54,10 @@ impl UnsyncFloatBuf {
}
}
/// 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<AtomicFloat>,
@ -65,18 +69,23 @@ 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<Self> {
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));
// Taking the pointer to the Vec data buffer is fine,
// 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 {
@ -85,6 +94,7 @@ impl UnsyncFloatBufImpl {
}
}
/// Read a sample.
fn read(&self, idx: usize) -> f32 {
if idx < self.len {
unsafe { (*self.ptr.add(idx)).get() }
@ -93,6 +103,7 @@ impl UnsyncFloatBufImpl {
}
}
/// Return the length of this buffer.
fn len(&self) -> usize {
self.len
}

View file

@ -13,20 +13,35 @@ fn check_node_scope_1() {
let mut matrix = Matrix::new(node_conf, 3, 3);
let mut chain = MatrixCellChain::new(CellDir::B);
chain.node_inp("scope", "inp").place(&mut matrix, 0, 0).unwrap();
chain.node_inp("scope", "in1").place(&mut matrix, 0, 0).unwrap();
matrix.sync().unwrap();
let scope = NodeId::Scope(0);
let inp_p = scope.inp_param("inp").unwrap();
let in1_p = scope.inp_param("in1").unwrap();
let in2_p = scope.inp_param("in2").unwrap();
let in3_p = scope.inp_param("in3").unwrap();
matrix.set_param(inp_p, SAtom::param(1.0));
matrix.set_param(in1_p, SAtom::param(1.0));
matrix.set_param(in2_p, SAtom::param(1.0));
matrix.set_param(in3_p, SAtom::param(1.0));
let _res = run_for_ms(&mut node_exec, 11.0);
let scope = matrix.get_scope_buffer(0).unwrap();
let scope = matrix.get_scope_buffers(0).unwrap();
let mut v = vec![];
for x in 0..SCOPE_SAMPLES {
v.push(scope.read(x));
v.push(scope[0].read(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));
}
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));
}
assert_decimated_feq!(v, 80, vec![0.0022, 0.1836, 0.3650, 0.5464, 0.7278, 0.9093, 1.0]);
}