linked-rolls 0.13.0 → 0.14.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 CHANGED
@@ -33,6 +33,41 @@ the keeper and the production metadata are nodes with a name and
33
33
  authority links, and the roll names its reproducing system. Exports
34
34
  are always in the current format.
35
35
 
36
+ ## Where a copy's features come from
37
+
38
+ `readFrom` states what a copy's features were read from: the roll
39
+ itself, a `scan` of it, an `analysis` somebody else measured on a
40
+ scan, an `emulation` in which the roll has already been read into
41
+ notes and commands, or a `recording` captured while the copy was
42
+ played. Beside the kind it holds who carried the capture out, on what
43
+ device, when, and a note. What is not known is left out.
44
+
45
+ This says where the numbers of the edition come from and nothing
46
+ about the state of the paper, which is a condition of the copy. In
47
+ RDF it is `reo:capture`, an activity typed by what it read from; the
48
+ file it produced is `crmdig:L11 had output`, the machine it ran on
49
+ `crmdig:L12 happened on device`.
50
+
51
+ Nothing records that a copy is doubtful. `reservationsAbout` works
52
+ out from what a copy states what the edition cannot vouch for in it:
53
+ that it names no source, that the making of its source is
54
+ undocumented, that its features are somebody else's reading, that its
55
+ source bears no physical evidence, that no measuring software is
56
+ recorded, that it is not calibrated. A reservation goes away when the
57
+ gap it names is filled.
58
+
59
+ ```ts
60
+ import { reservationsAbout, stateSource } from 'linked-rolls'
61
+
62
+ const next = produce(edition, stateSource(copyId, {
63
+ kind: 'emulation',
64
+ output: 'https://example.org/wm225.mid',
65
+ note: 'MIDI from a third party; the emulator is not named.'
66
+ }))
67
+
68
+ reservationsAbout(next.copies[0]).map(reservation => reservation.note)
69
+ ```
70
+
36
71
  ## Emulation
37
72
 
38
73
  `Emulation` turns a version of the edition into MIDI. The core of the
@@ -0,0 +1,65 @@
1
+ import { ActorAssignment, DateAssignment } from "./Assumption";
2
+ import { Concept } from "./Agent";
3
+ import { WithNote } from "./utils";
4
+ /**
5
+ * What a copy's features were read from. The kinds run from the paper
6
+ * itself to a file in which the roll has already been read into notes
7
+ * and commands.
8
+ */
9
+ export declare const sourceKinds: readonly ['roll', 'scan', 'analysis', 'emulation', 'recording'];
10
+ export type SourceKind = typeof sourceKinds[number];
11
+ /** How each kind of source is referred to in prose. */
12
+ export declare const sourceLabels: Record<SourceKind, string>;
13
+ /**
14
+ * Whether a source of this kind gives positions that were measured.
15
+ * An emulation and a recording give the reading somebody else made:
16
+ * the roll has been turned into notes and commands already, and
17
+ * turning those back into holes reconstructs them.
18
+ */
19
+ export declare const isMeasured: (kind: SourceKind) => boolean;
20
+ /**
21
+ * Whether a source of this kind bears witness to the paper. Punch
22
+ * diameter, hole separation, punching pattern, marks and writings can
23
+ * be observed on the roll and on an image of it, and on nothing else.
24
+ */
25
+ export declare const bearsPhysicalEvidence: (kind: SourceKind) => boolean;
26
+ /**
27
+ * The capture by which a copy's features became data: what they were
28
+ * read from, who read them, on what device and when. It states where
29
+ * the numbers of the edition come from and says nothing about the
30
+ * state of the paper, which is a condition.
31
+ *
32
+ * What is not known is left out. A reader is told about the gap by
33
+ * `reservationsAbout`, which works out what a copy cannot vouch for
34
+ * from what it states here.
35
+ * @see crm:E7 Activity
36
+ */
37
+ export interface FeatureSource extends WithNote {
38
+ /**
39
+ * What the features were read from.
40
+ * @see crm:P2 has type
41
+ */
42
+ kind: SourceKind;
43
+ /**
44
+ * The file the capture produced, where it has an address.
45
+ * @see crmdig:L11 had output
46
+ */
47
+ output?: string;
48
+ /**
49
+ * Who carried out the capture.
50
+ * @see crm:P14 carried out by
51
+ */
52
+ actor?: ActorAssignment;
53
+ /**
54
+ * The make and model of the scanner, camera or player the capture
55
+ * ran on.
56
+ * @see crmdig:L12 happened on device
57
+ */
58
+ device?: Concept;
59
+ /**
60
+ * When the capture took place.
61
+ * @format date
62
+ * @see dcterms:date
63
+ */
64
+ date?: DateAssignment;
65
+ }
@@ -0,0 +1,35 @@
1
+ /**
2
+ * What a copy's features were read from. The kinds run from the paper
3
+ * itself to a file in which the roll has already been read into notes
4
+ * and commands.
5
+ */
6
+ export const sourceKinds = [
7
+ 'roll',
8
+ 'scan',
9
+ 'analysis',
10
+ 'emulation',
11
+ 'recording'
12
+ ];
13
+ /** How each kind of source is referred to in prose. */
14
+ export const sourceLabels = {
15
+ roll: 'the roll itself',
16
+ scan: 'a scan of the roll',
17
+ analysis: 'a hole analysis of a scan',
18
+ emulation: 'an emulated MIDI file',
19
+ recording: 'a MIDI recording of the copy being played'
20
+ };
21
+ const measured = ['roll', 'scan', 'analysis'];
22
+ const physical = ['roll', 'scan'];
23
+ /**
24
+ * Whether a source of this kind gives positions that were measured.
25
+ * An emulation and a recording give the reading somebody else made:
26
+ * the roll has been turned into notes and commands already, and
27
+ * turning those back into holes reconstructs them.
28
+ */
29
+ export const isMeasured = (kind) => measured.includes(kind);
30
+ /**
31
+ * Whether a source of this kind bears witness to the paper. Punch
32
+ * diameter, hole separation, punching pattern, marks and writings can
33
+ * be observed on the roll and on an image of it, and on nothing else.
34
+ */
35
+ export const bearsPhysicalEvidence = (kind) => physical.includes(kind);
package/lib/RollCopy.d.ts CHANGED
@@ -6,6 +6,7 @@ import { AnyFeature } from "./Feature";
6
6
  import { ActorAssignment, DateAssignment, ObjectAssumption } from "./Assumption";
