linked-rolls 0.5.0 → 0.6.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 (44) hide show
  1. package/README.md +7 -4
  2. package/lib/Agent.d.ts +59 -0
  3. package/lib/Agent.js +1 -0
  4. package/lib/Assumption.d.ts +19 -1
  5. package/lib/Collation.d.ts +19 -0
  6. package/lib/Collation.js +25 -1
  7. package/lib/Edit.d.ts +1 -8
  8. package/lib/Edition.d.ts +4 -77
  9. package/lib/Edition.js +1 -1
  10. package/lib/EditionView.d.ts +0 -7
  11. package/lib/EditionView.js +0 -50
  12. package/lib/Feature.d.ts +3 -1
  13. package/lib/ReproducingSystem.d.ts +23 -1
  14. package/lib/RollCopy.d.ts +2 -68
  15. package/lib/RollCopy.js +2 -303
  16. package/lib/TrackerBar.d.ts +16 -14
  17. package/lib/TrackerBar.js +1 -56
  18. package/lib/alignment.d.ts +21 -0
  19. package/lib/alignment.js +117 -0
  20. package/lib/constraints.d.ts +27 -0
  21. package/lib/constraints.js +61 -0
  22. package/lib/editionOps.js +7 -10
  23. package/lib/index.d.ts +18 -13
  24. package/lib/index.js +18 -13
  25. package/lib/migrate.js +2 -1
  26. package/lib/readers/spencerMidi.d.ts +26 -0
  27. package/lib/readers/spencerMidi.js +58 -0
  28. package/lib/readers/stanfordAton.d.ts +18 -0
  29. package/lib/readers/stanfordAton.js +158 -0
  30. package/lib/schema.json +2 -2
  31. package/lib/systems/welteT100/bar.d.ts +14 -0
  32. package/lib/systems/welteT100/bar.js +56 -0
  33. package/lib/systems/{welteT100.d.ts → welteT100/system.d.ts} +17 -2
  34. package/lib/systems/{welteT100.js → welteT100/system.js} +19 -3
  35. package/lib/utils.d.ts +0 -8
  36. package/lib/validate.d.ts +0 -27
  37. package/lib/validate.js +0 -61
  38. package/package.json +4 -4
  39. package/lib/alignFeatures.d.ts +0 -12
  40. package/lib/alignFeatures.js +0 -55
  41. /package/lib/{aton → readers}/AtonParser.d.ts +0 -0
  42. /package/lib/{aton → readers}/AtonParser.js +0 -0
  43. /package/lib/{asMIDISpans.d.ts → readers/midiSpans.d.ts} +0 -0
  44. /package/lib/{asMIDISpans.js → readers/midiSpans.js} +0 -0
package/lib/validate.js CHANGED
@@ -1,7 +1,5 @@
1
1
  import Ajv from "ajv";
2
2
  import * as schema from "./schema.json";
