Logo Search packages:      
Sourcecode: rosegarden version File versions

musicxmlio.cpp

// -*- c-basic-offset: 4 -*-

/*
    Rosegarden-4 v0.1
    A sequencer and musical notation editor.

    This program is Copyright 2000-2006
        Guillaume Laurent   <glaurent@telegraph-road.org>,
        Chris Cannam        <cannam@all-day-breakfast.com>,
        Richard Bown        <bownie@bownie.com>

    This file is Copyright 2002
        Hans Kieserman      <hkieserman@mail.com>
    with heavy lifting from csoundio as it was on 13/5/2002.

    The moral right of the authors to claim authorship of this work
    has been asserted.

    This program is free software; you can redistribute it and/or
    modify it under the terms of the GNU General Public License as
    published by the Free Software Foundation; either version 2 of the
    License, or (at your option) any later version.  See the file
    COPYING included with this distribution for more information.
*/

#include "musicxmlio.h"

#include <iostream>
#include <fstream>
#include <string>

#include "Composition.h"
#include "CompositionTimeSliceAdapter.h"
#include "BaseProperties.h"
#include "SegmentNotationHelper.h"
#include "Instrument.h"

#include "rgapplication.h"

#include "notationstrings.h"
#include "rosedebug.h"

using Rosegarden::Bool;
using Rosegarden::Clef;
using Rosegarden::Composition;
using Rosegarden::Indication;
using Rosegarden::Instrument;
using Rosegarden::Int;
using Rosegarden::Key;
using Rosegarden::Note;
using Rosegarden::Event;
using Rosegarden::Segment;
using Rosegarden::SegmentNotationHelper;
using Rosegarden::String;
using Rosegarden::timeT;
using Rosegarden::TimeSignature;
using namespace Rosegarden::BaseProperties;

MusicXmlExporter::MusicXmlExporter(QObject *parent,
                                   RosegardenGUIDoc *doc,
                                   std::string fileName) :
    ProgressReporter(parent, "musicXmlExporter"),
    m_doc(doc),
    m_fileName(fileName) {
    // nothing else
}

MusicXmlExporter::~MusicXmlExporter() {
    // nothing
}


