use ringbuf::{Consumer, Producer}; use utils::buffers::*; use utils::pitch::*; use crate::BUFFER_LEN; type SampleRate = u32; pub struct ProcessChunk { pub(crate) buffers: Buffers, pub(crate) sample_rate: SampleRate, /// Midi note number to shift frequency to pub(crate) note: Option, /// If true, will listen to note /// If false, will snap to closest note pub(crate) manual: bool, /// Extra frequency shifting to do pub(crate) freq_gain: f32, } pub fn tuna(mut inputs: Consumer, mut outputs: Producer>) { // Keep track of last detected note, and use it in case of not detecting a new one let mut prev_l_freq: Option = None; let mut prev_r_freq: Option = None; let mut detector_l = generate_pitch_detector(BUFFER_LEN); let mut detector_r = generate_pitch_detector(BUFFER_LEN); // Sample rates get overriden on first iteration, so we just set 48k let mut shifter_l = generate_vocoder(48000); let mut shifter_r = generate_vocoder(48000); loop { if let Some(ProcessChunk { buffers: recording, sample_rate, note, manual, freq_gain, }) = inputs.pop() { log::info!("got a buffer to process"); // If we're on manual mode, and we don't have a note, just pass through if manual && note.is_none() { let _ = outputs.push(recording); continue; } // TODO It does weird stereo things // Update sample rate shifter_l.set_sample_rate(sample_rate as f64); shifter_r.set_sample_rate(sample_rate as f64); // Left let l = recording.l; // Try detecting note let l = if let Some((actual, _clarity)) = pitch_detect(&mut detector_l, &l, sample_rate) { log::info!("L: detected actual pitch: {}", actual); // If note is found, set it as previous, and pitch shift prev_l_freq = Some(actual); // If it's on manual mode, convert midi note to pitch // If not, snap to closest frequency let expected = if manual { midi_note_to_pitch(note.expect("We wouldn't be here if note is None")) } else { closest_note_freq(actual) }; // Perform pitch shift // `expected / actual` is how much to shift the pitch // If the actual pitch is 400, and expected is 800, we want to shift by 2 pitch_shift(&mut shifter_l, &l, freq_gain * expected / actual) } else if let Some(actual) = prev_l_freq { log::info!("L: reusing actual pitch: {}", actual); let expected = if manual { midi_note_to_pitch(note.expect("We wouldn't be here if note is None")) } else { closest_note_freq(actual) }; pitch_shift(&mut shifter_l, &l, freq_gain * expected / actual) } else { log::info!("L: no actual pitch"); // If there's nothing, leave it as is l }; // Same thing for the right side let r = recording.r; let r = if let Some((actual, _clarity)) = pitch_detect(&mut detector_r, &r, sample_rate) { log::info!("R: detected actual pitch: {}", actual); prev_r_freq = Some(actual); let expected = if manual { midi_note_to_pitch(note.expect("We wouldn't be here if note is None")) } else { closest_note_freq(actual) }; pitch_shift(&mut shifter_r, &l, freq_gain * expected / actual) } else if let Some(actual) = prev_r_freq { log::info!("R: reusing actual pitch: {}", actual); let expected = if manual { midi_note_to_pitch(note.expect("We wouldn't be here if note is None")) } else { closest_note_freq(actual) }; pitch_shift(&mut shifter_r, &l, freq_gain * expected / actual) } else { log::info!("R: no actual pitch"); r }; let _ = outputs.push(Buffers::from(l, r)); log::info!("finished processing a buffer"); } } }