timbre controls
This commit is contained in:
+14
-11
@@ -6,6 +6,7 @@ use crate::gui::Showable;
|
|||||||
use crate::noise::NoteType;
|
use crate::noise::NoteType;
|
||||||
use crate::scale::Scale;
|
use crate::scale::Scale;
|
||||||
use crate::tenori::LOOP_LENGTH;
|
use crate::tenori::LOOP_LENGTH;
|
||||||
|
use crate::timbre::Timbre;
|
||||||
|
|
||||||
impl NoteType {
|
impl NoteType {
|
||||||
fn color(&self) -> Color32 {
|
fn color(&self) -> Color32 {
|
||||||
@@ -38,8 +39,8 @@ pub struct Grid {
|
|||||||
pub id: Id,
|
pub id: Id,
|
||||||
pub name: String,
|
pub name: String,
|
||||||
pub open: bool,
|
pub open: bool,
|
||||||
pub envelope: Envelope,
|
pub timbre: Timbre,
|
||||||
pub envelope_open: bool
|
pub timbre_open: bool
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Grid {
|
impl Grid {
|
||||||
@@ -51,8 +52,8 @@ impl Grid {
|
|||||||
scale: Scale::CMajor,
|
scale: Scale::CMajor,
|
||||||
notes: vec![false; (LOOP_LENGTH * LOOP_LENGTH) as usize],
|
notes: vec![false; (LOOP_LENGTH * LOOP_LENGTH) as usize],
|
||||||
name: format!("{} Track", note_type.name()),
|
name: format!("{} Track", note_type.name()),
|
||||||
envelope: Envelope::default(),
|
timbre: Timbre::default(),
|
||||||
envelope_open: false,
|
timbre_open: false,
|
||||||
id
|
id
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -134,8 +135,8 @@ impl Showable<f32> for Grid {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
if ui.button("Envelope...").clicked() {
|
if ui.button("Timbre...").clicked() {
|
||||||
self.envelope_open = !self.envelope_open;
|
self.timbre_open = !self.timbre_open;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -149,11 +150,13 @@ impl Showable<f32> for Grid {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
if self.envelope_open {
|
if self.timbre_open {
|
||||||
let mut eopen = true;
|
let mut topen = true;
|
||||||
let (id, title) = (format!("{} envelope", self.id.value()).into(), format!("{} Envelope", self.name));
|
let mut name = self.name.clone();
|
||||||
(&mut self.envelope, &mut eopen).show(ctx, &(id, title));
|
let (id, title) = (format!("{} timbre", self.id.value()).into(), format!("{} Timbre", self.name));
|
||||||
self.envelope_open = eopen;
|
(&mut self.timbre, &mut topen, &mut name).show(ctx, &(id, title));
|
||||||
|
self.timbre_open = topen;
|
||||||
|
self.name = name;
|
||||||
}
|
}
|
||||||
|
|
||||||
self.open = open;
|
self.open = open;
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ mod scale;
|
|||||||
mod saveload;
|
mod saveload;
|
||||||
mod dialog;
|
mod dialog;
|
||||||
mod envelope;
|
mod envelope;
|
||||||
|
mod timbre;
|
||||||
|
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
use eframe::{App, Frame};
|
use eframe::{App, Frame};
|
||||||
|
|||||||
+6
-5
@@ -2,6 +2,7 @@ use rodio::mixer::Mixer;
|
|||||||
use rodio::Source;
|
use rodio::Source;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use crate::envelope::Envelope;
|
use crate::envelope::Envelope;
|
||||||
|
use crate::timbre::Timbre;
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)]
|
#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)]
|
||||||
pub enum NoteType {
|
pub enum NoteType {
|
||||||
@@ -24,7 +25,7 @@ pub struct Note {
|
|||||||
pub volume: f32,
|
pub volume: f32,
|
||||||
|
|
||||||
/// ADSR envelope
|
/// ADSR envelope
|
||||||
pub envelope: Envelope
|
pub timbre: Timbre
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The frequency for a given tone, in Hz.
|
/// The frequency for a given tone, in Hz.
|
||||||
@@ -52,9 +53,9 @@ impl NoteType {
|
|||||||
|
|
||||||
impl Note {
|
impl Note {
|
||||||
pub fn play(self, mixer: &Mixer) {
|
pub fn play(self, mixer: &Mixer) {
|
||||||
let note = self.note_type.source(self.tone);
|
let freq = freq(self.tone);
|
||||||
let note = self.envelope.modulate(note);
|
let source = self.timbre.source(freq);
|
||||||
let note = note.amplify(self.volume.clamp(0.0, 1.0));
|
let source = source.amplify_normalized(self.volume);
|
||||||
mixer.add(note)
|
mixer.add(source)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+5
-5
@@ -1,10 +1,10 @@
|
|||||||
use eframe::egui::Id;
|
use eframe::egui::Id;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use crate::envelope::Envelope;
|
|
||||||
use crate::grid::Grid;
|
use crate::grid::Grid;
|
||||||
use crate::noise::NoteType;
|
use crate::noise::NoteType;
|
||||||
use crate::scale::Scale;
|
use crate::scale::Scale;
|
||||||
use crate::tenori::Tenori;
|
use crate::tenori::Tenori;
|
||||||
|
use crate::timbre::Timbre;
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize)]
|
#[derive(Serialize, Deserialize)]
|
||||||
pub struct PersistedTenori {
|
pub struct PersistedTenori {
|
||||||
@@ -37,7 +37,7 @@ struct PersistedGrid {
|
|||||||
scale: Scale,
|
scale: Scale,
|
||||||
notes: String,
|
notes: String,
|
||||||
name: String,
|
name: String,
|
||||||
envelope: Envelope,
|
timbre: Timbre,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<&Grid> for PersistedGrid {
|
impl From<&Grid> for PersistedGrid {
|
||||||
@@ -48,7 +48,7 @@ impl From<&Grid> for PersistedGrid {
|
|||||||
volume: value.volume,
|
volume: value.volume,
|
||||||
scale: value.scale,
|
scale: value.scale,
|
||||||
name: value.name.clone(),
|
name: value.name.clone(),
|
||||||
envelope: value.envelope,
|
timbre: value.timbre,
|
||||||
notes
|
notes
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -62,9 +62,9 @@ impl PersistedGrid {
|
|||||||
volume: self.volume,
|
volume: self.volume,
|
||||||
scale: self.scale,
|
scale: self.scale,
|
||||||
name: self.name,
|
name: self.name,
|
||||||
envelope: self.envelope,
|
timbre: self.timbre,
|
||||||
open: true,
|
open: true,
|
||||||
envelope_open: false,
|
timbre_open: false,
|
||||||
notes,
|
notes,
|
||||||
id
|
id
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -106,7 +106,7 @@ impl Tenori {
|
|||||||
note_type: grid.note_type,
|
note_type: grid.note_type,
|
||||||
tone,
|
tone,
|
||||||
volume: grid.volume,
|
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