7
7
  import { WithId, WithType } from "./utils";
8
8
  import { Agent, Concept } from "./Agent";
9
+ import { FeatureSource } from "./FeatureSource";
9
10
  import { Measure, Millimeters, Quantity, Track } from "./Quantity";
10
11
  /**
11
12
  * This condition state is used to describe the roll's
@@ -282,6 +283,13 @@ export interface RollCopy extends WithType<'RollCopy'>, WithId {
282
283
  * @see crm:P138i has representation
283
284
  */
284
285
  scan?: string;
286
+ /**
287
+ * What this copy's features were read from. A copy that states no
288
+ * source is one whose features reached the edition by a way that
289
+ * was not written down.
290
+ * @see reo:capture
291
+ */
292
+ readFrom?: FeatureSource;
285
293
  }
286
294
  /**
287
295
  * Reads the features of a copy as the tracker bar would read them.
@@ -5,6 +5,7 @@ import { AnySymbol, PlacementRelation } from "./Symbol";
5
5
  import { CollationTolerance } from "./Collation";
6
6
  import { Edit } from "./Edit";
7
7
  import { RollCopy, ScaleReading, Shift } from "./RollCopy";
8
+ import { FeatureSource } from "./FeatureSource";
8
9
  import { AnyArgumentation, Certainty } from "./Assumption";
9
10
  /**
10
11
  * A change to an edition, written onto an immer draft of it. One
@@ -30,6 +31,10 @@ export declare const alignCopy: (copyId: string, shift: Shift, scale: number, re
30
31
  * being a fact about the copy.
31
32
  */
32
33
  export declare const unalignCopy: (copyId: string) => EditionOp;
34
+ /** States what the copy's features were read from, in place of any earlier statement. */
35
+ export declare const stateSource: (copyId: string, source: FeatureSource) => EditionOp;
36
+ /** Takes back the statement, leaving the copy silent about its source again. */
37
+ export declare const clearSource: (copyId: string) => EditionOp;
33
38
  /** The symbols of the versions that no other copy carries. */
34
39
  export declare const symbolsCarriedOnlyBy: (edition: Edition, copyId: string) => AnySymbol[];
35
40
  /** Takes the features off the copy, and out of the versions with what only they carried. */
package/lib/editionOps.js CHANGED
@@ -96,6 +96,14 @@ export const unalignCopy = (copyId) => onCopy(copyId, copy => {
96
96
  revertShift(copy);
97
97
  copy.conditions = without(copy.conditions, isPaperStretch);
98
98
  });
99
+ /** States what the copy's features were read from, in place of any earlier statement. */
100
+ export const stateSource = (copyId, source) => onCopy(copyId, copy => {
101
+ copy.readFrom = source;
102
+ });
103
+ /** Takes back the statement, leaving the copy silent about its source again. */
104
+ export const clearSource = (copyId) => onCopy(copyId, copy => {
105
+ copy.readFrom = undefined;
106
+ });
99
107
  const featureIdsOf = (copy) => new Set(copy.features.map(feature => feature.id));
