/*
 * JFugue - API for Music Programming
 * Copyright (C) 2003-2007  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.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.IOException;
import java.io.LineNumberReader;

import nu.xom.Attribute;
import nu.xom.DocType;
import nu.xom.Document;
import nu.xom.Element;
import nu.xom.Elements;
import nu.xom.Serializer;

/**
 * This class is used to build a MusicXML file) given a 
 * MIDI Sequence, Music String, etc. 
 *
 *@author E.Philip Sobolik
 */
public class MusicXmlRenderer implements ParserListener
{	private Element	root;			//	top-level node of entire MusicXML Document
	private Element elCurMeasure;	//	notes, etc. are added to this measure
	private Element elPartList;		//	may need to add score-parts to this
	private Element elCurScorePart;	//	may need to add instruments to this
	private Element elCurPart;		//	current 'voice' add measures to this
	private static final int MUSICXMLDIVISIONS = 4;	//	4 divisions per quarter note
	private static final double WHOLE = 1024.0;
	private static final double QUARTER = 256.0;
    
    public MusicXmlRenderer()
    {	root = new Element("score-partwise");
    
    	Element elID = new Element("identification");
    	Element elCreator = new Element("creator");
    	elCreator.addAttribute(new Attribute("type", "software"));
    	elCreator.appendChild("JFugue MusicXMLRenderer");
    	elID.appendChild(elCreator);
    	root.appendChild(elID);
    
    	//	add an empty score-part list here (before any parts are added)
    	//	score-parts are added to this as they are generated
    	elPartList = new Element("part-list");
    	root.appendChild(elPartList);
    }	//	MusicXmlRenderer

    /**
     * creates the internal <code>Document</code> with the top-level 
     * <code>Element</code> and then creates the MusicXML file (as a 
     * string) from the internal <code>Document</code>
     * @return the completed MusicXML file as a String
     */
    public String getMusicXMLString()
    {	Document xomDoc = getMusicXMLDoc();
    	return xomDoc.toXML();
    }
    
    /**
     * creates the internal <code>Document</code> with the top-level 
     * <code>Element</code>. 
     * @return the completed MusicXML file as a <code>Document</code>
     */
    public Document getMusicXMLDoc()
    {   finishCurrentVoice();

    	//	remove empty measures
		Elements elDocParts = root.getChildElements("part");
		for (int xP = 0; xP < elDocParts.size(); ++xP)
		{	Element elDocPart = elDocParts.get(xP);
			Elements elPartMeasures = elDocPart.getChildElements("measure");
			for (int xM = 0; xM < elPartMeasures.size(); ++xM)
				if (elPartMeasures.get(xM).getChildCount() < 1)
					elDocPart.removeChild(xM);
		}
		//	create the Document
		Document xomDoc = new Document(root);
		DocType docType = new DocType("score-partwise",
						"-//Recordare//DTD MusicXML 1.1 Partwise//EN",
						"http://www.musicxml.org/dtds/partwise.dtd");
		xomDoc.insertChild(docType, 0);
		return xomDoc;
    }	//	GetMusicXMLDoc

    public void doFirstMeasure(boolean bAddDefaults)
    {
    	if (elCurPart == null)
	    	newVoice(new Voice((byte)0));
    	if (elCurMeasure == null)
    	{	elCurMeasure = new Element("measure");
			elCurMeasure.addAttribute(new Attribute("number", Integer.toString(1)));
    		
    		//	assemble attributes element
    		Element elAttributes = new Element("attributes");
    		if (bAddDefaults)
    		{	//	divisions - 4 per beat
	    		Element elDivisions = new Element("divisions");
	    		elDivisions.appendChild(Integer.toString(MUSICXMLDIVISIONS));
	    		elAttributes.appendChild(elDivisions);
	    		//	beats - 1 beat per measure
	    		Element elTime = new Element("time");
	    		Element elBeats = new Element("beats");
	    		elBeats.appendChild(Integer.toString(4));
	    		elTime.appendChild(elBeats);
	    		Element elBeatType = new Element("beat-type");
	    		elBeatType.appendChild(Integer.toString(4));
	    		elTime.appendChild(elBeatType);
	    		elAttributes.appendChild(elTime);
    		}
    		if (bAddDefaults)
    		{	//	Clef - assumed to be treble clef
	    		Element elClef = new Element("clef");
	    		Element elSign = new Element("sign");
	    		elSign.appendChild("G");
	    		Element elLine = new Element("line");
	    		elLine.appendChild("2");
	    		elClef.appendChild(elSign);
	    		elClef.appendChild(elLine);
	    		elAttributes.appendChild(elClef);
    		}
    		//	add the attributes to the measure
    		if (elAttributes.getChildCount() > 0)
    			elCurMeasure.appendChild(elAttributes);
    		
       		//	key signature
    		if (bAddDefaults)
    			doKeySig(new KeySignature((byte)0, (byte)0));	//	C major
    		if (bAddDefaults)
    			doTempo(new Tempo(120));	//	120 BMP default
     	}
    }	//	doFirstMeasure
    
