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.
- package/CHANGELOG.md +64 -0
- package/LICENSE +26 -12
- package/README.md +128 -9
- package/dist/THIRD_PARTY_NOTICES.txt +1222 -0
- package/dist/assets/{import.worker-D0XfuAHu.js → import.worker-nfqQ5M0Y.js} +2 -2
- package/dist/assets/verovio.worker-DGik-H4a.js +7 -0
- package/dist/audio.d.ts +24 -0
- package/dist/audio.js +198 -0
- package/dist/audio.js.map +1 -0
- package/dist/components/PepperScore.d.ts +111 -2
- package/dist/index.d.ts +3 -2
- package/dist/index.js +2650 -3196
- package/dist/index.js.map +1 -1
- package/dist/lib/chrome.d.ts +5 -2
- package/dist/lib/clock.d.ts +13 -0
- package/dist/lib/file-state.d.ts +17 -4
- package/dist/lib/header-controls.d.ts +7 -3
- package/dist/lib/hit-test.d.ts +18 -0
- package/dist/lib/instruments.d.ts +82 -0
- package/dist/lib/metronome.d.ts +33 -0
- package/dist/lib/notation/mei-tab.d.ts +51 -0
- package/dist/lib/notation/mei-view.d.ts +8 -0
- package/dist/lib/playback/clock-time.d.ts +20 -0
- package/dist/lib/playback/timeline.d.ts +4 -1
- package/dist/lib/prefs.d.ts +50 -0
- package/dist/lib/render.d.ts +36 -1
- package/dist/lib/score.d.ts +7 -0
- package/dist/lib/sound-players.d.ts +9 -1
- package/dist/lib/tab/fret-assign.d.ts +53 -0
- package/dist/lib/tab/tab-parts.d.ts +58 -0
- package/dist/lib/verovio-protocol.d.ts +15 -0
- package/dist/lib/verovio-runtime.d.ts +9 -1
- package/dist/runtime-files.d.ts +15 -0
- package/dist/{sf2-player-CRlOJDhD.js → sf2-player-oWC6yrUH.js} +28 -6
- package/dist/sf2-player-oWC6yrUH.js.map +1 -0
- package/dist/sound-players-DdLBBZaR.js +1172 -0
- package/dist/sound-players-DdLBBZaR.js.map +1 -0
- package/dist/style.css +1 -1
- package/dist/verovio/LICENSE +858 -0
- package/dist/verovio/verovio-module.mjs +0 -0
- package/dist/verovio/verovio.mjs +397 -0
- package/package.json +7 -1
- package/dist/assets/verovio.worker-DkHQmOsT.js +0 -19034
- package/dist/sf2-player-CRlOJDhD.js.map +0 -1
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## 0.3.0 - 2026-10-23
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
|
|
7
|
+
- `pepperscore/audio`: the reader's audio engine as a framework-free subpath (`dist/audio.js` + `audio.d.ts`), so other apps on the page share one `AudioContext`, one playback owner and the same sounds. Exports `getSharedAudio`, `resumeAudio`, `claimPlayback`, `releasePlayback`, `loadInstrument(program | name | 'drums', { assetBaseUrl, soundPack, sources, context, destination, notes })`, `loadDrumKit`, `createMetronome({ voice: 'woodblock' | 'drum-machine' | 'click', gain })` and their types (`Instrument`, `AudioPlayer`, `Metronome`, `MetronomeVoice`, ...). Instruments resolve exactly as the reader does (sources, the pack's FluidR3 sound, TimGM6mb.sf2; drums from Perfect Drums plus TimGM6mb's kit), all from the asset base. The main entry's exports are unchanged, and both entries import the same chunk for the shared state. See README, "Shared audio engine".
|
|
8
|
+
- Text add-on sizes. Note names, chord symbols (source and inferred), beat counts and count subdivisions each have a size: `xs`, `sm`, `md`, `rg`, `lg` or `xl` (0.7x to 1.45x). `rg` (regular) is the default and draws exactly the earlier size. Hosts set them with `settings.annotationSizes` (`{ noteNames?, chordSymbols?, beatCounts?, subdivisions? }`, type `PepperScoreAnnotationSizes`; `AnnotationSize` is exported too); the full reader's options drawer has a segmented size control beside each add-on's switch while it is on. Lane rows and offsets grow with the labels, and source chord symbols are sized by Verovio (MEI `<rend fontsize>`), so it spaces them. Sizes are saved in the global prefs and in each score's record (`namesSize`, `chordSymbolsSize`, `countsSize`, `subdivisionsSize`; optional on read, so earlier records read as `rg`).
|
|
9
|
+
- Guitar TAB (docs/specs/guitar-tab.md). `settings.tabView` (`'notation'` default, `'tab'`, `'both'`), `settings.tabTuning` (open-string MIDI, low to high, default `[40, 45, 50, 55, 59, 64]`) and `settings.capo` (0–12). A score's own TAB staff (MusicXML clef `TAB`) shows in the TAB and Both views. Other scores get TAB generated by a fingering solver for their guitar parts, or for the first pitched part's top staff. Transposed views regenerate the frets. In TAB view the notation staff is hidden and its chord symbols, tempo and directions move to the TAB staff, and the playhead highlights the TAB.
|
|
10
|
+
- `controls.tabToggle`: a Notation / TAB / Both switch in the header (in the More panel on a phone) and in the drawer. With the drawer on, the header copy is left out between 761 and 1260px, where the full header has no room. On in the `full` preset, and shown only for a score with a pitched part.
|
|
11
|
+
- `settings.bpm` (initial tempo, 30–240) and `settings.keyFlip` (enharmonic spelling of a transposed view), which were reader-only settings before. Like `semitones`, they apply on each score load unless a saved per-score record has its own values.
|
|
12
|
+
- Saved per-score records (still version 2) keep `tabView`, `tabTuning` and `capo`. Records without them read with the defaults.
|
|
13
|
+
- `onElementClick(info)` (`PepperScoreClickInfo`): every click on the engraving, with every chrome, reports the note (`noteId`, `sourceId`), measure (`measureIndex`), position across it (`fraction`), nearest staff (`staff`) and `clientX`/`clientY`. With a `clock`, `onSeek` still fires as before. While it is set, notes and measures show a pointer cursor.
|
|
14
|
+
- `highlights` prop (`PepperScoreHighlight[]`): host classes on notes and measures, re-applied after every draw. New styles `ps-cursor`, `ps-selection` and `ps-cursor-measure` (`--ps-cursor`, `--ps-selection`).
|
|
15
|
+
- Handle: `scrollToMeasure(index)` (draws the measure's system if needed and scrolls it into view) and `seekToMeasure(index)` (seeks to the measure's first performed occurrence).
|
|
16
|
+
- Type exports `PepperScoreClickInfo` and `PepperScoreHighlight`.
|
|
17
|
+
- `PepperScoreClickInfo.nearestNoteId` / `nearestSourceId`: for a click that hits no note, the note or rest on the clicked staff in that measure whose notehead is nearest the click horizontally. A click anywhere in a note's drawn box (notehead, stem, flag, accidental, dots and the gaps between them) now reports that note as `noteId` (before, only the ink under the pointer did); where boxes overlap the nearest notehead wins, and noteheads stacked at one x (two voices, a chord) are split by the nearer in y. With a `clock`, such a click therefore seeks to that note (`onSeek`) where it used to seek to the start of the measure.
|
|
18
|
+
- Handle: `scrollIntoView(target, options)` brings notes (`ids`) or a measure (`measure`) into view with the least scroll. Nothing moves when the target is already inside `margin`. `lead` (0..1 of the view) adds room ahead in the direction of travel, and `bounds` (client coordinates) keeps the target clear of a host's overlays. `interactedSince` skips the scroll when the reader has scrolled, wheeled, touched or pressed on the score since that time (a momentum or smooth scroll that carries on from their wheel or flick counts until it stops). It does nothing while playback follows the playhead. Vertically it keeps the target's whole system in view. It draws the measure's system if needed, scrolls smoothly unless the user prefers reduced motion, and resolves `true` when it scrolled. New types: `PepperScoreScrollTarget`, `PepperScoreScrollOptions` and `PepperScoreInsets`.
|
|
19
|
+
- `onRender()` prop: called after every draw lands (the first paint, a re-layout or an in-place swap), once `highlights` are applied.
|
|
20
|
+
- `verovioBaseUrl` prop: engrave with another interface-compatible Verovio build (a directory laid out like the `verovio` package's `dist/`).
|
|
21
|
+
- `paintTransition` prop (`'reveal'` default, or `'none'`): with `'none'`, a new `musicXml` or `abc` for a score already on screen is swapped in place, with no fade, loading overlay or scroll reset.
|
|
22
|
+
- `dist/THIRD_PARTY_NOTICES.txt` (runtime dependencies, Verovio, the OFL fonts), linked as "Licences" in the full reader's options drawer.
|
|
23
|
+
|
|
24
|
+
### Changed
|
|
25
|
+
|
|
26
|
+
- The options drawer uses the reader's width: its cards (Reading, a new Annotations card holding the add-on switches and sizes, Pitch and ABC notation) sit in as many ~272px columns as fit, so on a desktop they sit side by side and the drawer is short; on a phone they stack in one column.
|
|
27
|
+
- A MusicXML TAB staff that doubles a notation part is no longer a part of its own: it is not played (playback had every note twice), has no annotation lanes, and is not listed in the part controls, mixer or `onReady` parts. A TAB-only score still plays its TAB staff.
|
|
28
|
+
- A highlighted measure (`highlights` with `measures`, any class) now shows a dashed box: a `.ps-measure-highlight` `<rect>` (in a `.ps-highlight-layer` overlay, so measure bounds are unchanged) with a 2px non-scaling stroke in `--ps-cursor`. The old `ps-cursor-measure` CSS outline was drawn in viewBox units and was nearly invisible.
|
|
29
|
+
- New highlight class `ps-transposed` (`--ps-transposed`, default `#d9741c`) for notes.
|
|
30
|
+
- `ps-selection` defaults to ink blue `#2f6a9a` (was green `#1f9d55`), so it does not clash with `ps-transposed`.
|
|
31
|
+
- A new score source draws immediately; the redraw fade only applies to setting changes.
|
|
32
|
+
- During playback the next two systems are drawn ahead of the playhead (`LOOKAHEAD_SYSTEMS = 2`), so a system is ready before its bar starts. Before, a system was requested only when the playhead entered it, and on a large score at a fast tempo the bar could end before the draw finished.
|
|
33
|
+
- With no `highlights` set and none applied, the highlight effect does no DOM queries and does not subscribe to redraws.
|
|
34
|
+
- Verovio is no longer bundled into the worker chunk. Its own unmodified files ship in `dist/verovio/` (with a licence comment and a `LICENSE`) and the worker imports them at runtime; host bundlers copy them like the worker chunks. Serve `.mjs` as JavaScript. The LICENSE's relink offer now points at upstream Verovio 6.3.0 source and `verovioBaseUrl`.
|
|
35
|
+
|
|
36
|
+
### Fixed
|
|
37
|
+
|
|
38
|
+
- `pepperscore/audio`: the metronome no longer leaks drum kits (overlapping `setVoice` calls share one load, a kit or TimGM6mb fallback that arrives after `dispose()` is released) and retries after a failed drum download. `createMetronome` resolves at once and plays the synthesized sound until the kit arrives (new `metronome.ready`); the default `'woodblock'` voice's ~6 MB download is documented. `Instrument.plan` is now the read-only `{ kind, sourceId }` (`InstrumentPlan`) and the internal `AudioPlayer.fallback` is no longer in the declarations.
|
|
39
|
+
- Chord-symbol sizing no longer scans to the end of the document for every symbol. The size buttons have accessible names ("Extra small" ... "Extra large"). Regular size draws an SVG with no `is-subdivision` class.
|
|
40
|
+
- Stopping playback now cancels SF2 (TimGM6mb and `kind: 'sf2'` source) notes already scheduled more than 200 ms ahead. smplr queues those in its own scheduler, and its argument-less `stop()` only stops sounding voices, so a queued note could still play after Stop. Each SF2 note now keeps its own stop handle (with a unique id: smplr's handle stopped every voice of the same pitch) and `stop()` calls them all.
|
|
41
|
+
- An ending (volta) that starts a line is drawn whole on that line. The page break went inside the `<ending>`, so Verovio drew a stub on the line before and labelled the bracket "(1)" as a continuation.
|
|
42
|
+
|
|
43
|
+
### Known follow-ups
|
|
44
|
+
|
|
45
|
+
- the default `loadInstrument` note range 21-108 decodes up to 88 buffers up front; `instruments.ts` and the reader assemble sounds separately; a pre-0.3 per-score record resets the four sizes to `rg`; the shared audio chunk is named after sound-players.
|
|
46
|
+
|
|
47
|
+
## 0.2.0
|
|
48
|
+
|
|
49
|
+
### Added
|
|
50
|
+
|
|
51
|
+
- `clock` prop (`ExternalClock`: `{ now(): number; playing: boolean; subscribe(listener): unsubscribe }`). A host clock drives the playhead, note highlight and Vertical/Endless follow. Its seconds map to the score at the score's own tempo map (tempo changes included, pickup bar starting at 0). The BPM setting is ignored. With a clock the reader plays no sound and hides its own play, stop and BPM controls. The playhead shows for every `chrome` preset, `"none"` included. A negative time (count-in) shows no playhead. Transposing keeps the playhead at `clock.now()`.
|
|
52
|
+
- `onSeek(seconds)`: with a `clock`, a click on a note (nearest performed occurrence) or anywhere in a measure (its start) reports the time instead of seeking.
|
|
53
|
+
- `audio` prop (default `true`). `false` never loads or schedules sound, and self-driven playback runs silently.
|
|
54
|
+
- `onTimeUpdate(seconds)`: about 10 Hz during self-driven playback.
|
|
55
|
+
- Handle: `seek(seconds)` and `currentTime()`. With a `clock`, `seek` reports through `onSeek` and `currentTime` returns `clock.now()`.
|
|
56
|
+
- Type exports `ExternalClock` and `ClockEvent`.
|
|
57
|
+
|
|
58
|
+
### Changed
|
|
59
|
+
|
|
60
|
+
- `handle.play()` and `handle.stop()` do nothing while a `clock` is set.
|
|
61
|
+
|
|
62
|
+
## 0.1.1
|
|
63
|
+
|
|
64
|
+
- Initial public release.
|
package/LICENSE
CHANGED
|
@@ -21,20 +21,34 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
|
21
21
|
SOFTWARE.
|
|
22
22
|
|
|
23
23
|
---
|
|
24
|
-
The MIT License above covers the pepperscore component code
|
|
25
|
-
Verovio
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
24
|
+
The MIT License above covers the pepperscore component code, including its
|
|
25
|
+
Web Worker chunks in dist/assets/. It does not cover Verovio or the other
|
|
26
|
+
third-party components listed in dist/THIRD_PARTY_NOTICES.txt.
|
|
27
|
+
|
|
28
|
+
Verovio is not compiled or bundled into pepperscore's code. pepperscore ships
|
|
29
|
+
two unmodified files of the Verovio 6.3.0 music engraving library
|
|
30
|
+
(https://www.verovio.org/, Copyright (c) Laurent Pugin and the Verovio
|
|
31
|
+
contributors), taken from the npm package verovio@6.3.0, with only a licence
|
|
32
|
+
comment added at the top of each:
|
|
33
|
+
|
|
34
|
+
dist/verovio/verovio-module.mjs the WebAssembly module and its glue code
|
|
35
|
+
dist/verovio/verovio.mjs the JavaScript toolkit
|
|
36
|
+
dist/verovio/LICENSE their licence notice
|
|
37
|
+
|
|
38
|
+
pepperscore's worker loads them at runtime. Verovio is licensed under the GNU
|
|
39
|
+
Lesser General Public License, version 3 or (at your option) any later version
|
|
32
40
|
(LGPL-3.0-or-later). Copies of the licenses are included in this package:
|
|
33
41
|
|
|
34
42
|
LICENSES/LGPL-3.0.txt GNU Lesser General Public License, version 3
|
|
35
43
|
LICENSES/GPL-3.0.txt GNU General Public License, version 3
|
|
36
44
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
45
|
+
Corresponding source for Verovio 6.3.0:
|
|
46
|
+
https://github.com/rism-digital/verovio/tree/version-6.3.0
|
|
47
|
+
(git tag version-6.3.0; also published on npm as verovio@6.3.0)
|
|
48
|
+
|
|
49
|
+
Replacing Verovio: you may use a modified or different, interface-compatible
|
|
50
|
+
build of Verovio in place of the shipped files without rebuilding pepperscore.
|
|
51
|
+
Either pass the URL of a directory holding your verovio-module.mjs and
|
|
52
|
+
verovio.mjs (laid out like the verovio npm package's dist/ directory) as the
|
|
53
|
+
`verovioBaseUrl` prop of PepperScore, or replace the two files in
|
|
54
|
+
dist/verovio/ (or the copies your bundler emitted) with your own.
|
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# pepperscore
|
|
2
2
|
|
|
3
|
-
An embeddable, browser-only music score reader for React. It opens MusicXML (`.musicxml`, `.xml`), compressed MXL, MIDI, MEI and ABC. [Verovio](https://www.verovio.org/) 6.3 engraves the score in a Web Worker,
|
|
3
|
+
An embeddable, browser-only music score reader for React. It opens MusicXML (`.musicxml`, `.xml`), compressed MXL, MIDI, MEI and ABC. [Verovio](https://www.verovio.org/) 6.3 engraves the score in a Web Worker, so the page never blocks on layout. Verovio ships with the package as its own, replaceable file (see [Verovio](#verovio)). Playback follows one performance timeline that handles ties, dynamics, repeats, tempo changes and percussion, with a playhead, note highlighting and follow-scrolling. You can use it as a full reader page with a header, drawer and mixer, or as small inline snippets, several to a page.
|
|
4
4
|
|
|
5
5
|
## Install
|
|
6
6
|
|
|
@@ -183,7 +183,7 @@ export default defineConfig({
|
|
|
183
183
|
|
|
184
184
|
- Production builds (`astro build`) need nothing.
|
|
185
185
|
- A plain Vite + React app needs nothing either, because Vite's dependency scan finds these from the app's entry. The same `include` is harmless there.
|
|
186
|
-
- Do **not** add `optimizeDeps.exclude: ['pepperscore']`. It is not needed (the worker URLs survive pre-bundling with Vite 8), and with pnpm it breaks `react/jsx-runtime`.
|
|
186
|
+
- Do **not** add `optimizeDeps.exclude: ['pepperscore']`. It is not needed (the worker and Verovio URLs survive pre-bundling with Vite 8), and with pnpm it breaks `react/jsx-runtime`.
|
|
187
187
|
- Your layout should declare `<meta charset="utf-8">` (Astro's starter layouts do). Without it, the server-rendered frame of a `client:load`/`client:visible` island is decoded as Latin-1 and React reports a hydration mismatch.
|
|
188
188
|
|
|
189
189
|
## Props
|
|
@@ -195,21 +195,31 @@ export default defineConfig({
|
|
|
195
195
|
| `scoreUrl` | `string` | | A score to download. Relative URLs resolve against the page. Cross-origin URLs need CORS. |
|
|
196
196
|
| `abc` | `string` | | ABC notation. |
|
|
197
197
|
| `chrome` | `'full' \| 'minimal' \| 'none'` | `'full'` | See the chrome table below. |
|
|
198
|
-
| `controls` | `{ transport?, viewToggle?, openFile?, mixer?, drawer?, zoom? }` (booleans) | from `chrome` | Per-control overrides of the preset, see below. |
|
|
198
|
+
| `controls` | `{ transport?, viewToggle?, openFile?, mixer?, drawer?, zoom?, tabToggle? }` (booleans) | from `chrome` | Per-control overrides of the preset, see below. |
|
|
199
199
|
| `view` | `'vertical' \| 'endless'` | saved or `'vertical'` | Vertical scrolls down, Endless scrolls sideways. Changing the prop re-applies it. |
|
|
200
200
|
| `height` | `string` | natural | `"fill"`: the parent's height. Any CSS length (`"420px"`, `"60vh"`, `"100dvh"`) fixes it. Omitted: `full` fills its parent with a 480px floor, and `minimal`/`none` take the height of their score. |
|
|
201
201
|
| `className` | `string` | | Extra classes on the `.pepperscore` root. |
|
|
202
202
|
| `ariaLabel` | `string` | `"Score: <title>"`, or `"Score reader"` | Accessible name of the reader's landmark region. By default it is `Score: ` and the loaded score's title, or `Score reader` while there is no titled score. Give each snippet on a page a distinct name when titles repeat. |
|
|
203
|
-
| `settings` | `PepperScoreSettings` | | **Initial** reader settings: `noteNames`, `chordSymbols`, `inferChords`, `beatCounts`, `subdivisions` (booleans); `musicFont` (`'Bravura' \| 'Petaluma'`); `staveFont` (`'Sans' \| 'Serif' \| 'Mono'`); `barsPerLine` (`'auto' \| 1–8`, Vertical view; without a `zoom`, an explicit count also sets the on-load size: that many bars fill the width, see below); `zoom` (stave scale, `null` = device default; with a `zoom`, `barsPerLine` only sets the line breaks); `semitones` (−12…12); `concert` (concert vs written pitch); `overlap` (0–3 lines kept on a page turn). Changing a key in the prop re-applies that key. Drawer edits still work in between. With `persist` on, **a saved per-score record overrides `settings` and `view`** when that score is opened again. The record holds the settings, view, zoom, transposition, part sounds and hidden staves the reader stored for that exact file. |
|
|
203
|
+
| `settings` | `PepperScoreSettings` | | **Initial** reader settings: `noteNames`, `chordSymbols`, `inferChords`, `beatCounts`, `subdivisions` (booleans); `musicFont` (`'Bravura' \| 'Petaluma'`); `staveFont` (`'Sans' \| 'Serif' \| 'Mono'`); `barsPerLine` (`'auto' \| 1–8`, Vertical view; without a `zoom`, an explicit count also sets the on-load size: that many bars fill the width, see below); `zoom` (stave scale, `null` = device default; with a `zoom`, `barsPerLine` only sets the line breaks); `semitones` (−12…12); `concert` (concert vs written pitch); `keyFlip` (spell a transposed view in the enharmonic key, for example Gb major for F# major); `bpm` (initial tempo, 30–240; the score's own tempo otherwise); `overlap` (0–3 lines kept on a page turn); `tabView`, `tabTuning`, `capo` (guitar TAB, see [Guitar TAB](#guitar-tab)); `annotationSizes` (text size per add-on, see below). Changing a key in the prop re-applies that key. Drawer edits still work in between. With `persist` on, **a saved per-score record overrides `settings` and `view`** when that score is opened again. The record holds the settings, view, zoom, transposition, part sounds and hidden staves the reader stored for that exact file. |
|
|
204
204
|
| `persist` | `string \| false` | `'pepperscore'` for `full`, `false` for `minimal`/`none` | Storage namespace, see below. `false` reads and writes nothing. |
|
|
205
205
|
| `assetBaseUrl` | `string` | jsDelivr copy of `pepperscore-sounds-<soundPack>` | Root of a sounds-package layout. Relative values resolve against the page, and a missing trailing slash is added. |
|
|
206
|
+
| `verovioBaseUrl` | `string` | the copy in `dist/verovio/` | A directory with an interface-compatible Verovio build to engrave with, laid out like the `verovio` npm package's `dist/` (`verovio-module.mjs`, `verovio.mjs`), e.g. `https://cdn.jsdelivr.net/npm/verovio@6.3.0/dist/`. Shared by every reader on the page: set it before the first score engraves. See [Verovio](#verovio). |
|
|
206
207
|
| `soundPack` | `'core' \| 'complete'` | `'core'` | Picks the default `assetBaseUrl`. With your own `assetBaseUrl`, the `pack.json` found there decides which FluidR3 sounds exist. |
|
|
207
208
|
| `soundSources` | `SoundSource[]` | `[]` | Your own sounds for specific programs or the drum kit, see below. |
|
|
208
209
|
| `onReady` | `(info: { title, composer?, parts: { id, name }[], measures }) => void` | | Once per loaded score, after it is drawn. |
|
|
209
210
|
| `onPlay` / `onStop` | `() => void` | | Playback starts or stops, including when another instance takes over playback. |
|
|
210
211
|
| `onError` | `(error: Error) => void` | | Each error the reader shows: an unreadable file, a failed download or a renderer failure. |
|
|
212
|
+
| `clock` | `ExternalClock` | | A host clock that drives the playhead, see "Driving the playhead from your own clock". |
|
|
213
|
+
| `audio` | `boolean` | `true` | `false` never loads or schedules sound. Self-driven playback then runs silently on wall-clock time. |
|
|
214
|
+
| `onSeek` | `(seconds: number) => void` | | With a `clock`: a click on a note or measure (and `ref.seek`) reports its time here. The reader does not move. |
|
|
215
|
+
| `onTimeUpdate` | `(seconds: number) => void` | | About 10 times a second during self-driven playback. |
|
|
216
|
+
| `onElementClick` | `(info: PepperScoreClickInfo) => void` | | Any click on the engraving, see "Host hooks". |
|
|
217
|
+
| `highlights` | `PepperScoreHighlight[]` | | Classes on notes and measures, see "Host hooks". |
|
|
211
218
|
| `autoFocus` | `boolean` | `false` | Focus the reader on mount so the page-turn keys work before any click. Keyboard shortcuts belong to the focused reader only. Each reader is one Tab stop, and keyboard focus shows a focus ring on it (clicks do not). |
|
|
212
|
-
| `
|
|
219
|
+
| `paintTransition` | `'reveal' \| 'none'` | `'reveal'` | How a new `musicXml` or `abc` replaces a score already on screen. `'reveal'` fades it out, shows the loading overlay, scrolls to the top and fades the new score in. `'none'` keeps the current engraving and scroll position until the new one replaces it in place, with no fade or overlay (for a host that edits the score it shows). The first paint, other sources and setting changes always reveal. |
|
|
220
|
+
| `ref` | `Ref<PepperScoreHandle>` | | `{ play(): Promise<void>; stop(): void; transpose(semitones: number): void; seek(seconds: number): void; currentTime(): number; scrollToMeasure(index: number): Promise<void>; seekToMeasure(index: number): void }`. `play` does nothing while playing, before a score has loaded, with `chrome="none"`, or with a `clock`. `stop` does nothing with a `clock`. `transpose` sets the **absolute** transposition (clamped to −12…12), the same as `settings.semitones`. It does not add to the current value. `seek` moves the playhead (and keeps playing from there when playing). With a `clock` it calls `onSeek` instead. `currentTime` returns the playhead seconds, or `clock.now()` with a `clock`. |
|
|
221
|
+
|
|
222
|
+
**Text add-on sizes with `annotationSizes`.** Each text add-on has its own size, in six steps: `'xs' | 'sm' | 'md' | 'rg' | 'lg' | 'xl'` (type `AnnotationSize`). `'rg'` (regular) is the default and draws the size earlier versions drew; the steps scale it by 0.7, 0.8, 0.9, 1, 1.2 and 1.45. Keys: `noteNames`, `chordSymbols` (source chord symbols and inferred ones), `beatCounts` (on-beat counts) and `subdivisions` (the e/&/a counts between beats). Give only the add-ons you want to change, for example `settings={{ noteNames: true, annotationSizes: { noteNames: 'lg' } }}`; unknown sizes are ignored. The rows under the staff grow with their labels, and Verovio sizes and spaces source chord symbols itself; the stave size and bar layout do not change. In the full reader each add-on's size is also a segmented control beside its switch in the options drawer (shown while that add-on is on), saved with the other reader settings and in each score's record.
|
|
213
223
|
|
|
214
224
|
**Sizing a snippet with `barsPerLine`.** In Vertical view, `settings={{ barsPerLine: N }}` without a `zoom` picks the stave size at which exactly N bars fill the reader's width, so N is the simplest way to set how large an embed draws: on the same width, `barsPerLine: 2` draws larger staves than `barsPerLine: 4`. The size is bounded (at most stave scale 2.5, at least 0.2; a single bar on a very wide reader is stretched to fill rather than drawn huge), and lines always hold N bars. A score shorter than one line takes its share of the width, centred. `'auto'` keeps the device default (four bars across on desktop). Endless view ignores `barsPerLine`.
|
|
215
225
|
|
|
@@ -217,7 +227,41 @@ When more than one source prop is set, the first present wins, in this order: `m
|
|
|
217
227
|
|
|
218
228
|
The component never reads `window.location`. To open `?score=…` links, read the query string yourself and pass it as `scoreUrl`.
|
|
219
229
|
|
|
220
|
-
`Reader` and `ReaderProps` are still exported as deprecated aliases of `PepperScore` and `PepperScoreProps` for one release. The package also exports the types `PepperScoreProps`, `PepperScoreHandle`, `PepperScoreReadyInfo`, `PepperScoreSettings`, `PepperScoreControls`, `ChromePreset`, `SoundSource` and `
|
|
230
|
+
`Reader` and `ReaderProps` are still exported as deprecated aliases of `PepperScore` and `PepperScoreProps` for one release. The package also exports the types `PepperScoreProps`, `PepperScoreHandle`, `PepperScoreReadyInfo`, `PepperScoreSettings`, `PepperScoreControls`, `ChromePreset`, `SoundSource`, `SoundPackName`, `ExternalClock`, `ClockEvent`, `PepperScoreClickInfo` and `PepperScoreHighlight`, plus `SOUNDS_VERSION` and `defaultAssetBaseUrl(pack)`.
|
|
231
|
+
|
|
232
|
+
### Driving the playhead from your own clock
|
|
233
|
+
|
|
234
|
+
Pass `clock` when something else owns time, such as your own audio engine, a video, or a practice app's transport. The reader then only shows where that clock is:
|
|
235
|
+
|
|
236
|
+
```tsx
|
|
237
|
+
import { PepperScore, type ExternalClock } from 'pepperscore';
|
|
238
|
+
|
|
239
|
+
// Adapt your transport once (keep the object stable: a new object re-subscribes).
|
|
240
|
+
const clock: ExternalClock = {
|
|
241
|
+
now: () => transport.seconds, // seconds from score beat 0; negative during a count-in
|
|
242
|
+
get playing() { return transport.playing; },
|
|
243
|
+
subscribe: listener => transport.on('change', event => listener({ type: event.type, time: transport.seconds })),
|
|
244
|
+
};
|
|
245
|
+
|
|
246
|
+
<PepperScore chrome="none" audio={false} musicXml={xml} clock={clock} onSeek={seconds => transport.seek(seconds)} />
|
|
247
|
+
```
|
|
248
|
+
|
|
249
|
+
- **Time mapping.** Seconds count from score beat 0 (the first beat of a pickup bar, if there is one). They follow the score's own tempo map, so every `<sound tempo>` / metronome change is included. The BPM setting is ignored. Repeats and jumps play in performed order. A fermata adds half its note's length.
|
|
250
|
+
- **Events.** `subscribe` should call the listener on `play`, `pause`, `seek` and `rate`. While `playing` is true, the reader reads `now()` every animation frame. Otherwise it reads it once per event.
|
|
251
|
+
- **No sound, no transport.** With a clock the reader never loads sound, whatever `audio` says. It also hides its own play, stop and BPM controls. The playhead, note highlight and Vertical/Endless follow work for every `chrome`, including `"none"`.
|
|
252
|
+
- **Seeking.** A click on a note reports that note's time (its performed occurrence nearest the clock), and a click elsewhere in a measure reports the measure's start. Both go through `onSeek`, and so does `ref.seek(seconds)`. Seek your clock there. The reader follows on the next `seek` event.
|
|
253
|
+
- `onPlay`/`onStop` are not called for clock play/pause.
|
|
254
|
+
- **Count-in.** A negative clock time shows no playhead and no highlighted note.
|
|
255
|
+
- Transposing while a `clock` is set does not move the playhead; it stays at `clock.now()`.
|
|
256
|
+
- `validation/fixtures/clock-contract.musicxml` with `clock-contract.expected.json` (in the repository) pins the onset seconds within 1 ms (a pickup bar, and tempo 120 → 60 → 180 mid-bar). Use them to check your own tempo map against the reader's.
|
|
257
|
+
|
|
258
|
+
### Host hooks
|
|
259
|
+
|
|
260
|
+
For hosts that build on the reader (selection, cursors, navigation):
|
|
261
|
+
|
|
262
|
+
- **`onElementClick(info)`** fires for any click on the engraving, with every `chrome` and with or without a `clock`. `info` is `{ noteId?, sourceId?, measureIndex?, fraction?, staff?, nearestNoteId?, nearestSourceId?, clientX, clientY }`: the note hit (and its MusicXML `id`, when known), the zero-based measure, the 0–1 position across that measure, and the 1-based nearest staff. A click anywhere in a note's drawn box (its notehead, stem, flag, accidental or dots, or the gaps between them) hits that note (overlapping boxes go to the nearest notehead; noteheads stacked at one x, as in two voices, to the nearer in y). When no note was hit, `nearestNoteId` and `nearestSourceId` name the note or rest on that staff in that measure whose notehead is nearest the click horizontally (stacked noteheads again split by y). Engraved spacing is not proportional to time, so this is a better guide than `fraction` to the event a click in staff space means. With a handler set, notes and measures show a pointer cursor.
|
|
263
|
+
- **`highlights`**: `{ ids?, measures?, className }[]`. Each entry adds `className` to those notes (note or MusicXML ids) and measures (zero-based), re-applied after every draw. Built-in classes: `ps-cursor`, `ps-selection` and `ps-transposed` (notes) and `ps-cursor-measure` (measures), coloured by `--ps-cursor`, `--ps-selection` (ink blue `#2f6a9a`) and `--ps-transposed` (orange `#d9741c`). Every highlighted measure, whatever its class, also gets a dashed box: a `<rect class="ps-measure-highlight" data-ps-class="...">` in a `<g class="ps-highlight-layer">` overlay beside the measures (so the measure's own bounds are unchanged), a few pixels outside the measure, with a 2px screen-space stroke (`vector-effect: non-scaling-stroke`) in `--ps-cursor`. Restyle it per class with `.ps-measure-highlight[data-ps-class~="my-class"] { stroke: ... }`. The box is removed when the highlight is cleared and redrawn with the measure.
|
|
264
|
+
- **`ref.scrollToMeasure(index)`** draws the measure's system if needed and scrolls it into view. **`ref.seekToMeasure(index)`** seeks to the measure's start, like `ref.seek` (with a `clock`, through `onSeek`).
|
|
221
265
|
|
|
222
266
|
### Chrome presets and `controls`
|
|
223
267
|
|
|
@@ -225,7 +269,7 @@ The component never reads `window.location`. To open `?score=…` links, read th
|
|
|
225
269
|
| --- | --- | --- | --- |
|
|
226
270
|
| Header transport (stop, play, BPM, view toggle, open, mixer) | yes | no | no |
|
|
227
271
|
| Compact play/stop button (corner overlay) | no | yes | no |
|
|
228
|
-
| Playhead, note highlight, follow | yes | yes | no |
|
|
272
|
+
| Playhead, note highlight, follow | yes | yes | no (yes with a `clock`) |
|
|
229
273
|
| Bottom drawer and handle | yes | no | no |
|
|
230
274
|
| Part icons, instrument picker, stave eyes | yes | no | no |
|
|
231
275
|
| Page arrows and zoom | yes | no | no |
|
|
@@ -242,8 +286,25 @@ The component never reads `window.location`. To open `?score=…` links, read th
|
|
|
242
286
|
| `mixer` | The track mixer, plus the part icons, instrument picker and stave eyes. |
|
|
243
287
|
| `zoom` | The stave-scale slider, pinch zoom and the page arrows row. |
|
|
244
288
|
| `drawer` | The bottom drawer and its handle. |
|
|
289
|
+
| `tabToggle` | The Notation / TAB / Both switch, in the header (in the More panel on a phone) and in the drawer. With the drawer on, the header copy is left out between 761 and 1260px, where the full header has no room for it. It shows only for a score with a pitched part. |
|
|
290
|
+
|
|
291
|
+
The header appears when any of `transport`, `viewToggle`, `openFile`, `mixer`, `zoom` or `tabToggle` is on. For example, `<PepperScore chrome="minimal" controls={{ viewToggle: true }} … />` keeps the compact play button and adds a small header with only the view toggle.
|
|
292
|
+
|
|
293
|
+
### Guitar TAB
|
|
245
294
|
|
|
246
|
-
|
|
295
|
+
`settings.tabView` sets which staves guitar parts show: `'notation'` (default), `'tab'` or `'both'`. Readers switch with `controls.tabToggle` (on in `full`).
|
|
296
|
+
|
|
297
|
+
| Setting | Type | Default | Meaning |
|
|
298
|
+
| --- | --- | --- | --- |
|
|
299
|
+
| `tabView` | `'notation' \| 'tab' \| 'both'` | `'notation'` | Notation only, TAB only (the notation staff is hidden and its chord symbols, tempo and directions move onto the TAB staff), or both. |
|
|
300
|
+
| `tabTuning` | `number[]` | `[40, 45, 50, 55, 59, 64]` | Open-string MIDI pitches, low to high, of generated TAB (EADGBE). |
|
|
301
|
+
| `capo` | `0`–`12` | `0` | Capo fret of generated TAB. Frets are relative to it. |
|
|
302
|
+
|
|
303
|
+
- **A score with a TAB staff** (MusicXML `<clef><sign>TAB</sign>` with `<staff-details>`, as MuseScore and Guitar Pro export) shows that staff in the TAB and Both views. The reader treats it as a view of its notation part: it is played once, has no note-name or count lanes, and the part controls and mixer list the instrument once.
|
|
304
|
+
- **Any other score** gets TAB generated from its pitches: for every guitar part (General MIDI programs 25–32, the guitar sound, or "guitar" in the part name), or else for the first pitched part's top staff. A fingering solver picks strings and frets for the whole passage (open strings, few position shifts, playable stretches). Notes out of range are moved by an octave. Chord notes that cannot all be fretted lose doubled notes first, then inner voices. MusicXML `<technical><string>`/`<fret>` on a note is kept as a fixed fingering.
|
|
305
|
+
- **Transposed views** regenerate TAB from the transposed pitches. An imported TAB staff is hidden then, because its frets no longer match.
|
|
306
|
+
- Tied continuations keep their rhythm on the TAB staff but show no fret.
|
|
307
|
+
- **Baked fingerings.** The repository's `npm run tab:bake` (`scripts/tab-bake.mjs`, see the root README) writes the solver's string/fret onto a score's notes ahead of time, and can add a TAB staff. The reader keeps those fingerings as they are.
|
|
247
308
|
|
|
248
309
|
### `persist`
|
|
249
310
|
|
|
@@ -291,6 +352,14 @@ cp -rL node_modules/pepperscore-sounds-core public/pepperscore-sounds # -L: pn
|
|
|
291
352
|
|
|
292
353
|
Use the sounds package version that matches `SOUNDS_VERSION` (exported by this package). A base on another origin must send CORS headers (`Access-Control-Allow-Origin`), because fonts and sounds are fetched.
|
|
293
354
|
|
|
355
|
+
### Verovio
|
|
356
|
+
|
|
357
|
+
Verovio is not bundled into pepperscore's code. The package ships upstream's own files from `verovio@6.3.0`, unmodified apart from a licence comment, in `dist/verovio/` (`verovio-module.mjs`, about 7.3 MB with its WebAssembly inlined, and `verovio.mjs`, the toolkit), and the Verovio worker imports them at runtime when the first score engraves. Host bundlers (Vite, Astro) copy them as assets like the worker chunks, so nothing needs configuring.
|
|
358
|
+
|
|
359
|
+
- Serve `.mjs` files as JavaScript (`text/javascript`). Most hosts do; a server that sends them as `application/octet-stream` makes the import fail, and the reader shows a "Could not load Verovio from …" error.
|
|
360
|
+
- To engrave with another build (a modified Verovio, a newer compatible version, or a CDN copy), pass `verovioBaseUrl`, a directory holding both files. A cross-origin directory must send CORS headers. Or replace the files your bundler emitted.
|
|
361
|
+
- The interface pepperscore expects: `verovio-module.mjs` default-exports an async factory returning the Emscripten module (`createVerovioModule`), and `verovio.mjs` exports `VerovioToolkit` and `enableLogToBuffer`, as in `verovio@6.3.0`.
|
|
362
|
+
|
|
294
363
|
**If the assets cannot be loaded** (a wrong `assetBaseUrl`, or nothing published there yet), the reader keeps working: the score still engraves (Verovio's own glyphs; text glyphs fall back), playback runs silently with the usual "playing silently" notice and no error, and the console shows one warning naming the `assetBaseUrl` that failed. A failing `soundSources` entry of your own still reports through `onError`.
|
|
295
364
|
|
|
296
365
|
### Testing before the sounds packages are published
|
|
@@ -328,6 +397,54 @@ Drums use the Perfect Drums note files unless a source claims them. `soundSource
|
|
|
328
397
|
/>
|
|
329
398
|
```
|
|
330
399
|
|
|
400
|
+
## Shared audio engine (`pepperscore/audio`)
|
|
401
|
+
|
|
402
|
+
The reader's audio engine is also a small, framework-free subpath (no React), so other music apps on the same page (a piano visualiser, a practice tool) share one `AudioContext`, one "who is playing" slot and the same sounds as every `PepperScore`:
|
|
403
|
+
|
|
404
|
+
```ts
|
|
405
|
+
import { claimPlayback, createMetronome, getSharedAudio, loadInstrument, releasePlayback, resumeAudio } from 'pepperscore/audio';
|
|
406
|
+
|
|
407
|
+
const shared = getSharedAudio(); // { context, master } or null without Web Audio
|
|
408
|
+
await resumeAudio(shared!.context); // from a user gesture; resolves false if audio cannot start
|
|
409
|
+
|
|
410
|
+
const piano = await loadInstrument(1, { assetBaseUrl: '/pepperscore-sounds/' }); // GM program, FluidR3 name or 'drums'
|
|
411
|
+
const metronome = await createMetronome({ voice: 'woodblock', assetBaseUrl: '/pepperscore-sounds/' });
|
|
412
|
+
|
|
413
|
+
const owner = { stop: () => { piano.stop(); metronome.stop(); } };
|
|
414
|
+
claimPlayback(owner); // stops any PepperScore (or other owner) that is playing
|
|
415
|
+
const t = shared!.context.currentTime + .1;
|
|
416
|
+
for (let beat = 0; beat < 4; beat++) metronome.tick({ time: t + beat * .5, beat }); // beat 0 is accented
|
|
417
|
+
const stopNote = piano.start({ note: 60, time: t, duration: 1, velocity: 90 });
|
|
418
|
+
// later: stopNote() stops (or cancels) that note; owner.stop() stops everything still sounding or scheduled
|
|
419
|
+
releasePlayback(owner); // when your playback ends
|
|
420
|
+
```
|
|
421
|
+
|
|
422
|
+
| Export | Signature | |
|
|
423
|
+
| --- | --- | --- |
|
|
424
|
+
| `getSharedAudio` | `() => { context: AudioContext; master: DynamicsCompressorNode } \| null` | The page's audio graph, created on first use. Readers play through `master`. |
|
|
425
|
+
| `resumeAudio` | `(context) => Promise<boolean>` | Resume with a bounded wait (a pending `resume()` never hangs your code). |
|
|
426
|
+
| `claimPlayback` / `releasePlayback` | `(owner: { stop(): void }) => void` | One player at a time: claiming stops the previous owner. A reader claims it when it starts playing, so your app and the readers never play over each other. |
|
|
427
|
+
| `loadInstrument` | `(sound: number \| string, options?) => Promise<Instrument>` | `sound`: GM program 1-128, FluidR3 name (`'acoustic_grand_piano'`) or `'drums'`. |
|
|
428
|
+
| `loadDrumKit` | `(options?) => Promise<Instrument>` | `loadInstrument('drums', options)`. |
|
|
429
|
+
| `createMetronome` | `(options?) => Promise<Metronome>` | See below. |
|
|
430
|
+
| `METRONOME_VOICES`, `SOUNDS_VERSION`, `defaultAssetBaseUrl` | | Constants. |
|
|
431
|
+
|
|
432
|
+
The drum kit is `'drums'` (`'drum-kit'`, the score part id, is accepted as an alias).
|
|
433
|
+
|
|
434
|
+
Options (`InstrumentOptions`): `assetBaseUrl`, `soundPack` (`'core'` default, `'complete'`) and `sources` (`SoundSource[]`) work exactly like the reader's `assetBaseUrl`, `soundPack` and `soundSources` props. `context` and `destination` default to the shared context and its master compressor. `notes` lists the notes to load before the promise resolves (default 21-108 for an instrument, none for the kit; call `loadNotes(notes)` for more, since a note that is not loaded starts silently).
|
|
435
|
+
|
|
436
|
+
An `Instrument` is an `AudioPlayer` (`start({ note, time, duration, velocity, destination? }) => stop`, `stop()`, `dispose()`, `loadNotes(notes)`, `covers(note)`) plus `context`, `output` (its volume `GainNode`) and `plan` (read-only `{ kind, sourceId }`: which source plays it, for example `'builtin:fluidr3'`). `stop()` stops every sounding note **and cancels every scheduled one**. A note's `destination` is honoured by sample-pack and note-file sounds. SF2 sounds always play through `output`.
|
|
437
|
+
|
|
438
|
+
Sounds resolve exactly as in the reader (above): your `sources`, then the pack's FluidR3 sound, then `TimGM6mb.sf2`. Drums use Perfect Drums, with TimGM6mb's kit for the notes it lacks. Every built-in file comes from `assetBaseUrl`, so a self-hosted base means no requests to any other host.
|
|
439
|
+
|
|
440
|
+
**Metronome.** `createMetronome({ voice?, gain?, ...InstrumentOptions })` returns `{ voice, output, ready, tick({ time, beat, accent? }) => stop, setVoice(voice), stop(), dispose() }`. It resolves at once and loads the drum kit in the background: beats play the synthesized sound until the kit arrives (`await metronome.ready` to wait; `setVoice` resolves when its notes have loaded). A failed kit download is not remembered: the next `setVoice`, or a tick after 10 seconds, tries again. `beat` counts from 0 within the bar, and beat 0 is accented (louder, or higher for synth sounds) unless `accent` says otherwise. `gain` defaults to a gentle 0.5.
|
|
441
|
+
|
|
442
|
+
| Voice | Beat 1 | Other beats | Source |
|
|
443
|
+
| --- | --- | --- | --- |
|
|
444
|
+
| `'woodblock'` (default) | GM hi wood block (76) | GM low wood block (77) | The drum kit (Perfect Drums has no wood blocks, so TimGM6mb's kit plays them). **The default voice downloads Perfect Drums' manifest and `TimGM6mb.sf2` (about 6 MB, once, cached) from `assetBaseUrl` in the background; use `voice: 'click'` to load nothing.** Without a kit wood block: a soft synthesized knock. |
|
|
445
|
+
| `'drum-machine'` | Kick (36) | Closed hi-hat (42) | The drum kit (Perfect Drums). Without a kit: synthesized ticks. |
|
|
446
|
+
| `'click'` | Soft high tick | Soft tick | Synthesized, short and quiet. Loads nothing. |
|
|
447
|
+
|
|
331
448
|
## Known v1 limits
|
|
332
449
|
|
|
333
450
|
- **`deviceClass()` uses a viewport media query.** Layout sizes come from the reader's root element, but the choice between "desktop" (a fine pointer and a viewport wider than 850px) and "compact" defaults still queries the viewport. That choice sets the default stave scale and the large-file notice wording. So a narrow reader on a wide desktop page gets desktop defaults.
|
|
@@ -341,7 +458,9 @@ Drums use the Perfect Drums note files unless a source claims them. `soundSource
|
|
|
341
458
|
The package licence is `MIT AND LGPL-3.0-or-later`:
|
|
342
459
|
|
|
343
460
|
- **The pepperscore component code is MIT licensed** (see `LICENSE`).
|
|
344
|
-
- **The
|
|
461
|
+
- **The pepperscore worker chunks in `dist/assets/` are MIT licensed too**: they contain no Verovio code.
|
|
462
|
+
- **Verovio is LGPL-3.0-or-later** and stays a separate file. `dist/verovio/` holds Verovio 6.3.0's own `verovio-module.mjs` and `verovio.mjs` (unmodified, with a licence comment added) and a `LICENSE`; the worker loads them at runtime, and you can swap in your own build with `verovioBaseUrl` (see [Verovio](#verovio)). Source: [rism-digital/verovio, tag version-6.3.0](https://github.com/rism-digital/verovio/tree/version-6.3.0). The LGPL-3.0 and GPL-3.0 texts ship in `LICENSES/`. Hosts that deploy pepperscore redistribute these files; keep their notices with them.
|
|
463
|
+
- **Third-party notices**: `dist/THIRD_PARTY_NOTICES.txt` lists the runtime dependencies (MIT and ISC, with their licence texts), Verovio, the SIL OFL 1.1 music fonts Verovio embeds (Leipzig, Bravura, Gootville, Leland, Petaluma) and Poppins (OFL 1.1, through `@fontsource/poppins`). Host bundlers copy it as an asset, and the full reader's options drawer links it ("Licences", next to the soundfont credits).
|
|
345
464
|
- **Sound packs** (`pepperscore-sounds-core`, `pepperscore-sounds-complete`): each asset set keeps its own license, listed in the package's `LICENSES.md`:
|
|
346
465
|
- TimGM6mb.sf2: GPL-2.0
|
|
347
466
|
- FluidR3 GM sample packs: CC BY 3.0 (attribution in `soundfonts/fluidr3/ATTRIBUTION.txt`)
|