annotate base/Pitch.cpp @ 1008:d9e0e59a1581

When using an aggregate model to pass data to a transform, zero-pad the shorter input to the duration of the longer rather than truncating the longer. (This is better behaviour for e.g. MATCH, and in any case the code was previously truncating incorrectly and ending up with garbage data at the end.)
author Chris Cannam
date Fri, 14 Nov 2014 13:51:33 +0000
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