    public void voiceEvent(Voice voice)
    {	
    	String sReqVoice = voice.getMusicString();
    	String sCurPartID = (elCurPart == null)
    							? null
    							: elCurPart.getAttribute("id").getValue();
    	
    	//	if current voice is the same as the requested one, do nothing
    	if (sCurPartID != null)
    		if (sReqVoice.compareTo(sCurPartID) == 0)
    			return;
    	
    	//	check if the requested voice already exists
    	boolean bNewVoiceExists = false;
    	Elements elParts = root.getChildElements("part");
    	Element elExistingNewPart = null;
    	for (int x = 0; x < elParts.size(); ++x)
    	{	Element elP = elParts.get(x);
    		String sPID = elP.getAttribute("id").getValue();
    		
    		if (sPID.compareTo(sReqVoice) == 0)
    		{	bNewVoiceExists = true;
    			elExistingNewPart = elP;
    		}
    	}
    	
    	finishCurrentVoice();
    
    	//	start the new part
    	//	if the new part exists, set the working part to the existing one
    	//	otherwise, start a new part
    	if (bNewVoiceExists)
    		elCurPart = elExistingNewPart;
    	else
    		newVoice(voice);
    	
    	//	start the first/next measure of the working part
    	//	note:  doesn't start a new measure if there
    	//			aren't any notes in the current measure
    	newMeasure();
    }	//	voiceEvent
    
    private void finishCurrentVoice()
    {
    	String sCurPartID = (elCurPart == null)
								? null
								: elCurPart.getAttribute("id").getValue();
    	boolean bCurVoiceExists = false;
    	Elements elParts = root.getChildElements("part");
    	Element elExistingCurPart = null;

    	for (int x = 0; x < elParts.size(); ++x)
    	{	Element elP = elParts.get(x);
    		String sPID = elP.getAttribute("id").getValue();
    		
    		if (sPID.compareTo(sCurPartID) == 0)
    		{	bCurVoiceExists = true;
    			elExistingCurPart = elP;
    		}
    	}
    	
    	//	finish the current measure
    	if (elCurPart != null)
    	{	finishCurrentMeasure();
	    	if (bCurVoiceExists == true)
				root.replaceChild(elExistingCurPart, elCurPart);
	    	else
    			root.appendChild(elCurPart);
    	}
    }	//	finishCurrentVoice

    private void newVoice(Voice voice)
    {//	add a part to the part list
    	elCurScorePart = new Element("score-part");
    	Attribute atPart = new Attribute("id", voice.getMusicString());
    	elCurScorePart.addAttribute(atPart);
    	//	empty part name - Finale ignores it and Sibelius gets it wrong
    	elCurScorePart.appendChild(new Element("part-name"));
    	Element elPL = root.getFirstChildElement("part-list");
    	elPL.appendChild(elCurScorePart);
    	
    	//	start a new part - note that the score-part and the part have the 
    	//	same id attribute
    	elCurPart = new Element("part");
    	Attribute atPart2 = new Attribute(atPart);
    	elCurPart.addAttribute(atPart2);
    	elCurMeasure = null;
    	doFirstMeasure(true);
    }	//	newVoice
    
    public void instrumentEvent(Instrument instrument)
    {
    	Element elInstrName = new Element("instrument-name");
    	elInstrName.appendChild(instrument.getInstrumentName());

    	Element elInstrument = new Element("score-instrument");
    	elInstrument.addAttribute(new Attribute("id", Byte.toString(instrument.getInstrument())));
    	elInstrument.appendChild(elInstrName);
    }
    
