use super::Generator;
use crate::algorithm::{Algorithm, AlgorithmCategory, AlgorithmMetadata};
use crate::math::Smoother;
use crate::vector::prelude::*;
use rill_core::traits::{ActionContext, ProcessResult};
use rill_core::Transcendental;
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum EnvelopeStage {
Attack,
Decay,
Sustain,
Release,
Off,
}
impl EnvelopeStage {
pub fn name(&self) -> &'static str {
match self {
EnvelopeStage::Attack => "Attack",
EnvelopeStage::Decay => "Decay",
EnvelopeStage::Sustain => "Sustain",
EnvelopeStage::Release => "Release",
EnvelopeStage::Off => "Off",
}
}
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum EnvelopeType {
ADSR, AR, ASR, }
pub struct EnvelopeGenerator<T: Transcendental> {
env_type: EnvelopeType,
attack: f32,
decay: f32,
sustain: ScalarVector1<T>,
release: f32,
stage: EnvelopeStage,
level: ScalarVector1<T>,
smoother: Smoother<T>,
attack_samples: usize,
decay_samples: usize,
release_samples: usize,
position: usize,
sample_rate: f32,
gate: bool,
}
impl<T: Transcendental> EnvelopeGenerator<T> {
pub fn adsr(attack: f32, decay: f32, sustain: T, release: f32) -> Self {
Self {
env_type: EnvelopeType::ADSR,
attack,
decay,
sustain: ScalarVector1::splat(sustain),
release,
stage: EnvelopeStage::Off,
level: ScalarVector1::splat(T::ZERO),
smoother: Smoother::new(T::from_f32(0.5)),
attack_samples: 0,
decay_samples: 0,
release_samples: 0,
position: 0,
sample_rate: 44100.0,
gate: false,
}
}
pub fn ar(attack: f32, release: f32) -> Self {
Self {
env_type: EnvelopeType::AR,
attack,
decay: 0.0,
sustain: ScalarVector1::splat(T::ZERO),
release,
stage: EnvelopeStage::Off,
level: ScalarVector1::splat(T::ZERO),
smoother: Smoother::new(T::from_f32(0.5)),
attack_samples: 0,
decay_samples: 0,
release_samples: 0,
position: 0,
sample_rate: 44100.0,
gate: false,
}
}
pub fn asr(attack: f32, sustain: T, release: f32) -> Self {
Self {
env_type: EnvelopeType::ASR,
attack,
decay: 0.0,
sustain: ScalarVector1::splat(sustain),
release,
stage: EnvelopeStage::Off,
level: ScalarVector1::splat(T::ZERO),
smoother: Smoother::new(T::from_f32(0.5)),
attack_samples: 0,
decay_samples: 0,
release_samples: 0,
position: 0,
sample_rate: 44100.0,
gate: false,
}
}
fn update_samples(&mut self) {
self.attack_samples = (self.attack * self.sample_rate) as usize;
self.decay_samples = (self.decay * self.sample_rate) as usize;
self.release_samples = (self.release * self.sample_rate) as usize;
}
pub fn trigger(&mut self) {
self.gate = true;
self.stage = EnvelopeStage::Attack;
self.position = 0;
}
pub fn release(&mut self) {
self.gate = false;
self.stage = EnvelopeStage::Release;
self.position = 0;
}
pub fn stage(&self) -> EnvelopeStage {
self.stage
}
pub fn is_active(&self) -> bool {
self.stage != EnvelopeStage::Off
}
}
impl<T: Transcendental> Algorithm<T> for EnvelopeGenerator<T> {
fn init(&mut self, sample_rate: f32) {
self.sample_rate = sample_rate;
self.update_samples();
self.reset();
}
fn reset(&mut self) {
self.stage = EnvelopeStage::Off;
self.level = ScalarVector1::splat(T::ZERO);
self.position = 0;
self.gate = false;
self.smoother.set_current(T::ZERO);
}
fn process(
&mut self,
input: Option<&[T]>,
output: &mut [T],
_ctx: &ActionContext,
) -> ProcessResult<()> {
let input = input.unwrap_or(&[]);
let len = input.len().min(output.len());
for i in 0..len {
let gate_signal = input[i];
if gate_signal.to_f32() > 0.5 && !self.gate {
self.trigger();
} else if gate_signal.to_f32() <= 0.5 && self.gate {
self.release();
}
match self.stage {
EnvelopeStage::Attack => {
let target = ScalarVector1::splat(T::from_f32(1.0));
self.level = self.level
+ (target - self.level)
* ScalarVector1::splat(T::from_f32(1.0 / self.attack_samples as f32));
self.position += 1;
if self.position >= self.attack_samples {
match self.env_type {
EnvelopeType::ADSR => self.stage = EnvelopeStage::Decay,
EnvelopeType::AR => self.stage = EnvelopeStage::Release,
EnvelopeType::ASR => self.stage = EnvelopeStage::Sustain,
}
self.position = 0;
}
}
EnvelopeStage::Decay => {
let target = self.sustain;
self.level = self.level
+ (target - self.level)
* ScalarVector1::splat(T::from_f32(1.0 / self.decay_samples as f32));
self.position += 1;
if self.position >= self.decay_samples {
self.stage = EnvelopeStage::Sustain;
self.position = 0;
}
}
EnvelopeStage::Sustain => {
self.level = self.sustain;
}
EnvelopeStage::Release => {
let target = ScalarVector1::splat(T::ZERO);
self.level = self.level
+ (target - self.level)
* ScalarVector1::splat(T::from_f32(1.0 / self.release_samples as f32));
self.position += 1;
if self.position >= self.release_samples {
self.stage = EnvelopeStage::Off;
self.position = 0;
}
}
EnvelopeStage::Off => {
self.level = ScalarVector1::splat(T::ZERO);
}
}
output[i] = self.smoother.process_sample(self.level.extract(0));
}
Ok(())
}
fn metadata(&self) -> AlgorithmMetadata {
AlgorithmMetadata {
name: match self.env_type {
EnvelopeType::ADSR => "ADSR Envelope",
EnvelopeType::AR => "AR Envelope",
EnvelopeType::ASR => "ASR Envelope",
},
category: AlgorithmCategory::Generator,
description: match self.env_type {
EnvelopeType::ADSR => "Attack-Decay-Sustain-Release envelope generator",
EnvelopeType::AR => "Attack-Release envelope generator (percussion)",
EnvelopeType::ASR => "Attack-Sustain-Release envelope generator (organ)",
},
author: "Rill",
version: env!("CARGO_PKG_VERSION"),
}
}
}
impl<T: Transcendental> Generator<T> for EnvelopeGenerator<T> {
fn phase(&self) -> T {
T::ZERO
}
fn set_phase(&mut self, _phase: T) {}
fn frequency(&self) -> f32 {
0.0
}
fn set_frequency(&mut self, _freq: f32) {}
fn amplitude(&self) -> T {
self.level.extract(0)
}
fn set_amplitude(&mut self, _amp: T) {}
}