100
108
  /**
101
109
  * A symbol every carrier of which lies among the features loses its
package/lib/index.d.ts CHANGED
@@ -14,7 +14,9 @@ export * from './systems/welteT100/bar';
14
14
  export * from './systems/welteLicensee/bar';
15
15
  export * from './systems';
16
16
  export * from './ReproducingSystem';
17
+ export * from './FeatureSource';
17
18
  export * from './RollCopy';
19
+ export * from './reservations';
18
20
  export * from './alignment';
19
21
  export * from './Edition';
20
22
  export * from './EditionView';
package/lib/index.js CHANGED
@@ -14,7 +14,9 @@ export * from './systems/welteT100/bar';
14
14
  export * from './systems/welteLicensee/bar';
15
15
  export * from './systems';
16
16
  export * from './ReproducingSystem';
17
+ export * from './FeatureSource';
17
18
  export * from './RollCopy';
19
+ export * from './reservations';
18
20
  export * from './alignment';
19
21
  export * from './Edition';
20
22
  export * from './EditionView';
@@ -0,0 +1,23 @@
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'];
3
+ export type ReservationType = typeof reservationTypes[number];
4
+ /**
5
+ * Something an edition cannot vouch for in one of its copies.
6
+ *
7
+ * A reservation is worked out from what the copy states about itself
8
+ * and is never written into the edition: it says that knowledge of
9
+ * the copy is incomplete, so filling in what is missing makes it go
10
+ * away. Nothing here describes the state of the paper, which is a
11
+ * condition of the copy.
12
+ */
13
+ export interface Reservation {
14
+ type: ReservationType;
15
+ /** What the reservation means for a reader, in one sentence. */
16
+ note: string;
17
+ }
18
+ /**
19
+ * What the edition cannot vouch for in a copy, in the order the
20
+ * checks are listed: where its features came from first, then what
21
+ * the measurement leaves open.
22
+ */
23
+ export declare const reservationsAbout: (copy: RollCopy) => Reservation[];
@@ -0,0 +1,53 @@
1
+ import { bearsPhysicalEvidence, isMeasured, sourceLabels } from "./FeatureSource";
2
+ import { calibrationOf } from "./RollCopy";
3
+ export const reservationTypes = [
4
+ 'source-not-stated',
5
+ 'source-undocumented',
6
+ 'features-interpreted',
7
+ 'no-physical-evidence',
8
+ 'measurement-undocumented',
9
+ 'not-calibrated'
10
+ ];
11
+ const sourceStated = copy => copy.readFrom ? undefined : {
12
+ type: 'source-not-stated',
13
+ note: 'The copy does not state what its features were read from.'
14
+ };
15
+ const sourceDocumented = copy => {
16
+ const source = copy.readFrom;
17
+ if (!source || source.actor || source.device || source.date)
18
+ return undefined;
19
+ return {
20
+ type: 'source-undocumented',
21
+ note: `The copy names ${sourceLabels[source.kind]} as its source, but records nobody who made it, no device and no date.`
22
+ };
23
+ };
24
+ const featuresMeasured = copy => copy.readFrom && !isMeasured(copy.readFrom.kind) ? {
25
+ type: 'features-interpreted',
26
+ note: `The features come from ${sourceLabels[copy.readFrom.kind]}, in which the roll has already been read into notes and commands by somebody else.`
27
+ } : undefined;
28
+ const physicalEvidence = copy => copy.readFrom && !bearsPhysicalEvidence(copy.readFrom.kind) ? {
29
+ type: 'no-physical-evidence',
30
+ note: 'Punch diameter, hole separation, punching pattern, marks and writings cannot be observed on this source.'
31
+ } : undefined;
32
+ const measurementDocumented = copy => !copy.measurements.measuredBy && copy.readFrom?.kind !== 'roll' ? {
33
+ type: 'measurement-undocumented',
34
+ note: 'No measuring software is recorded for this copy.'
35
+ } : undefined;
36
+ const calibrated = copy => calibrationOf(copy) ? undefined : {
37
+ type: 'not-calibrated',
38
+ note: 'The copy is not calibrated against the tracker bar, so its track positions rest on the numbering its source used.'
39
+ };
40
+ const checks = [
41
+ sourceStated,
42
+ sourceDocumented,
43
+ featuresMeasured,
44
+ physicalEvidence,
45
+ measurementDocumented,
46
+ calibrated
47
+ ];
48
+ /**
49
+ * What the edition cannot vouch for in a copy, in the order the
50
+ * checks are listed: where its features came from first, then what
51
+ * the measurement leaves open.
52
+ */
53
+ export const reservationsAbout = (copy) => checks.flatMap(check => check(copy) ?? []);
package/lib/schema.json CHANGED
@@ -543,6 +543,40 @@
543
543
  ],
