You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
rosegarden/src/base/NotationTypes.cpp

2412 lines
75 KiB

/*
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>
#include <sstream>
//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;
}
}
std::ostringstream os;
os << "No " << (isMinor ? "minor" : "major") << " key with "
<< accidentalCount << (isSharp ? " sharp(s)" : " flat(s)");
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;
}
}
std::ostringstream os;
os << "No " << (isMinor ? "minor" : "major") << " key with tonic pitch "
<< tonicPitch;
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::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