annotate base/Pitch.cpp @ 661:a4faa1840384

* If a FileSource URL won't convert at all in strict mode, try again in tolerant mode (necessary for e.g. filenames with square brackets in them)
author Chris Cannam
date Tue, 19 Oct 2010 21:47:55 +0100
parents bdc9bb371a9f
children 451f7f3ab6e7
rev   line source
Chris@49 1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
Chris@19 2
Chris@19 3 /*
Chris@52 4 Sonic Visualiser
Chris@52 5 An audio file viewer and annotation editor.
Chris@52 6 Centre for Digital Music, Queen Mary, University of London.
Chris@52 7 This file copyright 2006 Chris Cannam.
Chris@19 8
Chris@52 9 This program is free software; you can redistribute it and/or
Chris@52 10 modify it under the terms of the GNU General Public License as
Chris@52 11 published by the Free Software Foundation; either version 2 of the
Chris@52 12 License, or (at your option) any later version. See the file
Chris@52 13 COPYING included with this distribution for more information.
Chris@19 14 */
Chris@19 15
Chris@19 16 #include "Pitch.h"
Chris@141 17 #include "Preferences.h"
Chris@573 18 #include "system/System.h"
Chris@19 19
Chris@19 20 #include <cmath>
Chris@19 21
Chris@19 22 float
Chris@19 23 Pitch::getFrequencyForPitch(int midiPitch,
Chris@19 24 float centsOffset,
Chris@19 25 float concertA)
Chris@19 26 {
Chris@141 27 if (concertA <= 0.0) {
Chris@141 28 concertA = Preferences::getInstance()->getTuningFrequency();
Chris@141 29 }
Chris@19 30 float p = float(midiPitch) + (centsOffset / 100);
Chris@19 31 return concertA * powf(2.0, (p - 69.0) / 12.0);
Chris@19 32 }
Chris@19 33
Chris@19 34 int
Chris@19 35 Pitch::getPitchForFrequency(float frequency,
Chris@19 36 float *centsOffsetReturn,
Chris@19 37 float concertA)
Chris@19 38 {
Chris@141 39 if (concertA <= 0.0) {
Chris@141 40 concertA = Preferences::getInstance()->getTuningFrequency();
Chris@141 41 }
Chris@19 42 float p = 12.0 * (log(frequency / (concertA / 2.0)) / log(2.0)) + 57.0;
Chris@19 43
Chris@19 44 int midiPitch = int(p + 0.00001);
Chris@19 45 float centsOffset = (p - midiPitch) * 100.0;
Chris@19 46
Chris@19 47 if (centsOffset >= 50.0) {
Chris@19 48 midiPitch = midiPitch + 1;
Chris@19 49 centsOffset = -(100.0 - centsOffset);
Chris@19 50 }
Chris@19 51
Chris@19 52 if (centsOffsetReturn) *centsOffsetReturn = centsOffset;
Chris@19 53 return midiPitch;
Chris@19 54 }
Chris@19 55
Chris@373 56 int
Chris@373 57 Pitch::getPitchForFrequencyDifference(float frequencyA,
Chris@373 58 float frequencyB,
Chris@373 59 float *centsOffsetReturn,
Chris@373 60 float concertA)
Chris@373 61 {
Chris@373 62 if (concertA <= 0.0) {
Chris@373 63 concertA = Preferences::getInstance()->getTuningFrequency();
Chris@373 64 }
Chris@373 65
Chris@373 66 if (frequencyA > frequencyB) {
Chris@373 67 std::swap(frequencyA, frequencyB);
Chris@373 68 }
Chris@373 69
Chris@373 70 float pA = 12.0 * (log(frequencyA / (concertA / 2.0)) / log(2.0)) + 57.0;
Chris@373 71 float pB = 12.0 * (log(frequencyB / (concertA / 2.0)) / log(2.0)) + 57.0;
Chris@373 72
Chris@373 73 float p = pB - pA;
Chris@373 74
Chris@373 75 int midiPitch = int(p + 0.00001);
Chris@373 76 float centsOffset = (p - midiPitch) * 100.0;
Chris@373 77
Chris@373 78 if (centsOffset >= 50.0) {
Chris@373 79 midiPitch = midiPitch + 1;
Chris@373 80 centsOffset = -(100.0 - centsOffset);
Chris@373 81 }
Chris@373 82
Chris@373 83 if (centsOffsetReturn) *centsOffsetReturn = centsOffset;
Chris@373 84 return midiPitch;
Chris@373 85 }
Chris@373 86
Chris@19 87 static QString notes[] = {
Chris@19 88 "C%1", "C#%1", "D%1", "D#%1",
Chris@19 89 "E%1", "F%1", "F#%1", "G%1",
Chris@19 90 "G#%1", "A%1", "A#%1", "B%1"
Chris@19 91 };
Chris@19 92
Chris@26 93 static QString flatNotes[] = {
Chris@26 94 "C%1", "Db%1", "D%1", "Eb%1",
Chris@26 95 "E%1", "F%1", "Gb%1", "G%1",
Chris@26 96 "Ab%1", "A%1", "Bb%1", "B%1"
Chris@26 97 };
Chris@26 98
Chris@19 99 QString
Chris@19 100 Pitch::getPitchLabel(int midiPitch,
Chris@26 101 float centsOffset,
Chris@26 102 bool useFlats)
Chris@19 103 {
Chris@19 104 int octave = -2;
Chris@19 105
Chris@19 106 if (midiPitch < 0) {
Chris@19 107 while (midiPitch < 0) {
Chris@19 108 midiPitch += 12;
Chris@19 109 --octave;
Chris@19 110 }
Chris@19 111 } else {
Chris@19 112 octave = midiPitch / 12 - 2;
Chris@19 113 }
Chris@19 114
Chris@26 115 QString plain = (useFlats ? flatNotes : notes)[midiPitch % 12].arg(octave);
Chris@19 116
Chris@19 117 int ic = lrintf(centsOffset);
Chris@19 118 if (ic == 0) return plain;
Chris@19 119 else if (ic > 0) return QString("%1+%2c").arg(plain).arg(ic);
Chris@19 120 else return QString("%1%2c").arg(plain).arg(ic);
Chris@19 121 }
Chris@19 122
Chris@19 123 QString
Chris@19 124 Pitch::getPitchLabelForFrequency(float frequency,
Chris@26 125 float concertA,
Chris@26 126 bool useFlats)
Chris@19 127 {
Chris@141 128 if (concertA <= 0.0) {
Chris@141 129 concertA = Preferences::getInstance()->getTuningFrequency();
Chris@141 130 }
Chris@19 131 float centsOffset = 0.0;
Chris@19 132 int midiPitch = getPitchForFrequency(frequency, &centsOffset, concertA);
Chris@26 133 return getPitchLabel(midiPitch, centsOffset, useFlats);
Chris@19 134 }
Chris@19 135
Chris@373 136 QString
Chris@373 137 Pitch::getLabelForPitchRange(int semis, float cents)
Chris@373 138 {
Chris@433 139 if (semis > 0) {
Chris@433 140 while (cents < 0.f) {
Chris@433 141 --semis;
Chris@433 142 cents += 100.f;
Chris@433 143 }
Chris@433 144 }
Chris@433 145 if (semis < 0) {
Chris@433 146 while (cents > 0.f) {
Chris@433 147 ++semis;
Chris@433 148 cents -= 100.f;
Chris@433 149 }
Chris@433 150 }
Chris@433 151
Chris@373 152 int ic = lrintf(cents);
Chris@373 153
Chris@373 154 if (ic == 0) {
Chris@373 155 if (semis >= 12) {
Chris@373 156 return QString("%1'%2").arg(semis/12).arg(semis - 12*(semis/12));
Chris@373 157 } else {
Chris@373 158 return QString("%1").arg(semis);
Chris@373 159 }
Chris@373 160 } else {
Chris@373 161 if (ic > 0) {
Chris@373 162 if (semis >= 12) {
Chris@373 163 return QString("%1'%2+%3c").arg(semis/12).arg(semis - 12*(semis/12)).arg(ic);
Chris@373 164 } else {
Chris@373 165 return QString("%1+%3c").arg(semis).arg(ic);
Chris@373 166 }
Chris@373 167 } else {
Chris@373 168 if (semis >= 12) {
Chris@373 169 return QString("%1'%2%3c").arg(semis/12).arg(semis - 12*(semis/12)).arg(ic);
Chris@373 170 } else {
Chris@373 171 return QString("%1%3c").arg(semis).arg(ic);
Chris@373 172 }
Chris@373 173 }
Chris@373 174 }
Chris@373 175 }
Chris@373 176
Chris@274 177 bool
Chris@274 178 Pitch::isFrequencyInMidiRange(float frequency,
Chris@274 179 float concertA)
Chris@274 180 {
Chris@274 181 float centsOffset = 0.0;
Chris@274 182 int midiPitch = getPitchForFrequency(frequency, &centsOffset, concertA);
Chris@274 183 return (midiPitch >= 0 && midiPitch < 128);
Chris@274 184 }
Chris@274 185