void
MusicXmlExporter::writeNote(Event *e, Rosegarden::timeT lastNoteTime,
                      Rosegarden::AccidentalTable &accTable,
                      const Rosegarden::Clef &clef,
                      const Rosegarden::Key &key,
                      std::ofstream &str)
{
    str << "\t\t\t<note>" << std::endl;

    if (e->isa(Note::EventRestType)) {
        str << "\t\t\t\t<rest/>" << std::endl;

    } else {

      if (e->getNotationAbsoluteTime() == lastNoteTime) {
          str << "\t\t\t\t<chord/>" << std::endl;
      } else {
          accTable.update();
      }

      if (e->has(TIED_BACKWARD) &&
          e->get<Bool>(TIED_BACKWARD)) {
          str << "\t\t\t\t<tie type=\"stop\"/>" << std::endl;
      }
      if (e->has(TIED_FORWARD) &&
          e->get<Bool>(TIED_FORWARD)) {
          str << "\t\t\t\t<tie type=\"start\"/>" << std::endl;
      }

        str << "\t\t\t\t<pitch>" << std::endl;

        long p = 0;
        e->get<Int>(PITCH, p);
      Rosegarden::Pitch pitch(p);

        str << "\t\t\t\t\t<step>" << pitch.getNoteName(key) << "</step>" << std::endl;

      Rosegarden::Accidental acc(pitch.getAccidental(key.isSharp()));
      Rosegarden::Accidental displayAcc(pitch.getDisplayAccidental(key));

      bool cautionary = false;
      Rosegarden::Accidental processedDisplayAcc =
          accTable.processDisplayAccidental
          (displayAcc, pitch.getHeightOnStaff(clef, key), cautionary);

      // don't handle cautionary accidentals here:
      if (cautionary) processedDisplayAcc = Rosegarden::Accidentals::NoAccidental;

      if (acc == Rosegarden::Accidentals::DoubleFlat) {
          str << "\t\t\t\t\t<alter>-2</alter>" << std::endl;
      } else if (acc == Rosegarden::Accidentals::Flat) {
          str << "\t\t\t\t\t<alter>-1</alter>" << std::endl;
      } else if (acc == Rosegarden::Accidentals::Sharp) {
          str << "\t\t\t\t\t<alter>1</alter>" << std::endl;
      } else if (acc == Rosegarden::Accidentals::DoubleSharp) {
          str << "\t\t\t\t\t<alter>2</alter>" << std::endl;
      }

      int octave = pitch.getOctave(-1);
      str << "\t\t\t\t\t<octave>" << octave << "</octave>" << std::endl;

        str << "\t\t\t\t</pitch>" << std::endl;

      if (processedDisplayAcc == Rosegarden::Accidentals::DoubleFlat) {
          str << "\t\t\t\t<accidental>flat-flat</accidental>" << std::endl;
      } else if (processedDisplayAcc == Rosegarden::Accidentals::Flat) {
          str << "\t\t\t\t<accidental>flat</accidental>" << std::endl;
      } else if (processedDisplayAcc == Rosegarden::Accidentals::Natural) {
          str << "\t\t\t\t<accidental>natural</accidental>" << std::endl;
      } else if (processedDisplayAcc == Rosegarden::Accidentals::Sharp) {
          str << "\t\t\t\t<accidental>sharp</accidental>" << std::endl;
      } else if (processedDisplayAcc == Rosegarden::Accidentals::DoubleSharp) {
          str << "\t\t\t\t<accidental>double-sharp</accidental>" << std::endl;
      }

      bool haveNotations = false;
      if (e->has(TIED_BACKWARD) &&
          e->get<Bool>(TIED_BACKWARD)) {
          if (!haveNotations) {
            str << "\t\t\t\t<notations>" << std::endl;
            haveNotations = true;
          }
          str << "\t\t\t\t\t<tied type=\"stop\"/>" << std::endl;
      }
      if (e->has(TIED_FORWARD) &&
          e->get<Bool>(TIED_FORWARD)) {
          if (!haveNotations) {
            str << "\t\t\t\t<notations>" << std::endl;
            haveNotations = true;
          }
          str << "\t\t\t\t\t<tied type=\"start\"/>" << std::endl;
      }
      if (haveNotations) {
          str << "\t\t\t\t</notations>" << std::endl;
      }
    }

    // Since there's no way to provide the performance absolute time
    // for a note, there's also no point in providing the performance
    // duration, even though it might in principle be of interest
    str << "\t\t\t\t<duration>" << e->getNotationDuration() << "</duration>" << std::endl;
    
    // Incomplete: will RG ever use this?
    str << "\t\t\t\t<voice>" << "1" << "</voice>" << std::endl;
    Note note = Note::getNearestNote(e->getNotationDuration(), MAX_DOTS);

    static const char *noteNames[] = {
      "64th", "32nd", "16th", "eighth", "quarter", "half", "whole", "breve"
    };

    int noteType = note.getNoteType();
    if (noteType < 0 || noteType >= int(sizeof(noteNames)/sizeof(noteNames[0]))) {
      std::cerr << "WARNING: MusicXmlExporter::writeNote: bad note type "
              << noteType << std::endl;
      noteType = 4;
    }
    
    str << "\t\t\t\t<type>" << noteNames[noteType] << "</type>" << std::endl;
    for (int i = 0; i < note.getDots(); ++i) {
      str << "\t\t\t\t<dot/>" << std::endl;
    }

    // could also do <stem>down</stem> if you wanted
    str << "\t\t\t</note>" << std::endl;
}

void
MusicXmlExporter::writeKey(Event *event, std::ofstream &str) {
    Rosegarden::Key whichKey(*event);
    str << "\t\t\t\t<key>" << std::endl;
    str << "\t\t\t\t<fifths>"
      << (whichKey.isSharp() ? "" : "-")
      << (whichKey.getAccidentalCount()) << "</fifths>" << std::endl;
    str << "\t\t\t\t<mode>";                
    if (whichKey.isMinor()) {
        str << "minor";
    } else {
        str << "major";
    }
    str << "</mode>" << std::endl;
    str << "\t\t\t\t</key>" << std::endl;
}

void
MusicXmlExporter::writeTime(TimeSignature timeSignature, std::ofstream &str) {
    str << "\t\t\t\t<time>" << std::endl;
    str << "\t\t\t\t<beats>" << timeSignature.getNumerator() << "</beats>" << std::endl;
    str << "\t\t\t\t<beat-type>" << timeSignature.getDenominator() << "</beat-type>" << std::endl;
    str << "\t\t\t\t</time>" << std::endl;
}

