1
0
Fork 0

Add a second just as bad mode to Puberty Simulator

This commit is contained in:
Robbert van der Helm 2022-05-09 16:49:43 +02:00
parent c6acdfa020
commit 743998e388

View file

@ -87,7 +87,11 @@ enum PitchShiftingMode {
/// Directly linearly interpolate sine and cosine waves from different bins. This obviously /// Directly linearly interpolate sine and cosine waves from different bins. This obviously
/// sounds very bad, but it also sounds kind of hilarious. /// sounds very bad, but it also sounds kind of hilarious.
#[name = "Very broken"] #[name = "Very broken"]
VeryBroken, InterpolateRectangular,
/// The same as `InterpolateRectangular`, but interpolating the polar forms instead. This sounds
/// slightly better, which actually ends up making it sound a lot worse.
#[name = "Also very broken"]
InterpolatePolar,
} }
impl Default for PubertySimulator { impl Default for PubertySimulator {
@ -146,7 +150,7 @@ impl Default for PubertySimulatorParams {
) )
.with_value_to_string(power_of_two_val2str) .with_value_to_string(power_of_two_val2str)
.with_string_to_value(power_of_two_str2val), .with_string_to_value(power_of_two_str2val),
mode: EnumParam::new("Mode", PitchShiftingMode::VeryBroken), mode: EnumParam::new("Mode", PitchShiftingMode::InterpolateRectangular),
} }
} }
} }
@ -263,37 +267,42 @@ impl Plugin for PubertySimulator {
.process_with_scratch(real_fft_buffer, &mut self.complex_fft_buffer, &mut []) .process_with_scratch(real_fft_buffer, &mut self.complex_fft_buffer, &mut [])
.unwrap(); .unwrap();
// This simply interpolates between the complex sinusoids from the frequency bins // TODO: Move this to helper functions. These functions capture a lot of variables
// for this bin's frequency scaled by the octave pitch multiplies. The iteration // here so that might require some work. And branch preductors are probably
// order dependson the pitch shifting direction since we're doing it in place. // good enough to be able to put the match inside of the `process_bin`
// function, but it seems preferable to have it outside of the loop.
let num_bins = self.complex_fft_buffer.len(); let num_bins = self.complex_fft_buffer.len();
let mut process_bin = match self.params.mode.value() { match self.params.mode.value() {
PitchShiftingMode::VeryBroken => |bin_idx| { PitchShiftingMode::InterpolateRectangular => {
// This simply interpolates the sine and cosine waves composing the complex
// sinusoids from the frequency bins to neighbouring frequency bins scaled
// by the octave pitch multiplies. The iteration order dependson the pitch
// shifting direction since we're doing it in place.
let mut process_bin = |bin_idx| {
let frequency = bin_idx as f32 / window_size as f32 * sample_rate; let frequency = bin_idx as f32 / window_size as f32 * sample_rate;
let target_frequency = frequency * frequency_multiplier; let target_frequency = frequency * frequency_multiplier;
// Simple linear interpolation // Simple linear interpolation
let target_bin = target_frequency / sample_rate * window_size as f32; let target_bin = target_frequency / sample_rate * window_size as f32;
let target_bin_low = target_bin.floor() as usize; let target_bin_floor = target_bin.floor() as usize;
let target_bin_high = target_bin.ceil() as usize; let target_bin_ceil = target_bin.ceil() as usize;
let target_low_t = target_bin % 1.0; let target_floor_t = target_bin % 1.0;
let target_high_t = 1.0 - target_low_t; let target_ceil_t = 1.0 - target_floor_t;
let target_low = self let target_floor = self
.complex_fft_buffer .complex_fft_buffer
.get(target_bin_low) .get(target_bin_floor)
.copied() .copied()
.unwrap_or_default(); .unwrap_or_default();
let target_high = self let target_ceil = self
.complex_fft_buffer .complex_fft_buffer
.get(target_bin_high) .get(target_bin_ceil)
.copied() .copied()
.unwrap_or_default(); .unwrap_or_default();
self.complex_fft_buffer[bin_idx] = (target_low * target_low_t self.complex_fft_buffer[bin_idx] = (target_floor * target_floor_t
+ target_high * target_high_t) + target_ceil * target_ceil_t)
* 3.0 // Random extra gain, not sure * 3.0 // Random extra gain, not sure
* gain_compensation; * gain_compensation;
},
}; };
if frequency_multiplier >= 1.0 { if frequency_multiplier >= 1.0 {
@ -305,6 +314,57 @@ impl Plugin for PubertySimulator {
process_bin(bin_idx); process_bin(bin_idx);
} }
} }
}
PitchShiftingMode::InterpolatePolar => {
// Same as the above, but interpolating in the polar form instead. While
// this does sound more correct it doesn't sound nearly as hilarious, and it
// just sounds bad at this point. But maybe there's some use for this.
let mut process_bin = |bin_idx| {
let frequency = bin_idx as f32 / window_size as f32 * sample_rate;
let target_frequency = frequency * frequency_multiplier;
// Simple linear interpolation
let target_bin = target_frequency / sample_rate * window_size as f32;
let target_bin_floor = target_bin.floor() as usize;
let target_bin_ceil = target_bin.ceil() as usize;
let target_floor_t = target_bin % 1.0;
let target_ceil_t = 1.0 - target_floor_t;
let target_floor = self
.complex_fft_buffer
.get(target_bin_floor)
.copied()
.unwrap_or_default();
let target_ceil = self
.complex_fft_buffer
.get(target_bin_ceil)
.copied()
.unwrap_or_default();
let target_floor_magnitude = target_floor.norm();
let target_floor_phase = target_floor.arg();
let target_ceil_magnitude = target_ceil.norm();
let target_ceil_phase = target_ceil.arg();
self.complex_fft_buffer[bin_idx] = Complex32::from_polar(
(target_floor_magnitude * target_floor_t)
+ (target_ceil_magnitude * target_ceil_t),
(target_floor_phase * target_floor_t)
+ (target_ceil_phase * target_ceil_t),
) * 3.0 // Random extra gain, not sure
* gain_compensation;
};
if frequency_multiplier >= 1.0 {
for bin_idx in 0..num_bins {
process_bin(bin_idx);
}
} else {
for bin_idx in (0..num_bins).rev() {
process_bin(bin_idx);
}
}
}
}
// Make sure the imaginary components on the first and last bin are zero // Make sure the imaginary components on the first and last bin are zero
self.complex_fft_buffer[0].im = 0.0; self.complex_fft_buffer[0].im = 0.0;