164 lines
5.4 KiB
Rust
164 lines
5.4 KiB
Rust
use std::ops::RangeInclusive;
|
|
use eframe::egui;
|
|
use eframe::egui::{Color32, Context, Id, PointerButton, Pos2, Rangef, Sense, Ui, Vec2};
|
|
use crate::envelope::Envelope;
|
|
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 {
|
|
match self {
|
|
NoteType::Sine => Color32::from_rgb(3, 211, 252),
|
|
NoteType::Triangle => Color32::from_rgb(252, 202, 3),
|
|
NoteType::Sawtooth => Color32::from_rgb(252, 18, 61),
|
|
NoteType::Square => Color32::from_rgb(4, 219, 51),
|
|
NoteType::Noise => Color32::from_rgb(240, 240, 240),
|
|
}
|
|
}
|
|
|
|
fn name(&self) -> &'static str {
|
|
match self {
|
|
NoteType::Sine => "Sine",
|
|
NoteType::Triangle => "Triangle",
|
|
NoteType::Sawtooth => "Sawtooth",
|
|
NoteType::Square => "Square",
|
|
NoteType::Noise => "Noise"
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Clone)]
|
|
pub struct Grid {
|
|
pub note_type: NoteType,
|
|
pub volume: f32,
|
|
pub scale: Scale,
|
|
pub notes: Vec<bool>,
|
|
pub id: Id,
|
|
pub name: String,
|
|
pub open: bool,
|
|
pub timbre: Timbre,
|
|
pub timbre_open: bool
|
|
}
|
|
|
|
impl Grid {
|
|
pub fn for_note_type(note_type: NoteType, id: Id) -> Self {
|
|
Self {
|
|
note_type,
|
|
volume: 1.0,
|
|
open: true,
|
|
scale: Scale::CMajor,
|
|
notes: vec![false; (LOOP_LENGTH * LOOP_LENGTH) as usize],
|
|
name: format!("{} Track", note_type.name()),
|
|
timbre: Timbre::default(),
|
|
timbre_open: false,
|
|
id
|
|
}
|
|
}
|
|
|
|
fn draw_grid(&mut self, ui: &mut Ui, cursor: f32) {
|
|
let dim = 20.0 * LOOP_LENGTH as f32;
|
|
let (rect, response) = ui.allocate_exact_size(Vec2::new(dim, dim), Sense::click_and_drag());
|
|
|
|
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 response.contains_pointer() {
|
|
ui.input(|input| {
|
|
if input.pointer.button_clicked(PointerButton::Primary) {
|
|
if let Some(pos) = input.pointer.latest_pos() {
|
|
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<i32> {
|
|
let mut notes = vec![];
|
|
for y in 0..LOOP_LENGTH {
|
|
if self.notes[(y * LOOP_LENGTH + beat) as usize] {
|
|
let row = LOOP_LENGTH - y - 1;
|
|
notes.push(self.scale.tone(row))
|
|
}
|
|
}
|
|
notes
|
|
}
|
|
}
|
|
|
|
impl Showable<f32> for Grid {
|
|
fn show(&mut self, ctx: &Context, cursor: &f32) {
|
|
let mut open = true;
|
|
let win = egui::Window::new(&self.name)
|
|
.id(self.id)
|
|
.resizable(false)
|
|
.scroll([false, false])
|
|
.open(&mut open);
|
|
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]
|
|
}
|
|
|
|
ui.menu_button("Scale...", |ui| {
|
|
if ui.button(Scale::CMajor.label_text(self.scale)).clicked() {
|
|
self.scale = Scale::CMajor
|
|
}
|
|
if ui.button(Scale::CMinor.label_text(self.scale)).clicked() {
|
|
self.scale = Scale::CMinor
|
|
}
|
|
if ui.button(Scale::Chromatic.label_text(self.scale)).clicked() {
|
|
self.scale = Scale::Chromatic
|
|
}
|
|
if ui.button(Scale::Pentatonic.label_text(self.scale)).clicked() {
|
|
self.scale = Scale::Pentatonic
|
|
}
|
|
});
|
|
|
|
if ui.button("Timbre...").clicked() {
|
|
self.timbre_open = !self.timbre_open;
|
|
}
|
|
});
|
|
|
|
egui::MenuBar::new().ui(ui, |ui| {
|
|
ui.label("Volume");
|
|
ui.add(egui::Slider::new(&mut self.volume, RangeInclusive::new(0.0, 2.0)).show_value(false));
|
|
});
|
|
|
|
egui::Frame::new().inner_margin(3).show(ui, |ui| {
|
|
self.draw_grid(ui, *cursor)
|
|
});
|
|
});
|
|
|
|
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;
|
|
}
|
|
} |