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
|
||||
|
||||
- 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