@miadi/ava8-measure 0.1.0

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.js ADDED
@@ -0,0 +1,183 @@
1
+ /**
2
+ * `@miadi/ava8-measure` — read a rendered artefact and hold it to a claim.
3
+ *
4
+ * Everything else in the family writes. This reads back, and the difference is
5
+ * the whole point: a generator's header states an intention, and at least three
6
+ * tools sit between that intention and the bytes on disk. Each of them has
7
+ * silently changed something at least once.
8
+ *
9
+ * The three measurement surfaces stay behind namespaces, because they are
10
+ * three different subjects and their vocabularies collide on purpose — `read`
11
+ * means one thing for a MIDI file and another for a movement capture, and
12
+ * flattening them into one namespace would force one of them to be renamed to
13
+ * something less honest.
14
+ *
15
+ * ```js
16
+ * import { midi, verify } from "@miadi/ava8-measure"
17
+ *
18
+ * const m = midi.read(bytes)
19
+ * const verdict = verify(bytes, { voidBand: {lo: 45, hi: 53}, mode: "ddorian" })
20
+ * if (!verdict.ok) for (const f of verdict.failed) console.error(f.detail)
21
+ * ```
22
+ */
23
+ export * as midi from "./midi.js";
24
+ export * as movement from "./movement.js";
25
+ export * as fft from "./fft.js";
26
+ export * as audio from "./audio.js";
27
+ import * as midiNS from "./midi.js";
28
+ /**
29
+ * The wire value, counted from zero. ABC writes `%%MIDI channel 10` and every
30
+ * manual says ten; the four bits in the file say nine. A constant of 10 here
31
+ * would exclude channel 11 and let every drum hit through as an intruder in a
32
+ * void band — an open hi-hat is note 46, which sits inside 45-53.
33
+ */
34
+ const DRUM_CHANNEL = 9;
35
+ /**
36
+ * A pitch with its name, or the fact that there is no pitch there.
37
+ *
38
+ * `firstDivergence` carries `null` on the side that ran out of notes, and
39
+ * printing `null (undefined)` at the exact moment a person is trying to find
40
+ * out what changed is worse than saying nothing.
41
+ */
42
+ const named = (pitch) => pitch === null ? "no note (the file ended)" : `${pitch} (${midiNS.pitchName(pitch)})`;
43
+ /**
44
+ * Hold a rendered MIDI to what its author says about it.
45
+ *
46
+ * Nothing here trusts a generator's header. Every number comes out of the
47
+ * bytes.
48
+ *
49
+ * @param bytes the rendered file — not the ABC, not the score, the artefact
50
+ * @param claim what the piece says about itself
51
+ * @param parseMode a mode parser, normally `parseMode` from `@miadi/ava8-atelier`.
52
+ * It is injected rather than imported so that this function stays usable
53
+ * when a caller has their own vocabulary, and so a missing mode name is the
54
+ * caller's error rather than a silent pass.
55
+ */
56
+ export function verify(bytes, claim, parseMode) {
57
+ const read = midiNS.read(bytes);
58
+ const findings = [];
59
+ const note = (name, held, claimed, found, detail) => findings.push({ claim: name, held, claimed, found, detail });
60
+ // -- the register left empty on purpose ----------------------------------
61
+ if (claim.voidBand) {
62
+ const { lo, hi, name = `${claim.voidBand.lo}-${claim.voidBand.hi}` } = claim.voidBand;
63
+ const intruders = [
64
+ ...new Set(read.notes
65
+ .filter((n) => n.channel !== DRUM_CHANNEL)
66
+ .filter((n) => n.pitch >= lo && n.pitch <= hi)
67
+ .map((n) => n.pitch)),
68
+ ].sort((a, b) => a - b);
69
+ note("voidBand", intruders.length === 0, `${lo}-${hi} empty`, intruders, intruders.length === 0
70
+ ? `band ${name} (${lo}-${hi}) is empty of melodic notes, as claimed`
71
+ : `band ${name} (${lo}-${hi}) claims to be empty and holds ${intruders.length} ` +
72
+ `pitch(es): ${intruders.map((p) => `${p} (${midiNS.pitchName(p)})`).join(", ")}. ` +
73
+ `Channel ${DRUM_CHANNEL} was excluded, so these are not drums.`);
74
+ }
75
+ // -- the mode -------------------------------------------------------------
76
+ if (claim.mode) {
77
+ if (!parseMode) {
78
+ note("mode", false, claim.mode, null, `a mode was claimed but no parser was passed; import parseMode from ` +
79
+ `@miadi/ava8-atelier and hand it in — guessing the vocabulary here ` +
80
+ `would turn an unreadable spelling into a pass`);
81
+ }
82
+ else {
83
+ const floor = claim.minPurity ?? 1;
84
+ const purity = midiNS.modePurity(read.notes, parseMode(claim.mode));
85
+ const outside = purity.outside
86
+ .map((o) => `${midiNS.PC_NAMES[o.pc]} ${(o.share * 100).toFixed(2)} %`)
87
+ .join(" · ");
88
+ note("mode", purity.purity >= floor - 1e-12, `${claim.mode} at ${(floor * 100).toFixed(2)} % or better`, purity.purity, purity.purity >= floor - 1e-12
89
+ ? `${(purity.purity * 100).toFixed(2)} % of duration is inside ${claim.mode}`
90
+ : `${claim.mode} claims ${(floor * 100).toFixed(2)} % purity and the file ` +
91
+ `measures ${(purity.purity * 100).toFixed(2)} %. Outside: ${outside || "nothing"}`);
92
+ }
93
+ }
94
+ // -- tempo, read as events, not as a header -------------------------------
95
+ if (claim.tempos) {
96
+ const tol = claim.tempoTolerance ?? 0.5;
97
+ const found = read.tempos.map((t) => t.bpm);
98
+ const held = found.length === claim.tempos.length &&
99
+ claim.tempos.every((want, i) => Math.abs((found[i] ?? Number.NaN) - want) <= tol);
100
+ note("tempos", held, claim.tempos, found, held
101
+ ? `${found.length} tempo event(s), matching the claim within ${tol} bpm`
102
+ : `${claim.tempos.length} tempo event(s) claimed, ${found.length} in the file: ` +
103
+ `${found.map((b) => b.toFixed(2)).join(" → ") || "none"}. ` +
104
+ `A bare Q: line in the body of a multi-voice tune is dropped in silence — ` +
105
+ `write it inline as [Q:1/4=…] and it survives.`);
106
+ }
107
+ // -- the drums, by position and not by count ------------------------------
108
+ if (claim.kickPositions) {
109
+ const dp = midiNS.drumPositions(read.notes, read.division, read.timeSignatures);
110
+ // 36 is the kick. `byPitch` maps pitch -> (eighth slot -> hit count), so the
111
+ // slots a kick actually occupied are that inner map's keys — the count is
112
+ // deliberately not what is compared here, because the right number of hits
113
+ // in the wrong places is exactly the failure this claim exists to catch.
114
+ const found = [...(dp.byPitch.get(36)?.keys() ?? [])].sort((a, b) => a - b);
115
+ const want = [...new Set(claim.kickPositions)].sort((a, b) => a - b);
116
+ const held = found.length === want.length && found.every((p, i) => p === want[i]);
117
+ note("kickPositions", held, want, found, held
118
+ ? `the kick lands on ${found.join(", ")} and nowhere else`
119
+ : `the kick claims ${want.join(", ")} and lands on ${found.join(", ") || "nothing"}. ` +
120
+ `The right number of hits in the wrong places is the failure a count cannot see.`);
121
+ }
122
+ // -- length ----------------------------------------------------------------
123
+ if (claim.seconds) {
124
+ const last = read.notes.reduce((m, n) => Math.max(m, n.endTick), 0);
125
+ const found = midiNS.tickToSeconds(last, read.division, read.tempos);
126
+ const held = Math.abs(found - claim.seconds.value) <= claim.seconds.tolerance;
127
+ note("seconds", held, claim.seconds.value, found, held
128
+ ? `${found.toFixed(2)} s, within ${claim.seconds.tolerance} s of the claim`
129
+ : `${claim.seconds.value} s claimed, ${found.toFixed(2)} s measured`);
130
+ }
131
+ // -- the same material as another rendering -------------------------------
132
+ if (claim.source) {
133
+ const how = claim.sourceMatch ?? "notes";
134
+ const same = midiNS.samePitches(claim.source, bytes);
135
+ const held = how === "notes" ? same.noteForNote : same.multiset;
136
+ note("source", held, how === "notes" ? "note for note" : "the same notes in any order", { noteForNote: same.noteForNote, multiset: same.multiset, countA: same.countA, countB: same.countB }, held
137
+ ? how === "notes"
138
+ ? `${same.countB} notes, identical to the source in order and pitch`
139
+ : `the same ${same.countB} pitches as the source, reordered`
140
+ : same.firstDivergence
141
+ ? `first divergence at note ${same.firstDivergence.index}: ` +
142
+ `source ${named(same.firstDivergence.a)}, this file ${named(same.firstDivergence.b)}`
143
+ : `${same.countA} notes in the source, ${same.countB} here`);
144
+ }
145
+ // -- how many notes there are ---------------------------------------------
146
+ if (claim.noteCount !== undefined) {
147
+ note("noteCount", read.notes.length === claim.noteCount, claim.noteCount, read.notes.length, read.notes.length === claim.noteCount
148
+ ? `${read.notes.length} notes, as claimed`
149
+ : `${claim.noteCount} notes claimed, ${read.notes.length} found`);
150
+ }
151
+ // -- a note that never ends is a defect nobody claimed --------------------
152
+ if (read.unclosed > 0) {
153
+ note("unclosed", false, "every note ends", read.unclosed, `${read.unclosed} note-on(s) never received a note-off. Nothing claimed this; ` +
154
+ `it is reported because a hanging note plays until the file does.`);
155
+ }
156
+ const failed = findings.filter((f) => !f.held);
157
+ return {
158
+ ok: failed.length === 0,
159
+ findings,
160
+ failed,
161
+ checked: findings.map((f) => f.claim),
162
+ read,
163
+ };
164
+ }
165
+ /**
166
+ * One line per finding, for a terminal.
167
+ *
168
+ * A verdict with nothing checked prints as such rather than as a pass — the
169
+ * shape of the output has to make "I verified nothing" look different from
170
+ * "I verified everything and it held".
171
+ */
172
+ export function formatVerdict(verdict) {
173
+ if (verdict.findings.length === 0) {
174
+ return "nothing was claimed, so nothing was checked — this is not a pass";
175
+ }
176
+ const lines = verdict.findings.map((f) => `${f.held ? " ok " : " FAIL "} ${f.claim} ${f.detail}`);
177
+ const failed = verdict.failed.length;
178
+ lines.push(failed === 0
179
+ ? `all ${verdict.findings.length} claim(s) held`
180
+ : `${failed} of ${verdict.findings.length} claim(s) failed`);
181
+ return lines.join("\n");
182
+ }
183
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
package/dist/midi.d.ts ADDED
@@ -0,0 +1,333 @@
1
+ /**
2
+ * midi.ts — read a *rendered* Standard MIDI File and prove what is inside it.
3
+ *
4
+ * Hand-written, and it must stay hand-written. The Python this is ported from
5
+ * (`atelier_midi.py`) refuses to import `mido`, `pretty_midi` or `music21`
6
+ * because none of them exist on the host the atelier runs on, and a plugin
7
+ * that assumes them fails on the one machine it was built for. The same
8
+ * discipline here costs nothing and buys everything: a decoder you can audit
9
+ * in one sitting, with no dependency that can rot underneath it.
10
+ *
11
+ * The port keeps that promise and adds one of its own — **bytes in, never a
12
+ * path**. `read` takes a `Uint8Array` and uses `DataView`, so the whole
13
+ * measuring surface runs unchanged in a browser. Nothing here touches
14
+ * `node:fs`, and nothing here touches `Buffer`.
15
+ *
16
+ * WHAT THIS MODULE IS FOR
17
+ * Verification re-reads the rendered artefact, never the source. A note
18
+ * count that comes out right in the generator is not proof; the same count
19
+ * read back out of the .mid that `abc2midi` produced is. Every function
20
+ * answers one question that decided something in the atelier:
21
+ *
22
+ * registers() did a voice land in a register it was told to avoid
23
+ * bandOccupancy() is the singer's band empty (the 45-53 rule)
24
+ * pitchClasses() what mode does the rendered piece actually sit in
25
+ * modePurity() how much of it stays inside the field
26
+ * samePitches() were his notes changed between source and render
27
+ * drumPositions() is the kick really on the floor, eighth by eighth
28
+ * tempos did the mid-tune tempo change survive (a bare Q: does not)
29
+ *
30
+ * UNITS
31
+ * Tick durations are musical durations. Pitch-class and purity shares are
32
+ * weighted in ticks on purpose: they describe the written field, and a tempo
33
+ * change must not reweight it. Anything reported in seconds says so.
34
+ *
35
+ * @packageDocumentation
36
+ */
37
+ /**
38
+ * Channel index 9 — "channel 10" in ABC, in General MIDI, and in every manual.
39
+ *
40
+ * Note the deliberate divergence from `@miadi/ava8-atelier`, whose
41
+ * `DRUM_CHANNEL` is `10` because it names the channel the way a musician
42
+ * writes it. A decoder reads the wire, and on the wire the nibble is 9.
43
+ */
44
+ export declare const DRUM_CHANNEL = 9;
45
+ /** The General MIDI drum names the atelier actually uses, by note number. */
46
+ export declare const GM_DRUM_NAMES: Readonly<Record<number, string>>;
47
+ /** Sharp spellings, because a decoder has no key signature to spell against. */
48
+ export declare const PC_NAMES: readonly string[];
49
+ /** One paired note-on/note-off. Ticks are absolute within its own track. */
50
+ export interface Note {
51
+ readonly startTick: number;
52
+ readonly endTick: number;
53
+ readonly pitch: number;
54
+ readonly velocity: number;
55
+ readonly channel: number;
56
+ readonly track: number;
57
+ }
58
+ /** Sounding length in ticks. A musical duration, never seconds. */
59
+ export declare function noteDuration(note: Note): number;
60
+ /** One `set tempo` meta event, with the bpm already worked out. */
61
+ export interface Tempo {
62
+ readonly tick: number;
63
+ readonly usecPerQuarter: number;
64
+ readonly bpm: number;
65
+ readonly track: number;
66
+ }
67
+ /** One `time signature` meta event, denominator already un-log2'd. */
68
+ export interface TimeSignature {
69
+ readonly tick: number;
70
+ readonly numerator: number;
71
+ readonly denominator: number;
72
+ readonly clocksPerClick: number;
73
+ readonly notated32ndPerQuarter: number;
74
+ readonly track: number;
75
+ }
76
+ /** Everything read out of one file. */
77
+ export interface MidiRead {
78
+ readonly notes: Note[];
79
+ readonly division: number;
80
+ readonly tempos: Tempo[];
81
+ readonly timeSignatures: TimeSignature[];
82
+ readonly format: number;
83
+ readonly nTracks: number;
84
+ readonly trackNames: Map<number, string>;
85
+ readonly unclosed: number;
86
+ /** Whatever label the caller passed in. The reader never opened anything. */
87
+ readonly path: string;
88
+ }
89
+ /**
90
+ * Read a Standard MIDI File into paired notes. This is the only door in.
91
+ *
92
+ * Handles: format 0 and 1 (and 2, read as independent tracks), running status,
93
+ * meta events, sysex (`0xF0` and the `0xF7` escape form), tempo and
94
+ * time-signature maps, unknown chunk types (skipped by their declared length,
95
+ * as the spec instructs), and note-on-with-velocity-0 as note-off.
96
+ *
97
+ * Note-offs are paired FIFO per `(track, channel, pitch)` — pairing on pitch
98
+ * alone silently merges two voices that happen to share a pitch on different
99
+ * channels, and the merge is invisible in the output, which is the worst kind
100
+ * of wrong. Notes left hanging at end of track are closed at the last tick
101
+ * seen and counted in `unclosed`; a non-zero count means the file is malformed
102
+ * and every duration-weighted measure below it is approximate.
103
+ *
104
+ * The sort order is load-bearing, not cosmetic: `(startTick, track, channel,
105
+ * pitch)`. {@link samePitches} re-sorts on top of it and relies on a stable
106
+ * sort to keep the track/channel order under equal pitches, so two readers
107
+ * that disagree here will disagree about whether a file was altered.
108
+ *
109
+ * @param bytes the whole file. Never a path — the library must run in a browser.
110
+ * @param path a label to carry through for reporting. Nothing is opened.
111
+ */
112
+ export declare function read(bytes: Uint8Array, path?: string): MidiRead;
113
+ /**
114
+ * The canonical note order: `(startTick, track, channel, pitch)`.
115
+ *
116
+ * Exported because it is a contract, not an implementation detail. Reproduce
117
+ * it wrong and {@link samePitches} reports a file altered that was not, or
118
+ * misses one that was. Ties on `startTick` are broken by track first, which is
119
+ * why a two-voice render reads voice-by-voice inside each chord.
120
+ */
121
+ export declare function compareNotes(x: Note, y: Note): number;
122
+ /**
123
+ * Ticks per quarter note, or `null` when the file uses SMPTE timing.
124
+ *
125
+ * A SMPTE division (the 0x8000 bit) carries frames and subframes, not a beat
126
+ * grid, so every bar-relative measure — {@link drumPositions} above all — must
127
+ * refuse rather than guess. Returning `null` is how the refusal is made
128
+ * unmissable at the type level.
129
+ */
130
+ export declare function ticksPerBeat(division: number): number | null;
131
+ /**
132
+ * Absolute seconds for a tick, walking the tempo map. Reporting only.
133
+ *
134
+ * Used to say *when* something happened out loud, to a human who is listening
135
+ * rather than counting. No musical decision is taken on seconds: they drift
136
+ * with every tempo event, tick durations do not. With no tempo written, the
137
+ * SMF default of 120 bpm applies — which is exactly the trap a bare `Q:` in an
138
+ * ABC body falls into.
139
+ */
140
+ export declare function tickToSeconds(tick: number, division: number, tempos?: readonly Tempo[]): number;
141
+ /** Pitch extent, note count and channel set for one track. */
142
+ export interface TrackRegister {
143
+ min: number;
144
+ max: number;
145
+ count: number;
146
+ span: number;
147
+ channels: number[];
148
+ pitches: number[];
149
+ }
150
+ /** Where two tracks share a pitch region, and which pitches exactly. */
151
+ export interface RegisterOverlap {
152
+ a: number;
153
+ b: number;
154
+ low: number;
155
+ high: number;
156
+ semitones: number;
157
+ sharedPitches: number[];
158
+ }
159
+ export interface Registers {
160
+ tracks: Map<number, TrackRegister>;
161
+ overlaps: RegisterOverlap[];
162
+ }
163
+ /**
164
+ * Per-track pitch extent and count, plus the overlap between every pair.
165
+ *
166
+ * Decides: whether a voice went where it was told not to go, and whether two
167
+ * voices meant to stay out of each other's way in fact cross. The atelier
168
+ * reads this before publishing, on the *rendered* file — an ABC window that
169
+ * looks disjoint on paper can still collide once `abc2midi` has chosen an
170
+ * octave, because `clef=treble-8` sounds an octave below what is written.
171
+ *
172
+ * Overlaps come back as an array ordered by `(a, b)` rather than a
173
+ * tuple-keyed map, because JavaScript has no tuple key that survives a round
174
+ * trip through JSON.
175
+ */
176
+ export declare function registers(notes: Iterable<Note>): Registers;
177
+ export interface BandOccupancy {
178
+ low: number;
179
+ high: number;
180
+ count: number;
181
+ total: number;
182
+ share: number;
183
+ empty: boolean;
184
+ byTrack: Map<number, number>;
185
+ notes: Note[];
186
+ pitches: number[];
187
+ }
188
+ /**
189
+ * How many notes fall inside a MIDI range, inclusive.
190
+ *
191
+ * This is how "the singer's band is empty" is proven, and it is the only check
192
+ * in the atelier that is pass/fail rather than a number to read. His band was
193
+ * measured on the day: 94.1 % of the park drone lives in MIDI 45-53, so 45-53
194
+ * stays empty in every piece written for him to sing over. Every other measure
195
+ * here reports and lets the human judge; this one has a verdict because the
196
+ * band belongs to a person, not to a threshold someone picked.
197
+ *
198
+ * Returns the count, the share of all notes, the offending notes, and which
199
+ * tracks they came from — naming the track is what makes the correction one
200
+ * edit instead of a hunt.
201
+ */
202
+ export declare function bandOccupancy(notes: Iterable<Note>, lo: number, hi: number): BandOccupancy;
203
+ export interface PitchClassWeight {
204
+ ticks: number;
205
+ share: number;
206
+ events: number;
207
+ }
208
+ export interface PitchClasses {
209
+ totalTicks: number;
210
+ /** Twelve entries, indexed by pitch class. */
211
+ byPc: PitchClassWeight[];
212
+ /** Pitch classes ordered by descending ticks; ties stay in ascending pc order. */
213
+ ranked: number[];
214
+ }
215
+ /**
216
+ * Pitch-class histogram weighted by sounding duration, in ticks.
217
+ *
218
+ * Decides the mode. Counting note *events* lies: a drone struck once and held
219
+ * thirty seconds counts as one, and the piece then reads as whatever the busy
220
+ * voice happens to be doing. Weighting by duration is what found MI PHRYGIEN
221
+ * in his Songbird take — the mi2/re#2 beat held thirty seconds *was* the piece.
222
+ *
223
+ * The drum channel is excluded, because a kick is not a pitch class.
224
+ */
225
+ export declare function pitchClasses(notes: Iterable<Note>): PitchClasses;
226
+ export interface ModePurity {
227
+ allowed: number[];
228
+ purity: number;
229
+ insideTicks: number;
230
+ totalTicks: number;
231
+ /** What left the field, heaviest first. */
232
+ outside: Array<{
233
+ pc: number;
234
+ share: number;
235
+ }>;
236
+ }
237
+ /**
238
+ * Share of sounding duration that sits inside a given pitch-class set.
239
+ *
240
+ * Decides whether a take stays in the field, and names what leaves it. The
241
+ * number is not a verdict on its own: 21 % outside the white field of the bed
242
+ * was not an error in his Songbird — it was the material the intruder in
243
+ * opus 019 is made of. Report the share, name the strays, let the human read.
244
+ *
245
+ * A mode given as text (`"ddorian"`, `"e phrygian"`, `"Bb-major"`, or a bare
246
+ * `"0,2,4,5,7,9,11"`) is handed to `parseMode` from `@miadi/ava8-atelier`.
247
+ * The atelier owns what a mode *is*; this package only owns what a rendered
248
+ * file *did*. Two copies of that table would eventually disagree, and the
249
+ * disagreement would surface as a purity number nobody could explain.
250
+ */
251
+ export declare function modePurity(notes: Iterable<Note>, allowedPcs: string | Iterable<number>): ModePurity;
252
+ export interface SamePitches {
253
+ countA: number;
254
+ countB: number;
255
+ noteForNote: boolean;
256
+ multiset: boolean;
257
+ firstDivergence: {
258
+ index: number;
259
+ a: number | null;
260
+ b: number | null;
261
+ } | null;
262
+ /** Pitches whose count changed, ascending. `delta` is B minus A. */
263
+ multisetDelta: Array<{
264
+ pitch: number;
265
+ delta: number;
266
+ }>;
267
+ }
268
+ /** Either a whole file's bytes, or a note list already read. */
269
+ export type NoteSource = Uint8Array | Iterable<Note>;
270
+ /**
271
+ * Compare two renders note-for-note **and** as a multiset.
272
+ *
273
+ * This is what proves "his notes are unchanged". Two answers, and they are not
274
+ * the same answer — reporting only one of them would let a real change pass or
275
+ * would condemn an honest one:
276
+ *
277
+ * `noteForNote` same pitches in the same order. A variation that keeps his
278
+ * order — a register split, a re-voicing — passes this.
279
+ * `multiset` same pitches in any order. A mirror or a re-ordering passes
280
+ * only this, and that is then the honest claim to make about
281
+ * it: his material, his order changed.
282
+ *
283
+ * On divergence, `firstDivergence` gives the index and both pitches, which is
284
+ * where to look and nowhere else.
285
+ *
286
+ * The comparison re-sorts by `(startTick, pitch)` on top of the canonical
287
+ * order from {@link read}, and relies on the sort being stable so that equal
288
+ * pitches keep their track and channel order. That is why {@link compareNotes}
289
+ * is a contract.
290
+ */
291
+ export declare function samePitches(a: NoteSource, b: NoteSource): SamePitches;
292
+ export interface DrumHit {
293
+ pitch: number;
294
+ name: string;
295
+ tick: number;
296
+ bar: number;
297
+ /** Rounded eighth-note slot, wrapped into the bar. */
298
+ eighth: number;
299
+ /** The unrounded position. A hit at 1.97 is a writer artefact you want to see. */
300
+ exact: number;
301
+ eighthsPerBar: number;
302
+ meter: string;
303
+ track: number;
304
+ }
305
+ export interface DrumPositions {
306
+ hits: DrumHit[];
307
+ count: number;
308
+ /** pitch -> (eighth slot -> hits), both ascending. */
309
+ byPitch: Map<number, Map<number, number>>;
310
+ names: Map<number, string>;
311
+ /** Only for 35 and 36, and only when they sound at all. */
312
+ fourOnTheFloor: Map<number, boolean>;
313
+ }
314
+ /**
315
+ * For channel 10, the eighth-note position of every drum note within its bar.
316
+ *
317
+ * This is what proved four-on-the-floor: the kick reads 0, 2, 4, 6 and nothing
318
+ * else. Reading the ABC would only prove what was *written*; the grid that
319
+ * actually reaches the ear is the one in the rendered file.
320
+ *
321
+ * Bar length follows the time-signature map (numerator x 4/denominator
322
+ * quarters), with 4/4 assumed when the file declares nothing and for anything
323
+ * before the first declaration. `exact` is the unrounded position — a drum
324
+ * that lands on 1.97 instead of 2 is a rounding artefact of the writer, and
325
+ * you want to see that rather than have it quantised away behind your back.
326
+ *
327
+ * Refuses on SMPTE division, which carries no beat grid: there is no honest
328
+ * bar number to return, so it throws instead of inventing one.
329
+ */
330
+ export declare function drumPositions(notes: Iterable<Note>, division: number, timeSignatures?: readonly TimeSignature[]): DrumPositions;
331
+ /** `60` -> `"C4"`. Sharp spellings, middle C at 60, as every decoder prints it. */
332
+ export declare function pitchName(p: number): string;
333
+ //# sourceMappingURL=midi.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"midi.d.ts","sourceRoot":"","sources":["../src/midi.ts"],"names":[],"mappings":"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"}