linked-rolls 0.39.0 → 0.40.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/README.md +20 -15
- package/lib/Emulation.d.ts +1 -1
- package/lib/Emulation.js +5 -5
- package/lib/Feature.d.ts +8 -5
- package/lib/Quantity.d.ts +1 -1
- package/lib/ReproducingSystem.d.ts +2 -2
- package/lib/RollCopy.d.ts +9 -5
- package/lib/Symbol.d.ts +39 -39
- package/lib/Symbol.js +11 -11
- package/lib/constraints.d.ts +1 -1
- package/lib/constraints.js +16 -16
- package/lib/editionOps.d.ts +5 -5
- package/lib/editionOps.js +21 -21
- package/lib/notes.js +1 -1
- package/lib/schema.json +18 -18
- package/lib/substitution.d.ts +29 -29
- package/lib/substitution.js +35 -35
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -58,7 +58,7 @@ end of the end` on the `E52 Time-Span` the event has. `assignDate`,
|
|
|
58
58
|
`notBefore` and `notAfter` build the three shapes, and `dateOf`,
|
|
59
59
|
`earliestOf` and `latestOf` read them.
|
|
60
60
|
|
|
61
|
-
Note that `before` and `after` mean something else on a
|
|
61
|
+
Note that `before` and `after` mean something else on a command,
|
|
62
62
|
where they order two onsets. The context defines them for a date
|
|
63
63
|
within the date itself, so the two never meet.
|
|
64
64
|
|
|
@@ -78,12 +78,17 @@ derivation loads as a list of one.
|
|
|
78
78
|
## What brought a feature about
|
|
79
79
|
|
|
80
80
|
A copy states no list of features. Each feature stands in the act that
|
|
81
|
-
brought it about: the
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
81
|
+
brought it about: the copy's own production for what the punching made,
|
|
82
|
+
an `Alteration` for what a later hand made, an `Attachment` for a patch
|
|
83
|
+
glued on. A `Removal` names by id what was taken off, which stood there
|
|
84
|
+
before the act.
|
|
85
|
+
|
|
86
|
+
Which act a feature stands in says when it came about and by whose
|
|
87
|
+
hand, and says nothing about what kind of feature it is. A hole may be
|
|
88
|
+
punched by hand long after the roll was cut, and a mark may come from
|
|
89
|
+
the factory, rarely though that happens. A feature no later act is
|
|
90
|
+
known to have made belongs to the punching, which is where a reading of
|
|
91
|
+
a scan puts every hole it finds.
|
|
87
92
|
|
|
88
93
|
Three kinds of act rather than two, because the CRM tells them apart.
|
|
89
94
|
E79 Part Addition asks that what is added be "a separate identifiable
|
|
@@ -218,25 +223,25 @@ repository.
|
|
|
218
223
|
|
|
219
224
|
### Alignment, order and pairing
|
|
220
225
|
|
|
221
|
-
A
|
|
222
|
-
on a
|
|
223
|
-
way. `alignedWith` names another
|
|
226
|
+
A command takes its place from the holes that carry it. Four fields
|
|
227
|
+
on a command let an editor state where the measurement should give
|
|
228
|
+
way. `alignedWith` names another command whose onset this one takes
|
|
224
229
|
in the performance, as a "crescendo off" is meant to fall on the note
|
|
225
|
-
it belongs to. `before` and `after` name a
|
|
230
|
+
it belongs to. `before` and `after` name a command whose onset this
|
|
226
231
|
one precedes or follows, without saying by how much: where the copies
|
|
227
232
|
disagree on which side of a note an expression falls, the statement
|
|
228
|
-
settles the order. A
|
|
233
|
+
settles the order. A command the measurement already has on the
|
|
229
234
|
stated side keeps its place. One it does not is put on that side, as
|
|
230
235
|
far from the reference as the copies that agree with the statement put
|
|
231
|
-
it, and a punch diameter away where none does. A
|
|
236
|
+
it, and a punch diameter away where none does. A command makes one
|
|
232
237
|
of these three statements at most. `pairedWith` names a partner whose
|
|
233
238
|
distance to this one is fixed, as a "forzando on" belongs with its
|
|
234
239
|
"forzando off": whatever displaces the one displaces the other. Any two
|
|
235
|
-
|
|
240
|
+
commands may be paired, the relation is symmetric, and it is stated
|
|
236
241
|
on one side only. All are applied when a version is emulated.
|
|
237
242
|
`constraintProblems` lists, version by version, the cases in which the
|
|
238
243
|
statements cannot hold: a reference or partner that is absent, a
|
|
239
|
-
|
|
244
|
+
command placed relative to itself or in several ways at once, one
|
|
240
245
|
claimed by several pairs, or a pair whose members are both placed.
|
|
241
246
|
|
|
242
247
|
## Building
|
package/lib/Emulation.d.ts
CHANGED
|
@@ -26,7 +26,7 @@ export declare class Emulation<Options extends object> {
|
|
|
26
26
|
/**
|
|
27
27
|
* Moves the negotiated events to where their statements put them.
|
|
28
28
|
* The view supplies the copies, whose measurements decide how far
|
|
29
|
-
* before or after its reference a
|
|
29
|
+
* before or after its reference a command goes, and a punch
|
|
30
30
|
* diameter, or a millimetre, where no copy agrees with a statement.
|
|
31
31
|
*/
|
|
32
32
|
applyConstraints(view: EditionView): void;
|
package/lib/Emulation.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { MIDIControlEvents } from "midifile-ts";
|
|
2
2
|
import { idOf } from "./Assumption";
|
|
3
|
-
import {
|
|
3
|
+
import { isCommand, pairsAmong, placementsOf } from "./Symbol";
|
|
4
4
|
import { add, mean, mm, seconds, subtract } from "./Quantity";
|
|
5
5
|
/** The punch diameter the edition's copies report, where any of them does. */
|
|
6
6
|
const punchDiameterOf = (view) => {
|
|
@@ -137,7 +137,7 @@ export class Emulation {
|
|
|
137
137
|
/**
|
|
138
138
|
* Moves the negotiated events to where their statements put them.
|
|
139
139
|
* The view supplies the copies, whose measurements decide how far
|
|
140
|
-
* before or after its reference a
|
|
140
|
+
* before or after its reference a command goes, and a punch
|
|
141
141
|
* diameter, or a millimetre, where no copy agrees with a statement.
|
|
142
142
|
*/
|
|
143
143
|
applyConstraints(view) {
|
|
@@ -154,7 +154,7 @@ export class Emulation {
|
|
|
154
154
|
const inScope = (event) => !range || event.type !== 'note' || (event.horizontal.from > range[0] && event.horizontal.from < range[1]);
|
|
155
155
|
this.negotiatedEvents =
|
|
156
156
|
view.snapshot(version.id)
|
|
157
|
-
.filter(
|
|
157
|
+
.filter(isCommand)
|
|
158
158
|
.map(symbol => view.simplifySymbol(symbol, this.system.trackerBar))
|
|
159
159
|
.filter(event => event !== null)
|
|
160
160
|
.filter(inScope);
|
|
@@ -199,8 +199,8 @@ export class Emulation {
|
|
|
199
199
|
});
|
|
200
200
|
// both pedals start at rest
|
|
201
201
|
events.push(controller(MIDIControlEvents.SUSTAIN, 0), controller(MIDIControlEvents.SOFT_PEDAL, 0));
|
|
202
|
-
// a pedal step is labelled with its
|
|
203
|
-
//
|
|
202
|
+
// a pedal step is labelled with its command only where that
|
|
203
|
+
// command changes, so the file is not swamped with labels
|
|
204
204
|
const lastCause = {};
|
|
205
205
|
let currentTick = 0;
|
|
206
206
|
for (const event of this.midiEvents) {
|
package/lib/Feature.d.ts
CHANGED
|
@@ -169,9 +169,11 @@ export interface Writing extends Trace<'Writing'> {
|
|
|
169
169
|
*/
|
|
170
170
|
transcription: ObjectAssumption<Transcription>;
|
|
171
171
|
/**
|
|
172
|
-
* How far the writing stands askew
|
|
173
|
-
* the
|
|
174
|
-
*
|
|
172
|
+
* How far the writing stands askew. At zero it reads across the
|
|
173
|
+
* roll, from the bass side towards the treble, and the angle turns
|
|
174
|
+
* that reading direction towards the end of the roll, so that a
|
|
175
|
+
* right angle leaves it reading along the roll. A stamp pressed
|
|
176
|
+
* crooked and a label written lengthwise are both stated here.
|
|
175
177
|
* @see reo:rotation
|
|
176
178
|
*/
|
|
177
179
|
rotation?: Measure<'deg'>;
|
|
@@ -204,8 +206,9 @@ export interface GluedOn extends RollFeature<'GluedOn', typeof conditions.GluedO
|
|
|
204
206
|
*/
|
|
205
207
|
material: 'paper' | 'tape';
|
|
206
208
|
/**
|
|
207
|
-
* How far the patch was stuck on askew
|
|
208
|
-
*
|
|
209
|
+
* How far the patch was stuck on askew. At zero its edges stand
|
|
210
|
+
* square to the roll, and the angle turns them towards the end of
|
|
211
|
+
* it, as a writing's reading direction turns.
|
|
209
212
|
* @see reo:rotation
|
|
210
213
|
*/
|
|
211
214
|
rotation?: Measure<'deg'>;
|
package/lib/Quantity.d.ts
CHANGED
|
@@ -28,7 +28,7 @@ export type FeetPerMinute = Quantity<'ft/min'>;
|
|
|
28
28
|
export type MetersPerMinute = Quantity<'m/min'>;
|
|
29
29
|
/** How finely a scan was read: pixels of the image per inch of paper. */
|
|
30
30
|
export type Resolution = Quantity<'px/in'>;
|
|
31
|
-
/** An angle,
|
|
31
|
+
/** An angle on the roll, turning from the line across the paper towards the end of the roll. */
|
|
32
32
|
export type Degrees = Quantity<'deg'>;
|
|
33
33
|
/** Names a number in a unit. Partially apply it to make a constructor. */
|
|
34
34
|
export declare const quantity: <U extends string>(value: number) => Quantity<U>;
|
|
@@ -49,8 +49,8 @@ export interface PerformedNoteOffEvent extends PerformedNoteEvent<'noteOff'> {
|
|
|
49
49
|
}
|
|
50
50
|
/**
|
|
51
51
|
* One step of a pedal. A pedal driven by a bellows takes time to travel,
|
|
52
|
-
* so a single
|
|
53
|
-
*
|
|
52
|
+
* so a single command results in a run of these; `performs` is the
|
|
53
|
+
* command whose reading the step follows from.
|
|
54
54
|
*/
|
|
55
55
|
export interface PerformedPedalEvent extends PerformedRollFeature<'damper' | 'hammerRail'> {
|
|
56
56
|
/** 0 with the pedal up and 127 with it fully down. */
|
package/lib/RollCopy.d.ts
CHANGED
|
@@ -115,10 +115,12 @@ export interface ProductionEvent {
|
|
|
115
115
|
*/
|
|
116
116
|
speed?: ObjectAssumption<PaperSpeed>;
|
|
117
117
|
/**
|
|
118
|
-
* The features the
|
|
119
|
-
*
|
|
120
|
-
*
|
|
121
|
-
*
|
|
118
|
+
* The features the copy came from its punching with: the note and
|
|
119
|
+
* expression perforations, and now and then a mark the perforator
|
|
120
|
+
* left. A reading of a scan finds every hole on the paper at once
|
|
121
|
+
* and states it here, and a feature an editor reads as the work of
|
|
122
|
+
* a later hand belongs in the act that made it, whichever kind of
|
|
123
|
+
* feature it is.
|
|
122
124
|
* @see crm:P108 has produced
|
|
123
125
|
*/
|
|
124
126
|
produced?: AnyFeature[];
|
|
@@ -161,7 +163,9 @@ export type Modification = Partial<{
|
|
|
161
163
|
type: 'Alteration';
|
|
162
164
|
/**
|
|
163
165
|
* The features the act brought into being: a date written on the
|
|
164
|
-
* paper, a circle in pencil, a hole punched by hand.
|
|
166
|
+
* paper, a circle in pencil, a hole punched by hand. Any kind of
|
|
167
|
+
* feature may come of a later act, as any kind may come of the
|
|
168
|
+
* punching.
|
|
165
169
|
* @see crm:P108 has produced
|
|
166
170
|
*/
|
|
167
171
|
produced: AnyFeature[];
|
package/lib/Symbol.d.ts
CHANGED
|
@@ -19,52 +19,52 @@ export interface Symbol<T extends string> extends WithId {
|
|
|
19
19
|
carriers: ReferenceAssumption[];
|
|
20
20
|
}
|
|
21
21
|
export declare const isSymbol: (object: any) => object is AnySymbol;
|
|
22
|
-
export declare const
|
|
22
|
+
export declare const isCommand: (symbol: object | undefined) => symbol is AnyCommand;
|
|
23
23
|
/**
|
|
24
|
-
* A
|
|
25
|
-
*
|
|
26
|
-
*
|
|
27
|
-
*
|
|
24
|
+
* A command is what the tracker bar reads from a perforation, or from a
|
|
25
|
+
* group of them: the note it sounds, or the function it operates. It is
|
|
26
|
+
* typically carried by a single punched hole or a chain of them, but it
|
|
27
|
+
* might also have different physical appearences.
|
|
28
|
+
* @see reo:Command
|
|
28
29
|
*/
|
|
29
|
-
export interface
|
|
30
|
+
export interface Command<T extends string> extends Symbol<T> {
|
|
30
31
|
/**
|
|
31
|
-
* In piano rolls,
|
|
32
|
-
*
|
|
33
|
-
*
|
|
34
|
-
* to the perforation by its `@id`.
|
|
32
|
+
* In piano rolls, commands are often aligned with other commands,
|
|
33
|
+
* e.g. a "crescendo off" might be logically aligned to the start of
|
|
34
|
+
* a note. This points to the command by its `@id`.
|
|
35
35
|
* @see reo:alignedWith
|
|
36
36
|
*/
|
|
37
37
|
alignedWith?: ReferenceAssumption;
|
|
38
38
|
/**
|
|
39
|
-
* A
|
|
39
|
+
* A command whose onset this one precedes when the roll is
|
|
40
40
|
* performed, without saying by how much: a "crescendo off" ends
|
|
41
41
|
* before the note it leads to begins, even where the copies
|
|
42
42
|
* disagree on it. Where the measurement has it so, nothing moves;
|
|
43
43
|
* where it does not, this one is placed before the reference as
|
|
44
|
-
* far as the copies that agree put it. This points to the
|
|
45
|
-
*
|
|
44
|
+
* far as the copies that agree put it. This points to the command
|
|
45
|
+
* by its `@id`.
|
|
46
46
|
* @see reo:before
|
|
47
47
|
*/
|
|
48
48
|
before?: ReferenceAssumption;
|
|
49
49
|
/**
|
|
50
|
-
* A
|
|
50
|
+
* A command whose onset this one follows when the roll is
|
|
51
51
|
* performed, the counterpart of `before`. This points to the
|
|
52
|
-
*
|
|
52
|
+
* command by its `@id`.
|
|
53
53
|
* @see reo:after
|
|
54
54
|
*/
|
|
55
55
|
after?: ReferenceAssumption;
|
|
56
56
|
/**
|
|
57
|
-
* The
|
|
58
|
-
* with its "forzando off". Any two
|
|
57
|
+
* The command this one forms a pair with, e.g. a "forzando on"
|
|
58
|
+
* with its "forzando off". Any two commands may be paired.
|
|
59
59
|
* The distance between the two is fixed: whatever displaces the
|
|
60
60
|
* one displaces the other. The relation is symmetric and is stated
|
|
61
|
-
* on one side only. This points to the
|
|
61
|
+
* on one side only. This points to the command by its `@id`.
|
|
62
62
|
* @see reo:pairedWith
|
|
63
63
|
*/
|
|
64
64
|
pairedWith?: ReferenceAssumption;
|
|
65
65
|
}
|
|
66
66
|
export declare const placementRelations: readonly ['alignedWith', 'before', 'after'];
|
|
67
|
-
/** The ways a
|
|
67
|
+
/** The ways a command may be placed relative to another. */
|
|
68
68
|
export type PlacementRelation = typeof placementRelations[number];
|
|
69
69
|
type Placeable = Partial<Record<PlacementRelation, ReferenceAssumption>>;
|
|
70
70
|
export type Placement = {
|
|
@@ -72,25 +72,25 @@ export type Placement = {
|
|
|
72
72
|
reference: ReferenceAssumption;
|
|
73
73
|
};
|
|
74
74
|
/**
|
|
75
|
-
* The statements placing a
|
|
76
|
-
*
|
|
77
|
-
*
|
|
75
|
+
* The statements placing a command relative to others, alignment first.
|
|
76
|
+
* A command is meant to make one at most; the first is the one a
|
|
77
|
+
* performance applies.
|
|
78
78
|
*/
|
|
79
|
-
export declare const placementsOf: (
|
|
79
|
+
export declare const placementsOf: (command: Placeable) => Placement[];
|
|
80
80
|
type Pairable = WithId & {
|
|
81
81
|
pairedWith?: ReferenceAssumption;
|
|
82
82
|
};
|
|
83
83
|
/**
|
|
84
|
-
* The pairs among the given
|
|
85
|
-
*
|
|
84
|
+
* The pairs among the given commands, each once and in the order the
|
|
85
|
+
* pairing is stated. A pair whose partner is absent is left out.
|
|
86
86
|
*/
|
|
87
|
-
export declare const pairsAmong: <S extends Pairable>(
|
|
87
|
+
export declare const pairsAmong: <S extends Pairable>(commands: readonly S[]) => [S, S][];
|
|
88
88
|
/**
|
|
89
89
|
* A note symbol, representing a single pitched musical event on the roll.
|
|
90
90
|
* The pitch is encoded via the tracker bar position (track number).
|
|
91
91
|
* @see reo:Note
|
|
92
92
|
*/
|
|
93
|
-
export interface Note extends
|
|
93
|
+
export interface Note extends Command<'note'> {
|
|
94
94
|
/**
|
|
95
95
|
* The MIDI pitch number of the note (e.g. 60 for middle C).
|
|
96
96
|
* @see reo:pitch
|
|
@@ -98,29 +98,29 @@ export interface Note extends Perforation<'note'> {
|
|
|
98
98
|
pitch: number;
|
|
99
99
|
}
|
|
100
100
|
/**
|
|
101
|
-
* The scope of an expression
|
|
102
|
-
*
|
|
101
|
+
* The scope of an expression command, indicating whether it applies to
|
|
102
|
+
* the bass or treble register of the piano.
|
|
103
103
|
* @see reo:scope
|
|
104
104
|
*/
|
|
105
105
|
export type ExpressionScope = 'bass' | 'treble';
|
|
106
106
|
/**
|
|
107
|
-
* An expression symbol, representing a
|
|
108
|
-
*
|
|
109
|
-
*
|
|
107
|
+
* An expression symbol, representing a command that governs dynamics,
|
|
108
|
+
* pedaling, or mechanical functions of the reproducing piano rather
|
|
109
|
+
* than sounding a note. Each expression has a scope (bass or treble)
|
|
110
110
|
* and a specific expression type.
|
|
111
111
|
* @see reo:Expression
|
|
112
112
|
*/
|
|
113
|
-
export interface Expression extends
|
|
113
|
+
export interface Expression extends Command<'expression'> {
|
|
114
114
|
/**
|
|
115
115
|
* Whether this expression applies to the bass or treble register.
|
|
116
116
|
* @see reo:scope
|
|
117
117
|
*/
|
|
118
118
|
scope: ExpressionScope;
|
|
119
119
|
/**
|
|
120
|
-
* The
|
|
121
|
-
*
|
|
122
|
-
*
|
|
123
|
-
*
|
|
120
|
+
* The kind of command, named as the reproducing system of the roll
|
|
121
|
+
* names it: "SustainPedalOn", "ForzandoOff" and so on for the
|
|
122
|
+
* Welte-Mignon T-100. The tracker bar of the system lists the
|
|
123
|
+
* values it reads.
|
|
124
124
|
* @see crm:P2 has type
|
|
125
125
|
*/
|
|
126
126
|
expressionType: string;
|
|
@@ -139,8 +139,8 @@ export interface Text extends Symbol<'text'> {
|
|
|
139
139
|
}
|
|
140
140
|
/**
|
|
141
141
|
* A symbol can be either a note, an expression, or a text.
|
|
142
|
-
* Notes and expressions are
|
|
142
|
+
* Notes and expressions are commands; texts are carried by writings.
|
|
143
143
|
*/
|
|
144
144
|
export type AnySymbol = Note | Expression | Text;
|
|
145
|
-
export type
|
|
145
|
+
export type AnyCommand = Note | Expression;
|
|
146
146
|
export {};
|
package/lib/Symbol.js
CHANGED
|
@@ -3,24 +3,24 @@ export const isSymbol = (object) => {
|
|
|
3
3
|
return ('type' in object
|
|
4
4
|
&& (object.type === 'note' || object.type === 'expression' || object.type === 'text'));
|
|
5
5
|
};
|
|
6
|
-
export const
|
|
6
|
+
export const isCommand = (symbol) => symbol !== undefined && 'type' in symbol && (symbol.type === 'note' || symbol.type === 'expression');
|
|
7
7
|
export const placementRelations = ['alignedWith', 'before', 'after'];
|
|
8
8
|
/**
|
|
9
|
-
* The statements placing a
|
|
10
|
-
*
|
|
11
|
-
*
|
|
9
|
+
* The statements placing a command relative to others, alignment first.
|
|
10
|
+
* A command is meant to make one at most; the first is the one a
|
|
11
|
+
* performance applies.
|
|
12
12
|
*/
|
|
13
|
-
export const placementsOf = (
|
|
14
|
-
const reference =
|
|
13
|
+
export const placementsOf = (command) => placementRelations.flatMap(relation => {
|
|
14
|
+
const reference = command[relation];
|
|
15
15
|
return reference ? [{ relation, reference }] : [];
|
|
16
16
|
});
|
|
17
17
|
/**
|
|
18
|
-
* The pairs among the given
|
|
19
|
-
*
|
|
18
|
+
* The pairs among the given commands, each once and in the order the
|
|
19
|
+
* pairing is stated. A pair whose partner is absent is left out.
|
|
20
20
|
*/
|
|
21
|
-
export const pairsAmong = (
|
|
22
|
-
const byId = new Map(
|
|
23
|
-
return
|
|
21
|
+
export const pairsAmong = (commands) => {
|
|
22
|
+
const byId = new Map(commands.map(p => [p.id, p]));
|
|
23
|
+
return commands
|
|
24
24
|
.filter((p) => p.pairedWith !== undefined)
|
|
25
25
|
.map((p) => [p, byId.get(idOf(p.pairedWith))])
|
|
26
26
|
.filter((pair) => pair[1] !== undefined);
|
package/lib/constraints.d.ts
CHANGED
|
@@ -6,7 +6,7 @@ export type ConstraintProblem = {
|
|
|
6
6
|
};
|
|
7
7
|
/**
|
|
8
8
|
* Where the edition cannot hold as stated, version by version: a
|
|
9
|
-
* placement or pairing reference absent from the version, a
|
|
9
|
+
* placement or pairing reference absent from the version, a command
|
|
10
10
|
* placed relative to itself or in several ways at once, one claimed by
|
|
11
11
|
* several pairs, a pair whose members are both placed and so cannot
|
|
12
12
|
* keep their distance and follow their references at once, an
|
package/lib/constraints.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { idOf } from "./Assumption";
|
|
2
|
-
import {
|
|
2
|
+
import { isCommand, pairsAmong, placementsOf } from "./Symbol";
|
|
3
3
|
import { keyOf } from "./TrackerBar";
|
|
4
4
|
import { trackerBarOf } from "./systems";
|
|
5
5
|
import { barOf } from "./RollCopy";
|
|
@@ -8,30 +8,30 @@ const missingReference = {
|
|
|
8
8
|
before: 'before-reference-missing',
|
|
9
9
|
after: 'after-reference-missing'
|
|
10
10
|
};
|
|
11
|
-
const problemsIn = (version,
|
|
12
|
-
const ids = new Set(
|
|
11
|
+
const problemsIn = (version, commands) => {
|
|
12
|
+
const ids = new Set(commands.map(p => p.id));
|
|
13
13
|
const report = (symbol, problem) => ({ version, symbol, problem });
|
|
14
|
-
const missingReferences =
|
|
14
|
+
const missingReferences = commands
|
|
15
15
|
.flatMap(p => placementsOf(p)
|
|
16
16
|
.filter(({ reference }) => !ids.has(idOf(reference)))
|
|
17
17
|
.map(({ relation }) => report(p.id, missingReference[relation])));
|
|
18
|
-
const selfPlaced =
|
|
18
|
+
const selfPlaced = commands
|
|
19
19
|
.filter(p => placementsOf(p).some(({ reference }) => idOf(reference) === p.id))
|
|
20
20
|
.map(p => report(p.id, 'placed-relative-to-itself'));
|
|
21
|
-
const placedSeveralWays =
|
|
21
|
+
const placedSeveralWays = commands
|
|
22
22
|
.filter(p => placementsOf(p).length > 1)
|
|
23
23
|
.map(p => report(p.id, 'placed-several-ways'));
|
|
24
|
-
const missingPartners =
|
|
24
|
+
const missingPartners = commands
|
|
25
25
|
.filter(p => p.pairedWith && !ids.has(idOf(p.pairedWith)))
|
|
26
26
|
.map(p => report(p.id, 'partner-missing'));
|
|
27
|
-
const selfPaired =
|
|
27
|
+
const selfPaired = commands
|
|
28
28
|
.filter(p => p.pairedWith && idOf(p.pairedWith) === p.id)
|
|
29
29
|
.map(p => report(p.id, 'paired-with-itself'));
|
|
30
|
-
const pairs = pairsAmong(
|
|
30
|
+
const pairs = pairsAmong(commands);
|
|
31
31
|
const pairsPerId = pairs
|
|
32
32
|
.flatMap(([one, other]) => one === other ? [one] : [one, other])
|
|
33
33
|
.reduce((counts, p) => counts.set(p.id, (counts.get(p.id) ?? 0) + 1), new Map());
|
|
34
|
-
const inSeveralPairs =
|
|
34
|
+
const inSeveralPairs = commands
|
|
35
35
|
.filter(p => (pairsPerId.get(p.id) ?? 0) > 1)
|
|
36
36
|
.map(p => report(p.id, 'in-several-pairs'));
|
|
37
37
|
const placedOnBothSides = pairs
|
|
@@ -74,7 +74,7 @@ const typesNotOnTheBar = (version, snapshot) => {
|
|
|
74
74
|
* out says 59. Collation across systems consults only the place, so
|
|
75
75
|
* without this nothing checks the tracks at all.
|
|
76
76
|
*/
|
|
77
|
-
const carriersOffTheirMeaning = (view, version,
|
|
77
|
+
const carriersOffTheirMeaning = (view, version, commands) => {
|
|
78
78
|
const misread = (carrier, symbol) => {
|
|
79
79
|
const copy = view.copyOf(carrier.id);
|
|
80
80
|
if (!copy)
|
|
@@ -84,7 +84,7 @@ const carriersOffTheirMeaning = (view, version, perforations) => {
|
|
|
84
84
|
return !barOf(copy).meaningsOf(carrier.vertical)
|
|
85
85
|
.some(meaning => keyOf(meaning) === keyOf(symbol));
|
|
86
86
|
};
|
|
87
|
-
return
|
|
87
|
+
return commands
|
|
88
88
|
.filter(symbol => view.carriersOf(symbol).some(carrier => misread(carrier, symbol)))
|
|
89
89
|
.map(symbol => ({ version, symbol: symbol.id, problem: 'carrier-on-another-track' }));
|
|
90
90
|
};
|
|
@@ -102,7 +102,7 @@ const paperDisagreed = (view, version) => view.speedScalesIn(version).length > 1
|
|
|
102
102
|
: [];
|
|
103
103
|
/**
|
|
104
104
|
* Where the edition cannot hold as stated, version by version: a
|
|
105
|
-
* placement or pairing reference absent from the version, a
|
|
105
|
+
* placement or pairing reference absent from the version, a command
|
|
106
106
|
* placed relative to itself or in several ways at once, one claimed by
|
|
107
107
|
* several pairs, a pair whose members are both placed and so cannot
|
|
108
108
|
* keep their distance and follow their references at once, an
|
|
@@ -111,11 +111,11 @@ const paperDisagreed = (view, version) => view.speedScalesIn(version).length > 1
|
|
|
111
111
|
*/
|
|
112
112
|
export const constraintProblems = (view) => view.edition.versions.flatMap(version => {
|
|
113
113
|
const snapshot = view.snapshot(version.id);
|
|
114
|
-
const
|
|
114
|
+
const commands = snapshot.filter(isCommand);
|
|
115
115
|
return [
|
|
116
|
-
...problemsIn(version.id,
|
|
116
|
+
...problemsIn(version.id, commands),
|
|
117
117
|
...typesNotOnTheBar(version, snapshot),
|
|
118
|
-
...carriersOffTheirMeaning(view, version.id,
|
|
118
|
+
...carriersOffTheirMeaning(view, version.id, commands),
|
|
119
119
|
...paperDisagreed(view, version)
|
|
120
120
|
];
|
|
121
121
|
});
|
package/lib/editionOps.d.ts
CHANGED
|
@@ -77,7 +77,7 @@ export declare const symbolsCarriedOnlyBy: (edition: Edition, copyId: string) =>
|
|
|
77
77
|
* punched, being glued on, so `punched` says nothing of one.
|
|
78
78
|
*/
|
|
79
79
|
export interface FeatureAct {
|
|
80
|
-
/** The copy
|
|
80
|
+
/** The copy came from its punching with it, which is where a reading of a scan puts every hole. */
|
|
81
81
|
punched?: boolean;
|
|
82
82
|
/** The act that brought this feature about brought the new one about as well. */
|
|
83
83
|
beside?: string;
|
|
@@ -199,11 +199,11 @@ export declare const mergeEdits: (view: EditionView, versionId: string, toMerge:
|
|
|
199
199
|
/** Replaces the edit with one edit per inserted and one per deleted symbol. */
|
|
200
200
|
export declare const splitEdit: (versionId: string, toSplit: Edit) => EditionOp;
|
|
201
201
|
/** States how the follower is placed relative to the reference, in place of any earlier statement. */
|
|
202
|
-
export declare const
|
|
203
|
-
export declare const
|
|
202
|
+
export declare const placeCommand: (view: EditionView, followerId: string, referenceId: string, relation: PlacementRelation) => EditionOp;
|
|
203
|
+
export declare const unplaceCommand: (view: EditionView, followerId: string) => EditionOp;
|
|
204
204
|
/** The pair is stated on `statingId` only, as the format asks. */
|
|
205
|
-
export declare const
|
|
206
|
-
export declare const
|
|
205
|
+
export declare const pairCommands: (view: EditionView, statingId: string, partnerId: string) => EditionOp;
|
|
206
|
+
export declare const unpairCommand: (view: EditionView, statingId: string) => EditionOp;
|
|
207
207
|
/** Annotates the assumption at the path with a belief held true, for reasons to be added. */
|
|
208
208
|
export declare const createBelief: (path: Path) => EditionOp;
|
|
209
209
|
export declare const clearBelief: (path: Path) => EditionOp;
|
package/lib/editionOps.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { current, isDraft } from "immer";
|
|
2
2
|
import { v4 } from "uuid";
|
|
3
3
|
import { getAt } from "./EditionView";
|
|
4
|
-
import {
|
|
4
|
+
import { isCommand, placementRelations } from "./Symbol";
|
|
5
5
|
import { collationsOf, defaultCollationTolerance } from "./Collation";
|
|
6
6
|
import { collationToleranceOf, editsOf, insertedBy, principalDerivationOf } from "./Version";
|
|
7
7
|
import { asSymbols, barOf, featuresByAct, featuresOf, isPaperStretch, statesNothing } from "./RollCopy";
|
|
@@ -216,15 +216,15 @@ export const symbolsCarriedOnlyBy = (edition, copyId) => {
|
|
|
216
216
|
return copy ? insertedIn(edition.versions).filter(carriedOnlyOn(featureIdsOf(copy))) : [];
|
|
217
217
|
};
|
|
218
218
|
const references = [...placementRelations, 'pairedWith'];
|
|
219
|
-
/** The
|
|
220
|
-
const forgettingReferences = (
|
|
219
|
+
/** The command without its references to the dropped symbols, or the very same one where it makes none. */
|
|
220
|
+
const forgettingReferences = (command, dropped) => {
|
|
221
221
|
const stale = references.filter(relation => {
|
|
222
|
-
const reference =
|
|
222
|
+
const reference = command[relation];
|
|
223
223
|
return reference !== undefined && dropped.has(idOf(reference));
|
|
224
224
|
});
|
|
225
225
|
if (stale.length === 0)
|
|
226
|
-
return
|
|
227
|
-
const kept = { ...
|
|
226
|
+
return command;
|
|
227
|
+
const kept = { ...command };
|
|
228
228
|
stale.forEach(relation => { delete kept[relation]; });
|
|
229
229
|
return kept;
|
|
230
230
|
};
|
|
@@ -232,7 +232,7 @@ const forgettingReferences = (perforation, dropped) => {
|
|
|
232
232
|
const forgettingCarriers = (features, dropped) => (symbol) => {
|
|
233
233
|
const carriers = without(symbol.carriers, carrier => features.has(idOf(carrier)));
|
|
234
234
|
const relieved = carriers === symbol.carriers ? symbol : { ...symbol, carriers };
|
|
235
|
-
return
|
|
235
|
+
return isCommand(relieved) ? forgettingReferences(relieved, dropped) : relieved;
|
|
236
236
|
};
|
|
237
237
|
/** The edit without the dropped symbols and the features, or the very same edit where it had none of them. */
|
|
238
238
|
const forgettingFeatures = (features, dropped) => {
|
|
@@ -667,7 +667,7 @@ export const connectVersions = (view, childId, parentId, tolerance = defaultColl
|
|
|
667
667
|
const collated = new Set(collations.map(({ symbol }) => symbol.id));
|
|
668
668
|
const matched = new Set(collations.map(({ counterpart }) => counterpart.id));
|
|
669
669
|
/**
|
|
670
|
-
* Where the child is coded for another system, a held
|
|
670
|
+
* Where the child is coded for another system, a held command of
|
|
671
671
|
* its own often stands for a latched pair of the parent's. Saying so
|
|
672
672
|
* as one edit is the transfer being carried out, and leaving the two
|
|
673
673
|
* apart would make the apparatus a list of unexplained losses beside
|
|
@@ -872,29 +872,29 @@ export const splitEdit = (versionId, toSplit) => {
|
|
|
872
872
|
});
|
|
873
873
|
};
|
|
874
874
|
/**
|
|
875
|
-
* Runs the change on the
|
|
875
|
+
* Runs the change on the command the view locates by id, in whichever
|
|
876
876
|
* version inserted it. A statement made there holds in every version
|
|
877
|
-
* that carries the
|
|
877
|
+
* that carries the command.
|
|
878
878
|
*/
|
|
879
|
-
const
|
|
879
|
+
const onCommand = (view, id, op) => draft => {
|
|
880
880
|
const path = view.getPath(id);
|
|
881
881
|
const symbol = path && getAt(path, draft);
|
|
882
|
-
if (
|
|
882
|
+
if (isCommand(symbol))
|
|
883
883
|
op(symbol);
|
|
884
884
|
};
|
|
885
|
-
const clearPlacement = (
|
|
885
|
+
const clearPlacement = (command) => placementRelations.forEach(relation => { delete command[relation]; });
|
|
886
886
|
/** States how the follower is placed relative to the reference, in place of any earlier statement. */
|
|
887
|
-
export const
|
|
888
|
-
clearPlacement(
|
|
889
|
-
|
|
887
|
+
export const placeCommand = (view, followerId, referenceId, relation) => onCommand(view, followerId, command => {
|
|
888
|
+
clearPlacement(command);
|
|
889
|
+
command[relation] = assignReference(referenceId);
|
|
890
890
|
});
|
|
891
|
-
export const
|
|
891
|
+
export const unplaceCommand = (view, followerId) => onCommand(view, followerId, clearPlacement);
|
|
892
892
|
/** The pair is stated on `statingId` only, as the format asks. */
|
|
893
|
-
export const
|
|
894
|
-
|
|
893
|
+
export const pairCommands = (view, statingId, partnerId) => onCommand(view, statingId, command => {
|
|
894
|
+
command.pairedWith = assignReference(partnerId);
|
|
895
895
|
});
|
|
896
|
-
export const
|
|
897
|
-
delete
|
|
896
|
+
export const unpairCommand = (view, statingId) => onCommand(view, statingId, command => {
|
|
897
|
+
delete command.pairedWith;
|
|
898
898
|
});
|
|
899
899
|
const onAssumptionAt = (path, op) => draft => {
|
|
900
900
|
const assumption = getAt(path, draft);
|
package/lib/notes.js
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* `{{<id>}}`, where the id is the entity's, and is resolved when the note
|
|
5
5
|
* is shown.
|
|
6
6
|
*
|
|
7
|
-
* An entity the edition gives no name to, a
|
|
7
|
+
* An entity the edition gives no name to, a command or an edit, needs
|
|
8
8
|
* the note's own words to stand in the sentence: `{{<id>|die Stanzung bei
|
|
9
9
|
* 7364,7 mm}}`. The name wins where the edition has one, so a siglum
|
|
10
10
|
* still follows the stemma and a written-out label cannot freeze it.
|
package/lib/schema.json
CHANGED
|
@@ -101,7 +101,7 @@
|
|
|
101
101
|
"$ref": "#/definitions/Text"
|
|
102
102
|
}
|
|
103
103
|
],
|
|
104
|
-
"description": "A symbol can be either a note, an expression, or a text. Notes and expressions are
|
|
104
|
+
"description": "A symbol can be either a note, an expression, or a text. Notes and expressions are commands; texts are carried by writings."
|
|
105
105
|
},
|
|
106
106
|
"Argumentation": {
|
|
107
107
|
"description": "An argumentation provides reasons for a belief and may be associated with a person carrying out that argumentation.",
|
|
@@ -622,21 +622,21 @@
|
|
|
622
622
|
"type": "string"
|
|
623
623
|
},
|
|
624
624
|
"Expression": {
|
|
625
|
-
"description": "An expression symbol, representing a
|
|
625
|
+
"description": "An expression symbol, representing a command that governs dynamics, pedaling, or mechanical functions of the reproducing piano rather than sounding a note. Each expression has a scope (bass or treble) and a specific expression type.",
|
|
626
626
|
"properties": {
|
|
627
627
|
"after": {
|
|
628
628
|
"$ref": "#/definitions/ReferenceAssumption",
|
|
629
|
-
"description": "A
|
|
629
|
+
"description": "A command whose onset this one follows when the roll is performed, the counterpart of `before`. This points to the command by its `@id`.",
|
|
630
630
|
"ontology": "reo:after"
|
|
631
631
|
},
|
|
632
632
|
"alignedWith": {
|
|
633
633
|
"$ref": "#/definitions/ReferenceAssumption",
|
|
634
|
-
"description": "In piano rolls,
|
|
634
|
+
"description": "In piano rolls, commands are often aligned with other commands, e.g. a \"crescendo off\" might be logically aligned to the start of a note. This points to the command by its `@id`.",
|
|
635
635
|
"ontology": "reo:alignedWith"
|
|
636
636
|
},
|
|
637
637
|
"before": {
|
|
638
638
|
"$ref": "#/definitions/ReferenceAssumption",
|
|
639
|
-
"description": "A
|
|
639
|
+
"description": "A command whose onset this one precedes when the roll is performed, without saying by how much: a \"crescendo off\" ends before the note it leads to begins, even where the copies disagree on it. Where the measurement has it so, nothing moves; where it does not, this one is placed before the reference as far as the copies that agree put it. This points to the command by its `@id`.",
|
|
640
640
|
"ontology": "reo:before"
|
|
641
641
|
},
|
|
642
642
|
"carriers": {
|
|
@@ -648,13 +648,13 @@
|
|
|
648
648
|
"ontology": "crm:P128i is carried by"
|
|
649
649
|
},
|
|
650
650
|
"expressionType": {
|
|
651
|
-
"description": "The
|
|
651
|
+
"description": "The kind of command, named as the reproducing system of the roll names it: \"SustainPedalOn\", \"ForzandoOff\" and so on for the Welte-Mignon T-100. The tracker bar of the system lists the values it reads.",
|
|
652
652
|
"type": "string",
|
|
653
653
|
"ontology": "crm:P2 has type"
|
|
654
654
|
},
|
|
655
655
|
"pairedWith": {
|
|
656
656
|
"$ref": "#/definitions/ReferenceAssumption",
|
|
657
|
-
"description": "The
|
|
657
|
+
"description": "The command this one forms a pair with, e.g. a \"forzando on\" with its \"forzando off\". Any two commands may be paired. The distance between the two is fixed: whatever displaces the one displaces the other. The relation is symmetric and is stated on one side only. This points to the command by its `@id`.",
|
|
658
658
|
"ontology": "reo:pairedWith"
|
|
659
659
|
},
|
|
660
660
|
"scope": {
|
|
@@ -684,7 +684,7 @@
|
|
|
684
684
|
"ontology": "reo:Expression"
|
|
685
685
|
},
|
|
686
686
|
"ExpressionScope": {
|
|
687
|
-
"description": "The scope of an expression
|
|
687
|
+
"description": "The scope of an expression command, indicating whether it applies to the bass or treble register of the piano.",
|
|
688
688
|
"enum": [
|
|
689
689
|
"bass",
|
|
690
690
|
"treble"
|
|
@@ -782,7 +782,7 @@
|
|
|
782
782
|
},
|
|
783
783
|
"rotation": {
|
|
784
784
|
"$ref": "#/definitions/Measure%3C%22deg%22%3E",
|
|
785
|
-
"description": "How far the patch was stuck on askew
|
|
785
|
+
"description": "How far the patch was stuck on askew. At zero its edges stand square to the roll, and the angle turns them towards the end of it, as a writing's reading direction turns.",
|
|
786
786
|
"ontology": "reo:rotation"
|
|
787
787
|
},
|
|
788
788
|
"side": {
|
|
@@ -1264,7 +1264,7 @@
|
|
|
1264
1264
|
"ontology": "crm:P4 has time-span"
|
|
1265
1265
|
},
|
|
1266
1266
|
"produced": {
|
|
1267
|
-
"description": "The features the act brought into being: a date written on the paper, a circle in pencil, a hole punched by hand.",
|
|
1267
|
+
"description": "The features the act brought into being: a date written on the paper, a circle in pencil, a hole punched by hand. Any kind of feature may come of a later act, as any kind may come of the punching.",
|
|
1268
1268
|
"items": {
|
|
1269
1269
|
"$ref": "#/definitions/AnyFeature"
|
|
1270
1270
|
},
|
|
@@ -1458,7 +1458,7 @@
|
|
|
1458
1458
|
},
|
|
1459
1459
|
"rotation": {
|
|
1460
1460
|
"$ref": "#/definitions/Measure%3C%22deg%22%3E",
|
|
1461
|
-
"description": "How far the writing stands askew
|
|
1461
|
+
"description": "How far the writing stands askew. At zero it reads across the roll, from the bass side towards the treble, and the angle turns that reading direction towards the end of the roll, so that a right angle leaves it reading along the roll. A stamp pressed crooked and a label written lengthwise are both stated here.",
|
|
1462
1462
|
"ontology": "reo:rotation"
|
|
1463
1463
|
},
|
|
1464
1464
|
"side": {
|
|
@@ -1588,7 +1588,7 @@
|
|
|
1588
1588
|
},
|
|
1589
1589
|
"rotation": {
|
|
1590
1590
|
"$ref": "#/definitions/Measure%3C%22deg%22%3E",
|
|
1591
|
-
"description": "How far the patch was stuck on askew
|
|
1591
|
+
"description": "How far the patch was stuck on askew. At zero its edges stand square to the roll, and the angle turns them towards the end of it, as a writing's reading direction turns.",
|
|
1592
1592
|
"ontology": "reo:rotation"
|
|
1593
1593
|
},
|
|
1594
1594
|
"side": {
|
|
@@ -1646,17 +1646,17 @@
|
|
|
1646
1646
|
"properties": {
|
|
1647
1647
|
"after": {
|
|
1648
1648
|
"$ref": "#/definitions/ReferenceAssumption",
|
|
1649
|
-
"description": "A
|
|
1649
|
+
"description": "A command whose onset this one follows when the roll is performed, the counterpart of `before`. This points to the command by its `@id`.",
|
|
1650
1650
|
"ontology": "reo:after"
|
|
1651
1651
|
},
|
|
1652
1652
|
"alignedWith": {
|
|
1653
1653
|
"$ref": "#/definitions/ReferenceAssumption",
|
|
1654
|
-
"description": "In piano rolls,
|
|
1654
|
+
"description": "In piano rolls, commands are often aligned with other commands, e.g. a \"crescendo off\" might be logically aligned to the start of a note. This points to the command by its `@id`.",
|
|
1655
1655
|
"ontology": "reo:alignedWith"
|
|
1656
1656
|
},
|
|
1657
1657
|
"before": {
|
|
1658
1658
|
"$ref": "#/definitions/ReferenceAssumption",
|
|
1659
|
-
"description": "A
|
|
1659
|
+
"description": "A command whose onset this one precedes when the roll is performed, without saying by how much: a \"crescendo off\" ends before the note it leads to begins, even where the copies disagree on it. Where the measurement has it so, nothing moves; where it does not, this one is placed before the reference as far as the copies that agree put it. This points to the command by its `@id`.",
|
|
1660
1660
|
"ontology": "reo:before"
|
|
1661
1661
|
},
|
|
1662
1662
|
"carriers": {
|
|
@@ -1669,7 +1669,7 @@
|
|
|
1669
1669
|
},
|
|
1670
1670
|
"pairedWith": {
|
|
1671
1671
|
"$ref": "#/definitions/ReferenceAssumption",
|
|
1672
|
-
"description": "The
|
|
1672
|
+
"description": "The command this one forms a pair with, e.g. a \"forzando on\" with its \"forzando off\". Any two commands may be paired. The distance between the two is fixed: whatever displaces the one displaces the other. The relation is symmetric and is stated on one side only. This points to the command by its `@id`.",
|
|
1673
1673
|
"ontology": "reo:pairedWith"
|
|
1674
1674
|
},
|
|
1675
1675
|
"pitch": {
|
|
@@ -2467,7 +2467,7 @@
|
|
|
2467
2467
|
"ontology": "crm:P126 employed"
|
|
2468
2468
|
},
|
|
2469
2469
|
"produced": {
|
|
2470
|
-
"description": "The features the
|
|
2470
|
+
"description": "The features the copy came from its punching with: the note and expression perforations, and now and then a mark the perforator left. A reading of a scan finds every hole on the paper at once and states it here, and a feature an editor reads as the work of a later hand belongs in the act that made it, whichever kind of feature it is.",
|
|
2471
2471
|
"items": {
|
|
2472
2472
|
"$ref": "#/definitions/AnyFeature"
|
|
2473
2473
|
},
|
|
@@ -3092,7 +3092,7 @@
|
|
|
3092
3092
|
},
|
|
3093
3093
|
"rotation": {
|
|
3094
3094
|
"$ref": "#/definitions/Measure%3C%22deg%22%3E",
|
|
3095
|
-
"description": "How far the writing stands askew
|
|
3095
|
+
"description": "How far the writing stands askew. At zero it reads across the roll, from the bass side towards the treble, and the angle turns that reading direction towards the end of the roll, so that a right angle leaves it reading along the roll. A stamp pressed crooked and a label written lengthwise are both stated here.",
|
|
3096
3096
|
"ontology": "reo:rotation"
|
|
3097
3097
|
},
|
|
3098
3098
|
"side": {
|
package/lib/substitution.d.ts
CHANGED
|
@@ -3,40 +3,40 @@ import { AnySymbol, Expression } from "./Symbol";
|
|
|
3
3
|
import { Millimeters, Track } from "./Quantity";
|
|
4
4
|
import { TrackerBar } from "./TrackerBar";
|
|
5
5
|
/**
|
|
6
|
-
* How a scale
|
|
7
|
-
*
|
|
6
|
+
* How a scale words a function: as a command that turns it on, one that
|
|
7
|
+
* cancels it, or one that holds it for as long as it lasts.
|
|
8
8
|
*/
|
|
9
9
|
export type Spelling = 'on' | 'off' | 'held';
|
|
10
10
|
/**
|
|
11
|
-
*
|
|
12
|
-
*
|
|
11
|
+
* What a word of a scale operates, rather than the word itself, so that
|
|
12
|
+
* two scales can be compared at all.
|
|
13
13
|
*/
|
|
14
|
-
export interface
|
|
14
|
+
export interface Operation {
|
|
15
15
|
/** The function operated, named the same wherever a scale has it. */
|
|
16
16
|
operates: string;
|
|
17
17
|
spelling: Spelling;
|
|
18
18
|
/**
|
|
19
19
|
* Whether the half of the keyboard the valve serves is part of the
|
|
20
|
-
*
|
|
21
|
-
* two Welte scales put them on opposite edges of the paper,
|
|
22
|
-
* rule that compared sides would leave every pedal unpaired.
|
|
20
|
+
* function operated. The dynamics are per half; the pedals are not,
|
|
21
|
+
* and the two Welte scales put them on opposite edges of the paper,
|
|
22
|
+
* so a rule that compared sides would leave every pedal unpaired.
|
|
23
23
|
*/
|
|
24
24
|
sided: boolean;
|
|
25
25
|
}
|
|
26
|
-
/** What
|
|
27
|
-
export declare const
|
|
26
|
+
/** What an expression type operates, or nothing for a word no scale here knows. */
|
|
27
|
+
export declare const operationOf: (expressionType: string) => Operation | undefined;
|
|
28
28
|
/**
|
|
29
|
-
* A latched function of the older version and the held
|
|
30
|
-
*
|
|
29
|
+
* A latched function of the older version and the held command of the
|
|
30
|
+
* newer one that stands for it.
|
|
31
31
|
*/
|
|
32
32
|
export interface Substitution {
|
|
33
|
-
/** The
|
|
33
|
+
/** The command that turned the function on, and the one that cancelled it. */
|
|
34
34
|
replaced: readonly [Expression, Expression];
|
|
35
35
|
/**
|
|
36
|
-
* The
|
|
37
|
-
*
|
|
38
|
-
*
|
|
39
|
-
*
|
|
36
|
+
* The commands that say the same thing in the other scale's words.
|
|
37
|
+
* Usually one, but a held command is punched as a chain of round
|
|
38
|
+
* holes with paper bridges rather than as a slot, and a scan whose
|
|
39
|
+
* analysis reports those singly gives one symbol per punch.
|
|
40
40
|
*/
|
|
41
41
|
by: readonly Expression[];
|
|
42
42
|
}
|
|
@@ -51,23 +51,23 @@ export declare const defaultChainGap: import("./Quantity").Quantity<"mm">;
|
|
|
51
51
|
* The position a bar reads its own command for the same function on,
|
|
52
52
|
* where it has no word for this one.
|
|
53
53
|
*
|
|
54
|
-
* A version keeps the
|
|
55
|
-
*
|
|
56
|
-
*
|
|
57
|
-
*
|
|
58
|
-
*
|
|
59
|
-
*
|
|
54
|
+
* A version keeps the commands it does away with in its deletions, and
|
|
55
|
+
* those may be a scale the version is not coded for: a green version
|
|
56
|
+
* deletes the red `SlowCrescendoOn` it inherits. That has no position on
|
|
57
|
+
* the green bar, so drawing it as a perforation is out of the question,
|
|
58
|
+
* but an edit still has to be shown somewhere, and the lane the green
|
|
59
|
+
* scale gives the same function is where it belongs.
|
|
60
60
|
*/
|
|
61
61
|
export declare const positionOfSameFunction: (bar: TrackerBar, symbol: Expression) => Track | undefined;
|
|
62
62
|
/**
|
|
63
|
-
* Where a held
|
|
63
|
+
* Where a held command of the newer version stands for a latched
|
|
64
64
|
* function of the older one: the same function, on the same side where
|
|
65
65
|
* the side counts, spanning the same stretch of the roll.
|
|
66
66
|
*
|
|
67
|
-
* Only unambiguous correspondences are reported. Where two held
|
|
68
|
-
*
|
|
69
|
-
*
|
|
70
|
-
*
|
|
71
|
-
*
|
|
67
|
+
* Only unambiguous correspondences are reported. Where two held commands
|
|
68
|
+
* answer one latched interval, or one answers two, none of them is
|
|
69
|
+
* reported and the editor is left to say what happened. Nothing is
|
|
70
|
+
* invented either: both sides are perforations somebody punched, and all
|
|
71
|
+
* this asserts is which stands for which.
|
|
72
72
|
*/
|
|
73
73
|
export declare const substitutionsBetween: (own: readonly AnySymbol[], inherited: readonly AnySymbol[], locate: Locate, tolerance?: CollationTolerance, chainGap?: Millimeters) => Substitution[];
|
package/lib/substitution.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { defaultCollationTolerance } from "./Collation";
|
|
2
2
|
import { distance, mm, track } from "./Quantity";
|
|
3
3
|
/**
|
|
4
|
-
* What each Welte
|
|
4
|
+
* What each Welte expression type operates.
|
|
5
5
|
*
|
|
6
6
|
* This cannot be read off the names. The T-100 latches a function on
|
|
7
7
|
* with one perforation and cancels it with a second, while the T-98
|
|
@@ -10,8 +10,8 @@ import { distance, mm, track } from "./Quantity";
|
|
|
10
10
|
* between the scales: the red `SlowCrescendo` is the green `Crescendo`,
|
|
11
11
|
* and the red's single `Forzando` valve answers to two green ones that
|
|
12
12
|
* name the end they pull towards. So the correspondence is stated here
|
|
13
|
-
* rather than derived, and only two
|
|
14
|
-
*
|
|
13
|
+
* rather than derived, and only two types that operate one function can
|
|
14
|
+
* stand for each other.
|
|
15
15
|
*
|
|
16
16
|
* The T-100's `MotorOn`/`MotorOff`, `Rewind` and `ElectricCutOff` are
|
|
17
17
|
* left out on purpose: the green scale has no word for any of them, its
|
|
@@ -19,7 +19,7 @@ import { distance, mm, track } from "./Quantity";
|
|
|
19
19
|
* and its rewind riding on the bass sforzando-piano line. A red command
|
|
20
20
|
* of those kinds has no counterpart and stays a plain deletion.
|
|
21
21
|
*/
|
|
22
|
-
const
|
|
22
|
+
const operations = {
|
|
23
23
|
MezzoforteOn: { operates: 'mezzoforte', spelling: 'on', sided: true },
|
|
24
24
|
MezzoforteOff: { operates: 'mezzoforte', spelling: 'off', sided: true },
|
|
25
25
|
SlowCrescendoOn: { operates: 'crescendo', spelling: 'on', sided: true },
|
|
@@ -37,8 +37,8 @@ const commands = {
|
|
|
37
37
|
SustainPedal: { operates: 'sustainPedal', spelling: 'held', sided: false },
|
|
38
38
|
SoftPedal: { operates: 'softPedal', spelling: 'held', sided: false }
|
|
39
39
|
};
|
|
40
|
-
/** What
|
|
41
|
-
export const
|
|
40
|
+
/** What an expression type operates, or nothing for a word no scale here knows. */
|
|
41
|
+
export const operationOf = (expressionType) => operations[expressionType];
|
|
42
42
|
/**
|
|
43
43
|
* How wide a paper bridge may be for two held perforations to be one
|
|
44
44
|
* command. The T-98 punches a hold as a chain of round holes on a
|
|
@@ -48,20 +48,20 @@ export const commandOf = (expressionType) => commands[expressionType];
|
|
|
48
48
|
export const defaultChainGap = mm(3);
|
|
49
49
|
const isExpression = (symbol) => symbol.type === 'expression';
|
|
50
50
|
/** The key a command is matched on: the function, and the side where the side counts. */
|
|
51
|
-
const functionKey = (symbol,
|
|
51
|
+
const functionKey = (symbol, operation) => operation.sided ? `${operation.operates} ${symbol.scope}` : operation.operates;
|
|
52
52
|
/**
|
|
53
53
|
* The position a bar reads its own command for the same function on,
|
|
54
54
|
* where it has no word for this one.
|
|
55
55
|
*
|
|
56
|
-
* A version keeps the
|
|
57
|
-
*
|
|
58
|
-
*
|
|
59
|
-
*
|
|
60
|
-
*
|
|
61
|
-
*
|
|
56
|
+
* A version keeps the commands it does away with in its deletions, and
|
|
57
|
+
* those may be a scale the version is not coded for: a green version
|
|
58
|
+
* deletes the red `SlowCrescendoOn` it inherits. That has no position on
|
|
59
|
+
* the green bar, so drawing it as a perforation is out of the question,
|
|
60
|
+
* but an edit still has to be shown somewhere, and the lane the green
|
|
61
|
+
* scale gives the same function is where it belongs.
|
|
62
62
|
*/
|
|
63
63
|
export const positionOfSameFunction = (bar, symbol) => {
|
|
64
|
-
const wanted =
|
|
64
|
+
const wanted = operationOf(symbol.expressionType);
|
|
65
65
|
if (!wanted)
|
|
66
66
|
return undefined;
|
|
67
67
|
const positions = Array.from({ length: bar.trackCount }, (_, index) => track(index + 1));
|
|
@@ -69,26 +69,26 @@ export const positionOfSameFunction = (bar, symbol) => {
|
|
|
69
69
|
const meaning = bar.meaningOf(position);
|
|
70
70
|
if (meaning?.type !== 'expression')
|
|
71
71
|
return false;
|
|
72
|
-
const
|
|
73
|
-
return
|
|
74
|
-
&&
|
|
75
|
-
&& functionKey(meaning,
|
|
72
|
+
const operation = operationOf(meaning.expressionType);
|
|
73
|
+
return operation !== undefined
|
|
74
|
+
&& operation.operates === wanted.operates
|
|
75
|
+
&& functionKey(meaning, operation) === functionKey(symbol, wanted);
|
|
76
76
|
});
|
|
77
77
|
};
|
|
78
78
|
/**
|
|
79
|
-
* The intervals the older version latches: each
|
|
80
|
-
*
|
|
79
|
+
* The intervals the older version latches: each command that turns a
|
|
80
|
+
* function on, with the one that cancels it next on the same line.
|
|
81
81
|
*/
|
|
82
82
|
const latchedIn = (symbols, locate) => {
|
|
83
83
|
const byFunction = new Map();
|
|
84
84
|
symbols.filter(isExpression).forEach(symbol => {
|
|
85
|
-
const
|
|
85
|
+
const operation = operationOf(symbol.expressionType);
|
|
86
86
|
const at = locate(symbol)?.from;
|
|
87
|
-
if (!
|
|
87
|
+
if (!operation || operation.spelling === 'held' || at === undefined)
|
|
88
88
|
return;
|
|
89
|
-
const key = functionKey(symbol,
|
|
89
|
+
const key = functionKey(symbol, operation);
|
|
90
90
|
const group = byFunction.get(key) ?? [];
|
|
91
|
-
group.push({ symbol, at, spelling:
|
|
91
|
+
group.push({ symbol, at, spelling: operation.spelling });
|
|
92
92
|
byFunction.set(key, group);
|
|
93
93
|
});
|
|
94
94
|
return [...byFunction.values()].flatMap(group => {
|
|
@@ -109,7 +109,7 @@ const latchedIn = (symbols, locate) => {
|
|
|
109
109
|
off: entry.symbol,
|
|
110
110
|
from: state.open.from,
|
|
111
111
|
to: entry.at,
|
|
112
|
-
key: functionKey(state.open.on,
|
|
112
|
+
key: functionKey(state.open.on, operationOf(state.open.on.expressionType))
|
|
113
113
|
}]
|
|
114
114
|
}
|
|
115
115
|
: state;
|
|
@@ -117,17 +117,17 @@ const latchedIn = (symbols, locate) => {
|
|
|
117
117
|
});
|
|
118
118
|
};
|
|
119
119
|
/**
|
|
120
|
-
* The
|
|
120
|
+
* The functions the newer version holds, a chain of punches counting as
|
|
121
121
|
* the one command the paper shows rather than as a run of them.
|
|
122
122
|
*/
|
|
123
123
|
const heldRunsIn = (symbols, locate, chainGap) => {
|
|
124
124
|
const byFunction = new Map();
|
|
125
125
|
symbols.filter(isExpression).forEach(symbol => {
|
|
126
|
-
const
|
|
126
|
+
const operation = operationOf(symbol.expressionType);
|
|
127
127
|
const place = locate(symbol);
|
|
128
|
-
if (!
|
|
128
|
+
if (!operation || operation.spelling !== 'held' || !place)
|
|
129
129
|
return;
|
|
130
|
-
const key = functionKey(symbol,
|
|
130
|
+
const key = functionKey(symbol, operation);
|
|
131
131
|
const group = byFunction.get(key) ?? [];
|
|
132
132
|
group.push({ symbol, from: place.from, to: place.to });
|
|
133
133
|
byFunction.set(key, group);
|
|
@@ -147,15 +147,15 @@ const heldRunsIn = (symbols, locate, chainGap) => {
|
|
|
147
147
|
/** The only item of the list, or nothing where there is none or a rival. */
|
|
148
148
|
const theOnly = (items) => items.length === 1 ? items[0] : undefined;
|
|
149
149
|
/**
|
|
150
|
-
* Where a held
|
|
150
|
+
* Where a held command of the newer version stands for a latched
|
|
151
151
|
* function of the older one: the same function, on the same side where
|
|
152
152
|
* the side counts, spanning the same stretch of the roll.
|
|
153
153
|
*
|
|
154
|
-
* Only unambiguous correspondences are reported. Where two held
|
|
155
|
-
*
|
|
156
|
-
*
|
|
157
|
-
*
|
|
158
|
-
*
|
|
154
|
+
* Only unambiguous correspondences are reported. Where two held commands
|
|
155
|
+
* answer one latched interval, or one answers two, none of them is
|
|
156
|
+
* reported and the editor is left to say what happened. Nothing is
|
|
157
|
+
* invented either: both sides are perforations somebody punched, and all
|
|
158
|
+
* this asserts is which stands for which.
|
|
159
159
|
*/
|
|
160
160
|
export const substitutionsBetween = (own, inherited, locate, tolerance = defaultCollationTolerance, chainGap = defaultChainGap) => {
|
|
161
161
|
const latched = latchedIn(inherited, locate);
|
package/package.json
CHANGED