linked-rolls 0.20.0 → 0.22.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.
@@ -1,19 +1,10 @@
1
- import { Half, Instrument, Parameters, PedalMode, RollNumber, Spool } from "welte-t100-emulator";
2
- import { ReproducingSystem } from "../../ReproducingSystem";
1
+ import { Half, Parameters, PedalMode, Spool } from "welte-mignon-emulator";
2
+ import { Instrument, RollNumber } from "welte-mignon-emulator/t100";
3
+ import { NegotiatedEvent, Performance, ReproducingSystem, RollProperties } from "../../ReproducingSystem";
3
4
  import { Millimeters, Seconds, Track } from "../../Quantity";
4
- /**
5
- * MIDI velocity at the open rail of the Nuancierbalg, at the Mezzoforte pin,
6
- * and at the closed rail, joined linearly in between. Nothing in the
7
- * mechanism determines how bellows travel maps onto hammer velocity, so
8
- * these are anchors rather than measurements; the three values are the
9
- * ones midi2exp publishes.
10
- */
11
- export type VelocityMap = {
12
- piano: number;
13
- mezzoforte: number;
14
- forte: number;
15
- };
16
- export type { Instrument } from "welte-t100-emulator";
5
+ import { type VelocityMap } from "../velocity";
6
+ export type { VelocityMap } from "../velocity";
7
+ export type { Instrument } from "welte-mignon-emulator/t100";
17
8
  export type InstrumentName = 'consensus' | RollNumber;
18
9
  /**
19
10
  * The instruments the emulator was fitted as: the consensus over the six
@@ -76,7 +67,19 @@ export declare const defaultWelteT100Options: WelteT100Options;
76
67
  /** When the spool brings a place on the roll to the tracker bar. */
77
68
  export declare const secondsAt: (spool: Spool, place: Millimeters) => Seconds;
78
69
  /**
79
- * The red Welte, as welte-t100-emulator models it: the take-up spool sets
70
+ * The mechanism, given a way of naming the instrument on the curves it
71
+ * produces. The Licensee reads the same commands and is played by the same
72
+ * valves, so it shares this; what it may not share is the name, since a
73
+ * Licensee playback runs on constants fitted to Freiburg instruments and the
74
+ * curve has to say so.
75
+ *
76
+ * The edition's tempo adjustment is left aside: it is stated as a paper
77
+ * speed, and what the spool holds constant is its rate of revolution, so
78
+ * the two are not the same quantity. The spool in the options sets the speed.
79
+ */
80
+ export declare const performAs: (instrumentOf: (nuance: Record<Half, Parameters>) => string) => (events: readonly NegotiatedEvent[], options: WelteT100Options, roll: RollProperties) => Performance;
81
+ /**
82
+ * The red Welte, as welte-mignon-emulator models it: the take-up spool sets
80
83
  * the time axis, the Nuancierbälge fill through their conduits and are
81
84
  * arrested by the Mezzoforte pin, and the two pedals travel rather than
82
85
  * switch. The constants are the consensus fitted across the hand-drawn
@@ -1,7 +1,9 @@
1
- import { aperturePorts, CONSENSUS, DEFAULT_PUNCH_MM, geometryInMm, Grid, levelChanges, mezzoforteTravel, paperSeconds, pedalBrushing, pedalDefaults, pneumaticModel, PRESETS, ROWS_PER_MM, runPedals, TRACKER_BORE_MM, travelBetweenRails, WELTE_SPOOL, } from "welte-t100-emulator";
1
+ import { DEFAULT_PUNCH_MM, geometryInMm, Grid, levelChanges, paperSeconds, pedalBrushing, pedalDefaults, ROWS_PER_MM, TRACKER_BORE_MM, WELTE_SPOOL, } from "welte-mignon-emulator";
2
+ import { aperturePorts, CONSENSUS, mezzoforteTravel, pneumaticModel, PRESETS, runPedals, travelBetweenRails, } from "welte-mignon-emulator/t100";
2
3
  import { welteT100 } from "./bar";
3
- import { add, inCentimeters, mm, seconds, track } from "../../Quantity";
4
+ import { inCentimeters, mm, seconds, track } from "../../Quantity";
4
5
  import { partitionPoint } from "../../sorted";
6
+ import { defaultVelocityMap, velocityOf } from "../velocity";
5
7
  /**
6
8
  * The instruments the emulator was fitted as: the consensus over the six
7
9
  * rolls with drawn nuance lines, and the setting that drew each of them,
@@ -35,7 +37,7 @@ export const defaultWelteT100Options = {
35
37
  spool: WELTE_SPOOL,
36
38
  nuance: nuanceOf(instruments.consensus),
37
39
  pedals: pedalPresets.damping,
38
- velocity: { piano: 35, mezzoforte: 60, forte: 90 },
40
+ velocity: defaultVelocityMap,
39
41
  pedalMode: 'continuous',
40
42
  trackerBore: mm(TRACKER_BORE_MM),
41
43
  punchDiameter: mm(DEFAULT_PUNCH_MM),
@@ -62,62 +64,47 @@ const isWelteT100ExpressionType = (type) => Object.hasOwn(CODES, type);
62
64
  const codeOf = (expressionType) => isWelteT100ExpressionType(expressionType) ? CODES[expressionType] : undefined;
63
65
  /** Paper the grid runs on past the last hole, so that a final pedal release completes. */
