[opiate, pvoc] implement changing phase and not frequency

main
annieversary 2021-09-16 17:42:18 +02:00
parent be46998301
commit 43a66b4769
3 changed files with 99 additions and 52 deletions

View File

@ -4,7 +4,7 @@
// morphing algorithm from https://ccrma.stanford.edu/~jhsu/421b/ // morphing algorithm from https://ccrma.stanford.edu/~jhsu/421b/
use baseplug::{Plugin, ProcessContext}; use baseplug::{Plugin, ProcessContext};
use pvoc::{Bin, PhaseVocoder}; use pvoc::{PhaseBin, PhaseVocoder};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use utils::logs::*; use utils::logs::*;
@ -24,7 +24,7 @@ impl Default for OpiateModel {
} }
struct Opiate { struct Opiate {
pvoc: PhaseVocoder, pvoc: PhaseVocoder<PhaseBin>,
out_0: Vec<f32>, out_0: Vec<f32>,
out_1: Vec<f32>, out_1: Vec<f32>,
out_2: Vec<f32>, out_2: Vec<f32>,
@ -68,10 +68,10 @@ impl Plugin for Opiate {
} }
let out = &mut [ let out = &mut [
&mut self.out_0[..], &mut self.out_0[0..ctx.nframes],
&mut self.out_1[..], &mut self.out_1[0..ctx.nframes],
&mut self.out_2[..], &mut self.out_2[0..ctx.nframes],
&mut self.out_3[..], &mut self.out_3[0..ctx.nframes],
][..]; ][..];
let morph = model.morph[0] as f64; let morph = model.morph[0] as f64;
@ -79,27 +79,28 @@ impl Plugin for Opiate {
self.pvoc.process( self.pvoc.process(
input, input,
out, out,
|_channels: usize, bins: usize, input: &[Vec<Bin>], output: &mut [Vec<Bin>]| { |_channels: usize,
bins: usize,
input: &[Vec<PhaseBin>],
output: &mut [Vec<PhaseBin>]| {
for j in 0..bins { for j in 0..bins {
// TODO Check if working with the frequencies is the same as working with the phase // left
// i think we might need to try it to make sure it's the same
// to do that, we'll need to change how pvoc works
let mags = morph * (1.0 - input[0][j].amp) + input[0][j].amp; let mags = morph * (1.0 - input[0][j].amp) + input[0][j].amp;
let mags2 = imorph * (1.0 - input[2][j].amp) + input[2][j].amp; let mags2 = imorph * (1.0 - input[2][j].amp) + input[2][j].amp;
let phases = input[0][j].freq - (input[0][j].freq * morph); let phases = input[0][j].phase - (input[0][j].phase * morph);
let phases2 = input[2][j].freq - (input[2][j].freq * imorph); let phases2 = input[2][j].phase - (input[2][j].phase * imorph);
output[0][j].amp = mags * mags2; output[0][j].amp = mags * mags2;
output[0][j].freq = phases + phases2; output[0][j].phase = phases + phases2;
// right
let mags = morph * (1.0 - input[1][j].amp) + input[1][j].amp; let mags = morph * (1.0 - input[1][j].amp) + input[1][j].amp;
let mags2 = imorph * (1.0 - input[3][j].amp) + input[3][j].amp; let mags2 = imorph * (1.0 - input[3][j].amp) + input[3][j].amp;
let phases = input[1][j].freq - (input[1][j].freq * morph); let phases = input[1][j].phase - (input[1][j].phase * morph);
let phases2 = input[3][j].freq - (input[3][j].freq * imorph); let phases2 = input[3][j].phase - (input[3][j].phase * imorph);
output[1][j].amp = mags * mags2; output[1][j].amp = mags * mags2;
output[1][j].freq = phases + phases2; output[1][j].phase = phases + phases2;
} }
}, },
); );
@ -107,6 +108,9 @@ impl Plugin for Opiate {
for i in 0..ctx.nframes { for i in 0..ctx.nframes {
output[0][i] = self.out_0[i]; output[0][i] = self.out_0[i];
output[1][i] = self.out_1[i]; output[1][i] = self.out_1[i];
self.out_0[i] = 0.0;
self.out_1[i] = 0.0;
} }
} }
} }

View File

@ -15,32 +15,67 @@ use std::sync::Arc;
#[allow(non_camel_case_types)] #[allow(non_camel_case_types)]
type c64 = Complex<f64>; type c64 = Complex<f64>;
pub trait Bin: Clone + Copy {
fn empty() -> Self;
fn new(freq: f64, phase: f64, amp: f64) -> Self;
fn amp(&self) -> f64;
fn phase(&self, pvoc: &PhaseVocoder<Self>) -> f64;
}
/// Represents a component of the spectrum, composed of a phase and amplitude.
#[derive(Copy, Clone)]
pub struct PhaseBin {
pub phase: f64,
pub amp: f64,
}
impl Bin for PhaseBin {
fn empty() -> Self {
Self {
phase: 0.0,
amp: 0.0,
}
}
fn new(_: f64, phase: f64, amp: f64) -> Self {
Self { phase, amp }
}
fn amp(&self) -> f64 {
self.amp
}
fn phase(&self, _: &PhaseVocoder<Self>) -> f64 {
self.phase
}
}
/// Represents a component of the spectrum, composed of a frequency and amplitude. /// Represents a component of the spectrum, composed of a frequency and amplitude.
#[derive(Copy, Clone)] #[derive(Copy, Clone)]
pub struct Bin { pub struct FreqBin {
pub freq: f64, pub freq: f64,
pub amp: f64, pub amp: f64,
} }
impl Bin for FreqBin {
impl Bin { fn empty() -> Self {
pub fn new(freq: f64, amp: f64) -> Bin { Self {
Bin {
freq: freq,
amp: amp,
}
}
pub fn empty() -> Bin {
Bin {
freq: 0.0, freq: 0.0,
amp: 0.0, amp: 0.0,
} }
} }
fn new(freq: f64, _: f64, amp: f64) -> Self {
Self { freq, amp }
}
fn amp(&self) -> f64 {
self.amp
}
fn phase(&self, pvoc: &PhaseVocoder<Self>) -> f64 {
pvoc.frequency_to_phase(self.freq)
}
} }
/// A phase vocoder. /// A phase vocoder.
/// ///
/// Roughly translated from http://blogs.zynaptiq.com/bernsee/pitch-shifting-using-the-ft/ /// Roughly translated from http://blogs.zynaptiq.com/bernsee/pitch-shifting-using-the-ft/
pub struct PhaseVocoder { pub struct PhaseVocoder<B: Bin = FreqBin> {
channels: usize, channels: usize,
sample_rate: f64, sample_rate: f64,
frame_size: usize, frame_size: usize,
@ -61,11 +96,11 @@ pub struct PhaseVocoder {
fft_in: Vec<c64>, fft_in: Vec<c64>,
fft_out: Vec<c64>, fft_out: Vec<c64>,
fft_scratch: Vec<c64>, fft_scratch: Vec<c64>,
analysis_out: Vec<Vec<Bin>>, analysis_out: Vec<Vec<B>>,
synthesis_in: Vec<Vec<Bin>>, synthesis_in: Vec<Vec<B>>,
} }
impl PhaseVocoder { impl<B: Bin> PhaseVocoder<B> {
/// Constructs a new phase vocoder. /// Constructs a new phase vocoder.
/// ///
/// `channels` is the number of channels of audio. /// `channels` is the number of channels of audio.
@ -80,12 +115,7 @@ impl PhaseVocoder {
/// ///
/// # Panics /// # Panics
/// Panics if `frame_size` is `<= 1` after rounding. /// Panics if `frame_size` is `<= 1` after rounding.
pub fn new( pub fn new(channels: usize, sample_rate: f64, frame_size: usize, time_res: usize) -> Self {
channels: usize,
sample_rate: f64,
frame_size: usize,
time_res: usize,
) -> PhaseVocoder {
let mut frame_size = frame_size / time_res * time_res; let mut frame_size = frame_size / time_res * time_res;
if frame_size == 0 { if frame_size == 0 {
frame_size = time_res; frame_size = time_res;
@ -119,8 +149,8 @@ impl PhaseVocoder {
fft_in: vec![c64::new(0.0, 0.0); frame_size], fft_in: vec![c64::new(0.0, 0.0); frame_size],
fft_out: vec![c64::new(0.0, 0.0); frame_size], fft_out: vec![c64::new(0.0, 0.0); frame_size],
fft_scratch: vec![], fft_scratch: vec![],
analysis_out: vec![vec![Bin::empty(); frame_size]; channels], analysis_out: vec![vec![B::empty(); frame_size]; channels],
synthesis_in: vec![vec![Bin::empty(); frame_size]; channels], synthesis_in: vec![vec![B::empty(); frame_size]; channels],
}; };
pv.fft_scratch = vec![ pv.fft_scratch = vec![
c64::new(0.0, 0.0); c64::new(0.0, 0.0);
@ -182,10 +212,18 @@ impl PhaseVocoder {
) -> usize ) -> usize
where where
S: Float + ToPrimitive + FromPrimitive, S: Float + ToPrimitive + FromPrimitive,
F: FnMut(usize, usize, &[Vec<Bin>], &mut [Vec<Bin>]), F: FnMut(usize, usize, &[Vec<B>], &mut [Vec<B>]),
{ {
assert_eq!(input.len(), self.channels); assert_eq!(
assert_eq!(output.len(), self.channels); input.len(),
self.channels,
"input length does not equal channel count"
);
assert_eq!(
output.len(),
self.channels,
"output length does not equal channel count"
);
// push samples to input queue // push samples to input queue
for chan in 0..input.len() { for chan in 0..input.len() {
@ -205,7 +243,7 @@ impl PhaseVocoder {
// This may be removed in a future release. // This may be removed in a future release.
for synthesis_channel in self.synthesis_in.iter_mut() { for synthesis_channel in self.synthesis_in.iter_mut() {
for bin in synthesis_channel.iter_mut() { for bin in synthesis_channel.iter_mut() {
*bin = Bin::empty(); *bin = B::empty();
} }
} }
@ -225,10 +263,13 @@ impl PhaseVocoder {
for i in 0..self.frame_size { for i in 0..self.frame_size {
let x = self.fft_out[i]; let x = self.fft_out[i];
let (amp, phase) = x.to_polar(); let (amp, phase) = x.to_polar();
let freq = self.phase_to_frequency(i, phase - self.last_phase[chan][i]); let bin_phase = phase - self.last_phase[chan][i];
let freq = self.phase_to_frequency(i, bin_phase);
self.last_phase[chan][i] = phase; self.last_phase[chan][i] = phase;
self.analysis_out[chan][i] = Bin::new(freq, amp * 2.0); // yeah passing both and letting the constructor decide is ugly
// but it's fast to do so
self.analysis_out[chan][i] = B::new(freq, bin_phase, amp * 2.0);
} }
} }
@ -243,9 +284,11 @@ impl PhaseVocoder {
// SYNTHESIS // SYNTHESIS
for chan in 0..self.channels { for chan in 0..self.channels {
for i in 0..self.frame_size { for i in 0..self.frame_size {
let amp = self.synthesis_in[chan][i].amp; let amp = self.synthesis_in[chan][i].amp();
let freq = self.synthesis_in[chan][i].freq; // passing self as a param is slightly ugly but hey
let phase = self.frequency_to_phase(freq); // it works
let phase = self.synthesis_in[chan][i].phase(self);
self.sum_phase[chan][i] += phase; self.sum_phase[chan][i] += phase;
let phase = self.sum_phase[chan][i]; let phase = self.sum_phase[chan][i];
@ -318,7 +361,7 @@ impl PhaseVocoder {
} }
#[cfg(test)] #[cfg(test)]
fn identity(channels: usize, bins: usize, input: &[Vec<Bin>], output: &mut [Vec<Bin>]) { fn identity(channels: usize, bins: usize, input: &[Vec<FreqBin>], output: &mut [Vec<FreqBin>]) {
for i in 0..channels { for i in 0..channels {
for j in 0..bins { for j in 0..bins {
output[i][j] = input[i][j]; output[i][j] = input[i][j];

View File

@ -5,7 +5,7 @@ pub fn generate_vocoder(sample_rate: u32) -> PhaseVocoder {
} }
// From https://github.com/nwoeanhinnogaehr/pvoc-plugins/blob/master/src/plugins/pitchshifter.rs // From https://github.com/nwoeanhinnogaehr/pvoc-plugins/blob/master/src/plugins/pitchshifter.rs
use pvoc::{Bin, PhaseVocoder}; use pvoc::{FreqBin, PhaseVocoder};
pub fn pitch_shift<const LEN: usize>( pub fn pitch_shift<const LEN: usize>(
pvoc: &mut PhaseVocoder, pvoc: &mut PhaseVocoder,
input: &[f32], input: &[f32],
@ -17,7 +17,7 @@ pub fn pitch_shift<const LEN: usize>(
pvoc.process( pvoc.process(
&[&input], &[&input],
&mut [&mut output], &mut [&mut output],
|channels: usize, bins: usize, input: &[Vec<Bin>], output: &mut [Vec<Bin>]| { |channels: usize, bins: usize, input: &[Vec<FreqBin>], output: &mut [Vec<FreqBin>]| {
for i in 0..channels { for i in 0..channels {
for j in 0..bins / 2 { for j in 0..bins / 2 {
let index = ((j as f64) * shift) as usize; let index = ((j as f64) * shift) as usize;