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