11function plugindef ()
2+ finaleplugin .RequireDocument = false
23 finaleplugin .RequireSelection = false
34 finaleplugin .NoStore = true
45 finaleplugin .Author = " Robert Patterson"
@@ -8,24 +9,55 @@ function plugindef()
89 finaleplugin .CategoryTags = " Document"
910 finaleplugin .MinJWLuaVersion = 0.74
1011 finaleplugin .Notes = [[
11- This script reads a musicxml file exported from the current open document and makes changes to
12- improve the xml over what Finale produces . The best process is as follows:
12+ This script reads a musicxml file exported from Finale and makes modifies it to
13+ improve the importing into Dorico or MuseScore . The best process is as follows:
1314
14- 1. Export the current open document as uncompressed MusicXML.
15- 2. Keeping your document open, run this plugin on the output *.musicxml document.
16- 3. Import the massaged *.musicxml into a different program.
15+ 1. Export your document as uncompressed MusicXML.
16+ 2. Run this plugin on the output *.musicxml document.
17+ 3. The massaged file name has " massaged" appended to the file name.
18+ 3. Import the massaged *.musicxml into Dorico or MuseScore.
1719
1820 Here is a list of some of the changes the script makes:
1921
2022 - 8va/8vb and 15ma/15mb symbols are extended to include the last note and extended left to include leading grace notes.
23+
24+ Due to a limitation in the xml parser, all xml processing instructions are removed. These are metadata that neither
25+ Dorico nor MuseScore use, so their removal should not affect importing into those programs.
2126 ]]
2227 return " Massage MusicXML..." , " " , " Massages the MusicXML for the current open document."
2328end
2429
25- local mixin = require (" library.mixin" )
26-
2730local text_extension = " .musicxml"
2831
32+ local function remove_processing_instructions (file_path )
33+ -- Open the original file for reading
34+ local input_file <close> = io.open (file_path , " r" )
35+ if not input_file then
36+ error (" Cannot open file: " .. file_path )
37+ end
38+ -- Read the contents of the file
39+ local lines = {}
40+ for line in input_file :lines () do
41+ -- Keep the XML declaration (<?xml ... ?>), remove other processing instructions (<?...?>)
42+ if line :match (" ^%s*<%?xml" ) or not line :match (" ^%s*<%?.*%?>" ) then
43+ table.insert (lines , line )
44+ end
45+ end
46+ -- Close the input file
47+ input_file :close ()
48+ -- Open the file for writing (overwrite the original file)
49+ local output_file <close> = io.open (file_path , " w" )
50+ if not output_file then
51+ error (" Cannot open file for writing: " .. file_path )
52+ end
53+ -- Write the cleaned lines to the file
54+ for _ , line in ipairs (lines ) do
55+ output_file :write (line .. " \n " )
56+ end
57+ -- Close the output file
58+ output_file :close ()
59+ end
60+
2961function do_open_dialog (document )
3062 local path_name = finale .FCString ()
3163 local file_name = finale .FCString ()
@@ -35,18 +67,17 @@ function do_open_dialog(document)
3567 file_path :SplitToPathAndFile (path_name , file_name )
3668 end
3769 local full_file_name = file_name .LuaString
38- local extension = mixin .FCMString ()
39- :SetLuaString (file_name .LuaString )
40- :ExtractFileExtension ()
70+ local extension = finale .FCString (file_name .LuaString )
71+ extension :ExtractFileExtension ()
4172 if extension .Length > 0 then
4273 file_name :TruncateAt (file_name :FindLast (" ." .. extension .LuaString ))
4374 end
4475 file_name :AppendLuaString (text_extension )
45- local open_dialog = mixin . FCMFileOpenDialog (finenv .UI ())
46- :SetWindowTitle (finale .FCString (" Open MusicXML for " .. full_file_name ))
47- :AddFilter (finale .FCString (" *" .. text_extension ), finale .FCString (" MusicXML File" ))
48- :SetInitFolder (path_name )
49- :SetFileName (file_name )
76+ local open_dialog = finale . FCFileOpenDialog (finenv .UI ())
77+ open_dialog :SetWindowTitle (finale .FCString (" Open MusicXML for " .. full_file_name ))
78+ open_dialog :AddFilter (finale .FCString (" *" .. text_extension ), finale .FCString (" MusicXML File" ))
79+ open_dialog :SetInitFolder (path_name )
80+ open_dialog :SetFileName (file_name )
5081 open_dialog :AssureFileExtension (text_extension )
5182 if not open_dialog :Execute () then
5283 return nil
@@ -73,7 +104,6 @@ function fix_octave_shift(xml_measure)
73104 elseif shift_type == " up" or shift_type == " down" then
74105 local sign = shift_type == " down" and 1 or - 1 -- direction to transpose grace notes
75106 local octaves = (octave_shift :IntAttribute (" size" , 8 ) - 1 ) / 7
76- print (sign , octaves , " size:" , octave_shift :IntAttribute (" size" , 8 ))
77107 local prev_grace_note
78108 local prev_note = xml_direction :PreviousSiblingElement (" note" )
79109 while prev_note do
@@ -112,6 +142,20 @@ function process_xml(score_partwise)
112142 end
113143end
114144
145+ function append_massaged_to_filename (filepath )
146+ -- Extract the path, filename, and extension
147+ local path , filename , extension = filepath :match (" ^(.-)([^\\ /]-)%.([^\\ /%.]+)$" )
148+
149+ -- Check if the path extraction was successful
150+ if not path or not filename or not extension then
151+ error (" Invalid file path format" )
152+ end
153+
154+ -- Construct the new file path with " massaged" appended to the filename
155+ local new_filepath = path .. filename .. " massaged." .. extension
156+ return new_filepath
157+ end
158+
115159function music_xml_massage_export ()
116160 local documents = finale .FCDocuments ()
117161 documents :LoadAll ()
@@ -120,6 +164,8 @@ function music_xml_massage_export()
120164 if not xml_file then
121165 return
122166 end
167+ -- tinyxml2 can't parse processing instructions, so remove them
168+ remove_processing_instructions (xml_file ) -- hopefully not necessary forever
123169 local musicxml = tinyxml2 .XMLDocument ()
124170 local result = musicxml :LoadFile (xml_file )
125171 if result ~= tinyxml2 .XML_SUCCESS then
@@ -131,7 +177,9 @@ function music_xml_massage_export()
131177 error (" File " .. xml_file .. " does not appear to be a Finale-exported MusicXML file." )
132178 end
133179 process_xml (score_partwise )
134- musicxml :SaveFile (xml_file )
180+ local output_name = append_massaged_to_filename (xml_file )
181+ musicxml :SaveFile (output_name )
182+ finenv .UI ():AlertInfo (" Processed to " .. output_name .. " ." , " Processed File" )
135183end
136184
137185music_xml_massage_export ()
0 commit comments