Created
July 13, 2020 15:08
-
-
Save jix/53464e37ad68ceb34e7e573aa79e9c1c to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <pulse/simple.h> | |
#include <pulse/error.h> | |
#include <array> | |
#include <stdexcept> | |
#include <memory> | |
#include <iostream> | |
#include <complex> | |
#include <functional> | |
#include <GLFW/glfw3.h> | |
#include <fftw3.h> | |
/* | |
* compile using g++ --std=c++11 hellbert.cpp -o hellbert -lpulse -lpulse-simple -lglfw -lGL -lfftw3 | |
*/ | |
class scope_guard { | |
std::function<void()> function; | |
public: | |
scope_guard(std::function<void()> f) : function(std::move(f)) {} | |
scope_guard(scope_guard const &other) = delete; | |
~scope_guard() { function(); } | |
}; | |
using namespace std; | |
constexpr unsigned buffersize = 1 << 11; | |
constexpr unsigned skip = 1 << 6; | |
constexpr unsigned highpass = 2; | |
constexpr unsigned samplerate = 96000; | |
static_assert(2 * skip < buffersize, ""); | |
static_assert(2 * highpass < buffersize, ""); | |
typedef float color[4]; | |
int main(int argc, char *argv[]) | |
{ | |
try { | |
char * dev = argc >= 2 ? argv[1] : nullptr; //NULL; | |
int error; | |
pa_simple *s; | |
pa_sample_spec ss; | |
ss.format = PA_SAMPLE_FLOAT32NE; | |
ss.channels = 1; | |
ss.rate = samplerate; | |
s = pa_simple_new(NULL, | |
"Hellbert", | |
PA_STREAM_RECORD, | |
dev, | |
"Hellbert input", | |
&ss, | |
NULL, | |
NULL, | |
&error); | |
if (s == nullptr) | |
throw runtime_error(string("couldn't initialize pulseaudio: ") + pa_strerror(error)); | |
scope_guard free_pulse_audio([&]{pa_simple_free(s);}); | |
if(!glfwInit()) | |
throw runtime_error("couldn't initialize GLFW"); | |
scope_guard deinit_glfw(glfwTerminate); | |
unique_ptr<float[]> buffer(new float[buffersize]); | |
unique_ptr<float[]> window(new float[buffersize]); | |
unique_ptr<complex<double>[]> cbuffer(new complex<double>[buffersize]); | |
unique_ptr<complex<double>[]> fbuffer(new complex<double>[buffersize]); | |
unique_ptr<color[]> colors(new color[buffersize]); | |
for (unsigned i = 0; i < buffersize; i++) { | |
window[i] = 0.54 - 0.46 * cos(2 * M_PI * i / buffersize); | |
} | |
scope_guard cleanup_fftw(fftw_cleanup); | |
fftw_plan analyze = fftw_plan_dft_1d(buffersize, | |
reinterpret_cast<fftw_complex*>(cbuffer.get()), | |
reinterpret_cast<fftw_complex*>(fbuffer.get()), | |
FFTW_FORWARD, FFTW_DESTROY_INPUT); | |
fftw_plan synthesize = fftw_plan_dft_1d(buffersize, | |
reinterpret_cast<fftw_complex*>(fbuffer.get()), | |
reinterpret_cast<fftw_complex*>(cbuffer.get()), | |
FFTW_BACKWARD, FFTW_DESTROY_INPUT); | |
auto win = glfwCreateWindow(512, 512, "Hellbert", nullptr, nullptr); | |
glfwMakeContextCurrent(win); | |
if(!win) | |
throw runtime_error("couldn't open GLFW window"); | |
glClearColor(0,0,0,0); | |
float alpha = 0.3; | |
glColor4f(0.5 * alpha, alpha, 0, alpha); | |
glEnableClientState(GL_VERTEX_ARRAY); | |
glEnableClientState(GL_COLOR_ARRAY); | |
glEnable(GL_BLEND); | |
glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); | |
glLineWidth(2); | |
for (unsigned i = 0; i < buffersize; i++) { | |
float t = static_cast<float>(i) / buffersize; | |
float alpha = 0.3; | |
float a = 0.5+0.5*sin(15 * 2 * M_PI * t); | |
float b = 0.5+0.5*cos(15 * 2 * M_PI * t); | |
colors[i][0] = alpha * 0.7 * a; | |
colors[i][1] = alpha * (b + 0.3 * a); | |
colors[i][2] = 0; | |
colors[i][3] = alpha; | |
} | |
float scale = 1.0; | |
while (true) { | |
scale *= 0.99; | |
double startTime = glfwGetTime(); | |
if (pa_simple_read(s, buffer.get(), sizeof(float) * buffersize, &error) < 0) | |
throw runtime_error(string("couldn't read input stream: ") + pa_strerror(error)); | |
double endTime = glfwGetTime(); | |
if (endTime - startTime > 0.5 / samplerate * buffersize) { | |
int width, height; | |
glfwGetWindowSize(win, &width, &height); | |
glViewport(0, 0, width, height); | |
for (unsigned i = 0; i < buffersize; i++) { | |
scale = max(scale, abs(buffer[i])); | |
} | |
for (unsigned i = 0; i < buffersize; i++) { | |
cbuffer[i] = buffer[i] / buffersize / scale; | |
} | |
double energy = 0.0; | |
fftw_execute(analyze); | |
for (unsigned i = 0; i < buffersize; i++) { | |
energy += abs(fbuffer[i]); | |
} | |
for (unsigned i = 0; i < highpass; i++) { | |
fbuffer[i] = 0; | |
} | |
for (unsigned i = buffersize / 2; i < buffersize; i++) { | |
fbuffer[i] = 0; | |
} | |
fftw_execute(synthesize); | |
for (unsigned i = 0; i < buffersize; i++) { | |
// cbuffer[i] /= window[i]; | |
} | |
glClear(GL_COLOR_BUFFER_BIT); | |
glVertexPointer(2, GL_DOUBLE, 0, cbuffer.get()); | |
glColorPointer(4, GL_FLOAT, 0, colors.get()); | |
glDrawArrays(GL_LINE_STRIP, skip, buffersize - 2 * skip); | |
glfwSwapBuffers(win); | |
if (glfwWindowShouldClose(win)) | |
break; | |
} | |
} | |
} catch (std::exception &e) { | |
cerr << "error: " << e.what() << endl; | |
exit(1); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment