|
From: rainbyte <rai...@us...> - 2012-07-17 10:43:11
|
Author: Lasse Kärkkäinen <tronic+ndrm at trn.iki.fi> Date: Wed Oct 26 18:00:21 2011 +0300 Cleanup MusicalScale. No API changes. --- game/musicalscale.cc | 51 ++++++++++++++++++++++++++++++++++++++++++++++++++ game/musicalscale.hh | 29 ++++++++++++++++++++++++++++ game/notes.cc | 47 ---------------------------------------------- game/notes.hh | 26 +------------------------ 4 files changed, 81 insertions(+), 72 deletions(-) diff --git a/game/musicalscale.cc b/game/musicalscale.cc new file mode 100644 index 0000000..20ca870 --- /dev/null +++ b/game/musicalscale.cc @@ -0,0 +1,51 @@ +#include "musicalscale.hh" + +#include <cmath> +#include <sstream> +#include <stdexcept> + +// NOTE: This is the C major scale. +// The format needs to be preserved for isSharp, and any scale changes must be done in getNoteNum manually. +const char* const noteNames[12] = {"C ","C#","D ","D#","E ","F ","F#","G ","G#","A ","A#","B "}; + +std::string MusicalScale::getNoteStr(double freq) const try { + int id = getNoteId(freq); + std::ostringstream oss; + oss << noteNames[id % 12] << " " << int(freq + 0.5) << " Hz"; + return oss.str(); +} catch (std::logic_error&) { + return std::string(); +} + +unsigned int MusicalScale::getNoteNum(int id) const { + // C major scale + int n = id % 12; + return (n + (n > 4)) / 2; +} + +bool MusicalScale::isSharp(int id) const { + if (id < 0) throw std::logic_error("MusicalScale::isSharp: Invalid note ID"); + return noteNames[id % 12][1] == '#'; +} + +double MusicalScale::getNoteFreq(int id) const { + if (id == -1) return 0.0; + return m_baseFreq * std::pow(2.0, (id - m_baseId) / 12.0); +} + +int MusicalScale::getNoteId(double freq) const { + double note = getNote(freq); + return int(note + 0.5); // Mathematical rounding +} + +double MusicalScale::getNote(double freq) const { + double note = m_baseId + 12.0 * std::log(freq / m_baseFreq) / std::log(2.0); + if (note >= 0.0 && note <= 127.0) return note; + throw std::logic_error("MusicalScale::getNote: Invalid freq"); +} + +double MusicalScale::getNoteOffset(double freq) const { + double note = getNote(freq); + return note - int(note + 0.5); +} + diff --git a/game/musicalscale.hh b/game/musicalscale.hh new file mode 100644 index 0000000..0f2aba4 --- /dev/null +++ b/game/musicalscale.hh @@ -0,0 +1,29 @@ +#pragma once + +#include <string> + +/// Conversions for the C major musical scale +class MusicalScale { + private: + double m_baseFreq; + static const int m_baseId = 69; ///< What is the baseFreq in MIDI? + + public: + /// Construct a scale object + MusicalScale(double baseFreq = 440.0): m_baseFreq(baseFreq) {} + /// Get a human-readable string representation for the frequency + std::string getNoteStr(double freq) const; + /// Get a note line number in traditional notation (0 = C, 1 = D, ...) + unsigned int getNoteNum(int id) const; + /// Check if the note is sharp (#) + bool isSharp(int id) const; + /// Get the proper frequency of the note + double getNoteFreq(int id) const; + /// Get the nearest note for the frequency + int getNoteId(double freq) const; + /// Get the precise (non-rounded) note id for a the frequency + double getNote(double freq) const; + /// Get the offset (-0.5 to 0.5) from the nearest note + double getNoteOffset(double freq) const; +}; + diff --git a/game/notes.cc b/game/notes.cc index 14af3a2..258fec2 100644 --- a/game/notes.cc +++ b/game/notes.cc @@ -5,53 +5,6 @@ #include <sstream> #include <stdexcept> -std::string MusicalScale::getNoteStr(double freq) const { - int id = getNoteId(freq); - if (id == -1) return std::string(); - static const char * note[12] = {"C ","C#","D ","D#","E ","F ","F#","G ","G#","A ","A#","B "}; - std::ostringstream oss; - // Acoustical Society of America Octave Designation System - //int octave = 2 + id / 12; - oss << note[id%12] << " " << int(round(freq)) << " Hz"; - return oss.str(); -} - -unsigned int MusicalScale::getNoteNum(int id) const { - // C major scale - int n = id % 12; - return (n + (n > 4)) / 2; -} - -bool MusicalScale::isSharp(int id) const { - if (id < 0) throw std::logic_error("MusicalScale::isSharp: Invalid note ID"); - // C major scale - switch (id % 12) { - case 1: case 3: case 6: case 8: case 10: return true; - } - return false; -} - -double MusicalScale::getNoteFreq(int id) const { - if (id == -1) return 0.0; - return m_baseFreq * std::pow(2.0, (id - m_baseId) / 12.0); -} - -int MusicalScale::getNoteId(double freq) const { - double note = getNote(freq); - if (note >= 0.0 && note < 100.0) return int(note + 0.5); - return -1; -} - -double MusicalScale::getNote(double freq) const { - if (freq < 1.0) return getNaN(); - return m_baseId + 12.0 * std::log(freq / m_baseFreq) / std::log(2.0); -} - -double MusicalScale::getNoteOffset(double freq) const { - double frac = freq / getNoteFreq(getNoteId(freq)); - return 12.0 * std::log(frac) / std::log(2.0); -} - Note::Note(): begin(getNaN()), end(getNaN()), phase(getNaN()), power(getNaN()), type(NORMAL), note(), notePrev() {} double Note::diff(double note, double n) { return remainder(n - note, 12.0); } diff --git a/game/notes.hh b/game/notes.hh index 4c67bae..ebc5daf 100644 --- a/game/notes.hh +++ b/game/notes.hh @@ -5,31 +5,7 @@ #include <vector> #include "color.hh" - -/// musical scale, defaults to C major -class MusicalScale { - private: - double m_baseFreq; - static const int m_baseId = 33; - - public: - /// constructor - MusicalScale(double baseFreq = 440.0): m_baseFreq(baseFreq) {} - /// get name of note - std::string getNoteStr(double freq) const; - /// get note number for id - unsigned int getNoteNum(int id) const; - /// true if sharp note - bool isSharp(int id) const; - /// get frequence for note id - double getNoteFreq(int id) const; - /// get note id for frequence - int getNoteId(double freq) const; - /// get note for frequence - double getNote(double freq) const; - /// get note offset for frequence - double getNoteOffset(double freq) const; -}; +#include "musicalscale.hh" /// stores duration of a note struct Duration { |