svcore
1.9
|
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 #include "system/System.h" 00019 00020 #include <cmath> 00021 00022 float 00023 Pitch::getFrequencyForPitch(int midiPitch, 00024 float centsOffset, 00025 float concertA) 00026 { 00027 if (concertA <= 0.0) { 00028 concertA = Preferences::getInstance()->getTuningFrequency(); 00029 } 00030 float p = float(midiPitch) + (centsOffset / 100); 00031 return concertA * powf(2.0, (p - 69.0) / 12.0); 00032 } 00033 00034 int 00035 Pitch::getPitchForFrequency(float frequency, 00036 float *centsOffsetReturn, 00037 float concertA) 00038 { 00039 if (concertA <= 0.0) { 00040 concertA = Preferences::getInstance()->getTuningFrequency(); 00041 } 00042 float p = 12.0 * (log(frequency / (concertA / 2.0)) / log(2.0)) + 57.0; 00043 00044 int midiPitch = int(p + 0.00001); 00045 float centsOffset = (p - midiPitch) * 100.0; 00046 00047 if (centsOffset >= 50.0) { 00048 midiPitch = midiPitch + 1; 00049 centsOffset = -(100.0 - centsOffset); 00050 } 00051 00052 if (centsOffsetReturn) *centsOffsetReturn = centsOffset; 00053 return midiPitch; 00054 } 00055 00056 int 00057 Pitch::getPitchForFrequencyDifference(float frequencyA, 00058 float frequencyB, 00059 float *centsOffsetReturn, 00060 float concertA) 00061 { 00062 if (concertA <= 0.0) { 00063 concertA = Preferences::getInstance()->getTuningFrequency(); 00064 } 00065 00066 if (frequencyA > frequencyB) { 00067 std::swap(frequencyA, frequencyB); 00068 } 00069 00070 float pA = 12.0 * (log(frequencyA / (concertA / 2.0)) / log(2.0)) + 57.0; 00071 float pB = 12.0 * (log(frequencyB / (concertA / 2.0)) / log(2.0)) + 57.0; 00072 00073 float p = pB - pA; 00074 00075 int midiPitch = int(p + 0.00001); 00076 float centsOffset = (p - midiPitch) * 100.0; 00077 00078 if (centsOffset >= 50.0) { 00079 midiPitch = midiPitch + 1; 00080 centsOffset = -(100.0 - centsOffset); 00081 } 00082 00083 if (centsOffsetReturn) *centsOffsetReturn = centsOffset; 00084 return midiPitch; 00085 } 00086 00087 static QString notes[] = { 00088 "C%1", "C#%1", "D%1", "D#%1", 00089 "E%1", "F%1", "F#%1", "G%1", 00090 "G#%1", "A%1", "A#%1", "B%1" 00091 }; 00092 00093 static QString flatNotes[] = { 00094 "C%1", "Db%1", "D%1", "Eb%1", 00095 "E%1", "F%1", "Gb%1", "G%1", 00096 "Ab%1", "A%1", "Bb%1", "B%1" 00097 }; 00098 00099 QString 00100 Pitch::getPitchLabel(int midiPitch, 00101 float centsOffset, 00102 bool useFlats) 00103 { 00104 int baseOctave = Preferences::getInstance()->getOctaveOfLowestMIDINote(); 00105 int octave = baseOctave; 00106 00107 // Note, this only gets the right octave number at octave 00108 // boundaries because Cb is enharmonic with B (not B#) and B# is 00109 // enharmonic with C (not Cb). So neither B# nor Cb will be 00110 // spelled from a MIDI pitch + flats flag in isolation. 00111 00112 if (midiPitch < 0) { 00113 while (midiPitch < 0) { 00114 midiPitch += 12; 00115 --octave; 00116 } 00117 } else { 00118 octave = midiPitch / 12 + baseOctave; 00119 } 00120 00121 QString plain = (useFlats ? flatNotes : notes)[midiPitch % 12].arg(octave); 00122 00123 int ic = lrintf(centsOffset); 00124 if (ic == 0) return plain; 00125 else if (ic > 0) return QString("%1+%2c").arg(plain).arg(ic); 00126 else return QString("%1%2c").arg(plain).arg(ic); 00127 } 00128 00129 QString 00130 Pitch::getPitchLabelForFrequency(float frequency, 00131 float concertA, 00132 bool useFlats) 00133 { 00134 if (concertA <= 0.0) { 00135 concertA = Preferences::getInstance()->getTuningFrequency(); 00136 } 00137 float centsOffset = 0.0; 00138 int midiPitch = getPitchForFrequency(frequency, ¢sOffset, concertA); 00139 return getPitchLabel(midiPitch, centsOffset, useFlats); 00140 } 00141 00142 QString 00143 Pitch::getLabelForPitchRange(int semis, float cents) 00144 { 00145 if (semis > 0) { 00146 while (cents < 0.f) { 00147 --semis; 00148 cents += 100.f; 00149 } 00150 } 00151 if (semis < 0) { 00152 while (cents > 0.f) { 00153 ++semis; 00154 cents -= 100.f; 00155 } 00156 } 00157 00158 int ic = lrintf(cents); 00159 00160 if (ic == 0) { 00161 if (semis >= 12) { 00162 return QString("%1'%2").arg(semis/12).arg(semis - 12*(semis/12)); 00163 } else { 00164 return QString("%1").arg(semis); 00165 } 00166 } else { 00167 if (ic > 0) { 00168 if (semis >= 12) { 00169 return QString("%1'%2+%3c").arg(semis/12).arg(semis - 12*(semis/12)).arg(ic); 00170 } else { 00171 return QString("%1+%3c").arg(semis).arg(ic); 00172 } 00173 } else { 00174 if (semis >= 12) { 00175 return QString("%1'%2%3c").arg(semis/12).arg(semis - 12*(semis/12)).arg(ic); 00176 } else { 00177 return QString("%1%3c").arg(semis).arg(ic); 00178 } 00179 } 00180 } 00181 } 00182 00183 bool 00184 Pitch::isFrequencyInMidiRange(float frequency, 00185 float concertA) 00186 { 00187 float centsOffset = 0.0; 00188 int midiPitch = getPitchForFrequency(frequency, ¢sOffset, concertA); 00189 return (midiPitch >= 0 && midiPitch < 128); 00190 } 00191