Compare commits

..

8 commits

Author SHA1 Message Date
72f3e51b7b
Add libftw3, receive audio input and perform fft
This receives the audio input, performs the fft on it and has all the nescessary
preparations for then binding the audio data to the 2d texture for the GLSL.

Signed-off-by: Tyrolyean <tyrolyean@tyrolyean.net>
2022-07-19 00:23:43 +02:00
7e190a5729
Add data structures and functions
Signed-off-by: Tyrolyean <tyrolyean@tyrolyean.net>
2022-07-18 19:56:18 +02:00
84595eff00
Auto-set pipe size according to frame size, add iSampleRate uniform
The pipe size is now set automatically to avoid unnescessary buffering of frames
which aren't supposes to be there yet as well as preventing partial reads and
writes when larger resolutuions are used.

The iSampleRate uniform was added according to shadertoy spec.

Signed-off-by: Tyrolyean <tyrolyean@tyrolyean.net>
2022-07-18 00:59:36 +02:00
f0ffbc7c33
Set to newer c-standard and add source code fortification
The fortify source code was added to prevent builds from passing but then
failing in our build system and the c-standard was set to c11 to avoid the
compiler yelling at me whenever i want to print a size_t.

Signed-off-by: Tyrolyean <tyrolyean@tyrolyean.net>
2022-07-18 00:58:11 +02:00
47bbca6806
Add fix for output being O_NONBLOCK
This tries to remove the O_NONBLOCK flag from an output FD if the output seems
to be setup this way.

Signed-off-by: Tyrolyean <tyrolyean@tyrolyean.net>
2022-07-18 00:06:34 +02:00
51f808d12f
Revert "Add fix for O_NONBLOCK file-descriptor as output pipe"
This reverts commit be8f7d1716.

This was reverted as with this commit the program would consume large amounts
of cpu time just looping and calling the write function, which would not block.
2022-07-17 23:53:44 +02:00
75cdd01de8
Add sampling rate for audio input and data structures
Signed-off-by: Tyrolyean <tyrolyean@tyrolyean.net>
2022-07-17 23:52:56 +02:00
be8f7d1716
Add fix for O_NONBLOCK file-descriptor as output pipe
If the output of an fd is a pipe with O_NONBLOCK set, the script will die as
soon as the pipe fills up due to the write call returning with EAGAIN. This
fixes that.

Signed-off-by: Tyrolyean <tyrolyean@tyrolyean.net>
2022-07-17 20:03:32 +02:00
9 changed files with 174 additions and 640 deletions

View file

@ -1,5 +1,5 @@
CFLAGS = -std=c11 -pedantic -Wall -Wextra -O2 -Ofast -D_DEFAULT_SOURCE CFLAGS = -std=c11 -pedantic -Wall -Wextra -O2 -Ofast -D_DEFAULT_SOURCE -D_FORTIFY_SOURCE=2
LDFLAGS = -lX11 -lGL -lGLEW -lfftw3 -lm -lrt LDFLAGS = -lX11 -lGL -lGLEW -lm -lrt -lfftw3
shadermeh: shadermeh.o window.o shader.o shadermeh: shadermeh.o window.o shader.o

View file

@ -31,7 +31,6 @@ static const char *frag_src_pre =
"uniform vec4 iDate;\n" "uniform vec4 iDate;\n"
"uniform float iSampleRate;\n" "uniform float iSampleRate;\n"
"uniform vec3 iChannelResolution[4];\n" "uniform vec3 iChannelResolution[4];\n"
"uniform sampler2D iChannel0;"
"\n"; "\n";
static const char *frag_src_post = static const char *frag_src_post =

View file

