Working on the MidiP MIDI note input node

This commit is contained in:
Weird Constructor 2022-08-10 19:15:03 +02:00
parent 20f41b1dfd
commit ecd6b53c65
6 changed files with 223 additions and 19 deletions

View file

@ -519,6 +519,8 @@ mod node_noise;
#[allow(non_upper_case_globals)] #[allow(non_upper_case_globals)]
mod node_out; mod node_out;
#[allow(non_upper_case_globals)] #[allow(non_upper_case_globals)]
mod node_midip;
#[allow(non_upper_case_globals)]
mod node_pverb; mod node_pverb;
#[allow(non_upper_case_globals)] #[allow(non_upper_case_globals)]
mod node_quant; mod node_quant;
@ -570,6 +572,7 @@ use crate::fa_map_clip;
use crate::fa_mux9_in_cnt; use crate::fa_mux9_in_cnt;
use crate::fa_noise_mode; use crate::fa_noise_mode;
use crate::fa_out_mono; use crate::fa_out_mono;
use crate::fa_midip_chan;
use crate::fa_quant; use crate::fa_quant;
use crate::fa_sampl_dclick; use crate::fa_sampl_dclick;
use crate::fa_sampl_dir; use crate::fa_sampl_dir;
@ -601,6 +604,7 @@ use node_mix3::Mix3;
use node_mux9::Mux9; use node_mux9::Mux9;
use node_noise::Noise; use node_noise::Noise;
use node_out::Out; use node_out::Out;
use node_midip::MidiP;
use node_pverb::PVerb; use node_pverb::PVerb;
use node_quant::Quant; use node_quant::Quant;
use node_rndwk::RndWk; use node_rndwk::RndWk;
@ -1423,6 +1427,12 @@ macro_rules! node_list {
(3 force n_id n_id r_id f_def stp_d 0.0, 1.0, 0.5) (3 force n_id n_id r_id f_def stp_d 0.0, 1.0, 0.5)
(4 pos n_id n_id r_id f_def stp_d 0.0, 1.0, 0.5) (4 pos n_id n_id r_id f_def stp_d 0.0, 1.0, 0.5)
[0 sig], [0 sig],
midip => MidiP UIType::Generic UICategory::IOUtil
(0 det n_det d_det r_det f_det stp_f -0.2, 0.2, 0.0)
{1 0 chan setting(0) mode fa_midip_chan 0 16}
[0 freq]
[1 gate]
[2 vel],
out => Out UIType::Generic UICategory::IOUtil out => Out UIType::Generic UICategory::IOUtil
(0 ch1 n_id d_id r_id f_def stp_d -1.0, 1.0, 0.0) (0 ch1 n_id d_id r_id f_def stp_d -1.0, 1.0, 0.0)
(1 ch2 n_id d_id r_id f_def stp_d -1.0, 1.0, 0.0) (1 ch2 n_id d_id r_id f_def stp_d -1.0, 1.0, 0.0)

View file

@ -90,13 +90,11 @@ impl DspNode for FormFM {
let peak_val = denorm::FormFM::peak(peak_val, frame); let peak_val = denorm::FormFM::peak(peak_val, frame);
// make a triangle wave, with the peak at carrier center // make a triangle wave, with the peak at carrier center
let carrier_base = let carrier_base = (self.phase / side_val).min((1.0 - self.phase) / (1.0 - side_val));
(self.phase / side_val).min((1.0 - self.phase) / (1.0 - side_val));
// smoothstep // smoothstep
let carrier = 1.0 let carrier = 1.0
- ((1.0 - peak_val) - ((1.0 - peak_val) * (carrier_base * carrier_base * (3.0 - 2.0 * carrier_base)));
* (carrier_base * carrier_base * (3.0 - 2.0 * carrier_base)));
// multiple of the frequency the modulators are at // multiple of the frequency the modulators are at
let multiple = formant_freq / base_freq.max(1e-6); let multiple = formant_freq / base_freq.max(1e-6);

106
src/dsp/node_midip.rs Normal file
View file

@ -0,0 +1,106 @@
// Copyright (c) 2022 Weird Constructor <weirdconstructor@gmail.com>
// This file is a part of HexoDSP. Released under GPL-3.0-or-later.
// See README.md and COPYING for details.
use crate::dsp::{
at, denorm, inp, out_idx, DspNode, LedPhaseVals, NodeContext, NodeId, ProcBuf, SAtom,
};
use crate::nodes::{NodeAudioContext, NodeExecContext};
#[macro_export]
macro_rules! fa_midip_chan {
($formatter: expr, $v: expr, $denorm_v: expr) => {{
write!($formatter, "{}", $v.round() as usize)
}};
}
/// The (stereo) output port of the plugin
#[derive(Debug, Clone)]
pub struct MidiP {
/// - 0: signal channel 1
/// - 1: signal channel 2
#[allow(dead_code)]
input: [f32; 2],
}
impl MidiP {
pub fn new(_nid: &NodeId) -> Self {
Self { input: [0.0; 2] }
}
pub const chan: &'static str = "MidiP chan\nMIDI Channel 0 to 15\n";
pub const det: &'static str = "MidiP det\nDetune input pitch a bit\nRange: (-1..1)";
pub const freq: &'static str =
"MidiP freq\nMIDI note frequency, detuned by 'det'.\nRange: (-1..1)";
pub const gate: &'static str = "MidiP gate\nMIDI note gate\nRange: (0..1)";
pub const vel: &'static str = "MidiP vel\nMIDI note velocity\nRange: (0..1)";
pub const ch1: &'static str = "MidiP ch1\nAudio channel 1 (left)\nRange: (-1..1)";
pub const ch2: &'static str = "MidiP ch2\nAudio channel 2 (right)\nRange: (-1..1)";
pub const ch3: &'static str = "MidiP ch2\nAudio channel 2 (right)\nRange: (-1..1)";
pub const ch4: &'static str = "MidiP ch2\nAudio channel 2 (right)\nRange: (-1..1)";
pub const ch5: &'static str = "MidiP ch2\nAudio channel 2 (right)\nRange: (-1..1)";
pub const ch6: &'static str = "MidiP ch2\nAudio channel 2 (right)\nRange: (-1..1)";
pub const ch7: &'static str = "MidiP ch2\nAudio channel 2 (right)\nRange: (-1..1)";
pub const ch8: &'static str = "MidiP ch2\nAudio channel 2 (right)\nRange: (-1..1)";
pub const ch9: &'static str = "MidiP ch2\nAudio channel 2 (right)\nRange: (-1..1)";
pub const ch10: &'static str = "MidiP ch2\nAudio channel 2 (right)\nRange: (-1..1)";
pub const ch11: &'static str = "MidiP ch2\nAudio channel 2 (right)\nRange: (-1..1)";
pub const ch12: &'static str = "MidiP ch2\nAudio channel 2 (right)\nRange: (-1..1)";
pub const ch13: &'static str = "MidiP ch2\nAudio channel 2 (right)\nRange: (-1..1)";
pub const ch14: &'static str = "MidiP ch2\nAudio channel 2 (right)\nRange: (-1..1)";
pub const ch15: &'static str = "MidiP ch2\nAudio channel 2 (right)\nRange: (-1..1)";
pub const ch16: &'static str = "MidiP ch2\nAudio channel 2 (right)\nRange: (-1..1)";
pub const ch17: &'static str = "MidiP ch2\nAudio channel 2 (right)\nRange: (-1..1)";
pub const DESC: &'static str = "Audio Output Port\n\n\
This output port node allows you to send audio signals \
to audio devices or tracks in your DAW.";
pub const HELP: &'static str = r#"Audio Output Port
This output port node allows you to send audio signals to audio devices
or tracks in your DAW. If you need a stereo output but only have a mono
signal you can use the 'mono' setting to duplicate the signal on the 'ch1'
input to the second channel 'ch2'.
"#;
}
impl DspNode for MidiP {
fn outputs() -> usize {
0
}
fn set_sample_rate(&mut self, _srate: f32) {}
fn reset(&mut self) {}
#[inline]
fn process<T: NodeAudioContext>(
&mut self,
ctx: &mut T,
ectx: &mut NodeExecContext,
_nctx: &NodeContext,
atoms: &[SAtom],
_inputs: &[ProcBuf],
outputs: &mut [ProcBuf],
ctx_vals: LedPhaseVals,
) {
let out_i = out_idx::MidiP::freq();
let (freq, r) = outputs.split_at_mut(out_i);
let (gate, vel) = r.split_at_mut(1);
let freq = &mut freq[0];
let gate = &mut gate[0];
let vel = &mut vel[0];
for frame in 0..ctx.nframes() {
let chan = ectx.note_buffer.get_chan_at(0, frame as u8);
freq.write(frame, chan.note as f32 * 1.0 / 127.0);
gate.write(frame, chan.gate as f32);
vel.write(frame, chan.vel as f32);
}
let last_val = gate.read(ctx.nframes() - 1);
ctx_vals[0].set(last_val);
}
}

View file

@ -21,6 +21,7 @@ mod node_conf;
mod node_exec; mod node_exec;
mod node_graph_ordering; mod node_graph_ordering;
mod node_prog; mod node_prog;
mod note_buffer;
pub mod visual_sampling_filter; pub mod visual_sampling_filter;
pub(crate) use visual_sampling_filter::*; pub(crate) use visual_sampling_filter::*;
@ -30,6 +31,7 @@ pub use node_conf::*;
pub use node_exec::*; pub use node_exec::*;
pub use node_graph_ordering::NodeGraphOrdering; pub use node_graph_ordering::NodeGraphOrdering;
pub use node_prog::*; pub use node_prog::*;
pub use note_buffer::{NoteBuffer, NoteChannelState};
use crate::dsp::{Node, SAtom}; use crate::dsp::{Node, SAtom};
pub use crate::monitor::MinMaxMonitorSamples; pub use crate::monitor::MinMaxMonitorSamples;

View file

@ -2,6 +2,7 @@
// This file is a part of HexoDSP. Released under GPL-3.0-or-later. // This file is a part of HexoDSP. Released under GPL-3.0-or-later.
// See README.md and COPYING for details. // See README.md and COPYING for details.
use super::NoteBuffer;
use super::{ use super::{
DropMsg, GraphMessage, NodeProg, FB_DELAY_TIME_US, MAX_ALLOCATED_NODES, MAX_FB_DELAY_SIZE, DropMsg, GraphMessage, NodeProg, FB_DELAY_TIME_US, MAX_ALLOCATED_NODES, MAX_FB_DELAY_SIZE,
MAX_SMOOTHERS, UNUSED_MONITOR_IDX, MAX_SMOOTHERS, UNUSED_MONITOR_IDX,
@ -169,13 +170,14 @@ impl Default for FeedbackBuffer {
/// This is used for instance to implement the feedbackd delay nodes. /// This is used for instance to implement the feedbackd delay nodes.
pub struct NodeExecContext { pub struct NodeExecContext {
pub feedback_delay_buffers: Vec<FeedbackBuffer>, pub feedback_delay_buffers: Vec<FeedbackBuffer>,
pub note_buffer: NoteBuffer,
} }
impl NodeExecContext { impl NodeExecContext {
fn new() -> Self { fn new() -> Self {
let mut fbdb = vec![]; let mut fbdb = vec![];
fbdb.resize_with(MAX_ALLOCATED_NODES, FeedbackBuffer::new); fbdb.resize_with(MAX_ALLOCATED_NODES, FeedbackBuffer::new);
Self { feedback_delay_buffers: fbdb } Self { feedback_delay_buffers: fbdb, note_buffer: NoteBuffer::new() }
} }
fn set_sample_rate(&mut self, srate: f32) { fn set_sample_rate(&mut self, srate: f32) {
@ -352,6 +354,11 @@ impl NodeExecutor {
} }
} }
#[inline]
pub fn get_note_buffer(&mut self) -> &mut NoteBuffer {
&mut self.exec_ctx.note_buffer
}
#[inline] #[inline]
pub fn get_nodes(&self) -> &Vec<Node> { pub fn get_nodes(&self) -> &Vec<Node> {
&self.nodes &self.nodes

View file

@ -3,38 +3,119 @@
// See README.md and COPYING for details. // See README.md and COPYING for details.
use crate::dsp::MAX_BLOCK_SIZE; use crate::dsp::MAX_BLOCK_SIZE;
use crate::util::Smoother;
#[derive(Debug, Clone, Copy)] #[derive(Debug, Clone, Copy)]
pub struct ChannelState { pub struct NoteChannelState {
vel: f32, pub vel: f32,
pitch: u8, pub note: u8,
gate: u8, pub gate: u8,
}
impl std::fmt::Display for NoteChannelState {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
write!(f, "C<N={},G={},V={:5.3}>", self.note, self.gate, self.vel)
}
}
impl NoteChannelState {
pub fn new() -> Self {
Self { vel: 0.0, note: 0, gate: 0 }
}
} }
pub struct NoteBuffer { pub struct NoteBuffer {
interleaved_chans: Vec<ChannelState>, interleaved_chans: Vec<NoteChannelState>,
buf_idx: usize, buf_idx: usize,
} }
impl NoteBuffer { impl NoteBuffer {
pub fn new() -> Self { pub fn new() -> Self {
Self { Self { interleaved_chans: vec![NoteChannelState::new(); 16 * MAX_BLOCK_SIZE], buf_idx: 15 }
interleaved_chans: vec![ChannelState::new(); 16 * MAX_BLOCK_SIZE], }
buf_idx: 15,
#[inline]
pub fn reset(&mut self) {
let cur = self.buf_idx;
if cur != 0 {
self.interleaved_chans.copy_within((cur * 16)..((cur + 1) * 16), 0);
self.buf_idx = 0;
} }
} }
#[inline] #[inline]
pub fn step(&mut self) { pub fn step(&mut self) {
let cur = self.buf_idx; let cur = self.buf_idx;
let next = (self.buf_idx + 1) % 16; let next = (self.buf_idx + 1) % MAX_BLOCK_SIZE;
println!("COPY {}..{} => {}", (cur * 16), ((cur + 1) * 16), next * 16);
self.interleaved_chans.copy_within((cur * 16)..((cur + 1) * 16), next * 16); self.interleaved_chans.copy_within((cur * 16)..((cur + 1) * 16), next * 16);
self.buf_idx = next;
} }
// pub fn play_velocity(&mut self, channel: u8, vel: f32) { #[inline]
// let mut vel = &mut self.velocity[channel % 16]; pub fn note_on(&mut self, channel: u8, note: u8) {
// vel.set(vel.current(), vel); let mut chan = &mut self.interleaved_chans[(self.buf_idx * 16) + (channel as usize % 16)];
// } if chan.gate == 0 {
chan.gate = 1;
chan.note = note;
}
} }
#[inline]
pub fn note_off(&mut self, channel: u8, note: u8) {
let mut chan = &mut self.interleaved_chans[(self.buf_idx * 16) + (channel as usize % 16)];
if chan.gate == 1 && chan.note == note {
chan.gate = 0;
chan.note = 0;
}
}
#[inline]
pub fn set_velocity(&mut self, channel: u8, vel: f32) {
self.interleaved_chans[(self.buf_idx * 16) + (channel as usize % 16)].vel = vel;
}
#[inline]
pub fn get_chan_at(&self, channel: u8, frame: u8) -> &NoteChannelState {
&self.interleaved_chans[frame as usize * 16 + (channel as usize % 16)]
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn check_note_buffer() {
let mut buf = NoteBuffer::new();
buf.reset();
buf.note_on(0, 10);
buf.note_on(2, 12);
buf.set_velocity(0, 0.6);
buf.set_velocity(2, 0.8);
//d// println!("> {:?}", buf.get_chan_at(0, 0));
buf.step();
buf.note_on(0, 11);
for _ in 0..10 {
buf.step();
}
buf.note_off(0, 11);
buf.step();
buf.note_off(0, 10);
buf.step();
buf.set_velocity(2, 0.4);
//d// println!("> {:?}", buf.get_chan_at(0, 0));
for i in 0..(MAX_BLOCK_SIZE - 14) {
buf.step();
}
//d// for i in 0..MAX_BLOCK_SIZE {
//d// println!(">{} {}", i, buf.get_chan_at(2, i as u8));
//d// }
assert_eq!(buf.get_chan_at(0, 0).to_string(), "C<N=10,G=1,V=0.600>");
assert_eq!(buf.get_chan_at(0, 12).to_string(), "C<N=0,G=0,V=0.600>");
assert_eq!(buf.get_chan_at(2, 0).to_string(), "C<N=12,G=1,V=0.800>");
assert_eq!(buf.get_chan_at(2, 127).to_string(), "C<N=12,G=1,V=0.400>");
}
}