linked-rolls 0.19.1 → 0.21.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.
Files changed (49) hide show
  1. package/README.md +31 -6
  2. package/lib/Collation.d.ts +1 -3
  3. package/lib/Collation.js +9 -11
  4. package/lib/Edition.d.ts +1 -9
  5. package/lib/EditionView.d.ts +54 -6
  6. package/lib/EditionView.js +93 -20
  7. package/lib/Emulation.js +5 -4
  8. package/lib/ReproducingSystem.d.ts +38 -2
  9. package/lib/RollCopy.d.ts +25 -2
  10. package/lib/RollCopy.js +30 -4
  11. package/lib/TrackerBar.d.ts +57 -2
  12. package/lib/TrackerBar.js +35 -17
  13. package/lib/Version.d.ts +49 -1
  14. package/lib/asJsonLd.js +23 -8
  15. package/lib/constraints.d.ts +8 -19
  16. package/lib/constraints.js +71 -15
  17. package/lib/context.d.ts +10 -1
  18. package/lib/context.js +10 -1
  19. package/lib/editionOps.d.ts +3 -1
  20. package/lib/editionOps.js +77 -15
  21. package/lib/importJsonLd.js +4 -2
  22. package/lib/index.d.ts +1 -0
  23. package/lib/index.js +1 -0
  24. package/lib/migrate.js +85 -11
  25. package/lib/readers/phillipsEroll.d.ts +4 -6
  26. package/lib/readers/phillipsEroll.js +6 -7
  27. package/lib/readers/spencerBar.d.ts +4 -5
  28. package/lib/readers/spencerBar.js +7 -8
  29. package/lib/readers/stanfordAton.d.ts +4 -7
  30. package/lib/readers/stanfordAton.js +4 -5
  31. package/lib/reservations.d.ts +1 -1
  32. package/lib/reservations.js +24 -2
  33. package/lib/schema.json +27 -5
  34. package/lib/spec/context.json +1 -0
  35. package/lib/spec/welte-licensee.context.json +10 -0
  36. package/lib/spec/welte-t98.context.json +10 -0
  37. package/lib/substitution.d.ts +73 -0
  38. package/lib/substitution.js +175 -0
  39. package/lib/systems/velocity.d.ts +34 -0
  40. package/lib/systems/velocity.js +36 -0
  41. package/lib/systems/welteLicensee/system.d.ts +46 -0
  42. package/lib/systems/welteLicensee/system.js +60 -0
  43. package/lib/systems/welteT100/system.d.ts +19 -16
  44. package/lib/systems/welteT100/system.js +42 -47
  45. package/lib/systems/welteT98/bar.d.ts +15 -2
  46. package/lib/systems/welteT98/bar.js +31 -3
  47. package/lib/systems/welteT98/system.d.ts +119 -0
  48. package/lib/systems/welteT98/system.js +245 -0
  49. package/package.json +12 -4
package/lib/migrate.js CHANGED
@@ -1,6 +1,7 @@
1
1
  import { conditions } from "./Feature";
2
2
  import { rollConditions } from "./RollCopy";
3
- import { systemOf } from "./TrackerBar";
3
+ import { systemIdIn, systemOf, translationBetween } from "./TrackerBar";
4
+ import { trackerBars } from "./systems";
4
5
  import { welteT100 } from "./systems/welteT100/bar";
5
6
  import { versionTypes } from "./Version";
6
7
  const versionTypeValues = new Set(versionTypes);
@@ -88,20 +89,93 @@ const walk = (value) => {
88
89
  return withWalkedChildren(migrateNode(value));
89
90
  return value;
90
91
  };