@ -1,10 +1,16 @@
/* SPDX-License-Identifier: ISC */ /* SPDX-License-Identifier: ISC */
/* /*
* shadermeh.c * shadermeh.c
* *
* Copyright (C) 2022 David Oberhollenzer <goliath@infraroot.at> * Copyright (C) 2022 David Oberhollenzer <goliath@infraroot.at>
*/ */
#include "shadermeh.h" #include "shadermeh.h"
#include <fcntl.h>
#include <errno.h>
#define HAVE_ARCH_STRUCT_FLOCK
#include <linux/fcntl.h>
#include <fftw3.h>
static GLfloat vertex_buffer[] = { static GLfloat vertex_buffer[] = {
-1.0f, -1.0f, 0.0f, /* lower left corner */ -1.0f, -1.0f, 0.0f, /* lower left corner */
@ -20,107 +26,6 @@ static GLfloat vertex_buffer[] = {
1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f,
}; };
static GLubyte audio_buffer[AUDIO_SAMPLES * AUDIO_CHANNELS];
size_t sample_pointer = 0;
size_t sample_data_pointer = 0;
size_t sample_rate = 0;
static float *audio_sample_data;
static float *audio_receive_data;
static double *fftw_in;
static fftw_complex *fftw_out;
static fftw_plan plan;
static int try_fetch_audio(float iTimeDelta)
{
/* To avoid generating stale images, we keep our own sample buffer,
* which is then used to move a sliding window of data for the fft and
* wave samples. We need to do this, as otherwise we would set an upper
* limit of fps (20 at 4800kHz sample rate), which would not be good.
* The size of the window is set in the header file. The with our
* approach is that the buffer allows for drifting to occur within the
* buffer limits. If you buffer is 3s long the delay can grow to 3s.
* Choose your buffer size wisely for your application.
*/
size_t i;
ssize_t ret = 0;
memset(audio_receive_data, 0, AUDIO_BUFFER_SIZE *
sizeof(*audio_receive_data));
sample_pointer += (sample_rate * iTimeDelta);
for (;;) {
ret = read(STDIN_FILENO, (char *)audio_receive_data,
sizeof(*audio_receive_data)*AUDIO_BUFFER_SIZE);
if (ret < 0) {
if (errno == EINTR)
continue;
if (errno == EAGAIN || errno == EWOULDBLOCK)
break;
perror("stdin");
return -1;
}
if (ret == 0 || ret % sizeof(float) != 0){
break;
}
ret /= 4;
if((ret + sample_pointer) > AUDIO_BUFFER_SIZE){
/* Not enough storage space to store all new audio data,
* will override not output data with new one */
memset(audio_sample_data, 0,
AUDIO_BUFFER_SIZE * sizeof(*audio_sample_data));
memcpy(audio_sample_data, audio_receive_data,
ret * sizeof(*audio_sample_data));
sample_pointer = 0;
sample_data_pointer = ret;
}else{
memmove(audio_sample_data,
&audio_sample_data[sample_pointer],
(AUDIO_BUFFER_SIZE - sample_pointer)*
sizeof(*audio_sample_data));
if(sample_data_pointer <= sample_pointer){
sample_data_pointer = 0;
}else{
sample_data_pointer -= sample_pointer;
}
sample_pointer = 0;
size_t len = ret;
if((ret + sample_data_pointer) >= AUDIO_BUFFER_SIZE){
len = AUDIO_BUFFER_SIZE - sample_data_pointer;
}
memcpy(&audio_sample_data[sample_data_pointer],
audio_receive_data, len * sizeof(float));
sample_data_pointer += len;
break;
}
}
if((sample_pointer+AUDIO_FFT_SIZE) >= sample_data_pointer){
fprintf(stderr, "shadermeh input to slow %zu > %zu! wrapping around!\n", sample_pointer+AUDIO_FFT_SIZE, sample_data_pointer);
sample_pointer = 0;
}
memset(fftw_in, 0, sizeof(*fftw_in) * AUDIO_BUFFER_SIZE);
memset(fftw_out, 0, sizeof(*fftw_out) * AUDIO_BUFFER_SIZE);
for (i = 0; i < AUDIO_FFT_SIZE; ++i)
fftw_in[i] = audio_sample_data[sample_pointer+i];
fftw_execute(plan);
for (i = 0; i < AUDIO_SAMPLES; ++i) {
float a = cabs(fftw_out[i]);
audio_buffer[i + AUDIO_SAMPLES] = audio_sample_data[sample_pointer+i] * 127.0f + 127.0f;
audio_buffer[i] = log(fabsf(a)+1) * 50;
}
return 0;
}
static double diff_timespec(const struct timespec *time1, static double diff_timespec(const struct timespec *time1,
const struct timespec *time0) const struct timespec *time0)
{ {
@ -146,55 +51,28 @@ static void convert_for_ffmpeg(const uint8_t *in, uint8_t *out,
} }
} }
static int wait_fd_event(int fd, int events)
{
struct pollfd pfd;
int ret;
for (;;) {
pfd.fd = fd;
pfd.events = events;
pfd.revents = 0;
ret = poll(&pfd, 1, -1);
if (ret > 0) {
if (pfd.revents & events)
break;
if (pfd.revents & (POLLERR | POLLHUP)) {
fputs("poll reported error\n", stderr);
return -1;
}
}
if (ret < 0) {
if (errno == EINTR)
continue;
perror("poll");
return -1;
}
}
return 0;
}
static int write_retry(int fd, const void *buffer, size_t size) static int write_retry(int fd, const void *buffer, size_t size)
{ {
while (size > 0) { while (size > 0) {
int ret = write(fd, buffer, size); int ret = write(fd, buffer, size);
if (ret < 0) { if (ret < 0) {
if (errno == EINTR) if (errno == EINTR){
continue; continue;
}else if(errno == EAGAIN){
if (errno == EAGAIN) { perror("Output pipe probably has O_NONBLOCK "
if (wait_fd_event(fd, POLLOUT)) "set! Removing setting");
ret = fcntl(STDOUT_FILENO, F_SETFL,
fcntl(STDOUT_FILENO,F_GETFL)
&(~O_NONBLOCK));
if(ret == -1){
perror("Failed to set STDOUT blocking");
return -1; return -1;
continue;
} }
continue;
}else{
perror("write"); perror("write");
}
return -1; return -1;
} }
@ -208,16 +86,71 @@ static int write_retry(int fd, const void *buffer, size_t size)
return 0; return 0;
} }
#define SND_BUFFER_SIZE 512
#define BBUFFER_S ((sizeof(float) * SND_BUFFER_SIZE))
/* Read all available input. This function presumes fd to be O_NONBLOCK */
static int read_audio_buffers(int fd, float buffer[SND_BUFFER_SIZE],
unsigned int sampling_rate){
float temp_buf[sampling_rate];
size_t n = 0;
memset(temp_buf, 0, sizeof(temp_buf));
while(true){
int new_n = read(fd, temp_buf, sizeof(temp_buf));
if(new_n < 0){
if(errno == EAGAIN || errno == EWOULDBLOCK){
break;
}
perror("Read to audio buffer failed");
return -1;
}else if(new_n == 0){
break;
} else if(n < sizeof(temp_buf)){
n = new_n;
break;
}
/* Fall through and read the remaining buffer
* (last ditch effort to clear pipe)
*/
}
if(n < BBUFFER_S){
memmove(buffer, buffer+n, BBUFFER_S - n);
memcpy(buffer+ BBUFFER_S - n, temp_buf, n);
}else{
memcpy(buffer, (temp_buf+n-BBUFFER_S), BBUFFER_S);
}
return 0;
}
/* normalize the given input buffer from -1…1 in range (what alsa provides) to
* 01 in range (what the shadertoy shaders */
static void normalize_audio_buffers(float buffer_in[SND_BUFFER_SIZE],
float buffer_out[SND_BUFFER_SIZE]){
if(buffer_in == NULL || buffer_out == NULL){
return;
}
for(size_t i = 0; i < SND_BUFFER_SIZE; i++){
buffer_out[i] = (buffer_in[i]+1.0)/2.0;
}
return;
}
static const struct option long_opts[] = { static const struct option long_opts[] = {
{ "width", required_argument, NULL, 'w' }, { "width", required_argument, NULL, 'w' },
{ "height", required_argument, NULL, 'h' }, { "height", required_argument, NULL, 'h' },
{ "shader", required_argument, NULL, 's' }, { "shader", required_argument, NULL, 's' },
{ "sampling-rate", required_argument, NULL, 'r' },
{ "to-stdout", no_argument, NULL, 'S' }, { "to-stdout", no_argument, NULL, 'S' },
{ "stdin-audio", required_argument, NULL, 'a' },
{ NULL, 0, NULL, 0 }, { NULL, 0, NULL, 0 },
}; };
static const char *short_opts = "w:a:h:s:S"; static const char *short_opts = "r:w:h:s:S";
static const char *usage_str = static const char *usage_str =
"shadermeh OPTIONS...\n" "shadermeh OPTIONS...\n"
@ -226,27 +159,40 @@ static const char *usage_str =
"\n" "\n"
" --width, -w <pixels>\n" " --width, -w <pixels>\n"
" --height, -h <pixels>\n" " --height, -h <pixels>\n"
" --sampling-rate, -r <sampling rate[Hz]>\n"
"\n" "\n"
" --to-stdout, -S Poop raw RGB24 frames to stdout (blocking)\n" " --to-stdout, -S\n"
" --stdin-audio, -a <sample rate> Read raw PCM audio from stdin (non-blocking)\n"
"\n" "\n"
" --shader, -s <shader file>\n" " --shader, -s <shader file>\n"
"\n"; "\n";
int main(int argc, char **argv) int main(int argc, char **argv)
{ {
GLuint vao, vbo, fbo, fbo_tex, sound_tex, sampler_sound;
GLuint u_iResolution, u_iTime, u_iTimeDelta, u_iFrame; GLuint u_iResolution, u_iTime, u_iTimeDelta, u_iFrame;
GLfloat u_iSampleRate;
struct timespec start, frame_start, frame_end; struct timespec start, frame_start, frame_end;
unsigned int width, height, iFrame = 0; unsigned int width, height, iFrame = 0;
void *fb32 = NULL, *fb24 = NULL; void *fb32 = NULL, *fb24 = NULL;
const char *shader_file = NULL; const char *shader_file = NULL;
GLuint vao, vbo, fbo, fbo_tex;
GLint major, minor, prog; GLint major, minor, prog;
float iTime, iTimeDelta = 0; float iTime, iTimeDelta;
bool have_audio = false;
bool to_stdout = false; bool to_stdout = false;
window *wnd; window *wnd;
int i; int i, ret;
unsigned int sampling_rate = 0; /* Leaving this at 0 disables audio
* input
*/
float in_samples[SND_BUFFER_SIZE]; /* Raw input floats from -1...1 */
float norm_samples[SND_BUFFER_SIZE]; /* Normalized samples from 0...1 */
fftw_complex fftw_in[SND_BUFFER_SIZE];
fftw_complex fftw_out[SND_BUFFER_SIZE];
fftw_plan plan = fftw_plan_dft_1d(SND_BUFFER_SIZE,
fftw_in,
fftw_out,
FFTW_FORWARD,
FFTW_ESTIMATE);
/******************** parse options ************************/ /******************** parse options ************************/
width = 800; width = 800;
@ -267,32 +213,18 @@ int main(int argc, char **argv)
case 's': case 's':
shader_file = optarg; shader_file = optarg;
break; break;
case 'r':
sampling_rate = strtol(optarg, NULL, 10);
break;
case 'S': case 'S':
to_stdout = true; to_stdout = true;
break; break;
case 'a':
have_audio = true;
sample_rate = strtol(optarg, NULL, 10);
audio_sample_data = malloc(AUDIO_BUFFER_SIZE *
sizeof(float));
audio_receive_data = malloc(AUDIO_BUFFER_SIZE *
sizeof(float));
break;
default: default:
fputs(usage_str, stderr); fputs(usage_str, stderr);
return EXIT_FAILURE; return EXIT_FAILURE;
} }
} }
if (have_audio) {
int flags = fcntl(STDIN_FILENO, F_GETFL, 0);
if (fcntl(STDIN_FILENO, F_SETFL, flags | O_NONBLOCK)) {
perror("making stdin non-blocking");
return EXIT_FAILURE;
}
}
if (!shader_file) { if (!shader_file) {
fputs(usage_str, stderr); fputs(usage_str, stderr);
fputs("No shader file specified!\n", stderr); fputs("No shader file specified!\n", stderr);
@ -312,6 +244,52 @@ int main(int argc, char **argv)
free(fb32); free(fb32);
return EXIT_FAILURE; return EXIT_FAILURE;
} }
if(!isatty(STDOUT_FILENO)){
long old_pipe_size = fcntl(STDOUT_FILENO, F_GETPIPE_SZ);
if(old_pipe_size < 0){
perror("Failed to get stdout pipe size");
}else{
int psz = getpagesize();
size_t frame_size = (width * height * 3);
size_t frame_cnt = psz / frame_size;
if((psz % frame_size) != 0){
frame_cnt++;
}
size_t new_pipe_size = frame_size * frame_cnt;
int fin_psz = fcntl(STDOUT_FILENO, F_SETPIPE_SZ,
new_pipe_size);
if(fin_psz < 0){
fprintf(stderr, "Failed to set STDOUT "
"pipe size to %zu: %s\n",
new_pipe_size, strerror(errno));
}else{
fprintf(stderr, "stdout pipe size "
"change from %li to %zu "
"resulted in %i\n",
old_pipe_size,
new_pipe_size,
fin_psz);
}
/* A failure to change pipe size is not
* catastrophic, beacause the application will
* still perform as needed, just with a bigger
* or smaller buffer. */
}
}
}
if (sampling_rate != 0 && isatty(STDIN_FILENO)){
fputs("Sampling rate specified and STDIN a tty! "
"You habe been warnded!\n", stderr);
}
if(sampling_rate != 0){
ret = fcntl(STDIN_FILENO, F_SETFL,
fcntl(STDIN_FILENO,F_GETFL)
|(O_NONBLOCK));
if(ret == -1){
perror("Failed to set STDIN_FILENO to non-blocking "
"mode! This will likely mean failure");
}
} }
/********** create window and make context current **********/ /********** create window and make context current **********/
@ -340,9 +318,7 @@ int main(int argc, char **argv)
glGetIntegerv(GL_MAJOR_VERSION, &major); glGetIntegerv(GL_MAJOR_VERSION, &major);
glGetIntegerv(GL_MINOR_VERSION, &minor); glGetIntegerv(GL_MINOR_VERSION, &minor);
fprintf(stderr,"OpenGL version %d.%d\n", major, minor);
if (!to_stdout)
printf("OpenGL version %d.%d\n", major, minor);
/******************** initialization ********************/ /******************** initialization ********************/
glViewport(0, 0, width, height); glViewport(0, 0, width, height);
@ -370,7 +346,6 @@ int main(int argc, char **argv)
if (!shader_program_get_build_status(prog)) { if (!shader_program_get_build_status(prog)) {
shader_program_print_info_log(prog); shader_program_print_info_log(prog);
goto fail_vao;
} }
glUseProgram(prog); glUseProgram(prog);
@ -379,34 +354,13 @@ int main(int argc, char **argv)
u_iResolution = glGetUniformLocation(prog, "iResolution"); u_iResolution = glGetUniformLocation(prog, "iResolution");
u_iTime = glGetUniformLocation(prog, "iTime"); u_iTime = glGetUniformLocation(prog, "iTime");
u_iTimeDelta = glGetUniformLocation(prog, "iTimeDelta"); u_iTimeDelta = glGetUniformLocation(prog, "iTimeDelta");
u_iFrame = glGetUniformLocation(prog, "iFrame"); u_iFrame = glGetUniformLocation(prog, "iFrame;");
u_iSampleRate = glGetUniformLocation(prog, "iSampleRate");
glUniform3f(u_iResolution, width, height, 0.0f); glUniform3f(u_iResolution, width, height, 0.0f);
glUniform1f(u_iSampleRate, sampling_rate);
glUniform1i(glGetUniformLocation(prog, "iChannel0"), 0); clock_gettime(CLOCK_MONOTONIC_RAW, &start);
/************************* textures *************************/
glGenTextures(1, &sound_tex);
glBindTexture(GL_TEXTURE_2D, sound_tex);
glTexImage2D(GL_TEXTURE_2D, 0, GL_R8,
AUDIO_SAMPLES, AUDIO_CHANNELS, 0,
GL_RED, GL_UNSIGNED_BYTE, audio_buffer);
glBindTexture(GL_TEXTURE_2D, 0);
glGenSamplers(1, &sampler_sound);
glSamplerParameteri(sampler_sound, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glSamplerParameteri(sampler_sound, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glBindSampler(0, sampler_sound);
if (have_audio) {
fftw_in = fftw_alloc_real(AUDIO_BUFFER_SIZE);
fftw_out = fftw_alloc_complex(AUDIO_BUFFER_SIZE);
if(fftw_in == NULL || fftw_out == NULL)
goto fail_vao;
plan = fftw_plan_dft_r2c_1d(AUDIO_BUFFER_SIZE, fftw_in, fftw_out,
FFTW_MEASURE);
}
/******************** framebuffer object ********************/ /******************** framebuffer object ********************/
if (to_stdout) { if (to_stdout) {
@ -416,42 +370,20 @@ int main(int argc, char **argv)
glBindTexture(GL_TEXTURE_2D, fbo_tex); glBindTexture(GL_TEXTURE_2D, fbo_tex);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0,
GL_RGBA, GL_UNSIGNED_BYTE, NULL); GL_RGBA, GL_UNSIGNED_BYTE, NULL);
glBindTexture(GL_TEXTURE_2D, 0);
glBindFramebuffer(GL_FRAMEBUFFER, fbo); glBindFramebuffer(GL_FRAMEBUFFER, fbo);
glFramebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, glFramebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
fbo_tex, 0); fbo_tex, 0);
glBindFramebuffer(GL_FRAMEBUFFER, 0);
} else { } else {
window_show(wnd); window_show(wnd);
} }
/******************** drawing loop ********************/ /******************** drawing loop ********************/
clock_gettime(CLOCK_MONOTONIC_RAW, &start);
while (to_stdout || window_handle_events()) { while (to_stdout || window_handle_events()) {
/* render image to FBO */ /* render image to FBO */
clock_gettime(CLOCK_MONOTONIC_RAW, &frame_start); clock_gettime(CLOCK_MONOTONIC_RAW, &frame_start);
if (to_stdout)
glBindFramebuffer(GL_FRAMEBUFFER, fbo);
glClear(GL_COLOR_BUFFER_BIT); glClear(GL_COLOR_BUFFER_BIT);
if (have_audio) {
if (try_fetch_audio(iTimeDelta))
break;
glBindTexture(GL_TEXTURE_2D, sound_tex);
glTexImage2D(GL_TEXTURE_2D, 0, GL_R8,
AUDIO_SAMPLES, AUDIO_CHANNELS, 0,
GL_RED, GL_UNSIGNED_BYTE, audio_buffer);
}
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
glBindTexture(GL_TEXTURE_2D, 0);
if (to_stdout) { if (to_stdout) {
glFlush(); glFlush();
@ -461,14 +393,9 @@ int main(int argc, char **argv)
/* get image from FBO, dump to stdout */ /* get image from FBO, dump to stdout */
if (to_stdout) { if (to_stdout) {
glBindFramebuffer(GL_FRAMEBUFFER, 0);
glBindTexture(GL_TEXTURE_2D, fbo_tex);
glGetTexImage(GL_TEXTURE_2D, 0, GL_RGBA, glGetTexImage(GL_TEXTURE_2D, 0, GL_RGBA,
GL_UNSIGNED_BYTE, fb32); GL_UNSIGNED_BYTE, fb32);
glBindTexture(GL_TEXTURE_2D, 0);
convert_for_ffmpeg(fb32, fb24, width, height); convert_for_ffmpeg(fb32, fb24, width, height);
if (write_retry(STDOUT_FILENO, fb24, if (write_retry(STDOUT_FILENO, fb24,
@ -476,6 +403,19 @@ int main(int argc, char **argv)
break; break;
} }
} }
if(sampling_rate != 0){
if(read_audio_buffers(STDIN_FILENO, in_samples,
sampling_rate) < 0){
break;
}
normalize_audio_buffers(in_samples, norm_samples);
memset(fftw_in, 0, sizeof(fftw_in));
memset(fftw_out, 0, sizeof(fftw_out));
for(size_t i = 0; i < SND_BUFFER_SIZE; i++){
fftw_in[i][0] = norm_samples[i];
}
fftw_execute(plan);
}
/* update timers */ /* update timers */
clock_gettime(CLOCK_MONOTONIC_RAW, &frame_end); clock_gettime(CLOCK_MONOTONIC_RAW, &frame_end);
@ -490,8 +430,12 @@ int main(int argc, char **argv)
} }
/******************** cleanup ********************/ /******************** cleanup ********************/
glDeleteTextures(1, &sound_tex); glUseProgram(0);
glDeleteSamplers(1, &sampler_sound); glBindBuffer(GL_ARRAY_BUFFER, 0);
glDeleteBuffers(1, &vbo);
glBindVertexArray(0);
glDeleteVertexArrays(1, &vao);
glDeleteProgram(prog);
if (to_stdout) { if (to_stdout) {
glBindFramebuffer(GL_FRAMEBUFFER, 0); glBindFramebuffer(GL_FRAMEBUFFER, 0);
@ -499,23 +443,10 @@ int main(int argc, char **argv)
glDeleteTextures(1, &fbo_tex); glDeleteTextures(1, &fbo_tex);
} }
glUseProgram(0);
glDeleteProgram(prog);
if (have_audio) {
fftw_destroy_plan(plan);
}
fail_vao:
glBindBuffer(GL_ARRAY_BUFFER, 0);
glDeleteBuffers(1, &vbo);
glBindVertexArray(0);
glDeleteVertexArrays(1, &vao);
window_make_current(NULL); window_make_current(NULL);
free(fb32); free(fb32);
free(fb24); free(fb24);
fftw_free(fftw_in);
fftw_free(fftw_out);
window_destroy(wnd); window_destroy(wnd);
fftw_destroy_plan(plan);
return EXIT_SUCCESS; return EXIT_SUCCESS;
} }

View file

@ -23,19 +23,8 @@
#include <unistd.h> #include <unistd.h>
#include <stdio.h> #include <stdio.h>
#include <errno.h> #include <errno.h>
#include <fcntl.h>
#include <poll.h>
#include <time.h> #include <time.h>
#include <math.h>
#include <complex.h>
#include <fftw3.h>
#define AUDIO_SAMPLES (4096)
#define AUDIO_BUFFER_SIZE (sample_rate * 3)
#define AUDIO_FFT_SIZE (AUDIO_SAMPLES * 2)
#define AUDIO_CHANNELS (2)
typedef struct { typedef struct {
Window wnd; Window wnd;
GLXContext gl; GLXContext gl;

View file

@ -1,84 +0,0 @@
// credit: https://www.shadertoy.com/view/4tGXzt
#define BEATMOVE 1
const float FREQ_RANGE = 64.0;
const float PI = 3.1415;
const float RADIUS = 0.6;
const float BRIGHTNESS = 0.2;
const float SPEED = 0.5;
//convert HSV to RGB
vec3 hsv2rgb(vec3 c){
vec4 K = vec4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0);
vec3 p = abs(fract(c.xxx + K.xyz) * 6.0 - K.www);
return c.z * mix(K.xxx, clamp(p - K.xxx, 0.0, 1.0), c.y);
}
float luma(vec3 color) {
return dot(color, vec3(0.299, 0.587, 0.114));
}
float getfrequency(float x) {
return texture(iChannel0, vec2(floor(x * FREQ_RANGE + 1.0) / FREQ_RANGE, 0.25)).x + 0.06;
}
float getfrequency_smooth(float x) {
float index = floor(x * FREQ_RANGE) / FREQ_RANGE;
float next = floor(x * FREQ_RANGE + 1.0) / FREQ_RANGE;
return mix(getfrequency(index), getfrequency(next), smoothstep(0.0, 1.0, fract(x * FREQ_RANGE)));
}
float getfrequency_blend(float x) {
return mix(getfrequency(x), getfrequency_smooth(x), 0.5);
}
vec3 doHalo(vec2 fragment, float radius) {
float dist = length(fragment);
float ring = 1.0 / abs(dist - radius);
float b = dist < radius ? BRIGHTNESS * 0.3 : BRIGHTNESS;
vec3 col = vec3(0.0);
float angle = atan(fragment.x, fragment.y);
col += hsv2rgb( vec3( ( angle + iTime * 0.25 ) / (PI * 2.0), 1.0, 1.0 ) ) * ring * b;
float frequency = max(getfrequency_blend(abs(angle / PI)) - 0.02, 0.0);
col *= frequency;
// Black halo
col *= smoothstep(radius * 0.5, radius, dist);
return col;
}
vec3 doLine(vec2 fragment, float radius, float x) {
vec3 col = hsv2rgb(vec3(x * 0.23 + iTime * 0.12, 1.0, 1.0));
float freq = abs(fragment.x * 0.5);
col *= (1.0 / abs(fragment.y)) * BRIGHTNESS * getfrequency(freq);
col = col * smoothstep(radius, radius * 1.8, abs(fragment.x));
return col;
}
void mainImage( out vec4 fragColor, in vec2 fragCoord ) {
vec2 fragPos = fragCoord / iResolution.xy;
fragPos = (fragPos - 0.5) * 2.0;
fragPos.x *= iResolution.x / iResolution.y;
vec3 color = vec3(0.0134, 0.052, 0.1);
color += doHalo(fragPos, RADIUS);
float c = cos(iTime * SPEED);
float s = sin(iTime * SPEED);
vec2 rot = mat2(c,s,-s,c) * fragPos;
color += doLine(rot, RADIUS, rot.x);
color += max(luma(color) - 1.0, 0.0);
fragColor = vec4(color, 1.0);
}

View file

@ -1,65 +0,0 @@
/*
Linear vs Logarithmic FFT
some good test songs:
https://soundcloud.com/kraddy/winning
https://soundcloud.com/grey-houston/soothing-piano-melody
https://soundcloud.com/pointpoint/life-in-gr
*/
//from https://stackoverflow.com/questions/35799286
float toLog(float value, float min, float max){
float exp = (value-min) / (max-min);
return min * pow(max/min, exp);
}
float getLevel(float samplePos){
// the sound texture is 512x2
int tx = int(samplePos*512.0);
// first row is frequency data (48Khz/4 in 512 texels, meaning 23 Hz per texel)
return texelFetch( iChannel0, ivec2(tx,0), 0 ).x;
}
void mainImage( out vec4 fragColor, in vec2 fragCoord )
{
vec2 uv = fragCoord.xy / iResolution.xy;
float xPos;
float fft;
if (uv.y > 0.5){
//linear sampling
xPos = uv.x;
fft = getLevel(xPos);
}else{
//crop bottom and top of range
uv.x = mix(0.3,0.7, uv.x);
//logarithmic sampling
xPos = toLog(uv.x, 0.01, 1.0);
fft = getLevel(xPos);
//boost contrast
fft = pow(fft,3.0);
//boost gain
fft *= 1.5;
//contrast / brightness
float contrast = 1.4;
float brightness = 0.;
fft = (fft - 0.5) * contrast + 0.5 + brightness;
}
fragColor = vec4(vec3(fft),1.0);
}

View file

@ -1,158 +0,0 @@
//https://www.shadertoy.com/view/XlBXRh with mic not soundcloud
#define preset4
#ifdef preset1
#define cells vec2(14.,14.)
#define persp 1.5
#define height 1.
#define linewidth .5
#define lineexp 4.
#define brightness .7
#endif
#ifdef preset2
#define cells vec2(10.,5.)
#define persp 2.5
#define height 1.
#define linewidth 3.
#define lineexp 6.
#define brightness .4
#endif
#ifdef preset3
#define OPAQUE_MODE
#define INVERSE
#define cells vec2(16.,16.)
#define persp 1.
#define height 1.5
#define linewidth .1
#define lineexp .5
#define brightness .8
#endif
#ifdef preset4
#define OPAQUE_MODE
#define cells vec2(10.,10.)
#define persp 2.
#define height .75
#define linewidth .2
#define lineexp 1.
#define brightness 1.5
#endif
#ifdef preset5
#define INVERSE
#define cells vec2(6.,25.)
#define persp 1.
#define height 2.
#define linewidth .07
#define lineexp .3
#define brightness .35
#endif
#ifdef preset6
#define INVERSE
#define OPAQUE_MODE
#define cells vec2(15.,15.)
#define persp 2.5
#define height 1.
#define linewidth .05
#define lineexp .5
#define brightness 1.
#endif
#define hcells (cells*.5)
vec3 segment(vec2 p, vec3 from, vec3 to, float width, float dist) {
width=1./width;
vec2 seg=from.xy-to.xy;
float halfdist=distance(from.xy,to.xy)*.5;
float ang=atan(seg.y,seg.x);
float sine=sin(ang);
float cose=cos(ang);
p-=from.xy;
p*=mat2(cose,sine,-sine,cose);
float dx=abs(p.x+halfdist)-halfdist;
float dy=abs(p.y);
float h=1.-abs(p.x+halfdist*2.)/halfdist/2.;
float pz=-from.z-(to.z-from.z)*h;
float l=1.-clamp(max(dx,dy)*width/(pz+dist)*dist*dist,0.,.1)/.1;
l=pow(abs(l),lineexp)*(1.-pow(clamp(abs(dist-pz)*.45,0.,1.),.5))*4.;
return normalize(.25+abs(mix(from,to,h)))*l;
}
mat3 rotmat(vec3 v, float angle)
{
angle=radians(angle);
float c = cos(angle);
float s = sin(angle);
return mat3(c + (1.0 - c) * v.x * v.x, (1.0 - c) * v.x * v.y - s * v.z, (1.0 - c) * v.x * v.z + s * v.y,
(1.0 - c) * v.x * v.y + s * v.z, c + (1.0 - c) * v.y * v.y, (1.0 - c) * v.y * v.z - s * v.x,
(1.0 - c) * v.x * v.z - s * v.y, (1.0 - c) * v.y * v.z + s * v.x, c + (1.0 - c) * v.z * v.z
);
}
float getz(vec2 xy) {
xy=xy*10.+hcells;
//float pos=length(pow(abs(xy/cells),vec2(3.)))*8.;
float pos=(xy.y*cells.x+xy.x)/(cells.x*cells.y);
float s=texture(iChannel0,vec2(.5+pos*.5,.1)).x;
return .25-pow(s,1.5)*height;
}
void mainImage( out vec4 fragColor, in vec2 fragCoord )
{
vec2 uv = (gl_FragCoord.xy / iResolution.xy-.5)*2.;
uv.y*=iResolution.y/iResolution.x;
mat3 camrot=rotmat(normalize(vec3(0.,0.,1.)),iTime*25.)*rotmat(normalize(vec3(1.,0.*sin(iTime*.5),0.)),60.+30.*sin(iTime*.5));
float s=.1,maxc=0.;
vec3 p1,p2,p3;
vec3 rotv=vec3(0.,0.,1.);
float h;
vec3 col=vec3(0.);
float dist=1.2+pow(abs(sin(iTime*.3)),5.)*.5;
vec3 c=vec3(0.);
for (float y=0.; y<cells.y; y++) {
for (float x=0.; x<cells.x; x++) {
p1=vec3(x-hcells.x,y-hcells.y,0.)*.1; p1.z=getz(p1.xy);
p2=vec3(p1.x+.1,p1.y ,0.); p2.z=getz(p2.xy);
p3=vec3(p1.x ,p1.y+.1,0.); p3.z=getz(p3.xy);
p1*=camrot; p2*=camrot; p3*=camrot;
p1.xy*=persp/max(0.1,p1.z+dist);
p2.xy*=persp/max(0.1,p2.z+dist);
p3.xy*=persp/max(0.1,p3.z+dist);
if (max(p1.x,p2.x)>uv.x-linewidth/4. && min(p1.x,p2.x)<uv.x+linewidth/4. && x<cells.x-1.) {
if (max(p1.y,p2.y)>uv.y-linewidth/4. && min(p1.y,p2.y)<uv.y+linewidth/4.) {
#ifdef OPAQUE_MODE
c=max(c,segment(uv,p1,p2,linewidth,dist)*1.5);
#else
c+=segment(uv,p1,p2,linewidth,dist);
#endif
}
}
if (max(p1.x,p3.x)>uv.x-linewidth/4. && min(p1.x,p3.x)<uv.x+linewidth/4. && y<cells.y-1.) {
if (max(p1.y,p3.y)>uv.y-linewidth/4. && min(p1.y,p3.y)<uv.y+linewidth/4.) {
#ifdef OPAQUE_MODE
c=max(c,segment(uv,p1,p3,linewidth,dist)*1.5);
#else
c+=segment(uv,p1,p3,linewidth,dist);
#endif
}
}
}
}
c*=brightness;
#ifdef INVERSE
fragColor = vec4(1.-c,1.);
#else
fragColor = vec4(c,1.);
#endif
}

View file

@ -1,40 +0,0 @@
// Created by inigo quilez - iq/2013
// https://www.youtube.com/c/InigoQuilez
// https://iquilezles.org/
// See also:
//
// Input - Keyboard : https://www.shadertoy.com/view/lsXGzf
// Input - Microphone : https://www.shadertoy.com/view/llSGDh
// Input - Mouse : https://www.shadertoy.com/view/Mss3zH
// Input - Sound : https://www.shadertoy.com/view/Xds3Rr
// Input - SoundCloud : https://www.shadertoy.com/view/MsdGzn
// Input - Time : https://www.shadertoy.com/view/lsXGz8
// Input - TimeDelta : https://www.shadertoy.com/view/lsKGWV
// Inout - 3D Texture : https://www.shadertoy.com/view/4llcR4
void mainImage( out vec4 fragColor, in vec2 fragCoord )
{
// create pixel coordinates
vec2 uv = fragCoord.xy / iResolution.xy;
// the sound texture is 512x2
int tx = int(uv.x * 512.0);
// first row is frequency data (48Khz/4 in 512 texels, meaning 23 Hz per texel)
float fft = texelFetch( iChannel0, ivec2(tx,0), 0 ).x;
// second row is the sound wave, one texel is one mono sample
float wave = texelFetch( iChannel0, ivec2(tx,1), 0 ).x;
// convert frequency to colors
vec3 col = vec3( fft, 4.0*fft*(1.0-fft), 1.0-fft ) * fft;
// add wave form on top
col += 1.0 - smoothstep(0.0, 0.15, abs(wave - uv.y));
// output final color
fragColor = vec4(col,1.0);
}

View file

@ -1,38 +0,0 @@
/*
2D LED Spectrum - Visualiser
Based on Led Spectrum Analyser by: simesgreen - 27th February, 2013 https://www.shadertoy.com/view/Msl3zr
2D LED Spectrum by: uNiversal - 27th May, 2015
Creative Commons Attribution-NonCommercial-ShareAlike 3.0 Unported License.
*/
void mainImage( out vec4 fragColor, in vec2 fragCoord )
{
// create pixel coordinates
vec2 uv = fragCoord.xy / iResolution.xy;
// quantize coordinates
const float bands = 30.0;
const float segs = 40.0;
vec2 p;
p.x = floor(uv.x*bands)/bands;
p.y = floor(uv.y*segs)/segs;
// read frequency data from first row of texture
float fft = texture( iChannel0, vec2(p.x,0.0) ).x;
// led color
vec3 color = mix(vec3(0.0, 2.0, 0.0), vec3(2.0, 0.0, 0.0), sqrt(uv.y));
// mask for bar graph
float mask = (p.y < fft) ? 1.0 : 0.1;
// led shape
vec2 d = fract((uv - p) *vec2(bands, segs)) - 0.5;
float led = smoothstep(0.5, 0.35, abs(d.x)) *
smoothstep(0.5, 0.35, abs(d.y));
vec3 ledColor = led*color*mask;
// output final color
fragColor = vec4(ledColor, 1.0);
}