Skip to content

Commit e5a710d

Browse files
chore: autopublish 2024-09-23T00:26:47Z
1 parent 49aba50 commit e5a710d

File tree

4 files changed

+196
-3
lines changed

4 files changed

+196
-3
lines changed

dist/articulation_reset_auto_positioning.lua

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -363,8 +363,8 @@ end
363363
function plugindef()
364364
finaleplugin.Author = "Robert Patterson"
365365
finaleplugin.Copyright = "CC0 https://creativecommons.org/publicdomain/zero/1.0/"
366-
finaleplugin.Version = "1.1"
367-
finaleplugin.Date = "July 29, 2024"
366+
finaleplugin.Version = "1.1.1"
367+
finaleplugin.Date = "September 22, 2024"
368368
finaleplugin.CategoryTags = "Articulation"
369369
finaleplugin.MinFinaleVersionRaw = 0x1a000000
370370
finaleplugin.MinJWLuaVersion = 0.58

dist/musicxml_massage_export.lua

Lines changed: 192 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,192 @@
1+
function plugindef()
2+
finaleplugin.RequireDocument = false
3+
finaleplugin.RequireSelection = false
4+
finaleplugin.NoStore = true
5+
finaleplugin.Author = "Robert Patterson"
6+
finaleplugin.Copyright = "CC0 https://creativecommons.org/publicdomain/zero/1.0/"
7+
finaleplugin.Version = "1.0"
8+
finaleplugin.Date = "September 24, 2024"
9+
finaleplugin.CategoryTags = "Document"
10+
finaleplugin.MinJWLuaVersion = 0.74
11+
finaleplugin.Notes = [[
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:
14+
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.
19+
20+
Here is a list of some of the changes the script makes:
21+
22+
- 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.
26+
]]
27+
finaleplugin.RTFNotes = [[
28+
{\rtf1\ansi\deff0{\fonttbl{\f0 \fswiss Helvetica;}{\f1 \fmodern Courier New;}}
29+
{\colortbl;\red255\green0\blue0;\red0\green0\blue255;}
30+
\widowctrl\hyphauto
31+
\fs18
32+
{\info{\comment "os":"mac","fs18":"fs24","fs26":"fs32","fs23":"fs29","fs20":"fs26"}}
33+
{\pard \sl264 \slmult1 \ql \f0 \sa180 \li0 \fi0 This script reads a musicxml file exported from Finale and makes modifies it to improve the importing into Dorico or MuseScore. The best process is as follows:\par}
34+
{\pard \sl264 \slmult1 \ql \f0 \sa0 \li360 \fi-360 1.\tx360\tab Export your document as uncompressed MusicXML.\par}
35+
{\pard \sl264 \slmult1 \ql \f0 \sa0 \li360 \fi-360 2.\tx360\tab Run this plugin on the output *.musicxml document.\par}
36+
{\pard \sl264 \slmult1 \ql \f0 \sa0 \li360 \fi-360 3.\tx360\tab The massaged file name has " massaged" appended to the file name.\par}
37+
{\pard \sl264 \slmult1 \ql \f0 \sa0 \li360 \fi-360 4.\tx360\tab Import the massaged *.musicxml into Dorico or MuseScore.\sa180\par}
38+
{\pard \sl264 \slmult1 \ql \f0 \sa180 \li0 \fi0 Here is a list of some of the changes the script makes:\par}
39+
{\pard \sl264 \slmult1 \ql \f0 \sa0 \li360 \fi-360 \bullet \tx360\tab 8va/8vb and 15ma/15mb symbols are extended to include the last note and extended left to include leading grace notes.\sa180\par}
40+
{\pard \sl264 \slmult1 \ql \f0 \sa180 \li0 \fi0 Due to a limitation in the xml parser, all xml processing instructions are removed. These are metadata that neither Dorico nor MuseScore use, so their removal should not affect importing into those programs.\par}
41+
}
42+
]]
43+
finaleplugin.HashURL = "https://raw.githubusercontent.com/finale-lua/lua-scripts/master/hash/musicxml_massage_export.hash"
44+
return "Massage MusicXML...", "", "Massages the MusicXML for the current open document."
45+
end
46+
local text_extension = ".musicxml"
47+
local function remove_processing_instructions(file_path)
48+
49+
local input_file <close> = io.open(file_path, "r")
50+
if not input_file then
51+
error("Cannot open file: " .. file_path)
52+
end
53+
54+
local lines = {}
55+
for line in input_file:lines() do
56+
57+
if line:match("^%s*<%?xml") or not line:match("^%s*<%?.*%?>") then
58+
table.insert(lines, line)
59+
end
60+
end
61+
62+
input_file:close()
63+
64+
local output_file <close> = io.open(file_path, "w")
65+
if not output_file then
66+
error("Cannot open file for writing: " .. file_path)
67+
end
68+
69+
for _, line in ipairs(lines) do
70+
output_file:write(line .. "\n")
71+
end
72+
73+
output_file:close()
74+
end
75+
function do_open_dialog(document)
76+
local path_name = finale.FCString()
77+
local file_name = finale.FCString()
78+
local file_path = finale.FCString()
79+
if document then
80+
document:GetPath(file_path)
81+
file_path:SplitToPathAndFile(path_name, file_name)
82+
end
83+
local full_file_name = file_name.LuaString
84+
local extension = finale.FCString(file_name.LuaString)
85+
extension:ExtractFileExtension()
86+
if extension.Length > 0 then
87+
file_name:TruncateAt(file_name:FindLast("." .. extension.LuaString))
88+
end
89+
file_name:AppendLuaString(text_extension)
90+
local open_dialog = finale.FCFileOpenDialog(finenv.UI())
91+
open_dialog:SetWindowTitle(finale.FCString("Open MusicXML for " .. full_file_name))
92+
open_dialog:AddFilter(finale.FCString("*" .. text_extension), finale.FCString("MusicXML File"))
93+
open_dialog:SetInitFolder(path_name)
94+
open_dialog:SetFileName(file_name)
95+
open_dialog:AssureFileExtension(text_extension)
96+
if not open_dialog:Execute() then
97+
return nil
98+
end
99+
local selected_file_name = finale.FCString()
100+
open_dialog:GetFileName(selected_file_name)
101+
return selected_file_name.LuaString
102+
end
103+
function fix_octave_shift(xml_measure)
104+
for xml_direction in xmlelements(xml_measure, "direction") do
105+
local xml_direction_type = xml_direction:FirstChildElement("direction-type")
106+
if xml_direction_type then
107+
local octave_shift = xml_direction_type:FirstChildElement("octave-shift")
108+
if octave_shift then
109+
local direction_copy = xml_direction:DeepClone(xml_direction:GetDocument())
110+
local shift_type = octave_shift:Attribute("type")
111+
if shift_type == "stop" then
112+
local next_note = xml_direction:NextSiblingElement("note")
113+
if next_note and not next_note:FirstChildElement("rest") then
114+
xml_measure:DeleteChild(xml_direction)
115+
xml_measure:InsertAfterChild(next_note, direction_copy)
116+
end
117+
elseif shift_type == "up" or shift_type == "down" then
118+
local sign = shift_type == "down" and 1 or -1
119+
local octaves = (octave_shift:IntAttribute("size", 8) - 1) / 7
120+
local prev_grace_note
121+
local prev_note = xml_direction:PreviousSiblingElement("note")
122+
while prev_note do
123+
if not prev_note:FirstChildElement("rest") and prev_note:FirstChildElement("grace") then
124+
prev_grace_note = prev_note
125+
local pitch = prev_note:FirstChildElement("pitch")
126+
local octave = pitch and pitch:FirstChildElement("octave")
127+
if octave then
128+
octave:SetIntText(octave:IntText() + sign*octaves)
129+
end
130+
else
131+
break
132+
end
133+
prev_note = prev_note:PreviousSiblingElement("note")
134+
end
135+
if prev_grace_note then
136+
xml_measure:DeleteChild(xml_direction)
137+
local prev_element = prev_grace_note:PreviousSiblingElement()
138+
if prev_element then
139+
xml_measure:InsertAfterChild(prev_element, direction_copy)
140+
else
141+
xml_measure:InsertFirstChild(direction_copy)
142+
end
143+
end
144+
end
145+
end
146+
end
147+
end
148+
end
149+
function process_xml(score_partwise)
150+
for xml_part in xmlelements(score_partwise, "part") do
151+
for xml_measure in xmlelements(xml_part, "measure") do
152+
fix_octave_shift(xml_measure)
153+
end
154+
end
155+
end
156+
function append_massaged_to_filename(filepath)
157+
158+
local path, filename, extension = filepath:match("^(.-)([^\\/]-)%.([^\\/%.]+)$")
159+
160+
if not path or not filename or not extension then
161+
error("Invalid file path format")
162+
end
163+
164+
local new_filepath = path .. filename .. " massaged." .. extension
165+
return new_filepath
166+
end
167+
function music_xml_massage_export()
168+
local documents = finale.FCDocuments()
169+
documents:LoadAll()
170+
local document = documents:FindCurrent()
171+
local xml_file = do_open_dialog(document)
172+
if not xml_file then
173+
return
174+
end
175+
176+
remove_processing_instructions(xml_file)
177+
local musicxml = tinyxml2.XMLDocument()
178+
local result = musicxml:LoadFile(xml_file)
179+
if result ~= tinyxml2.XML_SUCCESS then
180+
error("Unable to process " .. xml_file .. ". " .. musicxml:ErrorStr())
181+
return
182+
end
183+
local score_partwise = musicxml:FirstChildElement("score-partwise")
184+
if not score_partwise then
185+
error("File " .. xml_file .. " does not appear to be a Finale-exported MusicXML file.")
186+
end
187+
process_xml(score_partwise)
188+
local output_name = append_massaged_to_filename(xml_file)
189+
musicxml:SaveFile(output_name)
190+
finenv.UI():AlertInfo("Processed to " .. output_name .. ".", "Processed File")
191+
end
192+
music_xml_massage_export()
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
c16685e5fba9862c5ea88c9577cb0ecc4902e486df81fa9f38e8a66a45452050487a01ea81b9ae19dca49c9d80fa4143c97f4fe6069bdc9fb62fea583ba13174 articulation_reset_auto_positioning.lua
1+
4f9c29a769dcbc9d9033e9a73a3529bdd96e94895e3218ea225a319e36ec093c5ba5563be96e6fb1a80e2685a64e0ce27af4dac0b3845bb50b504b9a4fab5c45 articulation_reset_auto_positioning.lua

hash/musicxml_massage_export.hash

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
c099c63267fabaa98df19e6e79687a4ba15c85ef33e86c1eb455c407c68f5571c068fcb14df262053c7180b900944454fb063911989248ddce7ef2d62493d7a4 musicxml_massage_export.lua

0 commit comments

Comments
 (0)