# HG changeset patch # User Chris Cannam # Date 1423477867 0 # Node ID ce1077bd663a337219375ed92b7f5564df7aa553 # Parent dbb7f0ab011e7cc6f634e9623b956bf50174caad# Parent 88b54a185a0a9b0daeca0efd0bbe22243f4dfbc0 Merge from default branch diff -r dbb7f0ab011e -r ce1077bd663a base/Pitch.cpp --- a/base/Pitch.cpp Mon Nov 17 15:31:07 2014 +0000 +++ b/base/Pitch.cpp Mon Feb 09 10:31:07 2015 +0000 @@ -19,45 +19,49 @@ #include -float +double Pitch::getFrequencyForPitch(int midiPitch, - float centsOffset, - float concertA) + double centsOffset, + double concertA) { if (concertA <= 0.0) { concertA = Preferences::getInstance()->getTuningFrequency(); } - float p = float(midiPitch) + (centsOffset / 100); - return concertA * powf(2.0, (p - 69.0) / 12.0); + double p = double(midiPitch) + (centsOffset / 100); + return concertA * pow(2.0, (p - 69.0) / 12.0); } int -Pitch::getPitchForFrequency(float frequency, - float *centsOffsetReturn, - float concertA) +Pitch::getPitchForFrequency(double frequency, + double *centsOffsetReturn, + double concertA) { if (concertA <= 0.0) { concertA = Preferences::getInstance()->getTuningFrequency(); } - float p = 12.0 * (log(frequency / (concertA / 2.0)) / log(2.0)) + 57.0; + double p = 12.0 * (log(frequency / (concertA / 2.0)) / log(2.0)) + 57.0; - int midiPitch = int(p + 0.00001); - float centsOffset = (p - midiPitch) * 100.0; + int midiPitch = int(round(p)); + double centsOffset = (p - midiPitch) * 100.0; if (centsOffset >= 50.0) { midiPitch = midiPitch + 1; centsOffset = -(100.0 - centsOffset); } + if (centsOffset < -50.0) { + midiPitch = midiPitch - 1; + centsOffset = (100.0 + centsOffset); + } if (centsOffsetReturn) *centsOffsetReturn = centsOffset; return midiPitch; } int -Pitch::getPitchForFrequencyDifference(float frequencyA, - float frequencyB, - float *centsOffsetReturn, - float concertA) +Pitch::getPitchForFrequencyDifference(double frequencyA, + double frequencyB, + double *centsOffsetReturn, + double concertA) { if (concertA <= 0.0) { concertA = Preferences::getInstance()->getTuningFrequency(); @@ -67,13 +71,13 @@ std::swap(frequencyA, frequencyB); } - float pA = 12.0 * (log(frequencyA / (concertA / 2.0)) / log(2.0)) + 57.0; - float pB = 12.0 * (log(frequencyB / (concertA / 2.0)) / log(2.0)) + 57.0; + double pA = 12.0 * (log(frequencyA / (concertA / 2.0)) / log(2.0)) + 57.0; + double pB = 12.0 * (log(frequencyB / (concertA / 2.0)) / log(2.0)) + 57.0; - float p = pB - pA; + double p = pB - pA; int midiPitch = int(p + 0.00001); - float centsOffset = (p - midiPitch) * 100.0; + double centsOffset = (p - midiPitch) * 100.0; if (centsOffset >= 50.0) { midiPitch = midiPitch + 1; @@ -96,13 +100,19 @@ "Ab%1", "A%1", "Bb%1", "B%1" }; -QString -Pitch::getPitchLabel(int midiPitch, - float centsOffset, - bool useFlats) +int +Pitch::getPitchForNoteAndOctave(int note, int octave) { int baseOctave = Preferences::getInstance()->getOctaveOfLowestMIDINote(); - int octave = baseOctave; + return (octave - baseOctave) * 12 + note; +} + +void +Pitch::getNoteAndOctaveForPitch(int midiPitch, int ¬e, int &octave) +{ + int baseOctave = Preferences::getInstance()->getOctaveOfLowestMIDINote(); + + octave = baseOctave; // Note, this only gets the right octave number at octave // boundaries because Cb is enharmonic with B (not B#) and B# is @@ -118,44 +128,55 @@ octave = midiPitch / 12 + baseOctave; } - QString plain = (useFlats ? flatNotes : notes)[midiPitch % 12].arg(octave); + note = midiPitch % 12; +} - int ic = lrintf(centsOffset); +QString +Pitch::getPitchLabel(int midiPitch, + double centsOffset, + bool useFlats) +{ + int note, octave; + getNoteAndOctaveForPitch(midiPitch, note, octave); + + QString plain = (useFlats ? flatNotes : notes)[note].arg(octave); + + int ic = lrint(centsOffset); if (ic == 0) return plain; else if (ic > 0) return QString("%1+%2c").arg(plain).arg(ic); else return QString("%1%2c").arg(plain).arg(ic); } QString -Pitch::getPitchLabelForFrequency(float frequency, - float concertA, +Pitch::getPitchLabelForFrequency(double frequency, + double concertA, bool useFlats) { if (concertA <= 0.0) { concertA = Preferences::getInstance()->getTuningFrequency(); } - float centsOffset = 0.0; + double centsOffset = 0.0; int midiPitch = getPitchForFrequency(frequency, ¢sOffset, concertA); return getPitchLabel(midiPitch, centsOffset, useFlats); } QString -Pitch::getLabelForPitchRange(int semis, float cents) +Pitch::getLabelForPitchRange(int semis, double cents) { if (semis > 0) { - while (cents < 0.f) { + while (cents < 0.0) { --semis; - cents += 100.f; + cents += 100.0; } } if (semis < 0) { - while (cents > 0.f) { + while (cents > 0.0) { ++semis; - cents -= 100.f; + cents -= 100.0; } } - int ic = lrintf(cents); + int ic = lrint(cents); if (ic == 0) { if (semis >= 12) { @@ -181,10 +202,10 @@ } bool -Pitch::isFrequencyInMidiRange(float frequency, - float concertA) +Pitch::isFrequencyInMidiRange(double frequency, + double concertA) { - float centsOffset = 0.0; + double centsOffset = 0.0; int midiPitch = getPitchForFrequency(frequency, ¢sOffset, concertA); return (midiPitch >= 0 && midiPitch < 128); } diff -r dbb7f0ab011e -r ce1077bd663a base/Pitch.h --- a/base/Pitch.h Mon Nov 17 15:31:07 2014 +0000 +++ b/base/Pitch.h Mon Feb 09 10:31:07 2015 +0000 @@ -30,9 +30,9 @@ * for the A at MIDI pitch 69; otherwise use the tuning frequency * specified in the application preferences (default 440Hz). */ - static float getFrequencyForPitch(int midiPitch, - float centsOffset = 0, - float concertA = 0.0); + static double getFrequencyForPitch(int midiPitch, + double centsOffset = 0, + double concertA = 0.0); /** * Return the nearest MIDI pitch to the given frequency. @@ -46,9 +46,22 @@ * for the A at MIDI pitch 69; otherwise use the tuning frequency * specified in the application preferences (default 440Hz). */ - static int getPitchForFrequency(float frequency, - float *centsOffsetReturn = 0, - float concertA = 0.0); + static int getPitchForFrequency(double frequency, + double *centsOffsetReturn = 0, + double concertA = 0.0); + + /** + * Compatibility version of getPitchForFrequency accepting float + * pointer argument. + */ + static int getPitchForFrequency(double frequency, + float *centsOffsetReturn, + double concertA = 0.0) { + double c; + int p = getPitchForFrequency(frequency, &c, concertA); + if (centsOffsetReturn) *centsOffsetReturn = float(c); + return p; + } /** * Return the nearest MIDI pitch range to the given frequency @@ -64,10 +77,41 @@ * for the A at MIDI pitch 69; otherwise use the tuning frequency * specified in the application preferences (default 440Hz). */ - static int getPitchForFrequencyDifference(float frequencyA, - float frequencyB, - float *centsOffsetReturn = 0, - float concertA = 0.0); + static int getPitchForFrequencyDifference(double frequencyA, + double frequencyB, + double *centsOffsetReturn = 0, + double concertA = 0.0); + + /** + * Compatibility version of getPitchForFrequencyDifference + * accepting float pointer argument. + */ + static int getPitchForFrequencyDifference(double frequencyA, + double frequencyB, + float *centsOffsetReturn, + double concertA = 0.0) { + double c; + int p = getPitchForFrequencyDifference(frequencyA, frequencyB, + &c, concertA); + if (centsOffsetReturn) *centsOffsetReturn = float(c); + return p; + } + + /** + * Return the MIDI pitch for the given note number (0-12 where 0 + * is C) and octave number. The octave numbering system is based + * on the application preferences (default is C4 = middle C, + * though in previous SV releases that was C3). + */ + static int getPitchForNoteAndOctave(int note, int octave); + + /** + * Return the note number (0-12 where 0 is C) and octave number + * for the given MIDI pitch. The octave numbering system is based + * on the application preferences (default is C4 = middle C, + * though in previous SV releases that was C3). + */ + static void getNoteAndOctaveForPitch(int midiPitch, int ¬e, int &octave); /** * Return a string describing the given MIDI pitch, with optional @@ -83,9 +127,9 @@ * e.g. Bb3 instead of A#3. */ static QString getPitchLabel(int midiPitch, - float centsOffset = 0, + double centsOffset = 0, bool useFlats = false); - + /** * Return a string describing the nearest MIDI pitch to the given * frequency, with cents offset. @@ -97,15 +141,15 @@ * If useFlats is true, spell notes with flats instead of sharps, * e.g. Bb3 instead of A#3. */ - static QString getPitchLabelForFrequency(float frequency, - float concertA = 0.0, + static QString getPitchLabelForFrequency(double frequency, + double concertA = 0.0, bool useFlats = false); /** * Return a string describing the given pitch range in octaves, * semitones and cents. This is in the form e.g. "1'2+4c". */ - static QString getLabelForPitchRange(int semis, float cents = 0); + static QString getLabelForPitchRange(int semis, double cents = 0); /** * Return true if the given frequency falls within the range of @@ -118,8 +162,8 @@ * for the A at MIDI pitch 69; otherwise use the tuning frequency * specified in the application preferences (default 440Hz). */ - static bool isFrequencyInMidiRange(float frequency, - float concertA = 0.0); + static bool isFrequencyInMidiRange(double frequency, + double concertA = 0.0); }; diff -r dbb7f0ab011e -r ce1077bd663a base/RealTime.cpp --- a/base/RealTime.cpp Mon Nov 17 15:31:07 2014 +0000 +++ b/base/RealTime.cpp Mon Feb 09 10:31:07 2015 +0000 @@ -59,7 +59,11 @@ RealTime RealTime::fromSeconds(double sec) { - return RealTime(int(sec), int((sec - int(sec)) * ONE_BILLION + 0.5)); + if (sec >= 0) { + return RealTime(int(sec), int((sec - int(sec)) * ONE_BILLION + 0.5)); + } else { + return -fromSeconds(-sec); + } } RealTime diff -r dbb7f0ab011e -r ce1077bd663a base/StringBits.cpp --- a/base/StringBits.cpp Mon Nov 17 15:31:07 2014 +0000 +++ b/base/StringBits.cpp Mon Feb 09 10:31:07 2015 +0000 @@ -20,6 +20,10 @@ #include "StringBits.h" +#include "Debug.h" + +using namespace std; + double StringBits::stringToDoubleLocaleFree(QString s, bool *ok) { @@ -73,6 +77,11 @@ QStringList tokens; QString tok; + // sep -> just seen a field separator (or start of line) + // unq -> in an unquoted field + // q1 -> in a single-quoted field + // q2 -> in a double-quoted field + enum { sep, unq, q1, q2 } mode = sep; for (int i = 0; i < s.length(); ++i) { @@ -83,14 +92,14 @@ switch (mode) { case sep: mode = q1; break; case unq: case q2: tok += c; break; - case q1: mode = sep; tokens << tok; tok = ""; break; + case q1: mode = unq; break; } } else if (c == '"') { switch (mode) { case sep: mode = q2; break; case unq: case q1: tok += c; break; - case q2: mode = sep; tokens << tok; tok = ""; break; + case q2: mode = unq; break; } } else if (c == separator || (separator == ' ' && c.isSpace())) { @@ -117,86 +126,19 @@ } } - if (tok != "" || mode != sep) tokens << tok; + if (tok != "" || mode != sep) { + if (mode == q1) { + tokens << ("'" + tok); // turns out it wasn't quoted after all + } else if (mode == q2) { + tokens << ("\"" + tok); + } else { + tokens << tok; + } + } + return tokens; } -/* - -void testSplit() -{ - QStringList tests; - tests << "a b c d"; - tests << "a \"b c\" d"; - tests << "a 'b c' d"; - tests << "a \"b c\\\" d\""; - tests << "a 'b c\\' d'"; - tests << "a \"b c' d\""; - tests << "a 'b c\" d'"; - tests << "aa 'bb cc\" dd'"; - tests << "a'a 'bb' \\\"cc\" dd\\\""; - tests << " a'a \\\' 'bb' \' \\\"cc\" ' dd\\\" '"; - - for (int j = 0; j < tests.size(); ++j) { - cout << endl; - cout << tests[j] << endl; - cout << "->" << endl << "("; - QStringList l = splitQuoted(tests[j], ' '); - for (int i = 0; i < l.size(); ++i) { - if (i > 0) cout << ";"; - cout << l[i]; - } - cout << ")" << endl; - } -} - -*/ - -/* - Results: - -a b c d --> -(a;b;c;d) - -a "b c" d --> -(a;b c;d) - -a 'b c' d --> -(a;b c;d) - -a "b c\" d" --> -(a;b c" d) - -a 'b c\' d' --> -(a;b c' d) - -a "b c' d" --> -(a;b c' d) - -a 'b c" d' --> -(a;b c" d) - -aa 'bb cc" dd' --> -(aa;bb cc" dd) - -a'a 'bb' \"cc" dd\" --> -(a'a;bb;"cc";dd") - - a'a \' 'bb' ' \"cc" ' dd\" ' --> -(a'a;';bb; "cc" ;dd";) - -*/ - QStringList StringBits::split(QString line, QChar separator, bool quoted) { diff -r dbb7f0ab011e -r ce1077bd663a base/test/TestPitch.h --- a/base/test/TestPitch.h Mon Nov 17 15:31:07 2014 +0000 +++ b/base/test/TestPitch.h Mon Feb 09 10:31:07 2015 +0000 @@ -72,18 +72,29 @@ QCOMPARE(Pitch::getPitchLabelForFrequency(261.63, 440, false), QString("C4")); } -#define MIDDLE_C 261.6255653f +#define MIDDLE_C 261.6255653005986 void frequencyForPitch() { QCOMPARE(Pitch::getFrequencyForPitch(60, 0), MIDDLE_C); - QCOMPARE(Pitch::getFrequencyForPitch(69, 0), 440.f); - QCOMPARE(Pitch::getFrequencyForPitch(60, 0, 220), MIDDLE_C / 2.f); - QCOMPARE(Pitch::getFrequencyForPitch(69, 0, 220), 220.f); + QCOMPARE(Pitch::getFrequencyForPitch(69, 0), 440.0); + QCOMPARE(Pitch::getFrequencyForPitch(60, 0, 220), MIDDLE_C / 2.0); + QCOMPARE(Pitch::getFrequencyForPitch(69, 0, 220), 220.0); } void pitchForFrequency() { + double centsOffset = 0.0; + QCOMPARE(Pitch::getPitchForFrequency(MIDDLE_C, ¢sOffset), 60); + QCOMPARE(centsOffset, 0.0); + QCOMPARE(Pitch::getPitchForFrequency(261.0, ¢sOffset), 60); + QCOMPARE(int(centsOffset), -4); + QCOMPARE(Pitch::getPitchForFrequency(440.0, ¢sOffset), 69); + QCOMPARE(centsOffset, 0.0); + } + + void pitchForFrequencyF() + { float centsOffset = 0.f; QCOMPARE(Pitch::getPitchForFrequency(MIDDLE_C, ¢sOffset), 60); QCOMPARE(centsOffset, 0.f); diff -r dbb7f0ab011e -r ce1077bd663a base/test/TestRealTime.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/base/test/TestRealTime.h Mon Feb 09 10:31:07 2015 +0000 @@ -0,0 +1,276 @@ +/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ + +/* + Sonic Visualiser + An audio file viewer and annotation editor. + Centre for Digital Music, Queen Mary, University of London. + + 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. +*/ + +#ifndef TEST_REALTIME_H +#define TEST_REALTIME_H + +#include "../RealTime.h" + +#include +#include +#include + +#include + +using namespace std; + +class TestRealTime : public QObject +{ + Q_OBJECT + +private slots: + +#define ONE_MILLION 1000000 +#define ONE_BILLION 1000000000 + + void zero() + { + QCOMPARE(RealTime(0, 0), RealTime::zeroTime); + QCOMPARE(RealTime(0, 0).sec, 0); + QCOMPARE(RealTime(0, 0).nsec, 0); + QCOMPARE(RealTime(0, 0).msec(), 0); + QCOMPARE(RealTime(0, 0).usec(), 0); + } + + void ctor() + { + QCOMPARE(RealTime(0, 0), RealTime(0, 0)); + + // wraparounds + QCOMPARE(RealTime(0, ONE_BILLION/2), RealTime(1, -ONE_BILLION/2)); + QCOMPARE(RealTime(0, -ONE_BILLION/2), RealTime(-1, ONE_BILLION/2)); + + QCOMPARE(RealTime(1, ONE_BILLION), RealTime(2, 0)); + QCOMPARE(RealTime(1, -ONE_BILLION), RealTime(0, 0)); + QCOMPARE(RealTime(-1, ONE_BILLION), RealTime(0, 0)); + QCOMPARE(RealTime(-1, -ONE_BILLION), RealTime(-2, 0)); + + QCOMPARE(RealTime(2, -ONE_BILLION*2), RealTime(0, 0)); + QCOMPARE(RealTime(2, -ONE_BILLION/2), RealTime(1, ONE_BILLION/2)); + + QCOMPARE(RealTime(-2, ONE_BILLION*2), RealTime(0, 0)); + QCOMPARE(RealTime(-2, ONE_BILLION/2), RealTime(-1, -ONE_BILLION/2)); + + QCOMPARE(RealTime(0, 1).sec, 0); + QCOMPARE(RealTime(0, 1).nsec, 1); + QCOMPARE(RealTime(0, -1).sec, 0); + QCOMPARE(RealTime(0, -1).nsec, -1); + QCOMPARE(RealTime(1, -1).sec, 0); + QCOMPARE(RealTime(1, -1).nsec, ONE_BILLION-1); + QCOMPARE(RealTime(-1, 1).sec, 0); + QCOMPARE(RealTime(-1, 1).nsec, -ONE_BILLION+1); + QCOMPARE(RealTime(-1, -1).sec, -1); + QCOMPARE(RealTime(-1, -1).nsec, -1); + + QCOMPARE(RealTime(2, -ONE_BILLION*2).sec, 0); + QCOMPARE(RealTime(2, -ONE_BILLION*2).nsec, 0); + QCOMPARE(RealTime(2, -ONE_BILLION/2).sec, 1); + QCOMPARE(RealTime(2, -ONE_BILLION/2).nsec, ONE_BILLION/2); + + QCOMPARE(RealTime(-2, ONE_BILLION*2).sec, 0); + QCOMPARE(RealTime(-2, ONE_BILLION*2).nsec, 0); + QCOMPARE(RealTime(-2, ONE_BILLION/2).sec, -1); + QCOMPARE(RealTime(-2, ONE_BILLION/2).nsec, -ONE_BILLION/2); + } + + void fromSeconds() + { + QCOMPARE(RealTime::fromSeconds(0), RealTime(0, 0)); + + QCOMPARE(RealTime::fromSeconds(0.5).sec, 0); + QCOMPARE(RealTime::fromSeconds(0.5).nsec, ONE_BILLION/2); + QCOMPARE(RealTime::fromSeconds(0.5).usec(), ONE_MILLION/2); + QCOMPARE(RealTime::fromSeconds(0.5).msec(), 500); + + QCOMPARE(RealTime::fromSeconds(0.5), RealTime(0, ONE_BILLION/2)); + QCOMPARE(RealTime::fromSeconds(1), RealTime(1, 0)); + QCOMPARE(RealTime::fromSeconds(1.5), RealTime(1, ONE_BILLION/2)); + + QCOMPARE(RealTime::fromSeconds(-0.5).sec, 0); + QCOMPARE(RealTime::fromSeconds(-0.5).nsec, -ONE_BILLION/2); + QCOMPARE(RealTime::fromSeconds(-0.5).usec(), -ONE_MILLION/2); + QCOMPARE(RealTime::fromSeconds(-0.5).msec(), -500); + + QCOMPARE(RealTime::fromSeconds(-1.5).sec, -1); + QCOMPARE(RealTime::fromSeconds(-1.5).nsec, -ONE_BILLION/2); + QCOMPARE(RealTime::fromSeconds(-1.5).usec(), -ONE_MILLION/2); + QCOMPARE(RealTime::fromSeconds(-1.5).msec(), -500); + + QCOMPARE(RealTime::fromSeconds(-0.5), RealTime(0, -ONE_BILLION/2)); + QCOMPARE(RealTime::fromSeconds(-1), RealTime(-1, 0)); + QCOMPARE(RealTime::fromSeconds(-1.5), RealTime(-1, -ONE_BILLION/2)); + } + + void fromMilliseconds() + { + QCOMPARE(RealTime::fromMilliseconds(0), RealTime(0, 0)); + QCOMPARE(RealTime::fromMilliseconds(500), RealTime(0, ONE_BILLION/2)); + QCOMPARE(RealTime::fromMilliseconds(1000), RealTime(1, 0)); + QCOMPARE(RealTime::fromMilliseconds(1500), RealTime(1, ONE_BILLION/2)); + + QCOMPARE(RealTime::fromMilliseconds(-0), RealTime(0, 0)); + QCOMPARE(RealTime::fromMilliseconds(-500), RealTime(0, -ONE_BILLION/2)); + QCOMPARE(RealTime::fromMilliseconds(-1000), RealTime(-1, 0)); + QCOMPARE(RealTime::fromMilliseconds(-1500), RealTime(-1, -ONE_BILLION/2)); + } + + void fromTimeval() + { + struct timeval tv; + + tv.tv_sec = 0; tv.tv_usec = 0; + QCOMPARE(RealTime::fromTimeval(tv), RealTime(0, 0)); + tv.tv_sec = 0; tv.tv_usec = ONE_MILLION/2; + QCOMPARE(RealTime::fromTimeval(tv), RealTime(0, ONE_BILLION/2)); + tv.tv_sec = 1; tv.tv_usec = 0; + QCOMPARE(RealTime::fromTimeval(tv), RealTime(1, 0)); + tv.tv_sec = 1; tv.tv_usec = ONE_MILLION/2; + QCOMPARE(RealTime::fromTimeval(tv), RealTime(1, ONE_BILLION/2)); + + tv.tv_sec = 0; tv.tv_usec = -ONE_MILLION/2; + QCOMPARE(RealTime::fromTimeval(tv), RealTime(0, -ONE_BILLION/2)); + tv.tv_sec = -1; tv.tv_usec = 0; + QCOMPARE(RealTime::fromTimeval(tv), RealTime(-1, 0)); + tv.tv_sec = -1; tv.tv_usec = -ONE_MILLION/2; + QCOMPARE(RealTime::fromTimeval(tv), RealTime(-1, -ONE_BILLION/2)); + } + + void fromXsdDuration() + { + QCOMPARE(RealTime::fromXsdDuration("PT0"), RealTime::zeroTime); + QCOMPARE(RealTime::fromXsdDuration("PT0S"), RealTime::zeroTime); + QCOMPARE(RealTime::fromXsdDuration("PT10S"), RealTime(10, 0)); + QCOMPARE(RealTime::fromXsdDuration("PT10.5S"), RealTime(10, ONE_BILLION/2)); + QCOMPARE(RealTime::fromXsdDuration("PT1.5S").sec, 1); + QCOMPARE(RealTime::fromXsdDuration("PT1.5S").msec(), 500); + QCOMPARE(RealTime::fromXsdDuration("-PT1.5S").sec, -1); + QCOMPARE(RealTime::fromXsdDuration("-PT1.5S").msec(), -500); + QCOMPARE(RealTime::fromXsdDuration("PT1M30.5S"), RealTime(90, ONE_BILLION/2)); + QCOMPARE(RealTime::fromXsdDuration("PT1H2M30.5S"), RealTime(3750, ONE_BILLION/2)); + } + + void toDouble() + { + QCOMPARE(RealTime(0, 0).toDouble(), 0.0); + QCOMPARE(RealTime(0, ONE_BILLION/2).toDouble(), 0.5); + QCOMPARE(RealTime(1, 0).toDouble(), 1.0); + QCOMPARE(RealTime(1, ONE_BILLION/2).toDouble(), 1.5); + + QCOMPARE(RealTime(0, -ONE_BILLION/2).toDouble(), -0.5); + QCOMPARE(RealTime(-1, 0).toDouble(), -1.0); + QCOMPARE(RealTime(-1, -ONE_BILLION/2).toDouble(), -1.5); + } + + void assign() + { + RealTime r; + r = RealTime(0, 0); + QCOMPARE(r, RealTime::zeroTime); + r = RealTime(0, ONE_BILLION/2); + QCOMPARE(r.toDouble(), 0.5); + r = RealTime(-1, -ONE_BILLION/2); + QCOMPARE(r.toDouble(), -1.5); + } + + void plus() + { + QCOMPARE(RealTime(0, 0) + RealTime(0, 0), RealTime(0, 0)); + + QCOMPARE(RealTime(0, 0) + RealTime(0, ONE_BILLION/2), RealTime(0, ONE_BILLION/2)); + QCOMPARE(RealTime(0, ONE_BILLION/2) + RealTime(0, ONE_BILLION/2), RealTime(1, 0)); + QCOMPARE(RealTime(1, 0) + RealTime(0, ONE_BILLION/2), RealTime(1, ONE_BILLION/2)); + + QCOMPARE(RealTime(0, 0) + RealTime(0, -ONE_BILLION/2), RealTime(0, -ONE_BILLION/2)); + QCOMPARE(RealTime(0, -ONE_BILLION/2) + RealTime(0, -ONE_BILLION/2), RealTime(-1, 0)); + QCOMPARE(RealTime(-1, 0) + RealTime(0, -ONE_BILLION/2), RealTime(-1, -ONE_BILLION/2)); + + QCOMPARE(RealTime(1, 0) + RealTime(0, -ONE_BILLION/2), RealTime(0, ONE_BILLION/2)); + QCOMPARE(RealTime(1, 0) + RealTime(0, -ONE_BILLION/2) + RealTime(0, -ONE_BILLION/2), RealTime(0, 0)); + QCOMPARE(RealTime(1, 0) + RealTime(0, -ONE_BILLION/2) + RealTime(0, -ONE_BILLION/2) + RealTime(0, -ONE_BILLION/2), RealTime(0, -ONE_BILLION/2)); + + QCOMPARE(RealTime(0, ONE_BILLION/2) + RealTime(-1, 0), RealTime(0, -ONE_BILLION/2)); + QCOMPARE(RealTime(0, -ONE_BILLION/2) + RealTime(1, 0), RealTime(0, ONE_BILLION/2)); + } + + void minus() + { + QCOMPARE(RealTime(0, 0) - RealTime(0, 0), RealTime(0, 0)); + + QCOMPARE(RealTime(0, 0) - RealTime(0, ONE_BILLION/2), RealTime(0, -ONE_BILLION/2)); + QCOMPARE(RealTime(0, ONE_BILLION/2) - RealTime(0, ONE_BILLION/2), RealTime(0, 0)); + QCOMPARE(RealTime(1, 0) - RealTime(0, ONE_BILLION/2), RealTime(0, ONE_BILLION/2)); + + QCOMPARE(RealTime(0, 0) - RealTime(0, -ONE_BILLION/2), RealTime(0, ONE_BILLION/2)); + QCOMPARE(RealTime(0, -ONE_BILLION/2) - RealTime(0, -ONE_BILLION/2), RealTime(0, 0)); + QCOMPARE(RealTime(-1, 0) - RealTime(0, -ONE_BILLION/2), RealTime(0, -ONE_BILLION/2)); + + QCOMPARE(RealTime(1, 0) - RealTime(0, -ONE_BILLION/2), RealTime(1, ONE_BILLION/2)); + QCOMPARE(RealTime(1, 0) - RealTime(0, -ONE_BILLION/2) - RealTime(0, -ONE_BILLION/2), RealTime(2, 0)); + QCOMPARE(RealTime(1, 0) - RealTime(0, -ONE_BILLION/2) - RealTime(0, -ONE_BILLION/2) - RealTime(0, -ONE_BILLION/2), RealTime(2, ONE_BILLION/2)); + + QCOMPARE(RealTime(0, ONE_BILLION/2) - RealTime(-1, 0), RealTime(1, ONE_BILLION/2)); + QCOMPARE(RealTime(0, -ONE_BILLION/2) - RealTime(1, 0), RealTime(-1, -ONE_BILLION/2)); + } + + void negate() + { + QCOMPARE(-RealTime(0, 0), RealTime(0, 0)); + QCOMPARE(-RealTime(1, 0), RealTime(-1, 0)); + QCOMPARE(-RealTime(1, ONE_BILLION/2), RealTime(-1, -ONE_BILLION/2)); + QCOMPARE(-RealTime(-1, -ONE_BILLION/2), RealTime(1, ONE_BILLION/2)); + } + + void compare() + { + int sec, nsec; + for (sec = -2; sec <= 2; sec += 2) { + for (nsec = -1; nsec <= 1; nsec += 1) { + QCOMPARE(RealTime(sec, nsec) < RealTime(sec, nsec), false); + QCOMPARE(RealTime(sec, nsec) > RealTime(sec, nsec), false); + QCOMPARE(RealTime(sec, nsec) == RealTime(sec, nsec), true); + QCOMPARE(RealTime(sec, nsec) != RealTime(sec, nsec), false); + QCOMPARE(RealTime(sec, nsec) <= RealTime(sec, nsec), true); + QCOMPARE(RealTime(sec, nsec) >= RealTime(sec, nsec), true); + } + } + RealTime prev(-3, 0); + for (sec = -2; sec <= 2; sec += 2) { + for (nsec = -1; nsec <= 1; nsec += 1) { + + RealTime curr(sec, nsec); + + QCOMPARE(prev < curr, true); + QCOMPARE(prev > curr, false); + QCOMPARE(prev == curr, false); + QCOMPARE(prev != curr, true); + QCOMPARE(prev <= curr, true); + QCOMPARE(prev >= curr, false); + + QCOMPARE(curr < prev, false); + QCOMPARE(curr > prev, true); + QCOMPARE(curr == prev, false); + QCOMPARE(curr != prev, true); + QCOMPARE(curr <= prev, false); + QCOMPARE(curr >= prev, true); + + prev = curr; + } + } + } + + + +}; + +#endif diff -r dbb7f0ab011e -r ce1077bd663a base/test/TestStringBits.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/base/test/TestStringBits.h Mon Feb 09 10:31:07 2015 +0000 @@ -0,0 +1,203 @@ +/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ + +/* + Sonic Visualiser + An audio file viewer and annotation editor. + Centre for Digital Music, Queen Mary, University of London. + + 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. +*/ + +#ifndef TEST_STRINGBITS_H +#define TEST_STRINGBITS_H + +#include "../StringBits.h" + +#include +#include +#include + +#include + +using namespace std; + +class TestStringBits : public QObject +{ + Q_OBJECT + +private: + void testSplitQuoted(QString in, QStringList out) { + // Only suitable where the output strings do not have + // consecutive spaces in them + QCOMPARE(StringBits::splitQuoted(in, ' '), out); + QString in2(in); + in2.replace(' ', ','); + QStringList out2; + foreach (QString o, out) { + out2 << o.replace(' ', ','); + } + QCOMPARE(StringBits::splitQuoted(in2, ','), out2); + } + +private slots: + void simple() { + QString in = "a b c d"; + QStringList out; + out << "a" << "b" << "c" << "d"; + testSplitQuoted(in, out); + } + + void dquoted() { + QString in = "a \"b c\" d"; + QStringList out; + out << "a" << "b c" << "d"; + testSplitQuoted(in, out); + } + + void drunon() { + QString in = "a \"b c\"d e"; + QStringList out; + out << "a" << "b cd" << "e"; + testSplitQuoted(in, out); + } + + void squoted() { + QString in = "a 'b c' d"; + QStringList out; + out << "a" << "b c" << "d"; + testSplitQuoted(in, out); + } + + void srunon() { + QString in = "a 'b c'd e"; + QStringList out; + out << "a" << "b cd" << "e"; + testSplitQuoted(in, out); + } + + void dempty() { + QString in = "\"\" \"\" \"\""; + QStringList out; + out << "" << "" << ""; + testSplitQuoted(in, out); + } + + void sempty() { + QString in = "'' '' ''"; + QStringList out; + out << "" << "" << ""; + testSplitQuoted(in, out); + } + + void descaped() { + QString in = "a \"b c\\\" d\""; + QStringList out; + out << "a" << "b c\" d"; + testSplitQuoted(in, out); + } + + void sescaped() { + QString in = "a 'b c\\' d'"; + QStringList out; + out << "a" << "b c' d"; + testSplitQuoted(in, out); + } + + void dnested() { + QString in = "a \"b c' d\""; + QStringList out; + out << "a" << "b c' d"; + testSplitQuoted(in, out); + } + + void snested() { + QString in = "a 'b c\" d'"; + QStringList out; + out << "a" << "b c\" d"; + testSplitQuoted(in, out); + } + + void snested2() { + QString in = "aa 'bb cc\" dd'"; + QStringList out; + out << "aa" << "bb cc\" dd"; + testSplitQuoted(in, out); + } + + void qquoted() { + QString in = "a'a 'bb' \\\"cc\" dd\\\""; + QStringList out; + out << "a'a" << "bb" << "\"cc\"" << "dd\""; + testSplitQuoted(in, out); + } + + void multispace() { + QString in = " a'a \\' 'bb' ' \\\"cc\" ' dd\\\" '"; + QStringList out; + out << "a'a" << "'" << "bb" << " \"cc\" " << "dd\"" << "'"; + QCOMPARE(StringBits::splitQuoted(in, ' '), out); + + QString in2 = ",,a'a,\\',,,,,,,,,'bb',,,,',,,,,,\\\"cc\",',dd\\\",'"; + QStringList out2; + out2 << "" << "" << "a'a" << "'" << "" << "" << "" << "" << "" << "" + << "" << "" << "bb" << "" << "" << "" << ",,,,,,\"cc\"," + << "dd\"" << "'"; + QCOMPARE(StringBits::splitQuoted(in2, ','), out2); + } +}; + +#endif + +/* r928 +Config: Using QtTest library 5.3.2, Qt 5.3.2 +PASS : TestStringBits::initTestCase() +PASS : TestStringBits::simple() +PASS : TestStringBits::dquoted() +PASS : TestStringBits::squoted() +PASS : TestStringBits::descaped() +FAIL! : TestStringBits::sescaped() Compared lists have different sizes. + Actual (StringBits::splitQuoted(in, ' ')) size: 3 + Expected (out) size: 2 + Loc: [o/../TestStringBits.h(65)] +PASS : TestStringBits::dnested() +PASS : TestStringBits::snested() +PASS : TestStringBits::snested2() +PASS : TestStringBits::qquoted() +FAIL! : TestStringBits::multispace() Compared lists differ at index 1. + Actual (StringBits::splitQuoted(in, ' ')): " " + Expected (out): "'" + Loc: [o/../TestStringBits.h(100)] +FAIL! : TestStringBits::qcommas() Compared lists have different sizes. + Actual (StringBits::splitQuoted(in, ',')) size: 4 + Expected (out) size: 3 + Loc: [o/../TestStringBits.h(107)] +PASS : TestStringBits::cleanupTestCase() +Totals: 10 passed, 3 failed, 0 skipped +*/ + +/*curr +PASS : TestStringBits::initTestCase() +PASS : TestStringBits::simple() +PASS : TestStringBits::dquoted() +PASS : TestStringBits::squoted() +PASS : TestStringBits::descaped() +FAIL! : TestStringBits::sescaped() Compared lists have different sizes. + Actual (StringBits::splitQuoted(in, ' ')) size: 3 + Expected (out) size: 2 + Loc: [o/../TestStringBits.h(65)] +PASS : TestStringBits::dnested() +PASS : TestStringBits::snested() +PASS : TestStringBits::snested2() +PASS : TestStringBits::qquoted() +FAIL! : TestStringBits::multispace() Compared lists have different sizes. + Actual (StringBits::splitQuoted(in, ' ')) size: 5 + Expected (out) size: 6 + Loc: [o/../TestStringBits.h(100)] +PASS : TestStringBits::qcommas() +PASS : TestStringBits::cleanupTestCase() +Totals: 11 passed, 2 failed, 0 skipped +*/ diff -r dbb7f0ab011e -r ce1077bd663a base/test/main.cpp --- a/base/test/main.cpp Mon Nov 17 15:31:07 2014 +0000 +++ b/base/test/main.cpp Mon Feb 09 10:31:07 2015 +0000 @@ -13,6 +13,8 @@ #include "TestRangeMapper.h" #include "TestPitch.h" +#include "TestRealTime.h" +#include "TestStringBits.h" #include @@ -36,6 +38,16 @@ if (QTest::qExec(&t, argc, argv) == 0) ++good; else ++bad; } + { + TestRealTime t; + if (QTest::qExec(&t, argc, argv) == 0) ++good; + else ++bad; + } + { + TestStringBits t; + if (QTest::qExec(&t, argc, argv) == 0) ++good; + else ++bad; + } if (bad > 0) { cerr << "\n********* " << bad << " test suite(s) failed!\n" << endl; diff -r dbb7f0ab011e -r ce1077bd663a base/test/test.pro --- a/base/test/test.pro Mon Nov 17 15:31:07 2014 +0000 +++ b/base/test/test.pro Mon Feb 09 10:31:07 2015 +0000 @@ -49,7 +49,7 @@ OBJECTS_DIR = o MOC_DIR = o -HEADERS += TestRangeMapper.h TestPitch.h +HEADERS += TestRangeMapper.h TestPitch.h TestRealTime.h TestStringBits.h SOURCES += main.cpp win* { diff -r dbb7f0ab011e -r ce1077bd663a data/fileio/CSVFormat.cpp --- a/data/fileio/CSVFormat.cpp Mon Nov 17 15:31:07 2014 +0000 +++ b/data/fileio/CSVFormat.cpp Mon Feb 09 10:31:07 2015 +0000 @@ -110,7 +110,7 @@ // something that indicates otherwise: ColumnQualities defaultQualities = - ColumnNumeric | ColumnIntegral | ColumnIncreasing; + ColumnNumeric | ColumnIntegral | ColumnIncreasing | ColumnNearEmpty; for (int i = 0; i < cols; ++i) { @@ -128,7 +128,12 @@ bool integral = (qualities & ColumnIntegral); bool increasing = (qualities & ColumnIncreasing); bool large = (qualities & ColumnLarge); // this one defaults to off + bool emptyish = (qualities & ColumnNearEmpty); + if (lineno > 1 && s.trimmed() != "") { + emptyish = false; + } + float value = 0.f; //!!! how to take into account headers? @@ -166,7 +171,8 @@ (numeric ? ColumnNumeric : 0) | (integral ? ColumnIntegral : 0) | (increasing ? ColumnIncreasing : 0) | - (large ? ColumnLarge : 0); + (large ? ColumnLarge : 0) | + (emptyish ? ColumnNearEmpty : 0); } if (lineno < 10) { @@ -190,11 +196,31 @@ m_timeUnits = CSVFormat::TimeWindows; int timingColumnCount = 0; + + // if our first column has zero or one entries in it and the rest + // have more, then we'll default to ignoring the first column and + // counting the next one as primary. (e.g. Sonic Annotator output + // with filename at start of first column.) + + int primaryColumnNo = 0; + + if (m_columnCount >= 2) { + if ( (m_columnQualities[0] & ColumnNearEmpty) && + !(m_columnQualities[1] & ColumnNearEmpty)) { + primaryColumnNo = 1; + } + } for (int i = 0; i < m_columnCount; ++i) { ColumnPurpose purpose = ColumnUnknown; - bool primary = (i == 0); + + if (i < primaryColumnNo) { + setColumnPurpose(i, purpose); + continue; + } + + bool primary = (i == primaryColumnNo); ColumnQualities qualities = m_columnQualities[i]; diff -r dbb7f0ab011e -r ce1077bd663a data/fileio/CSVFormat.h --- a/data/fileio/CSVFormat.h Mon Nov 17 15:31:07 2014 +0000 +++ b/data/fileio/CSVFormat.h Mon Feb 09 10:31:07 2015 +0000 @@ -53,10 +53,11 @@ }; enum ColumnQuality { - ColumnNumeric = 0x1, - ColumnIntegral = 0x2, - ColumnIncreasing = 0x4, - ColumnLarge = 0x8 + ColumnNumeric = 1, + ColumnIntegral = 2, + ColumnIncreasing = 4, + ColumnLarge = 8, + ColumnNearEmpty = 16, }; typedef unsigned int ColumnQualities; diff -r dbb7f0ab011e -r ce1077bd663a data/model/AlignmentModel.cpp --- a/data/model/AlignmentModel.cpp Mon Nov 17 15:31:07 2014 +0000 +++ b/data/model/AlignmentModel.cpp Mon Feb 09 10:31:07 2015 +0000 @@ -112,10 +112,17 @@ if (completion) *completion = 0; return false; } - if (m_pathComplete || !m_rawPath) { + if (m_pathComplete) { if (completion) *completion = 100; return true; } + if (!m_rawPath) { + // lack of raw path could mean path is complete (in which case + // m_pathComplete true above) or else no alignment has been + // set at all yet (this case) + if (completion) *completion = 0; + return false; + } return m_rawPath->isReady(completion); } @@ -347,6 +354,31 @@ } void +AlignmentModel::setPathFrom(SparseTimeValueModel *rawpath) +{ + if (m_rawPath) m_rawPath->aboutToDelete(); + delete m_rawPath; + + m_rawPath = rawpath; + + connect(m_rawPath, SIGNAL(modelChanged()), + this, SLOT(pathChanged())); + + connect(m_rawPath, SIGNAL(modelChangedWithin(int, int)), + this, SLOT(pathChangedWithin(int, int))); + + connect(m_rawPath, SIGNAL(completionChanged()), + this, SLOT(pathCompletionChanged())); + + constructPath(); + constructReversePath(); + + if (m_rawPath->isReady()) { + pathCompletionChanged(); + } +} + +void AlignmentModel::setPath(PathModel *path) { if (m_path) m_path->aboutToDelete(); diff -r dbb7f0ab011e -r ce1077bd663a data/model/AlignmentModel.h --- a/data/model/AlignmentModel.h Mon Nov 17 15:31:07 2014 +0000 +++ b/data/model/AlignmentModel.h Mon Feb 09 10:31:07 2015 +0000 @@ -52,6 +52,7 @@ int toReference(int frame) const; int fromReference(int frame) const; + void setPathFrom(SparseTimeValueModel *rawpath); void setPath(PathModel *path); virtual void toXml(QTextStream &stream, diff -r dbb7f0ab011e -r ce1077bd663a data/model/Model.cpp --- a/data/model/Model.cpp Mon Nov 17 15:31:07 2014 +0000 +++ b/data/model/Model.cpp Mon Feb 09 10:31:07 2015 +0000 @@ -85,9 +85,13 @@ m_alignment->aboutToDelete(); delete m_alignment; } + m_alignment = alignment; - connect(m_alignment, SIGNAL(completionChanged()), - this, SIGNAL(alignmentCompletionChanged())); + + if (m_alignment) { + connect(m_alignment, SIGNAL(completionChanged()), + this, SIGNAL(alignmentCompletionChanged())); + } } const AlignmentModel *