Reading Musicxml files

Home Forums General General MIDI discussion Reading Musicxml files

Tagged: 

Viewing 7 posts - 1 through 7 (of 7 total)
  • Author
    Posts
  • #24504
    doudbassctrlr
    Participant
      • Topics: 2
      • Replies: 13
      • Total: 15

      Hello,
      I was wondering if it could be possible to integrate in a panel the content of some musicxml files. I am thinking to integrate something very similar to http://irealpro.com/ song display in my ctrlr vst.
      It seems that “Luaxml” library could help, but I am still being a very new user of Lua and Ctrlr and also not sure that it can used with ctrlr.
      Any alternative ideas?
      Thanks
      Doud

      #24653
      doudbassctrlr
      Participant
        • Topics: 2
        • Replies: 13
        • Total: 15

        Hello,
        I am having a first try with a code named Slaxml.lua that is given below. I have integrated it to a method called when file is double clicked in the uiFileListBox options and get a lua runtime error saying “at line [-1]: [C] Error message: No such operator defined”.
        Any idea of what is going on?

        --[=====================================================================[
        --v0.6 Copyright © 2013-2014 Gavin Kistner <!@phrogz.net>; MIT Licensed
        --See http://github.com/Phrogz/SLAXML for details.
        --]=====================================================================]
        local SLAXML = {
        	VERSION = "0.6",
        	_call = {
        		pi = function(target,content)
        			print(string.format("<?%s %s?>",target,content))
        		end,
        		comment = function(content)
        			print(string.format("<!-- %s -->",content))
        		end,
        		startElement = function(name,nsURI,nsPrefix)
        			                 io.write("<")
        			if nsPrefix then io.write(nsPrefix,":") end
        			                 io.write(name)
        			if nsURI    then io.write(" (ns='",nsURI,"')") end
        			                 print(">")
        		end,
        		attribute = function(name,value,nsURI,nsPrefix)
        			io.write('  ')
        			if nsPrefix then io.write(nsPrefix,":") end
        			                 io.write(name,'=',string.format('%q',value))
        			if nsURI    then io.write(" (ns='",nsURI,"')") end
        			io.write("\n")
        		end,
        		text = function(text)
        			print(string.format("  text: %q",text))
        		end,
        		closeElement = function(name,nsURI,nsPrefix)
        			print(string.format("</%s>",name))
        		end,
        	}
        }
        
        function SLAXML:parser(callbacks)
        	return { _call=callbacks or self._call, parse=SLAXML.parse }
        end
        
        function SLAXML:parse(xml,options)
        	if not options then options = { stripWhitespace=false } end
        
        	-- Cache references for maximum speed
        	local find, sub, gsub, char, push, pop = string.find, string.sub, string.gsub, string.char, table.insert, table.remove
        	local first, last, match1, match2, match3, pos2, nsURI
        	local unpack = unpack or table.unpack
        	local pos = 1
        	local state = "text"
        	local textStart = 1
        	local currentElement={}
        	local currentAttributes={}
        	local currentAttributeCt -- manually track length since the table is re-used
        	local nsStack = {}
        
        	local entityMap  = { ["lt"]="<", ["gt"]=">", ["amp"]="&", ["quot"]='"', ["apos"]="'" }
        	local entitySwap = function(orig,n,s) return entityMap or n=="#" and char(s) or orig end
        	local function unescape(str) return gsub( str, '(&(#?)([%d%a]+);)', entitySwap ) end
        	local anyElement = false
        
        	local function finishText()
        		if first>textStart and self._call.text then
        			local text = sub(xml,textStart,first-1)
        			if options.stripWhitespace then
        				text = gsub(text,'^%s+','')
        				text = gsub(text,'%s+$','')
        				if #text==0 then text=nil end
        			end
        			if text then self._call.text(unescape(text)) end
        		end
        	end
        
        	local function findPI()
        		first, last, match1, match2 = find( xml, '^<%?([:%a_][:%w_.-]*) ?(.-)%?>', pos )
        		if first then
        			finishText()
        			if self._call.pi then self._call.pi(match1,match2) end
        			pos = last+1
        			textStart = pos
        			return true
        		end
        	end
        
        	local function findComment()
        		first, last, match1 = find( xml, '^<!%-%-(.-)%-%->', pos )
        		if first then
        			finishText()
        			if self._call.comment then self._call.comment(match1) end
        			pos = last+1
        			textStart = pos
        			return true
        		end
        	end
        
        	local function nsForPrefix(prefix)
        		if prefix=='xml' then return 'http://www.w3.org/XML/1998/namespace' end -- http://www.w3.org/TR/xml-names/#ns-decl
        		for i=#nsStack,1,-1 do if nsStack[prefix] then return nsStack[prefix] end end
        		error(("Cannot find namespace for prefix %s"):format(prefix))
        	end
        
        	local function startElement()
        		anyElement = true
        		first, last, match1 = find( xml, '^<([%a_][%w_.-]*)', pos )
        		if first then
        			currentElement[2] = nil -- reset the nsURI, since this table is re-used
        			currentElement[3] = nil -- reset the nsPrefix, since this table is re-used
        			finishText()
        			pos = last+1
        			first,last,match2 = find(xml, '^:([%a_][%w_.-]*)', pos )
        			if first then
        				currentElement[1] = match2
        				currentElement[3] = match1 -- Save the prefix for later resolution
        				match1 = match2
        				pos = last+1
        			else
        				currentElement[1] = match1
        				for i=#nsStack,1,-1 do if nsStack['!'] then currentElement[2] = nsStack['!']; break end end
        			end
        			currentAttributeCt = 0
        			push(nsStack,{})
        			return true
        		end
        	end
        
        	local function findAttribute()
        		first, last, match1 = find( xml, '^%s+([:%a_][:%w_.-]*)%s*=%s*', pos )
        		if first then
        			pos2 = last+1
        			first, last, match2 = find( xml, '^"([^<"]*)"', pos2 ) -- FIXME: disallow non-entity ampersands
        			if first then
        				pos = last+1
        				match2 = unescape(match2)
        			else
        				first, last, match2 = find( xml, "^'([^<']*)'", pos2 ) -- FIXME: disallow non-entity ampersands
        				if first then
        					pos = last+1
        					match2 = unescape(match2)
        				end
        			end
        		end
        		if match1 and match2 then
        			local currentAttribute = {match1,match2}
        			local prefix,name = string.match(match1,'^([^:]+):([^:]+)$')
        			if prefix then
        				if prefix=='xmlns' then
        					nsStack[#nsStack][name] = match2
        				else
        					currentAttribute[1] = name
        					currentAttribute[4] = prefix
        				end
        			else
        				if match1=='xmlns' then
        					nsStack[#nsStack]['!'] = match2
        					currentElement[2]      = match2
        				end
        			end
        			currentAttributeCt = currentAttributeCt + 1
        			currentAttributes[currentAttributeCt] = currentAttribute
        			return true
        		end
        	end
        
        	local function findCDATA()
        		first, last, match1 = find( xml, '^<!%[CDATA%[(.-)%]%]>', pos )
        		if first then
        			finishText()
        			if self._call.text then self._call.text(match1) end
        			pos = last+1
        			textStart = pos
        			return true
        		end
        	end
        
        	local function closeElement()
        		first, last, match1 = find( xml, '^%s*(/?)>', pos )
        		if first then
        			state = "text"
        			pos = last+1
        			textStart = pos
        
        			-- Resolve namespace prefixes AFTER all new/redefined prefixes have been parsed
        			if currentElement[3] then currentElement[2] = nsForPrefix(currentElement[3])    end
        			if self._call.startElement then self._call.startElement(unpack(currentElement)) end
        			if self._call.attribute then
        				for i=1,currentAttributeCt do
        					if currentAttributes[4] then currentAttributes[3] = nsForPrefix(currentAttributes[4]) end
        					self._call.attribute(unpack(currentAttributes))
        				end
        			end
        
        			if match1=="/" then
        				pop(nsStack)
        				if self._call.closeElement then self._call.closeElement(unpack(currentElement)) end
        			end
        			return true
        		end
        	end
        
        	local function findElementClose()
        		first, last, match1, match2 = find( xml, '^</([%a_][%w_.-]*)%s*>', pos )
        		if first then
        			nsURI = nil
        			for i=#nsStack,1,-1 do if nsStack['!'] then nsURI = nsStack['!']; break end end
        		else
        			first, last, match2, match1 = find( xml, '^</([%a_][%w_.-]*):([%a_][%w_.-]*)%s*>', pos )
        			if first then nsURI = nsForPrefix(match2) end
        		end
        		if first then
        			finishText()
        			if self._call.closeElement then self._call.closeElement(match1,nsURI) end
        			pos = last+1
        			textStart = pos
        			pop(nsStack)
        			return true
        		end
        	end
        
        	while pos<#xml do
        		if state=="text" then
        			if not (findPI() or findComment() or findCDATA() or findElementClose()) then		
        				if startElement() then
        					state = "attributes"
        				else
        					first, last = find( xml, '^[^<]+', pos )
        					pos = (first and last or pos) + 1
        				end
        			end
        		elseif state=="attributes" then
        			if not findAttribute() then
        				if not closeElement() then
        					error("Was in an element and couldn't find attributes or the close.")
        				end
        			end
        		end
        	end
        
        	if not anyElement then error("Parsing did not discover any elements") end
        	if #nsStack > 0 then error("Parsing ended with unclosed elements") end
        end
        
        --return SLAXML
        --
        -- Called when a file is double clicked
        --
        -- @modulator the modulator the event occured on
        -- @file      a File object that represents the double clicked file
        --
        MusixmlReader = function(modulator, file)
        SLAXML:parse(file)
        end
        #24655
        atom
        Keymaster
          • Topics: 159
          • Replies: 2945
          • Total: 3104
          • ★★★★★

          Oh man there is no way i can tell you quickly what went wrong here.

          One thing i can say is that a XML parser is already built into Ctrlr and you don’t need to implement you own, the entire XmlElement class from JUCE is available in Ctrlr/Lua
          http://www.juce.com/juce/api/classXmlElement.html

          #24659
          doudbassctrlr
          Participant
            • Topics: 2
            • Replies: 13
            • Total: 15

            Great I didn’t know about Juce and its xml functions. Moreover it looks quite flexible and it should allow me to read the Musicxml files.

            Just to start with, can someone help me to understand how to use Juce functions in Lua methods?

            #24666
            atom
            Keymaster
              • Topics: 159
              • Replies: 2945
              • Total: 3104
              • ★★★★★

              Have a look at the DEMO panels installed with Ctrlr (they should be in the Ctrlr installation directory) most of them use JUCE somehow.

              #24676
              doudbassctrlr
              Participant
                • Topics: 2
                • Replies: 13
                • Total: 15

                I have some troubles understanding how works Lua. XmlDocument is a C++ class implemented in Juce. I have some basics of C++ but I do not know how to use its constructor in Lua.
                What follows:

                -- @file      a File object that represents the double clicked file
                [...]
                myfile=XmlDocument(file)
                [...]

                gives me a runtime error message: “attempt to call global ‘XmlDocument’ (a nil value)”

                However file really seems to be a File object as required…

                #24679
                atom
                Keymaster
                  • Topics: 159
                  • Replies: 2945
                  • Total: 3104
                  • ★★★★★

                  You can shoot me on the spot. It looks like i didn’t add the XML classes for some reason, i will fix that and add them ASAP. I was sure they were there as i remember doing some XML based tests in Lua, but for some reason a lot of classes are there but those 2 are not.

                Viewing 7 posts - 1 through 7 (of 7 total)
                • The forum ‘General MIDI discussion’ is closed to new topics and replies.
                There is currently 0 users and 87 guests online
                No users are currently active
                Forum Statistics
                Threads: 2,495, Posts: 17,374, Members: 77,605
                Most users ever online was 12 on January 22, 2019 3:47 pm
                Ctrlr