92
+ /** The production of a copy, under whichever of its two names it carries. */
93
+ const productionKeyOf = (copy) => copy?.productionEvent && !copy?.production ? 'productionEvent' : 'production';
94
+ /** The list under the key, or nothing where the document holds something else there. */
95
+ const listAt = (edition, key) => Array.isArray(edition[key]) ? edition[key] : undefined;
96
+ /** A file written while the system belonged to the roll rather than to each version. */
97
+ const namesSystemOnTheRoll = (edition) => edition.roll?.system !== undefined
98
+ || (listAt(edition, 'versions') ?? []).some((version) => !version?.system);
99
+ const barNamed = (system) => trackerBars.find(bar => bar.id === systemIdIn(system?.['@id']));
100
+ const withSystem = (node, system) => node.system && typeof node.system === 'object' ? node : { ...node, system };
101
+ /** A span on the other bar, or nothing where that bar does not read one of its ends. */
102
+ const spanOnBar = (span, at) => {
103
+ const from = at(span.from);
104
+ if (from === undefined)
105
+ return undefined;
106
+ if (span.to === undefined)
107
+ return { ...span, from };
108
+ const to = at(span.to);
109
+ return to === undefined ? undefined : { ...span, from, to };
110
+ };
111
+ /** The features on the copy's own bar, those it does not read left out, patches and all. */
112
+ const featuresOnBar = (features, at) => features.flatMap((feature) => {
113
+ const vertical = feature.vertical?.unit === 'track'
114
+ ? spanOnBar(feature.vertical, at)
115
+ : feature.vertical;
116
+ if (!vertical)
117
+ return [];
118
+ return [{
119
+ ...feature,
120
+ vertical,
121
+ ...(Array.isArray(feature.features) && { features: featuresOnBar(feature.features, at) })
122
+ }];
123
+ });
124
+ /**
125
+ * A copy cut for another system had its holes put onto the roll's bar
126
+ * as it was read, so they are numbered in the roll's system and not in
127
+ * its own. They go back onto the bar the copy names, which is the only
128
+ * numbering that means anything once each copy is read by its own bar.
129
+ * Between two Welte scales the difference is two tracks, which is a
130
+ * legal position a whole tone away rather than a visible error.
131
+ */
132
+ const onOwnBar = (copy, rollSystem) => {
133
+ if (!copy || typeof copy !== 'object')
134
+ return copy;
135
+ const key = productionKeyOf(copy);
136
+ const own = copy[key]?.system;
137
+ const from = barNamed(rollSystem);
138
+ const to = own && typeof own === 'object' ? barNamed(own) : undefined;
139
+ if (!from || !to || from.id === to.id || !Array.isArray(copy.features))
140
+ return copy;
141
+ return { ...copy, features: featuresOnBar(copy.features, translationBetween(from, to)) };
142
+ };
91
143
  /**
92
- * Every 0.1 edition was read with the T-100 tracker bar, so a roll
93
- * without a system is a T-100 roll. The text a copy's production
94
- * gave for the system is kept as the name.
144
+ * Systems belonged to the roll before they belonged to the versions
145
+ * and the copies. Every 0.1 edition was read with the T-100 bar, so a
146
+ * roll that named no system was a T-100 roll, and the text a copy's
147
+ * production gave for it is kept as the system's name.
95
148
  */
