|
From: Tapio V. <aa...@us...> - 2010-08-21 14:02:37
|
Module: performous
Branch: joinmenu
Commit: cc83a0a46e68dcbede89d7cad00e50ab85ab42c6
Author: Tapio Vierros <tap...@gm...>
Date: Wed Aug 18 20:45:03 2010 +0300
Framework for pitch shifter guitar whammy bar audio effect.
Actual algorithm is missing, and the whammy value computation needs some tuning / AnimValue.
---
game/audio.cc | 58 +++++++++++++++++++++++++++++++++++++++++-----
game/audio.hh | 3 ++
game/guitargraph.cc | 11 +++++----
game/guitargraph.hh | 2 +
game/instrumentgraph.hh | 1 +
game/screen_sing.cc | 20 +++++++++++++---
6 files changed, 79 insertions(+), 16 deletions(-)
diff --git a/game/audio.cc b/game/audio.cc
index 84fdc7c..f86e728 100644
--- a/game/audio.cc
+++ b/game/audio.cc
@@ -15,6 +15,9 @@
using namespace boost::posix_time;
namespace {
+ /**
+ * A function to parse key=value pairs with quoting capabilites.
+ */
std::map<std::string, std::string> parseKeyValuePairs(const std::string& st) {
std::map<std::string, std::string> ret;
bool inside_quotes = false;
@@ -49,13 +52,26 @@ namespace {
if (!key.empty()) ret[key] = value;
return ret;
}
+
+ /**
+ * Pitch shifter.
+ * @param begin start of the buffer
+ * @param end end of the buffer
+ * @param factor shift factor from 0 (no shift) to 1 (maximum)
+ */
+ template <typename It> void PitchShift(It begin, It end, double factor) {
+ //FIXME: Dummy volume bender
+ for (It i = begin; i != end; ++i)
+ *i *= factor * factor; // Decrease music volume
+ }
}
class Music {
struct Track {
FFmpeg mpeg;
float fadeLevel;
- Track(std::string const& filename, unsigned int sr): mpeg(false, true, filename, sr), fadeLevel(1.0f) {}
+ float pitchFactor;
+ Track(std::string const& filename, unsigned int sr): mpeg(false, true, filename, sr), fadeLevel(1.0f), pitchFactor(0.0f) {}
};
typedef boost::ptr_map<std::string, Track> Tracks;
Tracks tracks; ///< Audio decoders
@@ -82,6 +98,7 @@ public:
double fadeLevel;
double fadeRate;
typedef std::map<std::string,std::string> Files;
+ typedef std::vector<float> Buffer;
Music(Files const& filenames, unsigned int sr, bool preview):
srate(sr), m_pos(), m_baseTime(getTime()),
m_preview(preview), fadeLevel(), fadeRate() {
@@ -95,10 +112,23 @@ public:
size_t samples = end - begin;
timeSync(samples); // Keep audio synced
bool eof = true;
- std::vector<float> mixbuf(end - begin);
+ Buffer mixbuf(end - begin);
for (Tracks::iterator it = tracks.begin(), itend = tracks.end(); it != itend; ++it) {
Track& t = *it->second;
- if (t.mpeg.audioQueue(&*mixbuf.begin(), &*mixbuf.end(), m_pos, t.fadeLevel)) eof = false;
+// if (it->first == "guitar") std::cout << t.pitchFactor << std::endl;
+ if (t.pitchFactor != 0) { // Pitch shift
+ Buffer tempbuf(end - begin);
+ // Get audio to temp buffer
+ if (t.mpeg.audioQueue(&*tempbuf.begin(), &*tempbuf.end(), m_pos, t.fadeLevel)) eof = false;
+ // Do the magic
+ PitchShift(&*tempbuf.begin(), &*tempbuf.end(), t.pitchFactor);
+ // Mix with other tracks
+ Buffer::iterator m = mixbuf.begin();
+ Buffer::iterator b = tempbuf.begin();
+ while (b != tempbuf.end())
+ *m++ += (*b++);
+ // Otherwise just get the audio and mix it straight away
+ } else if (t.mpeg.audioQueue(&*mixbuf.begin(), &*mixbuf.end(), m_pos, t.fadeLevel)) eof = false;
}
m_pos += samples;
for (size_t i = 0, iend = mixbuf.size(); i != iend; ++i) {
@@ -138,6 +168,11 @@ public:
if (it == tracks.end()) return;
it->second->fadeLevel = fadeLevel;
}
+ void trackPitchBend(std::string const& name, double pitchFactor) {
+ Tracks::iterator it = tracks.find(name);
+ if (it == tracks.end()) return;
+ it->second->pitchFactor = pitchFactor;
+ }
};
struct Sample {
@@ -198,11 +233,10 @@ struct Synth {
}
};
-
struct Command {
- enum { TRACK_FADE, SAMPLE_RESET } type;
+ enum { TRACK_FADE, TRACK_PITCHBEND, SAMPLE_RESET } type;
std::string track;
- double fadeLevel;
+ double factor;
};
struct Output {
@@ -230,7 +264,10 @@ struct Output {
Command const& cmd = commands[i];
switch (cmd.type) {
case Command::TRACK_FADE:
- if (!playing.empty()) playing[0].trackFade(cmd.track, cmd.fadeLevel);
+ if (!playing.empty()) playing[0].trackFade(cmd.track, cmd.factor);
+ break;
+ case Command::TRACK_PITCHBEND:
+ if (!playing.empty()) playing[0].trackPitchBend(cmd.track, cmd.factor);
break;
case Command::SAMPLE_RESET:
boost::ptr_map<std::string, Sample>::iterator it = samples.find(cmd.track);
@@ -529,6 +566,13 @@ void Audio::streamFade(std::string track, double fadeLevel) {
o.commands.push_back(cmd);
}
+void Audio::streamBend(std::string track, double pitchFactor) {
+ Output& o = self->output;
+ boost::mutex::scoped_lock l(o.mutex);
+ Command cmd = { Command::TRACK_PITCHBEND, track, pitchFactor };
+ o.commands.push_back(cmd);
+}
+
void Audio::toggleSynth(Notes const& notes) {
Output& o = self->output;
boost::mutex::scoped_lock l(o.synth_mutex);
diff --git a/game/audio.hh b/game/audio.hh
index 09c1657..15b9ba4 100644
--- a/game/audio.hh
+++ b/game/audio.hh
@@ -59,6 +59,9 @@ public:
void toggleSynth(Notes const&);
/** Adjust volume level of a single track (used for muting incorrectly played instruments). Range 0.0 to 1.0. **/
void streamFade(std::string track, double volume);
+ /** Do a pitch shift - used for guitar whammy bar */
+ void streamBend(std::string track, double pitchFactor);
+ /** Get sample rate */
double getSR() const { return 48000.0; }
};
diff --git a/game/guitargraph.cc b/game/guitargraph.cc
index e167176..7815d55 100644
--- a/game/guitargraph.cc
+++ b/game/guitargraph.cc
@@ -85,7 +85,8 @@ GuitarGraph::GuitarGraph(Audio& audio, Song const& song, bool drums, int number,
m_soloTotal(),
m_soloScore(),
m_solo(),
- m_practHold(false)
+ m_practHold(false),
+ m_whammy(0)
{
// Copy all tracks of supported types (either drums or non-drums) to m_instrumentTracks
for (InstrumentTracks::const_iterator it = m_song.instrumentTracks.begin(); it != m_song.instrumentTracks.end(); ++it) {
@@ -192,7 +193,7 @@ void GuitarGraph::engine() {
if (!m_drumfills.empty()) updateDrumFill(time); // Drum Fills / BREs
if (m_starpower.get() > 0.001) m_correctness.setTarget(1.0, true);
if (joining(time)) m_dead = 0; // Disable dead counting while joining
- double whammy = 0;
+ m_whammy = 0;
bool difficulty_changed = false;
// Handle all events
for (input::Event ev; m_input.tryPoll(ev);) {
@@ -213,7 +214,7 @@ void GuitarGraph::engine() {
continue;
}
if (ev.type == input::Event::RELEASE) endHold(ev.button, time);
- if (ev.type == input::Event::WHAMMY) whammy = (1.0 + ev.button + 2.0*(rand()/double(RAND_MAX))) / 4.0;
+ if (ev.type == input::Event::WHAMMY) m_whammy = (1.0 + ev.button + 2.0*(rand()/double(RAND_MAX))) / 4.0;
} else {
// Handle drum lefty-mode
if (m_leftymode && ev.button > 0) ev.button = m_pads - ev.button;
@@ -314,8 +315,8 @@ void GuitarGraph::engine() {
Event& ev = m_events[m_holds[fret] - 1];
ev.glow.setTarget(1.0, true);
// Whammy animvalue mangling
- ev.whammy.setTarget(whammy);
- if (whammy > 0) {
+ ev.whammy.setTarget(m_whammy);
+ if (m_whammy > 0) {
ev.whammy.move(0.5);
if (ev.whammy.get() > 1.0) ev.whammy.setValue(1.0);
}
diff --git a/game/guitargraph.hh b/game/guitargraph.hh
index 1067746..2ac6ab7 100644
--- a/game/guitargraph.hh
+++ b/game/guitargraph.hh
@@ -54,6 +54,7 @@ class GuitarGraph: public InstrumentGraph {
bool dead() const;
std::string getTrack() const { return m_track_index->first; }
std::string getDifficultyString() const;
+ double getWhammy() const { return m_whammy; }
private:
// Engine / scoring utils
@@ -142,5 +143,6 @@ class GuitarGraph: public InstrumentGraph {
double m_soloScore; /// score during solo
bool m_solo; /// are we currently playing a solo
bool m_practHold; /// true if holding a chord during practice
+ double m_whammy; /// whammy value for pitch shift
};
diff --git a/game/instrumentgraph.hh b/game/instrumentgraph.hh
index 7bc6ab8..0627cf2 100644
--- a/game/instrumentgraph.hh
+++ b/game/instrumentgraph.hh
@@ -89,6 +89,7 @@ class InstrumentGraph {
unsigned stream() const { return m_stream; }
double correctness() const { return m_correctness.get(); }
int getScore() const { return (m_score > 0 ? m_score : 0) * m_scoreFactor; }
+ virtual double getWhammy() const { return 0; }
protected:
// Core stuff
diff --git a/game/screen_sing.cc b/game/screen_sing.cc
index 3543c3e..c24919b 100644
--- a/game/screen_sing.cc
+++ b/game/screen_sing.cc
@@ -122,13 +122,20 @@ bool ScreenSing::instrumentLayout(double time) {
double iw = 1.0 / count;
typedef std::pair<unsigned, double> CountSum;
std::map<std::string, CountSum> volume; // Stream id to (count, sum)
+ std::map<std::string, CountSum> pitchbend; // Stream id to (count, sum)
for (Instruments::iterator it = m_instruments.begin(); it != m_instruments.end(); ++it) {
it->engine(); // Run engine even when dead so that joining is possible
if (!it->dead()) {
it->position((0.5 + i - 0.5 * count) * iw, iw); // Do layout stuff
- CountSum& cs = volume[it->getTrack()];
- cs.first++;
- cs.second += it->correctness();
+ {
+ CountSum& cs = volume[it->getTrack()];
+ cs.first++;
+ cs.second += it->correctness();
+ }{
+ CountSum& cs = pitchbend[it->getTrack()];
+ cs.first++;
+ cs.second += it->getWhammy();
+ }
it->draw(time);
++i;
}
@@ -141,7 +148,7 @@ bool ScreenSing::instrumentLayout(double time) {
// Set volume levels (averages of all instruments playing that track)
for( std::map<std::string,std::string>::iterator it = m_song->music.begin() ; it != m_song->music.end() ; ++it ) {
double level = 1.0;
- if( volume.find(it->first) == volume.end() ) {
+ if (volume.find(it->first) == volume.end()) {
m_audio.streamFade(it->first, level);
} else {
CountSum cs = volume[it->first];
@@ -149,6 +156,11 @@ bool ScreenSing::instrumentLayout(double time) {
if (m_song->music.size() <= 1) level = std::max(0.333, level);
m_audio.streamFade(it->first, level);
}
+ if (pitchbend.find(it->first) != pitchbend.end()) {
+ CountSum cs = pitchbend[it->first];
+ level = cs.second;
+ m_audio.streamBend(it->first, level);
+ }
}
return (count > 0);
}
|