summaryrefslogtreecommitdiffstats
path: root/src/base/test/pitch.cpp
diff options
context:
space:
mode:
authortpearson <tpearson@283d02a7-25f6-0310-bc7c-ecb5cbfe19da>2010-03-01 18:37:05 +0000
committertpearson <tpearson@283d02a7-25f6-0310-bc7c-ecb5cbfe19da>2010-03-01 18:37:05 +0000
commit145364a8af6a1fec06556221e66d4b724a62fc9a (patch)
tree53bd71a544008c518034f208d64c932dc2883f50 /src/base/test/pitch.cpp
downloadrosegarden-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/test/pitch.cpp')
-rw-r--r--src/base/test/pitch.cpp474
1 files changed, 474 insertions, 0 deletions
diff --git a/src/base/test/pitch.cpp b/src/base/test/pitch.cpp
new file mode 100644
index 0000000..5d46f9e
--- /dev/null
+++ b/src/base/test/pitch.cpp
@@ -0,0 +1,474 @@
+// -*- c-basic-offset: 4 -*-
+
+#include "NotationRules.h"
+#include "NotationTypes.h"
+
+using namespace Rosegarden;
+using std::cout;
+using std::endl;
+using std::string;
+
+static const int verbose = 0;
+
+// This is the old NotationDisplayPitch -- this file was written for
+// regression testing when implementing the new Pitch class. It won't
+// compile any more as NotationDisplayPitch needs to be a friend of
+// Pitch for this implementation to work. Add "friend class
+// NotationDisplayPitch;" to end of Pitch in ../NotationTypes.h to
+// build it
+
+/**
+ * NotationDisplayPitch stores a note's pitch in terms of the position
+ * of the note on the staff and its associated accidental, and
+ * converts these values to and from performance (MIDI) pitches.
+ *
+ * Rationale: When we insert a note, we need to query the height of the
+ * staff line next to which it's being inserted, then translate this
+ * back to raw pitch according to the clef in force at the x-coordinate
+ * at which the note is inserted. For display, we translate from raw
+ * pitch using both the clef and the key in force.
+ *
+ * Whether an accidental should be displayed or not depends on the
+ * current key, on whether we've already shown the same accidental for
+ * that pitch in the same bar, on whether the note event explicitly
+ * requests an accidental... All we calculate here is whether the
+ * pitch "should" have an accidental, not whether it really will
+ * (e.g. if the accidental has already appeared).
+ *
+ * (See also docs/discussion/units.txt for explanation of pitch units.)
+ */
+
+class NotationDisplayPitch
+{
+public:
+ /**
+ * Construct a NotationDisplayPitch containing the given staff
+ * height and accidental
+ */
+ NotationDisplayPitch(int heightOnStaff,
+ const Accidental &accidental);
+
+ /**
+ * Construct a NotationDisplayPitch containing the height and
+ * accidental to which the given performance pitch corresponds
+ * in the given clef and key
+ */
+ NotationDisplayPitch(int pitch, const Clef &clef, const Key &key,
+ const Accidental &explicitAccidental =
+ Accidentals::NoAccidental);
+
+ int getHeightOnStaff() const { return m_heightOnStaff; }
+ Accidental getAccidental() const { return m_accidental; }
+
+ /**
+ * Calculate and return the performance (MIDI) pitch
+ * corresponding to the stored height and accidental, in the
+ * given clef and key
+ */
+ int getPerformancePitch(const Clef &clef, const Key &key) const;
+
+ /**
+ * Calculate and return the performance (MIDI) pitch
+ * corresponding to the stored height and accidental,
+ * interpreting them as Rosegarden-2.1-style values (for
+ * backward compatibility use), in the given clef and key
+ */
+ int getPerformancePitchFromRG21Pitch(const Clef &clef,
+ const Key &key) const;
+
+ /**
+ * Return the stored pitch as a string (C4, Bb2, etc...)
+ * according to http://www.harmony-central.com/MIDI/Doc/table2.html
+ *
+ * If inclOctave is false, this will return C, Bb, etc.
+ */
+ std::string getAsString(const Clef &clef, const Key &key,
+ bool inclOctave = true,
+ int octaveBase = -2) const;
+
+ /**
+ * Return the stored pitch as a description of a note in a
+ * scale. Return values are:
+ *
+ * -- placeInScale: a number from 0-6 where 0 is C and 6 is B
+ *
+ * -- accidentals: a number from -2 to 2 where -2 is double flat,
+ * -1 is flat, 0 is nothing, 1 is sharp, 2 is double sharp
+ *
+ * -- octave: MIDI octave in range -2 to 8, where pitch 0 is in
+ * octave -2 and thus middle-C is in octave 3
+ *
+ * This function is guaranteed never to return values out of
+ * the above ranges.
+ */
+ void getInScale(const Clef &clef, const Key &key,
+ int &placeInScale, int &accidentals, int &octave) const;
+
+private:
+ int m_heightOnStaff;
+ Accidental m_accidental;
+
+ static void rawPitchToDisplayPitch(int, const Clef &, const Key &,
+ int &, Accidental &);
+ static void displayPitchToRawPitch(int, Accidental, const Clef &, const Key &,
+ int &, bool ignoreOffset = false);
+};
+//////////////////////////////////////////////////////////////////////
+// NotationDisplayPitch
+//////////////////////////////////////////////////////////////////////
+
+NotationDisplayPitch::NotationDisplayPitch(int heightOnStaff,
+ const Accidental &accidental)
+ : m_heightOnStaff(heightOnStaff),
+ m_accidental(accidental)
+{
+}
+
+NotationDisplayPitch::NotationDisplayPitch(int pitch, const Clef &clef,
+ const Key &key,
+ const Accidental &explicitAccidental) :
+ m_accidental(explicitAccidental)
+{
+ rawPitchToDisplayPitch(pitch, clef, key, m_heightOnStaff, m_accidental);
+}
+
+int
+NotationDisplayPitch::getPerformancePitch(const Clef &clef, const Key &key) const
+{
+ int p = 0;
+ displayPitchToRawPitch(m_heightOnStaff, m_accidental, clef, key, p);
+ return p;
+}
+
+int
+NotationDisplayPitch::getPerformancePitchFromRG21Pitch(const Clef &clef,
+ const Key &) const
+{
+ // 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(m_heightOnStaff, m_accidental, clef, Key(), p, true);
+ return p;
+}
+
+
+void
+NotationDisplayPitch::rawPitchToDisplayPitch(int rawpitch,
+ const Clef &clef,
+ const Key &key,
+ int &height,
+ Accidental &accidental)
+{
+ Pitch::rawPitchToDisplayPitch(rawpitch, clef, key, height, accidental);
+}
+
+void
+NotationDisplayPitch::displayPitchToRawPitch(int height,
+ Accidental accidental,
+ const Clef &clef,
+ const Key &key,
+ int &pitch,
+ bool ignoreOffset)
+{
+ Pitch::displayPitchToRawPitch(height, accidental, clef, key, pitch,
+ ignoreOffset);
+}
+string
+NotationDisplayPitch::getAsString(const Clef &clef, const Key &key,
+ bool inclOctave, int octaveBase) const
+{
+ static const string noteNamesSharps[] = {
+ "C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B"
+ };
+ static const string noteNamesFlats[] = {
+ "C", "Db", "D", "Eb", "E", "F", "Gb", "G", "Ab", "A", "Bb", "B"
+ };
+
+ int performancePitch = getPerformancePitch(clef, key);
+
+ // highly unlikely, but fatal if it happened:
+ if (performancePitch < 0) performancePitch = 0;
+
+ int pitch = performancePitch % 12;
+ int octave = performancePitch / 12;
+
+ if (!inclOctave)
+ return key.isSharp() ? noteNamesSharps[pitch] : noteNamesFlats[pitch];
+
+ char tmp[1024];
+
+ if (key.isSharp())
+ sprintf(tmp, "%s%d", noteNamesSharps[pitch].c_str(),
+ octave + octaveBase);
+ else
+ sprintf(tmp, "%s%d", noteNamesFlats[pitch].c_str(),
+ octave + octaveBase);
+
+ return string(tmp);
+}
+
+void
+NotationDisplayPitch::getInScale(const Clef &clef, const Key &key,
+ int &placeInScale, int &accidentals, int &octave) const
+{
+ //!!! Maybe we should bring the logic from rawPitchToDisplayPitch down
+ // into this method, and make rawPitchToDisplayPitch wrap this
+
+ static int pitches[2][12] = {
+ { 0, 0, 1, 1, 2, 3, 3, 4, 4, 5, 5, 6 },
+ { 0, 1, 1, 2, 2, 3, 4, 4, 5, 5, 6, 6 },
+ };
+ static int accidentalsForPitches[2][12] = {
+ { 0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 1, 0 },
+ { 0, -1, 0, -1, 0, 0, -1, 0, -1, 0, -1, 0 },
+ };
+
+ int performancePitch = getPerformancePitch(clef, key);
+
+ // highly unlikely, but fatal if it happened:
+ if (performancePitch < 0) performancePitch = 0;
+ if (performancePitch > 127) performancePitch = 127;
+
+ int pitch = performancePitch % 12;
+ octave = performancePitch / 12 - 2;
+
+ if (key.isSharp()) { //!!! need to [optionally?] handle minor keys (similarly in getAsString?)
+ placeInScale = pitches[0][pitch];
+ accidentals = accidentalsForPitches[0][pitch];
+ } else {
+ placeInScale = pitches[1][pitch];
+ accidentals = accidentalsForPitches[1][pitch];
+ }
+}
+
+
+
+int testNote(Accidental &acc, Key &key, int octave, int note)
+{
+ int rv = 0;
+
+ Pitch pitch(note, octave, key, acc);
+
+ static int prevPerformancePitch = -1;
+ static Accidental prevAcc = Accidentals::NoAccidental;
+ static int prevOctave = -2;
+
+ int p = pitch.getPerformancePitch();
+ if (p < prevPerformancePitch && (prevAcc == acc && prevOctave == octave)) {
+ cout << "testNote: " << note << " " << acc << ", " << key.getName() << ", octave " << octave << ": "
+ << "pitch is " << p << ", should be >= " << prevPerformancePitch << endl;
+ rv = 1;
+ }
+
+ int nis = pitch.getNoteInScale(key);
+ if (nis != note) {
+ cout << "testNote: " << note << " " << acc << ", " << key.getName() << ", octave " << octave << ": "
+ << "note in scale is " << nis << " (not " << note << ")" << endl;
+ rv = 1;
+ }
+
+ // can do special checks on C-major etc 'cos it's easy, and stuff like that
+
+ if (key == Key("C major")) {
+ if (acc == Accidentals::NoAccidental) {
+ Pitch comparative(scale_Cmajor[nis], octave);
+ if (comparative.getPerformancePitch() != p) {
+ cout << "testNote: " << note << " " << acc << ", " << key.getName() << ", octave " << octave << ": "
+ << "comparative pitch is " << comparative.getPerformancePitch() << ", should be " << p << endl;
+ rv = 1;
+ }
+ }
+ }
+
+ prevPerformancePitch = p;
+ prevOctave = octave;
+ prevAcc = acc;
+
+ if (!rv && verbose) {
+ cout << "testNote: " << note << " " << acc << ", " << key.getName() << ", octave " << octave << ": "
+ << "pitch " << p << endl;
+ }
+ return rv;
+}
+
+int testNoteName(Accidental &acc, Key &key, int octave, char noteName)
+{
+ int rv = 0;
+
+ Pitch pitch(noteName, octave, key, acc);
+
+ static int prevPerformancePitch = -1;
+ static Accidental prevAcc = Accidentals::NoAccidental;
+ static int prevOctave = -2;
+
+ int p = pitch.getPerformancePitch();
+ if (p < prevPerformancePitch && (prevAcc == acc && prevOctave == octave)) {
+ cout << "testNoteName: " << noteName << " " << acc << ", " << key.getName() << ", octave " << octave << ": "
+ << "pitch is " << p << ", should be >= " << prevPerformancePitch << endl;
+ rv = 1;
+ }
+
+ char nn = pitch.getNoteName(key);
+ if (nn != noteName) {
+ cout << "testNoteName: " << noteName << " " << acc << ", " << key.getName() << ", octave " << octave << ": "
+ << "note is " << nn << " (not " << noteName << ") (pitch was " << p << ")" << endl;
+ rv = 1;
+ }
+
+ prevPerformancePitch = p;
+ prevOctave = octave;
+ prevAcc = acc;
+
+ if (!rv && verbose) {
+ cout << "testNoteName: " << noteName << " " << acc << ", " << key.getName() << ", octave " << octave << ": "
+ << "pitch " << p << endl;
+ }
+ return rv;
+}
+
+int testPitchInOctave(Accidental &acc, Key &key, int octave, int pio)
+{
+ int rv = 0;
+
+ Pitch pitch(pio, octave, acc);
+
+ int p = pitch.getPerformancePitch();
+ if (p != (octave + 2) * 12 + pio) {
+ cout << "testPitchInOctave: " << pio << " " << acc << ", " << key.getName() << ", octave " << octave << ": "
+ << "pitch is " << p << ", should be " << ((octave + 2) * 12 + pio) << endl;
+ rv = 1;
+ }
+
+ if (!rv && verbose) {
+ cout << "testNote: " << pio << " " << acc << ", " << key.getName() << ", octave " << octave << ": "
+ << "pitch " << p << endl;
+ }
+ return rv;
+}
+
+int testPitch(Accidental &acc, Key &key, Clef &clef, int pp)
+{
+ int rv = 0;
+
+ Pitch pitch(pp, acc);
+ NotationDisplayPitch ndp(pp, clef, key, acc);
+
+ int h = pitch.getHeightOnStaff(clef, key);
+ int nh = ndp.getHeightOnStaff();
+ if (h != nh) {
+ cout << "testPitch: " << pp << ", " << acc << ", " << key.getName() << ", " << clef.getClefType() << ": "
+ << "height is " << h << " (ndp returns " << nh << ")" << endl;
+ rv = 1;
+ }
+
+ Accidental pa = pitch.getDisplayAccidental(key);
+ Accidental na = ndp.getAccidental();
+ if (pa != na) {
+ cout << "testPitch: " << pp << ", " << acc << ", " << key.getName() << ", " << clef.getClefType() << ": "
+ << "display acc is " << pa << " (ndp returns " << na << ")" << endl;
+ rv = 1;
+ }
+
+ return rv;
+}
+
+int testHeight(Accidental &acc, Key &key, Clef &clef, int height)
+{
+ int rv = 0;
+
+ Pitch pitch(height, clef, key, acc);
+ NotationDisplayPitch ndp(height, acc);
+ NotationDisplayPitch ndp2(pitch.getPerformancePitch(), clef, key, acc);
+
+ int ppp = pitch.getPerformancePitch();
+ int npp = ndp.getPerformancePitch(clef, key);
+
+ if (ppp != npp) {
+ cout << "testHeight: " << height << " " << acc << ", " << key.getName() << ", " << clef.getClefType() << ": "
+ << "pitch " << ppp << " (ndp returns " << npp << ")" << endl;
+ rv = 1;
+ }
+
+ int h = pitch.getHeightOnStaff(clef, key);
+ if (h != ndp.getHeightOnStaff() || h != height) {
+ cout << "testHeight: " << height << " " << acc << ", " << key.getName() << ", " << clef.getClefType() << ": "
+ << "height " << h << " (ndp returns " << ndp.getHeightOnStaff() << ")" << endl;
+ rv = 1;
+ }
+
+ // for NoAccidental, the Pitch object will acquire the accidental
+ // from the current key whereas NotationDisplayPitch will not --
+ // hence we skip this test for NoAccidental
+ if (acc != Accidentals::NoAccidental) {
+ Accidental nacc = ndp2.getAccidental();
+ Accidental pacc = pitch.getDisplayAccidental(key);
+ if (nacc != pacc) {
+ cout << "testHeight: " << height << " " << acc << ", " << key.getName() << ", " << clef.getClefType() << ": "
+ "acc " << pacc << " (ndp returns " << nacc << ")" << endl;
+ rv = 1;
+ }
+ }
+
+ if (!rv && verbose) {
+ cout << "testHeight: " << height << " " << acc << ", " << key.getName() << ", " << clef.getClefType() << ": "
+ << "pitch " << ppp << endl;
+ }
+ return rv;
+
+}
+
+
+int main(int argc, char **argv)
+{
+ Accidentals::AccidentalList accidentals(Accidentals::getStandardAccidentals());
+ Clef::ClefList clefs(Clef::getClefs());
+
+ Key::KeyList keys;
+ Key::KeyList majorKeys(Key::getKeys(false));
+ Key::KeyList minorKeys(Key::getKeys(true));
+ keys.insert(keys.end(), majorKeys.begin(), majorKeys.end());
+ keys.insert(keys.end(), minorKeys.begin(), minorKeys.end());
+
+ for (int a = 0; a < accidentals.size(); ++a) {
+
+ for (int k = 0; k < keys.size(); ++k) {
+
+ for (int o = -2; o < 9; ++o) {
+ for (int n = 0; n < 7; ++n) {
+ testNote(accidentals[a], keys[k], o, n);
+ }
+ }
+
+ for (int o = -2; o < 9; ++o) {
+ for (int p = 0; p < 12; ++p) {
+ testPitchInOctave(accidentals[a], keys[k], o, p);
+ }
+ }
+
+ for (int o = -2; o < 9; ++o) {
+ for (int p = 0; p < 7; ++p) {
+ testNoteName(accidentals[a], keys[k], o, Pitch::getNoteForIndex(p));
+ }
+ }
+
+ for (int c = 0; c < clefs.size(); ++c) {
+
+ for (int p = 0; p < 128; ++p) {
+ testPitch(accidentals[a], keys[k], clefs[c], p);
+ }
+
+ for (int h = -20; h < 30; ++h) {
+ testHeight(accidentals[a], keys[k], clefs[c], h);
+ }
+ }
+ }
+ }
+
+ return 0;
+}
+