initial commit

This commit is contained in:
kazu
2017-07-15 15:52:05 +02:00
parent 087def3773
commit fb0ff1c076
16 changed files with 1857 additions and 1 deletions

99
BassDetection.h Executable file
View 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
View 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
View 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
View 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
View 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
View 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
View 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

View File

@@ -1,2 +1,3 @@
# BeatDetector # BeatDetector
- plugin for gstreamer
- binary reading from pulseaudio

107
RingBuffer.h Executable file
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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