changeset 1025:88b54a185a0a

Use double instead of float for frequencies in Pitch, just for confidence
author Chris Cannam
date Mon, 08 Dec 2014 15:37:12 +0000
parents d1ce7a4a920b
children 01a90c025eaa ce1077bd663a abe13fe10ed6 d74ebd2d2c49
files base/Pitch.cpp base/Pitch.h base/test/TestPitch.h
diffstat 3 files changed, 96 insertions(+), 53 deletions(-) [+]
line wrap: on
line diff
--- a/base/Pitch.cpp	Tue Dec 02 17:53:17 2014 +0000
+++ b/base/Pitch.cpp	Mon Dec 08 15:37:12 2014 +0000
@@ -19,45 +19,49 @@
 
 #include <cmath>
 
-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;
@@ -129,7 +133,7 @@
 
 QString
 Pitch::getPitchLabel(int midiPitch,
-		     float centsOffset,
+		     double centsOffset,
 		     bool useFlats)
 {
     int note, octave;
@@ -137,42 +141,42 @@
 
     QString plain = (useFlats ? flatNotes : notes)[note].arg(octave);
 
-    int ic = lrintf(centsOffset);
+    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, &centsOffset, 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) {
@@ -198,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, &centsOffset, concertA);
     return (midiPitch >= 0 && midiPitch < 128);
 }
--- a/base/Pitch.h	Tue Dec 02 17:53:17 2014 +0000
+++ b/base/Pitch.h	Mon Dec 08 15:37:12 2014 +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,12 +77,27 @@
      * 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,
@@ -99,7 +127,7 @@
      * e.g. Bb3 instead of A#3.
      */
     static QString getPitchLabel(int midiPitch,
-				 float centsOffset = 0,
+				 double centsOffset = 0,
 				 bool useFlats = false);
     
     /**
@@ -113,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
@@ -134,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);
 };
 
 
--- a/base/test/TestPitch.h	Tue Dec 02 17:53:17 2014 +0000
+++ b/base/test/TestPitch.h	Mon Dec 08 15:37:12 2014 +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, &centsOffset), 60);
+	QCOMPARE(centsOffset, 0.0);
+	QCOMPARE(Pitch::getPitchForFrequency(261.0, &centsOffset), 60);
+	QCOMPARE(int(centsOffset), -4);
+	QCOMPARE(Pitch::getPitchForFrequency(440.0, &centsOffset), 69);
+	QCOMPARE(centsOffset, 0.0);
+    }
+
+    void pitchForFrequencyF()
+    {
 	float centsOffset = 0.f;
 	QCOMPARE(Pitch::getPitchForFrequency(MIDDLE_C, &centsOffset), 60);
 	QCOMPARE(centsOffset, 0.f);