Skip to content

Commit

Permalink
Merge pull request #5 from jacobh/jacobh/amplifier
Browse files Browse the repository at this point in the history
Amplifier module
  • Loading branch information
Charlie Somerville committed Apr 3, 2020
2 parents 584e3ee + 6ba66ec commit 32f41d2
Show file tree
Hide file tree
Showing 9 changed files with 178 additions and 18 deletions.
86 changes: 85 additions & 1 deletion frontend/src/workspace.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ use yew::{html, Component, ComponentLink, Html, ShouldRender, Properties, NodeRe
use yew::components::Select;
use yew::events::ChangeData;

use mixlab_protocol::{ModuleId, TerminalId, InputId, OutputId, ModuleParams, SineGeneratorParams, ClientMessage, WindowGeometry, Coords, Indication, OutputDeviceParams, OutputDeviceIndication, FmSineParams};
use mixlab_protocol::{ModuleId, TerminalId, InputId, OutputId, ModuleParams, SineGeneratorParams, ClientMessage, WindowGeometry, Coords, Indication, OutputDeviceParams, OutputDeviceIndication, FmSineParams, AmplifierParams};

use crate::{App, AppMsg, State};
use crate::util::{callback_ex, stop_propagation, prevent_default};
Expand Down Expand Up @@ -421,12 +421,14 @@ impl Workspace {
ModuleParams::OutputDevice(_) => vec![NodeRef::default()],
ModuleParams::Mixer2ch(()) => vec![NodeRef::default(), NodeRef::default()],
ModuleParams::FmSine(_) => vec![NodeRef::default()],
ModuleParams::Amplifier(_) => vec![NodeRef::default(), NodeRef::default()],
},
outputs: match module {
ModuleParams::SineGenerator(_) => vec![NodeRef::default()],
ModuleParams::OutputDevice(_) => vec![],
ModuleParams::Mixer2ch(()) => vec![NodeRef::default()],
ModuleParams::FmSine(_) => vec![NodeRef::default()],
ModuleParams::Amplifier(_) => vec![NodeRef::default()],
},
};

Expand Down Expand Up @@ -460,6 +462,7 @@ impl Workspace {
("Mixer (2 channel)", ModuleParams::Mixer2ch(())),
("Output Device", ModuleParams::OutputDevice(OutputDeviceParams { device: None })),
("FM Sine", ModuleParams::FmSine(FmSineParams { freq_lo: 90.0, freq_hi: 110.0 })),
("Amplifier", ModuleParams::Amplifier(AmplifierParams { amplitude: 1.0, mod_depth: 0.5 })),
];

html! {
Expand Down Expand Up @@ -655,6 +658,9 @@ impl Window {
ModuleParams::FmSine(params) => {
html! { <FmSine id={self.props.id} module={self.link.clone()} params={params} /> }
}
ModuleParams::Amplifier(params) => {
html! { <Amplifier id={self.props.id} module={self.link.clone()} params={params} /> }
}
}
}
}
Expand Down Expand Up @@ -842,6 +848,84 @@ impl Component for OutputDevice {
}
}

#[derive(Properties, Clone, Debug)]
pub struct AmplifierProps {
id: ModuleId,
module: ComponentLink<Window>,
params: AmplifierParams,
}

pub struct Amplifier {
props: AmplifierProps,
}

impl Component for Amplifier {
type Properties = AmplifierProps;
type Message = ();

fn create(props: Self::Properties, _: ComponentLink<Self>) -> Self {
Self { props }
}

fn update(&mut self, _msg: Self::Message) -> ShouldRender {
false
}

fn change(&mut self, props: Self::Properties) -> ShouldRender {
self.props = props;
true
}

fn view(&self) -> Html {
let amp_id = format!("w{}-amp", self.props.id.0);
let amp_params = self.props.params.clone();

let mod_id = format!("w{}-mod", self.props.id.0);
let mod_params = self.props.params.clone();

html! {
<>
<label for={&amp_id}>{"Volume"}</label>
<input type="range"
id={&amp_id}
min={0}
max={1}
step={0.01}
onchange={self.props.module.callback(move |ev| {
if let ChangeData::Value(amplitude_str) = ev {
let amplitude = amplitude_str.parse().unwrap_or(0.0);
let params = AmplifierParams { amplitude, ..amp_params };
WindowMsg::UpdateParams(
ModuleParams::Amplifier(params))
} else {
unreachable!()
}
})}
value={self.props.params.amplitude}
/>
<label for={&mod_id}>{"Mod Depth"}</label>
<input type="range"
id={&mod_id}
min={0}
max={1}
step={0.01}
onchange={self.props.module.callback(move |ev| {
if let ChangeData::Value(mod_str) = ev {
let mod_depth = mod_str.parse().unwrap_or(0.0);
let params = AmplifierParams { mod_depth, ..mod_params };
WindowMsg::UpdateParams(
ModuleParams::Amplifier(params))
} else {
unreachable!()
}
})}
value={self.props.params.mod_depth}
/>
</>
}
}
}