    public void tempoEvent(Tempo tempo)
    {	doTempo(tempo);
    }
    
    private void doTempo(Tempo tempo)
    {	Element elDirection = new Element("direction");
		elDirection.addAttribute(new Attribute("placement", "above"));
		Element elDirectionType = new Element("direction-type");
		Element elMetronome = new Element("metronome");
		Element elBeatUnit = new Element("beat-unit");
		//	assume quarter note beat unit
		elBeatUnit.appendChild("quarter");
		Element elPerMinute = new Element("per-minute");
		Integer iBPM = new Float(PPMtoBPM(tempo.getTempo())).intValue();
		elPerMinute.appendChild(iBPM.toString());
		//	assemble all the pieces
		elMetronome.appendChild(elBeatUnit);
		elMetronome.appendChild(elPerMinute);
		elDirectionType.appendChild(elMetronome);
		elDirection.appendChild(elDirectionType);
		//	attach the whole thing to the current measure
		if (elCurMeasure == null)
			doFirstMeasure(true);
		elCurMeasure.appendChild(elDirection);
    }	//	doTempo

    public void layerEvent(Layer layer)
    {
//    	        pattern.add(layer.getMusicString());
    }

    public void timeEvent(Time time)
    {
 //       pattern.add(time.getMusicString());
    }

    public void keySignatureEvent(KeySignature keySig)
    {	doKeySig(keySig);
    }
    
    private void doKeySig(KeySignature keySig)
    {	Element elKey = new Element("key");
    	//	build the key element
    	Element elFifths = new Element("fifths");
    	elFifths.appendChild(Byte.toString(keySig.getKeySig()));
    	elKey.appendChild(elFifths);
    	Element elMode = new Element("mode");
    	elMode.appendChild((keySig.getScale() == 1 ? "minor" : "major"));
    	elKey.appendChild(elMode);
    	//	add the key to the attributes element of the current measure
		if (elCurMeasure == null)
			doFirstMeasure(true);
    	Element elAttributes = elCurMeasure.getFirstChildElement("attributes");
    	boolean bNewAttributes = (elAttributes == null);
    	if (bNewAttributes == true)
    		elAttributes = new Element("attributes");
    	elAttributes.appendChild(elKey);
    	if (bNewAttributes == true)
    		elCurMeasure.appendChild(elAttributes);
    }	//	doKeySig

    public void measureEvent(Measure measure)
    {	// first measure stuff
    	if (elCurMeasure == null)
    		doFirstMeasure(false);
    	else
    	{  	//	add the current measure to the part
    		finishCurrentMeasure();
    		newMeasure();
    	}
    }	//	measureEvent

    private void finishCurrentMeasure()
    {
    	//	if the part exists, replace it with the new one
    	//	otherwise, add the new one
    	{	if (elCurMeasure.getParent() == null)
    			elCurPart.appendChild(elCurMeasure);
    		else
    		{	int sCurMNum = Integer.parseInt(elCurMeasure.getAttributeValue("number"));
    			Elements elMeasures = elCurPart.getChildElements("measure");
    			for (int x = 0; x < elMeasures.size(); ++x)
    			{	Element elM = elMeasures.get(x);
    				int sMNum = Integer.parseInt(elM.getAttributeValue("number"));
    				if (sMNum == sCurMNum)
    					elCurPart.replaceChild(elM, elCurMeasure);
    			}
    		}
    	}
    }	//	finishCurrentMeasure
    
    private void newMeasure()
    {	Integer nextNumber = 1;
    	boolean bNewMeasure = true;
    	// 	if there aren't any notes in the measure, 
		//	continue to use the current measure
    	Elements elMeasures = elCurPart.getChildElements("measure");
    	Element elLastMeasure = null;
		if (elMeasures.size() > 0)
		{	elLastMeasure = elMeasures.get(elMeasures.size()-1);
			//	get the new measure number from the last one
			Attribute elNumber = elLastMeasure.getAttribute("number");
			if (elLastMeasure.getChildElements("note").size() < 1)
				bNewMeasure = false;
			else
				nextNumber = Integer.parseInt(elNumber.getValue()) + 1;
    	}
		else
		{	//	first measure may not have been added yet
			bNewMeasure = (elCurMeasure.getChildElements("note").size() > 0);
		}
    	if (bNewMeasure)
    	{	//	start the new measure
			elCurMeasure = new Element("measure");
			
			//	add the new measure number
			elCurMeasure.addAttribute(new Attribute("number", 
										Integer.toString(nextNumber)));
    	}
    	//	else continue using the same elCurMeasure
    }	//	newMeasure
    
