Compare commits
7 commits
4123b78a81
...
f3bf686a52
Author | SHA1 | Date | |
---|---|---|---|
f3bf686a52 | |||
770deae6cd | |||
8c47f2726c | |||
542669ef04 | |||
22b2fd4864 | |||
6bfa914582 | |||
e58028ae18 |
4 changed files with 201 additions and 12 deletions
1
shader.c
1
shader.c
|
@ -31,6 +31,7 @@ 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 =
|
||||||
|
|
167
shadermeh.c
167
shadermeh.c
|
@ -20,6 +20,42 @@ static GLfloat vertex_buffer[] = {
|
||||||
1.0f, 1.0f, 1.0f,
|
1.0f, 1.0f, 1.0f,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static GLubyte audio_buffer[AUDIO_SAMPLES * AUDIO_CHANNELS];
|
||||||
|
static float audio_sample_data[AUDIO_SAMPLES];
|
||||||
|
|
||||||
|
static int try_fetch_audio(void)
|
||||||
|
{
|
||||||
|
size_t i, count = 0;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
for (;;) {
|
||||||
|
ret = read(STDIN_FILENO,
|
||||||
|
(char *)audio_sample_data + count,
|
||||||
|
sizeof(audio_sample_data) - count);
|
||||||
|
|
||||||
|
if (ret < 0) {
|
||||||
|
if (errno == EINTR)
|
||||||
|
continue;
|
||||||
|
if (errno == EAGAIN)
|
||||||
|
break;
|
||||||
|
perror("stdin");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ret == 0)
|
||||||
|
break;
|
||||||
|
|
||||||
|
count += ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < (count / sizeof(float)); ++i) {
|
||||||
|
audio_buffer[i + AUDIO_SAMPLES] = audio_sample_data[i] * 127.0f + 127.0f;
|
||||||
|
audio_buffer[i] = 0.0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
{
|
{
|
||||||
|
@ -45,6 +81,39 @@ 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) {
|
||||||
|
@ -53,6 +122,13 @@ static int write_retry(int fd, const void *buffer, size_t size)
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
if (errno == EINTR)
|
if (errno == EINTR)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
if (errno == EAGAIN) {
|
||||||
|
if (wait_fd_event(fd, POLLOUT))
|
||||||
|
return -1;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
perror("write");
|
perror("write");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
@ -72,10 +148,11 @@ static const struct option long_opts[] = {
|
||||||
{ "height", required_argument, NULL, 'h' },
|
{ "height", required_argument, NULL, 'h' },
|
||||||
{ "shader", required_argument, NULL, 's' },
|
{ "shader", required_argument, NULL, 's' },
|
||||||
{ "to-stdout", no_argument, NULL, 'S' },
|
{ "to-stdout", no_argument, NULL, 'S' },
|
||||||
|
{ "stdin-audio", no_argument, NULL, 'a' },
|
||||||
{ NULL, 0, NULL, 0 },
|
{ NULL, 0, NULL, 0 },
|
||||||
};
|
};
|
||||||
|
|
||||||
static const char *short_opts = "w:h:s:S";
|
static const char *short_opts = "w:h:s:Sa";
|
||||||
|
|
||||||
static const char *usage_str =
|
static const char *usage_str =
|
||||||
"shadermeh OPTIONS...\n"
|
"shadermeh OPTIONS...\n"
|
||||||
|
@ -85,21 +162,23 @@ static const char *usage_str =
|
||||||
" --width, -w <pixels>\n"
|
" --width, -w <pixels>\n"
|
||||||
" --height, -h <pixels>\n"
|
" --height, -h <pixels>\n"
|
||||||
"\n"
|
"\n"
|
||||||
" --to-stdout, -S\n"
|
" --to-stdout, -S Poop raw RGB24 frames to stdout (blocking)\n"
|
||||||
|
" --stdin-audio, -a 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;
|
||||||
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;
|
float iTime, iTimeDelta;
|
||||||
|
bool have_audio = false;
|
||||||
bool to_stdout = false;
|
bool to_stdout = false;
|
||||||
window *wnd;
|
window *wnd;
|
||||||
int i;
|
int i;
|
||||||
|
@ -126,12 +205,24 @@ int main(int argc, char **argv)
|
||||||
case 'S':
|
case 'S':
|
||||||
to_stdout = true;
|
to_stdout = true;
|
||||||
break;
|
break;
|
||||||
|
case 'a':
|
||||||
|
have_audio = true;
|
||||||
|
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);
|
||||||
|
@ -179,7 +270,9 @@ 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);
|
|
||||||
|
if (!to_stdout)
|
||||||
|
printf("OpenGL version %d.%d\n", major, minor);
|
||||||
|
|
||||||
/******************** initialization ********************/
|
/******************** initialization ********************/
|
||||||
glViewport(0, 0, width, height);
|
glViewport(0, 0, width, height);
|
||||||
|
@ -207,6 +300,7 @@ 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);
|
||||||
|
@ -215,11 +309,25 @@ 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");
|
||||||
|
|
||||||
glUniform3f(u_iResolution, width, height, 0.0f);
|
glUniform3f(u_iResolution, width, height, 0.0f);
|
||||||
|
|
||||||
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);
|
||||||
|
|
||||||
/******************** framebuffer object ********************/
|
/******************** framebuffer object ********************/
|
||||||
if (to_stdout) {
|
if (to_stdout) {
|
||||||
|
@ -229,20 +337,46 @@ 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 ********************/
|
||||||
|
for (i = 0; i < AUDIO_SAMPLES; ++i) {
|
||||||
|
audio_sample_data[i] = (float)i / (float)AUDIO_SAMPLES;
|
||||||
|
}
|
||||||
|
|
||||||
|
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())
|
||||||
|
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();
|
||||||
|
@ -252,9 +386,14 @@ 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,
|
||||||
|
@ -276,12 +415,8 @@ int main(int argc, char **argv)
|
||||||
}
|
}
|
||||||
|
|
||||||
/******************** cleanup ********************/
|
/******************** cleanup ********************/
|
||||||
glUseProgram(0);
|
glDeleteTextures(1, &sound_tex);
|
||||||
glBindBuffer(GL_ARRAY_BUFFER, 0);
|
glDeleteSamplers(1, &sampler_sound);
|
||||||
glDeleteBuffers(1, &vbo);
|
|
||||||
glBindVertexArray(0);
|
|
||||||
glDeleteVertexArrays(1, &vao);
|
|
||||||
glDeleteProgram(prog);
|
|
||||||
|
|
||||||
if (to_stdout) {
|
if (to_stdout) {
|
||||||
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
||||||
|
@ -289,6 +424,14 @@ int main(int argc, char **argv)
|
||||||
glDeleteTextures(1, &fbo_tex);
|
glDeleteTextures(1, &fbo_tex);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
glUseProgram(0);
|
||||||
|
glDeleteProgram(prog);
|
||||||
|
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);
|
||||||
|
|
|
@ -23,8 +23,13 @@
|
||||||
#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>
|
||||||
|
|
||||||
|
#define AUDIO_SAMPLES (512)
|
||||||
|
#define AUDIO_CHANNELS (2)
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
Window wnd;
|
Window wnd;
|
||||||
GLXContext gl;
|
GLXContext gl;
|
||||||
|
|
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);
|
||||||
|
}
|
Loading…
Reference in a new issue