master
Ondřej Hruška 4 years ago
parent 4b697f6664
commit 03ea8cef87
Signed by: MightyPork
GPG Key ID: 2C5FD5035250423D
  1. 152
      src/beep.rs
  2. 18
      src/beep/hack.rs
  3. 145
      src/beep/instrument.rs
  4. 355
      src/beep/mod.rs
  5. 86
      src/beep/orchestra.rs
  6. 376
      src/beep/osc.rs
  7. 68
      src/beep/sample.rs
  8. 89
      src/beep/tween.rs
  9. 4
      src/main.rs

@ -1,152 +0,0 @@
use std::sync::Arc;
use cpal::traits::{DeviceTrait, HostTrait, StreamTrait};
use std::ops::{Add, AddAssign, Sub};
use parking_lot::RwLock;
use std::time::Duration;
use std::thread;
use cpal::SampleFormat;
use crate::beep::osc::{Instrument, Oscillator};
pub mod osc;
struct AudioContext {
device: cpal::Device,
sample_format: SampleFormat,
config: cpal::StreamConfig
}
fn al_init() -> Result<AudioContext, anyhow::Error> {
let host = cpal::default_host();
let device = host.default_output_device()
.ok_or_else(|| anyhow::anyhow!("No audio output device!"))?;
let config = device.default_output_config()?;
Ok(AudioContext {
device,
sample_format: config.sample_format(),
config: config.into()
})
}
pub fn beep() -> Result<(), anyhow::Error> {
let ac = al_init()?;
match ac.sample_format {
cpal::SampleFormat::F32 => run::<f32>(&ac.device, &ac.config.into())?,
cpal::SampleFormat::I16 => run::<i16>(&ac.device, &ac.config.into())?,
cpal::SampleFormat::U16 => run::<u16>(&ac.device, &ac.config.into())?,
}
Ok(())
}
fn run<T>(device: &cpal::Device, config: &cpal::StreamConfig) -> Result<(), anyhow::Error>
where
T: cpal::Sample,
{
let sample_rate = config.sample_rate.0 as f32;
let channels = config.channels as usize;
println!("SR={}",sample_rate);
// Produce a sinusoid of maximum amplitude.
let mut ins = Instrument::new(vec![
Oscillator::new(440.0, 1.0, sample_rate),
//Oscillator::new(441.0, 1.0, sample_rate),
], sample_rate);
let instrument = Arc::new(parking_lot::RwLock::new(ins));
let err_fn = |err| eprintln!("an error occurred on stream: {}", err);
let m_instrument = instrument.clone();
let stream = device.build_output_stream(
config,
move |data: &mut [T], _: &cpal::OutputCallbackInfo| {
write_data(data, channels, &m_instrument)
},
err_fn,
)?;
stream.play()?;
// instrument.write().normalize(false);
// instrument.write().set_amp(1.0);
// thread::sleep(Duration::from_millis(500));
//
// instrument.write().set_amp(0.0);
// thread::sleep(Duration::from_millis(250));
instrument.write().set_amp(1.0);
instrument.write().normalize(true);
thread::sleep(Duration::from_millis(500));
instrument.write().set_amp(0.0);
thread::sleep(Duration::from_millis(250));
instrument.write().set_amp(0.5);
thread::sleep(Duration::from_millis(500));
instrument.write().set_amp(0.0);
thread::sleep(Duration::from_millis(250));
instrument.write().fade_amp(1.0, Duration::from_millis(10));
instrument.write().waveforms[0].fade_balance(-1.0, Duration::from_millis(10));
thread::sleep(Duration::from_millis(500));
instrument.write().fade_amp(0.0, Duration::from_millis(10));
thread::sleep(Duration::from_millis(500));
/*
//instrument.write().set_amp_fade(0.0, 4.0);
thread::sleep(Duration::from_millis(1000));
instrument.write().waveforms[1].fade_amp(0.0, Duration::from_millis(500));
thread::sleep(Duration::from_millis(500));
instrument.write().waveforms[0].fade_balance(-1.0, Duration::from_millis(1000));
thread::sleep(Duration::from_millis(1000));
instrument.write().waveforms[0].fade_balance(1.0, Duration::from_millis(2000));
instrument.write().waveforms[0].fade_freq(880.0, Duration::from_millis(1000));
thread::sleep(Duration::from_millis(2000));
instrument.write().waveforms[0].fade_balance(-1.0, Duration::from_millis(1000));
instrument.write().waveforms[0].fade_amp(0.0, Duration::from_millis(1000));
thread::sleep(Duration::from_millis(1000));
*/
// for _ in 0..10
// {
// instrument.write().harmonics[0].set_balance_fade(-1.0, 0.1);
// thread::sleep(Duration::from_millis(100));
// instrument.write().harmonics[0].set_balance_fade(1.0, 0.1);
// thread::sleep(Duration::from_millis(100));
// }
//
// instrument.write().harmonics[0].set_balance_fade(0.0, 0.5);
// instrument.write().harmonics[0].set_amp_fade(0.0, 0.05);
// thread::sleep(Duration::from_millis(200));
Ok(())
}
fn write_data<T>(output: &mut [T], channels: usize, instrument: &Arc<RwLock<Instrument>>)
where
T: cpal::Sample,
{
let mut instrument = instrument.write();
for frame in output.chunks_mut(channels) {
let sample = instrument.sample();
if channels == 1 {
frame[0] = cpal::Sample::from::<f32>(&((sample.left + sample.right) / 2.0));
} else {
frame[0] = cpal::Sample::from::<f32>(&sample.left);
frame[1] = cpal::Sample::from::<f32>(&sample.right);
}
}
}

