forked from goliath/shadermeh
Compare commits
8 commits
12396a8bb0
...
72f3e51b7b
Author | SHA1 | Date | |
---|---|---|---|
72f3e51b7b | |||
7e190a5729 | |||
84595eff00 | |||
f0ffbc7c33 | |||
47bbca6806 | |||
51f808d12f | |||
75cdd01de8 | |||
be8f7d1716 |
2 changed files with 164 additions and 9 deletions
4
Makefile
4
Makefile
|
@ -1,5 +1,5 @@
|
||||||
CFLAGS = -ansi -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 -lm -lrt
|
LDFLAGS = -lX11 -lGL -lGLEW -lm -lrt -lfftw3
|
||||||
|
|
||||||
shadermeh: shadermeh.o window.o shader.o
|
shadermeh: shadermeh.o window.o shader.o
|
||||||
|
|
||||||
|
|
163
shadermeh.c
163
shadermeh.c
|
@ -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 */
|
||||||
|
@ -51,9 +57,22 @@ static int write_retry(int fd, const void *buffer, size_t size)
|
||||||
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){
|
||||||
|
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");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}else{
|
||||||
perror("write");
|
perror("write");
|
||||||
|
}
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -67,15 +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
|
||||||
|
* 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[] = {
|
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' },
|
||||||
{ NULL, 0, NULL, 0 },
|
{ NULL, 0, NULL, 0 },
|
||||||
};
|
};
|
||||||
|
|
||||||
static const char *short_opts = "w: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"
|
||||||
|
@ -84,15 +159,18 @@ 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\n"
|
" --to-stdout, -S\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 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;
|
||||||
|
@ -102,7 +180,19 @@ int main(int argc, char **argv)
|
||||||
float iTime, iTimeDelta;
|
float iTime, iTimeDelta;
|
||||||
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;
|
||||||
|
@ -123,6 +213,9 @@ 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;
|
||||||
|
@ -151,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 **********/
|
||||||
|
@ -179,7 +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);
|
||||||
printf("OpenGL version %d.%d\n", major, minor);
|
fprintf(stderr,"OpenGL version %d.%d\n", major, minor);
|
||||||
|
|
||||||
/******************** initialization ********************/
|
/******************** initialization ********************/
|
||||||
glViewport(0, 0, width, height);
|
glViewport(0, 0, width, height);
|
||||||
|
@ -216,8 +355,10 @@ int main(int argc, char **argv)
|
||||||
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);
|
||||||
|
|
||||||
clock_gettime(CLOCK_MONOTONIC_RAW, &start);
|
clock_gettime(CLOCK_MONOTONIC_RAW, &start);
|
||||||
|
|
||||||
|
@ -262,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);
|
||||||
|
@ -293,5 +447,6 @@ int main(int argc, char **argv)
|
||||||
free(fb32);
|
free(fb32);
|
||||||
free(fb24);
|
free(fb24);
|
||||||
window_destroy(wnd);
|
window_destroy(wnd);
|
||||||
|
fftw_destroy_plan(plan);
|
||||||
return EXIT_SUCCESS;
|
return EXIT_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue