diff options
author | tpearson <tpearson@283d02a7-25f6-0310-bc7c-ecb5cbfe19da> | 2010-03-01 18:37:05 +0000 |
---|---|---|
committer | tpearson <tpearson@283d02a7-25f6-0310-bc7c-ecb5cbfe19da> | 2010-03-01 18:37:05 +0000 |
commit | 145364a8af6a1fec06556221e66d4b724a62fc9a (patch) | |
tree | 53bd71a544008c518034f208d64c932dc2883f50 /src/base/NotationTypes.cpp | |
download | rosegarden-145364a8af6a1fec06556221e66d4b724a62fc9a.tar.gz rosegarden-145364a8af6a1fec06556221e66d4b724a62fc9a.zip |
Added old abandoned KDE3 version of the RoseGarden MIDI tool
git-svn-id: svn://anonsvn.kde.org/home/kde/branches/trinity/applications/rosegarden@1097595 283d02a7-25f6-0310-bc7c-ecb5cbfe19da
Diffstat (limited to 'src/base/NotationTypes.cpp')
-rw-r--r-- | src/base/NotationTypes.cpp | 2436 |
1 files changed, 2436 insertions, 0 deletions
diff --git a/src/base/NotationTypes.cpp b/src/base/NotationTypes.cpp new file mode 100644 index 0000000..ceddf79 --- /dev/null +++ b/src/base/NotationTypes.cpp @@ -0,0 +1,2436 @@ +// -*- c-basic-offset: 4 -*- + + +/* + Rosegarden + A sequencer and musical notation editor. + + This program is Copyright 2000-2008 + Guillaume Laurent <glaurent@telegraph-road.org>, + Chris Cannam <cannam@all-day-breakfast.com>, + Richard Bown <bownie@bownie.com> + + 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 <cstdio> // needed for sprintf() +#include "NotationRules.h" +#include "NotationTypes.h" +#include "BaseProperties.h" +#include <iostream> +#include <cstdlib> // for atoi +#include <limits.h> // for SHRT_MIN +#include <cassert> + +#if (__GNUC__ < 3) +#include <strstream> +#else +#include <sstream> +#endif + +//dmm This will make everything excruciatingly slow if defined: +//#define DEBUG_PITCH + +namespace Rosegarden +{ +using std::string; +using std::vector; +using std::cout; +using std::cerr; +using std::endl; + +// This is the fundamental definition of the resolution used throughout. +// It must be a multiple of 16, and should ideally be a multiple of 96. +static const timeT basePPQ = 960; + +const int MIN_SUBORDERING = SHRT_MIN; + +namespace Accidentals +{ + /** + * NoAccidental means the accidental will be inferred + * based on the performance pitch and current key at the + * location of the note. + */ + const Accidental NoAccidental = "no-accidental"; + + const Accidental Sharp = "sharp"; + const Accidental Flat = "flat"; + const Accidental Natural = "natural"; + const Accidental DoubleSharp = "double-sharp"; + const Accidental DoubleFlat = "double-flat"; + + AccidentalList getStandardAccidentals() { + + static Accidental a[] = { + NoAccidental, Sharp, Flat, Natural, DoubleSharp, DoubleFlat + }; + + static AccidentalList v; + if (v.size() == 0) { + for (unsigned int i = 0; i < sizeof(a)/sizeof(a[0]); ++i) + v.push_back(a[i]); + } + return v; + } + + int getPitchOffset(const Accidental &acc) { + if (acc == DoubleSharp) return 2; + else if (acc == Sharp) return 1; + else if (acc == Flat) return -1; + else if (acc == DoubleFlat) return -2; + else return 0; + } + + Accidental getAccidental(int pitchChange) { + if (pitchChange == -2) return DoubleFlat; + if (pitchChange == -1) return Flat; + // Yielding 'Natural' will add a natural-sign even if not needed, so for now + // just return NoAccidental + if (pitchChange == 0) return NoAccidental; + if (pitchChange == 1) return Sharp; + if (pitchChange == 2) return DoubleSharp; + + // if we're getting into triple flats/sharps, we're probably atonal + // and don't case if the accidental is simplified + return NoAccidental; + } +} + +using namespace Accidentals; + + +namespace Marks +{ + const Mark NoMark = "no-mark"; + const Mark Accent = "accent"; + const Mark Tenuto = "tenuto"; + const Mark Staccato = "staccato"; + const Mark Staccatissimo = "staccatissimo"; + const Mark Marcato = "marcato"; + const Mark Sforzando = getTextMark("sf"); + const Mark Rinforzando = getTextMark("rf"); + const Mark Trill = "trill"; + const Mark LongTrill = "long-trill"; + const Mark TrillLine = "trill-line"; + const Mark Turn = "turn"; + const Mark Pause = "pause"; + const Mark UpBow = "up-bow"; + const Mark DownBow = "down-bow"; + + const Mark Mordent = "mordent"; + const Mark MordentInverted = "mordent-inverted"; + const Mark MordentLong = "mordent-long"; + const Mark MordentLongInverted = "mordent-long-inverted"; + + string getTextMark(string text) { + return string("text_") + text; + } + + bool isTextMark(Mark mark) { + return string(mark).substr(0, 5) == "text_"; + } + + string getTextFromMark(Mark mark) { + if (!isTextMark(mark)) return string(); + else return string(mark).substr(5); + } + + string getFingeringMark(string fingering) { + return string("finger_") + fingering; + } + + bool isFingeringMark(Mark mark) { + return string(mark).substr(0, 7) == "finger_"; + } + + string getFingeringFromMark(Mark mark) { + if (!isFingeringMark(mark)) return string(); + else return string(mark).substr(7); + } + + int getMarkCount(const Event &e) { + long markCount = 0; + e.get<Int>(BaseProperties::MARK_COUNT, markCount); + return markCount; + } + + std::vector<Mark> getMarks(const Event &e) { + + std::vector<Mark> marks; + + long markCount = 0; + e.get<Int>(BaseProperties::MARK_COUNT, markCount); + if (markCount == 0) return marks; + + for (long j = 0; j < markCount; ++j) { + + Mark mark(Marks::NoMark); + (void)e.get<String>(BaseProperties::getMarkPropertyName(j), mark); + + marks.push_back(mark); + } + + return marks; + } + + Mark getFingeringMark(const Event &e) { + + long markCount = 0; + e.get<Int>(BaseProperties::MARK_COUNT, markCount); + if (markCount == 0) return NoMark; + + for (long j = 0; j < markCount; ++j) { + + Mark mark(Marks::NoMark); + (void)e.get<String>(BaseProperties::getMarkPropertyName(j), mark); + + if (isFingeringMark(mark)) return mark; + } + + return NoMark; + } + + void addMark(Event &e, const Mark &mark, bool unique) { + if (unique && hasMark(e, mark)) return; + + long markCount = 0; + e.get<Int>(BaseProperties::MARK_COUNT, markCount); + e.set<Int>(BaseProperties::MARK_COUNT, markCount + 1); + + PropertyName markProperty = BaseProperties::getMarkPropertyName(markCount); + e.set<String>(markProperty, mark); + } + + bool removeMark(Event &e, const Mark &mark) { + + long markCount = 0; + e.get<Int>(BaseProperties::MARK_COUNT, markCount); + + for (long j = 0; j < markCount; ++j) { + PropertyName pn(BaseProperties::getMarkPropertyName(j)); + std::string m; + if (e.get<String>(pn, m) && m == mark) { + e.unset(pn); + while (j < markCount - 1) { + PropertyName npn(BaseProperties::getMarkPropertyName(j+1)); + if (e.get<String>(npn, m)) { + e.set<String>( pn, m); + } + pn = npn; + ++j; + } + e.set<Int>(BaseProperties::MARK_COUNT, markCount - 1); + return true; + } + } + + return false; + } + + bool hasMark(const Event &e, const Mark &mark) { + long markCount = 0; + e.get<Int>(BaseProperties::MARK_COUNT, markCount); + + for (long j = 0; j < markCount; ++j) { + std::string m; + if (e.get<String>(BaseProperties::getMarkPropertyName(j), m) && m == mark) { + return true; + } + } + + return false; + } + + std::vector<Mark> getStandardMarks() { + + static Mark a[] = { + NoMark, Accent, Tenuto, Staccato, Staccatissimo, Marcato, + Sforzando, Rinforzando, Trill, LongTrill, TrillLine, + Turn, Pause, UpBow, DownBow, + Mordent, MordentInverted, MordentLong, MordentLongInverted + }; + + static std::vector<Mark> v; + if (v.size() == 0) { + for (unsigned int i = 0; i < sizeof(a)/sizeof(a[0]); ++i) + v.push_back(a[i]); + } + return v; + } + +} + +using namespace Marks; + + +////////////////////////////////////////////////////////////////////// +// Clef +////////////////////////////////////////////////////////////////////// + +const string Clef::EventType = "clefchange"; +const int Clef::EventSubOrdering = -250; +const PropertyName Clef::ClefPropertyName = "clef"; +const PropertyName Clef::OctaveOffsetPropertyName = "octaveoffset"; +const string Clef::Treble = "treble"; +const string Clef::French = "french"; +const string Clef::Soprano = "soprano"; +const string Clef::Mezzosoprano = "mezzosoprano"; +const string Clef::Alto = "alto"; +const string Clef::Tenor = "tenor"; +const string Clef::Baritone = "baritone"; +const string Clef::Varbaritone = "varbaritone"; +const string Clef::Bass = "bass"; +const string Clef::Subbass = "subbass"; + +const Clef Clef::DefaultClef = Clef("treble"); + +Clef::Clef(const Event &e) : + m_clef(DefaultClef.m_clef), + m_octaveOffset(0) +{ + if (e.getType() != EventType) { + std::cerr << Event::BadType + ("Clef model event", EventType, e.getType()).getMessage() + << std::endl; + return; + } + + std::string s; + e.get<String>(ClefPropertyName, s); + + if (s != Treble && s != Soprano && s != French && s != Mezzosoprano && s != Alto && s != Tenor && s != Baritone && s != Bass && s != Varbaritone && s != Subbass) { + std::cerr << BadClefName("No such clef as \"" + s + "\"").getMessage() + << std::endl; + return; + } + + long octaveOffset = 0; + (void)e.get<Int>(OctaveOffsetPropertyName, octaveOffset); + + m_clef = s; + m_octaveOffset = octaveOffset; +} + +Clef::Clef(const std::string &s, int octaveOffset) + // throw (BadClefName) +{ + if (s != Treble && s != Soprano && s != French && s != Mezzosoprano && s != Alto && s != Tenor && s != Baritone && s != Bass && s != Varbaritone && s != Subbass) { + throw BadClefName("No such clef as \"" + s + "\""); + } + m_clef = s; + m_octaveOffset = octaveOffset; +} + +Clef &Clef::operator=(const Clef &c) +{ + if (this != &c) { + m_clef = c.m_clef; + m_octaveOffset = c.m_octaveOffset; + } + return *this; +} + +bool Clef::isValid(const Event &e) +{ + if (e.getType() != EventType) return false; + + std::string s; + e.get<String>(ClefPropertyName, s); + if (s != Treble && s != Soprano && s != French && s != Mezzosoprano && s != Alto && s != Tenor && s != Baritone && s != Bass && s != Varbaritone && s != Subbass) return false; + + return true; +} + +int Clef::getTranspose() const +{ +//!!! plus or minus? + return getOctave() * 12 - getPitchOffset(); +} + +int Clef::getOctave() const +{ + if (m_clef == Treble || m_clef == French) return 0 + m_octaveOffset; + else if (m_clef == Bass || m_clef == Varbaritone || m_clef == Subbass) return -2 + m_octaveOffset; + else return -1 + m_octaveOffset; +} + +int Clef::getPitchOffset() const +{ + if (m_clef == Treble) return 0; + else if (m_clef == French) return -2; + else if (m_clef == Soprano) return -5; + else if (m_clef == Mezzosoprano) return -3; + else if (m_clef == Alto) return -1; + else if (m_clef == Tenor) return 1; + else if (m_clef == Baritone) return 3; + else if (m_clef == Varbaritone) return -4; + else if (m_clef == Bass) return -2; + else if (m_clef == Subbass) return 0; + else return -2; +} + +int Clef::getAxisHeight() const +{ + if (m_clef == Treble) return 2; + else if (m_clef == French) return 0; + else if (m_clef == Soprano) return 0; + else if (m_clef == Mezzosoprano) return 2; + else if (m_clef == Alto) return 4; + else if (m_clef == Tenor) return 6; + else if (m_clef == Baritone) return 8; + else if (m_clef == Varbaritone) return 4; + else if (m_clef == Bass) return 6; + else if (m_clef == Subbass) return 8; + else return 6; +} + +Clef::ClefList +Clef::getClefs() +{ + ClefList clefs; + clefs.push_back(Clef(Bass)); + clefs.push_back(Clef(Varbaritone)); + clefs.push_back(Clef(Subbass)); + clefs.push_back(Clef(Baritone)); + clefs.push_back(Clef(Tenor)); + clefs.push_back(Clef(Alto)); + clefs.push_back(Clef(Mezzosoprano)); + clefs.push_back(Clef(Soprano)); + clefs.push_back(Clef(French)); + clefs.push_back(Clef(Treble)); + return clefs; +} + +Event *Clef::getAsEvent(timeT absoluteTime) const +{ + Event *e = new Event(EventType, absoluteTime, 0, EventSubOrdering); + e->set<String>(ClefPropertyName, m_clef); + e->set<Int>(OctaveOffsetPropertyName, m_octaveOffset); + return e; +} + + +////////////////////////////////////////////////////////////////////// +// Key +////////////////////////////////////////////////////////////////////// + +Key::KeyDetailMap Key::m_keyDetailMap = Key::KeyDetailMap(); + +const string Key::EventType = "keychange"; +const int Key::EventSubOrdering = -200; +const PropertyName Key::KeyPropertyName = "key"; +const Key Key::DefaultKey = Key("C major"); + +Key::Key() : + m_name(DefaultKey.m_name), + m_accidentalHeights(0) +{ + checkMap(); +} + + +Key::Key(const Event &e) : + m_name(""), + m_accidentalHeights(0) +{ + checkMap(); + if (e.getType() != EventType) { + std::cerr << Event::BadType + ("Key model event", EventType, e.getType()).getMessage() + << std::endl; + return; + } + e.get<String>(KeyPropertyName, m_name); + if (m_keyDetailMap.find(m_name) == m_keyDetailMap.end()) { + std::cerr << BadKeyName + ("No such key as \"" + m_name + "\"").getMessage() << std::endl; + return; + } +} + +Key::Key(const std::string &name) : + m_name(name), + m_accidentalHeights(0) +{ + checkMap(); + if (m_keyDetailMap.find(m_name) == m_keyDetailMap.end()) { + throw BadKeyName("No such key as \"" + m_name + "\""); + } +} + +Key::Key(int accidentalCount, bool isSharp, bool isMinor) : + m_accidentalHeights(0) +{ + checkMap(); + for (KeyDetailMap::const_iterator i = m_keyDetailMap.begin(); + i != m_keyDetailMap.end(); ++i) { + if ((*i).second.m_sharpCount == accidentalCount && + (*i).second.m_minor == isMinor && + ((*i).second.m_sharps == isSharp || + (*i).second.m_sharpCount == 0)) { + m_name = (*i).first; + return; + } + } + +#if (__GNUC__ < 3) + std::ostrstream os; +#else + std::ostringstream os; +#endif + + os << "No " << (isMinor ? "minor" : "major") << " key with " + << accidentalCount << (isSharp ? " sharp(s)" : " flat(s)"); + +#if (__GNUC__ < 3) + os << std::ends; +#endif + + throw BadKeySpec(os.str()); +} + +// Unfortunately this is ambiguous -- e.g. B major / Cb major. +// We need an isSharp argument, but we already have a constructor +// with that signature. Not quite sure what's the best solution. + +Key::Key(int tonicPitch, bool isMinor) : + m_accidentalHeights(0) +{ + checkMap(); + for (KeyDetailMap::const_iterator i = m_keyDetailMap.begin(); + i != m_keyDetailMap.end(); ++i) { + if ((*i).second.m_tonicPitch == tonicPitch && + (*i).second.m_minor == isMinor) { + m_name = (*i).first; + return; + } + } + +#if (__GNUC__ < 3) + std::ostrstream os; +#else + std::ostringstream os; +#endif + + os << "No " << (isMinor ? "minor" : "major") << " key with tonic pitch " + << tonicPitch; + +#if (__GNUC__ < 3) + os << std::ends; +#endif + + throw BadKeySpec(os.str()); +} + + +Key::Key(const Key &kc) : + m_name(kc.m_name), + m_accidentalHeights(0) +{ +} + +Key& Key::operator=(const Key &kc) +{ + m_name = kc.m_name; + m_accidentalHeights = 0; + return *this; +} + +bool Key::isValid(const Event &e) +{ + if (e.getType() != EventType) return false; + std::string name; + e.get<String>(KeyPropertyName, name); + if (m_keyDetailMap.find(name) == m_keyDetailMap.end()) return false; + return true; +} + +Key::KeyList Key::getKeys(bool minor) +{ + checkMap(); + KeyList result; + for (KeyDetailMap::const_iterator i = m_keyDetailMap.begin(); + i != m_keyDetailMap.end(); ++i) { + if ((*i).second.m_minor == minor) { + result.push_back(Key((*i).first)); + } + } + return result; +} + +Key::Key Key::transpose(int pitchDelta, int heightDelta) +{ + Pitch tonic(getTonicPitch()); + Pitch newTonic = tonic.transpose(*this, pitchDelta, heightDelta); + int newTonicPitch = (newTonic.getPerformancePitch() % 12 + 12) % 12; + return Key (newTonicPitch, isMinor()); +} + +Accidental Key::getAccidentalAtHeight(int height, const Clef &clef) const +{ + checkAccidentalHeights(); + height = canonicalHeight(height); + for (unsigned int i = 0; i < m_accidentalHeights->size(); ++i) { + if (height ==static_cast<int>(canonicalHeight((*m_accidentalHeights)[i] + + clef.getPitchOffset()))) { + return isSharp() ? Sharp : Flat; + } + } + return NoAccidental; +} + +Accidental Key::getAccidentalForStep(int step) const +{ + if (isMinor()) { + step = (step + 5) % 7; + } + + int accidentalCount = getAccidentalCount(); + + if (accidentalCount == 0) { + return NoAccidental; + } + + bool sharp = isSharp(); + + int currentAccidentalPosition = sharp ? 6 : 3; + + for (int i = 1; i <= accidentalCount; i++) { + if (step == currentAccidentalPosition) { + return sharp ? Sharp : Flat; + } + + currentAccidentalPosition = + (currentAccidentalPosition + (sharp ? 3 : 4)) % 7; + } + + return NoAccidental; +} + +vector<int> Key::getAccidentalHeights(const Clef &clef) const +{ + // staff positions of accidentals + checkAccidentalHeights(); + vector<int> v(*m_accidentalHeights); + int offset = clef.getPitchOffset(); + + for (unsigned int i = 0; i < v.size(); ++i) { + v[i] += offset; + if (offset > 0) + if (v[i] > 8) v[i] -= 7; + } + return v; +} + +void Key::checkAccidentalHeights() const +{ + if (m_accidentalHeights) return; + m_accidentalHeights = new vector<int>; + + bool sharp = isSharp(); + int accidentals = getAccidentalCount(); + int height = sharp ? 8 : 4; + + for (int i = 0; i < accidentals; ++i) { + m_accidentalHeights->push_back(height); + if (sharp) { height -= 3; if (height < 3) height += 7; } + else { height += 3; if (height > 7) height -= 7; } + } +} + +int Key::convertFrom(int p, const Key &previousKey, + const Accidental &explicitAccidental) const +{ + Pitch pitch(p, explicitAccidental); + int height = pitch.getHeightOnStaff(Clef(), previousKey); + Pitch newPitch(height, Clef(), *this, explicitAccidental); + return newPitch.getPerformancePitch(); +} + +int Key::transposeFrom(int pitch, const Key &previousKey) const +{ + int delta = getTonicPitch() - previousKey.getTonicPitch(); + if (delta > 6) delta -= 12; + if (delta < -6) delta += 12; + return pitch + delta; +} + +Event *Key::getAsEvent(timeT absoluteTime) const +{ + Event *e = new Event(EventType, absoluteTime, 0, EventSubOrdering); + e->set<String>(KeyPropertyName, m_name); + return e; +} + + +void Key::checkMap() { + if (!m_keyDetailMap.empty()) return; + + m_keyDetailMap["A major" ] = KeyDetails(true, false, 3, "F# minor", "A maj / F# min", 9); + m_keyDetailMap["F# minor"] = KeyDetails(true, true, 3, "A major", "A maj / F# min", 6); + m_keyDetailMap["Ab major"] = KeyDetails(false, false, 4, "F minor", "Ab maj / F min", 8); + m_keyDetailMap["F minor" ] = KeyDetails(false, true, 4, "Ab major", "Ab maj / F min", 5); + m_keyDetailMap["B major" ] = KeyDetails(true, false, 5, "G# minor", "B maj / G# min", 11); + m_keyDetailMap["G# minor"] = KeyDetails(true, true, 5, "B major", "B maj / G# min", 8); + m_keyDetailMap["Bb major"] = KeyDetails(false, false, 2, "G minor", "Bb maj / G min", 10); + m_keyDetailMap["G minor" ] = KeyDetails(false, true, 2, "Bb major", "Bb maj / G min", 7); + m_keyDetailMap["C major" ] = KeyDetails(true, false, 0, "A minor", "C maj / A min", 0); + m_keyDetailMap["A minor" ] = KeyDetails(false, true, 0, "C major", "C maj / A min", 9); + m_keyDetailMap["Cb major"] = KeyDetails(false, false, 7, "Ab minor", "Cb maj / Ab min", 11); + m_keyDetailMap["Ab minor"] = KeyDetails(false, true, 7, "Cb major", "Cb maj / Ab min", 8); + m_keyDetailMap["C# major"] = KeyDetails(true, false, 7, "A# minor", "C# maj / A# min", 1); + m_keyDetailMap["A# minor"] = KeyDetails(true, true, 7, "C# major", "C# maj / A# min", 10); + m_keyDetailMap["D major" ] = KeyDetails(true, false, 2, "B minor", "D maj / B min", 2); + m_keyDetailMap["B minor" ] = KeyDetails(true, true, 2, "D major", "D maj / B min", 11); + m_keyDetailMap["Db major"] = KeyDetails(false, false, 5, "Bb minor", "Db maj / Bb min", 1); + m_keyDetailMap["Bb minor"] = KeyDetails(false, true, 5, "Db major", "Db maj / Bb min", 10); + m_keyDetailMap["E major" ] = KeyDetails(true, false, 4, "C# minor", "E maj / C# min", 4); + m_keyDetailMap["C# minor"] = KeyDetails(true, true, 4, "E major", "E maj / C# min", 1); + m_keyDetailMap["Eb major"] = KeyDetails(false, false, 3, "C minor", "Eb maj / C min", 3); + m_keyDetailMap["C minor" ] = KeyDetails(false, true, 3, "Eb major", "Eb maj / C min", 0); + m_keyDetailMap["F major" ] = KeyDetails(false, false, 1, "D minor", "F maj / D min", 5); + m_keyDetailMap["D minor" ] = KeyDetails(false, true, 1, "F major", "F maj / D min", 2); + m_keyDetailMap["F# major"] = KeyDetails(true, false, 6, "D# minor", "F# maj / D# min", 6); + m_keyDetailMap["D# minor"] = KeyDetails(true, true, 6, "F# major", "F# maj / D# min", 3); + m_keyDetailMap["G major" ] = KeyDetails(true, false, 1, "E minor", "G maj / E min", 7); + m_keyDetailMap["E minor" ] = KeyDetails(true, true, 1, "G major", "G maj / E min", 4); + m_keyDetailMap["Gb major"] = KeyDetails(false, false, 6, "Eb minor", "Gb maj / Eb min", 6); + m_keyDetailMap["Eb minor"] = KeyDetails(false, true, 6, "Gb major", "Gb maj / Eb min", 3); +} + + +Key::KeyDetails::KeyDetails() + : m_sharps(false), m_minor(false), m_sharpCount(0), + m_equivalence(""), m_rg2name(""), m_tonicPitch(0) +{ +} + +Key::KeyDetails::KeyDetails(bool sharps, bool minor, int sharpCount, + std::string equivalence, std::string rg2name, + int tonicPitch) + : m_sharps(sharps), m_minor(minor), m_sharpCount(sharpCount), + m_equivalence(equivalence), m_rg2name(rg2name), m_tonicPitch(tonicPitch) +{ +} + +Key::KeyDetails::KeyDetails(const Key::KeyDetails &d) + : m_sharps(d.m_sharps), m_minor(d.m_minor), + m_sharpCount(d.m_sharpCount), m_equivalence(d.m_equivalence), + m_rg2name(d.m_rg2name), m_tonicPitch(d.m_tonicPitch) +{ +} + +Key::KeyDetails& Key::KeyDetails::operator=(const Key::KeyDetails &d) +{ + if (&d == this) return *this; + m_sharps = d.m_sharps; m_minor = d.m_minor; + m_sharpCount = d.m_sharpCount; m_equivalence = d.m_equivalence; + m_rg2name = d.m_rg2name; m_tonicPitch = d.m_tonicPitch; + return *this; +} + +////////////////////////////////////////////////////////////////////// +// Indication +////////////////////////////////////////////////////////////////////// + +const std::string Indication::EventType = "indication"; +const int Indication::EventSubOrdering = -50; +const PropertyName Indication::IndicationTypePropertyName = "indicationtype"; +//const PropertyName Indication::IndicationDurationPropertyName = "indicationduration"; +static const PropertyName IndicationDurationPropertyName = "indicationduration";//!!! + +const std::string Indication::Slur = "slur"; +const std::string Indication::PhrasingSlur = "phrasingslur"; +const std::string Indication::Crescendo = "crescendo"; +const std::string Indication::Decrescendo = "decrescendo"; +const std::string Indication::Glissando = "glissando"; +const std::string Indication::QuindicesimaUp = "ottava2up"; +const std::string Indication::OttavaUp = "ottavaup"; +const std::string Indication::OttavaDown = "ottavadown"; +const std::string Indication::QuindicesimaDown = "ottava2down"; + +Indication::Indication(const Event &e) +{ + if (e.getType() != EventType) { + throw Event::BadType("Indication model event", EventType, e.getType()); + } + std::string s; + e.get<String>(IndicationTypePropertyName, s); + if (!isValid(s)) { + throw BadIndicationName("No such indication as \"" + s + "\""); + } + m_indicationType = s; + + m_duration = e.getDuration(); + if (m_duration == 0) { + e.get<Int>(IndicationDurationPropertyName, m_duration); // obsolete property + } +} + +Indication::Indication(const std::string &s, timeT indicationDuration) +{ + if (!isValid(s)) { + throw BadIndicationName("No such indication as \"" + s + "\""); + } + m_indicationType = s; + m_duration = indicationDuration; +} + +Indication & +Indication::operator=(const Indication &m) +{ + if (&m != this) { + m_indicationType = m.m_indicationType; + m_duration = m.m_duration; + } + return *this; +} + +Event * +Indication::getAsEvent(timeT absoluteTime) const +{ + Event *e = new Event(EventType, absoluteTime, m_duration, EventSubOrdering); + e->set<String>(IndicationTypePropertyName, m_indicationType); + + // Set this obsolete property as well, as otherwise we could actually + // crash earlier versions of RG by loading files exported from this one! + e->set<Int>(IndicationDurationPropertyName, m_duration); + + return e; +} + +bool +Indication::isValid(const std::string &s) const +{ + return + (s == Slur || s == PhrasingSlur || + s == Crescendo || s == Decrescendo || + s == Glissando || + s == QuindicesimaUp || s == OttavaUp || + s == OttavaDown || s == QuindicesimaDown); +} + + + +////////////////////////////////////////////////////////////////////// +// Text +////////////////////////////////////////////////////////////////////// + +const std::string Text::EventType = "text"; +const int Text::EventSubOrdering = -70; +const PropertyName Text::TextPropertyName = "text"; +const PropertyName Text::TextTypePropertyName = "type"; +const PropertyName Text::LyricVersePropertyName = "verse"; + +// text styles +const std::string Text::UnspecifiedType = "unspecified"; +const std::string Text::StaffName = "staffname"; +const std::string Text::ChordName = "chordname"; +const std::string Text::KeyName = "keyname"; +const std::string Text::Dynamic = "dynamic"; +const std::string Text::Lyric = "lyric"; +const std::string Text::Chord = "chord"; +const std::string Text::Direction = "direction"; +const std::string Text::LocalDirection = "local_direction"; +const std::string Text::Tempo = "tempo"; +const std::string Text::LocalTempo = "local_tempo"; +const std::string Text::Annotation = "annotation"; +const std::string Text::LilyPondDirective = "lilypond_directive"; + +// special LilyPond directives +const std::string Text::Segno = "Segno"; +const std::string Text::Coda = "Coda"; +const std::string Text::Alternate1 = "Alt1 ->"; +const std::string Text::Alternate2 = "Alt2 ->"; +const std::string Text::BarDouble = "|| ->"; +const std::string Text::BarEnd = "|. ->"; +const std::string Text::BarDot = ": ->"; +const std::string Text::Gliss = "Gliss."; +const std::string Text::Arpeggio = "Arp."; +//const std::string Text::ArpeggioUp = "Arp.^"; +//const std::string Text::ArpeggioDn = "Arp._"; +const std::string Text::Tiny = "tiny ->"; +const std::string Text::Small = "small ->"; +const std::string Text::NormalSize = "norm. ->"; + +Text::Text(const Event &e) : + m_verse(0) +{ + if (e.getType() != EventType) { + throw Event::BadType("Text model event", EventType, e.getType()); + } + + m_text = ""; + m_type = Text::UnspecifiedType; + + e.get<String>(TextPropertyName, m_text); + e.get<String>(TextTypePropertyName, m_type); + e.get<Int>(LyricVersePropertyName, m_verse); +} + +Text::Text(const std::string &s, const std::string &type) : + m_text(s), + m_type(type), + m_verse(0) +{ + // nothing else +} + +Text::Text(const Text &t) : + m_text(t.m_text), + m_type(t.m_type), + m_verse(t.m_verse) +{ + // nothing else +} + +Text & +Text::operator=(const Text &t) +{ + if (&t != this) { + m_text = t.m_text; + m_type = t.m_type; + m_verse = t.m_verse; + } + return *this; +} + +Text::~Text() +{ + // nothing +} + +bool +Text::isTextOfType(Event *e, std::string type) +{ + return (e->isa(EventType) && + e->has(TextTypePropertyName) && + e->get<String>(TextTypePropertyName) == type); +} + +std::vector<std::string> +Text::getUserStyles() +{ + std::vector<std::string> v; + + v.push_back(Dynamic); + v.push_back(Direction); + v.push_back(LocalDirection); + v.push_back(Tempo); + v.push_back(LocalTempo); + v.push_back(Chord); + v.push_back(Lyric); + v.push_back(Annotation); + v.push_back(LilyPondDirective); + + return v; +} + +std::vector<std::string> +Text::getLilyPondDirectives() +{ + std::vector<std::string> v; + + v.push_back(Alternate1); + v.push_back(Alternate2); + v.push_back(Segno); + v.push_back(Coda); + v.push_back(BarDouble); + v.push_back(BarEnd); + v.push_back(BarDot); + v.push_back(Gliss); + v.push_back(Arpeggio); +// v.push_back(ArpeggioUp); +// v.push_back(ArpeggioDn); + v.push_back(Tiny); + v.push_back(Small); + v.push_back(NormalSize); + + return v; +} + +Event * +Text::getAsEvent(timeT absoluteTime) const +{ + Event *e = new Event(EventType, absoluteTime, 0, EventSubOrdering); + e->set<String>(TextPropertyName, m_text); + e->set<String>(TextTypePropertyName, m_type); + if (m_type == Lyric) e->set<Int>(LyricVersePropertyName, m_verse); + return e; +} + +bool +pitchInKey(int pitch, const Key& key) +{ + int pitchOffset = (pitch - key.getTonicPitch() + 12) % 12; + + static int pitchInMajor[] = + { true, false, true, false, true, true, false, true, false, true, false, true }; + static int pitchInMinor[] = + { true, false, true, true, false, true, false, true, true, false, true, false }; + + if (key.isMinor()) { + return pitchInMinor[pitchOffset]; + } + else { + return pitchInMajor[pitchOffset]; + } +} + +/** + * @param pitch in the range 0..11 (C..B) + * + * @author Arnout Engelen + */ +Accidental +resolveNoAccidental(int pitch, + const Key &key, + NoAccidentalStrategy noAccidentalStrategy) +{ + Accidental outputAccidental = ""; + + // Find out the accidental to use, based on the strategy specified + switch (noAccidentalStrategy) { + case UseKeySharpness: + noAccidentalStrategy = + key.isSharp() ? UseSharps : UseFlats; + // fall though + case UseFlats: + // shares code with UseSharps + case UseSharps: + if (pitchInKey(pitch, key)) { + outputAccidental = NoAccidental; + } + else { + if (noAccidentalStrategy == UseSharps) { + outputAccidental = Sharp; + } + else { + outputAccidental = Flat; + } + } + break; + case UseKey: + // the distance of the pitch from the tonic of the current + // key + int pitchOffset = (pitch - key.getTonicPitch() + 12) % 12; + // 0: major, 1: minor + int minor = key.isMinor(); + static int pitchToHeight[2][12] = + { + { 0, 0, 1, 2, 2, 3, 3, 4, 5, 5, 6, 6 }, + // a ., b, c, ., d, ., e, f, ., g, . + { 0, 1, 1, 2, 2, 3, 4, 4, 5, 5, 6, 6 } + }; + + // map pitchOffset to the extra correction, on top of any + // accidentals in the key. Example: in F major, with a pitchOffset + // of 6, the resulting height would be 3 (Bb) and the correction + // would be +1, so the resulting note would be B-natural + static int pitchToCorrection[2][12] = + { + { 0, +1, 0, -1, 0, 0, +1, 0, -1, 0, -1, 0 }, + { 0, -1, 0, 0, +1, 0, -1, 0, 0, +1, 0, +1 } + }; + + int correction = pitchToCorrection[minor][pitchOffset]; + + // Get the accidental normally associated with this height in this + // key. + Accidental normalAccidental = key.getAccidentalForStep(pitchToHeight[minor][pitchOffset]); + + // Apply the pitchCorrection and get the outputAccidental + outputAccidental = Accidentals::getAccidental( + getPitchOffset(normalAccidental) + correction); + + } + + return outputAccidental; +} + +/** + * @param pitch in the range 0..11 (C..B) + * + * @author Michael McIntyre + */ +void +resolveSpecifiedAccidental(int pitch, + const Clef &clef, + const Key &key, + int &height, + int &octave, + Accidental &inputAccidental, + Accidental &outputAccidental) +{ + // 4. Get info from the Key + long accidentalCount = key.getAccidentalCount(); + bool keyIsSharp = key.isSharp(), keyIsFlat = !keyIsSharp; + + // Calculate the flags needed for resolving accidentals against the key. + // First we initialize them false... + bool keyHasSharpC = false, keyHasSharpD = false, keyHasSharpE = false, + keyHasSharpF = false, keyHasSharpG = false, keyHasSharpA = false, + keyHasSharpB = false, keyHasFlatC = false, keyHasFlatD = false, + keyHasFlatE = false, keyHasFlatF = false, keyHasFlatG = false, + keyHasFlatA = false, keyHasFlatB = false; + + // Then we use "trip points" based on the flat/sharp state of the key and + // its number of accidentals to set the flags: + if (keyIsSharp) { + switch (accidentalCount) { + case 7: keyHasSharpB = true; + case 6: keyHasSharpE = true; + case 5: keyHasSharpA = true; + case 4: keyHasSharpD = true; + case 3: keyHasSharpG = true; + case 2: keyHasSharpC = true; + case 1: keyHasSharpF = true; + } + } else { + switch (accidentalCount) { + case 7: keyHasFlatF = true; + case 6: keyHasFlatC = true; + case 5: keyHasFlatG = true; + case 4: keyHasFlatD = true; + case 3: keyHasFlatA = true; + case 2: keyHasFlatE = true; + case 1: keyHasFlatB = true; + } + } + + + // 5. Determine height on staff and accidental note should display with for key... + // + // Every position on the staff is one of six accidental states: + // + // Natural, Sharp, Flat, DoubleSharp, DoubleFlat, NoAccidental + // + // DoubleSharp and DoubleFlat are always user-specified accidentals, so + // they are always used to decide how to draw the note, and they are + // always passed along unchanged. + // + // The Natural state indicates that a note is or might be going against + // the key. Since the Natural state will always be attached to a plain + // pitch that can never resolve to a "black key" note, it is not necessary + // to handle this case differently unless the key has "white key" notes + // that are supposed to take accidentals for the key. (eg. Cb Gb B C# major) + // For most keys we treat it the same as a NoAccidental, and use the key + // to decide where to draw the note, and what accidental to return. + // + // The Sharp and Flat states indicate that a user has specified an + // accidental for the note, and it might be "out of key." We check to see + // if that's the case. If the note is "in key" then the extra accidental + // property is removed, and we return NoAccidental. If the note is "out of + // key" then the Sharp or Flat is used to decide where to draw the note, and + // the accidental is passed along unchanged. (Incomplete? Will a failure + // to always pass along the accidental cause strange behavior if a user + // specifies an explicit Bb in key of F and then transposes to G, wishing + // the Bb to remain an explicit Bb? If someone complains, I'll know where + // to look.) + // + // The NoAccidental state is a default state. We have nothing else upon + // which to base a decision in this case, so we make the best decisions + // possible using only the pitch and key. Notes that are "in key" pass on + // with NoAccidental preserved, otherwise we return an appropriate + // accidental for the key. + + // We calculate height on a virtual staff, and then make necessary adjustments to + // translate them onto a particular Clef later on... + // + // ---------F--------- Staff Height Note(semitone) for each of five states: + // E + // ---------D--------- Natural| Sharp | Flat |DblSharp| DblFlat + // C | | | | + // ---------B--------- height 4 B(11) | B#( 0) | Bb(10) | Bx( 1) | Bbb( 9) + // A height 3 A( 9) | A#(10) | Ab( 8) | Ax(11) | Abb( 7) + // ---------G--------- height 2 G( 7) | G#( 8) | Gb( 6) | Gx( 9) | Gbb( 5) + // F height 1 F( 5) | F#( 6) | Fb( 4) | Fx( 7) | Fbb( 3) + // ---------E--------- height 0 E( 4) | E#( 5) | Eb( 3) | Ex( 6) | Ebb( 2) + // D height -1 D( 2) | D#( 3) | Db( 1) | Dx( 4) | Dbb( 0) + // ---C---- height -2 C( 0) | C#( 1) | Cb(11) | Cx( 2) | Cbb(10) + + + // use these constants instead of numeric literals in order to reduce the + // chance of making incorrect height assignments... + const int C = -2, D = -1, E = 0, F = 1, G = 2, A = 3, B = 4; + + // Here we do the actual work of making all the decisions explained above. + switch (pitch) { + case 0 : + if (inputAccidental == Sharp || // B# + (inputAccidental == NoAccidental && keyHasSharpB)) { + height = B; + octave--; + outputAccidental = (keyHasSharpB) ? NoAccidental : Sharp; + } else if (inputAccidental == DoubleFlat) { // Dbb + height = D; + outputAccidental = DoubleFlat; + } else { + height = C; // C or C-Natural + outputAccidental = (keyHasFlatC || keyHasSharpC || + (keyHasSharpB && + inputAccidental == Natural)) ? Natural : NoAccidental; + } + break; + case 1 : + if (inputAccidental == Sharp || // C# + (inputAccidental == NoAccidental && keyIsSharp)) { + height = C; + outputAccidental = (keyHasSharpC) ? NoAccidental : Sharp; + } else if (inputAccidental == Flat || // Db + (inputAccidental == NoAccidental && keyIsFlat)) { + height = D; + outputAccidental = (keyHasFlatD) ? NoAccidental : Flat; + } else if (inputAccidental == DoubleSharp) { // Bx + height = B; + octave--; + outputAccidental = DoubleSharp; + } + break; + case 2 : + if (inputAccidental == DoubleSharp) { // Cx + height = C; + outputAccidental = DoubleSharp; + } else if (inputAccidental == DoubleFlat) { // Ebb + height = E; + outputAccidental = DoubleFlat; + } else { // D or D-Natural + height = D; + outputAccidental = (keyHasSharpD || keyHasFlatD) ? Natural : NoAccidental; + } + break; + case 3 : + if (inputAccidental == Sharp || // D# + (inputAccidental == NoAccidental && keyIsSharp)) { + height = D; + outputAccidental = (keyHasSharpD) ? NoAccidental : Sharp; + } else if (inputAccidental == Flat || // Eb + (inputAccidental == NoAccidental && keyIsFlat)) { + height = E; + outputAccidental = (keyHasFlatE) ? NoAccidental : Flat; + } else if (inputAccidental == DoubleFlat) { // Fbb + height = F; + outputAccidental = DoubleFlat; + } + break; + case 4 : + if (inputAccidental == Flat || // Fb + (inputAccidental == NoAccidental && keyHasFlatF)) { + height = F; + outputAccidental = (keyHasFlatF) ? NoAccidental : Flat; + } else if (inputAccidental == DoubleSharp) { // Dx + height = D; + outputAccidental = DoubleSharp; + } else { // E or E-Natural + height = E; + outputAccidental = (keyHasSharpE || keyHasFlatE || + (keyHasFlatF && inputAccidental==Natural)) ? + Natural : NoAccidental; + } + break; + case 5 : + if (inputAccidental == Sharp || // E# + (inputAccidental == NoAccidental && keyHasSharpE)) { + height = E; + outputAccidental = (keyHasSharpE) ? NoAccidental : Sharp; + } else if (inputAccidental == DoubleFlat) { // Gbb + height = G; + outputAccidental = DoubleFlat; + } else { // F or F-Natural + height = F; + outputAccidental = (keyHasSharpF || keyHasFlatF || + (keyHasSharpE && inputAccidental==Natural))? + Natural : NoAccidental; + } + break; + case 6 : + if (inputAccidental == Sharp || + (inputAccidental == NoAccidental && keyIsSharp)) { // F# + height = F; + outputAccidental = (keyHasSharpF) ? NoAccidental : Sharp; + } else if (inputAccidental == Flat || // Gb + (inputAccidental == NoAccidental && keyIsFlat)) { + height = G; + outputAccidental = (keyHasFlatG) ? NoAccidental : Flat; + } else if (inputAccidental == DoubleSharp) { // Ex + height = E; + outputAccidental = DoubleSharp; + } + break; + case 7 : + if (inputAccidental == DoubleSharp) { // Fx + height = F; + outputAccidental = DoubleSharp; + } else if (inputAccidental == DoubleFlat) { // Abb + height = A; + outputAccidental = DoubleFlat; + } else { // G or G-Natural + height = G; + outputAccidental = (keyHasSharpG || keyHasFlatG) ? Natural : NoAccidental; + } + break; + case 8 : + if (inputAccidental == Sharp || + (inputAccidental == NoAccidental && keyIsSharp)) { // G# + height = G; + outputAccidental = (keyHasSharpG) ? NoAccidental : Sharp; + } else if (inputAccidental == Flat || // Ab + (inputAccidental == NoAccidental && keyIsFlat)) { + height = A; + outputAccidental = (keyHasFlatA) ? NoAccidental : Flat; + } + break; + case 9 : + if (inputAccidental == DoubleSharp) { // Gx + height = G; + outputAccidental = DoubleSharp; + } else if (inputAccidental == DoubleFlat) { // Bbb + height = B; + outputAccidental = DoubleFlat; + } else { // A or A-Natural + height = A; + outputAccidental = (keyHasSharpA || keyHasFlatA) ? Natural : NoAccidental; + } + break; + case 10: + if (inputAccidental == DoubleFlat) { // Cbb + height = C; + octave++; // tweak B/C divide + outputAccidental = DoubleFlat; + } else if (inputAccidental == Sharp || // A# + (inputAccidental == NoAccidental && keyIsSharp)) { + height = A; + outputAccidental = (keyHasSharpA) ? NoAccidental : Sharp; + } else if (inputAccidental == Flat || // Bb + (inputAccidental == NoAccidental && keyIsFlat)) { + height = B; + outputAccidental = (keyHasFlatB) ? NoAccidental : Flat; + } + break; + case 11: + if (inputAccidental == DoubleSharp) { // Ax + height = A; + outputAccidental = DoubleSharp; + } else if (inputAccidental == Flat || // Cb + (inputAccidental == NoAccidental && keyHasFlatC)) { + height = C; + octave++; // tweak B/C divide + outputAccidental = (keyHasFlatC) ? NoAccidental : Flat; + } else { // B or B-Natural + height = B; + outputAccidental = (keyHasSharpB || keyHasFlatB || + (keyHasFlatC && inputAccidental==Natural)) ? + Natural : NoAccidental; + } + } + + if (outputAccidental == NoAccidental && inputAccidental == Natural) { + outputAccidental = Natural; + } + +} + +bool +Pitch::validAccidental() const +{ +// std::cout << "Checking whether accidental is valid " << std::endl; + if (m_accidental == NoAccidental) + { + return true; + } + int naturalPitch = (m_pitch - + Accidentals::getPitchOffset(m_accidental) + 12) % 12; + switch(naturalPitch) + { + case 0: //C + return true; + case 1: + return false; + case 2: //D + return true; + case 3: + return false; + case 4: //E + return true; + case 5: //F + return true; + case 6: + return false; + case 7: //G + return true; + case 8: + return false; + case 9: //A + return true; + case 10: + return false; + case 11: //B + return true; + }; + std::cout << "Internal error in validAccidental" << std::endl; + return false; +} + +Event * +Pitch::getAsNoteEvent(timeT absoluteTime, timeT duration) const +{ + Event *e = new Event(Note::EventType, absoluteTime, duration); + e->set<Int>(BaseProperties::PITCH, m_pitch); + e->set<String>(BaseProperties::ACCIDENTAL, m_accidental); + return e; +} + +/** + * Converts performance pitch to height on staff + correct accidentals + * for current key. + * + * This method takes a Clef, Key, Accidental and raw performance pitch, then + * applies this information to return a height on staff value and an + * accidental state. The pitch itself contains a lot of information, but we + * need to use the Key and user-specified Accidental to make an accurate + * decision just where to put it on the staff, and what accidental it should + * display for (or against) the key. + * + * This function originally written by Chris Cannam for Rosegarden 2.1 + * Entirely rewritten by Chris Cannam for Rosegarden 4 + * Entirely rewritten by Hans Kieserman + * Entirely rewritten by Michael McIntyre + * This version by Michael McIntyre <dmmcintyr@users.sourceforge.net> + * Resolving the accidental was refactored out by Arnout Engelen + */ +void +Pitch::rawPitchToDisplayPitch(int rawpitch, + const Clef &clef, + const Key &key, + int &height, + Accidental &accidental, + NoAccidentalStrategy noAccidentalStrategy) +{ + + // 1. Calculate the octave (for later): + int octave = rawpitch / 12; + + // 2. Set initial height to 0 + height = 0; + + // 3. Calculate raw semitone number, yielding a value between 0 (C) and + // 11 (B) + int pitch = rawpitch % 12; + + // clear the in-coming accidental so we can trap any failure to re-set + // it on the way out: + Accidental userAccidental = accidental; + accidental = ""; + + if (userAccidental == NoAccidental || !Pitch(rawpitch, userAccidental).validAccidental()) + { + userAccidental = resolveNoAccidental(pitch, key, noAccidentalStrategy); + //std::cout << "Chose accidental " << userAccidental << " for pitch " << pitch << + // " in key " << key.getName() << std::endl; + } + //else + //{ + // std::cout << "Accidental was specified, as " << userAccidental << std::endl; + //} + + resolveSpecifiedAccidental(pitch, clef, key, height, octave, userAccidental, accidental); + + // Failsafe... If this ever executes, there's trouble to fix... +// WIP - DMM - munged up to explore #937389, which is temporarily deferred, +// owing to its non-critical nature, having been hacked around in the LilyPond +// code +#ifndef DEBUG_PITCH + if (accidental == "") { + std::cerr << "Pitch::rawPitchToDisplayPitch(): error! returning null accidental for:" +#else + std::cerr << "Pitch::rawPitchToDisplayPitch(): calculating: " +#endif + << std::endl << "pitch: " << rawpitch << " (" << pitch << " in oct " + << octave << ") userAcc: " << userAccidental + << " clef: " << clef.getClefType() << " key: " << key.getName() << std::endl; +#ifndef DEBUG_PITCH + } +#endif + + + // 6. "Recenter" height in case it's been changed: + height = ((height + 2) % 7) - 2; + + height += (octave - 5) * 7; + height += clef.getPitchOffset(); + + + // 7. Transpose up or down for the clef: + height -= 7 * clef.getOctave(); +} + +void +Pitch::displayPitchToRawPitch(int height, + Accidental accidental, + const Clef &clef, + const Key &key, + int &pitch, + bool ignoreOffset) +{ + int octave = 5; + + // 1. Ask Key for accidental if necessary + if (accidental == NoAccidental) { + accidental = key.getAccidentalAtHeight(height, clef); + } + + // 2. Get pitch and correct octave + + if (!ignoreOffset) height -= clef.getPitchOffset(); + + while (height < 0) { octave -= 1; height += 7; } + while (height >= 7) { octave += 1; height -= 7; } + + if (height > 4) ++octave; + + // Height is now relative to treble clef lines + switch (height) { + + case 0: pitch = 4; break; /* bottom line, treble clef: E */ + case 1: pitch = 5; break; /* F */ + case 2: pitch = 7; break; /* G */ + case 3: pitch = 9; break; /* A, in next octave */ + case 4: pitch = 11; break; /* B, likewise*/ + case 5: pitch = 0; break; /* C, moved up an octave (see above) */ + case 6: pitch = 2; break; /* D, likewise */ + } + // Pitch is now "natural"-ized note at given height + + // 3. Adjust pitch for accidental + + if (accidental != NoAccidental && + accidental != Natural) { + if (accidental == Sharp) { pitch++; } + else if (accidental == Flat) { pitch--; } + else if (accidental == DoubleSharp) { pitch += 2; } + else if (accidental == DoubleFlat) { pitch -= 2; } + } + + // 4. Adjust for clef + octave += clef.getOctave(); + + pitch += 12 * octave; +} + + + +Pitch::Pitch(const Event &e) : + // throw (Event::NoData) + m_accidental(NoAccidental) +{ + m_pitch = e.get<Int>(BaseProperties::PITCH); + e.get<String>(BaseProperties::ACCIDENTAL, m_accidental); +} + +Pitch::Pitch(int performancePitch, const Accidental &explicitAccidental) : + m_pitch(performancePitch), + m_accidental(explicitAccidental) +{ + // nothing +} + +Pitch::Pitch(int pitchInOctave, int octave, + const Accidental &explicitAccidental, int octaveBase) : + m_pitch((octave - octaveBase) * 12 + pitchInOctave), + m_accidental(explicitAccidental) +{ + // nothing else +} + +Pitch::Pitch(int noteInScale, int octave, const Key &key, + const Accidental &explicitAccidental, int octaveBase) : + m_pitch(0), + m_accidental(explicitAccidental) +{ + m_pitch = (key.getTonicPitch()); + m_pitch = (octave - octaveBase) * 12 + m_pitch % 12; + + if (key.isMinor()) m_pitch += scale_Cminor_harmonic[noteInScale]; + else m_pitch += scale_Cmajor[noteInScale]; + + m_pitch += Accidentals::getPitchOffset(m_accidental); +} + +Pitch::Pitch(int noteInCMajor, int octave, int pitch, + int octaveBase) : + m_pitch(pitch) +{ + int natural = (octave - octaveBase) * 12 + scale_Cmajor[noteInCMajor]; + m_accidental = Accidentals::getAccidental(pitch - natural); +} + + +Pitch::Pitch(char noteName, int octave, const Key &key, + const Accidental &explicitAccidental, int octaveBase) : + m_pitch(0), + m_accidental(explicitAccidental) +{ + int height = getIndexForNote(noteName) - 2; + displayPitchToRawPitch(height, explicitAccidental, + Clef(), key, m_pitch); + + // we now have the pitch within octave 5 (C == 60) -- though it + // might have spilled over at either end + if (m_pitch < 60) --octave; + if (m_pitch > 71) ++octave; + m_pitch = (octave - octaveBase) * 12 + m_pitch % 12; +} + +Pitch::Pitch(int heightOnStaff, const Clef &clef, const Key &key, + const Accidental &explicitAccidental) : + m_pitch(0), + m_accidental(explicitAccidental) +{ + displayPitchToRawPitch + (heightOnStaff, explicitAccidental, clef, key, m_pitch); +} + +Pitch::Pitch(const Pitch &p) : + m_pitch(p.m_pitch), + m_accidental(p.m_accidental) +{ + // nothing else +} + +Pitch & +Pitch::operator=(const Pitch &p) +{ + if (&p != this) { + m_pitch = p.m_pitch; + m_accidental = p.m_accidental; + } + return *this; +} + +int +Pitch::getPerformancePitch() const +{ + return m_pitch; +} + +Accidental +Pitch::getAccidental(bool useSharps) const +{ + return getDisplayAccidental(Key("C major"), + useSharps ? UseSharps : UseFlats); +} + +Accidental +Pitch::getAccidental(const Key &key) const +{ + if (m_accidental == NoAccidental || !validAccidental()) + { + Accidental retval = resolveNoAccidental(m_pitch, key, UseKey); + //std::cout << "Resolved No/invalid accidental: chose " << retval << std::endl; + return retval; + } + else + { + //std::cout << "Returning specified accidental" << std::endl; + return m_accidental; + } +} + +Accidental +Pitch::getDisplayAccidental(const Key &key) const +{ + return getDisplayAccidental(key, UseKey); +} + +Accidental +Pitch::getDisplayAccidental(const Key &key, NoAccidentalStrategy noAccidentalStrategy) const +{ + int heightOnStaff; + Accidental accidental(m_accidental); + rawPitchToDisplayPitch(m_pitch, Clef(), key, heightOnStaff, accidental, noAccidentalStrategy); + return accidental; +} + +int +Pitch::getNoteInScale(const Key &key) const +{ + int p = m_pitch; + p -= key.getTonicPitch(); + p -= Accidentals::getPitchOffset(getDisplayAccidental(key)); + p += 24; // in case these calculations made it -ve + p %= 12; + + if (key.isMinor()) return steps_Cminor_harmonic[p]; + else return steps_Cmajor[p]; +} + +char +Pitch::getNoteName(const Key &key) const +{ + int index = (getHeightOnStaff(Clef(Clef::Treble), key) + 72) % 7; + return getNoteForIndex(index); +} + +int +Pitch::getHeightOnStaff(const Clef &clef, const Key &key) const +{ + int heightOnStaff; + Accidental accidental(m_accidental); + rawPitchToDisplayPitch(m_pitch, clef, key, heightOnStaff, accidental, UseKey); + return heightOnStaff; +} + +int +Pitch::getHeightOnStaff(const Clef &clef, bool useSharps) const +{ + int heightOnStaff; + Accidental accidental(m_accidental); + rawPitchToDisplayPitch(m_pitch, clef, Key("C major"), heightOnStaff, accidental, + useSharps ? UseSharps : UseFlats); + return heightOnStaff; +} + +int +Pitch::getOctave(int octaveBase) const +{ + return m_pitch / 12 + octaveBase; +} + +int +Pitch::getPitchInOctave() const +{ + return m_pitch % 12; +} + +bool +Pitch::isDiatonicInKey(const Key &key) const +{ + if (getDisplayAccidental(key) == Accidentals::NoAccidental) return true; + + // ### as used in the chord identifiers, this calls chords built on + // the raised sixth step diatonic -- may be correct, but it's + // misleading, as we're really looking for whether chords are + // often built on given tone + + if (key.isMinor()) { + int stepsFromTonic = ((m_pitch - key.getTonicPitch() + 12) % 12); + if (stepsFromTonic == 9 || stepsFromTonic == 11) return true; + } + + return false; +} + +std::string +Pitch::getAsString(bool useSharps, bool inclOctave, int octaveBase) const +{ + Accidental acc = getAccidental(useSharps); + + std::string s; + s += getNoteName(useSharps ? Key("C major") : Key("A minor")); + + if (acc == Accidentals::Sharp) s += "#"; + else if (acc == Accidentals::Flat) s += "b"; + + if (!inclOctave) return s; + + char tmp[10]; + sprintf(tmp, "%s%d", s.c_str(), getOctave(octaveBase)); + return std::string(tmp); +} + +int +Pitch::getIndexForNote(char noteName) +{ + if (islower(noteName)) noteName = toupper(noteName); + if (noteName < 'C') { + if (noteName < 'A') return 0; // error, really + else return noteName - 'A' + 5; + } else { + if (noteName > 'G') return 0; // error, really + else return noteName - 'C'; + } +} + +char +Pitch::getNoteForIndex(int index) +{ + if (index < 0 || index > 6) return 'C'; // error, really + return "CDEFGAB"[index]; +} + +int +Pitch::getPerformancePitchFromRG21Pitch(int heightOnStaff, + const Accidental &accidental, + const Clef &clef, + const Key &) +{ + // Rosegarden 2.1 pitches are a bit weird; see + // docs/data_struct/units.txt + + // We pass the accidental and clef, a faked key of C major, and a + // flag telling displayPitchToRawPitch to ignore the clef offset + // and take only its octave into account + + int p = 0; + displayPitchToRawPitch(heightOnStaff, accidental, clef, Key(), p, true); + return p; +} + +Pitch Pitch::transpose(const Key &key, int pitchDelta, int heightDelta) +{ + // get old accidental + Accidental oldAccidental = getAccidental(key); + + // get old step + // TODO: maybe we should write an oldPitchObj.getOctave(0, key) that takes into account accidentals + // properly (e.g. yielding '0' instead of '1' for B#0). For now workaround here. + Pitch oldPitchWithoutAccidental(getPerformancePitch() - Accidentals::getPitchOffset(oldAccidental), Natural); + Key cmaj = Key(); + int oldStep = oldPitchWithoutAccidental.getNoteInScale(cmaj) + oldPitchWithoutAccidental.getOctave(0) * 7; + + // calculate new pitch and step + int newPitch = getPerformancePitch() + pitchDelta; + int newStep = oldStep + heightDelta; + + // could happen for example when transposing the tonic of a key downwards + if (newStep < 0 || newPitch < 0) { + newStep += 7; + newPitch += 12; + } + + // should not happen + if (newStep < 0 || newPitch < 0) { + std::cerr << "Internal error in NotationTypes, Pitch::transpose()" + << std::endl; + } + + // calculate new accidental for step + int pitchWithoutAccidental = ((newStep / 7) * 12 + scale_Cmajor[newStep % 7]); + int newAccidentalOffset = newPitch - pitchWithoutAccidental; + + // construct pitch-object to return + Pitch newPitchObj(newPitch, Accidentals::getAccidental(newAccidentalOffset)); + return newPitchObj; +} + +////////////////////////////////////////////////////////////////////// +// Note +////////////////////////////////////////////////////////////////////// + +const string Note::EventType = "note"; +const string Note::EventRestType = "rest"; +const int Note::EventRestSubOrdering = 10; + +const timeT Note::m_shortestTime = basePPQ / 16; + +Note& Note::operator=(const Note &n) +{ + if (&n == this) return *this; + m_type = n.m_type; + m_dots = n.m_dots; + return *this; +} + +timeT Note::getDurationAux() const +{ + int duration = m_shortestTime * (1 << m_type); + int extra = duration / 2; + for (int dots = m_dots; dots > 0; --dots) { + duration += extra; + extra /= 2; + } + return duration; +} + + +Note Note::getNearestNote(timeT duration, int maxDots) +{ + int tag = Shortest - 1; + timeT d(duration / m_shortestTime); + while (d > 0) { ++tag; d /= 2; } + +// cout << "Note::getNearestNote: duration " << duration << +// " leading to tag " << tag << endl; + if (tag < Shortest) return Note(Shortest); + if (tag > Longest) return Note(Longest, maxDots); + + timeT prospective = Note(tag, 0).getDuration(); + int dots = 0; + timeT extra = prospective / 2; + + while (dots <= maxDots && + dots <= tag) { // avoid TooManyDots exception from Note ctor + prospective += extra; + if (prospective > duration) return Note(tag, dots); + extra /= 2; + ++dots; +// cout << "added another dot okay" << endl; + } + + if (tag < Longest) return Note(tag + 1, 0); + else return Note(tag, std::max(maxDots, tag)); +} + +Event *Note::getAsNoteEvent(timeT absoluteTime, int pitch) const +{ + Event *e = new Event(EventType, absoluteTime, getDuration()); + e->set<Int>(BaseProperties::PITCH, pitch); + return e; +} + +Event *Note::getAsRestEvent(timeT absoluteTime) const +{ + Event *e = new Event(EventRestType, absoluteTime, getDuration()); + return e; +} + + + +////////////////////////////////////////////////////////////////////// +// TimeSignature +////////////////////////////////////////////////////////////////////// + +const string TimeSignature::EventType = "timesignature"; +const int TimeSignature::EventSubOrdering = -150; +const PropertyName TimeSignature::NumeratorPropertyName = "numerator"; +const PropertyName TimeSignature::DenominatorPropertyName = "denominator"; +const PropertyName TimeSignature::ShowAsCommonTimePropertyName = "common"; +const PropertyName TimeSignature::IsHiddenPropertyName = "hidden"; +const PropertyName TimeSignature::HasHiddenBarsPropertyName = "hiddenbars"; +const TimeSignature TimeSignature::DefaultTimeSignature = TimeSignature(4, 4); + +TimeSignature::TimeSignature(int numerator, int denominator, + bool preferCommon, bool hidden, bool hiddenBars) + // throw (BadTimeSignature) + : m_numerator(numerator), m_denominator(denominator), + m_common(preferCommon && + (m_denominator == m_numerator && + (m_numerator == 2 || m_numerator == 4))), + m_hidden(hidden), + m_hiddenBars(hiddenBars) +{ + if (numerator < 1 || denominator < 1) { + throw BadTimeSignature("Numerator and denominator must be positive"); + } +} + +TimeSignature::TimeSignature(const Event &e) + // throw (Event::NoData, Event::BadType, BadTimeSignature) +{ + if (e.getType() != EventType) { + throw Event::BadType("TimeSignature model event", EventType, e.getType()); + } + m_numerator = 4; + m_denominator = 4; + + if (e.has(NumeratorPropertyName)) { + m_numerator = e.get<Int>(NumeratorPropertyName); + } + + if (e.has(DenominatorPropertyName)) { + m_denominator = e.get<Int>(DenominatorPropertyName); + } + + m_common = false; + e.get<Bool>(ShowAsCommonTimePropertyName, m_common); + + m_hidden = false; + e.get<Bool>(IsHiddenPropertyName, m_hidden); + + m_hiddenBars = false; + e.get<Bool>(HasHiddenBarsPropertyName, m_hiddenBars); + + if (m_numerator < 1 || m_denominator < 1) { + throw BadTimeSignature("Numerator and denominator must be positive"); + } +} + +TimeSignature& TimeSignature::operator=(const TimeSignature &ts) +{ + if (&ts == this) return *this; + m_numerator = ts.m_numerator; + m_denominator = ts.m_denominator; + m_common = ts.m_common; + m_hidden = ts.m_hidden; + m_hiddenBars = ts.m_hiddenBars; + return *this; +} + +timeT TimeSignature::getBarDuration() const +{ + setInternalDurations(); + return m_barDuration; +} + +timeT TimeSignature::getBeatDuration() const +{ + setInternalDurations(); + return m_beatDuration; +} + +timeT TimeSignature::getUnitDuration() const +{ + return m_crotchetTime * 4 / m_denominator; +} + +Note::Type TimeSignature::getUnit() const +{ + int c, d; + for (c = 0, d = m_denominator; d > 1; d /= 2) ++c; + return Note::Semibreve - c; +} + +bool TimeSignature::isDotted() const +{ + setInternalDurations(); + return m_dotted; +} + +Event *TimeSignature::getAsEvent(timeT absoluteTime) const +{ + Event *e = new Event(EventType, absoluteTime, 0, EventSubOrdering); + e->set<Int>(NumeratorPropertyName, m_numerator); + e->set<Int>(DenominatorPropertyName, m_denominator); + e->set<Bool>(ShowAsCommonTimePropertyName, m_common); + e->set<Bool>(IsHiddenPropertyName, m_hidden); + e->set<Bool>(HasHiddenBarsPropertyName, m_hiddenBars); + return e; +} + +// This doesn't consider subdivisions of the bar larger than a beat in +// any time other than 4/4, but it should handle the usual time signatures +// correctly (compound time included). + +void TimeSignature::getDurationListForInterval(DurationList &dlist, + timeT duration, + timeT startOffset) const +{ + setInternalDurations(); + + timeT offset = startOffset; + timeT durationRemaining = duration; + + while (durationRemaining > 0) { + + // Everything in this loop is of the form, "if we're on a + // [unit] boundary and there's a [unit] of space left to fill, + // insert a [unit] of time." + + // See if we can insert a bar of time. + + if (offset % m_barDuration == 0 + && durationRemaining >= m_barDuration) { + + getDurationListForBar(dlist); + durationRemaining -= m_barDuration, + offset += m_barDuration; + + } + + // If that fails and we're in 4/4 time, see if we can insert a + // half-bar of time. + + //_else_ if! + else if (m_numerator == 4 && m_denominator == 4 + && offset % (m_barDuration/2) == 0 + && durationRemaining >= m_barDuration/2) { + + dlist.push_back(m_barDuration/2); + durationRemaining -= m_barDuration/2; + offset += m_barDuration; + + } + + // If that fails, see if we can insert a beat of time. + + else if (offset % m_beatDuration == 0 + && durationRemaining >= m_beatDuration) { + + dlist.push_back(m_beatDuration); + durationRemaining -= m_beatDuration; + offset += m_beatDuration; + + } + + // If that fails, see if we can insert a beat-division of time + // (half the beat in simple time, a third of the beat in compound + // time) + + else if (offset % m_beatDivisionDuration == 0 + && durationRemaining >= m_beatDivisionDuration) { + + dlist.push_back(m_beatDivisionDuration); + durationRemaining -= m_beatDivisionDuration; + offset += m_beatDivisionDuration; + + } + + // cc: In practice, if the time we have remaining is shorter + // than our shortest note then we should just insert a single + // unit of the correct time; we won't be able to do anything + // useful with any shorter units anyway. + + else if (durationRemaining <= Note(Note::Shortest).getDuration()) { + + dlist.push_back(durationRemaining); + offset += durationRemaining; + durationRemaining = 0; + + } + + // If that fails, keep halving the beat division until we + // find something to insert. (This could be part of the beat-division + // case; it's only in its own place for clarity.) + + else { + + timeT currentDuration = m_beatDivisionDuration; + + while ( !(offset % currentDuration == 0 + && durationRemaining >= currentDuration) ) { + + if (currentDuration <= Note(Note::Shortest).getDuration()) { + + // okay, this isn't working. If our duration takes + // us past the next beat boundary, fill with an exact + // rest duration to there and then continue --cc + + timeT toNextBeat = + m_beatDuration - (offset % m_beatDuration); + + if (durationRemaining > toNextBeat) { + currentDuration = toNextBeat; + } else { + currentDuration = durationRemaining; + } + break; + } + + currentDuration /= 2; + } + + dlist.push_back(currentDuration); + durationRemaining -= currentDuration; + offset += currentDuration; + + } + + } + +} + +void TimeSignature::getDurationListForBar(DurationList &dlist) const +{ + + // If the bar's length can be represented with one long symbol, do it. + // Otherwise, represent it as individual beats. + + if (m_barDuration == m_crotchetTime || + m_barDuration == m_crotchetTime * 2 || + m_barDuration == m_crotchetTime * 4 || + m_barDuration == m_crotchetTime * 8 || + m_barDuration == m_dottedCrotchetTime || + m_barDuration == m_dottedCrotchetTime * 2 || + m_barDuration == m_dottedCrotchetTime * 4 || + m_barDuration == m_dottedCrotchetTime * 8) { + + dlist.push_back(getBarDuration()); + + } else { + + for (int i = 0; i < getBeatsPerBar(); ++i) { + dlist.push_back(getBeatDuration()); + } + + } + +} + +int TimeSignature::getEmphasisForTime(timeT offset) +{ + setInternalDurations(); + + if (offset % m_barDuration == 0) + return 4; + else if (m_numerator == 4 && m_denominator == 4 && + offset % (m_barDuration/2) == 0) + return 3; + else if (offset % m_beatDuration == 0) + return 2; + else if (offset % m_beatDivisionDuration == 0) + return 1; + else + return 0; +} + + +void TimeSignature::getDivisions(int depth, std::vector<int> &divisions) const +{ + divisions.clear(); + + if (depth <= 0) return; + timeT base = getBarDuration(); // calls setInternalDurations +/* + if (m_numerator == 4 && m_denominator == 4) { + divisions.push_back(2); + base /= 2; + --depth; + } +*/ + if (depth <= 0) return; + + divisions.push_back(base / m_beatDuration); + base = m_beatDuration; + --depth; + + if (depth <= 0) return; + + if (m_dotted) divisions.push_back(3); + else divisions.push_back(2); + --depth; + + while (depth > 0) { + divisions.push_back(2); + --depth; + } + + return; +} + + +void TimeSignature::setInternalDurations() const +{ + int unitLength = m_crotchetTime * 4 / m_denominator; + + m_barDuration = m_numerator * unitLength; + + // Is 3/8 dotted time? This will report that it isn't, because of + // the check for m_numerator > 3 -- but otherwise we'd get a false + // positive with 3/4 + + // [rf] That's an acceptable answer, according to my theory book. In + // practice, you can say it's dotted time iff it has 6, 9, or 12 on top. + + m_dotted = (m_numerator % 3 == 0 && + m_numerator > 3 && + m_barDuration >= m_dottedCrotchetTime); + + if (m_dotted) { + m_beatDuration = unitLength * 3; + m_beatDivisionDuration = unitLength; + } + else { + m_beatDuration = unitLength; + m_beatDivisionDuration = unitLength / 2; + } + +} + +const timeT TimeSignature::m_crotchetTime = basePPQ; +const timeT TimeSignature::m_dottedCrotchetTime = basePPQ + basePPQ/2; + + + +////////////////////////////////////////////////////////////////////// +// AccidentalTable +////////////////////////////////////////////////////////////////////// + +AccidentalTable::AccidentalTable(const Key &key, const Clef &clef, + OctaveType octaves, BarResetType barReset) : + m_key(key), m_clef(clef), + m_octaves(octaves), m_barReset(barReset) +{ + // nothing else +} + +AccidentalTable::AccidentalTable(const AccidentalTable &t) : + m_key(t.m_key), m_clef(t.m_clef), + m_octaves(t.m_octaves), m_barReset(t.m_barReset), + m_accidentals(t.m_accidentals), + m_canonicalAccidentals(t.m_canonicalAccidentals), + m_newAccidentals(t.m_newAccidentals), + m_newCanonicalAccidentals(t.m_newCanonicalAccidentals) +{ + // nothing else +} + +AccidentalTable & +AccidentalTable::operator=(const AccidentalTable &t) +{ + if (&t != this) { + m_key = t.m_key; + m_clef = t.m_clef; + m_octaves = t.m_octaves; + m_barReset = t.m_barReset; + m_accidentals = t.m_accidentals; + m_canonicalAccidentals = t.m_canonicalAccidentals; + m_newAccidentals = t.m_newAccidentals; + m_newCanonicalAccidentals = t.m_newCanonicalAccidentals; + } + return *this; +} + +Accidental +AccidentalTable::processDisplayAccidental(const Accidental &acc0, int height, + bool &cautionary) +{ + Accidental acc = acc0; + + int canonicalHeight = Key::canonicalHeight(height); + Accidental keyAcc = m_key.getAccidentalAtHeight(canonicalHeight, m_clef); + + Accidental normalAcc = NoAccidental; + Accidental canonicalAcc = NoAccidental; + Accidental prevBarAcc = NoAccidental; + + if (m_octaves == OctavesEquivalent || + m_octaves == OctavesCautionary) { + + AccidentalMap::iterator i = m_canonicalAccidentals.find(canonicalHeight); + if (i != m_canonicalAccidentals.end() && !i->second.previousBar) { + canonicalAcc = i->second.accidental; + } + } + + if (m_octaves == OctavesEquivalent) { + normalAcc = canonicalAcc; + } else { + AccidentalMap::iterator i = m_accidentals.find(height); + if (i != m_accidentals.end() && !i->second.previousBar) { + normalAcc = i->second.accidental; + } + } + + if (m_barReset != BarResetNone) { + AccidentalMap::iterator i = m_accidentals.find(height); + if (i != m_accidentals.end() && i->second.previousBar) { + prevBarAcc = i->second.accidental; + } + } + +// std::cerr << "AccidentalTable::processDisplayAccidental: acc " << acc0 << ", h " << height << ", caut " << cautionary << ", ch " << canonicalHeight << ", keyacc " << keyAcc << " canacc " << canonicalAcc << " noracc " << normalAcc << " oct " << m_octaves << " barReset = " << m_barReset << " pbacc " << prevBarAcc << std::endl; + + if (acc == NoAccidental) acc = keyAcc; + + if (m_octaves == OctavesIndependent || + m_octaves == OctavesEquivalent) { + + if (normalAcc == NoAccidental) { + normalAcc = keyAcc; + } + + if (acc == normalAcc) { + if (!cautionary) acc = NoAccidental; + } else if (acc == NoAccidental) { + if (normalAcc != Natural) { + acc = Natural; + } + } + + } else { + + if (normalAcc != NoAccidental) { + if (acc != normalAcc) { + if (acc == NoAccidental) { + if (normalAcc != Natural) { + acc = Natural; + } + } + } else { // normalAcc != NoAccidental, acc == normalAcc + if (canonicalAcc != NoAccidental && canonicalAcc != normalAcc) { + cautionary = true; + } else { // canonicalAcc == NoAccidental || canonicalAcc == normalAcc + if (!cautionary) { + acc = NoAccidental; + } + } + } + } else { // normalAcc == NoAccidental + if (acc != keyAcc && keyAcc != Natural) { + if (acc == NoAccidental) { + acc = Natural; + } + } else { // normalAcc == NoAccidental, acc == keyAcc + if (canonicalAcc != NoAccidental && canonicalAcc != keyAcc) { + cautionary = true; + if (acc == NoAccidental) { + acc = Natural; + } + } else { // canonicalAcc == NoAccidental || canonicalAcc == keyAcc + if (!cautionary) { + acc = NoAccidental; + } + } + } + } + } + + if (m_barReset != BarResetNone) { + if (acc == NoAccidental) { + if (prevBarAcc != NoAccidental && + prevBarAcc != keyAcc && + !(prevBarAcc == Natural && keyAcc == NoAccidental)) { + cautionary = (m_barReset == BarResetCautionary); + if (keyAcc == NoAccidental) { + acc = Natural; + } else { + acc = keyAcc; + } + } + } + } + + if (acc != NoAccidental) { + m_newAccidentals[height] = AccidentalRec(acc, false); + m_newCanonicalAccidentals[canonicalHeight] = AccidentalRec(acc, false); + } + + return acc; +} + +void +AccidentalTable::update() +{ + m_accidentals = m_newAccidentals; + m_canonicalAccidentals = m_newCanonicalAccidentals; +} + +void +AccidentalTable::newBar() +{ + for (AccidentalMap::iterator i = m_accidentals.begin(); + i != m_accidentals.end(); ) { + + if (i->second.previousBar) { + AccidentalMap::iterator j = i; + ++j; + m_accidentals.erase(i); + i = j; + } else { + i->second.previousBar = true; + ++i; + } + } + + m_canonicalAccidentals.clear(); + + m_newAccidentals = m_accidentals; + m_newCanonicalAccidentals.clear(); +} + +void +AccidentalTable::newClef(const Clef &clef) +{ + m_clef = clef; +} + + +} // close namespace |