64
66
  const RUN_OUT = mm(100);
65
- /**
66
- * Which stack of valves an expression perforation belongs to. The symbol
67
- * usually says so, having been read off the tracker bar already; where it
68
- * does not, the bar is asked again, since sending a perforation to neither
69
- * side would quietly flatten the dynamics.
70
- */
71
- const scopeOf = (event) => {
72
- if (event.scope)
73
- return event.scope;
74
- const meaning = welteT100.meaningOf(event.vertical.from);
75
- return meaning?.type === 'expression' ? meaning.scope : undefined;
76
- };
77
67
  const isNote = (event) => event.type === 'note';
78
68
  const isExpression = (event) => event.type === 'expression';
79
- /** The row of the scan the constants were fitted on that a place on the roll falls in. */
80
- const rowOf = (place) => place * ROWS_PER_MM;
81
- const placeOfRow = (row) => mm(row / ROWS_PER_MM);
69
+ const paperOf = (toOwnPaper) => ({
70
+ rowOf: place => place * toOwnPaper * ROWS_PER_MM,
71
+ placeOfRow: row => mm(row / (toOwnPaper * ROWS_PER_MM)),
72
+ paperOfRow: row => mm(row / ROWS_PER_MM)
73
+ });
82
74
  /** When the spool brings a place on the roll to the tracker bar. */
83
75
  export const secondsAt = (spool, place) => seconds(paperSeconds(spool, inCentimeters(place)));
84
76
  const halfOf = (note, division) => note.vertical.from >= division ? 'treble' : 'bass';
85
- const readingOf = (event) => {
77
+ const readingOf = (paper) => (event) => {
86
78
  const code = codeOf(event.expressionType);
87
- const half = scopeOf(event);
88
- if (!code || !half)
79
+ if (!code)
89
80
  return undefined;
81
+ const half = event.scope;
90
82
  const [control, action] = code;
91
83
  return {
92
84
  event,
93
- punch: { half, control, action, rowOn: rowOf(event.horizontal.from), rowOff: rowOf(event.horizontal.to) }
85
+ punch: {
86
+ half,
87
+ control,
88
+ action,
89
+ rowOn: paper.rowOf(event.horizontal.from),
90
+ rowOff: paper.rowOf(event.horizontal.to)
91
+ }
94
92
  };
95
93
  };
96
94
  const clamp = (value, low, high) => Math.min(Math.max(value, low), high);
97
- /**
98
- * The velocity map joined linearly through its three anchors, with the
99
- * middle one at the Mezzoforte pin of the half in question.
100
- */
101
- const velocityOf = (travel, hook, map) => {
102
- const position = clamp(travel, 0, 1);
103
- if (position <= hook) {
104
- return map.piano + (position / hook) * (map.mezzoforte - map.piano);
105
- }
106
- return map.mezzoforte + ((position - hook) / (1 - hook)) * (map.forte - map.mezzoforte);
107
- };
108
95
  /**
109
96
  * One sample per row of the scan the constants were fitted on, from the
110
97
  * beginning of the roll to a little past the last hole. The rows are
111
98
  * equally spaced on the paper and not in time, which is what the
112
99
  * emulator expects.
113
100
  */
114
- const gridOver = (events, spool) => {
101
+ const gridOver = (events, spool, paper) => {
115
102
  const last = mm(events.reduce((furthest, event) => Math.max(furthest, event.horizontal.to), 0));
116
- const length = Math.ceil(rowOf(add(last, RUN_OUT))) + 1;
117
- const times = new Float64Array(length).map((_, row) => secondsAt(spool, placeOfRow(row)));
103
+ const length = Math.ceil(paper.rowOf(last) + RUN_OUT * ROWS_PER_MM) + 1;
104
+ const times = new Float64Array(length).map((_, row) => secondsAt(spool, paper.paperOfRow(row)));
118
105
  return new Grid(0, times);
119
106
  };
120
- const nuanceCurves = (grid, ports, samples, options) => {
107
+ const nuanceCurves = (grid, ports, samples, options, instrument) => {
121
108
  const curveOf = (half) => {
122
109
  const params = options.nuance[half];
123
110
  const output = pneumaticModel.run({ grid, half, ports }, params);
@@ -127,6 +114,7 @@ const nuanceCurves = (grid, ports, samples, options) => {
127
114
  ...samples,
128
115
  name: half,
129
116
  kind: 'dynamics',
117
+ instrument,
130
118
  travel,
131
119
  velocity: travel.map(value => velocityOf(value, hook, options.velocity))
132
120
  };
@@ -140,11 +128,11 @@ const pedalCurves = (grid, ports, samples, options) => {
140
128
  hammerRail: { ...samples, name: 'hammerRail', kind: 'pedal', travel: travel.hammerRail }
141
129
  };
142
130
  };
143
- const performNotes = (events, grid, nuance, options) => events
131
+ const performNotes = (events, grid, nuance, options, paper) => events
144
132
  .filter(isNote)
145
133
  .flatMap((note) => {
146
134
  const curve = nuance[halfOf(note, options.division)];
147
- const velocity = curve.velocity[grid.indexOfRow(rowOf(note.horizontal.from))];
135
+ const velocity = curve.velocity[grid.indexOfRow(paper.rowOf(note.horizontal.from))];
148
136
  return [
149
137
  { type: 'noteOn', performs: note, pitch: note.pitch, velocity, at: secondsAt(options.spool, note.horizontal.from) },
150
138
  { type: 'noteOff', performs: note, pitch: note.pitch, velocity: 127, at: secondsAt(options.spool, note.horizontal.to) }
@@ -170,28 +158,35 @@ const performPedal = (type, curve, grid, readings, mode) => {
170
158
  }));
171
159
  };
172
160
  /**
161
+ * The mechanism, given a way of naming the instrument on the curves it
162
+ * produces. The Licensee reads the same commands and is played by the same
163
+ * valves, so it shares this; what it may not share is the name, since a
164
+ * Licensee playback runs on constants fitted to Freiburg instruments and the
165
+ * curve has to say so.
166
+ *
173
167
  * The edition's tempo adjustment is left aside: it is stated as a paper
174
168
  * speed, and what the spool holds constant is its rate of revolution, so
175
169
  * the two are not the same quantity. The spool in the options sets the speed.
176
170
  */
177
- const perform = (events, options, roll) => {
171
+ export const performAs = (instrumentOf) => (events, options, roll) => {
172
+ const paper = paperOf(roll.toOwnPaper ?? 1);
178
173
  const readings = events
179
174
  .filter(isExpression)
180
- .map(readingOf)
175
+ .map(readingOf(paper))
181
176
  .filter((reading) => reading !== undefined);
182
- const grid = gridOver(events, options.spool);
177
+ const grid = gridOver(events, options.spool, paper);
183
178
  const geometry = geometryInMm(roll.punchDiameter ?? options.punchDiameter, options.trackerBore);
184
179
  const ports = aperturePorts(grid, readings.map(reading => reading.punch), geometry);
185
180
  const samples = {
186
- place: grid.seconds.map((_, row) => placeOfRow(row)),
181
+ place: grid.seconds.map((_, row) => paper.placeOfRow(row)),
187
182
  seconds: grid.seconds
188
183
  };
189
- const nuance = nuanceCurves(grid, ports, samples, options);
184
+ const nuance = nuanceCurves(grid, ports, samples, options, instrumentOf(options.nuance));
190
185
  const pedals = pedalCurves(grid, ports, samples, options);
191
186
  const readingsOf = (control) => readings.filter(reading => reading.punch.control === control);
192
187
  return {
193
188
  events: [
194
- ...performNotes(events, grid, nuance, options),
189
+ ...performNotes(events, grid, nuance, options, paper),
195
190
  ...performPedal('damper', pedals.damper, grid, readingsOf('sustainPedal'), options.pedalMode),
196
191
  ...performPedal('hammerRail', pedals.hammerRail, grid, readingsOf('hammerRail'), options.pedalMode)
197
192
  ],
@@ -199,7 +194,7 @@ const perform = (events, options, roll) => {
199
194
  };
200
195
  };
201
196
  /**
202
- * The red Welte, as welte-t100-emulator models it: the take-up spool sets
197
+ * The red Welte, as welte-mignon-emulator models it: the take-up spool sets
203
198
  * the time axis, the Nuancierbälge fill through their conduits and are
204
199
  * arrested by the Mezzoforte pin, and the two pedals travel rather than
205
200
  * switch. The constants are the consensus fitted across the hand-drawn
@@ -210,5 +205,5 @@ export const welteT100System = {
210
205
  name: 'Welte-Mignon T100',
211
206
  trackerBar: welteT100,
212
207
  defaultOptions: defaultWelteT100Options,
213
- perform
208
+ perform: performAs(nuance => `Welte-Mignon T-100, ${instrumentNameOf(nuance) ?? 'custom'}`)
214
209
  };
@@ -25,7 +25,20 @@ export type WelteT98ExpressionType = typeof welteT98ExpressionTypes[number];
25
25
  *
26
26
  * Measured against Julian Dyer's scan of the T-98 copy of roll 225
27
27
  * (Grünfeld, Träumerei): 98 columns 2.818 mm apart on paper 285.6 mm
28
- * wide, notes on the middle 80 columns, the sustain valve punched 53
29
- * times where the T-100 copy latches its sustain on 53 times.
28
+ * wide, notes on the middle 80 columns, the sustain valve punched 52
29
+ * times in the music where the T-100 copy latches its sustain on 52
30
+ * times. A 53rd perforation on that track belongs to the test pattern
31
+ * after the rewind.
32
+ *
33
+ * The paper speed is the Deutsches Museum's figure for its Welte grün /
34
+ * T 98 rolls, 220 cm/min. Phillips gives 7 ft/min = 2.134 m/min (p. 121);
35
+ * midi2exp's and PlaySK's green defaults of 72.2 and 72.27, read as the
36
+ * Stanford convention of feet per minute times ten, are 2.201 m/min, which
37
+ * is the museum's figure; and the two copies of roll 225 last the same
38
+ * time if the green starts at about 7.30 ft/min. Welte's own booklets give
39
+ * no speed, only that the roll must run from the first "A" of the chromatic
40
+ * scale to the cross-line bearing the dial number in half a minute
41
+ * (Skala-Rolle 98 §1b). This is documentation; what sets the emulator's
42
+ * time axis is the spool.
30
43
  */
31
44
  export declare const welteT98: TrackerBar;
@@ -1,5 +1,5 @@
1
1
  import { describeTrackerBar } from "../../TrackerBar";
2
- import { mm } from "../../Quantity";
2
+ import { metersPerMinute, mm } from "../../Quantity";
3
3
  /**
4
4
  * The commands of the Welte-Mignon T-98, as its tracker bar reads them.
5
5
  *
@@ -32,8 +32,21 @@ export const welteT98ExpressionTypes = [
32
32
  *
33
33
  * Measured against Julian Dyer's scan of the T-98 copy of roll 225
34
34
  * (Grünfeld, Träumerei): 98 columns 2.818 mm apart on paper 285.6 mm
35
- * wide, notes on the middle 80 columns, the sustain valve punched 53
36
- * times where the T-100 copy latches its sustain on 53 times.
35
+ * wide, notes on the middle 80 columns, the sustain valve punched 52
36
+ * times in the music where the T-100 copy latches its sustain on 52
37
+ * times. A 53rd perforation on that track belongs to the test pattern
38
+ * after the rewind.
39
+ *
40
+ * The paper speed is the Deutsches Museum's figure for its Welte grün /
41
+ * T 98 rolls, 220 cm/min. Phillips gives 7 ft/min = 2.134 m/min (p. 121);
42
+ * midi2exp's and PlaySK's green defaults of 72.2 and 72.27, read as the
43
+ * Stanford convention of feet per minute times ten, are 2.201 m/min, which
44
+ * is the museum's figure; and the two copies of roll 225 last the same
45
+ * time if the green starts at about 7.30 ft/min. Welte's own booklets give
46
+ * no speed, only that the roll must run from the first "A" of the chromatic
47
+ * scale to the cross-line bearing the dial number in half a minute
48
+ * (Skala-Rolle 98 §1b). This is documentation; what sets the emulator's
49
+ * time axis is the spool.
37
50
  */
38
51
  export const welteT98 = describeTrackerBar({
39
52
  id: 'welte-green',
@@ -41,7 +54,22 @@ export const welteT98 = describeTrackerBar({
41
54
  width: mm(286),
42
55
  trackCount: 98,
43
56
  notes: { from: 6, to: 93, lowestPitch: 21 },
57
+ paperSpeed: { value: metersPerMinute(2.2), unit: 'm/min' },
44
58
  rewindTrack: 1,
59
+ /**
60
+ * The T-98 gives the rewind no line of its own — Welte's Forzando P line
61
+ * doubles as it — so a perforation there is the rewind only when it is far
62
+ * longer than the command that line usually carries. On the Monteurscala
63
+ * the rewind is a single continuous 103.8 mm perforation (Kontrolle 10, 40
64
+ * punches with gaps of 0.51–0.93 mm), where a commanded sforzando-piano
65
+ * runs a couple of millimetres — a median of 0.06 s against the rewind's
66
+ * 10.2 s on WM 184. Dyer's scan of the green 225 says the same a third time:
67
+ * of the 30 perforations its track 1 carries, exactly one reaches 40 mm and
68
+ * it is 386 mm long. So the threshold is not a fitted number and does not
69
+ * need to be — every measurement of it so far leaves an order of magnitude
70
+ * either side.
71
+ */
72
+ rewindHold: mm(40),
45
73
  expressions: new Map([
46
74
  [1, 'SforzandoPiano'],
47
75
  [2, 'Mezzoforte'],
@@ -0,0 +1,119 @@
1
+ import { Half, Parameters, PedalMode, Spool } from "welte-mignon-emulator";
2
+ import { instrumentT98Of, labelOf, nuanceOf, WelteT98InstrumentName } from "welte-mignon-emulator/t98";
3
+ import { ReproducingSystem } from "../../ReproducingSystem";
4
+ import { Millimeters, Seconds, Track } from "../../Quantity";
5
+ import { type VelocityMap } from "../velocity";
6
+ export type { VelocityMap } from "../velocity";
7
+ export type { WelteT98Instrument, WelteT98InstrumentName } from "welte-mignon-emulator/t98";
8
+ /**
9
+ * The instruments the emulator offers for the green Welte, in three groups that
10
+ * answer three different questions and must not be listed as one.
11
+ *
12
+ * A **genuine** instrument is fitted to the drawn nuance line of a green roll
13
+ * and says what a green Welte did. A **derived** instrument is fitted so that
14
+ * the green code of a recording reproduces what the fitted T-100 emulator makes
15
+ * of the *red* copy of the same recording, and says what Welte's editor meant
16
+ * the green roll to sound like; its provenance names the T-100 instrument it
17
+ * inherits, since it is only as good as the red reading behind it. The
18
+ * difference between the two, in the printed ordinate both scales share, is how
19
+ * far the transfer of a red reading onto the green mechanism succeeded.
20
+ *
21
+ * Neither has been fitted yet, so both groups are empty and what a playback runs
22
+ * on until then is the third group: the **unfitted** starting values, arithmetic
23
+ * from Welte's regulation controls and the T-100 consensus with no green roll
24
+ * behind any of it. A curve produced with them says so in its own `instrument`
25
+ * field, and nothing should be published from them.
26
+ */
27
+ export declare const instruments: {
28
+ genuine: Readonly<Partial<Record<"consensus" | import("welte-mignon-emulator/t98").GenuineRoll, import("welte-mignon-emulator/t98").WelteT98Instrument>>>;
29
+ derived: Readonly<Partial<Record<"225", import("welte-mignon-emulator/t98").WelteT98Instrument>>>;
30
+ unfitted: {
31
+ 'starting-values': import("welte-mignon-emulator/t98").WelteT98Instrument;
32
+ };
33
+ };
34
+ export declare const instrumentNames: readonly WelteT98InstrumentName[];
35
+ /** The instrument a pair of nuancing constants belongs to, if it is one. */
36
+ export declare const instrumentNameOf: (nuance: Record<Half, Parameters>) => WelteT98InstrumentName | undefined;
37
+ export { instrumentT98Of, labelOf, nuanceOf };
38
+ /**
39
+ * The two readings of the pedal mechanism, which is the T-100's below the
40
+ * command: Hagmann has the valves and bellows that carry out the movements "in
41
+ * beiden Systemen dieselbe" (p. 106), and only the Vorpneumatik differs.
42
+ */
43
+ export declare const pedalPresets: {
44
+ damping: Readonly<Record<string, number>>;
45
+ brushing: Readonly<Record<string, number>>;
46
+ };
47
+ export type PedalPreset = keyof typeof pedalPresets;
48
+ export declare const pedalPresetOf: (pedals: Parameters) => PedalPreset | undefined;
49
+ export type WelteT98Options = {
50
+ /**
51
+ * The take-up spool, which sets the time axis. No source states a T-98
52
+ * spool geometry, so the default keeps the red circumference and layer and
53
+ * sets the revolution to make the initial paper speed 220 cm/min. It is
54
+ * better varied than trusted: it scales every conductance by k and every
55
+ * time constant by 1/k, and touches nothing dimensionless.
56
+ */
57
+ spool: Spool;
58
+ /** Constants of the nuancing mechanism, one set for each half of the keyboard. */
59
+ nuance: Record<Half, Parameters>;
60
+ /** Which instrument those constants are, so that a curve can say so. */
61
+ instrument: WelteT98InstrumentName;
62
+ /** Constants of the two pedal actions, one of `pedalPresets` or a set of one's own. */
63
+ pedals: Parameters;
64
+ velocity: VelocityMap;
65
+ pedalMode: PedalMode;
66
+ /** Diameter of the tracker-bar bore. No source gives the T-98's; the red figure is carried over. */
67
+ trackerBore: Millimeters;
68
+ /** Punch diameter for an edition whose copies record none. */
69
+ punchDiameter: Millimeters;
70
+ /**
71
+ * Chained punches whose gap is shorter than this are one perforation. A held
72
+ * T-98 command is punched as a chain of round holes on a 2.66 mm grid with
73
+ * paper bridges of about a millimetre, not as one slot, so a raw scan and an
74
+ * edition whose collation has already made one symbol of the chain see the
75
+ * same port only if the chain is merged.
76
+ */
77
+ chainGap: Millimeters;
78
+ /**
79
+ * The track at which the keyboard is divided, so that notes from here
80
+ * upwards follow the treble expression and the ones below it the bass.
81
+ * Welte puts the division between f♯ and g (Betriebsanleitung p. 7), which
82
+ * on this bar is track 52; PlaySK's green configuration independently gives
83
+ * the last bass note as MIDI 66. Which side an expression perforation
84
+ * belongs to is not decided here but read off the tracker bar.
85
+ *
86
+ * A green roll re-cut from a Mignon master uses only the middle 80 of the 88
87
+ * note positions, so the division falls inside the used compass either way.
88
+ */
89
+ division: Track;
90
+ /**
91
+ * What a long perforation on the bass sforzando-piano line does. It is the
92
+ * same valve and the same hole as the dynamic (Skala-Rolle §10), so it
93
+ * always acts as a sforzando-piano; `stop` additionally ends the performance
94
+ * there, which is what the instrument does — during rewind the suction to
95
+ * the primary pneumatics is cut and the Hauptventil closes, so no note
96
+ * sounds and the expression apparatus is out of action (Betriebsanleitung
97
+ * p. 17). `ignore` is for an edition that is a fragment, or that carries a
98
+ * rewind in the middle for some reason of its own.
99
+ */
100
+ rewind: 'stop' | 'ignore';
101
+ };
102
+ export declare const defaultWelteT98Options: WelteT98Options;
103
+ /** When the spool brings a place on the roll to the tracker bar. */
104
+ export declare const secondsAt: (spool: Spool, place: Millimeters) => Seconds;
105
+ /**
106
+ * The green Welte, as welte-mignon-emulator models it. Everything downstream of
107
+ * the relay is the T-100's, on Hagmann's authority that the nuancing unit is
108
+ * built the same for both tracker scales (p. 96); what differs is in front of
109
+ * it. Each function is held for exactly as long as its own perforation runs
110
+ * over the glide block, four conduits stand on one bellows and their drives add
111
+ * as flows, the crescendo's ceiling is the balance of throttle 98 against the
112
+ * permanently open bore 100, and a long perforation on the bass sforzando-piano
113
+ * line sends the roll back.
114
+ *
115
+ * Its constants are **not fitted**. `instruments.genuine` and
116
+ * `instruments.derived` are empty until their fits run, and what a playback runs
117
+ * on until then is the unfitted starting values, which every curve says.
118
+ */
119
+ export declare const welteT98System: ReproducingSystem<WelteT98Options>;