Skip to content

Commit 9b9a821

Browse files
Organizational changes with xml
1 parent 50c32ba commit 9b9a821

File tree

3 files changed

+109
-8
lines changed

3 files changed

+109
-8
lines changed

src/CompositionBlocks.jsx

Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,38 @@
11
import { useEffect, useRef, useState } from 'react';
22
import { BlocklyWorkspace } from 'react-blockly';
3-
import { buildToolBox, newBlocklyBlockForNote, notesFromJSON } from './blockly-setup';
3+
import { buildToolBox, notesFromJSON, blocklyNoteFromMusicXMLNote, newBlocklyBlockForNote, extractMeasures, recreateMusicJSON} from './blockly-setup';
4+
45

56

67

78
export default function CompositionBlocks({ flatJSON }) {
8-
console.log("composition blocks", flatJSON)
9+
console.log("composition blocks", flatJSON)
910
const [xml, setXml] = useState('');
1011
const renderedXMLRef = useRef(null);
1112

1213

14+
// const willSetXml = (newXml) => {
15+
// console.log('willSetXml', newXml);
16+
// console.log('current ref value', renderedXMLRef.current);
17+
18+
19+
// setXml(newXml);
20+
21+
22+
// };
23+
1324
const willSetXml = (newXml) => {
1425
console.log('willSetXml', newXml);
15-
console.log('current ref value', renderedXMLRef.current);
26+
27+
setXml(newXml);
1628

29+
// Update JSON with the new measures
30+
const updatedJSON = recreateMusicJSON(newXml, flatJSON);
1731

18-
setXml(newXml);
32+
console.log("Updated JSON:", updatedJSON);
33+
// TODO: Pass this back to flat?
34+
};
1935

20-
};
2136

2237
const [toolbox, setToolbox] = useState({});
2338
useEffect(() => {
@@ -41,7 +56,6 @@ export default function CompositionBlocks({ flatJSON }) {
4156

4257
return (
4358
<div>
44-
4559
{toolbox.contents && (
4660
<BlocklyWorkspace
4761
toolboxConfiguration={toolbox} // this must be a JSON toolbox definition

src/blockly-setup.js

Lines changed: 88 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,11 +78,98 @@ function createBlocksFromJSON(workspace, json) {
7878
}
7979
}
8080

81+
// Function to extract measure blocks from the XML string
82+
function extractMeasures(xmlDoc) {
83+
let measureBlocks = xmlDoc.getElementsByTagName("block"); // Get all <block> elements from input
84+
let extractedMeasures = [];
85+
86+
// Function to process a single measure block
87+
function processMeasure(measureBlock) {
88+
if (measureBlock.getAttribute("type") !== "measure") return null; // change/remove this if adding more blocks?
89+
90+
let measure = { note: [] };
91+
let notesStatement = measureBlock.getElementsByTagName("statement")[0]; // Find the NOTES statement inside measure
92+
93+
if (notesStatement) {
94+
let noteBlock = notesStatement.getElementsByTagName("block")[0]; // Get the first note block inside the statement
95+
let extractedNotes = [];
96+
97+
// Traverse through the linked notes using the <next> tag
98+
while (noteBlock) {
99+
if (noteBlock.getAttribute("type") === "play_sound") { // Ensure the block is a play_sound type
100+
let noteData = {}; // Object to store note properties
101+
let fields = noteBlock.querySelectorAll(":scope > field"); // Get only direct child fields
102+
103+
for (let field of fields) {
104+
noteData[field.getAttribute("name").toLowerCase()] = field.textContent; // Store note attributes
105+
}
106+
107+
// Push formatted note data into extractedNotes array
108+
extractedNotes.push({
109+
staff: "1",
110+
voice: "1",
111+
duration: noteData.duration === "quarter" ? "1" :
112+
noteData.duration === "half" ? "2" :
113+
noteData.duration === "whole" ? "4" : "1",
114+
pitch: { octave: noteData.octave, step: noteData.step },
115+
type: noteData.duration
116+
});
117+
}
118+
119+
// Move to the next note in the <next> chain
120+
let nextElement = noteBlock.querySelector(":scope > next");
121+
noteBlock = nextElement ? nextElement.querySelector(":scope > block") : null;
122+
}
123+
124+
measure.note = extractedNotes.reverse(); // Reverse extracted notes to maintain visual order
125+
}
126+
127+
return measure;
128+
}
129+
130+
// Loop through all measure blocks and extract data
131+
for (let block of measureBlocks) {
132+
let measure = processMeasure(block);
133+
if (measure) extractedMeasures.push(measure);
134+
}
135+
136+
return extractedMeasures;
137+
}
138+
139+
// Function to recreate JSON from updated XML
140+
function recreateMusicJSON(xmlString, originalJSON) {
141+
const parser = new DOMParser(); // Create a new DOMParser instance
142+
const xmlDoc = parser.parseFromString(xmlString, "text/xml"); // Parse XML string into a document
143+
144+
let newMeasures = extractMeasures(xmlDoc); // Extract measure data from the XML
145+
146+
let updatedJSON = JSON.parse(JSON.stringify(originalJSON)); // Deep copy original JSON to avoid modifying input
147+
let existingMeasures = updatedJSON?.["score-partwise"]?.["part"]?.[0]?.["measure"] || []; // Locate the measures section in JSON
148+
149+
// Update or add measures in the JSON
150+
for (let i = 0; i < newMeasures.length; i++) {
151+
if (existingMeasures[i]) {
152+
existingMeasures[i].note = newMeasures[i].note; // Update existing measure notes
153+
} else {
154+
existingMeasures.push(newMeasures[i]); // Add new measure if it doesn't exist
155+
}
156+
}
157+
158+
// Remove extra measures if necessary
159+
if (existingMeasures.length > newMeasures.length) {
160+
existingMeasures.length = newMeasures.length; // Trim array to match new data
161+
}
162+
163+
return updatedJSON;
164+
}
165+
81166

82167

83168
export {
84169
buildToolBox,
85170
notesFromJSON,
86171
blocklyNoteFromMusicXMLNote,
87-
newBlocklyBlockForNote
172+
newBlocklyBlockForNote,
173+
extractMeasures,
174+
recreateMusicJSON
88175
}

src/flatMelodyViewer.jsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import React, { useEffect, useState } from 'react';
44
import Embed from 'flat-embed';
55

66
function FlatMelodyViewer({
7-
height = 300,
7+
height = 400,
88
width = '100%',
99
score,
1010
onLoad,

0 commit comments

Comments
 (0)