use super::RegionPair;
use crate::prelude::*;
#[non_exhaustive]
pub(crate) struct Oscillator {
loop_mode: LoopMode,
end: i32,
start_loop: i32,
end_loop: i32,
root_key: i32,
tune: f32,
pitch_change_scale: f32,
sample_rate_ratio: f32,
looping: bool,
position_fp: i64,
}
impl Oscillator {
const FRAC_BITS: i32 = 24;
const FRAC_UNIT: i64 = 1_i64 << Oscillator::FRAC_BITS;
const FP_TO_SAMPLE: f32 = 1_f32 / (32768 * Oscillator::FRAC_UNIT) as f32;
pub(crate) fn new(settings: &SynthesizerSettings, region: &RegionPair) -> Self {
let sample_rate = region.instrument.sample_sample_rate;
let loop_mode = region.get_sample_modes();
let start = region.get_sample_start();
let end = region.get_sample_end();
let start_loop = region.get_sample_start_loop();
let end_loop = region.get_sample_end_loop();
let root_key = region.get_root_key();
let coarse_tune = region.get_coarse_tune();
let fine_tune = region.get_fine_tune();
let scale_tuning = region.get_scale_tuning();
let tune = coarse_tune as f32 + 0.01_f32 * fine_tune as f32;
let pitch_change_scale = 0.01_f32 * scale_tuning as f32;
let sample_rate_ratio = sample_rate as f32 / settings.sample_rate as f32;
let looping = loop_mode != LoopMode::NoLoop;
let position_fp = (start as i64) << Oscillator::FRAC_BITS;
Self {
loop_mode,
end,
start_loop,
end_loop,
root_key,
tune,
pitch_change_scale,
sample_rate_ratio,
looping,
position_fp,
}
}
pub(crate) fn release(&mut self) {
if self.loop_mode == LoopMode::LoopUntilNoteOff {
self.looping = false;
}
}
pub(crate) fn process(&mut self, data: &[i16], block: &mut [f32], pitch: f32) -> bool {
let pitch_change = self.pitch_change_scale * (pitch - self.root_key as f32) + self.tune;
let pitch_ratio = self.sample_rate_ratio * 2_f32.powf(pitch_change / 12_f32);
self.fill_block(data, block, pitch_ratio as f64)
}
fn fill_block(&mut self, data: &[i16], block: &mut [f32], pitch_ratio: f64) -> bool {
let pitch_ratio_fp = (Oscillator::FRAC_UNIT as f64 * pitch_ratio) as i64;
if self.looping {
self.fill_block_continuous(data, block, pitch_ratio_fp)
} else {
self.fill_block_no_loop(data, block, pitch_ratio_fp)
}
}
fn fill_block_no_loop(&mut self, data: &[i16], block: &mut [f32], pitch_ratio_fp: i64) -> bool {
for t in 0..block.len() {
let index = (self.position_fp >> Oscillator::FRAC_BITS) as usize;
if index >= self.end as usize {
if t > 0 {
let len = block.len();
block[t..len].fill(0_f32);
return true;
} else {
return false;
}
}
let x1 = data[index] as i64;
let x2 = data[index + 1] as i64;
let a_fp = self.position_fp & (Oscillator::FRAC_UNIT - 1);
block[t] = Oscillator::FP_TO_SAMPLE
* ((x1 << Oscillator::FRAC_BITS) + a_fp * (x2 - x1)) as f32;
self.position_fp += pitch_ratio_fp;
}
true
}
fn fill_block_continuous(
&mut self,
data: &[i16],
block: &mut [f32],
pitch_ratio_fp: i64,
) -> bool {
let end_loop_fp = (self.end_loop as i64) << Oscillator::FRAC_BITS;
let loop_length = (self.end_loop - self.start_loop) as i64;
let loop_length_fp = loop_length << Oscillator::FRAC_BITS;
for sample in block.iter_mut() {
if self.position_fp >= end_loop_fp {
self.position_fp -= loop_length_fp;
}
let index1 = (self.position_fp >> Oscillator::FRAC_BITS) as usize;
let mut index2 = index1 + 1;
if index2 >= self.end_loop as usize {
index2 -= loop_length as usize;
}
let x1 = data[index1] as i64;
let x2 = data[index2] as i64;
let a_fp = self.position_fp & (Oscillator::FRAC_UNIT - 1);
*sample = Oscillator::FP_TO_SAMPLE
* ((x1 << Oscillator::FRAC_BITS) + a_fp * (x2 - x1)) as f32;
self.position_fp += pitch_ratio_fp;
}
true
}
}