use super::audiounit::*;
use super::math::*;
use super::sequencer::*;
use super::signal::*;
use super::*;
use duplicate::duplicate_item;
use funutd::dna::*;
use funutd::map3base::{Texture, TilingMode};
use funutd::*;
#[duplicate_item(
f48 Voice48;
[ f64 ] [ Voice64 ];
[ f32 ] [ Voice32 ];
)]
#[derive(Clone)]
struct Voice48 {
pub next_time: f48,
}
#[duplicate_item(
f48 Granular48 Voice48 AudioUnit48 Sequencer48;
[ f64 ] [ Granular64 ] [ Voice64 ] [ AudioUnit64 ] [ Sequencer64 ];
[ f32 ] [ Granular32 ] [ Voice32 ] [ AudioUnit32 ] [ Sequencer32 ];
)]
#[derive(Clone)]
pub struct Granular48<
X: Fn(f48, f48, f48, f48, f48, f48) -> (f48, f48, Box<dyn AudioUnit48>) + Sync + Send + Clone,
> {
voices: Vec<Voice48>,
outputs: usize,
beat_length: f48,
beats_per_cycle: usize,
texture: Box<dyn Texture>,
jitter: f48,
texture_origin: Vec3a,
inner_radius: f48,
outer_radius: f48,
generator: X,
sequencer: Sequencer48,
sample_rate: f48,
time: f48,
rnd_seed: u64,
rnd: Rnd,
}
#[allow(clippy::unnecessary_cast)]
#[duplicate_item(
f48 Granular48 Voice48 AudioUnit48 Sequencer48;
[ f64 ] [ Granular64 ] [ Voice64 ] [ AudioUnit64 ] [ Sequencer64 ];
[ f32 ] [ Granular32 ] [ Voice32 ] [ AudioUnit32 ] [ Sequencer32 ];
)]
impl<
X: Fn(f48, f48, f48, f48, f48, f48) -> (f48, f48, Box<dyn AudioUnit48>) + Sync + Send + Clone,
> Granular48<X>
{
pub fn new(
outputs: usize,
voices: usize,
beat_length: f48,
beats_per_cycle: usize,
texture_seed: u64,
inner_radius: f48,
outer_radius: f48,
jitter: f48,
generator: X,
) -> Self {
let voice_vector = vec![Voice48 { next_time: 0.0 }; voices];
let mut dna = Dna::new(texture_seed);
let texture = funutd::map3gen::genmap3(100.0, TilingMode::Z, &mut dna);
let mut granular = Self {
voices: voice_vector,
outputs,
beat_length,
beats_per_cycle,
texture,
jitter,
texture_origin: vec3a(0.0, 0.0, 0.0),
inner_radius,
outer_radius,
generator,
sequencer: Sequencer48::new(DEFAULT_SR, outputs),
sample_rate: DEFAULT_SR as f48,
time: 0.0,
rnd_seed: texture_seed,
rnd: Rnd::from_u64(texture_seed),
};
granular.reset(None);
granular
}
fn helix_position(&mut self, voice: usize, time: f48) -> Vec3a {
let cycle_length = self.beat_length * self.beats_per_cycle as f48;
let cycle = (time / cycle_length).floor();
let cycle_start = cycle * cycle_length;
let z_depth = 1.0;
let cycle_d = (time - cycle_start) / cycle_length;
let z = cycle_d * z_depth;
let beat = cycle_d * self.beats_per_cycle as f48;
let voice_d = if self.voices.len() == 1 {
0.5
} else {
voice as f48 / (self.voices.len() - 1) as f48
};
let r = lerp(self.inner_radius, self.outer_radius, voice_d);
let x = cos(beat * TAU as f48) * r;
let y = sin(beat * TAU as f48) * r;
let random = vec3a(
self.rnd.f32() * 2.0 - 1.0,
self.rnd.f32() * 2.0 - 1.0,
self.rnd.f32() * 2.0 - 1.0,
) * self.jitter as f32;
self.texture_origin + vec3a(x as f32, y as f32, z as f32) + random
}
fn instantiate(&mut self, voice: usize) {
let t = self.voices[voice].next_time;
let position = self.helix_position(voice, t);
let v = self.texture.at(position);
let voice_d = if self.voices.len() == 1 {
0.5
} else {
voice as f48 / (self.voices.len() - 1) as f48
};
let (grain_length, envelope_length, mut grain) = (self.generator)(
t,
t / self.beat_length,
voice_d * 2.0 - 1.0,
v.x.clamp(-1.0, 1.0) as f48,
v.y.clamp(-1.0, 1.0) as f48,
v.z.clamp(-1.0, 1.0) as f48,
);
assert!(envelope_length >= 0.0);
assert!(envelope_length < grain_length);
if t == 0.0 {
assert!(voice == 0);
for i in 1..self.voices.len() {
self.voices[i].next_time =
(grain_length - envelope_length) * i as f48 / self.voices.len() as f48;
}
}
self.voices[voice].next_time = t + grain_length - envelope_length;
grain.ping(false, AttoHash::new(self.rnd.u64()));
self.sequencer
.add_duration(t, grain_length, envelope_length, envelope_length, grain);
}
fn instantiate_voices(&mut self, before_time: f48) {
for voice in 0..self.voices.len() {
while self.voices[voice].next_time < before_time {
self.instantiate(voice);
}
}
}
}
#[allow(clippy::unnecessary_cast)]
#[duplicate_item(
f48 Granular48 Thread48 AudioUnit48 Sequencer48;
[ f64 ] [ Granular64 ] [ Thread64 ] [ AudioUnit64 ] [ Sequencer64 ];
[ f32 ] [ Granular32 ] [ Thread32 ] [ AudioUnit32 ] [ Sequencer32 ];
)]
impl<
X: Fn(f48, f48, f48, f48, f48, f48) -> (f48, f48, Box<dyn AudioUnit48>) + Sync + Send + Clone,
> AudioUnit48 for Granular48<X>
{
fn reset(&mut self, sample_rate: Option<f64>) {
self.sequencer.reset(sample_rate);
self.sequencer.erase_future();
if let Some(rate) = sample_rate {
self.sample_rate = rate as f48;
}
for i in 0..self.voices.len() {
self.voices[i].next_time = 0.0;
}
self.time = 0.0;
self.rnd = Rnd::from_u64(self.rnd_seed);
}
fn tick(&mut self, input: &[f48], output: &mut [f48]) {
self.time += 1.0 / self.sample_rate;
self.instantiate_voices(self.time);
self.sequencer.tick(input, output);
}
fn process(&mut self, size: usize, input: &[&[f48]], output: &mut [&mut [f48]]) {
self.time += size as f48 / self.sample_rate;
self.instantiate_voices(self.time);
self.sequencer.process(size, input, output);
}
fn get_id(&self) -> u64 {
const ID: u64 = 72;
ID
}
fn set_hash(&mut self, hash: u64) {
self.rnd_seed = hash;
self.rnd = Rnd::from_u64(self.rnd_seed);
}
fn inputs(&self) -> usize {
0
}
fn outputs(&self) -> usize {
self.outputs
}
fn route(&mut self, input: &SignalFrame, frequency: f64) -> SignalFrame {
self.sequencer.route(input, frequency)
}
fn footprint(&self) -> usize {
std::mem::size_of::<Self>()
}
}