annotate base/Pitch.cpp @ 378:a38cd7823cb2

* PortAudio driver: do not specify frames per buffer, let PA decide * Remove old non-RubberBand time stretcher -- it doesn't work with varying buffer sizes such as the PA driver may now be using * Rewrite getCurrentPlayingFrame for greater correctness when using long buffer sizes (interpolating according to audio stream timestamp) * Several changes to make the timestretch management RT safe(r)
author Chris Cannam
date Fri, 08 Feb 2008 17:51:15 +0000
parents 0a44caddd9fe
children ad4911230a66
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@19 18
Chris@19 19 #include <cmath>
Chris@19 20
Chris@19 21 float
Chris@19 22 Pitch::getFrequencyForPitch(int midiPitch,
Chris@19 23 float centsOffset,
Chris@19 24 float concertA)
Chris@19 25 {
Chris@141 26 if (concertA <= 0.0) {
Chris@141 27 concertA = Preferences::getInstance()->getTuningFrequency();
Chris@141 28 }
Chris@19 29 float p = float(midiPitch) + (centsOffset / 100);
Chris@19 30 return concertA * powf(2.0, (p - 69.0) / 12.0);
Chris@19 31 }
Chris@19 32
Chris@19 33 int
Chris@19 34 Pitch::getPitchForFrequency(float frequency,
Chris@19 35 float *centsOffsetReturn,
Chris@19 36 float concertA)
Chris@19 37 {
Chris@141 38 if (concertA <= 0.0) {
Chris@141 39 concertA = Preferences::getInstance()->getTuningFrequency();
Chris@141 40 }
Chris@19 41 float p = 12.0 * (log(frequency / (concertA / 2.0)) / log(2.0)) + 57.0;
Chris@19 42
Chris@19 43 int midiPitch = int(p + 0.00001);
Chris@19 44 float centsOffset = (p - midiPitch) * 100.0;
Chris@19 45
Chris@19 46 if (centsOffset >= 50.0) {
Chris@19 47 midiPitch = midiPitch + 1;
Chris@19 48 centsOffset = -(100.0 - centsOffset);
Chris@19 49 }
Chris@19 50
Chris@19 51 if (centsOffsetReturn) *centsOffsetReturn = centsOffset;
Chris@19 52 return midiPitch;
Chris@19 53 }
Chris@19 54
Chris@373 55 int
Chris@373 56 Pitch::getPitchForFrequencyDifference(float frequencyA,
Chris@373 57 float frequencyB,
Chris@373 58 float *centsOffsetReturn,
Chris@373 59 float concertA)
Chris@373 60 {
Chris@373 61 if (concertA <= 0.0) {
Chris@373 62 concertA = Preferences::getInstance()->getTuningFrequency();
Chris@373 63 }
Chris@373 64
Chris@373 65 if (frequencyA > frequencyB) {
Chris@373 66 std::swap(frequencyA, frequencyB);
Chris@373 67 }
Chris@373 68
Chris@373 69 float pA = 12.0 * (log(frequencyA / (concertA / 2.0)) / log(2.0)) + 57.0;
Chris@373 70 float pB = 12.0 * (log(frequencyB / (concertA / 2.0)) / log(2.0)) + 57.0;
Chris@373 71
Chris@373 72 float p = pB - pA;
Chris@373 73
Chris@373 74 int midiPitch = int(p + 0.00001);
Chris@373 75 float centsOffset = (p - midiPitch) * 100.0;
Chris@373 76
Chris@373 77 if (centsOffset >= 50.0) {
Chris@373 78 midiPitch = midiPitch + 1;
Chris@373 79 centsOffset = -(100.0 - centsOffset);
Chris@373 80 }
Chris@373 81
Chris@373 82 if (centsOffsetReturn) *centsOffsetReturn = centsOffset;
Chris@373 83 return midiPitch;
Chris@373 84 }
Chris@373 85
Chris@19 86 static QString notes[] = {
Chris@19 87 "C%1", "C#%1", "D%1", "D#%1",
Chris@19 88 "E%1", "F%1", "F#%1", "G%1",
Chris@19 89 "G#%1", "A%1", "A#%1", "B%1"
Chris@19 90 };
Chris@19 91
Chris@26 92 static QString flatNotes[] = {
Chris@26 93 "C%1", "Db%1", "D%1", "Eb%1",
Chris@26 94 "E%1", "F%1", "Gb%1", "G%1",
Chris@26 95 "Ab%1", "A%1", "Bb%1", "B%1"
Chris@26 96 };
Chris@26 97
Chris@19 98 QString
Chris@19 99 Pitch::getPitchLabel(int midiPitch,
Chris@26 100 float centsOffset,
Chris@26 101 bool useFlats)
Chris@19 102 {
Chris@19 103 int octave = -2;
Chris@19 104
Chris@19 105 if (midiPitch < 0) {
Chris@19 106 while (midiPitch < 0) {
Chris@19 107 midiPitch += 12;
Chris@19 108 --octave;
Chris@19 109 }
Chris@19 110 } else {
Chris@19 111 octave = midiPitch / 12 - 2;
Chris@19 112 }
Chris@19 113
Chris@26 114 QString plain = (useFlats ? flatNotes : notes)[midiPitch % 12].arg(octave);
Chris@19 115
Chris@19 116 int ic = lrintf(centsOffset);
Chris@19 117 if (ic == 0) return plain;
Chris@19 118 else if (ic > 0) return QString("%1+%2c").arg(plain).arg(ic);
Chris@19 119 else return QString("%1%2c").arg(plain).arg(ic);
Chris@19 120 }
Chris@19 121
Chris@19 122 QString
Chris@19 123 Pitch::getPitchLabelForFrequency(float frequency,
Chris@26 124 float concertA,
Chris@26 125 bool useFlats)
Chris@19 126 {
Chris@141 127 if (concertA <= 0.0) {
Chris@141 128 concertA = Preferences::getInstance()->getTuningFrequency();
Chris@141 129 }
Chris@19 130 float centsOffset = 0.0;
Chris@19 131 int midiPitch = getPitchForFrequency(frequency, &centsOffset, concertA);
Chris@26 132 return getPitchLabel(midiPitch, centsOffset, useFlats);
Chris@19 133 }
Chris@19 134
Chris@373 135 QString
Chris@373 136 Pitch::getLabelForPitchRange(int semis, float cents)
Chris@373 137 {
Chris@373 138 int ic = lrintf(cents);
Chris@373 139
Chris@373 140 if (ic == 0) {
Chris@373 141 if (semis >= 12) {
Chris@373 142 return QString("%1'%2").arg(semis/12).arg(semis - 12*(semis/12));
Chris@373 143 } else {
Chris@373 144 return QString("%1").arg(semis);
Chris@373 145 }
Chris@373 146 } else {
Chris@373 147 if (ic > 0) {
Chris@373 148 if (semis >= 12) {
Chris@373 149 return QString("%1'%2+%3c").arg(semis/12).arg(semis - 12*(semis/12)).arg(ic);
Chris@373 150 } else {
Chris@373 151 return QString("%1+%3c").arg(semis).arg(ic);
Chris@373 152 }
Chris@373 153 } else {
Chris@373 154 if (semis >= 12) {
Chris@373 155 return QString("%1'%2%3c").arg(semis/12).arg(semis - 12*(semis/12)).arg(ic);
Chris@373 156 } else {
Chris@373 157 return QString("%1%3c").arg(semis).arg(ic);
Chris@373 158 }
Chris@373 159 }
Chris@373 160 }
Chris@373 161 }
Chris@373 162
Chris@274 163 bool
Chris@274 164 Pitch::isFrequencyInMidiRange(float frequency,
Chris@274 165 float concertA)
Chris@274 166 {
Chris@274 167 float centsOffset = 0.0;
Chris@274 168 int midiPitch = getPitchForFrequency(frequency, &centsOffset, concertA);
Chris@274 169 return (midiPitch >= 0 && midiPitch < 128);
Chris@274 170 }
Chris@274 171