    public void controllerEvent(Controller controller)
    {
//    	        pattern.add(controller.getMusicString());
    }

    public void channelPressureEvent(ChannelPressure channelPressure)
    {
//    	        pattern.add(channelPressure.getMusicString());
    }

    public void polyphonicPressureEvent(PolyphonicPressure polyphonicPressure)
    {
//    	        pattern.add(polyphonicPressure.getMusicString());
    }

    public void pitchBendEvent(PitchBend pitchBend)
    {
//    	        pattern.add(pitchBend.getMusicString());
    }

    public void noteEvent(Note note) 
    {	doNote(note, false);
    }
    
    private void doNote(Note note, boolean bChord)
    {
    	Element elNote = new Element("note");
    	
    	if (bChord)
    		elNote.appendChild(new Element("chord"));
    	
    	//	rest
    	if (note.isRest())
    	{	Element elRest = new Element("rest");
    		elNote.appendChild(elRest);
    	}
    	else
    	{	//	pitch
    		Element elPitch = new Element("pitch");
    		//	step - note letter name without sharp or flat
    		Element elStep = new Element("step");
    		String sPitch = Note.NOTES[note.getValue() % 12];
    		int iAlter = 0;
    		if (sPitch.length() > 1)
    		{	iAlter = sPitch.contains("#") ? 1 : -1;
    			sPitch = sPitch.substring(0,1);
    		}
    		elStep.appendChild(sPitch);
    		elPitch.appendChild(elStep);
    		//	alter - -1 = flat, 1 = sharp
    		if (iAlter != 0)
    		{	Element elAlter = new Element("alter");
    			elAlter.appendChild(Integer.toString(iAlter));
    			elPitch.appendChild(elAlter);
    		}
    		//	octave
    		Element elOctave = new Element("octave");
    		elOctave.appendChild(Integer.toString(note.getValue() / 12));
    		elPitch.appendChild(elOctave);
    		
    		elNote.appendChild(elPitch);
    	}
    	//	duration
    	Element elDuration = new Element("duration");
    	double dDuration = note.getDecimalDuration();
    	int iXMLDuration = (int)
    		((dDuration * WHOLE * MUSICXMLDIVISIONS) / QUARTER); 
    	elDuration.appendChild(Integer.toString(iXMLDuration));
    	elNote.appendChild(elDuration);
    	//	tie start/stop
    	boolean bDoNotation = false;
    	if (note.isStartOfTie())
    	{	Element elTie = new Element("tie");
    		Attribute atType = new Attribute("type", "start");
    		elTie.addAttribute(atType);
    		elNote.appendChild(elTie);
    		bDoNotation = true;
    	}
    	else if (note.isEndOfTie())
    	{	Element elTie = new Element("tie");
			Attribute atType = new Attribute("type", "stop");
			elTie.addAttribute(atType);
			elNote.appendChild(elTie);
    		bDoNotation = true;
		}
    	//	duration type
    	String sDuration;
    	boolean bDotted = false;
    	if (dDuration == 1.0)
    		sDuration = "whole";
        else if (dDuration == 0.75)
        {	sDuration = "half";
        	bDotted = true;
        }
        else if (dDuration == 0.5)
        	sDuration = "half";
        else if (dDuration == 0.375)
        {	sDuration = "quarter";
        	bDotted = true;
        }
        else if (dDuration == 0.25)
        	sDuration = "quarter";
        else if (dDuration == 0.1875)
        {	sDuration = "eighth";
        	bDotted = true;
        }
        else if (dDuration == 0.125)
        	sDuration = "eighth";
        else if (dDuration == 0.09375)
        {	sDuration = "16th";
        	bDotted = true;
        }
        else if (dDuration == 0.0625)
        	sDuration = "16th";
        else if (dDuration == 0.046875)
        {	sDuration = "32nd";
        	bDotted = true;
        }
        else if (dDuration == 0.03125)
        	sDuration = "32nd";
        else if (dDuration == 0.0234375)
        {	sDuration = "64th";
        	bDotted = true;
        }
        else if (dDuration == 0.015625)
        	sDuration = "64th";
        else if (dDuration == 0.01171875)
        {	sDuration = "128th";
        	bDotted = true;
        }
        else if (dDuration == 0.0078125)
        	sDuration = "128th";
        else sDuration = "/" + Double.toString(dDuration);
    	Element elType = new Element("type");
    	elType.appendChild(sDuration);
    	elNote.appendChild(elType);
    	//	dotted
    	if (bDotted)
    	{	Element elDot = new Element("dot");
    		elNote.appendChild(elDot);
    	}
    	//	notations
    	if (bDoNotation)
    	{	Element elNotations = new Element("notations");
    		if (note.isStartOfTie())
    		{	Element elTied = new Element("tied");
    			Attribute atStart = new Attribute("type", "start");
    			elTied.addAttribute(atStart);
    			elNotations.appendChild(elTied);
    		}
    		else if (note.isEndOfTie())
    		{	Element elTied = new Element("tied");
				Attribute atStart = new Attribute("type", "stop");
				elTied.addAttribute(atStart);
				elNotations.appendChild(elTied);
			}
    		elNote.appendChild(elNotations);
    	}
    	if (elCurMeasure == null)
    		doFirstMeasure(true);
    	elCurMeasure.appendChild(elNote);
    }	//	doNote

