2022-07-17 15:58:53 +02:00
|
|
|
/* SPDX-License-Identifier: ISC */
|
|
|
|
/*
|
|
|
|
* shadermeh.c
|
|
|
|
*
|
|
|
|
* Copyright (C) 2022 David Oberhollenzer <goliath@infraroot.at>
|
|
|
|
*/
|
2022-07-16 22:01:12 +02:00
|
|
|
#include "shadermeh.h"
|
|
|
|
|
|
|
|
static GLfloat vertex_buffer[] = {
|
|
|
|
-1.0f, -1.0f, 0.0f, /* lower left corner */
|
|
|
|
1.0f, 1.0f, 1.0f,
|
|
|
|
|
|
|
|
+1.0f, -1.0f, 0.0f, /* lower right corner */
|
|
|
|
1.0f, 1.0f, 1.0f,
|
|
|
|
|
|
|
|
-1.0f, 1.0f, 0.0f, /* uper left corner */
|
|
|
|
1.0f, 1.0f, 1.0f,
|
|
|
|
|
|
|
|
+1.0f, +1.0f, 0.0f, /* upper right corner */
|
|
|
|
1.0f, 1.0f, 1.0f,
|
|
|
|
};
|
|
|
|
|
2022-07-19 21:36:49 +02:00
|
|
|
static GLubyte audio_buffer[AUDIO_SAMPLES * AUDIO_CHANNELS];
|
2022-07-20 23:43:41 +02:00
|
|
|
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;
|
2022-07-19 22:37:09 +02:00
|
|
|
static fftw_plan plan;
|
2022-07-19 21:36:49 +02:00
|
|
|
|
2022-07-20 23:43:41 +02:00
|
|
|
static int try_fetch_audio(float iTimeDelta)
|
2022-07-19 22:10:23 +02:00
|
|
|
{
|
2022-07-20 23:43:41 +02:00
|
|
|
/* 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);
|
2022-07-19 22:10:23 +02:00
|
|
|
|
|
|
|
for (;;) {
|
2022-07-20 23:43:41 +02:00
|
|
|
ret = read(STDIN_FILENO, (char *)audio_receive_data,
|
|
|
|
sizeof(*audio_receive_data)*AUDIO_BUFFER_SIZE);
|
2022-07-19 22:10:23 +02:00
|
|
|
|
|
|
|
if (ret < 0) {
|
|
|
|
if (errno == EINTR)
|
|
|
|
continue;
|
2022-07-20 23:43:41 +02:00
|
|
|
if (errno == EAGAIN || errno == EWOULDBLOCK)
|
2022-07-19 22:10:23 +02:00
|
|
|
break;
|
|
|
|
perror("stdin");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2022-07-20 23:43:41 +02:00
|
|
|
if (ret == 0 || ret % sizeof(float) != 0){
|
2022-07-19 22:10:23 +02:00
|
|
|
break;
|
2022-07-20 23:43:41 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
2022-07-19 22:10:23 +02:00
|
|
|
|
|
|
|
}
|
2022-07-20 23:43:41 +02:00
|
|
|
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);
|
2022-07-19 22:10:23 +02:00
|
|
|
|
2022-07-20 23:43:41 +02:00
|
|
|
for (i = 0; i < AUDIO_FFT_SIZE; ++i)
|
|
|
|
fftw_in[i] = audio_sample_data[sample_pointer+i];
|
2022-07-19 22:37:09 +02:00
|
|
|
|
|
|
|
fftw_execute(plan);
|
|
|
|
|
|
|
|
for (i = 0; i < AUDIO_SAMPLES; ++i) {
|
2022-07-20 23:43:41 +02:00
|
|
|
float a = cabs(fftw_out[i]);
|
2022-07-19 22:37:09 +02:00
|
|
|
|
2022-07-20 23:43:41 +02:00
|
|
|
audio_buffer[i + AUDIO_SAMPLES] = audio_sample_data[sample_pointer+i] * 127.0f + 127.0f;
|
|
|
|
audio_buffer[i] = log(fabsf(a)+1) * 50;
|
2022-07-19 22:10:23 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2022-07-16 22:01:12 +02:00
|
|
|
static double diff_timespec(const struct timespec *time1,
|
|
|
|
const struct timespec *time0)
|
|
|
|
{
|
|
|
|
return (time1->tv_sec - time0->tv_sec)
|
|
|
|
+ (time1->tv_nsec - time0->tv_nsec) / 1000000000.0;
|
|
|
|
}
|
|
|
|
|
2022-07-17 01:58:46 +02:00
|
|
|
static void convert_for_ffmpeg(const uint8_t *in, uint8_t *out,
|
|
|
|
size_t width, size_t height)
|
|
|
|
{
|
|
|
|
size_t x, y;
|
|
|
|
|
|
|
|
for (y = 0; y < height; ++y) {
|
|
|
|
const uint8_t *src = in + y * width * 4;
|
|
|
|
uint8_t *dst = out + (height - 1 - y) * width * 3;
|
|
|
|
|
|
|
|
for (x = 0; x < width; ++x) {
|
|
|
|
*(dst++) = *(src++);
|
|
|
|
*(dst++) = *(src++);
|
|
|
|
*(dst++) = *(src++);
|
|
|
|
++src;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-07-19 21:45:36 +02:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2022-07-17 01:58:46 +02:00
|
|
|
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;
|
2022-07-19 21:45:36 +02:00
|
|
|
|
|
|
|
if (errno == EAGAIN) {
|
|
|
|
if (wait_fd_event(fd, POLLOUT))
|
|
|
|
return -1;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2022-07-17 01:58:46 +02:00
|
|
|
perror("write");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (ret == 0)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
size -= ret;
|
|
|
|
buffer = (const char *)buffer + ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2022-07-16 22:01:12 +02:00
|
|
|
static const struct option long_opts[] = {
|
|
|
|
{ "width", required_argument, NULL, 'w' },
|
|
|
|
{ "height", required_argument, NULL, 'h' },
|
|
|
|
{ "shader", required_argument, NULL, 's' },
|
2022-07-17 01:58:46 +02:00
|
|
|
{ "to-stdout", no_argument, NULL, 'S' },
|
2022-07-20 23:43:41 +02:00
|
|
|
{ "stdin-audio", required_argument, NULL, 'a' },
|
2022-07-16 22:01:12 +02:00
|
|
|
{ NULL, 0, NULL, 0 },
|
|
|
|
};
|
|
|
|
|
2022-07-20 23:43:41 +02:00
|
|
|
static const char *short_opts = "w:a:h:s:S";
|
2022-07-16 22:01:12 +02:00
|
|
|
|
|
|
|
static const char *usage_str =
|
|
|
|
"shadermeh OPTIONS...\n"
|
|
|
|
"\n"
|
|
|
|
"Possible options:\n"
|
|
|
|
"\n"
|
|
|
|
" --width, -w <pixels>\n"
|
|
|
|
" --height, -h <pixels>\n"
|
|
|
|
"\n"
|
2022-07-19 21:36:49 +02:00
|
|
|
" --to-stdout, -S Poop raw RGB24 frames to stdout (blocking)\n"
|
2022-07-20 23:43:41 +02:00
|
|
|
" --stdin-audio, -a <sample rate> Read raw PCM audio from stdin (non-blocking)\n"
|
2022-07-17 01:58:46 +02:00
|
|
|
"\n"
|
2022-07-16 22:01:12 +02:00
|
|
|
" --shader, -s <shader file>\n"
|
|
|
|
"\n";
|
|
|
|
|
|
|
|
int main(int argc, char **argv)
|
|
|
|
{
|
2022-07-19 21:36:49 +02:00
|
|
|
GLuint vao, vbo, fbo, fbo_tex, sound_tex, sampler_sound;
|
2022-07-16 22:01:12 +02:00
|
|
|
GLuint u_iResolution, u_iTime, u_iTimeDelta, u_iFrame;
|
|
|
|
struct timespec start, frame_start, frame_end;
|
|
|
|
unsigned int width, height, iFrame = 0;
|
2022-07-17 01:58:46 +02:00
|
|
|
void *fb32 = NULL, *fb24 = NULL;
|
2022-07-16 22:01:12 +02:00
|
|
|
const char *shader_file = NULL;
|
|
|
|
GLint major, minor, prog;
|
2022-07-20 23:43:41 +02:00
|
|
|
float iTime, iTimeDelta = 0;
|
2022-07-19 21:36:49 +02:00
|
|
|
bool have_audio = false;
|
2022-07-17 01:58:46 +02:00
|
|
|
bool to_stdout = false;
|
2022-07-16 22:01:12 +02:00
|
|
|
window *wnd;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
/******************** parse options ************************/
|
|
|
|
width = 800;
|
|
|
|
height = 450;
|
|
|
|
|
|
|
|
for (;;) {
|
|
|
|
i = getopt_long(argc, argv, short_opts, long_opts, NULL);
|
|
|
|
if (i == -1)
|
|
|
|
break;
|
|
|
|
|
|
|
|
switch (i) {
|
|
|
|
case 'w':
|
|
|
|
width = strtol(optarg, NULL, 10);
|
|
|
|
break;
|
|
|
|
case 'h':
|
|
|
|
height = strtol(optarg, NULL, 10);
|
|
|
|
break;
|
|
|
|
case 's':
|
|
|
|
shader_file = optarg;
|
|
|
|
break;
|
2022-07-17 01:58:46 +02:00
|
|
|
case 'S':
|
|
|
|
to_stdout = true;
|
|
|
|
break;
|
2022-07-19 21:36:49 +02:00
|
|
|
case 'a':
|
|
|
|
have_audio = true;
|
2022-07-20 23:43:41 +02:00
|
|
|
sample_rate = strtol(optarg, NULL, 10);
|
|
|
|
audio_sample_data = malloc(AUDIO_BUFFER_SIZE *
|
|
|
|
sizeof(float));
|
|
|
|
audio_receive_data = malloc(AUDIO_BUFFER_SIZE *
|
|
|
|
sizeof(float));
|
2022-07-19 21:36:49 +02:00
|
|
|
break;
|
2022-07-16 22:01:12 +02:00
|
|
|
default:
|
|
|
|
fputs(usage_str, stderr);
|
|
|
|
return EXIT_FAILURE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-07-19 22:10:23 +02:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-07-16 22:01:12 +02:00
|
|
|
if (!shader_file) {
|
|
|
|
fputs(usage_str, stderr);
|
|
|
|
fputs("No shader file specified!\n", stderr);
|
|
|
|
return EXIT_FAILURE;
|
|
|
|
}
|
|
|
|
|
2022-07-17 01:58:46 +02:00
|
|
|
if (to_stdout) {
|
|
|
|
fb32 = calloc(1, width * height * 4);
|
|
|
|
if (!fb32) {
|
|
|
|
perror("allocating scratch framebuffer");
|
|
|
|
return EXIT_FAILURE;
|
|
|
|
}
|
|
|
|
|
|
|
|
fb24 = calloc(1, width * height * 3);
|
|
|
|
if (!fb24) {
|
|
|
|
perror("allocating scratch framebuffer");
|
|
|
|
free(fb32);
|
|
|
|
return EXIT_FAILURE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-07-16 22:01:12 +02:00
|
|
|
/********** create window and make context current **********/
|
|
|
|
wnd = window_create(width, height, "shader meh...");
|
|
|
|
|
|
|
|
if (!wnd) {
|
|
|
|
fputs("failed to create window", stderr);
|
2022-07-17 01:58:46 +02:00
|
|
|
free(fb32);
|
|
|
|
free(fb24);
|
2022-07-16 22:01:12 +02:00
|
|
|
return EXIT_FAILURE;
|
|
|
|
}
|
|
|
|
|
|
|
|
window_make_current(wnd);
|
|
|
|
window_set_vsync(wnd, 1);
|
|
|
|
|
|
|
|
/******************** load entry points ********************/
|
|
|
|
glewExperimental = GL_TRUE;
|
|
|
|
|
|
|
|
if (glewInit() != GLEW_OK) {
|
|
|
|
fputs("glewInit() error", stderr);
|
|
|
|
window_destroy(wnd);
|
2022-07-17 01:58:46 +02:00
|
|
|
free(fb32);
|
|
|
|
free(fb24);
|
2022-07-16 22:01:12 +02:00
|
|
|
return EXIT_FAILURE;
|
|
|
|
}
|
|
|
|
|
|
|
|
glGetIntegerv(GL_MAJOR_VERSION, &major);
|
|
|
|
glGetIntegerv(GL_MINOR_VERSION, &minor);
|
2022-07-19 20:57:12 +02:00
|
|
|
|
|
|
|
if (!to_stdout)
|
|
|
|
printf("OpenGL version %d.%d\n", major, minor);
|
2022-07-16 22:01:12 +02:00
|
|
|
|
|
|
|
/******************** initialization ********************/
|
|
|
|
glViewport(0, 0, width, height);
|
|
|
|
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
|
|
|
|
|
|
|
|
/* vertex buffer object & vertex array object */
|
|
|
|
glGenVertexArrays(1, &vao);
|
|
|
|
glBindVertexArray(vao);
|
|
|
|
|
|
|
|
glGenBuffers(1, &vbo);
|
|
|
|
glBindBuffer(GL_ARRAY_BUFFER, vbo);
|
|
|
|
|
|
|
|
glBufferData(GL_ARRAY_BUFFER, sizeof(vertex_buffer),
|
|
|
|
vertex_buffer, GL_STATIC_DRAW);
|
|
|
|
|
|
|
|
glEnableVertexAttribArray(0);
|
|
|
|
glEnableVertexAttribArray(1);
|
|
|
|
|
|
|
|
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(GLfloat) * 6, 0);
|
|
|
|
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, sizeof(GLfloat) * 6,
|
|
|
|
(GLvoid *)(3 * sizeof(GLfloat)));
|
|
|
|
|
|
|
|
/* shader */
|
|
|
|
prog = shader_program_load(shader_file);
|
|
|
|
|
|
|
|
if (!shader_program_get_build_status(prog)) {
|
|
|
|
shader_program_print_info_log(prog);
|
2022-07-19 20:58:32 +02:00
|
|
|
goto fail_vao;
|
2022-07-16 22:01:12 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
glUseProgram(prog);
|
|
|
|
|
|
|
|
/* uniforms */
|
|
|
|
u_iResolution = glGetUniformLocation(prog, "iResolution");
|
|
|
|
u_iTime = glGetUniformLocation(prog, "iTime");
|
|
|
|
u_iTimeDelta = glGetUniformLocation(prog, "iTimeDelta");
|
2022-07-19 21:06:06 +02:00
|
|
|
u_iFrame = glGetUniformLocation(prog, "iFrame");
|
2022-07-16 22:01:12 +02:00
|
|
|
|
|
|
|
glUniform3f(u_iResolution, width, height, 0.0f);
|
|
|
|
|
2022-07-19 21:36:49 +02:00
|
|
|
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);
|
|
|
|
|
2022-07-19 22:37:09 +02:00
|
|
|
if (have_audio) {
|
2022-07-20 23:43:41 +02:00
|
|
|
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);
|
2022-07-19 22:37:09 +02:00
|
|
|
}
|
|
|
|
|
2022-07-17 01:58:46 +02:00
|
|
|
/******************** framebuffer object ********************/
|
|
|
|
if (to_stdout) {
|
|
|
|
glGenFramebuffers(1, &fbo);
|
|
|
|
glGenTextures(1, &fbo_tex);
|
|
|
|
|
|
|
|
glBindTexture(GL_TEXTURE_2D, fbo_tex);
|
|
|
|
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0,
|
|
|
|
GL_RGBA, GL_UNSIGNED_BYTE, NULL);
|
2022-07-19 21:04:43 +02:00
|
|
|
glBindTexture(GL_TEXTURE_2D, 0);
|
2022-07-17 01:58:46 +02:00
|
|
|
|
|
|
|
glBindFramebuffer(GL_FRAMEBUFFER, fbo);
|
|
|
|
glFramebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
|
|
|
|
fbo_tex, 0);
|
2022-07-19 21:04:43 +02:00
|
|
|
|
|
|
|
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
2022-07-17 02:33:15 +02:00
|
|
|
} else {
|
|
|
|
window_show(wnd);
|
2022-07-17 01:58:46 +02:00
|
|
|
}
|
|
|
|
|
2022-07-16 22:01:12 +02:00
|
|
|
/******************** drawing loop ********************/
|
2022-07-19 21:06:06 +02:00
|
|
|
clock_gettime(CLOCK_MONOTONIC_RAW, &start);
|
|
|
|
|
2022-07-17 02:33:15 +02:00
|
|
|
while (to_stdout || window_handle_events()) {
|
2022-07-17 01:58:46 +02:00
|
|
|
/* render image to FBO */
|
2022-07-16 22:01:12 +02:00
|
|
|
clock_gettime(CLOCK_MONOTONIC_RAW, &frame_start);
|
2022-07-19 21:04:43 +02:00
|
|
|
|
|
|
|
if (to_stdout)
|
|
|
|
glBindFramebuffer(GL_FRAMEBUFFER, fbo);
|
|
|
|
|
2022-07-16 22:01:12 +02:00
|
|
|
glClear(GL_COLOR_BUFFER_BIT);
|
2022-07-19 21:36:49 +02:00
|
|
|
|
|
|
|
if (have_audio) {
|
2022-07-20 23:43:41 +02:00
|
|
|
if (try_fetch_audio(iTimeDelta))
|
2022-07-19 22:10:23 +02:00
|
|
|
break;
|
2022-07-19 21:36:49 +02:00
|
|
|
|
2022-07-19 22:10:23 +02:00
|
|
|
glBindTexture(GL_TEXTURE_2D, sound_tex);
|
2022-07-19 21:36:49 +02:00
|
|
|
|
|
|
|
glTexImage2D(GL_TEXTURE_2D, 0, GL_R8,
|
|
|
|
AUDIO_SAMPLES, AUDIO_CHANNELS, 0,
|
|
|
|
GL_RED, GL_UNSIGNED_BYTE, audio_buffer);
|
|
|
|
}
|
|
|
|
|
2022-07-16 22:01:12 +02:00
|
|
|
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
|
2022-07-19 21:36:49 +02:00
|
|
|
glBindTexture(GL_TEXTURE_2D, 0);
|
2022-07-17 01:58:46 +02:00
|
|
|
|
2022-07-17 02:33:15 +02:00
|
|
|
if (to_stdout) {
|
|
|
|
glFlush();
|
|
|
|
} else {
|
2022-07-17 01:58:46 +02:00
|
|
|
window_swap_buffers(wnd);
|
2022-07-17 02:33:15 +02:00
|
|
|
}
|
2022-07-17 01:58:46 +02:00
|
|
|
|
2022-07-17 02:33:15 +02:00
|
|
|
/* get image from FBO, dump to stdout */
|
2022-07-17 01:58:46 +02:00
|
|
|
if (to_stdout) {
|
2022-07-19 21:04:43 +02:00
|
|
|
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
|
|
|
glBindTexture(GL_TEXTURE_2D, fbo_tex);
|
|
|
|
|
2022-07-17 01:58:46 +02:00
|
|
|
glGetTexImage(GL_TEXTURE_2D, 0, GL_RGBA,
|
|
|
|
GL_UNSIGNED_BYTE, fb32);
|
|
|
|
|
2022-07-19 21:04:43 +02:00
|
|
|
glBindTexture(GL_TEXTURE_2D, 0);
|
|
|
|
|
2022-07-17 01:58:46 +02:00
|
|
|
convert_for_ffmpeg(fb32, fb24, width, height);
|
|
|
|
|
|
|
|
if (write_retry(STDOUT_FILENO, fb24,
|
|
|
|
width * height * 3)) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2022-07-17 02:33:15 +02:00
|
|
|
|
|
|
|
/* update timers */
|
|
|
|
clock_gettime(CLOCK_MONOTONIC_RAW, &frame_end);
|
|
|
|
|
|
|
|
iFrame += 1;
|
|
|
|
iTimeDelta = diff_timespec(&frame_end, &frame_start);
|
|
|
|
iTime = diff_timespec(&frame_end, &start);
|
|
|
|
|
|
|
|
glUniform1f(u_iTimeDelta, iTimeDelta);
|
|
|
|
glUniform1f(u_iTime, iTime);
|
|
|
|
glUniform1ui(u_iFrame, iFrame);
|
2022-07-16 22:01:12 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/******************** cleanup ********************/
|
2022-07-19 21:36:49 +02:00
|
|
|
glDeleteTextures(1, &sound_tex);
|
|
|
|
glDeleteSamplers(1, &sampler_sound);
|
|
|
|
|
2022-07-17 01:58:46 +02:00
|
|
|
if (to_stdout) {
|
|
|
|
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
|
|
|
glDeleteFramebuffers(1, &fbo);
|
|
|
|
glDeleteTextures(1, &fbo_tex);
|
|
|
|
}
|
|
|
|
|
2022-07-19 20:58:32 +02:00
|
|
|
glUseProgram(0);
|
|
|
|
glDeleteProgram(prog);
|
2022-07-19 22:37:09 +02:00
|
|
|
|
|
|
|
if (have_audio) {
|
|
|
|
fftw_destroy_plan(plan);
|
|
|
|
}
|
2022-07-19 20:58:32 +02:00
|
|
|
fail_vao:
|
|
|
|
glBindBuffer(GL_ARRAY_BUFFER, 0);
|
|
|
|
glDeleteBuffers(1, &vbo);
|
|
|
|
glBindVertexArray(0);
|
|
|
|
glDeleteVertexArrays(1, &vao);
|
|
|
|
|
2022-07-16 22:01:12 +02:00
|
|
|
window_make_current(NULL);
|
2022-07-17 01:58:46 +02:00
|
|
|
free(fb32);
|
|
|
|
free(fb24);
|
2022-07-20 23:43:41 +02:00
|
|
|
fftw_free(fftw_in);
|
|
|
|
fftw_free(fftw_out);
|
2022-07-16 22:01:12 +02:00
|
|
|
window_destroy(wnd);
|
|
|
|
return EXIT_SUCCESS;
|
|
|
|
}
|