96
- const withRollSystem = (edition) => {
97
- if (!edition.roll || edition.roll.system)
149
+ const withSystems = (edition) => {
150
+ if (!edition.roll || !namesSystemOnTheRoll(edition))
98
151
  return edition;
99
- const stated = (edition.copies ?? [])
100
- .map((copy) => copy.production?.system ?? copy.productionEvent?.system)
152
+ const versions = listAt(edition, 'versions');
153
+ const copies = listAt(edition, 'copies');
154
+ const stated = (copies ?? [])
155
+ .map((copy) => copy?.[productionKeyOf(copy)]?.system)
101
156
  .find((system) => typeof system === 'string' && system !== '');
102
157
  const { id, ...concept } = systemOf(welteT100);
103
- const system = { '@id': id, ...concept, ...(stated && { name: stated }) };
104
- return { ...edition, roll: { ...edition.roll, system } };
158
+ const system = edition.roll.system ?? { '@id': id, ...concept, ...(stated && { name: stated }) };
159
+ const { system: _named, ...roll } = edition.roll;
160
+ const created = roll.recordingEvent?.created;
161
+ return {
162
+ ...edition,
163
+ roll: created
164
+ ? { ...roll, recordingEvent: { ...roll.recordingEvent, created: withSystem(created, system) } }
165
+ : roll,
166
+ ...(versions && {
167
+ versions: versions.map((version) => withSystem(version, system))
168
+ }),
169
+ ...(copies && {
170
+ copies: copies.map((copy) => {
171
+ if (!copy || typeof copy !== 'object')
172
+ return copy;
173
+ const key = productionKeyOf(copy);
174
+ const onOwn = onOwnBar(copy, system);
175
+ return { ...onOwn, [key]: withSystem(onOwn[key] ?? {}, system) };
176
+ })
177
+ })
178
+ };
105
179
  };
106
180
  /** An edition written before the editors were carried names none. */
107
181
  const withEditors = (edition) => !edition.creation || edition.creation.editors
@@ -126,5 +200,5 @@ const withDerivationTolerance = (edition) => {
126
200
  : version)
127
201
  };
128
202
  };
129
- const editionSteps = [withRollSystem, withEditors, withDerivationTolerance];
203
+ const editionSteps = [withSystems, withEditors, withDerivationTolerance];
130
204
  export const migrate = (edition) => walk(editionSteps.reduce((result, step) => step(result), edition));
@@ -37,8 +37,6 @@ export interface PhillipsErollOptions {
37
37
  * the roll he read.
38
38
  */
39
39
  system?: TrackerBar;
40
- /** The edition's bar, onto which the positions are put. */
41
- bar?: TrackerBar;
42
40
  /**
43
41
  * How far above its position an expression number sits. Defaults to
44
42
  * what his samples show for that bar.
@@ -47,16 +45,16 @@ export interface PhillipsErollOptions {
47
45
  /**
48
46
  * Where on the paper the roll had run after so many seconds. The
49
47
  * default is the constant speed the system states, which ignores
50
- * the take-up spool; pass `paperAt` of welte-t100-emulator to
48
+ * the take-up spool; pass `paperAt` of welte-mignon-emulator to
51
49
  * account for it.
52
50
  */
53
51
  placeAt?: (elapsed: Seconds) => Millimeters;
54
52
  }
55
53
  /**
56
54
  * Reads one of his e-roll files as a copy of the roll, in millimetres
57
- * of paper. A position the edition's bar does not read is left out, as
58
- * the bar would leave it.
55
+ * of paper, on the bar it was cut for and in that bar's numbering. A
56
+ * position the bar does not read is left out, as the bar would leave it.
59
57
  */
60
- export declare function readFromPhillipsEroll(buffer: ArrayBuffer, { system, bar, controlOffset, placeAt }?: PhillipsErollOptions): RollCopy;
58
+ export declare function readFromPhillipsEroll(buffer: ArrayBuffer, { system, controlOffset, placeAt }?: PhillipsErollOptions): RollCopy;
61
59
  /** The bars his files are known to be numbered by. */
62
60
  export declare const phillipsSystems: readonly TrackerBar[];
@@ -1,7 +1,7 @@
1
1
  import { read } from "midifile-ts";
2
2
  import { v4 } from "uuid";
3
3
  import { assignObject } from "../Assumption";
4
- import { systemOf, translationBetween } from "../TrackerBar";
4
+ import { systemOf } from "../TrackerBar";
5
5
  import { welteT100 } from "../systems/welteT100/bar";
6
6
  import { welteLicensee } from "../systems/welteLicensee/bar";
7
7
  import { inMetersPerMinute, mm, seconds, track } from "../Quantity";
@@ -116,21 +116,20 @@ const positionsByPitch = (bar) => new Map(Array.from({ length: bar.trackCount },
116
116
  }));
