@@ -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
83168export {
84169 buildToolBox ,
85170 notesFromJSON ,
86171 blocklyNoteFromMusicXMLNote ,
87- newBlocklyBlockForNote
172+ newBlocklyBlockForNote ,
173+ extractMeasures ,
174+ recreateMusicJSON
88175}
0 commit comments