@ -21,47 +21,101 @@ static GLfloat vertex_buffer[] = {
} ;
static GLubyte audio_buffer [ AUDIO_SAMPLES * AUDIO_CHANNELS ] ;
static float audio_sample_data [ AUDIO_SAMPLES ] ;
static fftw_complex fftw_in [ AUDIO_SAMPLES ] ;
static fftw_complex fftw_out [ AUDIO_SAMPLES ] ;
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 ;
static fftw_plan plan ;
static int try_fetch_audio ( void )
static int try_fetch_audio ( float iTimeDelta )
{
size_t i , count = 0 ;
int ret ;
/* 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 4800 kHz 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 3 s long the delay can grow to 3 s .
* 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 ) ;
for ( ; ; ) {
ret = read ( STDIN_FILENO ,
( char * ) audio_sample_data + count ,
sizeof ( audio_sample_data ) - count ) ;
ret = read ( STDIN_FILENO , ( char * ) audio_receive_data ,
sizeof ( * audio_receive_data ) * AUDIO_BUFFER_SIZE ) ;
if ( ret < 0 ) {
if ( errno = = EINTR )
continue ;
if ( errno = = EAGAIN )
if ( errno = = EAGAIN | | errno = = EWOULDBLOCK )
break ;
perror ( " stdin " ) ;
return - 1 ;
}
if ( ret = = 0 )
if ( ret = = 0 | | ret % sizeof ( float ) ! = 0 ) {
break ;
}
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 ;
}
count + = ret ;
}
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 ) ;
for ( i = 0 ; i < AUDIO_SAMPLES ; + + i )
fftw_in [ i ] [ 0 ] = audio_sample_data [ i ] ;
for ( i = 0 ; i < AUDIO_ FFT_SIZE ; + + i )
fftw_in [ i ] = audio_sample_data [ sample_pointer + i ] ;
fftw_execute ( plan ) ;
for ( i = 0 ; i < AUDIO_SAMPLES ; + + i ) {
float x = fftw_out [ i ] [ 0 ] , y = fftw_out [ i ] [ 1 ] ;
float a = sqrt ( x * x + y * y ) ;
float a = cabs ( fftw_out [ i ] ) ;
audio_buffer [ i + AUDIO_SAMPLES ] = audio_sample_data [ i ] * 127.0f + 127.0f ;
audio_buffer [ i ] = 127.0f + a * 127.0f ;
audio_buffer [ i + AUDIO_SAMPLES ] = audio_sample_data [ sample_pointer+ i] * 127.0f + 127.0f ;
audio_buffer [ i ] = log ( fabsf ( a ) + 1 ) * 50 ;
}
return 0 ;
@ -159,11 +213,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 ' } ,
{ " stdin-audio " , required _argument, NULL , ' a ' } ,
{ NULL , 0 , NULL , 0 } ,
} ;
static const char * short_opts = " w: h:s:Sa " ;
static const char * short_opts = " w: a: h:s:S" ;
static const char * usage_str =
" shadermeh OPTIONS... \n "
@ -174,7 +228,7 @@ static const char *usage_str =
" --height, -h <pixels> \n "
" \n "
" --to-stdout, -S Poop raw RGB24 frames to stdout (blocking) \n "
" --stdin-audio, -a Read raw PCM audio from stdin (non-blocking)\n "
" --stdin-audio, -a <sample rate> Read raw PCM audio from stdin (non-blocking)\n "
" \n "
" --shader, -s <shader file> \n "
" \n " ;
@ -188,7 +242,7 @@ int main(int argc, char **argv)
void * fb32 = NULL , * fb24 = NULL ;
const char * shader_file = NULL ;
GLint major , minor , prog ;
float iTime , iTimeDelta ;
float iTime , iTimeDelta = 0 ;
bool have_audio = false ;
bool to_stdout = false ;
window * wnd ;
@ -218,6 +272,11 @@ int main(int argc, char **argv)
break ;
case ' a ' :
have_audio = true ;
sample_rate = strtol ( optarg , NULL , 10 ) ;
audio_sample_data = malloc ( AUDIO_BUFFER_SIZE *
sizeof ( float ) ) ;
audio_receive_data = malloc ( AUDIO_BUFFER_SIZE *
sizeof ( float ) ) ;
break ;
default :
fputs ( usage_str , stderr ) ;
@ -341,8 +400,12 @@ int main(int argc, char **argv)
glBindSampler ( 0 , sampler_sound ) ;
if ( have_audio ) {
plan = fftw_plan_dft_1d ( AUDIO_SAMPLES , fftw_in , fftw_out ,
FFTW_FORWARD , FFTW_ESTIMATE ) ;
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 ) ;
}
/******************** framebuffer object ********************/
@ -377,7 +440,7 @@ int main(int argc, char **argv)
glClear ( GL_COLOR_BUFFER_BIT ) ;
if ( have_audio ) {
if ( try_fetch_audio ( ) )
if ( try_fetch_audio ( iTimeDelta ) )
break ;
glBindTexture ( GL_TEXTURE_2D , sound_tex ) ;
@ -451,6 +514,8 @@ fail_vao:
window_make_current ( NULL ) ;
free ( fb32 ) ;
free ( fb24 ) ;
fftw_free ( fftw_in ) ;
fftw_free ( fftw_out ) ;
window_destroy ( wnd ) ;
return EXIT_SUCCESS ;
}