@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.
Files changed (153) hide show
  1. package/dist/domain/commands/edit-commands.d.ts +66 -0
  2. package/dist/domain/commands/edit-commands.d.ts.map +1 -0
  3. package/dist/domain/commands/edit-commands.js +185 -0
  4. package/dist/domain/commands/edit-commands.js.map +1 -0
  5. package/dist/domain/commands/note-commands.d.ts +73 -0
  6. package/dist/domain/commands/note-commands.d.ts.map +1 -0
  7. package/dist/domain/commands/note-commands.js +292 -0
  8. package/dist/domain/commands/note-commands.js.map +1 -0
  9. package/dist/domain/commands/reflow.d.ts +69 -0
  10. package/dist/domain/commands/reflow.d.ts.map +1 -0
  11. package/dist/domain/commands/reflow.js +395 -0
  12. package/dist/domain/commands/reflow.js.map +1 -0
  13. package/dist/domain/commands/region-commands.d.ts +21 -0
  14. package/dist/domain/commands/region-commands.d.ts.map +1 -0
  15. package/dist/domain/commands/region-commands.js +47 -0
  16. package/dist/domain/commands/region-commands.js.map +1 -0
  17. package/dist/domain/commands/relocate-commands.d.ts +18 -0
  18. package/dist/domain/commands/relocate-commands.d.ts.map +1 -0
  19. package/dist/domain/commands/relocate-commands.js +108 -0
  20. package/dist/domain/commands/relocate-commands.js.map +1 -0
  21. package/dist/domain/commands/ripple-commands.d.ts +44 -0
  22. package/dist/domain/commands/ripple-commands.d.ts.map +1 -0
  23. package/dist/domain/commands/ripple-commands.js +104 -0
  24. package/dist/domain/commands/ripple-commands.js.map +1 -0
  25. package/dist/domain/commands/snapshot.d.ts +38 -0
  26. package/dist/domain/commands/snapshot.d.ts.map +1 -0
  27. package/dist/domain/commands/snapshot.js +44 -0
  28. package/dist/domain/commands/snapshot.js.map +1 -0
  29. package/dist/domain/commands/structure-commands.d.ts +38 -0
  30. package/dist/domain/commands/structure-commands.d.ts.map +1 -0
  31. package/dist/domain/commands/structure-commands.js +220 -0
  32. package/dist/domain/commands/structure-commands.js.map +1 -0
  33. package/dist/domain/commands/track-commands.d.ts +15 -0
  34. package/dist/domain/commands/track-commands.d.ts.map +1 -0
  35. package/dist/domain/commands/track-commands.js +56 -0
  36. package/dist/domain/commands/track-commands.js.map +1 -0
  37. package/dist/domain/commands/types.d.ts +31 -0
  38. package/dist/domain/commands/types.d.ts.map +1 -0
  39. package/dist/domain/commands/types.js +2 -0
  40. package/dist/domain/commands/types.js.map +1 -0
  41. package/dist/domain/instruments/gm-kit.d.ts +45 -0
  42. package/dist/domain/instruments/gm-kit.d.ts.map +1 -0
  43. package/dist/domain/instruments/gm-kit.js +59 -0
  44. package/dist/domain/instruments/gm-kit.js.map +1 -0
  45. package/dist/domain/instruments/gm-percussion.d.ts +42 -0
  46. package/dist/domain/instruments/gm-percussion.d.ts.map +1 -0
  47. package/dist/domain/instruments/gm-percussion.js +85 -0
  48. package/dist/domain/instruments/gm-percussion.js.map +1 -0
  49. package/dist/domain/instruments/gm-polyphony.d.ts +13 -0
  50. package/dist/domain/instruments/gm-polyphony.d.ts.map +1 -0
  51. package/dist/domain/instruments/gm-polyphony.js +76 -0
  52. package/dist/domain/instruments/gm-polyphony.js.map +1 -0
  53. package/dist/domain/instruments/gm-range.d.ts +15 -0
  54. package/dist/domain/instruments/gm-range.d.ts.map +1 -0
  55. package/dist/domain/instruments/gm-range.js +97 -0
  56. package/dist/domain/instruments/gm-range.js.map +1 -0
  57. package/dist/domain/instruments/gm-transposition.d.ts +11 -0
  58. package/dist/domain/instruments/gm-transposition.d.ts.map +1 -0
  59. package/dist/domain/instruments/gm-transposition.js +69 -0
  60. package/dist/domain/instruments/gm-transposition.js.map +1 -0
  61. package/dist/domain/instruments/gm.d.ts +28 -0
  62. package/dist/domain/instruments/gm.d.ts.map +1 -0
  63. package/dist/domain/instruments/gm.js +217 -0
  64. package/dist/domain/instruments/gm.js.map +1 -0
  65. package/dist/domain/pitch/pitch.d.ts +15 -0
  66. package/dist/domain/pitch/pitch.d.ts.map +1 -0
  67. package/dist/domain/pitch/pitch.js +81 -0
  68. package/dist/domain/pitch/pitch.js.map +1 -0
  69. package/dist/domain/pitch/transpose.d.ts +37 -0
  70. package/dist/domain/pitch/transpose.d.ts.map +1 -0
  71. package/dist/domain/pitch/transpose.js +65 -0
  72. package/dist/domain/pitch/transpose.js.map +1 -0
  73. package/dist/domain/quantization/options.d.ts +55 -0
  74. package/dist/domain/quantization/options.d.ts.map +1 -0
  75. package/dist/domain/quantization/options.js +2 -0
  76. package/dist/domain/quantization/options.js.map +1 -0
  77. package/dist/domain/quantization/quantize.d.ts +13 -0
  78. package/dist/domain/quantization/quantize.d.ts.map +1 -0
  79. package/dist/domain/quantization/quantize.js +199 -0
  80. package/dist/domain/quantization/quantize.js.map +1 -0
  81. package/dist/domain/score/factory.d.ts +39 -0
  82. package/dist/domain/score/factory.d.ts.map +1 -0
  83. package/dist/domain/score/factory.js +148 -0
  84. package/dist/domain/score/factory.js.map +1 -0
  85. package/dist/domain/score/fragment.d.ts +28 -0
  86. package/dist/domain/score/fragment.d.ts.map +1 -0
  87. package/dist/domain/score/fragment.js +68 -0
  88. package/dist/domain/score/fragment.js.map +1 -0
  89. package/dist/domain/score/ids.d.ts +8 -0
  90. package/dist/domain/score/ids.d.ts.map +1 -0
  91. package/dist/domain/score/ids.js +38 -0
  92. package/dist/domain/score/ids.js.map +1 -0
  93. package/dist/domain/score/queries.d.ts +34 -0
  94. package/dist/domain/score/queries.d.ts.map +1 -0
  95. package/dist/domain/score/queries.js +132 -0
  96. package/dist/domain/score/queries.js.map +1 -0
  97. package/dist/domain/score/ties.d.ts +73 -0
  98. package/dist/domain/score/ties.d.ts.map +1 -0
  99. package/dist/domain/score/ties.js +200 -0
  100. package/dist/domain/score/ties.js.map +1 -0
  101. package/dist/domain/selection/selection.d.ts +63 -0
  102. package/dist/domain/selection/selection.d.ts.map +1 -0
  103. package/dist/domain/selection/selection.js +129 -0
  104. package/dist/domain/selection/selection.js.map +1 -0
  105. package/dist/domain/selection/types.d.ts +10 -0
  106. package/dist/domain/selection/types.d.ts.map +1 -0
  107. package/dist/domain/selection/types.js +5 -0
  108. package/dist/domain/selection/types.js.map +1 -0
  109. package/dist/domain/time/durations.d.ts +21 -0
  110. package/dist/domain/time/durations.d.ts.map +1 -0
  111. package/dist/domain/time/durations.js +53 -0
  112. package/dist/domain/time/durations.js.map +1 -0
  113. package/dist/domain/time/fraction.d.ts +19 -0
  114. package/dist/domain/time/fraction.d.ts.map +1 -0
  115. package/dist/domain/time/fraction.js +60 -0
  116. package/dist/domain/time/fraction.js.map +1 -0
  117. package/dist/domain/time/tempo-map.d.ts +24 -0
  118. package/dist/domain/time/tempo-map.d.ts.map +1 -0
  119. package/dist/domain/time/tempo-map.js +85 -0
  120. package/dist/domain/time/tempo-map.js.map +1 -0
  121. package/dist/domain/time/ticks.d.ts +34 -0
  122. package/dist/domain/time/ticks.d.ts.map +1 -0
  123. package/dist/domain/time/ticks.js +93 -0
  124. package/dist/domain/time/ticks.js.map +1 -0
  125. package/dist/domain/validation/issues.d.ts +44 -0
  126. package/dist/domain/validation/issues.d.ts.map +1 -0
  127. package/dist/domain/validation/issues.js +34 -0
  128. package/dist/domain/validation/issues.js.map +1 -0
  129. package/dist/domain/validation/validator.d.ts +11 -0
  130. package/dist/domain/validation/validator.d.ts.map +1 -0
  131. package/dist/domain/validation/validator.js +375 -0
  132. package/dist/domain/validation/validator.js.map +1 -0
  133. package/dist/domain/voicing/allocate.d.ts +19 -0
  134. package/dist/domain/voicing/allocate.d.ts.map +1 -0
  135. package/dist/domain/voicing/allocate.js +100 -0
  136. package/dist/domain/voicing/allocate.js.map +1 -0
  137. package/dist/index.d.ts +46 -4
  138. package/dist/index.d.ts.map +1 -1
  139. package/dist/index.js +50 -2
  140. package/dist/index.js.map +1 -1
  141. package/dist/platform/audio.d.ts +12 -0
  142. package/dist/platform/audio.d.ts.map +1 -1
  143. package/dist/platform/playback.d.ts +85 -15
  144. package/dist/platform/playback.d.ts.map +1 -1
  145. package/dist/services/regeneration/controller.d.ts +60 -0
  146. package/dist/services/regeneration/controller.d.ts.map +1 -0
  147. package/dist/services/regeneration/controller.js +202 -0
  148. package/dist/services/regeneration/controller.js.map +1 -0
  149. package/dist/test/fixtures.d.ts +45 -0
  150. package/dist/test/fixtures.d.ts.map +1 -0
  151. package/dist/test/fixtures.js +420 -0
  152. package/dist/test/fixtures.js.map +1 -0
  153. 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"}
@@ -0,0 +1,5 @@
1
+ /** An empty selection: no events, measures, tracks, or range selected. */
2
+ export function emptySelection() {
3
+ return { eventIds: [], measureIds: [], trackIds: [] };
4
+ }
5
+ //# sourceMappingURL=types.js.map