@sudobility/music_types 0.11.30 → 0.11.31
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/domain/commands/edit-commands.d.ts +3 -3
- package/dist/domain/commands/edit-commands.d.ts.map +1 -1
- package/dist/domain/commands/edit-commands.js +25 -25
- package/dist/domain/commands/edit-commands.js.map +1 -1
- package/dist/domain/commands/history.d.ts +32 -0
- package/dist/domain/commands/history.d.ts.map +1 -0
- package/dist/domain/commands/history.js +63 -0
- package/dist/domain/commands/history.js.map +1 -0
- package/dist/domain/commands/note-commands.d.ts +147 -3
- package/dist/domain/commands/note-commands.d.ts.map +1 -1
- package/dist/domain/commands/note-commands.js +438 -46
- package/dist/domain/commands/note-commands.js.map +1 -1
- package/dist/domain/commands/reflow.d.ts +1 -1
- package/dist/domain/commands/reflow.js +17 -17
- package/dist/domain/commands/reflow.js.map +1 -1
- package/dist/domain/commands/region-commands.d.ts +4 -4
- package/dist/domain/commands/region-commands.d.ts.map +1 -1
- package/dist/domain/commands/region-commands.js +7 -7
- package/dist/domain/commands/region-commands.js.map +1 -1
- package/dist/domain/commands/relocate-commands.d.ts +3 -3
- package/dist/domain/commands/relocate-commands.js +15 -15
- package/dist/domain/commands/relocate-commands.js.map +1 -1
- package/dist/domain/commands/ripple-commands.d.ts +2 -2
- package/dist/domain/commands/ripple-commands.js +13 -13
- package/dist/domain/commands/ripple-commands.js.map +1 -1
- package/dist/domain/commands/snapshot.d.ts +3 -3
- package/dist/domain/commands/snapshot.js +5 -5
- package/dist/domain/commands/snapshot.js.map +1 -1
- package/dist/domain/commands/structure-commands.d.ts +86 -5
- package/dist/domain/commands/structure-commands.d.ts.map +1 -1
- package/dist/domain/commands/structure-commands.js +272 -36
- package/dist/domain/commands/structure-commands.js.map +1 -1
- package/dist/domain/commands/track-commands.d.ts +2 -2
- package/dist/domain/commands/track-commands.js +7 -7
- package/dist/domain/commands/track-commands.js.map +1 -1
- package/dist/domain/commands/types.d.ts +2 -2
- package/dist/domain/generation/replacement-region.d.ts +32 -0
- package/dist/domain/generation/replacement-region.d.ts.map +1 -0
- package/dist/domain/generation/replacement-region.js +79 -0
- package/dist/domain/generation/replacement-region.js.map +1 -0
- package/dist/domain/instruments/gm-icon.d.ts +12 -0
- package/dist/domain/instruments/gm-icon.d.ts.map +1 -0
- package/dist/domain/instruments/gm-icon.js +327 -0
- package/dist/domain/instruments/gm-icon.js.map +1 -0
- package/dist/domain/instruments/gm-kit.js +9 -9
- package/dist/domain/instruments/gm-kit.js.map +1 -1
- package/dist/domain/instruments/gm-percussion.js +48 -48
- package/dist/domain/instruments/gm-percussion.js.map +1 -1
- package/dist/domain/instruments/gm-polyphony.js +6 -6
- package/dist/domain/instruments/gm-range.js +6 -6
- package/dist/domain/instruments/gm-transposition.js +1 -1
- package/dist/domain/instruments/gm.d.ts +1 -1
- package/dist/domain/instruments/gm.js +161 -161
- package/dist/domain/instruments/gm.js.map +1 -1
- package/dist/domain/instruments/icon-art.d.ts +75 -0
- package/dist/domain/instruments/icon-art.d.ts.map +1 -0
- package/dist/domain/instruments/icon-art.js +93 -0
- package/dist/domain/instruments/icon-art.js.map +1 -0
- package/dist/domain/instruments/instrument-fit.d.ts +31 -0
- package/dist/domain/instruments/instrument-fit.d.ts.map +1 -0
- package/dist/domain/instruments/instrument-fit.js +117 -0
- package/dist/domain/instruments/instrument-fit.js.map +1 -0
- package/dist/domain/instruments/instrument-options.d.ts +62 -0
- package/dist/domain/instruments/instrument-options.d.ts.map +1 -0
- package/dist/domain/instruments/instrument-options.js +69 -0
- package/dist/domain/instruments/instrument-options.js.map +1 -0
- package/dist/domain/instruments/track-instrument.d.ts +80 -0
- package/dist/domain/instruments/track-instrument.d.ts.map +1 -0
- package/dist/domain/instruments/track-instrument.js +102 -0
- package/dist/domain/instruments/track-instrument.js.map +1 -0
- package/dist/domain/notation/chord-symbol.d.ts +26 -0
- package/dist/domain/notation/chord-symbol.d.ts.map +1 -0
- package/dist/domain/notation/chord-symbol.js +32 -0
- package/dist/domain/notation/chord-symbol.js.map +1 -0
- package/dist/domain/notation/lyric-syllables.d.ts +15 -0
- package/dist/domain/notation/lyric-syllables.d.ts.map +1 -0
- package/dist/domain/notation/lyric-syllables.js +15 -0
- package/dist/domain/notation/lyric-syllables.js.map +1 -0
- package/dist/domain/notation/music-vocabulary.d.ts +59 -0
- package/dist/domain/notation/music-vocabulary.d.ts.map +1 -0
- package/dist/domain/notation/music-vocabulary.js +164 -0
- package/dist/domain/notation/music-vocabulary.js.map +1 -0
- package/dist/domain/pitch/pitch.d.ts +1 -1
- package/dist/domain/pitch/pitch.js +29 -29
- package/dist/domain/pitch/transpose.d.ts +1 -1
- package/dist/domain/pitch/transpose.js +2 -2
- package/dist/domain/quantization/quantize.d.ts +2 -2
- package/dist/domain/quantization/quantize.js +9 -9
- package/dist/domain/quantization/quantize.js.map +1 -1
- package/dist/domain/score/articulation.d.ts +56 -0
- package/dist/domain/score/articulation.d.ts.map +1 -0
- package/dist/domain/score/articulation.js +56 -0
- package/dist/domain/score/articulation.js.map +1 -0
- package/dist/domain/score/bar-numbers.d.ts +38 -0
- package/dist/domain/score/bar-numbers.d.ts.map +1 -0
- package/dist/domain/score/bar-numbers.js +49 -0
- package/dist/domain/score/bar-numbers.js.map +1 -0
- package/dist/domain/score/collapse-rests.d.ts +23 -0
- package/dist/domain/score/collapse-rests.d.ts.map +1 -0
- package/dist/domain/score/collapse-rests.js +78 -0
- package/dist/domain/score/collapse-rests.js.map +1 -0
- package/dist/domain/score/cue-notes.d.ts +24 -0
- package/dist/domain/score/cue-notes.d.ts.map +1 -0
- package/dist/domain/score/cue-notes.js +105 -0
- package/dist/domain/score/cue-notes.js.map +1 -0
- package/dist/domain/score/dynamics.d.ts +62 -0
- package/dist/domain/score/dynamics.d.ts.map +1 -0
- package/dist/domain/score/dynamics.js +142 -0
- package/dist/domain/score/dynamics.js.map +1 -0
- package/dist/domain/score/effective-clef.d.ts +42 -0
- package/dist/domain/score/effective-clef.d.ts.map +1 -0
- package/dist/domain/score/effective-clef.js +39 -0
- package/dist/domain/score/effective-clef.js.map +1 -0
- package/dist/domain/score/extract-part.d.ts +10 -0
- package/dist/domain/score/extract-part.d.ts.map +1 -0
- package/dist/domain/score/extract-part.js +44 -0
- package/dist/domain/score/extract-part.js.map +1 -0
- package/dist/domain/score/factory.d.ts +1 -1
- package/dist/domain/score/factory.js +12 -12
- package/dist/domain/score/factory.js.map +1 -1
- package/dist/domain/score/fermata-tempo.d.ts +40 -0
- package/dist/domain/score/fermata-tempo.d.ts.map +1 -0
- package/dist/domain/score/fermata-tempo.js +95 -0
- package/dist/domain/score/fermata-tempo.js.map +1 -0
- package/dist/domain/score/flatten.d.ts +32 -0
- package/dist/domain/score/flatten.d.ts.map +1 -0
- package/dist/domain/score/flatten.js +132 -0
- package/dist/domain/score/flatten.js.map +1 -0
- package/dist/domain/score/fragment.d.ts +3 -3
- package/dist/domain/score/fragment.js +8 -8
- package/dist/domain/score/fragment.js.map +1 -1
- package/dist/domain/score/ids.d.ts +1 -1
- package/dist/domain/score/ids.js +6 -6
- package/dist/domain/score/ottava.d.ts +41 -0
- package/dist/domain/score/ottava.d.ts.map +1 -0
- package/dist/domain/score/ottava.js +98 -0
- package/dist/domain/score/ottava.js.map +1 -0
- package/dist/domain/score/performance-timeline.d.ts +42 -0
- package/dist/domain/score/performance-timeline.d.ts.map +1 -0
- package/dist/domain/score/performance-timeline.js +78 -0
- package/dist/domain/score/performance-timeline.js.map +1 -0
- package/dist/domain/score/queries.d.ts +2 -2
- package/dist/domain/score/queries.js +7 -7
- package/dist/domain/score/queries.js.map +1 -1
- package/dist/domain/score/rehearsal-marks.d.ts +30 -0
- package/dist/domain/score/rehearsal-marks.d.ts.map +1 -0
- package/dist/domain/score/rehearsal-marks.js +126 -0
- package/dist/domain/score/rehearsal-marks.js.map +1 -0
- package/dist/domain/score/repeat-order.d.ts +21 -0
- package/dist/domain/score/repeat-order.d.ts.map +1 -0
- package/dist/domain/score/repeat-order.js +123 -0
- package/dist/domain/score/repeat-order.js.map +1 -0
- package/dist/domain/score/snapshot-tree.d.ts +23 -0
- package/dist/domain/score/snapshot-tree.d.ts.map +1 -0
- package/dist/domain/score/snapshot-tree.js +45 -0
- package/dist/domain/score/snapshot-tree.js.map +1 -0
- package/dist/domain/score/ties.d.ts +1 -1
- package/dist/domain/score/ties.js +7 -7
- package/dist/domain/score/ties.js.map +1 -1
- package/dist/domain/score/written-pitch.d.ts +33 -0
- package/dist/domain/score/written-pitch.d.ts.map +1 -0
- package/dist/domain/score/written-pitch.js +110 -0
- package/dist/domain/score/written-pitch.js.map +1 -0
- package/dist/domain/selection/range-select.d.ts +13 -0
- package/dist/domain/selection/range-select.d.ts.map +1 -0
- package/dist/domain/selection/range-select.js +40 -0
- package/dist/domain/selection/range-select.js.map +1 -0
- package/dist/domain/selection/selection-editing.d.ts +30 -0
- package/dist/domain/selection/selection-editing.d.ts.map +1 -0
- package/dist/domain/selection/selection-editing.js +31 -0
- package/dist/domain/selection/selection-editing.js.map +1 -0
- package/dist/domain/selection/selection.d.ts +2 -2
- package/dist/domain/selection/selection.js +7 -7
- package/dist/domain/selection/selection.js.map +1 -1
- package/dist/domain/selection/types.d.ts +2 -2
- package/dist/domain/time/duration-modifiers.d.ts +40 -0
- package/dist/domain/time/duration-modifiers.d.ts.map +1 -0
- package/dist/domain/time/duration-modifiers.js +50 -0
- package/dist/domain/time/duration-modifiers.js.map +1 -0
- package/dist/domain/time/duration-selection.d.ts +23 -0
- package/dist/domain/time/duration-selection.d.ts.map +1 -0
- package/dist/domain/time/duration-selection.js +50 -0
- package/dist/domain/time/duration-selection.js.map +1 -0
- package/dist/domain/time/durations.js +6 -6
- package/dist/domain/time/durations.js.map +1 -1
- package/dist/domain/time/fraction.d.ts +1 -1
- package/dist/domain/time/fraction.js +1 -1
- package/dist/domain/time/tap-to-note.d.ts +21 -0
- package/dist/domain/time/tap-to-note.d.ts.map +1 -0
- package/dist/domain/time/tap-to-note.js +43 -0
- package/dist/domain/time/tap-to-note.js.map +1 -0
- package/dist/domain/time/tempo-map.d.ts +1 -1
- package/dist/domain/time/ticks.d.ts +1 -1
- package/dist/domain/time/ticks.d.ts.map +1 -1
- package/dist/domain/time/ticks.js +14 -14
- package/dist/domain/time/ticks.js.map +1 -1
- package/dist/domain/time/tuplets.d.ts +42 -0
- package/dist/domain/time/tuplets.d.ts.map +1 -0
- package/dist/domain/time/tuplets.js +53 -0
- package/dist/domain/time/tuplets.js.map +1 -0
- package/dist/domain/validation/issues.d.ts +1 -1
- package/dist/domain/validation/issues.js +20 -20
- package/dist/domain/validation/validator.d.ts +2 -2
- package/dist/domain/validation/validator.d.ts.map +1 -1
- package/dist/domain/validation/validator.js +31 -31
- package/dist/domain/validation/validator.js.map +1 -1
- package/dist/domain/voicing/allocate.d.ts +2 -2
- package/dist/domain/voicing/allocate.js +6 -6
- package/dist/domain/voicing/allocate.js.map +1 -1
- package/dist/index.d.ts +90 -59
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +151 -83
- package/dist/index.js.map +1 -1
- package/dist/platform/audio.d.ts.map +1 -1
- package/dist/platform/file.d.ts.map +1 -1
- package/dist/platform/index.d.ts +7 -7
- package/dist/platform/index.js +7 -7
- package/dist/platform/midi-input.d.ts +2 -2
- package/dist/platform/midi.d.ts.map +1 -1
- package/dist/platform/mod.d.ts +2 -2
- package/dist/platform/playback.d.ts +7 -7
- package/dist/platform/xml.js +1 -1
- package/dist/services/regeneration/controller.d.ts +6 -6
- package/dist/services/regeneration/controller.js +12 -12
- package/dist/services/regeneration/controller.js.map +1 -1
- package/dist/test/fixtures.d.ts +1 -1
- package/dist/test/fixtures.js +103 -103
- package/dist/test/fixtures.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import { gmInstrumentRange } from "./gm-range.js";
|
|
2
|
+
import { gmMaxPolyphony, UNLIMITED_POLYPHONY } from "./gm-polyphony.js";
|
|
3
|
+
import { gmWrittenTransposition } from "./gm-transposition.js";
|
|
4
|
+
import { gmKit, gmKitAt } from "./gm-kit.js";
|
|
5
|
+
import { GM_PERCUSSION_RANGE } from "./gm-percussion.js";
|
|
6
|
+
import { gmInstrumentIcon, gmKitIcon } from "./gm-icon.js";
|
|
7
|
+
import { gmInstrument } from "./gm.js";
|
|
8
|
+
/** Whether `track`'s `midiProgram` addresses a drum kit rather than an instrument. */
|
|
9
|
+
export function isPercussionTrack(track) {
|
|
10
|
+
return track.clef === "percussion";
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* The notes worth showing for `track` — General MIDI's drum range on a
|
|
14
|
+
* percussion track, the instrument's compass otherwise.
|
|
15
|
+
*/
|
|
16
|
+
export function trackKeyboardRange(track) {
|
|
17
|
+
if (isPercussionTrack(track))
|
|
18
|
+
return { ...GM_PERCUSSION_RANGE };
|
|
19
|
+
return gmInstrumentRange(track.midiProgram);
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* How many notes `track` can sound at once.
|
|
23
|
+
*
|
|
24
|
+
* Unlimited on a drum track: the limit exists to stop a part being written that
|
|
25
|
+
* nobody could play, and a kit is played with two hands and two feet across
|
|
26
|
+
* pieces that are all struck separately. Nothing about a kit address says
|
|
27
|
+
* otherwise.
|
|
28
|
+
*/
|
|
29
|
+
export function trackMaxPolyphony(track) {
|
|
30
|
+
if (isPercussionTrack(track))
|
|
31
|
+
return UNLIMITED_POLYPHONY;
|
|
32
|
+
return gmMaxPolyphony(track.midiProgram);
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Semitones between `track`'s sounding and written pitch.
|
|
36
|
+
*
|
|
37
|
+
* Always zero on a drum track. A percussion staff's positions name drums, so
|
|
38
|
+
* transposing one does not move a part into a reader's key — it renames every
|
|
39
|
+
* drum in it.
|
|
40
|
+
*/
|
|
41
|
+
export function trackWrittenTransposition(track) {
|
|
42
|
+
if (isPercussionTrack(track))
|
|
43
|
+
return 0;
|
|
44
|
+
return gmWrittenTransposition(track.midiProgram);
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* `track`'s program, corrected to something its clef can actually mean.
|
|
48
|
+
*
|
|
49
|
+
* On a percussion track that is the kit whose region the address falls in; a
|
|
50
|
+
* MIDI file is free to set program 45 on channel 10, and the kit that resolves
|
|
51
|
+
* to is Brush. Off a percussion track the program is already an instrument and
|
|
52
|
+
* is returned untouched.
|
|
53
|
+
*/
|
|
54
|
+
export function trackProgramForClef(track) {
|
|
55
|
+
return isPercussionTrack(track)
|
|
56
|
+
? gmKitAt(track.midiProgram).program
|
|
57
|
+
: track.midiProgram;
|
|
58
|
+
}
|
|
59
|
+
/** The icon for `track` — a kit on a percussion track, its instrument's art otherwise. */
|
|
60
|
+
export function trackInstrumentIcon(track) {
|
|
61
|
+
return isPercussionTrack(track)
|
|
62
|
+
? gmKitIcon()
|
|
63
|
+
: gmInstrumentIcon(track.midiProgram);
|
|
64
|
+
}
|
|
65
|
+
/** What to call `track`'s sound: its kit's name on a percussion track, its instrument's otherwise. */
|
|
66
|
+
export function trackInstrumentLabel(track) {
|
|
67
|
+
if (isPercussionTrack(track))
|
|
68
|
+
return gmKitAt(track.midiProgram).name;
|
|
69
|
+
return gmInstrument(track.midiProgram)?.name ?? "Instrument";
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* `score` with every percussion track's program resolved to a real kit.
|
|
73
|
+
*
|
|
74
|
+
* A drum track can arrive holding an address no kit sits at: a MIDI file sets
|
|
75
|
+
* whatever program it likes on channel 10, and nothing before this treated that
|
|
76
|
+
* number as a kit at all. The picker would then have to show something the
|
|
77
|
+
* score does not say, so the score is corrected instead of the display fudged.
|
|
78
|
+
*
|
|
79
|
+
* A track already on a kit address is returned untouched — including its
|
|
80
|
+
* `instrumentName`, which is the user's description of a sound that is not
|
|
81
|
+
* changing. Only a track being corrected has its name rewritten, because the
|
|
82
|
+
* old one described a different kit.
|
|
83
|
+
*
|
|
84
|
+
* Returns the **identical object** when nothing needs correcting, which is the
|
|
85
|
+
* usual case: `computeLayout` is cached by score identity, and a fresh object
|
|
86
|
+
* on every load would also read as a change to the autosaver.
|
|
87
|
+
*/
|
|
88
|
+
export function scoreWithResolvedKits(score) {
|
|
89
|
+
const needsWork = score.tracks.some((track) => isPercussionTrack(track) && gmKit(track.midiProgram) === null);
|
|
90
|
+
if (!needsWork)
|
|
91
|
+
return score;
|
|
92
|
+
return {
|
|
93
|
+
...score,
|
|
94
|
+
tracks: score.tracks.map((track) => {
|
|
95
|
+
if (!isPercussionTrack(track) || gmKit(track.midiProgram) !== null)
|
|
96
|
+
return track;
|
|
97
|
+
const kit = gmKitAt(track.midiProgram);
|
|
98
|
+
return { ...track, midiProgram: kit.program, instrumentName: kit.name };
|
|
99
|
+
}),
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
//# sourceMappingURL=track-instrument.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"track-instrument.js","sourceRoot":"","sources":["../../../src/domain/instruments/track-instrument.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Splitting a chord symbol into the parts MusicXML wants.
|
|
3
|
+
*
|
|
4
|
+
* The model stores the symbol as typed, because a lead sheet's vocabulary is
|
|
5
|
+
* wide and inconsistent and nothing a player writes should be refused. But
|
|
6
|
+
* MusicXML's `<harmony>` wants a `<root-step>` and an `<alter>` — so the root
|
|
7
|
+
* has to come out of the string for export, and only the root.
|
|
8
|
+
*
|
|
9
|
+
* The rest is carried verbatim as the kind's display text with `kind="other"`.
|
|
10
|
+
* That is a deliberate refusal to guess: mapping `-7`, `min7`, `m7`, `mi7` and
|
|
11
|
+
* `−7` onto MusicXML's enumeration is a dictionary that is wrong for somebody,
|
|
12
|
+
* and a symbol that round-trips as the exact text it was typed as is worth
|
|
13
|
+
* more here than one classified into a taxonomy. A reader that understands
|
|
14
|
+
* only the enumeration still sees the right root and the right printed text.
|
|
15
|
+
*/
|
|
16
|
+
/** What a chord symbol decomposes into. `null` when it does not start on a note letter. */
|
|
17
|
+
export type ParsedChordSymbol = {
|
|
18
|
+
/** A–G. */
|
|
19
|
+
step: string;
|
|
20
|
+
/** -1 for flat, 1 for sharp, 0 for neither. */
|
|
21
|
+
alter: number;
|
|
22
|
+
/** Everything after the root, e.g. `maj7` or `-7/Bb`. May be empty. */
|
|
23
|
+
quality: string;
|
|
24
|
+
};
|
|
25
|
+
export declare function parseChordSymbol(symbol: string): ParsedChordSymbol | null;
|
|
26
|
+
//# sourceMappingURL=chord-symbol.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"chord-symbol.d.ts","sourceRoot":"","sources":["../../../src/domain/notation/chord-symbol.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,2FAA2F;AAC3F,MAAM,MAAM,iBAAiB,GAAG;IAC9B,WAAW;IACX,IAAI,EAAE,MAAM,CAAC;IACb,+CAA+C;IAC/C,KAAK,EAAE,MAAM,CAAC;IACd,uEAAuE;IACvE,OAAO,EAAE,MAAM,CAAC;CACjB,CAAC;AAIF,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,MAAM,GAAG,iBAAiB,GAAG,IAAI,CAezE"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Splitting a chord symbol into the parts MusicXML wants.
|
|
3
|
+
*
|
|
4
|
+
* The model stores the symbol as typed, because a lead sheet's vocabulary is
|
|
5
|
+
* wide and inconsistent and nothing a player writes should be refused. But
|
|
6
|
+
* MusicXML's `<harmony>` wants a `<root-step>` and an `<alter>` — so the root
|
|
7
|
+
* has to come out of the string for export, and only the root.
|
|
8
|
+
*
|
|
9
|
+
* The rest is carried verbatim as the kind's display text with `kind="other"`.
|
|
10
|
+
* That is a deliberate refusal to guess: mapping `-7`, `min7`, `m7`, `mi7` and
|
|
11
|
+
* `−7` onto MusicXML's enumeration is a dictionary that is wrong for somebody,
|
|
12
|
+
* and a symbol that round-trips as the exact text it was typed as is worth
|
|
13
|
+
* more here than one classified into a taxonomy. A reader that understands
|
|
14
|
+
* only the enumeration still sees the right root and the right printed text.
|
|
15
|
+
*/
|
|
16
|
+
const ROOT = /^([A-G])([#b♯♭]?)(.*)$/;
|
|
17
|
+
export function parseChordSymbol(symbol) {
|
|
18
|
+
const match = ROOT.exec(symbol.trim());
|
|
19
|
+
if (!match)
|
|
20
|
+
return null;
|
|
21
|
+
const [, step, accidental, quality] = match;
|
|
22
|
+
return {
|
|
23
|
+
step,
|
|
24
|
+
alter: accidental === "#" || accidental === "♯"
|
|
25
|
+
? 1
|
|
26
|
+
: accidental === "b" || accidental === "♭"
|
|
27
|
+
? -1
|
|
28
|
+
: 0,
|
|
29
|
+
quality: quality.trim(),
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
//# sourceMappingURL=chord-symbol.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"chord-symbol.js","sourceRoot":"","sources":["../../../src/domain/notation/chord-symbol.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAYH,MAAM,IAAI,GAAG,wBAAwB,CAAC;AAEtC,MAAM,UAAU,gBAAgB,CAAC,MAAc;IAC7C,MAAM,KAAK,GAAG,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC,CAAC;IACvC,IAAI,CAAC,KAAK;QAAE,OAAO,IAAI,CAAC;IAExB,MAAM,CAAC,EAAE,IAAI,EAAE,UAAU,EAAE,OAAO,CAAC,GAAG,KAAK,CAAC;IAC5C,OAAO;QACL,IAAI;QACJ,KAAK,EACH,UAAU,KAAK,GAAG,IAAI,UAAU,KAAK,GAAG;YACtC,CAAC,CAAC,CAAC;YACH,CAAC,CAAC,UAAU,KAAK,GAAG,IAAI,UAAU,KAAK,GAAG;gBACxC,CAAC,CAAC,CAAC,CAAC;gBACJ,CAAC,CAAC,CAAC;QACT,OAAO,EAAE,OAAO,CAAC,IAAI,EAAE;KACxB,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* How a typed syllable joins its neighbours.
|
|
3
|
+
*
|
|
4
|
+
* Its own module rather than living beside the entry bar: it is a pure rule
|
|
5
|
+
* about words, the entry bar is a component, and keeping them together costs
|
|
6
|
+
* that file its fast refresh.
|
|
7
|
+
*/
|
|
8
|
+
import type { Syllabic } from "../../index.js";
|
|
9
|
+
/**
|
|
10
|
+
* Derived from whether the writer hyphenated this syllable and whether they
|
|
11
|
+
* hyphenated the one before — a writer knows they are in the middle of
|
|
12
|
+
* "beau-ti-ful" and should not also have to say so.
|
|
13
|
+
*/
|
|
14
|
+
export declare function syllabicFor(previousHyphenated: boolean, hyphenated: boolean): Syllabic;
|
|
15
|
+
//# sourceMappingURL=lyric-syllables.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"lyric-syllables.d.ts","sourceRoot":"","sources":["../../../src/domain/notation/lyric-syllables.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAE/C;;;;GAIG;AACH,wBAAgB,WAAW,CACzB,kBAAkB,EAAE,OAAO,EAC3B,UAAU,EAAE,OAAO,GAClB,QAAQ,CAKV"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Derived from whether the writer hyphenated this syllable and whether they
|
|
3
|
+
* hyphenated the one before — a writer knows they are in the middle of
|
|
4
|
+
* "beau-ti-ful" and should not also have to say so.
|
|
5
|
+
*/
|
|
6
|
+
export function syllabicFor(previousHyphenated, hyphenated) {
|
|
7
|
+
if (previousHyphenated && hyphenated)
|
|
8
|
+
return "middle";
|
|
9
|
+
if (previousHyphenated)
|
|
10
|
+
return "end";
|
|
11
|
+
if (hyphenated)
|
|
12
|
+
return "begin";
|
|
13
|
+
return "single";
|
|
14
|
+
}
|
|
15
|
+
//# sourceMappingURL=lyric-syllables.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"lyric-syllables.js","sourceRoot":"","sources":["../../../src/domain/notation/lyric-syllables.ts"],"names":[],"mappings":"AASA;;;;GAIG;AACH,MAAM,UAAU,WAAW,CACzB,kBAA2B,EAC3B,UAAmB;IAEnB,IAAI,kBAAkB,IAAI,UAAU;QAAE,OAAO,QAAQ,CAAC;IACtD,IAAI,kBAAkB;QAAE,OAAO,KAAK,CAAC;IACrC,IAAI,UAAU;QAAE,OAAO,OAAO,CAAC;IAC/B,OAAO,QAAQ,CAAC;AAClB,CAAC"}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Saying score values the way a musician says them.
|
|
3
|
+
*
|
|
4
|
+
* A score stores ticks, fifths and zero-based voice indexes because those are
|
|
5
|
+
* the right things to compute with. None of them is the right thing to *show*:
|
|
6
|
+
* a property panel that reads "Duration 480" and "Key (fifths) 2" is stating
|
|
7
|
+
* the storage format, and the reader has to convert in their head to know they
|
|
8
|
+
* are looking at a quarter note in D major.
|
|
9
|
+
*
|
|
10
|
+
* These are the conversions, kept in one place so every surface agrees — and
|
|
11
|
+
* kept here rather than in an app, because which tick count is a quarter note
|
|
12
|
+
* and how many sharps D major has are facts about music, not about a panel.
|
|
13
|
+
*
|
|
14
|
+
* No user-facing prose lives here: note values and key names are the terms of
|
|
15
|
+
* the domain, fixed across locales the way General MIDI's instrument names are.
|
|
16
|
+
*/
|
|
17
|
+
import type { Clef, DurationName, KeySignature, Pitch, Score } from "../../index.js";
|
|
18
|
+
/** Every duration name, longest first, so a picker reads top to bottom. */
|
|
19
|
+
export declare const DURATION_NAMES: DurationName[];
|
|
20
|
+
/** What to call `name` in a picker. */
|
|
21
|
+
export declare function durationLabel(name: DurationName): string;
|
|
22
|
+
/** The number of sharps or flats a signature carries, as a phrase. */
|
|
23
|
+
export declare function accidentalCountLabel(fifths: number): string;
|
|
24
|
+
/** e.g. `D major`. Falls back to the fifths count for a signature outside ±7. */
|
|
25
|
+
export declare function keySignatureName(key: KeySignature): string;
|
|
26
|
+
/** Every key a picker should offer, from seven flats to seven sharps. */
|
|
27
|
+
export declare function keySignatureOptions(mode: KeySignature["mode"]): Array<{
|
|
28
|
+
fifths: number;
|
|
29
|
+
label: string;
|
|
30
|
+
}>;
|
|
31
|
+
/**
|
|
32
|
+
* Where `tick` falls, counted the way a player counts: bar 1 upwards, beat 1
|
|
33
|
+
* upwards, with a fraction when it lands between beats.
|
|
34
|
+
*
|
|
35
|
+
* Read off the first track, since every track shares the measure grid once
|
|
36
|
+
* `rebuildMeasureTicks` has run. `null` when the score has no measures, or the
|
|
37
|
+
* tick lies past the end of them.
|
|
38
|
+
*/
|
|
39
|
+
export type BarBeat = {
|
|
40
|
+
bar: number;
|
|
41
|
+
beat: number;
|
|
42
|
+
};
|
|
43
|
+
export declare function barBeatForTick(score: Score, tick: number): BarBeat | null;
|
|
44
|
+
/**
|
|
45
|
+
* The tick at `bar`/`beat`, both counted from 1. `null` when that bar does not
|
|
46
|
+
* exist; a beat past the end of its bar is clamped into it, because a typed
|
|
47
|
+
* "beat 9" in 4/4 means the end of the bar rather than an error.
|
|
48
|
+
*/
|
|
49
|
+
export declare function tickForBarBeat(score: Score, bar: number, beat: number): number | null;
|
|
50
|
+
/**
|
|
51
|
+
* The pitch at a staff position, counted in half-spaces below the top line.
|
|
52
|
+
*
|
|
53
|
+
* `0` is the top line itself, `1` the space beneath it, `2` the next line down,
|
|
54
|
+
* and negative values are ledger positions above. Diatonic throughout, because
|
|
55
|
+
* a staff position names a letter rather than a semitone — what makes it an F♯
|
|
56
|
+
* rather than an F is the key signature, which is applied elsewhere.
|
|
57
|
+
*/
|
|
58
|
+
export declare function pitchAtStavePosition(clef: Clef, positionsBelowTopLine: number): Pitch;
|
|
59
|
+
//# sourceMappingURL=music-vocabulary.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"music-vocabulary.d.ts","sourceRoot":"","sources":["../../../src/domain/notation/music-vocabulary.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AACH,OAAO,KAAK,EACV,IAAI,EACJ,YAAY,EACZ,YAAY,EACZ,KAAK,EACL,KAAK,EAEN,MAAM,gBAAgB,CAAC;AAOxB,2EAA2E;AAC3E,eAAO,MAAM,cAAc,EAA6B,YAAY,EAAE,CAAC;AAuBvE,uCAAuC;AACvC,wBAAgB,aAAa,CAAC,IAAI,EAAE,YAAY,GAAG,MAAM,CAExD;AAyCD,sEAAsE;AACtE,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAK3D;AAED,iFAAiF;AACjF,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,YAAY,GAAG,MAAM,CAI1D;AAED,yEAAyE;AACzE,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,YAAY,CAAC,MAAM,CAAC,GAAG,KAAK,CAAC;IACrE,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;CACf,CAAC,CASD;AAID;;;;;;;GAOG;AACH,MAAM,MAAM,OAAO,GAAG;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC;AAEpD,wBAAgB,cAAc,CAAC,KAAK,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,GAAG,IAAI,CA2BzE;AAED;;;;GAIG;AACH,wBAAgB,cAAc,CAC5B,KAAK,EAAE,KAAK,EACZ,GAAG,EAAE,MAAM,EACX,IAAI,EAAE,MAAM,GACX,MAAM,GAAG,IAAI,CAef;AAuBD;;;;;;;GAOG;AACH,wBAAgB,oBAAoB,CAClC,IAAI,EAAE,IAAI,EACV,qBAAqB,EAAE,MAAM,GAC5B,KAAK,CAEP"}
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
import { DURATIONS, beatDurationTicks } from "../time/ticks.js";
|
|
2
|
+
import { barNumberAt, indexOfBarNumber } from "../score/bar-numbers.js";
|
|
3
|
+
import { shiftDiatonic } from "../pitch/transpose.js";
|
|
4
|
+
// ---- durations -------------------------------------------------------------
|
|
5
|
+
/** Every duration name, longest first, so a picker reads top to bottom. */
|
|
6
|
+
export const DURATION_NAMES = Object.keys(DURATIONS);
|
|
7
|
+
const DURATION_LABELS = {
|
|
8
|
+
whole: "Whole",
|
|
9
|
+
half: "Half",
|
|
10
|
+
quarter: "Quarter",
|
|
11
|
+
eighth: "Eighth",
|
|
12
|
+
sixteenth: "16th",
|
|
13
|
+
thirtysecond: "32nd",
|
|
14
|
+
"dotted-whole": "Dotted whole",
|
|
15
|
+
"dotted-half": "Dotted half",
|
|
16
|
+
"dotted-quarter": "Dotted quarter",
|
|
17
|
+
"dotted-eighth": "Dotted eighth",
|
|
18
|
+
"dotted-sixteenth": "Dotted 16th",
|
|
19
|
+
"dotted-thirtysecond": "Dotted 32nd",
|
|
20
|
+
"triplet-whole": "Whole triplet",
|
|
21
|
+
"triplet-half": "Half triplet",
|
|
22
|
+
"triplet-quarter": "Quarter triplet",
|
|
23
|
+
"triplet-eighth": "Eighth triplet",
|
|
24
|
+
"triplet-sixteenth": "16th triplet",
|
|
25
|
+
"triplet-thirtysecond": "32nd triplet",
|
|
26
|
+
};
|
|
27
|
+
/** What to call `name` in a picker. */
|
|
28
|
+
export function durationLabel(name) {
|
|
29
|
+
return DURATION_LABELS[name];
|
|
30
|
+
}
|
|
31
|
+
// ---- key signatures --------------------------------------------------------
|
|
32
|
+
/** Sharps are positive, flats negative — the MusicXML convention the model stores. */
|
|
33
|
+
const MAJOR_KEYS = {
|
|
34
|
+
[-7]: "C♭",
|
|
35
|
+
[-6]: "G♭",
|
|
36
|
+
[-5]: "D♭",
|
|
37
|
+
[-4]: "A♭",
|
|
38
|
+
[-3]: "E♭",
|
|
39
|
+
[-2]: "B♭",
|
|
40
|
+
[-1]: "F",
|
|
41
|
+
0: "C",
|
|
42
|
+
1: "G",
|
|
43
|
+
2: "D",
|
|
44
|
+
3: "A",
|
|
45
|
+
4: "E",
|
|
46
|
+
5: "B",
|
|
47
|
+
6: "F♯",
|
|
48
|
+
7: "C♯",
|
|
49
|
+
};
|
|
50
|
+
const MINOR_KEYS = {
|
|
51
|
+
[-7]: "A♭",
|
|
52
|
+
[-6]: "E♭",
|
|
53
|
+
[-5]: "B♭",
|
|
54
|
+
[-4]: "F",
|
|
55
|
+
[-3]: "C",
|
|
56
|
+
[-2]: "G",
|
|
57
|
+
[-1]: "D",
|
|
58
|
+
0: "A",
|
|
59
|
+
1: "E",
|
|
60
|
+
2: "B",
|
|
61
|
+
3: "F♯",
|
|
62
|
+
4: "C♯",
|
|
63
|
+
5: "G♯",
|
|
64
|
+
6: "D♯",
|
|
65
|
+
7: "A♯",
|
|
66
|
+
};
|
|
67
|
+
/** The number of sharps or flats a signature carries, as a phrase. */
|
|
68
|
+
export function accidentalCountLabel(fifths) {
|
|
69
|
+
if (fifths === 0)
|
|
70
|
+
return "no sharps or flats";
|
|
71
|
+
const count = Math.abs(fifths);
|
|
72
|
+
const kind = fifths > 0 ? "sharp" : "flat";
|
|
73
|
+
return `${count} ${kind}${count === 1 ? "" : "s"}`;
|
|
74
|
+
}
|
|
75
|
+
/** e.g. `D major`. Falls back to the fifths count for a signature outside ±7. */
|
|
76
|
+
export function keySignatureName(key) {
|
|
77
|
+
const table = key.mode === "minor" ? MINOR_KEYS : MAJOR_KEYS;
|
|
78
|
+
const tonic = table[key.fifths];
|
|
79
|
+
return tonic ? `${tonic} ${key.mode}` : `${key.fifths} fifths`;
|
|
80
|
+
}
|
|
81
|
+
/** Every key a picker should offer, from seven flats to seven sharps. */
|
|
82
|
+
export function keySignatureOptions(mode) {
|
|
83
|
+
const table = mode === "minor" ? MINOR_KEYS : MAJOR_KEYS;
|
|
84
|
+
return Object.keys(table)
|
|
85
|
+
.map(Number)
|
|
86
|
+
.sort((a, b) => a - b)
|
|
87
|
+
.map((fifths) => ({
|
|
88
|
+
fifths,
|
|
89
|
+
label: `${table[fifths]} ${mode} — ${accidentalCountLabel(fifths)}`,
|
|
90
|
+
}));
|
|
91
|
+
}
|
|
92
|
+
export function barBeatForTick(score, tick) {
|
|
93
|
+
const track = score.tracks[0];
|
|
94
|
+
if (!track || track.measures.length === 0)
|
|
95
|
+
return null;
|
|
96
|
+
const measure = track.measures.find((m) => tick >= m.startTick && tick < m.startTick + m.durationTicks);
|
|
97
|
+
if (!measure)
|
|
98
|
+
return null;
|
|
99
|
+
const beatTicks = beatDurationTicks(measure.timeSignature, score.ppq);
|
|
100
|
+
/*
|
|
101
|
+
Through `barNumberAt`, not `index + 1`: a pickup is not counted, so every
|
|
102
|
+
bar after one is a bar lower than its position. The readout has to say the
|
|
103
|
+
number the player's part says, or "go to bar 33" and the bar shown in the
|
|
104
|
+
inspector mean two different places.
|
|
105
|
+
|
|
106
|
+
A pickup itself has no number; it reports bar 0, which is what a player
|
|
107
|
+
calls it when they have to call it something.
|
|
108
|
+
*/
|
|
109
|
+
const index = track.measures.indexOf(measure);
|
|
110
|
+
return {
|
|
111
|
+
bar: barNumberAt(track.measures, index) ?? 0,
|
|
112
|
+
beat: (tick - measure.startTick) / beatTicks + 1,
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* The tick at `bar`/`beat`, both counted from 1. `null` when that bar does not
|
|
117
|
+
* exist; a beat past the end of its bar is clamped into it, because a typed
|
|
118
|
+
* "beat 9" in 4/4 means the end of the bar rather than an error.
|
|
119
|
+
*/
|
|
120
|
+
export function tickForBarBeat(score, bar, beat) {
|
|
121
|
+
const track = score.tracks[0];
|
|
122
|
+
if (!track)
|
|
123
|
+
return null;
|
|
124
|
+
// The inverse of the numbering above, so a typed bar number and a displayed
|
|
125
|
+
// one always name the same bar.
|
|
126
|
+
const index = indexOfBarNumber(track.measures, Math.round(bar));
|
|
127
|
+
const measure = index === null ? undefined : track.measures[index];
|
|
128
|
+
if (!measure)
|
|
129
|
+
return null;
|
|
130
|
+
const beatTicks = beatDurationTicks(measure.timeSignature, score.ppq);
|
|
131
|
+
const offset = Math.max(0, (beat - 1) * beatTicks);
|
|
132
|
+
return measure.startTick + Math.min(offset, measure.durationTicks - 1);
|
|
133
|
+
}
|
|
134
|
+
// ---- staff positions -------------------------------------------------------
|
|
135
|
+
/**
|
|
136
|
+
* The pitch printed on each clef's top staff line.
|
|
137
|
+
*
|
|
138
|
+
* Everything a click on the stave means is measured from here: a staff
|
|
139
|
+
* position is a line or the space below it, and the pitch is that many
|
|
140
|
+
* diatonic steps down from this note.
|
|
141
|
+
*
|
|
142
|
+
* Percussion has no pitch — a drum staff's lines are instruments, not notes —
|
|
143
|
+
* so it borrows treble's geometry and the caller decides what a position means
|
|
144
|
+
* on it.
|
|
145
|
+
*/
|
|
146
|
+
const TOP_LINE_PITCH = {
|
|
147
|
+
treble: { step: "F", accidental: 0, octave: 5 },
|
|
148
|
+
bass: { step: "A", accidental: 0, octave: 3 },
|
|
149
|
+
alto: { step: "G", accidental: 0, octave: 4 },
|
|
150
|
+
tenor: { step: "E", accidental: 0, octave: 4 },
|
|
151
|
+
percussion: { step: "F", accidental: 0, octave: 5 },
|
|
152
|
+
};
|
|
153
|
+
/**
|
|
154
|
+
* The pitch at a staff position, counted in half-spaces below the top line.
|
|
155
|
+
*
|
|
156
|
+
* `0` is the top line itself, `1` the space beneath it, `2` the next line down,
|
|
157
|
+
* and negative values are ledger positions above. Diatonic throughout, because
|
|
158
|
+
* a staff position names a letter rather than a semitone — what makes it an F♯
|
|
159
|
+
* rather than an F is the key signature, which is applied elsewhere.
|
|
160
|
+
*/
|
|
161
|
+
export function pitchAtStavePosition(clef, positionsBelowTopLine) {
|
|
162
|
+
return shiftDiatonic(TOP_LINE_PITCH[clef], -positionsBelowTopLine);
|
|
163
|
+
}
|
|
164
|
+
//# sourceMappingURL=music-vocabulary.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"music-vocabulary.js","sourceRoot":"","sources":["../../../src/domain/notation/music-vocabulary.ts"],"names":[],"mappings":"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"}
|
|
@@ -20,33 +20,33 @@ export function pitchToMidi(p) {
|
|
|
20
20
|
}
|
|
21
21
|
/** step/accidental spelling for each pitch class 0-11, preferring sharps. */
|
|
22
22
|
const SHARP_SPELLING = [
|
|
23
|
-
{ step:
|
|
24
|
-
{ step:
|
|
25
|
-
{ step:
|
|
26
|
-
{ step:
|
|
27
|
-
{ step:
|
|
28
|
-
{ step:
|
|
29
|
-
{ step:
|
|
30
|
-
{ step:
|
|
31
|
-
{ step:
|
|
32
|
-
{ step:
|
|
33
|
-
{ step:
|
|
34
|
-
{ step:
|
|
23
|
+
{ step: "C", accidental: 0 },
|
|
24
|
+
{ step: "C", accidental: 1 },
|
|
25
|
+
{ step: "D", accidental: 0 },
|
|
26
|
+
{ step: "D", accidental: 1 },
|
|
27
|
+
{ step: "E", accidental: 0 },
|
|
28
|
+
{ step: "F", accidental: 0 },
|
|
29
|
+
{ step: "F", accidental: 1 },
|
|
30
|
+
{ step: "G", accidental: 0 },
|
|
31
|
+
{ step: "G", accidental: 1 },
|
|
32
|
+
{ step: "A", accidental: 0 },
|
|
33
|
+
{ step: "A", accidental: 1 },
|
|
34
|
+
{ step: "B", accidental: 0 },
|
|
35
35
|
];
|
|
36
36
|
/** step/accidental spelling for each pitch class 0-11, preferring flats. */
|
|
37
37
|
const FLAT_SPELLING = [
|
|
38
|
-
{ step:
|
|
39
|
-
{ step:
|
|
40
|
-
{ step:
|
|
41
|
-
{ step:
|
|
42
|
-
{ step:
|
|
43
|
-
{ step:
|
|
44
|
-
{ step:
|
|
45
|
-
{ step:
|
|
46
|
-
{ step:
|
|
47
|
-
{ step:
|
|
48
|
-
{ step:
|
|
49
|
-
{ step:
|
|
38
|
+
{ step: "C", accidental: 0 },
|
|
39
|
+
{ step: "D", accidental: -1 },
|
|
40
|
+
{ step: "D", accidental: 0 },
|
|
41
|
+
{ step: "E", accidental: -1 },
|
|
42
|
+
{ step: "E", accidental: 0 },
|
|
43
|
+
{ step: "F", accidental: 0 },
|
|
44
|
+
{ step: "G", accidental: -1 },
|
|
45
|
+
{ step: "G", accidental: 0 },
|
|
46
|
+
{ step: "A", accidental: -1 },
|
|
47
|
+
{ step: "A", accidental: 0 },
|
|
48
|
+
{ step: "B", accidental: -1 },
|
|
49
|
+
{ step: "B", accidental: 0 },
|
|
50
50
|
];
|
|
51
51
|
/**
|
|
52
52
|
* Converts a MIDI note number to a `Pitch`, choosing an enharmonic spelling
|
|
@@ -63,11 +63,11 @@ export function midiToPitch(midi, keySignature) {
|
|
|
63
63
|
return { step, accidental, octave };
|
|
64
64
|
}
|
|
65
65
|
const ACCIDENTAL_SYMBOLS = {
|
|
66
|
-
[-2]:
|
|
67
|
-
[-1]:
|
|
68
|
-
[0]:
|
|
69
|
-
[1]:
|
|
70
|
-
[2]:
|
|
66
|
+
[-2]: "bb",
|
|
67
|
+
[-1]: "b",
|
|
68
|
+
[0]: "",
|
|
69
|
+
[1]: "#",
|
|
70
|
+
[2]: "##",
|
|
71
71
|
};
|
|
72
72
|
/** Formats a `Pitch` as a string, e.g. `C4`, `F#3`, `Bb4`, `F##4`. */
|
|
73
73
|
export function pitchToString(p) {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { KeySignature, Pitch } from
|
|
1
|
+
import type { KeySignature, Pitch } from "../../index.js";
|
|
2
2
|
/**
|
|
3
3
|
* Transposes a pitch by a number of semitones (may be negative), re-spelling
|
|
4
4
|
* the result according to the given key signature (see `midiToPitch`).
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { midiToPitch, pitchToMidi } from
|
|
1
|
+
import { midiToPitch, pitchToMidi } from "./pitch.js";
|
|
2
2
|
/**
|
|
3
3
|
* Transposes a pitch by a number of semitones (may be negative), re-spelling
|
|
4
4
|
* the result according to the given key signature (see `midiToPitch`).
|
|
@@ -14,7 +14,7 @@ export function transposeDiatonicOctave(p, octaves) {
|
|
|
14
14
|
return { ...p, octave: p.octave + octaves };
|
|
15
15
|
}
|
|
16
16
|
/** The seven letter names, in order, for diatonic movement. */
|
|
17
|
-
const STEPS = [
|
|
17
|
+
const STEPS = ["C", "D", "E", "F", "G", "A", "B"];
|
|
18
18
|
/**
|
|
19
19
|
* Moves `p` by `steps` positions on the staff — one step per line-to-adjacent-
|
|
20
20
|
* space — keeping its accidental.
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import type { MusicalEvent } from
|
|
2
|
-
import type { QuantizeOptions } from
|
|
1
|
+
import type { MusicalEvent } from "../../index.js";
|
|
2
|
+
import type { QuantizeOptions } from "./options.js";
|
|
3
3
|
/**
|
|
4
4
|
* Quantizes `events` per `opts` (spec §24): drops undersized notes, snaps
|
|
5
5
|
* starts (grid/triplet/swing/humanize tolerance) and durations (grid,
|
|
@@ -5,8 +5,8 @@
|
|
|
5
5
|
* means a deterministic "already close enough, leave it" tolerance, not
|
|
6
6
|
* random jitter). Exposed for both MIDI import cleanup and manual editing.
|
|
7
7
|
*/
|
|
8
|
-
import { createId } from
|
|
9
|
-
import { isNoteEvent } from
|
|
8
|
+
import { createId } from "../score/ids.js";
|
|
9
|
+
import { isNoteEvent } from "../../index.js";
|
|
10
10
|
/** `tripletGrid` subdivides `grid` into thirds-of-two (2/3 of `grid`); otherwise the grid unit is used as-is. */
|
|
11
11
|
function effectiveGridTicks(opts) {
|
|
12
12
|
return opts.tripletGrid ? (opts.grid * 2) / 3 : opts.grid;
|
|
@@ -53,7 +53,7 @@ function computeSnappedDuration(original, opts) {
|
|
|
53
53
|
function dropShortNotes(events, minDurationTicks) {
|
|
54
54
|
if (minDurationTicks === undefined)
|
|
55
55
|
return events;
|
|
56
|
-
return events.filter(event => !isNoteEvent(event) || event.durationTicks >= minDurationTicks);
|
|
56
|
+
return events.filter((event) => !isNoteEvent(event) || event.durationTicks >= minDurationTicks);
|
|
57
57
|
}
|
|
58
58
|
/** Groups events by (trackId, voiceId), preserving each group's first-seen order. */
|
|
59
59
|
function groupByVoice(events) {
|
|
@@ -69,7 +69,7 @@ function groupByVoice(events) {
|
|
|
69
69
|
}
|
|
70
70
|
group.push(event);
|
|
71
71
|
}
|
|
72
|
-
return order.map(key => groups.get(key));
|
|
72
|
+
return order.map((key) => groups.get(key));
|
|
73
73
|
}
|
|
74
74
|
/**
|
|
75
75
|
* Clusters near-simultaneous onsets (already sorted by `startTick`) onto a
|
|
@@ -101,7 +101,7 @@ function applyChordGrouping(sorted, tolerance) {
|
|
|
101
101
|
* into the next one" meaning of overlap resolution.
|
|
102
102
|
*/
|
|
103
103
|
function applyResolveOverlaps(sorted) {
|
|
104
|
-
const result = sorted.map(event => ({ ...event }));
|
|
104
|
+
const result = sorted.map((event) => ({ ...event }));
|
|
105
105
|
for (let i = 0; i < result.length - 1; i += 1) {
|
|
106
106
|
const current = result[i];
|
|
107
107
|
const next = result[i + 1];
|
|
@@ -121,7 +121,7 @@ function applyResolveOverlaps(sorted) {
|
|
|
121
121
|
* next one. Events sharing an exact start tick (a chord) are left alone.
|
|
122
122
|
*/
|
|
123
123
|
function applyLegatoCleanup(sorted) {
|
|
124
|
-
const result = sorted.map(event => ({ ...event }));
|
|
124
|
+
const result = sorted.map((event) => ({ ...event }));
|
|
125
125
|
for (let i = 0; i < result.length - 1; i += 1) {
|
|
126
126
|
const current = result[i];
|
|
127
127
|
const next = result[i + 1];
|
|
@@ -183,17 +183,17 @@ function processVoiceGroup(group, opts) {
|
|
|
183
183
|
*/
|
|
184
184
|
export function quantizeEvents(events, opts) {
|
|
185
185
|
const survivors = dropShortNotes(events, opts.minDurationTicks);
|
|
186
|
-
const withStarts = survivors.map(event => opts.quantizeStarts
|
|
186
|
+
const withStarts = survivors.map((event) => opts.quantizeStarts
|
|
187
187
|
? { ...event, startTick: computeSnappedStart(event.startTick, opts) }
|
|
188
188
|
: { ...event });
|
|
189
|
-
const withDurations = withStarts.map(event => opts.quantizeDurations
|
|
189
|
+
const withDurations = withStarts.map((event) => opts.quantizeDurations
|
|
190
190
|
? {
|
|
191
191
|
...event,
|
|
192
192
|
durationTicks: computeSnappedDuration(event.durationTicks, opts),
|
|
193
193
|
}
|
|
194
194
|
: event);
|
|
195
195
|
return groupByVoice(withDurations)
|
|
196
|
-
.map(group => processVoiceGroup(group, opts))
|
|
196
|
+
.map((group) => processVoiceGroup(group, opts))
|
|
197
197
|
.flat();
|
|
198
198
|
}
|
|
199
199
|
//# sourceMappingURL=quantize.js.map
|