    public void sequentialNoteEvent(Note note)
    {	
    }

    public void parallelNoteEvent(Note note) 
    {  	doNote(note, true);
    }
    
    /**
     * converts pulses per minute (PPM) to beats per minute (BPM) assuming 240 pulses per second
     * In MusicXML, BPM can be fractional, so <code>PPMtoBPM</code> returns a float
     * @param ppm
     * @return
     */
    public static float PPMtoBPM(int ppm)
    {	//	convert PPM to BPM assuming 240 pulses per second
		return( new Float((60.f * 240.f) / ppm) );
    }


    /**
	 ** Used for diagnostic purposes.  main() makes calls to test the Pattern-to-MusicXML
	 ** renderer.    
	 ** @param args not used
	 **/
	public static void main(String[] args)
	{	
//		FrereJacquesRound();
//		Entertainer();
		metronome(120);
	}
	
	private static void FrereJacquesRound()
	{	File fileXML = new File("C:\\Documents and Settings\\Philip Sobolik\\My Documents\\"
								+ "Visual Studio 2005\\WebSites\\NYSSMA3\\"
								+ "FrereJacquesRound.xml");
	    try {
	    	FileOutputStream fosXML = new FileOutputStream(fileXML, false);
	    	

			//	set up the source MusicXML file (parser)
	    	MusicXmlRenderer MusicXmlOut = new MusicXmlRenderer();
//		    MusicXmlParser.setTracing(Parser.TRACING_ON);
		
		    //	set up the target MusicString (renderer)
		    MusicStringParser MusicStringIn = new MusicStringParser();
		    
		    //	attach the render to the parser
		    MusicStringIn.addParserListener(MusicXmlOut);
		    
		    // "Frere Jacques"
		    Pattern pattern1 = new Pattern("C5q D5q E5q C5q |");

		    // "Dormez-vous?"
		    Pattern pattern2 = new Pattern("E5q F5q G5h |");

		    // "Sonnez les matines"
		    Pattern pattern3 = new Pattern("G5i A5i G5i F5i E5q C5q |");

		    // "Ding ding dong"
		    Pattern pattern4 = new Pattern("C5q G4q C5h |");

		    // Put it all together
		    Pattern song = new Pattern();
		    song.add(pattern1, 2); // Adds 'pattern1' to 'song' twice
		    song.add(pattern2, 2); // Adds 'pattern2' to 'song' twice
		    song.add(pattern3, 2); // Adds 'pattern3' to 'song' twice
		    song.add(pattern4, 2); // Adds 'pattern4' to 'song' twice
//		    MusicStringIn.parse(new Pattern("T160 I[Cello] "+
//		    	      "G3q G3q G3q Eb3q Bb3i G3q Eb3q Bb3i G3h"));
//		    MusicStringIn.parse(song);	

		    // "Frere Jacques" as a round
		    Pattern doubleMeasureRest = new Pattern("Rw | Rw |");

		    // Create the first voice
		    Pattern round1 = new Pattern("V0");
		    round1.add(song);
		    round1.add(doubleMeasureRest, 2);

		    // Create the second voice
		    Pattern round2 = new Pattern("V1");
		    round2.add(doubleMeasureRest);
		    round2.add(song);
		    round2.add(doubleMeasureRest);

		    // Create the third voice
		    Pattern round3 = new Pattern("V2");
		    round3.add(doubleMeasureRest, 2);
		    round3.add(song);

		    // Put the voices together
		    Pattern roundSong = new Pattern();
		    roundSong.add(round1);
		    roundSong.add(round2);
		    roundSong.add(round3);
		    
		    Player player = new Player();
		    player.play(roundSong);
		    System.out.println(roundSong.toString());
		    
		    //	start the parser
		    MusicStringIn.parse(roundSong);	
		    
		    //	write the MusicXML file unformatted
/*		    String p = MusicXmlOut.getMusicXMLString();
			fosXML.write(p.getBytes());

		    //	display the MusicXML file as an unformatted string
	        System.out.println(p);
			System.out.print('\n');
*/
		    //	write the MusicXML file formatted
		    Serializer ser = new Serializer(fosXML, "UTF-8");
		    ser.setIndent(4);
		    ser.write(MusicXmlOut.getMusicXMLDoc());
		    
		    fosXML.flush();
			fosXML.close();
	        
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
	
	private static void Entertainer()
	{	File fileSrc = new File("F:\\WIN\\JFugue\\org\\jfugue\\extras\\"
								+ "entertainer.jfugue");
		File fileXML = new File("C:\\Documents and Settings\\Philip Sobolik\\My Documents\\"
				+ "Visual Studio 2005\\WebSites\\NYSSMA3\\"
				+ "Entertainer.xml");
	    try {
	    	FileOutputStream fosXML = new FileOutputStream(fileXML, false);

			//	set up the source MusicXML file (parser)
	    	MusicXmlRenderer MusicXmlOut = new MusicXmlRenderer();
//		    MusicXmlParser.setTracing(Parser.TRACING_ON);
		
		    //	set up the target MusicString (renderer)
		    MusicStringParser MusicStringIn = new MusicStringParser();
		    
		    //	attach the render to the parser
		    MusicStringIn.addParserListener(MusicXmlOut);
		    
	    	//	read the song from the file
		    BufferedReader brSrc = new BufferedReader(new FileReader(fileSrc));
	    	LineNumberReader lnrSrc = new LineNumberReader(brSrc);
	    	
	    	Pattern song = new Pattern();
	    	for (String s = lnrSrc.readLine(); s != null; s = lnrSrc.readLine())
	    	{	if (s.length() > 0)
	    			if (s.charAt(0) != '#')
	    				song.add(s);
	    	}
	    	lnrSrc.close();
	    	
	    	//	play the song
//		    Player player = new Player();
//		    player.play(song);
		    System.out.println(song.toString());
		    
		    //	start the parser
		    MusicStringIn.parse(song);	
		    
		    //	write the MusicXML file unformatted
/*		    String p = MusicXmlOut.getMusicXMLString();
			fosXML.write(p.getBytes());

		    //	display the MusicXML file as an unformatted string
	        System.out.println(p);
			System.out.print('\n');
*/
		    //	write the MusicXML file formatted
		    Serializer ser = new Serializer(fosXML, "UTF-8");
		    ser.setIndent(4);
		    ser.write(MusicXmlOut.getMusicXMLDoc());
		    
		    fosXML.flush();
			fosXML.close();
	        
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
				e.printStackTrace();
		}
	}

	private static void metronome(int bpm)
	{
		Pattern p = new Pattern("T" + Integer.toString((60 * 240) / bpm));
		p.add("A4q", bpm);	//	should play for 1 minute
		Player pl = new Player();
		pl.play(p);
	}
	
}
