vector-score 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +240 -0
- package/dist/classes/MusicStaff.d.ts +127 -0
- package/dist/classes/NoteRenderer.d.ts +27 -0
- package/dist/classes/RhythmStaff.d.ts +118 -0
- package/dist/classes/SVGRenderer.d.ts +47 -0
- package/dist/classes/ScrollingStaff.d.ts +67 -0
- package/dist/constants.d.ts +19 -0
- package/dist/glyphs.d.ts +7 -0
- package/dist/helpers/notehelpers.d.ts +9 -0
- package/dist/index.d.ts +7 -0
- package/dist/strategies/GrandStaffStrategy.d.ts +14 -0
- package/dist/strategies/SingleStaffStrategy.d.ts +12 -0
- package/dist/strategies/StrategyInterface.d.ts +20 -0
- package/dist/types.d.ts +11 -0
- package/dist/vector-score.js +1224 -0
- package/dist/vector-score.umd.cjs +1 -0
- package/package.json +34 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Drake Buentello
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,240 @@
|
|
|
1
|
+
<img src="public/vector-score-icon.svg" alt="VectorScore Logo" width="150" height="150" />
|
|
2
|
+
|
|
3
|
+
# Vector Score
|
|
4
|
+
|
|
5
|
+
A lightweight, SVG-based TypeScript library for rendering musical staves, notes, and rhythm patterns in the browser.
|
|
6
|
+
|
|
7
|
+
## Features
|
|
8
|
+
|
|
9
|
+
* **Multiple Staff Types**: Supports Treble, Bass, Alto, and Grand staves.
|
|
10
|
+
* **Rhythm Staff**: Dedicated staff for rhythm exercises with customizable time signatures and bar handling.
|
|
11
|
+
* **Scrolling Staff** Staff made to allow for 'endless' style of notes.
|
|
12
|
+
* **SVG Rendering**: Crisp, scalable vector graphics suitable for any screen size.
|
|
13
|
+
* **Flexible Note Input**: Simple string-based syntax for defining notes, chords, and rests.
|
|
14
|
+
* **Interactive Features**: Includes methods for error feedback (highlighting wrong notes) and note justification.
|
|
15
|
+
|
|
16
|
+
## Installation
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
npm install DrakeB1234/VectorScore
|
|
20
|
+
````
|
|
21
|
+
|
|
22
|
+
## Development
|
|
23
|
+
|
|
24
|
+
To start the development server with a playground:
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
npm run dev
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
To build the library for production:
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
npm run build
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
## Usage
|
|
37
|
+
|
|
38
|
+
### 1. Setup HTML
|
|
39
|
+
|
|
40
|
+
Create a container element in your HTML where the staff will be rendered.
|
|
41
|
+
|
|
42
|
+
```html
|
|
43
|
+
<div id="staff-container"></div>
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
### 2. Import and Initialize
|
|
47
|
+
|
|
48
|
+
### Standard Music Staff (Treble, Bass, Alto)
|
|
49
|
+
|
|
50
|
+
```typescript
|
|
51
|
+
import { MusicStaff } from 'vector-score';
|
|
52
|
+
|
|
53
|
+
const container = document.getElementById('staff-container');
|
|
54
|
+
|
|
55
|
+
const staff = new MusicStaff(container, {
|
|
56
|
+
staffType: 'treble', // 'treble', 'bass', 'alto', or 'grand'
|
|
57
|
+
width: 400,
|
|
58
|
+
scale: 1.2,
|
|
59
|
+
spaceBelow: 1
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
// Draw a C Minor scale (quarter notes)
|
|
63
|
+
// Format: NoteName + Accidental(optional) + Octave + Duration
|
|
64
|
+
staff.drawNote(['C4q', 'D4q', 'Eb4q', 'F4q', 'G4q', 'Ab4q', 'Bb4q', 'C5q']);
|
|
65
|
+
|
|
66
|
+
// Draw a C Chord
|
|
67
|
+
staff.drawChord(['C4w', 'E4w', 'G4w']);
|
|
68
|
+
|
|
69
|
+
// Evenly space all notes on the staff
|
|
70
|
+
staff.justifyNotes();
|
|
71
|
+
```
|
|
72
|
+
#### Resulting Staff
|
|
73
|
+

|
|
74
|
+
|
|
75
|
+
### Grand Staff
|
|
76
|
+
|
|
77
|
+
```typescript
|
|
78
|
+
import { MusicStaff } from 'vector-score';
|
|
79
|
+
|
|
80
|
+
const grandStaff = new MusicStaff(container, {
|
|
81
|
+
staffType: 'grand',
|
|
82
|
+
width: 400,
|
|
83
|
+
spaceBelow: 2,
|
|
84
|
+
spaceAbove: 2
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
// Notes are automatically positioned on the correct stave based on pitch
|
|
88
|
+
grandStaff.drawNote(['G4q', 'E4h', 'C4w', "A3h", "F3h"]);
|
|
89
|
+
|
|
90
|
+
grandStaff.drawChord(["G3w", "C4w", "E4w"]);
|
|
91
|
+
```
|
|
92
|
+
#### Resulting Staff
|
|
93
|
+

|
|
94
|
+
|
|
95
|
+
### Rhythm Staff
|
|
96
|
+
|
|
97
|
+
```typescript
|
|
98
|
+
import { RhythmStaff } from 'vector-score';
|
|
99
|
+
|
|
100
|
+
const rhythmContainer = document.getElementById('rhythm-container');
|
|
101
|
+
|
|
102
|
+
const rhythm = new RhythmStaff(rhythmContainer, {
|
|
103
|
+
width: 400,
|
|
104
|
+
topNumber: 4, // Time signature numerator (e.g., 4/4 time)
|
|
105
|
+
barsCount: 2
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
// Draw notes and rests
|
|
109
|
+
rhythm.drawNote(['q', 'q']); // Duration only
|
|
110
|
+
rhythm.drawRest(['h']);
|
|
111
|
+
|
|
112
|
+
// Draw beamed note
|
|
113
|
+
rhythm.drawBeamedNotes("e", 4); // Draws a beamed note of 4 eighth notes
|
|
114
|
+
|
|
115
|
+
// Finish the bar
|
|
116
|
+
rhythm.drawNote(['q', 'q']);
|
|
117
|
+
|
|
118
|
+
// Increment the UI to show the first beat in the bar
|
|
119
|
+
rhythm.incrementCurrentBeatUI();
|
|
120
|
+
```
|
|
121
|
+
#### Resulting Staff
|
|
122
|
+

|
|
123
|
+
|
|
124
|
+
### Scrolling Staff
|
|
125
|
+
|
|
126
|
+
```typescript
|
|
127
|
+
import { ScrollingStaff } from 'vector-score';
|
|
128
|
+
|
|
129
|
+
const scrollingContainer = document.getElementById('scrolling-container');
|
|
130
|
+
|
|
131
|
+
const scrollingStaff = new ScrollingStaff(scrollingContainer, {
|
|
132
|
+
width: 400,
|
|
133
|
+
spaceBelow: 2,
|
|
134
|
+
spaceAbove: 2,
|
|
135
|
+
onNotesOut: handleNotesOut, // Connect a handler when notes run out (optional)
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
// Handler for when notes run out
|
|
139
|
+
function handleNotesOut() {
|
|
140
|
+
isNotesOut = true;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// ****
|
|
144
|
+
// NOTE CSS SELECTOR FOR NOTES FOR ANIMATION IS
|
|
145
|
+
// .vs-scrolling-notes-layer > g.vs-note-wrapper { transition: transform 0.2s ease-in }
|
|
146
|
+
// ****
|
|
147
|
+
|
|
148
|
+
// Add notes to the queue
|
|
149
|
+
// 5 single notes, a C chord, 2 more notes, finally a D chord
|
|
150
|
+
scrollingStaff.queueNotes([
|
|
151
|
+
"C4w",
|
|
152
|
+
"F4w",
|
|
153
|
+
"C4w",
|
|
154
|
+
"B4w",
|
|
155
|
+
"D4w",
|
|
156
|
+
["C4w", "E4w", "G4w"],
|
|
157
|
+
"B4w",
|
|
158
|
+
"D4w",
|
|
159
|
+
["D4w", "F#4w", "A4w"],
|
|
160
|
+
]);
|
|
161
|
+
|
|
162
|
+
// The button event listener calls 'advanceNotes()' to move the notes over, one step at a time.
|
|
163
|
+
```
|
|
164
|
+
#### Resulting Staff
|
|
165
|
+

|
|
166
|
+
|
|
167
|
+
## Note String Syntax
|
|
168
|
+
|
|
169
|
+
Notes are defined using a specific string format parsed by the library:
|
|
170
|
+
|
|
171
|
+
`[Name][Accidental?][Octave][Duration]`
|
|
172
|
+
|
|
173
|
+
* **Name**: `A` - `G` (Case insensitive)
|
|
174
|
+
* **Accidental**: `#` (Sharp) or `b` (Flat). Optional.
|
|
175
|
+
* **Octave**: `0` - `9`
|
|
176
|
+
* **Duration**:
|
|
177
|
+
* `w`: Whole
|
|
178
|
+
* `h`: Half
|
|
179
|
+
* `q`: Quarter
|
|
180
|
+
* `e`: Eighth
|
|
181
|
+
|
|
182
|
+
**Examples:**
|
|
183
|
+
* `C4w`: C, Octave 4, Whole note
|
|
184
|
+
* `F#5q`: F Sharp, Octave 5, Quarter note
|
|
185
|
+
* `Bb3e`: B Flat, Octave 3, Eighth note
|
|
186
|
+
|
|
187
|
+
## API Reference
|
|
188
|
+
|
|
189
|
+
### MusicStaff Class
|
|
190
|
+
|
|
191
|
+
| Method | Description |
|
|
192
|
+
| :--- | :--- |
|
|
193
|
+
| `drawNote(notes: string \| string[])` | Draws one or multiple notes sequentially. |
|
|
194
|
+
| `drawChord(notes: string \| string[])` | Draws multiple notes stacked as a chord at the current cursor position. |
|
|
195
|
+
| `justifyNotes()` | Evenly spaces all currently drawn notes across the staff width. |
|
|
196
|
+
| `clearAllNotes()` | Removes all notes from the staff and resets the cursor. |
|
|
197
|
+
| `changeNoteByIndex(note: string, index: number)` | Replaces a note at a specific index with a new note. |
|
|
198
|
+
| `changeChordByIndex(notes: string[], index: number)` | Replaces a chord at a specific index with a new chord. |
|
|
199
|
+
| `destroy()` | Destroys internal arrays and elements |
|
|
200
|
+
|
|
201
|
+
### RhythmStaff Class
|
|
202
|
+
|
|
203
|
+
| Method | Description |
|
|
204
|
+
| :--- | :--- |
|
|
205
|
+
| `drawNote(notes: string \| string[])` | Draws rhythm notes. |
|
|
206
|
+
| `drawRest(rests: string \| string[])` | Draws rests. |
|
|
207
|
+
| `drawBeamedNotes(type: 'e'\|'s', count: number)` | Draws a group of beamed eighth or sixteenth notes. |
|
|
208
|
+
| `clearAllNotes()` | Removes all items from the staff. |
|
|
209
|
+
| `incrementCurrentBeatUI()` | Starts the UI for showing the current beat. Connect to a external interval for accurate showing of current beat. |
|
|
210
|
+
| `resetCurrentBeatUI()` | Must be called if current beat goes over the total beats in the bar to reset its state |
|
|
211
|
+
| `destroy()` | Destroys internal arrays and elements |
|
|
212
|
+
|
|
213
|
+
### ScrollingStaff Class
|
|
214
|
+
|
|
215
|
+
| Method | Description |
|
|
216
|
+
| :--- | :--- |
|
|
217
|
+
| `queueNotes(notes: (string \| string[])[])` | Queues notes on the staff. ["C4", ["C4", "E4", "G4"], "B4"] |
|
|
218
|
+
| `advanceNotes()` | Advances notes to the next position |
|
|
219
|
+
| `clearAllNote()` | Clears all notes on the staff |
|
|
220
|
+
| `destroy()` | Destroys internal arrays and elements |
|
|
221
|
+
|
|
222
|
+
## Configuration Options
|
|
223
|
+
|
|
224
|
+
### MusicStaffOptions
|
|
225
|
+
* `width`: Total width of the SVG in pixels.
|
|
226
|
+
* `scale`: Zoom factor (default: 1).
|
|
227
|
+
* `staffType`: `'treble' | 'bass' | 'alto' | 'grand'`.
|
|
228
|
+
* `spaceAbove`: Padding units above the staff (in staff line spaces).
|
|
229
|
+
* `spaceBelow`: Padding units below the staff (in staff line spaces).
|
|
230
|
+
* `staffColor`: CSS color string for lines and notes.
|
|
231
|
+
* `staffBackgroundColor`: CSS color string for background.
|
|
232
|
+
|
|
233
|
+
### RhythmStaffOptions
|
|
234
|
+
* `topNumber`: The top number of the time signature (e.g., 4 for 4/4 time).
|
|
235
|
+
* `barsCount`: Number of measures to draw.
|
|
236
|
+
* `currentBeatUIColor`: CSS color string for current beat UI.
|
|
237
|
+
* *Inherits sizing and color options from MusicStaffOptions.*
|
|
238
|
+
|
|
239
|
+
### ScrollingStaffOptions
|
|
240
|
+
* same as MusicStaffOptions
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
import { StaffTypes } from '../types';
|
|
2
|
+
export type MusicStaffOptions = {
|
|
3
|
+
width?: number;
|
|
4
|
+
scale?: number;
|
|
5
|
+
staffType?: StaffTypes;
|
|
6
|
+
spaceAbove?: number;
|
|
7
|
+
spaceBelow?: number;
|
|
8
|
+
staffColor?: string;
|
|
9
|
+
staffBackgroundColor?: string;
|
|
10
|
+
};
|
|
11
|
+
export default class MusicStaff {
|
|
12
|
+
private svgRendererInstance;
|
|
13
|
+
private strategyInstance;
|
|
14
|
+
private noteRendererInstance;
|
|
15
|
+
private options;
|
|
16
|
+
private noteEntries;
|
|
17
|
+
private noteCursorX;
|
|
18
|
+
/**
|
|
19
|
+
* Creates an instance of a MusicStaff, A single staff.
|
|
20
|
+
*
|
|
21
|
+
* @param rootElementCtx - The element (div) reference that will append the music staff elements to.
|
|
22
|
+
* @param options - Optional configuration settings. Can adjust staff type (treble, bass, alto, grand), width, scale, spaces above/below, etc. All config options are in the type MusicStaffOptions
|
|
23
|
+
*/
|
|
24
|
+
constructor(rootElementCtx: HTMLElement, options?: MusicStaffOptions);
|
|
25
|
+
/**
|
|
26
|
+
* Draws a note on the staff.
|
|
27
|
+
* @param notes - A single string OR array of note strings in the format `[Root][Accidental?][Octave][Duration?]`.
|
|
28
|
+
* If an array is passed, it will draw each individual note on the staff.
|
|
29
|
+
*
|
|
30
|
+
* * **Root**: (A-G)
|
|
31
|
+
* * **Accidental** (Optional): `#` (sharp) `b` (flat) `n` (natural) `##` (double sharp) or `bb` (double flat).
|
|
32
|
+
* * **Octave**: The octave number (e.g., `4`).
|
|
33
|
+
* * **Duration** (Optional): `w` (whole) `h` (half) `q` (quarter) or `e` (eighth). Defaults to `w` if duration is omitted
|
|
34
|
+
* @returns void
|
|
35
|
+
* @throws {Error} If a note string is not correct format. If an array was passed, it will still draw whatever correctly formatted notes before it.
|
|
36
|
+
*
|
|
37
|
+
* * @example
|
|
38
|
+
* // Draws the specified notes individually on the staff
|
|
39
|
+
* drawNote(["D4w", "F4w", "A4w", "B#5q", "Ebb4e"]);
|
|
40
|
+
*
|
|
41
|
+
* * @example
|
|
42
|
+
* // Draws the specified single note on the staff
|
|
43
|
+
* drawNote("D4w");
|
|
44
|
+
*/
|
|
45
|
+
drawNote(notes: string | string[]): void;
|
|
46
|
+
/**
|
|
47
|
+
* Draws a chord on the staff.
|
|
48
|
+
* @param notes - An array of note strings in the format `[Root][Accidental?][Octave][Duration?]`.
|
|
49
|
+
*
|
|
50
|
+
* * **Root**: (A-G)
|
|
51
|
+
* * **Accidental** (Optional): `#` (sharp) `b` (flat) `n` (natural) `##` (double sharp) or `bb` (double flat).
|
|
52
|
+
* * **Octave**: The octave number (e.g., `4`).
|
|
53
|
+
* * **Duration** (Optional): `w` (whole) `h` (half) `q` (quarter) or `e` (eighth). Defaults to `w` if duration is omitted
|
|
54
|
+
* @returns void
|
|
55
|
+
* @throws {Error} If a note string is not correct format OR if less than one note was provided.
|
|
56
|
+
*
|
|
57
|
+
* * @example
|
|
58
|
+
* // Draw a D minor chord starting on 4th octave
|
|
59
|
+
* drawChord(["D4w", "F4w", "A4w"], 0);
|
|
60
|
+
*/
|
|
61
|
+
drawChord(notes: string[]): void;
|
|
62
|
+
/**
|
|
63
|
+
* Evenly spaces out the notes on the staff.
|
|
64
|
+
* @returns Returns early if no notes are on the staff
|
|
65
|
+
*/
|
|
66
|
+
justifyNotes(): void;
|
|
67
|
+
/**
|
|
68
|
+
* Clears staff of notes and resets internal positioning.
|
|
69
|
+
* @returns void
|
|
70
|
+
*/
|
|
71
|
+
clearAllNotes(): void;
|
|
72
|
+
/**
|
|
73
|
+
* Changes the note by index to the specified note.
|
|
74
|
+
* @param notes - A note string in the format `[Root][Accidental?][Octave][Duration?]`.
|
|
75
|
+
*
|
|
76
|
+
* * **Root**: (A-G)
|
|
77
|
+
* * **Accidental** (Optional): `#` (sharp) `b` (flat) `n` (natural) `##` (double sharp) or `bb` (double flat).
|
|
78
|
+
* * **Octave**: The octave number (e.g., `4`).
|
|
79
|
+
* * **Duration** (Optional): `w` (whole) `h` (half) `q` (quarter) or `e` (eighth). Defaults to `w` if duration is omitted
|
|
80
|
+
* @param noteIndex The index of the note that will replaced by the specified note.
|
|
81
|
+
* @returns void
|
|
82
|
+
* @throws {Error} If the index provided is out of bounds, or if a note string is not correct format.
|
|
83
|
+
*
|
|
84
|
+
* * @example
|
|
85
|
+
* // Changes note at pos `0` to a B flat quarter note on the 3rd octave
|
|
86
|
+
* changeNoteByIndex("Bb3q", 0);
|
|
87
|
+
*/
|
|
88
|
+
changeNoteByIndex(note: string, noteIndex: number): void;
|
|
89
|
+
/**
|
|
90
|
+
* Changes the note by index to the specified chord.
|
|
91
|
+
* @param notes - An array of note strings in the format `[Root][Accidental?][Octave][Duration?]`.
|
|
92
|
+
*
|
|
93
|
+
* * **Root**: (A-G)
|
|
94
|
+
* * **Accidental** (Optional): `#` (sharp) `b` (flat) `n` (natural) `##` (double sharp) or `bb` (double flat).
|
|
95
|
+
* * **Octave**: The octave number (e.g., `4`).
|
|
96
|
+
* * **Duration** (Optional): `w` (whole) `h` (half) `q` (quarter) or `e` (eighth). Defaults to `w` if duration is omitted
|
|
97
|
+
* @param noteIndex The index of the note that will replaced by the specified chord.
|
|
98
|
+
* @returns void
|
|
99
|
+
* @throws {Error} If the index provided is out of bounds, or if a note string is not correct format.
|
|
100
|
+
*
|
|
101
|
+
* * @example
|
|
102
|
+
* // Changes chord at pos `0` to a C Minor chord
|
|
103
|
+
* changeChordByIndex(["C4w", "D#4w", "G4w"], 0);
|
|
104
|
+
*/
|
|
105
|
+
changeChordByIndex(notes: string[], chordIndex: number): void;
|
|
106
|
+
/**
|
|
107
|
+
* Adds a class to the note by the index provided.
|
|
108
|
+
* @param className The name added to the note
|
|
109
|
+
* @param noteIndex The index of the note that will have 'className' added to it.
|
|
110
|
+
* @returns void
|
|
111
|
+
* @throws {Error} If the index provided is out of bounds
|
|
112
|
+
*/
|
|
113
|
+
addClassToNoteByIndex(className: string, noteIndex: number): void;
|
|
114
|
+
/**
|
|
115
|
+
* Removes a class to the note by the index provided.
|
|
116
|
+
* @param className The name removed from the note
|
|
117
|
+
* @param noteIndex The index of the note that will have 'className' removed from it.
|
|
118
|
+
* @returns void
|
|
119
|
+
* @throws {Error} If the index provided is out of bounds
|
|
120
|
+
*/
|
|
121
|
+
removeClassToNoteByIndex(className: string, noteIndex: number): void;
|
|
122
|
+
/**
|
|
123
|
+
* Removes the root svg element and cleans up arrays.
|
|
124
|
+
* @returns void
|
|
125
|
+
*/
|
|
126
|
+
destroy(): void;
|
|
127
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { StaffStrategy } from '../strategies/StrategyInterface';
|
|
2
|
+
import { NoteObj } from '../types';
|
|
3
|
+
import { default as SVGRenderer } from './SVGRenderer';
|
|
4
|
+
/**
|
|
5
|
+
* @param {SVGGElement} noteGroup - The group that is returned from renderNote or renderGroup
|
|
6
|
+
* @param {NoteObj} noteObj - The parse note string into NoteObj
|
|
7
|
+
* @param {number} noteYPos - The Y pos of the notes group
|
|
8
|
+
* @param {number} accidentalOffset - The total xOffset from any accidentals from a note. Chords could have a 1..3 of these offsets
|
|
9
|
+
* @param {number} cursorOffset - The requested amount the cursor should be offset. Chords use this when a note is close and offsetted to the left.
|
|
10
|
+
*/
|
|
11
|
+
export type RenderNoteReturn = {
|
|
12
|
+
noteGroup: SVGGElement;
|
|
13
|
+
noteObj: NoteObj;
|
|
14
|
+
noteYPos: number;
|
|
15
|
+
accidentalOffset: number;
|
|
16
|
+
cursorOffset: number;
|
|
17
|
+
};
|
|
18
|
+
export default class NoteRenderer {
|
|
19
|
+
private svgRendererInstance;
|
|
20
|
+
private strategyInstance;
|
|
21
|
+
constructor(svgRenderer: SVGRenderer, strategy: StaffStrategy);
|
|
22
|
+
private drawStem;
|
|
23
|
+
private chordOffsetConsecutiveAccidentals;
|
|
24
|
+
private chordOffsetCloseNotes;
|
|
25
|
+
renderNote(noteString: string): RenderNoteReturn;
|
|
26
|
+
renderChord(notes: string[]): RenderNoteReturn;
|
|
27
|
+
}
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
export type RhythmStaffOptions = {
|
|
2
|
+
width?: number;
|
|
3
|
+
scale?: number;
|
|
4
|
+
topNumber?: number;
|
|
5
|
+
barsCount?: number;
|
|
6
|
+
spaceAbove?: number;
|
|
7
|
+
spaceBelow?: number;
|
|
8
|
+
staffColor?: string;
|
|
9
|
+
staffBackgroundColor?: string;
|
|
10
|
+
currentBeatUIColor?: string;
|
|
11
|
+
};
|
|
12
|
+
export default class RhythmStaff {
|
|
13
|
+
private rendererInstance;
|
|
14
|
+
private options;
|
|
15
|
+
private barSpacing;
|
|
16
|
+
private quarterNoteSpacing;
|
|
17
|
+
private noteCursorX;
|
|
18
|
+
private noteEntries;
|
|
19
|
+
private maxBeatCount;
|
|
20
|
+
private currentBeatCount;
|
|
21
|
+
private currentBeatUICount;
|
|
22
|
+
private currentBeatUIElement;
|
|
23
|
+
private currentBeatUIXPos;
|
|
24
|
+
/**
|
|
25
|
+
* Creates an instance of a RhythmStaff, A single staff that will automatically apply positioning of elements based on the duration of a note.
|
|
26
|
+
*
|
|
27
|
+
* @param rootElementCtx - The element (div) reference that will append the music staff elements to.
|
|
28
|
+
* @param options - Optional configuration settings. All config options are in the type RhythmStaffOptions
|
|
29
|
+
* @throws {Error} - If top number is not 3 or 4 OR if bars count is not between 1 - 3. These are the currently only supported values.
|
|
30
|
+
*/
|
|
31
|
+
constructor(rootElementCtx: HTMLElement, options?: RhythmStaffOptions);
|
|
32
|
+
private createBeatUIElement;
|
|
33
|
+
private handleNewBar;
|
|
34
|
+
private translateGroupByDuration;
|
|
35
|
+
private drawStem;
|
|
36
|
+
private renderNote;
|
|
37
|
+
private renderRest;
|
|
38
|
+
private checkAndCreateNewBar;
|
|
39
|
+
private checkAndFillBarWithRests;
|
|
40
|
+
private createRemainingRests;
|
|
41
|
+
private renderBeamRect;
|
|
42
|
+
/**
|
|
43
|
+
* Draws a note duration on the staff.
|
|
44
|
+
* @param notes - A single string OR array of note strings in the format `[Duration]`.
|
|
45
|
+
* If an array is passed, it will draw each individual note duration on the staff.
|
|
46
|
+
* If a duration exceeds the remaining value on the bar, rests will fill the empty space.
|
|
47
|
+
*
|
|
48
|
+
* * **Duration**: `w` (whole) `h` (half) `q` (quarter) `e` (eighth)
|
|
49
|
+
* @returns void
|
|
50
|
+
* @throws {Error} If a note string is not correct format. If an array was passed, it will still draw whatever correctly formatted notes before it.
|
|
51
|
+
*
|
|
52
|
+
* * @example
|
|
53
|
+
* // Draws the specified note durations individually on the staff
|
|
54
|
+
* drawNote(["q", "q", "q", "q", "w"]);
|
|
55
|
+
*
|
|
56
|
+
* * @example
|
|
57
|
+
* // Draws the specified single note duration on the staff
|
|
58
|
+
* drawNote("w");
|
|
59
|
+
*/
|
|
60
|
+
drawNote(notes: string | string[]): void;
|
|
61
|
+
/**
|
|
62
|
+
* Draws a rest duration on the staff.
|
|
63
|
+
* @param rests - A single string OR array of rest strings in the format `[Duration]`.
|
|
64
|
+
* If an array is passed, it will draw each individual rest duration on the staff.
|
|
65
|
+
* If a duration exceeds the remaining value on the bar, rests will fill the empty space.
|
|
66
|
+
*
|
|
67
|
+
* * **Duration**: `w` (whole) `h` (half) `q` (quarter) `e` (eighth)
|
|
68
|
+
* @returns void
|
|
69
|
+
* @throws {Error} If a rest string is not correct format. If an array was passed, it will still draw whatever correctly formatted rests before it.
|
|
70
|
+
*
|
|
71
|
+
* * @example
|
|
72
|
+
* // Draws the specified rest durations individually on the staff
|
|
73
|
+
* drawRest(["q", "q", "q", "q", "w"]);
|
|
74
|
+
*
|
|
75
|
+
* * @example
|
|
76
|
+
* // Draws the specified single rest duration on the staff
|
|
77
|
+
* drawRest("w");
|
|
78
|
+
*/
|
|
79
|
+
drawRest(rests: string | string[]): void;
|
|
80
|
+
/**
|
|
81
|
+
* Draws a beamed note of specified duration/count on the staff.
|
|
82
|
+
* Will stop beam early if bar line is reached / if beat count is over max limit
|
|
83
|
+
* @param note - A duration string of either 'e' (eighth) or 's' (sixth).
|
|
84
|
+
* @param noteCount - The amount of notes in the beam
|
|
85
|
+
*
|
|
86
|
+
* @returns void
|
|
87
|
+
* @throws {Error} If a rest string is not correct format. If an array was passed, it will still draw whatever correctly formatted rests before it.
|
|
88
|
+
*
|
|
89
|
+
* * @example
|
|
90
|
+
* // Draws a 4 beamed eighth note
|
|
91
|
+
* drawBeamedNotes("e", 4);
|
|
92
|
+
*
|
|
93
|
+
* * @example
|
|
94
|
+
* // Draws a 8 beamed sixth note
|
|
95
|
+
* drawBeamedNotes("s", 8);
|
|
96
|
+
*/
|
|
97
|
+
drawBeamedNotes(note: "e" | "s", noteCount: number): void;
|
|
98
|
+
/**
|
|
99
|
+
* Will increment the UI showing the current beat in quarters. Once exceeded, must be reset with `resetCurrentBeatUI()`
|
|
100
|
+
* @returns void
|
|
101
|
+
*/
|
|
102
|
+
incrementCurrentBeatUI(): void;
|
|
103
|
+
/**
|
|
104
|
+
* Resets the ui showing the current beat value.
|
|
105
|
+
* @returns void
|
|
106
|
+
*/
|
|
107
|
+
resetCurrentBeatUI(): void;
|
|
108
|
+
/**
|
|
109
|
+
* Clears staff of notes and resets internal positioning.
|
|
110
|
+
* @returns void
|
|
111
|
+
*/
|
|
112
|
+
clearAllNotes(): void;
|
|
113
|
+
/**
|
|
114
|
+
* Removes the root svg element and cleans up arrays.
|
|
115
|
+
* @returns void
|
|
116
|
+
*/
|
|
117
|
+
destroy(): void;
|
|
118
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { GlyphNames } from '../glyphs';
|
|
2
|
+
export declare const SVG_HREF = "http://www.w3.org/2000/svg";
|
|
3
|
+
type SVGRendererOptions = {
|
|
4
|
+
width: number;
|
|
5
|
+
height: number;
|
|
6
|
+
scale: number;
|
|
7
|
+
staffColor: string;
|
|
8
|
+
staffBackgroundColor: string;
|
|
9
|
+
useGlyphs: GlyphNames[];
|
|
10
|
+
};
|
|
11
|
+
type LayerNames = 'staff' | 'notes' | 'ui';
|
|
12
|
+
type DrawGlyphOptions = {
|
|
13
|
+
yOffset?: number;
|
|
14
|
+
xOffset?: number;
|
|
15
|
+
};
|
|
16
|
+
type DrawRectOptions = {
|
|
17
|
+
x?: number;
|
|
18
|
+
y?: number;
|
|
19
|
+
fill?: string;
|
|
20
|
+
};
|
|
21
|
+
export default class SVGRenderer {
|
|
22
|
+
private rootElementRef;
|
|
23
|
+
private svgElementRef;
|
|
24
|
+
private parentGroupContainer;
|
|
25
|
+
private musicStaffLayer;
|
|
26
|
+
private musicNotesLayer;
|
|
27
|
+
private musicUILayer;
|
|
28
|
+
private width;
|
|
29
|
+
private scale;
|
|
30
|
+
private totalYOffset;
|
|
31
|
+
private totalHeight;
|
|
32
|
+
constructor(rootElementCtx: HTMLElement, options: SVGRendererOptions);
|
|
33
|
+
private makeGlyphDefs;
|
|
34
|
+
createGroup(className?: string): SVGGElement;
|
|
35
|
+
commitElementsToDOM(elements: SVGElement[] | SVGElement, parent?: HTMLElement | SVGElement): void;
|
|
36
|
+
getLayerByName(name: LayerNames): SVGGElement;
|
|
37
|
+
addTotalRootSvgHeight(amount: number): void;
|
|
38
|
+
addTotalRootSvgYOffset(amount: number): void;
|
|
39
|
+
applySizingToRootSvg(): void;
|
|
40
|
+
get rootSvgElement(): SVGElement;
|
|
41
|
+
get parentGroupElement(): SVGElement;
|
|
42
|
+
drawLine(x1: number, y1: number, x2: number, y2: number, parent: SVGElement): void;
|
|
43
|
+
drawRect(width: number, height: number, parent: SVGElement, options?: DrawRectOptions): SVGRectElement;
|
|
44
|
+
drawGlyph(glyphName: GlyphNames, parent: SVGElement, options?: DrawGlyphOptions): void;
|
|
45
|
+
destroy(): void;
|
|
46
|
+
}
|
|
47
|
+
export {};
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { StaffTypes } from '../types';
|
|
2
|
+
export type ScrollingStaffOptions = {
|
|
3
|
+
width?: number;
|
|
4
|
+
scale?: number;
|
|
5
|
+
staffType?: StaffTypes;
|
|
6
|
+
spaceAbove?: number;
|
|
7
|
+
spaceBelow?: number;
|
|
8
|
+
staffColor?: string;
|
|
9
|
+
staffBackgroundColor?: string;
|
|
10
|
+
onNotesOut?: () => void;
|
|
11
|
+
};
|
|
12
|
+
export type NoteSequence = (string | string[])[];
|
|
13
|
+
export default class ScrollingStaff {
|
|
14
|
+
private svgRendererInstance;
|
|
15
|
+
private strategyInstance;
|
|
16
|
+
private noteRendererInstance;
|
|
17
|
+
private options;
|
|
18
|
+
private activeEntries;
|
|
19
|
+
private noteBuffer;
|
|
20
|
+
private notesLayer;
|
|
21
|
+
private noteCursorX;
|
|
22
|
+
/**
|
|
23
|
+
* Creates an instance of a ScrollingStaff, A single staff that takes in a queue of notes that can be advanced with in a 'endless' style of staff.
|
|
24
|
+
*
|
|
25
|
+
* @param rootElementCtx - The element (div) reference that will append the music staff elements to.
|
|
26
|
+
* @param options - Optional configuration settings. All config options are in the type ScrollingStaffOptions
|
|
27
|
+
*/
|
|
28
|
+
constructor(rootElementCtx: HTMLElement, options?: ScrollingStaffOptions);
|
|
29
|
+
private renderFirstNoteGroups;
|
|
30
|
+
private renderNextNote;
|
|
31
|
+
/**
|
|
32
|
+
* Adds notes to the queue for scrolling staff. Clears any previously added notes.
|
|
33
|
+
* @param notes - A array of note strings or sub-arrays of strings.
|
|
34
|
+
* * A single string will draw a single note `C#4w`
|
|
35
|
+
* * A sub-array will draw a chord `["C4w", "E4w", "G4w"]`
|
|
36
|
+
*
|
|
37
|
+
* Note string format
|
|
38
|
+
* * **Root**: (A-G)
|
|
39
|
+
* * **Accidental** (Optional): `#` (sharp) `b` (flat) `n` (natural) `##` (double sharp) or `bb` (double flat).
|
|
40
|
+
* * **Octave**: The octave number (e.g., `4`).
|
|
41
|
+
* * **Duration** (Optional): `w` (whole) `h` (half) `q` (quarter) or `e` (eighth). Defaults to `w` if duration is omitted
|
|
42
|
+
* @param noteIndex The index of the note that will replaced by the specified note.
|
|
43
|
+
* @returns void
|
|
44
|
+
* @throws {Error} If the index provided is out of bounds, or if a note string is not correct format.
|
|
45
|
+
*
|
|
46
|
+
* * @example
|
|
47
|
+
* // Queues a few single notes, followed by a C chord
|
|
48
|
+
* queueNotes("Bb3q", "C4w", "G4q", ["C4w", "E4w", "G4w"]);
|
|
49
|
+
*/
|
|
50
|
+
queueNotes(notes: NoteSequence): void;
|
|
51
|
+
/**
|
|
52
|
+
* Advances to the next note in sequence, if theres any remaining notes left.
|
|
53
|
+
* @returns void
|
|
54
|
+
* @callback onNotesOut passed in from the constructor options once notes are out.
|
|
55
|
+
*/
|
|
56
|
+
advanceNotes(): void;
|
|
57
|
+
/**
|
|
58
|
+
* Clears staff of notes and resets internal positioning.
|
|
59
|
+
* @returns void
|
|
60
|
+
*/
|
|
61
|
+
clearAllNotes(): void;
|
|
62
|
+
/**
|
|
63
|
+
* Removes the root svg element and cleans up arrays.
|
|
64
|
+
* @returns void
|
|
65
|
+
*/
|
|
66
|
+
destroy(): void;
|
|
67
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { StaffParams } from './strategies/StrategyInterface';
|
|
2
|
+
import { Durations, StaffTypes } from './types';
|
|
3
|
+
export declare const NAMESPACE = "vs";
|
|
4
|
+
export declare const STAFF_LINE_COUNT = 5;
|
|
5
|
+
export declare const STAFF_LINE_SPACING = 10;
|
|
6
|
+
export declare const NOTE_LAYER_START_X = 38;
|
|
7
|
+
export declare const GRAND_STAFF_SPACING = 30;
|
|
8
|
+
export declare const ACCIDENTAL_OFFSET_X = -8;
|
|
9
|
+
export declare const DOUBLE_SHARP_ACCIDENTAL_OFFSET_X = -2;
|
|
10
|
+
export declare const DOUBLE_FLAT_ACCIDENTAL_OFFSET_X = -6;
|
|
11
|
+
export declare const HALF_NOTEHEAD_WIDTH = 10;
|
|
12
|
+
export declare const NOTEHEAD_STEM_HEIGHT = 28;
|
|
13
|
+
export declare const NOTE_SPACING = 28;
|
|
14
|
+
export declare const START_LEDGER_LINE_X = -2;
|
|
15
|
+
export declare const WHOLE_NOTE_LEDGER_LINE_WIDTH = 17;
|
|
16
|
+
export declare const HALF_NOTE_LEDGER_LINE_WIDTH = 12.5;
|
|
17
|
+
export declare const CHORD_MAX_CONSECUTIVE_ACCIDENTALS = 3;
|
|
18
|
+
export declare const staffParams: Record<StaffTypes, StaffParams>;
|
|
19
|
+
export declare const durationBeatValueMap: Record<Durations, number>;
|
package/dist/glyphs.d.ts
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export type GlyphEntry = {
|
|
2
|
+
path: string;
|
|
3
|
+
xOffset: number;
|
|
4
|
+
yOffset: number;
|
|
5
|
+
};
|
|
6
|
+
export type GlyphNames = "CLEF_TREBLE" | "CLEF_BASS" | "CLEF_ALTO" | "NOTE_HEAD_WHOLE" | "NOTE_HEAD_HALF" | "NOTE_HEAD_QUARTER" | "EIGHTH_NOTE" | "EIGHTH_NOTE_FLIPPED" | "REST_WHOLE" | "REST_HALF" | "REST_QUARTER" | "REST_EIGHTH" | "ACCIDENTAL_SHARP" | "ACCIDENTAL_FLAT" | "ACCIDENTAL_DOUBLE_SHARP" | "ACCIDENTAL_DOUBLE_FLAT" | "ACCIDENTAL_NATURAL" | "TIME_4" | "TIME_3";
|
|
7
|
+
export declare const GLPYH_ENTRIES: Record<GlyphNames, GlyphEntry>;
|