timbre controls
This commit is contained in:
+14
-11
@@ -6,6 +6,7 @@ use crate::gui::Showable;
|
||||
use crate::noise::NoteType;
|
||||
use crate::scale::Scale;
|
||||
use crate::tenori::LOOP_LENGTH;
|
||||
use crate::timbre::Timbre;
|
||||
|
||||
impl NoteType {
|
||||
fn color(&self) -> Color32 {
|
||||
@@ -38,8 +39,8 @@ pub struct Grid {
|
||||
pub id: Id,
|
||||
pub name: String,
|
||||
pub open: bool,
|
||||
pub envelope: Envelope,
|
||||
pub envelope_open: bool
|
||||
pub timbre: Timbre,
|
||||
pub timbre_open: bool
|
||||
}
|
||||
|
||||
impl Grid {
|
||||
@@ -51,8 +52,8 @@ impl Grid {
|
||||
scale: Scale::CMajor,
|
||||
notes: vec![false; (LOOP_LENGTH * LOOP_LENGTH) as usize],
|
||||
name: format!("{} Track", note_type.name()),
|
||||
envelope: Envelope::default(),
|
||||
envelope_open: false,
|
||||
timbre: Timbre::default(),
|
||||
timbre_open: false,
|
||||
id
|
||||
}
|
||||
}
|
||||
@@ -134,8 +135,8 @@ impl Showable<f32> for Grid {
|
||||
}
|
||||
});
|
||||
|
||||
if ui.button("Envelope...").clicked() {
|
||||
self.envelope_open = !self.envelope_open;
|
||||
if ui.button("Timbre...").clicked() {
|
||||
self.timbre_open = !self.timbre_open;
|
||||
}
|
||||
});
|
||||
|
||||
@@ -149,11 +150,13 @@ impl Showable<f32> for Grid {
|
||||
});
|
||||
});
|
||||
|
||||
if self.envelope_open {
|
||||
let mut eopen = true;
|
||||
let (id, title) = (format!("{} envelope", self.id.value()).into(), format!("{} Envelope", self.name));
|
||||
(&mut self.envelope, &mut eopen).show(ctx, &(id, title));
|
||||
self.envelope_open = eopen;
|
||||
if self.timbre_open {
|
||||
let mut topen = true;
|
||||
let mut name = self.name.clone();
|
||||
let (id, title) = (format!("{} timbre", self.id.value()).into(), format!("{} Timbre", self.name));
|
||||
(&mut self.timbre, &mut topen, &mut name).show(ctx, &(id, title));
|
||||
self.timbre_open = topen;
|
||||
self.name = name;
|
||||
}
|
||||
|
||||
self.open = open;
|
||||
|
||||
@@ -6,6 +6,7 @@ mod scale;
|
||||
mod saveload;
|
||||
mod dialog;
|
||||
mod envelope;
|
||||
mod timbre;
|
||||
|
||||
use std::time::Duration;
|
||||
use eframe::{App, Frame};
|
||||
|
||||
+6
-5
@@ -2,6 +2,7 @@ use rodio::mixer::Mixer;
|
||||
use rodio::Source;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use crate::envelope::Envelope;
|
||||
use crate::timbre::Timbre;
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)]
|
||||
pub enum NoteType {
|
||||
@@ -24,7 +25,7 @@ pub struct Note {
|
||||
pub volume: f32,
|
||||
|
||||
/// ADSR envelope
|
||||
pub envelope: Envelope
|
||||
pub timbre: Timbre
|
||||
}
|
||||
|
||||
/// The frequency for a given tone, in Hz.
|
||||
@@ -52,9 +53,9 @@ impl NoteType {
|
||||
|
||||
impl Note {
|
||||
pub fn play(self, mixer: &Mixer) {
|
||||
let note = self.note_type.source(self.tone);
|
||||
let note = self.envelope.modulate(note);
|
||||
let note = note.amplify(self.volume.clamp(0.0, 1.0));
|
||||
mixer.add(note)
|
||||
let freq = freq(self.tone);
|
||||
let source = self.timbre.source(freq);
|
||||
let source = source.amplify_normalized(self.volume);
|
||||
mixer.add(source)
|
||||
}
|
||||
}
|
||||
|
||||
+5
-5
@@ -1,10 +1,10 @@
|
||||
use eframe::egui::Id;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use crate::envelope::Envelope;
|
||||
use crate::grid::Grid;
|
||||
use crate::noise::NoteType;
|
||||
use crate::scale::Scale;
|
||||
use crate::tenori::Tenori;
|
||||
use crate::timbre::Timbre;
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
pub struct PersistedTenori {
|
||||
@@ -37,7 +37,7 @@ struct PersistedGrid {
|
||||
scale: Scale,
|
||||
notes: String,
|
||||
name: String,
|
||||
envelope: Envelope,
|
||||
timbre: Timbre,
|
||||
}
|
||||
|
||||
impl From<&Grid> for PersistedGrid {
|
||||
@@ -48,7 +48,7 @@ impl From<&Grid> for PersistedGrid {
|
||||
volume: value.volume,
|
||||
scale: value.scale,
|
||||
name: value.name.clone(),
|
||||
envelope: value.envelope,
|
||||
timbre: value.timbre,
|
||||
notes
|
||||
}
|
||||
}
|
||||
@@ -62,9 +62,9 @@ impl PersistedGrid {
|
||||
volume: self.volume,
|
||||
scale: self.scale,
|
||||
name: self.name,
|
||||
envelope: self.envelope,
|
||||
timbre: self.timbre,
|
||||
open: true,
|
||||
envelope_open: false,
|
||||
timbre_open: false,
|
||||
notes,
|
||||
id
|
||||
}
|
||||
|
||||
+1
-1
@@ -106,7 +106,7 @@ impl Tenori {
|
||||
note_type: grid.note_type,
|
||||
tone,
|
||||
volume: grid.volume,
|
||||
envelope: grid.envelope
|
||||
timbre: grid.timbre
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
+106
@@ -0,0 +1,106 @@
|
||||
use std::ops::RangeInclusive;
|
||||
use eframe::egui;
|
||||
use eframe::egui::{Context, Id, Label, Slider, Window};
|
||||
use rodio::mixer::MixerSource;
|
||||
use rodio::Source;
|
||||
use rodio::source::{SawtoothWave, SineWave, SquareWave, TriangleWave, WhiteUniform};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use crate::envelope::{Envelope, EnvelopeSource};
|
||||
use crate::gui::Showable;
|
||||
|
||||
#[derive(Copy, Clone, Debug, Serialize, Deserialize, PartialEq)]
|
||||
pub struct Timbre {
|
||||
sine: f32,
|
||||
triangle: f32,
|
||||
square: f32,
|
||||
sawtooth: f32,
|
||||
noise: f32,
|
||||
envelope: Envelope
|
||||
}
|
||||
|
||||
impl Default for Timbre {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
sine: 0.0,
|
||||
triangle: 0.0,
|
||||
square: 1.0,
|
||||
sawtooth: 0.0,
|
||||
noise: 0.0,
|
||||
envelope: Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Timbre {
|
||||
pub fn source(&self, frequency: f32) -> EnvelopeSource<MixerSource> {
|
||||
let (mut mixer, source) = rodio::mixer::mixer(1, 44100);
|
||||
if self.sine > 0.0 {
|
||||
mixer.add(SineWave::new(frequency).amplify_normalized(self.sine))
|
||||
}
|
||||
if self.triangle > 0.0 {
|
||||
mixer.add(TriangleWave::new(frequency).amplify_normalized(self.triangle))
|
||||
}
|
||||
if self.square > 0.0 {
|
||||
mixer.add(SquareWave::new(frequency).amplify_normalized(self.square))
|
||||
}
|
||||
if self.sawtooth > 0.0 {
|
||||
mixer.add(SawtoothWave::new(frequency).amplify_normalized(self.sawtooth))
|
||||
}
|
||||
if self.noise > 0.0 {
|
||||
mixer.add(WhiteUniform::new(44100).amplify_normalized(self.noise))
|
||||
}
|
||||
self.envelope.modulate(source)
|
||||
}
|
||||
}
|
||||
|
||||
impl Showable<(Id, String)> for (&mut Timbre, &mut bool, &mut String) {
|
||||
fn show(&mut self, ctx: &Context, (id, title): &(Id, String)) {
|
||||
let mut open = true;
|
||||
let window = Window::new(title)
|
||||
.id(*id)
|
||||
.open(&mut open)
|
||||
.resizable([false, false])
|
||||
.scroll([false, false]);
|
||||
|
||||
window.show(ctx, |ui| {
|
||||
ui.horizontal(|ui| {
|
||||
ui.label("Name");
|
||||
ui.text_edit_singleline(self.2);
|
||||
});
|
||||
|
||||
egui::Grid::new(id).show(ui, |ui| {
|
||||
ui.add(Label::new("Sine"));
|
||||
ui.add(Slider::new(&mut self.0.sine, RangeInclusive::new(0.0, 1.0)));
|
||||
ui.add(Label::new("Attack"));
|
||||
ui.add(Slider::new(&mut self.0.envelope.attack, RangeInclusive::new(0.0, 1.0)));
|
||||
ui.end_row();
|
||||
|
||||
ui.add(Label::new("Triangle"));
|
||||
ui.add(Slider::new(&mut self.0.triangle, RangeInclusive::new(0.0, 1.0)));
|
||||
ui.add(Label::new("Decay"));
|
||||
ui.add(Slider::new(&mut self.0.envelope.decay, RangeInclusive::new(0.0, 1.0)));
|
||||
ui.end_row();
|
||||
|
||||
ui.add(Label::new("Square"));
|
||||
ui.add(Slider::new(&mut self.0.square, RangeInclusive::new(0.0, 1.0)));
|
||||
ui.add(Label::new("Sustain"));
|
||||
ui.add(Slider::new(&mut self.0.envelope.sustain, RangeInclusive::new(0.0, 1.0)));
|
||||
ui.end_row();
|
||||
|
||||
ui.add(Label::new("Sawtooth"));
|
||||
ui.add(Slider::new(&mut self.0.sawtooth, RangeInclusive::new(0.0, 1.0)));
|
||||
ui.add(Label::new("Hold"));
|
||||
ui.add(Slider::new(&mut self.0.envelope.hold, RangeInclusive::new(0.0, 2.0)));
|
||||
ui.end_row();
|
||||
|
||||
ui.add(Label::new("Noise"));
|
||||
ui.add(Slider::new(&mut self.0.noise, RangeInclusive::new(0.0, 1.0)));
|
||||
ui.add(Label::new("Release"));
|
||||
ui.add(Slider::new(&mut self.0.envelope.release, RangeInclusive::new(0.0, 1.0)));
|
||||
ui.end_row();
|
||||
});
|
||||
});
|
||||
|
||||
*self.1 = open;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user