|
From: Tapio V. <aa...@us...> - 2010-08-19 14:11:27
|
Module: performous
Branch: pass-through
Commit: 36e8ba88cdb79b06903656990bb29a1155d980ff
Author: Tapio Vierros <tap...@gm...>
Date: Thu Aug 19 17:08:07 2010 +0300
Implement microphone pass-through in audio engine.
* No way to disable it, it even invades menus.
* Requires very low suggestedLatency (so some platforms are probably b0rked).
---
game/audio.cc | 13 +++++++++++++
game/libda/portaudio.hpp | 2 +-
game/pitch.cc | 18 ++++++++++++++++++
game/pitch.hh | 15 ++++++++++++++-
4 files changed, 46 insertions(+), 2 deletions(-)
diff --git a/game/audio.cc b/game/audio.cc
index f86e728..a9ae880 100644
--- a/game/audio.cc
+++ b/game/audio.cc
@@ -246,6 +246,7 @@ struct Output {
std::auto_ptr<Synth> synth;
std::auto_ptr<Music> preloading;
boost::ptr_vector<Music> playing, disposing;
+ std::vector<Analyzer*> mics;
boost::ptr_map<std::string, Sample> samples;
std::vector<Command> commands;
volatile bool paused;
@@ -294,6 +295,15 @@ struct Output {
}
++i;
}
+ // Mix in microphones (if pass-through is enabled)
+ // TODO: Create a config var for enable/disable
+ if (mics.size() > 0) {
+ for (float *i = begin; i < end; ++i) *i *= 0.3; // Decrease music volume
+ for (size_t i = 0; i < mics.size(); ++i) {
+ if (mics[i])
+ mics[i]->output(begin, end); // Do the actual mixing
+ }
+ }
// Mix in the samples currently playing
{
// samples should not be created/destroyed on the fly
@@ -454,6 +464,9 @@ struct Audio::Impl {
std::cerr << "Audio device '" << *it << "': " << e.what() << std::endl;
}
}
+ // Assign mic buffers to the output for pass-through
+ for (size_t i = 0; i < analyzers.size(); ++i)
+ output.mics.push_back(&analyzers[i]);
}
};
diff --git a/game/libda/portaudio.hpp b/game/libda/portaudio.hpp
index 4211545..91aa525 100644
--- a/game/libda/portaudio.hpp
+++ b/game/libda/portaudio.hpp
@@ -44,7 +44,7 @@ namespace portaudio {
PaStreamParameters params;
Params(PaStreamParameters const& init = PaStreamParameters()): params(init) {
// Some useful defaults so that things just work
- channelCount(2).sampleFormat(paFloat32).suggestedLatency(0.05);
+ channelCount(2).sampleFormat(paFloat32).suggestedLatency(0.01);
}
Params& channelCount(int val) { params.channelCount = val; return *this; }
Params& device(PaDeviceIndex val) { params.device = val; return *this; }
diff --git a/game/pitch.cc b/game/pitch.cc
index 773183a..0428bcf 100644
--- a/game/pitch.cc
+++ b/game/pitch.cc
@@ -47,6 +47,24 @@ Analyzer::Analyzer(double rate, std::size_t step):
}
}
+void Analyzer::output(float* begin, float* end) {
+ size_t frames = end - begin;
+ if (frames > outbuf.size() * 2) return;
+ size_t i = 0;
+ boost::mutex::scoped_lock l(m_mutex);
+ while (begin != end) {
+ float value = outbuf[i] * 15.0; // Amplify
+ *begin++ += value; // L channel
+ *begin++ += value; // R channel
+ ++i;
+ }
+ // Remove used data from the buffer
+ outbuf.erase(outbuf.begin(), outbuf.begin() + frames/2);
+ // If the ouput buffer grows very big, reset it
+ if (outbuf.size() > 5000) outbuf.clear();
+}
+
+
namespace {
bool sqrLT(float a, float b) { return a * a < b * b; }
diff --git a/game/pitch.hh b/game/pitch.hh
index 0fa159d..8b50021 100644
--- a/game/pitch.hh
+++ b/game/pitch.hh
@@ -3,8 +3,10 @@
#include <complex>
#include <vector>
#include <list>
+#include <deque>
#include <algorithm>
#include <cmath>
+#include <boost/thread.hpp>
/// struct to represent tones
struct Tone {
@@ -15,7 +17,7 @@ struct Tone {
double stabledb; ///< stable decibels
double harmonics[MAXHARM]; ///< harmonics array
std::size_t age; ///< age
- Tone();
+ Tone();
void print() const; ///< prints Tone
bool operator==(double f) const; ///< equality operator
void update(Tone const& t); ///< update Tone
@@ -47,8 +49,11 @@ class Analyzer {
Analyzer(double rate, std::size_t step = 200);
/** Add input data to buffer. This is thread-safe (against other functions). **/
template <typename InIt> void input(InIt begin, InIt end) {
+ std::vector<float> tempbuf; // Values to be inserted to the pass-through buffer
+ tempbuf.reserve(1024); // Let's reserve some space already in advance
while (begin != end) {
float s = *begin;
+ tempbuf.push_back(s); // Add to pass-through buffer
++begin;
m_peak *= 0.999;
float p = s * s;
@@ -60,6 +65,10 @@ class Analyzer {
m_buf[w] = s;
m_bufWrite = w2;
}
+ // No need to lock the mutex before this
+ boost::mutex::scoped_lock l(m_mutex);
+ // Insert the new values to the pass-through buffer
+ outbuf.insert(outbuf.end(), tempbuf.begin(), tempbuf.end());
}
/** Call this to process all data input so far. **/
void process();
@@ -87,8 +96,12 @@ class Analyzer {
m_oldfreq = (best ? best->freq : 0.0);
return best;
}
+ /** Give data away for mic pass-through */
+ void output(float* begin, float* end);
private:
+ boost::mutex m_mutex;
+ std::deque<float> outbuf;
std::size_t m_step;
double m_rate;
std::vector<float> m_window;
|