[opiate, pvoc] implement changing phase and not frequency
This commit is contained in:
parent
be46998301
commit
43a66b4769
3 changed files with 99 additions and 52 deletions
|
@ -4,7 +4,7 @@
|
|||
// morphing algorithm from https://ccrma.stanford.edu/~jhsu/421b/
|
||||
|
||||
use baseplug::{Plugin, ProcessContext};
|
||||
use pvoc::{Bin, PhaseVocoder};
|
||||
use pvoc::{PhaseBin, PhaseVocoder};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use utils::logs::*;
|
||||
|
||||
|
@ -24,7 +24,7 @@ impl Default for OpiateModel {
|
|||
}
|
||||
|
||||
struct Opiate {
|
||||
pvoc: PhaseVocoder,
|
||||
pvoc: PhaseVocoder<PhaseBin>,
|
||||
out_0: Vec<f32>,
|
||||
out_1: Vec<f32>,
|
||||
out_2: Vec<f32>,
|
||||
|
@ -68,10 +68,10 @@ impl Plugin for Opiate {
|
|||
}
|
||||
|
||||
let out = &mut [
|
||||
&mut self.out_0[..],
|
||||
&mut self.out_1[..],
|
||||
&mut self.out_2[..],
|
||||
&mut self.out_3[..],
|
||||
&mut self.out_0[0..ctx.nframes],
|
||||
&mut self.out_1[0..ctx.nframes],
|
||||
&mut self.out_2[0..ctx.nframes],
|
||||
&mut self.out_3[0..ctx.nframes],
|
||||
][..];
|
||||
|
||||
let morph = model.morph[0] as f64;
|
||||
|
@ -79,27 +79,28 @@ impl Plugin for Opiate {
|
|||
self.pvoc.process(
|
||||
input,
|
||||
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 {
|
||||
// TODO Check if working with the frequencies is the same as working with the phase
|
||||
// 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
|
||||
|
||||
// left
|
||||
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 phases = input[0][j].freq - (input[0][j].freq * morph);
|
||||
let phases2 = input[2][j].freq - (input[2][j].freq * imorph);
|
||||
let phases = input[0][j].phase - (input[0][j].phase * morph);
|
||||
let phases2 = input[2][j].phase - (input[2][j].phase * imorph);
|
||||
|
||||
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 mags2 = imorph * (1.0 - input[3][j].amp) + input[3][j].amp;
|
||||
let phases = input[1][j].freq - (input[1][j].freq * morph);
|
||||
let phases2 = input[3][j].freq - (input[3][j].freq * imorph);
|
||||
let phases = input[1][j].phase - (input[1][j].phase * morph);
|
||||
let phases2 = input[3][j].phase - (input[3][j].phase * imorph);
|
||||
|
||||
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 {
|
||||
output[0][i] = self.out_0[i];
|
||||
output[1][i] = self.out_1[i];
|
||||
|
||||
self.out_0[i] = 0.0;
|
||||
self.out_1[i] = 0.0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,32 +15,67 @@ use std::sync::Arc;
|
|||
#[allow(non_camel_case_types)]
|
||||
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.
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct Bin {
|
||||
pub struct FreqBin {
|
||||
pub freq: f64,
|
||||
pub amp: f64,
|
||||
}
|
||||
|
||||
impl Bin {
|
||||
pub fn new(freq: f64, amp: f64) -> Bin {
|
||||
Bin {
|
||||
freq: freq,
|
||||
amp: amp,
|
||||
}
|
||||
}
|
||||
pub fn empty() -> Bin {
|
||||
Bin {
|
||||
impl Bin for FreqBin {
|
||||
fn empty() -> Self {
|
||||
Self {
|
||||
freq: 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.
|
||||
///
|
||||
/// Roughly translated from http://blogs.zynaptiq.com/bernsee/pitch-shifting-using-the-ft/
|
||||
pub struct PhaseVocoder {
|
||||
pub struct PhaseVocoder<B: Bin = FreqBin> {
|
||||
channels: usize,
|
||||
sample_rate: f64,
|
||||
frame_size: usize,
|
||||
|
@ -61,11 +96,11 @@ pub struct PhaseVocoder {
|
|||
fft_in: Vec<c64>,
|
||||
fft_out: Vec<c64>,
|
||||
fft_scratch: Vec<c64>,
|
||||
analysis_out: Vec<Vec<Bin>>,
|
||||
synthesis_in: Vec<Vec<Bin>>,
|
||||
analysis_out: Vec<Vec<B>>,
|
||||
synthesis_in: Vec<Vec<B>>,
|
||||
}
|
||||
|
||||
impl PhaseVocoder {
|
||||
impl<B: Bin> PhaseVocoder<B> {
|
||||
/// Constructs a new phase vocoder.
|
||||
///
|
||||
/// `channels` is the number of channels of audio.
|
||||
|
@ -80,12 +115,7 @@ impl PhaseVocoder {
|
|||
///
|
||||
/// # Panics
|
||||
/// Panics if `frame_size` is `<= 1` after rounding.
|
||||
pub fn new(
|
||||
channels: usize,
|
||||
sample_rate: f64,
|
||||
frame_size: usize,
|
||||
time_res: usize,
|
||||
) -> PhaseVocoder {
|
||||
pub fn new(channels: usize, sample_rate: f64, frame_size: usize, time_res: usize) -> Self {
|
||||
let mut frame_size = frame_size / time_res * time_res;
|
||||
if frame_size == 0 {
|
||||
frame_size = time_res;
|
||||
|
@ -119,8 +149,8 @@ impl PhaseVocoder {
|
|||
fft_in: vec![c64::new(0.0, 0.0); frame_size],
|
||||
fft_out: vec![c64::new(0.0, 0.0); frame_size],
|
||||
fft_scratch: vec![],
|
||||
analysis_out: vec![vec![Bin::empty(); frame_size]; channels],
|
||||
synthesis_in: vec![vec![Bin::empty(); frame_size]; channels],
|
||||
analysis_out: vec![vec![B::empty(); frame_size]; channels],
|
||||
synthesis_in: vec![vec![B::empty(); frame_size]; channels],
|
||||
};
|
||||
pv.fft_scratch = vec![
|
||||
c64::new(0.0, 0.0);
|
||||
|
@ -182,10 +212,18 @@ impl PhaseVocoder {
|
|||
) -> usize
|
||||
where
|
||||
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!(output.len(), self.channels);
|
||||
assert_eq!(
|
||||
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
|
||||
for chan in 0..input.len() {
|
||||
|
@ -205,7 +243,7 @@ impl PhaseVocoder {
|
|||
// This may be removed in a future release.
|
||||
for synthesis_channel in self.synthesis_in.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 {
|
||||
let x = self.fft_out[i];
|
||||
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.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
|
||||
for chan in 0..self.channels {
|
||||
for i in 0..self.frame_size {
|
||||
let amp = self.synthesis_in[chan][i].amp;
|
||||
let freq = self.synthesis_in[chan][i].freq;
|
||||
let phase = self.frequency_to_phase(freq);
|
||||
let amp = self.synthesis_in[chan][i].amp();
|
||||
// passing self as a param is slightly ugly but hey
|
||||
// it works
|
||||
let phase = self.synthesis_in[chan][i].phase(self);
|
||||
|
||||
self.sum_phase[chan][i] += phase;
|
||||
let phase = self.sum_phase[chan][i];
|
||||
|
||||
|
@ -318,7 +361,7 @@ impl PhaseVocoder {
|
|||
}
|
||||
|
||||
#[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 j in 0..bins {
|
||||
output[i][j] = input[i][j];
|
||||
|
|
|
@ -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
|
||||
use pvoc::{Bin, PhaseVocoder};
|
||||
use pvoc::{FreqBin, PhaseVocoder};
|
||||
pub fn pitch_shift<const LEN: usize>(
|
||||
pvoc: &mut PhaseVocoder,
|
||||
input: &[f32],
|
||||
|
@ -17,7 +17,7 @@ pub fn pitch_shift<const LEN: usize>(
|
|||
pvoc.process(
|
||||
&[&input],
|
||||
&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 j in 0..bins / 2 {
|
||||
let index = ((j as f64) * shift) as usize;
|
||||
|
|
Loading…
Reference in a new issue