summaryrefslogtreecommitdiffstats
path: root/src/document/io/MusicXmlExporter.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/document/io/MusicXmlExporter.cpp')
-rw-r--r--src/document/io/MusicXmlExporter.cpp555
1 files changed, 555 insertions, 0 deletions
diff --git a/src/document/io/MusicXmlExporter.cpp b/src/document/io/MusicXmlExporter.cpp
new file mode 100644
index 0000000..e1384c6
--- /dev/null
+++ b/src/document/io/MusicXmlExporter.cpp
@@ -0,0 +1,555 @@
+/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
+
+/*
+ Rosegarden
+ A MIDI and audio sequencer and musical notation editor.
+
+ This program is Copyright 2000-2008
+ Guillaume Laurent <[email protected]>,
+ Chris Cannam <[email protected]>,
+ Richard Bown <[email protected]>
+
+ This file is Copyright 2002
+ Hans Kieserman <[email protected]>
+ with heavy lifting from csoundio as it was on 13/5/2002.
+
+ The moral rights of Guillaume Laurent, Chris Cannam, and Richard
+ Bown to claim authorship of this work have been asserted.
+
+ Other copyrights also apply to some parts of this work. Please
+ see the AUTHORS file and individual file headers for details.
+
+ 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 "MusicXmlExporter.h"
+
+#include "base/BaseProperties.h"
+#include "base/Composition.h"
+#include "base/CompositionTimeSliceAdapter.h"
+#include "base/Event.h"
+#include "base/Instrument.h"
+#include "base/NotationTypes.h"
+#include "base/XmlExportable.h"
+#include "document/RosegardenGUIDoc.h"
+#include "gui/application/RosegardenApplication.h"
+#include "gui/general/ProgressReporter.h"
+#include <qobject.h>
+
+namespace Rosegarden
+{
+
+using namespace 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, timeT lastNoteTime,
+ AccidentalTable &accTable,
+ const Clef &clef,
+ const Rosegarden::Key &key,
+ std::ofstream &str)
+{
+ str << "\t\t\t<note>" << std::endl;
+
+ Pitch pitch(64);
+ Accidental acc;
+ Accidental displayAcc;
+ bool cautionary;
+ Accidental processedDisplayAcc;
+
+ if (e->isa(Note::EventRestType)) {
+ str << "\t\t\t\t<rest/>" << std::endl;
+
+ } else {
+
+ // Order of MusicXML elements within a note:
+ // chord
+ // pitch
+ // duration
+ // tie
+ // instrument
+ // voice
+ // type
+ // dot(s)
+ // accidental
+ // time modification
+ // stem
+ // notehead
+ // staff
+ // beam
+ // notations
+ // lyric
+
+ if (e->getNotationAbsoluteTime() == lastNoteTime) {
+ str << "\t\t\t\t<chord/>" << std::endl;
+ } else {
+ accTable.update();
+ }
+
+ str << "\t\t\t\t<pitch>" << std::endl;
+
+ long p = 0;
+ e->get<Int>(PITCH, p);
+ pitch = p;
+
+ str << "\t\t\t\t\t<step>" << pitch.getNoteName(key) << "</step>" << std::endl;
+
+ acc = pitch.getAccidental(key.isSharp());
+ displayAcc = pitch.getDisplayAccidental(key);
+
+ cautionary = false;
+ processedDisplayAcc =
+ accTable.processDisplayAccidental
+ (displayAcc, pitch.getHeightOnStaff(clef, key), cautionary);
+
+ // don't handle cautionary accidentals here:
+ if (cautionary)
+ processedDisplayAcc = Accidentals::NoAccidental;
+
+ if (acc == Accidentals::DoubleFlat) {
+ str << "\t\t\t\t\t<alter>-2</alter>" << std::endl;
+ } else if (acc == Accidentals::Flat) {
+ str << "\t\t\t\t\t<alter>-1</alter>" << std::endl;
+ } else if (acc == Accidentals::Sharp) {
+ str << "\t\t\t\t\t<alter>1</alter>" << std::endl;
+ } else if (acc == 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;
+ }
+
+ // 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;
+
+ if (!e->isa(Note::EventRestType)) {
+
+ 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;
+ }
+
+ // Incomplete: will RG ever use this?
+ str << "\t\t\t\t<voice>" << "1" << "</voice>" << std::endl;
+ }
+
+ Note note = Note::getNearestNote(e->getNotationDuration());
+
+ 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;
+ }
+
+ if (!e->isa(Note::EventRestType)) {
+
+ if (processedDisplayAcc == Accidentals::DoubleFlat) {
+ str << "\t\t\t\t<accidental>flat-flat</accidental>" << std::endl;
+ } else if (processedDisplayAcc == Accidentals::Flat) {
+ str << "\t\t\t\t<accidental>flat</accidental>" << std::endl;
+ } else if (processedDisplayAcc == Accidentals::Natural) {
+ str << "\t\t\t\t<accidental>natural</accidental>" << std::endl;
+ } else if (processedDisplayAcc == Accidentals::Sharp) {
+ str << "\t\t\t\t<accidental>sharp</accidental>" << std::endl;
+ } else if (processedDisplayAcc == 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;
+ }
+ }
+
+ // could also do <stem>down</stem> if you wanted
+ str << "\t\t\t</note>" << std::endl;
+}
+
+void
+MusicXmlExporter::writeKey(Rosegarden::Key whichKey, std::ofstream &str)
+{
+ 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(Clef whichClef, std::ofstream &str)
+{
+ str << "\t\t\t\t<clef>" << std::endl;
+ 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;
+}
+
+std::string
+MusicXmlExporter::numToId(int num)
+{
+ int base = num % 52;
+ char c;
+ if (base < 26) c = 'A' + char(base);
+ else c = 'a' + char(base - 26);
+ std::string s;
+ s += c;
+ while (num / 52 > 0) {
+ s += c;
+ num /= 52;
+ }
+ return s;
+}
+
+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 1.1 Partwise//EN\" \"http://www.musicxml.org/dtds/partwise.dtd\">" << std::endl;
+ // MusicXml header information
+ str << "<score-partwise>" << std::endl;
+ str << "\t<work> <work-title>" << XmlExportable::encode(m_fileName)
+ << "</work-title></work> " << std::endl;
+ // Movement, etc. info goes here
+ str << "\t<identification> " << std::endl;
+ if (composition->getCopyrightNote() != "") {
+ str << "\t\t<rights>"
+ << 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 v" VERSION "</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;
+ 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=\"" << numToId((*i).first) << "\">" << std::endl;
+ str << "\t\t\t<part-name>" << XmlExportable::encode((*i).second->getLabel()) << "</part-name>" << std::endl;
+ if (trackInstrument) {
+/*
+ Removing this stuff for now. It doesn't work, because the ids are
+ are expected to be non-numeric names that refer to elements
+ elsewhere that define the actual instruments. I think.
+
+ 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
+ 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);
+ CompositionTimeSliceAdapter adapter(composition, trackSet);
+
+ int oldMeasureNumber = -1;
+ bool startedAttributes = false;
+ Rosegarden::Key key;
+ Clef clef;
+ AccidentalTable accTable(key, clef);
+ TimeSignature prevTimeSignature;
+
+ bool timeSigPending = false;
+ bool keyPending = false;
+ bool clefPending = false;
+
+ for (CompositionTimeSliceAdapter::iterator k = adapter.begin();
+ k != adapter.end(); ++k) {
+
+ Event *event = *k;
+ timeT absoluteTime = event->getNotationAbsoluteTime();
+
+ if (!startedPart) {
+ str << "\t<part id=\"" << numToId((*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);
+
+ TimeSignature timeSignature = composition->getTimeSignatureAt(absoluteTime);
+
+ if (measureNumber != oldMeasureNumber) {
+
+ if (startedAttributes) {
+
+ // rather bizarrely, MusicXML appears to require
+ // key, time, clef in that order
+
+ if (keyPending) {
+ writeKey(key, str);
+ keyPending = false;
+ }
+ if (timeSigPending) {
+ writeTime(prevTimeSignature, str);
+ timeSigPending = false;
+ }
+ if (clefPending) {
+ writeClef(clef, str);
+ clefPending = false;
+ }
+
+ str << "\t\t\t</attributes>" << std::endl;
+ startedAttributes = false;
+ }
+
+ while (measureNumber > oldMeasureNumber) {
+
+ bool first = (oldMeasureNumber < 0);
+
+ if (!first) {
+ if (startedAttributes) {
+ str << "\t\t\t</attributes>" << std::endl;
+ }
+ str << "\t\t</measure>\n" << std::endl;
+ }
+
+ ++oldMeasureNumber;
+
+ str << "\t\t<measure number=\"" << (oldMeasureNumber + 1) << "\">" << std::endl;
+
+ if (first) {
+ 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;
+ startedAttributes = true;
+ timeSigPending = true;
+ }
+ }
+
+ accTable = AccidentalTable(key, clef);
+ }
+
+ oldMeasureNumber = measureNumber;
+
+ if (timeSignature != prevTimeSignature) {
+ prevTimeSignature = timeSignature;
+ timeSigPending = true;
+ if (!startedAttributes) {
+ str << "\t\t\t<attributes>" << std::endl;
+ startedAttributes = true;
+ }
+ }
+
+ // process event
+ if (event->isa(Rosegarden::Key::EventType)) {
+
+ if (!startedAttributes) {
+ str << "\t\t\t<attributes>" << std::endl;
+ startedAttributes = true;
+ }
+ key = Rosegarden::Key(*event);
+ keyPending = true;
+ accTable = AccidentalTable(key, clef);
+
+ } else if (event->isa(Clef::EventType)) {
+
+ if (!startedAttributes) {
+ str << "\t\t\t<attributes>" << std::endl;
+ startedAttributes = true;
+ }
+ clef = Clef(*event);
+ clefPending = true;
+ accTable = AccidentalTable(key, clef);
+
+ } else if (event->isa(Note::EventRestType) ||
+ event->isa(Note::EventType)) {
+
+ if (startedAttributes) {
+
+ if (keyPending) {
+ writeKey(key, str);
+ keyPending = false;
+ }
+ if (timeSigPending) {
+ writeTime(prevTimeSignature, str);
+ timeSigPending = false;
+ }
+ if (clefPending) {
+ writeClef(clef, str);
+ clefPending = false;
+ }
+
+ 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) {
+ if (startedAttributes) {
+
+ if (keyPending) {
+ writeKey(key, str);
+ keyPending = false;
+ }
+ if (timeSigPending) {
+ writeTime(prevTimeSignature, str);
+ timeSigPending = false;
+ }
+ if (clefPending) {
+ writeClef(clef, str);
+ clefPending = false;
+ }
+
+ str << "\t\t\t</attributes>" << std::endl;
+ startedAttributes = false;
+ }
+
+ 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;
+}
+
+}