Package org.jfugue

Source Code of org.jfugue.MusicXmlRenderer

/*
* 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.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);
  }
 
}
TOP

Related Classes of org.jfugue.MusicXmlRenderer

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.