diff --git a/shader.c b/shader.c index 2cad446..968760c 100644 --- a/shader.c +++ b/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 = diff --git a/shadermeh.c b/shadermeh.c index ee27e7e..6249922 100644 --- a/shadermeh.c +++ b/shadermeh.c @@ -20,6 +20,9 @@ static GLfloat vertex_buffer[] = { 1.0f, 1.0f, 1.0f, }; +static GLubyte audio_buffer[AUDIO_SAMPLES * AUDIO_CHANNELS]; +static float audio_sample_data[AUDIO_SAMPLES]; + static double diff_timespec(const struct timespec *time1, const struct timespec *time0) { @@ -72,10 +75,11 @@ static const struct option long_opts[] = { { "height", required_argument, NULL, 'h' }, { "shader", required_argument, NULL, 's' }, { "to-stdout", no_argument, NULL, 'S' }, + { "stdin-audio", no_argument, NULL, 'a' }, { 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 = "shadermeh OPTIONS...\n" @@ -85,21 +89,23 @@ static const char *usage_str = " --width, -w \n" " --height, -h \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" " --shader, -s \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; 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; + bool have_audio = false; bool to_stdout = false; window *wnd; int i; @@ -126,6 +132,9 @@ int main(int argc, char **argv) case 'S': to_stdout = true; break; + case 'a': + have_audio = true; + break; default: fputs(usage_str, stderr); return EXIT_FAILURE; @@ -222,6 +231,22 @@ int main(int argc, char **argv) glUniform3f(u_iResolution, width, height, 0.0f); + 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 ********************/ if (to_stdout) { glGenFramebuffers(1, &fbo); @@ -242,6 +267,10 @@ int main(int argc, char **argv) } /******************** 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()) { @@ -252,7 +281,24 @@ int main(int argc, char **argv) glBindFramebuffer(GL_FRAMEBUFFER, fbo); glClear(GL_COLOR_BUFFER_BIT); + + if (have_audio) { + glBindTexture(GL_TEXTURE_2D, sound_tex); + + for (i = 0; i < AUDIO_SAMPLES; ++i) { + audio_buffer[i] = 255.0f * audio_sample_data[i]; + + audio_buffer[AUDIO_SAMPLES + i] = + audio_buffer[i]; + } + + 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(); @@ -291,6 +337,9 @@ int main(int argc, char **argv) } /******************** cleanup ********************/ + glDeleteTextures(1, &sound_tex); + glDeleteSamplers(1, &sampler_sound); + if (to_stdout) { glBindFramebuffer(GL_FRAMEBUFFER, 0); glDeleteFramebuffers(1, &fbo); diff --git a/shadermeh.h b/shadermeh.h index d8b7f46..aa5c5cd 100644 --- a/shadermeh.h +++ b/shadermeh.h @@ -25,6 +25,9 @@ #include #include +#define AUDIO_SAMPLES (512) +#define AUDIO_CHANNELS (2) + typedef struct { Window wnd; GLXContext gl; diff --git a/shaders/osci.frag b/shaders/osci.frag new file mode 100644 index 0000000..9138a88 --- /dev/null +++ b/shaders/osci.frag @@ -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); +}