current version.. forgot to commit
This commit is contained in:
114
lib/BassDetection.h
Executable file
114
lib/BassDetection.h
Executable file
@@ -0,0 +1,114 @@
|
||||
#ifndef BASSDETECTION_H
|
||||
#define BASSDETECTION_H
|
||||
|
||||
//#define PLOT_ME
|
||||
|
||||
#ifdef PLOT_ME
|
||||
#include <KLib/misc/gnuplot/Gnuplot.h>
|
||||
#include <KLib/misc/gnuplot/GnuplotPlot.h>
|
||||
#include <KLib/misc/gnuplot/GnuplotPlotElementLines.h>
|
||||
#include <KLib/misc/gnuplot/GnuplotSplot.h>
|
||||
#endif
|
||||
|
||||
#include "BiquadFilterGate.h"
|
||||
#include "MovingAVG.h"
|
||||
#include "EndlessAVG.h"
|
||||
|
||||
#include <iostream>
|
||||
|
||||
template <typename Scalar> class BassDetection {
|
||||
|
||||
#ifdef PLOT_ME
|
||||
K::Gnuplot gp;
|
||||
K::GnuplotPlot plot;
|
||||
K::GnuplotPlotElementLines lines0;
|
||||
#endif
|
||||
|
||||
BiquadFilterGate<1> filter;
|
||||
MovingAVG<float> avg;
|
||||
MovingAVG<float> avgLong;
|
||||
|
||||
public:
|
||||
|
||||
/** setup */
|
||||
BassDetection() : avg(2500), avgLong(100000) {
|
||||
setSampleRate(44100);
|
||||
#ifdef PLOT_ME
|
||||
plot.add(&lines0);
|
||||
#endif
|
||||
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;
|
||||
|
||||
#ifdef PLOT_ME
|
||||
lines0.add(K::GnuplotPoint2(x, delta));
|
||||
#endif
|
||||
|
||||
// debug view?
|
||||
//if (x % 6000 == 0) {show();}
|
||||
#ifdef PLOT_ME
|
||||
show();
|
||||
#endif
|
||||
|
||||
if (x % 1000 == 0) {
|
||||
return delta;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
#ifdef PLOT_ME
|
||||
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
|
||||
|
||||
};
|
||||
|
||||
#endif // BASSDETECTION_H
|
||||
189
lib/BeatDetection.h
Executable file
189
lib/BeatDetection.h
Executable file
@@ -0,0 +1,189 @@
|
||||
#ifndef ANALYZER_H
|
||||
#define ANALYZER_H
|
||||
|
||||
//#define PLOT_ME
|
||||
|
||||
#ifdef PLOT_ME
|
||||
#include <KLib/misc/gnuplot/Gnuplot.h>
|
||||
#include <KLib/misc/gnuplot/GnuplotPlot.h>
|
||||
#include <KLib/misc/gnuplot/GnuplotPlotElementLines.h>
|
||||
#include <KLib/misc/gnuplot/GnuplotSplot.h>
|
||||
#endif
|
||||
|
||||
#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 {
|
||||
|
||||
#ifdef PLOT_ME
|
||||
K::Gnuplot gp;
|
||||
K::GnuplotPlot plot;
|
||||
K::GnuplotPlotElementLines lines0;
|
||||
#endif
|
||||
|
||||
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);
|
||||
#ifdef PLOT_ME
|
||||
plot.add(&lines0);
|
||||
#endif
|
||||
}
|
||||
|
||||
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;
|
||||
// }
|
||||
|
||||
#ifdef PLOT_ME
|
||||
if (xxx % 8 == 0) {
|
||||
lines0.add(K::GnuplotPoint2(x, yy));
|
||||
}
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
// debug view?
|
||||
#ifdef PLOT_ME
|
||||
if (xxx % 5000 == 0) {show();}
|
||||
#endif
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
#ifdef PLOT_ME
|
||||
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
|
||||
|
||||
};
|
||||
|
||||
#endif // ANALYZER_H
|
||||
244
lib/BeatDetection2.h
Executable file
244
lib/BeatDetection2.h
Executable file
@@ -0,0 +1,244 @@
|
||||
#ifndef BEATDETECTION2_H
|
||||
#define BEATDETECTION2_H
|
||||
|
||||
|
||||
|
||||
|
||||
#include "BiquadFilterGate.h"
|
||||
#include "MovingAVG.h"
|
||||
#include "EndlessAVG.h"
|
||||
|
||||
#include <iostream>
|
||||
|
||||
//#define PLOT_ME
|
||||
|
||||
#ifdef PLOT_ME
|
||||
#include <KLib/misc/gnuplot/Gnuplot.h>
|
||||
#include <KLib/misc/gnuplot/GnuplotPlot.h>
|
||||
#include <KLib/misc/gnuplot/GnuplotPlotElementLines.h>
|
||||
#include <KLib/misc/gnuplot/GnuplotSplot.h>
|
||||
#endif
|
||||
|
||||
#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 = 8; // 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
|
||||
316
lib/BiquadFilterGate.h
Executable file
316
lib/BiquadFilterGate.h
Executable file
@@ -0,0 +1,316 @@
|
||||
#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;
|
||||
|
||||
#include <cstring>
|
||||
#include <cmath>
|
||||
#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
|
||||
38
lib/EndlessAVG.h
Executable file
38
lib/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
lib/MovingAVG.h
Executable file
63
lib/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
|
||||
216
lib/NetworkAddress.h
Normal file
216
lib/NetworkAddress.h
Normal file
@@ -0,0 +1,216 @@
|
||||
#ifndef NETWORKADDRESS_H
|
||||
#define NETWORKADDRESS_H
|
||||
|
||||
#ifndef K_SOCKETS_NETWORKADDRESS_H
|
||||
#define K_SOCKETS_NETWORKADDRESS_H
|
||||
|
||||
#include "../exception.h"
|
||||
|
||||
#include <cstring>
|
||||
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
#include <unistd.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <netdb.h>
|
||||
|
||||
|
||||
class NetworkAddress {
|
||||
|
||||
public:
|
||||
|
||||
/**
|
||||
* @brief create NetworkAddress from the given socket address. This will
|
||||
* mainly be used for incoming data frames. The ctor re-calculuates the
|
||||
* host-ip and port-number from the given address
|
||||
* @param address the socket address to parse
|
||||
*/
|
||||
NetworkAddress(const struct sockaddr_in& address) : port(0), sockAddr(address) {
|
||||
port = ntohs(sockAddr.sin_port);
|
||||
ipFromAddressStruct();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief create a new NetworkAddress identified by hostname and port-number
|
||||
* @param host the host to identify (e.g. "127.0.0.1" or "google.de")
|
||||
* @param port the 16-bit port-number to use
|
||||
*/
|
||||
NetworkAddress(const std::string& host, const uint16_t port) : port(port) {
|
||||
|
||||
// convert hostname to ip
|
||||
struct hostent* he = gethostbyname( host.c_str() );
|
||||
if (!he) {throw Exception("error while retrieving IP for hostname: '" + host + "'");}
|
||||
|
||||
// sanity checks
|
||||
// https://www.cs.rutgers.edu/~pxk/417/notes/sockets/udp.html
|
||||
|
||||
// build address struct
|
||||
memset( &sockAddr, 0, sizeof(sockAddr) );
|
||||
sockAddr.sin_family = AF_INET;
|
||||
//sockAddr.sin_addr = *((struct in_addr*)he->h_addr);
|
||||
sockAddr.sin_port = htons(port);
|
||||
memcpy((void*)&sockAddr.sin_addr, he->h_addr_list[0], he->h_length);
|
||||
|
||||
ipFromAddressStruct();
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief create a new NetworkAddress identified by the given port-number and ANY host-name
|
||||
* @param port the 16-bit port-number to use
|
||||
*/
|
||||
NetworkAddress(const uint16_t port) : port(port) {
|
||||
|
||||
// build address struct
|
||||
memset( &sockAddr, 0, sizeof(sockAddr) );
|
||||
sockAddr.sin_family = AF_INET;
|
||||
sockAddr.sin_addr.s_addr = htonl(INADDR_ANY);
|
||||
sockAddr.sin_port = htons(port);
|
||||
|
||||
ipFromAddressStruct();
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief create a new NetworkAddress matching ANY host and ANY port
|
||||
* e.g. used for receiving all UDP-datagrams sent to any local port / interface
|
||||
*/
|
||||
NetworkAddress() : port(0) {
|
||||
|
||||
// build address struct
|
||||
memset( &sockAddr, 0, sizeof(sockAddr) );
|
||||
sockAddr.sin_family = AF_INET;
|
||||
sockAddr.sin_addr.s_addr = htonl(INADDR_ANY);
|
||||
sockAddr.sin_port = htons(0);
|
||||
|
||||
ipFromAddressStruct();
|
||||
|
||||
}
|
||||
|
||||
|
||||
/** copy ctor */
|
||||
NetworkAddress(const NetworkAddress& other) :
|
||||
port(other.port), sockAddr(other.sockAddr) {
|
||||
|
||||
ipFromAddressStruct();
|
||||
|
||||
}
|
||||
|
||||
/** get a NetworkAddress to broadcast to the given port */
|
||||
static NetworkAddress getForBroadcast(const uint16_t port) {
|
||||
NetworkAddress adr;
|
||||
memset(&adr.sockAddr, 0, sizeof(adr.sockAddr));
|
||||
adr.sockAddr.sin_family = AF_INET;
|
||||
adr.sockAddr.sin_addr.s_addr = htonl(INADDR_BROADCAST);
|
||||
adr.sockAddr.sin_port = htons(port);
|
||||
adr.port = port;
|
||||
return adr;
|
||||
}
|
||||
|
||||
// /** assignment operator */
|
||||
// void operator = (const NetworkAddress& other) {
|
||||
// this->hostName = other.hostName;
|
||||
// this->hostIP = other.hostIP;
|
||||
// this->port = other.port;
|
||||
// this->sockAddr = other.sockAddr;
|
||||
// }
|
||||
|
||||
|
||||
/** dtor */
|
||||
~NetworkAddress() {
|
||||
;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/** get the network address as sockaddr_in struct */
|
||||
const struct sockaddr_in& getAsSocketAddress() const {
|
||||
return sockAddr;
|
||||
}
|
||||
|
||||
// /** get the remote's port */
|
||||
// uint16_t getPort() const {
|
||||
// return port;
|
||||
// }
|
||||
|
||||
// /** get the host's IP as string */
|
||||
// const std::string& getHostIP() const {
|
||||
// return hostIP;
|
||||
// }
|
||||
|
||||
// /** get the host's name as string */
|
||||
// const std::string& getHostName() {
|
||||
// if (hostName.empty()) { hostNameFromAddressStruct(); }
|
||||
// return hostName;
|
||||
// }
|
||||
|
||||
|
||||
|
||||
/** is the internal port valid for an outbound packet? */
|
||||
bool isValidTargetPort() const {
|
||||
return port != 0;
|
||||
}
|
||||
|
||||
/** is the internal ip valid for an outbound packet? */
|
||||
bool isValidTargetHost() const {
|
||||
return sockAddr.sin_addr.s_addr != htonl(INADDR_ANY);
|
||||
}
|
||||
|
||||
|
||||
|
||||
// /** check whether both NetworkAddresses are equal */
|
||||
// bool operator == (const NetworkAddress& other) const {
|
||||
// return memcmp( &this->sockAddr, &other.sockAddr, sizeof(sockAddr)) == 0;
|
||||
// }
|
||||
|
||||
|
||||
|
||||
private:
|
||||
|
||||
/** fill the hostIP string from the address struct */
|
||||
void ipFromAddressStruct() {
|
||||
|
||||
uint32_t ip = sockAddr.sin_addr.s_addr;
|
||||
hostIP = std::to_string( (ip>> 0) & 0xFF ) + "." +
|
||||
std::to_string( (ip>> 8) & 0xFF ) + "." +
|
||||
std::to_string( (ip>>16) & 0xFF ) + "." +
|
||||
std::to_string( (ip>>24) & 0xFF );
|
||||
|
||||
}
|
||||
|
||||
// /** fill the hostName string via reverse-lookup of the address struct */
|
||||
// void hostNameFromAddressStruct() {
|
||||
|
||||
// char hostNameBuf[128];
|
||||
// const int flags = 0;
|
||||
|
||||
// // reverse lookup
|
||||
// getnameinfo( (struct sockaddr*) &sockAddr, sizeof(sockAddr), hostNameBuf, 128, nullptr, 0, flags );
|
||||
// this->hostName = std::string(hostNameBuf);
|
||||
|
||||
// }
|
||||
|
||||
|
||||
|
||||
private:
|
||||
|
||||
/** the host's ip as string */
|
||||
std::string hostIP;
|
||||
|
||||
/** the host's name as string */
|
||||
std::string hostName;
|
||||
|
||||
/** the port-number from the ctor */
|
||||
uint16_t port;
|
||||
|
||||
/** the resulting socket-address-struct */
|
||||
struct sockaddr_in sockAddr;
|
||||
|
||||
};
|
||||
|
||||
|
||||
#endif // K_SOCKETS_NETWORKADDRESS_H
|
||||
|
||||
|
||||
#endif // NETWORKADDRESS_H
|
||||
107
lib/RingBuffer.h
Executable file
107
lib/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
|
||||
199
lib/Socket.h
Normal file
199
lib/Socket.h
Normal file
@@ -0,0 +1,199 @@
|
||||
#ifndef SOCKET_H
|
||||
#define SOCKET_H
|
||||
|
||||
#include <sys/socket.h>
|
||||
#include <vector>
|
||||
#include <errno.h>
|
||||
#include <netinet/in.h>
|
||||
|
||||
#include "NetworkAddress.h"
|
||||
#include "../exception.h"
|
||||
|
||||
class SocketUDP {
|
||||
|
||||
private:
|
||||
|
||||
static constexpr int MAX_DATAGRAM_SIZE = 64*1024;
|
||||
|
||||
bool bound = false;
|
||||
|
||||
public:
|
||||
|
||||
/** ctor */
|
||||
SocketUDP() : handle(0) {
|
||||
|
||||
// create socket
|
||||
handle = socket(AF_INET, SOCK_DGRAM, 0);
|
||||
if (handle == -1) {throw Exception("error while creating socket");}
|
||||
|
||||
}
|
||||
|
||||
/** dtor */
|
||||
~SocketUDP() {
|
||||
close();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief bind the socket to receive all datagrams sent to the given (local) port
|
||||
* a localPort of 0 will let the OS determine a free one.
|
||||
* @param localPort the local endpoint for datagrams sent from a remote
|
||||
*/
|
||||
void bind(const uint16_t localPort) {
|
||||
|
||||
if (bound) {throw Exception("socket already bound!");}
|
||||
|
||||
// local endpoint AF_INET struct
|
||||
struct sockaddr_in srvAddr;
|
||||
memset((char*)&srvAddr, 0, sizeof(srvAddr));
|
||||
srvAddr.sin_family = AF_INET;
|
||||
srvAddr.sin_addr.s_addr = htonl(INADDR_ANY); // every interface
|
||||
srvAddr.sin_port = htons(localPort);
|
||||
|
||||
// bind the socket to the given port
|
||||
int ret = ::bind(handle, (struct sockaddr*) &srvAddr, sizeof(srvAddr));
|
||||
if (ret < 0) {throw Exception("error while binding socket");}
|
||||
|
||||
// mark as bound
|
||||
bound = true;
|
||||
|
||||
}
|
||||
|
||||
/** close the socket */
|
||||
void close() {
|
||||
|
||||
// cleanup
|
||||
if (handle) {
|
||||
::close(handle);
|
||||
handle = 0;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// /** is the socket currently closed? */
|
||||
// bool isClosed() const {
|
||||
// return handle == 0;
|
||||
// }
|
||||
|
||||
// /** allow to broadcast packets using this socket? */
|
||||
// void allowBroadcast(const bool allow) {
|
||||
|
||||
// #if defined(__GNUC__)
|
||||
// const int broadcastEnable = (allow) ? (1) : (0);
|
||||
// const int ret = setsockopt(handle, SOL_SOCKET, SO_BROADCAST, &broadcastEnable, sizeof(broadcastEnable));
|
||||
// if (ret < 0) { throw SocketException("error while enabling broadcast", errno); }
|
||||
// #elif defined(_WIN32)
|
||||
// ;
|
||||
// #endif
|
||||
|
||||
// }
|
||||
|
||||
/**
|
||||
* @brief send a datagram to the given destination address
|
||||
* @param data the data to send
|
||||
* @param len the length of the data to send (max 64k!)
|
||||
* @param addr the destination address
|
||||
*/
|
||||
void sendDatagram(const uint8_t* data, uint32_t len, const NetworkAddress& addr) {
|
||||
|
||||
// sanity check
|
||||
if (!bound) {throw Exception("bind() the socket first!");}
|
||||
|
||||
// ensure max datagram size
|
||||
if (len > MAX_DATAGRAM_SIZE) {throw Exception("max datagram size is " + std::to_string(MAX_DATAGRAM_SIZE)+ " bytes!");}
|
||||
|
||||
// ensure correct destination address
|
||||
if (!addr.isValidTargetPort()) {throw Exception("the given destination address has no valid port number");}
|
||||
if (!addr.isValidTargetHost()) {throw Exception("the given destination address has no valid hostname");}
|
||||
|
||||
// send datagram to the given destionation
|
||||
const struct sockaddr_in& sockAddr = addr.getAsSocketAddress();
|
||||
const int options = 0;
|
||||
const int res = sendto(handle, (const char*)data, len, options, (struct sockaddr*) &sockAddr, sizeof(sockaddr));
|
||||
|
||||
// check
|
||||
if (res < 0) {throw Exception("error while sending datagram");}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief send a datagram to the given destination address
|
||||
* @param data the data to send (max 64k!)
|
||||
* @param addr the destination address
|
||||
*/
|
||||
void sendDatagram(const std::vector<uint8_t>& data, const NetworkAddress& addr) {
|
||||
sendDatagram(data.data(), (uint32_t) data.size(), addr);
|
||||
}
|
||||
|
||||
void sendDatagram(const std::string& data, const NetworkAddress& addr) {
|
||||
sendDatagram((const uint8_t*)data.data(), data.length(), addr);
|
||||
}
|
||||
|
||||
// /**
|
||||
// * @brief send the given datagram to its internal destination address
|
||||
// * @param d the datagram to send
|
||||
// */
|
||||
// void sendDatagram(const Datagram& d) {
|
||||
// sendDatagram(d.getData(), d.getLength(), d.getAddress());
|
||||
// }
|
||||
|
||||
|
||||
// /**
|
||||
// * @brief convenience function for receiveDatagram(d) which returns a new
|
||||
// * datagram instead of reusing an existing one.
|
||||
// * @return a newly created datagram holding the received message
|
||||
// */
|
||||
// DefaultDatagram receiveDatagram() {
|
||||
// DefaultDatagram dd;
|
||||
// receiveDatagram(dd);
|
||||
// return dd;
|
||||
// }
|
||||
|
||||
// /**
|
||||
// * @brief this method will block until a new datagram is received on the bound
|
||||
// * port of the underlying socket. the received data will be stored within the
|
||||
// * given datagram
|
||||
// * @param d the buffer to store the received datagram to
|
||||
// */
|
||||
// void receiveDatagram(Datagram& d) {
|
||||
|
||||
// const int flags = 0;
|
||||
// const int maxSize = MAX_DATAGRAM_SIZE;
|
||||
|
||||
// // sanity check
|
||||
// if (!bound) {throw SocketException("bind() the socket first!");}
|
||||
|
||||
// // ensure the target buffer may hold the largest possible datagram
|
||||
// d.ensureSpace(maxSize);
|
||||
|
||||
// // the datagrams sender will be stored here
|
||||
// struct sockaddr_in senderAddr;
|
||||
// socklen_t senderLength = sizeof(senderAddr);
|
||||
|
||||
// // receive datagram from socket and store sender information
|
||||
// int len = recvfrom(handle, (char*) d.getDataWriteable(), maxSize, flags, (struct sockaddr*) &senderAddr, &senderLength);
|
||||
// if (len < 0) {throw SocketException("error while receiving datagram", errno);}
|
||||
|
||||
// // store senders network-address in the datagram
|
||||
// NetworkAddress na(senderAddr);
|
||||
// d.setAddress(na);
|
||||
|
||||
// // inform buffer about the actual size of the datagram
|
||||
// d.setLength( (uint32_t) len );
|
||||
|
||||
// }
|
||||
|
||||
|
||||
|
||||
private:
|
||||
|
||||
/** the socket handle */
|
||||
int handle;
|
||||
|
||||
/** the local address the socket is bound to */
|
||||
NetworkAddress localAddress;
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
#endif // SOCKET_H
|
||||
Reference in New Issue
Block a user