MuseScore/mscore/importbww.cpp

565 lines
18 KiB
C++
Raw Normal View History

2012-08-08 20:46:29 +02:00
//=============================================================================
2012-05-26 14:49:10 +02:00
// MuseScore
// Linux Music Score Editor
// $Id: importbww.cpp 5427 2012-03-07 12:41:34Z wschweer $
//
// Copyright (C) 2010 Werner Schweer and others
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License version 2.
//
// This program 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 General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
//=============================================================================
// TODO LVI 2011-10-30: determine how to report import errors.
// Currently all output (both debug and error reports) are done using qDebug.
#include "bww2mxml/lexer.h"
#include "bww2mxml/writer.h"
#include "bww2mxml/parser.h"
#include "libmscore/barline.h"
#include "libmscore/box.h"
#include "libmscore/chord.h"
#include "libmscore/clef.h"
#include "libmscore/keysig.h"
#include "libmscore/layoutbreak.h"
#include "libmscore/measure.h"
#include "libmscore/note.h"
#include "libmscore/part.h"
#include "libmscore/pitchspelling.h"
#include "libmscore/score.h"
#include "libmscore/slur.h"
2013-08-22 12:18:14 +02:00
#include "libmscore/tie.h"
2012-05-26 14:49:10 +02:00
#include "libmscore/staff.h"
#include "libmscore/tempotext.h"
#include "libmscore/timesig.h"
#include "libmscore/tuplet.h"
#include "libmscore/volta.h"
#include "libmscore/segment.h"
#include "musescore.h"
#include "musicxml.h"
//---------------------------------------------------------
// addText
// copied from importxml.cpp
// TODO: remove duplicate code
//---------------------------------------------------------
static void addText(Ms::VBox*& vbx, Ms::Score* s, QString strTxt, Ms::TextStyleType stl)
2012-05-26 14:49:10 +02:00
{
if (!strTxt.isEmpty()) {
2013-05-13 18:49:17 +02:00
Ms::Text* text = new Ms::Text(s);
2012-05-26 14:49:10 +02:00
text->setTextStyleType(stl);
text->setPlainText(strTxt);
2012-05-26 14:49:10 +02:00
if (vbx == 0)
2013-05-13 18:49:17 +02:00
vbx = new Ms::VBox(s);
2012-05-26 14:49:10 +02:00
vbx->add(text);
}
}
//---------------------------------------------------------
// xmlSetPitch
// copied and adapted from importxml.cpp
// TODO: remove duplicate code
//---------------------------------------------------------
/**
Convert MusicXML \a step / \a alter / \a octave to midi pitch,
set pitch and tpc.
*/
2013-05-13 18:49:17 +02:00
static void xmlSetPitch(Ms::Note* n, char step, int alter, int octave)
2012-05-26 14:49:10 +02:00
{
int istep = step - 'A';
// a b c d e f g
static int table[7] = { 9, 11, 0, 2, 4, 5, 7 };
if (istep < 0 || istep > 6) {
qDebug("xmlSetPitch: illegal pitch %d, <%c>", istep, step);
2012-05-26 14:49:10 +02:00
return;
}
int pitch = table[istep] + alter + (octave+1) * 12;
if (pitch < 0)
pitch = 0;
if (pitch > 127)
pitch = 127;
n->setPitch(pitch);
// a b c d e f g
static int table1[7] = { 5, 6, 0, 1, 2, 3, 4 };
2013-05-13 18:49:17 +02:00
int tpc = step2tpc(table1[istep], Ms::AccidentalVal(alter));
2012-05-26 14:49:10 +02:00
n->setTpc(tpc);
}
//---------------------------------------------------------
// setTempo
// copied and adapted from importxml.cpp
// TODO: remove duplicate code
//---------------------------------------------------------
2013-05-13 18:49:17 +02:00
static void setTempo(Ms::Score* score, int tempo)
2012-05-26 14:49:10 +02:00
{
2013-05-13 18:49:17 +02:00
Ms::TempoText* tt = new Ms::TempoText(score);
2012-05-26 14:49:10 +02:00
tt->setTempo(double(tempo)/60.0);
tt->setTrack(0);
2014-11-09 21:18:32 +01:00
QString tempoText = Ms::TempoText::duration2tempoTextString(Ms::TDuration::DurationType::V_QUARTER);
tempoText += QString(" = %1").arg(tempo);
tt->setPlainText(tempoText);
2013-05-13 18:49:17 +02:00
Ms::Measure* measure = score->firstMeasure();
Ms::Segment* segment = measure->getSegment(Ms::Segment::Type::ChordRest, 0);
2012-05-26 14:49:10 +02:00
segment->add(tt);
}
namespace Bww {
/**
The writer that imports into MuseScore.
*/
class MsScWriter : public Writer
{
public:
MsScWriter();
void beginMeasure(const Bww::MeasureBeginFlags mbf);
void endMeasure(const Bww::MeasureEndFlags mef);
void header(const QString title, const QString type,
const QString composer, const QString footer,
const unsigned int temp);
void note(const QString pitch, const QVector<Bww::BeamType> beamList,
const QString type, const int dots,
bool tieStart = false, bool tieStop = false,
StartStop triplet = ST_NONE,
bool grace = false);
2013-05-13 18:49:17 +02:00
void setScore(Ms::Score* s) { score = s; }
2012-05-26 14:49:10 +02:00
void tsig(const int beats, const int beat);
void trailer();
private:
2013-05-13 18:49:17 +02:00
void doTriplet(Ms::Chord* cr, StartStop triplet = ST_NONE);
2012-05-26 14:49:10 +02:00
static const int WHOLE_DUR = 64; ///< Whole note duration
struct StepAlterOct { ///< MusicXML step/alter/oct values
QChar s;
int a;
int o;
2012-05-31 16:24:35 +02:00
StepAlterOct(QChar step = QChar('C'), int alter = 0, int oct = 1)
2012-05-26 14:49:10 +02:00
: s(step), a(alter), o(oct) {};
};
2013-05-13 18:49:17 +02:00
Ms::Score* score; ///< The score
2012-05-26 14:49:10 +02:00
int beats; ///< Number of beats
int beat; ///< Beat type
QMap<QString, StepAlterOct> stepAlterOctMap; ///< Map bww pitch to step/alter/oct
QMap<QString, QString> typeMap; ///< Map bww note types to MusicXML
unsigned int measureNumber; ///< Current measure number
unsigned int tick; ///< Current tick
2013-05-13 18:49:17 +02:00
Ms::Measure* currentMeasure; ///< Current measure
Ms::Tuplet* tuplet; ///< Current tuplet
Ms::Volta* lastVolta; ///< Current volta
2012-05-26 14:49:10 +02:00
unsigned int tempo; ///< Tempo (0 = not specified)
unsigned int ending; ///< Current ending
2014-09-04 15:15:49 +02:00
QList<Ms::Chord*> currentGraceNotes;
2012-05-26 14:49:10 +02:00
};
/**
MsScWriter constructor.
*/
MsScWriter::MsScWriter()
: score(0),
beats(4),
beat(4),
measureNumber(0),
tick(0),
currentMeasure(0),
tuplet(0),
lastVolta(0),
tempo(0)
{
qDebug() << "MsScWriter::MsScWriter()";
stepAlterOctMap["LG"] = StepAlterOct('G', 0, 4);
stepAlterOctMap["LA"] = StepAlterOct('A', 0, 4);
stepAlterOctMap["B"] = StepAlterOct('B', 0, 4);
stepAlterOctMap["C"] = StepAlterOct('C', 1, 5);
stepAlterOctMap["D"] = StepAlterOct('D', 0, 5);
stepAlterOctMap["E"] = StepAlterOct('E', 0, 5);
stepAlterOctMap["F"] = StepAlterOct('F', 1, 5);
stepAlterOctMap["HG"] = StepAlterOct('G', 0, 5);
stepAlterOctMap["HA"] = StepAlterOct('A', 0, 5);
typeMap["1"] = "whole";
typeMap["2"] = "half";
typeMap["4"] = "quarter";
typeMap["8"] = "eighth";
typeMap["16"] = "16th";
typeMap["32"] = "32nd";
}
/**
Begin a new measure.
*/
void MsScWriter::beginMeasure(const Bww::MeasureBeginFlags mbf)
{
qDebug() << "MsScWriter::beginMeasure()";
++measureNumber;
// create a new measure
2013-05-13 18:49:17 +02:00
currentMeasure = new Ms::Measure(score);
2012-05-26 14:49:10 +02:00
currentMeasure->setTick(tick);
2013-05-13 18:49:17 +02:00
currentMeasure->setTimesig(Ms::Fraction(beats, beat));
2012-05-26 14:49:10 +02:00
currentMeasure->setNo(measureNumber);
score->measures()->add(currentMeasure);
if (mbf.repeatBegin)
2016-02-04 11:27:47 +01:00
currentMeasure->setRepeatStart(true);
2012-05-26 14:49:10 +02:00
if (mbf.irregular)
currentMeasure->setIrregular(true);
if (mbf.endingFirst || mbf.endingSecond) {
2013-05-13 18:49:17 +02:00
Ms::Volta* volta = new Ms::Volta(score);
2012-05-26 14:49:10 +02:00
volta->setTrack(0);
volta->endings().clear();
if (mbf.endingFirst) {
volta->setText("1");
volta->endings().append(1);
ending = 1;
}
else {
volta->setText("2");
volta->endings().append(2);
ending = 2;
}
2013-06-10 11:03:34 +02:00
volta->setTick(currentMeasure->tick());
2014-12-07 21:56:00 +01:00
score->addElement(volta);
2012-05-26 14:49:10 +02:00
lastVolta = volta;
}
// set clef, key and time signature in the first measure
if (measureNumber == 1) {
// clef
2013-05-13 18:49:17 +02:00
Ms::Clef* clef = new Ms::Clef(score);
2013-09-05 16:37:49 +02:00
clef->setClefType(Ms::ClefType::G);
2012-05-26 14:49:10 +02:00
clef->setTrack(0);
2013-05-13 18:49:17 +02:00
Ms::Segment* s = currentMeasure->getSegment(clef, tick);
2012-05-26 14:49:10 +02:00
s->add(clef);
// keysig
2013-05-13 18:49:17 +02:00
Ms::KeySigEvent key;
2014-06-20 17:07:22 +02:00
key.setKey(Ms::Key::D);
2013-05-13 18:49:17 +02:00
Ms::KeySig* keysig = new Ms::KeySig(score);
2012-05-26 14:49:10 +02:00
keysig->setKeySigEvent(key);
keysig->setTrack(0);
s = currentMeasure->getSegment(keysig, tick);
s->add(keysig);
// timesig
2013-05-13 18:49:17 +02:00
Ms::TimeSig* timesig = new Ms::TimeSig(score);
timesig->setSig(Ms::Fraction(beats, beat));
2012-05-26 14:49:10 +02:00
timesig->setTrack(0);
s = currentMeasure->getSegment(timesig, tick);
s->add(timesig);
2014-11-09 21:18:32 +01:00
qDebug("tempo %d", tempo);
2012-05-26 14:49:10 +02:00
}
}
/**
End the current measure.
*/
void MsScWriter::endMeasure(const Bww::MeasureEndFlags mef)
{
qDebug() << "MsScWriter::endMeasure()";
if (mef.repeatEnd)
2016-02-04 11:27:47 +01:00
currentMeasure->setRepeatEnd(true);
2012-05-26 14:49:10 +02:00
if (mef.endingEnd) {
if (lastVolta) {
qDebug("adding volta");
2012-05-26 14:49:10 +02:00
if (ending == 1)
lastVolta->setVoltaType(Ms::Volta::Type::CLOSED);
2012-05-26 14:49:10 +02:00
else
lastVolta->setVoltaType(Ms::Volta::Type::OPEN);
2014-12-07 21:56:00 +01:00
lastVolta->setTick2(tick);
2012-05-26 14:49:10 +02:00
lastVolta = 0;
}
else {
qDebug("lastVolta == 0 on stop");
2012-05-26 14:49:10 +02:00
}
}
if (mef.lastOfSystem) {
2013-05-13 18:49:17 +02:00
Ms::LayoutBreak* lb = new Ms::LayoutBreak(score);
2012-05-26 14:49:10 +02:00
lb->setTrack(0);
lb->setLayoutBreakType(Ms::LayoutBreak::Type::LINE);
2012-05-26 14:49:10 +02:00
currentMeasure->add(lb);
}
if (mef.lastOfPart && !mef.repeatEnd) {
2016-01-04 14:48:58 +01:00
//TODO currentMeasure->setEndBarLineType(Ms::BarLineType::END, false, true);
2012-05-26 14:49:10 +02:00
}
else if (mef.doubleBarLine) {
2016-01-04 14:48:58 +01:00
//TODO currentMeasure->setEndBarLineType(Ms::BarLineType::DOUBLE, false, true);
2012-05-26 14:49:10 +02:00
}
// BarLine* barLine = new BarLine(score);
// bool visible = true;
// barLine->setSubtype(BarLineType::NORMAL);
2012-05-26 14:49:10 +02:00
// barLine->setTrack(0);
// currentMeasure->setEndBarLineType(barLine->subtype(), false, visible);
}
/**
Write a single note.
*/
void MsScWriter::note(const QString pitch, const QVector<Bww::BeamType> beamList,
const QString type, const int dots,
bool tieStart, bool /*TODO tieStop */,
StartStop triplet,
bool grace)
{
qDebug() << "MsScWriter::note()"
<< "type:" << type
<< "dots:" << dots
<< "grace" << grace
;
if (!stepAlterOctMap.contains(pitch)
|| !typeMap.contains(type)) {
// TODO: error message
return;
}
StepAlterOct sao = stepAlterOctMap.value(pitch);
2013-05-13 18:49:17 +02:00
int ticks = 4 * Ms::MScore::division / type.toInt();
2012-05-26 14:49:10 +02:00
if (dots) ticks = 3 * ticks / 2;
qDebug() << "ticks:" << ticks;
Ms::TDuration durationType(Ms::TDuration::DurationType::V_INVALID);
2012-05-26 14:49:10 +02:00
durationType.setVal(ticks);
qDebug() << "duration:" << durationType.name();
if (triplet != ST_NONE) ticks = 2 * ticks / 3;
Ms::Beam::Mode bm = (beamList.at(0) == Bww::BM_BEGIN) ? Ms::Beam::Mode::BEGIN : Ms::Beam::Mode::AUTO;
2016-03-02 13:20:19 +01:00
Ms::Direction sd = Ms::Direction::AUTO;
2012-05-26 14:49:10 +02:00
// create chord
2013-05-13 18:49:17 +02:00
Ms::Chord* cr = new Ms::Chord(score);
2012-05-26 14:49:10 +02:00
//ws cr->setTick(tick);
cr->setBeamMode(bm);
cr->setTrack(0);
if (grace) {
2014-05-27 10:35:28 +02:00
cr->setNoteType(Ms::NoteType::GRACE32);
cr->setDurationType(Ms::TDuration::DurationType::V_32ND);
2016-03-02 13:20:19 +01:00
sd = Ms::Direction::UP;
2012-05-26 14:49:10 +02:00
}
else {
if (durationType.type() == Ms::TDuration::DurationType::V_INVALID)
durationType.setType(Ms::TDuration::DurationType::V_QUARTER);
2012-05-26 14:49:10 +02:00
cr->setDurationType(durationType);
2016-03-02 13:20:19 +01:00
sd = Ms::Direction::DOWN;
2012-05-26 14:49:10 +02:00
}
cr->setDuration(durationType.fraction());
cr->setDots(dots);
cr->setStemDirection(sd);
// add note to chord
2013-05-13 18:49:17 +02:00
Ms::Note* note = new Ms::Note(score);
2012-05-26 14:49:10 +02:00
note->setTrack(0);
2013-01-02 16:39:19 +01:00
xmlSetPitch(note, sao.s.toLatin1(), sao.a, sao.o);
2012-05-26 14:49:10 +02:00
if (tieStart) {
2013-05-13 18:49:17 +02:00
Ms::Tie* tie = new Ms::Tie(score);
2012-05-26 14:49:10 +02:00
note->setTieFor(tie);
tie->setStartNote(note);
tie->setTrack(0);
}
cr->add(note);
// add chord to measure
if (!grace) {
2014-09-04 15:15:49 +02:00
Ms::Segment* s = currentMeasure->getSegment(cr, tick);
s->add(cr);
if (!currentGraceNotes.isEmpty()) {
for (int i = currentGraceNotes.size() - 1; i >=0; i--)
cr->add(currentGraceNotes.at(i));
currentGraceNotes.clear();
}
2012-05-26 14:49:10 +02:00
doTriplet(cr, triplet);
int tickBefore = tick;
tick += ticks;
2013-05-13 18:49:17 +02:00
Ms::Fraction nl(Ms::Fraction::fromTicks(tick - currentMeasure->tick()));
2012-05-26 14:49:10 +02:00
currentMeasure->setLen(nl);
qDebug() << "MsScWriter::note()"
<< "tickBefore:" << tickBefore
<< "tick:" << tick
<< "nl:" << nl.print()
;
}
2014-09-04 15:15:49 +02:00
else {
currentGraceNotes.append(cr);
}
2012-05-26 14:49:10 +02:00
}
/**
Write the header.
*/
void MsScWriter::header(const QString title, const QString type,
const QString composer, const QString footer,
const unsigned int temp)
{
qDebug() << "MsScWriter::header()"
<< "title:" << title
<< "type:" << type
<< "composer:" << composer
<< "footer:" << footer
<< "temp:" << temp
;
// save tempo for later use
tempo = temp;
if (!title.isEmpty()) score->setMetaTag("workTitle", title);
// TODO re-enable following statement
// currently disabled because it breaks the bww iotest
// if (!type.isEmpty()) score->setMetaTag("workNumber", type);
if (!composer.isEmpty()) score->setMetaTag("composer", composer);
2012-05-26 14:49:10 +02:00
if (!footer.isEmpty()) score->setMetaTag("copyright", footer);
// score->setWorkTitle(title);
2013-05-13 18:49:17 +02:00
Ms::VBox* vbox = 0;
addText(vbox, score, title, Ms::TextStyleType::TITLE);
addText(vbox, score, type, Ms::TextStyleType::SUBTITLE);
addText(vbox, score, composer, Ms::TextStyleType::COMPOSER);
// addText(vbox, score, strPoet, Ms::TextStyleType::POET);
// addText(vbox, score, strTranslator, Ms::TextStyleType::TRANSLATOR);
2012-05-26 14:49:10 +02:00
if (vbox) {
vbox->setTick(0);
score->measures()->add(vbox);
}
if (!footer.isEmpty())
2014-05-26 15:31:36 +02:00
score->style()->set(Ms::StyleIdx::oddFooterC, footer);
2012-05-26 14:49:10 +02:00
2013-05-13 18:49:17 +02:00
Ms::Part* part = score->staff(0)->part();
part->setPlainLongName(instrumentName());
2015-01-11 22:37:35 +01:00
part->setPartName(instrumentName());
part->instrument()->setTrackName(instrumentName());
2012-05-26 14:49:10 +02:00
part->setMidiProgram(midiProgram() - 1);
}
/**
Store beats and beat type for later use.
*/
void MsScWriter::tsig(const int bts, const int bt)
{
qDebug() << "MsScWriter::tsig()"
<< "beats:" << bts
<< "beat:" << bt
;
beats = bts;
beat = bt;
}
/**
Write the trailer.
*/
void MsScWriter::trailer()
{
qDebug() << "MsScWriter::trailer()"
;
if (tempo) setTempo(score, tempo);
}
/**
Handle the triplet.
*/
2013-05-13 18:49:17 +02:00
void MsScWriter::doTriplet(Ms::Chord* cr, StartStop triplet)
2012-05-26 14:49:10 +02:00
{
qDebug() << "MsScWriter::doTriplet(" << triplet << ")"
;
if (triplet == ST_START) {
2013-05-13 18:49:17 +02:00
tuplet = new Ms::Tuplet(score);
2012-05-26 14:49:10 +02:00
tuplet->setTrack(0);
2013-05-13 18:49:17 +02:00
tuplet->setRatio(Ms::Fraction(3, 2));
2012-05-26 14:49:10 +02:00
tuplet->setTick(tick);
currentMeasure->add(tuplet);
}
else if (triplet == ST_STOP) {
if (tuplet) {
cr->setTuplet(tuplet);
tuplet->add(cr);
tuplet = 0;
}
else
qDebug("BWW::import: triplet stop without triplet start");
2012-05-26 14:49:10 +02:00
}
else if (triplet == ST_CONTINUE) {
if (!tuplet)
qDebug("BWW::import: triplet continue without triplet start");
2012-05-26 14:49:10 +02:00
}
else if (triplet == ST_NONE) {
if (tuplet)
qDebug("BWW::import: triplet none inside triplet");
2012-05-26 14:49:10 +02:00
}
else
qDebug("unknown triplet type %d", triplet);
2012-05-26 14:49:10 +02:00
if (tuplet) {
cr->setTuplet(tuplet);
tuplet->add(cr);
}
}
} // namespace Bww
2013-05-13 18:49:17 +02:00
namespace Ms {
2012-05-26 14:49:10 +02:00
//---------------------------------------------------------
// importBww
//---------------------------------------------------------
2016-03-11 12:18:46 +01:00
Score::FileError importBww(MasterScore* score, const QString& path)
2012-05-26 14:49:10 +02:00
{
qDebug("Score::importBww(%s)", qPrintable(path));
2012-05-26 14:49:10 +02:00
QFile fp(path);
if(!fp.exists())
return Score::FileError::FILE_NOT_FOUND;
2012-05-26 14:49:10 +02:00
if (!fp.open(QIODevice::ReadOnly))
return Score::FileError::FILE_OPEN_ERROR;
2012-05-26 14:49:10 +02:00
QString id("importBww");
Part* part = new Part(score);
part->setId(id);
score->appendPart(part);
2014-08-16 13:32:08 +02:00
Staff* staff = new Staff(score);
staff->setPart(part);
2012-05-26 14:49:10 +02:00
part->staves()->push_back(staff);
score->staves().push_back(staff);
Bww::Lexer lex(&fp);
Bww::MsScWriter wrt;
wrt.setScore(score);
2014-09-04 15:15:49 +02:00
score->style()->set(StyleIdx::measureSpacing, 1.0);
2012-05-26 14:49:10 +02:00
Bww::Parser p(lex, wrt);
p.parse();
score->setSaved(false);
score->setCreated(true);
score->connectTies();
qDebug("Score::importBww() done");
return Score::FileError::FILE_NO_ERROR; // OK
2012-05-26 14:49:10 +02:00
}
2013-05-13 18:49:17 +02:00
} // namespace Ms