initial commit
This commit is contained in:
99
BassDetection.h
Executable file
99
BassDetection.h
Executable file
@@ -0,0 +1,99 @@
|
|||||||
|
#ifndef BASSDETECTION_H
|
||||||
|
#define BASSDETECTION_H
|
||||||
|
|
||||||
|
#include <KLib/misc/gnuplot/Gnuplot.h>
|
||||||
|
#include <KLib/misc/gnuplot/GnuplotPlot.h>
|
||||||
|
#include <KLib/misc/gnuplot/GnuplotPlotElementLines.h>
|
||||||
|
#include <KLib/misc/gnuplot/GnuplotSplot.h>
|
||||||
|
|
||||||
|
#include "BiquadFilterGate.h"
|
||||||
|
#include "MovingAVG.h"
|
||||||
|
#include "EndlessAVG.h"
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
template <typename Scalar> class BassDetection {
|
||||||
|
|
||||||
|
K::Gnuplot gp;
|
||||||
|
K::GnuplotPlot plot;
|
||||||
|
K::GnuplotPlotElementLines lines0;
|
||||||
|
|
||||||
|
BiquadFilterGate<1> filter;
|
||||||
|
MovingAVG<float> avg;
|
||||||
|
MovingAVG<float> 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
|
||||||
175
BeatDetection.h
Executable file
175
BeatDetection.h
Executable file
@@ -0,0 +1,175 @@
|
|||||||
|
#ifndef ANALYZER_H
|
||||||
|
#define ANALYZER_H
|
||||||
|
|
||||||
|
#include <KLib/misc/gnuplot/Gnuplot.h>
|
||||||
|
#include <KLib/misc/gnuplot/GnuplotPlot.h>
|
||||||
|
#include <KLib/misc/gnuplot/GnuplotPlotElementLines.h>
|
||||||
|
#include <KLib/misc/gnuplot/GnuplotSplot.h>
|
||||||
|
|
||||||
|
#include "BiquadFilterGate.h"
|
||||||
|
#include "MovingAVG.h"
|
||||||
|
#include "EndlessAVG.h"
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
|
||||||
|
template <typename Scalar> struct BeatBand {
|
||||||
|
|
||||||
|
BiquadFilterGate<1> filter;
|
||||||
|
MovingAVG<Scalar> avgShort;
|
||||||
|
MovingAVG<Scalar> avgLong;
|
||||||
|
EndlessAVG<Scalar> 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 <typename Scalar> class BeatDetection {
|
||||||
|
|
||||||
|
K::Gnuplot gp;
|
||||||
|
K::GnuplotPlot plot;
|
||||||
|
K::GnuplotPlotElementLines lines0;
|
||||||
|
|
||||||
|
BeatBand<float> band0;
|
||||||
|
BeatBand<float> band1;
|
||||||
|
BeatBand<float> band2;
|
||||||
|
BeatBand<float> band3;
|
||||||
|
|
||||||
|
MovingAVG<float> avg1;
|
||||||
|
MovingAVG<float> 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
|
||||||
240
BeatDetection2.h
Executable file
240
BeatDetection2.h
Executable file
@@ -0,0 +1,240 @@
|
|||||||
|
#ifndef BEATDETECTION2_H
|
||||||
|
#define BEATDETECTION2_H
|
||||||
|
|
||||||
|
|
||||||
|
#include <KLib/misc/gnuplot/Gnuplot.h>
|
||||||
|
#include <KLib/misc/gnuplot/GnuplotPlot.h>
|
||||||
|
#include <KLib/misc/gnuplot/GnuplotPlotElementLines.h>
|
||||||
|
#include <KLib/misc/gnuplot/GnuplotSplot.h>
|
||||||
|
|
||||||
|
#include "BiquadFilterGate.h"
|
||||||
|
#include "MovingAVG.h"
|
||||||
|
#include "EndlessAVG.h"
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
//#define PLOT_ME
|
||||||
|
|
||||||
|
#define BD2_SHORT 1024
|
||||||
|
|
||||||
|
template <typename Scalar> struct BeatBand2 {
|
||||||
|
|
||||||
|
BiquadFilterGate<2> filter;
|
||||||
|
MovingAVG<float> avgShort;
|
||||||
|
MovingAVG<float> avgLong;
|
||||||
|
MovingAVG<float> avgLongSquared;
|
||||||
|
MovingAVG<float> avgDiff;
|
||||||
|
MovingAVG<float> 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 <typename Scalar> 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<float> 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
|
||||||
314
BiquadFilterGate.h
Executable file
314
BiquadFilterGate.h
Executable file
@@ -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 <int channels> 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
|
||||||
88
CMakeLists.txt
Executable file
88
CMakeLists.txt
Executable file
@@ -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
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
38
EndlessAVG.h
Executable file
38
EndlessAVG.h
Executable file
@@ -0,0 +1,38 @@
|
|||||||
|
#ifndef ENDLESSAVG_H
|
||||||
|
#define ENDLESSAVG_H
|
||||||
|
|
||||||
|
template <typename T> 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
|
||||||
63
MovingAVG.h
Executable file
63
MovingAVG.h
Executable file
@@ -0,0 +1,63 @@
|
|||||||
|
#ifndef MOVINGAVG_H
|
||||||
|
#define MOVINGAVG_H
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
#include "RingBuffer.h"
|
||||||
|
|
||||||
|
template <typename T> 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<T> 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
|
||||||
@@ -1,2 +1,3 @@
|
|||||||
# BeatDetector
|
# BeatDetector
|
||||||
|
- plugin for gstreamer
|
||||||
|
- binary reading from pulseaudio
|
||||||
|
|||||||
107
RingBuffer.h
Executable file
107
RingBuffer.h
Executable file
@@ -0,0 +1,107 @@
|
|||||||
|
#ifndef RINGBUFFER_H
|
||||||
|
#define RINGBUFFER_H
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
|
||||||
|
template <typename Element> class RingBuffer {
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
std::vector<Element> 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<Element>& data;
|
||||||
|
|
||||||
|
/** ctor */
|
||||||
|
Iterator(const int idx, const int available, const std::vector<Element>& 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
|
||||||
26
exception.h
Executable file
26
exception.h
Executable file
@@ -0,0 +1,26 @@
|
|||||||
|
#ifndef EXCEPTION_H
|
||||||
|
#define EXCEPTION_H
|
||||||
|
|
||||||
|
#include <exception>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
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
|
||||||
253
gstbeatdetector.c
Executable file
253
gstbeatdetector.c
Executable file
@@ -0,0 +1,253 @@
|
|||||||
|
/* GStreamer
|
||||||
|
* Copyright (C) 2017 FIXME <fixme@example.com>
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*
|
||||||
|
* <refsect2>
|
||||||
|
* <title>Example launch line</title>
|
||||||
|
* |[
|
||||||
|
* gst-launch -v fakesrc ! beatdetector ! FIXME ! fakesink
|
||||||
|
* ]|
|
||||||
|
* FIXME Describe what the pipeline does.
|
||||||
|
* </refsect2>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifdef HAVE_CONFIG_H
|
||||||
|
#include "config.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <gst/gst.h>
|
||||||
|
#include <gst/base/gstbasetransform.h>
|
||||||
|
#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 <fixme@example.com>");
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
|
||||||
51
gstbeatdetector.h
Executable file
51
gstbeatdetector.h
Executable file
@@ -0,0 +1,51 @@
|
|||||||
|
/* GStreamer
|
||||||
|
* Copyright (C) 2017 FIXME <fixme@example.com>
|
||||||
|
*
|
||||||
|
* 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 <gst/base/gstbasetransform.h>
|
||||||
|
|
||||||
|
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
|
||||||
35
main.cpp
Executable file
35
main.cpp
Executable file
@@ -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<float> bestBass(Mode::BASS);
|
||||||
|
BeatDetection2<float> 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;}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
170
plugin.cpp
Executable file
170
plugin.cpp
Executable file
@@ -0,0 +1,170 @@
|
|||||||
|
#include <string.h>
|
||||||
|
#include <math.h>
|
||||||
|
#include <gst/gst.h>
|
||||||
|
#include <gst/audio/audio.h>
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
#include "plugin.h"
|
||||||
|
|
||||||
|
|
||||||
|
#include "BeatDetection.h"
|
||||||
|
static BeatDetection<float> beatDetection;
|
||||||
|
|
||||||
|
#include "BassDetection.h"
|
||||||
|
static BassDetection<float> bassDetection;
|
||||||
|
|
||||||
|
#include "BeatDetection2.h"
|
||||||
|
static BeatDetection2<float> 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);
|
||||||
89
plugin.h
Executable file
89
plugin.h
Executable file
@@ -0,0 +1,89 @@
|
|||||||
|
/* GStreamer
|
||||||
|
* Copyright (C) 1999 Erik Walthinsen <omega@cse.ogi.edu>
|
||||||
|
* Copyright (C) 2000,2001,2002,2003,2005
|
||||||
|
* Thomas Vander Stichele <thomas at apestaart dot org>
|
||||||
|
*
|
||||||
|
* 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 <gst/gst.h>
|
||||||
|
#include <gst/base/gstbasetransform.h>
|
||||||
|
#include <gst/audio/audio.h>
|
||||||
|
|
||||||
|
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__ */
|
||||||
107
pulseaudio.h
Executable file
107
pulseaudio.h
Executable file
@@ -0,0 +1,107 @@
|
|||||||
|
#ifndef PULSEAUDIO_H
|
||||||
|
#define PULSEAUDIO_H
|
||||||
|
|
||||||
|
#include <pulse/pulseaudio.h>
|
||||||
|
#include <pulse/simple.h>
|
||||||
|
#include <iostream>
|
||||||
|
#include "exception.h"
|
||||||
|
#include <thread>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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
|
||||||
Reference in New Issue
Block a user