1import { Pitch } from "notation/pitch"; 2import { Duration } from "notation/duration"; 3import { 4 Note, 5 Rest, 6 Tuplet, 7 Chord, 8 GraceNote, 9 Measure, 10 Score, 11} from "notation/score"; 12 13// ââ Score â JSON âââââââââââââââââââââââââââââââââââââââââââââââââââââ 14 15export function scoreToJson(score) { 16 const json = { 17 version: 2, 18 clef: score.clef, 19 key: score.key, 20 time: [...score.timeSignature], 21 tempo: score.tempo, 22 measures: score.measures.map((m) => measureToJson(m, score.timeSignature)), 23 }; 24 if (score.title != null) json.title = score.title; 25 if (score.subtitle != null) json.subtitle = score.subtitle; 26 if (score.composer != null) json.composer = score.composer; 27 if (score.parts) json.parts = score.parts; 28 if (score.staves) json.staves = score.staves.map((s) => ({ ...s })); 29 if (score.pickupBeats != null) json.pickup_beats = score.pickupBeats; 30 return json; 31} 32 33function measureToJson(measure, scoreTimeSig) { 34 const json = { 35 elements: measure.elements.map(elementToJson), 36 }; 37 // Per-measure key signature (null = inherit) 38 if (measure.keySignature != null) { 39 json.key_signature = measure.keySignature; 40 } 41 // Per-measure time signature (only when differs from score default) 42 const [sNum, sDen] = scoreTimeSig; 43 const [mNum, mDen] = measure.timeSignature; 44 if (mNum !== sNum || mDen !== sDen) { 45 json.time_signature = [mNum, mDen]; 46 } 47 if (measure.barlineType && measure.barlineType !== "normal") { 48 json.barline_type = measure.barlineType; 49 } 50 if (measure.volta) json.volta = measure.volta; 51 if (measure.navigationMark) json.navigation_mark = measure.navigationMark; 52 if (measure.rehearsalMark) json.rehearsal_mark = measure.rehearsalMark; 53 if (measure.systemBreak) json.system_break = true; 54 if (measure.isPickup) json.is_pickup = true; 55 if (measure.tempoChange) { 56 json.tempo_change = { ...measure.tempoChange }; 57 } 58 if (measure.clef) json.clef = measure.clef; 59 if (measure.section) json.section = measure.section; 60 return json; 61} 62 63function elementToJson(el) { 64 if (el instanceof GraceNote) return graceNoteToJson(el); 65 if (el instanceof Chord) return chordToJson(el); 66 if (el instanceof Note) return noteToJson(el); 67 if (el instanceof Rest) return restToJson(el); 68 if (el instanceof Tuplet) return tupletToJson(el); 69 throw new Error(`Unknown element type: ${el.constructor.name}`); 70} 71 72function noteToJson(note) { 73 const h = { 74 type: "note", 75 pitch: note.pitch.toString(), 76 dur: note.duration.toShorthand(), 77 }; 78 if (note.accidental) h.acc = note.accidental; 79 if (note.tie) h.tie = note.tie; 80 if (note.slurs?.length) h.slurs = note.slurs.map((s) => ({ ...s })); 81 if (note.articulations.length) h.articulations = [...note.articulations]; 82 if (note.beamGroup != null) h.beam_group = note.beamGroup; 83 if (note.textAbove) h.text_above = note.textAbove; 84 if (note.textBelow) h.text_below = note.textBelow; 85 if (note.chordSymbol) h.chord_symbol = note.chordSymbol; 86 if (note.slash) h.slash = true; 87 if (note.hairpin) h.hairpin = note.hairpin; 88 if (note.ottava) h.ottava = note.ottava; 89 if (note.fingering != null) h.fingering = note.fingering; 90 if (note.lyric) h.lyric = note.lyric; 91 if (note.pedal) h.pedal = note.pedal; 92 if (note.dynamic) h.dynamic = note.dynamic; 93 if (note.voice != null) h.voice = note.voice; 94 if (note.staff != null) h.staff = note.staff; 95 if (note.part != null) h.part = note.part; 96 return h; 97} 98 99function chordToJson(chord) { 100 const h = { 101 type: "chord", 102 pitches: chord.pitches.map((p) => p.toString()), 103 dur: chord.duration.toShorthand(), 104 }; 105 if (chord.accidentals.length) h.accidentals = [...chord.accidentals]; 106 if (chord.tie) h.tie = chord.tie; 107 if (chord.slurs?.length) h.slurs = chord.slurs.map((s) => ({ ...s })); 108 if (chord.articulations.length) h.articulations = [...chord.articulations]; 109 if (chord.beamGroup != null) h.beam_group = chord.beamGroup; 110 if (chord.textAbove) h.text_above = chord.textAbove; 111 if (chord.textBelow) h.text_below = chord.textBelow; 112 if (chord.chordSymbol) h.chord_symbol = chord.chordSymbol; 113 if (chord.slash) h.slash = true; 114 if (chord.hairpin) h.hairpin = chord.hairpin; 115 if (chord.ottava) h.ottava = chord.ottava; 116 if (chord.fingering != null) h.fingering = chord.fingering; 117 if (chord.lyric) h.lyric = chord.lyric; 118 if (chord.pedal) h.pedal = chord.pedal; 119 if (chord.dynamic) h.dynamic = chord.dynamic; 120 if (chord.voice != null) h.voice = chord.voice; 121 if (chord.staff != null) h.staff = chord.staff; 122 if (chord.part != null) h.part = chord.part; 123 return h; 124} 125 126function restToJson(rest) { 127 const h = { type: "rest", dur: rest.duration.toShorthand() }; 128 if (rest.chordSymbol) h.chord_symbol = rest.chordSymbol; 129 if (rest.dynamic) h.dynamic = rest.dynamic; 130 if (rest.voice != null) h.voice = rest.voice; 131 if (rest.staff != null) h.staff = rest.staff; 132 if (rest.part != null) h.part = rest.part; 133 return h; 134} 135 136function graceNoteToJson(grace) { 137 const h = { 138 type: "grace_note", 139 pitch: grace.pitch.toString(), 140 dur: grace.duration.toShorthand(), 141 grace_type: grace.graceType, 142 }; 143 if (grace.accidental) h.acc = grace.accidental; 144 if (grace.staff != null) h.staff = grace.staff; 145 if (grace.part != null) h.part = grace.part; 146 return h; 147} 148 149function tupletToJson(tuplet) { 150 return { 151 type: "tuplet", 152 ratio: [...tuplet.ratio], 153 notes: tuplet.notes.map(elementToJson), 154 }; 155} 156 157// ââ JSON â Score âââââââââââââââââââââââââââââââââââââââââââââââââââââ 158 159export function jsonToScore(json) { 160 const time = json.time || [4, 4]; 161 const measures = (json.measures || []).map((m) => parseMeasure(m, time)); 162 163 return new Score({ 164 measures, 165 key: json.key || "C", 166 clef: json.clef || "treble", 167 tempo: json.tempo ?? 120, 168 timeSignature: time, 169 title: json.title ?? null, 170 subtitle: json.subtitle ?? null, 171 composer: json.composer ?? null, 172 parts: json.parts || null, 173 staves: json.staves || null, 174 pickupBeats: json.pickup_beats ?? null, 175 }); 176} 177 178function parseMeasure(json, scoreTimeSig) { 179 const elements = (json.elements || []).map(parseElement); 180 // Per-measure time signature: use explicit override, else inherit score default 181 const timeSignature = json.time_signature || scoreTimeSig; 182 return new Measure({ 183 elements, 184 timeSignature, 185 keySignature: json.key_signature ?? null, 186 barlineType: json.barline_type || "normal", 187 volta: json.volta ?? null, 188 navigationMark: json.navigation_mark ?? null, 189 rehearsalMark: json.rehearsal_mark ?? null, 190 systemBreak: json.system_break || false, 191 isPickup: json.is_pickup || false, 192 tempoChange: json.tempo_change ?? null, 193 clef: json.clef ?? null, 194 section: json.section ?? null, 195 }); 196} 197 198function parseElement(json) { 199 switch (json.type) { 200 case "note": 201 return new Note({ 202 pitch: json.pitch, 203 duration: Duration.fromShorthand(json.dur), 204 accidental: json.acc || null, 205 tie: json.tie || null, 206 slurs: json.slurs || [], 207 articulations: json.articulations || [], 208 beamGroup: json.beam_group ?? null, 209 textAbove: json.text_above || null, 210 textBelow: json.text_below || null, 211 chordSymbol: json.chord_symbol || null, 212 slash: json.slash || false, 213 hairpin: json.hairpin || null,
214 ottava: json.ottava || null, 215 fingering: json.fingering ?? null, 216 lyric: json.lyric || null, 217 pedal: json.pedal || null, 218 dynamic: json.dynamic || null, 219 voice: json.voice ?? null, 220 staff: json.staff ?? null, 221 part: json.part ?? null, 222 }); 223 case "chord": 224 return new Chord({ 225 pitches: json.pitches, 226 duration: Duration.fromShorthand(json.dur), 227 accidentals: json.accidentals || [], 228 tie: json.tie || null, 229 slurs: json.slurs || [], 230 articulations: json.articulations || [], 231 beamGroup: json.beam_group ?? null, 232 textAbove: json.text_above || null, 233 textBelow: json.text_below || null, 234 chordSymbol: json.chord_symbol || null, 235 slash: json.slash || false, 236 hairpin: json.hairpin || null, 237 ottava: json.ottava || null, 238 fingering: json.fingering ?? null, 239 lyric: json.lyric || null, 240 pedal: json.pedal || null, 241 dynamic: json.dynamic || null, 242 voice: json.voice ?? null, 243 staff: json.staff ?? null, 244 part: json.part ?? null, 245 }); 246 case "rest": 247 return new Rest({ 248 duration: Duration.fromShorthand(json.dur), 249 chordSymbol: json.chord_symbol || null, 250 dynamic: json.dynamic || null, 251 voice: json.voice ?? null, 252 staff: json.staff ?? null, 253 part: json.part ?? null, 254 }); 255 case "grace_note": 256 return new GraceNote({ 257 pitch: json.pitch, 258 duration: json.dur ? Duration.fromShorthand(json.dur) : "eighth", 259 accidental: json.acc || null, 260 graceType: json.grace_type || "acciaccatura", 261 staff: json.staff ?? null, 262 part: json.part ?? null, 263 }); 264 case "tuplet": { 265 const notes = (json.notes || []).map(parseElement); 266 return new Tuplet({ ratio: json.ratio, notes }); 267 } 268 default: 269 throw new Error(`Unknown element type: ${json.type}`); 270 } 271}
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.