@ -0,0 +1,18 @@
/// Work-around for `f32::clamp()` being unstable
pub(crate) trait StableClamp {
fn clamp_(self, min: f32, max: f32) -> f32;
}
impl StableClamp for f32 {
fn clamp_(self, min: f32, max: f32) -> f32 {
assert!(min <= max);
let mut x = self;
if x < min {
x = min;
}
if x > max {
x = max;
}
x
}
}

@ -0,0 +1,145 @@
use crate::beep::osc::HarmonicOscillator;
use crate::beep::tween::{Tween, GainScale};
use std::time::Duration;
use crate::beep::sample::StereoSample;
use crate::beep::hack::StableClamp;
/// Waveform generator with multiple frequencies / waveforms
#[derive(Clone,Debug)]
pub struct Instrument {
/// Waveforms to combine to produce the final sound
pub waveforms: Vec<HarmonicOscillator>,
/// Balance fader -1..1
balance: Tween,
/// 0-1 left gain
pub(crate) ratio_left : f32,
/// 0-1 right gain
pub(crate) ratio_right : f32,
/// Master gain
gain: Tween,
}
impl Instrument {
pub fn new(mut waveforms: Vec<HarmonicOscillator>, sample_rate: f32) -> Self {
for (n, o) in waveforms.iter_mut().enumerate() {
o.set_sample_rate(sample_rate);
o.set_freq_imm(sample_rate * (n as f32 + 1.0));
}
let mut o = Self {
waveforms,
balance: Tween {
actual: 0.0,
target: 0.0,
min: -1.0,
max: 1.0,
step: None,
sample_rate
},
ratio_left: 0.7,
ratio_right: 0.7,
gain: Tween {
actual: 0.0,
target: 0.0,
step: None,
min: 0.0,
max: 1.0,
sample_rate
}
};
o.update_side_ratios();
o
}
/// Update the pre-computed channel gain variables
pub fn update_side_ratios(&mut self) {
let angle = ((1.0 + self.balance.actual) / 2.0) * std::f32::consts::FRAC_PI_2;
self.ratio_left = angle.sin();
self.ratio_right = angle.cos();
}
/// Get amplitude
pub fn get_amp(&self) -> f32 {
self.gain.actual()
}
/// Set amplitude (0..1), change at the end of the current waveform to reduce popping
pub fn set_amp(&mut self, amp : f32) {
self.gain.set_at_crossover(amp);
}
/// Set amplitude (0..1) immediate
pub fn set_amp_imm(&mut self, amp : f32) {
self.gain.set_immediate(amp);
}
/// Fade amplitude over a given time
pub fn fade_amp(&mut self, amp : f32, time: Duration) {
self.gain.fade(amp, time);
}
/// Get balance value (-1..1)
pub fn get_balance(&self) -> f32 {
self.balance.actual
}
/// Set balance (-1..1), change at the end of the current waveform to reduce popping
pub fn set_balance(&mut self, balance : f32) {
self.balance.set_at_crossover(balance);
}
/// Set balance (-1..1) immediate
pub fn set_balance_imm(&mut self, balance : f32) {
self.balance.set_immediate(balance);
}
/// Fade to balance (-1..1) over a given time
pub fn fade_balance(&mut self, balance : f32, time: Duration) {
self.balance.fade(balance, time);
}
pub fn get_peak_amplitude(&self) -> f32 {
self.waveforms.iter()
.fold((0.0), |acc, item| acc + item.gain.actual)
}
/// Get base frequency value (1..22050)
pub fn get_freq(&self) -> f32 {
self.waveforms[0].get_freq() // TODO error checking
}
/// Set base and harmonics frequency (1..22050), change at the end of the current waveform to reduce popping
pub fn set_freq(&mut self, freq : f32) {
let mut f = freq;
self.waveforms.iter_mut().for_each(|w| {
w.set_freq(f);
});
}
/// Set base and harmonics frequency (1..22050) immediate
pub fn set_freq_imm(&mut self, freq : f32) {
let mut f = freq;
self.waveforms.iter_mut().for_each(|w| {
w.set_freq_imm(f);
});
}
/// Fade base and harmonics to frequency (1..22050) over a given time
pub fn fade_freq(&mut self, freq : f32, time: Duration) {
let mut f = freq;
self.waveforms.iter_mut().for_each(|w| {
w.fade_freq(f, time);
});
}
/// Get a raw sample (with panning)
pub fn sample(&mut self) -> f32 {
self.balance.tick();
self.gain.tick();
self.waveforms.iter_mut()
.fold(0.0, |mut acc, item| {
acc + item.sample()
}) * self.gain.actual
}
}

