linked-rolls 0.0.1 → 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/README.md +48 -1
- package/lib/Assumption.d.ts +124 -0
- package/lib/Assumption.js +34 -0
- package/lib/Collation.d.ts +11 -0
- package/lib/ConditionState.d.ts +8 -3
- package/lib/ConditionState.js +0 -5
- package/lib/Edit.d.ts +44 -14
- package/lib/Edit.js +2 -95
- package/lib/Edition.d.ts +144 -4
- package/lib/EditionView.d.ts +51 -0
- package/lib/EditionView.js +294 -0
- package/lib/Emulation.d.ts +21 -68
- package/lib/Emulation.js +94 -386
- package/lib/Feature.d.ts +137 -16
- package/lib/Feature.js +9 -1
- package/lib/Plan.d.ts +190 -0
- package/lib/Plan.js +555 -0
- package/lib/ReproducingSystem.d.ts +80 -0
- package/lib/RollCopy.d.ts +259 -24
- package/lib/RollCopy.js +232 -215
- package/lib/Symbol.d.ts +70 -37
- package/lib/Symbol.js +2 -27
- package/lib/TrackCalibration.d.ts +33 -0
- package/lib/TrackCalibration.js +14 -0
- package/lib/TrackerBar.d.ts +52 -5
- package/lib/TrackerBar.js +70 -49
- package/lib/Version.d.ts +32 -33
- package/lib/Version.js +3 -209
- package/lib/alignFeatures.d.ts +3 -2
- package/lib/alignFeatures.js +5 -4
- package/lib/asJsonLd.d.ts +1 -1
- package/lib/asJsonLd.js +13 -26
- package/lib/importJsonLd.d.ts +0 -1
- package/lib/importJsonLd.js +9 -72
- package/lib/index.d.ts +7 -3
- package/lib/index.js +7 -3
- package/lib/schema.json +1739 -0
- package/lib/spec/context.json +125 -144
- package/lib/systems/welteT100.d.ts +55 -0
- package/lib/systems/welteT100.js +191 -0
- package/lib/utils.d.ts +29 -0
- package/lib/validate.d.ts +3 -0
- package/lib/validate.js +10 -0
- package/package.json +41 -8
- package/lib/Condition.d.ts +0 -10
- package/lib/EditorialAssumption.d.ts +0 -67
- package/lib/EditorialAssumption.js +0 -26
- package/lib/Measurement.d.ts +0 -9
- package/lib/PlaceTimeConversion.d.ts +0 -65
- package/lib/PlaceTimeConversion.js +0 -175
- package/lib/RollEvent.d.ts +0 -76
- package/lib/RollEvent.js +0 -3
- package/lib/Stage.d.ts +0 -37
- package/lib/Stage.js +0 -165
- package/lib/Transcription.d.ts +0 -7
- package/lib/Transcription.js +0 -9
- package/lib/WithId.d.ts +0 -3
- package/lib/WithId.js +0 -1
- package/lib/alignRolls.d.ts +0 -7
- package/lib/alignRolls.js +0 -49
- package/lib/alignSymbols.d.ts +0 -7
- package/lib/alignSymbols.js +0 -49
- package/lib/aton/AtonParser.test.d.ts +0 -1
- package/lib/aton/AtonParser.test.js +0 -16
- package/lib/build-schema.cjs +0 -113
- /package/lib/{Condition.js → ReproducingSystem.js} +0 -0
- /package/lib/{Measurement.js → utils.js} +0 -0
package/lib/spec/context.json
CHANGED
|
@@ -1,192 +1,173 @@
|
|
|
1
1
|
{
|
|
2
2
|
"@context": {
|
|
3
|
-
"
|
|
4
|
-
"lrmoo": "http://iflastandards.info/ns/lrm/lrmoo/",
|
|
3
|
+
"xsd": "http://www.w3.org/2001/XMLSchema#",
|
|
5
4
|
"rdfs": "http://www.w3.org/2000/01/rdf-schema#",
|
|
6
5
|
"owl": "http://www.w3.org/2002/07/owl#",
|
|
6
|
+
"dcterms": "http://purl.org/dc/terms/",
|
|
7
|
+
"crm": "http://www.cidoc-crm.org/cidoc-crm/",
|
|
8
|
+
"crmdig": "http://www.ics.forth.gr/isl/CRMdig/",
|
|
9
|
+
"crminf": "http://www.cidoc-crm.org/cidoc-crm/CRMinf/",
|
|
10
|
+
"lrmoo": "http://iflastandards.info/ns/lrm/lrmoo/",
|
|
7
11
|
"reo": "http://linked-rolls.org/reo/",
|
|
8
12
|
"@vocab": "http://linked-rolls.org/reo/",
|
|
9
|
-
"
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
13
|
+
"Edition": "lrmoo:F2_Expression",
|
|
14
|
+
"RollCopy": "reo:C1_Witness",
|
|
15
|
+
"Addition": "crm:E79_Part_Addition",
|
|
16
|
+
"Removal": "crm:E80_Part_Removal",
|
|
17
|
+
"title": "crm:P102_has_title",
|
|
18
|
+
"license": {
|
|
19
|
+
"@id": "dcterms:license",
|
|
20
|
+
"@type": "@id"
|
|
17
21
|
},
|
|
18
|
-
"
|
|
19
|
-
|
|
20
|
-
"@
|
|
21
|
-
|
|
22
|
-
"@id": "reo:P22_is_identified_by",
|
|
23
|
-
"@type": "xsd:string"
|
|
24
|
-
},
|
|
25
|
-
"witnesses": {
|
|
26
|
-
"@id": "reo:P2i_is_witnessed_by",
|
|
27
|
-
"@type": "@id"
|
|
28
|
-
}
|
|
29
|
-
}
|
|
22
|
+
"name": "rdfs:label",
|
|
23
|
+
"sameAs": {
|
|
24
|
+
"@id": "owl:sameAs",
|
|
25
|
+
"@type": "@id"
|
|
30
26
|
},
|
|
31
|
-
"
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
"basedOn": {
|
|
39
|
-
"@id": "reo:P10i_is_based_on",
|
|
40
|
-
"@type": "reo:C11_Derivation"
|
|
41
|
-
},
|
|
42
|
-
"edits": {
|
|
43
|
-
"@id": "reo:P16_involved_edit",
|
|
44
|
-
"@type": "reo:C8_Edit"
|
|
45
|
-
},
|
|
46
|
-
"intentions": {
|
|
47
|
-
"@id": "reo:P18_was_motivated_by",
|
|
48
|
-
"@type": "reo:C9_Assumed_Intention"
|
|
49
|
-
}
|
|
50
|
-
}
|
|
27
|
+
"role": "crm:P2_has_type",
|
|
28
|
+
"date": "crm:P4_has_time-span",
|
|
29
|
+
"actor": "crm:P14_carried_out_by",
|
|
30
|
+
"description": "crm:P3_has_note",
|
|
31
|
+
"depiction": {
|
|
32
|
+
"@id": "crm:P62i_is_depicted_by",
|
|
33
|
+
"@type": "@id"
|
|
51
34
|
},
|
|
52
|
-
"
|
|
53
|
-
|
|
35
|
+
"horizontal": "crm:P43_has_dimension",
|
|
36
|
+
"vertical": "crm:P43_has_dimension",
|
|
37
|
+
"unit": "crm:P91_has_unit",
|
|
38
|
+
"scan": {
|
|
39
|
+
"@id": "crm:P138i_has_representation",
|
|
40
|
+
"@type": "@id"
|
|
41
|
+
},
|
|
42
|
+
"carriers": {
|
|
43
|
+
"@id": "crm:P128i_is_carried_by",
|
|
44
|
+
"@type": "@id"
|
|
45
|
+
},
|
|
46
|
+
"pitch": "reo:P5_has_pitch",
|
|
47
|
+
"scope": "reo:P6_has_scope",
|
|
48
|
+
"expressionType": "crm:P2_has_type",
|
|
49
|
+
"alignedWith": "reo:P3_aligned_with",
|
|
50
|
+
"material": "crm:P45_consists_of",
|
|
51
|
+
"condition": "crm:P44_has_condition",
|
|
52
|
+
"creation": {
|
|
53
|
+
"@id": "lrmoo:R17i_was_created_by",
|
|
54
54
|
"@context": {
|
|
55
|
-
"
|
|
56
|
-
|
|
57
|
-
"@
|
|
55
|
+
"publisher": "crm:P14_carried_out_by",
|
|
56
|
+
"publicationDate": {
|
|
57
|
+
"@id": "crm:P4_has_time-span",
|
|
58
|
+
"@type": "xsd:date"
|
|
58
59
|
},
|
|
59
|
-
"
|
|
60
|
-
"@id": "crminf:J4i_is_subject_to",
|
|
61
|
-
"@type": "crminf:I2_Belief"
|
|
62
|
-
}
|
|
60
|
+
"collationTolerance": "crmdig:L13_used_parameters"
|
|
63
61
|
}
|
|
64
62
|
},
|
|
65
|
-
"
|
|
66
|
-
"@id": "
|
|
63
|
+
"roll": {
|
|
64
|
+
"@id": "lrmoo:R3i_realises",
|
|
67
65
|
"@context": {
|
|
68
|
-
"
|
|
69
|
-
|
|
70
|
-
"@
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
66
|
+
"catalogueNumber": "crm:P1_is_identified_by",
|
|
67
|
+
"recordingEvent": {
|
|
68
|
+
"@id": "lrmoo:R19i_was_realised_through",
|
|
69
|
+
"@context": {
|
|
70
|
+
"recorded": {
|
|
71
|
+
"@id": "lrmoo:R81_recorded",
|
|
72
|
+
"@context": {
|
|
73
|
+
"pianist": "crm:P14_carried_out_by",
|
|
74
|
+
"playing": {
|
|
75
|
+
"@type": "@id"
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
},
|
|
79
|
+
"place": "crm:P7_took_place_at",
|
|
80
|
+
"created": "lrmoo:R17_created"
|
|
81
|
+
}
|
|
75
82
|
}
|
|
76
83
|
}
|
|
77
84
|
},
|
|
78
|
-
"
|
|
85
|
+
"copies": {
|
|
86
|
+
"@id": "lrmoo:R2_is_derivative_of",
|
|
79
87
|
"@context": {
|
|
80
|
-
"
|
|
81
|
-
"
|
|
82
|
-
"@id": "reo:P21_is_identified_by",
|
|
83
|
-
"@type": "xsd:string"
|
|
84
|
-
},
|
|
85
|
-
"productionEvent": {
|
|
88
|
+
"location": "crm:P55_has_current_location",
|
|
89
|
+
"production": {
|
|
86
90
|
"@id": "lrmoo:R28i_was_produced_by",
|
|
87
|
-
"@type": "lrmoo:F33_Reproduction_Event",
|
|
88
91
|
"@context": {
|
|
89
|
-
"
|
|
90
|
-
|
|
91
|
-
"@type": "xsd:dateTime"
|
|
92
|
-
},
|
|
93
|
-
"paper": {
|
|
94
|
-
"@id": "crm:P126_employed",
|
|
95
|
-
"@type": "@id"
|
|
96
|
-
},
|
|
97
|
-
"company": {
|
|
98
|
-
"@id": "crm:P14_was_carried_out_by",
|
|
99
|
-
"@type": "@id"
|
|
100
|
-
}
|
|
92
|
+
"company": "crm:P14_carried_out_by",
|
|
93
|
+
"paper": "crm:P126_employed"
|
|
101
94
|
}
|
|
102
95
|
},
|
|
103
|
-
"
|
|
104
|
-
|
|
105
|
-
"@
|
|
96
|
+
"conditions": "crm:P44_has_condition",
|
|
97
|
+
"modifications": {
|
|
98
|
+
"@id": "crm:P31i_was_modified_by",
|
|
99
|
+
"@context": {
|
|
100
|
+
"added": "crm:P111_added",
|
|
101
|
+
"removed": "crm:P113_removed",
|
|
102
|
+
"purpose": "crm:P21_had_general_purpose"
|
|
103
|
+
}
|
|
106
104
|
},
|
|
107
105
|
"measurements": {
|
|
108
106
|
"@id": "crm:P39i_was_measured_by",
|
|
109
|
-
"@type": "reo:RollMeasurement",
|
|
110
107
|
"@context": {
|
|
111
|
-
"
|
|
112
|
-
"@
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
"software": {
|
|
116
|
-
"@id": "crmdig:L23_used_software_or_firmware",
|
|
117
|
-
"@type": "@id"
|
|
108
|
+
"measuredBy": {
|
|
109
|
+
"@context": {
|
|
110
|
+
"software": "crmdig:L23_used_software_or_firmware"
|
|
111
|
+
}
|
|
118
112
|
}
|
|
119
113
|
}
|
|
120
114
|
},
|
|
121
|
-
"
|
|
122
|
-
"@id": "crm:
|
|
115
|
+
"features": {
|
|
116
|
+
"@id": "crm:P56_bears_feature",
|
|
123
117
|
"@context": {
|
|
124
|
-
"
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
"annotates": {
|
|
129
|
-
"@id": "crm:P62i_is_depicted_by",
|
|
130
|
-
"@type": "@id"
|
|
131
|
-
},
|
|
132
|
-
"horizontal": {
|
|
133
|
-
"@id": "crm:P43_has_dimension",
|
|
134
|
-
"@type": "reo:C5_Event_Span"
|
|
135
|
-
},
|
|
136
|
-
"vertical": {
|
|
137
|
-
"@id": "crm:P43_has_dimension",
|
|
138
|
-
"@type": "reo:C5_Event_Span"
|
|
139
|
-
}
|
|
118
|
+
"method": "crm:P2_has_type",
|
|
119
|
+
"transcription": "crm:P128_carries",
|
|
120
|
+
"pattern": "crm:P2_has_type",
|
|
121
|
+
"features": "crm:P56_bears_feature"
|
|
140
122
|
}
|
|
141
123
|
}
|
|
142
124
|
}
|
|
143
125
|
},
|
|
144
|
-
"
|
|
145
|
-
"@
|
|
126
|
+
"versions": {
|
|
127
|
+
"@id": "lrmoo:R76_is_derivative_of",
|
|
146
128
|
"@context": {
|
|
147
|
-
"
|
|
148
|
-
|
|
129
|
+
"siglum": "reo:P22_is_identified_by",
|
|
130
|
+
"basedOn": {
|
|
131
|
+
"@id": "reo:P10i_is_based_on",
|
|
132
|
+
"@type": "@id"
|
|
133
|
+
},
|
|
134
|
+
"motivations": "@included",
|
|
135
|
+
"edits": {
|
|
136
|
+
"@id": "reo:P16_involved_edit",
|
|
137
|
+
"@context": {
|
|
138
|
+
"editType": "crm:P2_has_type",
|
|
139
|
+
"motivation": {
|
|
140
|
+
"@id": "crm:P17_was_motivated_by",
|
|
141
|
+
"@type": "@id"
|
|
142
|
+
},
|
|
143
|
+
"insert": "reo:P7_added",
|
|
144
|
+
"delete": {
|
|
145
|
+
"@id": "reo:P8_removed",
|
|
146
|
+
"@type": "@id"
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
149
|
}
|
|
150
150
|
}
|
|
151
151
|
},
|
|
152
|
-
"
|
|
153
|
-
"@id": "
|
|
154
|
-
"@type": "lrmoo:F28_Expression_Creation",
|
|
152
|
+
"belief": {
|
|
153
|
+
"@id": "crminf:J4i_is_subject_to",
|
|
155
154
|
"@context": {
|
|
156
|
-
"
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
"publisher": {
|
|
161
|
-
"@id": "crm:P14_carried_out_by",
|
|
162
|
-
"@type": "crm:E21_Person",
|
|
155
|
+
"belief": "crminf:I2_Belief",
|
|
156
|
+
"certainty": "crminf:J5_holds_to_be",
|
|
157
|
+
"reasons": {
|
|
158
|
+
"@id": "crminf:J2i_was_concluded_by",
|
|
163
159
|
"@context": {
|
|
164
|
-
"
|
|
165
|
-
"@id": "
|
|
166
|
-
"@type": "
|
|
160
|
+
"comprehends": {
|
|
161
|
+
"@id": "crminf:J22_interpreted_meaning_of",
|
|
162
|
+
"@type": "@id"
|
|
167
163
|
},
|
|
168
|
-
"
|
|
169
|
-
"@id": "
|
|
164
|
+
"premises": {
|
|
165
|
+
"@id": "crminf:J1_used_as_premise",
|
|
170
166
|
"@type": "@id"
|
|
171
167
|
}
|
|
172
168
|
}
|
|
173
169
|
}
|
|
174
170
|
}
|
|
175
|
-
}
|
|
176
|
-
"title": "crm:P102_has_title",
|
|
177
|
-
"license": {
|
|
178
|
-
"@id": "crm:P104_is_subject_to",
|
|
179
|
-
"@type": "@id"
|
|
180
|
-
},
|
|
181
|
-
"copies": {
|
|
182
|
-
"@id": "lrmoo:R2_is_derivative_of",
|
|
183
|
-
"@type": "reo:C1_Witness"
|
|
184
|
-
},
|
|
185
|
-
"collation": "crm:P9_consists_of",
|
|
186
|
-
"versions": {
|
|
187
|
-
"@id": "crm:P9_consists_of",
|
|
188
|
-
"@type": "reo:C13_Version_Creation"
|
|
189
|
-
},
|
|
190
|
-
"questions": "crm:P9_consists_of"
|
|
171
|
+
}
|
|
191
172
|
}
|
|
192
173
|
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { Half, Parameters, PedalMode, Spool } from "welte-t100-emulator";
|
|
2
|
+
import { ReproducingSystem } from "../ReproducingSystem";
|
|
3
|
+
/**
|
|
4
|
+
* MIDI velocity at the open rail of the Nuancierbalg, at the Mezzoforte pin,
|
|
5
|
+
* and at the closed rail, joined linearly in between. Nothing in the
|
|
6
|
+
* mechanism determines how bellows travel maps onto hammer velocity, so
|
|
7
|
+
* these are anchors rather than measurements; the three values are the
|
|
8
|
+
* ones midi2exp publishes.
|
|
9
|
+
*/
|
|
10
|
+
export type VelocityMap = {
|
|
11
|
+
piano: number;
|
|
12
|
+
mezzoforte: number;
|
|
13
|
+
forte: number;
|
|
14
|
+
};
|
|
15
|
+
export type WelteT100Options = {
|
|
16
|
+
/**
|
|
17
|
+
* The take-up spool, which sets the time axis: it is held at a constant
|
|
18
|
+
* rate of revolution, so the paper runs faster as the spool fills.
|
|
19
|
+
*/
|
|
20
|
+
spool: Spool;
|
|
21
|
+
/** Constants of the nuancing mechanism, one set for each half of the keyboard. */
|
|
22
|
+
nuance: Record<Half, Parameters>;
|
|
23
|
+
/** Constants of the two pedal actions. */
|
|
24
|
+
pedals: Parameters;
|
|
25
|
+
velocity: VelocityMap;
|
|
26
|
+
/**
|
|
27
|
+
* How the pedals go out: as a continuous controller carrying the travel
|
|
28
|
+
* of the bellows, or thresholded to the two values a switching renderer
|
|
29
|
+
* understands.
|
|
30
|
+
*/
|
|
31
|
+
pedalMode: PedalMode;
|
|
32
|
+
/** Diameter of the tracker-bar bore, in mm. */
|
|
33
|
+
trackerBore: number;
|
|
34
|
+
/** Punch diameter in mm, for an edition whose copies record none. */
|
|
35
|
+
punchDiameter: number;
|
|
36
|
+
/**
|
|
37
|
+
* The track at which the keyboard is divided, so that notes from
|
|
38
|
+
* here upwards follow the treble expression and the ones below it
|
|
39
|
+
* the bass. Which side an expression perforation itself belongs to
|
|
40
|
+
* is not decided here but read off the tracker bar.
|
|
41
|
+
*/
|
|
42
|
+
division: number;
|
|
43
|
+
};
|
|
44
|
+
export declare const defaultWelteT100Options: WelteT100Options;
|
|
45
|
+
/** When the spool brings a place on the roll, in mm, to the tracker bar. */
|
|
46
|
+
export declare const secondsAt: (spool: Spool, mm: number) => number;
|
|
47
|
+
/**
|
|
48
|
+
* The red Welte, as welte-t100-emulator models it: the take-up spool sets
|
|
49
|
+
* the time axis, the Nuancierbälge fill through their conduits and are
|
|
50
|
+
* arrested by the Mezzoforte pin, and the two pedals travel rather than
|
|
51
|
+
* switch. The constants are those fitted against the hand-drawn nuance
|
|
52
|
+
* lines of roll 3309, with the terms that describe the drawing apparatus
|
|
53
|
+
* switched off.
|
|
54
|
+
*/
|
|
55
|
+
export declare const welteT100System: ReproducingSystem<WelteT100Options>;
|
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
import { aperturePorts, DEFAULT_PUNCH_MM, geometryInMm, Grid, levelChanges, mezzoforteTravel, noteDensity, paperSeconds, pedalDefaults, playbackParameters, pneumaticModel, ROWS_PER_MM, runPedals, TRACKER_BORE_MM, travelBetweenRails, WELTE_SPOOL, } from "welte-t100-emulator";
|
|
2
|
+
import { welteT100 } from "../TrackerBar";
|
|
3
|
+
export const defaultWelteT100Options = {
|
|
4
|
+
spool: WELTE_SPOOL,
|
|
5
|
+
nuance: {
|
|
6
|
+
bass: playbackParameters('bass'),
|
|
7
|
+
treble: playbackParameters('treble')
|
|
8
|
+
},
|
|
9
|
+
pedals: pedalDefaults,
|
|
10
|
+
velocity: { piano: 35, mezzoforte: 60, forte: 90 },
|
|
11
|
+
pedalMode: 'continuous',
|
|
12
|
+
trackerBore: TRACKER_BORE_MM,
|
|
13
|
+
punchDiameter: DEFAULT_PUNCH_MM,
|
|
14
|
+
division: 54
|
|
15
|
+
};
|
|
16
|
+
/** What each expression code operates, in the emulator's terms. */
|
|
17
|
+
const CODES = new Map([
|
|
18
|
+
['MezzoforteOn', ['mezzoforte', 'on']],
|
|
19
|
+
['MezzoforteOff', ['mezzoforte', 'off']],
|
|
20
|
+
['SlowCrescendoOn', ['crescendo', 'on']],
|
|
21
|
+
['SlowCrescendoOff', ['crescendo', 'off']],
|
|
22
|
+
['ForzandoOn', ['sforzando', 'on']],
|
|
23
|
+
['ForzandoOff', ['sforzando', 'off']],
|
|
24
|
+
['SustainPedalOn', ['sustainPedal', 'on']],
|
|
25
|
+
['SustainPedalOff', ['sustainPedal', 'off']],
|
|
26
|
+
['SoftPedalOn', ['hammerRail', 'on']],
|
|
27
|
+
['SoftPedalOff', ['hammerRail', 'off']],
|
|
28
|
+
['MotorOn', ['windResistance', 'on']],
|
|
29
|
+
['MotorOff', ['windResistance', 'off']],
|
|
30
|
+
['Rewind', ['rewind', 'on']],
|
|
31
|
+
['ElectricCutOff', ['electricCutoff', 'on']]
|
|
32
|
+
]);
|
|
33
|
+
/** Paper the grid runs on past the last hole, so that a final pedal release completes. */
|
|
34
|
+
const RUN_OUT_MM = 100;
|
|
35
|
+
/**
|
|
36
|
+
* Which stack of valves an expression perforation belongs to. The symbol
|
|
37
|
+
* usually says so, having been read off the tracker bar already; where it
|
|
38
|
+
* does not, the bar is asked again, since sending a perforation to neither
|
|
39
|
+
* side would quietly flatten the dynamics.
|
|
40
|
+
*/
|
|
41
|
+
const scopeOf = (event) => {
|
|
42
|
+
if (event.scope)
|
|
43
|
+
return event.scope;
|
|
44
|
+
const meaning = welteT100.meaningOf(event.vertical.from);
|
|
45
|
+
return meaning?.type === 'expression' ? meaning.scope : undefined;
|
|
46
|
+
};
|
|
47
|
+
const isNote = (event) => event.type === 'note';
|
|
48
|
+
const isExpression = (event) => event.type === 'expression';
|
|
49
|
+
const rowOf = (mm) => mm * ROWS_PER_MM;
|
|
50
|
+
/** When the spool brings a place on the roll, in mm, to the tracker bar. */
|
|
51
|
+
export const secondsAt = (spool, mm) => paperSeconds(spool, mm / 10);
|
|
52
|
+
const halfOf = (note, division) => note.vertical.from >= division ? 'treble' : 'bass';
|
|
53
|
+
const readingOf = (event) => {
|
|
54
|
+
const code = CODES.get(event.expressionType);
|
|
55
|
+
const half = scopeOf(event);
|
|
56
|
+
if (!code || !half)
|
|
57
|
+
return undefined;
|
|
58
|
+
const [control, action] = code;
|
|
59
|
+
return {
|
|
60
|
+
event,
|
|
61
|
+
punch: { half, control, action, rowOn: rowOf(event.horizontal.from), rowOff: rowOf(event.horizontal.to) }
|
|
62
|
+
};
|
|
63
|
+
};
|
|
64
|
+
const clamp = (value, low, high) => Math.min(Math.max(value, low), high);
|
|
65
|
+
/**
|
|
66
|
+
* The velocity map joined linearly through its three anchors, with the
|
|
67
|
+
* middle one at the Mezzoforte pin of the half in question.
|
|
68
|
+
*/
|
|
69
|
+
const velocityOf = (travel, hook, map) => {
|
|
70
|
+
const position = clamp(travel, 0, 1);
|
|
71
|
+
if (position <= hook) {
|
|
72
|
+
return map.piano + (position / hook) * (map.mezzoforte - map.piano);
|
|
73
|
+
}
|
|
74
|
+
return map.mezzoforte + ((position - hook) / (1 - hook)) * (map.forte - map.mezzoforte);
|
|
75
|
+
};
|
|
76
|
+
/**
|
|
77
|
+
* One sample per row of the scan the constants were fitted on, from the
|
|
78
|
+
* beginning of the roll to a little past the last hole. The rows are
|
|
79
|
+
* equally spaced on the paper and not in time, which is what the
|
|
80
|
+
* emulator expects.
|
|
81
|
+
*/
|
|
82
|
+
const gridOver = (events, spool) => {
|
|
83
|
+
const lastMm = events.reduce((furthest, event) => Math.max(furthest, event.horizontal.to), 0);
|
|
84
|
+
const length = Math.ceil(rowOf(lastMm + RUN_OUT_MM)) + 1;
|
|
85
|
+
const seconds = Float64Array.from({ length }, (_, row) => secondsAt(spool, row / ROWS_PER_MM));
|
|
86
|
+
return new Grid(0, seconds);
|
|
87
|
+
};
|
|
88
|
+
const nuanceCurves = (grid, ports, samples, events, options) => {
|
|
89
|
+
const onsetRows = (half) => events
|
|
90
|
+
.filter(isNote)
|
|
91
|
+
.filter(note => halfOf(note, options.division) === half)
|
|
92
|
+
.map(note => rowOf(note.horizontal.from));
|
|
93
|
+
const density = {
|
|
94
|
+
bass: noteDensity(grid, onsetRows('bass')),
|
|
95
|
+
treble: noteDensity(grid, onsetRows('treble')),
|
|
96
|
+
};
|
|
97
|
+
const totalNoteDensity = Float64Array.from(density.bass, (value, index) => value + density.treble[index]);
|
|
98
|
+
const curveOf = (half) => {
|
|
99
|
+
const params = options.nuance[half];
|
|
100
|
+
const output = pneumaticModel.run({ grid, half, ports, noteDensity: density[half], totalNoteDensity }, params);
|
|
101
|
+
const travel = travelBetweenRails(output, params);
|
|
102
|
+
const hook = clamp(mezzoforteTravel(params), 0.01, 0.99);
|
|
103
|
+
return {
|
|
104
|
+
...samples,
|
|
105
|
+
name: half,
|
|
106
|
+
kind: 'dynamics',
|
|
107
|
+
travel,
|
|
108
|
+
velocity: Float64Array.from(travel, value => velocityOf(value, hook, options.velocity))
|
|
109
|
+
};
|
|
110
|
+
};
|
|
111
|
+
return { bass: curveOf('bass'), treble: curveOf('treble') };
|
|
112
|
+
};
|
|
113
|
+
const pedalCurves = (grid, ports, samples, options) => {
|
|
114
|
+
const travel = runPedals({ grid, ports }, options.pedals);
|
|
115
|
+
return {
|
|
116
|
+
damper: { ...samples, name: 'damper', kind: 'pedal', travel: travel.damper },
|
|
117
|
+
hammerRail: { ...samples, name: 'hammerRail', kind: 'pedal', travel: travel.hammerRail }
|
|
118
|
+
};
|
|
119
|
+
};
|
|
120
|
+
const performNotes = (events, grid, nuance, options) => events
|
|
121
|
+
.filter(isNote)
|
|
122
|
+
.flatMap((note) => {
|
|
123
|
+
const curve = nuance[halfOf(note, options.division)];
|
|
124
|
+
const velocity = curve.velocity[grid.indexOfRow(rowOf(note.horizontal.from))];
|
|
125
|
+
return [
|
|
126
|
+
{ type: 'noteOn', performs: note, pitch: note.pitch, velocity, at: secondsAt(options.spool, note.horizontal.from) },
|
|
127
|
+
{ type: 'noteOff', performs: note, pitch: note.pitch, velocity: 127, at: secondsAt(options.spool, note.horizontal.to) }
|
|
128
|
+
];
|
|
129
|
+
});
|
|
130
|
+
/**
|
|
131
|
+
* The travel of one pedal as controller steps. Each step is attributed to
|
|
132
|
+
* the last perforation of that pedal the tracker bar has reached, which is
|
|
133
|
+
* the one whose reading it follows from.
|
|
134
|
+
*/
|
|
135
|
+
const performPedal = (type, curve, grid, readings, mode) => {
|
|
136
|
+
if (readings.length === 0)
|
|
137
|
+
return [];
|
|
138
|
+
const ordered = readings.toSorted((a, b) => a.punch.rowOn - b.punch.rowOn);
|
|
139
|
+
const causeOf = (row) => ordered[Math.max(ordered.findLastIndex(reading => reading.punch.rowOn <= row), 0)].event;
|
|
140
|
+
return levelChanges(curve.travel, { mode })
|
|
141
|
+
.filter(change => change.index > 0)
|
|
142
|
+
.map(change => ({
|
|
143
|
+
type,
|
|
144
|
+
performs: causeOf(grid.rowAt(change.index)),
|
|
145
|
+
value: change.value,
|
|
146
|
+
at: curve.seconds[change.index]
|
|
147
|
+
}));
|
|
148
|
+
};
|
|
149
|
+
/**
|
|
150
|
+
* The edition's tempo adjustment is left aside: it is stated as a paper
|
|
151
|
+
* speed, and what the spool holds constant is its rate of revolution, so
|
|
152
|
+
* the two are not the same quantity. The spool in the options sets the speed.
|
|
153
|
+
*/
|
|
154
|
+
const perform = (events, options, roll) => {
|
|
155
|
+
const readings = events
|
|
156
|
+
.filter(isExpression)
|
|
157
|
+
.map(readingOf)
|
|
158
|
+
.filter((reading) => reading !== undefined);
|
|
159
|
+
const grid = gridOver(events, options.spool);
|
|
160
|
+
const geometry = geometryInMm(roll.punchDiameter ?? options.punchDiameter, options.trackerBore);
|
|
161
|
+
const ports = aperturePorts(grid, readings.map(reading => reading.punch), geometry);
|
|
162
|
+
const samples = {
|
|
163
|
+
place: Float64Array.from(grid.seconds, (_, row) => row / ROWS_PER_MM),
|
|
164
|
+
seconds: grid.seconds
|
|
165
|
+
};
|
|
166
|
+
const nuance = nuanceCurves(grid, ports, samples, events, options);
|
|
167
|
+
const pedals = pedalCurves(grid, ports, samples, options);
|
|
168
|
+
const readingsOf = (control) => readings.filter(reading => reading.punch.control === control);
|
|
169
|
+
return {
|
|
170
|
+
events: [
|
|
171
|
+
...performNotes(events, grid, nuance, options),
|
|
172
|
+
...performPedal('damper', pedals.damper, grid, readingsOf('sustainPedal'), options.pedalMode),
|
|
173
|
+
...performPedal('hammerRail', pedals.hammerRail, grid, readingsOf('hammerRail'), options.pedalMode)
|
|
174
|
+
],
|
|
175
|
+
curves: [nuance.bass, nuance.treble, pedals.damper, pedals.hammerRail]
|
|
176
|
+
};
|
|
177
|
+
};
|
|
178
|
+
/**
|
|
179
|
+
* The red Welte, as welte-t100-emulator models it: the take-up spool sets
|
|
180
|
+
* the time axis, the Nuancierbälge fill through their conduits and are
|
|
181
|
+
* arrested by the Mezzoforte pin, and the two pedals travel rather than
|
|
182
|
+
* switch. The constants are those fitted against the hand-drawn nuance
|
|
183
|
+
* lines of roll 3309, with the terms that describe the drawing apparatus
|
|
184
|
+
* switched off.
|
|
185
|
+
*/
|
|
186
|
+
export const welteT100System = {
|
|
187
|
+
name: 'Welte-Mignon T100',
|
|
188
|
+
trackerBar: welteT100,
|
|
189
|
+
defaultOptions: defaultWelteT100Options,
|
|
190
|
+
perform
|
|
191
|
+
};
|
package/lib/utils.d.ts
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { Person } from "./Edition";
|
|
2
|
+
export type PartialBy<T, K extends keyof T> = Omit<T, K> & Partial<Pick<T, K>>;
|
|
3
|
+
export type WithType<T extends string> = {
|
|
4
|
+
/**
|
|
5
|
+
* The type discriminator for this object.
|
|
6
|
+
* @see rdf:type
|
|
7
|
+
*/
|
|
8
|
+
readonly type: T;
|
|
9
|
+
};
|
|
10
|
+
export type WithId = {
|
|
11
|
+
/**
|
|
12
|
+
* A unique identifier for this object.
|
|
13
|
+
*/
|
|
14
|
+
readonly id: string;
|
|
15
|
+
};
|
|
16
|
+
export type WithActor = {
|
|
17
|
+
/**
|
|
18
|
+
* The person who carried out this activity.
|
|
19
|
+
* @see crm:P14 carried out by
|
|
20
|
+
*/
|
|
21
|
+
actor?: Person;
|
|
22
|
+
};
|
|
23
|
+
export type WithNote = {
|
|
24
|
+
/**
|
|
25
|
+
* A free-text note providing additional context.
|
|
26
|
+
* @see crm:P3 has note
|
|
27
|
+
*/
|
|
28
|
+
note?: string;
|
|
29
|
+
};
|