@ac-kit/model-music 0.0.0-stage → 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/CHANGELOG.md +24 -0
- package/LICENSE +28 -0
- package/README.md +42 -2
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +4 -0
- package/dist/index.js.map +1 -0
- package/dist/song/song.d.ts +96 -0
- package/dist/song/song.d.ts.map +1 -0
- package/dist/song/song.js +2 -0
- package/dist/song/song.js.map +1 -0
- package/dist/time/meter.d.ts +64 -0
- package/dist/time/meter.d.ts.map +1 -0
- package/dist/time/meter.js +79 -0
- package/dist/time/meter.js.map +1 -0
- package/dist/time/tempo-map.d.ts +81 -0
- package/dist/time/tempo-map.d.ts.map +1 -0
- package/dist/time/tempo-map.js +114 -0
- package/dist/time/tempo-map.js.map +1 -0
- package/package.json +61 -3
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# @ac-kit/model-music
|
|
2
|
+
|
|
3
|
+
## 0.1.1
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- Updated dependencies [235ec3c]
|
|
8
|
+
- @ac-kit/core@0.5.0
|
|
9
|
+
- @ac-kit/algo@0.2.2
|
|
10
|
+
|
|
11
|
+
## 0.1.0
|
|
12
|
+
|
|
13
|
+
### Minor Changes
|
|
14
|
+
|
|
15
|
+
- eb2aa3d: First release: `Song`, a piece of music as notes timed in ticks and seconds,
|
|
16
|
+
with its parts, `TempoMap` (`composeTempoMap`, `tempoMapSeconds`,
|
|
17
|
+
`tempoMapTick`), time and key signatures, measures (`composeMeasures`), pedals,
|
|
18
|
+
markers and lyrics.
|
|
19
|
+
|
|
20
|
+
### Patch Changes
|
|
21
|
+
|
|
22
|
+
- Updated dependencies [aca1cb1]
|
|
23
|
+
- @ac-kit/core@0.4.0
|
|
24
|
+
- @ac-kit/algo@0.2.1
|
package/LICENSE
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
BSD 3-Clause License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024-2026 Anthony Champagne and contributors
|
|
4
|
+
|
|
5
|
+
Redistribution and use in source and binary forms, with or without
|
|
6
|
+
modification, are permitted provided that the following conditions are met:
|
|
7
|
+
|
|
8
|
+
1. Redistributions of source code must retain the above copyright notice, this
|
|
9
|
+
list of conditions and the following disclaimer.
|
|
10
|
+
|
|
11
|
+
2. Redistributions in binary form must reproduce the above copyright notice,
|
|
12
|
+
this list of conditions and the following disclaimer in the documentation
|
|
13
|
+
and/or other materials provided with the distribution.
|
|
14
|
+
|
|
15
|
+
3. Neither the name of the copyright holder nor the names of its
|
|
16
|
+
contributors may be used to endorse or promote products derived from
|
|
17
|
+
this software without specific prior written permission.
|
|
18
|
+
|
|
19
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
20
|
+
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
21
|
+
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
22
|
+
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
|
23
|
+
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
24
|
+
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
25
|
+
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
|
26
|
+
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
|
27
|
+
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
28
|
+
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
package/README.md
CHANGED
|
@@ -1,3 +1,43 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @ac-kit/model-music
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Music as plain data: a `Song` lists the notes to play with the time each one is
|
|
4
|
+
played at, in ticks and in seconds, together with the parts, tempo map, meter,
|
|
5
|
+
key, measures, pedals, markers and lyrics around them.
|
|
6
|
+
|
|
7
|
+
```ts
|
|
8
|
+
import {
|
|
9
|
+
composeMeasures,
|
|
10
|
+
composeTempoMap,
|
|
11
|
+
tempoMapSeconds,
|
|
12
|
+
} from "@ac-kit/model-music";
|
|
13
|
+
|
|
14
|
+
const tempoMap = composeTempoMap(480, [
|
|
15
|
+
{ tick: 0, microsecondsPerQuarterNote: 600_000 },
|
|
16
|
+
{ tick: 7680, microsecondsPerQuarterNote: 500_000 },
|
|
17
|
+
]);
|
|
18
|
+
|
|
19
|
+
tempoMapSeconds(tempoMap, 1920); // 2.4
|
|
20
|
+
composeMeasures([{ tick: 0, numerator: 3, denominator: 4 }], tempoMap, 7680);
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## What it is for
|
|
24
|
+
|
|
25
|
+
One description of a piece, several sources and several readers. A Standard
|
|
26
|
+
MIDI File and a MusicXML score both convert to a `Song`; a practice tool, a
|
|
27
|
+
falling-notes view and a player read it without knowing where it came from.
|
|
28
|
+
`@ac-kit/format-midi` provides the MIDI conversion.
|
|
29
|
+
|
|
30
|
+
## Contents
|
|
31
|
+
|
|
32
|
+
- `Song`, `SongPart`, `SongNote`, `Spelling`, `SongPedal`, `SongMarker`,
|
|
33
|
+
`SongLyric` — the description. A note carries its part, staff, voice, MIDI
|
|
34
|
+
pitch, velocity and its start and end, plus the written spelling, fingering
|
|
35
|
+
and grace flag a score provides and a MIDI file cannot.
|
|
36
|
+
- `TempoMap`, `composeTempoMap`, `tempoMapSeconds`, `tempoMapTick` — ticks to
|
|
37
|
+
seconds and back, under every tempo change.
|
|
38
|
+
- `TimeSignature`, `KeySignature`, `Measure`, `composeMeasures` — the meter,
|
|
39
|
+
and measures built from it when the source has none of its own.
|
|
40
|
+
|
|
41
|
+
See the
|
|
42
|
+
[generated API docs](https://anthochamp.github.io/node-essentials/api/model-music/)
|
|
43
|
+
for the full reference.
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,gBAAgB,CAAC;AAC/B,cAAc,iBAAiB,CAAC;AAChC,cAAc,qBAAqB,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,gBAAgB,CAAC;AAC/B,cAAc,iBAAiB,CAAC;AAChC,cAAc,qBAAqB,CAAC"}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import type { KeySignature, Measure, TimeSignature } from "../time/meter.js";
|
|
2
|
+
import type { TempoMap } from "../time/tempo-map.js";
|
|
3
|
+
/**
|
|
4
|
+
* A piece of music as the notes to play and the time each is played at, with
|
|
5
|
+
* what a score or a player needs around them.
|
|
6
|
+
*
|
|
7
|
+
* Every `tick` counts in `tempoMap.ticksPerQuarterNote`, and every `seconds` is
|
|
8
|
+
* the time from the start of the song under that tempo map. Every list is
|
|
9
|
+
* sorted by tick; `notes` by start tick, then by pitch.
|
|
10
|
+
*/
|
|
11
|
+
export type Song = {
|
|
12
|
+
title: string | undefined;
|
|
13
|
+
tempoMap: TempoMap;
|
|
14
|
+
/** Where the song ends: its last event, or the end of its last note. */
|
|
15
|
+
endTick: number;
|
|
16
|
+
endSeconds: number;
|
|
17
|
+
parts: SongPart[];
|
|
18
|
+
notes: SongNote[];
|
|
19
|
+
timeSignatures: TimeSignature[];
|
|
20
|
+
keySignatures: KeySignature[];
|
|
21
|
+
measures: Measure[];
|
|
22
|
+
markers: SongMarker[];
|
|
23
|
+
pedals: SongPedal[];
|
|
24
|
+
lyrics: SongLyric[];
|
|
25
|
+
};
|
|
26
|
+
/** One instrument's part, which notes and pedals refer to by index. */
|
|
27
|
+
export type SongPart = {
|
|
28
|
+
name: string | undefined;
|
|
29
|
+
/** General MIDI program, 0–127, when the source names one. */
|
|
30
|
+
program: number | undefined;
|
|
31
|
+
/** Unpitched percussion: `pitch` then names a drum, not a pitch. */
|
|
32
|
+
percussion: boolean;
|
|
33
|
+
/** Staves the part is written on: 2 for a piano's grand staff. */
|
|
34
|
+
staves: number;
|
|
35
|
+
};
|
|
36
|
+
/** A pitch as written: letter, alteration and octave. */
|
|
37
|
+
export type Spelling = {
|
|
38
|
+
step: "A" | "B" | "C" | "D" | "E" | "F" | "G";
|
|
39
|
+
/** Semitones: -1 flat, 1 sharp; fractions for microtones. */
|
|
40
|
+
alter: number;
|
|
41
|
+
/** Scientific pitch notation: middle C is C4. */
|
|
42
|
+
octave: number;
|
|
43
|
+
};
|
|
44
|
+
/** One sounding note. Tied notes are one note. */
|
|
45
|
+
export type SongNote = {
|
|
46
|
+
/** Index into `Song.parts`. */
|
|
47
|
+
part: number;
|
|
48
|
+
/** 1-based staff of the part, when the source says. */
|
|
49
|
+
staff: number | undefined;
|
|
50
|
+
/** Voice within the part, when the source says. */
|
|
51
|
+
voice: number | undefined;
|
|
52
|
+
/** MIDI note number, 0–127; 60 is middle C. */
|
|
53
|
+
pitch: number;
|
|
54
|
+
/** On the MIDI velocity scale, 1–127. */
|
|
55
|
+
velocity: number;
|
|
56
|
+
startTick: number;
|
|
57
|
+
endTick: number;
|
|
58
|
+
startSeconds: number;
|
|
59
|
+
endSeconds: number;
|
|
60
|
+
/** The written pitch, which the MIDI number alone cannot tell (C♯ or D♭). */
|
|
61
|
+
spelling?: Spelling;
|
|
62
|
+
/** Finger number, 1 (thumb) to 5. */
|
|
63
|
+
fingering?: number;
|
|
64
|
+
/** A grace note, played in no time of its own as written. */
|
|
65
|
+
grace?: boolean;
|
|
66
|
+
};
|
|
67
|
+
/** A named point in the song. */
|
|
68
|
+
export type SongMarker = {
|
|
69
|
+
tick: number;
|
|
70
|
+
seconds: number;
|
|
71
|
+
text: string;
|
|
72
|
+
/** `rehearsal` is a score's rehearsal mark; `cue` a cue point for a performer. */
|
|
73
|
+
kind: "marker" | "cue" | "rehearsal";
|
|
74
|
+
};
|
|
75
|
+
/** A pedal position from `tick` on, until the next change of the same pedal. */
|
|
76
|
+
export type SongPedal = {
|
|
77
|
+
/** Index into `Song.parts`. */
|
|
78
|
+
part: number;
|
|
79
|
+
tick: number;
|
|
80
|
+
seconds: number;
|
|
81
|
+
pedal: "sustain" | "sostenuto" | "soft";
|
|
82
|
+
/** 0 is released, 1 fully pressed; values between are half-pedalling. */
|
|
83
|
+
value: number;
|
|
84
|
+
};
|
|
85
|
+
/** One sung syllable. */
|
|
86
|
+
export type SongLyric = {
|
|
87
|
+
/** Index into `Song.parts`, when the lyrics belong to one. */
|
|
88
|
+
part: number | undefined;
|
|
89
|
+
tick: number;
|
|
90
|
+
seconds: number;
|
|
91
|
+
/** The syllable, with the spaces between words. */
|
|
92
|
+
text: string;
|
|
93
|
+
/** A break before the syllable: a new line, or a new paragraph or screen. */
|
|
94
|
+
break: "none" | "line" | "paragraph";
|
|
95
|
+
};
|
|
96
|
+
//# sourceMappingURL=song.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"song.d.ts","sourceRoot":"","sources":["../../src/song/song.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,OAAO,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAC7E,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AAErD;;;;;;;GAOG;AACH,MAAM,MAAM,IAAI,GAAG;IAClB,KAAK,EAAE,MAAM,GAAG,SAAS,CAAC;IAC1B,QAAQ,EAAE,QAAQ,CAAC;IACnB,wEAAwE;IACxE,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,QAAQ,EAAE,CAAC;IAClB,KAAK,EAAE,QAAQ,EAAE,CAAC;IAClB,cAAc,EAAE,aAAa,EAAE,CAAC;IAChC,aAAa,EAAE,YAAY,EAAE,CAAC;IAC9B,QAAQ,EAAE,OAAO,EAAE,CAAC;IACpB,OAAO,EAAE,UAAU,EAAE,CAAC;IACtB,MAAM,EAAE,SAAS,EAAE,CAAC;IACpB,MAAM,EAAE,SAAS,EAAE,CAAC;CACpB,CAAC;AAEF,uEAAuE;AACvE,MAAM,MAAM,QAAQ,GAAG;IACtB,IAAI,EAAE,MAAM,GAAG,SAAS,CAAC;IACzB,8DAA8D;IAC9D,OAAO,EAAE,MAAM,GAAG,SAAS,CAAC;IAC5B,oEAAoE;IACpE,UAAU,EAAE,OAAO,CAAC;IACpB,kEAAkE;IAClE,MAAM,EAAE,MAAM,CAAC;CACf,CAAC;AAEF,yDAAyD;AACzD,MAAM,MAAM,QAAQ,GAAG;IACtB,IAAI,EAAE,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,CAAC;IAC9C,6DAA6D;IAC7D,KAAK,EAAE,MAAM,CAAC;IACd,iDAAiD;IACjD,MAAM,EAAE,MAAM,CAAC;CACf,CAAC;AAEF,kDAAkD;AAClD,MAAM,MAAM,QAAQ,GAAG;IACtB,+BAA+B;IAC/B,IAAI,EAAE,MAAM,CAAC;IACb,uDAAuD;IACvD,KAAK,EAAE,MAAM,GAAG,SAAS,CAAC;IAC1B,mDAAmD;IACnD,KAAK,EAAE,MAAM,GAAG,SAAS,CAAC;IAC1B,+CAA+C;IAC/C,KAAK,EAAE,MAAM,CAAC;IACd,yCAAyC;IACzC,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,YAAY,EAAE,MAAM,CAAC;IACrB,UAAU,EAAE,MAAM,CAAC;IACnB,6EAA6E;IAC7E,QAAQ,CAAC,EAAE,QAAQ,CAAC;IACpB,qCAAqC;IACrC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,6DAA6D;IAC7D,KAAK,CAAC,EAAE,OAAO,CAAC;CAChB,CAAC;AAEF,iCAAiC;AACjC,MAAM,MAAM,UAAU,GAAG;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;IACb,kFAAkF;IAClF,IAAI,EAAE,QAAQ,GAAG,KAAK,GAAG,WAAW,CAAC;CACrC,CAAC;AAEF,gFAAgF;AAChF,MAAM,MAAM,SAAS,GAAG;IACvB,+BAA+B;IAC/B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,SAAS,GAAG,WAAW,GAAG,MAAM,CAAC;IACxC,yEAAyE;IACzE,KAAK,EAAE,MAAM,CAAC;CACd,CAAC;AAEF,yBAAyB;AACzB,MAAM,MAAM,SAAS,GAAG;IACvB,8DAA8D;IAC9D,IAAI,EAAE,MAAM,GAAG,SAAS,CAAC;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,mDAAmD;IACnD,IAAI,EAAE,MAAM,CAAC;IACb,6EAA6E;IAC7E,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,WAAW,CAAC;CACrC,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"song.js","sourceRoot":"","sources":["../../src/song/song.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { type TempoMap } from "./tempo-map.js";
|
|
2
|
+
/**
|
|
3
|
+
* A time signature in force from `tick` on: `numerator` beats of
|
|
4
|
+
* `1/denominator` notes per measure.
|
|
5
|
+
*/
|
|
6
|
+
export type TimeSignature = {
|
|
7
|
+
tick: number;
|
|
8
|
+
/** Beats per measure, 1 or more. */
|
|
9
|
+
numerator: number;
|
|
10
|
+
/** The beat's note value: 4 is a quarter note, 8 an eighth. A power of two. */
|
|
11
|
+
denominator: number;
|
|
12
|
+
};
|
|
13
|
+
/** The mode of a key signature, as MusicXML names them. */
|
|
14
|
+
export type KeyMode = "major" | "minor" | "ionian" | "dorian" | "phrygian" | "lydian" | "mixolydian" | "aeolian" | "locrian" | "none";
|
|
15
|
+
/** A key signature in force from `tick` on. */
|
|
16
|
+
export type KeySignature = {
|
|
17
|
+
tick: number;
|
|
18
|
+
/** Sharps when positive, flats when negative, -7–7. */
|
|
19
|
+
fifths: number;
|
|
20
|
+
mode: KeyMode;
|
|
21
|
+
};
|
|
22
|
+
/** One measure: where it starts and ends, and the meter it is in. */
|
|
23
|
+
export type Measure = {
|
|
24
|
+
/** The measure number as printed: `"1"`, `"12a"`, `"0"` for a pickup. */
|
|
25
|
+
label: string;
|
|
26
|
+
startTick: number;
|
|
27
|
+
endTick: number;
|
|
28
|
+
startSeconds: number;
|
|
29
|
+
endSeconds: number;
|
|
30
|
+
numerator: number;
|
|
31
|
+
denominator: number;
|
|
32
|
+
};
|
|
33
|
+
/** 4/4, the meter of a song before any time signature (MIDI RP-001). */
|
|
34
|
+
export declare const DEFAULT_TIME_SIGNATURE: Omit<TimeSignature, "tick">;
|
|
35
|
+
/**
|
|
36
|
+
* Divides a song into measures from its time signatures, numbering them from 1,
|
|
37
|
+
* when nothing gives the measures themselves — a MIDI file, unlike a score.
|
|
38
|
+
*
|
|
39
|
+
* - Before the first time signature, or with none, the meter is
|
|
40
|
+
* {@link DEFAULT_TIME_SIGNATURE}, 4/4.
|
|
41
|
+
* - A time signature falling inside a measure cuts that measure short, and the
|
|
42
|
+
* next one starts at the change.
|
|
43
|
+
* - Measures cover `[0, endTick)`, with at least one; the last one keeps its full
|
|
44
|
+
* length past `endTick`.
|
|
45
|
+
*
|
|
46
|
+
* Time complexity: O(m + s) in the number of measures and of time signatures,
|
|
47
|
+
* plus O(m log t) in the tempo changes for the times. Memory: O(m).
|
|
48
|
+
*
|
|
49
|
+
* @example
|
|
50
|
+
* composeMeasures([], composeTempoMap(96, []), 400).map(
|
|
51
|
+
* ({ startTick }) => startTick,
|
|
52
|
+
* );
|
|
53
|
+
* // [0, 384]
|
|
54
|
+
*
|
|
55
|
+
* @param timeSignatures - Sorted by tick.
|
|
56
|
+
* @param tempoMap - The song's tempo map, for the times and the tick
|
|
57
|
+
* resolution.
|
|
58
|
+
* @param endTick - Where the song ends.
|
|
59
|
+
* @returns The measures, in order.
|
|
60
|
+
* @throws {RangeError} When a time signature has a numerator below 1 or a
|
|
61
|
+
* denominator that is not a power of two, or when they are not sorted.
|
|
62
|
+
*/
|
|
63
|
+
export declare function composeMeasures(timeSignatures: readonly TimeSignature[], tempoMap: TempoMap, endTick: number): Measure[];
|
|
64
|
+
//# sourceMappingURL=meter.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"meter.d.ts","sourceRoot":"","sources":["../../src/time/meter.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,KAAK,QAAQ,EAAmB,MAAM,gBAAgB,CAAC;AAEhE;;;GAGG;AACH,MAAM,MAAM,aAAa,GAAG;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,oCAAoC;IACpC,SAAS,EAAE,MAAM,CAAC;IAClB,+EAA+E;IAC/E,WAAW,EAAE,MAAM,CAAC;CACpB,CAAC;AAEF,2DAA2D;AAC3D,MAAM,MAAM,OAAO,GAChB,OAAO,GACP,OAAO,GACP,QAAQ,GACR,QAAQ,GACR,UAAU,GACV,QAAQ,GACR,YAAY,GACZ,SAAS,GACT,SAAS,GACT,MAAM,CAAC;AAEV,+CAA+C;AAC/C,MAAM,MAAM,YAAY,GAAG;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,uDAAuD;IACvD,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,OAAO,CAAC;CACd,CAAC;AAEF,qEAAqE;AACrE,MAAM,MAAM,OAAO,GAAG;IACrB,yEAAyE;IACzE,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,YAAY,EAAE,MAAM,CAAC;IACrB,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;CACpB,CAAC;AAEF,wEAAwE;AACxE,eAAO,MAAM,sBAAsB,EAAE,IAAI,CAAC,aAAa,EAAE,MAAM,CAG9D,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,wBAAgB,eAAe,CAC9B,cAAc,EAAE,SAAS,aAAa,EAAE,EACxC,QAAQ,EAAE,QAAQ,EAClB,OAAO,EAAE,MAAM,GACb,OAAO,EAAE,CAkDX"}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { isPowerOfTwo } from "@ac-kit/core";
|
|
2
|
+
import { tempoMapSeconds } from "./tempo-map.js";
|
|
3
|
+
/** 4/4, the meter of a song before any time signature (MIDI RP-001). */
|
|
4
|
+
export const DEFAULT_TIME_SIGNATURE = {
|
|
5
|
+
numerator: 4,
|
|
6
|
+
denominator: 4,
|
|
7
|
+
};
|
|
8
|
+
/**
|
|
9
|
+
* Divides a song into measures from its time signatures, numbering them from 1,
|
|
10
|
+
* when nothing gives the measures themselves — a MIDI file, unlike a score.
|
|
11
|
+
*
|
|
12
|
+
* - Before the first time signature, or with none, the meter is
|
|
13
|
+
* {@link DEFAULT_TIME_SIGNATURE}, 4/4.
|
|
14
|
+
* - A time signature falling inside a measure cuts that measure short, and the
|
|
15
|
+
* next one starts at the change.
|
|
16
|
+
* - Measures cover `[0, endTick)`, with at least one; the last one keeps its full
|
|
17
|
+
* length past `endTick`.
|
|
18
|
+
*
|
|
19
|
+
* Time complexity: O(m + s) in the number of measures and of time signatures,
|
|
20
|
+
* plus O(m log t) in the tempo changes for the times. Memory: O(m).
|
|
21
|
+
*
|
|
22
|
+
* @example
|
|
23
|
+
* composeMeasures([], composeTempoMap(96, []), 400).map(
|
|
24
|
+
* ({ startTick }) => startTick,
|
|
25
|
+
* );
|
|
26
|
+
* // [0, 384]
|
|
27
|
+
*
|
|
28
|
+
* @param timeSignatures - Sorted by tick.
|
|
29
|
+
* @param tempoMap - The song's tempo map, for the times and the tick
|
|
30
|
+
* resolution.
|
|
31
|
+
* @param endTick - Where the song ends.
|
|
32
|
+
* @returns The measures, in order.
|
|
33
|
+
* @throws {RangeError} When a time signature has a numerator below 1 or a
|
|
34
|
+
* denominator that is not a power of two, or when they are not sorted.
|
|
35
|
+
*/
|
|
36
|
+
export function composeMeasures(timeSignatures, tempoMap, endTick) {
|
|
37
|
+
let previousTick = 0;
|
|
38
|
+
for (const { tick, numerator, denominator } of timeSignatures) {
|
|
39
|
+
if (!(numerator >= 1 && Number.isInteger(numerator))) {
|
|
40
|
+
throw new RangeError(`time signature numerator ${numerator} is below 1`);
|
|
41
|
+
}
|
|
42
|
+
if (!(denominator >= 1) || isPowerOfTwo(denominator) !== true) {
|
|
43
|
+
throw new RangeError(`time signature denominator ${denominator} is not a power of two`);
|
|
44
|
+
}
|
|
45
|
+
if (!(tick >= previousTick)) {
|
|
46
|
+
throw new RangeError(`time signature at tick ${tick} is out of order`);
|
|
47
|
+
}
|
|
48
|
+
previousTick = tick;
|
|
49
|
+
}
|
|
50
|
+
const measures = [];
|
|
51
|
+
let meter = DEFAULT_TIME_SIGNATURE;
|
|
52
|
+
let nextIndex = 0;
|
|
53
|
+
let startTick = 0;
|
|
54
|
+
let startSeconds = 0;
|
|
55
|
+
while (startTick < endTick || measures.length === 0) {
|
|
56
|
+
while (nextIndex < timeSignatures.length &&
|
|
57
|
+
timeSignatures[nextIndex].tick <= startTick) {
|
|
58
|
+
meter = timeSignatures[nextIndex];
|
|
59
|
+
nextIndex++;
|
|
60
|
+
}
|
|
61
|
+
const fullLength = (tempoMap.ticksPerQuarterNote * 4 * meter.numerator) / meter.denominator;
|
|
62
|
+
const nextChangeTick = timeSignatures[nextIndex]?.tick ?? Infinity;
|
|
63
|
+
const endOfMeasure = Math.min(startTick + fullLength, nextChangeTick);
|
|
64
|
+
const endSeconds = tempoMapSeconds(tempoMap, endOfMeasure);
|
|
65
|
+
measures.push({
|
|
66
|
+
label: String(measures.length + 1),
|
|
67
|
+
startTick,
|
|
68
|
+
endTick: endOfMeasure,
|
|
69
|
+
startSeconds,
|
|
70
|
+
endSeconds,
|
|
71
|
+
numerator: meter.numerator,
|
|
72
|
+
denominator: meter.denominator,
|
|
73
|
+
});
|
|
74
|
+
startTick = endOfMeasure;
|
|
75
|
+
startSeconds = endSeconds;
|
|
76
|
+
}
|
|
77
|
+
return measures;
|
|
78
|
+
}
|
|
79
|
+
//# sourceMappingURL=meter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"meter.js","sourceRoot":"","sources":["../../src/time/meter.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAE5C,OAAO,EAAiB,eAAe,EAAE,MAAM,gBAAgB,CAAC;AA+ChE,wEAAwE;AACxE,MAAM,CAAC,MAAM,sBAAsB,GAAgC;IAClE,SAAS,EAAE,CAAC;IACZ,WAAW,EAAE,CAAC;CACd,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,MAAM,UAAU,eAAe,CAC9B,cAAwC,EACxC,QAAkB,EAClB,OAAe;IAEf,IAAI,YAAY,GAAG,CAAC,CAAC;IACrB,KAAK,MAAM,EAAE,IAAI,EAAE,SAAS,EAAE,WAAW,EAAE,IAAI,cAAc,EAAE,CAAC;QAC/D,IAAI,CAAC,CAAC,SAAS,IAAI,CAAC,IAAI,MAAM,CAAC,SAAS,CAAC,SAAS,CAAC,CAAC,EAAE,CAAC;YACtD,MAAM,IAAI,UAAU,CAAC,4BAA4B,SAAS,aAAa,CAAC,CAAC;QAC1E,CAAC;QACD,IAAI,CAAC,CAAC,WAAW,IAAI,CAAC,CAAC,IAAI,YAAY,CAAC,WAAW,CAAC,KAAK,IAAI,EAAE,CAAC;YAC/D,MAAM,IAAI,UAAU,CACnB,8BAA8B,WAAW,wBAAwB,CACjE,CAAC;QACH,CAAC;QACD,IAAI,CAAC,CAAC,IAAI,IAAI,YAAY,CAAC,EAAE,CAAC;YAC7B,MAAM,IAAI,UAAU,CAAC,0BAA0B,IAAI,kBAAkB,CAAC,CAAC;QACxE,CAAC;QACD,YAAY,GAAG,IAAI,CAAC;IACrB,CAAC;IAED,MAAM,QAAQ,GAAc,EAAE,CAAC;IAC/B,IAAI,KAAK,GAAG,sBAAsB,CAAC;IACnC,IAAI,SAAS,GAAG,CAAC,CAAC;IAClB,IAAI,SAAS,GAAG,CAAC,CAAC;IAClB,IAAI,YAAY,GAAG,CAAC,CAAC;IACrB,OAAO,SAAS,GAAG,OAAO,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACrD,OACC,SAAS,GAAG,cAAc,CAAC,MAAM;YACjC,cAAc,CAAC,SAAS,CAAE,CAAC,IAAI,IAAI,SAAS,EAC3C,CAAC;YACF,KAAK,GAAG,cAAc,CAAC,SAAS,CAAE,CAAC;YACnC,SAAS,EAAE,CAAC;QACb,CAAC;QAED,MAAM,UAAU,GACf,CAAC,QAAQ,CAAC,mBAAmB,GAAG,CAAC,GAAG,KAAK,CAAC,SAAS,CAAC,GAAG,KAAK,CAAC,WAAW,CAAC;QAC1E,MAAM,cAAc,GAAG,cAAc,CAAC,SAAS,CAAC,EAAE,IAAI,IAAI,QAAQ,CAAC;QACnE,MAAM,YAAY,GAAG,IAAI,CAAC,GAAG,CAAC,SAAS,GAAG,UAAU,EAAE,cAAc,CAAC,CAAC;QACtE,MAAM,UAAU,GAAG,eAAe,CAAC,QAAQ,EAAE,YAAY,CAAC,CAAC;QAC3D,QAAQ,CAAC,IAAI,CAAC;YACb,KAAK,EAAE,MAAM,CAAC,QAAQ,CAAC,MAAM,GAAG,CAAC,CAAC;YAClC,SAAS;YACT,OAAO,EAAE,YAAY;YACrB,YAAY;YACZ,UAAU;YACV,SAAS,EAAE,KAAK,CAAC,SAAS;YAC1B,WAAW,EAAE,KAAK,CAAC,WAAW;SAC9B,CAAC,CAAC;QACH,SAAS,GAAG,YAAY,CAAC;QACzB,YAAY,GAAG,UAAU,CAAC;IAC3B,CAAC;IAED,OAAO,QAAQ,CAAC;AACjB,CAAC"}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
/** A tempo in force from `tick` on, until the next change. */
|
|
2
|
+
export type TempoChange = {
|
|
3
|
+
tick: number;
|
|
4
|
+
/** The time of `tick`, from the start of the song. */
|
|
5
|
+
seconds: number;
|
|
6
|
+
/**
|
|
7
|
+
* Length of a quarter note, in microseconds; 500 000 is 120 bpm. A real
|
|
8
|
+
* number, not limited to what a MIDI Set Tempo event can store.
|
|
9
|
+
*/
|
|
10
|
+
microsecondsPerQuarterNote: number;
|
|
11
|
+
};
|
|
12
|
+
/**
|
|
13
|
+
* How ticks map to seconds over a whole song: the tick resolution and every
|
|
14
|
+
* tempo change, in tick order, the first one at tick 0.
|
|
15
|
+
*
|
|
16
|
+
* Build one with {@link composeTempoMap}, which keeps that invariant and fills
|
|
17
|
+
* in each change's `seconds`.
|
|
18
|
+
*/
|
|
19
|
+
export type TempoMap = {
|
|
20
|
+
/** Ticks per quarter note, the resolution every `tick` of a song uses. */
|
|
21
|
+
ticksPerQuarterNote: number;
|
|
22
|
+
changes: TempoChange[];
|
|
23
|
+
};
|
|
24
|
+
/** A tempo change before its time in seconds is known. */
|
|
25
|
+
export type TempoMark = Omit<TempoChange, "seconds">;
|
|
26
|
+
/** 120 bpm, the tempo of a song before any tempo mark (MIDI RP-001). */
|
|
27
|
+
export declare const DEFAULT_MICROSECONDS_PER_QUARTER_NOTE = 500000;
|
|
28
|
+
/**
|
|
29
|
+
* Builds the tempo map of a song from its tempo marks, computing the time of
|
|
30
|
+
* each.
|
|
31
|
+
*
|
|
32
|
+
* - A song with no mark at tick 0 starts at
|
|
33
|
+
* {@link DEFAULT_MICROSECONDS_PER_QUARTER_NOTE}, 120 bpm.
|
|
34
|
+
* - Of several marks at one tick, the last one given wins.
|
|
35
|
+
* - A mark equal to the tempo already in force is kept out of the map.
|
|
36
|
+
*
|
|
37
|
+
* Time complexity: O(n) in `marks.length`. Memory: O(n).
|
|
38
|
+
*
|
|
39
|
+
* @example
|
|
40
|
+
* composeTempoMap(96, [
|
|
41
|
+
* { tick: 192, microsecondsPerQuarterNote: 1_000_000 },
|
|
42
|
+
* ]);
|
|
43
|
+
* // { ticksPerQuarterNote: 96, changes: [
|
|
44
|
+
* // { tick: 0, seconds: 0, microsecondsPerQuarterNote: 500_000 },
|
|
45
|
+
* // { tick: 192, seconds: 1, microsecondsPerQuarterNote: 1_000_000 } ] }
|
|
46
|
+
*
|
|
47
|
+
* @param ticksPerQuarterNote - The song's tick resolution, above 0.
|
|
48
|
+
* @param marks - The tempo marks, sorted by tick, none below 0.
|
|
49
|
+
* @returns The tempo map.
|
|
50
|
+
* @throws {RangeError} When `ticksPerQuarterNote` or a tempo is not above 0, or
|
|
51
|
+
* when the marks are not sorted from tick 0 on.
|
|
52
|
+
*/
|
|
53
|
+
export declare function composeTempoMap(ticksPerQuarterNote: number, marks: readonly TempoMark[]): TempoMap;
|
|
54
|
+
/**
|
|
55
|
+
* The time of a tick, in seconds from the start of the song.
|
|
56
|
+
*
|
|
57
|
+
* Ticks need not be integers. A tick below 0 extrapolates the first tempo.
|
|
58
|
+
*
|
|
59
|
+
* Time complexity: O(log n) in the number of tempo changes. Memory: O(1).
|
|
60
|
+
*
|
|
61
|
+
* @example
|
|
62
|
+
* tempoMapSeconds(composeTempoMap(96, []), 96); // 0.5
|
|
63
|
+
*
|
|
64
|
+
* @see {@link tempoMapTick} for the converse.
|
|
65
|
+
*/
|
|
66
|
+
export declare function tempoMapSeconds(tempoMap: TempoMap, tick: number): number;
|
|
67
|
+
/**
|
|
68
|
+
* The tick at a time in seconds from the start of the song, as a real number:
|
|
69
|
+
* round it where a whole tick is needed.
|
|
70
|
+
*
|
|
71
|
+
* A time below 0 extrapolates the first tempo.
|
|
72
|
+
*
|
|
73
|
+
* Time complexity: O(log n) in the number of tempo changes. Memory: O(1).
|
|
74
|
+
*
|
|
75
|
+
* @example
|
|
76
|
+
* tempoMapTick(composeTempoMap(96, []), 0.5); // 96
|
|
77
|
+
*
|
|
78
|
+
* @see {@link tempoMapSeconds} for the converse.
|
|
79
|
+
*/
|
|
80
|
+
export declare function tempoMapTick(tempoMap: TempoMap, seconds: number): number;
|
|
81
|
+
//# sourceMappingURL=tempo-map.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tempo-map.d.ts","sourceRoot":"","sources":["../../src/time/tempo-map.ts"],"names":[],"mappings":"AAEA,8DAA8D;AAC9D,MAAM,MAAM,WAAW,GAAG;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,sDAAsD;IACtD,OAAO,EAAE,MAAM,CAAC;IAChB;;;OAGG;IACH,0BAA0B,EAAE,MAAM,CAAC;CACnC,CAAC;AAEF;;;;;;GAMG;AACH,MAAM,MAAM,QAAQ,GAAG;IACtB,0EAA0E;IAC1E,mBAAmB,EAAE,MAAM,CAAC;IAC5B,OAAO,EAAE,WAAW,EAAE,CAAC;CACvB,CAAC;AAEF,0DAA0D;AAC1D,MAAM,MAAM,SAAS,GAAG,IAAI,CAAC,WAAW,EAAE,SAAS,CAAC,CAAC;AAErD,wEAAwE;AACxE,eAAO,MAAM,qCAAqC,SAAU,CAAC;AAE7D;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,wBAAgB,eAAe,CAC9B,mBAAmB,EAAE,MAAM,EAC3B,KAAK,EAAE,SAAS,SAAS,EAAE,GACzB,QAAQ,CA8CV;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,eAAe,CAAC,QAAQ,EAAE,QAAQ,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAQxE;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,YAAY,CAAC,QAAQ,EAAE,QAAQ,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,CAcxE"}
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import { bisectRight } from "@ac-kit/algo";
|
|
2
|
+
/** 120 bpm, the tempo of a song before any tempo mark (MIDI RP-001). */
|
|
3
|
+
export const DEFAULT_MICROSECONDS_PER_QUARTER_NOTE = 500000;
|
|
4
|
+
/**
|
|
5
|
+
* Builds the tempo map of a song from its tempo marks, computing the time of
|
|
6
|
+
* each.
|
|
7
|
+
*
|
|
8
|
+
* - A song with no mark at tick 0 starts at
|
|
9
|
+
* {@link DEFAULT_MICROSECONDS_PER_QUARTER_NOTE}, 120 bpm.
|
|
10
|
+
* - Of several marks at one tick, the last one given wins.
|
|
11
|
+
* - A mark equal to the tempo already in force is kept out of the map.
|
|
12
|
+
*
|
|
13
|
+
* Time complexity: O(n) in `marks.length`. Memory: O(n).
|
|
14
|
+
*
|
|
15
|
+
* @example
|
|
16
|
+
* composeTempoMap(96, [
|
|
17
|
+
* { tick: 192, microsecondsPerQuarterNote: 1_000_000 },
|
|
18
|
+
* ]);
|
|
19
|
+
* // { ticksPerQuarterNote: 96, changes: [
|
|
20
|
+
* // { tick: 0, seconds: 0, microsecondsPerQuarterNote: 500_000 },
|
|
21
|
+
* // { tick: 192, seconds: 1, microsecondsPerQuarterNote: 1_000_000 } ] }
|
|
22
|
+
*
|
|
23
|
+
* @param ticksPerQuarterNote - The song's tick resolution, above 0.
|
|
24
|
+
* @param marks - The tempo marks, sorted by tick, none below 0.
|
|
25
|
+
* @returns The tempo map.
|
|
26
|
+
* @throws {RangeError} When `ticksPerQuarterNote` or a tempo is not above 0, or
|
|
27
|
+
* when the marks are not sorted from tick 0 on.
|
|
28
|
+
*/
|
|
29
|
+
export function composeTempoMap(ticksPerQuarterNote, marks) {
|
|
30
|
+
if (!(ticksPerQuarterNote > 0)) {
|
|
31
|
+
throw new RangeError(`ticks per quarter note ${ticksPerQuarterNote} is not above 0`);
|
|
32
|
+
}
|
|
33
|
+
const changes = [
|
|
34
|
+
{
|
|
35
|
+
tick: 0,
|
|
36
|
+
seconds: 0,
|
|
37
|
+
microsecondsPerQuarterNote: DEFAULT_MICROSECONDS_PER_QUARTER_NOTE,
|
|
38
|
+
},
|
|
39
|
+
];
|
|
40
|
+
for (const { tick, microsecondsPerQuarterNote } of marks) {
|
|
41
|
+
if (!(microsecondsPerQuarterNote > 0)) {
|
|
42
|
+
throw new RangeError(`tempo ${microsecondsPerQuarterNote} µs per quarter note is not above 0`);
|
|
43
|
+
}
|
|
44
|
+
const last = changes.at(-1);
|
|
45
|
+
if (!(tick >= last.tick)) {
|
|
46
|
+
throw new RangeError(`tempo mark at tick ${tick} is out of order`);
|
|
47
|
+
}
|
|
48
|
+
if (tick === last.tick) {
|
|
49
|
+
const previous = changes.at(-2);
|
|
50
|
+
if (previous?.microsecondsPerQuarterNote === microsecondsPerQuarterNote) {
|
|
51
|
+
changes.pop();
|
|
52
|
+
}
|
|
53
|
+
else {
|
|
54
|
+
last.microsecondsPerQuarterNote = microsecondsPerQuarterNote;
|
|
55
|
+
}
|
|
56
|
+
continue;
|
|
57
|
+
}
|
|
58
|
+
if (last.microsecondsPerQuarterNote === microsecondsPerQuarterNote) {
|
|
59
|
+
continue;
|
|
60
|
+
}
|
|
61
|
+
changes.push({
|
|
62
|
+
tick,
|
|
63
|
+
seconds: secondsAfter_(last, tick, ticksPerQuarterNote),
|
|
64
|
+
microsecondsPerQuarterNote,
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
return { ticksPerQuarterNote, changes };
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* The time of a tick, in seconds from the start of the song.
|
|
71
|
+
*
|
|
72
|
+
* Ticks need not be integers. A tick below 0 extrapolates the first tempo.
|
|
73
|
+
*
|
|
74
|
+
* Time complexity: O(log n) in the number of tempo changes. Memory: O(1).
|
|
75
|
+
*
|
|
76
|
+
* @example
|
|
77
|
+
* tempoMapSeconds(composeTempoMap(96, []), 96); // 0.5
|
|
78
|
+
*
|
|
79
|
+
* @see {@link tempoMapTick} for the converse.
|
|
80
|
+
*/
|
|
81
|
+
export function tempoMapSeconds(tempoMap, tick) {
|
|
82
|
+
const { changes, ticksPerQuarterNote } = tempoMap;
|
|
83
|
+
const index = Math.max(0, bisectRight(changes, tick, (change, value) => change.tick - value) - 1);
|
|
84
|
+
return secondsAfter_(changes[index], tick, ticksPerQuarterNote);
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* The tick at a time in seconds from the start of the song, as a real number:
|
|
88
|
+
* round it where a whole tick is needed.
|
|
89
|
+
*
|
|
90
|
+
* A time below 0 extrapolates the first tempo.
|
|
91
|
+
*
|
|
92
|
+
* Time complexity: O(log n) in the number of tempo changes. Memory: O(1).
|
|
93
|
+
*
|
|
94
|
+
* @example
|
|
95
|
+
* tempoMapTick(composeTempoMap(96, []), 0.5); // 96
|
|
96
|
+
*
|
|
97
|
+
* @see {@link tempoMapSeconds} for the converse.
|
|
98
|
+
*/
|
|
99
|
+
export function tempoMapTick(tempoMap, seconds) {
|
|
100
|
+
const { changes, ticksPerQuarterNote } = tempoMap;
|
|
101
|
+
const index = Math.max(0, bisectRight(changes, seconds, (change, value) => change.seconds - value) -
|
|
102
|
+
1);
|
|
103
|
+
const change = changes[index];
|
|
104
|
+
return (change.tick +
|
|
105
|
+
((seconds - change.seconds) * 1000000 * ticksPerQuarterNote) /
|
|
106
|
+
change.microsecondsPerQuarterNote);
|
|
107
|
+
}
|
|
108
|
+
// Measured from the change rather than accumulated, so a long song does not drift.
|
|
109
|
+
function secondsAfter_(change, tick, ticksPerQuarterNote) {
|
|
110
|
+
return (change.seconds +
|
|
111
|
+
((tick - change.tick) * change.microsecondsPerQuarterNote) /
|
|
112
|
+
(1000000 * ticksPerQuarterNote));
|
|
113
|
+
}
|
|
114
|
+
//# sourceMappingURL=tempo-map.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tempo-map.js","sourceRoot":"","sources":["../../src/time/tempo-map.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,64 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ac-kit/model-music",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"
|
|
5
|
-
"
|
|
3
|
+
"version": "0.1.1",
|
|
4
|
+
"description": "Music as plain data: the notes of a song with their times in ticks and seconds, its parts, tempo map, meter, measures, pedals, markers and lyrics.",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"measures",
|
|
7
|
+
"music",
|
|
8
|
+
"notes",
|
|
9
|
+
"song",
|
|
10
|
+
"tempo-map"
|
|
11
|
+
],
|
|
12
|
+
"homepage": "https://anthochamp.github.io/node-essentials/api/model-music/",
|
|
13
|
+
"bugs": {
|
|
14
|
+
"url": "https://git.grenadine.io/anthochamp/node-essentials/issues"
|
|
15
|
+
},
|
|
16
|
+
"license": "BSD-3-Clause",
|
|
17
|
+
"author": "Anthony Champagne <dev@anthonychampagne.fr>",
|
|
18
|
+
"repository": {
|
|
19
|
+
"type": "git",
|
|
20
|
+
"url": "git+https://git.grenadine.io/anthochamp/node-essentials.git",
|
|
21
|
+
"directory": "packages/model-music"
|
|
22
|
+
},
|
|
23
|
+
"files": [
|
|
24
|
+
"dist"
|
|
25
|
+
],
|
|
26
|
+
"type": "module",
|
|
27
|
+
"sideEffects": false,
|
|
28
|
+
"main": "./dist/index.js",
|
|
29
|
+
"exports": {
|
|
30
|
+
".": "./dist/index.js"
|
|
31
|
+
},
|
|
32
|
+
"publishConfig": {
|
|
33
|
+
"main": "./dist/index.js",
|
|
34
|
+
"exports": {
|
|
35
|
+
".": "./dist/index.js"
|
|
36
|
+
},
|
|
37
|
+
"access": "public"
|
|
38
|
+
},
|
|
39
|
+
"scripts": {
|
|
40
|
+
"compile": "tsc -p ./tsconfig.build.json -outDir dist",
|
|
41
|
+
"compile:check": "tsc -noEmit -p ./tsconfig.json",
|
|
42
|
+
"clean": "rimraf dist",
|
|
43
|
+
"build": "yarn clean && yarn compile",
|
|
44
|
+
"test": "yarn run -T vitest run",
|
|
45
|
+
"prepack": "yarn build"
|
|
46
|
+
},
|
|
47
|
+
"dependencies": {
|
|
48
|
+
"@ac-kit/algo": "^0.2.2",
|
|
49
|
+
"@ac-kit/core": "^0.5.0",
|
|
50
|
+
"@types/web": "^0.0.359"
|
|
51
|
+
},
|
|
52
|
+
"devDependencies": {
|
|
53
|
+
"@ac-kit/tsconfig": "0.1.0",
|
|
54
|
+
"@ac-kit/typedoc-config": "0.1.0",
|
|
55
|
+
"@ac-kit/vitest-config": "0.1.1",
|
|
56
|
+
"@typescript/native": "typescript@7.0.2",
|
|
57
|
+
"rimraf": "6.1.3",
|
|
58
|
+
"typedoc": "0.28.20",
|
|
59
|
+
"typescript": "@typescript/typescript6@6.0.2"
|
|
60
|
+
},
|
|
61
|
+
"engines": {
|
|
62
|
+
"node": ">=24"
|
|
63
|
+
}
|
|
6
64
|
}
|