Pitch.cpp

Go to the documentation of this file.
00001 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*-  vi:set ts=8 sts=4 sw=4: */
00002 
00003 /*
00004     Sonic Visualiser
00005     An audio file viewer and annotation editor.
00006     Centre for Digital Music, Queen Mary, University of London.
00007     This file copyright 2006 Chris Cannam.
00008     
00009     This program is free software; you can redistribute it and/or
00010     modify it under the terms of the GNU General Public License as
00011     published by the Free Software Foundation; either version 2 of the
00012     License, or (at your option) any later version.  See the file
00013     COPYING included with this distribution for more information.
00014 */
00015 
00016 #include "Pitch.h"
00017 #include "Preferences.h"
00018 
00019 #include <cmath>
00020 
00021 float
00022 Pitch::getFrequencyForPitch(int midiPitch,
00023                             float centsOffset,
00024                             float concertA)
00025 {
00026     if (concertA <= 0.0) {
00027         concertA = Preferences::getInstance()->getTuningFrequency();
00028     }
00029     float p = float(midiPitch) + (centsOffset / 100);
00030     return concertA * powf(2.0, (p - 69.0) / 12.0);
00031 }
00032 
00033 int
00034 Pitch::getPitchForFrequency(float frequency,
00035                             float *centsOffsetReturn,
00036                             float concertA)
00037 {
00038     if (concertA <= 0.0) {
00039         concertA = Preferences::getInstance()->getTuningFrequency();
00040     }
00041     float p = 12.0 * (log(frequency / (concertA / 2.0)) / log(2.0)) + 57.0;
00042 
00043     int midiPitch = int(p + 0.00001);
00044     float centsOffset = (p - midiPitch) * 100.0;
00045 
00046     if (centsOffset >= 50.0) {
00047         midiPitch = midiPitch + 1;
00048         centsOffset = -(100.0 - centsOffset);
00049     }
00050     
00051     if (centsOffsetReturn) *centsOffsetReturn = centsOffset;
00052     return midiPitch;
00053 }
00054 
00055 int
00056 Pitch::getPitchForFrequencyDifference(float frequencyA,
00057                                       float frequencyB,
00058                                       float *centsOffsetReturn,
00059                                       float concertA)
00060 {
00061     if (concertA <= 0.0) {
00062         concertA = Preferences::getInstance()->getTuningFrequency();
00063     }
00064 
00065     if (frequencyA > frequencyB) {
00066         std::swap(frequencyA, frequencyB);
00067     }
00068 
00069     float pA = 12.0 * (log(frequencyA / (concertA / 2.0)) / log(2.0)) + 57.0;
00070     float pB = 12.0 * (log(frequencyB / (concertA / 2.0)) / log(2.0)) + 57.0;
00071 
00072     float p = pB - pA;
00073 
00074     int midiPitch = int(p + 0.00001);
00075     float centsOffset = (p - midiPitch) * 100.0;
00076 
00077     if (centsOffset >= 50.0) {
00078         midiPitch = midiPitch + 1;
00079         centsOffset = -(100.0 - centsOffset);
00080     }
00081     
00082     if (centsOffsetReturn) *centsOffsetReturn = centsOffset;
00083     return midiPitch;
00084 }
00085 
00086 static QString notes[] = {
00087     "C%1",  "C#%1", "D%1",  "D#%1",
00088     "E%1",  "F%1",  "F#%1", "G%1",
00089     "G#%1", "A%1",  "A#%1", "B%1"
00090 };
00091 
00092 static QString flatNotes[] = {
00093     "C%1",  "Db%1", "D%1",  "Eb%1",
00094     "E%1",  "F%1",  "Gb%1", "G%1",
00095     "Ab%1", "A%1",  "Bb%1", "B%1"
00096 };
00097 
00098 QString
00099 Pitch::getPitchLabel(int midiPitch,
00100                      float centsOffset,
00101                      bool useFlats)
00102 {
00103     int octave = -2;
00104 
00105     if (midiPitch < 0) {
00106         while (midiPitch < 0) {
00107             midiPitch += 12;
00108             --octave;
00109         }
00110     } else {
00111         octave = midiPitch / 12 - 2;
00112     }
00113 
00114     QString plain = (useFlats ? flatNotes : notes)[midiPitch % 12].arg(octave);
00115 
00116     int ic = lrintf(centsOffset);
00117     if (ic == 0) return plain;
00118     else if (ic > 0) return QString("%1+%2c").arg(plain).arg(ic);
00119     else return QString("%1%2c").arg(plain).arg(ic);
00120 }
00121 
00122 QString
00123 Pitch::getPitchLabelForFrequency(float frequency,
00124                                  float concertA,
00125                                  bool useFlats)
00126 {
00127     if (concertA <= 0.0) {
00128         concertA = Preferences::getInstance()->getTuningFrequency();
00129     }
00130     float centsOffset = 0.0;
00131     int midiPitch = getPitchForFrequency(frequency, &centsOffset, concertA);
00132     return getPitchLabel(midiPitch, centsOffset, useFlats);
00133 }
00134 
00135 QString
00136 Pitch::getLabelForPitchRange(int semis, float cents)
00137 {
00138     int ic = lrintf(cents);
00139 
00140     if (ic == 0) {
00141         if (semis >= 12) {
00142             return QString("%1'%2").arg(semis/12).arg(semis - 12*(semis/12));
00143         } else {
00144             return QString("%1").arg(semis);
00145         }
00146     } else {
00147         if (ic > 0) {
00148             if (semis >= 12) {
00149                 return QString("%1'%2+%3c").arg(semis/12).arg(semis - 12*(semis/12)).arg(ic);
00150             } else {
00151                 return QString("%1+%3c").arg(semis).arg(ic);
00152             }
00153         } else {
00154             if (semis >= 12) {
00155                 return QString("%1'%2%3c").arg(semis/12).arg(semis - 12*(semis/12)).arg(ic);
00156             } else {
00157                 return QString("%1%3c").arg(semis).arg(ic);
00158             }
00159         }
00160     }
00161 }
00162 
00163 bool
00164 Pitch::isFrequencyInMidiRange(float frequency,
00165                               float concertA)
00166 {
00167     float centsOffset = 0.0;
00168     int midiPitch = getPitchForFrequency(frequency, &centsOffset, concertA);
00169     return (midiPitch >= 0 && midiPitch < 128);
00170 }
00171 

Generated on Wed Feb 20 15:45:27 2008 for SonicVisualiser by  doxygen 1.5.1