├── module.js
├── .github
└── FUNDING.yml
├── classes
├── svg.css
├── lyric.css
├── clef.css
├── instruction.css
├── accidental.css
├── head.css
├── ledge.css
├── tie.css
├── beam.css
├── stave
│ ├── chord.css
│ ├── alto.css
│ ├── bass.css
│ ├── treble-up.css
│ ├── treble-down.css
│ ├── treble.css
│ └── percussion.css
├── rest.css
├── timesig.css
├── chord.css
├── tuplet.css
└── stave.css
├── logo.png
├── module.css
├── .gitignore
├── card
├── card-1200x630.jpg
├── README.md
├── player.html
└── index.html
├── assets
├── images
│ ├── example.png
│ ├── so-what.png
│ ├── trees.avif
│ └── blue-in-green.png
├── font.css
└── lead-sheet-document.css
├── fonts
├── Leipzig
│ ├── Leipzig.ttf
│ └── Leipzig.woff2
├── Petaluma
│ ├── README.md
│ └── redist
│ │ ├── otf
│ │ ├── Petaluma.otf
│ │ ├── PetalumaText.otf
│ │ └── PetalumaScript.otf
│ │ ├── woff
│ │ ├── Petaluma.woff
│ │ ├── Petaluma.woff2
│ │ ├── PetalumaText.woff
│ │ ├── PetalumaText.woff2
│ │ ├── PetalumaScript.woff
│ │ └── PetalumaScript.woff2
│ │ ├── FONTLOG.txt
│ │ └── OFL.txt
├── Ash
│ ├── finaleash-webfont.woff
│ ├── finaleash-webfont.woff2
│ └── OFL.txt
├── Jazz
│ ├── finalejazz-webfont.woff
│ ├── finalejazz-webfont.woff2
│ └── OFL.txt
├── Bravura
│ ├── redist
│ │ ├── otf
│ │ │ ├── Bravura.otf
│ │ │ └── BravuraText.otf
│ │ ├── woff
│ │ │ ├── Bravura.woff
│ │ │ ├── Bravura.woff2
│ │ │ ├── BravuraText.woff
│ │ │ └── BravuraText.woff2
│ │ └── OFL.txt
│ ├── README.md
│ └── LICENSE.txt
├── AshText
│ ├── finaleashtext-webfont.woff
│ ├── finaleashtext-webfont.woff2
│ └── OFL.txt
├── Broadway
│ ├── finalebroadway-webfont.woff2
│ └── OFL.txt
├── JazzText
│ ├── finalejazztext-webfont.woff
│ ├── finalejazztext-webfont.woff2
│ └── OFL.txt
├── BroadwayText
│ ├── finalebroadwaytext-webfont.woff2
│ └── OFL.txt
├── leipzig.css
├── ash.css
├── bravura.css
├── petaluma.css
├── broadway.css
├── jazz.css
└── README.md
├── modules
├── maths.js
├── object
│ ├── length-of.js
│ ├── last.js
│ ├── every.js
│ ├── join.js
│ ├── map.js
│ └── push.js
├── event
│ ├── is-transposeable.js
│ ├── to-stop-beat.js
│ └── to-duration.js
├── number
│ ├── truncate.js
│ ├── mod-12.js
│ ├── ceil-to.js
│ ├── floor-to.js
│ ├── power-of-2.js
│ ├── grain-pow-2.js
│ └── float.js
├── timesig.js
├── stave
│ ├── percussion.js
│ ├── drum.js
│ ├── stave.js
│ └── piano.js
├── log.js
├── scale.js
├── request-data.js
├── config.js
├── pitch.js
├── quantise.js
├── event.js
├── spelling.js
├── parse.js
├── parse
│ ├── parse-sequence-text.js
│ └── parse-abc.js
├── stave.js
├── beam.js
├── chord.js
└── render.js
├── .gitmodules
├── breakpoints.json
├── scribe-music
├── element.css
└── README.md
├── deno.json
├── data
├── si-bheag-si-mhor.abc
├── so-what.json
├── caravan-drums.json
├── blue-in-green.json
└── dolphin-dance.json
├── attributes
├── data-divisor.css
└── data-duration.css
├── package.json
├── Makefile
├── test
├── bars.js
├── quantize.js
├── si-bheag-si-mhor.html
├── tuplets.js
├── dolphin-dance.html
├── index.html
└── repeats.html
└── chart.html
/module.js:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/.github/FUNDING.yml:
--------------------------------------------------------------------------------
1 | github: [stephband]
2 |
--------------------------------------------------------------------------------
/classes/svg.css:
--------------------------------------------------------------------------------
1 | svg {
2 |
3 | overflow: visible;
4 | }
5 |
--------------------------------------------------------------------------------
/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/stephband/scribe/HEAD/logo.png
--------------------------------------------------------------------------------
/module.css:
--------------------------------------------------------------------------------
1 |
2 | @import './fonts/jazz.css';
3 | @import './scribe-music/shadow.css';
4 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .venv/
2 | archive/*
3 | phil-taylor-tests/*
4 | *.m4a
5 | *.wav
6 | *.mp3
7 |
--------------------------------------------------------------------------------
/card/card-1200x630.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/stephband/scribe/HEAD/card/card-1200x630.jpg
--------------------------------------------------------------------------------
/assets/images/example.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/stephband/scribe/HEAD/assets/images/example.png
--------------------------------------------------------------------------------
/assets/images/so-what.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/stephband/scribe/HEAD/assets/images/so-what.png
--------------------------------------------------------------------------------
/assets/images/trees.avif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/stephband/scribe/HEAD/assets/images/trees.avif
--------------------------------------------------------------------------------
/fonts/Leipzig/Leipzig.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/stephband/scribe/HEAD/fonts/Leipzig/Leipzig.ttf
--------------------------------------------------------------------------------
/fonts/Leipzig/Leipzig.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/stephband/scribe/HEAD/fonts/Leipzig/Leipzig.woff2
--------------------------------------------------------------------------------
/fonts/Petaluma/README.md:
--------------------------------------------------------------------------------
1 | # Petaluma
2 | Petaluma music font, licensed under the SIL Open Font License.
3 |
--------------------------------------------------------------------------------
/assets/images/blue-in-green.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/stephband/scribe/HEAD/assets/images/blue-in-green.png
--------------------------------------------------------------------------------
/fonts/Ash/finaleash-webfont.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/stephband/scribe/HEAD/fonts/Ash/finaleash-webfont.woff
--------------------------------------------------------------------------------
/fonts/Ash/finaleash-webfont.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/stephband/scribe/HEAD/fonts/Ash/finaleash-webfont.woff2
--------------------------------------------------------------------------------
/fonts/Jazz/finalejazz-webfont.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/stephband/scribe/HEAD/fonts/Jazz/finalejazz-webfont.woff
--------------------------------------------------------------------------------
/fonts/Jazz/finalejazz-webfont.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/stephband/scribe/HEAD/fonts/Jazz/finalejazz-webfont.woff2
--------------------------------------------------------------------------------
/fonts/Bravura/redist/otf/Bravura.otf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/stephband/scribe/HEAD/fonts/Bravura/redist/otf/Bravura.otf
--------------------------------------------------------------------------------
/fonts/Bravura/redist/woff/Bravura.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/stephband/scribe/HEAD/fonts/Bravura/redist/woff/Bravura.woff
--------------------------------------------------------------------------------
/fonts/Petaluma/redist/otf/Petaluma.otf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/stephband/scribe/HEAD/fonts/Petaluma/redist/otf/Petaluma.otf
--------------------------------------------------------------------------------
/fonts/AshText/finaleashtext-webfont.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/stephband/scribe/HEAD/fonts/AshText/finaleashtext-webfont.woff
--------------------------------------------------------------------------------
/fonts/Bravura/redist/otf/BravuraText.otf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/stephband/scribe/HEAD/fonts/Bravura/redist/otf/BravuraText.otf
--------------------------------------------------------------------------------
/fonts/Bravura/redist/woff/Bravura.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/stephband/scribe/HEAD/fonts/Bravura/redist/woff/Bravura.woff2
--------------------------------------------------------------------------------
/fonts/Petaluma/redist/woff/Petaluma.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/stephband/scribe/HEAD/fonts/Petaluma/redist/woff/Petaluma.woff
--------------------------------------------------------------------------------
/modules/maths.js:
--------------------------------------------------------------------------------
1 |
2 | export function byGreater(a, b) {
3 | return a > b ? 1 :
4 | a < b ? -1 :
5 | 0 ;
6 | }
7 |
--------------------------------------------------------------------------------
/fonts/AshText/finaleashtext-webfont.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/stephband/scribe/HEAD/fonts/AshText/finaleashtext-webfont.woff2
--------------------------------------------------------------------------------
/fonts/Bravura/redist/woff/BravuraText.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/stephband/scribe/HEAD/fonts/Bravura/redist/woff/BravuraText.woff
--------------------------------------------------------------------------------
/fonts/Bravura/redist/woff/BravuraText.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/stephband/scribe/HEAD/fonts/Bravura/redist/woff/BravuraText.woff2
--------------------------------------------------------------------------------
/fonts/Broadway/finalebroadway-webfont.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/stephband/scribe/HEAD/fonts/Broadway/finalebroadway-webfont.woff2
--------------------------------------------------------------------------------
/fonts/JazzText/finalejazztext-webfont.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/stephband/scribe/HEAD/fonts/JazzText/finalejazztext-webfont.woff
--------------------------------------------------------------------------------
/fonts/JazzText/finalejazztext-webfont.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/stephband/scribe/HEAD/fonts/JazzText/finalejazztext-webfont.woff2
--------------------------------------------------------------------------------
/fonts/Petaluma/redist/otf/PetalumaText.otf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/stephband/scribe/HEAD/fonts/Petaluma/redist/otf/PetalumaText.otf
--------------------------------------------------------------------------------
/fonts/Petaluma/redist/woff/Petaluma.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/stephband/scribe/HEAD/fonts/Petaluma/redist/woff/Petaluma.woff2
--------------------------------------------------------------------------------
/fonts/Petaluma/redist/otf/PetalumaScript.otf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/stephband/scribe/HEAD/fonts/Petaluma/redist/otf/PetalumaScript.otf
--------------------------------------------------------------------------------
/fonts/Petaluma/redist/woff/PetalumaText.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/stephband/scribe/HEAD/fonts/Petaluma/redist/woff/PetalumaText.woff
--------------------------------------------------------------------------------
/fonts/Petaluma/redist/woff/PetalumaText.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/stephband/scribe/HEAD/fonts/Petaluma/redist/woff/PetalumaText.woff2
--------------------------------------------------------------------------------
/fonts/Petaluma/redist/woff/PetalumaScript.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/stephband/scribe/HEAD/fonts/Petaluma/redist/woff/PetalumaScript.woff
--------------------------------------------------------------------------------
/fonts/Petaluma/redist/woff/PetalumaScript.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/stephband/scribe/HEAD/fonts/Petaluma/redist/woff/PetalumaScript.woff2
--------------------------------------------------------------------------------
/classes/lyric.css:
--------------------------------------------------------------------------------
1 |
2 | .lyric {
3 | font-family: sans-serif;
4 | font-size: 0.375em;
5 | text-transform: uppercase;
6 | margin: 0;
7 | }
8 |
--------------------------------------------------------------------------------
/fonts/BroadwayText/finalebroadwaytext-webfont.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/stephband/scribe/HEAD/fonts/BroadwayText/finalebroadwaytext-webfont.woff2
--------------------------------------------------------------------------------
/modules/object/length-of.js:
--------------------------------------------------------------------------------
1 | export default function lengthOf(object) {
2 | let n = -1;
3 | while (object[++n] !== undefined);
4 | return n;
5 | }
6 |
--------------------------------------------------------------------------------
/modules/object/last.js:
--------------------------------------------------------------------------------
1 | import lengthOf from './length-of.js';
2 |
3 | export default function join(object) {
4 | return object[lengthOf(object) - 1];
5 | }
6 |
--------------------------------------------------------------------------------
/modules/event/is-transposeable.js:
--------------------------------------------------------------------------------
1 | const types = ['note', 'chord', 'key'];
2 |
3 | export default function isTransposeable(event) {
4 | return types.includes(event[1]);
5 | }
6 |
--------------------------------------------------------------------------------
/modules/event/to-stop-beat.js:
--------------------------------------------------------------------------------
1 |
2 | import toDuration from './to-duration.js';
3 |
4 | export default function toStopBeat(event) {
5 | return event[0] + toDuration(event);
6 | }
7 |
--------------------------------------------------------------------------------
/modules/object/every.js:
--------------------------------------------------------------------------------
1 | export default function every(object, fn) {
2 | let n = -1;
3 | while (object[++n] !== undefined) if (!fn(object[n], n, object)) return false;
4 | return true;
5 | }
6 |
--------------------------------------------------------------------------------
/classes/clef.css:
--------------------------------------------------------------------------------
1 |
2 | .clef {
3 | line-height: 2em;
4 | height: auto;
5 | }
6 |
7 | .alto-clef {
8 | line-height: 1em;
9 | }
10 |
11 | .drum-clef {
12 | line-height: 0.75em;
13 | }
14 |
--------------------------------------------------------------------------------
/modules/object/join.js:
--------------------------------------------------------------------------------
1 | export default function join(object, string) {
2 | let s = '' + object[0];
3 | let n = 0;
4 | while (object[++n] !== undefined) s += string + object[n];
5 | return s;
6 | }
7 |
--------------------------------------------------------------------------------
/modules/object/map.js:
--------------------------------------------------------------------------------
1 |
2 | export default function map(fn, object) {
3 | const array = [];
4 | let n = -1;
5 | while (object[++n] !== undefined) array.push(fn(object[n], n));
6 | return array;
7 | }
8 |
--------------------------------------------------------------------------------
/.gitmodules:
--------------------------------------------------------------------------------
1 | [submodule "fonts/MartianMono"]
2 | path = fonts/MartianMono
3 | url = https://github.com/evilmartians/mono.git
4 | [submodule "fonts/Leland"]
5 | path = fonts/Leland
6 | url = https://github.com/MuseScoreFonts/Leland.git
7 |
--------------------------------------------------------------------------------
/modules/number/truncate.js:
--------------------------------------------------------------------------------
1 |
2 | /* Returns number as string truncated to at most n decimal places with
3 | no trailing zeros. */
4 |
5 | export default function truncate(n, number) {
6 | return number.toFixed(n).replace(/\.?0+$/, '');
7 | }
8 |
--------------------------------------------------------------------------------
/fonts/leipzig.css:
--------------------------------------------------------------------------------
1 |
2 | @font-face {
3 | font-family: Leipzig;
4 | src: url('../fonts/Leipzig/Leipzig.woff2') format('woff2');
5 | font-weight: normal;
6 | font-style: normal;
7 | }
8 |
9 | .leipzig-font {
10 | font-family: Leipzig;
11 | }
12 |
--------------------------------------------------------------------------------
/modules/number/mod-12.js:
--------------------------------------------------------------------------------
1 |
2 | import mod from 'fn/mod.js';
3 |
4 | /* mod12()
5 | Finds the modulus `n % 12`, but using floored division (the JS operator uses
6 | euclidean division).
7 | */
8 |
9 | export default function mod12(n) {
10 | return mod(12, n);
11 | }
12 |
--------------------------------------------------------------------------------
/modules/object/push.js:
--------------------------------------------------------------------------------
1 | /* Array-like functions that operate on plain objects */
2 |
3 | export default function push(object, value) {
4 | let n = -1, a = 0;;
5 | while (object[++n] !== undefined);
6 | while (arguments[++a] !== undefined) object[n++] = arguments[a];
7 | return object;
8 | }
9 |
--------------------------------------------------------------------------------
/classes/instruction.css:
--------------------------------------------------------------------------------
1 |
2 | .instruction {
3 | /* Instruction happens outside of bar, make it fill full width of flex box */
4 | width: 100%;
5 | margin: 0;
6 | font-size: 0.8125em;
7 | /* This metric sits the baseline on the bottom of the box */
8 | height: 1em;
9 | line-height: 2em;
10 | }
11 |
--------------------------------------------------------------------------------
/classes/accidental.css:
--------------------------------------------------------------------------------
1 |
2 | .acci {
3 | /* Make it's box two stave spaces high */
4 | line-height: 0.5em;
5 | height: auto;
6 |
7 | /* Naturals can be very narrow and bunch up against other accidentals,
8 | give them a little minimum space */
9 | min-width: 0.25em;
10 | text-align: center;
11 | }
12 |
--------------------------------------------------------------------------------
/breakpoints.json:
--------------------------------------------------------------------------------
1 |
2 | [
3 | { "prefix": "", "width": "0em", "column-gap": 24, "row-gap": 24 },
4 | { "prefix": "\\@1-", "width": "50em", "column-gap": 24, "row-gap": 24 },
5 | { "prefix": "\\@2-", "width": "70em", "column-gap": 45, "row-gap": 30 },
6 | { "prefix": "\\@3-", "width": "90em", "column-gap": 60, "row-gap": 30 }
7 | ]
8 |
--------------------------------------------------------------------------------
/modules/number/ceil-to.js:
--------------------------------------------------------------------------------
1 |
2 | /**
3 | ceilTo(values, value)
4 | Given a sorted array of number `values`, ceils `value` to the nearest higher
5 | number in `values`, or `undefined`.
6 | **/
7 |
8 | export default function ceilTo(values, value) {
9 | let n = -1;
10 | while (values[++n] && values[n] < value);
11 | return values[n];
12 | }
13 |
--------------------------------------------------------------------------------
/modules/number/floor-to.js:
--------------------------------------------------------------------------------
1 |
2 | /**
3 | floorFrom(values, value)
4 | Given a sorted array of number `values`, floors `value` to the nearest lower
5 | number in `values`, or `undefined`.
6 | **/
7 |
8 | export default function floorTo(values, value) {
9 | let n = values.length;
10 | while (values[--n] && values[n] > value);
11 | return values[n];
12 | }
13 |
--------------------------------------------------------------------------------
/modules/event/to-duration.js:
--------------------------------------------------------------------------------
1 |
2 | import get from 'fn/get.js';
3 | import overload from 'fn/overload.js';
4 |
5 | export default overload(get(1), {
6 | chord: (event) => event[4],
7 | lyric: (event) => event[3],
8 | note: (event) => event[4],
9 | sequence: (event) => event[4],
10 | // Return 0 duration as default
11 | default: (event) => 0
12 | });
13 |
--------------------------------------------------------------------------------
/card/README.md:
--------------------------------------------------------------------------------
1 | **Scribe card**
2 |
3 | `index.html` file has meta tags for open graph and twitter card.
4 |
5 | They point to the player in player.html
6 |
7 | This makes twitter and mastodon display the player in an iframe and makes
8 | it an embedded player that can be played directly on those platforms when
9 | `https://labs.cruncher.ch/scribe/card` URL is posted on one of these platforms.
10 |
--------------------------------------------------------------------------------
/scribe-music/element.css:
--------------------------------------------------------------------------------
1 |
2 | /* Font must be imported outside the shadow DOM for Safari's sake */
3 | @import '../fonts/jazz.css';
4 |
5 | scribe-music {
6 | font-family: Jazz, JazzText, sans-serif;
7 | }
8 |
9 | scribe-music::after {
10 | /* Control spread of last line */
11 | content: '';
12 | flex: 6 1;
13 | }
14 |
15 | scribe-music:not(:defined) {
16 | display: none !important;
17 | }
18 |
--------------------------------------------------------------------------------
/fonts/ash.css:
--------------------------------------------------------------------------------
1 |
2 | @font-face {
3 | font-family: AshText;
4 | src: url('../fonts/AshText/finaleashtext-webfont.woff2') format('woff2');
5 | font-weight: normal;
6 | font-style: normal;
7 | }
8 |
9 | @font-face {
10 | font-family: Ash;
11 | src: url('../fonts/Ash/finaleash-webfont.woff2') format('woff2');
12 | font-weight: normal;
13 | font-style: normal;
14 | }
15 |
16 | .ash-font {
17 | font-family: Ash, AshText;
18 | }
19 |
--------------------------------------------------------------------------------
/classes/head.css:
--------------------------------------------------------------------------------
1 | :root,
2 | :host {
3 | /* Width of head of duration 1 */
4 | --head-1-size: 0.3375;
5 | }
6 |
7 | .head {
8 | /* Guarantee that multi-char heads, like those with augmentation dots,
9 | don't wrap */
10 | white-space: nowrap;
11 | }
12 |
13 | .head[data-glyph="headX"] {
14 | --stem-align-up: 0.18;
15 | --stem-align-down: -0.14;
16 | }
17 |
18 | .head[data-glyph="headDiamond"] {
19 | margin-left: 0.04em;
20 | }
21 |
--------------------------------------------------------------------------------
/fonts/bravura.css:
--------------------------------------------------------------------------------
1 |
2 | @font-face {
3 | font-family: BravuraText;
4 | src: url('../fonts/Bravura/redist/woff/BravuraText.woff2') format('woff2');
5 | font-weight: normal;
6 | font-style: normal;
7 | }
8 |
9 | @font-face {
10 | font-family: Bravura;
11 | src: url('../fonts/Bravura/redist/woff/Bravura.woff2') format('woff2');
12 | font-weight: normal;
13 | font-style: normal;
14 | }
15 |
16 | .bravura-font {
17 | font-family: Bravura, BravuraText;
18 | }
19 |
--------------------------------------------------------------------------------
/classes/ledge.css:
--------------------------------------------------------------------------------
1 |
2 | .ledge {
3 | overflow: hidden;
4 | width: 0.55em;
5 | vector-effect: non-scaling-stroke;
6 | stroke: currentcolor;
7 | stroke-width: 0.05em;
8 | stroke-linejoin: round;
9 | stroke-linecap: round;
10 | fill: none;
11 | }
12 |
13 | .ledge line {
14 | vector-effect: non-scaling-stroke;
15 | stroke: currentcolor;
16 | stroke-width: 0.03125em;
17 | stroke-linejoin: round;
18 | stroke-linecap: round;
19 | fill: none;
20 | }
21 |
--------------------------------------------------------------------------------
/deno.json:
--------------------------------------------------------------------------------
1 | {
2 | "imports": {
3 | "dom/": "https://cdn.jsdelivr.net/gh/stephband/dom@1.4.1/modules/",
4 | "fn/": "https://cdn.jsdelivr.net/gh/stephband/fn@1.5.x/modules/",
5 | "bolt/": "https://cdn.jsdelivr.net/gh/stephband/bolt-2@1.0.1/",
6 | "midi/": "https://cdn.jsdelivr.net/gh/stephband/midi@1.1.1/modules/",
7 | "sequence/": "https://cdn.jsdelivr.net/gh/soundio/sequence@0.1.5/modules/"
8 | },
9 |
10 | "lock": false
11 | }
12 |
--------------------------------------------------------------------------------
/fonts/petaluma.css:
--------------------------------------------------------------------------------
1 |
2 | @font-face {
3 | font-family: PetalumaText;
4 | src: url('../fonts/Petaluma/redist/woff/PetalumaScript.woff2') format('woff2');
5 | font-weight: normal;
6 | font-style: normal;
7 | }
8 |
9 | @font-face {
10 | font-family: Petaluma;
11 | src: url('../fonts/Petaluma/redist/woff/Petaluma.woff2') format('woff2');
12 | font-weight: normal;
13 | font-style: normal;
14 | }
15 |
16 | .petaluma-font {
17 | font-family: Petaluma, PetalumaText;
18 | }
19 |
--------------------------------------------------------------------------------
/fonts/broadway.css:
--------------------------------------------------------------------------------
1 |
2 | @font-face {
3 | font-family: BroadwayText;
4 | src: url('../fonts/BroadwayText/finalebroadwaytext-webfont.woff2') format('woff2');
5 | font-weight: normal;
6 | font-style: normal;
7 | }
8 |
9 | @font-face {
10 | font-family: Broadway;
11 | src: url('../fonts/Broadway/finalebroadway-webfont.woff2') format('woff2');
12 | font-weight: normal;
13 | font-style: normal;
14 | }
15 |
16 | .broadway-font {
17 | font-family: Broadway, BroadwayText;
18 | }
19 |
--------------------------------------------------------------------------------
/fonts/Bravura/README.md:
--------------------------------------------------------------------------------
1 | # Bravura music font
2 |
3 | Bravura is an OpenType music font developed for Steinberg's [Dorico](https://www.steinberg.net/dorico) music notation and composition software.
4 |
5 | It is also the reference font for [Standard Music Font Layout (SMuFL)](https://w3c.github.io/smufl/gitbook/), which provides a standard way of mapping the thousands of musical symbols required by conventional music notation into the Private Use Area in Unicode’s Basic Multilingual Plane for a single (format-independent) font.
6 |
--------------------------------------------------------------------------------
/classes/tie.css:
--------------------------------------------------------------------------------
1 | .tie {
2 | /*background: rgba(220, 180, 20, 0.4);*/
3 | min-width: 2em;
4 | height: 0.5em;
5 | }
6 |
7 | .down-tie {
8 | /*background-color: lightblue;*/
9 | }
10 |
11 | .down-tie > path {
12 | transform-origin: center center;
13 | transform: translateY(0.05px) scale(1, 0.9);
14 | }
15 |
16 | .up-tie {
17 | /*background-color: yellow;*/
18 | }
19 |
20 | .up-tie > path {
21 | transform-origin: center center;
22 | transform: translateY(-0.05px) scale(1, -0.9);
23 | }
24 |
--------------------------------------------------------------------------------
/data/si-bheag-si-mhor.abc:
--------------------------------------------------------------------------------
1 | X: 1
2 | T: Sí Bheag Sí Mhór
3 | C: Turlough O'Carolan
4 | Z: ceolachan
5 | S: https://thesession.org/tunes/449#setting13324
6 | R: waltz
7 | M: 3/4
8 | L: 1/8
9 | K: Dmaj
10 | f3 e d2|d2 e2 d2|B4 A2|F4 A2|BA Bc d2|e4 de|f2 f2 e2|d4 f2|
11 | B4 e2|A4 d2|F2 F2 E2|D4 f2|B4 e2|A4 dc|d6-|d4:|
12 | f3 e d2|ed ef a2|b4 a2|f4 ed|e2 e2 a2|f4 e2|d4 B2|B4 A2|
13 | F4 E2|D4 f2|B4 e2|A4 a2|ba gf ed|e3 fe|d1
14 | |de:|A)||D D Bm D G A D D G F#m Bm D G A D D||
15 | B)||D A G D A D D Bm D D G F#m G A D D||
16 |
17 |
--------------------------------------------------------------------------------
/attributes/data-divisor.css:
--------------------------------------------------------------------------------
1 |
2 | /* Converts data-divisor attribute, as found on tuplets, to --divisor variable */
3 |
4 | [data-divisor="2"] { --divisor: 2; }
5 | [data-divisor="3"] { --divisor: 3; }
6 | [data-divisor="4"] { --divisor: 4; }
7 | [data-divisor="5"] { --divisor: 5; }
8 | [data-divisor="6"] { --divisor: 6; }
9 | [data-divisor="7"] { --divisor: 7; }
10 | [data-divisor="8"] { --divisor: 8; }
11 | [data-divisor="9"] { --divisor: 9; }
12 | [data-divisor="10"] { --divisor: 10; }
13 | [data-divisor="11"] { --divisor: 11; }
14 |
--------------------------------------------------------------------------------
/classes/beam.css:
--------------------------------------------------------------------------------
1 | .beam {
2 | vector-effect: non-scaling-stroke;
3 | stroke: currentColor;
4 | stroke-width: 0;
5 | stroke-linejoin: round;
6 | stroke-linecap: round;
7 | fill: currentColor;
8 | }
9 |
10 | .up-beam > .beam-path-16 {
11 | transform: translateY(1.6666667px);
12 | }
13 |
14 | .up-beam > .beam-path-32 {
15 | transform: translateY(3.3333333px);
16 | }
17 |
18 | .down-beam > .beam-path-16 {
19 | transform: translateY(-1.6666667px);
20 | }
21 |
22 | .down-beam > .beam-path-32 {
23 | transform: translateY(-3.3333333px);
24 | }
25 |
--------------------------------------------------------------------------------
/modules/number/power-of-2.js:
--------------------------------------------------------------------------------
1 |
2 | import sum from 'fn/sum.js';
3 |
4 | export function ceilPow2(n) {
5 | return 2 ** Math.ceil(Math.log2(n));
6 | }
7 |
8 | export function floorPow2(n) {
9 | return 2 ** Math.floor(Math.log2(n));
10 | }
11 |
12 | export function roundPowerOf2(n) {
13 | return 2 ** Math.round(Math.log2(n));
14 | }
15 |
16 | export function averagePowerOf2(...values) {
17 | return 2 ** (values.map(Math.log2).reduce(sum, 0) / values.length);
18 | }
19 |
20 | export function isPowerOf2(n) {
21 | return n > 0 && (n & (n - 1)) === 0;
22 | }
23 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "title": "Scribe",
3 | "name": "scribe",
4 | "description": "",
5 | "version": "0.4.18",
6 | "author": {
7 | "name": "stephband",
8 | "url": "http://stephen.band",
9 | "twitter": "@stephband"
10 | },
11 | "repository": {
12 | "type": "git",
13 | "url": "https://github.com/stephband/scribe.git"
14 | },
15 | "bugs": {
16 | "url": "https://github.com/stephband/scribe/issues/"
17 | },
18 | "homepage": "https://stephen.band/scribe",
19 | "image": "https://stephen.band/scribe/logo.png",
20 | "favicons": []
21 | }
22 |
--------------------------------------------------------------------------------
/modules/timesig.js:
--------------------------------------------------------------------------------
1 |
2 | const rtimesig = /^(\d+)\/(\d+)$/;
3 |
4 | export function timesigToMeter(string) {
5 | const groups = rtimesig.exec(string);
6 | const num = parseInt(groups[1], 10);
7 | const div = 4 / parseInt(groups[2], 10);
8 | // Returns an object that can be assigned to a meter event
9 | return { 1: 'meter', 2: num * div, 3: div };
10 | }
11 |
12 | export function meterToTimesig(meter) {
13 | const dur = meter[2];
14 | const div = meter[3];
15 | const num = dur / div;
16 | const den = 4 / div;
17 | return num + '/' + den;
18 | }
19 |
--------------------------------------------------------------------------------
/modules/number/grain-pow-2.js:
--------------------------------------------------------------------------------
1 |
2 | import mod from 'fn/mod.js';
3 | import { eq } from './float.js';
4 |
5 | /*
6 | grainPower2(n)
7 | grainPower2(n, min, max)
8 | Finds the closest power-of-two number that fits into number `n` with no
9 | remainder. Where given, `min` and `max` must be power-of-two numbers.
10 | */
11 |
12 | export default function grainPower2(min, max, n) {
13 | // If n is 0 we can say we are on the maximum grain
14 | if (eq(0, n, min / 2)) return max;
15 |
16 | let g = max * 2;
17 | while ((g /= 2) > min) {
18 | if (eq(0, mod(g, n), min / 2)) return g;
19 | }
20 | return g;
21 | }
22 |
--------------------------------------------------------------------------------
/modules/stave/percussion.js:
--------------------------------------------------------------------------------
1 |
2 | import { toNoteNumber } from 'midi/note.js';
3 | import * as glyphs from "../glyphs.js";
4 | import DrumStave from './drum.js';
5 |
6 |
7 | export default class PercussionStave extends DrumStave {
8 | type = 'percussion';
9 | rows = ['','','','','','','','','note','','','','','','','',''];
10 |
11 | get maxPitch() {
12 | return this.rows[17];
13 | }
14 |
15 | get bottomPitch() {
16 | return this.rows[8];
17 | }
18 |
19 | get centerPitch() {
20 | return this.rows[8];
21 | }
22 |
23 | get topPitch() {
24 | return this.rows[8];
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/modules/number/float.js:
--------------------------------------------------------------------------------
1 |
2 | const { abs, EPSILON } = Math;
3 |
4 | export function round(d, n) {
5 | return Math.round(n / d) * d;
6 | }
7 |
8 | export function eq(a, b, precision = EPSILON) {
9 | return a !== undefined && (a === b || abs(a - b) < precision);
10 | }
11 |
12 | export function lte(a, b, precision) {
13 | return eq(a, b, precision) || a < b;
14 | }
15 |
16 | export function gte(a, b, precision) {
17 | return eq(a, b, precision) || a > b;
18 | }
19 |
20 | export function gt(a, b, precision) {
21 | return a !== undefined && b !== undefined && !lte(a, b, precision);
22 | }
23 |
24 | export function lt(a, b, precision) {
25 | return a !== undefined && b !== undefined && !gte(a, b, precision);
26 | }
27 |
--------------------------------------------------------------------------------
/classes/stave/chord.css:
--------------------------------------------------------------------------------
1 |
2 | .chord-stave {
3 | grid-template-rows:
4 | [max] 0.125em
5 | 0.125em
6 | 0.125em
7 | [stave-top] 0.125em
8 | 0.125em
9 | 0.125em
10 | 0.125em
11 | 0.125em
12 | [stave-center stave-top] 0.125em
13 | 0.125em
14 | 0.125em
15 | [chords] 0.125em
16 | 0.125em
17 | [stave-bottom] 0.125em
18 | 0.125em
19 | [lyrics] 0.125em
20 | [min];
21 |
22 | background-image: none;
23 | }
24 |
25 | /* TEMP */
26 | /* Just a precaution. */
27 | .chord-stave > .ledge,
28 | .chord-stave > .acci {
29 | display: none !important;
30 | }
31 |
32 | .chord-stave > [data-pitch] {
33 | display: none !important;
34 | }
35 |
--------------------------------------------------------------------------------
/card/player.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Scribe
7 |
8 |
9 |
10 |
11 |
12 |
13 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/modules/log.js:
--------------------------------------------------------------------------------
1 |
2 | import noop from 'fn/noop.js';
3 |
4 | const global = globalThis || window;
5 | const colors = {
6 | 'pink': '#FE267E',
7 | 'grey': '#81868f'
8 | };
9 |
10 | const log = (global.DEBUG && global.DEBUG.scribe !== false) ?
11 | function log($1, $2, $3 = '', $4 = '') {
12 | console.log('%cScribe %c' + $1 + ' %c' + $2 + ' %c' + $3 + ' %c' + $4,
13 | 'color: ' + (colors.pink) + '; font-weight: 300;',
14 | 'color: ' + (colors.grey) + '; font-weight: 300;',
15 | 'color: ' + (colors.pink) + '; font-weight: 300;',
16 | 'color: ' + (colors.grey) + '; font-weight: 300;',
17 | 'color: ' + (colors.pink) + '; font-weight: 300;'
18 | );
19 | } :
20 | noop ;
21 |
22 | export default log;
23 |
--------------------------------------------------------------------------------
/modules/scale.js:
--------------------------------------------------------------------------------
1 |
2 | import mod12 from './number/mod-12.js';
3 | import { byGreater } from './maths.js';
4 |
5 | export const major = [0,2,4,5,7,9,11];
6 |
7 | /**
8 | transposeScale(notes, tranpose)
9 | Returns a new scale, that is, an array of unique note numbers in the range
10 | `0-12`, from an array of note numbers, `notes`.
11 | **/
12 |
13 | function unique(value, i, array) {
14 | return array.indexOf(value) === i;
15 | }
16 |
17 | export function transposeScale(scale, transpose) {
18 | return scale
19 | // Transpose
20 | .map((n) => mod12(n + transpose))
21 | // Make unique
22 | .filter(unique)
23 | // Small to big, BTW may not start with 0
24 | .sort(byGreater);
25 | }
26 |
27 | export function transposeScale4ths(scale, transpose) {
28 | return transposeScale(scale, transpose * 7);
29 | }
30 |
--------------------------------------------------------------------------------
/modules/request-data.js:
--------------------------------------------------------------------------------
1 |
2 | import cache from 'fn/cache-by-key.js';
3 | import overload from 'fn/overload.js';
4 | import { requestGet } from 'dom/request.js';
5 | import parse from './parse.js';
6 |
7 | const requestData = cache(requestGet);
8 | const rpath = /^\.*\/|^https?:\/\//;
9 |
10 | /**
11 | requestData(type, url)
12 | **/
13 |
14 | export default overload((type, value) => typeof value, {
15 | string: function(type, url) {
16 | if (window.DEBUG && !rpath.test(url)) {
17 | throw new TypeError('URL not supported "' + url + '"');
18 | }
19 |
20 | return requestData(url)
21 | .then((source) => parse(type, source))
22 | .catch((error) => console.error(error));
23 | },
24 |
25 | default: function(name, internals, type, value) {
26 | internals[name] = value;
27 | }
28 | });
29 |
--------------------------------------------------------------------------------
/modules/config.js:
--------------------------------------------------------------------------------
1 | export default {
2 | // Spelling
3 | spellChordRootCFlatAsB: true,
4 | spellChordRootBSharpAsC: true,
5 | spellChordRootESharpAsF: true,
6 | spellChordRootFFlatAsE: true,
7 |
8 | // Swing and shuffle interpretation
9 | swingAsStraight8ths: true,
10 | swingAsStraight16ths: true,
11 |
12 | // Allowed head durations
13 | headDurations: [
14 | 1/8, 6/32, /*7/32,*/
15 | 1/4, 6/16, /*7/16,*/
16 | 1/2, 6/8, /*7/8, */
17 | 1, 6/4, /*7/4, */
18 | 2, 6/2, /*7/2, */
19 | 4, 6, /*7, */
20 | 8
21 | ],
22 |
23 | // Allowed rest durations
24 | restDurations: [
25 | 1/8, 6/32, // 7/32,
26 | 1/4, 6/16, // 7/16,
27 | 1/2, 6/8, // 7/8,
28 | 1, 6/4, // 7/4,
29 | 2, 3, // 7/2
30 | 4, 6,
31 | 8
32 | ]
33 | }
34 |
--------------------------------------------------------------------------------
/assets/font.css:
--------------------------------------------------------------------------------
1 |
2 | h1, .font-01 { font-size: calc(4.75rem + 2.5vw); font-weight: 600; line-height: 1.1em; }
3 | h2, .font-02 { font-size: calc(1.66176471rem + 0.44117647vw); font-weight: 600; line-height: 1.1em; }
4 | h3, .font-03 { font-size: calc(1.27573529rem + 0.18382353vw); font-weight: 600; line-height: 1.1em; }
5 | h4, .font-04 { font-size: calc(1.1171875rem + 0.078125vw); font-weight: 300; line-height: 1.4em; }
6 | body, .font-08 { font-size: 1rem; font-weight: 400; line-height: 1.4em; }
7 |
8 |
9 | .darkpink-fg { color: oklch(0.36 0.05 0); }
10 |
11 | .sea-fg { color: oklch(0.5 0.12 213.52); }
12 |
13 | a {
14 | white-space: nowrap;
15 | color: oklch(0.58 0.26 354);
16 | }
17 |
18 | code { font-size: 0.9375em; }
19 | strong { font-weight: 600; }
20 |
21 |
22 | .x-sub-grid {
23 | grid-template-columns: subgrid;
24 | }
25 |
26 | .y-sub-grid {
27 | grid-template-rows: subgrid;
28 | }
29 |
--------------------------------------------------------------------------------
/modules/pitch.js:
--------------------------------------------------------------------------------
1 |
2 |
3 | export const rrootname = /^([A-G])([b#♭♯𝄫𝄪])?/;
4 | export const rpitch = /^([A-G])([b#♭♯𝄫𝄪])?(-?\d)?$/;
5 | export const rflat = /b|♭/;
6 | export const rsharp = /#|♯/;
7 | export const rdoubleflat = /bb|𝄫/;
8 | export const rdoublesharp = /##|𝄪/;
9 | export const rflatsharp = /b|♭|#|♯/g;
10 |
11 | export const accidentalChars = {
12 | '-2': '𝄫',
13 | '-1': '♭',
14 | '0': '',
15 | '1': '♯',
16 | '2': '𝄪'
17 | };
18 |
19 | const fathercharles = [
20 | // Father Charles Goes Down And Ends Battle,
21 | 'F♯', 'C♯', 'G♯', 'D♯', 'A♯', 'E♯', 'B♯',
22 | // Battle Ends And Down Goes Charles Father
23 | 'B♭', 'E♭', 'A♭', 'D♭', 'G♭', 'C♭', 'F♭'
24 | ];
25 |
26 | export function byFatherCharlesPitch(a, b) {
27 | const ai = fathercharles.indexOf(a.pitch);
28 | const bi = fathercharles.indexOf(b.pitch);
29 | return ai > bi ? 1 : ai < bi ? -1 : 0;
30 | }
31 |
--------------------------------------------------------------------------------
/Makefile:
--------------------------------------------------------------------------------
1 | DEBUG=
2 |
3 | # Tell make to ignore existing folders and allow overwriting existing files
4 | .PHONY: literal modules
5 |
6 | # Must format with tabs not spaces
7 | #literal:
8 | # deno run --allow-read --allow-env --allow-net --allow-write --allow-run --unstable ./lib/literal/deno/make-literal.js ./ debug
9 |
10 | modules:
11 | @rm -f deno.lock
12 | rm -rf ./build
13 |
14 | deno run --allow-read --allow-write --allow-net --allow-env --allow-run --no-lock --reload --config ./deno.json https://cdn.jsdelivr.net/gh/stephband/fn@master/deno/make-modules.js ./build/ \
15 | module.js \
16 | scribe-music/element.js
17 | deno run --allow-read --allow-write --allow-net --allow-env --allow-run --no-lock --reload --config ./deno.json https://cdn.jsdelivr.net/gh/stephband/fn@master/deno/make-css.js ./build/ \
18 | module.css \
19 | fonts/ash.css \
20 | fonts/bravura.css \
21 | fonts/broadway.css \
22 | fonts/jazz.css \
23 | fonts/leipzig.css \
24 | fonts/petaluma.css \
25 | scribe-music/element.css \
26 | scribe-music/shadow.css
27 |
28 | @rm -f deno.lock
29 |
--------------------------------------------------------------------------------
/fonts/jazz.css:
--------------------------------------------------------------------------------
1 |
2 | @font-face {
3 | font-family: Jazz;
4 | src: url('../fonts/Jazz/finalejazz-webfont.woff2') format('woff2');
5 | font-weight: normal;
6 | font-style: normal;
7 | }
8 |
9 | @font-face {
10 | font-family: JazzText;
11 | src: url('../fonts/JazzText/finalejazztext-webfont.woff2') format('woff2');
12 | font-weight: normal;
13 | font-style: normal;
14 | }
15 |
16 | .scribe-jazz {
17 | font-family: Jazz, JazzText;
18 |
19 | /* Width of standard black note head */
20 | --head-1-size: 0.3375;
21 | }
22 |
23 | .scribe-jazz .note {
24 | --stem-height: 1;
25 | --stem-width: 0.033;
26 | /* Alignment of root of stem relative to baseline */
27 | --stem-align-up: 0.03;
28 | --stem-align-down: -0.04;
29 | }
30 |
31 | .scribe-jazz .head[data-glyph="headX"] {
32 | --stem-align-up: 0.14;
33 | --stem-align-down: -0.12;
34 | }
35 |
36 | .scribe-jazz .note[data-duration^="0.1"] {
37 | /* Temporarily mark notes that should have a 32nd flag, but dont in Jazz font */
38 | color: red !important;
39 | }
40 |
--------------------------------------------------------------------------------
/classes/rest.css:
--------------------------------------------------------------------------------
1 | .rest {
2 | justify-self: center;
3 | line-height: 0.75em;
4 | height: auto;
5 | /* Don't allow extra glyphs, like augmentation dots, to wrap */
6 | white-space: nowrap;
7 | }
8 |
9 | .rest[data-duration="0.125"],
10 | .rest[data-duration="0.1875"] {
11 | line-height: 0.875em;
12 | }
13 |
14 | .rest[data-duration="0.5"],
15 | .rest[data-duration="0.75"] {
16 | line-height: 0.625em;
17 | }
18 |
19 | .rest[data-duration^="0.66"] {
20 | margin-left: 0.1875em;
21 | }
22 |
23 | .rest[data-duration="2"],
24 | .rest[data-duration="3"],
25 | .rest[data-duration="4"],
26 | .rest[data-duration="6"] {
27 | line-height: 0.25em;
28 | }
29 |
30 | .rest[data-duration="2"] {
31 | margin-left: 0.75em;
32 | margin-right: 0.75em;
33 | }
34 |
35 | .rest[data-duration="3"] {
36 | margin-left: 0.5em;
37 | margin-right: 0.5em;
38 | }
39 |
40 | .rest[data-duration="4"] {
41 | margin-left: 0.625em;
42 | margin-right: 0.625em;
43 | }
44 |
45 | .rest[data-duration="6"] {
46 | margin-left: 0.575em;
47 | margin-right: 0.575em;
48 | }
49 |
--------------------------------------------------------------------------------
/classes/timesig.css:
--------------------------------------------------------------------------------
1 | /*
2 | .timesig {
3 | display: grid;
4 | grid-auto-flow: rows;
5 | }
6 |
7 | .timesig > * {
8 | font-weight: bold;
9 | align-self: end;
10 | justify-self: center;
11 | margin: 0;
12 | }
13 |
14 | .timesig > * + * {
15 | align-self: start;
16 | justify-self: center;
17 | }
18 | */
19 |
20 | /* The 'new' way! */
21 |
22 | .timesig,
23 | button.timesig {
24 | display: grid;
25 | }
26 |
27 | .timesig {
28 | row-gap: 0;
29 | max-height: 1.84em;
30 | }
31 |
32 | button.timesig {
33 | padding: 0;
34 | border-width: 0;
35 | }
36 |
37 | button.timesig:hover {
38 | background-color: #eeeeee;
39 | }
40 |
41 | .timesig > sup,
42 | .timesig > sub {
43 | display: block;
44 | position: static;
45 |
46 | font-size: inherit;
47 | line-height: 0.625em;
48 |
49 | margin: 0;
50 | grid-column: 1;
51 | justify-self: center;
52 | vertical-align: baseline;
53 | }
54 |
55 | .timesig > sup {
56 | grid-row: 1;
57 | align-self: end;
58 | }
59 |
60 | .timesig > sub {
61 | grid-row: 2;
62 | align-self: start;
63 | }
64 |
--------------------------------------------------------------------------------
/fonts/README.md:
--------------------------------------------------------------------------------
1 | # Fonts
2 |
3 | Scribe supports (SMuFL)[https://w3c.github.io/smufl/latest/index.html] compliant
4 | fonts. The fonts included in this repo are all originally published under the
5 | (Open Font License)[https://openfontlicense.org/] (as far as the authors are aware).
6 |
7 | - Bravura and Petaluma come from (Steinberg's github account)[https://github.com/steinbergmedia]
8 | - Leipzig comes from the (Verovio repository)[https://github.com/rism-digital/verovio]
9 | - Leland is available in (MuseScore's repo)[https://github.com/MuseScoreFonts/Leland]
10 | - Ash, Jazz and Broadway are originally part of Finale and can be found (here)[https://makemusic.zendesk.com/hc/en-us/articles/1500013053461-MakeMusic-Fonts-and-Licensing-Information]
11 | - MartianMono, by Evil Martians, is not an SMuFL font, and is included for documentation
12 |
13 | Not all these fonts have all glyphs that Scribe supports. There is a test page
14 | at (test/glyphs.html)[../test/glyphs.html]. In particular Ash and Leipzig have
15 | several note heads and chord symbols missing. Your mileage may vary.
16 |
17 | ## Scribe Developers
18 |
19 | Where fonts are available in a git repo, feel free to add them here as submodules
20 | along with a CSS file that includes them.
21 |
--------------------------------------------------------------------------------
/data/so-what.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "So What",
3 | "author": { "name": "Miles Davis" },
4 | "events": [
5 | [0, "key", "C"],
6 | [0, "meter", 4, 1],
7 | [0, "sequence", 2, 0, 32],
8 | [32, "sequence", 2, 0, 32],
9 | [64, "sequence", 2, 0, 32, "transpose", 1],
10 | [96, "sequence", 2, 0, 32]
11 | ],
12 |
13 | "sequences": [{
14 | "id": 1,
15 | "name": "Horns",
16 | "events": [
17 | [0, "chord", 2, "-7", 32],
18 | [2, "note", "B4", 0.1, 1.5],
19 | [2, "note", "G4", 0.1, 1.5],
20 | [2, "note", "D4", 0.1, 1.5],
21 | [2, "note", "A3", 0.1, 1.5],
22 | [3.5, "note", "A4", 0.1, 0.5],
23 | [3.5, "note", "F4", 0.1, 0.5],
24 | [3.5, "note", "C4", 0.1, 0.5],
25 | [3.5, "note", "G3", 0.1, 0.5]
26 | ]
27 | }, {
28 | "id": 2,
29 | "name": "Section",
30 | "events": [
31 | [0, "sequence", 1, 0, 8],
32 | [8, "sequence", 1, 0, 8],
33 | [16, "sequence", 1, 0, 8],
34 | [24, "sequence", 1, 0, 8]
35 | ]
36 | }],
37 |
38 | "notes": {
39 | "links": [{
40 | "text": "So What – Miles Davis – Kind of Blue",
41 | "url": "https://www.youtube.com/watch?v=ylXk1LBvIqU&list=RDylXk1LBvIqU"
42 | }]
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/modules/quantise.js:
--------------------------------------------------------------------------------
1 |
2 | import mod from 'fn/mod.js';
3 |
4 | /**
5 | function quantise(grid, amount, beat)
6 |
7 | A quantise `grid` is an array of positive 64-bit beat numbers in ascending order.
8 | There is no implicit beat 0, it should be included in the grid if desired. The
9 | last number in the array is the grid's duration, not a quantisation beat. The
10 | grid is looped, both forwards and backwards in time, at this duration.
11 |
12 | Quantisation `amount` scales the amount of quantisation applied to `beat`.
13 | **/
14 |
15 | export default function quantise(grid, amount, beat) {
16 | const length = grid.length;
17 | const duration = grid[length - 1];
18 | const remainder = mod(duration, beat);
19 |
20 | // Scan forward until we find a quantisation entry greater than remainder
21 | let n = -1;
22 | while (remainder > grid[++n]);
23 |
24 | // Calculate distance from previous entry
25 | const fromgap = remainder - (n === 0 ?
26 | grid[length - 2] - duration :
27 | grid[n - 1]);
28 |
29 | // Calculate distance form next entry
30 | const togap = remainder - (n === length - 1 ?
31 | grid[0] + duration :
32 | grid[n]);
33 |
34 | // Apply scaled quantisation to nearest entry
35 | return beat - amount * (-togap < fromgap ? togap : fromgap) ;
36 | }
37 |
--------------------------------------------------------------------------------
/assets/lead-sheet-document.css:
--------------------------------------------------------------------------------
1 | html, body { margin: 0; padding: 0; }
2 | body { padding: 1rem; }
3 | a { color: #FE267E; text-decoration: none; }
4 | a:hover, a:focus-visible { text-decoration: underline; }
5 | header { display: grid; grid-template-columns: 1fr auto 1fr; grid-template-rows:; }
6 | header > * { grid-row: 1; margin: 0; }
7 | header > h1 { font-size: 3em; line-height: 2em; grid-column: 2; justify-self: center; align-self: baseline; }
8 | header > form { font-size: 1.2rem; line-height: 2em; grid-column: 3; justify-self: end; align-self: baseline; }
9 | header > .author { font-size: 1.2rem; grid-column: 1; justify-self: start; align-self: baseline; }
10 | header + scribe-music { margin-top: 3em; }
11 | scribe-music { font-size: 1.125rem; }
12 | label { display: inline-block; }
13 | select {
14 | font-family: inherit;
15 | font-size: inherit;
16 | height: auto;
17 |
18 | /* Knock selects out of system rendering look */
19 | border: 0;
20 | background: transparent;
21 | background-image: none;
22 | border-image: none;
23 | appearance: none;
24 | }
25 |
26 | select:focus {
27 | /* Make option text the same size as system menus when select is open */
28 | font-family: system-ui, ui-sans-serif, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
29 | font-size: 0.8125rem;
30 | font-weight: 400;
31 | }
32 |
33 | label + select { margin-left: 0.25em; }
34 |
--------------------------------------------------------------------------------
/test/bars.js:
--------------------------------------------------------------------------------
1 | import 'https://cdn.jsdelivr.net/gh/stephband/fn@1.5.x/deno/deno-2-support.js';
2 |
3 | import Sequence from 'sequence/sequence.js';
4 | import Stave from '../modules/stave.js';
5 | import createBars from '../modules/create-bars.js';
6 |
7 | // Get the stave controller
8 | const stave = Stave['treble'];
9 | /*
10 | console.log(createBars(new Sequence([]), stave));
11 |
12 | console.log(createBars(new Sequence([
13 | [10, "note", "C4", 0.1, 4]
14 | ]), stave));
15 |
16 | console.log(createBars(new Sequence([
17 | [0, "meter", 4, 1],
18 | [10, "note", "C4", 0.1, 4]
19 | ]), stave));
20 |
21 | console.log(createBars(new Sequence([
22 | [0, "key", "Bb"],
23 | [0, "meter", 4, 1],
24 | [0, "note", "C4", 0.1, 0.67],
25 | [0.67, "note", "C4", 0.1, 0.67],
26 | [1.33, "note", "C4", 0.1, 0.67]
27 | ]), stave));
28 |
29 | console.log(createBars(new Sequence([
30 | [0, "key", "Bb"],
31 | [0, "meter", 4, 1],
32 | [1, "note", "G3", 0.1, 3],
33 | [2, "note", "C4", 0.1, 4],
34 | [4, "note", "E4", 0.1, 2],
35 | [6, "note", "G4", 0.1, 4],
36 | [8, "note", "C#5", 0.1, 3]
37 | ]), stave));
38 | */
39 |
40 | console.log(createBars(new Sequence([
41 | [0, "key", "Bb"],
42 | [0, "meter", 4, 1],
43 | [1.333, "note", "D4", 0.1, 0.333],
44 | [1.667, "note", "C4", 0.1, 0.333],
45 | [1.667, "note", "E4", 0.1, 0.333],
46 | [1.667, "note", "G4", 0.1, 0.333],
47 | [2, "note", "C5", 0.1, 1]
48 | ]), stave));
49 |
--------------------------------------------------------------------------------
/modules/event.js:
--------------------------------------------------------------------------------
1 |
2 | import { toRootNumber, toNoteNumber } from 'midi/note.js';
3 | import mod12 from './number/mod-12.js';
4 |
5 | /* Event ids */
6 |
7 | const $id = Symbol('scribe-id');
8 |
9 | const map = {};
10 |
11 | let id = 0;
12 |
13 | const weak = new WeakMap();
14 |
15 | export function identify(event) {
16 | // Get original event
17 | while (event.event) event = event.event;
18 |
19 | if (event[$id]) return event[$id];
20 | event[$id] = ++id;
21 | map[id] = event;
22 | return id;
23 | }
24 |
25 | export function find(events, id) {
26 | return events.find((event) => (event[$id] === id));
27 | }
28 |
29 | export function retrieve(id) {
30 | return map[id];
31 | }
32 |
33 | export function move(n, event) {
34 | event[0] = event[0] + n;
35 | return event;
36 | }
37 |
38 | export function transpose(n, event) {
39 | switch (event[1]) {
40 | case "chord":
41 | case "key":
42 | event[2] = mod12(toRootNumber(event[2]) + n);
43 | break;
44 | case "note":
45 | case "start":
46 | case "stop":
47 | event[2] = toNoteNumber(event[2]) + n;
48 | break;
49 | case "sequence":
50 | console.log('TODO: transpose sequence');
51 | break;
52 | }
53 |
54 | return event;
55 | }
56 |
57 | export function durate(n, event) {
58 | switch (event[1]) {
59 | case "chord":
60 | case "note":
61 | case "sequence":
62 | event[4] = event[4] + n <= 0 ?
63 | event[4] :
64 | event[4] + n ;
65 | break;
66 | }
67 |
68 | return event;
69 | }
70 |
--------------------------------------------------------------------------------
/modules/spelling.js:
--------------------------------------------------------------------------------
1 |
2 | import nothing from 'fn/nothing.js';
3 | import { noteNames, toNoteNumber, toRootNumber, toNoteOctave } from 'midi/note.js';
4 | import mod12 from './number/mod-12.js';
5 | import keys from './keys.js';
6 | import { rpitch } from './pitch.js';
7 |
8 | const accidentals = {
9 | '-2': '𝄫',
10 | '-1': '♭',
11 | '0': '',
12 | '1': '♯',
13 | '2': '𝄪'
14 | };
15 |
16 | export function spellRoot(key, pitch) {
17 | const keyData = keys[key];
18 | const n = toRootNumber(pitch);
19 | const a = keyData.spellings[mod12(n)];
20 | return noteNames[mod12(n - a)] + accidentals[a];
21 | }
22 |
23 | export function spellPitch(key, pitch) {
24 | const keyData = keys[key];
25 | let n, a, o;
26 |
27 | if (typeof pitch === 'string') {
28 | let [notename, letter, accidental, octave] = rpitch.exec(pitch) || [pitch];
29 | if (octave) {
30 | // pitch is note name like "C4", deconstruct it and put it back together
31 | n = toNoteNumber(pitch);
32 | a = keyData.spellings[mod12(n)];
33 | o = toNoteOctave(n - a);
34 | }
35 | }
36 | else {
37 | // pitch is a number
38 | n = pitch;
39 | a = keyData.spellings[mod12(n)];
40 | o = toNoteOctave(n - a);
41 | }
42 |
43 | // key.spellings makes sure name is a natural note name
44 | const name = noteNames[mod12(n - a)];
45 | const accidental = accidentals[a];
46 |
47 | if (window.DEBUG && name === undefined) {
48 | throw new Error('Incorrect spelling for pitch number ' + n + ': "' + name + '"');
49 | }
50 |
51 | return name + accidental + o;
52 | }
53 |
--------------------------------------------------------------------------------
/card/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Scribe
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 | You are listening to the test player on labs.cruncher.ch
29 |
30 |
31 |
--------------------------------------------------------------------------------
/modules/parse.js:
--------------------------------------------------------------------------------
1 |
2 | //import parseABC from './parse/parse-abc.js';
3 | import parseSequenceText from './parse/parse-sequence-text.js';
4 |
5 |
6 | /* Parse data
7 | (TODO: Yeah, API requests need tidied up) */
8 |
9 | function fromGist(gist) {
10 | // Get first file
11 | const file = gist.files[Object.keys(gist.files)[0]];
12 | return parseSource(file.type, file.content);
13 | }
14 |
15 | function fromTheSession(object) {
16 | // Get first file
17 | const song = object.settings.find(matches({ id: 13324 })) || object.settings[0];
18 | return parseSource('abc', song.abc);
19 | }
20 |
21 | export default function parseSource(type, source) {
22 | // source is an object
23 | if (typeof source === 'object') {
24 | // source is json from api.github.com/gists/
25 | return source.files ? fromGist(source) :
26 | // source is from thesession.org
27 | source.settings ? fromTheSession() :
28 | // source is an events array
29 | Array.isArray(source) ? { id: 0, events: source } :
30 | // source is a sequence object
31 | source ;
32 | }
33 | // Data is ABC
34 | /*
35 | else if (type === 'abc' || type === 'text/x-abc') {
36 | // Strip space following line breaks
37 | const music = parseABC(source.replace(/\n\s+/g, '\n'));
38 | return music.sequences[0];
39 | }
40 | */
41 | // Data is step sequence text
42 | else if (type === 'sequence' || type === 'text/plain') {
43 | return { events: parseSequenceText(source) };
44 | }
45 | // Data is JSON
46 | else {
47 | const events = JSON.parse(source);
48 | return Array.isArray(events) ?
49 | { id: 0, events } :
50 | events ;
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/classes/stave/alto.css:
--------------------------------------------------------------------------------
1 | .alto-stave {
2 | padding-top: 0.0625em;
3 | padding-bottom: 0.0625em;
4 | grid-template-rows:
5 | [B5 max] 0.125em
6 | [A5 chords] 0.125em
7 | [G5] 0.125em
8 | [F5] 0.125em
9 | [E5] 0.125em
10 | [D5] 0.125em
11 | [C5] 0.125em
12 | [B4] 0.125em
13 | [A4] 0.125em
14 | [G4 stave-top] 0.125em
15 | [F4] 0.125em
16 | [E4] 0.125em
17 | [D4] 0.125em
18 | [C4 stave-center] 0.125em
19 | [B3] 0.125em
20 | [A3] 0.125em
21 | [G3 bottom-space] 0.125em
22 | [F3 bottom] 0.125em
23 | [E3 stave-bottom] 0.125em
24 | [D3 barcount] 0.125em
25 | [C3] 0.125em
26 | [B2] 0.125em
27 | [A2] 0.125em
28 | [G2] 0.125em
29 | [F2] 0.125em
30 | [E2] 0.125em
31 | [D2 lyrics] 0.125em
32 | [min];
33 | }
34 |
35 |
36 | /* Key signature */
37 |
38 | .alto-stave > [data-pitch="F♭"] { grid-row-start: F3; }
39 | .alto-stave > [data-pitch="C♭"] { grid-row-start: C4; }
40 | .alto-stave > [data-pitch="G♭"] { grid-row-start: G3; }
41 | .alto-stave > [data-pitch="D♭"] { grid-row-start: D4; }
42 | .alto-stave > [data-pitch="A♭"] { grid-row-start: A3; }
43 | .alto-stave > [data-pitch="E♭"] { grid-row-start: E4; }
44 | .alto-stave > [data-pitch="B♭"] { grid-row-start: B3; }
45 | .alto-stave > [data-pitch="F♯"] { grid-row-start: F4; }
46 | .alto-stave > [data-pitch="C♯"] { grid-row-start: C4; }
47 | .alto-stave > [data-pitch="G♯"] { grid-row-start: G4; }
48 | .alto-stave > [data-pitch="D♯"] { grid-row-start: D4; }
49 | .alto-stave > [data-pitch="A♯"] { grid-row-start: A3; }
50 | .alto-stave > [data-pitch="E♯"] { grid-row-start: E4; }
51 | .alto-stave > [data-pitch="B♯"] { grid-row-start: B3; }
52 |
--------------------------------------------------------------------------------
/classes/stave/bass.css:
--------------------------------------------------------------------------------
1 | .bass-stave {
2 | padding-top: 0.0625em;
3 | padding-bottom: 0.0625em;
4 | grid-template-rows:
5 | [C5 max] 0.125em
6 | [B4] 0.125em
7 | [A4 chords] 0.125em
8 | [G4] 0.125em
9 | [F4] 0.125em
10 | [E4] 0.125em
11 | [D4] 0.125em
12 | [C4] 0.125em
13 | [B3] 0.125em
14 | [A3 stave-top] 0.125em
15 | [G3] 0.125em
16 | [F3] 0.125em
17 | [E3] 0.125em
18 | [D3 stave-center] 0.125em
19 | [C3] 0.125em
20 | [B2] 0.125em
21 | [A2] 0.125em
22 | [G2] 0.125em
23 | [F2 stave-bottom] 0.125em
24 | [E2] 0.125em
25 | [D2] 0.125em
26 | [C2] 0.125em
27 | [B1] 0.125em
28 | [A1] 0.125em
29 | [G1] 0.125em
30 | [F1] 0.125em
31 | [E1 lyrics] 0.125em
32 | [min];
33 | }
34 |
35 | /* Map unoctaved pitches to stave lines for key signatures. */
36 | .bass-stave > [data-pitch="F♭"] { grid-row-start: F2; }
37 | .bass-stave > [data-pitch="C♭"] { grid-row-start: C3; }
38 | .bass-stave > [data-pitch="G♭"] { grid-row-start: G2; }
39 | .bass-stave > [data-pitch="D♭"] { grid-row-start: D3; }
40 | .bass-stave > [data-pitch="A♭"] { grid-row-start: A2; }
41 | .bass-stave > [data-pitch="E♭"] { grid-row-start: E3; }
42 | .bass-stave > [data-pitch="B♭"] { grid-row-start: B2; }
43 | .bass-stave > [data-pitch="F♯"] { grid-row-start: F3; }
44 | .bass-stave > [data-pitch="C♯"] { grid-row-start: C3; }
45 | .bass-stave > [data-pitch="G♯"] { grid-row-start: G3; }
46 | .bass-stave > [data-pitch="D♯"] { grid-row-start: D3; }
47 | .bass-stave > [data-pitch="A♯"] { grid-row-start: A2; }
48 | .bass-stave > [data-pitch="E♯"] { grid-row-start: E3; }
49 | .bass-stave > [data-pitch="B♯"] { grid-row-start: B2; }
50 |
--------------------------------------------------------------------------------
/classes/stave/treble-up.css:
--------------------------------------------------------------------------------
1 | .treble-up-stave {
2 | padding-top: 0.0625em;
3 | padding-bottom: 0.0625em;
4 | grid-template-rows:
5 | [max A7] 0.125em
6 | [G7] 0.125em
7 | [F7 chords] 0.125em
8 | [E7] 0.125em
9 | [D7] 0.125em
10 | [C7] 0.125em
11 | [B6] 0.125em
12 | [A6] 0.125em
13 | [G6] 0.125em
14 | [F6 stave-top] 0.125em
15 | [E6] 0.125em
16 | [D6] 0.125em
17 | [C6] 0.125em
18 | [B5 stave-center] 0.125em
19 | [A5] 0.125em
20 | [G5] 0.125em
21 | [F5] 0.125em
22 | [E5] 0.125em
23 | [D5 stave-bottom] 0.125em
24 | [C5 barcount] 0.125em
25 | [B4] 0.125em
26 | [A4] 0.125em
27 | [G4] 0.125em
28 | [F4] 0.125em
29 | [E4] 0.125em
30 | [D4] 0.125em
31 | [C4 lyrics] 0.125em
32 | [min];
33 | }
34 |
35 | /* Map unoctaved pitches to stave lines for key signatures. */
36 | .treble-up-stave > [data-pitch="F♭"] { grid-row-start: F5; }
37 | .treble-up-stave > [data-pitch="C♭"] { grid-row-start: C6; }
38 | .treble-up-stave > [data-pitch="G♭"] { grid-row-start: G5; }
39 | .treble-up-stave > [data-pitch="D♭"] { grid-row-start: D6; }
40 | .treble-up-stave > [data-pitch="A♭"] { grid-row-start: A5; }
41 | .treble-up-stave > [data-pitch="E♭"] { grid-row-start: E6; }
42 | .treble-up-stave > [data-pitch="B♭"] { grid-row-start: B5; }
43 | .treble-up-stave > [data-pitch="F♯"] { grid-row-start: F6; }
44 | .treble-up-stave > [data-pitch="C♯"] { grid-row-start: C6; }
45 | .treble-up-stave > [data-pitch="G♯"] { grid-row-start: G6; }
46 | .treble-up-stave > [data-pitch="D♯"] { grid-row-start: D6; }
47 | .treble-up-stave > [data-pitch="A♯"] { grid-row-start: A5; }
48 | .treble-up-stave > [data-pitch="E♯"] { grid-row-start: E6; }
49 | .treble-up-stave > [data-pitch="B♯"] { grid-row-start: B5; }
50 |
51 |
--------------------------------------------------------------------------------
/classes/stave/treble-down.css:
--------------------------------------------------------------------------------
1 | .treble-down-stave {
2 | padding-top: 0.0625em;
3 | padding-bottom: 0.0625em;
4 | grid-template-rows:
5 | [A5 max] 0.125em
6 | [G5] 0.125em
7 | [F5 chords] 0.125em
8 | [E5] 0.125em
9 | [D5] 0.125em
10 | [C5] 0.125em
11 | [B4] 0.125em
12 | [A4] 0.125em
13 | [G4] 0.125em
14 | [F4 stave-top] 0.125em
15 | [E4] 0.125em
16 | [D4] 0.125em
17 | [C4] 0.125em
18 | [B3 stave-center] 0.125em
19 | [A3] 0.125em
20 | [G3] 0.125em
21 | [F3] 0.125em
22 | [E3] 0.125em
23 | [D3 stave-bottom] 0.125em
24 | [C3] 0.125em
25 | [B2] 0.125em
26 | [A2] 0.125em
27 | [G2] 0.125em
28 | [F2] 0.125em
29 | [E2] 0.125em
30 | [D2] 0.125em
31 | [C2 lyrics] 0.125em
32 | [min];
33 | }
34 |
35 | /* Map unoctaved pitches to stave lines for key signatures. */
36 | .treble-down-stave > [data-pitch="F♭"] { grid-row-start: F3; }
37 | .treble-down-stave > [data-pitch="C♭"] { grid-row-start: C4; }
38 | .treble-down-stave > [data-pitch="G♭"] { grid-row-start: G3; }
39 | .treble-down-stave > [data-pitch="D♭"] { grid-row-start: D4; }
40 | .treble-down-stave > [data-pitch="A♭"] { grid-row-start: A3; }
41 | .treble-down-stave > [data-pitch="E♭"] { grid-row-start: E4; }
42 | .treble-down-stave > [data-pitch="B♭"] { grid-row-start: B3; }
43 | .treble-down-stave > [data-pitch="F♯"] { grid-row-start: F4; }
44 | .treble-down-stave > [data-pitch="C♯"] { grid-row-start: C4; }
45 | .treble-down-stave > [data-pitch="G♯"] { grid-row-start: G4; }
46 | .treble-down-stave > [data-pitch="D♯"] { grid-row-start: D4; }
47 | .treble-down-stave > [data-pitch="A♯"] { grid-row-start: A3; }
48 | .treble-down-stave > [data-pitch="E♯"] { grid-row-start: E4; }
49 | .treble-down-stave > [data-pitch="B♯"] { grid-row-start: B3; }
50 |
--------------------------------------------------------------------------------
/classes/stave/treble.css:
--------------------------------------------------------------------------------
1 | .treble-stave {
2 | padding-top: 0.0625em;
3 | padding-bottom: 0.0625em;
4 | grid-template-rows:
5 | [A6 max] 0.125em
6 | [G6 chords] 0.125em
7 | [F6] 0.125em
8 | [E6] 0.125em
9 | [D6] 0.125em
10 | [C6] 0.125em
11 | [B5] 0.125em
12 | [A5] 0.125em
13 | [G5] 0.125em
14 | [F5 stave-top] 0.125em
15 | [E5] 0.125em
16 | [D5] 0.125em
17 | [C5] 0.125em
18 | [B4 stave-center] 0.125em
19 | [A4] 0.125em
20 | [G4] 0.125em
21 | [F4 bottom-space] 0.125em
22 | [E4 bottom] 0.125em
23 | [D4 stave-bottom] 0.125em
24 | [C4 barcount] 0.125em
25 | [B3] 0.125em
26 | [A3] 0.125em
27 | [G3] 0.125em
28 | [F3] 0.125em
29 | [E3] 0.125em
30 | [D3] 0.125em
31 | [C3 lyrics] 0.125em
32 | [min];
33 | }
34 |
35 | /*
36 | .treble-stave::before {
37 | content: '\E050';
38 | grid-row: G4;
39 | }
40 | */
41 |
42 | /* Key signature */
43 |
44 | .treble-stave > [data-pitch="F♭"] { grid-row-start: F4; }
45 | .treble-stave > [data-pitch="C♭"] { grid-row-start: C5; }
46 | .treble-stave > [data-pitch="G♭"] { grid-row-start: G4; }
47 | .treble-stave > [data-pitch="D♭"] { grid-row-start: D5; }
48 | .treble-stave > [data-pitch="A♭"] { grid-row-start: A4; }
49 | .treble-stave > [data-pitch="E♭"] { grid-row-start: E5; }
50 | .treble-stave > [data-pitch="B♭"] { grid-row-start: B4; }
51 | .treble-stave > [data-pitch="F♯"] { grid-row-start: F5; }
52 | .treble-stave > [data-pitch="C♯"] { grid-row-start: C5; }
53 | .treble-stave > [data-pitch="G♯"] { grid-row-start: G5; }
54 | .treble-stave > [data-pitch="D♯"] { grid-row-start: D5; }
55 | .treble-stave > [data-pitch="A♯"] { grid-row-start: A4; }
56 | .treble-stave > [data-pitch="E♯"] { grid-row-start: E5; }
57 | .treble-stave > [data-pitch="B♯"] { grid-row-start: B4; }
58 |
--------------------------------------------------------------------------------
/classes/chord.css:
--------------------------------------------------------------------------------
1 |
2 | /* Chord symbols are awkward. Sizing and alignment of various glyphs varies
3 | enormously between fonts. */
4 |
5 | .chord-abbr {
6 | vertical-align: baseline;
7 | text-decoration: none;
8 | line-height: 1em;
9 | /*letter-spacing: 0.002em;*/
10 | }
11 |
12 | .chord-abbr .chord-maj {
13 | font-size: inherit;
14 | vertical-align: 0em;
15 | }
16 |
17 | .chord-abbr .chord-min {
18 | font-size: inherit;
19 | vertical-align: 0.1875em;
20 | }
21 |
22 | .chord-abbr .chord-halfdim {
23 | font-size: inherit;
24 | vertical-align: 0.3125em;
25 | }
26 |
27 | .chord-abbr .chord-dim {
28 | font-size: inherit;
29 | vertical-align: 0.3125em;
30 | }
31 |
32 | .chord-abbr .chord-aug {
33 | font-size: inherit;
34 | vertical-align: 0.1875em;
35 | }
36 |
37 | .chord-abbr .chord-root,
38 | .chord-abbr .chord-ext,
39 | .chord-abbr .chord-bass {
40 | font-size: 0.75em;
41 | }
42 |
43 | .chord-abbr .chord-bass {
44 | vertical-align: -0.25em;
45 | }
46 |
47 | .chord-abbr .chord-flat {
48 | vertical-align: 0.3em;
49 | margin-left: 0.05em;
50 | }
51 |
52 | .chord-abbr .chord-sharp {
53 | vertical-align: 0.333333333em;
54 | }
55 |
56 | .chord-abbr sup {
57 | font-size: 0.75em;
58 | font-weight: 700;
59 | vertical-align: 0.333333333em;
60 | margin-left: 0.04em;
61 | }
62 |
63 | .chord-abbr sub {
64 | font-size: 0.75em;
65 | vertical-align: 0;
66 | }
67 |
68 | .chord-abbr .chord-brackets {
69 | font-size: 0.66666667em;
70 | vertical-align: 0.4em;
71 | margin-left: 0.1em;
72 | }
73 |
74 | .chord-ext-∆ {
75 | font-size: 0.875em;
76 | vertical-align: 0;
77 | }
78 |
79 | .chord-ext-7 {
80 | font-size: 0.833333333em;
81 | vertical-align: 0.166666667em;
82 | }
83 |
84 | .chord-ext-- {
85 | font-size: 1.125em;
86 | vertical-align: 0;
87 | }
88 |
89 | .chord-ext-ø {
90 | font-size: 1em;
91 | vertical-align: 0.15em;
92 | }
93 |
94 |
95 |
--------------------------------------------------------------------------------
/data/caravan-drums.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "Caravan – drum transcription",
3 |
4 | "events": [
5 | [0, "sequence", 1, 0, 32]
6 | ],
7 |
8 | "sequences": [{
9 | "id": 1,
10 | "name": "Intro",
11 | "events": [
12 | [0, "sequence", 2, 0, 8],
13 | [8, "sequence", 2, 0, 8],
14 | [16, "sequence", 2, 0, 8],
15 | [24, "sequence", 2, 0, 8]
16 | ]
17 | }, {
18 | "id": 2,
19 | "events": [
20 | [0, "meter", 4, 1],
21 | [1, "note", "A♭2", 0.25, 0.5],
22 | [3, "note", "A♭2", 0.25, 0.5],
23 | [5, "note", "A♭2", 0.25, 0.5],
24 | [7, "note", "A♭2", 0.25, 0.5],
25 | [0, "note", "B1", 0.3, 1],
26 | [1.5, "note", "B1", 0.3, 0.5],
27 | [3, "note", "B1", 0.3, 1],
28 | [4.5, "note", "B1", 0.3, 0.5],
29 | [6, "note", "B1", 0.3, 1],
30 | [7, "note", "B1", 0.3, 1],
31 | [1, "note", "D2", 0.3, 0.5],
32 | [2.5, "note", "D2", 0.3, 0.5],
33 | [4, "note", "D2", 0.3, 0.5],
34 | [5.5, "note", "D2", 0.3, 0.5],
35 | [7, "note", "D2", 0.3, 1],
36 | [0, "note", "F3", 0.25, 1],
37 | [1, "note", "F3", 0.25, 0.5],
38 | [1.5, "note", "F3", 0.25, 0.5],
39 | [2, "note", "F3", 0.25, 1],
40 | [3, "note", "F3", 0.25, 0.5],
41 | [3.5, "note", "F3", 0.25, 0.5],
42 | [4, "note", "F3", 0.25, 1],
43 | [5, "note", "F3", 0.25, 0.5],
44 | [5.5, "note", "F3", 0.25, 0.5],
45 | [6, "note", "F3", 0.25, 1],
46 | [7, "note", "F3", 0.25, 1]
47 | ]
48 | }],
49 |
50 | "notes": {
51 | "links": [{
52 | "url": "https://www.youtube.com/watch?v=JsVMXCirqto&list=RDJsVMXCirqto",
53 | "text": "Caravan – Wynton Marsalis – Standard Time"
54 | }]
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/modules/parse/parse-sequence-text.js:
--------------------------------------------------------------------------------
1 | const lengths = {
2 | /* Length of data following 'type' */
3 | chord: 3, /* chord duration, not used, chords can be either 2 or 3 long */
4 | note: 3, /* pitch gain duration */
5 | key: 1, /* A-G */
6 | meter: 2, /* duration division */
7 | rate: 1, /* rate */
8 | lyric: 2, /* string, duration */
9 | };
10 |
11 | const rnote = /^[ABCDEFG][b♭#♯]{0,1}-?\d$/;
12 | const rroot = /^[ABCDEFG][b♭#♯]{0,1}/;
13 |
14 |
15 | export default function parseSequenceText(source) {
16 | const data = source.trim().split(/\s+/);
17 | const events = [];
18 |
19 | let n = -1;
20 | while (data[++n] !== undefined) {
21 | let time = Number(data[n]);
22 | let type = data[++n];
23 |
24 | // Automatically detect type. If type has been omitted, it will match
25 | // a value for note pitch or chord. Set type, rewind n.
26 | if (rnote.test(type)) {
27 | type = 'note';
28 | --n;
29 | }
30 | else if (rroot.test(type)) {
31 | type = 'chord';
32 | --n;
33 | }
34 |
35 | let event = [time, type];
36 | let m = lengths[type];
37 | if (m === undefined) {
38 | throw new TypeError('Unrecognised type "' + type + '" in sequence data');
39 | }
40 |
41 | if (type === 'chord') {
42 | // Detect and parse chord root/extension written as one
43 | // parameter "C7" or as two "C", "7"
44 | let root = rroot.exec(data[++n])[0];
45 | let extension = data[n].slice(root.length) || data[++n];
46 | let duration = Number(data[++n]);
47 | event.push(root, extension, duration);
48 | }
49 | else {
50 | // Push values into event, converting to number where possible
51 | while (m--) {
52 | let value = Number(data[++n]);
53 | event.push(Number.isNaN(value) ? data[n] : value);
54 | }
55 | }
56 |
57 | events.push(event);
58 | }
59 |
60 | return events;
61 | }
62 |
--------------------------------------------------------------------------------
/classes/stave/percussion.css:
--------------------------------------------------------------------------------
1 |
2 | /*
3 | Useful on drum notation:
4 | https://drumbeatsonline.com/blog/drum-notation-sheet-music-how-to-read-it
5 | https://web.mit.edu/merolish/Public/drums.pdf
6 | */
7 |
8 | .percussion-stave {
9 | grid-template-rows:
10 | [max cymbals-beam] 0.125em
11 | 0.125em
12 | [chords] 0.125em
13 | 0.125em
14 | 0.125em
15 | [stave-top] 0.125em
16 | 0.125em
17 | [stave-center stave-top] 0.125em
18 | 0.125em
19 | 0.125em
20 | [stave-bottom] 0.125em
21 | 0.125em
22 | 0.125em
23 | 0.125em
24 | [drums-beam lyrics] 0.125em
25 | [min];
26 |
27 | /*background-image: url('data:image/svg+xml;utf8,\
28 | \
29 | \
30 | ');
31 |
32 | background-size: 100% 0.625em;*/
33 |
34 | background-image: linear-gradient(to bottom,
35 | transparent 0%,
36 | transparent calc(0.125em - 0.5 * var(--line-thickness)), currentcolor calc(0.125em - 0.5 * var(--line-thickness)), currentcolor calc(0.125em + 0.5 * var(--line-thickness)), transparent calc(0.125em + 0.5 * var(--line-thickness)),
37 | transparent 100%
38 | );
39 |
40 | background-size: 100% 0.25em;
41 | }
42 |
43 | /* TEMP */
44 | .percussion-stave > .ledge,
45 | /* Just a precaution. */
46 | .percussion-stave > .acci {
47 | display: none !important;
48 | }
49 |
50 |
51 | /* Moved all pitched symbols to the central stave line. Selectors must match
52 | the importance of data-pitch selectors found on the base class .stave, hence
53 | the double attribute selector. */
54 | .percussion-stave > [data-pitch][data-pitch] {
55 | grid-row: stave-center;
56 | }
57 |
58 | .percussion-stave > .rest[data-duration="4"],
59 | .percussion-stave > .rest[data-duration="6"] {
60 | grid-row: stave-center;
61 | }
62 |
--------------------------------------------------------------------------------
/fonts/Petaluma/redist/FONTLOG.txt:
--------------------------------------------------------------------------------
1 | FONTLOG for the Petaluma font
2 | =============================
3 |
4 | This file provides detailed information on the Petaluma Font Software. This information should be distributed along with the Petaluma fonts and any derivative works.
5 |
6 | The Petaluma Font Software is a family consisting of three fonts: Petaluma, Petaluma Text, and Petaluma Script.
7 |
8 |
9 | Basic font information
10 | ----------------------
11 | Petaluma is a Unicode typeface designed by Steinberg for its Dorico music notation and scoring application.
12 |
13 | It is compliant with version 1.3 of the Standard Music Font Layout (SMuFL), a community-driven standard for how music symbols should be laid out in the Unicode Private Use Area (PUA) in the Basic Multilingual Plane (BMP) for compatibility between different scoring applications.
14 |
15 | More information about Dorico can be found at:
16 |
17 | http://www.steinberg.net/dorico
18 |
19 | The latest version of the SMuFL specification can be found at:
20 |
21 | https://w3c.github.io/smufl/gitbook/
22 |
23 |
24 | Note for developers
25 | -------------------
26 | If you are intending to use Petaluma as part of your own software application, please refer to the SMuFL specification for useful information about Petaluma's metrics and how glyphs are registered:
27 |
28 | https://w3c.github.io/smufl/gitbook/specification/index.html
29 |
30 |
31 | Changelog
32 | ---------
33 |
34 | 29 May 2018 (Anthony Hughes & Daniel Spreadbury)
35 | – Initial release
36 |
37 | 3 December 2018 (Anthony Hughes) Petaluma version 1.055 / Petaluma Script version 1.10
38 | – Repositioned beat units for metronome marks
39 | – Larger accidentals for chord symbols
40 |
41 | 10 May 2020 (Anthony Hughes) Petaluma version 1.063
42 | – Added handwritten-style glyphs for Figured Bass and Multi-Segment Lines ranges.
43 |
44 | 10 June 2020 (Anthony Hughes) Petaluma version 1.064
45 | – Fixed rotation of downward arpeggio swash and arrow.
46 |
47 | 27 January 2021 (Daniel Spreadbury) Petaluma version 1.065
48 | – Fixed registration of reversedBracketBottom (U+E006).
49 | – Increased size of 'Dynamics' range (U+E520–U+E54F) in Petaluma Text.
--------------------------------------------------------------------------------
/data/blue-in-green.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "Blue in Green",
3 | "author": { "name": "Miles Davis / Bill Evans" },
4 | "events": [
5 | [0, "key", "F"],
6 | [0, "meter", 4, 1],
7 | [0, "sequence", 1, 0, 40]
8 | ],
9 |
10 | "sequences": [{
11 | "id": 1,
12 | "name": "Head",
13 | "events": [
14 | [0, "chord", "Bb", "∆♯11", 4],
15 | [4, "chord", "A", "7alt", 4],
16 | [8, "chord", "D", "-7", 2],
17 | [10, "chord", "Db", "7", 2],
18 | [12, "chord", "C", "-7", 2],
19 | [14, "chord", "F", "7♭9", 2],
20 | [16, "chord", "Bb", "∆♯11", 4],
21 | [20, "chord", "A", "7♭13", 4],
22 | [24, "chord", "D", "-7", 4],
23 | [28, "chord", "E", "7alt", 4],
24 | [32, "chord", "A", "-7", 4],
25 | [36, "chord", "D", "-7", 4],
26 |
27 | [0, "note", "E5", 0.5, 3],
28 | [3, "note", "D5", 0.5, 1],
29 | [4, "note", "C5", 0.5, 3],
30 | [7, "note", "Bb4", 0.5, 1],
31 | [8, "note", "A4", 0.5, 3],
32 | [11, "note", "G4", 0.5, 1],
33 | [12, "note", "F4", 0.5, 1],
34 | [13, "note", "D5", 0.5, 3],
35 | [16, "note", "E4", 0.5, 1.5],
36 | [17.5, "note", "D4", 0.5, 0.5],
37 | [18, "note", "C#4", 0.5, 0.5],
38 | [18.5, "note", "D4", 0.5, 0.5],
39 | [19, "note", "F4", 0.5, 0.5],
40 | [19.5, "note", "A4", 0.5, 0.5],
41 | [20, "note", "C5", 0.5, 3],
42 | [23, "note", "A4", 0.5, 1],
43 | [24, "note", "G4", 0.5, 3],
44 | [27, "note", "F4", 0.5, 1],
45 | [28, "note", "C5", 0.5, 3],
46 | [31, "note", "G#4", 0.5, 1],
47 | [32, "note", "B4", 0.5, 3],
48 | [35, "note", "A4", 0.5, 1],
49 | [36, "note", "F5", 0.5, 3],
50 | [39, "note", "C#5", 0.5, 1]
51 | ]
52 | }],
53 |
54 | "notes": {
55 | "links": [{
56 | "text": "Blue In Green – Miles Davis – Kind of Blue",
57 | "url": "https://www.youtube.com/watch?v=TLDflhhdPCg&list=RDTLDflhhdPCg"
58 | }]
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/classes/tuplet.css:
--------------------------------------------------------------------------------
1 |
2 | .tuplet {
3 | /* Skew angle of tuplet bracket */
4 | --angle: 0deg;
5 |
6 | display: grid;
7 | text-align: center;
8 | align-self: center;
9 | grid-template-columns: 1fr min-content 1fr;
10 | /* Gap betweeen bracket and number and number and bracket */
11 | column-gap: 0.133333em;
12 | justify-items: stretch;
13 | align-items: baseline;
14 | margin-left: calc(-2em * var(--stem-width));
15 | margin-right: calc(-2em * var(--stem-width));
16 | }
17 |
18 | .up-tuplet[data-rhythm^="1"] {
19 | /* Move the left position by one head width to center number on beam */
20 | margin-left: calc(1em * var(--head-1-size) - 3em * var(--stem-width));
21 | }
22 |
23 | .tuplet::before,
24 | .tuplet::after {
25 | display: inline-block;
26 | align-self: center;
27 | background-color: transparent;
28 | background-clip: content-box;
29 | border-top: 0.05em solid currentcolor;
30 | border-radius: 0.03em;
31 | height: 0.21875em;
32 | transform: skew(0, var(--angle));
33 | /* Center tuplet bracket to text. */
34 | position: relative;
35 | top: -0.075em;
36 | }
37 |
38 | .tuplet::before {
39 | content: '';
40 | border-left: 0.05em solid currentcolor;
41 | border-top-left-radius: 0.05em;
42 | /* Move transform origin to tuplet centre, to the right of before */
43 | transform-origin: calc(100% + 0.2333em) 50%;
44 | }
45 |
46 | .tuplet::after {
47 | content: '';
48 | border-right: 0.05em solid currentcolor;
49 | border-top-right-radius: 0.05em;
50 | /* Move transform origin to tuplet centre, to the left of after */
51 | transform-origin: -0.2333em 50%;
52 | }
53 |
54 | .up-tuplet[data-duration^="0."][data-rhythm^="111"],
55 | .up-tuplet[data-duration="1"][data-rhythm^="111"] {
56 | /* Center tuplet numbers on top of beams. A tuplet is definitely beamed if
57 | its duration is 1 or less and its rhythm is full of 1s. */
58 | grid-template-columns: min-content;
59 | justify-content: center;
60 | }
61 |
62 | .up-tuplet[data-duration^="0."][data-rhythm^="111"]::before,
63 | .up-tuplet[data-duration^="0."][data-rhythm^="111"]::after,
64 | .up-tuplet[data-duration="1"][data-rhythm^="111"]::before,
65 | .up-tuplet[data-duration="1"][data-rhythm^="111"]::after {
66 | /* Remove tuplet brackets from beamed tuplets */
67 | content: none;
68 | }
69 |
70 | /*
71 | .down-tuplet::before,
72 | .down-tuplet::after {
73 | border-top: 0;
74 | border-bottom: 0.05em solid currentcolor;
75 | position: relative;
76 | top: -0.26667em;
77 | }
78 | */
79 |
--------------------------------------------------------------------------------
/modules/stave.js:
--------------------------------------------------------------------------------
1 |
2 | import * as glyphs from "./glyphs.js";
3 | import Stave from './stave/stave.js';
4 | import DrumStave from './stave/drum.js';
5 | import PercussionStave from './stave/percussion.js';
6 | import PianoStave from './stave/piano.js';
7 |
8 |
9 | const assign = Object.assign;
10 |
11 |
12 | class TrebleUpStave extends Stave {
13 | type = 'treble-up';
14 | clef = glyphs.clefTrebleUp;
15 | rows = ["A7", "G7", "F7", "E7", "D7", "C7", "B6", "A6", "G6", "F6", "E6", "D6", "C6", "B5", "A5", "G5", "F5", "E5", "D5", "C5", "B4", "A4", "G4", "F4", "E4", "D4", "C4"];
16 | }
17 |
18 | class TrebleStave extends Stave {
19 | type = 'treble';
20 | clef = glyphs.clefTreble;
21 | rows = ["A6", "G6", "F6", "E6", "D6", "C6", "B5", "A5", "G5", "F5", "E5", "D5", "C5", "B4", "A4", "G4", "F4", "E4", "D4", "C4", "B3", "A3", "G3", "F3", "E3", "D3", "C3"];
22 | }
23 |
24 | class TrebleDownStave extends Stave {
25 | type = 'treble-down';
26 | clef = glyphs.clefTrebleDown;
27 | rows = ["A5", "G5", "F5", "E5", "D5", "C5", "B4", "A4", "G4", "F4", "E4", "D4", "C4", "B3", "A3", "G3", "F3", "E3", "D3", "C3", "B2", "A2", "G2", "F2", "E2", "D2", "C2"];
28 | }
29 |
30 | class AltoStave extends Stave {
31 | type = 'alto';
32 | clef = glyphs.clefAlto;
33 | rows = ["B5", "A5", "G5", "F5", "E5", "D5", "C5", "B4", "A4", "G4", "F4", "E4", "D4", "C4", "B3", "A3", "G3", "F3", "E3", "D3", "C3", "B2", "A2", "G2", "F2", "E2", "D2"];
34 | }
35 |
36 | class BassStave extends Stave {
37 | type = 'bass';
38 | clef = glyphs.clefBass;
39 | rows = ["C5", "B4", "A4", "G4", "F4", "E4", "D4", "C4", "B3", "A3", "G3", "F3", "E3", "D3", "C3", "B2", "A2", "G2", "F2", "E2", "D2", "C2", "B1", "A1", "G1", "F1", "E1"];
40 | }
41 |
42 |
43 | // Register staves by type. Types are the same string used by the clef attribute,
44 | // as in . Create a stave by type with:
45 | // Stave.create(type);
46 |
47 | Stave['treble'] = new TrebleStave();
48 | Stave['treble-up'] = new TrebleUpStave();
49 | Stave['treble-down'] = new TrebleDownStave();
50 | Stave['alto'] = new AltoStave();
51 | Stave['bass'] = new BassStave();
52 | Stave['piano'] = new PianoStave();
53 | Stave['drum'] = new DrumStave();
54 | Stave['percussion'] = new PercussionStave();
55 |
56 |
57 | /**
58 | Stave.create(type)
59 | Create a stave object by type.
60 | **/
61 | Stave.create = (type) => {
62 | console.warn('Deprecated: Stave.create(type). Use Stave[type].');
63 | return Stave[type];
64 | }
65 |
66 | export default Stave;
67 |
--------------------------------------------------------------------------------
/test/quantize.js:
--------------------------------------------------------------------------------
1 |
2 | import quantize from '../modules/quantize.js';
3 | import test from 'fn/test.js';
4 |
5 | const assign = Object.assign;
6 | const round32 = Math.fround;
7 |
8 | test('Quantize', [
9 | [],
10 | [[0, 'note', 'C4', 1, 1]],
11 | [[round32(0.0833333333), 'note', 'C4', 1, 1]],
12 | [[round32(0.1666666667), 'note', 'C4', 1, 1]],
13 | [[0.25, 'note', 'C4', 1, 1]],
14 | [[round32(0.3333333333), 'note', 'C4', 1, 1]],
15 | [[round32(0.3333333333), 'note', 'C4', 1, 1]],
16 | [[round32(0.4166666667), 'note', 'C4', 1, 1]],
17 | [[0.5, 'note', 'C4', 1, 1]],
18 | [[round32(0.5833333333), 'note', 'C4', 1, 1]],
19 | [[round32(0.6666666667), 'note', 'C4', 1, 1]],
20 | [[round32(0.6666666667), 'note', 'C4', 1, 1]],
21 | [[0.75, 'note', 'C4', 1, 1]],
22 | [[round32(0.8333333333), 'note', 'C4', 1, 1]],
23 | [[round32(0.9166666667), 'note', 'C4', 1, 1]],
24 | [[1, 'note', 'C4', 1, 1]],
25 | [[1, 'note', 'C4', 1, 1]],
26 | [[1, 'note', 'C4', 1, 1]],
27 | [[1, 'note', 'C4', 1, 1]],
28 | [[1, 'note', 'C4', 1, 1]],
29 | [[1, 'note', 'C4', 1, 1]]
30 | ], (expect, done) => {
31 | expect(quantize([]));
32 | expect(quantize([[0.0, 'note', 'C4', 1, 1]]));
33 | expect(quantize([[0.1, 'note', 'C4', 1, 1]]));
34 | expect(quantize([[0.2, 'note', 'C4', 1, 1]]));
35 | expect(quantize([[0.25, 'note', 'C4', 1, 1]]));
36 | expect(quantize([[0.3, 'note', 'C4', 1, 1]]));
37 | expect(quantize([[0.33, 'note', 'C4', 1, 1]]));
38 | expect(quantize([[0.4, 'note', 'C4', 1, 1]]));
39 | expect(quantize([[0.5, 'note', 'C4', 1, 1]]));
40 | expect(quantize([[0.6, 'note', 'C4', 1, 1]]));
41 | expect(quantize([[0.67, 'note', 'C4', 1, 1]]));
42 | expect(quantize([[0.7, 'note', 'C4', 1, 1]]));
43 | expect(quantize([[0.75, 'note', 'C4', 1, 1]]));
44 | expect(quantize([[0.8, 'note', 'C4', 1, 1]]));
45 | expect(quantize([[0.9, 'note', 'C4', 1, 1]]));
46 | expect(quantize([[0.97, 'note', 'C4', 1, 1]]));
47 | expect(quantize([[0.98, 'note', 'C4', 1, 1]]));
48 | expect(quantize([[0.99, 'note', 'C4', 1, 1]]));
49 | expect(quantize([[1.0, 'note', 'C4', 1, 1]]));
50 | expect(quantize([[1.01, 'note', 'C4', 1, 1]]));
51 | expect(quantize([[1.02, 'note', 'C4', 1, 1]]));
52 | done();
53 | });
54 |
55 | test('Quantize', [
56 | [
57 | [1, 'note', 'C4', 1, 1],
58 | [1, 'note', 'D4', 1, 1],
59 | [1.5, 'note', 'E4', 1, 1],
60 | [1.5, 'note', 'F4', 1, 1],
61 | [3, 'note', 'G4', 1, 1],
62 | [round32(3.9166666667), 'note', 'A4', 1, 1]
63 | ]
64 | ], (expect, done) => {
65 | expect(quantize([
66 | [0.97, 'note', 'C4', 1, 1],
67 | [0.98, 'note', 'D4', 1, 1],
68 | [1.47, 'note', 'E4', 1, 1],
69 | [1.48, 'note', 'F4', 1, 1],
70 | [3.01, 'note', 'G4', 1, 1],
71 | [3.92, 'note', 'A4', 1, 1]
72 | ]));
73 | done();
74 | });
75 |
--------------------------------------------------------------------------------
/attributes/data-duration.css:
--------------------------------------------------------------------------------
1 |
2 | /* Converts data-duration attribute to --duration variable */
3 |
4 | [data-duration^="0.083"] { --duration: 0.0833333333; } /* Triplet over 0.25 */
5 | [data-duration^="0.1"] { --duration: 0.1; } /* Quintuplet over 0.5 */
6 | [data-duration^="0.11"] { --duration: 0.1111111111; } /* Nonuplet over 1 */
7 | [data-duration^="0.125"] { --duration: 0.125; } /* 32nd */
8 | [data-duration^="0.14"] { --duration: 0.1428571429; } /* Septuplet over 1 */
9 | [data-duration^="0.16"] { --duration: 0.1666666667; } /* Triplet over 0.5 */
10 | [data-duration^="0.2"] { --duration: 0.2; } /* Quintuplet over 1 */
11 | [data-duration^="0.22"] { --duration: 0.2222222222; } /* Nonuplet over 2 */
12 | [data-duration^="0.25"] { --duration: 0.25; } /* 16th */
13 | [data-duration^="0.28"] { --duration: 0.2857142857; } /* Septuplet over 2 */
14 | [data-duration^="0.33"] { --duration: 0.3333333333; } /* Triplet over 1 */
15 | [data-duration^="0.375"] { --duration: 0.375; } /* 16th dotted */
16 | [data-duration^="0.4"] { --duration: 0.4; } /* Quintuplet over 2 */
17 | [data-duration^="0.44"] { --duration: 0.4444444444; } /* Nonuplet over 4 */
18 | [data-duration^="0.5"] { --duration: 0.5; } /* 8th */
19 | [data-duration^="0.57"] { --duration: 0.5714285714; } /* Septuplet over 4 */
20 | [data-duration^="0.66"] { --duration: 0.6666666667; } /* Triplet over 2 */
21 | [data-duration^="0.75"] { --duration: 0.75; } /* 8th dotted */
22 | [data-duration^="0.8"] { --duration: 0.8; } /* Quintuplet over 4 */
23 | [data-duration^="0.83"] { --duration: 0.8333333333; } /* Sextuplet beam over 1 */
24 | [data-duration^="0.85"] { --duration: 0.8571428571; } /* Septuplet beam over 1 */
25 | [data-duration^="0.875"] { --duration: 0.875; } /* 8th double dotted */
26 | [data-duration^="0.88"] { --duration: 0.8888888889; } /* Nonuplet over 8 */
27 | [data-duration^="1"] { --duration: 1; } /* Quarter */
28 | [data-duration^="1.14"] { --duration: 1.1428571429; } /* Septuplet over 8 */
29 | [data-duration^="1.33"] { --duration: 1.3333333333; } /* Triplet over 4 */
30 | [data-duration^="1.5"] { --duration: 1.5; } /* Quarter dotted */
31 | [data-duration^="1.6"] { --duration: 1.6; } /* Quintuplet over 8 */
32 | [data-duration^="1.75"] { --duration: 1.75; } /* Quarter double dotted */
33 | [data-duration^="1.77"] { --duration: 1.7777777778; } /* Nonuplet beam over 2 */
34 | [data-duration^="2"] { --duration: 2; } /* Minim */
35 | [data-duration^="2.5"] { --duration: 2.5; } /* 5/8 bar */
36 | [data-duration^="2.66"] { --duration: 2.6666666667; } /* Triplet over 8 */
37 | [data-duration^="3"] { --duration: 3; } /* Minim dotted */
38 | [data-duration^="3.5"] { --duration: 3.5; } /* Minim double dotted */
39 | [data-duration^="4"] { --duration: 4; } /* Whole */
40 | [data-duration^="4.5"] { --duration: 4.5; } /* 9/8 bar */
41 | [data-duration^="5"] { --duration: 5; } /* 5/4 bar */
42 | [data-duration^="5.5"] { --duration: 5.5; } /* 11/8 bar */
43 | [data-duration^="6"] { --duration: 6; } /* Whole dotted */
44 | [data-duration^="6.5"] { --duration: 6.5; } /* 13/8 bar */
45 | [data-duration^="7"] { --duration: 7; } /* Whole double dotted */
46 | [data-duration^="7.5"] { --duration: 7.5; } /* 15/8 bar */
47 | [data-duration^="8"] { --duration: 8; } /* Doublewhole */
48 |
--------------------------------------------------------------------------------
/chart.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | My Tune
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
88 |
89 |
90 |
109 |
110 |
111 |
112 | My Tune
113 | Piano
114 | Composed by My Name
115 |
116 |
117 |
118 |
119 |
122 |
123 |
124 |
--------------------------------------------------------------------------------
/test/si-bheag-si-mhor.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | <scribe-music>
5 |
6 |
7 |
8 |
9 |
10 |
11 |
20 |
21 |
22 |
23 |
24 |
25 |
30 |
31 |
38 |
39 |
40 |
41 |
42 | Sí Bheag Sí Mhór
43 |
73 |
74 |
75 |
76 | X: 1
77 | T: Sí Bheag Sí Mhór
78 | C: Turlough O'Carolan
79 | Z: ceolachan
80 | S: https://thesession.org/tunes/449#setting13324
81 | R: waltz
82 | M: 3/4
83 | L: 1/8
84 | K: Dmaj
85 | f3 e d2|d2 e2 d2|B4 A2|F4 A2|BA Bc d2|e4 de|f2 f2 e2|d4 f2|
86 | B4 e2|A4 d2|F2 F2 E2|D4 f2|B4 e2|A4 dc|d6-|d4:|
87 | f3 e d2|ed ef a2|b4 a2|f4 ed|e2 e2 a2|f4 e2|d4 B2|B4 A2|
88 | F4 E2|D4 f2|B4 e2|A4 a2|ba gf ed|e3 fe|d1
89 | |de:|A)||D D Bm D G A D D G F#m Bm D G A D D||
90 | B)||D A G D A D D Bm D D G F#m G A D D||
91 |
92 |
93 |
96 |
97 |
98 |
--------------------------------------------------------------------------------
/modules/beam.js:
--------------------------------------------------------------------------------
1 |
2 | import create from 'dom/create.js';
3 | import rect from 'dom/rect.js';
4 |
5 | /* Beams */
6 |
7 | export const beamThickness = 1.1;
8 |
9 | function getDataBeat(note) {
10 | return parseFloat(note.dataset.beat);
11 | }
12 |
13 | function getDataDuration(note) {
14 | return parseFloat(note.dataset.duration);
15 | }
16 |
17 | function removeBeamPaths(svg) {
18 | const childNodes = svg.childNodes;
19 | while (childNodes.length > 1) {
20 | svg.removeChild(svg.lastChild);
21 | }
22 | }
23 |
24 | function renderPathData(range, xs, beam, beat, duration) {
25 | const i0 = beam[0];
26 |
27 | const x0 = beam.length === 1 ?
28 | // If beat is divisible by 2 x duration the sub beam projects forward
29 | beat % (duration * 2) === 0 ?
30 | // Forward tail beam starts at i0
31 | xs[i0] :
32 | // Backward tail beam starts before i0
33 | xs[i0] - 0.4 * (xs[i0] - xs[i0 - 1]) :
34 | // Beam spans whole duration
35 | xs[i0] ;
36 |
37 | const x1 = beam.length === 1 ?
38 | // If beat is divisible by 2 x duration the sub beam projects forward
39 | beat % (duration * 2) === 0 ?
40 | // Forward tail beam stops after i0
41 | xs[i0] + 0.5 * (xs[i0 + 1] - xs[i0]) :
42 | // Backward tail beam stops at i0
43 | xs[i0] :
44 | // Beam spans whole duration
45 | xs[beam[beam.length - 1]] ;
46 |
47 | return `M${ x0 }, ${ -range * x0 - 0.5 * beamThickness }
48 | L${ x1 },${ -range * x1 - 0.5 * beamThickness }
49 | L${ x1 },${ -range * x1 + 0.5 * beamThickness }
50 | L${ x0 }, ${ -range * x0 + 0.5 * beamThickness }
51 | Z` ;
52 | }
53 |
54 | function createBeamPaths(svg, beats, durations, xs, i, range, duration) {
55 | // Don't render anything shorter than triplet 32nd note beams
56 | if (duration < 0.0833) return;
57 |
58 | let n = i - 1;
59 | let beam;
60 |
61 | while (durations[++n]) {
62 | if (durations[n] <= duration) {
63 | // Push to existing beam
64 | if (beam) { beam.push(n); }
65 | // Or start new beam
66 | else { beam = [n]; }
67 | }
68 | // Render beam
69 | else if (beam) {
70 | svg.appendChild(create('path', {
71 | // Remember duration is the duration of the beam above this one
72 | class: `beam-path-${ 4 / duration } beam-path`,
73 | d: renderPathData(range, xs, beam, beats[n - 1], duration)
74 | }));
75 |
76 | createBeamPaths(svg, beats, durations, xs, beam[0], range, duration / 2);
77 | beam = undefined;
78 | }
79 | }
80 |
81 | // Render beam
82 | if (beam) {
83 | svg.appendChild(create('path', {
84 | class: `beam-path-${ 4 / duration } beam-path`,
85 | d: renderPathData(range, xs, beam, beats[n - 1], duration)
86 | }));
87 |
88 | createBeamPaths(svg, beats, durations, xs, beam[0], range, duration / 2);
89 | beam = undefined;
90 | }
91 | }
92 |
93 | export function renderBeam(svg) {
94 | const id = svg.dataset.beam;
95 | const parent = svg.parentElement;
96 | /* There is only one .top-note (or .bottom-note) per beam division, select that */
97 | const notes = Array.from(parent.querySelectorAll('.top-note[data-beam="' + id + '"]'));
98 | const beats = notes.map(getDataBeat);
99 | const durations = notes.map(getDataDuration);
100 | const box = svg.viewBox.baseVal;
101 | const range =
102 | box.y < -0.5 ? box.y + 0.5 :
103 | box.height - 1;
104 |
105 | const boxes = notes.map(rect);
106 | const firstX = boxes[0].x;
107 | const lastX = boxes[boxes.length - 1].x;
108 | const xs = boxes.map((box) => (box.x - firstX) / (lastX - firstX));
109 |
110 | removeBeamPaths(svg);
111 | createBeamPaths(svg, beats, durations, xs, 0, -range, 0.25);
112 | }
113 |
--------------------------------------------------------------------------------
/modules/chord.js:
--------------------------------------------------------------------------------
1 |
2 | // THIS FILE REDUNDANT, CURRENTLY
3 |
4 |
5 | import nothing from 'fn/nothing.js';
6 | import { noteNumbers, toRootNumber, toRootName } from 'midi/note.js';
7 | import keys from '../keys.js';
8 | import mod12 from '../number/mod-12.js';
9 | import { byGreater } from '../maths.js';
10 |
11 |
12 | const rrootname = /^[A-G][b#♭♯𝄫𝄪]?/;
13 | const rchord = /([ABCDEFG][b#♭♯𝄫𝄪]?)([^\/]*)(?:\/([ABCDEFG]))?/;
14 |
15 | const modes = {
16 | '∆': 0,
17 | '∆7': 0,
18 | '-7': 2,
19 | 'sus♭9': 4,
20 | '7sus♭9': 4,
21 | '∆♯11': 5,
22 | '∆(♯11)': 5,
23 | '7': 7,
24 | '13': 7,
25 | 'sus': 7,
26 | '7sus': 7,
27 | '-♭6': 9,
28 | 'ø': 11,
29 |
30 | // Here we treat melodic minor as though it were the fourth degree of a
31 | // major scale, making the spellings work out nicely, or so it is hoped,
32 | // but also because it is strongly related (think E7alt -> Am).
33 | '-∆': 5,
34 | '13sus♭9': 7,
35 | '∆+': 8,
36 | '∆♯5': 8,
37 | '7♯11': 10,
38 | '7♭13': 0,
39 | 'ø(9)': 2,
40 | '7alt': 4
41 | };
42 |
43 | // TODO Move this. It is used for lookup to get scale of chord, but we need to
44 | // move this to a central place that handles theory
45 | const chordNotes = {
46 | '∆': [0, 2, 4, 7, 9, 11],
47 | '∆7': [0, 2, 4, 7, 9, 11],
48 | '-': [0, 3, 7],
49 | '-7': [0, 2, 3, 5, 7, 9, 10],
50 | 'sus♭9': [0, 1, 5, 7, 10],
51 | '7sus♭9': [0, 1, 5, 7, 10],
52 | '∆♯11': [0, 2, 4, 6, 7, 9, 11],
53 | '∆(♯11)': [0, 2, 4, 6, 7, 9, 11],
54 | '7': [0, 4, 7, 10],
55 | '13': [0, 4, 7, 9, 10],
56 | 'sus': [0, 2, 5, 7, 10],
57 | '7sus': [0, 2, 5, 7, 10],
58 | '-♭6': [0, 2, 3, 5, 7, 8],
59 | 'ø': [0, 3, 6, 10],
60 |
61 | // Harmonic minor
62 | '7♭9♭13': [0, 1, 4, 5, 7, 8, 10],
63 |
64 | // Melodic minor
65 | '-∆': [0, 2, 3, 5, 7, 9, 11],
66 | '13sus♭9': [0, 1, 5, 7, 9, 10],
67 | '∆+': [0, 2, 4, 6, 8, 9, 11],
68 | '∆♯5': [0, 2, 4, 6, 8, 9, 11],
69 | '7♯11': [0, 2, 4, 6, 7, 9, 10],
70 | '7♭13': [0, 2, 4, 7, 8, 10],
71 | 'ø(9)': [0, 2, 3, 6, 10],
72 | '7alt': [0, 1, 3, 4, 6, 8, 10],
73 | //'∆♭6': [0, 4, 7, 8, 11],
74 |
75 | // Harmonic major
76 | '∆♭6': [0, 2, 4, 7, 8, 11],
77 |
78 | // Diminished
79 | '7♭9': [0, 1, 3, 4, 6, 7, 9, 10],
80 | '7♯9': [0, 1, 3, 4, 6, 7, 9, 10],
81 | 'º': [0, 2, 3, 5, 6, 8, 9, 11],
82 |
83 | // Whole tone
84 | '7+': [0, 2, 4, 6, 8, 10]
85 | };
86 |
87 |
88 | // Map functions
89 |
90 | export function normaliseExtensionName(str) {
91 | return str.replace(/(maj)|(min)|(dim)/, ($0, $maj, $min, $dim) => {
92 | return $maj ? '∆' :
93 | $min ? '-' :
94 | $dim ? 'º' :
95 | '' ;
96 | });
97 | }
98 |
99 | export function toRoot(str) {
100 | var name = (rchord.exec(str) || nothing)[1];
101 | return noteNumbers[name];
102 | }
103 |
104 | export function toExtension(str) {
105 | const chordName = normaliseExtensionName(str);
106 | return (rchord.exec(chordName) || empty)[2];
107 | }
108 |
109 | export function toMode(str) {
110 | return modes[toExtension(str)];
111 | }
112 |
113 | export function toBass(str) {
114 | var result = rchord.exec(str) || empty;
115 | return result[3] || result[1];
116 | }
117 |
118 | export function toKey(str) {
119 | return keys[mod12(toRoot(str) - toMode(str))];
120 | }
121 |
122 | export function toChordNotes(root, extension, bass) {
123 | const r = toRootNumber(root);
124 | const e = normaliseExtensionName(extension);
125 | // Ignore bass note for now: TODO
126 | //const b = toRootNumber(bass);
127 | return chordNotes[e] ?
128 | // If r is not 0 transpose the mode
129 | r ? chordNotes[e].map((n) => mod12(n + r)).sort(byGreater) :
130 | // Otherwise return the mode as-is
131 | chordNotes[e] :
132 | // No mode found
133 | [] ;
134 | }
135 |
136 | export function transposeChord(str, n) {
137 | const root = (rrootname.exec(str) || nothing)[0];
138 | return toRootName(toRootNumber(root) + n) + str.slice(root.length);
139 | }
140 |
--------------------------------------------------------------------------------
/modules/stave/drum.js:
--------------------------------------------------------------------------------
1 |
2 | import slugify from 'fn/slugify.js';
3 | import { toNoteName, toDrumName, toNoteNumber } from 'midi/note.js';
4 | import { spellRoot, spellPitch } from '../spelling.js';
5 | import * as glyphs from "../glyphs.js";
6 | import Stave from './stave.js';
7 |
8 |
9 | const global = globalThis || window;
10 |
11 |
12 | function toDrumPitch(number) {
13 | return toDrumName(number)
14 | .toLowerCase()
15 | .replace(/\s+/g, '-')
16 | .replace(/-drum|-cymbal/, '');
17 | }
18 |
19 | export default class DrumStave extends Stave {
20 | type = 'drum';
21 |
22 | pitched = false;
23 |
24 | rows = [
25 | "",
26 | "",
27 | "",
28 | "",
29 | "splash chinese",
30 | "crash-2",
31 | "crash-1",
32 | "ride-1 ride-2 ride-bell",
33 | "closed-hi-hat open-hi-hat",
34 | "high-tom cowbell",
35 | "low-mid-tom high-mid-tom",
36 | "snare-1 snare2 side-stick",
37 | "low-tom sticks hand-clap tambourine",
38 | "high-floor-tom",
39 | "low-floor-tom",
40 | "low-bass high-bass",
41 | "",
42 | "pedal-hi-hat",
43 | "",
44 | "",
45 | "",
46 | "",
47 | "",
48 | "",
49 | ""
50 | ];
51 |
52 | #headnames = {
53 | 31: 'headX', // Sticks
54 | 37: 'headCircle', // Side Stick
55 | 39: 'headX', // Hand Clap
56 | 42: 'headX', // Closed Hi-Hat
57 | 44: 'headX', // Pedal Hi-Hat
58 | 46: 'headCircleX', // Open Hi-Hat
59 | 49: 'headCircleX', // Crash Cymbal 1
60 | 51: 'headX', // Ride Cymbal 1
61 | 52: 'headCircleX', // Chinese Cymbal
62 | 53: 'headDiamond', // Ride Bell
63 | 54: 'headX', // Tambourine
64 | 55: 'headX', // Splash Cymbal
65 | 56: 'headTriangleUp', // Cowbell
66 | 57: 'headCircleX', // Crash Symbol 2
67 | 58: 'headTriangleUp', // Vibraslap
68 | 59: 'headX' // Ride Cymbal 2
69 | };
70 |
71 |
72 |
73 | getNoteHTML(pitch, dynamic, duration) {
74 | const number = toNoteNumber(pitch);
75 | const name = this.#headnames[number];
76 | const head = glyphs[name];
77 | const html = name ?
78 | `${ head } ` :
79 | super.getNoteHTML(pitch, dynamic, duration) ;
80 |
81 | // Ghost note gets brackets
82 | return dynamic < 0.02 ?
83 | glyphs.headBracketLeft + html + glyphs.headBracketRight :
84 | html ;
85 | }
86 |
87 | parts = [{
88 | name: 'drums',
89 | beam: 'drums-beam',
90 | stemup: false
91 | }, {
92 | name: 'cymbals',
93 | beam: 'cymbals-beam',
94 | stemup: true
95 | }];
96 |
97 | getPart(number) {
98 | // Split drums stave into drums and cymbals parts
99 | return [35, 36, 37, 38, 40, 41, 43, 44, 45, 47, 50].includes(toNoteNumber(number)) ?
100 | this.parts[0] :
101 | this.parts[1] ;
102 | }
103 |
104 | /**
105 | .getRow(pitch)
106 | Returns the row index of a given pitch name or number.
107 | **/
108 | getRow(part, n) {
109 | const pitch = typeof n === 'string' ? n : toDrumPitch(n) ;
110 | if (!pitch) throw new Error('Drum name not found for pitch ' + n);
111 | const i = this.rows.findIndex((row) => row.includes(pitch));
112 | if (global.DEBUG && i === -1) throw new Error('Pitch "' + pitch + '" is not supported by stave ' + this.constructor.name);
113 | if (i === -1) console.warn('Pitch "' + pitch + '" is not supported by stave ' + this.constructor.name);
114 | return i > -1 ? i : undefined ;
115 | }
116 |
117 | getSpelling(key, event) {
118 | if (event[1] === 'note') {
119 | // Use standard MIDI note names. We don't want any spelling happening
120 | // on drum parts.
121 | return toDrumPitch(toNoteNumber(event[2]));
122 | }
123 |
124 | return super.getSpelliing(key, event[2]);
125 | }
126 |
127 | createSignatureSymbols(key) {
128 | return [{
129 | type: 'clef',
130 | clef: this.type
131 | }];
132 | }
133 |
134 | yRatioToPitch(y) {
135 | const i = floor(y * this.rows.length);
136 | const j = i < 4 ? 4 : i > 17 ? 17 : i ;
137 | return this.pitches[j];
138 | }
139 | }
140 |
--------------------------------------------------------------------------------
/fonts/Bravura/LICENSE.txt:
--------------------------------------------------------------------------------
1 | Copyright © 2019, Steinberg Media Technologies GmbH (http://www.steinberg.net/),
2 | with Reserved Font Name "Bravura".
3 |
4 | This Font Software is licensed under the SIL Open Font License, Version 1.1.
5 | This license is copied below, and is also available with a FAQ at:
6 | http://scripts.sil.org/OFL
7 |
8 | -----------------------------------------------------------
9 | SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
10 | -----------------------------------------------------------
11 |
12 | PREAMBLE
13 | The goals of the Open Font License (OFL) are to stimulate worldwide
14 | development of collaborative font projects, to support the font creation
15 | efforts of academic and linguistic communities, and to provide a free and
16 | open framework in which fonts may be shared and improved in partnership
17 | with others.
18 |
19 | The OFL allows the licensed fonts to be used, studied, modified and
20 | redistributed freely as long as they are not sold by themselves. The
21 | fonts, including any derivative works, can be bundled, embedded,
22 | redistributed and/or sold with any software provided that any reserved
23 | names are not used by derivative works. The fonts and derivatives,
24 | however, cannot be released under any other type of license. The
25 | requirement for fonts to remain under this license does not apply
26 | to any document created using the fonts or their derivatives.
27 |
28 | DEFINITIONS
29 | "Font Software" refers to the set of files released by the Copyright
30 | Holder(s) under this license and clearly marked as such. This may
31 | include source files, build scripts and documentation.
32 |
33 | "Reserved Font Name" refers to any names specified as such after the
34 | copyright statement(s).
35 |
36 | "Original Version" refers to the collection of Font Software components as
37 | distributed by the Copyright Holder(s).
38 |
39 | "Modified Version" refers to any derivative made by adding to, deleting,
40 | or substituting -- in part or in whole -- any of the components of the
41 | Original Version, by changing formats or by porting the Font Software to a
42 | new environment.
43 |
44 | "Author" refers to any designer, engineer, programmer, technical
45 | writer or other person who contributed to the Font Software.
46 |
47 | PERMISSION AND CONDITIONS
48 | Permission is hereby granted, free of charge, to any person obtaining
49 | a copy of the Font Software, to use, study, copy, merge, embed, modify,
50 | redistribute, and sell modified and unmodified copies of the Font
51 | Software, subject to the following conditions:
52 |
53 | 1) Neither the Font Software nor any of its individual components,
54 | in Original or Modified Versions, may be sold by itself.
55 |
56 | 2) Original or Modified Versions of the Font Software may be bundled,
57 | redistributed and/or sold with any software, provided that each copy
58 | contains the above copyright notice and this license. These can be
59 | included either as stand-alone text files, human-readable headers or
60 | in the appropriate machine-readable metadata fields within text or
61 | binary files as long as those fields can be easily viewed by the user.
62 |
63 | 3) No Modified Version of the Font Software may use the Reserved Font
64 | Name(s) unless explicit written permission is granted by the corresponding
65 | Copyright Holder. This restriction only applies to the primary font name as
66 | presented to the users.
67 |
68 | 4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
69 | Software shall not be used to promote, endorse or advertise any
70 | Modified Version, except to acknowledge the contribution(s) of the
71 | Copyright Holder(s) and the Author(s) or with their explicit written
72 | permission.
73 |
74 | 5) The Font Software, modified or unmodified, in part or in whole,
75 | must be distributed entirely under this license, and must not be
76 | distributed under any other license. The requirement for fonts to
77 | remain under this license does not apply to any document created
78 | using the Font Software.
79 |
80 | TERMINATION
81 | This license becomes null and void if any of the above conditions are
82 | not met.
83 |
84 | DISCLAIMER
85 | THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
86 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
87 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
88 | OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
89 | COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
90 | INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
91 | DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
92 | FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
93 | OTHER DEALINGS IN THE FONT SOFTWARE.
94 |
--------------------------------------------------------------------------------
/fonts/Bravura/redist/OFL.txt:
--------------------------------------------------------------------------------
1 | Copyright © 2015, Steinberg Media Technologies GmbH (http://www.steinberg.net/),
2 | with Reserved Font Name "Bravura".
3 |
4 | This Font Software is licensed under the SIL Open Font License, Version 1.1.
5 | This license is copied below, and is also available with a FAQ at:
6 | http://scripts.sil.org/OFL
7 |
8 |
9 | -----------------------------------------------------------
10 | SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
11 | -----------------------------------------------------------
12 |
13 | PREAMBLE
14 | The goals of the Open Font License (OFL) are to stimulate worldwide
15 | development of collaborative font projects, to support the font creation
16 | efforts of academic and linguistic communities, and to provide a free and
17 | open framework in which fonts may be shared and improved in partnership
18 | with others.
19 |
20 | The OFL allows the licensed fonts to be used, studied, modified and
21 | redistributed freely as long as they are not sold by themselves. The
22 | fonts, including any derivative works, can be bundled, embedded,
23 | redistributed and/or sold with any software provided that any reserved
24 | names are not used by derivative works. The fonts and derivatives,
25 | however, cannot be released under any other type of license. The
26 | requirement for fonts to remain under this license does not apply
27 | to any document created using the fonts or their derivatives.
28 |
29 | DEFINITIONS
30 | "Font Software" refers to the set of files released by the Copyright
31 | Holder(s) under this license and clearly marked as such. This may
32 | include source files, build scripts and documentation.
33 |
34 | "Reserved Font Name" refers to any names specified as such after the
35 | copyright statement(s).
36 |
37 | "Original Version" refers to the collection of Font Software components as
38 | distributed by the Copyright Holder(s).
39 |
40 | "Modified Version" refers to any derivative made by adding to, deleting,
41 | or substituting -- in part or in whole -- any of the components of the
42 | Original Version, by changing formats or by porting the Font Software to a
43 | new environment.
44 |
45 | "Author" refers to any designer, engineer, programmer, technical
46 | writer or other person who contributed to the Font Software.
47 |
48 | PERMISSION & CONDITIONS
49 | Permission is hereby granted, free of charge, to any person obtaining
50 | a copy of the Font Software, to use, study, copy, merge, embed, modify,
51 | redistribute, and sell modified and unmodified copies of the Font
52 | Software, subject to the following conditions:
53 |
54 | 1) Neither the Font Software nor any of its individual components,
55 | in Original or Modified Versions, may be sold by itself.
56 |
57 | 2) Original or Modified Versions of the Font Software may be bundled,
58 | redistributed and/or sold with any software, provided that each copy
59 | contains the above copyright notice and this license. These can be
60 | included either as stand-alone text files, human-readable headers or
61 | in the appropriate machine-readable metadata fields within text or
62 | binary files as long as those fields can be easily viewed by the user.
63 |
64 | 3) No Modified Version of the Font Software may use the Reserved Font
65 | Name(s) unless explicit written permission is granted by the corresponding
66 | Copyright Holder. This restriction only applies to the primary font name as
67 | presented to the users.
68 |
69 | 4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
70 | Software shall not be used to promote, endorse or advertise any
71 | Modified Version, except to acknowledge the contribution(s) of the
72 | Copyright Holder(s) and the Author(s) or with their explicit written
73 | permission.
74 |
75 | 5) The Font Software, modified or unmodified, in part or in whole,
76 | must be distributed entirely under this license, and must not be
77 | distributed under any other license. The requirement for fonts to
78 | remain under this license does not apply to any document created
79 | using the Font Software.
80 |
81 | TERMINATION
82 | This license becomes null and void if any of the above conditions are
83 | not met.
84 |
85 | DISCLAIMER
86 | THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
87 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
88 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
89 | OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
90 | COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
91 | INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
92 | DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
93 | FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
94 | OTHER DEALINGS IN THE FONT SOFTWARE.
95 |
--------------------------------------------------------------------------------
/fonts/Petaluma/redist/OFL.txt:
--------------------------------------------------------------------------------
1 | Copyright © 2018, Steinberg Media Technologies GmbH (http://www.steinberg.net/),
2 | with Reserved Font Name "Petaluma".
3 |
4 | This Font Software is licensed under the SIL Open Font License, Version 1.1.
5 | This license is copied below, and is also available with a FAQ at:
6 | http://scripts.sil.org/OFL
7 |
8 |
9 | -----------------------------------------------------------
10 | SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
11 | -----------------------------------------------------------
12 |
13 | PREAMBLE
14 | The goals of the Open Font License (OFL) are to stimulate worldwide
15 | development of collaborative font projects, to support the font creation
16 | efforts of academic and linguistic communities, and to provide a free and
17 | open framework in which fonts may be shared and improved in partnership
18 | with others.
19 |
20 | The OFL allows the licensed fonts to be used, studied, modified and
21 | redistributed freely as long as they are not sold by themselves. The
22 | fonts, including any derivative works, can be bundled, embedded,
23 | redistributed and/or sold with any software provided that any reserved
24 | names are not used by derivative works. The fonts and derivatives,
25 | however, cannot be released under any other type of license. The
26 | requirement for fonts to remain under this license does not apply
27 | to any document created using the fonts or their derivatives.
28 |
29 | DEFINITIONS
30 | "Font Software" refers to the set of files released by the Copyright
31 | Holder(s) under this license and clearly marked as such. This may
32 | include source files, build scripts and documentation.
33 |
34 | "Reserved Font Name" refers to any names specified as such after the
35 | copyright statement(s).
36 |
37 | "Original Version" refers to the collection of Font Software components as
38 | distributed by the Copyright Holder(s).
39 |
40 | "Modified Version" refers to any derivative made by adding to, deleting,
41 | or substituting -- in part or in whole -- any of the components of the
42 | Original Version, by changing formats or by porting the Font Software to a
43 | new environment.
44 |
45 | "Author" refers to any designer, engineer, programmer, technical
46 | writer or other person who contributed to the Font Software.
47 |
48 | PERMISSION & CONDITIONS
49 | Permission is hereby granted, free of charge, to any person obtaining
50 | a copy of the Font Software, to use, study, copy, merge, embed, modify,
51 | redistribute, and sell modified and unmodified copies of the Font
52 | Software, subject to the following conditions:
53 |
54 | 1) Neither the Font Software nor any of its individual components,
55 | in Original or Modified Versions, may be sold by itself.
56 |
57 | 2) Original or Modified Versions of the Font Software may be bundled,
58 | redistributed and/or sold with any software, provided that each copy
59 | contains the above copyright notice and this license. These can be
60 | included either as stand-alone text files, human-readable headers or
61 | in the appropriate machine-readable metadata fields within text or
62 | binary files as long as those fields can be easily viewed by the user.
63 |
64 | 3) No Modified Version of the Font Software may use the Reserved Font
65 | Name(s) unless explicit written permission is granted by the corresponding
66 | Copyright Holder. This restriction only applies to the primary font name as
67 | presented to the users.
68 |
69 | 4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
70 | Software shall not be used to promote, endorse or advertise any
71 | Modified Version, except to acknowledge the contribution(s) of the
72 | Copyright Holder(s) and the Author(s) or with their explicit written
73 | permission.
74 |
75 | 5) The Font Software, modified or unmodified, in part or in whole,
76 | must be distributed entirely under this license, and must not be
77 | distributed under any other license. The requirement for fonts to
78 | remain under this license does not apply to any document created
79 | using the Font Software.
80 |
81 | TERMINATION
82 | This license becomes null and void if any of the above conditions are
83 | not met.
84 |
85 | DISCLAIMER
86 | THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
87 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
88 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
89 | OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
90 | COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
91 | INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
92 | DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
93 | FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
94 | OTHER DEALINGS IN THE FONT SOFTWARE.
95 |
--------------------------------------------------------------------------------
/test/tuplets.js:
--------------------------------------------------------------------------------
1 |
2 | import detectTuplets from '../modules/tuplet.js';
3 |
4 | console.log('Nothing');
5 | console.log(detectTuplets(4, []));
6 |
7 | console.log('Crotchets');
8 | console.log(detectTuplets(4, [[0]]));
9 |
10 | console.log('Duplets');
11 | console.log(detectTuplets(4, [[0], [0.5]]));
12 | console.log(detectTuplets(4, [ [0.5]]));
13 |
14 | console.log('Triplets');
15 | console.log(detectTuplets(4, [[0], [0.33]]));
16 | console.log(detectTuplets(4, [[0], [0.333], [0.667]]));
17 | console.log(detectTuplets(4, [ [0.333], [0.667]]));
18 | console.log(detectTuplets(4, [[0], [0.333] ]));
19 | console.log(detectTuplets(4, [ [0.333] ]));
20 | console.log(detectTuplets(4, [[0], [0.333], [0.7]]));
21 | console.log(detectTuplets(4, [[0.1], [0.3], [0.7]]), 'Human');
22 |
23 | console.log(detectTuplets(4, [[0], [0.667] ]));
24 | console.log(detectTuplets(4, [[0], [0.667], [1.333]]));
25 | console.log(detectTuplets(4, [ [0.667], [1.333]]));
26 | console.log(detectTuplets(4, [ [1.333]]));
27 |
28 | console.log('Quadruplets');
29 | console.log(detectTuplets(4, [[0], [0.25]]));
30 | console.log(detectTuplets(4, [[0], [0.25], [0.5]]));
31 | console.log(detectTuplets(4, [[0], [0.25], [0.5], [0.75]]));
32 | console.log(detectTuplets(4, [ [0.25], [0.5], [0.75]]));
33 | console.log(detectTuplets(4, [[0], [0.5], [0.75]]));
34 | console.log(detectTuplets(4, [[0], [0.25], [0.75]]));
35 | console.log(detectTuplets(4, [[0], [0.25], [0.5] ]));
36 | console.log(detectTuplets(4, [ [0.5], [0.75]]));
37 | console.log(detectTuplets(4, [[0], [0.75]]));
38 | console.log(detectTuplets(4, [[0], [0.25] ]));
39 | console.log(detectTuplets(4, [ [0.25], [0.5] ]));
40 | console.log(detectTuplets(4, [ [0.25], [0.75]]));
41 | console.log(detectTuplets(4, [ [0.25] ]));
42 | console.log(detectTuplets(4, [ [0.75]]));
43 | console.log(detectTuplets(4, [[0], [0.29], [0.52], [0.7]]), 'Human');
44 |
45 | console.log('Quintuplets');
46 | console.log(detectTuplets(4, [[0], [1/5]]));
47 | console.log(detectTuplets(4, [[0], [1/5], [2/5]]));
48 | console.log(detectTuplets(4, [[0], [1/5], [2/5], [3/5]]));
49 | console.log(detectTuplets(4, [[0], [1/5], [2/5], [3/5], [4/5]]));
50 | console.log(detectTuplets(4, [ [1/5], [2/5], [3/5], [4/5]]));
51 | console.log(detectTuplets(4, [[0], [2/5], [3/5], [4/5]]));
52 | console.log(detectTuplets(4, [[0], [1/5], [3/5], [4/5]]));
53 | console.log(detectTuplets(4, [[0], [1/5], [2/5], [4/5]]));
54 | console.log(detectTuplets(4, [[0], [1/5], [2/5], [3/5] ]));
55 | console.log(detectTuplets(4, [ [2/5], [3/5], [4/5]]));
56 | console.log(detectTuplets(4, [[0], [3/5], [4/5]]));
57 | console.log(detectTuplets(4, [[0], [1/5], [4/5]]));
58 | console.log(detectTuplets(4, [[0], [1/5], [2/5], ]));
59 | console.log(detectTuplets(4, [ [1/5], [2/5], [3/5] ]));
60 | console.log(detectTuplets(4, [ [3/5], [4/5]]));
61 | console.log(detectTuplets(4, [[0], [4/5]]));
62 | console.log(detectTuplets(4, [[0], [1/5] ]));
63 | console.log(detectTuplets(4, [ [1/5], [2/5] ]));
64 | console.log(detectTuplets(4, [ [2/5], [3/5] ]));
65 | console.log(detectTuplets(4, [ [1/5] ]));
66 | console.log(detectTuplets(4, [ [2/5] ]));
67 | console.log(detectTuplets(4, [ [3/5] ]));
68 | console.log(detectTuplets(4, [ [4/5]]));
69 |
70 | console.log('Septuplets');
71 | console.log(detectTuplets(4, [[0], [1/7]]));
72 | console.log(detectTuplets(4, [[0], [1/7], [2/7]]));
73 | console.log(detectTuplets(4, [[0], [1/7], [2/7], [3/7]]));
74 | console.log(detectTuplets(4, [[0], [1/7], [2/7], [3/7], [4/7]]));
75 | console.log(detectTuplets(4, [[0], [1/7], [2/7], [3/7], [4/7], [5/7]]));
76 | console.log(detectTuplets(4, [[0], [1/7], [2/7], [3/7], [4/7], [5/7], [6/7]]));
77 | console.log(detectTuplets(4, [ [1/7], [2/7], [3/7], [4/7], [5/7], [6/7]]));
78 | console.log(detectTuplets(4, [[0], [2/7], [3/7], [4/7], [5/7], [6/7]]));
79 | console.log(detectTuplets(4, [[0], [1/7], [3/7], [4/7], [5/7], [6/7]]));
80 | console.log(detectTuplets(4, [[0], [1/7], [2/7], [4/7], [5/7], [6/7]]));
81 | console.log(detectTuplets(4, [[0], [1/7], [2/7], [3/7], [5/7], [6/7]]));
82 | console.log(detectTuplets(4, [[0], [1/7], [2/7], [3/7], [4/7], [6/7]]));
83 | console.log(detectTuplets(4, [[0], [1/7], [2/7], [3/7], [4/7], [5/7] ]));
84 | console.log(detectTuplets(4, [ [2/7], [3/7], [4/7], [5/7], [6/7]]));
85 | console.log(detectTuplets(4, [[0], [3/7], [4/7], [5/7], [6/7]]));
86 | console.log(detectTuplets(4, [[0], [1/7], [4/7], [5/7], [6/7]]));
87 | console.log(detectTuplets(4, [[0], [1/7], [2/7], [5/7], [6/7]]));
88 | console.log(detectTuplets(4, [[0], [1/7], [2/7], [3/7], [6/7]]));
89 | console.log(detectTuplets(4, [[0], [1/7], [2/7], [3/7], [4/7], ]));
90 | console.log(detectTuplets(4, [ [1/7], [2/7], [3/7], [4/7], [5/7] ]));
91 |
--------------------------------------------------------------------------------
/test/dolphin-dance.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | <scribe-music>
5 |
6 |
7 |
8 |
9 |
10 |
11 |
20 |
21 |
30 |
31 |
40 |
41 |
42 |
43 |
44 |
45 |
52 |
53 |
68 |
69 |
70 |
71 |
72 | Dolphin Dance
73 | Herbie Hancock
74 |
112 |
113 |
114 |
115 |
116 |
119 |
120 |
121 |
--------------------------------------------------------------------------------
/fonts/Ash/OFL.txt:
--------------------------------------------------------------------------------
1 | Copyright © 2021 MakeMusic, Inc. (https://www.makemusic.com/), with Reserved Font Name “Finale”.
2 | Copyright © 2021 MakeMusic, Inc. (https://www.makemusic.com/), with Reserved Font Name “Maestro”.
3 | Copyright © 2021 MakeMusic, Inc. (https://www.makemusic.com/), with Reserved Font Name “Broadway”.
4 | Copyright © 2021 MakeMusic, Inc. (https://www.makemusic.com/), with Reserved Font Name “Engraver”.
5 | Copyright © 2021 MakeMusic, Inc. (https://www.makemusic.com/), with Reserved Font Name “Jazz”.
6 | Copyright © 2021 MakeMusic, Inc. (https://www.makemusic.com/), with Reserved Font Name “Ash”.
7 | Copyright © 2021 MakeMusic, Inc. (https://www.makemusic.com/), with Reserved Font Name “Legacy”.
8 |
9 | This Font Software is licensed under the SIL Open Font License, Version 1.1.
10 | This license is copied below, and is also available with a FAQ at:
11 | http://scripts.sil.org/OFL
12 |
13 |
14 | -----------------------------------------------------------
15 | SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
16 | -----------------------------------------------------------
17 |
18 | PREAMBLE
19 | The goals of the Open Font License (OFL) are to stimulate worldwide
20 | development of collaborative font projects, to support the font creation
21 | efforts of academic and linguistic communities, and to provide a free and
22 | open framework in which fonts may be shared and improved in partnership
23 | with others.
24 |
25 | The OFL allows the licensed fonts to be used, studied, modified and
26 | redistributed freely as long as they are not sold by themselves. The
27 | fonts, including any derivative works, can be bundled, embedded,
28 | redistributed and/or sold with any software provided that any reserved
29 | names are not used by derivative works. The fonts and derivatives,
30 | however, cannot be released under any other type of license. The
31 | requirement for fonts to remain under this license does not apply
32 | to any document created using the fonts or their derivatives.
33 |
34 | DEFINITIONS
35 | "Font Software" refers to the set of files released by the Copyright
36 | Holder(s) under this license and clearly marked as such. This may
37 | include source files, build scripts and documentation.
38 |
39 | "Reserved Font Name" refers to any names specified as such after the
40 | copyright statement(s).
41 |
42 | "Original Version" refers to the collection of Font Software components as
43 | distributed by the Copyright Holder(s).
44 |
45 | "Modified Version" refers to any derivative made by adding to, deleting,
46 | or substituting -- in part or in whole -- any of the components of the
47 | Original Version, by changing formats or by porting the Font Software to a
48 | new environment.
49 |
50 | "Author" refers to any designer, engineer, programmer, technical
51 | writer or other person who contributed to the Font Software.
52 |
53 | PERMISSION & CONDITIONS
54 | Permission is hereby granted, free of charge, to any person obtaining
55 | a copy of the Font Software, to use, study, copy, merge, embed, modify,
56 | redistribute, and sell modified and unmodified copies of the Font
57 | Software, subject to the following conditions:
58 |
59 | 1) Neither the Font Software nor any of its individual components,
60 | in Original or Modified Versions, may be sold by itself.
61 |
62 | 2) Original or Modified Versions of the Font Software may be bundled,
63 | redistributed and/or sold with any software, provided that each copy
64 | contains the above copyright notice and this license. These can be
65 | included either as stand-alone text files, human-readable headers or
66 | in the appropriate machine-readable metadata fields within text or
67 | binary files as long as those fields can be easily viewed by the user.
68 |
69 | 3) No Modified Version of the Font Software may use the Reserved Font
70 | Name(s) unless explicit written permission is granted by the corresponding
71 | Copyright Holder. This restriction only applies to the primary font name as
72 | presented to the users.
73 |
74 | 4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
75 | Software shall not be used to promote, endorse or advertise any
76 | Modified Version, except to acknowledge the contribution(s) of the
77 | Copyright Holder(s) and the Author(s) or with their explicit written
78 | permission.
79 |
80 | 5) The Font Software, modified or unmodified, in part or in whole,
81 | must be distributed entirely under this license, and must not be
82 | distributed under any other license. The requirement for fonts to
83 | remain under this license does not apply to any document created
84 | using the Font Software.
85 |
86 | TERMINATION
87 | This license becomes null and void if any of the above conditions are
88 | not met.
89 |
90 | DISCLAIMER
91 | THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
92 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
93 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
94 | OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
95 | COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
96 | INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
97 | DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
98 | FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
99 | OTHER DEALINGS IN THE FONT SOFTWARE.
100 |
--------------------------------------------------------------------------------
/fonts/AshText/OFL.txt:
--------------------------------------------------------------------------------
1 | Copyright © 2021 MakeMusic, Inc. (https://www.makemusic.com/), with Reserved Font Name “Finale”.
2 | Copyright © 2021 MakeMusic, Inc. (https://www.makemusic.com/), with Reserved Font Name “Maestro”.
3 | Copyright © 2021 MakeMusic, Inc. (https://www.makemusic.com/), with Reserved Font Name “Broadway”.
4 | Copyright © 2021 MakeMusic, Inc. (https://www.makemusic.com/), with Reserved Font Name “Engraver”.
5 | Copyright © 2021 MakeMusic, Inc. (https://www.makemusic.com/), with Reserved Font Name “Jazz”.
6 | Copyright © 2021 MakeMusic, Inc. (https://www.makemusic.com/), with Reserved Font Name “Ash”.
7 | Copyright © 2021 MakeMusic, Inc. (https://www.makemusic.com/), with Reserved Font Name “Legacy”.
8 |
9 | This Font Software is licensed under the SIL Open Font License, Version 1.1.
10 | This license is copied below, and is also available with a FAQ at:
11 | http://scripts.sil.org/OFL
12 |
13 |
14 | -----------------------------------------------------------
15 | SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
16 | -----------------------------------------------------------
17 |
18 | PREAMBLE
19 | The goals of the Open Font License (OFL) are to stimulate worldwide
20 | development of collaborative font projects, to support the font creation
21 | efforts of academic and linguistic communities, and to provide a free and
22 | open framework in which fonts may be shared and improved in partnership
23 | with others.
24 |
25 | The OFL allows the licensed fonts to be used, studied, modified and
26 | redistributed freely as long as they are not sold by themselves. The
27 | fonts, including any derivative works, can be bundled, embedded,
28 | redistributed and/or sold with any software provided that any reserved
29 | names are not used by derivative works. The fonts and derivatives,
30 | however, cannot be released under any other type of license. The
31 | requirement for fonts to remain under this license does not apply
32 | to any document created using the fonts or their derivatives.
33 |
34 | DEFINITIONS
35 | "Font Software" refers to the set of files released by the Copyright
36 | Holder(s) under this license and clearly marked as such. This may
37 | include source files, build scripts and documentation.
38 |
39 | "Reserved Font Name" refers to any names specified as such after the
40 | copyright statement(s).
41 |
42 | "Original Version" refers to the collection of Font Software components as
43 | distributed by the Copyright Holder(s).
44 |
45 | "Modified Version" refers to any derivative made by adding to, deleting,
46 | or substituting -- in part or in whole -- any of the components of the
47 | Original Version, by changing formats or by porting the Font Software to a
48 | new environment.
49 |
50 | "Author" refers to any designer, engineer, programmer, technical
51 | writer or other person who contributed to the Font Software.
52 |
53 | PERMISSION & CONDITIONS
54 | Permission is hereby granted, free of charge, to any person obtaining
55 | a copy of the Font Software, to use, study, copy, merge, embed, modify,
56 | redistribute, and sell modified and unmodified copies of the Font
57 | Software, subject to the following conditions:
58 |
59 | 1) Neither the Font Software nor any of its individual components,
60 | in Original or Modified Versions, may be sold by itself.
61 |
62 | 2) Original or Modified Versions of the Font Software may be bundled,
63 | redistributed and/or sold with any software, provided that each copy
64 | contains the above copyright notice and this license. These can be
65 | included either as stand-alone text files, human-readable headers or
66 | in the appropriate machine-readable metadata fields within text or
67 | binary files as long as those fields can be easily viewed by the user.
68 |
69 | 3) No Modified Version of the Font Software may use the Reserved Font
70 | Name(s) unless explicit written permission is granted by the corresponding
71 | Copyright Holder. This restriction only applies to the primary font name as
72 | presented to the users.
73 |
74 | 4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
75 | Software shall not be used to promote, endorse or advertise any
76 | Modified Version, except to acknowledge the contribution(s) of the
77 | Copyright Holder(s) and the Author(s) or with their explicit written
78 | permission.
79 |
80 | 5) The Font Software, modified or unmodified, in part or in whole,
81 | must be distributed entirely under this license, and must not be
82 | distributed under any other license. The requirement for fonts to
83 | remain under this license does not apply to any document created
84 | using the Font Software.
85 |
86 | TERMINATION
87 | This license becomes null and void if any of the above conditions are
88 | not met.
89 |
90 | DISCLAIMER
91 | THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
92 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
93 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
94 | OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
95 | COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
96 | INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
97 | DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
98 | FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
99 | OTHER DEALINGS IN THE FONT SOFTWARE.
100 |
--------------------------------------------------------------------------------
/fonts/Broadway/OFL.txt:
--------------------------------------------------------------------------------
1 | Copyright © 2021 MakeMusic, Inc. (https://www.makemusic.com/), with Reserved Font Name “Finale”.
2 | Copyright © 2021 MakeMusic, Inc. (https://www.makemusic.com/), with Reserved Font Name “Maestro”.
3 | Copyright © 2021 MakeMusic, Inc. (https://www.makemusic.com/), with Reserved Font Name “Broadway”.
4 | Copyright © 2021 MakeMusic, Inc. (https://www.makemusic.com/), with Reserved Font Name “Engraver”.
5 | Copyright © 2021 MakeMusic, Inc. (https://www.makemusic.com/), with Reserved Font Name “Jazz”.
6 | Copyright © 2021 MakeMusic, Inc. (https://www.makemusic.com/), with Reserved Font Name “Ash”.
7 | Copyright © 2021 MakeMusic, Inc. (https://www.makemusic.com/), with Reserved Font Name “Legacy”.
8 |
9 | This Font Software is licensed under the SIL Open Font License, Version 1.1.
10 | This license is copied below, and is also available with a FAQ at:
11 | http://scripts.sil.org/OFL
12 |
13 |
14 | -----------------------------------------------------------
15 | SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
16 | -----------------------------------------------------------
17 |
18 | PREAMBLE
19 | The goals of the Open Font License (OFL) are to stimulate worldwide
20 | development of collaborative font projects, to support the font creation
21 | efforts of academic and linguistic communities, and to provide a free and
22 | open framework in which fonts may be shared and improved in partnership
23 | with others.
24 |
25 | The OFL allows the licensed fonts to be used, studied, modified and
26 | redistributed freely as long as they are not sold by themselves. The
27 | fonts, including any derivative works, can be bundled, embedded,
28 | redistributed and/or sold with any software provided that any reserved
29 | names are not used by derivative works. The fonts and derivatives,
30 | however, cannot be released under any other type of license. The
31 | requirement for fonts to remain under this license does not apply
32 | to any document created using the fonts or their derivatives.
33 |
34 | DEFINITIONS
35 | "Font Software" refers to the set of files released by the Copyright
36 | Holder(s) under this license and clearly marked as such. This may
37 | include source files, build scripts and documentation.
38 |
39 | "Reserved Font Name" refers to any names specified as such after the
40 | copyright statement(s).
41 |
42 | "Original Version" refers to the collection of Font Software components as
43 | distributed by the Copyright Holder(s).
44 |
45 | "Modified Version" refers to any derivative made by adding to, deleting,
46 | or substituting -- in part or in whole -- any of the components of the
47 | Original Version, by changing formats or by porting the Font Software to a
48 | new environment.
49 |
50 | "Author" refers to any designer, engineer, programmer, technical
51 | writer or other person who contributed to the Font Software.
52 |
53 | PERMISSION & CONDITIONS
54 | Permission is hereby granted, free of charge, to any person obtaining
55 | a copy of the Font Software, to use, study, copy, merge, embed, modify,
56 | redistribute, and sell modified and unmodified copies of the Font
57 | Software, subject to the following conditions:
58 |
59 | 1) Neither the Font Software nor any of its individual components,
60 | in Original or Modified Versions, may be sold by itself.
61 |
62 | 2) Original or Modified Versions of the Font Software may be bundled,
63 | redistributed and/or sold with any software, provided that each copy
64 | contains the above copyright notice and this license. These can be
65 | included either as stand-alone text files, human-readable headers or
66 | in the appropriate machine-readable metadata fields within text or
67 | binary files as long as those fields can be easily viewed by the user.
68 |
69 | 3) No Modified Version of the Font Software may use the Reserved Font
70 | Name(s) unless explicit written permission is granted by the corresponding
71 | Copyright Holder. This restriction only applies to the primary font name as
72 | presented to the users.
73 |
74 | 4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
75 | Software shall not be used to promote, endorse or advertise any
76 | Modified Version, except to acknowledge the contribution(s) of the
77 | Copyright Holder(s) and the Author(s) or with their explicit written
78 | permission.
79 |
80 | 5) The Font Software, modified or unmodified, in part or in whole,
81 | must be distributed entirely under this license, and must not be
82 | distributed under any other license. The requirement for fonts to
83 | remain under this license does not apply to any document created
84 | using the Font Software.
85 |
86 | TERMINATION
87 | This license becomes null and void if any of the above conditions are
88 | not met.
89 |
90 | DISCLAIMER
91 | THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
92 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
93 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
94 | OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
95 | COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
96 | INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
97 | DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
98 | FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
99 | OTHER DEALINGS IN THE FONT SOFTWARE.
100 |
--------------------------------------------------------------------------------
/fonts/Jazz/OFL.txt:
--------------------------------------------------------------------------------
1 | Copyright © 2021 MakeMusic, Inc. (https://www.makemusic.com/), with Reserved Font Name “Finale”.
2 | Copyright © 2021 MakeMusic, Inc. (https://www.makemusic.com/), with Reserved Font Name “Maestro”.
3 | Copyright © 2021 MakeMusic, Inc. (https://www.makemusic.com/), with Reserved Font Name “Broadway”.
4 | Copyright © 2021 MakeMusic, Inc. (https://www.makemusic.com/), with Reserved Font Name “Engraver”.
5 | Copyright © 2021 MakeMusic, Inc. (https://www.makemusic.com/), with Reserved Font Name “Jazz”.
6 | Copyright © 2021 MakeMusic, Inc. (https://www.makemusic.com/), with Reserved Font Name “Ash”.
7 | Copyright © 2021 MakeMusic, Inc. (https://www.makemusic.com/), with Reserved Font Name “Legacy”.
8 |
9 | This Font Software is licensed under the SIL Open Font License, Version 1.1.
10 | This license is copied below, and is also available with a FAQ at:
11 | http://scripts.sil.org/OFL
12 |
13 |
14 | -----------------------------------------------------------
15 | SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
16 | -----------------------------------------------------------
17 |
18 | PREAMBLE
19 | The goals of the Open Font License (OFL) are to stimulate worldwide
20 | development of collaborative font projects, to support the font creation
21 | efforts of academic and linguistic communities, and to provide a free and
22 | open framework in which fonts may be shared and improved in partnership
23 | with others.
24 |
25 | The OFL allows the licensed fonts to be used, studied, modified and
26 | redistributed freely as long as they are not sold by themselves. The
27 | fonts, including any derivative works, can be bundled, embedded,
28 | redistributed and/or sold with any software provided that any reserved
29 | names are not used by derivative works. The fonts and derivatives,
30 | however, cannot be released under any other type of license. The
31 | requirement for fonts to remain under this license does not apply
32 | to any document created using the fonts or their derivatives.
33 |
34 | DEFINITIONS
35 | "Font Software" refers to the set of files released by the Copyright
36 | Holder(s) under this license and clearly marked as such. This may
37 | include source files, build scripts and documentation.
38 |
39 | "Reserved Font Name" refers to any names specified as such after the
40 | copyright statement(s).
41 |
42 | "Original Version" refers to the collection of Font Software components as
43 | distributed by the Copyright Holder(s).
44 |
45 | "Modified Version" refers to any derivative made by adding to, deleting,
46 | or substituting -- in part or in whole -- any of the components of the
47 | Original Version, by changing formats or by porting the Font Software to a
48 | new environment.
49 |
50 | "Author" refers to any designer, engineer, programmer, technical
51 | writer or other person who contributed to the Font Software.
52 |
53 | PERMISSION & CONDITIONS
54 | Permission is hereby granted, free of charge, to any person obtaining
55 | a copy of the Font Software, to use, study, copy, merge, embed, modify,
56 | redistribute, and sell modified and unmodified copies of the Font
57 | Software, subject to the following conditions:
58 |
59 | 1) Neither the Font Software nor any of its individual components,
60 | in Original or Modified Versions, may be sold by itself.
61 |
62 | 2) Original or Modified Versions of the Font Software may be bundled,
63 | redistributed and/or sold with any software, provided that each copy
64 | contains the above copyright notice and this license. These can be
65 | included either as stand-alone text files, human-readable headers or
66 | in the appropriate machine-readable metadata fields within text or
67 | binary files as long as those fields can be easily viewed by the user.
68 |
69 | 3) No Modified Version of the Font Software may use the Reserved Font
70 | Name(s) unless explicit written permission is granted by the corresponding
71 | Copyright Holder. This restriction only applies to the primary font name as
72 | presented to the users.
73 |
74 | 4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
75 | Software shall not be used to promote, endorse or advertise any
76 | Modified Version, except to acknowledge the contribution(s) of the
77 | Copyright Holder(s) and the Author(s) or with their explicit written
78 | permission.
79 |
80 | 5) The Font Software, modified or unmodified, in part or in whole,
81 | must be distributed entirely under this license, and must not be
82 | distributed under any other license. The requirement for fonts to
83 | remain under this license does not apply to any document created
84 | using the Font Software.
85 |
86 | TERMINATION
87 | This license becomes null and void if any of the above conditions are
88 | not met.
89 |
90 | DISCLAIMER
91 | THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
92 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
93 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
94 | OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
95 | COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
96 | INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
97 | DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
98 | FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
99 | OTHER DEALINGS IN THE FONT SOFTWARE.
100 |
--------------------------------------------------------------------------------
/fonts/JazzText/OFL.txt:
--------------------------------------------------------------------------------
1 | Copyright © 2021 MakeMusic, Inc. (https://www.makemusic.com/), with Reserved Font Name “Finale”.
2 | Copyright © 2021 MakeMusic, Inc. (https://www.makemusic.com/), with Reserved Font Name “Maestro”.
3 | Copyright © 2021 MakeMusic, Inc. (https://www.makemusic.com/), with Reserved Font Name “Broadway”.
4 | Copyright © 2021 MakeMusic, Inc. (https://www.makemusic.com/), with Reserved Font Name “Engraver”.
5 | Copyright © 2021 MakeMusic, Inc. (https://www.makemusic.com/), with Reserved Font Name “Jazz”.
6 | Copyright © 2021 MakeMusic, Inc. (https://www.makemusic.com/), with Reserved Font Name “Ash”.
7 | Copyright © 2021 MakeMusic, Inc. (https://www.makemusic.com/), with Reserved Font Name “Legacy”.
8 |
9 | This Font Software is licensed under the SIL Open Font License, Version 1.1.
10 | This license is copied below, and is also available with a FAQ at:
11 | http://scripts.sil.org/OFL
12 |
13 |
14 | -----------------------------------------------------------
15 | SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
16 | -----------------------------------------------------------
17 |
18 | PREAMBLE
19 | The goals of the Open Font License (OFL) are to stimulate worldwide
20 | development of collaborative font projects, to support the font creation
21 | efforts of academic and linguistic communities, and to provide a free and
22 | open framework in which fonts may be shared and improved in partnership
23 | with others.
24 |
25 | The OFL allows the licensed fonts to be used, studied, modified and
26 | redistributed freely as long as they are not sold by themselves. The
27 | fonts, including any derivative works, can be bundled, embedded,
28 | redistributed and/or sold with any software provided that any reserved
29 | names are not used by derivative works. The fonts and derivatives,
30 | however, cannot be released under any other type of license. The
31 | requirement for fonts to remain under this license does not apply
32 | to any document created using the fonts or their derivatives.
33 |
34 | DEFINITIONS
35 | "Font Software" refers to the set of files released by the Copyright
36 | Holder(s) under this license and clearly marked as such. This may
37 | include source files, build scripts and documentation.
38 |
39 | "Reserved Font Name" refers to any names specified as such after the
40 | copyright statement(s).
41 |
42 | "Original Version" refers to the collection of Font Software components as
43 | distributed by the Copyright Holder(s).
44 |
45 | "Modified Version" refers to any derivative made by adding to, deleting,
46 | or substituting -- in part or in whole -- any of the components of the
47 | Original Version, by changing formats or by porting the Font Software to a
48 | new environment.
49 |
50 | "Author" refers to any designer, engineer, programmer, technical
51 | writer or other person who contributed to the Font Software.
52 |
53 | PERMISSION & CONDITIONS
54 | Permission is hereby granted, free of charge, to any person obtaining
55 | a copy of the Font Software, to use, study, copy, merge, embed, modify,
56 | redistribute, and sell modified and unmodified copies of the Font
57 | Software, subject to the following conditions:
58 |
59 | 1) Neither the Font Software nor any of its individual components,
60 | in Original or Modified Versions, may be sold by itself.
61 |
62 | 2) Original or Modified Versions of the Font Software may be bundled,
63 | redistributed and/or sold with any software, provided that each copy
64 | contains the above copyright notice and this license. These can be
65 | included either as stand-alone text files, human-readable headers or
66 | in the appropriate machine-readable metadata fields within text or
67 | binary files as long as those fields can be easily viewed by the user.
68 |
69 | 3) No Modified Version of the Font Software may use the Reserved Font
70 | Name(s) unless explicit written permission is granted by the corresponding
71 | Copyright Holder. This restriction only applies to the primary font name as
72 | presented to the users.
73 |
74 | 4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
75 | Software shall not be used to promote, endorse or advertise any
76 | Modified Version, except to acknowledge the contribution(s) of the
77 | Copyright Holder(s) and the Author(s) or with their explicit written
78 | permission.
79 |
80 | 5) The Font Software, modified or unmodified, in part or in whole,
81 | must be distributed entirely under this license, and must not be
82 | distributed under any other license. The requirement for fonts to
83 | remain under this license does not apply to any document created
84 | using the Font Software.
85 |
86 | TERMINATION
87 | This license becomes null and void if any of the above conditions are
88 | not met.
89 |
90 | DISCLAIMER
91 | THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
92 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
93 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
94 | OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
95 | COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
96 | INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
97 | DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
98 | FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
99 | OTHER DEALINGS IN THE FONT SOFTWARE.
100 |
--------------------------------------------------------------------------------
/fonts/BroadwayText/OFL.txt:
--------------------------------------------------------------------------------
1 | Copyright © 2021 MakeMusic, Inc. (https://www.makemusic.com/), with Reserved Font Name “Finale”.
2 | Copyright © 2021 MakeMusic, Inc. (https://www.makemusic.com/), with Reserved Font Name “Maestro”.
3 | Copyright © 2021 MakeMusic, Inc. (https://www.makemusic.com/), with Reserved Font Name “Broadway”.
4 | Copyright © 2021 MakeMusic, Inc. (https://www.makemusic.com/), with Reserved Font Name “Engraver”.
5 | Copyright © 2021 MakeMusic, Inc. (https://www.makemusic.com/), with Reserved Font Name “Jazz”.
6 | Copyright © 2021 MakeMusic, Inc. (https://www.makemusic.com/), with Reserved Font Name “Ash”.
7 | Copyright © 2021 MakeMusic, Inc. (https://www.makemusic.com/), with Reserved Font Name “Legacy”.
8 |
9 | This Font Software is licensed under the SIL Open Font License, Version 1.1.
10 | This license is copied below, and is also available with a FAQ at:
11 | http://scripts.sil.org/OFL
12 |
13 |
14 | -----------------------------------------------------------
15 | SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
16 | -----------------------------------------------------------
17 |
18 | PREAMBLE
19 | The goals of the Open Font License (OFL) are to stimulate worldwide
20 | development of collaborative font projects, to support the font creation
21 | efforts of academic and linguistic communities, and to provide a free and
22 | open framework in which fonts may be shared and improved in partnership
23 | with others.
24 |
25 | The OFL allows the licensed fonts to be used, studied, modified and
26 | redistributed freely as long as they are not sold by themselves. The
27 | fonts, including any derivative works, can be bundled, embedded,
28 | redistributed and/or sold with any software provided that any reserved
29 | names are not used by derivative works. The fonts and derivatives,
30 | however, cannot be released under any other type of license. The
31 | requirement for fonts to remain under this license does not apply
32 | to any document created using the fonts or their derivatives.
33 |
34 | DEFINITIONS
35 | "Font Software" refers to the set of files released by the Copyright
36 | Holder(s) under this license and clearly marked as such. This may
37 | include source files, build scripts and documentation.
38 |
39 | "Reserved Font Name" refers to any names specified as such after the
40 | copyright statement(s).
41 |
42 | "Original Version" refers to the collection of Font Software components as
43 | distributed by the Copyright Holder(s).
44 |
45 | "Modified Version" refers to any derivative made by adding to, deleting,
46 | or substituting -- in part or in whole -- any of the components of the
47 | Original Version, by changing formats or by porting the Font Software to a
48 | new environment.
49 |
50 | "Author" refers to any designer, engineer, programmer, technical
51 | writer or other person who contributed to the Font Software.
52 |
53 | PERMISSION & CONDITIONS
54 | Permission is hereby granted, free of charge, to any person obtaining
55 | a copy of the Font Software, to use, study, copy, merge, embed, modify,
56 | redistribute, and sell modified and unmodified copies of the Font
57 | Software, subject to the following conditions:
58 |
59 | 1) Neither the Font Software nor any of its individual components,
60 | in Original or Modified Versions, may be sold by itself.
61 |
62 | 2) Original or Modified Versions of the Font Software may be bundled,
63 | redistributed and/or sold with any software, provided that each copy
64 | contains the above copyright notice and this license. These can be
65 | included either as stand-alone text files, human-readable headers or
66 | in the appropriate machine-readable metadata fields within text or
67 | binary files as long as those fields can be easily viewed by the user.
68 |
69 | 3) No Modified Version of the Font Software may use the Reserved Font
70 | Name(s) unless explicit written permission is granted by the corresponding
71 | Copyright Holder. This restriction only applies to the primary font name as
72 | presented to the users.
73 |
74 | 4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
75 | Software shall not be used to promote, endorse or advertise any
76 | Modified Version, except to acknowledge the contribution(s) of the
77 | Copyright Holder(s) and the Author(s) or with their explicit written
78 | permission.
79 |
80 | 5) The Font Software, modified or unmodified, in part or in whole,
81 | must be distributed entirely under this license, and must not be
82 | distributed under any other license. The requirement for fonts to
83 | remain under this license does not apply to any document created
84 | using the Font Software.
85 |
86 | TERMINATION
87 | This license becomes null and void if any of the above conditions are
88 | not met.
89 |
90 | DISCLAIMER
91 | THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
92 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
93 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
94 | OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
95 | COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
96 | INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
97 | DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
98 | FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
99 | OTHER DEALINGS IN THE FONT SOFTWARE.
100 |
--------------------------------------------------------------------------------
/data/dolphin-dance.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "Dolphin Dance",
3 | "author": { "name": "Herbie Hancock" },
4 | "events": [
5 | [0, "key", "Eb"],
6 | [0, "meter", 4, 1],
7 | [0, "sequence", 1, 0, 16],
8 | [16, "sequence", 2, 0, 136, "displace", 16],
9 | [152, "sequence", 4, 0, 16]
10 | ],
11 |
12 | "sequences": [{
13 | "id": 1,
14 | "name": "Intro",
15 | "events": [
16 | [0,"chord",3,"∆",4],
17 | [2,"note",67,0.25,0.5],
18 | [2.5,"note",68,0.25,0.5],
19 | [3,"note",70,0.25,0.5],
20 | [3.5,"note",65,0.25,3.5],
21 | [4,"chord",10,"-",4],
22 | [8,"chord",3,"∆",4],
23 | [10,"note",67,0.25,0.5],
24 | [10.5,"note",68,0.25,0.5],
25 | [11,"note",70,0.25,0.5],
26 | [11.5,"note",65,0.25,3.5],
27 | [12,"chord",2,"ø",2],
28 | [14,"chord",7,"7alt",2]
29 | ]
30 | }, {
31 | "id": 2,
32 | "name": "Head",
33 | "events": [
34 | [16,"chord",0,"-",4],
35 | [18,"note",63,0.25,0.5],
36 | [18.5,"note",65,0.25,1],
37 | [19.5,"note",67,0.25,0.5],
38 | [20,"chord",8,"∆(♯11)",2],
39 | [20,"note",62,0.25,1],
40 | [21,"note",62,0.25,2],
41 | [22,"chord",7,"7alt",2],
42 | [24,"chord",0,"-",4],
43 | [26,"note",63,0.25,0.5],
44 | [26.5,"note",65,0.25,0.5],
45 | [27,"note",67,0.25,0.5],
46 | [27.5,"note",62,0.25,3.5],
47 | [28,"chord",9,"-7",2],
48 | [30,"chord",2,"7",2],
49 | [31,"note",60,0.25,1],
50 | [32,"chord",7,"∆",4],
51 | [32,"note",59,0.25,1.5],
52 | [33.5,"note",66,0.25,2.5],
53 | [36,"chord",8,"-7",4],
54 | [36,"note",66,0.25,1.5],
55 | [37.5,"note",66,0.25,0.5],
56 | [38,"note",68,0.25,0.5],
57 | [38.5,"note",66,0.25,0.5],
58 | [39,"note",68,0.25,0.5],
59 | [39.5,"note",70,0.25,4.5],
60 | [40,"chord",5,"-7",6],
61 | [46,"chord",7,"7alt",2],
62 | [48,"chord",0,"-7",8],
63 | [48,"note",67,0.25,1.5],
64 | [49.5,"note",70,0.25,2.5],
65 | [52,"note",70,0.25,1],
66 | [53,"note",70,0.25,0.5],
67 | [53.5,"note",70,0.25,0.5],
68 | [54,"note",72,0.25,0.5],
69 | [54.5,"note",70,0.25,0.5],
70 | [55,"note",72,0.25,0.5],
71 | [55.5,"note",74,0.25,8.5],
72 | [56,"chord",9,"-7",4],
73 | [60,"chord",2,"7",4],
74 | [64,"chord",7,"∆",4],
75 | [66,"note",71,0.25,0.5],
76 | [66.5,"note",72,0.25,0.5],
77 | [67,"note",74,0.25,0.5],
78 | [67.5,"note",69,0.25,3.5],
79 | [68,"chord",7,"7sus",4],
80 | [72,"chord",7,"∆♯11",4],
81 | [74,"note",67,0.25,0.5],
82 | [74.5,"note",69,0.25,0.5],
83 | [75,"note",71,0.25,0.5],
84 | [75.5,"note",65,0.25,3.5],
85 | [76,"chord",7,"7sus",4],
86 | [80,"chord",5,"7sus",4],
87 | [82,"note",63,0.25,0.5],
88 | [82.5,"note",65,0.25,1],
89 | [83.5,"note",67,0.25,0.5],
90 | [84,"chord",5,"∆♯11",4],
91 | [84,"note",62,0.25,1],
92 | [85,"note",62,0.25,2],
93 | [88,"chord",5,"7sus",4],
94 | [90,"note",63,0.25,0.5],
95 | [90.5,"note",65,0.25,0.5],
96 | [91,"note",67,0.25,0.5],
97 | [91.5,"note",69,0.25,4.5],
98 | [92,"chord",4,"-7",2],
99 | [94,"chord",9,"7",2],
100 | [96,"chord",3,"7♯11",4],
101 | [96,"note",69,0.25,0.5],
102 | [96.5,"note",70,0.25,0.5],
103 | [97.3333,"note",69,0.25,0.3333],
104 | [97.6667,"note",68,0.25,0.3333],
105 | [98,"note",69,0.25,1],
106 | [99,"note",69,0.25,0.5],
107 | [99.5,"note",74,0.25,0.5],
108 | [100,"chord",9,"-7",2],
109 | [100.5,"note",71,0.25,3.5],
110 | [102,"chord",2,"7",2],
111 | [104,"chord",11,"-7",2],
112 | [104,"note",71,0.25,0.5],
113 | [104.5,"note",73,0.25,0.5],
114 | [105.3333,"note",71,0.25,0.3333],
115 | [105.6667,"note",69,0.25,0.3333],
116 | [106,"note",71,0.25,1],
117 | [107,"note",71,0.25,0.5],
118 | [107.5,"note",76,0.25,2.5],
119 | [108,"chord",4,"7",2],
120 | [110,"chord",2,"-7",2],
121 | [110,"note",77,0.25,2],
122 | [112,"chord",1,"-7",4],
123 | [112,"note",78,0.25,1.5],
124 | [113.5,"note",76,0.25,1],
125 | [114.5,"note",71,0.25,1],
126 | [115.5,"note",68,0.25,0.5],
127 | [116,"chord",6,"7",4],
128 | [116,"note",75,0.25,3],
129 | [119,"note",66,0.25,0.5],
130 | [119.5,"note",71,0.25,8.5],
131 | [120,"chord",4,"-7",4],
132 | [124,"chord",4,"-♭6",4],
133 | [128,"chord",4,"-7",4],
134 | [132,"chord",4,"-♭6",4],
135 | [136,"chord",3,"7sus",4],
136 | [138,"note",72,0.25,0.5],
137 | [138.5,"note",73,0.25,0.5],
138 | [139,"note",75,0.25,0.5],
139 | [139.5,"note",70,0.25,3.5],
140 | [140,"chord",3,"∆♭6",4],
141 | [144,"chord",3,"7sus♭9",4],
142 | [146,"note",67,0.25,0.5],
143 | [146.5,"note",68,0.25,0.5],
144 | [147,"note",70,0.25,0.5],
145 | [147.5,"note",65,0.25,3.5],
146 | [148,"chord",7,"7alt",4]
147 | ]
148 | }, {
149 | "id": 4,
150 | "name": "Coda",
151 | "events": [
152 | [0, "symbol", "coda"],
153 | [0, "chord",4,"-7",4],
154 | [4, "chord",4,"-♭6",4],
155 | [8, "chord",4,"-7",4],
156 | [12,"chord",4,"-♭6",4]
157 | ]
158 | }],
159 |
160 | "notes": {
161 | "links": [{
162 | "text": "Dolphin Dance – Herbie Hancock – Maiden Voyage",
163 | "url": "https://www.youtube.com/watch?v=RaHCnfI7y74&list=RDRaHCnfI7y74"
164 | }]
165 | }}
166 |
--------------------------------------------------------------------------------
/test/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Scribe
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
28 |
29 |
38 |
39 |
107 |
108 |
109 |
113 |
114 |
124 |
125 |
133 |
134 |
135 |
--------------------------------------------------------------------------------
/modules/parse/parse-abc.js:
--------------------------------------------------------------------------------
1 |
2 | /**
3 | parseABC()
4 | A quick-and-dirty interface with abcjs's parser. Please clean me up.
5 | **/
6 |
7 | import matches from 'fn/matches.js';
8 | import ABCParser from '../../lib/abcjs/modules/parse/abc_parse.js';
9 |
10 | const parser = new ABCParser();
11 |
12 | function TODO(message) {
13 | throw new Error('Todo: ' + message);
14 | }
15 |
16 | function getSequence(id, data) {
17 | let sequence = data.sequences.find(matches({ id }));
18 | if (sequence) return sequence;
19 |
20 | sequence = { id, events: [], cursor: 0 };
21 | data.sequences.push(sequence);
22 | data.events.push([0, "sequence", id]);
23 | return sequence;
24 | }
25 |
26 | function findLastOfType(type, events) {
27 | let n = events.length;
28 | while (n--) {
29 | if (events[n][1] === type) return events[n];
30 | }
31 | }
32 |
33 | function getAbsoluteFromDiatonicPitch(root, degree) {
34 | // Not entirely sure how ABC decides where the 'root' of the melody is.
35 | // We just add 60, but its arbitrary.
36 | return [0,
37 | 0, 2, 4, 5, 7, 9, 11,
38 | 12, 14, 16, 17, 19, 21, 23,
39 | 24, 26, 28, 29, 31, 33, 35,
40 | 36, 38, 40, 41, 43, 45, 47,
41 | 48
42 | ][degree] + root + 60;
43 | }
44 |
45 | export default function parseABC(abc) {
46 | parser.parse(abc);
47 |
48 | const tune = parser.getTune();
49 | const data = tune.lines.reduce((data, line) => {
50 | line.staff.reduce((sequences, staff, i) => {
51 | const sequence = getSequence(i, data);
52 | const events = sequence.events;
53 | //console.log(staff);
54 | // If clef has changed push in a clef event
55 | if (staff.clef) {
56 | const clef = staff.clef.type;
57 | const clefEvent = findLastOfType('clef', events);
58 | if (!clefEvent || clefEvent[2] !== clef) {
59 | events.push([0, 'clef', clef]);
60 | }
61 | }
62 |
63 | // If key has changed push in a key event
64 | let keyEvent = findLastOfType('key', events);
65 | if (staff.key) {
66 | const root = staff.key.mode === '' ?
67 | staff.key.root :
68 | TODO('What happens when ABC decides key.mode is not an empty string?') ;
69 |
70 | const roots = { C: 0, D: 2, E: 4, F: 5, G: 7, A: 9, B: 11 };
71 |
72 | if (!roots[root]) {
73 | TODO('ABC says root is "' + root + '", we have not implemented support for that');
74 | }
75 |
76 | const rootN = roots[root];
77 |
78 | if (!keyEvent || keyEvent[2] !== root) {
79 | keyEvent = [sequence.cursor, 'key', rootN];
80 | events.push(keyEvent);
81 | }
82 | }
83 |
84 | // If meter has changed push in a meter event
85 | if (staff.meter) {
86 | // Not sure why staff.meter.value is an array? Either way it
87 | // contains strings for num/den so we need to convert...
88 | const denominator = 1 / (+staff.meter.value[0].den / 4);
89 | const numerator = +staff.meter.value[0].num * denominator;
90 | const meterEvent = findLastOfType('meter', events);
91 | if (!meterEvent || meterEvent[2] !== numerator || meterEvent[3] !== denominator) {
92 | events.push([0, 'meter', numerator, denominator]);
93 | }
94 | }
95 |
96 | // Push in notes
97 | if (staff.voices) {
98 | const noteEvent = findLastOfType('note', events);
99 | // I am not currently sure why voices is an array
100 | staff.voices[0].reduce((events, voice) => {
101 | if (voice.el_type === 'note') {
102 | voice.pitches.reduce((events, abcPitch) => {
103 | // abcPitch numbers are relative to the key root and
104 | // describe diatonic steps, not semitones
105 |
106 | events.push([sequence.cursor, "note", getAbsoluteFromDiatonicPitch(keyEvent[2], abcPitch.pitch), 0.25, voice.duration * 4]);
107 | return events;
108 | }, events);
109 |
110 | sequence.cursor += voice.duration * 4;
111 | }
112 | else if (voice.el_type === 'bar') {
113 | // If cursor is not at bar start when a bar comes around...
114 | const meterEvent = findLastOfType('meter', events);
115 | if ((sequence.cursor - meterEvent[0]) % meterEvent[2] !== 0) {
116 | // ...advance cursor to next bar start
117 | const wholeBars = Math.floor((sequence.cursor - meterEvent[0]) / meterEvent[2]);
118 | const beat = (wholeBars + 1) * meterEvent[2];
119 | sequence.cursor = beat;
120 | }
121 | }
122 | else {
123 | console.log('ABC voice ignored:', voice);
124 | }
125 |
126 | return events;
127 | }, events);
128 |
129 | if (staff.voices[1]) {
130 | TODO('What do we do with a second array of voices?');
131 | }
132 | }
133 |
134 | return sequences;
135 | }, data.sequences);
136 |
137 | return data;
138 | }, {
139 | name: tune.metaText.title,
140 | events: [],
141 | sequences: [],
142 | meta: tune.metaText
143 | });
144 |
145 | // Remove cursor property which was just for tracking start beats
146 | data.sequences.forEach((sequence) => delete sequence.cursor);
147 | return data;
148 | }
149 |
--------------------------------------------------------------------------------
/modules/render.js:
--------------------------------------------------------------------------------
1 |
2 | import create from 'dom/create.js';
3 | import px from 'dom/parse-length.js';
4 | import rect from 'dom/rect.js';
5 | import { toRootName } from 'midi/note.js';
6 | import SequenceIterator from 'sequence/sequence-iterator.js';
7 | import Stave from './stave.js';
8 | import createBars from './create-bars.js';
9 | import createElement from './create-element.js';
10 | import { renderBeam } from './beam.js';
11 | import * as glyphs from './glyphs.js';
12 | import truncate from './number/truncate.js';
13 | import config from './config.js';
14 | import log from './log.js';
15 |
16 |
17 | const global = globalThis || window;
18 |
19 |
20 | function isInitialMeterEvent(event) {
21 | return event[0] <= 0 && event[1] === 'meter';
22 | }
23 |
24 | function isInitialKeyEvent(event) {
25 | return event[0] <= 0 && event[1] === 'key';
26 | }
27 |
28 | function toElements(nodes, symbol) {
29 | const element = createElement(symbol);
30 | if (element) { nodes.push(element); }
31 | return nodes;
32 | }
33 |
34 | export function toBarElements(elements, bar) {
35 | elements.push(create('div', {
36 | class: `${ bar.stave.type }-stave stave ${ bar.doubleBarLine ? 'end-bar ' : '' }bar${ bar.error ? ' error ': '' }`,
37 | data: {
38 | beat: bar.beat,
39 | duration: bar.duration,
40 | count: bar.count,
41 | key: toRootName(bar.key),
42 | stave: bar.stave.type
43 | },
44 | children: bar.symbols.reduce(toElements, [])
45 | }));
46 |
47 | return elements;
48 | }
49 |
50 | /* Glyphs for triplet mark d d = d 3 d, instead of saying Swing 8ths, for example
51 | text = glyphs.textNoteShort
52 | + glyphs.textBeam8Short
53 | + glyphs.textNote8Short
54 | + '='
55 | + glyphs.textNoteShort
56 | + ' '
57 | + glyphs.textTuplet3Long
58 | + glyphs.note05Up
59 | */
60 |
61 | export function renderElements(data, excludes, clef, keyname, meter, duration = Infinity, transpose = 0, displace = 0, settings = config) {
62 | const events = data.events;
63 |
64 | // If events contains no initial meter and meter is set, insert a meter event
65 | const meterEvent = events.find(isInitialMeterEvent);
66 | if (!meterEvent && meter) events.unshift([0, 'meter', meter[2], meter[3]]);
67 |
68 | // If events contains no initial key and keyname is set, insert a key event
69 | const keyEvent = events.find(isInitialKeyEvent);
70 | if (!keyEvent && keyname) events.unshift([0, 'key', keyname]);
71 |
72 | // Get the stave controller
73 | const stave = Stave[clef || 'treble'];
74 |
75 | // Make transforms list
76 | const transforms = [];
77 | if (transpose) transforms.push("transpose", transpose);
78 | if (displace) transforms.push("displace", displace);
79 |
80 | // Create sequence object
81 | const sequence = new SequenceIterator(events, data.sequences, transforms);
82 |
83 | // Create bar elements
84 | const bars = createBars(sequence, excludes, stave, settings);
85 | const elements = bars.reduce(toBarElements, []);
86 |
87 | //const keysig = elements[0].querySelectorAll('.acci:not([data-beat])');
88 |
89 | // Creates stave with clef and key signature
90 | const bar0 = bars[0];
91 | const key = bar0.key;
92 | const signature = stave.createSignatureSymbols(key);
93 |
94 | // Quick and dirty way of rendering clefs into left hand side bar
95 | const sidebar = create('div', {
96 | class: `${ stave.type }-stave signature-stave stave`,
97 | children: signature.map(createElement),
98 | data: { key: toRootName(key) }
99 | });
100 |
101 | const column = create('div', {
102 | id: 'side',
103 | class: 'side',
104 | data: { key: toRootName(key) }
105 | });
106 |
107 | let n = 24;
108 | while (n--) column.appendChild(sidebar.cloneNode(true));
109 |
110 | // Return array of elements
111 | elements.unshift(column);
112 | return elements;
113 | }
114 |
115 |
116 | const heads = [
117 | create('span', { html: glyphs.head1, style: 'width: min-content;' }),
118 | create('span', { html: glyphs.head2, style: 'width: min-content;' }),
119 | create('span', { html: glyphs.head4, style: 'width: min-content;' })
120 | ];
121 |
122 | function remove(element) {
123 | element.remove();
124 | }
125 |
126 | export function renderStyle(element, root = element) {
127 | // Measure head widths. Be aware that root may be a fragment (a shadow root)
128 | // and getComputedStyle() won't work on a fragment
129 | root.append.apply(root, heads);
130 |
131 | const head1Width = rect(heads[0]).width;
132 | const head2Width = rect(heads[1]).width;
133 | const head3Width = rect(heads[2]).width;
134 | const computed = window.getComputedStyle(heads[0]);
135 | const fontSize = px(computed.fontSize);
136 |
137 | if (global.DEBUG) log('post render font-size', fontSize, 'heads', `${ head1Width }, ${ head2Width }, ${ head3Width }`);
138 |
139 | heads.forEach(remove);
140 |
141 | // Calculate clef and key signature width
142 |
143 | // TODO: sort out a better system of getting the key signature metrics in here.
144 | // One that isn't going to rely on the font so much? Or maybe include the
145 | // metrics in the font stylesheet?
146 | const side = root.querySelector('.side');
147 | const key = side.dataset.key;
148 | const counts = {'G♭': 6, 'C♯': 5, 'D♭': 5, 'G♯': 4, 'A♭': 4, 'D♯': 3, 'E♭': 3, 'B♭': 2, 'F': 1, 'C': 0, 'G': 1, 'D': 2, 'A': 3, 'E': 4, 'B': 5, 'F♯': 6};
149 | const count = counts[key];
150 |
151 | return `--head-1-size: ${ truncate(6, head1Width / fontSize) }; `
152 | + `--head-2-size: ${ truncate(6, head2Width / fontSize) }; `
153 | + `--head-4-size: ${ truncate(6, head3Width / fontSize) }; `
154 | + `--signature-width: ${ (2.25 + count * 0.625 + 0.625).toFixed(4) }em;`;
155 | }
156 |
157 | export function renderDOM(element, root = element) {
158 | // Render beams
159 | root.querySelectorAll('.beam').forEach(renderBeam);
160 | }
161 |
--------------------------------------------------------------------------------
/classes/stave.css:
--------------------------------------------------------------------------------
1 | @import './stave/stave.css';
2 | @import './stave/treble.css';
3 | @import './stave/treble-up.css';
4 | @import './stave/treble-down.css';
5 | @import './stave/alto.css';
6 | @import './stave/bass.css';
7 | @import './stave/piano.css';
8 | @import './stave/drum.css';
9 | @import './stave/percussion.css';
10 |
11 | /* Add a background line of white behind the start of the bar in Safari so that
12 | when bars overlap to disguise Safari's rounding errors this bar hides the
13 | end of the previous bar. */
14 | /*
15 | :host(.safari) > .stave {
16 | background-image: url('data:image/svg+xml;utf8,\
17 | \
18 | \
19 | \
20 | \
21 | \
22 | \
23 | \
24 | \
25 | \
26 | ');
27 | }
28 |
29 | :host(.safari[clef="piano"]) > .stave {
30 | background-image: url('data:image/svg+xml;utf8,\
31 | \
32 | \
33 | \
34 | \
35 | \
36 | \
37 | \
38 | \
39 | \
40 | \
41 | \
42 | \
43 | \
44 | \
45 | ');
46 | }
47 |
48 | :host(.safari[clef="percussion"]) > .stave {
49 | background-image: url('data:image/svg+xml;utf8,\
50 | \
51 | \
52 | \
53 | \
54 | \
55 | ');
56 | }
57 | */
58 |
59 | .stave > .tuplet {
60 | grid-row: stave-center;
61 | /* Do some work to center tuplet vertically in stave row, where normally the
62 | baseline is in the center of the stave row. This may not be the same for
63 | every font, approximative. */
64 | align-self: center;
65 | position: relative;
66 | top: calc(0.175em - 1.3125em);
67 | }
68 |
69 | .stave > .down-tuplet {
70 | top: calc(0.175em - 0.875em);
71 | }
72 |
73 | .stave > .coda {
74 | margin: 0;
75 | margin-left: calc(-1 * var(--bar-padding-left) - 0.5em);
76 | margin-right: 0.25em;
77 | grid-column: bar-begin;
78 | grid-row: chords;
79 | align-self: center;
80 | }
81 |
82 | .bar > .barline {
83 | grid-row: bottom;
84 | align-self: center;
85 | }
86 |
87 | .bar > .acci:not([data-beat]) {
88 | /* Accidental with no beat belongs to key signature in the first column */
89 | grid-column-start: 1;
90 | justify-self: start;
91 |
92 | --acci-acci-gap: 0.3em;
93 | }
94 |
95 | .stave > .clef {
96 | margin-right: 0.25em;
97 | }
98 |
99 | .stave > .acci:not([data-beat]) + .acci:not([data-beat]) {
100 | margin-left: 0.03125em;
101 | }
102 |
103 | .signature-stave > [data-pitch="F♭"] { grid-column: 8; }
104 | .signature-stave > [data-pitch="C♭"] { grid-column: 7; }
105 | .signature-stave > [data-pitch="G♭"] { grid-column: 6; }
106 | .signature-stave > [data-pitch="D♭"] { grid-column: 5; }
107 | .signature-stave > [data-pitch="A♭"] { grid-column: 4; }
108 | .signature-stave > [data-pitch="E♭"] { grid-column: 3; }
109 | .signature-stave > [data-pitch="B♭"] { grid-column: 2; }
110 | .signature-stave > [data-pitch="F♯"] { grid-column: 2; }
111 | .signature-stave > [data-pitch="C♯"] { grid-column: 3; }
112 | .signature-stave > [data-pitch="G♯"] { grid-column: 4; }
113 | .signature-stave > [data-pitch="D♯"] { grid-column: 5; }
114 | .signature-stave > [data-pitch="A♯"] { grid-column: 6; }
115 | .signature-stave > [data-pitch="E♯"] { grid-column: 7; }
116 | .signature-stave > [data-pitch="B♯"] { grid-column: 8; }
117 |
118 | .signature-stave[data-key="C♯"] { width: 3.45em; }
119 | .signature-stave[data-key="F♯"] { width: 3.15em; }
120 | .signature-stave[data-key="B"] { width: 2.85em; }
121 | .signature-stave[data-key="E"] { width: 2.55em; }
122 | .signature-stave[data-key="A"] { width: 2.25em; }
123 | .signature-stave[data-key="D"] { width: 1.95em; }
124 | .signature-stave[data-key="G"] { width: 1.65em; }
125 | .signature-stave { width: 1.35em; }
126 | .signature-stave[data-key="F"] { width: 1.65em; }
127 | .signature-stave[data-key="B♭"] { width: 1.95em; }
128 | .signature-stave[data-key="E♭"] { width: 2.25em; }
129 | .signature-stave[data-key="A♭"] { width: 2.55em; }
130 | .signature-stave[data-key="D♭"] { width: 2.85em; }
131 | .signature-stave[data-key="G♭"] { width: 3.15em; }
132 | .signature-stave[data-key="C♭"] { width: 3.45em; }
133 |
--------------------------------------------------------------------------------
/modules/stave/stave.js:
--------------------------------------------------------------------------------
1 |
2 | import nothing from 'fn/nothing.js';
3 | import { toNoteName, toNoteNumber, toRootName, toRootNumber } from 'midi/note.js';
4 | import { toKeyScale } from '../keys.js';
5 | import { spellRoot, spellPitch } from '../spelling.js';
6 | import { rflatsharp, byFatherCharlesPitch, accidentalChars } from '../pitch.js';
7 | import { major } from '../scale.js';
8 | import * as glyphs from "../glyphs.js";
9 |
10 |
11 | const global = globalThis || window;
12 | const assign = Object.assign;
13 | const { floor, round } = Math;
14 |
15 |
16 | /* Stave */
17 |
18 | export default class Stave {
19 | constructor() {}
20 |
21 | /**
22 | .pitched
23 | A boolean indicating whether this stave supports keys and transposition.
24 | **/
25 | pitched = true;
26 |
27 | /**
28 | .rows
29 | An array of row names, from bottom to top of stave. Row names must correspond
30 | to those defined for a given stave in CSS.
31 | **/
32 | rows = nothing;
33 |
34 | /**
35 | .getNoteHTML(pitch, dynamic, duration)
36 | Get the HTML content used for a note of given `pitch`, `dynamic` and
37 | `duration`. For normal chromatic staves only `duration` really matters, but
38 | percussion staves may replace heads based on pitch and dynamics.
39 | **/
40 |
41 | getNoteHTML(pitch, dynamic, duration) {
42 | const name =
43 | // Semibreve
44 | duration >= 4 ? 'head4' :
45 | // Triplet semibreve
46 | Math.fround(duration) === Math.fround(2.666666667) ? 'head4' :
47 | // Minim
48 | duration >= 2 ? 'head2' :
49 | // Triplet minim
50 | Math.fround(duration) === Math.fround(1.333333333) ? 'head2' :
51 | // Everything else
52 | 'head1' ;
53 |
54 | return `${ glyphs[name] } `;
55 | }
56 |
57 | /**
58 | .minRow, .bottomRow, .centerRow, .topRow, .maxRow
59 | Minimum and maximum row numbers supported by the stave. Note that `.minRow`
60 | is greater than `.maxRow`. We are counting rows in pitch order.
61 | **/
62 |
63 | get maxRow() { return 0; }
64 | get topRow() { return this.centerRow - 4; }
65 | get centerRow() { return floor(0.5 * (this.rows.length - 1)); }
66 | get bottomRow() { return this.centerRow + 5; }
67 | get minRow() { return this.rows.length - 1; }
68 |
69 | /**
70 | .minPitch, .bottomPitch, .centerPitch, .topPitch, .maxPitch
71 | Minimum and maximum pitch names supported by the stave corresponding to the
72 | first and last row names in `.rows`, and lower, middle and upper stave pitches
73 | corresponding to the lower, middle and upper lines of the stave.
74 | **/
75 |
76 | get maxPitch() { return this.rows[this.maxRow]; }
77 | get topPitch() { return this.rows[this.topRow]; }
78 | get centerPitch() { return this.rows[this.centerRow]; }
79 | get bottomPitch() { return this.rows[this.bottomRow]; }
80 | get minPitch() { return this.rows[this.minRow]; }
81 |
82 | /**
83 | .movePitch(pitch, n)
84 | Chromatically transpose `pitch` by `n` semitones within the bounds of
85 | `.minPitch` and `.maxPitch`. Should a transposed pitch fall outside this
86 | range, returns `undefined`.
87 | **/
88 | movePitch(pitch, n) {
89 | // Chromatic transpose
90 | const min = toNoteNumber(this.minPitch);
91 | const max = toNoteNumber(this.maxPitch);
92 | const number = toNoteNumber(pitch) + n;
93 | // Don't transpose outside the limits of the stave
94 | return number >= min && number <= max ?
95 | number :
96 | undefined ;
97 | }
98 |
99 | /**
100 | .getPart(pitch)
101 | **/
102 |
103 | parts = [{
104 | name: 'main'
105 | }];
106 |
107 | getPart(number) {
108 | return this.parts[0];
109 | }
110 |
111 | /**
112 | .getRow(part, pitch)
113 | Returns the row index of a given pitch name or number. Multi-staff staves
114 | require part to know which staff they are positioned upon.
115 | **/
116 | getRow(part, pitch) {
117 | const name = typeof pitch === 'string' ? pitch : toNoteName(pitch) ;
118 | const row = name.replace(rflatsharp, '');
119 | const i = this.rows.indexOf(row);
120 | if (global.DEBUG && i === -1) throw new Error('Pitch "' + pitch + '" is not supported by stave ' + this.constructor.name);
121 | if (i === -1) console.warn('Pitch "' + pitch + '" is not supported by stave ' + this.constructor.name);
122 | return i > -1 ? i : undefined ;
123 | }
124 |
125 | /**
126 | .getPitch(row)
127 | Returns the pitch of a given row number.
128 | **/
129 | getPitch(row) {
130 | const names = this.rows[row];
131 | return names.split(/\s+/)[0];
132 | }
133 |
134 | /**
135 | .getSpelling()
136 | Gets spelling of a given pitch. Returns a pitch name string.
137 | **/
138 | getSpelling(key, event) {
139 | return event[1] === 'chord' ?
140 | spellRoot(key, event[2]) :
141 | spellPitch(key, event[2]) ;
142 | }
143 |
144 | createKeySymbols(key) {
145 | const symbols = [];
146 | const keynumber = toRootNumber(key);
147 | const keyscale = toKeyScale(keynumber);
148 |
149 | // Add key signature
150 | symbols.push.apply(symbols, keyscale
151 | .map((n, i) => (n - major[i] && {
152 | type: 'acci',
153 | pitch: toRootName(major[i]) + accidentalChars[n - major[i]],
154 | value: n - major[i],
155 | part: this.parts[0]
156 | }))
157 | .filter((o) => !!o)
158 | .sort(byFatherCharlesPitch)
159 | );
160 |
161 | return symbols;
162 | }
163 |
164 | createSignatureSymbols(key) {
165 | const symbols = [{
166 | type: 'clef',
167 | clef: this.type,
168 | part: this.parts[0]
169 | }];
170 |
171 | return symbols.concat(this.createKeySymbols(key));
172 | }
173 |
174 | /**
175 | .yRatioToPitch(y)
176 | Used for converting pointer movements to pitch changes.
177 | **/
178 | yRatioToPitch(y) {
179 | const n = floor(y * this.pitches.length);
180 | return n < 0 ? this.pitches[0] :
181 | n > this.pitches.length - 1 ? this.pitches[this.pitches.length - 1] :
182 | this.pitches[n] ;
183 | }
184 | }
185 |
--------------------------------------------------------------------------------
/modules/stave/piano.js:
--------------------------------------------------------------------------------
1 |
2 | import * as glyphs from "../glyphs.js";
3 | import { toNoteName, toRootNumber, toRootName } from 'midi/note.js';
4 | import { toKeyScale } from '../keys.js';
5 | import { rflatsharp, byFatherCharlesPitch, accidentalChars } from '../pitch.js';
6 | import { major } from '../scale.js';
7 | import Stave from './stave.js';
8 |
9 |
10 | const global = globalThis || window;
11 | const assign = Object.assign;
12 |
13 |
14 | /**
15 | A simple piano grand stave which brute splits treble from bass at Bb3.
16 | **/
17 |
18 | export default class PianoStave extends Stave {
19 | type = 'piano';
20 | rows = [
21 | "rh-A6 soprano-A6 alto-A6",
22 | "rh-G6 soprano-G6 alto-G6",
23 | "rh-F6 soprano-F6 alto-F6",
24 | "rh-E6 soprano-E6 alto-E6",
25 | "rh-D6 soprano-D6 alto-D6",
26 | "rh-C6 soprano-C6 alto-C6",
27 | "rh-B5 soprano-B5 alto-B5",
28 | "rh-A5 soprano-A5 alto-A5",
29 | "rh-G5 soprano-G5 alto-G5",
30 | "rh-F5 soprano-F5 alto-F5",
31 | "rh-E5 soprano-E5 alto-E5",
32 | "rh-D5 soprano-D5 alto-D5",
33 | "rh-C5 soprano-C5 alto-C5",
34 | "rh-B4 soprano-B4 alto-B4",
35 | "rh-A4 soprano-A4 alto-A4",
36 | "rh-G4 soprano-G4 alto-G4",
37 | "rh-F4 soprano-F4 alto-F4",
38 | "rh-E4 soprano-E4 alto-E4",
39 | "rh-D4 soprano-D4 alto-D4",
40 | "rh-C4 soprano-C4 alto-C4",
41 | "rh-B3 soprano-B3 alto-B3",
42 | "lh-B4 rh-A3 soprano-A3 alto-A3 tenor-B4 bass-B4",
43 | "lh-A4 rh-G3 soprano-G3 alto-G3 tenor-A4 bass-A4",
44 | "lh-G4 rh-F3 soprano-F3 alto-F3 tenor-G4 bass-G4",
45 | "lh-F4 rh-E3 soprano-E3 alto-E3 tenor-F4 bass-F4",
46 | "lh-E4 rh-D3 soprano-D3 alto-D3 tenor-E4 bass-E4",
47 | "lh-D4 tenor-D4 bass-D4",
48 | "lh-C4 tenor-C4 bass-C4",
49 | "lh-B3 tenor-B3 bass-B3",
50 | "lh-A3 tenor-A3 bass-A3",
51 | "lh-G3 tenor-G3 bass-G3",
52 | "lh-F3 tenor-F3 bass-F3",
53 | "lh-E3 tenor-E3 bass-E3",
54 | "lh-D3 tenor-D3 bass-D3",
55 | "lh-C3 tenor-C3 bass-C3",
56 | "lh-B2 tenor-B2 bass-B2",
57 | "lh-A2 tenor-A2 bass-A2",
58 | "lh-G2 tenor-G2 bass-G2",
59 | "lh-F2 tenor-F2 bass-F2",
60 | "lh-E2 tenor-E2 bass-E2",
61 | "lh-D2 tenor-D2 bass-D2",
62 | "lh-C2 tenor-C2 bass-C2",
63 | "lh-B1 tenor-B1 bass-B1",
64 | "lh-A1 tenor-A1 bass-A1",
65 | "lh-G1 tenor-G1 bass-G1",
66 | "lh-F1 tenor-F1 bass-F1",
67 | "lh-E1 tenor-E1 bass-E1"
68 | ];
69 |
70 | getTimeSigHTML(numerator, denominator, eventId) {
71 | return `
72 | ${ glyphs['timeSig' + numerator] }
73 | ${ glyphs['timeSig' + denominator] }
74 |
75 |
76 | ${ glyphs['timeSig' + numerator] }
77 | ${ glyphs['timeSig' + denominator] }
78 | `;
79 | }
80 |
81 | staffs = ['bass', 'treble'];
82 |
83 | parts = [{}, {
84 | name: 'lh',
85 | staff: 'bass',
86 | topRow: 29,
87 | centerRow: 33,
88 | bottomRow: 38
89 | }, {
90 | name: 'rh',
91 | staff: 'treble',
92 | topRow: 9,
93 | centerRow: 13,
94 | bottomRow: 18
95 | }, {
96 | // Voice range C4-C6
97 | // Soprano sax Ab3-Eb6
98 | // Trumpet F#3-C6
99 | name: 'soprano',
100 | staff: 'treble',
101 | topRow: 9,
102 | centerRow: 13,
103 | bottomRow: 18,
104 | stemup: true
105 | }, {
106 | // Voice range G3-G5
107 | // Flugel horn E3-Bb5
108 | // Alto sax rannge Db3-Ab5
109 | name: 'alto',
110 | staff: 'treble',
111 | topRow: 9,
112 | centerRow: 13,
113 | bottomRow: 18,
114 | stemup: false
115 | }, {
116 | // Voice range C3-C5
117 | // Tenor Sax range Ab2-Eb5
118 | name: 'tenor',
119 | staff: 'bass',
120 | topRow: 29,
121 | centerRow: 33,
122 | bottomRow: 38,
123 | stemup: true
124 | }, {
125 | // Voice range E2-C4
126 | // Trombone range E2-Bb4
127 | // Sousaphone Eb1-Bb4
128 | name: 'bass',
129 | staff: 'bass',
130 | topRow: 29,
131 | centerRow: 33,
132 | bottomRow: 38,
133 | stemup: false
134 | }];
135 |
136 | /**
137 | .getRow(part, pitch)
138 | Returns the row index of a given pitch name or number.
139 | **/
140 | getRow(part, pitch) {
141 | pitch = typeof pitch === 'string' ? pitch : toNoteName(pitch) ;
142 | const name = part.name + '-' + pitch.replace(rflatsharp, '');
143 | const i = this.rows.findIndex((row) => row.includes(name));
144 | if (global.DEBUG && i === -1) throw new Error('Pitch "' + name + '" is not supported by ' + this.constructor.name);
145 | if (i === -1) console.warn('Pitch "' + name + '" is not supported by ' + this.constructor.name);
146 | return i > -1 ? i : undefined ;
147 | }
148 |
149 | getPart(number) {
150 | return typeof number === 'number' ?
151 | number < 60 ?
152 | this.parts[1] :
153 | this.parts[2] :
154 | /[012]$|[AC-G][b#♭♯𝄫𝄪]*3$/.test(number) ?
155 | this.parts[1] :
156 | this.parts[2] ;
157 | }
158 |
159 | createKeySymbols(key) {
160 | const symbols = [];
161 | const keynumber = toRootNumber(key);
162 | const keyscale = toKeyScale(keynumber);
163 | const keysig = keyscale
164 | .map((n, i) => (n - major[i] && {
165 | type: 'acci',
166 | pitch: toRootName(major[i]) + accidentalChars[n - major[i]],
167 | value: n - major[i]
168 | }))
169 | .filter((o) => !!o)
170 | .sort(byFatherCharlesPitch);
171 |
172 | // Add key signature to both staffs
173 | symbols.push.apply(symbols, keysig.map((symbol) => assign({ part: this.parts[1] }, symbol)));
174 | symbols.push.apply(symbols, keysig.map((symbol) => assign({ part: this.parts[2] }, symbol)));
175 |
176 | return symbols;
177 | }
178 |
179 | createSignatureSymbols(key) {
180 | const symbols = [{
181 | type: 'clef',
182 | clef: 'treble',
183 | part: this.parts[1],
184 | stave: this
185 | }, {
186 | type: 'clef',
187 | clef: 'bass',
188 | part: this.parts[2],
189 | stave: this
190 | }];
191 |
192 | return symbols.concat(this.createKeySymbols(key));
193 | }
194 | }
195 |
--------------------------------------------------------------------------------
/test/repeats.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | <scribe-music>
5 |
6 |
7 |
8 |
9 |
10 |
11 |
20 |
21 |
30 |
31 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
53 |
54 |
55 |
56 | Bar repeats
57 |
58 |
59 |
66 |
67 |
68 |
69 |
77 |
78 |
79 |
80 |
89 |
90 |
91 |
92 |
107 |
108 |
109 |
110 |
117 |
118 |
119 |
120 |
129 |
130 |
131 |
132 |
143 |
144 |
145 |
146 |
165 |
166 |
167 |
168 |
169 |
170 |
171 |
--------------------------------------------------------------------------------
/scribe-music/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | # ``
5 |
6 | A custom element that imports, interprets and renders music notation from JSON
7 | data.
8 |
9 | ## Get started
10 |
11 | To use the `` custom element, import the CSS and JS from the CDN:
12 |
13 | ```html
14 |
15 |
16 | ```
17 |
18 | The `` element is now registered. It renders music notation from
19 | JSON data imported via its `src` attribute:
20 |
21 | ```html
22 |
23 | ```
24 |
25 |
26 |
27 |
28 | ## Attributes and properties
29 |
30 | | Attribute | Property | Type | Description |
31 | | :---------- | :----------- | :-------- | :---------- |
32 | | `src` | `.src` | `URL` | A URL of a JSON file or hashref of a script in the document |
33 | | | `.data` | `object` | Gets and sets sequence data |
34 | | `clef` | `.clef` | `string` | One of `"treble"`, `"bass"`, `"piano"`, `"alto"`, `"drum"` or `"percussion"` |
35 | | `key` | `.key` | `string` | The name of a major key, eg. `"Ab"` |
36 | | `meter` | `.meter` | `string` | The time signature, eg. `"4/4"` |
37 | | `transpose` | `.transpose` | `number` | Transposes notation by a given number of semitones |
38 | | `layout` | | `string` | Either `"compact"` or `"regular"` |
39 | | `shuffle` | | `boolean` | Boolean attribute, sets display of swung 16ths as straight 16ths |
40 | | `swing` | | `boolean` | Boolean attribute, sets display of swung 8ths as straight 8ths |
41 |
42 |
43 | ### `src="url"`
44 |
45 | Both an attribute and a property. The URL of a JSON file containing
46 | sequence data.
47 |
48 | ```html
49 |
50 | ```
51 |
52 | The `src` attribute may alternatively reference a `
80 |
81 |
82 |
83 | ```
84 |
85 | A few examples of sequence data can be found in the data/
86 | directory.
87 |
88 |
89 | ### `.data`
90 |
91 | Property only.
92 | Set an object with an `events` array, structured as a sequence object.
93 |
94 | ```js
95 | const element = document.body.querySelector('scribe-music');
96 |
97 | element.data = {
98 | name: 'My Song',
99 | events: [...],
100 | sequences: [...]
101 | };
102 | ```
103 |
104 | Get Scribe's sequence object.
105 | To export Sequence JSON, stringify `scribe.data`:
106 |
107 | ```js
108 | let scribe = document.body.querySelector('scribe-music');
109 | let mySong = JSON.stringify(scribe.data);
110 | ```
111 |
112 |
113 | ### `clef="treble"`
114 |
115 | Both an attribute and a property.
116 | The name of the clef, one of `"treble"`, `"treble-up"`, `"treble-down"`, `"alto"`,
117 | `"bass"`, `"piano"`, `"drum"` or `"percussion"`. Defaults to `"treble"`.
118 |
119 | ```html
120 |
121 | ```
122 |
123 | ```js
124 | let scribe = document.body.querySelector('scribe-music');
125 | console.log(scribe.clef) // "bass";
126 | ```
127 |
128 |
129 | ### `key="C"`
130 |
131 | Both an attribute and a property.
132 | Gets and sets the key signature of the stave. Accepts any chromatic note name,
133 | spelled with unicode sharps `♯` and flats `♭` or with hash `#` and small case `b`.
134 | This is the name of the tonic of a major scale. Defaults to `"C"`.
135 |
136 | ```html
137 |
138 | ```
139 |
140 | ```js
141 | let scribe = document.body.querySelector('scribe-music');
142 | scribe.key = "B♭";
143 | ```
144 |
145 | There is no provision for choosing a minor key. Declare its relative major.
146 |
147 | The key is the key signature pre-transposition. If `transpose` is set to
148 | something other than `0`, the key signature is also transposed.
149 |
150 | The key attribute/property is superceded by a `"key"` event found in the data
151 | at beat `0`.
152 |
153 |
154 | ### `meter="4/4"`
155 |
156 | Both an attribute and a property.
157 | The meter, expressed as a standard time signature.
158 |
159 | ```html
160 |
161 | ```
162 |
163 | ```js
164 | let scribe = document.body.querySelector('scribe-music');
165 | scribe.meter = "3/4";
166 | ```
167 |
168 | This setting is superceded by a `"meter"` event found in the data at beat `0`.
169 | If this attribute is omitted (or the property not set in JS), no time signature is displayed (unless the data contains a `"meter"` event at beat `0`).
170 | Defaults to `"4/4"`.
171 |
172 |
173 | ### `transpose="0"`
174 |
175 | Both an attribute and a property.
176 | Sets scribe to render notation transposed by `transpose` semitones. Transposition
177 | is applied to key signature, notes and chords before render, and not to the underlying data.
178 |
179 | ```html
180 |
181 | ```
182 |
183 | ```js
184 | let scribe = document.body.querySelector('scribe-music');
185 | scribe.transpose = 2;
186 | ```
187 |
188 | ### `layout="compact"`
189 |
190 | Both an attribute and a property.
191 | Sets scribe to render notation transposed by `transpose` semitones. Transposition
192 | is applied to key signature, notes and chords before render, and not to the underlying data.
193 |
194 | ```html
195 |
196 | ```
197 |
198 | ```js
199 | let scribe = document.body.querySelector('scribe-music');
200 | scribe.transpose = 2;
201 | ```
202 |
203 |
204 | ### `shuffle`
205 |
206 | A boolean attribute. Displays swung 16ths as straight 16ths.
207 |
208 | ```html
209 |
210 | ```
211 |
212 |
213 | ### `swing`
214 |
215 | A boolean attribute. Displays swung 8ths as straight 8ths.
216 |
217 | ```html
218 |
219 | ```
220 |
--------------------------------------------------------------------------------