linked-rolls 0.8.0 → 0.9.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/lib/Collation.d.ts +7 -11
- package/lib/Collation.js +4 -3
- package/lib/EditionView.js +12 -11
- package/lib/Emulation.d.ts +3 -2
- package/lib/Emulation.js +14 -18
- package/lib/Feature.d.ts +5 -4
- package/lib/Quantity.d.ts +61 -0
- package/lib/Quantity.js +25 -0
- package/lib/ReproducingSystem.d.ts +15 -12
- package/lib/RollCopy.d.ts +23 -54
- package/lib/RollCopy.js +8 -8
- package/lib/TrackCalibration.d.ts +10 -9
- package/lib/TrackCalibration.js +6 -5
- package/lib/TrackerBar.d.ts +14 -8
- package/lib/TrackerBar.js +12 -11
- package/lib/alignment.d.ts +3 -2
- package/lib/alignment.js +24 -32
- package/lib/editionOps.js +5 -2
- package/lib/index.d.ts +1 -0
- package/lib/index.js +1 -0
- package/lib/readers/midiSpans.d.ts +5 -3
- package/lib/readers/midiSpans.js +4 -3
- package/lib/readers/spencerMidi.d.ts +6 -5
- package/lib/readers/spencerMidi.js +9 -7
- package/lib/readers/stanfordAton.d.ts +2 -1
- package/lib/readers/stanfordAton.js +25 -26
- package/lib/schema.json +224 -103
- package/lib/systems/welteT100/bar.js +2 -1
- package/lib/systems/welteT100/system.d.ts +8 -7
- package/lib/systems/welteT100/system.js +16 -13
- package/package.json +1 -1
package/lib/Collation.d.ts
CHANGED
|
@@ -1,19 +1,15 @@
|
|
|
1
1
|
import { HorizontalSpan } from "./Feature";
|
|
2
2
|
import { AnySymbol } from "./Symbol";
|
|
3
|
+
import { Millimeters } from "./Quantity";
|
|
3
4
|
/**
|
|
4
|
-
* Tolerance used in collation of roll copies
|
|
5
|
-
*
|
|
6
|
-
* deviation (in mm) when aligning features across copies.
|
|
5
|
+
* Tolerance used in collation of roll copies: the acceptable deviation
|
|
6
|
+
* at either end when aligning features across copies.
|
|
7
7
|
*/
|
|
8
8
|
export interface CollationTolerance {
|
|
9
|
-
/**
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
/**
|
|
14
|
-
* Tolerance at the end position of a feature (in mm).
|
|
15
|
-
*/
|
|
16
|
-
toleranceEnd: number;
|
|
9
|
+
/** Tolerance at the start position of a feature. */
|
|
10
|
+
toleranceStart: Millimeters;
|
|
11
|
+
/** Tolerance at the end position of a feature. */
|
|
12
|
+
toleranceEnd: Millimeters;
|
|
17
13
|
}
|
|
18
14
|
export declare const defaultCollationTolerance: CollationTolerance;
|
|
19
15
|
/** Where a symbol lies along the roll, as its carriers put it, or nothing for a symbol without a place. */
|
package/lib/Collation.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
|
|
1
|
+
import { distance, mm } from "./Quantity";
|
|
2
|
+
export const defaultCollationTolerance = { toleranceStart: mm(5), toleranceEnd: mm(5) };
|
|
2
3
|
/**
|
|
3
4
|
* Two symbols collate when they are of one kind, say the same thing
|
|
4
5
|
* (pitch, or expression type and scope), and lie at about the same
|
|
@@ -16,8 +17,8 @@ export const isCollatable = (a, b, locate, tolerance = defaultCollationTolerance
|
|
|
16
17
|
const there = locate(b)?.horizontal;
|
|
17
18
|
if (!here || !there)
|
|
18
19
|
return false;
|
|
19
|
-
return
|
|
20
|
-
&&
|
|
20
|
+
return distance(here.from, there.from) <= tolerance.toleranceStart
|
|
21
|
+
&& distance(here.to, there.to) <= tolerance.toleranceEnd;
|
|
21
22
|
};
|
|
22
23
|
/** Each of the own symbols with every inherited symbol it collates with. */
|
|
23
24
|
export const collationsOf = (own, inherited, locate, tolerance = defaultCollationTolerance) => own.flatMap(symbol => inherited
|
package/lib/EditionView.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { idOf, idsOf } from "./Assumption";
|
|
2
|
+
import { mean } from "./Quantity";
|
|
2
3
|
export const getAt = (path, obj) => {
|
|
3
4
|
let node = obj;
|
|
4
5
|
for (const key of path) {
|
|
@@ -150,18 +151,18 @@ export class EditionView {
|
|
|
150
151
|
if (carriers.length === 0) {
|
|
151
152
|
return;
|
|
152
153
|
}
|
|
153
|
-
const
|
|
154
|
-
const horizontalTo = carriers.reduce((hAcc, h) => hAcc + h.horizontal.to, 0) / carriers.length;
|
|
155
|
-
const verticalFrom = carriers.reduce((vAcc, v) => vAcc + v.vertical.from, 0) / carriers.length;
|
|
156
|
-
const definedVerticalTo = carriers.filter(v => v.vertical.to !== undefined);
|
|
157
|
-
let verticalTo = undefined;
|
|
158
|
-
if (definedVerticalTo.length > 0) {
|
|
159
|
-
verticalTo = definedVerticalTo
|
|
160
|
-
.reduce((vAcc, v) => vAcc + v.vertical.to, 0) / definedVerticalTo.length;
|
|
161
|
-
}
|
|
154
|
+
const farEnds = carriers.flatMap(carrier => carrier.vertical.to === undefined ? [] : [carrier.vertical.to]);
|
|
162
155
|
return {
|
|
163
|
-
horizontal: {
|
|
164
|
-
|
|
156
|
+
horizontal: {
|
|
157
|
+
unit: 'mm',
|
|
158
|
+
from: mean(carriers.map(carrier => carrier.horizontal.from)),
|
|
159
|
+
to: mean(carriers.map(carrier => carrier.horizontal.to))
|
|
160
|
+
},
|
|
161
|
+
vertical: {
|
|
162
|
+
unit: 'track',
|
|
163
|
+
from: mean(carriers.map(carrier => carrier.vertical.from)),
|
|
164
|
+
...(farEnds.length > 0 && { to: mean(farEnds) })
|
|
165
|
+
}
|
|
165
166
|
};
|
|
166
167
|
}
|
|
167
168
|
snapshot(versionId) {
|
package/lib/Emulation.d.ts
CHANGED
|
@@ -2,9 +2,10 @@ import { MidiFile } from "midifile-ts";
|
|
|
2
2
|
import { EditionView } from "./EditionView";
|
|
3
3
|
import { Version } from "./Version";
|
|
4
4
|
import { AnyPerformedRollFeature, EmulatedCurve, NegotiatedEvent, ReproducingSystem } from "./ReproducingSystem";
|
|
5
|
+
import { Millimeters } from "./Quantity";
|
|
5
6
|
export type EmulationScope = {
|
|
6
|
-
/** Only notes whose onset lies within this span of the roll
|
|
7
|
-
range?: [
|
|
7
|
+
/** Only notes whose onset lies within this span of the roll are played. */
|
|
8
|
+
range?: [Millimeters, Millimeters];
|
|
8
9
|
/** Start the clock at the first note rather than at the beginning of the roll. */
|
|
9
10
|
skipToFirstNote?: boolean;
|
|
10
11
|
};
|
package/lib/Emulation.js
CHANGED
|
@@ -1,20 +1,18 @@
|
|
|
1
1
|
import { MIDIControlEvents } from "midifile-ts";
|
|
2
2
|
import { idOf } from "./Assumption";
|
|
3
3
|
import { pairsAmong, placementsOf } from "./Symbol";
|
|
4
|
+
import { add, mean, mm, seconds, subtract } from "./Quantity";
|
|
4
5
|
/** The punch diameter the edition's copies report, where any of them does. */
|
|
5
6
|
const punchDiameterOf = (view) => {
|
|
6
7
|
const measured = view.edition.copies
|
|
7
8
|
.map(copy => copy.measurements.punchDiameter?.value)
|
|
8
9
|
.filter((value) => value !== undefined && value > 0);
|
|
9
|
-
|
|
10
|
-
return undefined;
|
|
11
|
-
return measured.reduce((sum, value) => sum + value, 0) / measured.length;
|
|
10
|
+
return measured.length > 0 ? mean(measured) : undefined;
|
|
12
11
|
};
|
|
13
12
|
const propertiesOf = (view) => ({
|
|
14
13
|
punchDiameter: punchDiameterOf(view),
|
|
15
14
|
tempo: view.edition.tempoAdjustment
|
|
16
15
|
});
|
|
17
|
-
const mean = (values) => values.reduce((sum, value) => sum + value, 0) / values.length;
|
|
18
16
|
/** The onset a symbol has on each copy carrying it, by the copy's index, as the mean of its holes there. */
|
|
19
17
|
const onsetsByCopy = (view, symbolId) => {
|
|
20
18
|
const symbol = view.get(symbolId);
|
|
@@ -36,7 +34,7 @@ const offsetsBetween = (view, followerId, referenceId) => {
|
|
|
36
34
|
const references = onsetsByCopy(view, referenceId);
|
|
37
35
|
return [...onsetsByCopy(view, followerId)].flatMap(([copy, onset]) => {
|
|
38
36
|
const reference = references.get(copy);
|
|
39
|
-
return reference === undefined ? [] : [onset
|
|
37
|
+
return reference === undefined ? [] : [subtract(onset, reference)];
|
|
40
38
|
});
|
|
41
39
|
};
|
|
42
40
|
/**
|
|
@@ -45,13 +43,11 @@ const offsetsBetween = (view, followerId, referenceId) => {
|
|
|
45
43
|
* that agree with the statement put it, and one gap where none does.
|
|
46
44
|
*/
|
|
47
45
|
const distanceOnSide = (side, offsets, gap) => {
|
|
48
|
-
const agreeing = offsets
|
|
49
|
-
|
|
50
|
-
.map(Math.abs);
|
|
51
|
-
return agreeing.length > 0 ? mean(agreeing) : gap;
|
|
46
|
+
const agreeing = offsets.filter(offset => side === 'before' ? offset < 0 : offset > 0);
|
|
47
|
+
return agreeing.length > 0 ? mm(Math.abs(mean(agreeing))) : gap;
|
|
52
48
|
};
|
|
53
49
|
/**
|
|
54
|
-
* How far each event has to move
|
|
50
|
+
* How far each event has to move for the placements to hold:
|
|
55
51
|
* an aligned event takes the onset of its reference, one placed before
|
|
56
52
|
* or after its reference keeps its place where the measurement has it
|
|
57
53
|
* on that side and is moved there where it does not, and a paired
|
|
@@ -71,12 +67,12 @@ const displacementsOf = (events, offsetsBetween, gap) => {
|
|
|
71
67
|
const distance = (side) => distanceOnSide(side, offsetsBetween(event.id, reference.id), gap);
|
|
72
68
|
switch (placement.relation) {
|
|
73
69
|
case 'alignedWith': return referenceOnset;
|
|
74
|
-
case 'before': return measured < referenceOnset ? measured : referenceOnset
|
|
75
|
-
case 'after': return measured > referenceOnset ? measured : referenceOnset
|
|
70
|
+
case 'before': return measured < referenceOnset ? measured : subtract(referenceOnset, distance('before'));
|
|
71
|
+
case 'after': return measured > referenceOnset ? measured : add(referenceOnset, distance('after'));
|
|
76
72
|
}
|
|
77
73
|
};
|
|
78
74
|
const displacements = new Map(events
|
|
79
|
-
.map((event) => [event, placedOnsetOf(event)
|
|
75
|
+
.map((event) => [event, subtract(placedOnsetOf(event), event.horizontal.from)])
|
|
80
76
|
.filter(([, distance]) => distance !== 0));
|
|
81
77
|
pairsAmong(events).forEach(([one, other]) => {
|
|
82
78
|
const shared = displacements.get(one) ?? displacements.get(other);
|
|
@@ -142,11 +138,11 @@ export class Emulation {
|
|
|
142
138
|
* diameter, or a millimetre, where no copy agrees with a statement.
|
|
143
139
|
*/
|
|
144
140
|
applyConstraints(view) {
|
|
145
|
-
const gap = punchDiameterOf(view) ?? 1;
|
|
141
|
+
const gap = punchDiameterOf(view) ?? mm(1);
|
|
146
142
|
const offsets = (follower, reference) => offsetsBetween(view, follower, reference);
|
|
147
143
|
displacementsOf(this.negotiatedEvents, offsets, gap).forEach((distance, event) => {
|
|
148
|
-
event.horizontal.from
|
|
149
|
-
event.horizontal.to
|
|
144
|
+
event.horizontal.from = add(event.horizontal.from, distance);
|
|
145
|
+
event.horizontal.to = add(event.horizontal.to, distance);
|
|
150
146
|
});
|
|
151
147
|
}
|
|
152
148
|
emulateVersion(version, view, { range, skipToFirstNote = false } = {}) {
|
|
@@ -176,9 +172,9 @@ export class Emulation {
|
|
|
176
172
|
const performance = this.system.perform(this.negotiatedEvents, this.options, propertiesOf(view));
|
|
177
173
|
this.curves = performance.curves;
|
|
178
174
|
const onsets = performance.events.filter(event => event.type === 'noteOn').map(event => event.at);
|
|
179
|
-
const origin = skipToFirstNote && onsets.length > 0 ? Math.min(...onsets) : 0;
|
|
175
|
+
const origin = seconds(skipToFirstNote && onsets.length > 0 ? Math.min(...onsets) : 0);
|
|
180
176
|
this.midiEvents = performance.events
|
|
181
|
-
.map(event => ({ ...event, at: event.at
|
|
177
|
+
.map(event => ({ ...event, at: subtract(event.at, origin) }))
|
|
182
178
|
.filter(event => event.at >= 0)
|
|
183
179
|
.sort((a, b) => a.at - b.at);
|
|
184
180
|
return this.midiEvents;
|
package/lib/Feature.d.ts
CHANGED
|
@@ -2,6 +2,7 @@ import { ObjectAssumption } from "./Assumption";
|
|
|
2
2
|
import { ConditionState } from "./ConditionState";
|
|
3
3
|
import { Text } from "./Symbol";
|
|
4
4
|
import { PartialBy, WithId, WithType } from "./utils";
|
|
5
|
+
import { Millimeters, Track } from "./Quantity";
|
|
5
6
|
/**
|
|
6
7
|
* Describes the horizontal extent of a feature on the roll,
|
|
7
8
|
* measured in millimeters from the beginning of the roll.
|
|
@@ -20,13 +21,13 @@ export interface HorizontalSpan {
|
|
|
20
21
|
* from the beginning of the roll.
|
|
21
22
|
* @see reo:from
|
|
22
23
|
*/
|
|
23
|
-
from:
|
|
24
|
+
from: Millimeters;
|
|
24
25
|
/**
|
|
25
26
|
* The end position of the feature in millimeters
|
|
26
27
|
* from the beginning of the roll.
|
|
27
28
|
* @see reo:to
|
|
28
29
|
*/
|
|
29
|
-
to:
|
|
30
|
+
to: Millimeters;
|
|
30
31
|
}
|
|
31
32
|
/**
|
|
32
33
|
* Describes the vertical extent of a feature on the roll,
|
|
@@ -44,13 +45,13 @@ export interface VerticalSpan {
|
|
|
44
45
|
* The start track number of the feature.
|
|
45
46
|
* @see reo:from
|
|
46
47
|
*/
|
|
47
|
-
from:
|
|
48
|
+
from: Track;
|
|
48
49
|
/**
|
|
49
50
|
* The end track number, if the feature spans multiple tracks.
|
|
50
51
|
* If omitted, the feature occupies a single track.
|
|
51
52
|
* @see reo:to
|
|
52
53
|
*/
|
|
53
|
-
to?:
|
|
54
|
+
to?: Track;
|
|
54
55
|
}
|
|
55
56
|
export declare const featureTypes: readonly ["Hole", "Writing", "Mark", "GluedOn"];
|
|
56
57
|
export type FeatureType = typeof featureTypes[number];
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Numbers that carry their unit in the type. At runtime a quantity is
|
|
3
|
+
* the plain number it was made from, so it compares, sorts and prints
|
|
4
|
+
* as one and goes wherever a number is wanted. The other direction is
|
|
5
|
+
* closed: a number becomes a quantity only through the constructors
|
|
6
|
+
* here, and a quantity in one unit cannot stand in for one in another.
|
|
7
|
+
* The unit names are the ones the records state in their `unit` field.
|
|
8
|
+
*/
|
|
9
|
+
declare const unit: unique symbol;
|
|
10
|
+
export type Unit = 'mm' | 'cm' | 'px' | 'track' | 's' | 'ms' | 'ft/min' | 'm/min';
|
|
11
|
+
export type Quantity<U extends Unit> = number & {
|
|
12
|
+
readonly [unit]: U;
|
|
13
|
+
};
|
|
14
|
+
/** A place along the roll, or any length on the paper. */
|
|
15
|
+
export type Millimeters = Quantity<'mm'>;
|
|
16
|
+
export type Centimeters = Quantity<'cm'>;
|
|
17
|
+
/** A place in a scan. Comparable within one scan only, since the resolution is the scan's. */
|
|
18
|
+
export type Pixels = Quantity<'px'>;
|
|
19
|
+
/** A position across the roll, numbered as the tracker bar does. */
|
|
20
|
+
export type Track = Quantity<'track'>;
|
|
21
|
+
export type Seconds = Quantity<'s'>;
|
|
22
|
+
export type Milliseconds = Quantity<'ms'>;
|
|
23
|
+
export type FeetPerMinute = Quantity<'ft/min'>;
|
|
24
|
+
export type MetersPerMinute = Quantity<'m/min'>;
|
|
25
|
+
export declare const mm: (value: number) => Quantity<"mm">;
|
|
26
|
+
export declare const cm: (value: number) => Quantity<"cm">;
|
|
27
|
+
export declare const px: (value: number) => Quantity<"px">;
|
|
28
|
+
export declare const track: (value: number) => Quantity<"track">;
|
|
29
|
+
export declare const seconds: (value: number) => Quantity<"s">;
|
|
30
|
+
export declare const milliseconds: (value: number) => Quantity<"ms">;
|
|
31
|
+
export declare const feetPerMinute: (value: number) => Quantity<"ft/min">;
|
|
32
|
+
export declare const metersPerMinute: (value: number) => Quantity<"m/min">;
|
|
33
|
+
/**
|
|
34
|
+
* A value together with the unit it was measured in, as a record
|
|
35
|
+
* states it.
|
|
36
|
+
* @see crm:E54 Dimension
|
|
37
|
+
*/
|
|
38
|
+
export interface Measure<U extends Unit> {
|
|
39
|
+
/**
|
|
40
|
+
* The measured value.
|
|
41
|
+
* @see crm:P90 has value
|
|
42
|
+
*/
|
|
43
|
+
value: Quantity<U>;
|
|
44
|
+
/**
|
|
45
|
+
* The unit of measurement.
|
|
46
|
+
* @see crm:P91 has unit
|
|
47
|
+
*/
|
|
48
|
+
unit: U;
|
|
49
|
+
}
|
|
50
|
+
export declare const add: <U extends Unit>(a: Quantity<U>, b: Quantity<NoInfer<U>>) => Quantity<U>;
|
|
51
|
+
export declare const subtract: <U extends Unit>(a: Quantity<U>, b: Quantity<NoInfer<U>>) => Quantity<U>;
|
|
52
|
+
export declare const distance: <U extends Unit>(a: Quantity<U>, b: Quantity<NoInfer<U>>) => Quantity<U>;
|
|
53
|
+
/** A quantity times a plain factor, such as a stretch. */
|
|
54
|
+
export declare const scale: <U extends Unit>(a: Quantity<U>, factor: number) => Quantity<U>;
|
|
55
|
+
export declare const sum: <U extends Unit>(values: readonly Quantity<U>[]) => Quantity<U>;
|
|
56
|
+
export declare const mean: <U extends Unit>(values: readonly Quantity<U>[]) => Quantity<U>;
|
|
57
|
+
/** A place in a scan taken at `dpi` dots per inch, on the paper. */
|
|
58
|
+
export declare const inMillimeters: (place: Pixels, dpi: number) => Millimeters;
|
|
59
|
+
export declare const inCentimeters: (length: Millimeters) => Centimeters;
|
|
60
|
+
export declare const inSeconds: (time: Milliseconds) => Seconds;
|
|
61
|
+
export {};
|
package/lib/Quantity.js
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
const quantity = (value) => value;
|
|
2
|
+
export const mm = (quantity);
|
|
3
|
+
export const cm = (quantity);
|
|
4
|
+
export const px = (quantity);
|
|
5
|
+
export const track = (quantity);
|
|
6
|
+
export const seconds = (quantity);
|
|
7
|
+
export const milliseconds = (quantity);
|
|
8
|
+
export const feetPerMinute = (quantity);
|
|
9
|
+
export const metersPerMinute = (quantity);
|
|
10
|
+
// The second operand takes its unit from the first. Inferred from both,
|
|
11
|
+
// TypeScript would unite two units rather than reject them.
|
|
12
|
+
export const add = (a, b) => quantity(a + b);
|
|
13
|
+
export const subtract = (a, b) => quantity(a - b);
|
|
14
|
+
export const distance = (a, b) => quantity(Math.abs(a - b));
|
|
15
|
+
/** A quantity times a plain factor, such as a stretch. */
|
|
16
|
+
export const scale = (a, factor) => quantity(a * factor);
|
|
17
|
+
// A list is taken at its element type, so one that mixes units yields a
|
|
18
|
+
// union unit rather than an error; only the binary operations reject a mix.
|
|
19
|
+
export const sum = (values) => quantity(values.reduce((total, value) => total + value, 0));
|
|
20
|
+
export const mean = (values) => quantity(sum(values) / values.length);
|
|
21
|
+
const MM_PER_INCH = 25.4;
|
|
22
|
+
/** A place in a scan taken at `dpi` dots per inch, on the paper. */
|
|
23
|
+
export const inMillimeters = (place, dpi) => mm(place / dpi * MM_PER_INCH);
|
|
24
|
+
export const inCentimeters = (length) => cm(length / 10);
|
|
25
|
+
export const inSeconds = (time) => seconds(time / 1000);
|
|
@@ -1,29 +1,32 @@
|
|
|
1
1
|
import { Hole } from "./Feature";
|
|
2
2
|
import { Expression, Note } from "./Symbol";
|
|
3
3
|
import { TrackerBar } from "./TrackerBar";
|
|
4
|
+
import { Millimeters, Quantity, Seconds } from "./Quantity";
|
|
5
|
+
export type SpeedUnit = 'ft/min' | 'm/min';
|
|
4
6
|
/**
|
|
5
|
-
* The playback tempo of the roll
|
|
6
|
-
* and
|
|
7
|
-
* of the roll
|
|
7
|
+
* The playback tempo of the roll as a paper speed, stated at the
|
|
8
|
+
* beginning and at the end, since the speed may change over the
|
|
9
|
+
* course of the roll through acceleration.
|
|
8
10
|
* @see crm:E54 Dimension
|
|
9
11
|
*/
|
|
10
|
-
export interface
|
|
12
|
+
export interface RollTempoIn<U extends SpeedUnit> {
|
|
11
13
|
/**
|
|
12
14
|
* The tempo at the beginning of the roll.
|
|
13
15
|
* @see reo:from
|
|
14
16
|
*/
|
|
15
|
-
startsWith:
|
|
17
|
+
startsWith: Quantity<U>;
|
|
16
18
|
/**
|
|
17
19
|
* The tempo at the end of the roll.
|
|
18
20
|
* @see reo:to
|
|
19
21
|
*/
|
|
20
|
-
endsWith:
|
|
22
|
+
endsWith: Quantity<U>;
|
|
21
23
|
/**
|
|
22
|
-
* The unit of the tempo measurement
|
|
24
|
+
* The unit of the tempo measurement.
|
|
23
25
|
* @see crm:P91 has unit
|
|
24
26
|
*/
|
|
25
|
-
unit:
|
|
27
|
+
unit: U;
|
|
26
28
|
}
|
|
29
|
+
export type RollTempo = RollTempoIn<'ft/min'> | RollTempoIn<'m/min'>;
|
|
27
30
|
/**
|
|
28
31
|
* A note or expression of a version with the dimensions of its carriers
|
|
29
32
|
* averaged in and the editorial assumptions applied, which is all a
|
|
@@ -33,8 +36,8 @@ export type NegotiatedEvent = Omit<Note | Expression, 'carriers'> & Pick<Hole, '
|
|
|
33
36
|
interface PerformedRollFeature<T> {
|
|
34
37
|
type: T;
|
|
35
38
|
performs: NegotiatedEvent;
|
|
36
|
-
/**
|
|
37
|
-
at:
|
|
39
|
+
/** Time from the beginning of the roll. */
|
|
40
|
+
at: Seconds;
|
|
38
41
|
}
|
|
39
42
|
interface PerformedNoteEvent<T> extends PerformedRollFeature<T> {
|
|
40
43
|
pitch: number;
|
|
@@ -76,8 +79,8 @@ export type PedalCurve = CurveSamples & {
|
|
|
76
79
|
export type EmulatedCurve = DynamicsCurve | PedalCurve;
|
|
77
80
|
/** What the edition records about the roll that a mechanism may want to know. */
|
|
78
81
|
export type RollProperties = {
|
|
79
|
-
/** Diameter of the punches
|
|
80
|
-
punchDiameter?:
|
|
82
|
+
/** Diameter of the punches, where the copies record it. */
|
|
83
|
+
punchDiameter?: Millimeters;
|
|
81
84
|
/** The tempo the edition adjusts the roll to, where it states one. */
|
|
82
85
|
tempo?: RollTempo;
|
|
83
86
|
};
|
package/lib/RollCopy.d.ts
CHANGED
|
@@ -6,6 +6,7 @@ import { AnyFeature } from "./Feature";
|
|
|
6
6
|
import { ActorAssignment, DateAssignment, ObjectAssumption } from "./Assumption";
|
|
7
7
|
import { WithId, WithType } from "./utils";
|
|
8
8
|
import { Agent, Concept } from "./Agent";
|
|
9
|
+
import { Measure, Millimeters, Quantity, Track } from "./Quantity";
|
|
9
10
|
/**
|
|
10
11
|
* This condition state is used to describe the roll's
|
|
11
12
|
* paper shrinkage or stretching. It might be calculated
|
|
@@ -39,14 +40,16 @@ export declare const rollConditions: readonly ["general", "paper-stretch"];
|
|
|
39
40
|
* (along the roll length, in mm) and vertical (across tracks).
|
|
40
41
|
*/
|
|
41
42
|
export interface Shift {
|
|
42
|
-
/**
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
43
|
+
/** Along the roll. */
|
|
44
|
+
horizontal: Millimeters;
|
|
45
|
+
/** Across the tracker bar. */
|
|
46
|
+
vertical: Track;
|
|
47
|
+
}
|
|
48
|
+
/** The margins on the treble and bass sides of the roll, in the unit the scan was measured in. */
|
|
49
|
+
export interface Margins<U extends 'px' | 'mm'> {
|
|
50
|
+
treble: Quantity<U>;
|
|
51
|
+
bass: Quantity<U>;
|
|
52
|
+
unit: U;
|
|
50
53
|
}
|
|
51
54
|
/**
|
|
52
55
|
* Describes the production of a roll copy: the manufacturer,
|
|
@@ -140,71 +143,37 @@ export interface RollCopy extends WithType<'RollCopy'>, WithId {
|
|
|
140
143
|
*/
|
|
141
144
|
dimensions: {
|
|
142
145
|
/**
|
|
143
|
-
* The width of the roll
|
|
146
|
+
* The width of the roll.
|
|
144
147
|
* @see reo:width
|
|
145
148
|
*/
|
|
146
|
-
width:
|
|
149
|
+
width: Millimeters;
|
|
147
150
|
/**
|
|
148
|
-
* The total height (length) of the roll
|
|
151
|
+
* The total height (length) of the roll.
|
|
149
152
|
* @see reo:height
|
|
150
153
|
*/
|
|
151
|
-
height:
|
|
154
|
+
height: Millimeters;
|
|
152
155
|
/**
|
|
153
|
-
* The unit of measurement
|
|
156
|
+
* The unit of measurement.
|
|
154
157
|
* @see crm:P91 has unit
|
|
155
158
|
*/
|
|
156
|
-
unit:
|
|
159
|
+
unit: 'mm';
|
|
157
160
|
};
|
|
158
161
|
/**
|
|
159
162
|
* The average diameter of punched holes.
|
|
160
163
|
* @see reo:punchDiameter
|
|
161
164
|
*/
|
|
162
|
-
punchDiameter:
|
|
163
|
-
/**
|
|
164
|
-
* The measured punch diameter value.
|
|
165
|
-
* @see crm:P90 has value
|
|
166
|
-
*/
|
|
167
|
-
value: number;
|
|
168
|
-
/**
|
|
169
|
-
* The unit of measurement (e.g. 'mm').
|
|
170
|
-
* @see crm:P91 has unit
|
|
171
|
-
*/
|
|
172
|
-
unit: string;
|
|
173
|
-
};
|
|
165
|
+
punchDiameter: Measure<'mm'>;
|
|
174
166
|
/**
|
|
175
|
-
* The distance between adjacent tracker bar holes
|
|
167
|
+
* The distance between adjacent tracker bar holes, in the
|
|
168
|
+
* unit the scan was measured in.
|
|
176
169
|
* @see reo:holeSeparation
|
|
177
170
|
*/
|
|
178
|
-
holeSeparation:
|
|
179
|
-
/**
|
|
180
|
-
* The measured hole separation value.
|
|
181
|
-
* @see crm:P90 has value
|
|
182
|
-
*/
|
|
183
|
-
value: number;
|
|
184
|
-
/**
|
|
185
|
-
* The unit of measurement (e.g. 'px', 'mm').
|
|
186
|
-
* @see crm:P91 has unit
|
|
187
|
-
*/
|
|
188
|
-
unit: string;
|
|
189
|
-
};
|
|
171
|
+
holeSeparation: Measure<'px'> | Measure<'mm'>;
|
|
190
172
|
/**
|
|
191
173
|
* The margins on the treble and bass sides of the roll.
|
|
192
174
|
* Not exported to RDF.
|
|
193
175
|
*/
|
|
194
|
-
margins:
|
|
195
|
-
/**
|
|
196
|
-
* The margin on the treble side.
|
|
197
|
-
*/
|
|
198
|
-
treble: number;
|
|
199
|
-
/**
|
|
200
|
-
* The margin on the bass side.
|
|
201
|
-
*/
|
|
202
|
-
bass: number;
|
|
203
|
-
/**
|
|
204
|
-
* The unit of measurement (e.g. 'px', 'mm').
|
|
205
|
-
*/
|
|
206
|
-
unit: string;
|
|
207
|
-
};
|
|
176
|
+
margins: Margins<'px'> | Margins<'mm'>;
|
|
208
177
|
/**
|
|
209
178
|
* The shift applied to align this copy with the others.
|
|
210
179
|
* Not exported to RDF.
|
|
@@ -282,7 +251,7 @@ export declare function asSymbols(features: AnyFeature[], bar?: TrackerBar): Any
|
|
|
282
251
|
* The tracks a copy carries holes on that the tracker bar does not read.
|
|
283
252
|
* A non-empty result usually means the scan is calibrated wrongly.
|
|
284
253
|
*/
|
|
285
|
-
export declare function unreadTracks(features: AnyFeature[], bar?: TrackerBar): Map<
|
|
254
|
+
export declare function unreadTracks(features: AnyFeature[], bar?: TrackerBar): Map<Track, number>;
|
|
286
255
|
/**
|
|
287
256
|
* Falls back to the way the scan geometry was reconstructed before the
|
|
288
257
|
* calibration was recorded: the bass hard margin stood in for the phase
|
package/lib/RollCopy.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { v4 } from "uuid";
|
|
2
2
|
import { welteT100 } from "./systems/welteT100/bar";
|
|
3
3
|
import { assignReference } from "./Assumption";
|
|
4
|
+
import { px, track } from "./Quantity";
|
|
4
5
|
export const rollConditions = [
|
|
5
6
|
'general',
|
|
6
7
|
'paper-stretch'
|
|
@@ -34,8 +35,8 @@ export function unreadTracks(features, bar = welteT100) {
|
|
|
34
35
|
.filter(feature => feature.type === 'Hole')
|
|
35
36
|
.filter(feature => !bar.meaningOf(feature.vertical.from))
|
|
36
37
|
.forEach(feature => {
|
|
37
|
-
const
|
|
38
|
-
counts.set(
|
|
38
|
+
const position = feature.vertical.from;
|
|
39
|
+
counts.set(position, (counts.get(position) || 0) + 1);
|
|
39
40
|
});
|
|
40
41
|
return counts;
|
|
41
42
|
}
|
|
@@ -53,14 +54,13 @@ export const calibrationOf = (copy) => {
|
|
|
53
54
|
if (copy.measurements.trackCalibration) {
|
|
54
55
|
return copy.measurements.trackCalibration;
|
|
55
56
|
}
|
|
56
|
-
const
|
|
57
|
-
|
|
58
|
-
if (separation === undefined || bassMargin === undefined)
|
|
57
|
+
const { holeSeparation, margins } = copy.measurements;
|
|
58
|
+
if (holeSeparation?.unit !== 'px' || margins?.unit !== 'px')
|
|
59
59
|
return undefined;
|
|
60
60
|
return {
|
|
61
61
|
unit: 'px',
|
|
62
|
-
offset:
|
|
63
|
-
separation,
|
|
64
|
-
shift: 0
|
|
62
|
+
offset: px(margins.bass + 1.5 * holeSeparation.value),
|
|
63
|
+
separation: holeSeparation.value,
|
|
64
|
+
shift: track(0)
|
|
65
65
|
};
|
|
66
66
|
};
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { Pixels, Track } from "./Quantity";
|
|
1
2
|
/**
|
|
2
3
|
* Relates a scan to the tracker bar it was read with.
|
|
3
4
|
*
|
|
@@ -12,22 +13,22 @@
|
|
|
12
13
|
export interface TrackCalibration {
|
|
13
14
|
unit: 'px';
|
|
14
15
|
/** Image column of the scanning software's track 0. */
|
|
15
|
-
offset:
|
|
16
|
+
offset: Pixels;
|
|
16
17
|
/** Distance between the centres of adjacent tracks. */
|
|
17
|
-
separation:
|
|
18
|
+
separation: Pixels;
|
|
18
19
|
/** Added to the scanning software's numbering to reach the tracker bar. */
|
|
19
|
-
shift:
|
|
20
|
+
shift: Track;
|
|
20
21
|
}
|
|
21
22
|
/** Image column at the centre of a tracker bar track. */
|
|
22
|
-
export declare const columnOf: (
|
|
23
|
+
export declare const columnOf: (position: Track, calibration: TrackCalibration) => Pixels;
|
|
23
24
|
/** Tracker bar track covering an image column, unrounded. */
|
|
24
|
-
export declare const trackAt: (column:
|
|
25
|
+
export declare const trackAt: (column: Pixels, calibration: TrackCalibration) => Track;
|
|
25
26
|
/**
|
|
26
27
|
* The columns covered by a run of tracks, from the outer edge of the
|
|
27
28
|
* first to the outer edge of the last.
|
|
28
29
|
*/
|
|
29
|
-
export declare const columnsOf: (from:
|
|
30
|
-
from:
|
|
31
|
-
to:
|
|
32
|
-
width:
|
|
30
|
+
export declare const columnsOf: (from: Track, to: Track, calibration: TrackCalibration) => {
|
|
31
|
+
from: Pixels;
|
|
32
|
+
to: Pixels;
|
|
33
|
+
width: Pixels;
|
|
33
34
|
};
|
package/lib/TrackCalibration.js
CHANGED
|
@@ -1,14 +1,15 @@
|
|
|
1
|
+
import { px, subtract, track } from "./Quantity";
|
|
1
2
|
/** Image column at the centre of a tracker bar track. */
|
|
2
|
-
export const columnOf = (
|
|
3
|
+
export const columnOf = (position, calibration) => px(calibration.offset + (position - calibration.shift) * calibration.separation);
|
|
3
4
|
/** Tracker bar track covering an image column, unrounded. */
|
|
4
|
-
export const trackAt = (column, calibration) => (column - calibration.offset) / calibration.separation + calibration.shift;
|
|
5
|
+
export const trackAt = (column, calibration) => track((column - calibration.offset) / calibration.separation + calibration.shift);
|
|
5
6
|
/**
|
|
6
7
|
* The columns covered by a run of tracks, from the outer edge of the
|
|
7
8
|
* first to the outer edge of the last.
|
|
8
9
|
*/
|
|
9
10
|
export const columnsOf = (from, to, calibration) => {
|
|
10
11
|
const [lower, upper] = from <= to ? [from, to] : [to, from];
|
|
11
|
-
const start = columnOf(lower, calibration) - calibration.separation / 2;
|
|
12
|
-
const end = columnOf(upper, calibration) + calibration.separation / 2;
|
|
13
|
-
return { from: start, to: end, width: end
|
|
12
|
+
const start = px(columnOf(lower, calibration) - calibration.separation / 2);
|
|
13
|
+
const end = px(columnOf(upper, calibration) + calibration.separation / 2);
|
|
14
|
+
return { from: start, to: end, width: subtract(end, start) };
|
|
14
15
|
};
|