@sudobility/music_types 0.11.19 → 0.11.21
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 +66 -0
- package/dist/domain/commands/edit-commands.d.ts.map +1 -0
- package/dist/domain/commands/edit-commands.js +185 -0
- package/dist/domain/commands/edit-commands.js.map +1 -0
- package/dist/domain/commands/note-commands.d.ts +73 -0
- package/dist/domain/commands/note-commands.d.ts.map +1 -0
- package/dist/domain/commands/note-commands.js +292 -0
- package/dist/domain/commands/note-commands.js.map +1 -0
- package/dist/domain/commands/reflow.d.ts +69 -0
- package/dist/domain/commands/reflow.d.ts.map +1 -0
- package/dist/domain/commands/reflow.js +395 -0
- package/dist/domain/commands/reflow.js.map +1 -0
- package/dist/domain/commands/region-commands.d.ts +21 -0
- package/dist/domain/commands/region-commands.d.ts.map +1 -0
- package/dist/domain/commands/region-commands.js +47 -0
- package/dist/domain/commands/region-commands.js.map +1 -0
- package/dist/domain/commands/relocate-commands.d.ts +18 -0
- package/dist/domain/commands/relocate-commands.d.ts.map +1 -0
- package/dist/domain/commands/relocate-commands.js +108 -0
- package/dist/domain/commands/relocate-commands.js.map +1 -0
- package/dist/domain/commands/ripple-commands.d.ts +44 -0
- package/dist/domain/commands/ripple-commands.d.ts.map +1 -0
- package/dist/domain/commands/ripple-commands.js +104 -0
- package/dist/domain/commands/ripple-commands.js.map +1 -0
- package/dist/domain/commands/snapshot.d.ts +38 -0
- package/dist/domain/commands/snapshot.d.ts.map +1 -0
- package/dist/domain/commands/snapshot.js +44 -0
- package/dist/domain/commands/snapshot.js.map +1 -0
- package/dist/domain/commands/structure-commands.d.ts +38 -0
- package/dist/domain/commands/structure-commands.d.ts.map +1 -0
- package/dist/domain/commands/structure-commands.js +220 -0
- package/dist/domain/commands/structure-commands.js.map +1 -0
- package/dist/domain/commands/track-commands.d.ts +15 -0
- package/dist/domain/commands/track-commands.d.ts.map +1 -0
- package/dist/domain/commands/track-commands.js +56 -0
- package/dist/domain/commands/track-commands.js.map +1 -0
- package/dist/domain/commands/types.d.ts +31 -0
- package/dist/domain/commands/types.d.ts.map +1 -0
- package/dist/domain/commands/types.js +2 -0
- package/dist/domain/commands/types.js.map +1 -0
- package/dist/domain/instruments/gm-kit.d.ts +45 -0
- package/dist/domain/instruments/gm-kit.d.ts.map +1 -0
- package/dist/domain/instruments/gm-kit.js +59 -0
- package/dist/domain/instruments/gm-kit.js.map +1 -0
- package/dist/domain/instruments/gm-percussion.d.ts +42 -0
- package/dist/domain/instruments/gm-percussion.d.ts.map +1 -0
- package/dist/domain/instruments/gm-percussion.js +85 -0
- package/dist/domain/instruments/gm-percussion.js.map +1 -0
- package/dist/domain/instruments/gm-polyphony.d.ts +13 -0
- package/dist/domain/instruments/gm-polyphony.d.ts.map +1 -0
- package/dist/domain/instruments/gm-polyphony.js +76 -0
- package/dist/domain/instruments/gm-polyphony.js.map +1 -0
- package/dist/domain/instruments/gm-range.d.ts +15 -0
- package/dist/domain/instruments/gm-range.d.ts.map +1 -0
- package/dist/domain/instruments/gm-range.js +97 -0
- package/dist/domain/instruments/gm-range.js.map +1 -0
- package/dist/domain/instruments/gm-transposition.d.ts +11 -0
- package/dist/domain/instruments/gm-transposition.d.ts.map +1 -0
- package/dist/domain/instruments/gm-transposition.js +69 -0
- package/dist/domain/instruments/gm-transposition.js.map +1 -0
- package/dist/domain/instruments/gm.d.ts +28 -0
- package/dist/domain/instruments/gm.d.ts.map +1 -0
- package/dist/domain/instruments/gm.js +217 -0
- package/dist/domain/instruments/gm.js.map +1 -0
- package/dist/domain/pitch/pitch.d.ts +15 -0
- package/dist/domain/pitch/pitch.d.ts.map +1 -0
- package/dist/domain/pitch/pitch.js +81 -0
- package/dist/domain/pitch/pitch.js.map +1 -0
- package/dist/domain/pitch/transpose.d.ts +37 -0
- package/dist/domain/pitch/transpose.d.ts.map +1 -0
- package/dist/domain/pitch/transpose.js +65 -0
- package/dist/domain/pitch/transpose.js.map +1 -0
- package/dist/domain/quantization/options.d.ts +55 -0
- package/dist/domain/quantization/options.d.ts.map +1 -0
- package/dist/domain/quantization/options.js +2 -0
- package/dist/domain/quantization/options.js.map +1 -0
- package/dist/domain/quantization/quantize.d.ts +13 -0
- package/dist/domain/quantization/quantize.d.ts.map +1 -0
- package/dist/domain/quantization/quantize.js +199 -0
- package/dist/domain/quantization/quantize.js.map +1 -0
- package/dist/domain/score/factory.d.ts +39 -0
- package/dist/domain/score/factory.d.ts.map +1 -0
- package/dist/domain/score/factory.js +148 -0
- package/dist/domain/score/factory.js.map +1 -0
- package/dist/domain/score/fragment.d.ts +28 -0
- package/dist/domain/score/fragment.d.ts.map +1 -0
- package/dist/domain/score/fragment.js +68 -0
- package/dist/domain/score/fragment.js.map +1 -0
- package/dist/domain/score/ids.d.ts +8 -0
- package/dist/domain/score/ids.d.ts.map +1 -0
- package/dist/domain/score/ids.js +38 -0
- package/dist/domain/score/ids.js.map +1 -0
- package/dist/domain/score/queries.d.ts +34 -0
- package/dist/domain/score/queries.d.ts.map +1 -0
- package/dist/domain/score/queries.js +132 -0
- package/dist/domain/score/queries.js.map +1 -0
- package/dist/domain/score/ties.d.ts +73 -0
- package/dist/domain/score/ties.d.ts.map +1 -0
- package/dist/domain/score/ties.js +200 -0
- package/dist/domain/score/ties.js.map +1 -0
- package/dist/domain/selection/selection.d.ts +63 -0
- package/dist/domain/selection/selection.d.ts.map +1 -0
- package/dist/domain/selection/selection.js +129 -0
- package/dist/domain/selection/selection.js.map +1 -0
- package/dist/domain/selection/types.d.ts +10 -0
- package/dist/domain/selection/types.d.ts.map +1 -0
- package/dist/domain/selection/types.js +5 -0
- package/dist/domain/selection/types.js.map +1 -0
- package/dist/domain/time/durations.d.ts +21 -0
- package/dist/domain/time/durations.d.ts.map +1 -0
- package/dist/domain/time/durations.js +53 -0
- package/dist/domain/time/durations.js.map +1 -0
- package/dist/domain/time/fraction.d.ts +19 -0
- package/dist/domain/time/fraction.d.ts.map +1 -0
- package/dist/domain/time/fraction.js +60 -0
- package/dist/domain/time/fraction.js.map +1 -0
- package/dist/domain/time/tempo-map.d.ts +24 -0
- package/dist/domain/time/tempo-map.d.ts.map +1 -0
- package/dist/domain/time/tempo-map.js +85 -0
- package/dist/domain/time/tempo-map.js.map +1 -0
- package/dist/domain/time/ticks.d.ts +34 -0
- package/dist/domain/time/ticks.d.ts.map +1 -0
- package/dist/domain/time/ticks.js +93 -0
- package/dist/domain/time/ticks.js.map +1 -0
- package/dist/domain/validation/issues.d.ts +44 -0
- package/dist/domain/validation/issues.d.ts.map +1 -0
- package/dist/domain/validation/issues.js +34 -0
- package/dist/domain/validation/issues.js.map +1 -0
- package/dist/domain/validation/validator.d.ts +11 -0
- package/dist/domain/validation/validator.d.ts.map +1 -0
- package/dist/domain/validation/validator.js +375 -0
- package/dist/domain/validation/validator.js.map +1 -0
- package/dist/domain/voicing/allocate.d.ts +19 -0
- package/dist/domain/voicing/allocate.d.ts.map +1 -0
- package/dist/domain/voicing/allocate.js +100 -0
- package/dist/domain/voicing/allocate.js.map +1 -0
- package/dist/index.d.ts +46 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +50 -2
- package/dist/index.js.map +1 -1
- package/dist/platform/audio.d.ts +12 -0
- package/dist/platform/audio.d.ts.map +1 -1
- package/dist/platform/playback.d.ts +85 -15
- package/dist/platform/playback.d.ts.map +1 -1
- package/dist/services/regeneration/controller.d.ts +60 -0
- package/dist/services/regeneration/controller.d.ts.map +1 -0
- package/dist/services/regeneration/controller.js +202 -0
- package/dist/services/regeneration/controller.js.map +1 -0
- package/dist/test/fixtures.d.ts +45 -0
- package/dist/test/fixtures.d.ts.map +1 -0
- package/dist/test/fixtures.js +420 -0
- package/dist/test/fixtures.js.map +1 -0
- package/package.json +2 -1
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
import { isNoteEvent } from '../../index.js';
|
|
2
|
+
import { pitchToMidi } from '../pitch/pitch.js';
|
|
3
|
+
/** Finds a track by id, or `null` if no track has that id. */
|
|
4
|
+
export function findTrack(score, trackId) {
|
|
5
|
+
return score.tracks.find(track => track.id === trackId) ?? null;
|
|
6
|
+
}
|
|
7
|
+
/** Finds a measure by id across all tracks, or `null` if none has that id. */
|
|
8
|
+
export function findMeasure(score, measureId) {
|
|
9
|
+
for (const track of score.tracks) {
|
|
10
|
+
const measure = track.measures.find(m => m.id === measureId);
|
|
11
|
+
if (measure)
|
|
12
|
+
return measure;
|
|
13
|
+
}
|
|
14
|
+
return null;
|
|
15
|
+
}
|
|
16
|
+
/** Finds an event (note or rest) by id across all tracks/measures/voices, or `null`. */
|
|
17
|
+
export function findEvent(score, eventId) {
|
|
18
|
+
for (const track of score.tracks) {
|
|
19
|
+
for (const measure of track.measures) {
|
|
20
|
+
for (const voice of measure.voices) {
|
|
21
|
+
const event = voice.events.find(e => e.id === eventId);
|
|
22
|
+
if (event)
|
|
23
|
+
return event;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
return null;
|
|
28
|
+
}
|
|
29
|
+
/** Whether track `trackId` is included by a range's `trackIds` (empty array means "all tracks"). */
|
|
30
|
+
function rangeIncludesTrack(range, trackId) {
|
|
31
|
+
return range.trackIds.length === 0 || range.trackIds.includes(trackId);
|
|
32
|
+
}
|
|
33
|
+
/** Whether `[startTick, startTick + durationTicks)` overlaps `[range.startTick, range.endTick)`. */
|
|
34
|
+
function overlapsRange(startTick, durationTicks, range) {
|
|
35
|
+
return (startTick < range.endTick && startTick + durationTicks > range.startTick);
|
|
36
|
+
}
|
|
37
|
+
/** All note events (rests excluded) on the requested tracks overlapping the tick range. */
|
|
38
|
+
export function eventsInRange(score, range) {
|
|
39
|
+
const result = [];
|
|
40
|
+
for (const track of score.tracks) {
|
|
41
|
+
if (!rangeIncludesTrack(range, track.id))
|
|
42
|
+
continue;
|
|
43
|
+
for (const measure of track.measures) {
|
|
44
|
+
for (const voice of measure.voices) {
|
|
45
|
+
for (const event of voice.events) {
|
|
46
|
+
if (isNoteEvent(event) &&
|
|
47
|
+
overlapsRange(event.startTick, event.durationTicks, range)) {
|
|
48
|
+
result.push(event);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
return result;
|
|
55
|
+
}
|
|
56
|
+
/** Measures overlapping the tick range, grouped by track. */
|
|
57
|
+
export function measuresInRange(score, range) {
|
|
58
|
+
const result = [];
|
|
59
|
+
for (const track of score.tracks) {
|
|
60
|
+
if (!rangeIncludesTrack(range, track.id))
|
|
61
|
+
continue;
|
|
62
|
+
const measures = track.measures.filter(m => overlapsRange(m.startTick, m.durationTicks, range));
|
|
63
|
+
result.push({ trackId: track.id, measures });
|
|
64
|
+
}
|
|
65
|
+
return result;
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Finds a note event on `trackId` whose span covers `tick`
|
|
69
|
+
* (`startTick <= tick < startTick + durationTicks`), optionally also
|
|
70
|
+
* matching a specific MIDI pitch. Returns `null` if none match.
|
|
71
|
+
*/
|
|
72
|
+
export function noteAt(score, trackId, tick, midi) {
|
|
73
|
+
const track = findTrack(score, trackId);
|
|
74
|
+
if (!track)
|
|
75
|
+
return null;
|
|
76
|
+
for (const measure of track.measures) {
|
|
77
|
+
for (const voice of measure.voices) {
|
|
78
|
+
for (const event of voice.events) {
|
|
79
|
+
if (!isNoteEvent(event))
|
|
80
|
+
continue;
|
|
81
|
+
if (tick < event.startTick ||
|
|
82
|
+
tick >= event.startTick + event.durationTicks)
|
|
83
|
+
continue;
|
|
84
|
+
if (midi !== undefined && pitchToMidi(event.pitch) !== midi)
|
|
85
|
+
continue;
|
|
86
|
+
return event;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
return null;
|
|
91
|
+
}
|
|
92
|
+
/** The tick at which the score's last measure (across all tracks) ends; 0 if there are none. */
|
|
93
|
+
export function scoreEndTick(score) {
|
|
94
|
+
let end = 0;
|
|
95
|
+
for (const track of score.tracks) {
|
|
96
|
+
const lastMeasure = track.measures[track.measures.length - 1];
|
|
97
|
+
if (lastMeasure) {
|
|
98
|
+
end = Math.max(end, lastMeasure.startTick + lastMeasure.durationTicks);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
return end;
|
|
102
|
+
}
|
|
103
|
+
/** All note events (rests excluded) across every track/measure/voice. */
|
|
104
|
+
export function allNotes(score) {
|
|
105
|
+
const notes = [];
|
|
106
|
+
for (const track of score.tracks) {
|
|
107
|
+
for (const measure of track.measures) {
|
|
108
|
+
for (const voice of measure.voices) {
|
|
109
|
+
for (const event of voice.events) {
|
|
110
|
+
if (isNoteEvent(event))
|
|
111
|
+
notes.push(event);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
return notes;
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* `score` with only the named tracks, in score order.
|
|
120
|
+
*
|
|
121
|
+
* Returns `score` itself when `trackIds` names every track, so the common
|
|
122
|
+
* "nothing is hidden" export path costs nothing. Kept tracks are returned by
|
|
123
|
+
* reference — this filters, it never rewrites the music.
|
|
124
|
+
*/
|
|
125
|
+
export function scoreWithTracks(score, trackIds) {
|
|
126
|
+
const wanted = new Set(trackIds);
|
|
127
|
+
const tracks = score.tracks.filter(track => wanted.has(track.id));
|
|
128
|
+
if (tracks.length === score.tracks.length)
|
|
129
|
+
return score;
|
|
130
|
+
return { ...score, tracks };
|
|
131
|
+
}
|
|
132
|
+
//# sourceMappingURL=queries.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"queries.js","sourceRoot":"","sources":["../../../src/domain/score/queries.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import type { MusicalEvent, NoteEvent, Score, Track, UUID } from '../../index.js';
|
|
2
|
+
/**
|
|
3
|
+
* Splits a note at every measure boundary that falls strictly inside its
|
|
4
|
+
* span, returning contiguous tied segments that together cover the
|
|
5
|
+
* original note. The first segment keeps the original id and any incoming
|
|
6
|
+
* tie (`tieStop`); the last keeps any outgoing tie (`tieStart`); segments
|
|
7
|
+
* in between are tied on both sides. Returns `[note]` unchanged if no
|
|
8
|
+
* boundary falls inside its span.
|
|
9
|
+
*
|
|
10
|
+
* Every returned segment inherits `note`'s `voiceId`/`trackId` verbatim.
|
|
11
|
+
* This function has no `Score`/`Measure` context, so it cannot know which
|
|
12
|
+
* voice a later segment will actually land in once inserted into the
|
|
13
|
+
* score — the caller (which does have that context) is responsible for
|
|
14
|
+
* reassigning each segment's `voiceId` to its destination measure's voice
|
|
15
|
+
* before/while inserting it (the same normalization `fragment.ts`'s
|
|
16
|
+
* `replaceFragment` performs).
|
|
17
|
+
*/
|
|
18
|
+
export declare function splitNoteAcrossMeasures(note: NoteEvent, measureBoundaries: number[]): NoteEvent[];
|
|
19
|
+
/**
|
|
20
|
+
* Merges runs of contiguous, same-pitch, tie-linked note events into single
|
|
21
|
+
* notes spanning the combined duration. Partners are matched by
|
|
22
|
+
* `(startTick, pitch, tie flags)`, not raw array adjacency, so tied chord
|
|
23
|
+
* members can be joined even when another chord member sits between them.
|
|
24
|
+
* Rests and non-tied notes pass through unchanged. Assumes `events` are
|
|
25
|
+
* already in ascending `startTick` order (e.g. one voice's events).
|
|
26
|
+
*/
|
|
27
|
+
export declare function joinTiedNotes(events: MusicalEvent[]): MusicalEvent[];
|
|
28
|
+
/** A note event annotated with the index (within its track) of the measure it came from. */
|
|
29
|
+
export type ChannelCandidate = {
|
|
30
|
+
event: NoteEvent;
|
|
31
|
+
measureIndex: number;
|
|
32
|
+
};
|
|
33
|
+
/**
|
|
34
|
+
* Every note event from the voice at ordinal position `voiceIndex` in
|
|
35
|
+
* each of `track`'s measures (a measure with no voice at that index
|
|
36
|
+
* contributes nothing), in measure/tick order. This is the "channel" a
|
|
37
|
+
* tie chain is searched within.
|
|
38
|
+
*
|
|
39
|
+
* Voice ids are freshly generated per measure and so are not stable
|
|
40
|
+
* across a barline (spec §25 permits per-measure voice reallocation), so
|
|
41
|
+
* they can't be used to correlate "the same voice" from one measure to
|
|
42
|
+
* the next. A voice's *ordinal position* in `measure.voices` is used as
|
|
43
|
+
* a stand-in instead — a deliberate, documented approximation, not a
|
|
44
|
+
* guarantee that voice N in one measure is musically continuous with
|
|
45
|
+
* voice N in the next.
|
|
46
|
+
*
|
|
47
|
+
* Exported (Task 17) so `validation/validator.ts`'s `checkTieTargets` can
|
|
48
|
+
* build each track/voice-ordinal channel once and index it for O(1)-average
|
|
49
|
+
* partner lookups, instead of paying for a fresh `tieChainFor` walk (which
|
|
50
|
+
* itself rebuilds a channel from scratch) per tied note — see that
|
|
51
|
+
* function's doc comment for the super-linear-worst-case history.
|
|
52
|
+
*/
|
|
53
|
+
export declare function voiceChannel(track: Track, voiceIndex: number): ChannelCandidate[];
|
|
54
|
+
/**
|
|
55
|
+
* Returns the full chain of tied note events (in tick order) that `noteId`
|
|
56
|
+
* belongs to, searching across all of its track's measures. A chain may
|
|
57
|
+
* span measure boundaries. Returns `[]` if `noteId` does not exist or is
|
|
58
|
+
* not a note event; returns a single-element array for a note with no
|
|
59
|
+
* ties.
|
|
60
|
+
*
|
|
61
|
+
* Chain membership is searched only within the target's own "voice
|
|
62
|
+
* channel" (`voiceChannel`, above — the voice at the same ordinal
|
|
63
|
+
* position across every measure of the track), never across voices. This
|
|
64
|
+
* matters once a measure has more than one voice (spec §25 voice
|
|
65
|
+
* allocation): searching across all voices indiscriminately would let a
|
|
66
|
+
* coincidental same-pitch, tie-flagged note in an unrelated voice at a
|
|
67
|
+
* barline get spliced into the chain, or shadow the true partner when
|
|
68
|
+
* both share the same tick. Within the channel, a partner is found by
|
|
69
|
+
* explicit (tick, pitch, tie-flag) matching, not raw array adjacency, for
|
|
70
|
+
* the same reason.
|
|
71
|
+
*/
|
|
72
|
+
export declare function tieChainFor(score: Score, noteId: UUID): NoteEvent[];
|
|
73
|
+
//# sourceMappingURL=ties.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ties.d.ts","sourceRoot":"","sources":["../../../src/domain/score/ties.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EACV,YAAY,EACZ,SAAS,EAET,KAAK,EACL,KAAK,EACL,IAAI,EACL,MAAM,gBAAgB,CAAC;AAWxB;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,uBAAuB,CACrC,IAAI,EAAE,SAAS,EACf,iBAAiB,EAAE,MAAM,EAAE,GAC1B,SAAS,EAAE,CAsBb;AAED;;;;;;;GAOG;AACH,wBAAgB,aAAa,CAAC,MAAM,EAAE,YAAY,EAAE,GAAG,YAAY,EAAE,CAmCpE;AAED,4FAA4F;AAC5F,MAAM,MAAM,gBAAgB,GAAG;IAAE,KAAK,EAAE,SAAS,CAAC;IAAC,YAAY,EAAE,MAAM,CAAA;CAAE,CAAC;AAiB1E;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,YAAY,CAC1B,KAAK,EAAE,KAAK,EACZ,UAAU,EAAE,MAAM,GACjB,gBAAgB,EAAE,CAcpB;AAqCD;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,GAAG,SAAS,EAAE,CA8BnE"}
|
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
import { createId } from './ids.js';
|
|
2
|
+
import { findEvent, findTrack } from './queries.js';
|
|
3
|
+
import { isNoteEvent } from '../../index.js';
|
|
4
|
+
import { splitAtBoundaries } from '../time/durations.js';
|
|
5
|
+
/** Whether two pitches have identical spelling (step, accidental, and octave). */
|
|
6
|
+
function samePitch(a, b) {
|
|
7
|
+
return (a.step === b.step && a.accidental === b.accidental && a.octave === b.octave);
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Splits a note at every measure boundary that falls strictly inside its
|
|
11
|
+
* span, returning contiguous tied segments that together cover the
|
|
12
|
+
* original note. The first segment keeps the original id and any incoming
|
|
13
|
+
* tie (`tieStop`); the last keeps any outgoing tie (`tieStart`); segments
|
|
14
|
+
* in between are tied on both sides. Returns `[note]` unchanged if no
|
|
15
|
+
* boundary falls inside its span.
|
|
16
|
+
*
|
|
17
|
+
* Every returned segment inherits `note`'s `voiceId`/`trackId` verbatim.
|
|
18
|
+
* This function has no `Score`/`Measure` context, so it cannot know which
|
|
19
|
+
* voice a later segment will actually land in once inserted into the
|
|
20
|
+
* score — the caller (which does have that context) is responsible for
|
|
21
|
+
* reassigning each segment's `voiceId` to its destination measure's voice
|
|
22
|
+
* before/while inserting it (the same normalization `fragment.ts`'s
|
|
23
|
+
* `replaceFragment` performs).
|
|
24
|
+
*/
|
|
25
|
+
export function splitNoteAcrossMeasures(note, measureBoundaries) {
|
|
26
|
+
const segments = splitAtBoundaries(note.startTick, note.durationTicks, measureBoundaries);
|
|
27
|
+
if (segments.length <= 1) {
|
|
28
|
+
return [note];
|
|
29
|
+
}
|
|
30
|
+
return segments.map((segment, i) => {
|
|
31
|
+
const isFirst = i === 0;
|
|
32
|
+
const isLast = i === segments.length - 1;
|
|
33
|
+
return {
|
|
34
|
+
...note,
|
|
35
|
+
id: isFirst ? note.id : createId(),
|
|
36
|
+
startTick: segment.startTick,
|
|
37
|
+
durationTicks: segment.durationTicks,
|
|
38
|
+
tieStart: isLast ? note.tieStart : true,
|
|
39
|
+
tieStop: isFirst ? note.tieStop : true,
|
|
40
|
+
};
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Merges runs of contiguous, same-pitch, tie-linked note events into single
|
|
45
|
+
* notes spanning the combined duration. Partners are matched by
|
|
46
|
+
* `(startTick, pitch, tie flags)`, not raw array adjacency, so tied chord
|
|
47
|
+
* members can be joined even when another chord member sits between them.
|
|
48
|
+
* Rests and non-tied notes pass through unchanged. Assumes `events` are
|
|
49
|
+
* already in ascending `startTick` order (e.g. one voice's events).
|
|
50
|
+
*/
|
|
51
|
+
export function joinTiedNotes(events) {
|
|
52
|
+
const result = [];
|
|
53
|
+
const consumed = new Set();
|
|
54
|
+
for (const event of events) {
|
|
55
|
+
if (consumed.has(event.id))
|
|
56
|
+
continue;
|
|
57
|
+
if (!isNoteEvent(event) || !event.tieStart) {
|
|
58
|
+
result.push(event);
|
|
59
|
+
continue;
|
|
60
|
+
}
|
|
61
|
+
let merged = event;
|
|
62
|
+
consumed.add(event.id);
|
|
63
|
+
while (merged.tieStart) {
|
|
64
|
+
const next = events.find((candidate) => !consumed.has(candidate.id) &&
|
|
65
|
+
isNoteEvent(candidate) &&
|
|
66
|
+
Boolean(candidate.tieStop) &&
|
|
67
|
+
candidate.startTick === merged.startTick + merged.durationTicks &&
|
|
68
|
+
samePitch(candidate.pitch, merged.pitch));
|
|
69
|
+
if (!next)
|
|
70
|
+
break;
|
|
71
|
+
consumed.add(next.id);
|
|
72
|
+
merged = {
|
|
73
|
+
...merged,
|
|
74
|
+
durationTicks: merged.durationTicks + next.durationTicks,
|
|
75
|
+
tieStart: next.tieStart,
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
result.push(merged);
|
|
79
|
+
}
|
|
80
|
+
return result;
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* The ordinal index (position within `measure.voices`) of the voice
|
|
84
|
+
* containing `noteId`, searching every measure of `track` in order.
|
|
85
|
+
* Returns -1 if `noteId` isn't found in any voice of this track.
|
|
86
|
+
*/
|
|
87
|
+
function locateVoiceIndex(track, noteId) {
|
|
88
|
+
for (const measure of track.measures) {
|
|
89
|
+
const voiceIndex = measure.voices.findIndex(voice => voice.events.some(e => e.id === noteId));
|
|
90
|
+
if (voiceIndex !== -1)
|
|
91
|
+
return voiceIndex;
|
|
92
|
+
}
|
|
93
|
+
return -1;
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Every note event from the voice at ordinal position `voiceIndex` in
|
|
97
|
+
* each of `track`'s measures (a measure with no voice at that index
|
|
98
|
+
* contributes nothing), in measure/tick order. This is the "channel" a
|
|
99
|
+
* tie chain is searched within.
|
|
100
|
+
*
|
|
101
|
+
* Voice ids are freshly generated per measure and so are not stable
|
|
102
|
+
* across a barline (spec §25 permits per-measure voice reallocation), so
|
|
103
|
+
* they can't be used to correlate "the same voice" from one measure to
|
|
104
|
+
* the next. A voice's *ordinal position* in `measure.voices` is used as
|
|
105
|
+
* a stand-in instead — a deliberate, documented approximation, not a
|
|
106
|
+
* guarantee that voice N in one measure is musically continuous with
|
|
107
|
+
* voice N in the next.
|
|
108
|
+
*
|
|
109
|
+
* Exported (Task 17) so `validation/validator.ts`'s `checkTieTargets` can
|
|
110
|
+
* build each track/voice-ordinal channel once and index it for O(1)-average
|
|
111
|
+
* partner lookups, instead of paying for a fresh `tieChainFor` walk (which
|
|
112
|
+
* itself rebuilds a channel from scratch) per tied note — see that
|
|
113
|
+
* function's doc comment for the super-linear-worst-case history.
|
|
114
|
+
*/
|
|
115
|
+
export function voiceChannel(track, voiceIndex) {
|
|
116
|
+
const channel = [];
|
|
117
|
+
track.measures.forEach((measure, measureIndex) => {
|
|
118
|
+
const voice = measure.voices[voiceIndex];
|
|
119
|
+
if (!voice)
|
|
120
|
+
return;
|
|
121
|
+
for (const event of voice.events) {
|
|
122
|
+
if (isNoteEvent(event))
|
|
123
|
+
channel.push({ event, measureIndex });
|
|
124
|
+
}
|
|
125
|
+
});
|
|
126
|
+
channel.sort((a, b) => a.measureIndex - b.measureIndex || a.event.startTick - b.event.startTick);
|
|
127
|
+
return channel;
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* The note in `channel` that `note` ties into (its `tieStart` partner),
|
|
131
|
+
* found by explicit (tick, pitch, tie-flag) matching — never by array
|
|
132
|
+
* adjacency, so an unrelated same-tick note (e.g. a coincidental chord
|
|
133
|
+
* tone sharing `note`'s channel) can't be picked up by accident.
|
|
134
|
+
*/
|
|
135
|
+
function findForwardPartner(channel, note) {
|
|
136
|
+
if (!note.tieStart)
|
|
137
|
+
return undefined;
|
|
138
|
+
return channel.find(c => c.event.id !== note.id &&
|
|
139
|
+
c.event.startTick === note.startTick + note.durationTicks &&
|
|
140
|
+
Boolean(c.event.tieStop) &&
|
|
141
|
+
samePitch(c.event.pitch, note.pitch))?.event;
|
|
142
|
+
}
|
|
143
|
+
/** The note in `channel` that ties into `note` (its `tieStop` partner); see `findForwardPartner`. */
|
|
144
|
+
function findBackwardPartner(channel, note) {
|
|
145
|
+
if (!note.tieStop)
|
|
146
|
+
return undefined;
|
|
147
|
+
return channel.find(c => c.event.id !== note.id &&
|
|
148
|
+
c.event.startTick + c.event.durationTicks === note.startTick &&
|
|
149
|
+
Boolean(c.event.tieStart) &&
|
|
150
|
+
samePitch(c.event.pitch, note.pitch))?.event;
|
|
151
|
+
}
|
|
152
|
+
/**
|
|
153
|
+
* Returns the full chain of tied note events (in tick order) that `noteId`
|
|
154
|
+
* belongs to, searching across all of its track's measures. A chain may
|
|
155
|
+
* span measure boundaries. Returns `[]` if `noteId` does not exist or is
|
|
156
|
+
* not a note event; returns a single-element array for a note with no
|
|
157
|
+
* ties.
|
|
158
|
+
*
|
|
159
|
+
* Chain membership is searched only within the target's own "voice
|
|
160
|
+
* channel" (`voiceChannel`, above — the voice at the same ordinal
|
|
161
|
+
* position across every measure of the track), never across voices. This
|
|
162
|
+
* matters once a measure has more than one voice (spec §25 voice
|
|
163
|
+
* allocation): searching across all voices indiscriminately would let a
|
|
164
|
+
* coincidental same-pitch, tie-flagged note in an unrelated voice at a
|
|
165
|
+
* barline get spliced into the chain, or shadow the true partner when
|
|
166
|
+
* both share the same tick. Within the channel, a partner is found by
|
|
167
|
+
* explicit (tick, pitch, tie-flag) matching, not raw array adjacency, for
|
|
168
|
+
* the same reason.
|
|
169
|
+
*/
|
|
170
|
+
export function tieChainFor(score, noteId) {
|
|
171
|
+
const target = findEvent(score, noteId);
|
|
172
|
+
if (!target || !isNoteEvent(target))
|
|
173
|
+
return [];
|
|
174
|
+
const track = findTrack(score, target.trackId);
|
|
175
|
+
if (!track)
|
|
176
|
+
return [target];
|
|
177
|
+
const voiceIndex = locateVoiceIndex(track, target.id);
|
|
178
|
+
if (voiceIndex === -1)
|
|
179
|
+
return [target];
|
|
180
|
+
const channel = voiceChannel(track, voiceIndex);
|
|
181
|
+
const chain = [target];
|
|
182
|
+
let current = target;
|
|
183
|
+
for (let guard = 0; guard < channel.length; guard += 1) {
|
|
184
|
+
const partner = findBackwardPartner(channel, current);
|
|
185
|
+
if (!partner)
|
|
186
|
+
break;
|
|
187
|
+
chain.unshift(partner);
|
|
188
|
+
current = partner;
|
|
189
|
+
}
|
|
190
|
+
current = target;
|
|
191
|
+
for (let guard = 0; guard < channel.length; guard += 1) {
|
|
192
|
+
const partner = findForwardPartner(channel, current);
|
|
193
|
+
if (!partner)
|
|
194
|
+
break;
|
|
195
|
+
chain.push(partner);
|
|
196
|
+
current = partner;
|
|
197
|
+
}
|
|
198
|
+
return chain;
|
|
199
|
+
}
|
|
200
|
+
//# sourceMappingURL=ties.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ties.js","sourceRoot":"","sources":["../../../src/domain/score/ties.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import type { Score } from '../../index.js';
|
|
2
|
+
import type { ScoreRange, ScoreSelection } from './types.js';
|
|
3
|
+
/**
|
|
4
|
+
* The smallest tick range, aligned to full-measure boundaries on every
|
|
5
|
+
* implicated track, that covers everything named by `sel` (selected
|
|
6
|
+
* events, measures, an explicit `range`, and/or `trackIds`). Returns
|
|
7
|
+
* `null` when the selection carries no tick information at all (no
|
|
8
|
+
* resolvable events, no resolvable measures, and no `range`) — a bare
|
|
9
|
+
* `trackIds` selection has no timeline extent to convert.
|
|
10
|
+
*
|
|
11
|
+
* Track scope for the result is the union of: `sel.trackIds`, the owning
|
|
12
|
+
* track of every resolved event/measure, and `sel.range?.trackIds`. An
|
|
13
|
+
* id that doesn't resolve to anything in `score` (stale selection) is
|
|
14
|
+
* simply skipped when gathering tick anchors, but is still carried
|
|
15
|
+
* through into the result's `trackIds` (see `selectionIsRegenerable`,
|
|
16
|
+
* which treats that as a disqualifying condition).
|
|
17
|
+
*/
|
|
18
|
+
export declare function selectionToRange(score: Score, sel: ScoreSelection): ScoreRange | null;
|
|
19
|
+
/**
|
|
20
|
+
* Returns a selection with: duplicate ids removed; event/measure/track ids
|
|
21
|
+
* that no longer resolve in `score` dropped (a stale reference left over
|
|
22
|
+
* from an edit that deleted the selected object); and, if present, `range`
|
|
23
|
+
* normalized (its own `trackIds` deduplicated, and `startTick`/`endTick`
|
|
24
|
+
* swapped into ascending order if given reversed).
|
|
25
|
+
*/
|
|
26
|
+
export declare function normalizeSelection(score: Score, sel: ScoreSelection): ScoreSelection;
|
|
27
|
+
/**
|
|
28
|
+
* Whether `sel` can be converted (via `selectionToRange`) into a valid
|
|
29
|
+
* regeneration region: a non-empty, nonnegative tick span that doesn't
|
|
30
|
+
* extend past the end of the score, on tracks that all actually exist.
|
|
31
|
+
*/
|
|
32
|
+
export declare function selectionIsRegenerable(score: Score, sel: ScoreSelection): boolean;
|
|
33
|
+
/**
|
|
34
|
+
* A short, human-readable summary of what's selected (e.g. "3 note(s)
|
|
35
|
+
* selected"), checked in the same priority order `selectionToRange`
|
|
36
|
+
* resolves tick anchors in (events, then measures, then bare tracks).
|
|
37
|
+
* Spec §27: screen-reader text summarizing the current selection, shared
|
|
38
|
+
* between `AppLayout`'s status bar and the score-editor/piano-roll
|
|
39
|
+
* containers' own SR-only summaries (`ScoreEditorView`/`PianoRollView`) so
|
|
40
|
+
* all three always agree on the same wording.
|
|
41
|
+
*
|
|
42
|
+
* `regenerated` appends ", regenerated". Note state is carried by color
|
|
43
|
+
* alone on the canvas now (the highlight overlay's solid/dashed/dotted
|
|
44
|
+
* stroke patterns went away with it), so this label is the non-color
|
|
45
|
+
* channel keeping that state perceivable — not a cosmetic extra.
|
|
46
|
+
*/
|
|
47
|
+
/**
|
|
48
|
+
* The words this summary is built from, supplied by the caller.
|
|
49
|
+
*
|
|
50
|
+
* Functions rather than strings because the counts have to agree with their
|
|
51
|
+
* nouns, and which forms a number takes is a property of the language, not of
|
|
52
|
+
* a selection — English has two, Chinese one, Polish three.
|
|
53
|
+
*/
|
|
54
|
+
export type SelectionSummaryCopy = {
|
|
55
|
+
notes: (count: number) => string;
|
|
56
|
+
measures: (count: number) => string;
|
|
57
|
+
tracks: (count: number) => string;
|
|
58
|
+
none: string;
|
|
59
|
+
/** Wraps a summary to say it describes regenerated music. */
|
|
60
|
+
regenerated: (summary: string) => string;
|
|
61
|
+
};
|
|
62
|
+
export declare function selectionSummaryLabel(sel: ScoreSelection, copy: SelectionSummaryCopy, regenerated?: boolean): string;
|
|
63
|
+
//# sourceMappingURL=selection.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"selection.d.ts","sourceRoot":"","sources":["../../../src/domain/selection/selection.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAE,KAAK,EAAS,MAAM,gBAAgB,CAAC;AACnD,OAAO,KAAK,EAAE,UAAU,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAE7D;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,gBAAgB,CAC9B,KAAK,EAAE,KAAK,EACZ,GAAG,EAAE,cAAc,GAClB,UAAU,GAAG,IAAI,CA6DnB;AAOD;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAChC,KAAK,EAAE,KAAK,EACZ,GAAG,EAAE,cAAc,GAClB,cAAc,CA2BhB;AAED;;;;GAIG;AACH,wBAAgB,sBAAsB,CACpC,KAAK,EAAE,KAAK,EACZ,GAAG,EAAE,cAAc,GAClB,OAAO,CAMT;AAED;;;;;;;;;;;;;GAaG;AACH;;;;;;GAMG;AACH,MAAM,MAAM,oBAAoB,GAAG;IACjC,KAAK,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,MAAM,CAAC;IACjC,QAAQ,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,MAAM,CAAC;IACpC,MAAM,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,MAAM,CAAC;IAClC,IAAI,EAAE,MAAM,CAAC;IACb,6DAA6D;IAC7D,WAAW,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,MAAM,CAAC;CAC1C,CAAC;AAEF,wBAAgB,qBAAqB,CACnC,GAAG,EAAE,cAAc,EACnB,IAAI,EAAE,oBAAoB,EAC1B,WAAW,UAAQ,GAClB,MAAM,CAQR"}
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Selection helpers (spec §9). Pure, read-only functions over `Score` +
|
|
3
|
+
* `ScoreSelection`/`ScoreRange` — no UI, store, or rendering concerns.
|
|
4
|
+
*/
|
|
5
|
+
import { findEvent, findMeasure, findTrack, scoreEndTick, } from '../score/queries.js';
|
|
6
|
+
/**
|
|
7
|
+
* The smallest tick range, aligned to full-measure boundaries on every
|
|
8
|
+
* implicated track, that covers everything named by `sel` (selected
|
|
9
|
+
* events, measures, an explicit `range`, and/or `trackIds`). Returns
|
|
10
|
+
* `null` when the selection carries no tick information at all (no
|
|
11
|
+
* resolvable events, no resolvable measures, and no `range`) — a bare
|
|
12
|
+
* `trackIds` selection has no timeline extent to convert.
|
|
13
|
+
*
|
|
14
|
+
* Track scope for the result is the union of: `sel.trackIds`, the owning
|
|
15
|
+
* track of every resolved event/measure, and `sel.range?.trackIds`. An
|
|
16
|
+
* id that doesn't resolve to anything in `score` (stale selection) is
|
|
17
|
+
* simply skipped when gathering tick anchors, but is still carried
|
|
18
|
+
* through into the result's `trackIds` (see `selectionIsRegenerable`,
|
|
19
|
+
* which treats that as a disqualifying condition).
|
|
20
|
+
*/
|
|
21
|
+
export function selectionToRange(score, sel) {
|
|
22
|
+
let minTick = Infinity;
|
|
23
|
+
let maxTick = -Infinity;
|
|
24
|
+
const trackIds = new Set();
|
|
25
|
+
for (const eventId of sel.eventIds) {
|
|
26
|
+
const event = findEvent(score, eventId);
|
|
27
|
+
if (!event)
|
|
28
|
+
continue;
|
|
29
|
+
minTick = Math.min(minTick, event.startTick);
|
|
30
|
+
maxTick = Math.max(maxTick, event.startTick + event.durationTicks);
|
|
31
|
+
trackIds.add(event.trackId);
|
|
32
|
+
}
|
|
33
|
+
for (const measureId of sel.measureIds) {
|
|
34
|
+
const measure = findMeasure(score, measureId);
|
|
35
|
+
if (!measure)
|
|
36
|
+
continue;
|
|
37
|
+
minTick = Math.min(minTick, measure.startTick);
|
|
38
|
+
maxTick = Math.max(maxTick, measure.startTick + measure.durationTicks);
|
|
39
|
+
const owningTrack = score.tracks.find(t => t.measures.some(m => m.id === measureId));
|
|
40
|
+
if (owningTrack)
|
|
41
|
+
trackIds.add(owningTrack.id);
|
|
42
|
+
}
|
|
43
|
+
if (sel.range) {
|
|
44
|
+
minTick = Math.min(minTick, sel.range.startTick);
|
|
45
|
+
maxTick = Math.max(maxTick, sel.range.endTick);
|
|
46
|
+
for (const id of sel.range.trackIds)
|
|
47
|
+
trackIds.add(id);
|
|
48
|
+
}
|
|
49
|
+
for (const id of sel.trackIds)
|
|
50
|
+
trackIds.add(id);
|
|
51
|
+
if (!Number.isFinite(minTick) || !Number.isFinite(maxTick)) {
|
|
52
|
+
return null;
|
|
53
|
+
}
|
|
54
|
+
const scopedTrackIds = [...trackIds];
|
|
55
|
+
const tracksInScope = scopedTrackIds.length > 0
|
|
56
|
+
? scopedTrackIds
|
|
57
|
+
.map(id => findTrack(score, id))
|
|
58
|
+
.filter((t) => t !== null)
|
|
59
|
+
: score.tracks;
|
|
60
|
+
let alignedStart = minTick;
|
|
61
|
+
let alignedEnd = maxTick;
|
|
62
|
+
for (const track of tracksInScope) {
|
|
63
|
+
for (const measure of track.measures) {
|
|
64
|
+
const measureEnd = measure.startTick + measure.durationTicks;
|
|
65
|
+
if (measure.startTick < maxTick && measureEnd > minTick) {
|
|
66
|
+
alignedStart = Math.min(alignedStart, measure.startTick);
|
|
67
|
+
alignedEnd = Math.max(alignedEnd, measureEnd);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
return {
|
|
72
|
+
startTick: alignedStart,
|
|
73
|
+
endTick: alignedEnd,
|
|
74
|
+
trackIds: scopedTrackIds,
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
/** Deduplicates an array while preserving first-seen order. */
|
|
78
|
+
function dedupe(ids) {
|
|
79
|
+
return [...new Set(ids)];
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Returns a selection with: duplicate ids removed; event/measure/track ids
|
|
83
|
+
* that no longer resolve in `score` dropped (a stale reference left over
|
|
84
|
+
* from an edit that deleted the selected object); and, if present, `range`
|
|
85
|
+
* normalized (its own `trackIds` deduplicated, and `startTick`/`endTick`
|
|
86
|
+
* swapped into ascending order if given reversed).
|
|
87
|
+
*/
|
|
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);
|
|
92
|
+
const normalized = { eventIds, measureIds, trackIds };
|
|
93
|
+
if (sel.range) {
|
|
94
|
+
const startTick = Math.max(0, Math.min(sel.range.startTick, sel.range.endTick));
|
|
95
|
+
const endTick = Math.max(sel.range.startTick, sel.range.endTick);
|
|
96
|
+
normalized.range = {
|
|
97
|
+
startTick,
|
|
98
|
+
endTick,
|
|
99
|
+
trackIds: dedupe(sel.range.trackIds),
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
return normalized;
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Whether `sel` can be converted (via `selectionToRange`) into a valid
|
|
106
|
+
* regeneration region: a non-empty, nonnegative tick span that doesn't
|
|
107
|
+
* extend past the end of the score, on tracks that all actually exist.
|
|
108
|
+
*/
|
|
109
|
+
export function selectionIsRegenerable(score, sel) {
|
|
110
|
+
const range = selectionToRange(score, sel);
|
|
111
|
+
if (!range)
|
|
112
|
+
return false;
|
|
113
|
+
if (range.startTick < 0 || range.startTick >= range.endTick)
|
|
114
|
+
return false;
|
|
115
|
+
if (range.endTick > scoreEndTick(score))
|
|
116
|
+
return false;
|
|
117
|
+
return range.trackIds.every(id => findTrack(score, id) !== null);
|
|
118
|
+
}
|
|
119
|
+
export function selectionSummaryLabel(sel, copy, regenerated = false) {
|
|
120
|
+
const mark = (summary) => regenerated ? copy.regenerated(summary) : summary;
|
|
121
|
+
if (sel.eventIds.length > 0)
|
|
122
|
+
return mark(copy.notes(sel.eventIds.length));
|
|
123
|
+
if (sel.measureIds.length > 0)
|
|
124
|
+
return mark(copy.measures(sel.measureIds.length));
|
|
125
|
+
if (sel.trackIds.length > 0)
|
|
126
|
+
return mark(copy.tracks(sel.trackIds.length));
|
|
127
|
+
return copy.none;
|
|
128
|
+
}
|
|
129
|
+
//# sourceMappingURL=selection.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"selection.js","sourceRoot":"","sources":["../../../src/domain/selection/selection.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Selection model (spec §9). The `ScoreRange`/`ScoreSelection` types are
|
|
3
|
+
* canonical in @sudobility/music_types and re-exported here so domain
|
|
4
|
+
* modules keep one import site; `emptySelection` is the runtime helper.
|
|
5
|
+
*/
|
|
6
|
+
export type { ScoreRange, ScoreSelection } from '../../index.js';
|
|
7
|
+
import type { ScoreSelection } from '../../index.js';
|
|
8
|
+
/** An empty selection: no events, measures, tracks, or range selected. */
|
|
9
|
+
export declare function emptySelection(): ScoreSelection;
|
|
10
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/domain/selection/types.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,YAAY,EAAE,UAAU,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AACjE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAErD,0EAA0E;AAC1E,wBAAgB,cAAc,IAAI,cAAc,CAE/C"}
|