void
MusicXmlExporter::writeClef(Event *event, std::ofstream &str) {
    str << "\t\t\t\t<clef>" << std::endl;
    std::string whichClef = Clef::Treble;
    event->get<String>(Clef::ClefPropertyName, whichClef);
    if (whichClef == Clef::Treble) {
        str << "\t\t\t\t<sign>G</sign>" << std::endl;
        str << "\t\t\t\t<line>2</line>" << std::endl;
    } else if (whichClef == Clef::Alto) {
        str << "\t\t\t\t<sign>C</sign>" << std::endl;
        str << "\t\t\t\t<line>3</line>" << std::endl;
    } else if (whichClef == Clef::Tenor) {
        str << "\t\t\t\t<sign>C</sign>" << std::endl;
        str << "\t\t\t\t<line>4</line>" << std::endl;
    } else if (whichClef == Clef::Bass) {
        str << "\t\t\t\t<sign>F</sign>" << std::endl;
        str << "\t\t\t\t<line>4</line>" << std::endl;
    }
    str << "\t\t\t\t</clef>" << std::endl;
}

bool
MusicXmlExporter::write() {
    Composition *composition = &m_doc->getComposition();

    std::ofstream str(m_fileName.c_str(), std::ios::out);
    if (!str) {
        std::cerr << "MusicXmlExporter::write() - can't write file " << m_fileName << std::endl;
        return false;
    }

    // XML header information
    str << "<?xml version=\"1.0\"?>" << std::endl;
    str << "<!DOCTYPE score-partwise PUBLIC \"-//Recordare//DTD MusicXML 0.6 Partwise//EN\" \"http://www.musicxml.org/dtds/partwise.dtd\">" << std::endl;
    // MusicXml header information
    str << "<score-partwise>" << std::endl;
    str << "\t<work> <work-title>" << m_fileName <<
        "</work-title></work> " << std::endl;
    // Movement, etc. info goes here
    str << "\t<identification> " << std::endl;
    if (composition->getCopyrightNote() != "") {
        str << "\t\t<rights>"
            << Rosegarden::XmlExportable::encode(composition->getCopyrightNote())
          << "</rights>" << std::endl;
    }
    str << "\t\t<encoding>" << std::endl;
    // Incomplete: Insert date!
    //    str << "\t\t\t<encoding-date>" << << "</encoding-date>" << std::endl;
    str << "\t\t\t<software>Rosegarden-4</software>" << std::endl;
    str << "\t\t</encoding>" << std::endl;
    str << "\t</identification> " << std::endl;

    // MIDI information
    str << "\t<part-list>" << std::endl;
    Composition::trackcontainer& tracks = composition->getTracks();

    int trackNo = 0;
    Rosegarden::timeT lastNoteTime = -1;
    
    for (Composition::trackiterator i = tracks.begin();
         i != tracks.end(); ++i) {
        // Incomplete: What about all the other Midi stuff?
        // Incomplete: (Future) GUI to set labels if they're not already
        Instrument * trackInstrument = (&m_doc->getStudio())->getInstrumentById((*i).second->getInstrument());
        str << "\t\t<score-part id=\"" << (*i).first << "\">" << std::endl;
        str << "\t\t\t<part-name>" << (*i).second->getLabel() << "</part-name>" << std::endl;
        if (trackInstrument) {
            str << "\t\t\t<score-instrument id=\"" << trackInstrument->getName() << "\">" << std::endl;
            str << "\t\t\t\t<instrument-name>" << trackInstrument->getType() << "</instrument-name>" << std::endl;
            str << "\t\t\t</score-instrument>" << std::endl;
            str << "\t\t\t<midi-instrument id=\"" << trackInstrument->getName() << "\">" << std::endl;
            str << "\t\t\t\t<midi-channel>" << ((unsigned int)trackInstrument->getMidiChannel() + 1) << "</midi-channel>" << std::endl;
          if (trackInstrument->sendsProgramChange()) {
            str << "\t\t\t\t<midi-program>" << ((unsigned int)trackInstrument->getProgramChange() + 1) << "</midi-program>" << std::endl;
          }
            str << "\t\t\t</midi-instrument>" << std::endl;
        }
        str << "\t\t</score-part>" << std::endl;

        emit setProgress(int(double(trackNo++)/double(tracks.size()) * 20.0));
        rgapp->refreshGUI(50);

    } // end track iterator
    str << "\t</part-list>" << std::endl;
    
    // Notes!
    // Write out all segments for each Track
    trackNo = 0;

    for (Composition::trackiterator j = tracks.begin();
         j != tracks.end(); ++j) {

      bool startedPart = false;

        // Code courtesy docs/code/iterators.txt
        Rosegarden::CompositionTimeSliceAdapter::TrackSet trackSet;

        // Incomplete: get the track info for each track (i.e. this should
        // be in an iterator loop) into the track set
        trackSet.insert((*j).first);
        Rosegarden::CompositionTimeSliceAdapter adapter(composition, trackSet);

        int oldMeasureNumber = -1;
        bool startedAttributes = false;
      Rosegarden::Key key;
      Rosegarden::Clef clef;
      Rosegarden::AccidentalTable accTable(key, clef);
        TimeSignature prevTimeSignature;

        for (Rosegarden::CompositionTimeSliceAdapter::iterator k = adapter.begin();
             k != adapter.end(); ++k) {

            Event *event = *k;
            timeT absoluteTime = event->getNotationAbsoluteTime();

          if (!startedPart) {
            str << "\t<part id=\"" << (*j).first << "\">" << std::endl;
            startedPart = true;
          }

            // Open a new measure if necessary
            // Incomplete: How does MusicXML handle non-contiguous measures?
            int measureNumber = composition->getBarNumber(absoluteTime);

            if (oldMeasureNumber < 0) {
                str << "\t\t<measure number=\""<< (measureNumber+1) << "\">" << std::endl;
                str << "\t\t\t<attributes>" << std::endl;
                // Divisions is divisions of crotchet (quarter-note) on which all
                // note-lengths are based
                str << "\t\t\t\t<divisions>" << Note(Note::Crotchet).getDuration() << "</divisions>" << std::endl;
                prevTimeSignature = composition->getTimeSignatureAt(composition->getStartMarker());
                writeTime(prevTimeSignature, str);
                startedAttributes = true;

            } else if (measureNumber > oldMeasureNumber) {

                if (startedAttributes) {
                    str << "\t\t\t</attributes>" << std::endl;
                    startedAttributes = false;
                }

            while (measureNumber > oldMeasureNumber) {

                ++oldMeasureNumber;
                str << "\t\t</measure>\n" << std::endl;
                str << "\t\t<measure number=\""<< (oldMeasureNumber + 1) << "\">" << std::endl;
            }

            accTable = Rosegarden::AccidentalTable(key, clef);

            } else if (measureNumber < oldMeasureNumber) {
                // Incomplete: Error!
            }
            oldMeasureNumber = measureNumber;
            
            // process event
            if (event->isa(Rosegarden::Key::EventType)) {

                if (!startedAttributes) {
                    str << "\t\t\t<attributes>" << std::endl;
                    startedAttributes = true;
                }
                writeKey(event, str);
                key = Rosegarden::Key(*event);
            accTable = Rosegarden::AccidentalTable(key, clef);

            } else if (event->isa(Clef::EventType)) {

                if (!startedAttributes) {
                    str << "\t\t\t<attributes>" << std::endl;
                    startedAttributes = true;
                }
                writeClef(event, str);
            clef = Rosegarden::Clef(*event);
            accTable = Rosegarden::AccidentalTable(key, clef);

            } else if (event->isa(Note::EventRestType) ||
                   event->isa(Note::EventType)) {

                // Random TimeSignature events in the middle of nowhere will
                // be ignored, for better or worse
                TimeSignature timeSignature = composition->getTimeSignatureAt(absoluteTime);
                if (timeSignature.getNumerator() != prevTimeSignature.getNumerator() ||
                    timeSignature.getDenominator() != prevTimeSignature.getDenominator()) {
                    if (!startedAttributes) {
                        str << "\t\t\t<attributes>" << std::endl;
                        startedAttributes = true;
                    }
                    writeTime(timeSignature, str);
                prevTimeSignature = timeSignature;
                }
                if (startedAttributes) {
                    str << "\t\t\t</attributes>" << std::endl;
                    startedAttributes = false;
                }

                writeNote(event, lastNoteTime, accTable, clef, key, str);

            if (event->isa(Note::EventType)) {
                lastNoteTime = event->getNotationAbsoluteTime();
            } else if (event->isa(Note::EventRestType)) {
                lastNoteTime = -1;
            }
            }
        }

      if (startedPart) {
            str << "\t\t</measure>" << std::endl;
          str << "\t</part>" << std::endl;
      }

        emit setProgress(20 +
                         int(double(trackNo++)/double(tracks.size()) * 80.0));
        rgapp->refreshGUI(50);
    }

    str << "</score-partwise>" << std::endl;
    str.close();
    return true;
}

Generated by  Doxygen 1.6.0   Back to index