Chris@49: /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ Chris@19: Chris@19: /* Chris@52: Sonic Visualiser Chris@52: An audio file viewer and annotation editor. Chris@52: Centre for Digital Music, Queen Mary, University of London. Chris@52: This file copyright 2006 Chris Cannam. Chris@19: Chris@52: This program is free software; you can redistribute it and/or Chris@52: modify it under the terms of the GNU General Public License as Chris@52: published by the Free Software Foundation; either version 2 of the Chris@52: License, or (at your option) any later version. See the file Chris@52: COPYING included with this distribution for more information. Chris@19: */ Chris@19: Chris@19: #include "Pitch.h" Chris@141: #include "Preferences.h" Chris@573: #include "system/System.h" Chris@19: Chris@19: #include Chris@19: Chris@19: float Chris@19: Pitch::getFrequencyForPitch(int midiPitch, Chris@19: float centsOffset, Chris@19: float concertA) Chris@19: { Chris@141: if (concertA <= 0.0) { Chris@141: concertA = Preferences::getInstance()->getTuningFrequency(); Chris@141: } Chris@19: float p = float(midiPitch) + (centsOffset / 100); Chris@19: return concertA * powf(2.0, (p - 69.0) / 12.0); Chris@19: } Chris@19: Chris@19: int Chris@19: Pitch::getPitchForFrequency(float frequency, Chris@19: float *centsOffsetReturn, Chris@19: float concertA) Chris@19: { Chris@141: if (concertA <= 0.0) { Chris@141: concertA = Preferences::getInstance()->getTuningFrequency(); Chris@141: } Chris@19: float p = 12.0 * (log(frequency / (concertA / 2.0)) / log(2.0)) + 57.0; Chris@19: Chris@19: int midiPitch = int(p + 0.00001); Chris@19: float centsOffset = (p - midiPitch) * 100.0; Chris@19: Chris@19: if (centsOffset >= 50.0) { Chris@19: midiPitch = midiPitch + 1; Chris@19: centsOffset = -(100.0 - centsOffset); Chris@19: } Chris@19: Chris@19: if (centsOffsetReturn) *centsOffsetReturn = centsOffset; Chris@19: return midiPitch; Chris@19: } Chris@19: Chris@373: int Chris@373: Pitch::getPitchForFrequencyDifference(float frequencyA, Chris@373: float frequencyB, Chris@373: float *centsOffsetReturn, Chris@373: float concertA) Chris@373: { Chris@373: if (concertA <= 0.0) { Chris@373: concertA = Preferences::getInstance()->getTuningFrequency(); Chris@373: } Chris@373: Chris@373: if (frequencyA > frequencyB) { Chris@373: std::swap(frequencyA, frequencyB); Chris@373: } Chris@373: Chris@373: float pA = 12.0 * (log(frequencyA / (concertA / 2.0)) / log(2.0)) + 57.0; Chris@373: float pB = 12.0 * (log(frequencyB / (concertA / 2.0)) / log(2.0)) + 57.0; Chris@373: Chris@373: float p = pB - pA; Chris@373: Chris@373: int midiPitch = int(p + 0.00001); Chris@373: float centsOffset = (p - midiPitch) * 100.0; Chris@373: Chris@373: if (centsOffset >= 50.0) { Chris@373: midiPitch = midiPitch + 1; Chris@373: centsOffset = -(100.0 - centsOffset); Chris@373: } Chris@373: Chris@373: if (centsOffsetReturn) *centsOffsetReturn = centsOffset; Chris@373: return midiPitch; Chris@373: } Chris@373: Chris@19: static QString notes[] = { Chris@19: "C%1", "C#%1", "D%1", "D#%1", Chris@19: "E%1", "F%1", "F#%1", "G%1", Chris@19: "G#%1", "A%1", "A#%1", "B%1" Chris@19: }; Chris@19: Chris@26: static QString flatNotes[] = { Chris@26: "C%1", "Db%1", "D%1", "Eb%1", Chris@26: "E%1", "F%1", "Gb%1", "G%1", Chris@26: "Ab%1", "A%1", "Bb%1", "B%1" Chris@26: }; Chris@26: Chris@19: QString Chris@19: Pitch::getPitchLabel(int midiPitch, Chris@26: float centsOffset, Chris@26: bool useFlats) Chris@19: { Chris@19: int octave = -2; Chris@19: Chris@19: if (midiPitch < 0) { Chris@19: while (midiPitch < 0) { Chris@19: midiPitch += 12; Chris@19: --octave; Chris@19: } Chris@19: } else { Chris@19: octave = midiPitch / 12 - 2; Chris@19: } Chris@19: Chris@26: QString plain = (useFlats ? flatNotes : notes)[midiPitch % 12].arg(octave); Chris@19: Chris@19: int ic = lrintf(centsOffset); Chris@19: if (ic == 0) return plain; Chris@19: else if (ic > 0) return QString("%1+%2c").arg(plain).arg(ic); Chris@19: else return QString("%1%2c").arg(plain).arg(ic); Chris@19: } Chris@19: Chris@19: QString Chris@19: Pitch::getPitchLabelForFrequency(float frequency, Chris@26: float concertA, Chris@26: bool useFlats) Chris@19: { Chris@141: if (concertA <= 0.0) { Chris@141: concertA = Preferences::getInstance()->getTuningFrequency(); Chris@141: } Chris@19: float centsOffset = 0.0; Chris@19: int midiPitch = getPitchForFrequency(frequency, ¢sOffset, concertA); Chris@26: return getPitchLabel(midiPitch, centsOffset, useFlats); Chris@19: } Chris@19: Chris@373: QString Chris@373: Pitch::getLabelForPitchRange(int semis, float cents) Chris@373: { Chris@433: if (semis > 0) { Chris@433: while (cents < 0.f) { Chris@433: --semis; Chris@433: cents += 100.f; Chris@433: } Chris@433: } Chris@433: if (semis < 0) { Chris@433: while (cents > 0.f) { Chris@433: ++semis; Chris@433: cents -= 100.f; Chris@433: } Chris@433: } Chris@433: Chris@373: int ic = lrintf(cents); Chris@373: Chris@373: if (ic == 0) { Chris@373: if (semis >= 12) { Chris@373: return QString("%1'%2").arg(semis/12).arg(semis - 12*(semis/12)); Chris@373: } else { Chris@373: return QString("%1").arg(semis); Chris@373: } Chris@373: } else { Chris@373: if (ic > 0) { Chris@373: if (semis >= 12) { Chris@373: return QString("%1'%2+%3c").arg(semis/12).arg(semis - 12*(semis/12)).arg(ic); Chris@373: } else { Chris@373: return QString("%1+%3c").arg(semis).arg(ic); Chris@373: } Chris@373: } else { Chris@373: if (semis >= 12) { Chris@373: return QString("%1'%2%3c").arg(semis/12).arg(semis - 12*(semis/12)).arg(ic); Chris@373: } else { Chris@373: return QString("%1%3c").arg(semis).arg(ic); Chris@373: } Chris@373: } Chris@373: } Chris@373: } Chris@373: Chris@274: bool Chris@274: Pitch::isFrequencyInMidiRange(float frequency, Chris@274: float concertA) Chris@274: { Chris@274: float centsOffset = 0.0; Chris@274: int midiPitch = getPitchForFrequency(frequency, ¢sOffset, concertA); Chris@274: return (midiPitch >= 0 && midiPitch < 128); Chris@274: } Chris@274: