From fb0ff1c076358eaec1135366e9783c95a3889fb8 Mon Sep 17 00:00:00 2001 From: kazu Date: Sat, 15 Jul 2017 15:52:05 +0200 Subject: [PATCH] initial commit --- BassDetection.h | 99 ++++++++++++++ BeatDetection.h | 175 +++++++++++++++++++++++++ BeatDetection2.h | 240 ++++++++++++++++++++++++++++++++++ BiquadFilterGate.h | 314 +++++++++++++++++++++++++++++++++++++++++++++ CMakeLists.txt | 88 +++++++++++++ EndlessAVG.h | 38 ++++++ MovingAVG.h | 63 +++++++++ README.md | 3 +- RingBuffer.h | 107 +++++++++++++++ exception.h | 26 ++++ gstbeatdetector.c | 253 ++++++++++++++++++++++++++++++++++++ gstbeatdetector.h | 51 ++++++++ main.cpp | 35 +++++ plugin.cpp | 170 ++++++++++++++++++++++++ plugin.h | 89 +++++++++++++ pulseaudio.h | 107 +++++++++++++++ 16 files changed, 1857 insertions(+), 1 deletion(-) create mode 100755 BassDetection.h create mode 100755 BeatDetection.h create mode 100755 BeatDetection2.h create mode 100755 BiquadFilterGate.h create mode 100755 CMakeLists.txt create mode 100755 EndlessAVG.h create mode 100755 MovingAVG.h create mode 100755 RingBuffer.h create mode 100755 exception.h create mode 100755 gstbeatdetector.c create mode 100755 gstbeatdetector.h create mode 100755 main.cpp create mode 100755 plugin.cpp create mode 100755 plugin.h create mode 100755 pulseaudio.h diff --git a/BassDetection.h b/BassDetection.h new file mode 100755 index 0000000..a3ee121 --- /dev/null +++ b/BassDetection.h @@ -0,0 +1,99 @@ +#ifndef BASSDETECTION_H +#define BASSDETECTION_H + +#include +#include +#include +#include + +#include "BiquadFilterGate.h" +#include "MovingAVG.h" +#include "EndlessAVG.h" + +#include + +template class BassDetection { + + K::Gnuplot gp; + K::GnuplotPlot plot; + K::GnuplotPlotElementLines lines0; + + BiquadFilterGate<1> filter; + MovingAVG avg; + MovingAVG avgLong; + +public: + + /** setup */ + BassDetection() : avg(2500), avgLong(100000) { + setSampleRate(44100); + plot.add(&lines0); + avgLong.add(1); + } + + void setSampleRate(int srate) { + std::cout << "setting sample-rate to " << srate << std::endl; + filter.setLowPass(80, 1, srate); + } + + /** add single value */ + float add(Scalar s) { + + const float val = s / 32768.0f; + const float res = filter.filter(0, val); + + avg.add(res*res); + avgLong.add(res*res); + + const float res2 = avg.get() / avgLong.get(); + avgLong.add(res2); + + static float prev = 0; + + static int x = 0; ++x; + + if (x % 1000 == 0) { + + const float delta = res2 - prev; + prev = res2; + + lines0.add(K::GnuplotPoint2(x, delta)); + + // debug view? + //if (x % 6000 == 0) {show();} + show(); + + if (x % 1000 == 0) { + return delta; + } + + } + + return 0; + + + + + } + + void show() { + + int limit = 500; + int d0 = lines0.size() - limit; + if (d0 > 0) { + lines0.remove(0, d0); + } + + int x0 = lines0[0].x; + int x1 = lines0[lines0.size()-1].x; + plot.getAxisX().setRange(x0, x1); + plot.getAxisY().setRange(-1, +5); + //plot.getAxisY().setRange(-32768, +32768); + gp.draw(plot); + gp.flush(); + + } + +}; + +#endif // BASSDETECTION_H diff --git a/BeatDetection.h b/BeatDetection.h new file mode 100755 index 0000000..7111d2e --- /dev/null +++ b/BeatDetection.h @@ -0,0 +1,175 @@ +#ifndef ANALYZER_H +#define ANALYZER_H + +#include +#include +#include +#include + +#include "BiquadFilterGate.h" +#include "MovingAVG.h" +#include "EndlessAVG.h" + +#include + + +template struct BeatBand { + + BiquadFilterGate<1> filter; + MovingAVG avgShort; + MovingAVG avgLong; + EndlessAVG avgAll; + + BeatBand() : avgShort(800), avgLong(40000) { + avgAll.add(1); + } + + float getMul() const { + return avgAll.get(); + } + + float add(Scalar s) { + + const Scalar filtered = filter.filter(0, s) / getMul(); + const Scalar filtered2 = filtered*filtered; + avgShort.add(filtered2); // short-term average (1/40 sec) + avgLong.add(filtered2); // long-term average (1sec) + avgAll.add(filtered2); // whole average (everything) + + if (avgLong.get() < 0.0001) {return 0;} + + const float delta = std::abs(avgShort.get()) / std::abs(avgLong.get()); + //return (delta); + return avgShort.get(); + + } + +}; + +template class BeatDetection { + + K::Gnuplot gp; + K::GnuplotPlot plot; + K::GnuplotPlotElementLines lines0; + + BeatBand band0; + BeatBand band1; + BeatBand band2; + BeatBand band3; + + MovingAVG avg1; + MovingAVG avg2; + + + +public: + + /** setup */ + BeatDetection() : avg1(600), avg2(10000) { + setSampleRate(44100); + plot.add(&lines0); + } + + void setSampleRate(int srate) { + + std::cout << "setting sample-rate to " << srate << std::endl; + //band0.filter.setBandPass(70, 1, srate); // base + band0.filter.setLowPass(100, 1, srate); + //band0.mul = 1; + band1.filter.setBandPass(3500, 1.0, srate); // claps + //band1.mul = 3.5; + band2.filter.setBandPass(10000, 0.4, srate); // snare??? + //band2.mul = 5.5; + + } + + float area = 0; + int block = 0; + + /** add single value */ + bool add(Scalar s) { + + // debug view + static int xxx = 0; ++xxx; + + bool beat = false; + const float val = s / 32768.0f; // not needed + static int x = 0; x += 1; + float y0 = band0.add(val); + float y1 = 0;//band1.add(val); + float y2 = 0;//band2.add(val); + float y = std::abs(y0)+std::abs(y1*0.5)+std::abs(y2*0.7); + + if (block > 0) {--block;} + + if (y == y) { + //yy = yy * 0.99 + y * 0.01; + avg1.add(y); + avg2.add(y); + float yy = avg1.get() - avg2.get(); + + if (yy > 0.5 && !block) { + beat = true; + block = 9000; + } + + // worked somewhat +// float limit = 0.10; +// if (yy > limit && block == 0) { +// area += (yy-limit); +// if (area > 1000) { +// beat = true; +// area = 0; +// block = 9000; +// } +// } else { +// area = 0; +// } + + if (xxx % 8 == 0) { + lines0.add(K::GnuplotPoint2(x, yy)); + } + + } + + // debug view? + if (xxx % 5000 == 0) {show();} + + if (beat) { + std::cout << band0.getMul() << " " << band1.getMul() << " " << band2.getMul() << std::endl; + } + + return beat; + + } + + /** add multiple values */ + bool add(Scalar* s, int num) { + bool beat = false; + for (int i = 0; i < num; ++i) { + if (add(s[i])) {beat = true;} + } + return beat; + } + + void show() { + + int limit = 8000; + int d0 = lines0.size() - limit; + if (d0 > 0) { + lines0.remove(0, d0); + } + + int x0 = lines0[0].x; + int x1 = lines0[lines0.size()-1].x; + plot.getAxisX().setRange(x0, x1); + plot.getAxisY().setRange(-1, +5); + //plot.getAxisY().setRange(-32768, +32768); + gp.draw(plot); + gp.flush(); + + } + +}; + +#endif // ANALYZER_H diff --git a/BeatDetection2.h b/BeatDetection2.h new file mode 100755 index 0000000..cdbd3b7 --- /dev/null +++ b/BeatDetection2.h @@ -0,0 +1,240 @@ +#ifndef BEATDETECTION2_H +#define BEATDETECTION2_H + + +#include +#include +#include +#include + +#include "BiquadFilterGate.h" +#include "MovingAVG.h" +#include "EndlessAVG.h" + +#include + +//#define PLOT_ME + +#define BD2_SHORT 1024 + +template struct BeatBand2 { + + BiquadFilterGate<2> filter; + MovingAVG avgShort; + MovingAVG avgLong; + MovingAVG avgLongSquared; + MovingAVG avgDiff; + MovingAVG avgDiffSquared; + + + + + BeatBand2() : avgShort(BD2_SHORT), avgLong(1024*64), avgLongSquared(1024*64), avgDiff(1024*80), avgDiffSquared(1024*80) { + ; + } + + void add(Scalar left, Scalar right) { + + // filter + const Scalar fLeft = std::abs(filter.filter(0, left)); + const Scalar fRight = std::abs(filter.filter(1, right)); + //const Scalar energy = fLeft*fLeft + fRight*fRight; + //const Scalar energy = fLeft + fRight; + const Scalar energy = fLeft*fLeft*fLeft + fRight*fRight*fRight; + + // update + avgShort.add(energy); + avgLong.add(energy); + avgLongSquared.add(energy*energy); + + const float diff = avgShort.get() - avgLong.get(); + avgDiff.add(diff); + avgDiffSquared.add(diff*diff); + + } + + float getRatio() const { + return avgShort.get() / avgLong.get(); + } + + float getVariance() const { + return avgLongSquared.get() - (avgLong.get() * avgLong.get()); + } + + float getStdDev() const { + return std::sqrt(getVariance()); + } + + float getDiffVariance() const { + return avgDiffSquared.get() - (avgDiff.get() * avgDiff.get()); + } + float getDiffStdDev() const { + return std::sqrt(getDiffVariance()); + } + +}; + +enum class Mode { + BASS, + SNARE, +}; + +template class BeatDetection2 { + +private: + +#ifdef PLOT_ME + K::Gnuplot gp; + K::GnuplotPlot plot; + K::GnuplotPlotElementLines lines0; + K::GnuplotPlotElementLines linesVar; + K::GnuplotPlotElementLines linesAvgLong; +#endif + + int cnt = 0; + BeatBand2 band0; + Mode mode; + float thresholdMul; + +public: + + BeatDetection2() : BeatDetection2(Mode::BASS) { + ; + } + + /** setup */ + BeatDetection2(Mode mode) : mode(mode) { + + setSampleRate(44100); + + switch(mode) { + case Mode::BASS: thresholdMul = 0.9; break; + case Mode::SNARE: thresholdMul = 1.5; break; + default: throw "invalid mode"; + } + +#ifdef PLOT_ME + gp.setTerminal("wxt", K::GnuplotSize(20, 12)); + plot.add(&lines0); + plot.add(&linesVar); linesVar.getStroke().getColor().setHexStr("#0000ff"); + plot.add(&linesAvgLong); linesAvgLong.getStroke().getColor().setHexStr("#00aa00"); +#endif + } + + + + void setSampleRate(int srate) { + std::cout << "setting sample-rate to " << srate << std::endl; + //band0.filter.setLowPass(110, 1, srate); + switch(mode) { + case Mode::BASS: band0.filter.setBandPass(40, 0.90f, srate); break; + case Mode::SNARE: band0.filter.setBandPass(200, 0.20f, srate); break; + default: throw "invalid mode"; + } + //band0.filter.setBandPass(5000, 1, srate); + } + + int block = 0; + bool lastWasBeat = false; + bool gapFound = false; + + /** add single value */ + bool add(Scalar left, Scalar right) { + + static int x = 0; ++x; + + left /= 36768.0f; + right /= 36768.0f; + + ++cnt; + band0.add(left, right); + bool curIsBeat = false; + + if (cnt == BD2_SHORT) { + + cnt = 0; + + const float ratio0 = band0.getRatio(); + const float var0 = band0.getVariance(); + const float stdDev0 = band0.getDiffStdDev(); + const float diff0 = band0.avgShort.get() - band0.avgLong.get(); + const float avgShort0 = band0.avgShort.get(); + const float avgLong0 = band0.avgLong.get(); + const float threshold0 = band0.avgLong.get() + stdDev0 * thresholdMul; // HERE! + + + //avgRes.add(avgShort0); + //const float zz = avgRes.get(); + const float zz = avgShort0; + //const float C = 2.7 - (20 * var0); + //const float C = 1.0 + (var0); + //if (ratio0 > C) {curIsBeat = true;} + //if (zz > 0.05 && zz > threshold0) {curIsBeat = true;} + if (zz > threshold0) {curIsBeat = true;} + +#ifdef PLOT_ME + + lines0.add(K::GnuplotPoint2(x, zz)); + linesVar.add(K::GnuplotPoint2(x, threshold0)); + //linesAvgLong.add(K::GnuplotPoint2(x, avgLong0)); + + static int xx = 0; ++xx; + if (xx % 5 == 0) { + show(); + } + +#endif + + if (!curIsBeat) {gapFound = true;} + + //if (block > 0 && !curIsBeat) {--block;} + if (block > 0 && gapFound) {--block;} + + if (block == 0 && curIsBeat) { + block = 6; // very short! + gapFound = false; + //std::cout << ratio0 << " : " << var0 << " : " << C << std::endl; + return true; + } + + //const bool res = curIsBeat && !lastWasBeat; + //lastWasBeat = curIsBeat; + +// if (res) { +// std::cout << ratio0 << " : " << var0 << " : " << C << std::endl; + +// } + + //return res; + + } + + return false; + + } + + + +#ifdef PLOT_ME + void show() { + + int limit = 200; + int d0 = lines0.size() - limit; + if (d0 > 0) { + lines0.remove(0, d0); + } + + int x0 = lines0[0].x; + int x1 = lines0[lines0.size()-1].x; + plot.getAxisX().setRange(x0, x1); + //plot.getAxisY().setRange(-0.1, 0.25); + //plot.getAxisY().setRange(-32768, +32768); + gp.draw(plot); + gp.flush(); + + } +#endif + +}; + +#endif // BEATDETECTION2_H diff --git a/BiquadFilterGate.h b/BiquadFilterGate.h new file mode 100755 index 0000000..a19abc1 --- /dev/null +++ b/BiquadFilterGate.h @@ -0,0 +1,314 @@ +#ifndef BIQUADFILTERGATE_H +#define BIQUADFILTERGATE_H + + +/** lower frequency limit. about 20 Hz at 48.000 Hz */ +#define BFG_MIN 0.0005 +#define BFG_MAX 0.4995 + +using Frequency = float; +using Amplitude = float; +using SampleRate = int; + +#define K_PI M_PI + +/** + * a simple biquad filter that can be used + * for low- or high-pass filtering + * + * http://www.musicdsp.org/files/Audio-EQ-Cookbook.txt + */ +template class BiquadFilterGate { + +public: + + /** ctor */ + BiquadFilterGate() : in(), out(), disabled(false) { + reset(); + } + + /** filter the given amplitude of the given channel (history) */ + Amplitude filter( const unsigned int ch, const Amplitude aIn ) { + + Amplitude aOut = 0; + aOut += aIn *(b0a0); + aOut += in[ch][0] *(b1a0); + aOut += in[ch][1] *(b2a0); + aOut -= out[ch][0]*(a1a0); + aOut -= out[ch][1]*(a2a0); + + //aOut = clamp1(aOut); + + in[ch][1] = in[ch][0]; + in[ch][0] = aIn; + + out[ch][1] = out[ch][0]; + out[ch][0] = aOut; + + return aOut; + + } + + + /** reset (disable) the filter */ + void reset() { + + b0a0 = 1.0; + b1a0 = 0.0; + b2a0 = 0.0; + a1a0 = 0.0; + a2a0 = 0.0; + + memset(in, 0, sizeof(in)); + memset(out, 0, sizeof(out)); + + } + + /** configure the filter as low-pass. freqFact between ]0;0.5[ */ + void setLowPass( double freqFact, const float octaves ) { + + if (freqFact < BFG_MIN) {freqFact = BFG_MIN;} + if (freqFact > BFG_MAX) {disabled = true; return;} + disabled = false; + + double w0 = 2.0 * K_PI * freqFact; + double alpha = sin(w0)*sinh( log(2)/2 * octaves * w0/sin(w0) ); + + double b0 = (1.0 - cos(w0))/2.0; + double b1 = 1.0 - cos(w0); + double b2 = (1.0 - cos(w0))/2.0; + double a0 = 1.0 + alpha; + double a1 = -2.0*cos(w0); + double a2 = 1.0 - alpha; + + setValues(a0, a1, a2, b0, b1, b2); + + } + + + /** configure the filter as low-pass */ + void setLowPass( const Frequency freq, const float octaves, const SampleRate sRate ) { + double freqFact = double(freq) / double(sRate); + setLowPass(freqFact, octaves); + } + + + + //http://dspwiki.com/index.php?title=Lowpass_Resonant_Biquad_Filter + //http://www.opensource.apple.com/source/WebCore/WebCore-7536.26.14/platform/audio/Biquad.cpp + /** + * configure as low-pass filter with resonance + * @param freqFact the frequency factor between ]0;0.5[ + * @param res + */ + void setLowPassResonance( double freqFact, float res ) { + + if (freqFact < BFG_MIN) {freqFact = BFG_MIN;} + if (freqFact > BFG_MAX) {disabled = true; return;} + disabled = false; + + res *= 10; + + double g = pow(10.0, 0.05 * res); + double d = sqrt((4 - sqrt(16 - 16 / (g * g))) / 2); + + double theta = K_PI * freqFact; + double sn = 0.5 * d * sin(theta); + double beta = 0.5 * (1 - sn) / (1 + sn); + double gamma = (0.5 + beta) * cos(theta); + double alpha = 0.25 * (0.5 + beta - gamma); + + double a0 = 1.0; + double b0 = 2.0 * alpha; + double b1 = 2.0 * 2.0 * alpha; + double b2 = 2.0 * alpha; + double a1 = 2.0 * -gamma; + double a2 = 2.0 * beta; + + setValues(a0, a1, a2, b0, b1, b2); + + } + + /** configure the filter as high-pass. freqFact between ]0;0.5[ */ + void setHighPass( double freqFact, const float octaves ) { + + if (freqFact < BFG_MIN) {disabled = true; return;} + if (freqFact > BFG_MAX) {freqFact = BFG_MAX;} + disabled = false; + + double w0 = 2.0 * K_PI * freqFact; + double alpha = sin(w0)*sinh( log(2)/2 * octaves * w0/sin(w0) ); + + double b0 = (1.0 + cos(w0))/2.0; + double b1 = -(1.0 + cos(w0)); + double b2 = (1.0 + cos(w0))/2.0; + double a0 = 1.0 + alpha; + double a1 = -2.0*cos(w0); + double a2 = 1.0 - alpha; + + setValues(a0, a1, a2, b0, b1, b2); + + } + + /** configure the filter as high-pass */ + void setHighPass( const Frequency freq, const float octaves, const SampleRate sRate ) { + double freqFact = double(freq) / double(sRate); + setHighPass(freqFact, octaves); + } + + /** configure the filter as band-pass. freqFact between ]0;0.5[ */ + void setBandPass( double freqFact, const float octaves ) { + + if (freqFact < BFG_MIN) {disabled = true; return;} + if (freqFact > BFG_MAX) {disabled = true; return;} + disabled = false; + + //double w0 = 2 * K_PI * / 2 / freqFact; + double w0 = 2.0 * K_PI * freqFact; + double alpha = sin(w0)*sinh( log(2)/2 * octaves * w0/sin(w0) ); + + double b0 = sin(w0)/2.0; + double b1 = 0.0; + double b2 = -sin(w0)/2.0; + double a0 = 1.0 + alpha; + double a1 = -2.0*cos(w0); + double a2 = 1.0 - alpha; + + setValues(a0, a1, a2, b0, b1, b2); + + } + + /** configure the filter as band-pass */ + void setBandPass( const Frequency freq, const float octaves, SampleRate sRate ) { + double freqFact = double(freq) / double(sRate); + setBandPass(freqFact, octaves); + } + + + /** configure the filter as all-pass. freqFact between ]0;0.5[ */ + void setAllPass( double freqFact, const float octaves ) { + + double w0 = 2.0 * K_PI * freqFact; + double alpha = sin(w0)*sinh( log(2)/2 * octaves * w0/sin(w0) ); + + double b0 = 1 - alpha; + double b1 = -2*cos(w0); + double b2 = 1 + alpha; + double a0 = 1 + alpha; + double a1 = -2*cos(w0); + double a2 = 1 - alpha; + + setValues(a0, a1, a2, b0, b1, b2); + + } + + /** configure the filter as all-pass */ + void setAllPass( const Frequency freq, const float octaves, const SampleRate sRate ) { + double freqFact = double(freq) / double(sRate); + setAllPass(freqFact, octaves); + } + + /** configure as notch filter. freqFact between ]0;0.5[ */ + void setNotch( double freqFact, const float octaves ) { + + double w0 = 2.0 * K_PI * freqFact; + double alpha = sin(w0)*sinh( log(2)/2 * octaves * w0/sin(w0) ); + + double b0 = 1.0; + double b1 = -2.0*cos(w0); + double b2 = 1.0; + double a0 = 1.0 + alpha; + double a1 = -2.0*cos(w0); + double a2 = 1.0 - alpha; + + setValues(a0, a1, a2, b0, b1, b2); + + } + + /** configure as notch filter */ + void setNotch( const Frequency freq, const float octaves, const SampleRate sRate ) { + double freqFact = double(freq) / double(sRate); + setNotch(freqFact, octaves); + } + + /** configure the filter as low-shelf. increase all aplitudes below freq? freqFact between ]0;0.5[ */ + void setLowShelf( double freqFact, const float octaves, const float gain ) { + + double A = sqrt( pow(10, (gain/20.0)) ); + double w0 = 2.0 * K_PI * freqFact; + double alpha = sin(w0)*sinh( log(2)/2 * octaves * w0/sin(w0) ); + + double b0 = A*( (A+1.0) - (A-1.0)*cos(w0) + 2.0*sqrt(A)*alpha ); + double b1 = 2.0*A*( (A-1.0) - (A+1.0)*cos(w0) ); + double b2 = A*( (A+1.0) - (A-1.0)*cos(w0) - 2.0*sqrt(A)*alpha ); + double a0 = (A+1.0) + (A-1.0)*cos(w0) + 2.0*sqrt(A)*alpha; + double a1 = -2.0*( (A-1.0) + (A+1.0)*cos(w0) ); + double a2 = (A+1.0) + (A-1.0)*cos(w0) - 2.0*sqrt(A)*alpha; + + setValues(a0, a1, a2, b0, b1, b2); + + } + + /** configure the filter as low-shelf. increase all aplitudes below freq? */ + void setLowShelf( const Frequency freq, const float octaves, const float gain, const SampleRate sRate ) { + double freqFact = double(freq) / double(sRate); + setLowShelf(freqFact, octaves, gain); + } + + /** configure the filter as high-shelf. increase all amplitues above freq? freqFact between ]0;0.5[ */ + void setHighShelf( double freqFact, const float octaves, const float gain ) { + + double A = sqrt( pow(10, (gain/20.0)) ); + double w0 = 2.0 * K_PI * freqFact; + double alpha = sin(w0)*sinh( log(2)/2 * octaves * w0/sin(w0) ); + + double b0 = A*( (A+1.0) + (A-1.0)*cos(w0) + 2.0*sqrt(A)*alpha ); + double b1 = -2.0*A*( (A-1.0) + (A+1.0)*cos(w0) ); + double b2 = A*( (A+1.0) + (A-1.0)*cos(w0) - 2.0*sqrt(A)*alpha ); + double a0 = (A+1.0) - (A-1.0)*cos(w0) + 2.0*sqrt(A)*alpha; + double a1 = 2.0*( (A-1.0) - (A+1.0)*cos(w0) ); + double a2 = (A+1.0) - (A-1.0)*cos(w0) - 2.0*sqrt(A)*alpha; + + setValues(a0, a1, a2, b0, b1, b2); + + } + + + /** configure the filter as high-shelf. increase all amplitues above freq? */ + void setHighShelf( const Frequency freq, const float octaves, const float gain, const SampleRate sRate ) { + double freqFact = double(freq) / double(sRate); + setHighShelf(freqFact, octaves, gain); + } + + +protected: + + /** pre-calculate the quotients for the filtering */ + void setValues(double a0, double a1, double a2, double b0, double b1, double b2) { + b0a0 = float(b0/a0); + b1a0 = float(b1/a0); + b2a0 = float(b2/a0); + a2a0 = float(a2/a0); + a1a0 = float(a1/a0); + } + + /** the bi-quad filter params */ + float b0a0; + float b1a0; + float b2a0; + + float a1a0; + float a2a0; + + /** history for input values, per channel */ + Amplitude in[channels][2]; + + /** history for ouput values, per channel */ + Amplitude out[channels][2]; + + /** filter disabled due to wrong params? */ + bool disabled; + +}; + +#endif // BIQUADFILTERGATE_H diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100755 index 0000000..824d82f --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,88 @@ +# Usage: +# Create build folder, like RC-build next to RobotControl and WifiScan folder +# CD into build folder and execute 'cmake -DCMAKE_BUILD_TYPE=Debug ../RobotControl' +# make + +CMAKE_MINIMUM_REQUIRED(VERSION 2.8) + +# select build type +SET( CMAKE_BUILD_TYPE "${CMAKE_BUILD_TYPE}" ) + +PROJECT(Beat) + +IF(NOT CMAKE_BUILD_TYPE) + MESSAGE(STATUS "No build type selected. Default to Debug") + SET(CMAKE_BUILD_TYPE "Debug") +ENDIF() + + + + +# pkg-config --cflags --libs gstreamer-1.0 +INCLUDE_DIRECTORIES( + /usr/include/gstreamer-1.0 + /usr/include/glib-2.0 + /usr/lib64/glib-2.0/include + /usr/lib/x86_64-linux-gnu/glib-2.0/include + /mnt/vm/workspace/IRGame/ +) + + +FILE(GLOB HEADERS + ./*.h +) + +FILE(GLOB SOURCES + ./*.cpp +) + + +# system specific compiler flags +ADD_DEFINITIONS( +# -O2 + -std=c++11 + -Wall + -Werror=return-type + -Wextra +#-g + -O2 +# #-Wconversion +) + + + +# GSTREAMER PLUGIN + + ADD_LIBRARY( + gst_beat_plugin SHARED + plugin.cpp + ${HEADERS} + ) + + # pkg-config --cflags --libs gstreamer-1.0 + TARGET_LINK_LIBRARIES( + gst_beat_plugin + gstreamer-1.0 + gstaudio-1.0 + gobject-2.0 + glib-2.0 + ) + + +# EXECUTABLE RECORDING FROM PULSEAUDIO + + ADD_EXECUTABLE( + paBeatRecorder + main.cpp + ${HEADERS} + #${SOURCES} + ) + + TARGET_LINK_LIBRARIES( + paBeatRecorder + pulse + pulse-simple + pthread + ) + + diff --git a/EndlessAVG.h b/EndlessAVG.h new file mode 100755 index 0000000..0f382f4 --- /dev/null +++ b/EndlessAVG.h @@ -0,0 +1,38 @@ +#ifndef ENDLESSAVG_H +#define ENDLESSAVG_H + +template class EndlessAVG { + +private: + + /** track the current sum of the vector's values */ + T curSum; + + /** the number of elements to average */ + int count; + +public: + + /** ctor */ + EndlessAVG() : curSum(), count(0) {;} + + /** add a new value */ + void add(const T val) { + curSum += val; + ++count; + } + + /** get the current average */ + T get() const { + return curSum / count; + } + + /** get number of entries to average */ + int getSize() const { + return count; + } + +}; + + +#endif // ENDLESSAVG_H diff --git a/MovingAVG.h b/MovingAVG.h new file mode 100755 index 0000000..ebd937e --- /dev/null +++ b/MovingAVG.h @@ -0,0 +1,63 @@ +#ifndef MOVINGAVG_H +#define MOVINGAVG_H + +#include +#include "RingBuffer.h" + +template class MovingAVG { + +private: + + /** track the current sum of the vector's values */ + T curSum; + + /** the number of elements to average */ + int size; + + /** up to "size" elements */ + RingBuffer values; + +public: + + /** ctor */ + MovingAVG(const int size) : curSum(), size(size), values(size) {;} + + /** add a new value */ + void add(const T val) { + + // add new value + //values.push_back(val); + values.add(val); + curSum += val; + + // too many values? + //if ((int) values.size() > size) { + // curSum -= values.front(); + // values.erase(values.begin()); + //} + if (values.size() >= size) { + curSum -= values.get(); + } + + } + + /** get the current average */ + T get() const { + return curSum / values.size(); + } + + + /** get the number of used entries */ + int getNumUsed() const { + return (int) values.size(); + } + + /** get number of entries to average */ + int getSize() const { + return size; + } + + +}; + +#endif // MOVINGAVG_H diff --git a/README.md b/README.md index 10e0daf..23b054b 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,3 @@ # BeatDetector - +- plugin for gstreamer +- binary reading from pulseaudio diff --git a/RingBuffer.h b/RingBuffer.h new file mode 100755 index 0000000..f4afb60 --- /dev/null +++ b/RingBuffer.h @@ -0,0 +1,107 @@ +#ifndef RINGBUFFER_H +#define RINGBUFFER_H + +#include + + +template class RingBuffer { + +private: + + std::vector data; + + int iWrite; + int iRead; + int available; + +public: + + /** ctor with the size of the buffer */ + RingBuffer(const int size) : data(size), iWrite(0), iRead(0), available(0) { + ; + } + + /** add a new element WITH overflow check! */ + void addSafe(const Element& element) { + //Assert::isTrue(available != (int)data.size(), "buffer overflow"); + add(element); + } + + /** add a new element WIUTHOUT checking for overflows */ + void add(const Element& element) { + data[iWrite] = element; + ++iWrite; + iWrite %= data.size(); + ++available; + } + + /** get the next element */ + const Element& get() { + //Assert::isNot0(available, "buffer is empty"); + const Element& tmp = data[iRead]; + ++iRead; + iRead %= data.size(); + --available; + if (available < 0) {available = 0;} + return tmp; + } + + /** peek into the given element without removing it */ + const Element& peek(const int idx) const { + const Element& tmp = data[(iRead + idx) % data.size()]; + return tmp; + } + + /** peek into the given element without removing it */ + const Element& operator [] (const int idx) const { + return peek(idx); + } + + /** does the buffer contain the given element? */ + bool contains(const Element& e) const { + for (int i = 0; i < available; ++i) { + if (peek(i) == e) {return true;} + } + return false; + } + + /** reset the ringbuffer */ + void reset() { + iWrite = 0; + iRead = 0; + available = 0; + } + + /** is the buffer empty? */ + bool empty() const { + return available == 0; + } + + /** get the number of available entries */ + int size() const { + return available; + } + + struct Iterator { + + int idx; + int available; + const std::vector& data; + + /** ctor */ + Iterator(const int idx, const int available, const std::vector& data) : idx(idx), available(available), data(data) {;} + + bool operator != (const Iterator& other) {return other.available != available;} + void operator ++ () {idx = (idx+1) % data.size(); --available;} + const Element& operator * () const {return data[idx];} + + + }; + + + Iterator begin() const {return Iterator(iRead, available, data);} + Iterator end() const {return Iterator(iWrite, 0, data);} + +}; + +#endif // RINGBUFFER_H diff --git a/exception.h b/exception.h new file mode 100755 index 0000000..0bd0fc8 --- /dev/null +++ b/exception.h @@ -0,0 +1,26 @@ +#ifndef EXCEPTION_H +#define EXCEPTION_H + +#include +#include + +class Exception : public std::exception { + +private: + + std::string msg; + +public: + + /** ctor */ + Exception(const std::string& msg) : msg(msg) { + ; + } + + const char* what() const throw() { + return msg.c_str(); + } + +}; + +#endif // EXCEPTION_H diff --git a/gstbeatdetector.c b/gstbeatdetector.c new file mode 100755 index 0000000..6ac5811 --- /dev/null +++ b/gstbeatdetector.c @@ -0,0 +1,253 @@ +/* GStreamer + * Copyright (C) 2017 FIXME + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Suite 500, + * Boston, MA 02110-1335, USA. + */ +/** + * SECTION:element-gstbeatdetector + * + * The beatdetector element does FIXME stuff. + * + * + * Example launch line + * |[ + * gst-launch -v fakesrc ! beatdetector ! FIXME ! fakesink + * ]| + * FIXME Describe what the pipeline does. + * + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include "gstbeatdetector.h" + +GST_DEBUG_CATEGORY_STATIC (gst_beatdetector_debug_category); +#define GST_CAT_DEFAULT gst_beatdetector_debug_category + +/* prototypes */ + +static GstCaps *gst_beatdetector_transform_caps (GstBaseTransform * trans, + GstPadDirection direction, GstCaps * caps); +static void +gst_beatdetector_fixate_caps (GstBaseTransform * trans, + GstPadDirection direction, GstCaps * caps, GstCaps * othercaps); +static gboolean +gst_beatdetector_transform_size (GstBaseTransform * trans, + GstPadDirection direction, + GstCaps * caps, guint size, GstCaps * othercaps, guint * othersize); +static gboolean +gst_beatdetector_get_unit_size (GstBaseTransform * trans, GstCaps * caps, + guint * size); +static gboolean +gst_beatdetector_set_caps (GstBaseTransform * trans, GstCaps * incaps, + GstCaps * outcaps); +static gboolean gst_beatdetector_start (GstBaseTransform * trans); +static gboolean gst_beatdetector_stop (GstBaseTransform * trans); +static gboolean gst_beatdetector_event (GstBaseTransform * trans, GstEvent * event); +static GstFlowReturn +gst_beatdetector_transform (GstBaseTransform * trans, GstBuffer * inbuf, + GstBuffer * outbuf); +static GstFlowReturn +gst_beatdetector_transform_ip (GstBaseTransform * trans, GstBuffer * buf); +static GstFlowReturn +gst_beatdetector_prepare_output_buffer (GstBaseTransform * trans, + GstBuffer * input, gint size, GstCaps * caps, GstBuffer ** buf); +static gboolean +gst_beatdetector_src_event (GstBaseTransform * trans, GstEvent * event); +static void +gst_beatdetector_before_transform (GstBaseTransform * trans, GstBuffer * buffer); + +enum +{ + PROP_0 +}; + +/* pad templates */ + + +/* class initialization */ + +#define DEBUG_INIT(bla) \ + GST_DEBUG_CATEGORY_INIT (gst_beatdetector_debug_category, "beatdetector", 0, \ + "debug category for beatdetector element"); + +GST_BOILERPLATE_FULL (GstBeatDetector, gst_beatdetector, GstBaseTransform, + GST_TYPE_BASE_TRANSFORM, DEBUG_INIT); + +static void +gst_beatdetector_base_init (gpointer g_class) +{ + GstElementClass *element_class = GST_ELEMENT_CLASS (g_class); + + + gst_element_class_set_details_simple (element_class, "FIXME Long name", + "Generic", "FIXME Description", "FIXME "); +} + +static void +gst_beatdetector_class_init (GstBeatDetectorClass * klass) +{ + GstBaseTransformClass *base_transform_class = GST_BASE_TRANSFORM_CLASS (klass); + + base_transform_class->transform_caps = GST_DEBUG_FUNCPTR (gst_beatdetector_transform_caps); + base_transform_class->fixate_caps = GST_DEBUG_FUNCPTR (gst_beatdetector_fixate_caps); + base_transform_class->transform_size = GST_DEBUG_FUNCPTR (gst_beatdetector_transform_size); + base_transform_class->get_unit_size = GST_DEBUG_FUNCPTR (gst_beatdetector_get_unit_size); + base_transform_class->set_caps = GST_DEBUG_FUNCPTR (gst_beatdetector_set_caps); + base_transform_class->start = GST_DEBUG_FUNCPTR (gst_beatdetector_start); + base_transform_class->stop = GST_DEBUG_FUNCPTR (gst_beatdetector_stop); + base_transform_class->event = GST_DEBUG_FUNCPTR (gst_beatdetector_event); + base_transform_class->transform = GST_DEBUG_FUNCPTR (gst_beatdetector_transform); + base_transform_class->transform_ip = GST_DEBUG_FUNCPTR (gst_beatdetector_transform_ip); + base_transform_class->prepare_output_buffer = GST_DEBUG_FUNCPTR (gst_beatdetector_prepare_output_buffer); + base_transform_class->src_event = GST_DEBUG_FUNCPTR (gst_beatdetector_src_event); + base_transform_class->before_transform = GST_DEBUG_FUNCPTR (gst_beatdetector_before_transform); + +} + +static void +gst_beatdetector_init (GstBeatDetector * beatdetector, GstBeatDetectorClass * beatdetector_class) +{ +} + +static GstCaps * +gst_beatdetector_transform_caps (GstBaseTransform * trans, + GstPadDirection direction, GstCaps * caps) +{ + + return NULL; +} + +static void +gst_beatdetector_fixate_caps (GstBaseTransform * trans, + GstPadDirection direction, GstCaps * caps, GstCaps * othercaps) +{ + +} + +static gboolean +gst_beatdetector_transform_size (GstBaseTransform * trans, + GstPadDirection direction, + GstCaps * caps, guint size, GstCaps * othercaps, guint * othersize) +{ + + return FALSE; +} + +static gboolean +gst_beatdetector_get_unit_size (GstBaseTransform * trans, GstCaps * caps, + guint * size) +{ + + return FALSE; +} + +static gboolean +gst_beatdetector_set_caps (GstBaseTransform * trans, GstCaps * incaps, + GstCaps * outcaps) +{ + + return FALSE; +} + +static gboolean +gst_beatdetector_start (GstBaseTransform * trans) +{ + + return FALSE; +} + +static gboolean +gst_beatdetector_stop (GstBaseTransform * trans) +{ + + return FALSE; +} + +static gboolean +gst_beatdetector_event (GstBaseTransform * trans, GstEvent * event) +{ + + return FALSE; +} + +static GstFlowReturn +gst_beatdetector_transform (GstBaseTransform * trans, GstBuffer * inbuf, + GstBuffer * outbuf) +{ + + return GST_FLOW_ERROR; +} + +static GstFlowReturn +gst_beatdetector_transform_ip (GstBaseTransform * trans, GstBuffer * buf) +{ + + return GST_FLOW_ERROR; +} + +static GstFlowReturn +gst_beatdetector_prepare_output_buffer (GstBaseTransform * trans, + GstBuffer * input, gint size, GstCaps * caps, GstBuffer ** buf) +{ + + return GST_FLOW_ERROR; +} + +static gboolean +gst_beatdetector_src_event (GstBaseTransform * trans, GstEvent * event) +{ + + return FALSE; +} + +static void +gst_beatdetector_before_transform (GstBaseTransform * trans, GstBuffer * buffer) +{ + +} + +static gboolean +plugin_init (GstPlugin * plugin) +{ + + return gst_element_register (plugin, "beatdetector", GST_RANK_NONE, + GST_TYPE_BEATDETECTOR); +} + +#ifndef VERSION +#define VERSION "0.0.FIXME" +#endif +#ifndef PACKAGE +#define PACKAGE "FIXME_package" +#endif +#ifndef PACKAGE_NAME +#define PACKAGE_NAME "FIXME_package_name" +#endif +#ifndef GST_PACKAGE_ORIGIN +#define GST_PACKAGE_ORIGIN "http://FIXME.org/" +#endif + +GST_PLUGIN_DEFINE (GST_VERSION_MAJOR, + GST_VERSION_MINOR, + "beatdetector", + "FIXME plugin description", + plugin_init, VERSION, "LGPL", PACKAGE_NAME, GST_PACKAGE_ORIGIN) + diff --git a/gstbeatdetector.h b/gstbeatdetector.h new file mode 100755 index 0000000..68b26fa --- /dev/null +++ b/gstbeatdetector.h @@ -0,0 +1,51 @@ +/* GStreamer + * Copyright (C) 2017 FIXME + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef _GST_BEATDETECTOR_H_ +#define _GST_BEATDETECTOR_H_ + +#include + +G_BEGIN_DECLS + +#define GST_TYPE_BEATDETECTOR (gst_beatdetector_get_type()) +#define GST_BEATDETECTOR(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_BEATDETECTOR,GstBeatDetector)) +#define GST_BEATDETECTOR_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_BEATDETECTOR,GstBeatDetectorClass)) +#define GST_IS_BEATDETECTOR(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_BEATDETECTOR)) +#define GST_IS_BEATDETECTOR_CLASS(obj) (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_BEATDETECTOR)) + +typedef struct _GstBeatDetector GstBeatDetector; +typedef struct _GstBeatDetectorClass GstBeatDetectorClass; + +struct _GstBeatDetector +{ + GstBaseTransform base_beatdetector; + +}; + +struct _GstBeatDetectorClass +{ + GstBaseTransformClass base_beatdetector_class; +}; + +GType gst_beatdetector_get_type (void); + +G_END_DECLS + +#endif diff --git a/main.cpp b/main.cpp new file mode 100755 index 0000000..394b06a --- /dev/null +++ b/main.cpp @@ -0,0 +1,35 @@ +#include "pulseaudio.h" + +#include "BeatDetection2.h" + + +int main(int argc, char *argv[]) { + + PulseAudio pa; + //pa.join(); + + int16_t buf[1024]; + + BeatDetection2 bestBass(Mode::BASS); + BeatDetection2 beatSnare(Mode::SNARE); + + while(true) { + + pa.read(buf, 1024); + + for (int i = 0; i < 1024; ++i) { + const float left = buf[i]; + const float right = buf[i]; + + const bool bBass = bestBass.add(left, right); + if (bBass) {std::cout << "bass\n" << std::flush;} + + const bool bSnare = beatSnare.add(left, right); + if (bSnare) {std::cout << "snare...\n" << std::flush;} + + } + + } + + +} diff --git a/plugin.cpp b/plugin.cpp new file mode 100755 index 0000000..3f01f01 --- /dev/null +++ b/plugin.cpp @@ -0,0 +1,170 @@ +#include +#include +#include +#include +#include + +#include "plugin.h" + + +#include "BeatDetection.h" +static BeatDetection beatDetection; + +#include "BassDetection.h" +static BassDetection bassDetection; + +#include "BeatDetection2.h" +static BeatDetection2 beatDetection2; + +GST_DEBUG_CATEGORY_STATIC (beat_detector_debug); +#define GST_CAT_DEFAULT beat_detector_debug + +static GstStaticPadTemplate sink_template_factory = +GST_STATIC_PAD_TEMPLATE ("sink", + GST_PAD_SINK, + GST_PAD_ALWAYS, + GST_STATIC_CAPS ("audio/x-raw, " + "format = (string) " GST_AUDIO_NE (S16) ", " + "rate = (int) [ 1, MAX ], " + "channels = (int) [ 1, MAX ]" + ) +); + +static GstStaticPadTemplate src_template_factory = +GST_STATIC_PAD_TEMPLATE ("src", + GST_PAD_SRC, + GST_PAD_ALWAYS, + GST_STATIC_CAPS ("audio/x-raw, " + "format = (string) " GST_AUDIO_NE (S16) ", " + "rate = (int) [ 1, MAX ], " + "channels = (int) [ 1, MAX ]" + ) +); + +#define gst_beat_detector_parent_class parent_class +G_DEFINE_TYPE (GstBeatDetector, gst_beat_detector, GST_TYPE_BASE_TRANSFORM); + + + +static void gst_beat_detector_finalize(GObject* obj); + + + +static gboolean gst_level_set_caps (GstBaseTransform* trans, GstCaps* in, GstCaps* out) { + GstBeatDetector* filter = GST_BEAT_DETECTOR(trans); + + return gst_audio_info_from_caps(&filter->info, in); +} + + + + + + + +static GstFlowReturn gst_beat_detector_transform_ip(GstBaseTransform* trans, GstBuffer* in) { + + GstBeatDetector *filter = GST_BEAT_DETECTOR(trans); + GstMapInfo map; + gst_buffer_map (in, &map, GST_MAP_READ); + + gint channels = GST_AUDIO_INFO_CHANNELS (&filter->info); + gint bps = GST_AUDIO_INFO_BPS (&filter->info); + //gint rate = GST_AUDIO_INFO_RATE (&filter->info); + guint num_samples = map.size / bps; + //guint num_frames = num_samples / channels; + + //std::cout << channels << " " << bps << " " << rate << std::endl; + //std::cout << filter->info.layout << std::endl; + + // map signed-16-bit data + const int16_t* data = (const int16_t*) map.data; + + static size_t samples = 0; ++samples; + + // process 2 interleaved channels + for (guint i = 0; i < num_samples; i += 2) { + + const int left = data[i+0]; + const int right = data[i+1]; + const int sample = (left+right)/2; + + //const bool beat = beatDetection.add(sample); + //if (beat) {std::cout << "beat" << i << std::endl;} + + //const float res = bassDetection.add(sample); + //if (res > 0) {std::cout << res << std::endl;} + + const bool beat = beatDetection2.add(left, right); + if (beat) {std::cout << "beat" << i << std::endl << std::flush;} + + + } + + // done + return GST_FLOW_OK; + +} + + + + + + +static void gst_beat_detector_class_init(GstBeatDetectorClass* klass) { + std::cout << "hallo!" << std::endl; + GObjectClass* gobject_class = G_OBJECT_CLASS (klass); + GstElementClass* element_class = GST_ELEMENT_CLASS (klass); + GstBaseTransformClass* trans_class = GST_BASE_TRANSFORM_CLASS (klass); + + gobject_class->finalize = gst_beat_detector_finalize; + + GST_DEBUG_CATEGORY_INIT (beat_detector_debug, "beat_detector", 0, "Beat detection"); + + gst_element_class_add_static_pad_template (element_class, &sink_template_factory); + gst_element_class_add_static_pad_template (element_class, &src_template_factory); + gst_element_class_set_static_metadata (element_class, "BeatDetector", + "Filter/Analyzer/Audio", + "BeatDetector operating of a stream of audio/raw", + "Unknown"); + + trans_class->transform_ip = GST_DEBUG_FUNCPTR (gst_beat_detector_transform_ip); + trans_class->set_caps = GST_DEBUG_FUNCPTR (gst_level_set_caps); + + + +} + +static void gst_beat_detector_init(GstBeatDetector* filter) { + + gst_audio_info_init (&filter->info); + std::cout << "Init audio info." << std::endl; + gst_base_transform_set_gap_aware (GST_BASE_TRANSFORM (filter), TRUE); + +// filterBass.setLowPass(150, 1.5, 44100); +// filterSnare.setBandPass(5000, 1.5, 44100); + beatDetection.setSampleRate(44100); + bassDetection.setSampleRate(44100); + beatDetection2.setSampleRate(44100); + +} + +static void gst_beat_detector_finalize(GObject* obj) { + //GstBeatDetector *filter = GST_BEAT_DETECTOR (obj); +} + + + + + +/* REGISTER PLUGIN */ + +static gboolean plugin_init(GstPlugin* plugin) { + return gst_element_register(plugin, "beat_detector", GST_RANK_NONE, GST_TYPE_BEAT_DETECTOR); +} + +GST_PLUGIN_DEFINE (GST_VERSION_MAJOR, + GST_VERSION_MINOR, + beat_detector, + "Audio beat detecting plugin", + plugin_init, VERSION, GST_LICENSE, GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN); diff --git a/plugin.h b/plugin.h new file mode 100755 index 0000000..bc15468 --- /dev/null +++ b/plugin.h @@ -0,0 +1,89 @@ +/* GStreamer + * Copyright (C) 1999 Erik Walthinsen + * Copyright (C) 2000,2001,2002,2003,2005 + * Thomas Vander Stichele + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + + +#ifndef __GST_BEAT_DETECTOR_H__ +#define __GST_BEAT_DETECTOR_H__ + +#define VERSION "1" +#define GST_PACKAGE_NAME "package" +#define GST_PACKAGE_ORIGIN "unknown" +#define GST_LICENSE "LGPL" +#define PACKAGE "gst-beat-detector" + +#include +#include +#include + +G_BEGIN_DECLS + + +#define GST_TYPE_BEAT_DETECTOR \ + (gst_beat_detector_get_type()) +#define GST_BEAT_DETECTOR(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_BEAT_DETECTOR,GstBeatDetector)) +#define GST_BEAT_DETECTOR_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_BEAT_DETECTOR,GstBeatDetectorClass)) +#define GST_BEAT_DETECTOR_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS((obj),GST_TYPE_BEAT_DETECTOR,GstBeatDetectorClass)) +#define GST_IS_BEAT_DETECTOR(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_BEAT_DETECTOR)) +#define GST_IS_BEAT_DETECTOR_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_BEAT_DETECTOR)) + + +typedef struct _GstBeatDetector GstBeatDetector; +typedef struct _GstBeatDetectorClass GstBeatDetectorClass; + +/** + * GstBeatDetector: + * + * Opaque data structure. + */ +struct _GstBeatDetector { + GstBaseTransform element; + + /* properties */ + gboolean post_messages; /* whether or not to post messages */ + guint64 interval; /* how many nanoseconds between emits */ + gdouble decay_peak_ttl; /* time to live for peak in nanoseconds */ + gdouble decay_peak_falloff; /* falloff in dB/sec */ + + GstAudioInfo info; + gint num_frames; /* frame count (1 sample per channel) + * since last emit */ + gint interval_frames; /* after how many frame to sent a message */ + GstClockTime message_ts; /* starttime for next message */ + + void (*process)(gpointer, guint, guint, gdouble*, gdouble*); +}; + +struct _GstBeatDetectorClass { + GstBaseTransformClass parent_class; +}; + +GType gst_beat_detector_get_type (void); + + +G_END_DECLS + + +#endif /* __GST_BEAT_DETECTOR_H__ */ diff --git a/pulseaudio.h b/pulseaudio.h new file mode 100755 index 0000000..e5aa870 --- /dev/null +++ b/pulseaudio.h @@ -0,0 +1,107 @@ +#ifndef PULSEAUDIO_H +#define PULSEAUDIO_H + +#include +#include +#include +#include "exception.h" +#include + +/** + * receive events from pulse audio + */ +class PulseAudio { + +private: + + pa_sample_spec sampleSpec; + pa_simple* simple; + std::thread thread; + +public: + + /** ctor */ + PulseAudio() { + + sampleSpec.format = PA_SAMPLE_S16LE; + sampleSpec.rate = 44100; + sampleSpec.channels = 1; + + // pactl list | grep "\.monitor" + + const char* server = nullptr; + const char* clientName = "beat detection"; + const char* streamName = "recording beats"; + const char* dev = "alsa_output.pci-0000_00_1b.0.analog-stereo.monitor"; //nullptr; + //const char* dev = "alsa_output.usb-0d8c_USB_Sound_Device-00-Device.analog-surround-51.monitor"; + + // connect + int error; + simple = pa_simple_new(server, clientName, PA_STREAM_RECORD, dev, streamName, &sampleSpec, NULL, NULL, &error); + if (!simple) {throw Exception("error while connecting to PulseAudio");} + std::cout << simple << std::endl; + + + + } + + void start() { + std::cout << "starting read loop" << std::endl; + thread = std::thread(&PulseAudio::readLoop, this); + } + + void join() { + thread.join(); + } + + void read(int16_t* dst, int samples) { + const int bytes = samples * sizeof(int16_t); + int error; + const int res = pa_simple_read(simple, dst, bytes, &error); + if (res < 0) {throw Exception("error while reading data");} + } + +private: + + void readLoop() { + + std::cout << "hello from read loop" << std::endl; + int16_t buffer[1024]; + int error = 0; + + while(true) { + + const int res = pa_simple_read(simple, buffer, sizeof(buffer), &error); + std::cout << res << ", " << error << std::endl; + if (res < 0) {throw Exception("error while reading data");} + + std::cout << buffer[0] << "," << buffer[1] << "," << buffer[2] << "," << buffer[3] << std::endl; + + } + + } + + /* + int i = -1; + + + while (!exit_program) { + i = (i+1) % BUFNUMBER; + + pthread_mutex_lock(&(buffer[i].write)); + // Record data and save it to the buffer + if (pa_simple_read(s, buffer[i].buf, sizeof(buffer[i].buf), &error) < 0) { + fprintf(stderr, __FILE__": pa_simple_read() failed: %s\n", pa_strerror(error)); + pa_simple_free(s); + exit(EXIT_FAILURE); + } + + // unlock the reading mutex + pthread_mutex_unlock(&(buffer[i].read)); // open up for reading + + } +*/ + +}; + +#endif // PULSEAUDIO_H