forked from goliath/shadermeh
Compare commits
11 commits
72f3e51b7b
...
12396a8bb0
Author | SHA1 | Date | |
---|---|---|---|
12396a8bb0 | |||
664f8d0305 | |||
b2fa322e30 | |||
b519a77ad9 | |||
f3bf686a52 | |||
770deae6cd | |||
8c47f2726c | |||
542669ef04 | |||
22b2fd4864 | |||
6bfa914582 | |||
e58028ae18 |
9 changed files with 640 additions and 174 deletions
4
Makefile
4
Makefile
|
@ -1,5 +1,5 @@
|
|||
CFLAGS = -std=c11 -pedantic -Wall -Wextra -O2 -Ofast -D_DEFAULT_SOURCE -D_FORTIFY_SOURCE=2
|
||||
LDFLAGS = -lX11 -lGL -lGLEW -lm -lrt -lfftw3
|
||||
CFLAGS = -std=c11 -pedantic -Wall -Wextra -O2 -Ofast -D_DEFAULT_SOURCE
|
||||
LDFLAGS = -lX11 -lGL -lGLEW -lfftw3 -lm -lrt
|
||||
|
||||
shadermeh: shadermeh.o window.o shader.o
|
||||
|
||||
|
|
1
shader.c
1
shader.c
|
@ -31,6 +31,7 @@ static const char *frag_src_pre =
|
|||
"uniform vec4 iDate;\n"
|
||||
"uniform float iSampleRate;\n"
|
||||
"uniform vec3 iChannelResolution[4];\n"
|
||||
"uniform sampler2D iChannel0;"
|
||||
"\n";
|
||||
|
||||
static const char *frag_src_post =
|
||||
|
|
413
shadermeh.c
413
shadermeh.c
|
@ -1,16 +1,10 @@
|
|||
/* SPDX-License-Identifier: ISC */
|
||||
|
||||
/*
|
||||
* shadermeh.c
|
||||
*
|
||||
* Copyright (C) 2022 David Oberhollenzer <goliath@infraroot.at>
|
||||
*/
|
||||
#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[] = {
|
||||
-1.0f, -1.0f, 0.0f, /* lower left corner */
|
||||
|
@ -26,6 +20,107 @@ static GLfloat vertex_buffer[] = {
|
|||
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,
|
||||
const struct timespec *time0)
|
||||
{
|
||||
|
@ -51,29 +146,56 @@ 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)
|
||||
{
|
||||
while (size > 0) {
|
||||
int ret = write(fd, buffer, size);
|
||||
|
||||
if (ret < 0) {
|
||||
if (errno == EINTR){
|
||||
continue;
|
||||
}else if(errno == EAGAIN){
|
||||
perror("Output pipe probably has O_NONBLOCK "
|
||||
"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");
|
||||
if (errno == EINTR)
|
||||
continue;
|
||||
|
||||
if (errno == EAGAIN) {
|
||||
if (wait_fd_event(fd, POLLOUT))
|
||||
return -1;
|
||||
}
|
||||
continue;
|
||||
}else{
|
||||
perror("write");
|
||||
}
|
||||
return -1;
|
||||
continue;
|
||||
}
|
||||
|
||||
perror("write");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (ret == 0)
|
||||
|
@ -86,71 +208,16 @@ static int write_retry(int fd, const void *buffer, size_t size)
|
|||
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
|
||||
* 0…1 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[] = {
|
||||
{ "width", required_argument, NULL, 'w' },
|
||||
{ "height", required_argument, NULL, 'h' },
|
||||
{ "shader", required_argument, NULL, 's' },
|
||||
{ "sampling-rate", required_argument, NULL, 'r' },
|
||||
{ "to-stdout", no_argument, NULL, 'S' },
|
||||
{ "stdin-audio", required_argument, NULL, 'a' },
|
||||
{ NULL, 0, NULL, 0 },
|
||||
};
|
||||
|
||||
static const char *short_opts = "r:w:h:s:S";
|
||||
static const char *short_opts = "w:a:h:s:S";
|
||||
|
||||
static const char *usage_str =
|
||||
"shadermeh OPTIONS...\n"
|
||||
|
@ -159,40 +226,27 @@ static const char *usage_str =
|
|||
"\n"
|
||||
" --width, -w <pixels>\n"
|
||||
" --height, -h <pixels>\n"
|
||||
" --sampling-rate, -r <sampling rate[Hz]>\n"
|
||||
"\n"
|
||||
" --to-stdout, -S\n"
|
||||
" --to-stdout, -S Poop raw RGB24 frames to stdout (blocking)\n"
|
||||
" --stdin-audio, -a <sample rate> Read raw PCM audio from stdin (non-blocking)\n"
|
||||
"\n"
|
||||
" --shader, -s <shader file>\n"
|
||||
"\n";
|
||||
|
||||
|
||||
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;
|
||||
GLfloat u_iSampleRate;
|
||||
struct timespec start, frame_start, frame_end;
|
||||
unsigned int width, height, iFrame = 0;
|
||||
void *fb32 = NULL, *fb24 = NULL;
|
||||
const char *shader_file = NULL;
|
||||
GLuint vao, vbo, fbo, fbo_tex;
|
||||
GLint major, minor, prog;
|
||||
float iTime, iTimeDelta;
|
||||
float iTime, iTimeDelta = 0;
|
||||
bool have_audio = false;
|
||||
bool to_stdout = false;
|
||||
window *wnd;
|
||||
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);
|
||||
int i;
|
||||
|
||||
/******************** parse options ************************/
|
||||
width = 800;
|
||||
|
@ -213,18 +267,32 @@ int main(int argc, char **argv)
|
|||
case 's':
|
||||
shader_file = optarg;
|
||||
break;
|
||||
case 'r':
|
||||
sampling_rate = strtol(optarg, NULL, 10);
|
||||
break;
|
||||
case 'S':
|
||||
to_stdout = true;
|
||||
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:
|
||||
fputs(usage_str, stderr);
|
||||
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) {
|
||||
fputs(usage_str, stderr);
|
||||
fputs("No shader file specified!\n", stderr);
|
||||
|
@ -244,52 +312,6 @@ int main(int argc, char **argv)
|
|||
free(fb32);
|
||||
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 **********/
|
||||
|
@ -318,7 +340,9 @@ int main(int argc, char **argv)
|
|||
|
||||
glGetIntegerv(GL_MAJOR_VERSION, &major);
|
||||
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 ********************/
|
||||
glViewport(0, 0, width, height);
|
||||
|
@ -346,6 +370,7 @@ int main(int argc, char **argv)
|
|||
|
||||
if (!shader_program_get_build_status(prog)) {
|
||||
shader_program_print_info_log(prog);
|
||||
goto fail_vao;
|
||||
}
|
||||
|
||||
glUseProgram(prog);
|
||||
|
@ -354,13 +379,34 @@ int main(int argc, char **argv)
|
|||
u_iResolution = glGetUniformLocation(prog, "iResolution");
|
||||
u_iTime = glGetUniformLocation(prog, "iTime");
|
||||
u_iTimeDelta = glGetUniformLocation(prog, "iTimeDelta");
|
||||
u_iFrame = glGetUniformLocation(prog, "iFrame;");
|
||||
u_iSampleRate = glGetUniformLocation(prog, "iSampleRate");
|
||||
u_iFrame = glGetUniformLocation(prog, "iFrame");
|
||||
|
||||
glUniform3f(u_iResolution, width, height, 0.0f);
|
||||
glUniform1f(u_iSampleRate, sampling_rate);
|
||||
|
||||
clock_gettime(CLOCK_MONOTONIC_RAW, &start);
|
||||
glUniform1i(glGetUniformLocation(prog, "iChannel0"), 0);
|
||||
|
||||
/************************* 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 ********************/
|
||||
if (to_stdout) {
|
||||
|
@ -370,20 +416,42 @@ int main(int argc, char **argv)
|
|||
glBindTexture(GL_TEXTURE_2D, fbo_tex);
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0,
|
||||
GL_RGBA, GL_UNSIGNED_BYTE, NULL);
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, fbo);
|
||||
glFramebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
|
||||
fbo_tex, 0);
|
||||
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
||||
} else {
|
||||
window_show(wnd);
|
||||
}
|
||||
|
||||
/******************** drawing loop ********************/
|
||||
clock_gettime(CLOCK_MONOTONIC_RAW, &start);
|
||||
|
||||
while (to_stdout || window_handle_events()) {
|
||||
/* render image to FBO */
|
||||
clock_gettime(CLOCK_MONOTONIC_RAW, &frame_start);
|
||||
|
||||
if (to_stdout)
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, fbo);
|
||||
|
||||
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);
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
|
||||
if (to_stdout) {
|
||||
glFlush();
|
||||
|
@ -393,9 +461,14 @@ int main(int argc, char **argv)
|
|||
|
||||
/* get image from FBO, dump to stdout */
|
||||
if (to_stdout) {
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
||||
glBindTexture(GL_TEXTURE_2D, fbo_tex);
|
||||
|
||||
glGetTexImage(GL_TEXTURE_2D, 0, GL_RGBA,
|
||||
GL_UNSIGNED_BYTE, fb32);
|
||||
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
|
||||
convert_for_ffmpeg(fb32, fb24, width, height);
|
||||
|
||||
if (write_retry(STDOUT_FILENO, fb24,
|
||||
|
@ -403,19 +476,6 @@ int main(int argc, char **argv)
|
|||
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 */
|
||||
clock_gettime(CLOCK_MONOTONIC_RAW, &frame_end);
|
||||
|
@ -430,12 +490,8 @@ int main(int argc, char **argv)
|
|||
}
|
||||
|
||||
/******************** cleanup ********************/
|
||||
glUseProgram(0);
|
||||
glBindBuffer(GL_ARRAY_BUFFER, 0);
|
||||
glDeleteBuffers(1, &vbo);
|
||||
glBindVertexArray(0);
|
||||
glDeleteVertexArrays(1, &vao);
|
||||
glDeleteProgram(prog);
|
||||
glDeleteTextures(1, &sound_tex);
|
||||
glDeleteSamplers(1, &sampler_sound);
|
||||
|
||||
if (to_stdout) {
|
||||
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
||||
|
@ -443,10 +499,23 @@ int main(int argc, char **argv)
|
|||
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);
|
||||
free(fb32);
|
||||
free(fb24);
|
||||
fftw_free(fftw_in);
|
||||
fftw_free(fftw_out);
|
||||
window_destroy(wnd);
|
||||
fftw_destroy_plan(plan);
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
|
11
shadermeh.h
11
shadermeh.h
|
@ -23,8 +23,19 @@
|
|||
#include <unistd.h>
|
||||
#include <stdio.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <poll.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 {
|
||||
Window wnd;
|
||||
GLXContext gl;
|
||||
|
|
84
shaders/eclipse.frag
Normal file
84
shaders/eclipse.frag
Normal file
|
@ -0,0 +1,84 @@
|
|||
// 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);
|
||||
}
|
65
shaders/fft.frag
Normal file
65
shaders/fft.frag
Normal file
|
@ -0,0 +1,65 @@
|
|||
/*
|
||||
|
||||
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);
|
||||
|
||||
}
|
158
shaders/grid.frag
Normal file
158
shaders/grid.frag
Normal file
|
@ -0,0 +1,158 @@
|
|||
//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
|
||||
}
|
40
shaders/osci.frag
Normal file
40
shaders/osci.frag
Normal file
|
@ -0,0 +1,40 @@
|
|||
// 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);
|
||||
}
|
38
shaders/spectrum.frag
Normal file
38
shaders/spectrum.frag
Normal file
|
@ -0,0 +1,38 @@
|
|||
/*
|
||||
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);
|
||||
}
|
||||
|
Loading…
Reference in a new issue