noise
This commit is contained in:
+21
-59
@@ -1,15 +1,8 @@
|
||||
use std::ops::RangeInclusive;
|
||||
use eframe::egui;
|
||||
use eframe::egui::{Color32, Context, Id, Margin, PointerButton, Pos2, Rangef, Response, Sense, Stroke, TopBottomPanel, Ui, Vec2, Widget};
|
||||
use crate::nome::LOOP_LENGTH;
|
||||
|
||||
pub enum NoteType {
|
||||
Sine,
|
||||
Triangle,
|
||||
Sawtooth,
|
||||
Square,
|
||||
Noise
|
||||
}
|
||||
use eframe::egui::{Color32, Context, Id, PointerButton, Pos2, Rangef, Sense, Ui, Vec2, Widget};
|
||||
use crate::noise::NoteType;
|
||||
use crate::tenori::LOOP_LENGTH;
|
||||
|
||||
impl NoteType {
|
||||
fn color(&self) -> Color32 {
|
||||
@@ -24,29 +17,40 @@ impl NoteType {
|
||||
}
|
||||
|
||||
pub struct Grid {
|
||||
note_type: NoteType,
|
||||
volume: f32,
|
||||
pub note_type: NoteType,
|
||||
pub volume: f32,
|
||||
pub length: u64,
|
||||
notes: Vec<bool>,
|
||||
pub id: String
|
||||
id: String
|
||||
}
|
||||
|
||||
impl Grid {
|
||||
pub fn for_note_type(note_type: NoteType, counter: usize) -> Self {
|
||||
Self {
|
||||
note_type,
|
||||
volume: 50.0,
|
||||
volume: 1.0,
|
||||
length: 250,
|
||||
notes: vec![false; (LOOP_LENGTH * LOOP_LENGTH) as usize],
|
||||
id: format!("Track {}", counter)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn show(&mut self, ctx: &Context, cursor: f32) {
|
||||
let id = Id::from(self.id.clone());
|
||||
let win = egui::Window::new(&self.id).resizable(false).scroll([false, false]);
|
||||
win.show(ctx, |ui| {
|
||||
egui::MenuBar::new().ui(ui, |ui| {
|
||||
if ui.button("Clear").clicked() {
|
||||
self.notes = vec![false; (LOOP_LENGTH * LOOP_LENGTH) as usize]
|
||||
}
|
||||
});
|
||||
|
||||
egui::MenuBar::new().ui(ui, |ui| {
|
||||
ui.label("Volume");
|
||||
ui.add(egui::Slider::new(&mut self.volume, RangeInclusive::new(0.0, 100.0)));
|
||||
ui.add(egui::Slider::new(&mut self.volume, RangeInclusive::new(0.0, 2.0)).show_value(false));
|
||||
|
||||
ui.label("Length");
|
||||
ui.add(egui::Slider::new(&mut self.length, RangeInclusive::new(0, 2000)).show_value(false));
|
||||
|
||||
});
|
||||
|
||||
egui::Frame::new().inner_margin(3).show(ui, |ui| {
|
||||
@@ -81,7 +85,6 @@ impl Grid {
|
||||
ui.input(|input| {
|
||||
if input.pointer.button_clicked(PointerButton::Primary) {
|
||||
if let Some(pos) = input.pointer.latest_pos() {
|
||||
println!("window: {} pos: {} {}", self.id, pos.x - rect.left(), pos.y - rect.top());
|
||||
let x = ((pos.x - rect.left()) / 20.0).floor() as usize;
|
||||
let y = ((pos.y - rect.top()) / 20.0).floor() as usize;
|
||||
let n = x + y * LOOP_LENGTH as usize;
|
||||
@@ -92,52 +95,11 @@ impl Grid {
|
||||
}
|
||||
}
|
||||
|
||||
// fn frame(&mut self, ui: &mut Ui, cursor: f32) {
|
||||
// egui::Frame::new().inner_margin(3).show(ui, |ui| {
|
||||
// let dim = 20.0 * LOOP_LENGTH as f32;
|
||||
// ui.set_width(dim);
|
||||
// ui.set_height(dim);
|
||||
// let rect = ui.clip_rect().translate(Vec2::new(6.0, 6.0));
|
||||
// //ui.painter().debug_rect(rect, Color32::from_rgb(0, 255, 0), "blah");
|
||||
// for (i, lit) in self.notes.iter().enumerate() {
|
||||
// let (x, y) = (i as u32 % LOOP_LENGTH, i as u32 / LOOP_LENGTH);
|
||||
// let center = Pos2::new(
|
||||
// (x * 20 + 10) as f32 + rect.left(),
|
||||
// (y * 20 + 10) as f32 + rect.top());
|
||||
// if *lit {
|
||||
// ui.painter().circle_filled(center, 10.0, self.note_type.color());
|
||||
// } else {
|
||||
// ui.painter().circle_stroke(center, 10.0, (1.0, Color32::from_gray(0x88)));
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// ui.painter().vline(
|
||||
// cursor * dim + rect.left(),
|
||||
// Rangef::new(rect.top(), rect.top() + dim),
|
||||
// (1.0, self.note_type.color())
|
||||
// );
|
||||
//
|
||||
// if ui.response().contains_pointer() {
|
||||
// ui.input(|input| {
|
||||
// if input.pointer.button_clicked(PointerButton::Primary) {
|
||||
// if let Some(pos) = input.pointer.latest_pos() {
|
||||
// println!("window: {} pos: {} {}", self.id, pos.x - rect.left(), pos.y - rect.top());
|
||||
// let x = ((pos.x - rect.left()) / 20.0).floor() as usize;
|
||||
// let y = ((pos.y - rect.top()) / 20.0).floor() as usize;
|
||||
// let n = x + y * LOOP_LENGTH as usize;
|
||||
// self.notes[n] = !self.notes[n]
|
||||
// }
|
||||
// }
|
||||
// })
|
||||
// }
|
||||
// });
|
||||
// }
|
||||
|
||||
pub fn notes(&self, beat: u32) -> Vec<u32> {
|
||||
let mut notes = vec![];
|
||||
for y in 0..LOOP_LENGTH {
|
||||
if self.notes[(y * LOOP_LENGTH + beat) as usize] {
|
||||
notes.push(y)
|
||||
notes.push(LOOP_LENGTH - y)
|
||||
}
|
||||
}
|
||||
notes
|
||||
|
||||
+3
-3
@@ -1,10 +1,10 @@
|
||||
use std::ops::RangeInclusive;
|
||||
use eframe::egui;
|
||||
use eframe::egui::{Context, TopBottomPanel};
|
||||
use crate::grid::{Grid, NoteType};
|
||||
use crate::Nome;
|
||||
use crate::noise::NoteType;
|
||||
use crate::Tenori;
|
||||
|
||||
impl Nome {
|
||||
impl Tenori {
|
||||
pub fn menu(&mut self, ctx: &Context) {
|
||||
TopBottomPanel::top("menu_panel").show(ctx, |ui| {
|
||||
egui::MenuBar::new().ui(ui, |ui| {
|
||||
|
||||
+8
-7
@@ -1,21 +1,22 @@
|
||||
mod gui;
|
||||
mod nome;
|
||||
mod tenori;
|
||||
mod grid;
|
||||
mod noise;
|
||||
|
||||
use std::time::Duration;
|
||||
use eframe::{App, Frame};
|
||||
use eframe::egui::Context;
|
||||
use crate::nome::Nome;
|
||||
use crate::tenori::Tenori;
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() {
|
||||
let native_options = eframe::NativeOptions::default();
|
||||
eframe::run_native("Fleen", native_options, Box::new(|_cc| {
|
||||
Ok(Box::new(Nome::default()))
|
||||
eframe::run_native("Tenori-ish", native_options, Box::new(|_cc| {
|
||||
Ok(Box::new(Tenori::default()))
|
||||
})).expect("Error running application");
|
||||
}
|
||||
|
||||
impl App for Nome {
|
||||
impl App for Tenori {
|
||||
fn update(&mut self, ctx: &Context, frame: &mut Frame) {
|
||||
let play = self.tick();
|
||||
let cursor = self.ratio();
|
||||
@@ -25,8 +26,8 @@ impl App for Nome {
|
||||
}
|
||||
|
||||
if play {
|
||||
for (track, row) in self.notes_for_beat() {
|
||||
println!("{}: {}", track, row)
|
||||
for note in self.notes_for_beat() {
|
||||
self.play(note)
|
||||
}
|
||||
}
|
||||
ctx.request_repaint_after(Duration::from_millis(17))
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
use std::time::Duration;
|
||||
use rodio::mixer::Mixer;
|
||||
use rodio::Source;
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq)]
|
||||
pub enum NoteType {
|
||||
Sine,
|
||||
Triangle,
|
||||
Sawtooth,
|
||||
Square,
|
||||
Noise
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq)]
|
||||
pub struct Note {
|
||||
/// What kind of timbre
|
||||
pub note_type: NoteType,
|
||||
|
||||
/// Which semitone up / down from A4
|
||||
pub tone: i32,
|
||||
|
||||
/// How loud, 0.0 .. 2.0
|
||||
pub volume: f32,
|
||||
|
||||
/// How long to fade out
|
||||
pub duration: Duration
|
||||
}
|
||||
|
||||
/// The frequency for a given tone, in Hz.
|
||||
/// Tone 0 is A4, A above middle C, which is defined at 440 Hz.
|
||||
/// Moving up or down one is a single semitone, which means a
|
||||
/// change in `tone` of `12` is one octave.
|
||||
fn freq(tone: i32) -> f32 {
|
||||
440.0 * 1.0595f32.powf(tone as f32)
|
||||
}
|
||||
|
||||
impl NoteType {
|
||||
fn source(self, tone: i32) -> Box<dyn Source + Send> {
|
||||
let freq = freq(tone);
|
||||
let source: Box<dyn Source + Send> = match self {
|
||||
NoteType::Sine => Box::new(rodio::source::SineWave::new(freq)),
|
||||
NoteType::Triangle => Box::new(rodio::source::TriangleWave::new(freq)),
|
||||
NoteType::Sawtooth => Box::new(rodio::source::SawtoothWave::new(freq)),
|
||||
NoteType::Square => Box::new(rodio::source::SquareWave::new(freq)),
|
||||
NoteType::Noise => Box::new(rodio::source::noise::WhiteUniform::new(44100)
|
||||
.low_pass_with_q(freq as u32, 2.0))
|
||||
};
|
||||
source
|
||||
}
|
||||
}
|
||||
|
||||
impl Note {
|
||||
pub fn play(self, mixer: &Mixer) {
|
||||
let note = self.note_type.source(self.tone);
|
||||
let note = note.fade_out(self.duration);
|
||||
let note = note.amplify(self.volume.clamp(0.0, 1.0));
|
||||
let note = note.take_duration(self.duration);
|
||||
mixer.add(note)
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,11 @@
|
||||
use std::time::Instant;
|
||||
use crate::grid::{Grid, NoteType};
|
||||
use std::time::{Duration, Instant};
|
||||
use rodio::OutputStream;
|
||||
use crate::grid::Grid;
|
||||
use crate::noise::{Note, NoteType};
|
||||
|
||||
pub const LOOP_LENGTH: u32 = 16;
|
||||
|
||||
pub struct Nome {
|
||||
pub struct Tenori {
|
||||
/// Tempo in beats per minute
|
||||
pub tempo: u32,
|
||||
|
||||
@@ -20,23 +22,30 @@ pub struct Nome {
|
||||
pub grids: Vec<Grid>,
|
||||
|
||||
// Running count of windows created (for ids)
|
||||
window_counter: usize
|
||||
window_counter: usize,
|
||||
|
||||
// The audio output stream to which we will play notes
|
||||
output_stream: OutputStream
|
||||
}
|
||||
|
||||
impl Default for Nome {
|
||||
impl Default for Tenori {
|
||||
fn default() -> Self {
|
||||
let output_stream = rodio::OutputStreamBuilder::open_default_stream()
|
||||
.expect("Open audio output stream");
|
||||
|
||||
Self {
|
||||
tempo: 90,
|
||||
timer: 0.0,
|
||||
playing: true,
|
||||
last_tick: None,
|
||||
grids: vec![],
|
||||
window_counter: 0
|
||||
window_counter: 0,
|
||||
output_stream
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Nome {
|
||||
impl Tenori {
|
||||
/// Call this every frame to update the timer / last tick based on the current instant
|
||||
/// and the tempo
|
||||
pub fn tick(&mut self) -> bool {
|
||||
@@ -76,15 +85,26 @@ impl Nome {
|
||||
self.grids.push(Grid::for_note_type(note_type, self.window_counter));
|
||||
}
|
||||
|
||||
pub fn notes_for_beat(&self) -> Vec<(String, u32)> {
|
||||
pub fn notes_for_beat(&self) -> Vec<Note> {
|
||||
let mut notes = vec![];
|
||||
let beat = self.beat();
|
||||
|
||||
for grid in self.grids.iter() {
|
||||
for note in grid.notes(beat).into_iter() {
|
||||
notes.push((grid.id.clone(), note))
|
||||
let tone: i32 = note as i32 - 8;
|
||||
|
||||
notes.push(Note {
|
||||
note_type: grid.note_type,
|
||||
tone,
|
||||
volume: grid.volume,
|
||||
duration: Duration::from_millis(grid.length as u64),
|
||||
})
|
||||
}
|
||||
}
|
||||
notes
|
||||
}
|
||||
|
||||
pub fn play(&mut self, note: Note) {
|
||||
note.play(self.output_stream.mixer())
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user