Fixing subtle bugs
This commit is contained in:
@@ -88,4 +88,5 @@ pub enum Op {
|
||||
DivEnd,
|
||||
StackStart,
|
||||
EmitN,
|
||||
ClearCmd,
|
||||
}
|
||||
|
||||
@@ -135,7 +135,14 @@ impl CmdRegister {
|
||||
}
|
||||
|
||||
pub(super) fn snapshot(&self) -> Option<(Value, Vec<(String, Value)>)> {
|
||||
self.sound.as_ref().map(|s| (s.clone(), self.params.clone()))
|
||||
self.sound
|
||||
.as_ref()
|
||||
.map(|s| (s.clone(), self.params.clone()))
|
||||
}
|
||||
|
||||
pub(super) fn clear(&mut self) {
|
||||
self.sound = None;
|
||||
self.params.clear();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -742,6 +742,10 @@ impl Forth {
|
||||
scope_stack.push(new_scope);
|
||||
}
|
||||
|
||||
Op::ClearCmd => {
|
||||
cmd.clear();
|
||||
}
|
||||
|
||||
Op::EmitN => {
|
||||
let n = stack.pop().ok_or("stack underflow")?.as_int()?;
|
||||
if n < 0 {
|
||||
|
||||
@@ -1534,6 +1534,54 @@ pub const WORDS: &[Word] = &[
|
||||
example: "0.02 chorusdelay",
|
||||
compile: Param,
|
||||
},
|
||||
Word {
|
||||
name: "eqlo",
|
||||
category: "EQ",
|
||||
stack: "(f --)",
|
||||
desc: "Set low shelf gain (dB)",
|
||||
example: "3 eqlo",
|
||||
compile: Param,
|
||||
},
|
||||
Word {
|
||||
name: "eqmid",
|
||||
category: "EQ",
|
||||
stack: "(f --)",
|
||||
desc: "Set mid peak gain (dB)",
|
||||
example: "-2 eqmid",
|
||||
compile: Param,
|
||||
},
|
||||
Word {
|
||||
name: "eqhi",
|
||||
category: "EQ",
|
||||
stack: "(f --)",
|
||||
desc: "Set high shelf gain (dB)",
|
||||
example: "1 eqhi",
|
||||
compile: Param,
|
||||
},
|
||||
Word {
|
||||
name: "tilt",
|
||||
category: "EQ",
|
||||
stack: "(f --)",
|
||||
desc: "Set tilt EQ (-1 dark, 1 bright)",
|
||||
example: "-0.5 tilt",
|
||||
compile: Param,
|
||||
},
|
||||
Word {
|
||||
name: "width",
|
||||
category: "Stereo",
|
||||
stack: "(f --)",
|
||||
desc: "Set stereo width (0 mono, 1 normal, 2 wide)",
|
||||
example: "0 width",
|
||||
compile: Param,
|
||||
},
|
||||
Word {
|
||||
name: "haas",
|
||||
category: "Stereo",
|
||||
stack: "(f --)",
|
||||
desc: "Set Haas delay in ms (spatial placement)",
|
||||
example: "8 haas",
|
||||
compile: Param,
|
||||
},
|
||||
Word {
|
||||
name: "comb",
|
||||
category: "Filter",
|
||||
@@ -1766,6 +1814,14 @@ pub const WORDS: &[Word] = &[
|
||||
example: "1 reset",
|
||||
compile: Param,
|
||||
},
|
||||
Word {
|
||||
name: "clear",
|
||||
category: "Sound",
|
||||
stack: "(--)",
|
||||
desc: "Clear sound register (sound and all params)",
|
||||
example: "\"kick\" s 0.5 gain . clear \"hat\" s .",
|
||||
compile: Simple,
|
||||
},
|
||||
// Quotation execution
|
||||
Word {
|
||||
name: "apply",
|
||||
@@ -1871,6 +1927,7 @@ pub(super) fn simple_op(name: &str) -> Option<Op> {
|
||||
"stack" => Op::StackStart,
|
||||
"~" => Op::DivEnd,
|
||||
".!" => Op::EmitN,
|
||||
"clear" => Op::ClearCmd,
|
||||
_ => return None,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -35,11 +35,13 @@ impl From<&Project> for ProjectFile {
|
||||
|
||||
impl From<ProjectFile> for Project {
|
||||
fn from(file: ProjectFile) -> Self {
|
||||
Self {
|
||||
let mut project = Self {
|
||||
banks: file.banks,
|
||||
sample_paths: file.sample_paths,
|
||||
tempo: file.tempo,
|
||||
}
|
||||
};
|
||||
project.normalize();
|
||||
project
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -282,4 +282,14 @@ impl Project {
|
||||
pub fn pattern_at_mut(&mut self, bank: usize, pattern: usize) -> &mut Pattern {
|
||||
&mut self.banks[bank].patterns[pattern]
|
||||
}
|
||||
|
||||
pub fn normalize(&mut self) {
|
||||
self.banks.resize_with(MAX_BANKS, Bank::default);
|
||||
for bank in &mut self.banks {
|
||||
bank.patterns.resize_with(MAX_PATTERNS, Pattern::default);
|
||||
for pattern in &mut bank.patterns {
|
||||
pattern.steps.resize_with(MAX_STEPS, Step::default);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
36
src/app.rs
36
src/app.rs
@@ -53,7 +53,8 @@ impl App {
|
||||
let variables = Arc::new(Mutex::new(HashMap::new()));
|
||||
let dict = Arc::new(Mutex::new(HashMap::new()));
|
||||
let rng = Arc::new(Mutex::new(StdRng::seed_from_u64(0)));
|
||||
let script_engine = ScriptEngine::new(Arc::clone(&variables), Arc::clone(&dict), Arc::clone(&rng));
|
||||
let script_engine =
|
||||
ScriptEngine::new(Arc::clone(&variables), Arc::clone(&dict), Arc::clone(&rng));
|
||||
let live_keys = Arc::new(LiveKeyState::new());
|
||||
|
||||
Self {
|
||||
@@ -251,7 +252,9 @@ impl App {
|
||||
})
|
||||
.collect();
|
||||
self.editor_ctx.editor.set_candidates(candidates);
|
||||
self.editor_ctx.editor.set_completion_enabled(self.ui.show_completion);
|
||||
self.editor_ctx
|
||||
.editor
|
||||
.set_completion_enabled(self.ui.show_completion);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -333,7 +336,8 @@ impl App {
|
||||
{
|
||||
step.command = None;
|
||||
}
|
||||
self.ui.flash(&format!("Script error: {e}"), 300, FlashKind::Error);
|
||||
self.ui
|
||||
.flash(&format!("Script error: {e}"), 300, FlashKind::Error);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -409,21 +413,14 @@ impl App {
|
||||
let is_playing = snapshot.is_playing(bank, pattern);
|
||||
let pattern_data = self.project_state.project.pattern_at(bank, pattern);
|
||||
|
||||
let existing = self
|
||||
.playback
|
||||
.staged_changes
|
||||
.iter()
|
||||
.position(|c| {
|
||||
let existing = self.playback.staged_changes.iter().position(|c| {
|
||||
c.change.pattern_id().bank == bank && c.change.pattern_id().pattern == pattern
|
||||
});
|
||||
|
||||
if let Some(idx) = existing {
|
||||
self.playback.staged_changes.remove(idx);
|
||||
self.ui.set_status(format!(
|
||||
"B{:02}:P{:02} unstaged",
|
||||
bank + 1,
|
||||
pattern + 1
|
||||
));
|
||||
self.ui
|
||||
.set_status(format!("B{:02}:P{:02} unstaged", bank + 1, pattern + 1));
|
||||
} else if is_playing {
|
||||
self.playback.staged_changes.push(StagedChange {
|
||||
change: PatternChange::Stop { bank, pattern },
|
||||
@@ -467,7 +464,8 @@ impl App {
|
||||
}
|
||||
let count = self.playback.staged_changes.len();
|
||||
self.playback.staged_changes.clear();
|
||||
self.ui.set_status(format!("Cleared {count} staged changes"));
|
||||
self.ui
|
||||
.set_status(format!("Cleared {count} staged changes"));
|
||||
}
|
||||
|
||||
pub fn select_edit_pattern(&mut self, pattern: usize) {
|
||||
@@ -705,8 +703,11 @@ impl App {
|
||||
}
|
||||
self.project_state.mark_dirty(bank, pattern);
|
||||
self.load_step_to_editor();
|
||||
self.ui
|
||||
.flash(&format!("Linked to step {:02}", copied.step + 1), 150, FlashKind::Success);
|
||||
self.ui.flash(
|
||||
&format!("Linked to step {:02}", copied.step + 1),
|
||||
150,
|
||||
FlashKind::Success,
|
||||
);
|
||||
}
|
||||
|
||||
pub fn harden_step(&mut self) {
|
||||
@@ -896,7 +897,8 @@ impl App {
|
||||
// If current step is a shallow copy, navigate to source step
|
||||
let pattern = &self.project_state.project.banks[self.editor_ctx.bank].patterns
|
||||
[self.editor_ctx.pattern];
|
||||
if let Some(source) = pattern.steps[self.editor_ctx.step].source {
|
||||
if let Some(source) = pattern.step(self.editor_ctx.step).and_then(|s| s.source)
|
||||
{
|
||||
self.editor_ctx.step = source;
|
||||
}
|
||||
self.load_step_to_editor();
|
||||
|
||||
@@ -51,11 +51,57 @@ const OPERATORS: &[&str] = &[
|
||||
"or", "not", "ceil", "floor", "round", "mtof", "ftom",
|
||||
];
|
||||
const KEYWORDS: &[&str] = &[
|
||||
"if", "else", "then", "emit", "rand", "rrand", "seed", "cycle", "choose", "chance", "[", "]",
|
||||
"zoom", "scale!", "stack", "echo", "necho", "for", "div", "each", "at", "pop", "adsr", "ad",
|
||||
"?", "!?", "<<", ">>", "|", "@", "!", "pcycle", "tempo!", "prob", "sometimes", "often",
|
||||
"rarely", "almostAlways", "almostNever", "always", "never", "coin", "fill", "iter", "every",
|
||||
"gt", "lt", ":", ";", "apply",
|
||||
"if",
|
||||
"else",
|
||||
"then",
|
||||
"emit",
|
||||
"rand",
|
||||
"rrand",
|
||||
"seed",
|
||||
"cycle",
|
||||
"choose",
|
||||
"chance",
|
||||
"[",
|
||||
"]",
|
||||
"zoom",
|
||||
"scale!",
|
||||
"stack",
|
||||
"echo",
|
||||
"necho",
|
||||
"for",
|
||||
"div",
|
||||
"each",
|
||||
"at",
|
||||
"pop",
|
||||
"adsr",
|
||||
"ad",
|
||||
"?",
|
||||
"!?",
|
||||
"<<",
|
||||
">>",
|
||||
"|",
|
||||
"@",
|
||||
"!",
|
||||
"pcycle",
|
||||
"tempo!",
|
||||
"prob",
|
||||
"sometimes",
|
||||
"often",
|
||||
"rarely",
|
||||
"almostAlways",
|
||||
"almostNever",
|
||||
"always",
|
||||
"never",
|
||||
"coin",
|
||||
"fill",
|
||||
"iter",
|
||||
"every",
|
||||
"gt",
|
||||
"lt",
|
||||
":",
|
||||
";",
|
||||
"apply",
|
||||
"clear",
|
||||
];
|
||||
const SOUND: &[&str] = &["sound", "s"];
|
||||
const CONTEXT: &[&str] = &[
|
||||
@@ -168,6 +214,23 @@ const PARAMS: &[&str] = &[
|
||||
"n",
|
||||
"cut",
|
||||
"reset",
|
||||
"eqlo",
|
||||
"eqmid",
|
||||
"eqhi",
|
||||
"tilt",
|
||||
"width",
|
||||
"haas",
|
||||
"llpf",
|
||||
"llpq",
|
||||
"lhpf",
|
||||
"lhpq",
|
||||
"lbpf",
|
||||
"lbpq",
|
||||
"sub",
|
||||
"suboct",
|
||||
"subwave",
|
||||
"bank",
|
||||
"loop",
|
||||
];
|
||||
const INTERVALS: &[&str] = &[
|
||||
"P1", "unison", "m2", "M2", "m3", "M3", "P4", "aug4", "dim5", "tritone", "P5", "m6", "M6",
|
||||
@@ -176,34 +239,27 @@ const INTERVALS: &[&str] = &[
|
||||
];
|
||||
|
||||
const NOTES: &[&str] = &[
|
||||
"c0", "c1", "c2", "c3", "c4", "c5", "c6", "c7", "c8", "c9",
|
||||
"d0", "d1", "d2", "d3", "d4", "d5", "d6", "d7", "d8", "d9",
|
||||
"e0", "e1", "e2", "e3", "e4", "e5", "e6", "e7", "e8", "e9",
|
||||
"f0", "f1", "f2", "f3", "f4", "f5", "f6", "f7", "f8", "f9",
|
||||
"g0", "g1", "g2", "g3", "g4", "g5", "g6", "g7", "g8", "g9",
|
||||
"a0", "a1", "a2", "a3", "a4", "a5", "a6", "a7", "a8", "a9",
|
||||
"b0", "b1", "b2", "b3", "b4", "b5", "b6", "b7", "b8", "b9",
|
||||
"cs0", "cs1", "cs2", "cs3", "cs4", "cs5", "cs6", "cs7", "cs8", "cs9",
|
||||
"ds0", "ds1", "ds2", "ds3", "ds4", "ds5", "ds6", "ds7", "ds8", "ds9",
|
||||
"es0", "es1", "es2", "es3", "es4", "es5", "es6", "es7", "es8", "es9",
|
||||
"fs0", "fs1", "fs2", "fs3", "fs4", "fs5", "fs6", "fs7", "fs8", "fs9",
|
||||
"gs0", "gs1", "gs2", "gs3", "gs4", "gs5", "gs6", "gs7", "gs8", "gs9",
|
||||
"as0", "as1", "as2", "as3", "as4", "as5", "as6", "as7", "as8", "as9",
|
||||
"bs0", "bs1", "bs2", "bs3", "bs4", "bs5", "bs6", "bs7", "bs8", "bs9",
|
||||
"cb0", "cb1", "cb2", "cb3", "cb4", "cb5", "cb6", "cb7", "cb8", "cb9",
|
||||
"db0", "db1", "db2", "db3", "db4", "db5", "db6", "db7", "db8", "db9",
|
||||
"eb0", "eb1", "eb2", "eb3", "eb4", "eb5", "eb6", "eb7", "eb8", "eb9",
|
||||
"fb0", "fb1", "fb2", "fb3", "fb4", "fb5", "fb6", "fb7", "fb8", "fb9",
|
||||
"gb0", "gb1", "gb2", "gb3", "gb4", "gb5", "gb6", "gb7", "gb8", "gb9",
|
||||
"ab0", "ab1", "ab2", "ab3", "ab4", "ab5", "ab6", "ab7", "ab8", "ab9",
|
||||
"bb0", "bb1", "bb2", "bb3", "bb4", "bb5", "bb6", "bb7", "bb8", "bb9",
|
||||
"c#0", "c#1", "c#2", "c#3", "c#4", "c#5", "c#6", "c#7", "c#8", "c#9",
|
||||
"d#0", "d#1", "d#2", "d#3", "d#4", "d#5", "d#6", "d#7", "d#8", "d#9",
|
||||
"e#0", "e#1", "e#2", "e#3", "e#4", "e#5", "e#6", "e#7", "e#8", "e#9",
|
||||
"f#0", "f#1", "f#2", "f#3", "f#4", "f#5", "f#6", "f#7", "f#8", "f#9",
|
||||
"g#0", "g#1", "g#2", "g#3", "g#4", "g#5", "g#6", "g#7", "g#8", "g#9",
|
||||
"a#0", "a#1", "a#2", "a#3", "a#4", "a#5", "a#6", "a#7", "a#8", "a#9",
|
||||
"b#0", "b#1", "b#2", "b#3", "b#4", "b#5", "b#6", "b#7", "b#8", "b#9",
|
||||
"c0", "c1", "c2", "c3", "c4", "c5", "c6", "c7", "c8", "c9", "d0", "d1", "d2", "d3", "d4", "d5",
|
||||
"d6", "d7", "d8", "d9", "e0", "e1", "e2", "e3", "e4", "e5", "e6", "e7", "e8", "e9", "f0", "f1",
|
||||
"f2", "f3", "f4", "f5", "f6", "f7", "f8", "f9", "g0", "g1", "g2", "g3", "g4", "g5", "g6", "g7",
|
||||
"g8", "g9", "a0", "a1", "a2", "a3", "a4", "a5", "a6", "a7", "a8", "a9", "b0", "b1", "b2", "b3",
|
||||
"b4", "b5", "b6", "b7", "b8", "b9", "cs0", "cs1", "cs2", "cs3", "cs4", "cs5", "cs6", "cs7",
|
||||
"cs8", "cs9", "ds0", "ds1", "ds2", "ds3", "ds4", "ds5", "ds6", "ds7", "ds8", "ds9", "es0",
|
||||
"es1", "es2", "es3", "es4", "es5", "es6", "es7", "es8", "es9", "fs0", "fs1", "fs2", "fs3",
|
||||
"fs4", "fs5", "fs6", "fs7", "fs8", "fs9", "gs0", "gs1", "gs2", "gs3", "gs4", "gs5", "gs6",
|
||||
"gs7", "gs8", "gs9", "as0", "as1", "as2", "as3", "as4", "as5", "as6", "as7", "as8", "as9",
|
||||
"bs0", "bs1", "bs2", "bs3", "bs4", "bs5", "bs6", "bs7", "bs8", "bs9", "cb0", "cb1", "cb2",
|
||||
"cb3", "cb4", "cb5", "cb6", "cb7", "cb8", "cb9", "db0", "db1", "db2", "db3", "db4", "db5",
|
||||
"db6", "db7", "db8", "db9", "eb0", "eb1", "eb2", "eb3", "eb4", "eb5", "eb6", "eb7", "eb8",
|
||||
"eb9", "fb0", "fb1", "fb2", "fb3", "fb4", "fb5", "fb6", "fb7", "fb8", "fb9", "gb0", "gb1",
|
||||
"gb2", "gb3", "gb4", "gb5", "gb6", "gb7", "gb8", "gb9", "ab0", "ab1", "ab2", "ab3", "ab4",
|
||||
"ab5", "ab6", "ab7", "ab8", "ab9", "bb0", "bb1", "bb2", "bb3", "bb4", "bb5", "bb6", "bb7",
|
||||
"bb8", "bb9", "c#0", "c#1", "c#2", "c#3", "c#4", "c#5", "c#6", "c#7", "c#8", "c#9", "d#0",
|
||||
"d#1", "d#2", "d#3", "d#4", "d#5", "d#6", "d#7", "d#8", "d#9", "e#0", "e#1", "e#2", "e#3",
|
||||
"e#4", "e#5", "e#6", "e#7", "e#8", "e#9", "f#0", "f#1", "f#2", "f#3", "f#4", "f#5", "f#6",
|
||||
"f#7", "f#8", "f#9", "g#0", "g#1", "g#2", "g#3", "g#4", "g#5", "g#6", "g#7", "g#8", "g#9",
|
||||
"a#0", "a#1", "a#2", "a#3", "a#4", "a#5", "a#6", "a#7", "a#8", "a#9", "b#0", "b#1", "b#2",
|
||||
"b#3", "b#4", "b#5", "b#6", "b#7", "b#8", "b#9",
|
||||
];
|
||||
|
||||
pub fn tokenize_line(line: &str) -> Vec<Token> {
|
||||
|
||||
Reference in New Issue
Block a user