|
From: Tapio V. <aa...@us...> - 2011-01-06 18:04:16
|
Module: editor
Branch: master
Commit: 20c314c21bbc3f3ca2a9c2e13d006c493d94eec0
Author: Tapio Vierros <tap...@gm...>
Date: Thu Jan 6 19:49:53 2011 +0200
Import some (modified) note classes and utils from Performous.
---
notes.cc | 69 ++++++++++++++++++++++++++++++++++++++++++++++++++
notes.hh | 84 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
util.hh | 43 +++++++++++++++++++++++++++++++
3 files changed, 196 insertions(+), 0 deletions(-)
diff --git a/notes.cc b/notes.cc
new file mode 100644
index 0000000..4596c89
--- /dev/null
+++ b/notes.cc
@@ -0,0 +1,69 @@
+#include "notes.hh"
+
+#include "util.hh"
+#include <cmath>
+#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);
+}
+
+Duration::Duration(): begin(getNaN()), end(getNaN()) {}
+
+Note::Note(): begin(getNaN()), end(getNaN()), phase(getNaN()), type(NORMAL), note(), notePrev() {}
+
+double Note::diff(double note, double n) { return remainder(n - note, 12.0); }
+
+VocalTrack::VocalTrack(std::string name) : name(name) {reload();}
+
+void VocalTrack::reload() {
+ notes.clear();
+ m_scoreFactor = 0.0;
+ noteMin = std::numeric_limits<int>::max();
+ noteMax = std::numeric_limits<int>::min();
+ beginTime = endTime = getNaN();
+}
diff --git a/notes.hh b/notes.hh
new file mode 100644
index 0000000..6557afe
--- /dev/null
+++ b/notes.hh
@@ -0,0 +1,84 @@
+#pragma once
+
+#include <map>
+#include <string>
+#include <vector>
+
+/// 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;
+};
+
+/// stores duration of a note
+struct Duration {
+ double begin, ///< beginning timestamp in seconds
+ end; ///< ending timestamp in seconds
+ Duration();
+ /// create a new Duration object and initialize begin and end
+ Duration(double b, double e): begin(b), end(e) {}
+ /// compares begin timestamps of two Duration structs
+ static bool ltBegin(Duration const& a, Duration const& b) { return a.begin < b.begin; }
+ /// compares end timestamps of two Duration structs
+ static bool ltEnd(Duration const& a, Duration const& b) { return a.end < b.end; }
+};
+
+typedef std::vector<Duration> Durations;
+typedef std::map<int, Durations> NoteMap;
+
+/// note read from songfile
+struct Note {
+ Note();
+ double begin, ///< begin time
+ end; ///< end time
+ double phase; /// position within a measure, [0, 1)
+ /// note type
+ enum Type { FREESTYLE = 'F', NORMAL = ':', GOLDEN = '*', SLIDE = '+', SLEEP = '-',
+ TAP = '1', HOLDBEGIN = '2', HOLDEND = '3', ROLL = '4', MINE = 'M', LIFT = 'L'} type;
+ int note; ///< MIDI pitch of the note (at the end for slide notes)
+ int notePrev; ///< MIDI pitch of the previous note (should be same as note for everything but SLIDE)
+ /// lyrics syllable for that note
+ std::string syllable;
+ /// difference of n from note
+ double diff(double n) const { return diff(note, n); }
+ /// difference of n from note, so that note + diff(note, n) is n (mod 12)
+ static double diff(double note, double n);
+ /// compares begin of two notes
+ static bool ltBegin(Note const& a, Note const& b) { return a.begin < b.begin; }
+ /// compares end of two notes
+ static bool ltEnd(Note const& a, Note const& b) { return a.end < b.end; }
+};
+
+typedef std::vector<Note> Notes;
+
+struct VocalTrack {
+ VocalTrack(std::string name);
+ void reload();
+ std::string name;
+ Notes notes;
+ int noteMin, noteMax; ///< lowest and highest note
+ double beginTime, endTime; ///< the period where there are notes
+ double m_scoreFactor; ///< normalization factor for the scoring system
+ MusicalScale scale; ///< scale in which song is sung
+};
+
+typedef std::map<std::string, VocalTrack> VocalTracks;
diff --git a/util.hh b/util.hh
new file mode 100644
index 0000000..c684317
--- /dev/null
+++ b/util.hh
@@ -0,0 +1,43 @@
+#pragma once
+
+#include <limits>
+#include <stdexcept>
+
+/** Implement C99 mathematical rounding (which C++ unfortunately currently lacks) **/
+template <typename T> T round(T val) { return int(val + (val >= 0 ? 0.5 : -0.5)); }
+
+/** Implement C99 remainder function (not precisely, but almost) **/
+template <typename T> T remainder(T val, T div) { return val - round(val/div) * div; }
+
+/** Limit val to range [min, max] **/
+template <typename T> T clamp(T val, T min = 0, T max = 1) {
+ if (min > max) throw std::logic_error("min > max");
+ if (val < min) return min;
+ if (val > max) return max;
+ return val;
+}
+
+/** A convenient way for getting NaNs **/
+static inline double getNaN() { return std::numeric_limits<double>::quiet_NaN(); }
+
+/** A convenient way for getting infs **/
+static inline double getInf() { return std::numeric_limits<double>::infinity(); }
+
+static inline bool isPow2(unsigned int val) {
+ if (val == 0) return false;
+ if ((val & (val-1)) == 0) return true; // From Wikipedia: Power_of_two
+ return false;
+}
+
+static inline unsigned int nextPow2(unsigned int val) {
+ unsigned int ret = 1;
+ while (ret < val) ret *= 2;
+ return ret;
+}
+
+static inline unsigned int prevPow2(unsigned int val) {
+ unsigned int ret = 1;
+ while ((ret*2) < val) ret *= 2;
+ return ret;
+}
+
|