pepperscore 0.1.1 → 0.3.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/CHANGELOG.md +64 -0
  2. package/LICENSE +26 -12
  3. package/README.md +128 -9
  4. package/dist/THIRD_PARTY_NOTICES.txt +1222 -0
  5. package/dist/assets/{import.worker-D0XfuAHu.js → import.worker-nfqQ5M0Y.js} +2 -2
  6. package/dist/assets/verovio.worker-DGik-H4a.js +7 -0
  7. package/dist/audio.d.ts +24 -0
  8. package/dist/audio.js +198 -0
  9. package/dist/audio.js.map +1 -0
  10. package/dist/components/PepperScore.d.ts +111 -2
  11. package/dist/index.d.ts +3 -2
  12. package/dist/index.js +2650 -3196
  13. package/dist/index.js.map +1 -1
  14. package/dist/lib/chrome.d.ts +5 -2
  15. package/dist/lib/clock.d.ts +13 -0
  16. package/dist/lib/file-state.d.ts +17 -4
  17. package/dist/lib/header-controls.d.ts +7 -3
  18. package/dist/lib/hit-test.d.ts +18 -0
  19. package/dist/lib/instruments.d.ts +82 -0
  20. package/dist/lib/metronome.d.ts +33 -0
  21. package/dist/lib/notation/mei-tab.d.ts +51 -0
  22. package/dist/lib/notation/mei-view.d.ts +8 -0
  23. package/dist/lib/playback/clock-time.d.ts +20 -0
  24. package/dist/lib/playback/timeline.d.ts +4 -1
  25. package/dist/lib/prefs.d.ts +50 -0
  26. package/dist/lib/render.d.ts +36 -1
  27. package/dist/lib/score.d.ts +7 -0
  28. package/dist/lib/sound-players.d.ts +9 -1
  29. package/dist/lib/tab/fret-assign.d.ts +53 -0
  30. package/dist/lib/tab/tab-parts.d.ts +58 -0
  31. package/dist/lib/verovio-protocol.d.ts +15 -0
  32. package/dist/lib/verovio-runtime.d.ts +9 -1
  33. package/dist/runtime-files.d.ts +15 -0
  34. package/dist/{sf2-player-CRlOJDhD.js → sf2-player-oWC6yrUH.js} +28 -6
  35. package/dist/sf2-player-oWC6yrUH.js.map +1 -0
  36. package/dist/sound-players-DdLBBZaR.js +1172 -0
  37. package/dist/sound-players-DdLBBZaR.js.map +1 -0
  38. package/dist/style.css +1 -1
  39. package/dist/verovio/LICENSE +858 -0
  40. package/dist/verovio/verovio-module.mjs +0 -0
  41. package/dist/verovio/verovio.mjs +397 -0
  42. package/package.json +7 -1
  43. package/dist/assets/verovio.worker-DkHQmOsT.js +0 -19034
  44. package/dist/sf2-player-CRlOJDhD.js.map +0 -1
@@ -50,6 +50,21 @@ export type VerovioRequest = {
50
50
  chunkBudget: number;
51
51
  simulateOom?: boolean;
52
52
  });
