@sudobility/music_types 0.11.30 → 0.11.31
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/domain/commands/edit-commands.d.ts +3 -3
- package/dist/domain/commands/edit-commands.d.ts.map +1 -1
- package/dist/domain/commands/edit-commands.js +25 -25
- package/dist/domain/commands/edit-commands.js.map +1 -1
- package/dist/domain/commands/history.d.ts +32 -0
- package/dist/domain/commands/history.d.ts.map +1 -0
- package/dist/domain/commands/history.js +63 -0
- package/dist/domain/commands/history.js.map +1 -0
- package/dist/domain/commands/note-commands.d.ts +147 -3
- package/dist/domain/commands/note-commands.d.ts.map +1 -1
- package/dist/domain/commands/note-commands.js +438 -46
- package/dist/domain/commands/note-commands.js.map +1 -1
- package/dist/domain/commands/reflow.d.ts +1 -1
- package/dist/domain/commands/reflow.js +17 -17
- package/dist/domain/commands/reflow.js.map +1 -1
- package/dist/domain/commands/region-commands.d.ts +4 -4
- package/dist/domain/commands/region-commands.d.ts.map +1 -1
- package/dist/domain/commands/region-commands.js +7 -7
- package/dist/domain/commands/region-commands.js.map +1 -1
- package/dist/domain/commands/relocate-commands.d.ts +3 -3
- package/dist/domain/commands/relocate-commands.js +15 -15
- package/dist/domain/commands/relocate-commands.js.map +1 -1
- package/dist/domain/commands/ripple-commands.d.ts +2 -2
- package/dist/domain/commands/ripple-commands.js +13 -13
- package/dist/domain/commands/ripple-commands.js.map +1 -1
- package/dist/domain/commands/snapshot.d.ts +3 -3
- package/dist/domain/commands/snapshot.js +5 -5
- package/dist/domain/commands/snapshot.js.map +1 -1
- package/dist/domain/commands/structure-commands.d.ts +86 -5
- package/dist/domain/commands/structure-commands.d.ts.map +1 -1
- package/dist/domain/commands/structure-commands.js +272 -36
- package/dist/domain/commands/structure-commands.js.map +1 -1
- package/dist/domain/commands/track-commands.d.ts +2 -2
- package/dist/domain/commands/track-commands.js +7 -7
- package/dist/domain/commands/track-commands.js.map +1 -1
- package/dist/domain/commands/types.d.ts +2 -2
- package/dist/domain/generation/replacement-region.d.ts +32 -0
- package/dist/domain/generation/replacement-region.d.ts.map +1 -0
- package/dist/domain/generation/replacement-region.js +79 -0
- package/dist/domain/generation/replacement-region.js.map +1 -0
- package/dist/domain/instruments/gm-icon.d.ts +12 -0
- package/dist/domain/instruments/gm-icon.d.ts.map +1 -0
- package/dist/domain/instruments/gm-icon.js +327 -0
- package/dist/domain/instruments/gm-icon.js.map +1 -0
- package/dist/domain/instruments/gm-kit.js +9 -9
- package/dist/domain/instruments/gm-kit.js.map +1 -1
- package/dist/domain/instruments/gm-percussion.js +48 -48
- package/dist/domain/instruments/gm-percussion.js.map +1 -1
- package/dist/domain/instruments/gm-polyphony.js +6 -6
- package/dist/domain/instruments/gm-range.js +6 -6
- package/dist/domain/instruments/gm-transposition.js +1 -1
- package/dist/domain/instruments/gm.d.ts +1 -1
- package/dist/domain/instruments/gm.js +161 -161
- package/dist/domain/instruments/gm.js.map +1 -1
- package/dist/domain/instruments/icon-art.d.ts +75 -0
- package/dist/domain/instruments/icon-art.d.ts.map +1 -0
- package/dist/domain/instruments/icon-art.js +93 -0
- package/dist/domain/instruments/icon-art.js.map +1 -0
- package/dist/domain/instruments/instrument-fit.d.ts +31 -0
- package/dist/domain/instruments/instrument-fit.d.ts.map +1 -0
- package/dist/domain/instruments/instrument-fit.js +117 -0
- package/dist/domain/instruments/instrument-fit.js.map +1 -0
- package/dist/domain/instruments/instrument-options.d.ts +62 -0
- package/dist/domain/instruments/instrument-options.d.ts.map +1 -0
- package/dist/domain/instruments/instrument-options.js +69 -0
- package/dist/domain/instruments/instrument-options.js.map +1 -0
- package/dist/domain/instruments/track-instrument.d.ts +80 -0
- package/dist/domain/instruments/track-instrument.d.ts.map +1 -0
- package/dist/domain/instruments/track-instrument.js +102 -0
- package/dist/domain/instruments/track-instrument.js.map +1 -0
- package/dist/domain/notation/chord-symbol.d.ts +26 -0
- package/dist/domain/notation/chord-symbol.d.ts.map +1 -0
- package/dist/domain/notation/chord-symbol.js +32 -0
- package/dist/domain/notation/chord-symbol.js.map +1 -0
- package/dist/domain/notation/lyric-syllables.d.ts +15 -0
- package/dist/domain/notation/lyric-syllables.d.ts.map +1 -0
- package/dist/domain/notation/lyric-syllables.js +15 -0
- package/dist/domain/notation/lyric-syllables.js.map +1 -0
- package/dist/domain/notation/music-vocabulary.d.ts +59 -0
- package/dist/domain/notation/music-vocabulary.d.ts.map +1 -0
- package/dist/domain/notation/music-vocabulary.js +164 -0
- package/dist/domain/notation/music-vocabulary.js.map +1 -0
- package/dist/domain/pitch/pitch.d.ts +1 -1
- package/dist/domain/pitch/pitch.js +29 -29
- package/dist/domain/pitch/transpose.d.ts +1 -1
- package/dist/domain/pitch/transpose.js +2 -2
- package/dist/domain/quantization/quantize.d.ts +2 -2
- package/dist/domain/quantization/quantize.js +9 -9
- package/dist/domain/quantization/quantize.js.map +1 -1
- package/dist/domain/score/articulation.d.ts +56 -0
- package/dist/domain/score/articulation.d.ts.map +1 -0
- package/dist/domain/score/articulation.js +56 -0
- package/dist/domain/score/articulation.js.map +1 -0
- package/dist/domain/score/bar-numbers.d.ts +38 -0
- package/dist/domain/score/bar-numbers.d.ts.map +1 -0
- package/dist/domain/score/bar-numbers.js +49 -0
- package/dist/domain/score/bar-numbers.js.map +1 -0
- package/dist/domain/score/collapse-rests.d.ts +23 -0
- package/dist/domain/score/collapse-rests.d.ts.map +1 -0
- package/dist/domain/score/collapse-rests.js +78 -0
- package/dist/domain/score/collapse-rests.js.map +1 -0
- package/dist/domain/score/cue-notes.d.ts +24 -0
- package/dist/domain/score/cue-notes.d.ts.map +1 -0
- package/dist/domain/score/cue-notes.js +105 -0
- package/dist/domain/score/cue-notes.js.map +1 -0
- package/dist/domain/score/dynamics.d.ts +62 -0
- package/dist/domain/score/dynamics.d.ts.map +1 -0
- package/dist/domain/score/dynamics.js +142 -0
- package/dist/domain/score/dynamics.js.map +1 -0
- package/dist/domain/score/effective-clef.d.ts +42 -0
- package/dist/domain/score/effective-clef.d.ts.map +1 -0
- package/dist/domain/score/effective-clef.js +39 -0
- package/dist/domain/score/effective-clef.js.map +1 -0
- package/dist/domain/score/extract-part.d.ts +10 -0
- package/dist/domain/score/extract-part.d.ts.map +1 -0
- package/dist/domain/score/extract-part.js +44 -0
- package/dist/domain/score/extract-part.js.map +1 -0
- package/dist/domain/score/factory.d.ts +1 -1
- package/dist/domain/score/factory.js +12 -12
- package/dist/domain/score/factory.js.map +1 -1
- package/dist/domain/score/fermata-tempo.d.ts +40 -0
- package/dist/domain/score/fermata-tempo.d.ts.map +1 -0
- package/dist/domain/score/fermata-tempo.js +95 -0
- package/dist/domain/score/fermata-tempo.js.map +1 -0
- package/dist/domain/score/flatten.d.ts +32 -0
- package/dist/domain/score/flatten.d.ts.map +1 -0
- package/dist/domain/score/flatten.js +132 -0
- package/dist/domain/score/flatten.js.map +1 -0
- package/dist/domain/score/fragment.d.ts +3 -3
- package/dist/domain/score/fragment.js +8 -8
- package/dist/domain/score/fragment.js.map +1 -1
- package/dist/domain/score/ids.d.ts +1 -1
- package/dist/domain/score/ids.js +6 -6
- package/dist/domain/score/ottava.d.ts +41 -0
- package/dist/domain/score/ottava.d.ts.map +1 -0
- package/dist/domain/score/ottava.js +98 -0
- package/dist/domain/score/ottava.js.map +1 -0
- package/dist/domain/score/performance-timeline.d.ts +42 -0
- package/dist/domain/score/performance-timeline.d.ts.map +1 -0
- package/dist/domain/score/performance-timeline.js +78 -0
- package/dist/domain/score/performance-timeline.js.map +1 -0
- package/dist/domain/score/queries.d.ts +2 -2
- package/dist/domain/score/queries.js +7 -7
- package/dist/domain/score/queries.js.map +1 -1
- package/dist/domain/score/rehearsal-marks.d.ts +30 -0
- package/dist/domain/score/rehearsal-marks.d.ts.map +1 -0
- package/dist/domain/score/rehearsal-marks.js +126 -0
- package/dist/domain/score/rehearsal-marks.js.map +1 -0
- package/dist/domain/score/repeat-order.d.ts +21 -0
- package/dist/domain/score/repeat-order.d.ts.map +1 -0
- package/dist/domain/score/repeat-order.js +123 -0
- package/dist/domain/score/repeat-order.js.map +1 -0
- package/dist/domain/score/snapshot-tree.d.ts +23 -0
- package/dist/domain/score/snapshot-tree.d.ts.map +1 -0
- package/dist/domain/score/snapshot-tree.js +45 -0
- package/dist/domain/score/snapshot-tree.js.map +1 -0
- package/dist/domain/score/ties.d.ts +1 -1
- package/dist/domain/score/ties.js +7 -7
- package/dist/domain/score/ties.js.map +1 -1
- package/dist/domain/score/written-pitch.d.ts +33 -0
- package/dist/domain/score/written-pitch.d.ts.map +1 -0
- package/dist/domain/score/written-pitch.js +110 -0
- package/dist/domain/score/written-pitch.js.map +1 -0
- package/dist/domain/selection/range-select.d.ts +13 -0
- package/dist/domain/selection/range-select.d.ts.map +1 -0
- package/dist/domain/selection/range-select.js +40 -0
- package/dist/domain/selection/range-select.js.map +1 -0
- package/dist/domain/selection/selection-editing.d.ts +30 -0
- package/dist/domain/selection/selection-editing.d.ts.map +1 -0
- package/dist/domain/selection/selection-editing.js +31 -0
- package/dist/domain/selection/selection-editing.js.map +1 -0
- package/dist/domain/selection/selection.d.ts +2 -2
- package/dist/domain/selection/selection.js +7 -7
- package/dist/domain/selection/selection.js.map +1 -1
- package/dist/domain/selection/types.d.ts +2 -2
- package/dist/domain/time/duration-modifiers.d.ts +40 -0
- package/dist/domain/time/duration-modifiers.d.ts.map +1 -0
- package/dist/domain/time/duration-modifiers.js +50 -0
- package/dist/domain/time/duration-modifiers.js.map +1 -0
- package/dist/domain/time/duration-selection.d.ts +23 -0
- package/dist/domain/time/duration-selection.d.ts.map +1 -0
- package/dist/domain/time/duration-selection.js +50 -0
- package/dist/domain/time/duration-selection.js.map +1 -0
- package/dist/domain/time/durations.js +6 -6
- package/dist/domain/time/durations.js.map +1 -1
- package/dist/domain/time/fraction.d.ts +1 -1
- package/dist/domain/time/fraction.js +1 -1
- package/dist/domain/time/tap-to-note.d.ts +21 -0
- package/dist/domain/time/tap-to-note.d.ts.map +1 -0
- package/dist/domain/time/tap-to-note.js +43 -0
- package/dist/domain/time/tap-to-note.js.map +1 -0
- package/dist/domain/time/tempo-map.d.ts +1 -1
- package/dist/domain/time/ticks.d.ts +1 -1
- package/dist/domain/time/ticks.d.ts.map +1 -1
- package/dist/domain/time/ticks.js +14 -14
- package/dist/domain/time/ticks.js.map +1 -1
- package/dist/domain/time/tuplets.d.ts +42 -0
- package/dist/domain/time/tuplets.d.ts.map +1 -0
- package/dist/domain/time/tuplets.js +53 -0
- package/dist/domain/time/tuplets.js.map +1 -0
- package/dist/domain/validation/issues.d.ts +1 -1
- package/dist/domain/validation/issues.js +20 -20
- package/dist/domain/validation/validator.d.ts +2 -2
- package/dist/domain/validation/validator.d.ts.map +1 -1
- package/dist/domain/validation/validator.js +31 -31
- package/dist/domain/validation/validator.js.map +1 -1
- package/dist/domain/voicing/allocate.d.ts +2 -2
- package/dist/domain/voicing/allocate.js +6 -6
- package/dist/domain/voicing/allocate.js.map +1 -1
- package/dist/index.d.ts +90 -59
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +151 -83
- package/dist/index.js.map +1 -1
- package/dist/platform/audio.d.ts.map +1 -1
- package/dist/platform/file.d.ts.map +1 -1
- package/dist/platform/index.d.ts +7 -7
- package/dist/platform/index.js +7 -7
- package/dist/platform/midi-input.d.ts +2 -2
- package/dist/platform/midi.d.ts.map +1 -1
- package/dist/platform/mod.d.ts +2 -2
- package/dist/platform/playback.d.ts +7 -7
- package/dist/platform/xml.js +1 -1
- package/dist/services/regeneration/controller.d.ts +6 -6
- package/dist/services/regeneration/controller.js +12 -12
- package/dist/services/regeneration/controller.js.map +1 -1
- package/dist/test/fixtures.d.ts +1 -1
- package/dist/test/fixtures.js +103 -103
- package/dist/test/fixtures.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Caret-to-click range selection (the cmd-click gesture). Pure over `Score`
|
|
3
|
+
* — no store, no DOM — so the tick math is unit-testable without rendering.
|
|
4
|
+
*/
|
|
5
|
+
import { isNoteEvent } from "../../index.js";
|
|
6
|
+
/**
|
|
7
|
+
* Every note whose `startTick` falls in `[min(fromTick,toTick),
|
|
8
|
+
* max(...))` on one of `trackIds`, in ascending tick order.
|
|
9
|
+
*
|
|
10
|
+
* Half-open on purpose: cmd-clicking exactly on a note selects everything up
|
|
11
|
+
* to it but not the note itself, so two adjacent ranges sharing a boundary
|
|
12
|
+
* don't both claim the note sitting on it. Rests are skipped — they aren't
|
|
13
|
+
* selectable material for any downstream action (transpose, regenerate,
|
|
14
|
+
* copy all operate on notes).
|
|
15
|
+
*/
|
|
16
|
+
export function noteIdsInTickRange(score, fromTick, toTick, trackIds) {
|
|
17
|
+
const start = Math.min(fromTick, toTick);
|
|
18
|
+
const end = Math.max(fromTick, toTick);
|
|
19
|
+
if (start === end || trackIds.length === 0)
|
|
20
|
+
return [];
|
|
21
|
+
const wanted = new Set(trackIds);
|
|
22
|
+
const hits = [];
|
|
23
|
+
for (const track of score.tracks) {
|
|
24
|
+
if (!wanted.has(track.id))
|
|
25
|
+
continue;
|
|
26
|
+
for (const measure of track.measures) {
|
|
27
|
+
for (const voice of measure.voices) {
|
|
28
|
+
for (const event of voice.events) {
|
|
29
|
+
if (!isNoteEvent(event))
|
|
30
|
+
continue;
|
|
31
|
+
if (event.startTick >= start && event.startTick < end) {
|
|
32
|
+
hits.push({ id: event.id, startTick: event.startTick });
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
return hits.sort((a, b) => a.startTick - b.startTick).map((h) => h.id);
|
|
39
|
+
}
|
|
40
|
+
//# sourceMappingURL=range-select.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"range-select.js","sourceRoot":"","sources":["../../../src/domain/selection/range-select.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAG7C;;;;;;;;;GASG;AACH,MAAM,UAAU,kBAAkB,CAChC,KAAY,EACZ,QAAgB,EAChB,MAAc,EACd,QAAyB;IAEzB,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;IACzC,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;IACvC,IAAI,KAAK,KAAK,GAAG,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,EAAE,CAAC;IAEtD,MAAM,MAAM,GAAG,IAAI,GAAG,CAAC,QAAQ,CAAC,CAAC;IACjC,MAAM,IAAI,GAA2C,EAAE,CAAC;IAExD,KAAK,MAAM,KAAK,IAAI,KAAK,CAAC,MAAM,EAAE,CAAC;QACjC,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC;YAAE,SAAS;QACpC,KAAK,MAAM,OAAO,IAAI,KAAK,CAAC,QAAQ,EAAE,CAAC;YACrC,KAAK,MAAM,KAAK,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC;gBACnC,KAAK,MAAM,KAAK,IAAI,KAAK,CAAC,MAAM,EAAE,CAAC;oBACjC,IAAI,CAAC,WAAW,CAAC,KAAK,CAAC;wBAAE,SAAS;oBAClC,IAAI,KAAK,CAAC,SAAS,IAAI,KAAK,IAAI,KAAK,CAAC,SAAS,GAAG,GAAG,EAAE,CAAC;wBACtD,IAAI,CAAC,IAAI,CAAC,EAAE,EAAE,EAAE,KAAK,CAAC,EAAE,EAAE,SAAS,EAAE,KAAK,CAAC,SAAS,EAAE,CAAC,CAAC;oBAC1D,CAAC;gBACH,CAAC;YACH,CAAC;QACH,CAAC;IACH,CAAC;IAED,OAAO,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,SAAS,GAAG,CAAC,CAAC,SAAS,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;AACzE,CAAC"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* When the piano keyboard edits the selection instead of entering notes.
|
|
3
|
+
*
|
|
4
|
+
* Pure over a note list — no store, no DOM — so the rule that decides between
|
|
5
|
+
* the keyboard's two jobs is testable on its own, in the same shape as
|
|
6
|
+
* `tap-to-note.ts` and `pitch-drag.ts`.
|
|
7
|
+
*/
|
|
8
|
+
import type { NoteEvent } from "../../index.js";
|
|
9
|
+
export type ChordSelection = {
|
|
10
|
+
startTick: number;
|
|
11
|
+
durationTicks: number;
|
|
12
|
+
trackId: string;
|
|
13
|
+
voiceId: string;
|
|
14
|
+
notes: NoteEvent[];
|
|
15
|
+
};
|
|
16
|
+
/**
|
|
17
|
+
* The selection as one editable chord, or `null` when it is not exactly one.
|
|
18
|
+
*
|
|
19
|
+
* A chord is notes sharing a start tick in one voice. A selection spanning
|
|
20
|
+
* several ticks contains more than one chord, and there is no defensible way
|
|
21
|
+
* to guess which one the player meant — so the keyboard stays in entry mode
|
|
22
|
+
* rather than editing something arbitrary. That the rule fits in a sentence is
|
|
23
|
+
* the point: it has to be explainable in the UI.
|
|
24
|
+
*
|
|
25
|
+
* Voice, not just track: two voices on one stave routinely sound together, and
|
|
26
|
+
* treating a melody note and the bass note underneath it as one chord would
|
|
27
|
+
* let a key press edit a line the player was not looking at.
|
|
28
|
+
*/
|
|
29
|
+
export declare function chordSelection(notes: readonly NoteEvent[]): ChordSelection | null;
|
|
30
|
+
//# sourceMappingURL=selection-editing.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"selection-editing.d.ts","sourceRoot":"","sources":["../../../src/domain/selection/selection-editing.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAEhD,MAAM,MAAM,cAAc,GAAG;IAC3B,SAAS,EAAE,MAAM,CAAC;IAClB,aAAa,EAAE,MAAM,CAAC;IACtB,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,SAAS,EAAE,CAAC;CACpB,CAAC;AAEF;;;;;;;;;;;;GAYG;AACH,wBAAgB,cAAc,CAC5B,KAAK,EAAE,SAAS,SAAS,EAAE,GAC1B,cAAc,GAAG,IAAI,CAmBvB"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The selection as one editable chord, or `null` when it is not exactly one.
|
|
3
|
+
*
|
|
4
|
+
* A chord is notes sharing a start tick in one voice. A selection spanning
|
|
5
|
+
* several ticks contains more than one chord, and there is no defensible way
|
|
6
|
+
* to guess which one the player meant — so the keyboard stays in entry mode
|
|
7
|
+
* rather than editing something arbitrary. That the rule fits in a sentence is
|
|
8
|
+
* the point: it has to be explainable in the UI.
|
|
9
|
+
*
|
|
10
|
+
* Voice, not just track: two voices on one stave routinely sound together, and
|
|
11
|
+
* treating a melody note and the bass note underneath it as one chord would
|
|
12
|
+
* let a key press edit a line the player was not looking at.
|
|
13
|
+
*/
|
|
14
|
+
export function chordSelection(notes) {
|
|
15
|
+
if (notes.length === 0)
|
|
16
|
+
return null;
|
|
17
|
+
const [first] = notes;
|
|
18
|
+
const sameChord = notes.every((note) => note.startTick === first.startTick &&
|
|
19
|
+
note.trackId === first.trackId &&
|
|
20
|
+
note.voiceId === first.voiceId);
|
|
21
|
+
if (!sameChord)
|
|
22
|
+
return null;
|
|
23
|
+
return {
|
|
24
|
+
startTick: first.startTick,
|
|
25
|
+
durationTicks: first.durationTicks,
|
|
26
|
+
trackId: first.trackId,
|
|
27
|
+
voiceId: first.voiceId,
|
|
28
|
+
notes: [...notes],
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
//# sourceMappingURL=selection-editing.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"selection-editing.js","sourceRoot":"","sources":["../../../src/domain/selection/selection-editing.ts"],"names":[],"mappings":"AAiBA;;;;;;;;;;;;GAYG;AACH,MAAM,UAAU,cAAc,CAC5B,KAA2B;IAE3B,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,IAAI,CAAC;IAEpC,MAAM,CAAC,KAAK,CAAC,GAAG,KAAK,CAAC;IACtB,MAAM,SAAS,GAAG,KAAK,CAAC,KAAK,CAC3B,CAAC,IAAI,EAAE,EAAE,CACP,IAAI,CAAC,SAAS,KAAK,KAAK,CAAC,SAAS;QAClC,IAAI,CAAC,OAAO,KAAK,KAAK,CAAC,OAAO;QAC9B,IAAI,CAAC,OAAO,KAAK,KAAK,CAAC,OAAO,CACjC,CAAC;IACF,IAAI,CAAC,SAAS;QAAE,OAAO,IAAI,CAAC;IAE5B,OAAO;QACL,SAAS,EAAE,KAAK,CAAC,SAAS;QAC1B,aAAa,EAAE,KAAK,CAAC,aAAa;QAClC,OAAO,EAAE,KAAK,CAAC,OAAO;QACtB,OAAO,EAAE,KAAK,CAAC,OAAO;QACtB,KAAK,EAAE,CAAC,GAAG,KAAK,CAAC;KAClB,CAAC;AACJ,CAAC"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import type { Score } from
|
|
2
|
-
import type { ScoreRange, ScoreSelection } from
|
|
1
|
+
import type { Score } from "../../index.js";
|
|
2
|
+
import type { ScoreRange, ScoreSelection } from "./types.js";
|
|
3
3
|
/**
|
|
4
4
|
* The smallest tick range, aligned to full-measure boundaries on every
|
|
5
5
|
* implicated track, that covers everything named by `sel` (selected
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* Selection helpers (spec §9). Pure, read-only functions over `Score` +
|
|
3
3
|
* `ScoreSelection`/`ScoreRange` — no UI, store, or rendering concerns.
|
|
4
4
|
*/
|
|
5
|
-
import { findEvent, findMeasure, findTrack, scoreEndTick, } from
|
|
5
|
+
import { findEvent, findMeasure, findTrack, scoreEndTick, } from "../score/queries.js";
|
|
6
6
|
/**
|
|
7
7
|
* The smallest tick range, aligned to full-measure boundaries on every
|
|
8
8
|
* implicated track, that covers everything named by `sel` (selected
|
|
@@ -36,7 +36,7 @@ export function selectionToRange(score, sel) {
|
|
|
36
36
|
continue;
|
|
37
37
|
minTick = Math.min(minTick, measure.startTick);
|
|
38
38
|
maxTick = Math.max(maxTick, measure.startTick + measure.durationTicks);
|
|
39
|
-
const owningTrack = score.tracks.find(t => t.measures.some(m => m.id === measureId));
|
|
39
|
+
const owningTrack = score.tracks.find((t) => t.measures.some((m) => m.id === measureId));
|
|
40
40
|
if (owningTrack)
|
|
41
41
|
trackIds.add(owningTrack.id);
|
|
42
42
|
}
|
|
@@ -54,7 +54,7 @@ export function selectionToRange(score, sel) {
|
|
|
54
54
|
const scopedTrackIds = [...trackIds];
|
|
55
55
|
const tracksInScope = scopedTrackIds.length > 0
|
|
56
56
|
? scopedTrackIds
|
|
57
|
-
.map(id => findTrack(score, id))
|
|
57
|
+
.map((id) => findTrack(score, id))
|
|
58
58
|
.filter((t) => t !== null)
|
|
59
59
|
: score.tracks;
|
|
60
60
|
let alignedStart = minTick;
|
|
@@ -86,9 +86,9 @@ function dedupe(ids) {
|
|
|
86
86
|
* swapped into ascending order if given reversed).
|
|
87
87
|
*/
|
|
88
88
|
export function normalizeSelection(score, sel) {
|
|
89
|
-
const eventIds = dedupe(sel.eventIds).filter(id => findEvent(score, id) !== null);
|
|
90
|
-
const measureIds = dedupe(sel.measureIds).filter(id => findMeasure(score, id) !== null);
|
|
91
|
-
const trackIds = dedupe(sel.trackIds).filter(id => findTrack(score, id) !== null);
|
|
89
|
+
const eventIds = dedupe(sel.eventIds).filter((id) => findEvent(score, id) !== null);
|
|
90
|
+
const measureIds = dedupe(sel.measureIds).filter((id) => findMeasure(score, id) !== null);
|
|
91
|
+
const trackIds = dedupe(sel.trackIds).filter((id) => findTrack(score, id) !== null);
|
|
92
92
|
const normalized = { eventIds, measureIds, trackIds };
|
|
93
93
|
if (sel.range) {
|
|
94
94
|
const startTick = Math.max(0, Math.min(sel.range.startTick, sel.range.endTick));
|
|
@@ -114,7 +114,7 @@ export function selectionIsRegenerable(score, sel) {
|
|
|
114
114
|
return false;
|
|
115
115
|
if (range.endTick > scoreEndTick(score))
|
|
116
116
|
return false;
|
|
117
|
-
return range.trackIds.every(id => findTrack(score, id) !== null);
|
|
117
|
+
return range.trackIds.every((id) => findTrack(score, id) !== null);
|
|
118
118
|
}
|
|
119
119
|
export function selectionSummaryLabel(sel, copy, regenerated = false) {
|
|
120
120
|
const mark = (summary) => regenerated ? copy.regenerated(summary) : summary;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"selection.js","sourceRoot":"","sources":["../../../src/domain/selection/selection.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,EACL,SAAS,EACT,WAAW,EACX,SAAS,EACT,YAAY,GACb,MAAM,qBAAqB,CAAC;AAI7B;;;;;;;;;;;;;;GAcG;AACH,MAAM,UAAU,gBAAgB,CAC9B,KAAY,EACZ,GAAmB;IAEnB,IAAI,OAAO,GAAG,QAAQ,CAAC;IACvB,IAAI,OAAO,GAAG,CAAC,QAAQ,CAAC;IACxB,MAAM,QAAQ,GAAG,IAAI,GAAG,EAAU,CAAC;IAEnC,KAAK,MAAM,OAAO,IAAI,GAAG,CAAC,QAAQ,EAAE,CAAC;QACnC,MAAM,KAAK,GAAG,SAAS,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC;QACxC,IAAI,CAAC,KAAK;YAAE,SAAS;QACrB,OAAO,GAAG,IAAI,CAAC,GAAG,CAAC,OAAO,EAAE,KAAK,CAAC,SAAS,CAAC,CAAC;QAC7C,OAAO,GAAG,IAAI,CAAC,GAAG,CAAC,OAAO,EAAE,KAAK,CAAC,SAAS,GAAG,KAAK,CAAC,aAAa,CAAC,CAAC;QACnE,QAAQ,CAAC,GAAG,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;IAC9B,CAAC;IAED,KAAK,MAAM,SAAS,IAAI,GAAG,CAAC,UAAU,EAAE,CAAC;QACvC,MAAM,OAAO,GAAG,WAAW,CAAC,KAAK,EAAE,SAAS,CAAC,CAAC;QAC9C,IAAI,CAAC,OAAO;YAAE,SAAS;QACvB,OAAO,GAAG,IAAI,CAAC,GAAG,CAAC,OAAO,EAAE,OAAO,CAAC,SAAS,CAAC,CAAC;QAC/C,OAAO,GAAG,IAAI,CAAC,GAAG,CAAC,OAAO,EAAE,OAAO,CAAC,SAAS,GAAG,OAAO,CAAC,aAAa,CAAC,CAAC;QACvE,MAAM,WAAW,GAAG,KAAK,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,
|
|
1
|
+
{"version":3,"file":"selection.js","sourceRoot":"","sources":["../../../src/domain/selection/selection.ts"],"names":[],"mappings":"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"}
|
|
@@ -3,8 +3,8 @@
|
|
|
3
3
|
* canonical in @sudobility/music_types and re-exported here so domain
|
|
4
4
|
* modules keep one import site; `emptySelection` is the runtime helper.
|
|
5
5
|
*/
|
|
6
|
-
export type { ScoreRange, ScoreSelection } from
|
|
7
|
-
import type { ScoreSelection } from
|
|
6
|
+
export type { ScoreRange, ScoreSelection } from "../../index.js";
|
|
7
|
+
import type { ScoreSelection } from "../../index.js";
|
|
8
8
|
/** An empty selection: no events, measures, tracks, or range selected. */
|
|
9
9
|
export declare function emptySelection(): ScoreSelection;
|
|
10
10
|
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Composing and decomposing a `DurationName`.
|
|
3
|
+
*
|
|
4
|
+
* The model spells out all eighteen names — `quarter`, `dotted-quarter`,
|
|
5
|
+
* `triplet-quarter` and so on — but a toolbar with eighteen buttons is
|
|
6
|
+
* unreadable. The editor instead offers six base values and two modifiers,
|
|
7
|
+
* which is how notation editors have always done it: pick the note, then dot
|
|
8
|
+
* or tuplet it.
|
|
9
|
+
*
|
|
10
|
+
* Pure, so the composition rules are testable without a toolbar.
|
|
11
|
+
*/
|
|
12
|
+
import type { DurationName } from "../../index.js";
|
|
13
|
+
/** The six plain note values, longest first — the toolbar's row. */
|
|
14
|
+
export declare const BASE_DURATIONS: readonly ["whole", "half", "quarter", "eighth", "sixteenth", "thirtysecond"];
|
|
15
|
+
export type BaseDuration = (typeof BASE_DURATIONS)[number];
|
|
16
|
+
/**
|
|
17
|
+
* A duration split into the parts a toolbar shows.
|
|
18
|
+
*
|
|
19
|
+
* `dotted` and `triplet` are mutually exclusive, because the model has no name
|
|
20
|
+
* for a dotted triplet — so the type cannot represent one either.
|
|
21
|
+
*/
|
|
22
|
+
export type DurationParts = {
|
|
23
|
+
base: BaseDuration;
|
|
24
|
+
modifier: "none" | "dotted" | "triplet";
|
|
25
|
+
};
|
|
26
|
+
/** Splits a `DurationName` into its base value and modifier. */
|
|
27
|
+
export declare function durationParts(duration: DurationName): DurationParts;
|
|
28
|
+
/** The `DurationName` for a base value and modifier. */
|
|
29
|
+
export declare function composeDuration(parts: DurationParts): DurationName;
|
|
30
|
+
/**
|
|
31
|
+
* `duration` with `modifier` applied, or stripped if it was already active.
|
|
32
|
+
*
|
|
33
|
+
* Toggling one modifier clears the other rather than stacking: there is no
|
|
34
|
+
* dotted triplet in the model, and silently producing one of the two would be
|
|
35
|
+
* worse than plainly replacing it.
|
|
36
|
+
*/
|
|
37
|
+
export declare function withModifier(duration: DurationName, modifier: "dotted" | "triplet"): DurationName;
|
|
38
|
+
/** `duration` rebased onto a different note value, keeping its modifier. */
|
|
39
|
+
export declare function withBase(duration: DurationName, base: BaseDuration): DurationName;
|
|
40
|
+
//# sourceMappingURL=duration-modifiers.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"duration-modifiers.d.ts","sourceRoot":"","sources":["../../../src/domain/time/duration-modifiers.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AACH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAEnD,oEAAoE;AACpE,eAAO,MAAM,cAAc,8EAOjB,CAAC;AAEX,MAAM,MAAM,YAAY,GAAG,CAAC,OAAO,cAAc,CAAC,CAAC,MAAM,CAAC,CAAC;AAE3D;;;;;GAKG;AACH,MAAM,MAAM,aAAa,GAAG;IAC1B,IAAI,EAAE,YAAY,CAAC;IACnB,QAAQ,EAAE,MAAM,GAAG,QAAQ,GAAG,SAAS,CAAC;CACzC,CAAC;AAEF,gEAAgE;AAChE,wBAAgB,aAAa,CAAC,QAAQ,EAAE,YAAY,GAAG,aAAa,CAcnE;AAED,wDAAwD;AACxD,wBAAgB,eAAe,CAAC,KAAK,EAAE,aAAa,GAAG,YAAY,CAGlE;AAED;;;;;;GAMG;AACH,wBAAgB,YAAY,CAC1B,QAAQ,EAAE,YAAY,EACtB,QAAQ,EAAE,QAAQ,GAAG,SAAS,GAC7B,YAAY,CAMd;AAED,4EAA4E;AAC5E,wBAAgB,QAAQ,CACtB,QAAQ,EAAE,YAAY,EACtB,IAAI,EAAE,YAAY,GACjB,YAAY,CAEd"}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/** The six plain note values, longest first — the toolbar's row. */
|
|
2
|
+
export const BASE_DURATIONS = [
|
|
3
|
+
"whole",
|
|
4
|
+
"half",
|
|
5
|
+
"quarter",
|
|
6
|
+
"eighth",
|
|
7
|
+
"sixteenth",
|
|
8
|
+
"thirtysecond",
|
|
9
|
+
];
|
|
10
|
+
/** Splits a `DurationName` into its base value and modifier. */
|
|
11
|
+
export function durationParts(duration) {
|
|
12
|
+
if (duration.startsWith("dotted-")) {
|
|
13
|
+
return {
|
|
14
|
+
base: duration.slice("dotted-".length),
|
|
15
|
+
modifier: "dotted",
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
if (duration.startsWith("triplet-")) {
|
|
19
|
+
return {
|
|
20
|
+
base: duration.slice("triplet-".length),
|
|
21
|
+
modifier: "triplet",
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
return { base: duration, modifier: "none" };
|
|
25
|
+
}
|
|
26
|
+
/** The `DurationName` for a base value and modifier. */
|
|
27
|
+
export function composeDuration(parts) {
|
|
28
|
+
if (parts.modifier === "none")
|
|
29
|
+
return parts.base;
|
|
30
|
+
return `${parts.modifier}-${parts.base}`;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* `duration` with `modifier` applied, or stripped if it was already active.
|
|
34
|
+
*
|
|
35
|
+
* Toggling one modifier clears the other rather than stacking: there is no
|
|
36
|
+
* dotted triplet in the model, and silently producing one of the two would be
|
|
37
|
+
* worse than plainly replacing it.
|
|
38
|
+
*/
|
|
39
|
+
export function withModifier(duration, modifier) {
|
|
40
|
+
const parts = durationParts(duration);
|
|
41
|
+
return composeDuration({
|
|
42
|
+
base: parts.base,
|
|
43
|
+
modifier: parts.modifier === modifier ? "none" : modifier,
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
/** `duration` rebased onto a different note value, keeping its modifier. */
|
|
47
|
+
export function withBase(duration, base) {
|
|
48
|
+
return composeDuration({ base, modifier: durationParts(duration).modifier });
|
|
49
|
+
}
|
|
50
|
+
//# sourceMappingURL=duration-modifiers.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"duration-modifiers.js","sourceRoot":"","sources":["../../../src/domain/time/duration-modifiers.ts"],"names":[],"mappings":"AAaA,oEAAoE;AACpE,MAAM,CAAC,MAAM,cAAc,GAAG;IAC5B,OAAO;IACP,MAAM;IACN,SAAS;IACT,QAAQ;IACR,WAAW;IACX,cAAc;CACN,CAAC;AAeX,gEAAgE;AAChE,MAAM,UAAU,aAAa,CAAC,QAAsB;IAClD,IAAI,QAAQ,CAAC,UAAU,CAAC,SAAS,CAAC,EAAE,CAAC;QACnC,OAAO;YACL,IAAI,EAAE,QAAQ,CAAC,KAAK,CAAC,SAAS,CAAC,MAAM,CAAiB;YACtD,QAAQ,EAAE,QAAQ;SACnB,CAAC;IACJ,CAAC;IACD,IAAI,QAAQ,CAAC,UAAU,CAAC,UAAU,CAAC,EAAE,CAAC;QACpC,OAAO;YACL,IAAI,EAAE,QAAQ,CAAC,KAAK,CAAC,UAAU,CAAC,MAAM,CAAiB;YACvD,QAAQ,EAAE,SAAS;SACpB,CAAC;IACJ,CAAC;IACD,OAAO,EAAE,IAAI,EAAE,QAAwB,EAAE,QAAQ,EAAE,MAAM,EAAE,CAAC;AAC9D,CAAC;AAED,wDAAwD;AACxD,MAAM,UAAU,eAAe,CAAC,KAAoB;IAClD,IAAI,KAAK,CAAC,QAAQ,KAAK,MAAM;QAAE,OAAO,KAAK,CAAC,IAAI,CAAC;IACjD,OAAO,GAAG,KAAK,CAAC,QAAQ,IAAI,KAAK,CAAC,IAAI,EAAkB,CAAC;AAC3D,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,YAAY,CAC1B,QAAsB,EACtB,QAA8B;IAE9B,MAAM,KAAK,GAAG,aAAa,CAAC,QAAQ,CAAC,CAAC;IACtC,OAAO,eAAe,CAAC;QACrB,IAAI,EAAE,KAAK,CAAC,IAAI;QAChB,QAAQ,EAAE,KAAK,CAAC,QAAQ,KAAK,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,QAAQ;KAC1D,CAAC,CAAC;AACL,CAAC;AAED,4EAA4E;AAC5E,MAAM,UAAU,QAAQ,CACtB,QAAsB,EACtB,IAAkB;IAElB,OAAO,eAAe,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,aAAa,CAAC,QAAQ,CAAC,CAAC,QAAQ,EAAE,CAAC,CAAC;AAC/E,CAAC"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { DurationName, NoteEvent } from "../../index.js";
|
|
2
|
+
import type { BaseDuration } from "../time/duration-modifiers.js";
|
|
3
|
+
export type DurationDisplay =
|
|
4
|
+
/** A single agreed duration: the selection's, or the armed one when nothing is selected. */
|
|
5
|
+
{
|
|
6
|
+
kind: "single";
|
|
7
|
+
duration: DurationName;
|
|
8
|
+
base: BaseDuration;
|
|
9
|
+
}
|
|
10
|
+
/** Selected notes disagree, or one of them is a length no name covers. */
|
|
11
|
+
| {
|
|
12
|
+
kind: "mixed";
|
|
13
|
+
};
|
|
14
|
+
/**
|
|
15
|
+
* The duration to show for `notes`, falling back to `armed` when none are
|
|
16
|
+
* selected.
|
|
17
|
+
*
|
|
18
|
+
* A note whose length matches no duration name (an imported or quantized note
|
|
19
|
+
* of an odd tick count) counts as `mixed` rather than being rounded: the
|
|
20
|
+
* control would otherwise offer to "keep" a value the note does not have.
|
|
21
|
+
*/
|
|
22
|
+
export declare function durationDisplay(notes: readonly NoteEvent[], ppq: number, armed: DurationName): DurationDisplay;
|
|
23
|
+
//# sourceMappingURL=duration-selection.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"duration-selection.d.ts","sourceRoot":"","sources":["../../../src/domain/time/duration-selection.ts"],"names":[],"mappings":"AAsBA,OAAO,KAAK,EAAE,YAAY,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAE9D,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,+BAA+B,CAAC;AAElE,MAAM,MAAM,eAAe;AACzB,4FAA4F;AAC1F;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAC,QAAQ,EAAE,YAAY,CAAC;IAAC,IAAI,EAAE,YAAY,CAAA;CAAE;AAChE,0EAA0E;GACxE;IAAE,IAAI,EAAE,OAAO,CAAA;CAAE,CAAC;AAEtB;;;;;;;GAOG;AACH,wBAAgB,eAAe,CAC7B,KAAK,EAAE,SAAS,SAAS,EAAE,EAC3B,GAAG,EAAE,MAAM,EACX,KAAK,EAAE,YAAY,GAClB,eAAe,CAgBjB"}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What the duration selector shows, given the selection.
|
|
3
|
+
*
|
|
4
|
+
* One control replaced six toggle buttons, and it has to answer a different
|
|
5
|
+
* question depending on what is selected:
|
|
6
|
+
*
|
|
7
|
+
* - **nothing selected** — the length the *next* note will be written at, which
|
|
8
|
+
* is the store's `snapGrid`. The control is an instruction about the future.
|
|
9
|
+
* - **one note selected** — that note's length. The control is a readout.
|
|
10
|
+
* - **several, all the same length** — that length; they agree, so there is an
|
|
11
|
+
* answer.
|
|
12
|
+
* - **several of different lengths** — no single answer, so `mixed`. Not the
|
|
13
|
+
* first note's length, and not the armed length: either would claim the
|
|
14
|
+
* selection is something it is not, and picking that same value from the menu
|
|
15
|
+
* would then look like a no-op while silently rewriting every other note.
|
|
16
|
+
*
|
|
17
|
+
* Picking a value always applies to every selected note *and* arms it for the
|
|
18
|
+
* next one, so the control never has to explain which of its two jobs it is
|
|
19
|
+
* doing. Kept pure and separate from the toolbar so the four cases can be
|
|
20
|
+
* tested without rendering one.
|
|
21
|
+
*/
|
|
22
|
+
import { durationNameForTicks } from "../../index.js";
|
|
23
|
+
import { durationParts } from "../time/duration-modifiers.js";
|
|
24
|
+
/**
|
|
25
|
+
* The duration to show for `notes`, falling back to `armed` when none are
|
|
26
|
+
* selected.
|
|
27
|
+
*
|
|
28
|
+
* A note whose length matches no duration name (an imported or quantized note
|
|
29
|
+
* of an odd tick count) counts as `mixed` rather than being rounded: the
|
|
30
|
+
* control would otherwise offer to "keep" a value the note does not have.
|
|
31
|
+
*/
|
|
32
|
+
export function durationDisplay(notes, ppq, armed) {
|
|
33
|
+
if (notes.length === 0) {
|
|
34
|
+
return { kind: "single", duration: armed, base: durationParts(armed).base };
|
|
35
|
+
}
|
|
36
|
+
let agreed = null;
|
|
37
|
+
for (const note of notes) {
|
|
38
|
+
const name = durationNameForTicks(note.durationTicks, ppq);
|
|
39
|
+
if (name === null)
|
|
40
|
+
return { kind: "mixed" };
|
|
41
|
+
if (agreed === null)
|
|
42
|
+
agreed = name;
|
|
43
|
+
else if (agreed !== name)
|
|
44
|
+
return { kind: "mixed" };
|
|
45
|
+
}
|
|
46
|
+
return agreed === null
|
|
47
|
+
? { kind: "mixed" }
|
|
48
|
+
: { kind: "single", duration: agreed, base: durationParts(agreed).base };
|
|
49
|
+
}
|
|
50
|
+
//# sourceMappingURL=duration-selection.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"duration-selection.js","sourceRoot":"","sources":["../../../src/domain/time/duration-selection.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,OAAO,EAAE,oBAAoB,EAAE,MAAM,gBAAgB,CAAC;AAEtD,OAAO,EAAE,aAAa,EAAE,MAAM,+BAA+B,CAAC;AAS9D;;;;;;;GAOG;AACH,MAAM,UAAU,eAAe,CAC7B,KAA2B,EAC3B,GAAW,EACX,KAAmB;IAEnB,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACvB,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,QAAQ,EAAE,KAAK,EAAE,IAAI,EAAE,aAAa,CAAC,KAAK,CAAC,CAAC,IAAI,EAAE,CAAC;IAC9E,CAAC;IAED,IAAI,MAAM,GAAwB,IAAI,CAAC;IACvC,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,MAAM,IAAI,GAAG,oBAAoB,CAAC,IAAI,CAAC,aAAa,EAAE,GAAG,CAAC,CAAC;QAC3D,IAAI,IAAI,KAAK,IAAI;YAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC;QAC5C,IAAI,MAAM,KAAK,IAAI;YAAE,MAAM,GAAG,IAAI,CAAC;aAC9B,IAAI,MAAM,KAAK,IAAI;YAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC;IACrD,CAAC;IAED,OAAO,MAAM,KAAK,IAAI;QACpB,CAAC,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE;QACnB,CAAC,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,aAAa,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,CAAC;AAC7E,CAAC"}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import { DURATIONS, ticksFor } from
|
|
2
|
-
const NON_TRIPLET_DURATION_NAMES = Object.keys(DURATIONS).filter(name => !name.startsWith(
|
|
1
|
+
import { DURATIONS, ticksFor } from "./ticks.js";
|
|
2
|
+
const NON_TRIPLET_DURATION_NAMES = Object.keys(DURATIONS).filter((name) => !name.startsWith("triplet-"));
|
|
3
3
|
/** Distinct, descending tick lengths for every renderable (non-triplet) duration at a given PPQ. */
|
|
4
4
|
function decomposableDurationTicks(ppq) {
|
|
5
|
-
const ticks = new Set(NON_TRIPLET_DURATION_NAMES.map(name => ticksFor(name, ppq)));
|
|
6
|
-
return [...ticks].filter(t => t > 0).sort((a, b) => b - a);
|
|
5
|
+
const ticks = new Set(NON_TRIPLET_DURATION_NAMES.map((name) => ticksFor(name, ppq)));
|
|
6
|
+
return [...ticks].filter((t) => t > 0).sort((a, b) => b - a);
|
|
7
7
|
}
|
|
8
8
|
/**
|
|
9
9
|
* Greedily decomposes a tick length into the largest renderable (non-triplet,
|
|
@@ -19,7 +19,7 @@ export function decomposeDuration(ticks, ppq) {
|
|
|
19
19
|
const result = [];
|
|
20
20
|
let remaining = ticks;
|
|
21
21
|
while (remaining > 0) {
|
|
22
|
-
const next = candidates.find(c => c <= remaining);
|
|
22
|
+
const next = candidates.find((c) => c <= remaining);
|
|
23
23
|
if (next === undefined) {
|
|
24
24
|
result.push(remaining);
|
|
25
25
|
break;
|
|
@@ -39,7 +39,7 @@ export function decomposeDuration(ticks, ppq) {
|
|
|
39
39
|
export function splitAtBoundaries(startTick, durationTicks, boundaries) {
|
|
40
40
|
const endTick = startTick + durationTicks;
|
|
41
41
|
const cutPoints = [...new Set(boundaries)]
|
|
42
|
-
.filter(b => b > startTick && b < endTick)
|
|
42
|
+
.filter((b) => b > startTick && b < endTick)
|
|
43
43
|
.sort((a, b) => a - b);
|
|
44
44
|
const segments = [];
|
|
45
45
|
let cursor = startTick;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"durations.js","sourceRoot":"","sources":["../../../src/domain/time/durations.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAEjD,MAAM,0BAA0B,GAC9B,MAAM,CAAC,IAAI,CAAC,SAAS,CACtB,CAAC,MAAM,CAAC,IAAI,
|
|
1
|
+
{"version":3,"file":"durations.js","sourceRoot":"","sources":["../../../src/domain/time/durations.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAEjD,MAAM,0BAA0B,GAC9B,MAAM,CAAC,IAAI,CAAC,SAAS,CACtB,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,UAAU,CAAC,CAAC,CAAC;AAEjD,oGAAoG;AACpG,SAAS,yBAAyB,CAAC,GAAW;IAC5C,MAAM,KAAK,GAAG,IAAI,GAAG,CACnB,0BAA0B,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,QAAQ,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC,CAC9D,CAAC;IACF,OAAO,CAAC,GAAG,KAAK,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;AAC/D,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,iBAAiB,CAAC,KAAa,EAAE,GAAW;IAC1D,IAAI,KAAK,IAAI,CAAC;QAAE,OAAO,EAAE,CAAC;IAE1B,MAAM,UAAU,GAAG,yBAAyB,CAAC,GAAG,CAAC,CAAC;IAClD,MAAM,MAAM,GAAa,EAAE,CAAC;IAC5B,IAAI,SAAS,GAAG,KAAK,CAAC;IAEtB,OAAO,SAAS,GAAG,CAAC,EAAE,CAAC;QACrB,MAAM,IAAI,GAAG,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,IAAI,SAAS,CAAC,CAAC;QACpD,IAAI,IAAI,KAAK,SAAS,EAAE,CAAC;YACvB,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;YACvB,MAAM;QACR,CAAC;QACD,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAClB,SAAS,IAAI,IAAI,CAAC;IACpB,CAAC;IAED,OAAO,MAAM,CAAC;AAChB,CAAC;AAID;;;;;;GAMG;AACH,MAAM,UAAU,iBAAiB,CAC/B,SAAiB,EACjB,aAAqB,EACrB,UAAoB;IAEpB,MAAM,OAAO,GAAG,SAAS,GAAG,aAAa,CAAC;IAC1C,MAAM,SAAS,GAAG,CAAC,GAAG,IAAI,GAAG,CAAC,UAAU,CAAC,CAAC;SACvC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,SAAS,IAAI,CAAC,GAAG,OAAO,CAAC;SAC3C,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;IAEzB,MAAM,QAAQ,GAAkB,EAAE,CAAC;IACnC,IAAI,MAAM,GAAG,SAAS,CAAC;IACvB,KAAK,MAAM,GAAG,IAAI,SAAS,EAAE,CAAC;QAC5B,QAAQ,CAAC,IAAI,CAAC,EAAE,SAAS,EAAE,MAAM,EAAE,aAAa,EAAE,GAAG,GAAG,MAAM,EAAE,CAAC,CAAC;QAClE,MAAM,GAAG,GAAG,CAAC;IACf,CAAC;IACD,QAAQ,CAAC,IAAI,CAAC,EAAE,SAAS,EAAE,MAAM,EAAE,aAAa,EAAE,OAAO,GAAG,MAAM,EAAE,CAAC,CAAC;IAEtE,OAAO,QAAQ,CAAC;AAClB,CAAC"}
|
|
@@ -17,7 +17,7 @@ export function fraction(numerator, denominator) {
|
|
|
17
17
|
*/
|
|
18
18
|
export function normalizeFraction(f) {
|
|
19
19
|
if (f.denominator === 0) {
|
|
20
|
-
throw new Error(
|
|
20
|
+
throw new Error("normalizeFraction: denominator cannot be zero");
|
|
21
21
|
}
|
|
22
22
|
if (f.numerator === 0) {
|
|
23
23
|
return { numerator: 0, denominator: 1 };
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Turning a key tap into a written note.
|
|
3
|
+
*
|
|
4
|
+
* Pure — no DOM, no store, no audio — so the timing maths is unit-testable, in
|
|
5
|
+
* the same shape as the editor's other geometry modules.
|
|
6
|
+
*/
|
|
7
|
+
import type { DurationName } from "../../index.js";
|
|
8
|
+
/**
|
|
9
|
+
* The written duration closest to a tap of `heldMs` at `bpm`.
|
|
10
|
+
*
|
|
11
|
+
* Matched on a *ratio*, not a difference: a 40ms overshoot is nothing against a
|
|
12
|
+
* whole note and everything against a thirty-second, and comparing absolute
|
|
13
|
+
* error would push almost every tap toward the longest value. Comparing
|
|
14
|
+
* `log(actual / candidate)` treats being 20% long the same at every duration,
|
|
15
|
+
* which is how a player experiences it.
|
|
16
|
+
*
|
|
17
|
+
* A tap shorter than the briefest candidate returns that candidate rather than
|
|
18
|
+
* nothing: someone stabbing at a key means the shortest note, not no note.
|
|
19
|
+
*/
|
|
20
|
+
export declare function durationForTap(heldMs: number, bpm: number): DurationName;
|
|
21
|
+
//# sourceMappingURL=tap-to-note.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tap-to-note.d.ts","sourceRoot":"","sources":["../../../src/domain/time/tap-to-note.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAkBnD;;;;;;;;;;;GAWG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,YAAY,CAexE"}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The durations a tap can land on, longest first, with their length as a
|
|
3
|
+
* fraction of a whole note.
|
|
4
|
+
*
|
|
5
|
+
* Stops at a thirty-second because that is what the duration toolbar offers:
|
|
6
|
+
* a tap cannot produce something the buttons cannot.
|
|
7
|
+
*/
|
|
8
|
+
const CANDIDATES = [
|
|
9
|
+
{ name: "whole", whole: 1 },
|
|
10
|
+
{ name: "half", whole: 1 / 2 },
|
|
11
|
+
{ name: "quarter", whole: 1 / 4 },
|
|
12
|
+
{ name: "eighth", whole: 1 / 8 },
|
|
13
|
+
{ name: "sixteenth", whole: 1 / 16 },
|
|
14
|
+
{ name: "thirtysecond", whole: 1 / 32 },
|
|
15
|
+
];
|
|
16
|
+
/**
|
|
17
|
+
* The written duration closest to a tap of `heldMs` at `bpm`.
|
|
18
|
+
*
|
|
19
|
+
* Matched on a *ratio*, not a difference: a 40ms overshoot is nothing against a
|
|
20
|
+
* whole note and everything against a thirty-second, and comparing absolute
|
|
21
|
+
* error would push almost every tap toward the longest value. Comparing
|
|
22
|
+
* `log(actual / candidate)` treats being 20% long the same at every duration,
|
|
23
|
+
* which is how a player experiences it.
|
|
24
|
+
*
|
|
25
|
+
* A tap shorter than the briefest candidate returns that candidate rather than
|
|
26
|
+
* nothing: someone stabbing at a key means the shortest note, not no note.
|
|
27
|
+
*/
|
|
28
|
+
export function durationForTap(heldMs, bpm) {
|
|
29
|
+
const safeBpm = bpm > 0 ? bpm : 120;
|
|
30
|
+
const wholeNoteMs = (60000 / safeBpm) * 4;
|
|
31
|
+
const held = Math.max(1, heldMs) / wholeNoteMs;
|
|
32
|
+
let best = CANDIDATES[CANDIDATES.length - 1];
|
|
33
|
+
let bestError = Number.POSITIVE_INFINITY;
|
|
34
|
+
for (const candidate of CANDIDATES) {
|
|
35
|
+
const error = Math.abs(Math.log(held / candidate.whole));
|
|
36
|
+
if (error < bestError) {
|
|
37
|
+
bestError = error;
|
|
38
|
+
best = candidate;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
return best.name;
|
|
42
|
+
}
|
|
43
|
+
//# sourceMappingURL=tap-to-note.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tap-to-note.js","sourceRoot":"","sources":["../../../src/domain/time/tap-to-note.ts"],"names":[],"mappings":"AAQA;;;;;;GAMG;AACH,MAAM,UAAU,GAAyD;IACvE,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC,EAAE;IAC3B,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,GAAG,CAAC,EAAE;IAC9B,EAAE,IAAI,EAAE,SAAS,EAAE,KAAK,EAAE,CAAC,GAAG,CAAC,EAAE;IACjC,EAAE,IAAI,EAAE,QAAQ,EAAE,KAAK,EAAE,CAAC,GAAG,CAAC,EAAE;IAChC,EAAE,IAAI,EAAE,WAAW,EAAE,KAAK,EAAE,CAAC,GAAG,EAAE,EAAE;IACpC,EAAE,IAAI,EAAE,cAAc,EAAE,KAAK,EAAE,CAAC,GAAG,EAAE,EAAE;CACxC,CAAC;AAEF;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,cAAc,CAAC,MAAc,EAAE,GAAW;IACxD,MAAM,OAAO,GAAG,GAAG,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC;IACpC,MAAM,WAAW,GAAG,CAAC,KAAM,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC;IAC3C,MAAM,IAAI,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,MAAM,CAAC,GAAG,WAAW,CAAC;IAE/C,IAAI,IAAI,GAAG,UAAU,CAAC,UAAU,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;IAC7C,IAAI,SAAS,GAAG,MAAM,CAAC,iBAAiB,CAAC;IACzC,KAAK,MAAM,SAAS,IAAI,UAAU,EAAE,CAAC;QACnC,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,GAAG,SAAS,CAAC,KAAK,CAAC,CAAC,CAAC;QACzD,IAAI,KAAK,GAAG,SAAS,EAAE,CAAC;YACtB,SAAS,GAAG,KAAK,CAAC;YAClB,IAAI,GAAG,SAAS,CAAC;QACnB,CAAC;IACH,CAAC;IACD,OAAO,IAAI,CAAC,IAAI,CAAC;AACnB,CAAC"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { DurationName, Fraction, TimeSignature } from
|
|
1
|
+
import type { DurationName, Fraction, TimeSignature } from "../../index.js";
|
|
2
2
|
/**
|
|
3
3
|
* Duration name -> fraction of a whole note. Values are consumed by
|
|
4
4
|
* `fractionToTicks`, where a quarter note (1/4) equals `ppq` ticks.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ticks.d.ts","sourceRoot":"","sources":["../../../src/domain/time/ticks.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"ticks.d.ts","sourceRoot":"","sources":["../../../src/domain/time/ticks.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,QAAQ,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AA0B5E;;;GAGG;AACH,eAAO,MAAM,SAAS,EAAE,MAAM,CAAC,YAAY,EAAE,QAAQ,CAyBpD,CAAC;AAEF,gEAAgE;AAChE,wBAAgB,QAAQ,CAAC,QAAQ,EAAE,YAAY,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,CAEpE;AAKD;;;;;;;;;;;;GAYG;AACH,wBAAgB,oBAAoB,CAClC,KAAK,EAAE,MAAM,EACb,GAAG,EAAE,MAAM,GACV,YAAY,GAAG,IAAI,CAErB;AAED,2EAA2E;AAC3E,wBAAgB,oBAAoB,CAAC,EAAE,EAAE,aAAa,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,CAE3E;AAUD;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAAC,EAAE,EAAE,aAAa,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,CAMxE;AAED,8EAA8E;AAC9E,wBAAgB,cAAc,CAAC,EAAE,EAAE,aAAa,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,EAAE,CAQvE"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { fraction, fractionToTicks } from
|
|
1
|
+
import { fraction, fractionToTicks } from "./fraction.js";
|
|
2
2
|
/** Multiplies a fraction by a positive integer/rational scalar expressed as a fraction. */
|
|
3
3
|
function scaleFraction(f, scale) {
|
|
4
4
|
return fraction(f.numerator * scale.numerator, f.denominator * scale.denominator);
|
|
@@ -24,18 +24,18 @@ export const DURATIONS = {
|
|
|
24
24
|
eighth: BASE_DURATIONS.eighth,
|
|
25
25
|
sixteenth: BASE_DURATIONS.sixteenth,
|
|
26
26
|
thirtysecond: BASE_DURATIONS.thirtysecond,
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
27
|
+
"dotted-whole": scaleFraction(BASE_DURATIONS.whole, DOTTED_SCALE),
|
|
28
|
+
"dotted-half": scaleFraction(BASE_DURATIONS.half, DOTTED_SCALE),
|
|
29
|
+
"dotted-quarter": scaleFraction(BASE_DURATIONS.quarter, DOTTED_SCALE),
|
|
30
|
+
"dotted-eighth": scaleFraction(BASE_DURATIONS.eighth, DOTTED_SCALE),
|
|
31
|
+
"dotted-sixteenth": scaleFraction(BASE_DURATIONS.sixteenth, DOTTED_SCALE),
|
|
32
|
+
"dotted-thirtysecond": scaleFraction(BASE_DURATIONS.thirtysecond, DOTTED_SCALE),
|
|
33
|
+
"triplet-whole": scaleFraction(BASE_DURATIONS.whole, TRIPLET_SCALE),
|
|
34
|
+
"triplet-half": scaleFraction(BASE_DURATIONS.half, TRIPLET_SCALE),
|
|
35
|
+
"triplet-quarter": scaleFraction(BASE_DURATIONS.quarter, TRIPLET_SCALE),
|
|
36
|
+
"triplet-eighth": scaleFraction(BASE_DURATIONS.eighth, TRIPLET_SCALE),
|
|
37
|
+
"triplet-sixteenth": scaleFraction(BASE_DURATIONS.sixteenth, TRIPLET_SCALE),
|
|
38
|
+
"triplet-thirtysecond": scaleFraction(BASE_DURATIONS.thirtysecond, TRIPLET_SCALE),
|
|
39
39
|
};
|
|
40
40
|
/** Integer tick length of a named duration at the given PPQ. */
|
|
41
41
|
export function ticksFor(duration, ppq) {
|
|
@@ -57,7 +57,7 @@ const DURATION_NAMES = Object.keys(DURATIONS);
|
|
|
57
57
|
* the sake of eighteen comparisons.
|
|
58
58
|
*/
|
|
59
59
|
export function durationNameForTicks(ticks, ppq) {
|
|
60
|
-
return DURATION_NAMES.find(name => ticksFor(name, ppq) === ticks) ?? null;
|
|
60
|
+
return DURATION_NAMES.find((name) => ticksFor(name, ppq) === ticks) ?? null;
|
|
61
61
|
}
|
|
62
62
|
/** Integer tick length of one full measure in the given time signature. */
|
|
63
63
|
export function measureDurationTicks(ts, ppq) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ticks.js","sourceRoot":"","sources":["../../../src/domain/time/ticks.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"ticks.js","sourceRoot":"","sources":["../../../src/domain/time/ticks.ts"],"names":[],"mappings":"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"}
|