pub struct Connections {
canvas: NodeRef,
ctx: Option<RefCell<CanvasRenderingContext2d>>,
Expand Down
8 changes: 8 additions & 0 deletions protocol/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ pub enum ModuleParams {
OutputDevice(OutputDeviceParams),
Mixer2ch(()),
FmSine(FmSineParams),
Amplifier(AmplifierParams),
}

#[derive(Serialize, Deserialize, Clone, Debug)]
Expand All @@ -96,6 +97,7 @@ pub enum Indication {
OutputDevice(OutputDeviceIndication),
Mixer2ch(()),
FmSine(()),
Amplifier(()),
}

#[derive(Serialize, Deserialize, Clone, Debug)]
Expand All @@ -119,6 +121,12 @@ pub struct FmSineParams {
pub freq_hi: f32,
}

#[derive(Serialize, Deserialize, Clone, Debug)]
pub struct AmplifierParams {
pub amplitude: f32,
pub mod_depth: f32,
}

#[derive(Serialize, Deserialize, Debug, Clone, Copy)]
pub struct Coords {
pub x: i32,
Expand Down
20 changes: 14 additions & 6 deletions src/engine.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ use crate::module::mixer_2ch::Mixer2ch;
use crate::module::output_device::OutputDevice;
use crate::module::sine_generator::SineGenerator;
use crate::module::fm_sine::FmSine;
use crate::module::amplifier::Amplifier;

pub type Sample = f32;

Expand All @@ -23,12 +24,16 @@ pub const SAMPLE_RATE: usize = 44100;
const TICKS_PER_SECOND: usize = 100;
const SAMPLES_PER_TICK: usize = SAMPLE_RATE / TICKS_PER_SECOND;

pub static ZERO_BUFFER: [Sample; SAMPLES_PER_TICK * CHANNELS] = [0.0; SAMPLES_PER_TICK * CHANNELS];
pub static ONE_BUFFER: [Sample; SAMPLES_PER_TICK * CHANNELS] = [1.0; SAMPLES_PER_TICK * CHANNELS];

#[derive(Debug)]
enum Module {
SineGenerator(SineGenerator),
OutputDevice(OutputDevice),
Mixer2ch(Mixer2ch),
FmSine(FmSine),
Amplifier(Amplifier),
}

impl Module {
Expand All @@ -51,6 +56,7 @@ impl Module {
OutputDevice,
Mixer2ch,
FmSine,
Amplifier,
}
}

Expand All @@ -68,6 +74,7 @@ impl Module {
OutputDevice,
Mixer2ch,
FmSine,
Amplifier,
}
}

Expand All @@ -93,10 +100,11 @@ impl Module {
OutputDevice,
Mixer2ch,
FmSine,
Amplifier,
}
}