3
- import { idOf } from "./Assumption";
4
- import { isPerforation, pairsAmong, placementsOf } from "./Symbol";
5
3
  const ajv = new Ajv({
6
4
  strict: false,
7
5
  formats: {
@@ -10,62 +8,3 @@ const ajv = new Ajv({
10
8
  });
11
9
  const validate = ajv.compile(schema);
12
10
  export { validate };
13
- const missingReference = {
14
- alignedWith: 'alignment-reference-missing',
15
- before: 'before-reference-missing',
16
- after: 'after-reference-missing'
17
- };
18
- const problemsIn = (version, perforations) => {
19
- const ids = new Set(perforations.map(p => p.id));
20
- const report = (symbol, problem) => ({ version, symbol, problem });
21
- const missingReferences = perforations
22
- .flatMap(p => placementsOf(p)
23
- .filter(({ reference }) => !ids.has(idOf(reference)))
24
- .map(({ relation }) => report(p.id, missingReference[relation])));
25
- const selfPlaced = perforations
26
- .filter(p => placementsOf(p).some(({ reference }) => idOf(reference) === p.id))
27
- .map(p => report(p.id, 'placed-relative-to-itself'));
28
- const placedSeveralWays = perforations
29
- .filter(p => placementsOf(p).length > 1)
30
- .map(p => report(p.id, 'placed-several-ways'));
31
- const missingPartners = perforations
32
- .filter(p => p.pairedWith && !ids.has(idOf(p.pairedWith)))
33
- .map(p => report(p.id, 'partner-missing'));
34
- const selfPaired = perforations
35
- .filter(p => p.pairedWith && idOf(p.pairedWith) === p.id)
36
- .map(p => report(p.id, 'paired-with-itself'));
37
- const pairs = pairsAmong(perforations);
38
- const pairsOf = (p) => pairs.filter(pair => pair.includes(p));
39
- const inSeveralPairs = perforations
40
- .filter(p => pairsOf(p).length > 1)
41
- .map(p => report(p.id, 'in-several-pairs'));
42
- const placedOnBothSides = pairs
43
- .filter(([one, other]) => placementsOf(one).length > 0 && placementsOf(other).length > 0)
44
- .flatMap(pair => pair.map(p => report(p.id, 'pair-placed-on-both-sides')));
45
- return [
46
- ...missingReferences, ...selfPlaced, ...placedSeveralWays,
47
- ...missingPartners, ...selfPaired, ...inSeveralPairs, ...placedOnBothSides
48
- ];
49
- };
50
- /**
51
- * Where the placements and pairings of the edition cannot hold as
52
- * stated, version by version: a reference or partner absent from the
53
- * version, a perforation placed relative to itself or in several ways
54
- * at once, one claimed by several pairs, or a pair whose members are
55
- * both placed and so cannot keep their distance and follow their
56
- * references at once.
57
- */
58
- export const constraintProblems = (view) => view.edition.versions.flatMap(version => problemsIn(version.id, view.snapshot(version.id).filter(isPerforation)));
59
- const isExpression = (symbol) => symbol.type === 'expression';
60
- /**
61
- * The expressions of the edition whose type the tracker bar does not
62
- * read, version by version. The roll names its system; a type from
63
- * another system, or a misspelt one, means nothing on it.
64
- */
65
- export const unknownExpressionTypes = (view, bar) => {
66
- const known = new Set(bar.expressionTypes);
67
- return view.edition.versions.flatMap(version => view.snapshot(version.id)
68
- .filter(isExpression)
69
- .filter(symbol => !known.has(symbol.expressionType))
70
- .map(symbol => ({ version: version.id, symbol: symbol.id, expressionType: symbol.expressionType })));
71
- };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "linked-rolls",
3
- "version": "0.5.0",
3
+ "version": "0.6.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": {
@@ -16,8 +16,8 @@
16
16
  "default": "./lib/index.js"
17
17
  },
18
18
  "./welte-t100": {
19
- "types": "./lib/systems/welteT100.d.ts",
20
- "default": "./lib/systems/welteT100.js"
19
+ "types": "./lib/systems/welteT100/system.d.ts",
20
+ "default": "./lib/systems/welteT100/system.js"
21
21
  },
22
22
  "./lib/*": "./lib/*",
23
23
  "./package.json": "./package.json"
@@ -41,7 +41,7 @@
41
41
  "welte-t100-emulator": "file:../welte-t100"
42
42
  },
43
43
  "peerDependencies": {
44
- "welte-t100-emulator": "^0.3.0"
44
+ "welte-t100-emulator": "^0.4.0"
45
45
  },
46
46
  "peerDependenciesMeta": {
47
47
  "welte-t100-emulator": {
@@ -1,12 +0,0 @@
1
- import { AnyFeature } from "./Feature";
2
- import { TrackerBar } from "./TrackerBar";
3
- type AlignmentResult = {
4
- shift: number;
5
- stretch: number;
6
- };
7
- /**
8
- * Align two rolls by computing independent linear fits of each roll's note-onset positions
9
- * using only the first and last segments, then deriving a transform x2 = (x1 + shift) * stretch.
10
- */
11
- export declare function alignFeatures(rollA: AnyFeature[], rollB: AnyFeature[], bar?: TrackerBar): AlignmentResult;
12
- export {};
@@ -1,55 +0,0 @@
1
- import { welteT100 } from "./TrackerBar";
2
- const isNoteOn = (bar) => (feature) => {
3
- return feature.type === 'Hole' && bar.meaningOf(feature.vertical.from)?.type === 'note';
4
- };
5
- /**
6
- * Fit a line: position = alpha * index + beta via least squares.
7
- */
8
- function fitIndexToPosition(indices, positions) {
9
- const n = indices.length;
10
- const meanIdx = indices.reduce((s, i) => s + i, 0) / n;
11
- const meanPos = positions.reduce((s, p) => s + p, 0) / n;
12
- let num = 0;
13
- let den = 0;
14
- for (let i = 0; i < n; i++) {
15
- const d = indices[i] - meanIdx;
16
- num += d * (positions[i] - meanPos);
17
- den += d * d;
18
- }
19
- const alpha = den === 0 ? 1 : num / den;
20
- const beta = meanPos - alpha * meanIdx;
21
- return { alpha, beta };
22
- }
23
- /**
24
- * Selects the first and last N elements of an array (or fewer if length is smaller).
25
- */
26
- function selectEnds(arr, count) {
27
- const n = arr.length;
28
- if (count * 2 >= n)
29
- return arr.slice();
30
- return arr.slice(0, count).concat(arr.slice(n - count, n));
31
- }
32
- /**
33
- * Align two rolls by computing independent linear fits of each roll's note-onset positions
34
- * using only the first and last segments, then deriving a transform x2 = (x1 + shift) * stretch.
35
- */
36
- export function alignFeatures(rollA, rollB, bar = welteT100) {
37
- // 1. Extract note-onset positions
38
- const isNote = isNoteOn(bar);
39
- const allXA = rollA.filter(isNote).map(f => f.horizontal.from);
40
- const allXB = rollB.filter(isNote).map(f => f.horizontal.from);
41
- // 2. Determine segment size (e.g. 10% of notes, min 5)
42
- const segCount = Math.max(5, Math.floor(allXA.length * 0.1));
43
- // 3. Select only first and last segments
44
- const XA = selectEnds(allXA, segCount);
45
- const idxA = XA.map((_, i) => i);
46
- const XB = selectEnds(allXB, segCount);
47
- const idxB = XB.map((_, i) => i);
48
- // 4. Fit index->position for each roll on selected ends
49
- const { alpha: alphaA, beta: betaA } = fitIndexToPosition(idxA, XA);
50
- const { alpha: alphaB, beta: betaB } = fitIndexToPosition(idxB, XB);
51
- // 5. Derive stretch and shift such that x2 = (x1 + shift) * stretch
52
- const stretch = alphaB / alphaA;
53
- const shift = betaB / stretch - betaA;
54
- return { stretch, shift };
55
- }
File without changes
File without changes
File without changes