gt: 					Element eMode = attr.getFirstChildElement("mode");
context: /*
   * JFugue - API for Music Programming
   * Copyright (C) 2003-2008  David Koelle
   *
   * http://www.jfugue.org 
   * 
   * This library is free software; you can redistribute it and/or
   * modify it under the terms of the GNU Lesser General Public
   * License as published by the Free Software Foundation; either
   * version 2.1 of the License, or any later version.
   * 
   * This library is distributed in the hope that it will be useful,
   * but WITHOUT ANY WARRANTY; without even the implied warranty of
   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   * Lesser General Public License for more details.
   * 
   * You should have received a copy of the GNU Lesser General Public
   * License along with this library; if not, write to the Free Software
   * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
   *  
   */
  
  package org.jfugue;
  
  import java.io.File;
  import java.io.FileInputStream;
  import java.io.FileNotFoundException;
  import java.io.IOException;
  import java.io.Reader;
  import java.nio.ByteBuffer;
  import java.nio.channels.FileChannel;
  import java.util.HashMap;
  import java.util.Map;
  
  import nu.xom.Attribute;
  import nu.xom.Builder;
  import nu.xom.DocType;
  import nu.xom.Document;
  import nu.xom.Element;
  import nu.xom.Elements;
  import nu.xom.Node;
  import nu.xom.ParsingException;
  import nu.xom.ValidityException;
  
  //	helper class
  class XMLpart extends Object
  {	public String	ID;
  	public String	part_name;
  	public String	score_instruments;
  	public String	midi_instruments;	//	channel1|name1~channel2|name2
  	public XMLpart()
  	{	ID = "";
  		part_name = "";
  		score_instruments = "";
  		midi_instruments = "";
  	}
  };
  
  /**
   * voiceDef
   * MusicString voice can be a combination of part and voice
   */
  
  class voiceDef
  {
  	int	part;
  	int voice;
  }
  
  /**
   * Parses a MusicXML file, and fires events for <code>ParserListener</code> interfaces
   * when tokens are interpreted. The <code>ParserListener</code> does intelligent things
   * with the resulting events, such as create music, draw sheet music, or
   * transform the data.
   *
   * As of Version 3.0, the Parser supports turning MIDI Sequences into JFugue Patterns with the parse(Sequence)
   * method.  In this case, the ParserListeners established by a ParserBuilder use the parsed
   * events to construct the Pattern string.
   *
   * MusicXmlParser.parse can be called with a file name, File, InputStream, or Reader
   * 
   * @author E.Philip Sobolik
   *
   */
  public final class MusicXmlParser extends Parser
  {	private Map<String, Object> dictionaryMap;
  	private Builder xomBuilder;
  	private Document xomDoc;
  	private String[] volumes = {"pppppp", "ppppp", "pppp", "ppp", "pp", "p", "mp",
  								"mf", "f", "ff", "fff", "ffff", "fffff", "ffffff"};
  	//	note difference between maxVolume and minVolume should be divisible by 13
  	private byte	minVelocity = 10;
  	private byte	maxVelocity = 127;
  	private byte	curVelocity = Note.DEFAULT_VELOCITY;
  	private byte	beats;		//	beats per measure
  	private byte	divisions;	//	divisions per beat
  	private int		curVoice;	//	current voice
  	private int		nextVoice;	//	next available voice # for a new voice
  	private voiceDef[]	voices;
  //	private	double	totalMeasurePct;
  //	private double	lastNoteInMeasureDuration;	//	adjusted duration of the
  												//	last note in the measure
  	
  	public MusicXmlParser()
  	{	xomBuilder = new Builder();
  		dictionaryMap = new HashMap<String, Object>();
  		JFugueDefinitions.populateDictionary(dictionaryMap);
  		beats = 1;
  		divisions = 1;
  		curVoice = -1;
  		nextVoice = 0;
  		voices = new voiceDef[15];
  //		totalMeasurePct = 0.;
  //		lastNoteInMeasureDuration = 0.f;
  	}
  
      public void parse(String musicXmlString)
      {
          try 
          { xomDoc = xomBuilder.build(musicXmlString, (String)null);  // URI is null
          } 
          catch (ValidityException e)
          {   e.printStackTrace();
          }
          catch (ParsingException e)
          {   e.printStackTrace();
          }
          catch (IOException e)
          {   e.printStackTrace();
          }
  
          parse();
      }
  
  	public void parse(File fileXMLin)
  	{	try
  		{	xomDoc = xomBuilder.build(fileXMLin);
  		}
  		catch (ValidityException e)
  		{	e.printStackTrace();
  		}
  		catch (ParsingException e)
  		{	e.printStackTrace();
  		}
  		catch (IOException e)
  		{	e.printStackTrace();
  		}
  
  		parse();
  	}
  	
  	public void parse(FileInputStream fisXMLin)
  	{	try
  		{	xomDoc = xomBuilder.build(fisXMLin);
  		}
  		catch (ValidityException e)
  		{	e.printStackTrace();
  		}
  		catch (ParsingException e)
  		{	e.printStackTrace();
  		}
  		catch (IOException e)
  		{	e.printStackTrace();
  		}
  
  		parse();
  	}
  	
  	public void parse(Reader rXMLin)
  	{	try
  		{	xomDoc = xomBuilder.build(rXMLin);
  		}
  		catch (ValidityException e)
  		{	e.printStackTrace();
  		}
  		catch (ParsingException e)
  		{	e.printStackTrace();
  		}
  		catch (IOException e)
  		{	e.printStackTrace();
  		}
  
  		parse();
  	}
  	
      /////////////////////////////////////////////////////////////////////////
      // Tempo methods
      //
  
      /** The default value for the Tempo. */
      private int tempo = 120;
  
      /**
       * Sets the tempo for the current song.  Tempo is measured in "pulses per quarter".
       * The parser uses this value to convert note durations, which are relative values and
       * not directly related to time measurements, into actual times.  For example, a whole
       * note has the same duration as four quarter notes, but neither a whole note nor a
       * quarter note equates to any real-life time delay until it's multplied by the tempo.
       *
       * The default value for Tempo is 120 pulses per quarter.
       *
       * @param tempo the tempo for the current song, in pulses per quarter.
       */
      protected void setTempo(int tempo)
      {	this.tempo = tempo;
      }
  
      /**
       * Returns the tempo for the current song.
       */
      protected int getTempo()
      {	return this.tempo;
      }
  
      //
      // End Tempo methods
      /////////////////////////////////////////////////////////////////////////
  
      /**
       * Parses a MusicXML file and fires events to subscribed <code>ParserListener</code>
       * interfaces.  As the file is parsed, events are sent
       * to <code>ParserLisener</code> interfaces, which are responsible for doing
       * something interesting with the music data, such as playing the music,
       * displaying it as sheet music, or transforming the pattern.
       *
       * the input is a XOM Document, which has been built previously
       * @throws Exception if there is an error parsing the pattern
       */
  	
  	public void parse() throws JFugueException
  	{	DocType docType = xomDoc.getDocType();
  		Element	root = xomDoc.getRootElement();
  	
  		if (docType.getRootElementName().compareToIgnoreCase("score-partwise") == 0)
  		{	Element partlist = root.getFirstChildElement("part-list");
  			Elements parts = partlist.getChildElements();
  			XMLpart[] partHeaders = new XMLpart[parts.size()];
  			for (int p = 0; p < parts.size(); ++p)
  			{	partHeaders[p] = new XMLpart();
  				parsePartHeader(parts.get(p), partHeaders[p]);
  			}
  			parts = root.getChildElements("part");
  			for (int p = 0; p < parts.size(); ++p)
  			{	parsePart(p, parts.get(p), partHeaders);
  			}
  		}
  	}
  	/**
  	 * Parses a <code>part</code> element in the <code>part-list</code> section
  	 * @param part is the <code>part</code> element
  	 * @param partHeader is the array of <code>XMLpart</code> classes that stores
  	 * the <code>part-list</code> elements
  	 */
  	private void parsePartHeader(Element part, XMLpart partHeader)
  	{	//	ID
  		Attribute ID = part.getAttribute("id");
  		//	may be changed by midi-instrument below
  		partHeader.ID = ID.getValue();
  		//	part-name
  		Element partName = part.getFirstChildElement("part-name");
  		partHeader.part_name = partName.getValue();
  		//	may or may not have 1 or more score-instrument and 
  		//	midi-instrument elements
  		//	score-instruments
  		int x;
  		Elements scoreInsts = part.getChildElements("score-instrument");
  		for (x = 0; x < scoreInsts.size(); ++x )
  		{	partHeader.score_instruments += scoreInsts.get(x).getValue();
  			if (x < scoreInsts.size()-1)
  				partHeader.score_instruments += "~";
  		}
  		//	midi-instruments
  		Elements midiInsts = part.getChildElements("midi-instrument");
  		for (x = 0; x < midiInsts.size(); ++x )
  		{	Element midi_instrument = midiInsts.get(x);
  			Element midi_channel = midi_instrument.getFirstChildElement("midi-channel");
  			String midiChannel = (midi_channel == null) ? "" : midi_channel.getValue();
  			if (midiChannel.length() > 0)
  			{	partHeader.midi_instruments += midiChannel;
  				partHeader.midi_instruments += "|";
  			}
  			Element midi_inst = midi_instrument.getFirstChildElement("midi-name");
  			String midiInst = (midi_inst == null) ? "" : midi_inst.getValue();
  			if (midiInst.length() < 1)
  			{	Element midi_bank = midi_instrument.getFirstChildElement("midi-bank"); 
  				midiInst = (midi_bank == null) ? "" : midi_bank.getValue();
  				if (midiInst.length() < 1)
  				{	Element midi_program = midi_instrument.getFirstChildElement("program");
  					midiInst = (midi_program == null) ? "" : midi_program.getValue();
  				}
  			}
  			partHeader.midi_instruments += midiInst;
  			if (x < midiInsts.size()-1)
  				partHeader.midi_instruments += "~";
  		}
  	}
  	/**
  	 * Parses a <code>part</code> and fires all the appropriate note events
  	 * @param part is the entire <code>part</part>
  	 * @param partHeaders is the array of XMLpart classes that contains
  	 * instrument info for the <code>part</code>s
  	 */
  	private void parsePart(int p, Element part, XMLpart[] partHeaders)
  	{	for (int x = 0; x < partHeaders.length; ++x)
  		{	if (part.getAttribute("id").getValue().equals(partHeaders[x].ID))
  			{	if (partHeaders[x].midi_instruments.length() < 1)
  				{	parseVoice(p, x);
  					parsePartElementInstruments(p, partHeaders[x].part_name);
  			
  				}
  				else
  				{	
  					parsePartElementInstruments(p, partHeaders[x].midi_instruments);
  				
  				}
  			}
  		}
  		Elements measures = part.getChildElements("measure");
  		for (int m = 0; m < measures.size(); ++m)
  		{	Element measure = measures.get(m);
  			Element attributes = measure.getFirstChildElement("attributes");
  
  			if (attributes != null)
  			{	//	default key = Cmaj
  				byte key = 0, scale = 0;	//	scale 0 = minor, 1 = major
  				Element attr = attributes.getFirstChildElement("key");
  				if (attr != null)
  				{	Element eKey = attr.getFirstChildElement("fifths");
  					if (eKey != null)
  						key = Byte.parseByte(eKey.getValue());
  