MuseScore/bww2mxml/parser.cpp
Andres Fernandez de Prado 33dff96a20 This commit contains changes required for MuseScore to compile under MSVC with no warnings.
This commit contains changes required for MuseScore to compile under MSVC with no warnings.

MuseScore is being compiled with the /W4 setting (warning level 4), which is similar to -wall -wextra on clang. This generates lots of warnings on MSVC, mainly for non-standard constructs and for constructs which might be bugs or might lead to bugs.

Most warnings are in the following categories:
- Name hiding: a variable hides a variable with the same name on a larger scope (or a field, or a function parameter). This can easily lead to bugs, and it is a best practice to avoid hiding variable names (see recommendation ES.12 in the C++ Core Guidelines by Stroustrop & Sutter (http://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#Res-reuse : ES.12: Do not reuse names in nested scopes)
- Narrowing conversion: a numeric conversion results in loss of significant digits (for example, double -> float). The general recommendation is to use a cast to indicate this is designed behaviour.
- Unreachable code: in several instances, there is unreachable code. The unreachable code is commented out.
- (Potentially) uninitialized local variable. Just initialized the vars.
- foreach(,) -> for(:): this does not generate a warning per-se (only a few of these generate warnings due to name hiding), but changed in keeping with "MuseScore Coding Rules" (https://musescore.org/en/handbook/musescore-coding-rules#Loops), which tells explicitly "Use C++11's "for" instead of Qt's "foreach":" ... "If you happen to be fixing some code and see a "foreach", please change that loop into a "for"."

Most changes are in the categories indicated above. The next listing shows detailed changes for files which are *not* of the aforementioned types.

- all.h: Disable warning C4127 (conditional expression is constant - generated in Qt header file qvector.h)
- awl/aslider.h: unreachable code.
- awl/knob.cpp: name hiding
- awl/mslider.cpp: name hiding
- awl/slider.cpp: name hiding
- bww2mxml/parser.cpp: name hiding
- effects/compressor/compressor.cpp: narrowing conversion
- effects/zita1/zitagui.cpp: name hiding
- fluid/fluid.cpp: foreach replacement. Name hiding.
- fluid/mod.cpp: name hiding.
- fluid/sfont.cpp: foreach replacement. Name hiding. Initialize vars.
- fluid/voice.cpp: Name hiding.
- libmscore/accidental.cpp: Name hiding.
- libmscore/ambitus.cpp: Initialize vars.
- libmscore/barline.cpp: Name hiding. Unreachable code.
- libmscore/beam.cpp: Name hiding.
- libmscore/chordrest.cpp: Unreachable code.
- libmscore/scorefile.cpp: Name hiding.
- manual/genManual.cpp: Name hiding. foreach replacement.
- midi/midifile.cpp: Name hiding. Unreachable code.
- omr/importpdf.cpp: Name hiding. foreach replacement.
- omr/omr.cpp: Name hiding. foreach replacement.
- omr/omrpage.cpp: Name hiding. foreach replacement.
- omr/omrview.cpp: Name hiding. foreach replacement.
- synthesizer/event.cpp: Unreachable code.
- zerberus\channel.cpp: Narrowing conversion.
- zerberus\instrument.cpp: Name hiding.
- zerberus\sfz.cpp: Name hiding.
- zerberus\voice.h: Suppress warning C4201: "nonstandard extension used: nameless struct/union"
- zerberus\zerberus.cpp: Name hiding. Unreferenced parameter.
- zerberus\zerberusgui.cpp: Name hiding.
2018-08-03 09:15:42 +02:00

918 lines
24 KiB
C++

//=============================================================================
// BWW to MusicXML converter
// Part of MusE Score
// Linux Music Score Editor
// $Id: parser.cpp 4873 2011-10-19 19:33:04Z lvinken $
//
// 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.
//=============================================================================
/**
\file
A simple parser for bww files. The file header is handled one line at a time,
until a line starting with "&" is found. The parser then builds measures from
uinterrupted sequences of notes.
*/
// #include <iostream>
#include <QtCore/QStringList>
#include <QtCore/QtDebug>
#include <QtCore/QMap>
#include "lexer.h"
#include "parser.h"
#include "writer.h"
// Duration of a whole measure in ticks
static const int WHOLE_MEASURE_DURATION = 192;
/**
Determine if symbol is part of a note sequence
*/
static bool isNote(Bww::Symbol sym)
{
return (sym == Bww::NOTE
|| sym == Bww::TIE
|| sym == Bww::TRIPLET
|| sym == Bww::GRACE);
}
/**
Determine if symbol is part of a non-note sequence
*/
static bool isNonNote(Bww::Symbol sym)
{
return (sym == Bww::CLEF
|| sym == Bww::KEY
|| sym == Bww::TSIG
|| sym == Bww::PART
|| sym == Bww::BAR);
}
/**
Dump beams
*/
static void dumpBeams(QList<Bww::MeasureDescription> const& measures)
{
for (int j = 0; j < measures.size(); ++j)
{
QString beams;
QString beamStates;
QVector<QString> beamList(3);
for (int i = 0; i < measures.at(j).notes.size(); ++i)
{
QString beam = measures.at(j).notes.at(i).beam;
if (beam == "")
beam = " ";
beams += beam;
switch (measures.at(j).notes.at(i).beamState)
{
case Bww::ST_NONE: beamStates += " "; break;
case Bww::ST_START: beamStates += "["; break;
case Bww::ST_CONTINUE: beamStates += "_"; break;
case Bww::ST_STOP: beamStates += "]"; break;
default: beamStates += " ";
}
for (int k = 0; k < 3; k++)
switch (measures.at(j).notes.at(i).beamList.at(k))
{
case Bww::BM_NONE: beamList[k] += " "; break;
case Bww::BM_BEGIN: beamList[k] += "b"; break;
case Bww::BM_CONTINUE: beamList[k] += "c"; break;
case Bww::BM_END: beamList[k] += "e"; break;
case Bww::BM_FORWARD_HOOK: beamList[k] += ">"; break;
case Bww::BM_BACKWARD_HOOK: beamList[k] += "<"; break;
default: beamList[k] += "?";
}
}
qDebug() << "beams measure #" << j + 1 << beams;
qDebug() << "beams measure #" << j + 1 << beamStates;
for (int k = 0; k < 3; k++)
qDebug() << "beams measure #" << j + 1 << beamList.at(k);
}
}
/**
Dump measure contents
*/
static void dumpMeasures(QList<Bww::MeasureDescription> const& measures)
{
qDebug() << "dumpMeasures #measures" << measures.size()
;
for (int j = 0; j < measures.size(); ++j)
{
qDebug() << "measure #" << j + 1;
qDebug() << "Measure contents:";
qDebug() << "mbf:"
<< "repeatBegin" << measures.at(j).mbf.repeatBegin
<< "endingFirst" << measures.at(j).mbf.endingFirst
<< "endingSecond" << measures.at(j).mbf.endingSecond
<< "firstOfSystem" << measures.at(j).mbf.firstOfSystem
<< "irregular" << measures.at(j).mbf.irregular
;
for (int i = 0; i < measures.at(j).notes.size(); ++i)
{
qDebug()
<< measures.at(j).notes.at(i).pitch
<< measures.at(j).notes.at(i).beam
<< measures.at(j).notes.at(i).type
<< measures.at(j).notes.at(i).dots
<< measures.at(j).notes.at(i).tieStart
<< measures.at(j).notes.at(i).tieStop
<< measures.at(j).notes.at(i).triplet
<< measures.at(j).notes.at(i).grace
;
}
qDebug() << "mef:"
<< "repeatEnd" << measures.at(j).mef.repeatEnd
<< "endingEnd" << measures.at(j).mef.endingEnd
<< "lastOfSystem" << measures.at(j).mef.lastOfSystem
<< "lastOfPart" << measures.at(j).mef.lastOfPart
;
qDebug() << "duration:" << measures.at(j).duration;
}
}
/**
Calculate measure durations
*/
static void calculateMeasureDurations(QList<Bww::MeasureDescription> & measures)
{
for (int j = 0; j < measures.size(); ++j)
{
int measureDuration = 0;
for (int i = 0; i < measures.at(j).notes.size(); ++i)
{
int ticks = WHOLE_MEASURE_DURATION / measures.at(j).notes.at(i).type.toInt();
if (measures.at(j).notes.at(i).dots) ticks = 3 * ticks / 2;
if (measures.at(j).notes.at(i).triplet != Bww::ST_NONE) ticks = 2 * ticks / 3;
if (measures.at(j).notes.at(i).grace) ticks = 0; // grace notes don't count
measureDuration += ticks;
qDebug()
<< measures.at(j).notes.at(i).pitch
<< measures.at(j).notes.at(i).beam
<< measures.at(j).notes.at(i).type
<< measures.at(j).notes.at(i).dots
<< measures.at(j).notes.at(i).tieStart
<< measures.at(j).notes.at(i).tieStop
<< measures.at(j).notes.at(i).triplet
<< measures.at(j).notes.at(i).grace
<< "->" << ticks
;
}
qDebug() << "measureDuration:" << measureDuration;
measures[j].duration = measureDuration;
}
}
/**
Determine time signature
*/
static void determineTimesig(QList<Bww::MeasureDescription> const& measures, int & beats, int & beat)
{
QMap<int, int> map;
for (int j = 0; j < measures.size(); ++j)
{
int dur = measures[j].duration;
if (map.contains(dur))
map[dur]++;
else
map.insert(dur, 1);
}
// determine most common duration
int commonDur = 0;
int max = 0;
QMap<int, int>::const_iterator i = map.constBegin();
while (i != map.constEnd())
{
qDebug() << "measureDurations:" << i.key() << i.value() << endl;
if (i.value() > max)
{
commonDur = i.key();
max = i.value();
}
++i;
}
qDebug() << "measureDuration commonDur:" << commonDur << "max:" << max;
// determine time signature
beat = 4;
beats = 0;
int divisor = WHOLE_MEASURE_DURATION / 4;
for (; beat < 64; beat *= 2, divisor /= 2)
{
if ((commonDur % divisor) == 0)
{
beats = commonDur / divisor;
qDebug()
<< "measureDuration found beat:" << beat
<< "beats:" << beats
<< "divisor:" << divisor
;
return;
}
}
// could not determine time signature, set default
beat = 4;
beats = 4;
}
/**
Find irregular measures
*/
static void findIrregularMeasures(QList<Bww::MeasureDescription> & measures, int beats, int beat)
{
qDebug() << "findIrregularMeasures" << measures.size()
<< "beats" << beats
<< "beat" << beat
;
int normalDuration = WHOLE_MEASURE_DURATION * beats / beat;
// need at least one measure
if (measures.size() == 0) return;
// if the first measure is shorter that normal, it is irregular
if (measures.at(0).duration < normalDuration) measures[0].mbf.irregular = true;
for (int j = 1; j < measures.size(); ++j)
{
// the second measure of a pair where the sum of their duration adds up
// to the normal duration is also irregular
const int d1 = measures.at(j - 1).duration;
const int d2 = measures.at(j).duration;
if (d1 > 0 && d2 > 0 && (d1 + d2) == normalDuration)
measures[j].mbf.irregular = true;
}
}
/**
Set mef.lastOfPart flag on last measure
*/
static void setLastOfPart(QList<Bww::MeasureDescription> & measures)
{
qDebug() << "dumpMeasures #measures" << measures.size()
;
// need at least one measure
if (measures.size() == 0) return;
// set lastOfPart flag on last measure
int j = measures.size() - 1;
measures[j].mef.lastOfPart = true;
}
static QString findNextNextNoteBeam(QList<Bww::MeasureDescription> const& measures, int measureNr, int noteNr)
{
for (int i = noteNr + 1; i < measures.at(measureNr).notes.size(); ++i)
{
if (measures.at(measureNr).notes.at(i).grace)
// ignore grace notes
continue;
return measures.at(measureNr).notes.at(i).beam;
}
return " "; // no next non-grace note found
}
static int type2beams(QString type)
{
if (type == "8") return 1;
else if (type == "16") return 2;
else if (type == "32") return 3;
else return 0;
}
static void calculateHigherBeamStates(Bww::MeasureDescription & m)
{
qDebug() << "calculateHigherBeamStates";
for (int i = 0; i < m.notes.size(); ++i)
{
qDebug()
<< m.notes.at(i).pitch
<< m.notes.at(i).beam
<< m.notes.at(i).type
<< m.notes.at(i).dots
<< m.notes.at(i).tieStart
<< m.notes.at(i).tieStop
<< m.notes.at(i).triplet
<< m.notes.at(i).grace
;
if (m.notes.at(i).grace)
{
// just copy beamList[0] into beamList[1] and beamList[2]
m.notes[i].beamList[1] = m.notes.at(i).beamList[0];
m.notes[i].beamList[2] = m.notes.at(i).beamList[0];
}
else
{
int blp = -1; // beam level previous chord
int blc = -1; // beam level current chord
int bln = -1; // beam level next chord
// find beam level current note
blc = type2beams(m.notes.at(i).type);
if (blc == 0)
continue; // note does not have a beam
// find beam level previous note
if (m.notes.at(i).beamList[0] == Bww::BM_CONTINUE
|| m.notes.at(i).beamList[0] == Bww::BM_END)
{
for (int j = i - 1; blp == -1 && j >= 0; --j)
{
if (m.notes.at(j).grace)
continue; // ignore grace notes
blp = type2beams(m.notes.at(j).type);
}
}
// find beam level next note
if (m.notes.at(i).beamList[0] == Bww::BM_BEGIN
|| m.notes.at(i).beamList[0] == Bww::BM_CONTINUE)
{
for (int j = i + 1; bln == -1 && j < m.notes.size(); ++j)
{
if (m.notes.at(j).grace)
continue; // ignore grace notes
bln = type2beams(m.notes.at(j).type);
}
}
qDebug()
<< "blp" << blp
<< "blc" << blc
<< "bln" << bln;
for (int j = 2; j <= blc; ++j)
{
Bww::BeamType bt = Bww::BM_NONE;
if (blp < j && bln >= j) bt = Bww::BM_BEGIN;
else if (blp < j && bln < j) {
if (bln > 0) bt = Bww::BM_FORWARD_HOOK;
else if (blp > 0) bt = Bww::BM_BACKWARD_HOOK;
}
else if (blp >= j && bln < j) bt = Bww::BM_END;
else if (blp >= j && bln >= j) bt = Bww::BM_CONTINUE;
m.notes[i].beamList[j - 1] = bt;
qDebug() << "beamList" << j - 1 << "=" << bt;
}
} // else
} // for (int i = 0; i < m.notes.size(); ++i)
}
/**
Determine all beam states.
First for normal notes convert "r" and "l" in notes.beam into BM_BEGIN,
BM_CONTINUE and BM_END in notes.beamList[0]. For grace notes "b", "c" and
"e" can be directly converted into the corresponding beam states in
notes.beamList[0].
Then calculate the higher level beams.
*/
static void determineBeamStates(QList<Bww::MeasureDescription> & measures)
{
enum State { NONE, LEFT, RIGHT };
for (int j = 0; j < measures.size(); ++j)
{
State state = NONE;
for (int i = 0; i < measures.at(j).notes.size(); ++i)
{
QString beam = measures.at(j).notes.at(i).beam;
// handle normal notes
if (beam == "")
{
measures[j].notes[i].beamState = Bww::ST_NONE;
measures[j].notes[i].beamList[0] = Bww::BM_NONE;
state = NONE;
}
else if (beam == "r")
{
if (state == NONE)
{
measures[j].notes[i].beamState = Bww::ST_START;
measures[j].notes[i].beamList[0] = Bww::BM_BEGIN;
state = LEFT; // now in left part of beam
}
else if (state == LEFT)
{
measures[j].notes[i].beamState = Bww::ST_CONTINUE;
measures[j].notes[i].beamList[0] = Bww::BM_CONTINUE;
}
else if (state == RIGHT)
{
// shouldn't happen TODO report (internal?) error
}
}
else if (beam == "l")
{
if (state == NONE)
{
// shouldn't happen TODO report error
}
else if (state == LEFT || state == RIGHT)
{
// if the beam does not end here (next note has beam "l")
// then beamState is CONTINUE else STOP
if (findNextNextNoteBeam(measures, j, i) == "l")
{
measures[j].notes[i].beamState = Bww::ST_CONTINUE;
measures[j].notes[i].beamList[0] = Bww::BM_CONTINUE;
state = RIGHT; // now in right part of beam
}
else
{
measures[j].notes[i].beamState = Bww::ST_STOP;
measures[j].notes[i].beamList[0] = Bww::BM_END;
state = NONE; // now in right part of beam
}
}
}
// handle grace notes
else if (beam == "b") measures[j].notes[i].beamList[0] = Bww::BM_BEGIN;
else if (beam == "c") measures[j].notes[i].beamList[0] = Bww::BM_CONTINUE;
else if (beam == "e") measures[j].notes[i].beamList[0] = Bww::BM_END;
}
calculateHigherBeamStates(measures[j]);
}
}
namespace Bww {
/**
Parser constructor, using Lexer \a l and Writer \a w.
*/
Parser::Parser(Lexer& l, Writer& w)
: lex(l),
wrt(w),
tempo(0),
//inMeasure(false),
//measureNr(0),
tieStart(false),
inTie(false),
tripletStart(false),
inTriplet(false),
tsigFound(false)
{
qDebug() << "Parser::Parser()";
}
/**
Parse the input stream and write result.
*/
void Parser::parse()
{
// read the header, handling only strings and tune tempo
while (lex.symType() == COMMENT
|| lex.symType() == STRING
|| lex.symType() == TEMPO)
{
if (lex.symType() == STRING)
parseString();
else if (lex.symType() == TEMPO)
parseTempo();
else if (lex.symType() == COMMENT)
lex.getSym();
}
qDebug() << "Parser::parse()"
<< "title:" << title
<< "type:" << type
<< "composer:" << composer
<< "footer:" << footer
;
wrt.header(title, type, composer, footer, tempo);
// read the actual music
if (lex.symType() != CLEF)
errorHandler("clef ('&') expected");
while (lex.symType() != NONE)
{
if (isNonNote(lex.symType()))
parseSeqNonNotes();
else if (isNote(lex.symType()))
parseSeqNotes();
else if (lex.symType() == UNKNOWN)
{
errorHandler("unknown symbol '" + lex.symValue() + "'");
lex.getSym();
}
else
{
; // others not implemented yet: silently ignored
lex.getSym();
}
}
qDebug() << "Parser::parse() finished, #measures" << measures.size()
;
calculateMeasureDurations(measures);
if (!tsigFound)
{
determineTimesig(measures, beats, beat);
wrt.tsig(beats, beat);
}
findIrregularMeasures(measures, beats, beat);
determineBeamStates(measures);
setLastOfPart(measures);
dumpMeasures(measures);
dumpBeams(measures);
for (int j = 0; j < measures.size(); ++j)
{
wrt.beginMeasure(measures.at(j).mbf);
for (int i = 0; i < measures.at(j).notes.size(); ++i)
{
wrt.note(measures.at(j).notes.at(i).pitch,
measures.at(j).notes.at(i).beamList,
measures.at(j).notes.at(i).type,
measures.at(j).notes.at(i).dots,
measures.at(j).notes.at(i).tieStart,
measures.at(j).notes.at(i).tieStop,
measures.at(j).notes.at(i).triplet,
measures.at(j).notes.at(i).grace
);
}
wrt.endMeasure(measures.at(j).mef);
}
// trailer
wrt.trailer();
}
/**
Display error \a s.
*/
void Parser::errorHandler(QString /*s*/)
{
#if 0 // WS
std::cerr << "Parse error line "
<< lex.symLineNumber() + 1
<< ": "
<< qPrintable(s)
<< std::endl;
#endif
}
/**
Parse a bww bar symbol.
*/
void Parser::parseBar(Bww::MeasureEndFlags& mef)
{
qDebug() << "Parser::parseBar() value:" << qPrintable(lex.symValue());
if (lex.symValue() == "!!t") mef.doubleBarLine = true;
lex.getSym();
}
/**
Parse a bww note.
*/
void Parser::parseNote()
{
qDebug() << "Parser::parseNote() value:" << qPrintable(lex.symValue());
QRegExp rNotes("(LG|LA|[B-F]|HG|HA)([lr]?)_(1|2|4|8|16|32)");
QStringList caps;
if (rNotes.exactMatch(lex.symValue()))
{
caps = rNotes.capturedTexts();
qDebug() << " match" << caps.size();
if (caps.size() == 4)
{
qDebug()
<< "caps[1]" << caps.at(1)
<< "caps[2]" << caps.at(2)
<< "caps[3]" << caps.at(3)
;
}
}
lex.getSym();
int dots = 0;
bool tieStop = false;
if (tieStart) inTie = true;
bool tripletStop = false;
if (tripletStart) inTriplet = true;
if (lex.symType() == DOT)
{
qDebug() << " dot" << qPrintable(lex.symValue());
++dots;
lex.getSym();
}
else if (lex.symType() == TIE)
{
qDebug() << " tie" << qPrintable(lex.symValue());
if (lex.symValue() == "^ts")
{
if (inTie) errorHandler("tie start ('^ts') unexpected");
}
else
{
if (!inTie) errorHandler("tie end ('^te') unexpected");
else
{
tieStop = true;
inTie = false;
lex.getSym();
}
}
}
else if (lex.symType() == TRIPLET)
{
qDebug() << " triplet" << qPrintable(lex.symValue());
if (lex.symValue() == "^3s")
{
if (inTriplet) errorHandler("triplet start ('^3s') unexpected");
}
else
{
if (!inTriplet) errorHandler("triplet end ('^3e') unexpected");
else
{
tripletStop = true;
}
}
lex.getSym();
}
StartStop triplet = ST_NONE;
if (inTriplet)
{
if (tripletStart) triplet = ST_START;
else if (tripletStop) triplet = ST_STOP;
else triplet = ST_CONTINUE;
}
qDebug() << " tie start" << tieStart << " tie stop" << tieStop;
qDebug() << " triplet start" << tripletStart << " triplet stop" << tripletStop;
NoteDescription noteDesc(caps[1], caps[2], caps[3], dots, tieStart, tieStop, triplet);
if (measures.isEmpty())
{
errorHandler("cannot append note: no measure");
}
else
{
measures.last().notes.append(noteDesc);
}
tieStart = false;
tripletStart = false;
if (tripletStop)
{
inTriplet = false;
tripletStop = false;
}
}
/**
Determine beam for grace note \a index in a group of \a size grace notes.
*/
static QString graceBeam(const int size, const int index)
{
if (size <= 1) return " "; // no beam
if (index < 0) return " "; // no beam (but should not happen)
if (index == 0) return "b"; // begin
else if (index == (size - 1)) return "e"; // end
else if (index >= size) return " "; // no beam (but should not happen)
else return "c"; // continue
}
/**
Parse a bww embellishment.
*/
void Parser::parseGraces()
{
qDebug() << "Parser::parseGraces() value:" << qPrintable(lex.symValue());
const QString c_type = "32";
const int dots = 0;
QStringList graces = lex.symValue().split(" ");
for (int i = 0; i < graces.size(); ++i)
{
const QString beam = graceBeam(graces.size(), i);
NoteDescription noteDesc(graces.at(i), beam, c_type, dots, false, false, ST_NONE, true);
if (measures.isEmpty())
{
errorHandler("cannot append note: no measure");
}
else
{
measures.last().notes.append(noteDesc);
}
}
lex.getSym();
}
/**
Parse a bww part symbol.
*/
void Parser::parsePart(Bww::MeasureBeginFlags& mbf, Bww::MeasureEndFlags& mef)
{
qDebug() << "Parser::parsePart() value:" << qPrintable(lex.symValue());
if (lex.symValue() == "I!''")
{
mbf.repeatBegin = true;
}
else if (lex.symValue() == "'1")
{
mbf.endingFirst = true;
}
else if (lex.symValue() == "'2")
{
mbf.endingSecond = true;
}
else if (lex.symValue() == "''!I")
{
mef.repeatEnd = true;
}
else if (lex.symValue() == "_'")
{
mef.endingEnd = true;
}
else
{
; // other silently ignored
}
lex.getSym();
}
/**
Parse a sequence of non-notes.
*/
void Parser::parseSeqNonNotes()
{
qDebug() << "Parser::parseSeqNonNotes() value:" << qPrintable(lex.symValue());
MeasureBeginFlags mbfl;
MeasureEndFlags mefl;
while (isNonNote(lex.symType()))
{
if (lex.symType() == CLEF)
{
mbfl.firstOfSystem = true;
mefl.lastOfSystem = true;
lex.getSym();
}
else if (lex.symType() == KEY)
lex.getSym(); // ignore
else if (lex.symType() == TSIG)
parseTSig();
else if (lex.symType() == PART)
parsePart(mbfl, mefl);
else if (lex.symType() == BAR)
parseBar(mefl);
}
// First end the previous measure
if (!measures.isEmpty())
{
measures.last().mef = mefl;
}
// Then start a new measure, if necessary
if (isNote(lex.symType()))
{
MeasureDescription md;
md.mbf = mbfl;
measures.append(md);
}
}
/**
Parse a sequence of notes.
Includes handling ties and triplets, but without extensive error checking.
May break on invalid input.
*/
void Parser::parseSeqNotes()
{
qDebug() << "Parser::parseSeqNotes() value:" << qPrintable(lex.symValue());
while (lex.symType() == GRACE || lex.symType() == NOTE || lex.symType() == TIE || lex.symType() == TRIPLET)
{
if (lex.symType() == GRACE) parseGraces();
else if (lex.symType() == NOTE) parseNote();
else if (lex.symType() == TIE)
{
if (lex.symValue() == "^ts")
{
if (inTie) errorHandler("tie start ('^ts') unexpected");
else tieStart = true;
}
else
{
errorHandler("tie end ('^te') unexpected");
}
lex.getSym();
}
else if (lex.symType() == TRIPLET)
{
if (lex.symValue() == "^3s")
{
if (inTriplet) errorHandler("triplet start ('^3s') unexpected");
else tripletStart = true;
}
else
{
errorHandler("triplet end ('^3e') unexpected");
}
lex.getSym();
}
}
}
/**
Parse a bww string. Extract text and type.
Example: "Air",(Y,C,0,0,Times New Roman,14,400,0,0,18,0,0,0)
*/
void Parser::parseString()
{
qDebug() << "Parser::parseString() value:" << qPrintable(lex.symValue());
QRegExp rString("\\\"(.*)\\\",\\(([A-Z]),.*\\)");
if (rString.exactMatch(lex.symValue()))
{
QStringList caps = rString.capturedTexts();
if (caps.size() == 3)
{
if (caps.at(2) == "T") title = caps.at(1);
if (caps.at(2) == "Y") type = caps.at(1);
if (caps.at(2) == "M") composer = caps.at(1);
if (caps.at(2) == "F") footer = caps.at(1);
}
}
lex.getSym();
}
/**
Parse a bww tempo symbol.
*/
void Parser::parseTempo()
{
qDebug() << "Parser::parseTempo() value:" << qPrintable(lex.symValue());
QRegExp rTempo("^TuneTempo,(\\d+)");
if (rTempo.exactMatch(lex.symValue()))
{
QStringList caps = rTempo.capturedTexts();
if (caps.size() == 2)
{
tempo = caps.at(1).toInt();
}
}
lex.getSym();
}
/**
Parse a bww tsig symbol.
*/
void Parser::parseTSig()
{
qDebug() << "Parser::parseTSig() value:" << qPrintable(lex.symValue());
QRegExp rTSig("(\\d+)_(1|2|4|8|16|32)");
if (rTSig.exactMatch(lex.symValue()))
{
QStringList caps = rTSig.capturedTexts();
if (caps.size() == 3)
{
beats = caps.at(1).toInt();
beat = caps.at(2).toInt();
tsigFound = true;
wrt.tsig(beats, beat);
}
}
lex.getSym();
}
} // namespace Bww