Compare commits
No commits in common. "bdd8f43ac3bb7d8082d6e5c6ca656279b2fab774" and "a09d9f83a40535bbccc8cda48785050ff1d0353d" have entirely different histories.
bdd8f43ac3
...
a09d9f83a4
|
@ -261,15 +261,12 @@ analyzes pitch of incoming audio and sends out the corresponding midi note
|
|||
|
||||
params:
|
||||
- `passthrough`: how much of the original audio to be let through
|
||||
- `min note length`: how many beats a note will last
|
||||
|
||||
latency should be close to 1024 samples, which is around 20ms at 48k i think. the pitch detection is not excellent and it's monophonic, so if you have a noisy input or a bunch of notes at the same time, it's anyone's guess what the detected note will be. there's also no pitch bending or anything fancy like that, so it'll jump around notes even if the input has portamento
|
||||
|
||||
aside from that, it works well enough on my quick tests
|
||||
|
||||
you can use it to play synths using other instruments, like a guitar or by whistling! it's quite fun
|
||||
|
||||
`min note length` controls the note length, so if you see that the detected note jumps around a bunch, you can raise this so the notes last longer, and it'll be a bit smoother
|
||||
you can use it to play synths using other instruments, like a guitar, or by whistling! it's quite fun
|
||||
|
||||
### reverter
|
||||
|
||||
|
|
|
@ -14,26 +14,18 @@ baseplug::model! {
|
|||
#[model(min = 0.0, max = 1.0)]
|
||||
#[parameter(name = "passthrough")]
|
||||
passthrough: f32,
|
||||
#[model(min = 0.0, max = 2.0)]
|
||||
#[parameter(name = "min note length")]
|
||||
min_note_len: f32,
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for TransmutePitchModel {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
passthrough: 0.0,
|
||||
min_note_len: 0.0,
|
||||
}
|
||||
Self { passthrough: 1.0 }
|
||||
}
|
||||
}
|
||||
|
||||
struct TransmutePitch {
|
||||
detector_thread: pitch_detection::PitchDetectorThread<BUFFER_LEN>,
|
||||
last_note: Option<u8>,
|
||||
last_note_time: Option<usize>,
|
||||
counter: usize,
|
||||
}
|
||||
|
||||
impl Plugin for TransmutePitch {
|
||||
|
@ -52,8 +44,6 @@ impl Plugin for TransmutePitch {
|
|||
Self {
|
||||
detector_thread,
|
||||
last_note: None,
|
||||
last_note_time: None,
|
||||
counter: 0,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -63,13 +53,7 @@ impl Plugin for TransmutePitch {
|
|||
let output = &mut ctx.outputs[0].buffers;
|
||||
let enqueue_midi = &mut ctx.enqueue_event;
|
||||
|
||||
let curr_bpm = ctx.musical_time.bpm;
|
||||
let beat_in_seconds = 60.0 / curr_bpm;
|
||||
let samples_in_beat = beat_in_seconds * ctx.sample_rate as f64;
|
||||
|
||||
for i in 0..ctx.nframes {
|
||||
self.counter += 1;
|
||||
|
||||
output[0][i] = model.passthrough[i] * input[0][i];
|
||||
output[1][i] = model.passthrough[i] * input[1][i];
|
||||
|
||||
|
@ -77,17 +61,13 @@ impl Plugin for TransmutePitch {
|
|||
self.detector_thread
|
||||
.write(input[0][i], 0.0, ctx.sample_rate as u32);
|
||||
|
||||
let min_len = model.min_note_len[i] as f64 * samples_in_beat;
|
||||
|
||||
// Try to get a processed buffer from the processor thread
|
||||
match self.detector_thread.try_get_pitch() {
|
||||
Some((Some(pitch), _)) => {
|
||||
let note = pitch_to_midi_note(pitch);
|
||||
|
||||
// If note changed
|
||||
if self.last_note != Some(note)
|
||||
&& min_len < (self.counter - self.last_note_time.unwrap_or(0)) as f64
|
||||
{
|
||||
if self.last_note != Some(note) {
|
||||
// Send note off for last note
|
||||
if let Some(last_note) = self.last_note {
|
||||
let note_off = Event::<TransmutePitch> {
|
||||
|
@ -106,24 +86,18 @@ impl Plugin for TransmutePitch {
|
|||
|
||||
// Update note
|
||||
self.last_note = Some(note);
|
||||
self.last_note_time = Some(self.counter);
|
||||
}
|
||||
}
|
||||
Some((None, _)) => {
|
||||
if min_len < (self.counter - self.last_note_time.unwrap_or(0)) as f64 {
|
||||
if let Some(last_note) = self.last_note {
|
||||
let note_off = Event::<TransmutePitch> {
|
||||
frame: i,
|
||||
data: Data::Midi([0x80, last_note, 0]),
|
||||
};
|
||||
enqueue_midi(note_off);
|
||||
}
|
||||
|
||||
self.last_note = None;
|
||||
self.last_note_time = None;
|
||||
// restart the counter so we don't go super far away ig
|
||||
self.counter = min_len as usize;
|
||||
if let Some(last_note) = self.last_note {
|
||||
let note_off = Event::<TransmutePitch> {
|
||||
frame: i,
|
||||
data: Data::Midi([0x80, last_note, 0]),
|
||||
};
|
||||
enqueue_midi(note_off);
|
||||
}
|
||||
|
||||
self.last_note = None;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
|
|
@ -13,9 +13,6 @@ baseplug::model! {
|
|||
#[model(min = 0.0, max = 127.9)]
|
||||
#[parameter(name = "max_vel")]
|
||||
max_vel: f32,
|
||||
#[model(min = 0.0, max = 10.)]
|
||||
#[parameter(name = "repetition")]
|
||||
repetition: f32,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -24,15 +21,12 @@ impl Default for VelociterModel {
|
|||
Self {
|
||||
min_vel: 0.0,
|
||||
max_vel: 127.9,
|
||||
repetition: 0.0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct Velociter {
|
||||
notes: Vec<[u8; 3]>,
|
||||
last_val: u8,
|
||||
counter: u8,
|
||||
}
|
||||
|
||||
impl Plugin for Velociter {
|
||||
|
@ -49,8 +43,6 @@ impl Plugin for Velociter {
|
|||
fn new(_sample_rate: f32, _model: &VelociterModel) -> Self {
|
||||
Self {
|
||||
notes: Vec::with_capacity(300),
|
||||
last_val: 0,
|
||||
counter: u8::MAX,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -63,32 +55,23 @@ impl Plugin for Velociter {
|
|||
use rand::{thread_rng, Rng};
|
||||
let mut rng = thread_rng();
|
||||
|
||||
let min = model.min_vel[0];
|
||||
let max = model.max_vel[0];
|
||||
// make sure they're right
|
||||
let (min, max) = (min.min(max), max.max(min));
|
||||
|
||||
for mut note in self.notes.drain(0..) {
|
||||
let val = if model.repetition[0] < self.counter as f32 {
|
||||
self.counter = 0;
|
||||
rng.gen_range(min..max).trunc() as u8
|
||||
} else {
|
||||
self.counter += 1;
|
||||
self.last_val
|
||||
};
|
||||
self.last_val = val;
|
||||
|
||||
note[2] = val;
|
||||
let note = Event::<Velociter> {
|
||||
frame: 0,
|
||||
data: Data::Midi(note),
|
||||
};
|
||||
enqueue_midi(note);
|
||||
}
|
||||
|
||||
for i in 0..ctx.nframes {
|
||||
output[0][i] = input[0][i];
|
||||
output[1][i] = input[1][i];
|
||||
|
||||
let min = model.min_vel[i];
|
||||
let max = model.max_vel[i];
|
||||
// make sure they're right
|
||||
let (min, max) = (min.min(max), max.max(min));
|
||||
|
||||
for mut note in self.notes.drain(0..) {
|
||||
note[2] = rng.gen_range(min..max).trunc() as u8;
|
||||
let note = Event::<Velociter> {
|
||||
frame: 0,
|
||||
data: Data::Midi(note),
|
||||
};
|
||||
enqueue_midi(note);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -98,4 +81,4 @@ impl MidiReceiver for Velociter {
|
|||
}
|
||||
}
|
||||
|
||||
baseplug::vst2!(Velociter, b"iter");
|
||||
baseplug::vst2!(Velociter, b"tAnE");
|
||||
|
|
Loading…
Reference in New Issue