diff --git a/src/dsp/mod.rs b/src/dsp/mod.rs index d9c2191..5c9291b 100644 --- a/src/dsp/mod.rs +++ b/src/dsp/mod.rs @@ -513,6 +513,8 @@ mod node_map; #[allow(non_upper_case_globals)] mod node_midip; #[allow(non_upper_case_globals)] +mod node_midicc; +#[allow(non_upper_case_globals)] mod node_mix3; #[allow(non_upper_case_globals)] mod node_mux9; @@ -571,6 +573,7 @@ use crate::fa_delay_mode; use crate::fa_map_clip; use crate::fa_midip_chan; use crate::fa_midip_gmode; +use crate::fa_midicc_cc; use crate::fa_mux9_in_cnt; use crate::fa_noise_mode; use crate::fa_out_mono; @@ -602,6 +605,7 @@ use node_fbwr_fbrd::FbWr; use node_formfm::FormFM; use node_map::Map; use node_midip::MidiP; +use node_midicc::MidiCC; use node_mix3::Mix3; use node_mux9::Mux9; use node_noise::Noise; @@ -1436,6 +1440,15 @@ macro_rules! node_list { [0 freq] [1 gate] [2 vel], + midicc => MidiCC UIType::Generic UICategory::IOUtil + (0 slew n_lfot d_lfot r_lfot f_lfoms stp_f 0.0, 1.0, 0.0) + {1 0 chan setting(0) mode fa_midip_chan 0 16} + {2 1 cc1 setting(0) mode fa_midicc_cc 0 127} + {3 2 cc2 setting(0) mode fa_midicc_cc 0 127} + {4 3 cc3 setting(0) mode fa_midicc_cc 0 127} + [0 sig1] + [1 sig2] + [2 sig3], out => Out UIType::Generic UICategory::IOUtil (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) diff --git a/src/dsp/node_midicc.rs b/src/dsp/node_midicc.rs new file mode 100644 index 0000000..8f2bed1 --- /dev/null +++ b/src/dsp/node_midicc.rs @@ -0,0 +1,121 @@ +// 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::dsp::{ + at, denorm, inp, out_idx, DspNode, LedPhaseVals, NodeContext, NodeId, ProcBuf, SAtom, +}; +use crate::nodes::{HxMidiEvent, MidiEventPointer, NodeAudioContext, NodeExecContext}; + +#[macro_export] +macro_rules! fa_midicc_cc { + ($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 MidiCC { + cur_cc1: f32, + cur_cc2: f32, + cur_cc3: f32, +} + +impl MidiCC { + pub fn new(_nid: &NodeId) -> Self { + Self { cur_cc1: 0.0, cur_cc2: 0.0, cur_cc3: 0.0 } + } + + pub const chan: &'static str = "MidiCC chan\nMIDI Channel 0 to 15\n"; + pub const slew: &'static str = "MidiCC slew\nSlew limiter for the 3 CCs\n- 'MIDI' gate same as MIDI input\n- 'Trigger' output only triggers on 'gate' output\n- 'Gate Len' output gate with the length of the 'gatel' parameter\n"; + pub const cc1: &'static str = "MidiCC cc1\nMIDI selected CC"; + pub const cc2: &'static str = "MidiCC cc1\nMIDI selected CC"; + pub const cc3: &'static str = "MidiCC cc1\nMIDI selected CC"; + + pub const sig1: &'static str = "MidiCC sig1\nCC output channel 1\nRange: (0..1)"; + pub const sig2: &'static str = "MidiCC sig1\nCC output channel 1\nRange: (0..1)"; + pub const sig3: &'static str = "MidiCC sig1\nCC output channel 1\nRange: (0..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 MidiCC { + fn outputs() -> usize { + 0 + } + + fn set_sample_rate(&mut self, _srate: f32) {} + fn reset(&mut self) {} + + #[inline] + fn process( + &mut self, + ctx: &mut T, + ectx: &mut NodeExecContext, + _nctx: &NodeContext, + atoms: &[SAtom], + inputs: &[ProcBuf], + outputs: &mut [ProcBuf], + ctx_vals: LedPhaseVals, + ) { + let chan = at::MidiCC::chan(atoms); + let cc1 = at::MidiCC::cc1(atoms); + let cc2 = at::MidiCC::cc2(atoms); + let cc3 = at::MidiCC::cc3(atoms); + let sig2_i = out_idx::MidiCC::sig2(); + let (sig1, r) = outputs.split_at_mut(sig2_i); + let (sig2, sig3) = r.split_at_mut(1); + let sig1 = &mut sig1[0]; + let sig2 = &mut sig2[0]; + let sig3 = &mut sig3[0]; + + let midicc_channel = (chan.i() as usize % 16) as u8; + let midicc_cc1 = (chan.i() as usize % 128) as u8; + let midicc_cc2 = (chan.i() as usize % 128) as u8; + let midicc_cc3 = (chan.i() as usize % 128) as u8; + + let mut ptr = MidiEventPointer::new(&ectx.midi_ccs[..]); + + let mut change = false; + + for frame in 0..ctx.nframes() { + while let Some(ev) = ptr.next_at(frame) { + match ev { + HxMidiEvent::CC { channel, cc, value } => { + if channel != midicc_channel { + continue; + } + + if cc == midicc_cc1 { + self.cur_cc1 = value; + change = true; + } else if cc == midicc_cc2 { + self.cur_cc2 = value; + change = true; + } else if cc == midicc_cc3 { + self.cur_cc3 = value; + change = true; + } + } + _ => (), + } + } + + sig1.write(frame, self.cur_cc1); + sig2.write(frame, self.cur_cc2); + sig3.write(frame, self.cur_cc3); + } + + ctx_vals[0].set(if change { 1.0 } else { 0.0 }); + } +} diff --git a/src/dsp/node_midip.rs b/src/dsp/node_midip.rs index e1dbbd0..f7fae9e 100644 --- a/src/dsp/node_midip.rs +++ b/src/dsp/node_midip.rs @@ -122,7 +122,6 @@ impl DspNode for MidiP { self.next_gate = 0; while let Some(ev) = ptr.next_at(frame) { - println!("MIDIP EV: {} {:?}", frame, ev); match ev { HxMidiEvent::NoteOn { channel, note, vel } => { if channel != midip_channel {