diff options
Diffstat (limited to 'src/base/test')
-rw-r--r-- | src/base/test/Makefile | 57 | ||||
-rw-r--r-- | src/base/test/accidentals.cpp | 60 | ||||
-rw-r--r-- | src/base/test/colour.cpp | 222 | ||||
-rw-r--r-- | src/base/test/colour.output | 76 | ||||
-rw-r--r-- | src/base/test/pitch.cpp | 474 | ||||
-rw-r--r-- | src/base/test/seq/Makefile | 6 | ||||
-rw-r--r-- | src/base/test/seq/complainer.c | 74 | ||||
-rw-r--r-- | src/base/test/seq/generator.c | 96 | ||||
-rw-r--r-- | src/base/test/seq/queue-timer-jack.c | 166 | ||||
-rw-r--r-- | src/base/test/seq/queue-timer.c | 123 | ||||
-rw-r--r-- | src/base/test/test.cpp | 535 | ||||
-rw-r--r-- | src/base/test/thread.cpp | 126 | ||||
-rw-r--r-- | src/base/test/transpose.cpp | 83 | ||||
-rw-r--r-- | src/base/test/utf8.cpp | 96 |
14 files changed, 2194 insertions, 0 deletions
diff --git a/src/base/test/Makefile b/src/base/test/Makefile new file mode 100644 index 0000000..b517955 --- /dev/null +++ b/src/base/test/Makefile @@ -0,0 +1,57 @@ + +# debug flags need to be consistent with base build +#CPPFLAGS = -O2 +CPPFLAGS = -g + +LIBBASE = ../../../RGbuild/libRosegardenCommon.a + +INCPATH = -I.. + +SRCS := test.C pitch.C + +default: test utf8 colour transpose accidentals + +clean: + rm -f test test.o pitch pitch.o utf8 utf8.o colour colour.o transpose.o transpose accidentals.o accidentals + +%.o: %.cpp + $(CXX) $(CPPFLAGS) -c $< $(INCPATH) -o $@ + +test: test.o + $(CXX) $< $(LIBBASE) -o $@ + +pitch: pitch.o + $(CXX) $< $(LIBBASE) -o $@ + +utf8: utf8.o + $(CXX) $< $(LIBBASE) -o $@ + +colour: colour.o + $(CXX) $< $(LIBBASE) -o $@ + +transpose: transpose.o + $(CXX) $< $(LIBBASE) -o $@ + +accidentals: accidentals.o + $(CXX) $< $(LIBBASE) -o $@ + + +depend: + makedepend $(INCPATH) -- $(CPPFLAGS) -- $(SRCS) + +# DO NOT DELETE + +test.o: ../Event.h ../PropertyMap.h ../Property.h ../RealTime.h +test.o: ../PropertyName.h ../Exception.h ../Segment.h ../Track.h +test.o: ../XmlExportable.h ../Instrument.h ../NotationTypes.h #../StringHash.h +test.o: ../XmlExportable.h ../Instrument.h ../NotationTypes.h +test.o: ../RefreshStatus.h ../Composition.h ../FastVector.h +test.o: ../Configuration.h ../ColourMap.h ../Colour.h +test.o: ../SegmentNotationHelper.h ../SegmentPerformanceHelper.h +test.o: ../MidiTypes.h +pitch.o: ../NotationTypes.h ../Event.h ../PropertyMap.h ../Property.h +pitch.o: ../RealTime.h ../PropertyName.h ../Exception.h ../Instrument.h +pitch.o: ../XmlExportable.h #../StringHash.h + +transpose.o: ../NotationTypes.h +accidentals.o: ../NotationTypes.h diff --git a/src/base/test/accidentals.cpp b/src/base/test/accidentals.cpp new file mode 100644 index 0000000..53dbfc8 --- /dev/null +++ b/src/base/test/accidentals.cpp @@ -0,0 +1,60 @@ +// -*- c-basic-offset: 4 -*- + +#include "NotationTypes.h" + +using namespace Rosegarden; +using std::cout; + +// Unit test-ish tests for resolving accidentals +// +// Returns -1 (or crashes :)) on error, 0 on success +void assertHasAccidental(Pitch &pitch, + const Accidental& accidental, const Key& key) +{ + Accidental calculatedAccidental = + pitch.getAccidental(key); + + std::cout << "Got " << calculatedAccidental << " for pitch " << pitch.getPerformancePitch() << " in key " << key.getName() << std::endl; + + if (calculatedAccidental != accidental) + { + std::cout << "Expected " << accidental << std::endl; + exit(-1); + } +} + +void testBInEMinor() +{ + // a B, also in E minor, has no accidental + Pitch testPitch(59 % 12); + assertHasAccidental(testPitch, + Accidentals::NoAccidental, Key("E minor")); +} + +/** + * + */ +void testFInBMinor() +{ + Pitch testPitch(77); + assertHasAccidental(testPitch, + Accidentals::NoAccidental, Key("B minor")); +} + +void testInvalidSuggestion() +{ + // If we specify an invalid suggestion, + // getAccidental() should be robust against that. + Pitch testPitch = Pitch(59, Accidentals::Sharp); + assertHasAccidental(testPitch, + Accidentals::NoAccidental, Key("E minor")); +} + +int main(int argc, char **argv) +{ + testBInEMinor(); + testFInBMinor(); + testInvalidSuggestion(); + std::cout << "Success" << std::endl; + exit(0); +} diff --git a/src/base/test/colour.cpp b/src/base/test/colour.cpp new file mode 100644 index 0000000..3aa7ba2 --- /dev/null +++ b/src/base/test/colour.cpp @@ -0,0 +1,222 @@ +// -*- c-basic-offset: 4 -*- + + +/* + Rosegarden-4 + A sequencer and musical notation editor. + + This program is Copyright 2000-2003 + Guillaume Laurent <[email protected]>, + Chris Cannam <[email protected]>, + Richard Bown <[email protected]> + + This file is Copyright 2003 + Mark Hymers <[email protected]> + + 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. +*/ + +/* + If you compile this to a test program + g++ -o colour -I../ ../Colour.C ../ColourMap.C colour.C + + you can then run it like this: + ./colour > temp.output + + and do a diff to check that it worked: + diff -u temp.output colour.output + + If there are any differences, there's a problem + (or colour.output hasn't been updated when colour.C has been changed) +*/ + +#include "Colour.h" +#include "ColourMap.h" +#include <iostream> + + +using namespace Rosegarden; +using std::cout; +using std::string; + +// Some printing routines + +void printRC(Colour const *temp) +{ + cout << "red: " << temp->getRed() << " green: " << temp->getGreen() << " blue: " << temp->getBlue() << "\n"; +} + +void printSRC(const string *s, const Colour *c) +{ + cout << "name: " << *s << " "; + printRC(c); +} + +void printSIRC(const unsigned int *i, const string *s, const Colour *c) +{ + cout << "index: " << *i << " "; + printSRC(s, c); +} + +void printIteratorContents (ColourMap *input) +{ + RCMap::const_iterator iter = input->begin(); + for ( ; !(iter == input->end()) ; ++iter) + printSIRC(&(iter->first), &(iter->second.second), &(iter->second.first)); +} + +// The main test program +int main() +{ + cout << "TEST: Colour.C\n\n"; + cout << "Can we create an Colour with the right default values?\n"; + Colour *red = new Colour; + printRC(red); + + cout << "Can we set values; green here is invalid - it should be set to 0 instead\n"; + red->setRed(210); + red->setGreen(276); + red->setBlue(100); + + cout << "Testing the copy constructor\n"; + Colour *blue = new Colour(*red); + printRC(blue); + + cout << "Check operator= works\n"; + Colour green; + green = *red; + printRC(&green); + + cout << "Check the setColour routine\n"; + green.setColour(1,2,3); + printRC(&green); + + cout << "Check the getColour routine\n"; + unsigned int r, g, b; + green.getColour(r, g, b); + printRC(&green); + + cout << "\nTEST: ColourMap.C\n\n"; + cout << "Can we create a ColourMap with the right default Colour + String\n"; + ColourMap *map = new ColourMap(); + + cout << "Can we get the default colour back out of it?\n"; + string s1 = map->getNameByIndex(0); + green = map->getColourByIndex(0); + printSRC(&s1, &green); + + cout << "Can we create a ColourMap with a specified default Colour?\n"; + ColourMap *map2 = new ColourMap(*red); + + cout << "Can we get the information back out of it?\n"; + s1 = map2->getNameByIndex(0); + green = map2->getColourByIndex(0); + printSRC(&s1, &green); + + cout << "Can we add a Colour\n"; + s1 = "TEST1"; + green.setColour(100, 101, 102); + map2->addItem(green, s1); + + cout << "Can we get the info back out?\n"; + s1 = ""; + s1 = map2->getNameByIndex(1); + green = map2->getColourByIndex(1); + printSRC(&s1, &green); + + cout << "Add a couple more colours\n"; + s1 = "TEST2"; + green.setColour(101, 102, 103); + map2->addItem(green, s1); + s1 = "TEST3"; + green.setColour(102, 103, 104); + map2->addItem(green, s1); + s1 = "TEST4"; + green.setColour(103, 104, 105); + map2->addItem(green, s1); + + // From an iterator: + // iterator->first ==> Index + // iterator->second.first ==> Colour + // iterator->second.second ==> string + // This rather unwieldy notation is because we store a pair in the map which is made up of a pair + // to start with + printIteratorContents(map2); + + cout << "Now try deleting the third item\n"; + map2->deleteItemByIndex(3); + + // Print the map again + printIteratorContents(map2); + + cout << "Make sure we get false when we try and modify item number 3\n"; + s1 = "NO"; + green.setColour(199,199,199); + bool check = map2->modifyColourByIndex(3, green); + if (check) cout << "WARNING: Managed to modify colour which doesn't exist\n"; + check = map2->modifyNameByIndex(3, s1); + if (check) cout << "WARNING: Managed to modify name which doesn't exist\n"; + + cout << "Check we can modify a colour which *is* there\n"; + s1 = "YES"; + green.setColour(233,233,233); + + check = map2->modifyColourByIndex(4, green); + if (!check) cout << "WARNING: Couldn't modify colour which does exist\n"; + + check = map2->modifyNameByIndex(4, s1); + if (!check) cout << "WARNING: Couldn't modify name which does exist\n"; + + // Print the map again + printIteratorContents(map2); + + cout << "Now try adding another item - it should take the place of the one we removed.\n"; + s1 = "NEW"; + green.setColour(211, 212, 213); + map2->addItem(green, s1); + + // Print the map again + printIteratorContents(map2); + + cout << "Try swapping two items:\n"; + check = map2->swapItems(3, 4); + if (!check) cout << "WARNING: Couldn't swap two items which both exist\n"; + + // Print the map again + printIteratorContents(map2); + + cout << "\nTEST: Generic Colour routines\n\n"; + + cout << "Try getting a combination colour:\n"; + Colour blah = map2->getColourByIndex(0); + Colour blah2 = map2->getColourByIndex(1); + cout << "Original colours:\n"; + printRC(&blah); + printRC(&blah2); + cout << "Combination colour:\n"; + blah = getCombinationColour(blah, blah2); + printRC(&blah); + + // Test the XML output + cout << "\nTEST: XML Output\n\n"; + cout << "For a single colour:\n"; + cout << blah.toXmlString(); + + cout << "For a colourmap:\n"; + cout << map2->toXmlString(std::string("segmentmap")); + + + delete map; + delete map2; + delete red; + delete blue; + + return 0; +} diff --git a/src/base/test/colour.output b/src/base/test/colour.output new file mode 100644 index 0000000..d6dc301 --- /dev/null +++ b/src/base/test/colour.output @@ -0,0 +1,76 @@ +TEST: Colour.C + +Can we create an Colour with the right default values? +red: 0 green: 0 blue: 0 +Can we set values; green here is invalid - it should be set to 0 instead +Testing the copy constructor +red: 210 green: 0 blue: 100 +Check operator= works +red: 210 green: 0 blue: 100 +Check the setColour routine +red: 1 green: 2 blue: 3 +Check the getColour routine +red: 1 green: 2 blue: 3 + +TEST: ColourMap.C + +Can we create a ColourMap with the right default Colour + String +Can we get the default colour back out of it? +name: red: 197 green: 211 blue: 125 +Can we create a ColourMap with a specified default Colour? +Can we get the information back out of it? +name: red: 210 green: 0 blue: 100 +Can we add a Colour +Can we get the info back out? +name: TEST1 red: 100 green: 101 blue: 102 +Add a couple more colours +index: 0 name: red: 210 green: 0 blue: 100 +index: 1 name: TEST1 red: 100 green: 101 blue: 102 +index: 2 name: TEST2 red: 101 green: 102 blue: 103 +index: 3 name: TEST3 red: 102 green: 103 blue: 104 +index: 4 name: TEST4 red: 103 green: 104 blue: 105 +Now try deleting the third item +index: 0 name: red: 210 green: 0 blue: 100 +index: 1 name: TEST1 red: 100 green: 101 blue: 102 +index: 2 name: TEST2 red: 101 green: 102 blue: 103 +index: 4 name: TEST4 red: 103 green: 104 blue: 105 +Make sure we get false when we try and modify item number 3 +Check we can modify a colour which *is* there +index: 0 name: red: 210 green: 0 blue: 100 +index: 1 name: TEST1 red: 100 green: 101 blue: 102 +index: 2 name: TEST2 red: 101 green: 102 blue: 103 +index: 4 name: YES red: 233 green: 233 blue: 233 +Now try adding another item - it should take the place of the one we removed. +index: 0 name: red: 210 green: 0 blue: 100 +index: 1 name: TEST1 red: 100 green: 101 blue: 102 +index: 2 name: TEST2 red: 101 green: 102 blue: 103 +index: 3 name: NEW red: 211 green: 212 blue: 213 +index: 4 name: YES red: 233 green: 233 blue: 233 +Try swapping two items: +index: 0 name: red: 210 green: 0 blue: 100 +index: 1 name: TEST1 red: 100 green: 101 blue: 102 +index: 2 name: TEST2 red: 101 green: 102 blue: 103 +index: 3 name: YES red: 233 green: 233 blue: 233 +index: 4 name: NEW red: 211 green: 212 blue: 213 + +TEST: Generic Colour routines + +Try getting a combination colour: +Original colours: +red: 210 green: 0 blue: 100 +red: 100 green: 101 blue: 102 +Combination colour: +red: 155 green: 50 blue: 101 + +TEST: XML Output + +For a single colour: +<colour red="155" green="50" blue="101"/> +For a colourmap: + <colourmap name="segmentmap"> + <colourpair id="0" name="" red="210" green="0" blue="100"/> + <colourpair id="1" name="TEST1" red="100" green="101" blue="102"/> + <colourpair id="2" name="TEST2" red="101" green="102" blue="103"/> + <colourpair id="3" name="YES" red="233" green="233" blue="233"/> + <colourpair id="4" name="NEW" red="211" green="212" blue="213"/> + </colourmap> 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; +} + diff --git a/src/base/test/seq/Makefile b/src/base/test/seq/Makefile new file mode 100644 index 0000000..c32946e --- /dev/null +++ b/src/base/test/seq/Makefile @@ -0,0 +1,6 @@ + +all: complainer generator queue-timer queue-timer-jack + +%: %.c + cc $< -o $@ -ljack -lasound + diff --git a/src/base/test/seq/complainer.c b/src/base/test/seq/complainer.c new file mode 100644 index 0000000..afe0a7f --- /dev/null +++ b/src/base/test/seq/complainer.c @@ -0,0 +1,74 @@ + +#include <alsa/asoundlib.h> +#include <alsa/seq.h> +#include <sys/time.h> +#include <sched.h> + +void +callback(snd_seq_t *handle) +{ + snd_seq_event_t *ev = 0; + + do { + if (snd_seq_event_input(handle, &ev) > 0) { + + if (ev->type == SND_SEQ_EVENT_NOTEON) { + + struct timeval tv; + static long last_usec = 0; + int pitch = ev->data.note.note; + + snd_seq_timestamp_t evt = ev->time; + + gettimeofday(&tv, 0); + printf("pitch %d at %ld sec %ld usec, off by %ld usec\n", + pitch, tv.tv_sec, tv.tv_usec, tv.tv_usec - ((last_usec + 500000) % 1000000)); + + last_usec = tv.tv_usec; + } + } + + } while (snd_seq_event_input_pending(handle, 0) > 0); +} + +int +main(int argc, char **argv) +{ + snd_seq_t *handle; + int portid; + int npfd; + struct pollfd *pfd; + struct sched_param param; + + if (snd_seq_open(&handle, "hw", SND_SEQ_OPEN_DUPLEX, 0) < 0) { + fprintf(stderr, "failed to open ALSA sequencer interface\n"); + return 1; + } + + snd_seq_set_client_name(handle, "complainer"); + + if ((portid = snd_seq_create_simple_port + (handle, "complainer", + SND_SEQ_PORT_CAP_WRITE | SND_SEQ_PORT_CAP_SUBS_WRITE, 0)) < 0) { + fprintf(stderr, "failed to create ALSA sequencer port\n"); + return 1; + } + + npfd = snd_seq_poll_descriptors_count(handle, POLLIN); + pfd = (struct pollfd *)alloca(npfd * sizeof(struct pollfd)); + snd_seq_poll_descriptors(handle, pfd, npfd, POLLIN); + + param.sched_priority = 99; + if (sched_setscheduler(0, SCHED_FIFO, ¶m)) { + perror("failed to set high-priority scheduler"); + } + + printf("ready\n", npfd); + + while (1) { + if (poll(pfd, npfd, 100000) > 0) { + callback(handle); + } + } +} + diff --git a/src/base/test/seq/generator.c b/src/base/test/seq/generator.c new file mode 100644 index 0000000..9f64d61 --- /dev/null +++ b/src/base/test/seq/generator.c @@ -0,0 +1,96 @@ + +#include <alsa/asoundlib.h> +#include <alsa/seq.h> +#include <sys/time.h> + +int +main(int argc, char **argv) +{ + snd_seq_t *handle; + int portid; + int npfd; + struct pollfd *pfd; + int queue; + int i; + int rval; + int target; + snd_seq_queue_timer_t *timer; + snd_timer_id_t *timerid; + + if (argc != 2) { + fprintf(stderr, "usage: generator <target-client-id>\n"); + exit(2); + } + target = atoi(argv[1]); + + if (snd_seq_open(&handle, "hw", SND_SEQ_OPEN_DUPLEX, 0) < 0) { + fprintf(stderr, "failed to open ALSA sequencer interface\n"); + return 1; + } + + snd_seq_set_client_name(handle, "generator"); + + if ((portid = snd_seq_create_simple_port + (handle, "generator", + SND_SEQ_PORT_CAP_READ | SND_SEQ_PORT_CAP_SUBS_READ, 0)) < 0) { + fprintf(stderr, "failed to create ALSA sequencer port\n"); + return 1; + } + + if ((queue = snd_seq_alloc_queue(handle)) < 0) { + fprintf(stderr, "failed to create ALSA sequencer queue\n"); + return 1; + } +/* + snd_seq_queue_timer_alloca(&timer); + snd_seq_get_queue_timer(handle, queue, timer); + snd_timer_id_alloca(&timerid); + snd_timer_id_set_class(timerid, SND_TIMER_CLASS_PCM); + snd_timer_id_set_sclass(timerid, SND_TIMER_SCLASS_NONE); + snd_timer_id_set_card(timerid, 0); + snd_timer_id_set_device(timerid, 0); + snd_timer_id_set_subdevice(timerid, 0); + snd_seq_queue_timer_set_id(timer, timerid); + snd_seq_set_queue_timer(handle, queue, timer); +*/ + snd_seq_start_queue(handle, queue, 0); + + // stuff two minutes worth of events on the queue + for (i = 0; i < 240; ++i) { + snd_seq_real_time_t rtime; + rtime.tv_sec = i / 2; + rtime.tv_nsec = (i % 2) * 500000000; + snd_seq_event_t ev; + snd_seq_ev_clear(&ev); + snd_seq_ev_set_source(&ev, portid); + snd_seq_ev_set_dest(&ev, target, 0); + snd_seq_ev_schedule_real(&ev, queue, 0, &rtime); + snd_seq_ev_set_noteon(&ev, 0, 64, 127); + if ((rval = snd_seq_event_output(handle, &ev)) < 0) { + fprintf(stderr, "failed to write event: %s", snd_strerror(rval)); + } + } + + snd_seq_drain_output(handle); + + for (i = 0; i < 120; ++i) { + snd_seq_queue_status_t *status; + const snd_seq_real_time_t *rtime; + struct timeval tv; + + snd_seq_queue_status_alloca(&status); + + snd_seq_get_queue_status(handle, queue, status); + rtime = snd_seq_queue_status_get_real_time(status); + + gettimeofday(&tv, 0); + + fprintf(stderr, " real time: %ld sec, %ld usec\nqueue time: %ld sec, %ld usec (diff to real time %ld sec %ld usec)\n", + tv.tv_sec, tv.tv_usec, + rtime->tv_sec, rtime->tv_nsec / 1000, + tv.tv_sec - rtime->tv_sec, tv.tv_usec - (rtime->tv_nsec / 1000)); + + sleep(1); + } +} + diff --git a/src/base/test/seq/queue-timer-jack.c b/src/base/test/seq/queue-timer-jack.c new file mode 100644 index 0000000..2648e94 --- /dev/null +++ b/src/base/test/seq/queue-timer-jack.c @@ -0,0 +1,166 @@ + +#include <alsa/asoundlib.h> +#include <alsa/seq.h> +#include <jack/jack.h> +#include <sys/time.h> + +static jack_nframes_t sample_frames = 0; + +void normalize(struct timeval *tv) +{ + if (tv->tv_sec == 0) { + while (tv->tv_usec <= -1000000) { tv->tv_usec += 1000000; --tv->tv_sec; } + while (tv->tv_usec >= 1000000) { tv->tv_usec -= 1000000; ++tv->tv_sec; } + } else if (tv->tv_sec < 0) { + while (tv->tv_usec <= -1000000) { tv->tv_usec += 1000000; --tv->tv_sec; } + while (tv->tv_usec > 0) { tv->tv_usec -= 1000000; ++tv->tv_sec; } + } else { + while (tv->tv_usec >= 1000000) { tv->tv_usec -= 1000000; ++tv->tv_sec; } + while (tv->tv_usec < 0) { tv->tv_usec += 1000000; --tv->tv_sec; } + } +} + +int +jack_process(jack_nframes_t nframes, void *arg) +{ + sample_frames += nframes; +} + +jack_nframes_t +rt_to_frame(struct timeval tv, jack_nframes_t sample_rate) +{ + if (tv.tv_sec < 0) tv.tv_sec = -tv.tv_sec; + if (tv.tv_usec < 0) tv.tv_usec = -tv.tv_usec; + return + tv.tv_sec * sample_rate + + ((tv.tv_usec / 1000) * sample_rate) / 1000 + + ((tv.tv_usec - 1000 * (tv.tv_usec / 1000)) * sample_rate) / 1000000; +} + +int +main(int argc, char **argv) +{ + snd_seq_t *handle; + int portid; + int npfd; + struct pollfd *pfd; + int queue; + int i; + int rval; + struct timeval starttv; + int countdown = -1; + snd_seq_queue_timer_t *timer; + snd_timer_id_t *timerid; + jack_client_t *jclient; + jack_nframes_t sample_rate; + + if ((jclient = jack_client_new("queue-timer-jack")) == 0) { + fprintf(stderr, "failed to connect to JACK server\n"); + return 1; + } + + jack_set_process_callback(jclient, jack_process, 0); + + sample_rate = jack_get_sample_rate(jclient); + + if (snd_seq_open(&handle, "hw", SND_SEQ_OPEN_DUPLEX, 0) < 0) { + fprintf(stderr, "failed to open ALSA sequencer interface\n"); + return 1; + } + + snd_seq_set_client_name(handle, "generator"); + + if ((portid = snd_seq_create_simple_port + (handle, "generator", + SND_SEQ_PORT_CAP_READ | SND_SEQ_PORT_CAP_SUBS_READ, 0)) < 0) { + fprintf(stderr, "failed to create ALSA sequencer port\n"); + return 1; + } + + if ((queue = snd_seq_alloc_queue(handle)) < 0) { + fprintf(stderr, "failed to create ALSA sequencer queue\n"); + return 1; + } + + snd_seq_queue_timer_alloca(&timer); + snd_seq_get_queue_timer(handle, queue, timer); + snd_timer_id_alloca(&timerid); + + /* To test a PCM timer: */ +/* + snd_timer_id_set_class(timerid, SND_TIMER_CLASS_PCM); + snd_timer_id_set_sclass(timerid, SND_TIMER_SCLASS_NONE); + snd_timer_id_set_card(timerid, 0); + snd_timer_id_set_device(timerid, 0); + snd_timer_id_set_subdevice(timerid, 0); +*/ + + /* To test the system timer: */ + snd_timer_id_set_class(timerid, SND_TIMER_CLASS_GLOBAL); + snd_timer_id_set_sclass(timerid, SND_TIMER_SCLASS_NONE); + snd_timer_id_set_device(timerid, SND_TIMER_GLOBAL_SYSTEM); + + snd_seq_queue_timer_set_id(timer, timerid); + snd_seq_set_queue_timer(handle, queue, timer); + + if (jack_activate(jclient)) { + fprintf (stderr, "cannot activate jack client"); + exit(1); + } + + snd_seq_start_queue(handle, queue, 0); + snd_seq_drain_output(handle); + + gettimeofday(&starttv, 0); + + while (countdown != 0) { + + snd_seq_queue_status_t *status; + const snd_seq_real_time_t *rtime; + struct timeval tv, qtv, jtv, diff, jdiff; + jack_nframes_t frames_now; + + snd_seq_queue_status_alloca(&status); + + snd_seq_get_queue_status(handle, queue, status); + rtime = snd_seq_queue_status_get_real_time(status); + + gettimeofday(&tv, 0); + + frames_now = sample_frames; + fprintf(stderr, " frames: %ld\n", frames_now); + + qtv.tv_sec = rtime->tv_sec; + qtv.tv_usec = rtime->tv_nsec / 1000; + + tv.tv_sec -= starttv.tv_sec; + tv.tv_usec -= starttv.tv_usec; + normalize(&tv); + + jtv.tv_sec = frames_now / sample_rate; + frames_now -= jtv.tv_sec * sample_rate; + jtv.tv_usec = (int)(((float)frames_now * 1000000) / sample_rate); + + diff.tv_sec = tv.tv_sec - qtv.tv_sec; + diff.tv_usec = tv.tv_usec - qtv.tv_usec; + normalize(&diff); + + jdiff.tv_sec = jtv.tv_sec - qtv.tv_sec; + jdiff.tv_usec = jtv.tv_usec - qtv.tv_usec; + normalize(&jdiff); + + fprintf(stderr, " real time: %12ld sec %8ld usec /%12ld frames\nqueue time: %12ld sec %8ld usec /%12ld frames\n jack time: %12ld sec %8ld usec /%12ld frames\n rq diff: %12ld sec %8ld usec /%12ld frames\n jq diff: %12ld sec %8ld usec /%12ld frames\n", + tv.tv_sec, tv.tv_usec, rt_to_frame(tv, sample_rate), + qtv.tv_sec, qtv.tv_usec, rt_to_frame(qtv, sample_rate), + jtv.tv_sec, jtv.tv_usec, rt_to_frame(jtv, sample_rate), + diff.tv_sec, diff.tv_usec, rt_to_frame(diff, sample_rate), + jdiff.tv_sec, jdiff.tv_usec, rt_to_frame(jdiff, sample_rate)); + + fprintf(stderr, "\n"); + struct timespec ts; + ts.tv_sec = 1; + ts.tv_nsec = 0; + nanosleep(&ts, 0); + } +} + diff --git a/src/base/test/seq/queue-timer.c b/src/base/test/seq/queue-timer.c new file mode 100644 index 0000000..2b7bac4 --- /dev/null +++ b/src/base/test/seq/queue-timer.c @@ -0,0 +1,123 @@ + +#include <alsa/asoundlib.h> +#include <alsa/seq.h> +#include <sys/time.h> + +void normalize(struct timeval *tv) +{ + if (tv->tv_sec == 0) { + while (tv->tv_usec <= -1000000) { tv->tv_usec += 1000000; --tv->tv_sec; } + while (tv->tv_usec >= 1000000) { tv->tv_usec -= 1000000; ++tv->tv_sec; } + } else if (tv->tv_sec < 0) { + while (tv->tv_usec <= -1000000) { tv->tv_usec += 1000000; --tv->tv_sec; } + while (tv->tv_usec > 0) { tv->tv_usec -= 1000000; ++tv->tv_sec; } + } else { + while (tv->tv_usec >= 1000000) { tv->tv_usec -= 1000000; ++tv->tv_sec; } + while (tv->tv_usec < 0) { tv->tv_usec += 1000000; --tv->tv_sec; } + } +} + +int +main(int argc, char **argv) +{ + snd_seq_t *handle; + int portid; + int npfd; + struct pollfd *pfd; + int queue; + int i; + int rval; + struct timeval starttv, prevdiff; + int countdown = -1; + snd_seq_queue_timer_t *timer; + snd_timer_id_t *timerid; + + if (snd_seq_open(&handle, "hw", SND_SEQ_OPEN_DUPLEX, 0) < 0) { + fprintf(stderr, "failed to open ALSA sequencer interface\n"); + return 1; + } + + snd_seq_set_client_name(handle, "generator"); + + if ((portid = snd_seq_create_simple_port + (handle, "generator", + SND_SEQ_PORT_CAP_READ | SND_SEQ_PORT_CAP_SUBS_READ, 0)) < 0) { + fprintf(stderr, "failed to create ALSA sequencer port\n"); + return 1; + } + + if ((queue = snd_seq_alloc_queue(handle)) < 0) { + fprintf(stderr, "failed to create ALSA sequencer queue\n"); + return 1; + } +/* + snd_seq_queue_timer_alloca(&timer); + snd_seq_get_queue_timer(handle, queue, timer); + snd_timer_id_alloca(&timerid); + snd_timer_id_set_class(timerid, SND_TIMER_CLASS_PCM); + snd_timer_id_set_sclass(timerid, SND_TIMER_SCLASS_NONE); + snd_timer_id_set_card(timerid, 0); + snd_timer_id_set_device(timerid, 0); + snd_timer_id_set_subdevice(timerid, 0); + snd_seq_queue_timer_set_id(timer, timerid); + snd_seq_set_queue_timer(handle, queue, timer); +*/ + snd_seq_start_queue(handle, queue, 0); + snd_seq_drain_output(handle); + + gettimeofday(&starttv, 0); + prevdiff.tv_sec = 0; + prevdiff.tv_usec = 0; + + while (countdown != 0) { + + snd_seq_queue_status_t *status; + const snd_seq_real_time_t *rtime; + struct timeval tv, diff, diffdiff; + + snd_seq_queue_status_alloca(&status); + + snd_seq_get_queue_status(handle, queue, status); + rtime = snd_seq_queue_status_get_real_time(status); + + gettimeofday(&tv, 0); + + tv.tv_sec -= starttv.tv_sec; + tv.tv_usec -= starttv.tv_usec; + normalize(&tv); + + diff.tv_sec = tv.tv_sec - rtime->tv_sec; + diff.tv_usec = tv.tv_usec - rtime->tv_nsec / 1000; + normalize(&diff); + + diffdiff.tv_sec = diff.tv_sec - prevdiff.tv_sec; + diffdiff.tv_usec = diff.tv_usec - prevdiff.tv_usec; + normalize(&diffdiff); + prevdiff = diff; + + fprintf(stderr, " real time: %12ld sec %8ld usec\nqueue time: %12ld sec %8ld usec\n diff: %12ld sec %8ld usec\n diffdiff: %12ld sec %8ld usec\n", + tv.tv_sec, tv.tv_usec, + rtime->tv_sec, rtime->tv_nsec / 1000, + diff.tv_sec, diff.tv_usec, + diffdiff.tv_sec, diffdiff.tv_usec); + + if (diffdiff.tv_usec > 5000 || + diffdiff.tv_usec < -5000) { + fprintf(stderr, "oops! queue slipped\n"); + if (tv.tv_sec < 5) { + fprintf(stderr, "(ignoring in first few seconds)\n"); + } else { + countdown = 2; + } + } else { + if (countdown > 0) --countdown; + } + + fprintf(stderr, "\n"); + struct timespec ts; + ts.tv_sec = 1; + ts.tv_nsec = 0; + nanosleep(&ts, 0); + } +} + diff --git a/src/base/test/test.cpp b/src/base/test/test.cpp new file mode 100644 index 0000000..9a9b496 --- /dev/null +++ b/src/base/test/test.cpp @@ -0,0 +1,535 @@ +// -*- c-basic-offset: 4 -*- +// -*- c-file-style: "bsd" -*- + +#define NDEBUG + +// This does some rather shoddy tests on a small selection of core classes. + +#include "Event.h" +#include "Segment.h" +#include "Composition.h" +//#include "Sets.h" + +#define TEST_NOTATION_TYPES 1 +#define TEST_SPEED 1 + +#ifdef TEST_NOTATION_TYPES +#include "NotationTypes.h" +#include "SegmentNotationHelper.h" +#include "SegmentPerformanceHelper.h" +#endif + +#include "MidiTypes.h" + +#include <cstdio> + +#include <sys/times.h> +#include <iostream> + +using namespace std; +using namespace Rosegarden; + +static const PropertyName DURATION_PROPERTY = "duration"; +static const PropertyName SOME_INT_PROPERTY = "someIntProp"; +static const PropertyName SOME_BOOL_PROPERTY = "someBoolProp"; +static const PropertyName SOME_STRING_PROPERTY = "someStringProp"; +static const PropertyName NONEXISTENT_PROPERTY = "nonexistentprop"; +static const PropertyName ANNOTATION_PROPERTY = "annotation"; + +#if 0 +// Some attempts at reproducing the func-template-within-template problem +// +enum FooType {A, B, C}; + +class Foo +{ +public: + template<FooType T> void func(); +}; + +template<class T> +void Foo::func() +{ + // dummy code + T j = 0; + for(T i = 0; i < 100; ++i) j += i; +} + +//template void Foo::func<int>(); + +template <class R> +class FooR +{ +public: + void rfunc(); +}; + +template<class R> +void FooR<R>::rfunc() +{ + // this won't compile + Foo* foo; + foo->func<A>(); +} + +void templateTest() +{ + Foo foo; + foo.func<A>(); + +// FooR<float> foor; +// foor.rfunc(); +} + + +template <class Element, class Container> +class GenericSet // abstract base +{ +public: + typedef typename Container::iterator Iterator; + + /// Return true if this element, known to test() true, is a set member + virtual bool sample(const Iterator &i); +}; + + +template <class Element, class Container> +bool +GenericSet<Element, Container>::sample(const Iterator &i) +{ + Event *e; + long p = e->get<Int>("blah"); +} + +#endif + +int main(int argc, char **argv) +{ + typedef std::vector<int> intvect; + +// intvect foo; + +// GenericSet<int, intvect> genset; +// genset.sample(foo.begin()); + + clock_t st, et; + struct tms spare; + +#ifdef TEST_WIDE_STRING + basic_string<wchar_t> widestring(L"This is a test"); + widestring += L" of wide character strings"; + for (unsigned int i = 0; i < widestring.length(); ++i) { + if (widestring[i] == L'w' || + widestring[i] == L'c') { + widestring[i] = toupper(widestring[i]); + } + } + cout << "Testing wide string: string value is \"" << widestring << "\"" + << endl; + cout << "String's length is " << widestring.length() << endl; + cout << "and storage space is " + << (widestring.length() * sizeof(widestring[0])) + << endl; + cout << "Characters are: "; + for (unsigned int i = 0; i < widestring.length(); ++i) { + cout << widestring[i]; + if (i < widestring.length()-1) cout << " "; + else cout << endl; + } +#endif + + cout << "\nTesting Event..." << endl + << "sizeof Event : " << sizeof(Event) << endl; + + Event e("note", 0); + e.set<Int>(DURATION_PROPERTY, 20); + cout << "duration is " << e.get<Int>(DURATION_PROPERTY) << endl; + + e.set<Bool>(SOME_BOOL_PROPERTY, true); + e.set<String>(SOME_STRING_PROPERTY, "foobar"); + + cout << "sizeof event after some properties set : " + << sizeof e << endl; + + try { + cout << "duration is " << e.get<String>(DURATION_PROPERTY) << endl; + } catch (Event::BadType bt) { + cout << "Correctly caught BadType when trying to get<String> of duration" << endl; + } + + string s; + + if (!e.get<String>(DURATION_PROPERTY, s)) { + cout << "Correctly got error when trying to get<String> of duration" << endl; + } else { + cerr << "ERROR AT " << __LINE__ << endl; + } + + try { + cout << "dummy prop is " << e.get<String>(NONEXISTENT_PROPERTY) << endl; + } catch (Event::NoData bt) { + cout << "Correctly caught NoData when trying to get non existent property" << endl; + } + + if (!e.get<String>(NONEXISTENT_PROPERTY, s)) { + cout << "Correctly got error when trying to get<String> of non existent property" << endl; + } else { + cerr << "ERROR AT " << __LINE__ << endl; + } + + + e.setFromString<Int>(DURATION_PROPERTY, "30"); + cout << "duration is " << e.get<Int>(DURATION_PROPERTY) << endl; + + e.setFromString<String>(ANNOTATION_PROPERTY, "This is my house"); + cout << "annotation is " << e.get<String>(ANNOTATION_PROPERTY) << endl; + + long durationVal; + if (e.get<Int>(DURATION_PROPERTY, durationVal)) + cout << "duration is " << durationVal << endl; + else + cerr << "ERROR AT " << __LINE__ << endl; + + if (e.get<String>(ANNOTATION_PROPERTY, s)) + cout << "annotation is " << s << endl; + else + cerr << "ERROR AT " << __LINE__ << endl; + + cout << "\nTesting persistence & setMaybe..." << endl; + + e.setMaybe<Int>(SOME_INT_PROPERTY, 1); + if (e.get<Int>(SOME_INT_PROPERTY) == 1) { + cout << "a. Correct: 1" << endl; + } else { + cout << "a. ERROR: " << e.get<Int>(SOME_INT_PROPERTY) << endl; + } + + e.set<Int>(SOME_INT_PROPERTY, 2, false); + e.setMaybe<Int>(SOME_INT_PROPERTY, 3); + if (e.get<Int>(SOME_INT_PROPERTY) == 3) { + cout << "b. Correct: 3" << endl; + } else { + cout << "b. ERROR: " << e.get<Int>(SOME_INT_PROPERTY) << endl; + } + + e.set<Int>(SOME_INT_PROPERTY, 4); + e.setMaybe<Int>(SOME_INT_PROPERTY, 5); + if (e.get<Int>(SOME_INT_PROPERTY) == 4) { + cout << "c. Correct: 4" << endl; + } else { + cout << "c. ERROR: " << e.get<Int>(SOME_INT_PROPERTY) << endl; + } + + cout << "\nTesting debug dump : " << endl; + e.dump(cout); + cout << endl << "dump finished" << endl; + +#if TEST_SPEED + cout << "Testing speed of Event..." << endl; + int i; + long j; + + char b[20]; + strcpy(b, "test"); + +#define NAME_COUNT 500 + + PropertyName names[NAME_COUNT]; + for (i = 0; i < NAME_COUNT; ++i) { + sprintf(b+4, "%d", i); + names[i] = b; + } + + Event e1("note", 0); + int gsCount = 200000; + + st = times(&spare); + for (i = 0; i < gsCount; ++i) { + e1.set<Int>(names[i % NAME_COUNT], i); + } + et = times(&spare); + cout << "Event: " << gsCount << " setInts: " << (et-st)*10 << "ms\n"; + + st = times(&spare); + j = 0; + for (i = 0; i < gsCount; ++i) { + if (i%4==0) sprintf(b+4, "%d", i); + j += e1.get<Int>(names[i % NAME_COUNT]); + } + et = times(&spare); + cout << "Event: " << gsCount << " getInts: " << (et-st)*10 << "ms (result: " << j << ")\n"; + + st = times(&spare); + for (i = 0; i < 1000; ++i) { + Event e11(e1); + (void)e11.get<Int>(names[i % NAME_COUNT]); + } + et = times(&spare); + cout << "Event: 1000 copy ctors of " << e1.getStorageSize() << "-byte element: " + << (et-st)*10 << "ms\n"; + +// gsCount = 100000; + + for (i = 0; i < NAME_COUNT; ++i) { + sprintf(b+4, "%ds", i); + names[i] = b; + } + + st = times(&spare); + for (i = 0; i < gsCount; ++i) { + e1.set<String>(names[i % NAME_COUNT], b); + } + et = times(&spare); + cout << "Event: " << gsCount << " setStrings: " << (et-st)*10 << "ms\n"; + + st = times(&spare); + j = 0; + for (i = 0; i < gsCount; ++i) { + if (i%4==0) sprintf(b+4, "%ds", i); + j += e1.get<String>(names[i % NAME_COUNT]).size(); + } + et = times(&spare); + cout << "Event: " << gsCount << " getStrings: " << (et-st)*10 << "ms (result: " << j << ")\n"; + + st = times(&spare); + for (i = 0; i < 1000; ++i) { + Event e11(e1); + (void)e11.get<String>(names[i % NAME_COUNT]); + } + et = times(&spare); + cout << "Event: 1000 copy ctors of " << e1.getStorageSize() << "-byte element: " + << (et-st)*10 << "ms\n"; + + st = times(&spare); + for (i = 0; i < 1000; ++i) { + Event e11(e1); + (void)e11.get<String>(names[i % NAME_COUNT]); + (void)e11.set<String>(names[i % NAME_COUNT], "blah"); + } + et = times(&spare); + cout << "Event: 1000 copy ctors plus set<String> of " << e1.getStorageSize() << "-byte element: " + << (et-st)*10 << "ms\n"; + +// gsCount = 1000000; + + st = times(&spare); + for (i = 0; i < gsCount; ++i) { + Event e21("dummy", i, 0, MIN_SUBORDERING); + } + et = times(&spare); + cout << "Event: " << gsCount << " event ctors alone: " + << (et-st)*10 << "ms\n"; + + st = times(&spare); + for (i = 0; i < gsCount; ++i) { + std::string s0("dummy"); + std::string s1 = s0; + } + et = times(&spare); + cout << "Event: " << gsCount << " string ctors+assignents: " + << (et-st)*10 << "ms\n"; + + st = times(&spare); + for (i = 0; i < gsCount; ++i) { + Event e21("dummy", i, 0, MIN_SUBORDERING); + (void)e21.getAbsoluteTime(); + (void)e21.getDuration(); + (void)e21.getSubOrdering(); + } + et = times(&spare); + cout << "Event: " << gsCount << " event ctors plus getAbsTime/Duration/SubOrdering: " + << (et-st)*10 << "ms\n"; + + st = times(&spare); + for (i = 0; i < gsCount; ++i) { + Event e21("dummy", i, 0, MIN_SUBORDERING); + (void)e21.getAbsoluteTime(); + (void)e21.getDuration(); + (void)e21.getSubOrdering(); + e21.set<Int>(names[0], 40); + (void)e21.get<Int>(names[0]); + } + et = times(&spare); + cout << "Event: " << gsCount << " event ctors plus one get/set and getAbsTime/Duration/SubOrdering: " + << (et-st)*10 << "ms\n"; + + +#else + cout << "Skipping test speed of Event\n"; +#endif // TEST_SPEED + +#ifdef NOT_DEFINED + cout << "Testing segment shrinking\n"; + + Segment segment(5, 0); + unsigned int nbBars = segment.getNbBars(); + + cout << "Segment nbBars : " << nbBars << endl; + if (nbBars != 5) { + cerr << "%%%ERROR : segment nbBars should be 5\n"; + } + + Segment::iterator iter = segment.end(); + --iter; + cout << "Last segment el. time : " << (*iter)->getAbsoluteTime() << endl; + + cout << "Shrinking segment to 3 bars : \n"; + segment.setNbBars(3); + nbBars = segment.getNbBars(); + + cout << "Segment new nbBars : " << nbBars << endl; + if (nbBars != 3) { + cerr << "%%%ERROR : segment new nbBars should be 3\n"; + } +#endif // NOT_DEFINED + +#ifdef TEST_NOTATION_TYPES + cout << "Testing duration-list stuff\n"; + + cout << "2/4..." << endl; + TimeSignature ts(2,4); + DurationList dlist; + ts.getDurationListForInterval + (dlist, 1209, + ts.getBarDuration() - Note(Note::Semiquaver, true).getDuration()); + int acc = 0; + for (DurationList::iterator i = dlist.begin(); i != dlist.end(); ++i) { + cout << "duration: " << *i << endl; + acc += *i; + } + cout << "total: " << acc << " (on bar duration of " << ts.getBarDuration() << ")" << endl; + + + + cout << "4/4 96/96..." << endl; + ts = TimeSignature(4,4); + dlist = DurationList(); + ts.getDurationListForInterval(dlist, 96, 96); + acc = 0; + for (DurationList::iterator i = dlist.begin(); i != dlist.end(); ++i) { + cout << "duration: " << *i << endl; + acc += *i; + } + cout << "total: " << acc << " (on bar duration of " << ts.getBarDuration() << ")" << endl; + + + + cout << "6/8..." << endl; + ts = TimeSignature(6,8); + dlist = DurationList(); + ts.getDurationListForInterval + (dlist, 1209, + ts.getBarDuration() - Note(Note::Semiquaver, true).getDuration()); + acc = 0; + for (DurationList::iterator i = dlist.begin(); i != dlist.end(); ++i) { + cout << "duration: " << *i << endl; + acc += *i; + } + cout << "total: " << acc << " (on bar duration of " << ts.getBarDuration() << ")" << endl; + + cout << "3/4..." << endl; + ts = TimeSignature(3,4); + dlist = DurationList(); + ts.getDurationListForInterval + (dlist, 1209, + ts.getBarDuration() - Note(Note::Semiquaver, true).getDuration()); + acc = 0; + for (DurationList::iterator i = dlist.begin(); i != dlist.end(); ++i) { + cout << "duration: " << *i << endl; + acc += *i; + } + cout << "total: " << acc << " (on bar duration of " << ts.getBarDuration() << ")" << endl; + + cout << "4/4..." << endl; + ts = TimeSignature(4,4); + dlist = DurationList(); + ts.getDurationListForInterval + (dlist, 1209, + ts.getBarDuration() - Note(Note::Semiquaver, true).getDuration()); + acc = 0; + for (DurationList::iterator i = dlist.begin(); i != dlist.end(); ++i) { + cout << "duration: " << *i << endl; + acc += *i; + } + cout << "total: " << acc << " (on bar duration of " << ts.getBarDuration() << ")" << endl; + + cout << "3/8..." << endl; + ts = TimeSignature(3,8); + dlist = DurationList(); + ts.getDurationListForInterval + (dlist, 1209, + ts.getBarDuration() - Note(Note::Semiquaver, true).getDuration()); + acc = 0; + for (DurationList::iterator i = dlist.begin(); i != dlist.end(); ++i) { + cout << "duration: " << *i << endl; + acc += *i; + } + cout << "total: " << acc << " (on bar duration of " << ts.getBarDuration() << ")" << endl; + + cout << "4/4 wacky placement..." << endl; + ts = TimeSignature(4,4); + dlist = DurationList(); + ts.getDurationListForInterval(dlist, 160, 1280); + acc = 0; + for (DurationList::iterator i = dlist.begin(); i != dlist.end(); ++i) { + cout << "duration: " << *i << endl; + acc += *i; + } + cout << "total: " << acc << " (on bar duration of " << ts.getBarDuration() << ")" << endl; + + cout << "Testing Segment::splitIntoTie() - splitting 384 -> 2*192\n"; + + Composition c; + Segment *ht = new Segment(); + c.addSegment(ht); + Segment &t(*ht); + SegmentNotationHelper nh(t); + SegmentPerformanceHelper ph(t); + + Event *ev = new Event("note", 0, 384); + ev->set<Int>("pitch", 60); + t.insert(ev); + + Segment::iterator sb(t.begin()); + nh.splitIntoTie(sb, 384/2); + + for(Segment::iterator i = t.begin(); i != t.end(); ++i) { + cout << "Event at " << (*i)->getAbsoluteTime() + << " - duration : " << (*i)->getDuration() + << endl; + } + + Segment::iterator half2 = t.begin(); ++half2; + + cout << "Splitting 192 -> (48 + 144) : \n"; + + sb = t.begin(); + nh.splitIntoTie(sb, 48); + + for(Segment::iterator i = t.begin(); i != t.end(); ++i) { + cout << "Event at " << (*i)->getAbsoluteTime() + << " - duration : " << (*i)->getDuration() + << endl; + } + + cout << "Splitting 192 -> (144 + 48) : \n"; + + nh.splitIntoTie(half2, 144); + + + for(Segment::iterator i = t.begin(); i != t.end(); ++i) { + cout << "Event at " << (*i)->getAbsoluteTime() + << " - duration : " << (*i)->getDuration() + << " - performance duration : " << + ph.getSoundingDuration(i) << endl; + + cout << endl; + (*i)->dump(cout); + cout << endl; + } + + nh.autoBeam(t.begin(), t.end(), "beamed"); + +#endif // TEST_NOTATION_TYPES +}; + diff --git a/src/base/test/thread.cpp b/src/base/test/thread.cpp new file mode 100644 index 0000000..ab327ff --- /dev/null +++ b/src/base/test/thread.cpp @@ -0,0 +1,126 @@ +// -*- c-basic-offset: 4 -*- +// -*- c-file-style: "bsd" -*- + +// This does some rather shoddy tests on a small selection of core classes. + +#include "Lock.h" +#include "Composition.h" +#include "Segment.h" +#include "Event.h" + +#include <cstdio> +#include <sys/times.h> +#include <iostream> + +#include <pthread.h> +#include <unistd.h> + +using namespace std; +using namespace Rosegarden; + +static void* +writer_thread1(void *arg) +{ + pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL); + cout << "write_thread1 - init" << endl; + + Rosegarden::Composition *comp = + static_cast<Rosegarden::Composition*>(arg); + + Rosegarden::Composition::segmentcontainer segs = comp->getSegments(); + Rosegarden::Composition::segmentcontainer::iterator it = segs.begin(); + Rosegarden::Segment *segment = *it; + + Rosegarden::timeT insertTime = 50000; + while (true) + { + usleep(90000); + cout << "LENGTH = " << comp->getNbBars() << endl; + segment->insert(new Event(Note::EventType, insertTime)); + insertTime += 96; + } +} + +static void* +write_thread2(void *arg) +{ + pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL); + cout << "write_thread2 - init" << endl; + + Rosegarden::Composition *comp = + static_cast<Rosegarden::Composition*>(arg); + + Rosegarden::Composition::segmentcontainer segs = comp->getSegments(); + Rosegarden::Composition::segmentcontainer::iterator it = segs.begin(); + Rosegarden::Segment *segment = *it; + + Rosegarden::timeT insertTime = 0; + while (true) + { + usleep(50); + cout << "LENGTH = " << comp->getNbBars() << endl; + segment->insert(new Event(Note::EventType, insertTime)); + insertTime += 96; + } +} + + +int +main(int argc, char **argv) +{ + clock_t st, et; + struct tms spare; + + cout << "Threading test" << endl; + + pthread_t thread1; + pthread_t thread2; + Rosegarden::Composition comp; + Rosegarden::Segment segment; + comp.addSegment(&segment); + + if (pthread_create(&thread1, 0, writer_thread1, &comp)) + { + cerr << "Couldn't start thread 1" << endl; + exit(1); + } + pthread_detach(thread1); + + if (pthread_create(&thread2, 0, write_thread2, &comp)) + { + cerr << "Couldn't start thread 2" << endl; + exit(1); + } + pthread_detach(thread2); + + pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL); + + static Lock lock; + + if (lock.getWriteLock(1)) + { + cout << "got write lock" << endl; + } + + if (lock.getWriteLock(0)) + { + cout << "got second write lock" << endl; + } + else + { + cout << "couldn't get second write lock" << endl; + } + + Rosegarden::timeT insertTime = 0; + while(true) + { + usleep(50000); + + cout << "Inserting Event at time " << insertTime << endl; + segment.insert(new Event(Note::EventType, insertTime)); + insertTime += 96; + } + +}; + + diff --git a/src/base/test/transpose.cpp b/src/base/test/transpose.cpp new file mode 100644 index 0000000..b1254f5 --- /dev/null +++ b/src/base/test/transpose.cpp @@ -0,0 +1,83 @@ +// -*- c-basic-offset: 4 -*- + +#include "NotationTypes.h" + +using namespace Rosegarden; +using std::cout; + +// Unit test-ish tests for transposition. +// +// Returns -1 (or crashes :)) on error, 0 on success + +/** + * should be in Pitch eventually + */ +void testAisDisplayAccidentalInCmaj() +{ + Pitch ais(70, Accidentals::Sharp); + Key cmaj ("C major"); + Accidental accidental = ais.getDisplayAccidental(cmaj); + if (accidental != Accidentals::Sharp) + { + std::cout << "Accidental for A# in Cmaj was " << accidental << " instead of expected Sharp" << std::endl; + exit(-1); + } +} + +/** + * transpose an A# up by a major second, should + * yield a B# (as C would be a minor triad) + */ +void testAisToBis() +{ + std::cout << "Testing transposing A# to B#... "; + Pitch ais(70, Accidentals::Sharp); + Key cmaj ("C major"); + + Pitch result = ais.transpose(cmaj, 2, 1); + + Accidental resultAccidental = result.getAccidental(cmaj); + int resultPitch = result.getPerformancePitch(); + if (resultAccidental != Accidentals::Sharp || resultPitch != 72) + { + std::cout << "Transposing A# up by a major second didn't yield B#, but " << result.getNoteName(cmaj) << resultAccidental << std::endl; + exit(-1); + } + std::cout << "Success" << std::endl; +} + +/** + * Transpose G to D in the key of D major. + */ +void testGToD() +{ + std::cout << "Testing transposing G to D... "; + Pitch g(67, Accidentals::Natural); + Key* dmaj = new Key("D major"); + + Pitch result = g.transpose(*dmaj, 7, 4); + + Accidental resultAccidental = result.getAccidental(*dmaj); + int resultPitch = result.getPerformancePitch(); + if (resultAccidental != Accidentals::NoAccidental || resultPitch != 74) + { + std::cout << "Transposing G up by a fifth didn't yield D, but " << result.getNoteName(*dmaj) << resultAccidental << std::endl; + exit(-1); + } + std::cout << "Success" << std::endl; +} + +void testKeyTransposition() +{ + +} + +int main(int argc, char **argv) +{ + testAisDisplayAccidentalInCmaj(); + testAisToBis(); + testGToD(); + testKeyTransposition(); + + return 0; +} diff --git a/src/base/test/utf8.cpp b/src/base/test/utf8.cpp new file mode 100644 index 0000000..7104cc0 --- /dev/null +++ b/src/base/test/utf8.cpp @@ -0,0 +1,96 @@ +// -*- c-basic-offset: 4 -*- + +#include "XmlExportable.h" +#include <iostream> +#include <string> + +using namespace Rosegarden; +using std::cout; +using std::cerr; +using std::endl; +using std::string; + + +string binary(unsigned char c) +{ + string s; + for (int i = 0; i < 8; ++i) { + s = ((c & 0x1) ? '1' : '0') + s; + c >>= 1; + } + return s; +} + + +int main(int argc, char **argv) +{ + string valid[] = { + "ニュース", + "주요 뉴스", + "Nyheter", + "天气", + "Notícias", + }; + + string escapable[] = { + "ニュ&ース", + "주요 <뉴스>", + "\"Nyheter\"", + "\'Notícias\'", + }; + + string invalid[] = { + "����ース", + "주� � 뉴스", + "Nyhe\004ter", + "�天气", + "Not�cias", + }; + + cout << "Testing valid strings -- should be no errors here" << endl; + + for (int i = 0; i < sizeof(valid)/sizeof(valid[0]); ++i) { + string encoded = XmlExportable::encode(valid[i]); + if (encoded != valid[i]) { + cerr << "Encoding failed:" << endl; + for (int j = 0; j < valid[i].length(); ++j) { + cerr << (char)valid[i][j] << " (" + << binary(valid[i][j]) << ")" << endl; + } + exit(1); + } + } + + cout << "Testing escapable strings -- should be no errors here" << endl; + + for (int i = 0; i < sizeof(escapable)/sizeof(escapable[0]); ++i) { + string encoded = XmlExportable::encode(escapable[i]); + if (encoded == escapable[i]) { + cerr << "Escaping failed:" << endl; + for (int j = 0; j < escapable[i].length(); ++j) { + cerr << (char)escapable[i][j] << " (" + << binary(escapable[i][j]) << ")" << endl; + } + exit(1); + } + } + + cout << "Testing invalid strings -- should be " + << (sizeof(invalid)/sizeof(invalid[0])) + << " errors here (but no fatal ones)" << endl; + + for (int i = 0; i < sizeof(invalid)/sizeof(invalid[0]); ++i) { + string encoded = XmlExportable::encode(invalid[i]); + if (encoded == invalid[i]) { + cerr << "Encoding succeeded but should have failed:" << endl; + for (int j = 0; j < invalid[i].length(); ++j) { + cerr << (char)invalid[i][j] << " (" + << binary(invalid[i][j]) << ")" << endl; + } + exit(1); + } + } + + exit(0); +} + |