fix(midi-editor): Quantize snaps to the grid the editor shows (#226)

This commit is contained in:
Marco Arnulfo
2026-10-12 12:32:41 +02:00
parent df0c4790b7
commit f792eae580
2 changed files with 52 additions and 14 deletions
+14 -14
View File
@@ -84,6 +84,19 @@ pub fn show(app: &mut SoundApp, ui: &mut Ui) {
regular(11.0),
t.text_dim,
);
let grid_ticks = match app.engine.session().edit.grid {
soundcraft_time::GridValue::Note { value, dotted, triplet } => {
let mut g = value.ticks();
if dotted {
g = g * 3 / 2;
}
if triplet {
g = g * 2 / 3;
}
g.max(1)
}
_ => TICKS_PER_QUARTER / 4,
};
// Header toolbar: whole-clip MIDI operations.
let bar = Rect::from_min_max(pos2(header.max.x - 330.0, header.min.y + 1.0), pos2(header.max.x - 6.0, header.max.y - 1.0));
let mut tb = ui.new_child(egui::UiBuilder::new().max_rect(bar).layout(egui::Layout::right_to_left(egui::Align::Center)));
@@ -99,7 +112,7 @@ pub fn show(app: &mut SoundApp, ui: &mut Ui) {
op = Some(("event.change_velocity", json!({"add": 10})));
}
if tb.small_button("Quantize").on_hover_text("Quantize to the grid").clicked() {
op = Some(("event.quantize", json!({"grid": "1/16", "strength": 100})));
op = Some(("event.quantize", json!({"grid": grid_ticks, "strength": 100})));
}
if let Some((id, mut p)) = op {
p["clips"] = json!([cid.0]);
@@ -109,19 +122,6 @@ pub fn show(app: &mut SoundApp, ui: &mut Ui) {
let sr = s.sample_rate;
let base_tick = s.tempo.samples_to_ticks(clip.start, sr);
let len_ticks = (s.tempo.samples_to_ticks(clip.end(), sr) - base_tick).max(1);
let grid_ticks = match s.edit.grid {
soundcraft_time::GridValue::Note { value, dotted, triplet } => {
let mut g = value.ticks();
if dotted {
g = g * 3 / 2;
}
if triplet {
g = g * 2 / 3;
}
g.max(1)
}
_ => TICKS_PER_QUARTER / 4,
};
let roll = Rect::from_min_max(pos2(full.min.x + KEY_W, header.max.y), pos2(full.max.x - 4.0, full.max.y - VEL_H - 4.0));
let keys = Rect::from_min_max(pos2(full.min.x, roll.min.y), pos2(roll.min.x, roll.max.y));
let vel = Rect::from_min_max(pos2(roll.min.x, roll.max.y + 4.0), pos2(roll.max.x, full.max.y - 2.0));
+38
View File
@@ -0,0 +1,38 @@
//! The MIDI Editor's Quantize button snaps to the grid the editor shows.
use egui::vec2;
use egui_kittest::{Harness, kittest::Queryable};
use serde_json::json;
use soundcraft_ui_egui::{Services, SoundApp};
fn quantize_on(grid: &str) -> i64 {
let mut app = SoundApp::new(soundcraft_engine::Engine::default(), None, Services::default());
app.run("track.new", json!({"name": "Grid Check", "kind": "midi"})).unwrap();
let clip = app.run("midi.new_clip", json!({"track": "Grid Check", "start": 0, "end": 96_000})).unwrap()["clip"].clone();
app.run("midi.note_add", json!({"clip": clip, "pitch": 60, "start_ticks": 300, "length_ticks": 240})).unwrap();
app.run("edit.select", json!({"clips": [clip]})).unwrap();
app.run("view.grid", json!({"value": grid})).unwrap();
app.ui.show_midi_editor = true;
let mut h = Harness::builder().with_size(vec2(1600.0, 1000.0)).build_ui_state(
|ui, app: &mut SoundApp| {
app.logic(&ui.ctx().clone());
app.ui(ui);
},
app,
);
h.run_steps(5);
h.get_by_label("Quantize").click();
h.run_steps(3);
let notes = h.state_mut().run("midi.notes", json!({"clip": clip})).unwrap();
assert_eq!(notes["notes"][0]["length"], 240, "{notes}");
notes["notes"][0]["start"].as_i64().unwrap()
}
#[test]
fn quantize_follows_a_quarter_note_grid() {
assert_eq!(quantize_on("1/4"), 0);
}
#[test]
fn quantize_follows_a_sixteenth_note_grid() {
assert_eq!(quantize_on("1/16"), 240);
}