@ -0,0 +1,355 @@
use std::sync::Arc;
use cpal::traits::{DeviceTrait, HostTrait, StreamTrait};
use std::ops::{Add, AddAssign, Sub};
use parking_lot::RwLock;
use std::time::Duration;
use std::{thread, panic};
use cpal::SampleFormat;
use crate::beep::instrument::Instrument;
use crate::beep::orchestra::Orchestra;
use crate::beep::osc::HarmonicOscillator;
use std::panic::UnwindSafe;
mod hack;
pub mod sample;
pub mod tween;
pub mod osc;
pub mod instrument;
pub mod orchestra;
struct AudioContext {
device: cpal::Device,
sample_format: SampleFormat,
config: cpal::StreamConfig
}
fn al_init() -> Result<AudioContext, anyhow::Error> {
let host = cpal::default_host();
let device = host.default_output_device()
.ok_or_else(|| anyhow::anyhow!("No audio output device!"))?;
let config = device.default_output_config()?;
Ok(AudioContext {
device,
sample_format: config.sample_format(),
config: config.into()
})
}
pub fn beep() -> Result<(), anyhow::Error> {
let ac = al_init()?;
match ac.sample_format {
cpal::SampleFormat::F32 => run::<f32>(&ac.device, &ac.config.into())?,
cpal::SampleFormat::I16 => run::<i16>(&ac.device, &ac.config.into())?,
cpal::SampleFormat::U16 => run::<u16>(&ac.device, &ac.config.into())?,
}
Ok(())
}
fn run<T>(device: &cpal::Device, config: &cpal::StreamConfig) -> Result<(), anyhow::Error>
where
T: cpal::Sample,
{
let sr = config.sample_rate.0 as f32;
let channels = config.channels as usize;
println!("SR={}", sr);
// Produce a sinusoid of maximum amplitude.
let ins1 = Instrument::new(vec![
HarmonicOscillator::new(1.0, 1.0),
HarmonicOscillator::new(0.5, 5.0),
], sr);
let ins2 = Instrument::new(vec![
HarmonicOscillator::new(1.0, 1.0),
HarmonicOscillator::new(0.5, 5.0),
], sr);
let mut orch = Orchestra::new(vec![ins1, ins2], sr);
orch.normalize(true);
let handle = Arc::new(parking_lot::RwLock::new(orch));
let err_fn = |err| eprintln!("an error occurred on stream: {}", err);
let m_handle = handle.clone();
let stream = device.build_output_stream(
config,
move |data: &mut [T], _: &cpal::OutputCallbackInfo| {
write_data(data, channels, &m_handle)
},
err_fn,
)?;
stream.play()?;
const C3 : i32 = 28;
const C3X : i32 = 29;
const D3 : i32 = 30;
const D3X : i32 = 31;
const E3 : i32 = 32;
const F3 : i32 = 33;
const F3X : i32 = 34;
const G3 : i32 = 35;
const G3X : i32 = 36;
const A3 : i32 = 37;
const A3X : i32 = 38;
const B3 : i32 = 39;
const C4 : i32 = 40;
const C4X : i32 = 41;
const D4 : i32 = 42;
const D4X : i32 = 43;
const E4 : i32 = 44;
const F4 : i32 = 45;
const F4X : i32 = 46;
const G4 : i32 = 47;
const G4X : i32 = 48;
const A4 : i32 = 49;
const A4X : i32 = 50;
const B4 : i32 = 51;
const C5 : i32 = 52;
const C5X : i32 = 53;
const D5 : i32 = 54;
const D5X : i32 = 55;
const E5 : i32 = 56;
const F5 : i32 = 57;
const F5X : i32 = 58;
const G5 : i32 = 59;
const G5X : i32 = 60;
const A5 : i32 = 61;
const A5X : i32 = 62;
const B5 : i32 = 63;
let key2freq = |key| {
match key {
// C0 => 16.35,
// C0X => 17.32,
// D0 => 18.35,
// D0X => 19.45,
// E0 => 20.60,
// F0 => 21.83,
// F0X => 23.12,
// G0 => 24.50,
// G0X => 25.96,
// A0 => 27.50,
// A0X => 29.14,
// B0 => 30.87,
// C1 => 32.70,
// C1X => 34.65,
// D1 => 36.71,
// D1X => 38.89,
// E1 => 41.20,
// F1 => 43.65,
// F1X => 46.25,
// G1 => 49.00,
// G1X => 51.91,
// A1 => 55.00,
// A1X => 58.27,
// B1 => 61.74,
// C2 => 65.41,
// C2X => 69.30,
// D2 => 73.42,
// D2X => 77.78,
// E2 => 82.41,
// F2 => 87.31,
// F2X => 92.50,
// G2 => 98.00,
// G2X => 103.83,
// A2 => 110.00,
// A2X => 116.54,
// B2 => 123.47,
C3 => 130.81,
C3X => 138.59,
D3 => 146.83,
D3X => 155.56,
E3 => 164.81,
F3 => 174.61,
F3X => 185.00,
G3 => 196.00,
G3X => 207.65,
A3 => 220.00,
A3X => 233.08,
B3 => 246.94,
C4 => 261.63,
C4X => 277.18,
D4 => 293.66,
D4X => 311.13,
E4 => 329.63,
F4 => 349.23,
F4X => 369.99,
G4 => 392.00,
G4X => 415.30,
A4 => 440.00,
A4X => 466.16,
B4 => 493.88,
C5 => 523.25,
C5X => 554.37,
D5 => 587.33,
D5X => 622.25,
E5 => 659.25,
F5 => 698.46,
F5X => 739.99,
G5 => 783.99,
G5X => 830.61,
A5 => 880.00,
A5X => 932.33,
B5 => 987.77,
// C6 => 1046.50,
// C6X => 1108.73,
// D6 => 1174.66,
// D6X => 1244.51,
// E6 => 1318.51,
// F6 => 1396.91,
// F6X => 1479.98,
// G6 => 1567.98,
// G6X => 1661.22,
// A6 => 1760.00,
// A6X => 1864.66,
// B6 => 1975.53,
// C7 => 2093.00,
// C7X => 2217.46,
// D7 => 2349.32,
// D7X => 2489.02,
// E7 => 2637.02,
// F7 => 2793.83,
// F7X => 2959.96,
// G7 => 3135.96,
// G7X => 3322.44,
// A7 => 3520.00,
// A7X => 3729.31,
// B7 => 3951.07,
// C8 => 4186.01,
// C8X => 4434.92,
// D8 => 4698.63,
// D8X => 4978.03,
// E8 => 5274.04,
// F8 => 5587.65,
// F8X => 5919.91,
// G8 => 6271.93,
// G8X => 6644.88,
// A8 => 7040.00,
// A8X => 7458.62,
// B8 => 7902.13,
_ => 440.0
}
};
// this is greensleeves painfully transcribed from the sheet here:
// https://www.guitarcommand.com/greensleeves-guitar-tab/
let alt = vec![
(2, A4),
(4, C5),
(2, D5),
(3, E5), (1, F5X), (2, E5),
(4, D5),
(2, B4),
(3, G4), (1, A4), (2, B4),
(4, C5),
(2, A4),
(3, A4), (1, G4X), (2, A4),
(4, B4),
(2, G4X),
(4, E4),
];
// Bass line, must be kept in sync with alt. Insert -1 notes for padding where needed.
let bass = vec![
(2, -1),
(6, A3),
(6, C4),
(6, G3),
(6, G4),
(6, A3),
(6, A4),
(6, E3),
(6, E4),
];
const D_ONOFF: Duration = Duration::from_millis(10);
let bpm = 25.0;
let mut ticks_alt = 0;
let mut ticks_bass = 0;
let mut alt_iter = alt.iter();
let mut bass_iter = bass.iter();
loop {
let mut wg = handle.write();
if ticks_alt == 0 {
if let Some((len, key)) = alt_iter.next() {
ticks_alt = *len;
if *key >= 0 {
let f = key2freq(*key);
wg.instruments[0].fade_amp(1.0, D_ONOFF);
wg.instruments[0].set_freq_imm(f);
} else {
wg.instruments[0].fade_amp(0.0, D_ONOFF);
}
}
}
if ticks_bass == 0 {
if let Some((len, key)) = bass_iter.next() {
ticks_bass = len - 1;
if *key >= 0 {
let f = key2freq(*key);
wg.instruments[1].fade_amp(0.5, D_ONOFF);
wg.instruments[1].set_freq_imm(f);
} else {
wg.instruments[1].fade_amp(0.0, D_ONOFF);
}
}
}
drop(wg);
if ticks_alt == 0 && ticks_bass == 0 {
break;
}
thread::sleep(Duration::from_secs_f32((60.0/bpm) / 16.0));
if ticks_alt > 0 {
ticks_alt -= 1;
}
if ticks_bass > 0 {
ticks_bass -= 1;
}
}
Ok(())
}
fn write_data<T>(output: &mut [T], channels: usize, handle: &Arc<RwLock<Orchestra>>)
where
T: cpal::Sample,
{
let mut wg = handle.write();
for frame in output.chunks_mut(channels) {
if channels == 1 {
let sample = wg.sample_mono();
frame[0] = cpal::Sample::from::<f32>(&sample);
} else {
let sample = wg.sample_stereo();
frame[0] = cpal::Sample::from::<f32>(&sample.left);
frame[1] = cpal::Sample::from::<f32>(&sample.right);
}
}
}

@ -0,0 +1,86 @@
use crate::beep::instrument::Instrument;
use crate::beep::tween::Tween;
use std::time::Duration;
use crate::beep::sample::StereoSample;
use crate::beep::hack::StableClamp;
/// A set of instruments
pub struct Orchestra {
pub instruments: Vec<Instrument>,
/// Normalize the output to produce constant amplitude of 1.0 (instead of clipping)
normalize: bool,
/// Sample rate, used to calculate time based effects
sample_rate : f32,
/// Master gain
gain: Tween,
}
impl Orchestra {
pub fn new(instruments: Vec<Instrument>, sample_rate: f32) -> Self {
let mut o = Self {
instruments,
normalize: false,
sample_rate,
gain: Tween {
actual: 1.0,
target: 1.0,
step: None,
min: 0.0,
max: 1.0,
sample_rate
}
};
o
}
pub fn normalize(&mut self, normalize: bool) {
self.normalize = normalize;
}
pub fn get_amp(&self) -> f32 {
self.gain.actual()
}
pub fn set_amp(&mut self, amp : f32) {
self.gain.set_immediate(amp);
}
pub fn fade_amp(&mut self, amp : f32, time: Duration) {
self.gain.fade(amp, time);
}
fn calc_gain(&self) -> f32 {
(if self.normalize {
1.0 / (
self.instruments.iter()
.fold(0.0, |acc, item| acc + item.get_peak_amplitude())
)
} else { 1.0 }) * self.gain.actual
}
pub fn sample_stereo(&mut self) -> StereoSample {
let gain = self.calc_gain();
self.instruments
.iter_mut()
.fold(StereoSample::default(), |mut acc, item| {
let s = item.sample() * gain;
acc.left += s * item.ratio_left;
acc.right += s * item.ratio_left;
acc
})
.clip()
}
pub fn sample_mono(&mut self) -> f32 {
let gain = self.calc_gain();
self.instruments
.iter_mut()
.fold(0.0, |mut acc, item| {
acc + item.sample() * gain
})
.clamp_(0.0, 1.0)
}
}

@ -1,184 +1,69 @@
use std::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign};
use std::sync::Arc;
use std::time::Duration;
use parking_lot::RwLock;
use hack::StableClamp;
mod hack {
/// Work-around for `f32::clamp()` being unstable
pub(super) trait StableClamp {
fn clamp_(self, min: f32, max: f32) -> f32;
}
impl StableClamp for f32 {
fn clamp_(self, min: f32, max: f32) -> f32 {
assert!(min <= max);
let mut x = self;
if x < min {
x = min;
}
if x > max {
x = max;
}
x
}
}
}
/// Value fader
#[derive(Clone,Debug)]
struct Tween {
/// Actual value
actual: f32,
/// Target value, approached by adding "step" to :actual" every sample
target: f32,
/// Value added to "actual" per sample. None = set to target at the end of a cycle
step: Option<f32>,
// Min value
min: f32,
// Max value
max: f32,
}
/// Scale function amplitude by a gain
trait GainScale {
fn scale_gain(self, gain: f32) -> Self;
}
impl GainScale for f32 {
/// Scale by gain 0-1
fn scale_gain(self, gain: f32) -> Self {
// TODO what scaling to use?
// self * (1.0 + gain * 9.0).log10()
// self * gain.sqrt()
self * gain
}
}
impl Tween {
/// Update values for the next sample
pub fn tick(&mut self) {
if let Some(step) = self.step {
let actual0 = self.actual;
self.actual += step;
if (self.target > actual0 && self.target <= self.actual)
|| (self.target <= actual0 && self.target > self.actual)
{
self.step = None;
self.actual = self.target;
}
}
}
/// Get actual value
pub fn actual(&self) -> f32 {
self.actual
}
/// Check if the change from actual to target is scheduled to happen instantly
/// at the end of a cycle.
pub fn is_change_scheduled_at_crossover(&self) -> bool {
self.target != self.actual
&& self.step.is_none()
}
/// Check if the fading is in progress
pub fn is_fading(&self) -> bool {
self.step.is_some()
}
/// Schedule change at crossover
fn set_at_crossover(&mut self, val: f32) {
self.target = val.clamp_(self.min, self.max);
self.step = None; // change at the earliest convenience
}
/// Start fading
fn fade(&mut self, val: f32, time: Duration, sample_rate: f32) {
self.target = val.clamp_(self.min, self.max);
let nsamples = (time.as_secs_f32() * sample_rate).ceil();
self.step = Some((self.target - self.actual) / nsamples);
}
/// Set value immediately
fn set_immediate(&mut self, val: f32) {
self.target = val;
self.actual = val;
self.step = None;
}
}
use crate::beep::tween::{Tween, GainScale};
use crate::beep::sample::StereoSample;
#[derive(Clone,Debug)]
pub struct Oscillator {
pub struct HarmonicOscillator {
/// Amplitude fader 0..1
gain: Tween,
pub(crate) gain: Tween,
/// Frequency fader 1..22050
freq: Tween,
/// Balance fader -1..1
balance: Tween,
/// Left channel multiplier
gain_left: f32,
/// Right channel multiplier
gain_right: f32,
/// Sample rate (Hz)
sample_rate: f32,
/// Phase increment per sample
phase_step: f32,
/// Phase 0-TAU
phase: f32,
/// Harmonic multiple
pub harmonic: f32,
}
impl Oscillator {
impl HarmonicOscillator {
/// New oscillator with a frequency and amplitude
pub fn new(freq: f32, amp: f32, sample_rate : f32) -> Self {
pub fn new(amp: f32, n: f32) -> Self {
let mut o = Self {
gain: Tween {
actual: amp,
target: amp,
min: 0.0,
max: 1.0,
step: None
step: None,
sample_rate: 0.0,
},
freq: Tween {
actual: freq,
target: freq,
actual: 440.0,
target: 440.0,
min: 1.0,
max: sample_rate / 2.0,
step: None
},
balance: Tween {
actual: 0.0,
target: 0.0,
min: -1.0,
max: 1.0,
step: None
max: 20000.0,
step: None,
sample_rate: 0.0,
},
phase_step: 0.0,
gain_left: 0.0,
gain_right: 0.0,
sample_rate,
phase: 0.0
sample_rate: 0.0,
phase: 0.0,
harmonic: n,
};
o.update_step();
o.update_gains(1.0);
o
}
pub(crate) fn set_sample_rate(&mut self, sample_rate : f32) {
self.sample_rate = sample_rate;
self.gain.sample_rate = sample_rate;
self.freq.sample_rate = sample_rate;
self.freq.max = sample_rate / 2.0;
self.update_step();
}
/// Update the step variable
fn update_step(&mut self) {
self.phase_step = (self.freq.actual / self.sample_rate) * std::f32::consts::TAU;
}
/// Update the pre-computed channel gain variables
fn update_gains(&mut self, gain_scale: f32) {
let angle = ((1.0 + self.balance.actual) / 2.0) * std::f32::consts::FRAC_PI_2;
let act = self.gain.actual * gain_scale;
self.gain_left = act.scale_gain(angle.sin());
self.gain_right = act.scale_gain(angle.cos());
}
/// Get actual amplitude (0..1)
pub fn get_amp(&self) -> f32 {
self.gain.actual
@ -189,24 +74,15 @@ impl Oscillator {
self.gain.set_at_crossover(amp);
}
/// Fade to amplitude (0..1) over a given time
pub fn fade_amp(&mut self, amp : f32, time: Duration) {
self.gain.fade(amp, time, self.sample_rate);
}
/// Get balance value (-1..1)
pub fn get_balance(&self) -> f32 {
self.balance.actual
/// Set amplitude (0..1) immediate
pub fn set_amp_imm(&mut self, amp : f32) {
self.gain.set_immediate(amp);
}
/// Set balance (-1..1), change at the end of the current waveform to reduce popping
pub fn set_balance(&mut self, balance : f32) {
self.balance.set_at_crossover(balance);
}
/// Fade to balance (-1..1) over a given time
pub fn fade_balance(&mut self, balance : f32, time: Duration) {
self.balance.fade(balance, time, self.sample_rate);
/// Fade to amplitude (0..1) over a given time
pub fn fade_amp(&mut self, amp : f32, time: Duration) {
self.gain.fade(amp, time);
}
/// Get frequency value (1..22050)
@ -216,27 +92,29 @@ impl Oscillator {
/// Set frequency (1..22050), change at the end of the current waveform to reduce popping
pub fn set_freq(&mut self, freq : f32) {
self.freq.set_at_crossover(freq);
self.freq.set_at_crossover(freq * self.harmonic);
self.update_step();
}
/// Set frequency (1..22050) immediate
pub fn set_freq_imm(&mut self, freq : f32) {
self.freq.set_immediate(freq * self.harmonic);
self.update_step();
}
/// Fade to frequency (1..22050) over a given time
pub fn fade_freq(&mut self, freq : f32, time: Duration) {
self.freq.fade(freq, time, self.sample_rate);
self.freq.fade(freq * self.harmonic, time);
self.update_step();
}
/// Take a sample. Mutable to update faders.
pub fn sample(&mut self, gain_scale: f32) -> StereoSample {
pub fn sample(&mut self) -> f32 {
if self.freq.is_fading() {
self.freq.tick();
self.update_step();
}
if self.balance.is_fading() || self.gain.is_fading() {
self.balance.tick();
self.gain.tick();
self.update_gains(gain_scale);
}
let ph0 = self.phase;
self.phase = (self.phase + self.phase_step) % std::f32::consts::TAU;
@ -244,12 +122,6 @@ impl Oscillator {
if self.phase < ph0 {
if self.gain.is_change_scheduled_at_crossover() {
self.gain.actual = self.gain.target;
self.update_gains(gain_scale);
}
if self.balance.is_change_scheduled_at_crossover() {
self.balance.actual = self.balance.target;
self.update_gains(gain_scale);
}
if self.freq.is_change_scheduled_at_crossover() {
@ -257,168 +129,8 @@ impl Oscillator {
}
}
let y = self.phase.sin() * self.gain.actual;
StereoSample {
left: self.gain_left * y,
right: self.gain_right * y,
}
}
}
/// Waveform generator with multiple frequencies / waveforms
#[derive(Clone,Debug)]
pub struct Instrument {
/// Waveforms to combine to produce the final sound
pub waveforms: Vec<Oscillator>,
/// Normalize the output to produce constant amplitude of 1.0 (instead of clipping)
normalize: bool,
sample_rate : f32,
master: Tween,
}
impl Instrument {
pub fn new(waveforms: Vec<Oscillator>, sample_rate: f32) -> Self {
let mut o = Self {
waveforms,
normalize: false,
sample_rate,
master: Tween {
actual: 1.0,
target: 1.0,
step: None,
min: 0.0,
max: 1.0
}
};
o
}
pub fn normalize(&mut self, normalize: bool) {
self.normalize = normalize;
// This is an artifact of how Oscillator works internally:
// The left/right gains are pre-computed and updated only when they change
if normalize {
self.force_normalize();
} else {
self.waveforms.iter_mut()
.for_each(|item| item.update_gains(1.0));
}
}
pub fn get_amp(&self) -> f32 {
self.master.actual()
}
pub fn set_amp(&mut self, amp : f32) {
self.master.set_immediate(amp);
}
pub fn fade_amp(&mut self, amp : f32, time: Duration) {
self.master.fade(amp, time, self.sample_rate);
}
fn force_normalize(&mut self) {
let gain_scale = 1.0 / self.waveforms.iter()
.fold((0.0), |acc, item| acc + item.gain.actual);
println!("Gain scale {}", gain_scale);
self.waveforms.iter_mut()
.for_each(|item| item.update_gains(gain_scale));
}
/// Get a sample (left, right)
pub fn sample(&mut self) -> StereoSample {
self.master.tick();
let gain_scaling = if self.normalize {
1.0 / self.waveforms.iter()
.fold((0.0), |acc, item| acc + item.gain.actual)
} else {
1.0
};
let (mut value, gains)= self.waveforms.iter_mut()
.fold((StereoSample::default(), 0.0), |mut acc, item| {
acc.0 += item.sample(gain_scaling);
acc.1 += item.gain.actual;
acc
});
if self.normalize {
value.left /= gains;
value.right /= gains;
}
value.left = value.left.scale_gain(self.master.actual).clamp_(-1.0, 1.0);
value.right = value.right.scale_gain(self.master.actual).clamp_(-1.0, 1.0);
value.clip()
}
}
#[derive(Default, Clone, Copy)]
pub struct StereoSample {
pub left: f32,
pub right: f32,
}
impl StereoSample {
pub fn clip(self) -> Self {
Self {
left: self.left.clamp_(-1.0, 1.0),
right: self.right.clamp_(-1.0, 1.0),
}
}
}
impl Add for StereoSample {
type Output = Self;
fn add(self, rhs: Self) -> Self::Output {
Self {
left: self.left + rhs.left,
right: self.right + rhs.right,
}
}
}
impl Div<f32> for StereoSample {
type Output = Self;
fn div(self, rhs: f32) -> Self::Output {
Self {
left: self.left / rhs,
right: self.right / rhs,
}
}
}
impl Mul<f32> for StereoSample {
type Output = Self;
fn mul(self, rhs: f32) -> Self::Output {
Self {
left: self.left * rhs,
right: self.right * rhs,
}
self.phase.sin() * self.gain.actual
}
}
impl AddAssign<Self> for StereoSample {
fn add_assign(&mut self, rhs: Self) {
*self = *self + rhs
}
}
impl DivAssign<f32> for StereoSample {
fn div_assign(&mut self, rhs: f32) {
*self = *self / rhs
}
}
impl MulAssign<f32> for StereoSample {
fn mul_assign(&mut self, rhs: f32) {
*self = *self * rhs
}
}

@ -0,0 +1,68 @@
use std::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign};
use crate::beep::hack::StableClamp;
#[derive(Default, Clone, Copy)]
pub struct StereoSample {
pub left: f32,
pub right: f32,
}
impl StereoSample {
pub fn clip(self) -> Self {
Self {
left: self.left.clamp_(-1.0, 1.0),
right: self.right.clamp_(-1.0, 1.0),
}
}
}
impl Add for StereoSample {
type Output = Self;
fn add(self, rhs: Self) -> Self::Output {
Self {
left: self.left + rhs.left,
right: self.right + rhs.right,
}
}
}
impl Div<f32> for StereoSample {
type Output = Self;
fn div(self, rhs: f32) -> Self::Output {
Self {
left: self.left / rhs,
right: self.right / rhs,
}
}
}
impl Mul<f32> for StereoSample {
type Output = Self;
fn mul(self, rhs: f32) -> Self::Output {
Self {
left: self.left * rhs,
right: self.right * rhs,
}
}
}
impl AddAssign<Self> for StereoSample {
fn add_assign(&mut self, rhs: Self) {
*self = *self + rhs
}
}
impl DivAssign<f32> for StereoSample {
fn div_assign(&mut self, rhs: f32) {
*self = *self / rhs
}
}
impl MulAssign<f32> for StereoSample {
fn mul_assign(&mut self, rhs: f32) {
*self = *self * rhs
}
}

@ -0,0 +1,89 @@
use crate::beep::hack::StableClamp;
use std::time::Duration;
/// Scale function amplitude by a gain
pub trait GainScale {
fn scale_gain(self, gain: f32) -> Self;
}
impl GainScale for f32 {
/// Scale by gain 0-1
fn scale_gain(self, gain: f32) -> Self {
// TODO what scaling to use?
// self * (1.0 + gain * 9.0).log10()
// self * gain.sqrt()
self * gain
}
}
/// Value fader
#[derive(Clone,Debug)]
pub(crate) struct Tween {
/// Actual value
pub actual: f32,
/// Target value, approached by adding "step" to :actual" every sample
pub target: f32,
/// Value added to "actual" per sample. None = set to target at the end of a cycle
pub step: Option<f32>,
// Min value
pub min: f32,
// Max value
pub max: f32,
/// Sample rate, used to calculate time based effects
pub sample_rate : f32,
}
impl Tween {
/// Update values for the next sample
pub fn tick(&mut self) {
if let Some(step) = self.step {
let actual0 = self.actual;
self.actual += step;
if (self.target > actual0 && self.target <= self.actual)
|| (self.target <= actual0 && self.target > self.actual)
{
self.step = None;
self.actual = self.target;
}
}
}
/// Get actual value
pub fn actual(&self) -> f32 {
self.actual
}
/// Check if the change from actual to target is scheduled to happen instantly
/// at the end of a cycle.
pub fn is_change_scheduled_at_crossover(&self) -> bool {
self.target != self.actual
&& self.step.is_none()
}
/// Check if the fading is in progress
pub fn is_fading(&self) -> bool {
self.step.is_some()
}
/// Schedule change at crossover
pub(crate) fn set_at_crossover(&mut self, val: f32) {
self.target = val.clamp_(self.min, self.max);
self.step = None; // change at the earliest convenience
}
/// Start fading
pub(crate) fn fade(&mut self, val: f32, time: Duration) {
self.target = val.clamp_(self.min, self.max);
let nsamples = (time.as_secs_f32() * self.sample_rate).ceil();
self.step = Some((self.target - self.actual) / nsamples);
}
/// Set value immediately
pub(crate) fn set_immediate(&mut self, val: f32) {
self.target = val.clamp_(self.min, self.max);
self.actual = self.target;
self.step = None;
}
}

@ -1,7 +1,9 @@
mod beep;
extern crate anyhow;
extern crate cpal;
mod beep;
fn main() -> Result<(), anyhow::Error> {
beep::beep()
}

Loading…
Cancel
Save