fn run_tick(&mut self, t: u64, inputs: &[&[Sample]], outputs: &mut [&mut [Sample]]) -> Option<Indication> {
fn run_tick(&mut self, t: u64, inputs: &[Option<&[Sample]>], outputs: &mut [&mut [Sample]]) -> Option<Indication> {
macro_rules! gen {
($( $module:ident , )*) => {
match self {
Expand All @@ -112,6 +120,7 @@ impl Module {
OutputDevice,
Mixer2ch,
FmSine,
Amplifier,
}
}

Expand All @@ -129,6 +138,7 @@ impl Module {
OutputDevice,
Mixer2ch,
FmSine,
Amplifier,
}
}

Expand All @@ -146,6 +156,7 @@ impl Module {
OutputDevice,
Mixer2ch,
FmSine,
Amplifier,
}
}
}
Expand Down Expand Up @@ -466,14 +477,11 @@ impl Engine {
}

{
static ZERO_BUFFER: [Sample; SAMPLES_PER_TICK * CHANNELS] = [0.0; SAMPLES_PER_TICK * CHANNELS];

let input_refs = (0..module.input_count())
.map(|i| InputId(*module_id, i))
.map(|input| connections.get(&input)
.map(|output| buffers[output].as_slice())
.unwrap_or(&ZERO_BUFFER))
.collect::<Vec<&[Sample]>>();
.map(|output| buffers[output].as_slice()))
.collect::<Vec<Option<&[Sample]>>>();

let mut output_refs = output_buffers.iter_mut()
.map(|vec| vec.as_mut_slice())
Expand Down
54 changes: 54 additions & 0 deletions src/module/amplifier.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
use crate::engine::{Sample, ZERO_BUFFER, ONE_BUFFER};
use crate::module::Module;

use mixlab_protocol::AmplifierParams;

#[derive(Debug)]
pub struct Amplifier {
params: AmplifierParams
}

impl Module for Amplifier {
type Params = AmplifierParams;
type Indication = ();

fn create(params: Self::Params) -> (Self, Self::Indication) {
(Amplifier {params}, ())
}

fn params(&self) -> Self::Params {
self.params.clone()
}

fn update(&mut self, params: Self::Params) -> Option<Self::Indication> {
self.params = params;
None
}

fn run_tick(&mut self, _t: u64, inputs: &[Option<&[Sample]>], outputs: &mut [&mut [Sample]]) -> Option<Self::Indication> {
let AmplifierParams {mod_depth, amplitude} = self.params;
let len = outputs[0].len();

let input = &inputs[0].unwrap_or(&ZERO_BUFFER);
let mod_input = &inputs[1].unwrap_or(&ONE_BUFFER);
let output = &mut outputs[0];

for i in 0..len {
output[i] = input[i] * depth(mod_input[i], mod_depth) * amplitude;
}

None
}

fn input_count(&self) -> usize {
2
}

fn output_count(&self) -> usize {
1
}
}

pub fn depth(value: f32, depth: f32) -> f32 {
1.0 - depth + depth * value
}
8 changes: 5 additions & 3 deletions src/module/fm_sine.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use std::f32;

use mixlab_protocol::FmSineParams;

use crate::engine::{Sample, SAMPLE_RATE, CHANNELS};
use crate::engine::{Sample, SAMPLE_RATE, CHANNELS, ZERO_BUFFER};
use crate::module::Module;

#[derive(Debug)]
Expand All @@ -27,14 +27,16 @@ impl Module for FmSine {
None
}

fn run_tick(&mut self, t: u64, inputs: &[&[Sample]], outputs: &mut [&mut [Sample]]) -> Option<Self::Indication> {
fn run_tick(&mut self, t: u64, inputs: &[Option<&[Sample]>], outputs: &mut [&mut [Sample]]) -> Option<Self::Indication> {
let len = outputs[0].len() / CHANNELS;

let input = inputs[0].unwrap_or(&ZERO_BUFFER);

let freq_amp = (self.params.freq_hi - self.params.freq_lo) / 2.0;
let freq_mid = self.params.freq_lo + freq_amp;

for i in 0..len {
let co = (freq_mid + freq_amp * inputs[0][i * 2]) * 2.0 * f32::consts::PI;
let co = (freq_mid + freq_amp * input[i * 2]) * 2.0 * f32::consts::PI;
let t = (t + i as u64) as Sample / SAMPLE_RATE as Sample;
let x = Sample::sin(co * t);

Expand Down
9 changes: 6 additions & 3 deletions src/module/mixer_2ch.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use crate::engine::Sample;
use crate::engine::{Sample, ZERO_BUFFER};
use crate::module::Module;

#[derive(Debug)]
Expand All @@ -20,11 +20,14 @@ impl Module for Mixer2ch {
None
}

fn run_tick(&mut self, _t: u64, inputs: &[&[Sample]], outputs: &mut [&mut [Sample]]) -> Option<Self::Indication> {
fn run_tick(&mut self, _t: u64, inputs: &[Option<&[Sample]>], outputs: &mut [&mut [Sample]]) -> Option<Self::Indication> {
let len = outputs[0].len();

let input0 = &inputs[0].unwrap_or(&ZERO_BUFFER);
let input1 = &inputs[1].unwrap_or(&ZERO_BUFFER);

for i in 0..len {
outputs[0][i] = inputs[0][i] + inputs[1][i];
outputs[0][i] = input0[i] + input1[i];
}

None
Expand Down
3 changes: 2 additions & 1 deletion src/module/mod.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
pub mod amplifier;
pub mod fm_sine;
pub mod mixer_2ch;
pub mod output_device;
Expand All @@ -12,7 +13,7 @@ pub trait Module: Sized {
fn create(params: Self::Params) -> (Self, Self::Indication);
fn params(&self) -> Self::Params;
fn update(&mut self, new_params: Self::Params) -> Option<Self::Indication>;
fn run_tick(&mut self, t: u64, inputs: &[&[Sample]], outputs: &mut [&mut [Sample]]) -> Option<Self::Indication>;
fn run_tick(&mut self, t: u64, inputs: &[Option<&[Sample]>], outputs: &mut [&mut [Sample]]) -> Option<Self::Indication>;
fn input_count(&self) -> usize;
fn output_count(&self) -> usize;
}
6 changes: 3 additions & 3 deletions src/module/output_device.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use ringbuf::{RingBuffer, Producer};

use mixlab_protocol::{OutputDeviceParams, OutputDeviceIndication};

use crate::engine::Sample;
use crate::engine::{Sample, ZERO_BUFFER};
use crate::module::Module;

pub struct OutputDevice {
Expand Down Expand Up @@ -95,9 +95,9 @@ impl Module for OutputDevice {
None
}

fn run_tick(&mut self, _t: u64, inputs: &[&[Sample]], _outputs: &mut [&mut [Sample]]) -> Option<Self::Indication> {
fn run_tick(&mut self, _t: u64, inputs: &[Option<&[Sample]>], _outputs: &mut [&mut [Sample]]) -> Option<Self::Indication> {
if let Some(tx) = &mut self.tx {
tx.push_slice(inputs[0]);
tx.push_slice(inputs[0].unwrap_or(&ZERO_BUFFER));
}

None
Expand Down
Loading

0 comments on commit 32f41d2

Please sign in to comment.