@sudobility/music_codecs 0.1.1
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/index.d.ts +43 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +46 -0
- package/dist/index.js.map +1 -0
- package/dist/midi/analyze.d.ts +53 -0
- package/dist/midi/analyze.d.ts.map +1 -0
- package/dist/midi/analyze.js +58 -0
- package/dist/midi/analyze.js.map +1 -0
- package/dist/midi/export.d.ts +20 -0
- package/dist/midi/export.d.ts.map +1 -0
- package/dist/midi/export.js +135 -0
- package/dist/midi/export.js.map +1 -0
- package/dist/midi/grid-detection.d.ts +41 -0
- package/dist/midi/grid-detection.d.ts.map +1 -0
- package/dist/midi/grid-detection.js +61 -0
- package/dist/midi/grid-detection.js.map +1 -0
- package/dist/midi/import-options.d.ts +45 -0
- package/dist/midi/import-options.d.ts.map +1 -0
- package/dist/midi/import-options.js +38 -0
- package/dist/midi/import-options.js.map +1 -0
- package/dist/midi/import.d.ts +43 -0
- package/dist/midi/import.d.ts.map +1 -0
- package/dist/midi/import.js +441 -0
- package/dist/midi/import.js.map +1 -0
- package/dist/midi/key-detection.d.ts +11 -0
- package/dist/midi/key-detection.d.ts.map +1 -0
- package/dist/midi/key-detection.js +96 -0
- package/dist/midi/key-detection.js.map +1 -0
- package/dist/midi/measures.d.ts +39 -0
- package/dist/midi/measures.d.ts.map +1 -0
- package/dist/midi/measures.js +141 -0
- package/dist/midi/measures.js.map +1 -0
- package/dist/mod/export.d.ts +54 -0
- package/dist/mod/export.d.ts.map +1 -0
- package/dist/mod/export.js +172 -0
- package/dist/mod/export.js.map +1 -0
- package/dist/mod/fill.d.ts +11 -0
- package/dist/mod/fill.d.ts.map +1 -0
- package/dist/mod/fill.js +50 -0
- package/dist/mod/fill.js.map +1 -0
- package/dist/mod/import.d.ts +14 -0
- package/dist/mod/import.d.ts.map +1 -0
- package/dist/mod/import.js +182 -0
- package/dist/mod/import.js.map +1 -0
- package/dist/mod/limits.d.ts +26 -0
- package/dist/mod/limits.d.ts.map +1 -0
- package/dist/mod/limits.js +14 -0
- package/dist/mod/limits.js.map +1 -0
- package/dist/mod/timing.d.ts +29 -0
- package/dist/mod/timing.d.ts.map +1 -0
- package/dist/mod/timing.js +61 -0
- package/dist/mod/timing.js.map +1 -0
- package/dist/mod/types.d.ts +8 -0
- package/dist/mod/types.d.ts.map +1 -0
- package/dist/mod/types.js +2 -0
- package/dist/mod/types.js.map +1 -0
- package/dist/musicxml/duration-map.d.ts +34 -0
- package/dist/musicxml/duration-map.d.ts.map +1 -0
- package/dist/musicxml/duration-map.js +79 -0
- package/dist/musicxml/duration-map.js.map +1 -0
- package/dist/musicxml/export.d.ts +35 -0
- package/dist/musicxml/export.d.ts.map +1 -0
- package/dist/musicxml/export.js +271 -0
- package/dist/musicxml/export.js.map +1 -0
- package/dist/musicxml/import.d.ts +52 -0
- package/dist/musicxml/import.d.ts.map +1 -0
- package/dist/musicxml/import.js +637 -0
- package/dist/musicxml/import.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/dist/test/musicxml-warnings.d.ts +11 -0
- package/dist/test/musicxml-warnings.d.ts.map +1 -0
- package/dist/test/musicxml-warnings.js +26 -0
- package/dist/test/musicxml-warnings.js.map +1 -0
- package/dist/test/platform.d.ts +13 -0
- package/dist/test/platform.d.ts.map +1 -0
- package/dist/test/platform.js +191 -0
- package/dist/test/platform.js.map +1 -0
- package/package.json +71 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"import.js","sourceRoot":"","sources":["../../src/mod/import.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AACH,OAAO,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAC3D,OAAO,EAAE,QAAQ,EAAE,MAAM,yBAAyB,CAAC;AACnD,OAAO,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAC;AACtD,OAAO,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AACzD,OAAO,EAAE,kBAAkB,EAAE,MAAM,WAAW,CAAC;AAS/C,sDAAsD;AACtD,MAAM,aAAa,GAAG,CAAC,CAAC;AACxB,MAAM,iBAAiB,GAAG,CAAC,CAAC;AAS5B;;;;;;GAMG;AACH,MAAM,UAAU,WAAW,CAAC,MAAqB;IAC/C,MAAM,IAAI,GAAoB,EAAE,CAAC;IACjC,KAAK,MAAM,YAAY,IAAI,MAAM,CAAC,KAAK,EAAE,CAAC;QACxC,MAAM,OAAO,GAAG,MAAM,CAAC,QAAQ,CAAC,YAAY,CAAC,CAAC;QAC9C,IAAI,CAAC,OAAO;YAAE,SAAS;QACvB,KAAK,MAAM,GAAG,IAAI,OAAO,EAAE,CAAC;YAC1B,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;YACf,IAAI,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,YAAY,CAAC;gBAAE,MAAM;QACjD,CAAC;IACH,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED;;;;;;GAMG;AACH,SAAS,UAAU,CAAC,MAAqB;IACvC,MAAM,IAAI,GAAG,WAAW,CAAC,MAAM,CAAC,CAAC;IACjC,MAAM,MAAM,GAAa,EAAE,CAAC;IAC5B,MAAM,IAAI,GAAG,IAAI,GAAG,EAAkB,CAAC;IAEvC,IAAI,CAAC,OAAO,CAAC,CAAC,KAAK,EAAE,GAAG,EAAE,EAAE;QAC1B,KAAK,CAAC,OAAO,CAAC,CAAC,IAAI,EAAE,OAAO,EAAE,EAAE;YAC9B,IAAI,IAAI,CAAC,IAAI,KAAK,IAAI;gBAAE,OAAO;YAE/B,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;YACnC,IAAI,QAAQ;gBAAE,QAAQ,CAAC,MAAM,GAAG,GAAG,CAAC;YACpC,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;YAErB,2DAA2D;YAC3D,IAAI,IAAI,CAAC,IAAI,KAAK,KAAK;gBAAE,OAAO;YAEhC,MAAM,IAAI,GAAW;gBACnB,UAAU,EAAE,IAAI,CAAC,UAAU;gBAC3B,QAAQ,EAAE,GAAG;gBACb,MAAM,EAAE,GAAG,GAAG,CAAC;gBACf,IAAI,EAAE,IAAI,CAAC,IAAI;aAChB,CAAC;YACF,IAAI,CAAC,GAAG,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC;YACxB,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACpB,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,8DAA8D;AAC9D,SAAS,UAAU,CAAC,MAAqB,EAAE,WAAmB;IAC5D,uEAAuE;IACvE,wCAAwC;IACxC,OAAO,YAAY,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;QACtD,EAAE,EAAE,QAAQ,EAAE;QACd,IAAI,EAAE,MAAM,CAAC,GAAG,GAAG,WAAW;QAC9B,GAAG,EAAE,MAAM,CAAC,GAAG;KAChB,CAAC,CAAC,CAAC;AACN,CAAC;AAED,MAAM,UAAU,cAAc,CAAC,MAAqB;IAClD,MAAM,MAAM,GAAG,UAAU,CAAC,MAAM,CAAC,CAAC;IAElC,6EAA6E;IAC7E,mEAAmE;IACnE,MAAM,eAAe,GAAa,EAAE,CAAC;IACrC,KAAK,MAAM,IAAI,IAAI,MAAM,EAAE,CAAC;QAC1B,IAAI,CAAC,eAAe,CAAC,QAAQ,CAAC,IAAI,CAAC,UAAU,CAAC;YAC5C,eAAe,CAAC,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;IAC1C,CAAC;IAED,MAAM,MAAM,GAAG,CAAC,UAAkB,EAAU,EAAE;QAC5C,MAAM,KAAK,GAAG,MAAM,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,KAAK,KAAK,UAAU,CAAC,CAAC;QACnE,OAAO,KAAK,IAAI,KAAK,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC;YACnC,CAAC,CAAC,KAAK,CAAC,IAAI;YACZ,CAAC,CAAC,cAAc,UAAU,EAAE,CAAC;IACjC,CAAC,CAAC;IAEF,yEAAyE;IACzE,6CAA6C;IAC7C,MAAM,SAAS,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC,MAAM,CAAC,CAAC;IAC1D,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG,CACvB,CAAC,EACD,IAAI,CAAC,IAAI,CAAC,SAAS,GAAG,CAAC,aAAa,GAAG,iBAAiB,CAAC,CAAC,CAC3D,CAAC;IAEF,MAAM,IAAI,GAAG,gBAAgB,CAAC;QAC5B,KAAK,EAAE,MAAM,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,QAAQ;QACxD,QAAQ;QACR,MAAM,EACJ,eAAe,CAAC,MAAM,GAAG,CAAC;YACxB,CAAC,CAAC,eAAe,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;gBACxB,IAAI,EAAE,MAAM,CAAC,CAAC,CAAC;gBACf,cAAc,EAAE,MAAM,CAAC,CAAC,CAAC;gBACzB,IAAI,EAAE,QAAiB;aACxB,CAAC,CAAC;YACL,CAAC,CAAC;gBACE;oBACE,IAAI,EAAE,QAAQ;oBACd,cAAc,EAAE,QAAQ;oBACxB,IAAI,EAAE,QAAiB;iBACxB;aACF;KACR,CAAC,CAAC;IAEH,MAAM,WAAW,GAAG,IAAI,CAAC,GAAG,GAAG,aAAa,CAAC;IAE7C,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,UAAU,EAAE,EAAE;QACnD,MAAM,UAAU,GAAG,eAAe,CAAC,UAAU,CAAC,CAAC;QAC/C,MAAM,IAAI,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,UAAU,KAAK,UAAU,CAAC,CAAC;QAE7D,MAAM,SAAS,GAAG,KAAK,CAAC,QAAQ,CAAC,GAAG,CAAC,OAAO,CAAC,EAAE;YAC7C,MAAM,UAAU,GAAG,OAAO,CAAC,SAAS,GAAG,OAAO,CAAC,aAAa,CAAC;YAC7D,MAAM,IAAI,GAAG,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE;gBAC3B,MAAM,IAAI,GAAG,CAAC,CAAC,QAAQ,GAAG,WAAW,CAAC;gBACtC,OAAO,IAAI,IAAI,OAAO,CAAC,SAAS,IAAI,IAAI,GAAG,UAAU,CAAC;YACxD,CAAC,CAAC,CAAC;YACH,gEAAgE;YAChE,iDAAiD;YACjD,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC;gBAAE,OAAO,OAAO,CAAC;YAEtC,qEAAqE;YACrE,sEAAsE;YACtE,6BAA6B;YAC7B,MAAM,SAAS,GAAa,EAAE,CAAC;YAC/B,MAAM,WAAW,GAAkB,EAAE,CAAC;YAEtC,KAAK,MAAM,IAAI,IAAI,IAAI,EAAE,CAAC;gBACxB,MAAM,SAAS,GAAG,IAAI,CAAC,QAAQ,GAAG,WAAW,CAAC;gBAC9C,MAAM,OAAO,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,MAAM,GAAG,WAAW,EAAE,UAAU,CAAC,CAAC;gBAChE,IAAI,KAAK,GAAG,SAAS,CAAC,SAAS,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,IAAI,SAAS,CAAC,CAAC;gBACzD,IAAI,KAAK,KAAK,CAAC,CAAC,EAAE,CAAC;oBACjB,KAAK,GAAG,SAAS,CAAC,MAAM,CAAC;oBACzB,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;oBAClB,WAAW,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;gBACvB,CAAC;gBACD,SAAS,CAAC,KAAK,CAAC,GAAG,OAAO,CAAC;gBAC3B,WAAW,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC;oBACtB,EAAE,EAAE,QAAQ,EAAE;oBACd,KAAK,EAAE,WAAW,CAAC,IAAI,CAAC,IAAI,EAAE,OAAO,CAAC,YAAY,CAAC;oBACnD,SAAS;oBACT,aAAa,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;oBAC/C,QAAQ,EAAE,EAAE;oBACZ,OAAO,EAAE,EAAE;oBACX,OAAO,EAAE,KAAK,CAAC,EAAE;iBAClB,CAAC,CAAC;YACL,CAAC;YAED,MAAM,MAAM,GAAY,WAAW,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE;gBACpD,MAAM,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,IAAI,QAAQ,EAAE,CAAC;gBAC/C,OAAO;oBACL,EAAE;oBACF,IAAI,EAAE,SAAS,CAAC,GAAG,CAAC,EAAE;oBACtB,sEAAsE;oBACtE,MAAM,EAAE,kBAAkB,CACxB,MAAM,EACN,OAAO,CAAC,SAAS,EACjB,OAAO,CAAC,aAAa,EACrB,IAAI,CAAC,GAAG,EACR,KAAK,CAAC,EAAE,EACR,EAAE,CACH;iBACF,CAAC;YACJ,CAAC,CAAC,CAAC;YAEH,OAAO,EAAE,GAAG,OAAO,EAAE,MAAM,EAAE,CAAC;QAChC,CAAC,CAAC,CAAC;QAEH,OAAO,EAAE,GAAG,KAAK,EAAE,QAAQ,EAAE,SAAS,EAAE,CAAC;IAC3C,CAAC,CAAC,CAAC;IAEH,OAAO,EAAE,GAAG,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,UAAU,CAAC,MAAM,EAAE,WAAW,CAAC,EAAE,CAAC;AACxE,CAAC;AAED,OAAO,EAAE,YAAY,EAAE,YAAY,EAAE,CAAC"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What each tracker format can hold.
|
|
3
|
+
*
|
|
4
|
+
* Every number here is anchored to **our own decoder** rather than to the
|
|
5
|
+
* format documentation, because the two only have to agree with each other for
|
|
6
|
+
* a round trip to work. MOD's range is the clearest case: `period.ts` takes
|
|
7
|
+
* ProTracker's C-1 period as MIDI 36, so three octaves is 36-71. A different
|
|
8
|
+
* but equally defensible anchor would fail the round-trip test for reasons that
|
|
9
|
+
* look like a writer bug.
|
|
10
|
+
*
|
|
11
|
+
* DSM and MPTM are absent deliberately: nothing reads DSM that does not read a
|
|
12
|
+
* better format, and OpenMPT reads IT natively, so writing either buys nothing.
|
|
13
|
+
*/
|
|
14
|
+
import type { TrackerFormat } from '@sudobility/music_types';
|
|
15
|
+
/** The formats export can write. A subset of `TrackerFormat`, which import also covers. */
|
|
16
|
+
export type WritableTrackerFormat = 'mod' | 's3m' | 'xm' | 'it';
|
|
17
|
+
export type TrackerLimits = {
|
|
18
|
+
channels: number;
|
|
19
|
+
instruments: number;
|
|
20
|
+
/** Inclusive MIDI note range this format can express. */
|
|
21
|
+
lowestMidi: number;
|
|
22
|
+
highestMidi: number;
|
|
23
|
+
};
|
|
24
|
+
export declare const TRACKER_LIMITS: Record<WritableTrackerFormat, TrackerLimits>;
|
|
25
|
+
export declare function isWritableTrackerFormat(format: TrackerFormat): format is WritableTrackerFormat;
|
|
26
|
+
//# sourceMappingURL=limits.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"limits.d.ts","sourceRoot":"","sources":["../../src/mod/limits.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AACH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,yBAAyB,CAAC;AAE7D,2FAA2F;AAC3F,MAAM,MAAM,qBAAqB,GAAG,KAAK,GAAG,KAAK,GAAG,IAAI,GAAG,IAAI,CAAC;AAEhE,MAAM,MAAM,aAAa,GAAG;IAC1B,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;IACpB,yDAAyD;IACzD,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;CACrB,CAAC;AAEF,eAAO,MAAM,cAAc,EAAE,MAAM,CAAC,qBAAqB,EAAE,aAAa,CASvE,CAAC;AAEF,wBAAgB,uBAAuB,CACrC,MAAM,EAAE,aAAa,GACpB,MAAM,IAAI,qBAAqB,CAIjC"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export const TRACKER_LIMITS = {
|
|
2
|
+
// Three octaves, anchored on period.ts's C-1 = MIDI 36.
|
|
3
|
+
mod: { channels: 4, instruments: 31, lowestMidi: 36, highestMidi: 71 },
|
|
4
|
+
// s3m.ts: NOTE_BASE 12, octaves 0-7 -> 12 + 7*12 + 11 = 107.
|
|
5
|
+
s3m: { channels: 32, instruments: 99, lowestMidi: 12, highestMidi: 107 },
|
|
6
|
+
// xm.ts: NOTE_BASE 11, notes 1-96 -> 12..107.
|
|
7
|
+
xm: { channels: 32, instruments: 128, lowestMidi: 12, highestMidi: 107 },
|
|
8
|
+
// it.ts: NOTE_BASE 0, notes 0-119.
|
|
9
|
+
it: { channels: 64, instruments: 99, lowestMidi: 0, highestMidi: 119 },
|
|
10
|
+
};
|
|
11
|
+
export function isWritableTrackerFormat(format) {
|
|
12
|
+
return (format === 'mod' || format === 's3m' || format === 'xm' || format === 'it');
|
|
13
|
+
}
|
|
14
|
+
//# sourceMappingURL=limits.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"limits.js","sourceRoot":"","sources":["../../src/mod/limits.ts"],"names":[],"mappings":"AA0BA,MAAM,CAAC,MAAM,cAAc,GAAiD;IAC1E,wDAAwD;IACxD,GAAG,EAAE,EAAE,QAAQ,EAAE,CAAC,EAAE,WAAW,EAAE,EAAE,EAAE,UAAU,EAAE,EAAE,EAAE,WAAW,EAAE,EAAE,EAAE;IACtE,6DAA6D;IAC7D,GAAG,EAAE,EAAE,QAAQ,EAAE,EAAE,EAAE,WAAW,EAAE,EAAE,EAAE,UAAU,EAAE,EAAE,EAAE,WAAW,EAAE,GAAG,EAAE;IACxE,8CAA8C;IAC9C,EAAE,EAAE,EAAE,QAAQ,EAAE,EAAE,EAAE,WAAW,EAAE,GAAG,EAAE,UAAU,EAAE,EAAE,EAAE,WAAW,EAAE,GAAG,EAAE;IACxE,mCAAmC;IACnC,EAAE,EAAE,EAAE,QAAQ,EAAE,EAAE,EAAE,WAAW,EAAE,EAAE,EAAE,UAAU,EAAE,CAAC,EAAE,WAAW,EAAE,GAAG,EAAE;CACvE,CAAC;AAEF,MAAM,UAAU,uBAAuB,CACrC,MAAqB;IAErB,OAAO,CACL,MAAM,KAAK,KAAK,IAAI,MAAM,KAAK,KAAK,IAAI,MAAM,KAAK,IAAI,IAAI,MAAM,KAAK,IAAI,CAC3E,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Turning a module's two timing knobs into a musical tempo.
|
|
3
|
+
*
|
|
4
|
+
* Pure arithmetic over the parsed cells — no score model, no bytes — so the
|
|
5
|
+
* conversion is testable against known values. Lives in music_lib rather than
|
|
6
|
+
* music_io because it is a musical conversion, not byte handling: the codec
|
|
7
|
+
* returns raw data and the mapping happens here, exactly as MIDI does.
|
|
8
|
+
*
|
|
9
|
+
* `periodToMidi` used to live here and now lives beside the MOD reader in
|
|
10
|
+
* music_io: every other tracker format stores note numbers directly, so the
|
|
11
|
+
* decoder is the only thing that ever sees a period.
|
|
12
|
+
*/
|
|
13
|
+
import type { TrackerCell } from './types.js';
|
|
14
|
+
/**
|
|
15
|
+
* Musical BPM from the two knobs.
|
|
16
|
+
*
|
|
17
|
+
* A row is a sixteenth (four to the beat) and a tick lasts 2.5/tempo seconds,
|
|
18
|
+
* so a beat is `4 × speed × 2.5 / tempo` seconds — which reduces to
|
|
19
|
+
* `6 × tempo / speed` beats per minute. At the defaults that is 125, as it
|
|
20
|
+
* should be.
|
|
21
|
+
*/
|
|
22
|
+
export declare function effectiveBpm(speed: number, tempo: number): number;
|
|
23
|
+
export type TempoChange = {
|
|
24
|
+
row: number;
|
|
25
|
+
bpm: number;
|
|
26
|
+
};
|
|
27
|
+
/** One entry at row 0, then one wherever either knob actually moves. */
|
|
28
|
+
export declare function tempoChanges(rows: readonly TrackerCell[][]): TempoChange[];
|
|
29
|
+
//# sourceMappingURL=timing.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"timing.d.ts","sourceRoot":"","sources":["../../src/mod/timing.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AACH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAS9C;;;;;;;GAOG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CASjE;AAED,MAAM,MAAM,WAAW,GAAG;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,CAAC;AAEvD,wEAAwE;AACxE,wBAAgB,YAAY,CAAC,IAAI,EAAE,SAAS,WAAW,EAAE,EAAE,GAAG,WAAW,EAAE,CAmC1E"}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/** The range a `TempoEvent` may hold; mirrors `domain/validation/validator.ts`. */
|
|
2
|
+
const MIN_BPM = 20;
|
|
3
|
+
const MAX_BPM = 400;
|
|
4
|
+
const DEFAULT_SPEED = 6;
|
|
5
|
+
const DEFAULT_TEMPO = 125;
|
|
6
|
+
/**
|
|
7
|
+
* Musical BPM from the two knobs.
|
|
8
|
+
*
|
|
9
|
+
* A row is a sixteenth (four to the beat) and a tick lasts 2.5/tempo seconds,
|
|
10
|
+
* so a beat is `4 × speed × 2.5 / tempo` seconds — which reduces to
|
|
11
|
+
* `6 × tempo / speed` beats per minute. At the defaults that is 125, as it
|
|
12
|
+
* should be.
|
|
13
|
+
*/
|
|
14
|
+
export function effectiveBpm(speed, tempo) {
|
|
15
|
+
if (speed <= 0)
|
|
16
|
+
return DEFAULT_TEMPO;
|
|
17
|
+
// Clamped to what a score can hold. A module is free to set a speed of 1,
|
|
18
|
+
// which at the default tempo works out to 750 BPM — real, and outside the
|
|
19
|
+
// 20-400 the score model accepts, so an unclamped value imports a score the
|
|
20
|
+
// validator rejects outright. A real S3M (modarchive 54606) does exactly
|
|
21
|
+
// this. Clamping keeps the music fast and the score valid; the alternative
|
|
22
|
+
// is a failed import.
|
|
23
|
+
return Math.min(MAX_BPM, Math.max(MIN_BPM, Math.round((6 * tempo) / speed)));
|
|
24
|
+
}
|
|
25
|
+
/** One entry at row 0, then one wherever either knob actually moves. */
|
|
26
|
+
export function tempoChanges(rows) {
|
|
27
|
+
let speed = DEFAULT_SPEED;
|
|
28
|
+
let tempo = DEFAULT_TEMPO;
|
|
29
|
+
const changes = [{ row: 0, bpm: effectiveBpm(speed, tempo) }];
|
|
30
|
+
rows.forEach((cells, row) => {
|
|
31
|
+
let moved = false;
|
|
32
|
+
for (const cell of cells) {
|
|
33
|
+
// Already normalised by the decoder: which effect carries which knob is
|
|
34
|
+
// format knowledge and does not belong here.
|
|
35
|
+
if (cell.speed !== undefined &&
|
|
36
|
+
cell.speed !== 0 &&
|
|
37
|
+
speed !== cell.speed) {
|
|
38
|
+
speed = cell.speed;
|
|
39
|
+
moved = true;
|
|
40
|
+
}
|
|
41
|
+
if (cell.bpm !== undefined && cell.bpm !== 0 && tempo !== cell.bpm) {
|
|
42
|
+
tempo = cell.bpm;
|
|
43
|
+
moved = true;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
if (!moved)
|
|
47
|
+
return;
|
|
48
|
+
const bpm = effectiveBpm(speed, tempo);
|
|
49
|
+
// Row 0's entry exists already, seeded from the *defaults* — so it has to be
|
|
50
|
+
// rewritten rather than skipped. Skipping it (which this did) threw away the
|
|
51
|
+
// tempo of every module that states one on its first row, which is where a
|
|
52
|
+
// module normally states it: the import silently came in at the 125 BPM
|
|
53
|
+
// default instead.
|
|
54
|
+
if (row === 0)
|
|
55
|
+
changes[0] = { row: 0, bpm };
|
|
56
|
+
else
|
|
57
|
+
changes.push({ row, bpm });
|
|
58
|
+
});
|
|
59
|
+
return changes;
|
|
60
|
+
}
|
|
61
|
+
//# sourceMappingURL=timing.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"timing.js","sourceRoot":"","sources":["../../src/mod/timing.ts"],"names":[],"mappings":"AAcA,mFAAmF;AACnF,MAAM,OAAO,GAAG,EAAE,CAAC;AACnB,MAAM,OAAO,GAAG,GAAG,CAAC;AAEpB,MAAM,aAAa,GAAG,CAAC,CAAC;AACxB,MAAM,aAAa,GAAG,GAAG,CAAC;AAE1B;;;;;;;GAOG;AACH,MAAM,UAAU,YAAY,CAAC,KAAa,EAAE,KAAa;IACvD,IAAI,KAAK,IAAI,CAAC;QAAE,OAAO,aAAa,CAAC;IACrC,0EAA0E;IAC1E,0EAA0E;IAC1E,4EAA4E;IAC5E,yEAAyE;IACzE,2EAA2E;IAC3E,sBAAsB;IACtB,OAAO,IAAI,CAAC,GAAG,CAAC,OAAO,EAAE,IAAI,CAAC,GAAG,CAAC,OAAO,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,GAAG,KAAK,CAAC,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;AAC/E,CAAC;AAID,wEAAwE;AACxE,MAAM,UAAU,YAAY,CAAC,IAA8B;IACzD,IAAI,KAAK,GAAG,aAAa,CAAC;IAC1B,IAAI,KAAK,GAAG,aAAa,CAAC;IAC1B,MAAM,OAAO,GAAkB,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,GAAG,EAAE,YAAY,CAAC,KAAK,EAAE,KAAK,CAAC,EAAE,CAAC,CAAC;IAE7E,IAAI,CAAC,OAAO,CAAC,CAAC,KAAK,EAAE,GAAG,EAAE,EAAE;QAC1B,IAAI,KAAK,GAAG,KAAK,CAAC;QAClB,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;YACzB,wEAAwE;YACxE,6CAA6C;YAC7C,IACE,IAAI,CAAC,KAAK,KAAK,SAAS;gBACxB,IAAI,CAAC,KAAK,KAAK,CAAC;gBAChB,KAAK,KAAK,IAAI,CAAC,KAAK,EACpB,CAAC;gBACD,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC;gBACnB,KAAK,GAAG,IAAI,CAAC;YACf,CAAC;YACD,IAAI,IAAI,CAAC,GAAG,KAAK,SAAS,IAAI,IAAI,CAAC,GAAG,KAAK,CAAC,IAAI,KAAK,KAAK,IAAI,CAAC,GAAG,EAAE,CAAC;gBACnE,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC;gBACjB,KAAK,GAAG,IAAI,CAAC;YACf,CAAC;QACH,CAAC;QACD,IAAI,CAAC,KAAK;YAAE,OAAO;QACnB,MAAM,GAAG,GAAG,YAAY,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC;QACvC,6EAA6E;QAC7E,6EAA6E;QAC7E,2EAA2E;QAC3E,wEAAwE;QACxE,mBAAmB;QACnB,IAAI,GAAG,KAAK,CAAC;YAAE,OAAO,CAAC,CAAC,CAAC,GAAG,EAAE,GAAG,EAAE,CAAC,EAAE,GAAG,EAAE,CAAC;;YACvC,OAAO,CAAC,IAAI,CAAC,EAAE,GAAG,EAAE,GAAG,EAAE,CAAC,CAAC;IAClC,CAAC,CAAC,CAAC;IAEH,OAAO,OAAO,CAAC;AACjB,CAAC"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The shape `music_io`'s `TrackerCodec.decode` produces.
|
|
3
|
+
*
|
|
4
|
+
* Re-exported rather than redeclared: the model lives in music_types beside
|
|
5
|
+
* `MidiFile`, so both halves of the import path see the same type.
|
|
6
|
+
*/
|
|
7
|
+
export type { TrackerCell, TrackerFormat, TrackerInstrument, TrackerModule, } from '@sudobility/music_types';
|
|
8
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/mod/types.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,YAAY,EACV,WAAW,EACX,aAAa,EACb,iBAAiB,EACjB,aAAa,GACd,MAAM,yBAAyB,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/mod/types.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/** MusicXML `<type>` element values this adapter supports (whole down to 32nd; no 64th+ or breve/long). */
|
|
2
|
+
export type MusicXmlNoteType = 'whole' | 'half' | 'quarter' | 'eighth' | '16th' | '32nd';
|
|
3
|
+
/** True for any string this adapter recognizes as a `MusicXmlNoteType`. */
|
|
4
|
+
export declare function isMusicXmlNoteType(value: string): value is MusicXmlNoteType;
|
|
5
|
+
export type NotatedDuration = {
|
|
6
|
+
ticks: number;
|
|
7
|
+
type: MusicXmlNoteType;
|
|
8
|
+
dots: number;
|
|
9
|
+
};
|
|
10
|
+
/**
|
|
11
|
+
* Splits `ticks` into one or more notated segments (type + dot count) that
|
|
12
|
+
* together sum back to `ticks`, via `decomposeDuration`'s greedy largest-
|
|
13
|
+
* fit-first algorithm (which itself only ever emits base/singly-dotted
|
|
14
|
+
* `DURATIONS` lengths, or — as a last resort — a residual shorter than a
|
|
15
|
+
* thirty-second note). More than one segment means the caller must emit
|
|
16
|
+
* tied `<note>` elements for `ticks` to round-trip exactly.
|
|
17
|
+
*
|
|
18
|
+
* A residual too short to classify (shorter than a thirty-second note; only
|
|
19
|
+
* possible for a tick length that isn't a multiple of the score's own
|
|
20
|
+
* smallest grid unit) is notated as a thirty-second note whose `<duration>`
|
|
21
|
+
* still carries its true, smaller tick length — MusicXML's `<duration>` is
|
|
22
|
+
* authoritative for playback timing, so `<type>` is just a best-effort
|
|
23
|
+
* visual approximation in this pathological case.
|
|
24
|
+
*/
|
|
25
|
+
export declare function notateDuration(ticks: number, ppq: number): NotatedDuration[];
|
|
26
|
+
/**
|
|
27
|
+
* Ticks for a MusicXML `type` + dot count at `ppq`, via the general dotted-
|
|
28
|
+
* duration formula `base * (2 - 2^-dots)` (dots=0 -> base; dots=1 -> 1.5x;
|
|
29
|
+
* dots=2 -> 1.75x; ...), so import can size a note from `<type>`/`<dot>`
|
|
30
|
+
* alone (e.g. a `<duration>`-less shorthand rest) even for a dot count
|
|
31
|
+
* beyond the singly-dotted variants this adapter's own export emits.
|
|
32
|
+
*/
|
|
33
|
+
export declare function ticksForNotatedType(type: MusicXmlNoteType, dots: number, ppq: number): number;
|
|
34
|
+
//# sourceMappingURL=duration-map.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"duration-map.d.ts","sourceRoot":"","sources":["../../src/musicxml/duration-map.ts"],"names":[],"mappings":"AAYA,2GAA2G;AAC3G,MAAM,MAAM,gBAAgB,GAC1B,OAAO,GAAG,MAAM,GAAG,SAAS,GAAG,QAAQ,GAAG,MAAM,GAAG,MAAM,CAAC;AAgC5D,2EAA2E;AAC3E,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,MAAM,GAAG,KAAK,IAAI,gBAAgB,CAE3E;AAED,MAAM,MAAM,eAAe,GAAG;IAC5B,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,gBAAgB,CAAC;IACvB,IAAI,EAAE,MAAM,CAAC;CACd,CAAC;AAgBF;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,eAAe,EAAE,CAU5E;AAED;;;;;;GAMG;AACH,wBAAgB,mBAAmB,CACjC,IAAI,EAAE,gBAAgB,EACtB,IAAI,EAAE,MAAM,EACZ,GAAG,EAAE,MAAM,GACV,MAAM,CAIR"}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { decomposeDuration } from '@sudobility/music_types';
|
|
2
|
+
import { ticksFor } from '@sudobility/music_types';
|
|
3
|
+
const BASE_NAMES = [
|
|
4
|
+
'whole',
|
|
5
|
+
'half',
|
|
6
|
+
'quarter',
|
|
7
|
+
'eighth',
|
|
8
|
+
'sixteenth',
|
|
9
|
+
'thirtysecond',
|
|
10
|
+
];
|
|
11
|
+
const BASE_NAME_TO_XML_TYPE = {
|
|
12
|
+
whole: 'whole',
|
|
13
|
+
half: 'half',
|
|
14
|
+
quarter: 'quarter',
|
|
15
|
+
eighth: 'eighth',
|
|
16
|
+
sixteenth: '16th',
|
|
17
|
+
thirtysecond: '32nd',
|
|
18
|
+
};
|
|
19
|
+
const XML_TYPE_TO_BASE_NAME = {
|
|
20
|
+
whole: 'whole',
|
|
21
|
+
half: 'half',
|
|
22
|
+
quarter: 'quarter',
|
|
23
|
+
eighth: 'eighth',
|
|
24
|
+
'16th': 'sixteenth',
|
|
25
|
+
'32nd': 'thirtysecond',
|
|
26
|
+
};
|
|
27
|
+
/** True for any string this adapter recognizes as a `MusicXmlNoteType`. */
|
|
28
|
+
export function isMusicXmlNoteType(value) {
|
|
29
|
+
return Object.prototype.hasOwnProperty.call(XML_TYPE_TO_BASE_NAME, value);
|
|
30
|
+
}
|
|
31
|
+
/** Matches `ticks` exactly against a base or singly-dotted `DURATIONS` entry at `ppq`, or `null` if none fits. */
|
|
32
|
+
function classifyExact(ticks, ppq) {
|
|
33
|
+
for (const baseName of BASE_NAMES) {
|
|
34
|
+
if (ticksFor(baseName, ppq) === ticks) {
|
|
35
|
+
return { ticks, type: BASE_NAME_TO_XML_TYPE[baseName], dots: 0 };
|
|
36
|
+
}
|
|
37
|
+
const dottedName = `dotted-${baseName}`;
|
|
38
|
+
if (ticksFor(dottedName, ppq) === ticks) {
|
|
39
|
+
return { ticks, type: BASE_NAME_TO_XML_TYPE[baseName], dots: 1 };
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
return null;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Splits `ticks` into one or more notated segments (type + dot count) that
|
|
46
|
+
* together sum back to `ticks`, via `decomposeDuration`'s greedy largest-
|
|
47
|
+
* fit-first algorithm (which itself only ever emits base/singly-dotted
|
|
48
|
+
* `DURATIONS` lengths, or — as a last resort — a residual shorter than a
|
|
49
|
+
* thirty-second note). More than one segment means the caller must emit
|
|
50
|
+
* tied `<note>` elements for `ticks` to round-trip exactly.
|
|
51
|
+
*
|
|
52
|
+
* A residual too short to classify (shorter than a thirty-second note; only
|
|
53
|
+
* possible for a tick length that isn't a multiple of the score's own
|
|
54
|
+
* smallest grid unit) is notated as a thirty-second note whose `<duration>`
|
|
55
|
+
* still carries its true, smaller tick length — MusicXML's `<duration>` is
|
|
56
|
+
* authoritative for playback timing, so `<type>` is just a best-effort
|
|
57
|
+
* visual approximation in this pathological case.
|
|
58
|
+
*/
|
|
59
|
+
export function notateDuration(ticks, ppq) {
|
|
60
|
+
const segments = decomposeDuration(ticks, ppq);
|
|
61
|
+
return segments.map(segmentTicks => classifyExact(segmentTicks, ppq) ?? {
|
|
62
|
+
ticks: segmentTicks,
|
|
63
|
+
type: '32nd',
|
|
64
|
+
dots: 0,
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Ticks for a MusicXML `type` + dot count at `ppq`, via the general dotted-
|
|
69
|
+
* duration formula `base * (2 - 2^-dots)` (dots=0 -> base; dots=1 -> 1.5x;
|
|
70
|
+
* dots=2 -> 1.75x; ...), so import can size a note from `<type>`/`<dot>`
|
|
71
|
+
* alone (e.g. a `<duration>`-less shorthand rest) even for a dot count
|
|
72
|
+
* beyond the singly-dotted variants this adapter's own export emits.
|
|
73
|
+
*/
|
|
74
|
+
export function ticksForNotatedType(type, dots, ppq) {
|
|
75
|
+
const baseTicks = ticksFor(XML_TYPE_TO_BASE_NAME[type], ppq);
|
|
76
|
+
const multiplier = 2 - 2 ** -dots;
|
|
77
|
+
return Math.round(baseTicks * multiplier);
|
|
78
|
+
}
|
|
79
|
+
//# sourceMappingURL=duration-map.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"duration-map.js","sourceRoot":"","sources":["../../src/musicxml/duration-map.ts"],"names":[],"mappings":"AASA,OAAO,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAC;AAC5D,OAAO,EAAE,QAAQ,EAAE,MAAM,yBAAyB,CAAC;AASnD,MAAM,UAAU,GAAuB;IACrC,OAAO;IACP,MAAM;IACN,SAAS;IACT,QAAQ;IACR,WAAW;IACX,cAAc;CACf,CAAC;AAEF,MAAM,qBAAqB,GAA+C;IACxE,KAAK,EAAE,OAAO;IACd,IAAI,EAAE,MAAM;IACZ,OAAO,EAAE,SAAS;IAClB,MAAM,EAAE,QAAQ;IAChB,SAAS,EAAE,MAAM;IACjB,YAAY,EAAE,MAAM;CACrB,CAAC;AAEF,MAAM,qBAAqB,GAA+C;IACxE,KAAK,EAAE,OAAO;IACd,IAAI,EAAE,MAAM;IACZ,OAAO,EAAE,SAAS;IAClB,MAAM,EAAE,QAAQ;IAChB,MAAM,EAAE,WAAW;IACnB,MAAM,EAAE,cAAc;CACvB,CAAC;AAEF,2EAA2E;AAC3E,MAAM,UAAU,kBAAkB,CAAC,KAAa;IAC9C,OAAO,MAAM,CAAC,SAAS,CAAC,cAAc,CAAC,IAAI,CAAC,qBAAqB,EAAE,KAAK,CAAC,CAAC;AAC5E,CAAC;AAQD,kHAAkH;AAClH,SAAS,aAAa,CAAC,KAAa,EAAE,GAAW;IAC/C,KAAK,MAAM,QAAQ,IAAI,UAAU,EAAE,CAAC;QAClC,IAAI,QAAQ,CAAC,QAAQ,EAAE,GAAG,CAAC,KAAK,KAAK,EAAE,CAAC;YACtC,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,qBAAqB,CAAC,QAAQ,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,CAAC;QACnE,CAAC;QACD,MAAM,UAAU,GAAG,UAAU,QAAQ,EAAkB,CAAC;QACxD,IAAI,QAAQ,CAAC,UAAU,EAAE,GAAG,CAAC,KAAK,KAAK,EAAE,CAAC;YACxC,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,qBAAqB,CAAC,QAAQ,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,CAAC;QACnE,CAAC;IACH,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED;;;;;;;;;;;;;;GAcG;AACH,MAAM,UAAU,cAAc,CAAC,KAAa,EAAE,GAAW;IACvD,MAAM,QAAQ,GAAG,iBAAiB,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC;IAC/C,OAAO,QAAQ,CAAC,GAAG,CACjB,YAAY,CAAC,EAAE,CACb,aAAa,CAAC,YAAY,EAAE,GAAG,CAAC,IAAI;QAClC,KAAK,EAAE,YAAY;QACnB,IAAI,EAAE,MAAe;QACrB,IAAI,EAAE,CAAC;KACR,CACJ,CAAC;AACJ,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,mBAAmB,CACjC,IAAsB,EACtB,IAAY,EACZ,GAAW;IAEX,MAAM,SAAS,GAAG,QAAQ,CAAC,qBAAqB,CAAC,IAAI,CAAC,EAAE,GAAG,CAAC,CAAC;IAC7D,MAAM,UAAU,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC;IAClC,OAAO,IAAI,CAAC,KAAK,CAAC,SAAS,GAAG,UAAU,CAAC,CAAC;AAC5C,CAAC"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `Score` -> MusicXML (score-partwise 4.0) export (spec §17). Pure and
|
|
3
|
+
* read-only: never mutates `score`. Built by hand-assembling XML strings (a
|
|
4
|
+
* small `escapeXml` helper covers the handful of characters MusicXML text
|
|
5
|
+
* content needs escaped) rather than depending on an XML-building library,
|
|
6
|
+
* per the Task 8 brief.
|
|
7
|
+
*
|
|
8
|
+
* Velocity has no standard per-note MusicXML representation (the closest,
|
|
9
|
+
* `<sound dynamics="...">`, is a `<direction>`-level playback hint, not a
|
|
10
|
+
* per-note one) — per the Task 8 brief's guidance, this adapter simply
|
|
11
|
+
* doesn't emit velocity on export; `import.ts` documents the matching
|
|
12
|
+
* default it applies on the way back in.
|
|
13
|
+
*/
|
|
14
|
+
import type { Score } from '@sudobility/music_types';
|
|
15
|
+
/** Escapes the five characters not otherwise safe inside MusicXML element text/attribute values. */
|
|
16
|
+
export declare function escapeXml(text: string): string;
|
|
17
|
+
/**
|
|
18
|
+
* Exports `score` as a MusicXML (score-partwise 4.0) document string:
|
|
19
|
+
* work title/composer, part-list (with MIDI program/channel per track),
|
|
20
|
+
* per-track parts with attributes (divisions/key/time, emitted on the
|
|
21
|
+
* first measure and again whenever key/time change; clef, once per part),
|
|
22
|
+
* notes/rests/chords/voices (`<backup>` between voices), ties (splitting a
|
|
23
|
+
* note whose duration doesn't notate as a single value into tied
|
|
24
|
+
* segments), articulations, and tempo (`<sound tempo>` directions, emitted
|
|
25
|
+
* once — in the first part — per tempo-map entry).
|
|
26
|
+
*/
|
|
27
|
+
export declare function exportMusicXml(score: Score): string;
|
|
28
|
+
/**
|
|
29
|
+
* Slugifies `title` into a filesystem/URL-safe download filename (no
|
|
30
|
+
* extension). Identical behavior to `adapters/midi/export.ts`'s
|
|
31
|
+
* `safeFilename` (kept as a local copy so this module has no cross-adapter
|
|
32
|
+
* dependency); see that module for the rationale.
|
|
33
|
+
*/
|
|
34
|
+
export declare function safeFilename(title: string): string;
|
|
35
|
+
//# sourceMappingURL=export.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"export.d.ts","sourceRoot":"","sources":["../../src/musicxml/export.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AACH,OAAO,KAAK,EAMV,KAAK,EAKN,MAAM,yBAAyB,CAAC;AAKjC,oGAAoG;AACpG,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAO9C;AAqTD;;;;;;;;;GASG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,KAAK,GAAG,MAAM,CAyBnD;AAED;;;;;GAKG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAMlD"}
|
|
@@ -0,0 +1,271 @@
|
|
|
1
|
+
import { isNoteEvent } from '@sudobility/music_types';
|
|
2
|
+
import { notateDuration } from './duration-map.js';
|
|
3
|
+
/** Escapes the five characters not otherwise safe inside MusicXML element text/attribute values. */
|
|
4
|
+
export function escapeXml(text) {
|
|
5
|
+
return text
|
|
6
|
+
.replace(/&/g, '&')
|
|
7
|
+
.replace(/</g, '<')
|
|
8
|
+
.replace(/>/g, '>')
|
|
9
|
+
.replace(/"/g, '"')
|
|
10
|
+
.replace(/'/g, ''');
|
|
11
|
+
}
|
|
12
|
+
// ---- Part-list --------------------------------------------------------------
|
|
13
|
+
function buildScorePartXml(track, partId) {
|
|
14
|
+
const instrumentId = `${partId}-I1`;
|
|
15
|
+
return (`<score-part id="${partId}">` +
|
|
16
|
+
`<part-name>${escapeXml(track.name)}</part-name>` +
|
|
17
|
+
`<score-instrument id="${instrumentId}"><instrument-name>${escapeXml(track.instrumentName)}</instrument-name></score-instrument>` +
|
|
18
|
+
`<midi-instrument id="${instrumentId}">` +
|
|
19
|
+
`<midi-channel>${track.midiChannel + 1}</midi-channel>` +
|
|
20
|
+
`<midi-program>${track.midiProgram + 1}</midi-program>` +
|
|
21
|
+
`</midi-instrument>` +
|
|
22
|
+
`</score-part>\n`);
|
|
23
|
+
}
|
|
24
|
+
// ---- Attributes: key / time / clef -------------------------------------------
|
|
25
|
+
function sameTimeSignature(a, b) {
|
|
26
|
+
return a.numerator === b.numerator && a.denominator === b.denominator;
|
|
27
|
+
}
|
|
28
|
+
function sameKeySignature(a, b) {
|
|
29
|
+
return a.fifths === b.fifths && a.mode === b.mode;
|
|
30
|
+
}
|
|
31
|
+
function buildKeyXml(key) {
|
|
32
|
+
return `<key><fifths>${key.fifths}</fifths><mode>${key.mode}</mode></key>`;
|
|
33
|
+
}
|
|
34
|
+
function buildTimeXml(ts) {
|
|
35
|
+
return `<time><beats>${ts.numerator}</beats><beat-type>${ts.denominator}</beat-type></time>`;
|
|
36
|
+
}
|
|
37
|
+
/** MusicXML `<clef>` sign/line for each supported domain `Clef`. */
|
|
38
|
+
const CLEF_SIGN_LINE = {
|
|
39
|
+
treble: { sign: 'G', line: 2 },
|
|
40
|
+
bass: { sign: 'F', line: 4 },
|
|
41
|
+
alto: { sign: 'C', line: 3 },
|
|
42
|
+
tenor: { sign: 'C', line: 4 },
|
|
43
|
+
percussion: { sign: 'percussion' },
|
|
44
|
+
};
|
|
45
|
+
function buildClefXml(clef) {
|
|
46
|
+
const { sign, line } = CLEF_SIGN_LINE[clef];
|
|
47
|
+
return `<clef><sign>${sign}</sign>${line !== undefined ? `<line>${line}</line>` : ''}</clef>`;
|
|
48
|
+
}
|
|
49
|
+
// ---- Tempo directions ---------------------------------------------------------
|
|
50
|
+
/** Tempo events whose tick falls within `[measure.startTick, measure end)`, sorted by tick. */
|
|
51
|
+
function tempoEventsInMeasure(tempoMap, measure) {
|
|
52
|
+
const end = measure.startTick + measure.durationTicks;
|
|
53
|
+
return tempoMap
|
|
54
|
+
.filter(t => t.tick >= measure.startTick && t.tick < end)
|
|
55
|
+
.sort((a, b) => a.tick - b.tick);
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* A `<direction>` carrying a metronome mark and `<sound tempo>` for one
|
|
59
|
+
* tempo change. When the event doesn't fall exactly at the measure's start,
|
|
60
|
+
* an `<offset>` (in divisions from measure start) records its position —
|
|
61
|
+
* MusicXML treats `<offset>` as an authoritative timing hint independent of
|
|
62
|
+
* where the `<direction>` happens to sit among the measure's `<note>`
|
|
63
|
+
* elements, so this doesn't need to be interleaved into the note sequence.
|
|
64
|
+
*/
|
|
65
|
+
function buildTempoDirectionXml(tempo, measure) {
|
|
66
|
+
const offsetTicks = tempo.tick - measure.startTick;
|
|
67
|
+
const offsetXml = offsetTicks > 0 ? `<offset>${offsetTicks}</offset>` : '';
|
|
68
|
+
const bpm = Number.isInteger(tempo.bpm)
|
|
69
|
+
? String(tempo.bpm)
|
|
70
|
+
: tempo.bpm.toFixed(2);
|
|
71
|
+
return (`<direction placement="above">` +
|
|
72
|
+
`<direction-type><metronome><beat-unit>quarter</beat-unit><per-minute>${bpm}</per-minute></metronome></direction-type>` +
|
|
73
|
+
`${offsetXml}` +
|
|
74
|
+
`<sound tempo="${bpm}"/>` +
|
|
75
|
+
`</direction>\n`);
|
|
76
|
+
}
|
|
77
|
+
// ---- Notes --------------------------------------------------------------------
|
|
78
|
+
/** Domain `Articulation` -> MusicXML `<articulations>` child element name. `marcato` maps to MusicXML's `<strong-accent>` (its name for the same mark). */
|
|
79
|
+
const ARTICULATION_ELEMENT = {
|
|
80
|
+
staccato: 'staccato',
|
|
81
|
+
accent: 'accent',
|
|
82
|
+
tenuto: 'tenuto',
|
|
83
|
+
marcato: 'strong-accent',
|
|
84
|
+
};
|
|
85
|
+
function buildArticulationXml(articulation) {
|
|
86
|
+
const name = ARTICULATION_ELEMENT[articulation];
|
|
87
|
+
return name === 'strong-accent' ? '<strong-accent type="up"/>' : `<${name}/>`;
|
|
88
|
+
}
|
|
89
|
+
function buildPitchXml(note) {
|
|
90
|
+
const { step, accidental, octave } = note.pitch;
|
|
91
|
+
const alter = accidental !== 0 ? `<alter>${accidental}</alter>` : '';
|
|
92
|
+
return `<pitch><step>${step}</step>${alter}<octave>${octave}</octave></pitch>`;
|
|
93
|
+
}
|
|
94
|
+
/** One `<note>` element for a pitched note segment (one of possibly several tied segments notating a single `NoteEvent`). */
|
|
95
|
+
function buildPitchedNoteXml(note, opts) {
|
|
96
|
+
const tieElements = [
|
|
97
|
+
opts.tieStop ? '<tie type="stop"/>' : '',
|
|
98
|
+
opts.tieStart ? '<tie type="start"/>' : '',
|
|
99
|
+
].join('');
|
|
100
|
+
const tiedNotations = [
|
|
101
|
+
opts.tieStop ? '<tied type="stop"/>' : '',
|
|
102
|
+
opts.tieStart ? '<tied type="start"/>' : '',
|
|
103
|
+
].join('');
|
|
104
|
+
const articulationsXml = note.articulation
|
|
105
|
+
? `<articulations>${buildArticulationXml(note.articulation)}</articulations>`
|
|
106
|
+
: '';
|
|
107
|
+
const notations = tiedNotations || articulationsXml
|
|
108
|
+
? `<notations>${tiedNotations}${articulationsXml}</notations>`
|
|
109
|
+
: '';
|
|
110
|
+
const dots = '<dot/>'.repeat(opts.segment.dots);
|
|
111
|
+
return (`<note>` +
|
|
112
|
+
`${opts.isChord ? '<chord/>' : ''}` +
|
|
113
|
+
buildPitchXml(note) +
|
|
114
|
+
`<duration>${opts.segment.ticks}</duration>` +
|
|
115
|
+
tieElements +
|
|
116
|
+
`<voice>${opts.voiceNumber}</voice>` +
|
|
117
|
+
`<type>${opts.segment.type}</type>` +
|
|
118
|
+
dots +
|
|
119
|
+
notations +
|
|
120
|
+
`</note>\n`);
|
|
121
|
+
}
|
|
122
|
+
/** One `<note><rest/></note>` element for a rest segment (a rest whose length needs splitting across several `<type>`s isn't tied — MusicXML has no tie concept for rests). */
|
|
123
|
+
function buildRestNoteXml(segment, voiceNumber) {
|
|
124
|
+
const dots = '<dot/>'.repeat(segment.dots);
|
|
125
|
+
return `<note><rest/><duration>${segment.ticks}</duration><voice>${voiceNumber}</voice><type>${segment.type}</type>${dots}</note>\n`;
|
|
126
|
+
}
|
|
127
|
+
/** Groups `events` by exact `startTick`, in ascending tick order, preserving each group's original relative order. */
|
|
128
|
+
function groupByStartTick(events) {
|
|
129
|
+
const order = [];
|
|
130
|
+
const groups = new Map();
|
|
131
|
+
for (const event of events) {
|
|
132
|
+
let group = groups.get(event.startTick);
|
|
133
|
+
if (!group) {
|
|
134
|
+
group = [];
|
|
135
|
+
groups.set(event.startTick, group);
|
|
136
|
+
order.push(event.startTick);
|
|
137
|
+
}
|
|
138
|
+
group.push(event);
|
|
139
|
+
}
|
|
140
|
+
order.sort((a, b) => a - b);
|
|
141
|
+
return order.map(tick => groups.get(tick));
|
|
142
|
+
}
|
|
143
|
+
/**
|
|
144
|
+
* XML for one voice's events, in MusicXML note order. Notes sharing a
|
|
145
|
+
* `startTick` are exported as a chord (the first is the "root" that
|
|
146
|
+
* advances the cursor / determines the notated segments; every note in the
|
|
147
|
+
* group — including the root — is notated using the *root's* duration, a
|
|
148
|
+
* documented simplification that round-trips exactly whenever simultaneous
|
|
149
|
+
* notes share a duration, as every fixture/test in this adapter's scope
|
|
150
|
+
* does; a same-tick note with a genuinely different duration is notated at
|
|
151
|
+
* the root's length instead of its own).
|
|
152
|
+
*/
|
|
153
|
+
function buildVoiceEventsXml(voice, voiceNumber, ppq) {
|
|
154
|
+
let xml = '';
|
|
155
|
+
for (const group of groupByStartTick(voice.events)) {
|
|
156
|
+
const root = group[0];
|
|
157
|
+
if (!isNoteEvent(root)) {
|
|
158
|
+
for (const segment of notateDuration(root.durationTicks, ppq)) {
|
|
159
|
+
xml += buildRestNoteXml(segment, voiceNumber);
|
|
160
|
+
}
|
|
161
|
+
continue;
|
|
162
|
+
}
|
|
163
|
+
const segments = notateDuration(root.durationTicks, ppq);
|
|
164
|
+
group.forEach((event, groupIndex) => {
|
|
165
|
+
const note = event;
|
|
166
|
+
segments.forEach((segment, segmentIndex) => {
|
|
167
|
+
xml += buildPitchedNoteXml(note, {
|
|
168
|
+
isChord: groupIndex > 0,
|
|
169
|
+
segment,
|
|
170
|
+
tieStop: segmentIndex === 0 ? Boolean(note.tieStop) : true,
|
|
171
|
+
tieStart: segmentIndex === segments.length - 1
|
|
172
|
+
? Boolean(note.tieStart)
|
|
173
|
+
: true,
|
|
174
|
+
voiceNumber,
|
|
175
|
+
});
|
|
176
|
+
});
|
|
177
|
+
});
|
|
178
|
+
}
|
|
179
|
+
return xml;
|
|
180
|
+
}
|
|
181
|
+
// ---- Measures / parts -----------------------------------------------------------
|
|
182
|
+
function buildMeasureXml(measure, isFirstMeasure, prevTimeSignature, prevKeySignature, clef, ppq, tempoMap, includeTempo) {
|
|
183
|
+
const attrs = [];
|
|
184
|
+
if (isFirstMeasure)
|
|
185
|
+
attrs.push(`<divisions>${ppq}</divisions>`);
|
|
186
|
+
if (!prevKeySignature ||
|
|
187
|
+
!sameKeySignature(prevKeySignature, measure.keySignature)) {
|
|
188
|
+
attrs.push(buildKeyXml(measure.keySignature));
|
|
189
|
+
}
|
|
190
|
+
if (!prevTimeSignature ||
|
|
191
|
+
!sameTimeSignature(prevTimeSignature, measure.timeSignature)) {
|
|
192
|
+
attrs.push(buildTimeXml(measure.timeSignature));
|
|
193
|
+
}
|
|
194
|
+
if (isFirstMeasure)
|
|
195
|
+
attrs.push(buildClefXml(clef));
|
|
196
|
+
let xml = `<measure number="${measure.index + 1}">\n`;
|
|
197
|
+
if (attrs.length > 0)
|
|
198
|
+
xml += `<attributes>${attrs.join('')}</attributes>\n`;
|
|
199
|
+
if (includeTempo) {
|
|
200
|
+
for (const tempo of tempoEventsInMeasure(tempoMap, measure)) {
|
|
201
|
+
xml += buildTempoDirectionXml(tempo, measure);
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
measure.voices.forEach((voice, voiceIndex) => {
|
|
205
|
+
if (voiceIndex > 0) {
|
|
206
|
+
// Every voice sums exactly to the measure's duration (spec §23), so
|
|
207
|
+
// rewinding by the full measure length always lands back at its start.
|
|
208
|
+
xml += `<backup><duration>${measure.durationTicks}</duration></backup>\n`;
|
|
209
|
+
}
|
|
210
|
+
xml += buildVoiceEventsXml(voice, voiceIndex + 1, ppq);
|
|
211
|
+
});
|
|
212
|
+
xml += `</measure>\n`;
|
|
213
|
+
return xml;
|
|
214
|
+
}
|
|
215
|
+
function buildPartXml(track, partId, ppq, tempoMap, includeTempo) {
|
|
216
|
+
let xml = `<part id="${partId}">\n`;
|
|
217
|
+
let prevTimeSignature = null;
|
|
218
|
+
let prevKeySignature = null;
|
|
219
|
+
track.measures.forEach((measure, index) => {
|
|
220
|
+
xml += buildMeasureXml(measure, index === 0, prevTimeSignature, prevKeySignature, track.clef, ppq, tempoMap, includeTempo);
|
|
221
|
+
prevTimeSignature = measure.timeSignature;
|
|
222
|
+
prevKeySignature = measure.keySignature;
|
|
223
|
+
});
|
|
224
|
+
xml += `</part>\n`;
|
|
225
|
+
return xml;
|
|
226
|
+
}
|
|
227
|
+
// ---- Entry point ------------------------------------------------------------
|
|
228
|
+
/**
|
|
229
|
+
* Exports `score` as a MusicXML (score-partwise 4.0) document string:
|
|
230
|
+
* work title/composer, part-list (with MIDI program/channel per track),
|
|
231
|
+
* per-track parts with attributes (divisions/key/time, emitted on the
|
|
232
|
+
* first measure and again whenever key/time change; clef, once per part),
|
|
233
|
+
* notes/rests/chords/voices (`<backup>` between voices), ties (splitting a
|
|
234
|
+
* note whose duration doesn't notate as a single value into tied
|
|
235
|
+
* segments), articulations, and tempo (`<sound tempo>` directions, emitted
|
|
236
|
+
* once — in the first part — per tempo-map entry).
|
|
237
|
+
*/
|
|
238
|
+
export function exportMusicXml(score) {
|
|
239
|
+
const partIds = score.tracks.map((_, i) => `P${i + 1}`);
|
|
240
|
+
const partList = score.tracks
|
|
241
|
+
.map((track, i) => buildScorePartXml(track, partIds[i]))
|
|
242
|
+
.join('');
|
|
243
|
+
const parts = score.tracks
|
|
244
|
+
.map((track, i) => buildPartXml(track, partIds[i], score.ppq, score.tempoMap, i === 0))
|
|
245
|
+
.join('');
|
|
246
|
+
const work = `<work><work-title>${escapeXml(score.metadata.title)}</work-title></work>\n`;
|
|
247
|
+
const identification = score.metadata.composer
|
|
248
|
+
? `<identification><creator type="composer">${escapeXml(score.metadata.composer)}</creator></identification>\n`
|
|
249
|
+
: '';
|
|
250
|
+
return (`<?xml version="1.0" encoding="UTF-8"?>\n` +
|
|
251
|
+
`<score-partwise version="4.0">\n` +
|
|
252
|
+
work +
|
|
253
|
+
identification +
|
|
254
|
+
`<part-list>\n${partList}</part-list>\n` +
|
|
255
|
+
parts +
|
|
256
|
+
`</score-partwise>\n`);
|
|
257
|
+
}
|
|
258
|
+
/**
|
|
259
|
+
* Slugifies `title` into a filesystem/URL-safe download filename (no
|
|
260
|
+
* extension). Identical behavior to `adapters/midi/export.ts`'s
|
|
261
|
+
* `safeFilename` (kept as a local copy so this module has no cross-adapter
|
|
262
|
+
* dependency); see that module for the rationale.
|
|
263
|
+
*/
|
|
264
|
+
export function safeFilename(title) {
|
|
265
|
+
const slug = title
|
|
266
|
+
.toLowerCase()
|
|
267
|
+
.replace(/[^a-z0-9]+/g, '-')
|
|
268
|
+
.replace(/^-+|-+$/g, '');
|
|
269
|
+
return slug.length > 0 ? slug : 'untitled';
|
|
270
|
+
}
|
|
271
|
+
//# sourceMappingURL=export.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"export.js","sourceRoot":"","sources":["../../src/musicxml/export.ts"],"names":[],"mappings":"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"}
|