capture 3 signals at once
This commit is contained in:
parent
64ece501ad
commit
8d0dbe797c
7 changed files with 82 additions and 38 deletions
|
@ -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 atv n_id d_id r_id f_def stp_d -1.0, 1.0, 1.0)
|
||||||
[0 sig],
|
[0 sig],
|
||||||
scope => Scope UIType::Generic UICategory::IOUtil
|
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 in1 n_id d_id r_id f_def stp_d -1.0, 1.0, 0.0)
|
||||||
[0 sig],
|
(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
|
ad => Ad UIType::Generic UICategory::Mod
|
||||||
(0 inp n_id d_id r_id f_def stp_d -1.0, 1.0, 1.0)
|
(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)
|
(1 trig n_id d_id r_id f_def stp_d -1.0, 1.0, 0.0)
|
||||||
|
|
|
@ -3,38 +3,42 @@
|
||||||
// See README.md and COPYING for details.
|
// See README.md and COPYING for details.
|
||||||
|
|
||||||
//use super::helpers::{sqrt4_to_pow4, TrigSignal, Trigger};
|
//use super::helpers::{sqrt4_to_pow4, TrigSignal, Trigger};
|
||||||
use crate::dsp::{
|
use crate::nodes::SCOPE_SAMPLES;
|
||||||
DspNode, LedPhaseVals, NodeContext, NodeId, ProcBuf, SAtom,
|
use crate::dsp::{DspNode, LedPhaseVals, NodeContext, NodeId, ProcBuf, SAtom};
|
||||||
};
|
|
||||||
use crate::nodes::{NodeAudioContext, NodeExecContext};
|
use crate::nodes::{NodeAudioContext, NodeExecContext};
|
||||||
use crate::UnsyncFloatBuf;
|
use crate::UnsyncFloatBuf;
|
||||||
|
|
||||||
/// A simple signal scope
|
/// A simple signal scope
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct Scope {
|
pub struct Scope {
|
||||||
buf: UnsyncFloatBuf,
|
buf: [UnsyncFloatBuf; 3],
|
||||||
idx: usize,
|
idx: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Scope {
|
impl Scope {
|
||||||
pub fn new(_nid: &NodeId) -> Self {
|
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 in1: &'static str = "Scope in1\nSignal input 1.\nRange: (-1..1)\n";
|
||||||
pub const sig: &'static str =
|
pub const in2: &'static str = "Scope in2\nSignal input 2.\nRange: (-1..1)\n";
|
||||||
"Scope sig\nSignal output. The exact same signal that was received on 'inp'!\n";
|
pub const in3: &'static str = "Scope in3\nSignal input 3.\nRange: (-1..1)\n";
|
||||||
pub const DESC: &'static str = r#"Signal Oscilloscope Probe
|
pub const DESC: &'static str = r#"Signal Oscilloscope Probe
|
||||||
|
|
||||||
This is a signal oscilloscope probe node. You can have up to 16 of these,
|
This is a signal oscilloscope probe node, you can capture up to 3 signals.
|
||||||
which will be displayed in the GUI.
|
|
||||||
"#;
|
"#;
|
||||||
pub const HELP: &'static str = r#"Scope - Signal Oscilloscope Probe
|
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
|
You can have up to 8 different scopes in your patch. That means you can in theory
|
||||||
forwarded to the GUI and you can inspect the waveform there.
|
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;
|
self.buf = buf;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -61,17 +65,23 @@ impl DspNode for Scope {
|
||||||
) {
|
) {
|
||||||
use crate::dsp::{inp, out};
|
use crate::dsp::{inp, out};
|
||||||
|
|
||||||
let inp = inp::Ad::inp(inputs);
|
let in1 = inp::Scope::in1(inputs);
|
||||||
let out = out::Scope::sig(outputs);
|
let in2 = inp::Scope::in2(inputs);
|
||||||
|
let in3 = inp::Scope::in3(inputs);
|
||||||
|
let inputs = [in1, in2, in3];
|
||||||
|
|
||||||
for frame in 0..ctx.nframes() {
|
for frame in 0..ctx.nframes() {
|
||||||
let in_val = inp.read(frame);
|
for (i, input) in inputs.iter().enumerate() {
|
||||||
self.buf.write(self.idx, in_val);
|
let in_val = input.read(frame);
|
||||||
self.idx = (self.idx + 1) % self.buf.len();
|
self.buf[i].write(self.idx, in_val);
|
||||||
out.write(frame, in_val);
|
}
|
||||||
|
|
||||||
|
self.idx = (self.idx + 1) % SCOPE_SAMPLES;
|
||||||
}
|
}
|
||||||
|
|
||||||
let last_frame = ctx.nframes() - 1;
|
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),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -582,8 +582,8 @@ impl Matrix {
|
||||||
self.config.get_pattern_data(tracker_id)
|
self.config.get_pattern_data(tracker_id)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_scope_buffer(&self, scope: usize) -> Option<UnsyncFloatBuf> {
|
pub fn get_scope_buffers(&self, scope: usize) -> Option<[UnsyncFloatBuf; 3]> {
|
||||||
self.config.get_scope_buffer(scope)
|
self.config.get_scope_buffers(scope)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Checks if pattern data updates need to be sent to the
|
/// Checks if pattern data updates need to be sent to the
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
// See README.md and COPYING for details.
|
// See README.md and COPYING for details.
|
||||||
|
|
||||||
pub const MAX_ALLOCATED_NODES: usize = 256;
|
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 SCOPE_SAMPLES: usize = 512;
|
||||||
pub const MAX_INPUTS: usize = 32;
|
pub const MAX_INPUTS: usize = 32;
|
||||||
pub const MAX_SMOOTHERS: usize = 36 + 4; // 6 * 6 modulator inputs + 4 UI Knobs
|
pub const MAX_SMOOTHERS: usize = 36 + 4; // 6 * 6 modulator inputs + 4 UI Knobs
|
||||||
|
|
|
@ -3,15 +3,15 @@
|
||||||
// See README.md and COPYING for details.
|
// See README.md and COPYING for details.
|
||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
FeedbackFilter, GraphMessage, NodeOp, NodeProg, MAX_ALLOCATED_NODES,
|
FeedbackFilter, GraphMessage, NodeOp, NodeProg, MAX_ALLOCATED_NODES, MAX_AVAIL_TRACKERS,
|
||||||
MAX_AVAIL_TRACKERS, MAX_INPUTS, UNUSED_MONITOR_IDX, MAX_SCOPES, SCOPE_SAMPLES
|
MAX_INPUTS, MAX_SCOPES, SCOPE_SAMPLES, UNUSED_MONITOR_IDX,
|
||||||
};
|
};
|
||||||
use crate::dsp::tracker::{PatternData, Tracker};
|
use crate::dsp::tracker::{PatternData, Tracker};
|
||||||
use crate::dsp::{node_factory, Node, NodeId, NodeInfo, ParamId, SAtom};
|
use crate::dsp::{node_factory, Node, NodeId, NodeInfo, ParamId, SAtom};
|
||||||
use crate::monitor::{new_monitor_processor, MinMaxMonitorSamples, Monitor, MON_SIG_CNT};
|
use crate::monitor::{new_monitor_processor, MinMaxMonitorSamples, Monitor, MON_SIG_CNT};
|
||||||
use crate::nodes::drop_thread::DropThread;
|
use crate::nodes::drop_thread::DropThread;
|
||||||
use crate::util::AtomicFloat;
|
|
||||||
use crate::unsync_float_buf::UnsyncFloatBuf;
|
use crate::unsync_float_buf::UnsyncFloatBuf;
|
||||||
|
use crate::util::AtomicFloat;
|
||||||
use crate::SampleLibrary;
|
use crate::SampleLibrary;
|
||||||
|
|
||||||
use ringbuf::{Producer, RingBuffer};
|
use ringbuf::{Producer, RingBuffer};
|
||||||
|
@ -179,7 +179,7 @@ pub struct NodeConfigurator {
|
||||||
/// Holding the tracker sequencers
|
/// Holding the tracker sequencers
|
||||||
pub(crate) trackers: Vec<Tracker>,
|
pub(crate) trackers: Vec<Tracker>,
|
||||||
/// Holding the scope buffers:
|
/// Holding the scope buffers:
|
||||||
pub(crate) scopes: Vec<UnsyncFloatBuf>,
|
pub(crate) scopes: Vec<[UnsyncFloatBuf; 3]>,
|
||||||
/// The shared parts of the [NodeConfigurator]
|
/// The shared parts of the [NodeConfigurator]
|
||||||
/// and the [crate::nodes::NodeExecutor].
|
/// and the [crate::nodes::NodeExecutor].
|
||||||
pub(crate) shared: SharedNodeConf,
|
pub(crate) shared: SharedNodeConf,
|
||||||
|
@ -282,7 +282,14 @@ impl NodeConfigurator {
|
||||||
atom_values: std::collections::HashMap::new(),
|
atom_values: std::collections::HashMap::new(),
|
||||||
node2idx: HashMap::new(),
|
node2idx: HashMap::new(),
|
||||||
trackers: vec![Tracker::new(); MAX_AVAIL_TRACKERS],
|
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,
|
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()
|
self.scopes.get(scope).cloned()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -687,7 +694,7 @@ impl NodeConfigurator {
|
||||||
|
|
||||||
if let Node::Scope { node } = &mut node {
|
if let Node::Scope { node } = &mut node {
|
||||||
if let Some(buf) = self.scopes.get(ni.instance()) {
|
if let Some(buf) = self.scopes.get(ni.instance()) {
|
||||||
node.set_scope_buffer(buf.clone());
|
node.set_scope_buffers(buf.clone());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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)]
|
#[derive(Debug)]
|
||||||
struct UnsyncFloatBufImpl {
|
struct UnsyncFloatBufImpl {
|
||||||
data_store: Vec<AtomicFloat>,
|
data_store: Vec<AtomicFloat>,
|
||||||
|
@ -65,18 +69,23 @@ unsafe impl Sync for UnsyncFloatBuf {}
|
||||||
unsafe impl Send for UnsyncFloatBuf {}
|
unsafe impl Send for UnsyncFloatBuf {}
|
||||||
|
|
||||||
impl UnsyncFloatBufImpl {
|
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> {
|
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 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");
|
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));
|
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.
|
// because it will not be moved when inside the Arc.
|
||||||
unsync_buf.ptr = unsync_buf.data_store.as_mut_ptr();
|
unsync_buf.ptr = unsync_buf.data_store.as_mut_ptr();
|
||||||
|
|
||||||
rc
|
rc
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Write a sample.
|
||||||
fn write(&self, idx: usize, v: f32) {
|
fn write(&self, idx: usize, v: f32) {
|
||||||
if idx < self.len {
|
if idx < self.len {
|
||||||
unsafe {
|
unsafe {
|
||||||
|
@ -85,6 +94,7 @@ impl UnsyncFloatBufImpl {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Read a sample.
|
||||||
fn read(&self, idx: usize) -> f32 {
|
fn read(&self, idx: usize) -> f32 {
|
||||||
if idx < self.len {
|
if idx < self.len {
|
||||||
unsafe { (*self.ptr.add(idx)).get() }
|
unsafe { (*self.ptr.add(idx)).get() }
|
||||||
|
@ -93,6 +103,7 @@ impl UnsyncFloatBufImpl {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Return the length of this buffer.
|
||||||
fn len(&self) -> usize {
|
fn len(&self) -> usize {
|
||||||
self.len
|
self.len
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,20 +13,35 @@ fn check_node_scope_1() {
|
||||||
let mut matrix = Matrix::new(node_conf, 3, 3);
|
let mut matrix = Matrix::new(node_conf, 3, 3);
|
||||||
|
|
||||||
let mut chain = MatrixCellChain::new(CellDir::B);
|
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();
|
matrix.sync().unwrap();
|
||||||
|
|
||||||
let scope = NodeId::Scope(0);
|
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 _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![];
|
let mut v = vec![];
|
||||||
for x in 0..SCOPE_SAMPLES {
|
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]);
|
assert_decimated_feq!(v, 80, vec![0.0022, 0.1836, 0.3650, 0.5464, 0.7278, 0.9093, 1.0]);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue