linked-rolls 0.1.0 → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/RollCopy.d.ts CHANGED
@@ -284,8 +284,14 @@ export interface StanfordAtonOptions {
284
284
  */
285
285
  trackShift?: number;
286
286
  bar?: TrackerBar;
287
+ /**
288
+ * Where the scan the analysis was made from can be seen. Stanford's
289
+ * files name their scan by DRUID, so this is only needed for an
290
+ * analysis of a scan hosted elsewhere.
291
+ */
292
+ scan?: string;
287
293
  }
288
- export declare function readFromStanfordAton(atonString: string, { trackShift, bar }?: StanfordAtonOptions): RollCopy;
294
+ export declare function readFromStanfordAton(atonString: string, { trackShift, bar, scan }?: StanfordAtonOptions): RollCopy;
289
295
  /**
290
296
  * How a MIDI key number in one of Spencer Chase's roll files names a
291
297
  * tracker bar track.
package/lib/RollCopy.js CHANGED
@@ -100,6 +100,21 @@ export const calibrationOf = (copy) => {
100
100
  };
101
101
  /** Values in these files carry their unit as a suffix, e.g. "37.7646px". */
102
102
  const px = (value) => parseFloat(value);
103
+ /** The parser writes one record as an object and several as an array. */
104
+ const listOf = (value) => value === undefined ? [] : Array.isArray(value) ? value : [value];
105
+ /**
106
+ * Holes the parser set aside as suspicious after it had already chained
107
+ * them into a note: the head of such a chain is usually a punch that
108
+ * came out a little short, and leaving it out would lose the whole
109
+ * chain. They carry no track number of their own, so the column says
110
+ * which track they sit on.
111
+ */
112
+ const chainedBadHoles = (holes, calibration) => holes
113
+ .filter(hole => hole.NOTE_ATTACK && hole.OFF_TIME)
114
+ .map(hole => ({
115
+ ...hole,
116
+ TRACKER_HOLE: `${Math.round((px(hole.CENTROID_COL) - calibration.offset) / calibration.separation)}`
117
+ }));
103
118
  const millimeters = (pixels, dpi) => pixels / dpi * 25.4;
104
119
  const median = (values) => {
105
120
  const sorted = [...values].sort((a, b) => a - b);
@@ -139,13 +154,37 @@ const punchDiameterOf = (holes, dpi) => {
139
154
  return undefined;
140
155
  return circular.reduce((sum, diameter) => sum + diameter, 0) / circular.length;
141
156
  };
142
- export function readFromStanfordAton(atonString, { trackShift, bar = welteT100 } = {}) {
157
+ /**
158
+ * The image service Stanford keeps for a scan, and a crop of a
159
+ * feature from it.
160
+ */
161
+ const stanfordScan = (druid) => ({
162
+ scan: `https://stacks.stanford.edu/image/iiif/${druid}%2F${druid}_0001/`,
163
+ depictionOf: (column, row, width, height) => `https://stacks.stanford.edu/image/iiif/${druid}/${druid}_0001/${column},${row},${width},${height}/128,/270/default.jpg`
164
+ });
165
+ /**
166
+ * The software behind an analysis and when it was run, as the
167
+ * analysis file states them.
168
+ */
169
+ const measuredByOf = (rollinfo) => {
170
+ const date = new Date(rollinfo.ANALYSIS_DATE);
171
+ if (!rollinfo.HOLE_SOFTWARE || isNaN(date.getTime()))
172
+ return undefined;
173
+ return {
174
+ software: rollinfo.HOLE_SOFTWARE,
175
+ version: rollinfo.SOFTWARE_DATE ?? '',
176
+ date
177
+ };
178
+ };
179
+ export function readFromStanfordAton(atonString, { trackShift, bar = welteT100, scan } = {}) {
143
180
  const parser = new AtonParser();
144
181
  const json = parser.parse(atonString);
145
182
  const holes = json.ROLLINFO.HOLES.HOLE;
146
183
  const druid = json.ROLLINFO.DRUID;
184
+ const stanford = druid ? stanfordScan(druid) : undefined;
147
185
  const separation = px(json.ROLLINFO.HOLE_SEPARATION);
148
186
  const dpi = parseFloat(json.ROLLINFO.LENGTH_DPI);
187
+ const measuredBy = measuredByOf(json.ROLLINFO);
149
188
  const rewindTrack = rewindTrackIn(holes);
150
189
  const shift = trackShift
151
190
  ?? (rewindTrack === undefined ? 0 : bar.rewindTrack - rewindTrack);
@@ -156,8 +195,10 @@ export function readFromStanfordAton(atonString, { trackShift, bar = welteT100 }
156
195
  shift
157
196
  };
158
197
  const punchDiameter = punchDiameterOf(holes, dpi);
159
- const features = holes
198
+ const chains = [...holes, ...chainedBadHoles(listOf(json.ROLLINFO.BADHOLES?.HOLE), calibration)]
160
199
  .filter(hole => hole.NOTE_ATTACK && hole.OFF_TIME)
200
+ .sort((a, b) => px(a.NOTE_ATTACK) - px(b.NOTE_ATTACK));
201
+ const features = chains
161
202
  .map((hole) => {
162
203
  const attack = px(hole.NOTE_ATTACK);
163
204
  const release = px(hole.OFF_TIME);
@@ -166,7 +207,9 @@ export function readFromStanfordAton(atonString, { trackShift, bar = welteT100 }
166
207
  return {
167
208
  type: 'Hole',
168
209
  id: v4(),
169
- depiction: `https://stacks.stanford.edu/image/iiif/${druid}/${druid}_0001/${column},${attack},${columnWidth},${release - attack}/128,/270/default.jpg`,
210
+ ...(stanford && {
211
+ depiction: stanford.depictionOf(column, attack, columnWidth, release - attack)
212
+ }),
170
213
  vertical: {
171
214
  from: +hole.TRACKER_HOLE + shift,
172
215
  unit: 'track'
@@ -185,7 +228,7 @@ export function readFromStanfordAton(atonString, { trackShift, bar = welteT100 }
185
228
  conditions: [],
186
229
  location: '',
187
230
  modifications: [],
188
- scan: `https://stacks.stanford.edu/image/iiif/${druid}%2F${druid}_0001/`,
231
+ ...((scan ?? stanford) && { scan: scan ?? stanford?.scan }),
189
232
  measurements: {
190
233
  dimensions: {
191
234
  width: millimeters(px(json.ROLLINFO.ROLL_WIDTH), dpi),
@@ -204,7 +247,8 @@ export function readFromStanfordAton(atonString, { trackShift, bar = welteT100 }
204
247
  bass: px(json.ROLLINFO.HARD_MARGIN_BASS),
205
248
  unit: 'px'
206
249
  },
207
- trackCalibration: calibration
250
+ trackCalibration: calibration,
251
+ ...(measuredBy && { measuredBy })
208
252
  },
209
253
  features
210
254
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "linked-rolls",
3
- "version": "0.1.0",
3
+ "version": "0.1.1",
4
4
  "description": "Digital editions of piano rolls: import, collation, editorial assumptions, JSON-LD export, and emulation through a reproducing system",
5
5
  "repository": {
6
6
  "type": "git",