|
From: Yoda-JM <yo...@us...> - 2009-08-29 17:58:13
|
Module: performous
Branch: master
Commit: 7d39b328bf2ef9c0f62485282082dc5c40e9e48c
Author: Vincent Le Ligeour <yo...@us...>
Date: Sat Aug 29 19:57:45 2009 +0200
Added yet another non-working editor stuff
---
editor/main.cc | 39 ++++++++++++++++++---
editor/notes.cc | 82 ++++++++++++++++++++++++++++++++++++++++++++
editor/notes.hh | 101 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 216 insertions(+), 6 deletions(-)
diff --git a/editor/main.cc b/editor/main.cc
index 2855334..a60b742 100644
--- a/editor/main.cc
+++ b/editor/main.cc
@@ -1,23 +1,50 @@
#include "pitch.hh"
#include "ffmpeg.hh"
+#include "notes.hh"
#include <boost/math/special_functions/fpclassify.hpp>
int main(int argc, char **argv) {
// Load audio
FFmpeg mpeg(false, true, argv[1], 48000);
- int64_t position = 0;
-
- da::sample_t *sample = new da::sample_t(1500);
- da::pcm_data data(sample, 1500, 2, 48000);
+ Analyzer analyzer(48000, 1500);
+ MusicalScale scale;
// wait for ffmpeg to be ready
// TODO: fix this it is probably not enough
while( boost::math::isinf(mpeg.duration()) );
+ // this should majorate the song duration
+ int64_t duration = (mpeg.duration() + 0.5) * 48000 * 2;
+
+ unsigned int i = 0;
+ int64_t position = 0;
+ da::sample_t *sample = new da::sample_t(2);
+ da::pcm_data data(sample, 1500, 2, 48000);
while( mpeg.audioQueue(data, position) ) {
- std::cout << "Position: " << position << " " << double(position) / double(48000*2)<< std::endl;
+ std::cout << i << ": " << position << " " << double(position) / double(48000*2)<< std::endl;
+
+ analyzer.input(data.begin(0), data.end(0));
+ analyzer.process();
+
+ Tone const* tone = analyzer.findTone();
+ double freq = (tone ? tone->freq : 0.0);
+
+ if( freq != 0.0 ) {
+ std::cout << " Found: " << freq << std::endl;
+ Analyzer::tones_t tones = analyzer.getTones();
+ for (Analyzer::tones_t::const_iterator t = tones.begin(); t != tones.end(); ++t) {
+ if (t->age < Tone::MINAGE) continue;
+ std::cout << " " << t->freq << std::endl;
+ int note = scale.getNoteId(t->freq);
+ if (note < 0) continue;
+ // Here I could spot X=position, Y=note, Z=t->db
+ }
+ } else {
+ std::cout << " No tone here" << std::endl;
+ }
+ i++;
}
- std::cout << "Duration: " << mpeg.duration() << std::endl;
+ std::cout << i << ": " << duration << " " << mpeg.duration() << std::endl;
return EXIT_SUCCESS;
}
diff --git a/editor/notes.cc b/editor/notes.cc
new file mode 100644
index 0000000..49664e0
--- /dev/null
+++ b/editor/notes.cc
@@ -0,0 +1,82 @@
+#include "notes.hh"
+
+#include "util.hh"
+#include <cmath>
+#include <algorithm>
+#include <limits>
+#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 * 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()), power(getNaN()), type(NORMAL), note(), notePrev() {}
+
+double Note::diff(double note, double n) { return remainder(n - note, 12.0); }
+double Note::maxScore() const { return scoreMultiplier(0.0) * (end - begin); }
+
+double Note::score(double n, double b, double e) const {
+ double len = std::min(e, end) - std::max(b, begin);
+ if (len <= 0.0 || !(n > 0.0)) return 0.0;
+ return scoreMultiplier(std::abs(diff(n))) * len;
+}
+
+double Note::scoreMultiplier(double error) const {
+ double max = 0.0;
+ switch (type) {
+ case FREESTYLE: power += 1.0; return 1.0;
+ case NORMAL: case SLIDE: max = 1.0; break;
+ case GOLDEN: max = 2.0; break;
+ case SLEEP: break;
+ }
+ double accuracy = clamp(1.5 - error, 0.0, 1.0);
+ power += accuracy;
+ return accuracy * max;
+}
+
+Duration::Duration(): begin(getNaN()), end(getNaN()) {}
+
diff --git a/editor/notes.hh b/editor/notes.hh
new file mode 100644
index 0000000..59c3fc6
--- /dev/null
+++ b/editor/notes.hh
@@ -0,0 +1,101 @@
+#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;
+
+struct Track {
+ // TODO: name should not be needed here (contained into the map)
+ Track(std::string n): name(n) {}
+ std::string name;
+ NoteMap nm;
+};
+
+// TODO: remove the following two types
+typedef std::vector<Track> TrackVector;
+typedef std::vector<Track const*> TrackVectorConstPtr;
+// keep these ones
+typedef std::map<std::string,Track> TrackMap;
+typedef std::map<std::string,Track const*> TrackMapConstPtr; // this one really needed ? can't we save only the map key for comparison ?
+
+namespace {
+ bool isTrackInside(TrackMap &track_map, std::string name) {
+ if( track_map.find(name) != track_map.end() ) return true;
+ return false;
+ }
+}
+
+// TODO: Make Note use Duration
+
+/// note read from songfile
+struct Note {
+ Note();
+ double begin, ///< begin time
+ end; ///< end time
+ /// power of note
+ mutable double power;
+ /// note type
+ enum Type { FREESTYLE = 'F', NORMAL = ':', GOLDEN = '*', SLIDE = '+', SLEEP = '-'} 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);
+ /// maximum score
+ double maxScore() const;
+ /// score when singing
+ double score(double freq, double b, double e) const;
+ /// 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; }
+ private:
+ double scoreMultiplier(double error) const;
+};
+
+typedef std::vector<Note> Notes;
+
|