@sudobility/music_types 0.11.20 → 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/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,199 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Reusable quantization engine (spec §24). `quantizeEvents` is a pure
|
|
3
|
+
* function: it never mutates `events` or any event within it, and its
|
|
4
|
+
* output depends only on its inputs (no randomness — "humanization" here
|
|
5
|
+
* means a deterministic "already close enough, leave it" tolerance, not
|
|
6
|
+
* random jitter). Exposed for both MIDI import cleanup and manual editing.
|
|
7
|
+
*/
|
|
8
|
+
import { createId } from '../score/ids.js';
|
|
9
|
+
import { isNoteEvent } from '../../index.js';
|
|
10
|
+
/** `tripletGrid` subdivides `grid` into thirds-of-two (2/3 of `grid`); otherwise the grid unit is used as-is. */
|
|
11
|
+
function effectiveGridTicks(opts) {
|
|
12
|
+
return opts.tripletGrid ? (opts.grid * 2) / 3 : opts.grid;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Snaps `original` to the nearest grid line (round-half-up, i.e. a note
|
|
16
|
+
* exactly at the midpoint between two grid lines snaps to the later one),
|
|
17
|
+
* then, if `swing` is set, delays every second grid slot (odd slot index)
|
|
18
|
+
* by `swing` * one grid unit. If `humanizeToleranceTicks` is set and
|
|
19
|
+
* `original` is already within that many ticks of the computed target, the
|
|
20
|
+
* event is left at `original` instead (not force-snapped).
|
|
21
|
+
*/
|
|
22
|
+
function computeSnappedStart(original, opts) {
|
|
23
|
+
const grid = effectiveGridTicks(opts);
|
|
24
|
+
if (grid <= 0)
|
|
25
|
+
return original;
|
|
26
|
+
const slotIndex = Math.round(original / grid);
|
|
27
|
+
let target = slotIndex * grid;
|
|
28
|
+
if (opts.swing && slotIndex % 2 !== 0) {
|
|
29
|
+
target += opts.swing * grid;
|
|
30
|
+
}
|
|
31
|
+
target = Math.round(target);
|
|
32
|
+
if (opts.humanizeToleranceTicks !== undefined &&
|
|
33
|
+
Math.abs(original - target) <= opts.humanizeToleranceTicks) {
|
|
34
|
+
return original;
|
|
35
|
+
}
|
|
36
|
+
return target;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Snaps `original` to the nearest multiple of the grid unit, then clamps
|
|
40
|
+
* the result up to at least `minDurationTicks` (default a hard 1-tick
|
|
41
|
+
* floor when unset) so quantization can never produce a zero/negative
|
|
42
|
+
* duration, which every other event in the domain model forbids.
|
|
43
|
+
*/
|
|
44
|
+
function computeSnappedDuration(original, opts) {
|
|
45
|
+
const grid = effectiveGridTicks(opts);
|
|
46
|
+
if (grid <= 0)
|
|
47
|
+
return Math.max(original, opts.minDurationTicks ?? 1);
|
|
48
|
+
const units = Math.round(original / grid);
|
|
49
|
+
const snapped = Math.round(units * grid);
|
|
50
|
+
return Math.max(snapped, opts.minDurationTicks ?? 1);
|
|
51
|
+
}
|
|
52
|
+
/** Drops `NoteEvent`s whose *original* duration is shorter than `minDurationTicks`. `RestEvent`s are never dropped. */
|
|
53
|
+
function dropShortNotes(events, minDurationTicks) {
|
|
54
|
+
if (minDurationTicks === undefined)
|
|
55
|
+
return events;
|
|
56
|
+
return events.filter(event => !isNoteEvent(event) || event.durationTicks >= minDurationTicks);
|
|
57
|
+
}
|
|
58
|
+
/** Groups events by (trackId, voiceId), preserving each group's first-seen order. */
|
|
59
|
+
function groupByVoice(events) {
|
|
60
|
+
const order = [];
|
|
61
|
+
const groups = new Map();
|
|
62
|
+
for (const event of events) {
|
|
63
|
+
const key = `${event.trackId}::${event.voiceId}`;
|
|
64
|
+
let group = groups.get(key);
|
|
65
|
+
if (!group) {
|
|
66
|
+
group = [];
|
|
67
|
+
groups.set(key, group);
|
|
68
|
+
order.push(key);
|
|
69
|
+
}
|
|
70
|
+
group.push(event);
|
|
71
|
+
}
|
|
72
|
+
return order.map(key => groups.get(key));
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Clusters near-simultaneous onsets (already sorted by `startTick`) onto a
|
|
76
|
+
* shared start tick: the first event in a run seeds a cluster "anchor";
|
|
77
|
+
* every subsequent event within `tolerance` ticks of the anchor is
|
|
78
|
+
* reassigned to the anchor's tick, otherwise it becomes the next cluster's
|
|
79
|
+
* anchor. Anchoring (rather than comparing each event to its immediate
|
|
80
|
+
* neighbor) bounds how far a chain of close onsets can drift in total.
|
|
81
|
+
*/
|
|
82
|
+
function applyChordGrouping(sorted, tolerance) {
|
|
83
|
+
let anchor = 0;
|
|
84
|
+
return sorted.map((event, index) => {
|
|
85
|
+
if (index === 0) {
|
|
86
|
+
anchor = event.startTick;
|
|
87
|
+
return event;
|
|
88
|
+
}
|
|
89
|
+
if (event.startTick - anchor <= tolerance) {
|
|
90
|
+
return { ...event, startTick: anchor };
|
|
91
|
+
}
|
|
92
|
+
anchor = event.startTick;
|
|
93
|
+
return event;
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Trims an event that overlaps the following event's start (already
|
|
98
|
+
* sorted by `startTick`) down to end exactly at that start. Events sharing
|
|
99
|
+
* an exact start tick are a chord, not an overlap, and are left alone.
|
|
100
|
+
* Only adjacent pairs are compared, matching the everyday "this note runs
|
|
101
|
+
* into the next one" meaning of overlap resolution.
|
|
102
|
+
*/
|
|
103
|
+
function applyResolveOverlaps(sorted) {
|
|
104
|
+
const result = sorted.map(event => ({ ...event }));
|
|
105
|
+
for (let i = 0; i < result.length - 1; i += 1) {
|
|
106
|
+
const current = result[i];
|
|
107
|
+
const next = result[i + 1];
|
|
108
|
+
if (current.startTick === next.startTick)
|
|
109
|
+
continue;
|
|
110
|
+
const currentEnd = current.startTick + current.durationTicks;
|
|
111
|
+
if (currentEnd > next.startTick) {
|
|
112
|
+
current.durationTicks = Math.max(1, next.startTick - current.startTick);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
return result;
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* Extends an event that falls short of the following event's start
|
|
119
|
+
* (already sorted by `startTick`) so it ends exactly there, closing small
|
|
120
|
+
* silent gaps. Never shrinks an event that already reaches or overlaps the
|
|
121
|
+
* next one. Events sharing an exact start tick (a chord) are left alone.
|
|
122
|
+
*/
|
|
123
|
+
function applyLegatoCleanup(sorted) {
|
|
124
|
+
const result = sorted.map(event => ({ ...event }));
|
|
125
|
+
for (let i = 0; i < result.length - 1; i += 1) {
|
|
126
|
+
const current = result[i];
|
|
127
|
+
const next = result[i + 1];
|
|
128
|
+
if (current.startTick === next.startTick)
|
|
129
|
+
continue;
|
|
130
|
+
const currentEnd = current.startTick + current.durationTicks;
|
|
131
|
+
if (currentEnd < next.startTick) {
|
|
132
|
+
current.durationTicks = next.startTick - current.startTick;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
return result;
|
|
136
|
+
}
|
|
137
|
+
/** Inserts a fresh `RestEvent` for every remaining silent gap between consecutive events (already sorted by `startTick`). */
|
|
138
|
+
function applyFillGaps(sorted, trackId, voiceId) {
|
|
139
|
+
const result = [];
|
|
140
|
+
let cursor = null;
|
|
141
|
+
for (const event of sorted) {
|
|
142
|
+
if (cursor !== null && event.startTick > cursor) {
|
|
143
|
+
result.push({
|
|
144
|
+
id: createId(),
|
|
145
|
+
startTick: cursor,
|
|
146
|
+
durationTicks: event.startTick - cursor,
|
|
147
|
+
voiceId,
|
|
148
|
+
trackId,
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
result.push(event);
|
|
152
|
+
const end = event.startTick + event.durationTicks;
|
|
153
|
+
cursor = cursor === null ? end : Math.max(cursor, end);
|
|
154
|
+
}
|
|
155
|
+
return result;
|
|
156
|
+
}
|
|
157
|
+
/** Runs the chord-grouping/overlap-resolution/legato/gap-filling stages (each opt-in) over one (trackId, voiceId) group. */
|
|
158
|
+
function processVoiceGroup(group, opts) {
|
|
159
|
+
let current = [...group].sort((a, b) => a.startTick - b.startTick);
|
|
160
|
+
if (opts.chordToleranceTicks !== undefined) {
|
|
161
|
+
current = applyChordGrouping(current, opts.chordToleranceTicks);
|
|
162
|
+
}
|
|
163
|
+
if (opts.resolveOverlaps) {
|
|
164
|
+
current = applyResolveOverlaps(current);
|
|
165
|
+
}
|
|
166
|
+
if (opts.legatoCleanup) {
|
|
167
|
+
current = applyLegatoCleanup(current);
|
|
168
|
+
}
|
|
169
|
+
if (opts.fillGaps) {
|
|
170
|
+
const [{ trackId, voiceId }] = group;
|
|
171
|
+
current = applyFillGaps(current, trackId, voiceId);
|
|
172
|
+
}
|
|
173
|
+
return current;
|
|
174
|
+
}
|
|
175
|
+
/**
|
|
176
|
+
* Quantizes `events` per `opts` (spec §24): drops undersized notes, snaps
|
|
177
|
+
* starts (grid/triplet/swing/humanize tolerance) and durations (grid,
|
|
178
|
+
* clamped to a minimum), then — per (trackId, voiceId) group — clusters
|
|
179
|
+
* near-simultaneous onsets into chords, resolves overlaps, closes gaps
|
|
180
|
+
* (legato), and fills remaining silence with rests. Every stage after
|
|
181
|
+
* dropping is opt-in via `opts`; omitting a field disables that stage.
|
|
182
|
+
* Returns a new array; never mutates `events` or its elements.
|
|
183
|
+
*/
|
|
184
|
+
export function quantizeEvents(events, opts) {
|
|
185
|
+
const survivors = dropShortNotes(events, opts.minDurationTicks);
|
|
186
|
+
const withStarts = survivors.map(event => opts.quantizeStarts
|
|
187
|
+
? { ...event, startTick: computeSnappedStart(event.startTick, opts) }
|
|
188
|
+
: { ...event });
|
|
189
|
+
const withDurations = withStarts.map(event => opts.quantizeDurations
|
|
190
|
+
? {
|
|
191
|
+
...event,
|
|
192
|
+
durationTicks: computeSnappedDuration(event.durationTicks, opts),
|
|
193
|
+
}
|
|
194
|
+
: event);
|
|
195
|
+
return groupByVoice(withDurations)
|
|
196
|
+
.map(group => processVoiceGroup(group, opts))
|
|
197
|
+
.flat();
|
|
198
|
+
}
|
|
199
|
+
//# sourceMappingURL=quantize.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"quantize.js","sourceRoot":"","sources":["../../../src/domain/quantization/quantize.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import type { KeySignature, Score, TimeSignature, Track } from '../../index.js';
|
|
2
|
+
export type CreateTrackOptions = Partial<Track> & {
|
|
3
|
+
name: string;
|
|
4
|
+
};
|
|
5
|
+
/** Builds a `Track` with sensible defaults, overridden by any fields in `opts`. */
|
|
6
|
+
export declare function createTrack(opts: CreateTrackOptions): Track;
|
|
7
|
+
export type CreateEmptyScoreOptions = {
|
|
8
|
+
title: string;
|
|
9
|
+
ppq?: number;
|
|
10
|
+
tempo?: number;
|
|
11
|
+
timeSignature?: TimeSignature;
|
|
12
|
+
keySignature?: KeySignature;
|
|
13
|
+
measures?: number;
|
|
14
|
+
tracks?: Array<Partial<Track> & {
|
|
15
|
+
name: string;
|
|
16
|
+
}>;
|
|
17
|
+
};
|
|
18
|
+
/**
|
|
19
|
+
* Builds a new `Score` with `measures` fully-rested measures on each of
|
|
20
|
+
* `tracks`, one default voice per track per measure. Defaults: 480 PPQ,
|
|
21
|
+
* 120 bpm, 4/4, C major, one measure, one Piano track.
|
|
22
|
+
*/
|
|
23
|
+
export declare function createEmptyScore(opts: CreateEmptyScoreOptions): Score;
|
|
24
|
+
/**
|
|
25
|
+
* Appends one fully-rested measure to every track, continuing each track's
|
|
26
|
+
* own tick numbering and reusing its last measure's time/key signature
|
|
27
|
+
* (falling back to 4/4 C major for a track with no existing measures).
|
|
28
|
+
*/
|
|
29
|
+
export declare function appendMeasure(score: Score): Score;
|
|
30
|
+
/**
|
|
31
|
+
* Recomputes `index`/`startTick` for every track's measures from their
|
|
32
|
+
* current array order (each measure keeps its own `durationTicks`), and
|
|
33
|
+
* shifts each measure's events by the same delta so they stay positioned
|
|
34
|
+
* consistently within their (possibly moved) measure. Measures whose
|
|
35
|
+
* index/startTick are already consistent are returned unchanged
|
|
36
|
+
* (referentially equal) so unaffected structure is preserved.
|
|
37
|
+
*/
|
|
38
|
+
export declare function rebuildMeasureTicks(score: Score): Score;
|
|
39
|
+
//# sourceMappingURL=factory.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"factory.d.ts","sourceRoot":"","sources":["../../../src/domain/score/factory.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,YAAY,EAEZ,KAAK,EAEL,aAAa,EACb,KAAK,EACN,MAAM,gBAAgB,CAAC;AAUxB,MAAM,MAAM,kBAAkB,GAAG,OAAO,CAAC,KAAK,CAAC,GAAG;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC;AAEnE,mFAAmF;AACnF,wBAAgB,WAAW,CAAC,IAAI,EAAE,kBAAkB,GAAG,KAAK,CAc3D;AAwDD,MAAM,MAAM,uBAAuB,GAAG;IACpC,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,aAAa,CAAC,EAAE,aAAa,CAAC;IAC9B,YAAY,CAAC,EAAE,YAAY,CAAC;IAC5B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,GAAG;QAAE,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CACnD,CAAC;AAEF;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,uBAAuB,GAAG,KAAK,CAwCrE;AAED;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,KAAK,GAAG,KAAK,CA0BjD;AAED;;;;;;;GAOG;AACH,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,KAAK,GAAG,KAAK,CA6BvD"}
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
import { createId } from './ids.js';
|
|
2
|
+
import { measureDurationTicks } from '../time/ticks.js';
|
|
3
|
+
const DEFAULT_PPQ = 480;
|
|
4
|
+
const DEFAULT_TEMPO_BPM = 120;
|
|
5
|
+
const DEFAULT_TIME_SIGNATURE = { numerator: 4, denominator: 4 };
|
|
6
|
+
const DEFAULT_KEY_SIGNATURE = { fifths: 0, mode: 'major' };
|
|
7
|
+
const DEFAULT_MEASURE_COUNT = 1;
|
|
8
|
+
const DEFAULT_VOICE_NAME = 'Voice 1';
|
|
9
|
+
/** Builds a `Track` with sensible defaults, overridden by any fields in `opts`. */
|
|
10
|
+
export function createTrack(opts) {
|
|
11
|
+
return {
|
|
12
|
+
id: opts.id ?? createId(),
|
|
13
|
+
name: opts.name,
|
|
14
|
+
instrumentName: opts.instrumentName ?? 'Piano',
|
|
15
|
+
midiProgram: opts.midiProgram ?? 0,
|
|
16
|
+
midiChannel: opts.midiChannel ?? 0,
|
|
17
|
+
clef: opts.clef ?? 'treble',
|
|
18
|
+
volume: opts.volume ?? 1,
|
|
19
|
+
pan: opts.pan ?? 0,
|
|
20
|
+
muted: opts.muted ?? false,
|
|
21
|
+
solo: opts.solo ?? false,
|
|
22
|
+
measures: opts.measures ?? [],
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
/** Builds a single fully-rested measure at the given index/startTick. */
|
|
26
|
+
function buildRestMeasure(index, startTick, timeSignature, keySignature, durationTicks, trackId) {
|
|
27
|
+
const voiceId = createId();
|
|
28
|
+
return {
|
|
29
|
+
id: createId(),
|
|
30
|
+
index,
|
|
31
|
+
startTick,
|
|
32
|
+
durationTicks,
|
|
33
|
+
timeSignature,
|
|
34
|
+
keySignature,
|
|
35
|
+
voices: [
|
|
36
|
+
{
|
|
37
|
+
id: voiceId,
|
|
38
|
+
name: DEFAULT_VOICE_NAME,
|
|
39
|
+
events: [
|
|
40
|
+
{ id: createId(), startTick, durationTicks, voiceId, trackId },
|
|
41
|
+
],
|
|
42
|
+
},
|
|
43
|
+
],
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
/** Builds `count` consecutive fully-rested measures starting at tick 0. */
|
|
47
|
+
function buildMeasures(count, timeSignature, keySignature, ppq, trackId) {
|
|
48
|
+
const durationTicks = measureDurationTicks(timeSignature, ppq);
|
|
49
|
+
const measures = [];
|
|
50
|
+
for (let index = 0; index < count; index += 1) {
|
|
51
|
+
measures.push(buildRestMeasure(index, index * durationTicks, timeSignature, keySignature, durationTicks, trackId));
|
|
52
|
+
}
|
|
53
|
+
return measures;
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Builds a new `Score` with `measures` fully-rested measures on each of
|
|
57
|
+
* `tracks`, one default voice per track per measure. Defaults: 480 PPQ,
|
|
58
|
+
* 120 bpm, 4/4, C major, one measure, one Piano track.
|
|
59
|
+
*/
|
|
60
|
+
export function createEmptyScore(opts) {
|
|
61
|
+
const ppq = opts.ppq ?? DEFAULT_PPQ;
|
|
62
|
+
const timeSignature = opts.timeSignature ?? DEFAULT_TIME_SIGNATURE;
|
|
63
|
+
const keySignature = opts.keySignature ?? DEFAULT_KEY_SIGNATURE;
|
|
64
|
+
const measureCount = opts.measures ?? DEFAULT_MEASURE_COUNT;
|
|
65
|
+
const trackOptions = opts.tracks ?? [
|
|
66
|
+
{ name: 'Piano', instrumentName: 'Piano', clef: 'treble' },
|
|
67
|
+
];
|
|
68
|
+
const tracks = trackOptions.map(trackOpts => {
|
|
69
|
+
const track = createTrack(trackOpts);
|
|
70
|
+
return {
|
|
71
|
+
...track,
|
|
72
|
+
measures: buildMeasures(measureCount, timeSignature, keySignature, ppq, track.id),
|
|
73
|
+
};
|
|
74
|
+
});
|
|
75
|
+
const now = new Date().toISOString();
|
|
76
|
+
const metadata = {
|
|
77
|
+
title: opts.title,
|
|
78
|
+
createdAt: now,
|
|
79
|
+
updatedAt: now,
|
|
80
|
+
};
|
|
81
|
+
return {
|
|
82
|
+
id: createId(),
|
|
83
|
+
version: 1,
|
|
84
|
+
ppq,
|
|
85
|
+
metadata,
|
|
86
|
+
tempoMap: [
|
|
87
|
+
{ id: createId(), tick: 0, bpm: opts.tempo ?? DEFAULT_TEMPO_BPM },
|
|
88
|
+
],
|
|
89
|
+
tracks,
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Appends one fully-rested measure to every track, continuing each track's
|
|
94
|
+
* own tick numbering and reusing its last measure's time/key signature
|
|
95
|
+
* (falling back to 4/4 C major for a track with no existing measures).
|
|
96
|
+
*/
|
|
97
|
+
export function appendMeasure(score) {
|
|
98
|
+
const tracks = score.tracks.map(track => {
|
|
99
|
+
const lastMeasure = track.measures[track.measures.length - 1];
|
|
100
|
+
const timeSignature = lastMeasure?.timeSignature ?? DEFAULT_TIME_SIGNATURE;
|
|
101
|
+
const keySignature = lastMeasure?.keySignature ?? DEFAULT_KEY_SIGNATURE;
|
|
102
|
+
const durationTicks = measureDurationTicks(timeSignature, score.ppq);
|
|
103
|
+
const startTick = lastMeasure
|
|
104
|
+
? lastMeasure.startTick + lastMeasure.durationTicks
|
|
105
|
+
: 0;
|
|
106
|
+
const newMeasure = buildRestMeasure(track.measures.length, startTick, timeSignature, keySignature, durationTicks, track.id);
|
|
107
|
+
return { ...track, measures: [...track.measures, newMeasure] };
|
|
108
|
+
});
|
|
109
|
+
return {
|
|
110
|
+
...score,
|
|
111
|
+
tracks,
|
|
112
|
+
metadata: { ...score.metadata, updatedAt: new Date().toISOString() },
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* Recomputes `index`/`startTick` for every track's measures from their
|
|
117
|
+
* current array order (each measure keeps its own `durationTicks`), and
|
|
118
|
+
* shifts each measure's events by the same delta so they stay positioned
|
|
119
|
+
* consistently within their (possibly moved) measure. Measures whose
|
|
120
|
+
* index/startTick are already consistent are returned unchanged
|
|
121
|
+
* (referentially equal) so unaffected structure is preserved.
|
|
122
|
+
*/
|
|
123
|
+
export function rebuildMeasureTicks(score) {
|
|
124
|
+
const tracks = score.tracks.map(track => {
|
|
125
|
+
let cursor = 0;
|
|
126
|
+
const measures = track.measures.map((measure, index) => {
|
|
127
|
+
const startTick = cursor;
|
|
128
|
+
const delta = startTick - measure.startTick;
|
|
129
|
+
cursor += measure.durationTicks;
|
|
130
|
+
if (delta === 0 && index === measure.index) {
|
|
131
|
+
return measure;
|
|
132
|
+
}
|
|
133
|
+
const voices = delta === 0
|
|
134
|
+
? measure.voices
|
|
135
|
+
: measure.voices.map(voice => ({
|
|
136
|
+
...voice,
|
|
137
|
+
events: voice.events.map(event => ({
|
|
138
|
+
...event,
|
|
139
|
+
startTick: event.startTick + delta,
|
|
140
|
+
})),
|
|
141
|
+
}));
|
|
142
|
+
return { ...measure, index, startTick, voices };
|
|
143
|
+
});
|
|
144
|
+
return { ...track, measures };
|
|
145
|
+
});
|
|
146
|
+
return { ...score, tracks };
|
|
147
|
+
}
|
|
148
|
+
//# sourceMappingURL=factory.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"factory.js","sourceRoot":"","sources":["../../../src/domain/score/factory.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { Score, ScoreFragment } from '../../index.js';
|
|
2
|
+
import type { ScoreRange } from '../selection/types.js';
|
|
3
|
+
/**
|
|
4
|
+
* `ScoreFragment` is canonical in @sudobility/music_types and re-exported
|
|
5
|
+
* here so domain modules keep one import site. A fragment is a snapshot of
|
|
6
|
+
* a tick range of a score (measures shared by reference, not deep-cloned).
|
|
7
|
+
*/
|
|
8
|
+
export type { ScoreFragment } from '../../index.js';
|
|
9
|
+
/** Captures the measures overlapping `range` (per track named in `range.trackIds`, or all tracks if empty). */
|
|
10
|
+
export declare function extractFragment(score: Score, range: ScoreRange): ScoreFragment;
|
|
11
|
+
/**
|
|
12
|
+
* Splices `fragment`'s measures into `score`, one track at a time. For each
|
|
13
|
+
* track named in `fragment.tracks`, the contiguous block of that track's
|
|
14
|
+
* existing measures overlapping `fragment.range` is replaced by the
|
|
15
|
+
* fragment's measures for that track (deep-replacing their voices); tracks
|
|
16
|
+
* with no measures overlapping the range are left untouched. Track ids
|
|
17
|
+
* named in the fragment but absent from the score are ignored.
|
|
18
|
+
*
|
|
19
|
+
* Every replaced event's `trackId`/`voiceId` is renumbered to match its
|
|
20
|
+
* new, actual track and voice (never trusted from the incoming fragment),
|
|
21
|
+
* so a regenerated fragment's placeholder ids never leak into the score.
|
|
22
|
+
*
|
|
23
|
+
* Measure `index`/`startTick` for every track are then recomputed via
|
|
24
|
+
* `rebuildMeasureTicks`, which leaves measures outside the edited region
|
|
25
|
+
* referentially unchanged.
|
|
26
|
+
*/
|
|
27
|
+
export declare function replaceFragment(score: Score, fragment: ScoreFragment): Score;
|
|
28
|
+
//# sourceMappingURL=fragment.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fragment.d.ts","sourceRoot":"","sources":["../../../src/domain/score/fragment.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAW,KAAK,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AACpE,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAC;AAExD;;;;GAIG;AACH,YAAY,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAEpD,+GAA+G;AAC/G,wBAAgB,eAAe,CAC7B,KAAK,EAAE,KAAK,EACZ,KAAK,EAAE,UAAU,GAChB,aAAa,CAMf;AAUD;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,KAAK,EAAE,QAAQ,EAAE,aAAa,GAAG,KAAK,CA6C5E"}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { rebuildMeasureTicks } from './factory.js';
|
|
2
|
+
import { measuresInRange } from './queries.js';
|
|
3
|
+
/** Captures the measures overlapping `range` (per track named in `range.trackIds`, or all tracks if empty). */
|
|
4
|
+
export function extractFragment(score, range) {
|
|
5
|
+
return {
|
|
6
|
+
range,
|
|
7
|
+
ppq: score.ppq,
|
|
8
|
+
tracks: measuresInRange(score, range),
|
|
9
|
+
};
|
|
10
|
+
}
|
|
11
|
+
/** Whether a measure's span overlaps `[range.startTick, range.endTick)`. */
|
|
12
|
+
function overlapsRange(measure, range) {
|
|
13
|
+
return (measure.startTick < range.endTick &&
|
|
14
|
+
measure.startTick + measure.durationTicks > range.startTick);
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Splices `fragment`'s measures into `score`, one track at a time. For each
|
|
18
|
+
* track named in `fragment.tracks`, the contiguous block of that track's
|
|
19
|
+
* existing measures overlapping `fragment.range` is replaced by the
|
|
20
|
+
* fragment's measures for that track (deep-replacing their voices); tracks
|
|
21
|
+
* with no measures overlapping the range are left untouched. Track ids
|
|
22
|
+
* named in the fragment but absent from the score are ignored.
|
|
23
|
+
*
|
|
24
|
+
* Every replaced event's `trackId`/`voiceId` is renumbered to match its
|
|
25
|
+
* new, actual track and voice (never trusted from the incoming fragment),
|
|
26
|
+
* so a regenerated fragment's placeholder ids never leak into the score.
|
|
27
|
+
*
|
|
28
|
+
* Measure `index`/`startTick` for every track are then recomputed via
|
|
29
|
+
* `rebuildMeasureTicks`, which leaves measures outside the edited region
|
|
30
|
+
* referentially unchanged.
|
|
31
|
+
*/
|
|
32
|
+
export function replaceFragment(score, fragment) {
|
|
33
|
+
const replacementsByTrackId = new Map(fragment.tracks.map(t => [t.trackId, t.measures]));
|
|
34
|
+
const tracks = score.tracks.map(track => {
|
|
35
|
+
const replacementMeasures = replacementsByTrackId.get(track.id);
|
|
36
|
+
if (!replacementMeasures)
|
|
37
|
+
return track;
|
|
38
|
+
const firstIndex = track.measures.findIndex(m => overlapsRange(m, fragment.range));
|
|
39
|
+
if (firstIndex === -1)
|
|
40
|
+
return track;
|
|
41
|
+
let lastIndex = firstIndex;
|
|
42
|
+
while (lastIndex < track.measures.length &&
|
|
43
|
+
overlapsRange(track.measures[lastIndex], fragment.range)) {
|
|
44
|
+
lastIndex += 1;
|
|
45
|
+
}
|
|
46
|
+
const normalizedMeasures = replacementMeasures.map(measure => ({
|
|
47
|
+
...measure,
|
|
48
|
+
voices: measure.voices.map(voice => ({
|
|
49
|
+
...voice,
|
|
50
|
+
events: voice.events.map(event => ({
|
|
51
|
+
...event,
|
|
52
|
+
trackId: track.id,
|
|
53
|
+
voiceId: voice.id,
|
|
54
|
+
})),
|
|
55
|
+
})),
|
|
56
|
+
}));
|
|
57
|
+
return {
|
|
58
|
+
...track,
|
|
59
|
+
measures: [
|
|
60
|
+
...track.measures.slice(0, firstIndex),
|
|
61
|
+
...normalizedMeasures,
|
|
62
|
+
...track.measures.slice(lastIndex),
|
|
63
|
+
],
|
|
64
|
+
};
|
|
65
|
+
});
|
|
66
|
+
return rebuildMeasureTicks({ ...score, tracks });
|
|
67
|
+
}
|
|
68
|
+
//# sourceMappingURL=fragment.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fragment.js","sourceRoot":"","sources":["../../../src/domain/score/fragment.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,MAAM,cAAc,CAAC;AACnD,OAAO,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAW/C,+GAA+G;AAC/G,MAAM,UAAU,eAAe,CAC7B,KAAY,EACZ,KAAiB;IAEjB,OAAO;QACL,KAAK;QACL,GAAG,EAAE,KAAK,CAAC,GAAG;QACd,MAAM,EAAE,eAAe,CAAC,KAAK,EAAE,KAAK,CAAC;KACtC,CAAC;AACJ,CAAC;AAED,4EAA4E;AAC5E,SAAS,aAAa,CAAC,OAAgB,EAAE,KAAiB;IACxD,OAAO,CACL,OAAO,CAAC,SAAS,GAAG,KAAK,CAAC,OAAO;QACjC,OAAO,CAAC,SAAS,GAAG,OAAO,CAAC,aAAa,GAAG,KAAK,CAAC,SAAS,CAC5D,CAAC;AACJ,CAAC;AAED;;;;;;;;;;;;;;;GAeG;AACH,MAAM,UAAU,eAAe,CAAC,KAAY,EAAE,QAAuB;IACnE,MAAM,qBAAqB,GAAG,IAAI,GAAG,CACnC,QAAQ,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,CAClD,CAAC;IAEF,MAAM,MAAM,GAAG,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE;QACtC,MAAM,mBAAmB,GAAG,qBAAqB,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;QAChE,IAAI,CAAC,mBAAmB;YAAE,OAAO,KAAK,CAAC;QAEvC,MAAM,UAAU,GAAG,KAAK,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,CAC9C,aAAa,CAAC,CAAC,EAAE,QAAQ,CAAC,KAAK,CAAC,CACjC,CAAC;QACF,IAAI,UAAU,KAAK,CAAC,CAAC;YAAE,OAAO,KAAK,CAAC;QAEpC,IAAI,SAAS,GAAG,UAAU,CAAC;QAC3B,OACE,SAAS,GAAG,KAAK,CAAC,QAAQ,CAAC,MAAM;YACjC,aAAa,CAAC,KAAK,CAAC,QAAQ,CAAC,SAAS,CAAC,EAAE,QAAQ,CAAC,KAAK,CAAC,EACxD,CAAC;YACD,SAAS,IAAI,CAAC,CAAC;QACjB,CAAC;QAED,MAAM,kBAAkB,GAAG,mBAAmB,CAAC,GAAG,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;YAC7D,GAAG,OAAO;YACV,MAAM,EAAE,OAAO,CAAC,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;gBACnC,GAAG,KAAK;gBACR,MAAM,EAAE,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;oBACjC,GAAG,KAAK;oBACR,OAAO,EAAE,KAAK,CAAC,EAAE;oBACjB,OAAO,EAAE,KAAK,CAAC,EAAE;iBAClB,CAAC,CAAC;aACJ,CAAC,CAAC;SACJ,CAAC,CAAC,CAAC;QAEJ,OAAO;YACL,GAAG,KAAK;YACR,QAAQ,EAAE;gBACR,GAAG,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,EAAE,UAAU,CAAC;gBACtC,GAAG,kBAAkB;gBACrB,GAAG,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,SAAS,CAAC;aACnC;SACF,CAAC;IACJ,CAAC,CAAC,CAAC;IAEH,OAAO,mBAAmB,CAAC,EAAE,GAAG,KAAK,EAAE,MAAM,EAAE,CAAC,CAAC;AACnD,CAAC"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { UUID } from '../../index.js';
|
|
2
|
+
/**
|
|
3
|
+
* Generates a v4 UUID. Uses `crypto.randomUUID` when available, falling back
|
|
4
|
+
* to a `Math.random`-based generator in non-secure contexts (e.g. `http://`
|
|
5
|
+
* origins other than `localhost`) where `crypto.randomUUID` is undefined.
|
|
6
|
+
*/
|
|
7
|
+
export declare function createId(): UUID;
|
|
8
|
+
//# sourceMappingURL=ids.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ids.d.ts","sourceRoot":"","sources":["../../../src/domain/score/ids.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,gBAAgB,CAAC;AA0B3C;;;;GAIG;AACH,wBAAgB,QAAQ,IAAI,IAAI,CAQ/B"}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
const HEX_CHARS = '0123456789abcdef';
|
|
2
|
+
/**
|
|
3
|
+
* Fallback v4-UUID generator for non-secure contexts, where
|
|
4
|
+
* `crypto.randomUUID` is unavailable (it requires a secure context per the
|
|
5
|
+
* Web Crypto spec). Not cryptographically strong; adequate for local
|
|
6
|
+
* identifiers only.
|
|
7
|
+
*/
|
|
8
|
+
function randomUuidFallback() {
|
|
9
|
+
let result = '';
|
|
10
|
+
for (let i = 0; i < 36; i += 1) {
|
|
11
|
+
if (i === 8 || i === 13 || i === 18 || i === 23) {
|
|
12
|
+
result += '-';
|
|
13
|
+
}
|
|
14
|
+
else if (i === 14) {
|
|
15
|
+
result += '4';
|
|
16
|
+
}
|
|
17
|
+
else if (i === 19) {
|
|
18
|
+
result += HEX_CHARS[8 + Math.floor(Math.random() * 4)]; // one of 8, 9, a, b
|
|
19
|
+
}
|
|
20
|
+
else {
|
|
21
|
+
result += HEX_CHARS[Math.floor(Math.random() * 16)];
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
return result;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Generates a v4 UUID. Uses `crypto.randomUUID` when available, falling back
|
|
28
|
+
* to a `Math.random`-based generator in non-secure contexts (e.g. `http://`
|
|
29
|
+
* origins other than `localhost`) where `crypto.randomUUID` is undefined.
|
|
30
|
+
*/
|
|
31
|
+
export function createId() {
|
|
32
|
+
if (typeof crypto !== 'undefined' &&
|
|
33
|
+
typeof crypto.randomUUID === 'function') {
|
|
34
|
+
return crypto.randomUUID();
|
|
35
|
+
}
|
|
36
|
+
return randomUuidFallback();
|
|
37
|
+
}
|
|
38
|
+
//# sourceMappingURL=ids.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ids.js","sourceRoot":"","sources":["../../../src/domain/score/ids.ts"],"names":[],"mappings":"AAEA,MAAM,SAAS,GAAG,kBAAkB,CAAC;AAErC;;;;;GAKG;AACH,SAAS,kBAAkB;IACzB,IAAI,MAAM,GAAG,EAAE,CAAC;IAChB,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC;QAC/B,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,EAAE,IAAI,CAAC,KAAK,EAAE,IAAI,CAAC,KAAK,EAAE,EAAE,CAAC;YAChD,MAAM,IAAI,GAAG,CAAC;QAChB,CAAC;aAAM,IAAI,CAAC,KAAK,EAAE,EAAE,CAAC;YACpB,MAAM,IAAI,GAAG,CAAC;QAChB,CAAC;aAAM,IAAI,CAAC,KAAK,EAAE,EAAE,CAAC;YACpB,MAAM,IAAI,SAAS,CAAC,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,oBAAoB;QAC9E,CAAC;aAAM,CAAC;YACN,MAAM,IAAI,SAAS,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,EAAE,GAAG,EAAE,CAAC,CAAC,CAAC;QACtD,CAAC;IACH,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,QAAQ;IACtB,IACE,OAAO,MAAM,KAAK,WAAW;QAC7B,OAAO,MAAM,CAAC,UAAU,KAAK,UAAU,EACvC,CAAC;QACD,OAAO,MAAM,CAAC,UAAU,EAAE,CAAC;IAC7B,CAAC;IACD,OAAO,kBAAkB,EAAE,CAAC;AAC9B,CAAC"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import type { ScoreRange } from '../selection/types.js';
|
|
2
|
+
import type { Measure, MusicalEvent, NoteEvent, Score, Track, UUID } from '../../index.js';
|
|
3
|
+
/** Finds a track by id, or `null` if no track has that id. */
|
|
4
|
+
export declare function findTrack(score: Score, trackId: UUID): Track | null;
|
|
5
|
+
/** Finds a measure by id across all tracks, or `null` if none has that id. */
|
|
6
|
+
export declare function findMeasure(score: Score, measureId: UUID): Measure | null;
|
|
7
|
+
/** Finds an event (note or rest) by id across all tracks/measures/voices, or `null`. */
|
|
8
|
+
export declare function findEvent(score: Score, eventId: UUID): MusicalEvent | null;
|
|
9
|
+
/** All note events (rests excluded) on the requested tracks overlapping the tick range. */
|
|
10
|
+
export declare function eventsInRange(score: Score, range: ScoreRange): NoteEvent[];
|
|
11
|
+
/** Measures overlapping the tick range, grouped by track. */
|
|
12
|
+
export declare function measuresInRange(score: Score, range: ScoreRange): Array<{
|
|
13
|
+
trackId: UUID;
|
|
14
|
+
measures: Measure[];
|
|
15
|
+
}>;
|
|
16
|
+
/**
|
|
17
|
+
* Finds a note event on `trackId` whose span covers `tick`
|
|
18
|
+
* (`startTick <= tick < startTick + durationTicks`), optionally also
|
|
19
|
+
* matching a specific MIDI pitch. Returns `null` if none match.
|
|
20
|
+
*/
|
|
21
|
+
export declare function noteAt(score: Score, trackId: UUID, tick: number, midi?: number): NoteEvent | null;
|
|
22
|
+
/** The tick at which the score's last measure (across all tracks) ends; 0 if there are none. */
|
|
23
|
+
export declare function scoreEndTick(score: Score): number;
|
|
24
|
+
/** All note events (rests excluded) across every track/measure/voice. */
|
|
25
|
+
export declare function allNotes(score: Score): NoteEvent[];
|
|
26
|
+
/**
|
|
27
|
+
* `score` with only the named tracks, in score order.
|
|
28
|
+
*
|
|
29
|
+
* Returns `score` itself when `trackIds` names every track, so the common
|
|
30
|
+
* "nothing is hidden" export path costs nothing. Kept tracks are returned by
|
|
31
|
+
* reference — this filters, it never rewrites the music.
|
|
32
|
+
*/
|
|
33
|
+
export declare function scoreWithTracks(score: Score, trackIds: string[]): Score;
|
|
34
|
+
//# sourceMappingURL=queries.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"queries.d.ts","sourceRoot":"","sources":["../../../src/domain/score/queries.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAC;AACxD,OAAO,KAAK,EACV,OAAO,EACP,YAAY,EACZ,SAAS,EACT,KAAK,EACL,KAAK,EACL,IAAI,EACL,MAAM,gBAAgB,CAAC;AAIxB,8DAA8D;AAC9D,wBAAgB,SAAS,CAAC,KAAK,EAAE,KAAK,EAAE,OAAO,EAAE,IAAI,GAAG,KAAK,GAAG,IAAI,CAEnE;AAED,8EAA8E;AAC9E,wBAAgB,WAAW,CAAC,KAAK,EAAE,KAAK,EAAE,SAAS,EAAE,IAAI,GAAG,OAAO,GAAG,IAAI,CAMzE;AAED,wFAAwF;AACxF,wBAAgB,SAAS,CAAC,KAAK,EAAE,KAAK,EAAE,OAAO,EAAE,IAAI,GAAG,YAAY,GAAG,IAAI,CAU1E;AAkBD,2FAA2F;AAC3F,wBAAgB,aAAa,CAAC,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,UAAU,GAAG,SAAS,EAAE,CAkB1E;AAED,6DAA6D;AAC7D,wBAAgB,eAAe,CAC7B,KAAK,EAAE,KAAK,EACZ,KAAK,EAAE,UAAU,GAChB,KAAK,CAAC;IAAE,OAAO,EAAE,IAAI,CAAC;IAAC,QAAQ,EAAE,OAAO,EAAE,CAAA;CAAE,CAAC,CAU/C;AAED;;;;GAIG;AACH,wBAAgB,MAAM,CACpB,KAAK,EAAE,KAAK,EACZ,OAAO,EAAE,IAAI,EACb,IAAI,EAAE,MAAM,EACZ,IAAI,CAAC,EAAE,MAAM,GACZ,SAAS,GAAG,IAAI,CAmBlB;AAED,gGAAgG;AAChG,wBAAgB,YAAY,CAAC,KAAK,EAAE,KAAK,GAAG,MAAM,CASjD;AAED,yEAAyE;AACzE,wBAAgB,QAAQ,CAAC,KAAK,EAAE,KAAK,GAAG,SAAS,EAAE,CAYlD;AAED;;;;;;GAMG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,KAAK,EAAE,QAAQ,EAAE,MAAM,EAAE,GAAG,KAAK,CAKvE"}
|