117
117
  /**
118
118
  * Reads one of his e-roll files as a copy of the roll, in millimetres
119
- * of paper. A position the edition's bar does not read is left out, as
120
- * the bar would leave it.
119
+ * of paper, on the bar it was cut for and in that bar's numbering. A
120
+ * position the bar does not read is left out, as the bar would leave it.
121
121
  */
122
- export function readFromPhillipsEroll(buffer, { system = welteT100, bar = welteT100, controlOffset, placeAt } = {}) {
122
+ export function readFromPhillipsEroll(buffer, { system = welteT100, controlOffset, placeAt } = {}) {
123
123
  const file = read(new Uint8Array(buffer));
124
124
  const timed = onOneClock(file.tracks);
125
125
  const elapsedAt = clockOf(temposIn(timed), file.header.ticksPerBeat);
126
126
  const place = placeAt ?? atStatedSpeed(system);
127
127
  const offset = controlOffsetOf(system, controlOffset);
128
128
  const notePositions = positionsByPitch(system);
129
- const onBar = translationBetween(system, bar);
130
129
  const positionOf = (number) => notePositions.get(number) ?? track(number - offset);
131
130
  const features = notesIn(timed).flatMap((note) => {
132
- const position = onBar(positionOf(note.pitch));
133
- if (position === undefined)
131
+ const position = positionOf(note.pitch);
132
+ if (!system.meaningOf(position))
134
133
  return [];
135
134
  return [{
136
135
  type: 'Hole',
@@ -16,14 +16,13 @@ export interface SpencerBarOptions {
16
16
  * the roll it was scanned from. His Welte files are Licensee rolls.
17
17
  */
18
18
  system?: TrackerBar;
19
- /** The edition's bar, onto which the positions are put. */
20
- bar?: TrackerBar;
21
19
  }
22
20
  /**
23
- * Reads the copy onto the edition's bar. A hole on a position the
24
- * edition's bar does not read is left out, as the bar would leave it.
21
+ * Reads the copy on the bar it was cut for, whose numbering it keeps.
22
+ * A hole on a position that bar does not read is left out, as the bar
23
+ * would leave it.
25
24
  */
26
- export declare function readFromSpencerBar(buffer: ArrayBuffer, { rowsPerInch, system, bar }?: SpencerBarOptions): RollCopy;
25
+ export declare function readFromSpencerBar(buffer: ArrayBuffer, { rowsPerInch, system }?: SpencerBarOptions): RollCopy;
27
26
  /**
28
27
  * The `.ann` file beside a `.bar` holds the player's settings for the
29
28
  * roll as lines of "/key: value": title, composer, pianist, roll
@@ -1,6 +1,5 @@
1
1
  import { v4 } from "uuid";
2
- import { systemOf, translationBetween } from "../TrackerBar";
3
- import { welteT100 } from "../systems/welteT100/bar";
2
+ import { systemOf } from "../TrackerBar";
4
3
  import { welteLicensee } from "../systems/welteLicensee/bar";
5
4
  import { feetPerMinute, inMillimeters, px, track } from "../Quantity";
6
5
  /**
@@ -80,20 +79,20 @@ const holesOf = (events) => {
80
79
  return holes.sort((a, b) => a.from - b.from);
81
80
  };
82
81
  /**
83
- * Reads the copy onto the edition's bar. A hole on a position the
84
- * edition's bar does not read is left out, as the bar would leave it.
82
+ * Reads the copy on the bar it was cut for, whose numbering it keeps.
83
+ * A hole on a position that bar does not read is left out, as the bar
84
+ * would leave it.
85
85
  */
86
- export function readFromSpencerBar(buffer, { rowsPerInch = SPENCER_ROWS_PER_INCH, system = welteLicensee, bar = welteT100 } = {}) {
86
+ export function readFromSpencerBar(buffer, { rowsPerInch = SPENCER_ROWS_PER_INCH, system = welteLicensee } = {}) {
87
87
  const bytes = new Uint8Array(buffer);
88
88
  if (byteAt(bytes, TEXT_AT) !== TEXT_TAG) {
89
89
  throw new Error('Not a Spencer .bar file: no text after the header');
90
90
  }
91
91
  const placeOf = (row) => inMillimeters(px(row), rowsPerInch);
92
- const onBar = translationBetween(system, bar);
93
92
  const features = holesOf(eventsIn(bytes, endOfText(bytes, TEXT_AT + 1)))
94
93
  .flatMap((hole) => {
95
- const position = onBar(track(hole.position));
96
- if (position === undefined)
94
+ const position = track(hole.position);
95
+ if (!system.meaningOf(position))
97
96
  return [];
98
97
  return [{
99
98
  type: 'Hole',
@@ -8,13 +8,10 @@ export interface StanfordAtonOptions {
8
8
  * perforation on the bar's rewind track.
9
9
  */
10
10
  trackShift?: Track;
11
- /** The edition's bar, onto which the holes are put. */
12
- bar?: TrackerBar;
13
11
  /**
14
- * The bar the scanned roll was cut for, where it is not the
15
- * edition's. The scan is calibrated on it, and the holes are then
16
- * put onto the edition's bar; one on a position that bar does not
17
- * read is left out.
12
+ * The bar the scanned roll was cut for. The scan is calibrated on
13
+ * it and the holes keep its numbering, since a copy is read by the
14
+ * bar it was cut for and by no other.
18
15
  */
19
16
  system?: TrackerBar;
20
17
  /**
@@ -24,4 +21,4 @@ export interface StanfordAtonOptions {
24
21
  */
25
22
  scan?: string;
26
23
  }
27
- export declare function readFromStanfordAton(atonString: string, { trackShift, bar, system, scan }?: StanfordAtonOptions): RollCopy;
24
+ export declare function readFromStanfordAton(atonString: string, { trackShift, system, scan }?: StanfordAtonOptions): RollCopy;
@@ -1,6 +1,6 @@
1
1
  import { v4 } from "uuid";
2
2
  import { AtonParser } from "./AtonParser";
3
- import { systemOf, translationBetween } from "../TrackerBar";
3
+ import { systemOf } from "../TrackerBar";
4
4
  import { welteT100 } from "../systems/welteT100/bar";
5
5
  import { inMillimeters, mean, mm, pixelsPerInch, px, subtract, track } from "../Quantity";
6
6
  /** Values in these files carry their unit as a suffix, e.g. "37.7646px". */
@@ -110,7 +110,7 @@ const measuredByOf = (rollinfo) => {
110
110
  date
111
111
  };
112
112
  };
113
- export function readFromStanfordAton(atonString, { trackShift, bar = welteT100, system = bar, scan } = {}) {
113
+ export function readFromStanfordAton(atonString, { trackShift, system = welteT100, scan } = {}) {
114
114
  const parser = new AtonParser();
115
115
  const json = parser.parse(atonString);
116
116
  const holes = json.ROLLINFO.HOLES.HOLE;
@@ -128,11 +128,10 @@ export function readFromStanfordAton(atonString, { trackShift, bar = welteT100,
128
128
  };
129
129
  const punchDiameter = punchDiameterOf(holes, dpi);
130
130
  const chains = chainsAmong([...holes, ...chainedBadHoles(listOf(json.ROLLINFO.BADHOLES?.HOLE), calibration)]);
131
- const onBar = translationBetween(system, bar);
132
131
  const features = chains
133
132
  .flatMap(({ hole, attack, release }) => {
134
- const position = onBar(track(+hole.TRACKER_HOLE + shift));
135
- if (position === undefined)
133
+ const position = track(+hole.TRACKER_HOLE + shift);
134
+ if (!system.meaningOf(position))
136
135
  return [];
137
136
  const column = readPx(hole.ORIGIN_COL);
138
137
  const columnWidth = readPx(hole.WIDTH_COL);
@@ -1,5 +1,5 @@
1
1
  import { RollCopy } from "./RollCopy";
2
- export declare const reservationTypes: readonly ['source-not-stated', 'source-undocumented', 'features-interpreted', 'no-physical-evidence', 'measurement-undocumented', 'not-calibrated'];
2
+ export declare const reservationTypes: readonly ['source-not-stated', 'source-undocumented', 'features-interpreted', 'no-physical-evidence', 'measurement-undocumented', 'not-calibrated', 'system-unknown'];
3
3
  export type ReservationType = typeof reservationTypes[number];
4
4
  /**
5
5
  * Something an edition cannot vouch for in one of its copies.
@@ -1,12 +1,14 @@
1
1
  import { bearsPhysicalEvidence, isMeasured, sourceLabels } from "./FeatureSource";
2
2
  import { calibrationOf } from "./RollCopy";
3
+ import { trackerBarOf } from "./systems";
3
4
  export const reservationTypes = [
4
5
  'source-not-stated',
5
6
  'source-undocumented',
6
7
  'features-interpreted',
7
8
  'no-physical-evidence',
8
9
  'measurement-undocumented',
9
- 'not-calibrated'
10
+ 'not-calibrated',
11
+ 'system-unknown'
10
12
  ];
11
13
  const sourceStated = copy => copy.readFrom ? undefined : {
12
14
  type: 'source-not-stated',
@@ -37,13 +39,33 @@ const calibrated = copy => calibrationOf(copy) ? undefined : {
37
39
  type: 'not-calibrated',
38
40
  note: 'The copy is not calibrated against the tracker bar, so its track positions rest on the numbering its source used.'
39
41
  };
42
+ /**
43
+ * The bar decides what every track on the copy means, so a copy the
44
+ * edition cannot place in a system is read by the T-100 for want of
45
+ * anything better. Between two Welte scales that is a semitone rather
46
+ * than a visible error, which is why it is said out loud.
47
+ */
48
+ const systemKnown = copy => {
49
+ const system = copy.production?.system;
50
+ if (system === undefined) {
51
+ return {
52
+ type: 'system-unknown',
53
+ note: 'The copy names no reproducing system, so it is read by the T-100 tracker bar.'
54
+ };
55
+ }
56
+ return trackerBarOf(system) ? undefined : {
57
+ type: 'system-unknown',
58
+ note: `The copy names ${system.name || 'a reproducing system'}, which the edition has no tracker bar for, so it is read by the T-100's.`
59
+ };
60
+ };
40
61
  const checks = [
41
62
  sourceStated,
42
63
  sourceDocumented,
43
64
  featuresMeasured,
44
65
  physicalEvidence,
45
66
  measurementDocumented,
46
- calibrated
67
+ calibrated,
68
+ systemKnown
47
69
  ];
48
70
  /**
49
71
  * What the edition cannot vouch for in a copy, in the order the
package/lib/schema.json CHANGED
@@ -1802,15 +1802,10 @@
1802
1802
  "recordingEvent": {
1803
1803
  "$ref": "#/definitions/RecordingEvent",
1804
1804
  "description": "[ontology: lrmoo:R19i was realised through]"
1805
- },
1806
- "system": {
1807
- "$ref": "#/definitions/Concept",
1808
- "description": "The reproducing system the roll was cut for. A system the type vocabulary knows carries the IRI of its concept as `id`, from which the export takes the system's own context, so that the expression types are read as that system's. [ontology: crm:P2 has type]"
1809
1805
  }
1810
1806
  },
1811
1807
  "required": [
1812
1808
  "catalogueNumber",
1813
- "system",
1814
1809
  "recordingEvent"
1815
1810
  ],
1816
1811
  "type": "object"
@@ -2089,6 +2084,10 @@
2089
2084
  "$ref": "#/definitions/Derivation",
2090
2085
  "description": "If no derivation is defined, it is assumed that this version represents the mother roll. [ontology: lrmoo:R76 is derivative of]"
2091
2086
  },
2087
+ "creation": {
2088
+ "$ref": "#/definitions/VersionCreation",
2089
+ "description": "The act that made this version, where it is known: who carried it out, when, and by what rule. [ontology: lrmoo:R17i was created by]"
2090
+ },
2092
2091
  "edits": {
2093
2092
  "description": "The list of edits that, applied to the base version, produce this version. [ontology: reo:involvedEdit]",
2094
2093
  "items": {
@@ -2107,6 +2106,10 @@
2107
2106
  "description": "A short siglum to identify the version, e.g. \"A\", \"B1\", \"B2_rev\", etc. [ontology: reo:siglum]",
2108
2107
  "type": "string"
2109
2108
  },
2109
+ "system": {
2110
+ "$ref": "#/definitions/Concept",
2111
+ "description": "The reproducing system this version is coded for. One roll was often issued for several of them, and a version is a reading in one system's words: its expression types are that system's vocabulary and its notes sit on that bar's positions. A system the type vocabulary knows carries the IRI of its concept as `id`, from which the export takes the system's own context. [ontology: crm:P2 has type]"
2112
+ },
2110
2113
  "versionType": {
2111
2114
  "$ref": "#/definitions/VersionType",
2112
2115
  "description": "Whether the version served as a master for reproductions or exists on one copy only. [ontology: crm:P2 has type]"
@@ -2127,11 +2130,30 @@
2127
2130
  "@id",
2128
2131
  "motivations",
2129
2132
  "siglum",
2133
+ "system",
2130
2134
  "@type",
2131
2135
  "versionType"
2132
2136
  ],
2133
2137
  "type": "object"
2134
2138
  },
2139
+ "VersionCreation": {
2140
+ "description": "How a version was made, where that is known and worth stating.\n\nA roll issued for another system was re-punched by an editor of the publisher's, and that was an editorial act rather than a conversion: Lawson names Kähle as the man who corrected second masters for the green system. The rule he worked by is the procedure named here, and the version's edits carry it out, so the mechanical part of a transfer is stated once instead of being spelled out per note. [ontology: lrmoo:F28 Expression Creation]",
2141
+ "properties": {
2142
+ "actor": {
2143
+ "$ref": "#/definitions/ActorAssignment",
2144
+ "description": "Who carried the act out. An `ObjectAssumption`, so an attribution can carry the belief it rests on and the reasons for it. [ontology: crm:P14 carried out by]"
2145
+ },
2146
+ "date": {
2147
+ "$ref": "#/definitions/DateAssignment",
2148
+ "description": "When it took place. [ontology: dcterms:date]"
2149
+ },
2150
+ "procedure": {
2151
+ "$ref": "#/definitions/Concept",
2152
+ "description": "The rule followed, as a term of the vocabulary: the notes stand at their pitch, the expression is re-spelled in the other system's words. [ontology: crm:P33 used specific technique]"
2153
+ }
2154
+ },
2155
+ "type": "object"
2156
+ },
2135
2157
  "VersionType": {
2136
2158
  "description": "The type of a version. An 'edition' version may serve as the master for several roll copies; a 'unicum' version exists only on one specific copy.",
2137
2159
  "enum": [
@@ -76,6 +76,7 @@
76
76
  "@type": "@id"
77
77
  },
78
78
  "creation": "lrmoo:R17i_was_created_by",
79
+ "procedure": "crm:P33_used_specific_technique",
79
80
  "editors": "crm:P14_carried_out_by",
80
81
  "publisher": "crm:P14_carried_out_by",
81
82
  "publicationDate": {
@@ -0,0 +1,10 @@
1
+ {
2
+ "@context": {
3
+ "@version": 1.1,
4
+ "expressionType": {
5
+ "@id": "http://www.cidoc-crm.org/cidoc-crm/P2_has_type",
6
+ "@type": "@vocab",
7
+ "@context": { "@vocab": "https://w3id.org/reo/type/welte-t100/" }
8
+ }
9
+ }
10
+ }
@@ -0,0 +1,10 @@
1
+ {
2
+ "@context": {
3
+ "@version": 1.1,
4
+ "expressionType": {
5
+ "@id": "http://www.cidoc-crm.org/cidoc-crm/P2_has_type",
6
+ "@type": "@vocab",
7
+ "@context": { "@vocab": "https://w3id.org/reo/type/welte-green/" }
8
+ }
9
+ }
10
+ }
@@ -0,0 +1,73 @@
1
+ import { CollationTolerance, Locate } from "./Collation";
2
+ import { AnySymbol, Expression } from "./Symbol";
3
+ import { Millimeters, Track } from "./Quantity";
4
+ import { TrackerBar } from "./TrackerBar";
5
+ /**
6
+ * How a scale spells a command: as a perforation that turns a function
7
+ * on, one that cancels it, or one that holds it for as long as it lasts.
8
+ */
9
+ export type Spelling = 'on' | 'off' | 'held';
10
+ /**
11
+ * A command as what it operates rather than as the word a scale uses
12
+ * for it, so that two scales can be compared at all.
13
+ */
14
+ export interface Command {
15
+ /** The function operated, named the same wherever a scale has it. */
16
+ operates: string;
17
+ spelling: Spelling;
18
+ /**
19
+ * Whether the half of the keyboard the valve serves is part of the
20
+ * command. The dynamics are per half; the pedals are not, and the
21
+ * two Welte scales put them on opposite edges of the paper, so a
22
+ * rule that compared sides would leave every pedal unpaired.
23
+ */
24
+ sided: boolean;
25
+ }
26
+ /** What a command operates, or nothing for a word no scale here knows. */
27
+ export declare const commandOf: (expressionType: string) => Command | undefined;
28
+ /**
29
+ * A latched function of the older version and the held perforation of
30
+ * the newer one that stands for it.
31
+ */
32
+ export interface Substitution {
33
+ /** The perforation that turned the function on, and the one that cancelled it. */
34
+ replaced: readonly [Expression, Expression];
35
+ /**
36
+ * The perforations that say the same thing in the other scale's
37
+ * words. Usually one, but a held command is punched as a chain of
38
+ * round holes with paper bridges rather than as a slot, and a scan
39
+ * whose analysis reports those singly gives one symbol per punch.
40
+ */
41
+ by: readonly Expression[];
42
+ }
43
+ /**
44
+ * How wide a paper bridge may be for two held perforations to be one
45
+ * command. The T-98 punches a hold as a chain of round holes on a
46
+ * 2.66 mm grid with bridges of about a millimetre, so a scan read hole
47
+ * by hole shows a run where the paper shows one command.
48
+ */
49
+ export declare const defaultChainGap: import("./Quantity").Quantity<"mm">;
50
+ /**
51
+ * The position a bar reads its own command for the same function on,
52
+ * where it has no word for this one.
53
+ *
54
+ * A version keeps the perforations it does away with in its deletions,
55
+ * and those may be a scale the version is not coded for: a green
56
+ * version deletes the red `SlowCrescendoOn` it inherits. That has no
57
+ * position on the green bar, so drawing it as a perforation is out of
58
+ * the question, but an edit still has to be shown somewhere, and the
59
+ * lane the green scale gives the same function is where it belongs.
60
+ */
61
+ export declare const positionOfSameFunction: (bar: TrackerBar, symbol: Expression) => Track | undefined;
62
+ /**
63
+ * Where a held perforation of the newer version stands for a latched
64
+ * function of the older one: the same function, on the same side where
65
+ * the side counts, spanning the same stretch of the roll.
66
+ *
67
+ * Only unambiguous correspondences are reported. Where two held
68
+ * perforations answer one latched interval, or one answers two, none of
69
+ * them is reported and the editor is left to say what happened. Nothing
70
+ * is invented either: both sides are perforations somebody punched, and
71
+ * all this asserts is which stands for which.
72
+ */
73
+ export declare const substitutionsBetween: (own: readonly AnySymbol[], inherited: readonly AnySymbol[], locate: Locate, tolerance?: CollationTolerance, chainGap?: Millimeters) => Substitution[];