various grid fixes
This commit is contained in:
+29
-9
@@ -1,7 +1,6 @@
|
|||||||
use std::ops::RangeInclusive;
|
use std::ops::RangeInclusive;
|
||||||
use eframe::egui;
|
use eframe::egui;
|
||||||
use eframe::egui::{Color32, Context, PointerButton, Pos2, Rangef, Sense, Ui, Vec2};
|
use eframe::egui::{Color32, Context, Id, PointerButton, Pos2, Rangef, Sense, Ui, Vec2};
|
||||||
use serde::{Deserialize, Serialize};
|
|
||||||
use crate::gui::Showable;
|
use crate::gui::Showable;
|
||||||
use crate::noise::NoteType;
|
use crate::noise::NoteType;
|
||||||
use crate::scale::Scale;
|
use crate::scale::Scale;
|
||||||
@@ -17,27 +16,41 @@ impl NoteType {
|
|||||||
NoteType::Noise => Color32::from_rgb(240, 240, 240),
|
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(Serialize, Deserialize, Clone)]
|
#[derive(Clone)]
|
||||||
pub struct Grid {
|
pub struct Grid {
|
||||||
pub note_type: NoteType,
|
pub note_type: NoteType,
|
||||||
pub volume: f32,
|
pub volume: f32,
|
||||||
pub length: u64,
|
pub length: u64,
|
||||||
scale: Scale,
|
pub scale: Scale,
|
||||||
notes: Vec<bool>,
|
pub notes: Vec<bool>,
|
||||||
id: String
|
pub id: Id,
|
||||||
|
pub name: String,
|
||||||
|
pub open: bool
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Grid {
|
impl Grid {
|
||||||
pub fn for_note_type(note_type: NoteType, counter: usize) -> Self {
|
pub fn for_note_type(note_type: NoteType, id: Id) -> Self {
|
||||||
Self {
|
Self {
|
||||||
note_type,
|
note_type,
|
||||||
volume: 1.0,
|
volume: 1.0,
|
||||||
length: 250,
|
length: 250,
|
||||||
|
open: true,
|
||||||
scale: Scale::CMajor,
|
scale: Scale::CMajor,
|
||||||
notes: vec![false; (LOOP_LENGTH * LOOP_LENGTH) as usize],
|
notes: vec![false; (LOOP_LENGTH * LOOP_LENGTH) as usize],
|
||||||
id: format!("Track {}", counter)
|
name: format!("{} Track", note_type.name()),
|
||||||
|
id
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -91,7 +104,12 @@ impl Grid {
|
|||||||
|
|
||||||
impl Showable<f32> for Grid {
|
impl Showable<f32> for Grid {
|
||||||
fn show(&mut self, ctx: &Context, cursor: &f32) {
|
fn show(&mut self, ctx: &Context, cursor: &f32) {
|
||||||
let win = egui::Window::new(&self.id).resizable(false).scroll([false, false]);
|
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| {
|
win.show(ctx, |ui| {
|
||||||
egui::MenuBar::new().ui(ui, |ui| {
|
egui::MenuBar::new().ui(ui, |ui| {
|
||||||
if ui.button("Clear").clicked() {
|
if ui.button("Clear").clicked() {
|
||||||
@@ -128,5 +146,7 @@ impl Showable<f32> for Grid {
|
|||||||
self.draw_grid(ui, *cursor)
|
self.draw_grid(ui, *cursor)
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
self.open = open;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
+9
-6
@@ -2,7 +2,7 @@ use std::fs;
|
|||||||
use std::ops::RangeInclusive;
|
use std::ops::RangeInclusive;
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
use eframe::egui;
|
use eframe::egui;
|
||||||
use eframe::egui::{Context, TopBottomPanel};
|
use eframe::egui::{Context, Id, TopBottomPanel};
|
||||||
use crate::noise::NoteType;
|
use crate::noise::NoteType;
|
||||||
use crate::saveload::PersistedTenori;
|
use crate::saveload::PersistedTenori;
|
||||||
use crate::Tenori;
|
use crate::Tenori;
|
||||||
@@ -90,11 +90,7 @@ impl Tenori {
|
|||||||
.pick_file() {
|
.pick_file() {
|
||||||
let serialized = fs::read_to_string(path).map_err(|e| e.to_string())?;
|
let serialized = fs::read_to_string(path).map_err(|e| e.to_string())?;
|
||||||
let persisted = toml::from_str::<PersistedTenori>(serialized.as_str()).map_err(|e| e.to_string())?;
|
let persisted = toml::from_str::<PersistedTenori>(serialized.as_str()).map_err(|e| e.to_string())?;
|
||||||
|
persisted.apply_to(self);
|
||||||
self.grids = persisted.grids;
|
|
||||||
self.tempo = persisted.tempo;
|
|
||||||
self.playing = false;
|
|
||||||
self.timer = 0.0;
|
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@@ -103,6 +99,13 @@ impl Tenori {
|
|||||||
for g in self.grids.iter_mut() {
|
for g in self.grids.iter_mut() {
|
||||||
g.show(ctx, cursor)
|
g.show(ctx, cursor)
|
||||||
}
|
}
|
||||||
|
self.grids.retain(|g| g.open);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Return a unique (among all the windows created since a file load) id string for a window.
|
||||||
|
pub fn window_id(&mut self) -> Id {
|
||||||
|
self.window_counter += 1;
|
||||||
|
format!("window {}", self.window_counter).into()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+55
-3
@@ -1,18 +1,70 @@
|
|||||||
|
use eframe::egui::Id;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use crate::grid::Grid;
|
use crate::grid::Grid;
|
||||||
|
use crate::noise::NoteType;
|
||||||
|
use crate::scale::Scale;
|
||||||
use crate::tenori::Tenori;
|
use crate::tenori::Tenori;
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize)]
|
#[derive(Serialize, Deserialize)]
|
||||||
pub struct PersistedTenori {
|
pub struct PersistedTenori {
|
||||||
pub tempo: u32,
|
tempo: u32,
|
||||||
pub grids: Vec<Grid>
|
grids: Vec<PersistedGrid>
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<&Tenori> for PersistedTenori {
|
impl From<&Tenori> for PersistedTenori {
|
||||||
fn from(value: &Tenori) -> Self {
|
fn from(value: &Tenori) -> Self {
|
||||||
Self {
|
Self {
|
||||||
tempo: value.tempo,
|
tempo: value.tempo,
|
||||||
grids: value.grids.iter().map(Grid::clone).collect()
|
grids: value.grids.iter().map(PersistedGrid::from).collect()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PersistedTenori {
|
||||||
|
pub fn apply_to(self, tenori: &mut Tenori) {
|
||||||
|
tenori.grids = self.grids.into_iter().map(|g| g.into_grid(tenori.window_id())).collect();
|
||||||
|
tenori.tempo = self.tempo;
|
||||||
|
tenori.playing = false; // Start paused
|
||||||
|
tenori.timer = 0.0; // Start at the beginning of the loop
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize)]
|
||||||
|
struct PersistedGrid {
|
||||||
|
note_type: NoteType,
|
||||||
|
volume: f32,
|
||||||
|
length: u64,
|
||||||
|
scale: Scale,
|
||||||
|
notes: String,
|
||||||
|
name: String
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<&Grid> for PersistedGrid {
|
||||||
|
fn from(value: &Grid) -> Self {
|
||||||
|
let notes: String = value.notes.iter().map(|n| if *n { '1' } else { '0' }).collect();
|
||||||
|
Self {
|
||||||
|
note_type: value.note_type,
|
||||||
|
volume: value.volume,
|
||||||
|
length: value.length,
|
||||||
|
scale: value.scale,
|
||||||
|
name: value.name.clone(),
|
||||||
|
notes
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PersistedGrid {
|
||||||
|
pub fn into_grid(self, id: Id) -> Grid {
|
||||||
|
let notes: Vec<_> = self.notes.chars().map(|c| c == '1').collect();
|
||||||
|
Grid {
|
||||||
|
note_type: self.note_type,
|
||||||
|
volume: self.volume,
|
||||||
|
length: self.length,
|
||||||
|
scale: self.scale,
|
||||||
|
name: self.name,
|
||||||
|
open: true,
|
||||||
|
notes,
|
||||||
|
id
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
+4
-4
@@ -22,8 +22,8 @@ pub struct Tenori {
|
|||||||
/// The grids that we currently have going
|
/// The grids that we currently have going
|
||||||
pub grids: Vec<Grid>,
|
pub grids: Vec<Grid>,
|
||||||
|
|
||||||
// Running count of windows created (for ids)
|
/// Running count of windows created (for ids)
|
||||||
window_counter: usize,
|
pub window_counter: usize,
|
||||||
|
|
||||||
// The audio output stream to which we will play notes
|
// The audio output stream to which we will play notes
|
||||||
output_stream: OutputStream,
|
output_stream: OutputStream,
|
||||||
@@ -91,8 +91,8 @@ impl Tenori {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn add_window(&mut self, note_type: NoteType) {
|
pub fn add_window(&mut self, note_type: NoteType) {
|
||||||
self.window_counter += 1;
|
let id = self.window_id();
|
||||||
self.grids.push(Grid::for_note_type(note_type, self.window_counter));
|
self.grids.push(Grid::for_note_type(note_type, id));
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn notes_for_beat(&self) -> Vec<Note> {
|
pub fn notes_for_beat(&self) -> Vec<Note> {
|
||||||
|
|||||||
Reference in New Issue
Block a user