Add lazy piano transcription and optional leased server worker
This commit is contained in:
59
frontend/piano-core.js
Normal file
59
frontend/piano-core.js
Normal file
@@ -0,0 +1,59 @@
|
||||
/* Compact note sequences and practice ranges, shared by browser and server. */
|
||||
(function (root) {
|
||||
'use strict';
|
||||
const MAX_NOTES = 30000;
|
||||
function normalizeNotes(input) {
|
||||
if (!Array.isArray(input) || input.length > MAX_NOTES)
|
||||
throw new Error('Invalid or oversized note sequence');
|
||||
return input
|
||||
.map((note) => {
|
||||
const pitch = Number(note.pitch),
|
||||
start = Number(note.start),
|
||||
end = Number(note.end),
|
||||
velocity = Number(note.velocity);
|
||||
if (
|
||||
!Number.isInteger(pitch) ||
|
||||
pitch < 21 ||
|
||||
pitch > 108 ||
|
||||
!Number.isFinite(start) ||
|
||||
start < 0 ||
|
||||
!Number.isFinite(end) ||
|
||||
end <= start ||
|
||||
end > 3600 ||
|
||||
!Number.isFinite(velocity)
|
||||
)
|
||||
throw new Error('Invalid piano note');
|
||||
return {
|
||||
pitch,
|
||||
start: Math.round(start * 1000) / 1000,
|
||||
end: Math.max(Math.round(end * 1000) / 1000, Math.round(start * 1000) / 1000 + 0.001),
|
||||
velocity: Math.max(1, Math.min(127, Math.round(velocity))),
|
||||
hand: ['left', 'right'].includes(note.hand) ? note.hand : pitch < 60 ? 'left' : 'right',
|
||||
};
|
||||
})
|
||||
.sort((a, b) => a.start - b.start || a.pitch - b.pitch);
|
||||
}
|
||||
function fromBasicPitch(notes) {
|
||||
return normalizeNotes(
|
||||
notes
|
||||
.filter((n) => n.pitchMidi >= 21 && n.pitchMidi <= 108)
|
||||
.map((n) => ({
|
||||
pitch: n.pitchMidi,
|
||||
start: n.startTimeSeconds,
|
||||
end: n.startTimeSeconds + n.durationSeconds,
|
||||
velocity: n.amplitude * 127,
|
||||
})),
|
||||
);
|
||||
}
|
||||
const activeNotes = (notes, time) => notes.filter((n) => n.start <= time && n.end > time);
|
||||
function practiceRange(time, duration) {
|
||||
const start = Math.max(0, Number(time) || 0),
|
||||
end = Math.min(start + 8, Number(duration) || start + 8);
|
||||
return end > start
|
||||
? { start: Math.round(start * 1000) / 1000, end: Math.round(end * 1000) / 1000 }
|
||||
: null;
|
||||
}
|
||||
const api = { normalizeNotes, fromBasicPitch, activeNotes, practiceRange, MAX_NOTES };
|
||||
if (typeof module !== 'undefined') module.exports = api;
|
||||
else root.PianoCore = api;
|
||||
})(typeof window !== 'undefined' ? window : globalThis);
|
||||
Reference in New Issue
Block a user