53
+ /**
54
+ * Where the worker imports Verovio from: upstream's own, unmodified ES modules, loaded at runtime so they stay a
55
+ * separately replaceable library (LGPL-3.0-or-later). `module` is the Emscripten module (npm verovio's
56
+ * dist/verovio-module.mjs, default export: an async factory of the WASM module); `toolkit` is the JavaScript toolkit
57
+ * (dist/verovio.mjs, exports `VerovioToolkit` and `enableLogToBuffer`). Absolute URLs.
58
+ */
59
+ export type VerovioFiles = {
60
+ module: string;
61
+ toolkit: string;
62
+ };
63
+ /** Sent once by the runtime right after it starts a worker, before any request. */
64
+ export type VerovioConfigure = {
65
+ kind: 'configure';
66
+ files: VerovioFiles;
67
+ };
53
68
  export type VerovioReply = {
54
69
  id: number;
55
70
  value?: unknown;
@@ -1,5 +1,5 @@
1
1
  import type { SourceDocument } from './notation/document';
2
- import type { BaselineInfo, PagesResult, ProbeResult, VerovioReply, ViewSpec } from './verovio-protocol';
2
+ import type { BaselineInfo, PagesResult, ProbeResult, VerovioFiles, VerovioReply, ViewSpec } from './verovio-protocol';
3
3
  /** A worker failure; `code` says why (see VerovioReply). */
4
4
  export declare class VerovioError extends Error {
5
5
  readonly code?: VerovioReply['code'];
@@ -24,6 +24,14 @@ export declare const chunkSettings: () => {
24
24
  * threshold) on a fresh worker. The caller then opens or draws the score again. Returns the new budget.
25
25
  */
26
26
  export declare function reduceChunkSize(): number;
27
+ /** The Verovio files under a directory laid out like the verovio npm package's dist/; a relative URL resolves against the page. */
28
+ export declare function verovioFilesAt(base: string, page?: string): VerovioFiles;
29
+ /**
30
+ * Load Verovio from `base` (see PepperScoreProps.verovioBaseUrl) instead of the shipped copy. Every reader on the page
31
+ * shares one worker, so this applies to all of them, from the next time the worker starts (the first engraving, or
32
+ * a restart); a blank `base` changes nothing.
33
+ */
34
+ export declare function setVerovioBaseUrl(base: string | undefined): void;
27
35
  export declare function verovioRevision(document: SourceDocument): number;
28
36
  /** A reader mounted: a channel of its own, so its imports and renders never supersede another reader's. */
29
37
  export declare function openVerovioChannel(): number;
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Files pepperscore loads at runtime instead of bundling them: Verovio (LGPL-3.0-or-later, kept as upstream's own
3
+ * separately replaceable files) and the third-party notices. The build (vite.config.ts) writes them to dist/verovio/
4
+ * and dist/THIRD_PARTY_NOTICES.txt.
5
+ *
6
+ * Keep this file at the root of src/: its code lands in dist/index.js, so `./` is dist/. The library build must not
7
+ * resolve these URLs (`@vite-ignore`; lib mode would inline the files as data URLs); its runtime-files plugin then
8
+ * turns them back into plain `new URL('./…', import.meta.url)` in dist/index.js, the pattern host bundlers (Vite,
9
+ * Astro) recognise to copy the files as assets. scripts/finalize-dist.mjs fails the build if they do not survive.
10
+ */
11
+ import type { VerovioFiles } from './lib/verovio-protocol';
12
+ /** The Verovio files shipped with pepperscore. */
13
+ export declare const bundledVerovioFiles: () => VerovioFiles;
14
+ /** The third-party notices shipped with pepperscore (linked from the drawer's Licences entry). */
15
+ export declare const thirdPartyNoticesUrl: () => string;
@@ -52,15 +52,37 @@ async function a(e, { url: n, drums: a, program: o, preset: s, destination: c })
52
52
  } catch (e) {
53
53
  throw g.dispose(), h.disconnect(), e;
54
54
  }
55
- return f && (h.gain.value = 1 / Math.sqrt(Math.max(1, ...Array.from({ length: 128 }, (e, t) => p.zones.filter((e) => e.keyRange.lo <= t && t <= e.keyRange.hi).length)))), {
56
- start: (e) => g.start(e),
57
- stop: () => g.stop(),
55
+ f && (h.gain.value = 1 / Math.sqrt(Math.max(1, ...Array.from({ length: 128 }, (e, t) => p.zones.filter((e) => e.keyRange.lo <= t && t <= e.keyRange.hi).length))));
56
+ let _ = /* @__PURE__ */ new Map(), v = 0, y = () => {
57
+ for (let e of [..._.keys()]) e();
58
+ _.clear(), g.stop();
59
+ }, b = new Set(p.zones.flatMap(({ keyRange: { lo: e, hi: t } = {
60
+ lo: 0,
61
+ hi: 127
62
+ } }) => Array.from({ length: Math.max(0, t - e + 1) }, (t, n) => e + n)));
63
+ return {
64
+ start: ({ note: t, time: n, duration: r, velocity: i }) => {
65
+ let a = e.currentTime;
66
+ for (let [e, t] of _) t < a && _.delete(e);
67
+ let o = g.start({
68
+ note: t,
69
+ time: n,
70
+ duration: r,
71
+ velocity: i,
72
+ stopId: `n${v++}`
73
+ }), s = () => {
74
+ _.delete(s), o();
75
+ };
76
+ return _.set(s, Math.max(n, a) + r + 2), s;
77
+ },
78
+ stop: y,
58
79
  dispose: () => {
59
- g.dispose(), h.disconnect();
60
- }
80
+ y(), g.dispose(), h.disconnect();
81
+ },
82
+ covers: (e) => b.has(e)
61
83
  };
62
84
  }
