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.
- package/README.md +7 -4
- package/lib/Agent.d.ts +59 -0
- package/lib/Agent.js +1 -0
- package/lib/Assumption.d.ts +19 -1
- package/lib/Collation.d.ts +19 -0
- package/lib/Collation.js +25 -1
- package/lib/Edit.d.ts +1 -8
- package/lib/Edition.d.ts +4 -77
- package/lib/Edition.js +1 -1
- package/lib/EditionView.d.ts +0 -7
- package/lib/EditionView.js +0 -50
- package/lib/Feature.d.ts +3 -1
- package/lib/ReproducingSystem.d.ts +23 -1
- package/lib/RollCopy.d.ts +2 -68
- package/lib/RollCopy.js +2 -303
- package/lib/TrackerBar.d.ts +16 -14
- package/lib/TrackerBar.js +1 -56
- package/lib/alignment.d.ts +21 -0
- package/lib/alignment.js +117 -0
- package/lib/constraints.d.ts +27 -0
- package/lib/constraints.js +61 -0
- package/lib/editionOps.js +7 -10
- package/lib/index.d.ts +18 -13
- package/lib/index.js +18 -13
- package/lib/migrate.js +2 -1
- package/lib/readers/spencerMidi.d.ts +26 -0
- package/lib/readers/spencerMidi.js +58 -0
- package/lib/readers/stanfordAton.d.ts +18 -0
- package/lib/readers/stanfordAton.js +158 -0
- package/lib/schema.json +2 -2
- package/lib/systems/welteT100/bar.d.ts +14 -0
- package/lib/systems/welteT100/bar.js +56 -0
- package/lib/systems/{welteT100.d.ts → welteT100/system.d.ts} +17 -2
- package/lib/systems/{welteT100.js → welteT100/system.js} +19 -3
- package/lib/utils.d.ts +0 -8
- package/lib/validate.d.ts +0 -27
- package/lib/validate.js +0 -61
- package/package.json +4 -4
- package/lib/alignFeatures.d.ts +0 -12
- package/lib/alignFeatures.js +0 -55
- /package/lib/{aton → readers}/AtonParser.d.ts +0 -0
- /package/lib/{aton → readers}/AtonParser.js +0 -0
- /package/lib/{asMIDISpans.d.ts → readers/midiSpans.d.ts} +0 -0
- /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.
|
|
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.
|
|
44
|
+
"welte-t100-emulator": "^0.4.0"
|
|
45
45
|
},
|
|
46
46
|
"peerDependenciesMeta": {
|
|
47
47
|
"welte-t100-emulator": {
|
package/lib/alignFeatures.d.ts
DELETED
|
@@ -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 {};
|
package/lib/alignFeatures.js
DELETED
|
@@ -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
|
|
File without changes
|