544
544
  "type": "string"
545
545
  },
546
+ "FeatureSource": {
547
+ "description": "The capture by which a copy's features became data: what they were read from, who read them, on what device and when. It states where the numbers of the edition come from and says nothing about the state of the paper, which is a condition.\n\nWhat is not known is left out. A reader is told about the gap by `reservationsAbout`, which works out what a copy cannot vouch for from what it states here. [ontology: crm:E7 Activity]",
548
+ "properties": {
549
+ "actor": {
550
+ "$ref": "#/definitions/ActorAssignment",
551
+ "description": "Who carried out the capture. [ontology: crm:P14 carried out by]"
552
+ },
553
+ "date": {
554
+ "$ref": "#/definitions/DateAssignment",
555
+ "description": "When the capture took place. [ontology: dcterms:date]",
556
+ "format": "date"
557
+ },
558
+ "device": {
559
+ "$ref": "#/definitions/Concept",
560
+ "description": "The make and model of the scanner, camera or player the capture ran on. [ontology: crmdig:L12 happened on device]"
561
+ },
562
+ "kind": {
563
+ "$ref": "#/definitions/SourceKind",
564
+ "description": "What the features were read from. [ontology: crm:P2 has type]"
565
+ },
566
+ "note": {
567
+ "description": "A free-text note providing additional context. [ontology: crm:P3 has note]",
568
+ "type": "string"
569
+ },
570
+ "output": {
571
+ "description": "The file the capture produced, where it has an address. [ontology: crmdig:L11 had output]",
572
+ "type": "string"
573
+ }
574
+ },
575
+ "required": [
576
+ "kind"
577
+ ],
578
+ "type": "object"
579
+ },
546
580
  "GluedOn": {
547
581
  "description": "A piece of material (paper or tape) glued onto the roll surface. Glued-on features are typically used to cover perforations (for corrections) or to reinforce damaged areas. They may themselves carry other features such as writings or additional holes. [ontology: crm:E22 Human-Made Object]",
548
582
  "properties": {
@@ -1816,6 +1850,10 @@
1816
1850
  "$ref": "#/definitions/ProductionEvent",
1817
1851
  "description": "The production event that created this roll copy. [ontology: lrmoo:R28i was produced by]"
1818
1852
  },
1853
+ "readFrom": {
1854
+ "$ref": "#/definitions/FeatureSource",
1855
+ "description": "What this copy's features were read from. A copy that states no source is one whose features reached the edition by a way that was not written down. [ontology: reo:capture]"
1856
+ },
1819
1857
  "scan": {
1820
1858
  "description": "The scan URL or IIIF URL of the roll. [ontology: crm:P138i has representation]",
1821
1859
  "type": "string"
@@ -1861,6 +1899,16 @@
1861
1899
  ],
1862
1900
  "type": "object"
1863
1901
  },
1902
+ "SourceKind": {
1903
+ "enum": [
1904
+ "roll",
1905
+ "scan",
1906
+ "analysis",
1907
+ "emulation",
1908
+ "recording"
1909
+ ],
1910
+ "type": "string"
1911
+ },
1864
1912
  "Text": {
1865
1913
  "description": "A textual symbol, e.g. a label or annotation found on the roll. [ontology: crm:E33 Linguistic Object]",
1866
1914
  "properties": {
@@ -165,6 +165,22 @@
165
165
  "@id": "crm:P138i_has_representation",
166
166
  "@type": "@id"
167
167
  },
168
+ "readFrom": {
169
+ "@id": "reo:capture",
170
+ "@context": {
171
+ "kind": {
172
+ "@id": "crm:P2_has_type",
173
+ "@type": "@vocab",
174
+ "@context": { "@vocab": "https://w3id.org/reo/type/" }
175
+ },
176
+ "output": {
177
+ "@id": "crmdig:L11_had_output",
178
+ "@type": "@id"
179
+ },
180
+ "device": "crmdig:L12_happened_on_device",
181
+ "note": "crm:P3_has_note"
182
+ }
183
+ },
168
184
  "depiction": {
169
185
  "@id": "crm:P138i_has_representation",
170
186
  "@type": "@id"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "linked-rolls",
3
- "version": "0.13.0",
3
+ "version": "0.14.0",
4
4
  "description": "Digital editions of piano rolls: import, collation, editorial assumptions, JSON-LD export, and emulation through a reproducing system",
5
5
  "license": "MIT",
6
6
  "repository": {