annotate base/Pitch.cpp @ 997:2104ea2204d2

Separate out stdout ability (not all writers that support one-file will necessarily want to support it, e.g. for binary formats)
author Chris Cannam
date Mon, 13 Oct 2014 10:56:16 +0100
parents 451f7f3ab6e7
children d1ce7a4a920b
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@892 104 int baseOctave = Preferences::getInstance()->getOctaveOfLowestMIDINote();
Chris@892 105 int octave = baseOctave;
Chris@892 106
Chris@892 107 // Note, this only gets the right octave number at octave
Chris@892 108 // boundaries because Cb is enharmonic with B (not B#) and B# is
Chris@892 109 // enharmonic with C (not Cb). So neither B# nor Cb will be
Chris@892 110 // spelled from a MIDI pitch + flats flag in isolation.
Chris@19 111
Chris@19 112 if (midiPitch < 0) {
Chris@19 113 while (midiPitch < 0) {
Chris@19 114 midiPitch += 12;
Chris@19 115 --octave;
Chris@19 116 }
Chris@19 117 } else {
Chris@892 118 octave = midiPitch / 12 + baseOctave;
Chris@19 119 }
Chris@19 120
Chris@26 121 QString plain = (useFlats ? flatNotes : notes)[midiPitch % 12].arg(octave);
Chris@19 122
Chris@19 123 int ic = lrintf(centsOffset);
Chris@19 124 if (ic == 0) return plain;
Chris@19 125 else if (ic > 0) return QString("%1+%2c").arg(plain).arg(ic);
Chris@19 126 else return QString("%1%2c").arg(plain).arg(ic);
Chris@19 127 }
Chris@19 128
Chris@19 129 QString
Chris@19 130 Pitch::getPitchLabelForFrequency(float frequency,
Chris@26 131 float concertA,
Chris@26 132 bool useFlats)
Chris@19 133 {
Chris@141 134 if (concertA <= 0.0) {
Chris@141 135 concertA = Preferences::getInstance()->getTuningFrequency();
Chris@141 136 }
Chris@19 137 float centsOffset = 0.0;
Chris@19 138 int midiPitch = getPitchForFrequency(frequency, &centsOffset, concertA);
Chris@26 139 return getPitchLabel(midiPitch, centsOffset, useFlats);
Chris@19 140 }
Chris@19 141
Chris@373 142 QString
Chris@373 143 Pitch::getLabelForPitchRange(int semis, float cents)
Chris@373 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 if (semis < 0) {
Chris@433 152 while (cents > 0.f) {
Chris@433 153 ++semis;
Chris@433 154 cents -= 100.f;
Chris@433 155 }
Chris@433 156 }
Chris@433 157
Chris@373 158 int ic = lrintf(cents);
Chris@373 159
Chris@373 160 if (ic == 0) {
Chris@373 161 if (semis >= 12) {
Chris@373 162 return QString("%1'%2").arg(semis/12).arg(semis - 12*(semis/12));
Chris@373 163 } else {
Chris@373 164 return QString("%1").arg(semis);
Chris@373 165 }
Chris@373 166 } else {
Chris@373 167 if (ic > 0) {
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 } else {
Chris@373 174 if (semis >= 12) {
Chris@373 175 return QString("%1'%2%3c").arg(semis/12).arg(semis - 12*(semis/12)).arg(ic);
Chris@373 176 } else {
Chris@373 177 return QString("%1%3c").arg(semis).arg(ic);
Chris@373 178 }
Chris@373 179 }
Chris@373 180 }
Chris@373 181 }
Chris@373 182
Chris@274 183 bool
Chris@274 184 Pitch::isFrequencyInMidiRange(float frequency,
Chris@274 185 float concertA)
Chris@274 186 {
Chris@274 187 float centsOffset = 0.0;
Chris@274 188 int midiPitch = getPitchForFrequency(frequency, &centsOffset, concertA);
Chris@274 189 return (midiPitch >= 0 && midiPitch < 128);
Chris@274 190 }
Chris@274 191