63
85
  //#endregion
64
86
  export { a as loadSf2Player };
65
87
 
66
- //# sourceMappingURL=sf2-player-CRlOJDhD.js.map
88
+ //# sourceMappingURL=sf2-player-oWC6yrUH.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sf2-player-oWC6yrUH.js","names":["soundfonts","parsedSoundfont","cached","url","response","buffer","soundfont2","LAYERED","layeredInstrument","zones","preset","presetZone","lo","zone","hi","layer","findPreset","presets","item","drums","program","loadSf2Player","smplrInstrument","soundfont","match","instrument","Sf2Sampler","sf2InstrumentToPreset","options","ctx","smplr","json","buffers","output","context","destination","sampler","error","note","pending","nextId","stopAll","stop","covered","i","now","end","handle","time","duration"],"sources":["../src/lib/sf2-player.ts"],"sourcesContent":["import type { Instrument, InstrumentZone, Preset, SoundFont2 } from 'soundfont2';\nimport type { AudioPlayer } from './sound-players';\n\nconst soundfonts = new Map<string, Promise<SoundFont2>>();\n\n/**\n * Each SF2 is downloaded and parsed once per URL. Every program, part and instance builds its player from the same\n * parsed soundfont (read-only here), so disposing one player never touches data another is using.\n */\nfunction parsedSoundfont(url: string): Promise<SoundFont2> {\n let cached = soundfonts.get(url);\n if (!cached) {\n cached = Promise.all([\n fetch(url).then(response => {\n if (!response.ok) throw new Error(`Could not load the soundfont (${response.status}).`);\n return response.arrayBuffer();\n }),\n import('soundfont2'),\n ]).then(([buffer, { default: soundfont2 }]) => new soundfont2.SoundFont2(new Uint8Array(buffer)));\n cached.catch(() => soundfonts.delete(url));\n soundfonts.set(url, cached);\n }\n return cached;\n}\n\n/** Name of the synthetic instrument that layers every instrument zone of the chosen preset. */\nconst LAYERED = '\\u0000pepperscore-preset';\n\n/**\n * smplr's Soundfont2 plays one SF2 instrument, but a GM preset often layers several (TimGM6mb's Standard kit is\n * Standard0 + Standard1, its strings stack six). This merges them into one instrument, clipped to each preset zone's key\n * range; smplr then sounds every region that matches a note. Sample names get a layer prefix because smplr keys its\n * buffers by name. Velocity ranges, pan and attenuation are not applied (smplr's SF2 conversion ignores them).\n */\nfunction layeredInstrument(preset: Preset): Instrument {\n const zones = preset.zones.flatMap((presetZone, layer) => (presetZone.instrument?.zones ?? []).flatMap((zone): InstrumentZone[] => {\n const lo = Math.max(zone.keyRange?.lo ?? 0, presetZone.keyRange?.lo ?? 0);\n const hi = Math.min(zone.keyRange?.hi ?? 127, presetZone.keyRange?.hi ?? 127);\n if (!zone.sample || lo > hi) return [];\n return [{ ...zone, keyRange: { lo, hi }, sample: { ...zone.sample, header: { ...zone.sample.header, name: `${layer}:${zone.sample.header.name}` } } }];\n }));\n return { header: { name: LAYERED, bagIndex: -1 }, zones };\n}\n\n/** The GM preset: bank 128 preset 0 (Standard kit) for drums, else bank 0 preset `program - 1`; or a preset named `preset`. */\nconst findPreset = (presets: Preset[], drums: boolean, program = 1, preset?: string) =>\n presets.find(item => preset ? item.header.name === preset : drums ? item.header.bank === 128 && item.header.preset === 0 : item.header.bank === 0 && item.header.preset === program - 1);\n\nexport type Sf2Options = { url: string; drums: boolean; program?: number; preset?: string; destination: AudioNode };\n\n/** A player for one preset of an SF2. `preset` names an SF2 preset, or failing that a single SF2 instrument. */\nexport async function loadSf2Player(context: AudioContext, { url, drums, program, preset, destination }: Sf2Options): Promise<AudioPlayer> {\n const [{ Instrument: smplrInstrument, sf2InstrumentToPreset }, soundfont] = await Promise.all([import('smplr'), parsedSoundfont(url)]);\n const match = findPreset(soundfont.presets, drums, program, preset);\n const instrument = match ? layeredInstrument(match) : preset ? soundfont.instruments.find(item => item.header.name === preset) : undefined;\n if (!instrument) throw new Error(preset ? `The soundfont has no preset or instrument named \"${preset}\".` : drums ? 'The soundfont has no standard drum kit.' : `The soundfont has no instrument for GM program ${program}.`);\n // smplr's own Soundfont2 fetches and parses the file per sampler; this plugin only turns the shared instrument into samples.\n const Sf2Sampler = smplrInstrument<{ instrument: Instrument }>((ctx, options, smplr) => {\n const { json, buffers } = sf2InstrumentToPreset(options.instrument, ctx);\n return smplr.loadInstrument(json, buffers);\n });\n const output = context.createGain();\n output.connect(destination);\n const sampler = Sf2Sampler(context, { instrument, destination: output });\n try { await sampler.ready; } catch (error) { sampler.dispose(); output.disconnect(); throw error; }\n // Stacked layers add up: scale by 1/sqrt(the most regions any one note sounds) so the preset sits near single-layer\n // ones. A global approximation: notes with fewer layers are scaled by the same amount.\n if (match) output.gain.value = 1 / Math.sqrt(Math.max(1, ...Array.from({ length: 128 }, (_, note) => instrument.zones.filter(zone => zone.keyRange!.lo <= note && note <= zone.keyRange!.hi).length)));\n // smplr queues a note more than its 200 ms lookahead ahead in its own scheduler, and its argument-less stop() only\n // stops sounding voices: queued notes would still play after a stop. So every start keeps its own stop handle (with a\n // unique stopId, since smplr's handle stops every voice sharing the id, which defaults to the pitch), and stop() calls\n // them all. Handles are dropped once their note has ended.\n const pending = new Map<() => void, number>();\n let nextId = 0;\n const release = 2; // seconds after a note's end before its handle is forgotten (covers smplr's release tail)\n const stopAll = () => { for (const stop of [...pending.keys()]) stop(); pending.clear(); sampler.stop(); };\n // Notes some zone sounds (a zone without a key range covers every key).\n const covered = new Set(instrument.zones.flatMap(({ keyRange: { lo, hi } = { lo: 0, hi: 127 } }) => Array.from({ length: Math.max(0, hi - lo + 1) }, (_, i) => lo + i)));\n return {\n start: ({ note, time, duration, velocity }) => {\n const now = context.currentTime;\n for (const [stop, end] of pending) if (end < now) pending.delete(stop);\n const handle = sampler.start({ note, time, duration, velocity, stopId: `n${nextId++}` });\n const stop = () => { pending.delete(stop); handle(); };\n pending.set(stop, Math.max(time, now) + duration + release);\n return stop;\n },\n stop: stopAll,\n dispose: () => { stopAll(); sampler.dispose(); output.disconnect(); },\n covers: note => covered.has(note),\n };\n}\n"],"mappings":"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"}