UI consolidation round: pace checkbox, unified Limit notes section, fixes

- Pace dropdown REMOVED from Phrase setup — replaced by a 'Sync to
  exercise' checkbox in the Metronome widget (analog to 'Auto' in
  single-note modes). pace is derived from the checkbox (checked = tempo,
  unchecked = step).
- Preview clicks now registered in PREVIEW_GROUP: stopping/abandoning a
  preview cancels its scheduled clicks — no more parallel ghost
  metronomes after preview->stop->next exercise.
- Chord pool replaces the chord dropdown entirely (single chord AND
  sequences): togglable degree list, empty = all 7.
- Giuliani figures corrected to NOTE sequences (p always takes the bass/
  root): p-i-m, p-i-m-a, p-a-m-i, a-i-m, p-i-m-a-m-i, p-m-a-m-i-m,
  p-e-a-m-a-m (replaced the fingering-driven patterns that made no
  musical sense).
- 'Limit notes' is now one bordered section like Tuner/Metronome, with a
  header, Clear-all link, and Strings behind a toggle (row hidden until
  restricted).
This commit is contained in:
OpenClaw
2026-09-14 21:08:15 +00:00
parent 6253194632
commit 9acf32a32f
6 changed files with 120 additions and 116 deletions

View File

@@ -140,8 +140,10 @@ class AudioEngine {
* Short NON-TONAL metronome click (~10 ms noise burst). Not registered in
* the pitched-audio gate; instead the mic is blanked for 30 ms so the
* click itself can't be scored as a pitch (contract E2).
* Optional groupId: the click becomes cancellable via cancelGroup (used
* by preview clicks so stopping a preview also stops its clicks).
*/
playClickAt(time: number, volume = 0.5, accent = false): void {
playClickAt(time: number, volume = 0.5, accent = false, groupId?: string): void {
const ctx = this.ensureContext();
if (!this.noiseBuffer) {
const len = Math.floor(ctx.sampleRate * 0.01);
@@ -160,6 +162,11 @@ class AudioEngine {
src.start(time);
src.stop(time + 0.02);
this.clickBlankUntil = Math.max(this.clickBlankUntil, time + 0.03);
if (groupId) {
// Register as a cancellable node (cast: clicks are buffer sources,
// cancelGroup only calls stop() on them, which BufferSource has).
this.active.push({ osc: src as unknown as OscillatorNode, gain: gainNode, endsAt: time + 0.02, groupId });
}
}
/** True for ~30 ms after a scheduled click (mic frames should be skipped). */