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
2 changed files with 164 additions and 9 deletions

View file

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

View file

@ -1,10 +1,16 @@
/* 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 */
@ -51,10 +57,23 @@ static int write_retry(int fd, const void *buffer, size_t size)
int ret = write(fd, buffer, size);
if (ret < 0) {
if (errno == EINTR)
continue;
perror("write");
return -1;
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");
return -1;
}
continue;
}else{
perror("write");
}
return -1;
}
if (ret == 0)
@ -67,15 +86,71 @@ 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
* 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[] = {
{ "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' },
{ 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 =
"shadermeh OPTIONS...\n"
@ -84,15 +159,18 @@ 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"
"\n"
" --shader, -s <shader file>\n"
"\n";
int main(int argc, char **argv)
{
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;
@ -102,7 +180,19 @@ int main(int argc, char **argv)
float iTime, iTimeDelta;
bool to_stdout = false;
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 ************************/
width = 800;
@ -123,6 +213,9 @@ 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;
@ -151,6 +244,52 @@ 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 **********/
@ -179,7 +318,7 @@ int main(int argc, char **argv)
glGetIntegerv(GL_MAJOR_VERSION, &major);
glGetIntegerv(GL_MINOR_VERSION, &minor);
printf("OpenGL version %d.%d\n", major, minor);
fprintf(stderr,"OpenGL version %d.%d\n", major, minor);
/******************** initialization ********************/
glViewport(0, 0, width, height);
@ -216,8 +355,10 @@ int main(int argc, char **argv)
u_iTime = glGetUniformLocation(prog, "iTime");
u_iTimeDelta = glGetUniformLocation(prog, "iTimeDelta");
u_iFrame = glGetUniformLocation(prog, "iFrame;");
u_iSampleRate = glGetUniformLocation(prog, "iSampleRate");
glUniform3f(u_iResolution, width, height, 0.0f);
glUniform1f(u_iSampleRate, sampling_rate);
clock_gettime(CLOCK_MONOTONIC_RAW, &start);
@ -262,6 +403,19 @@ 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);
@ -293,5 +447,6 @@ int main(int argc, char **argv)
free(fb32);
free(fb24);
window_destroy(wnd);
fftw_destroy_plan(plan);
return EXIT_SUCCESS;
}