//----------------------------------------------------------------------------- // name: VisualSine.cpp // desc: HelloSine, visualized // // Stanford University | Music 256a: Music, Computing, and Design // http://ccrma.stanford.edu/courses/256a/ //----------------------------------------------------------------------------- #include #include #include #include //#include #ifdef __APPLE__ #include #else #include #include #endif #include #include "visualizer.h" #include "RtAudio.h" #include "ticktimer.h" #include "textfile.h" using namespace std; // some defines #define SAMPLE double #define PI 3.14159265358979 #define TARGET_FPS 90 #define SAMPLE_RATE 44100 #define FORMAT RTAUDIO_FLOAT64 #define BASE_RATE 0.005 #define CHANNELS 1 #define N_FOUNTAINS 256 RtAudio::StreamOptions g_options; TickTimer tt; /* Search for extName in the extension string */ /* From the OpenGL 1.1 Programming Guide */ GLboolean CheckExtension(char *extName) { #ifndef GL_VERSION_2_0 cout << "gl 2.0 is not defined!" << endl; #endif char *p = (char *) glGetString(GL_EXTENSIONS); char *end; int extNameLen; extNameLen = strlen(extName); end = p + strlen(p); while (p < end) { int n = strcspn(p, " "); if ((extNameLen == n) && (strncmp(extName, p, n) == 0)) { return GL_TRUE; } p += (n + 1); } return GL_FALSE; } /* Print errors from shaders */ #ifdef __APPLE__ void printShaderInfoLog(GLuint obj) { int infologLength = 0; int charsWritten = 0; char *infoLog; glGetShaderiv(obj, GL_INFO_LOG_LENGTH,&infologLength); if (infologLength > 0) { infoLog = (char *)malloc(infologLength); glGetShaderInfoLog(obj, infologLength, &charsWritten, infoLog); printf("%s\n",infoLog); free(infoLog); } } void printProgramInfoLog(GLuint obj) { int infologLength = 0; int charsWritten = 0; char *infoLog; glGetProgramiv(obj, GL_INFO_LOG_LENGTH,&infologLength); if (infologLength > 0) { infoLog = (char *)malloc(infologLength); glGetProgramInfoLog(obj, infologLength, &charsWritten, infoLog); printf("%s\n",infoLog); free(infoLog); } } #else void printInfoLog(GLuint obj) { int infologLength = 0; int charsWritten = 0; char *infoLog; glGetObjectParameterivARB(obj, GL_OBJECT_INFO_LOG_LENGTH_ARB, &infologLength); if (infologLength > 1) { infoLog = (char *)malloc(infologLength); glGetInfoLogARB(obj, infologLength, &charsWritten, infoLog); printf("OpenGL InfoLog: %s\n",infoLog); free(infoLog); } } #endif // audio callback int Visualizer::audio_callback(void *outputBuffer, void *inputBuffer, unsigned int nBufferFrames, double streamTime, RtAudioStreamStatus status, void* user_data) { /* hack to get this instance from inside static callback */ Visualizer * self = (Visualizer *) user_data; SAMPLE * ibuffy = (SAMPLE *)inputBuffer; for( int i = 0; i < nBufferFrames; i++ ) { self->_audio_buffer_2[i] = ibuffy[i]; // zero out buffer ibuffy[i] = 0.0; } fftw_execute(self->_fft_plan); // convert fft data to a frequency spectrum double* raw_fft = self->_raw_fft; for (int i = 0; i < self->_buffer_size/2; i++) { self->_spectrum_buffer_2[i] = sqrt(raw_fft[i + 1] * raw_fft[i + 1] + raw_fft[self->_buffer_size - 1 - i] * raw_fft[self->_buffer_size - 1 - i]) / self->_buffer_size; } swap(self->_audio_buffer_1, self->_audio_buffer_2); swap(self->_spectrum_buffer_1, self->_spectrum_buffer_2); return 0; } // entry point Visualizer::Visualizer(int argc, char** argv) { _window_width = 800; _window_height = 600; init_sdl(); init_gl(); _fountain = new Fountain; _dac = new RtAudio; if ( _dac->getDeviceCount() < 1 ) { std::cout << "No audio devices found!\n"; exit( 1 ); } // Let RtAudio print messages to stderr. _dac->showWarnings( true ); unsigned int offset = 0; _buffer_size = 512; RtAudio::StreamParameters iParams; iParams.deviceId = _dac->getDefaultInputDevice(); iParams.nChannels = CHANNELS; iParams.firstChannel = offset; try { _dac->openStream( NULL, &iParams, FORMAT, SAMPLE_RATE, &_buffer_size, audio_callback, this, NULL ); _audio_buffer_1 = new SAMPLE[_buffer_size]; _audio_buffer_2 = new SAMPLE[_buffer_size]; _raw_fft = new SAMPLE[_buffer_size]; _spectrum_buffer_1 = new SAMPLE[_buffer_size/2]; _spectrum_buffer_2 = new SAMPLE[_buffer_size/2]; _fft_plan = fftw_plan_r2r_1d(_buffer_size, _audio_buffer_2, _raw_fft, FFTW_R2HC, FFTW_MEASURE); _dac->startStream(); } catch ( RtError& e ) { e.printMessage(); return; } // let GLUT handle the current thread from here cout << "initialization SUCCESS! buffer size is " << _buffer_size << endl; } Visualizer::~Visualizer() { delete _dac; } void Visualizer::clean_up() { try { _dac->stopStream(); } catch ( RtError& e ) { e.printMessage(); } if ( _dac->isStreamOpen() ) _dac->closeStream(); } /*** init stuff ***/ GLint sl_n_particles_loc; GLint sl_particles_loc; GLuint sl_program; //#define GET_PROC(x) x = (void)() SDL_GL_GetProcAddress(#x) void Visualizer::init_gl() { if ((CheckExtension("GL_ARB_vertex_shader") && CheckExtension("GL_ARB_fragment_shader") && CheckExtension("GL_ARB_shader_objects") && CheckExtension("GL_ARB_shading_language_100")) == GL_TRUE) { printf("You have the support for OpenGL Shading Language.\n"); } else { printf("Missing extensions for OpenGL Shading Language!\n"); exit(-1); } GLuint f, p; char *vs, *fs; /* ----- Shaders setup --------------- */ #ifdef __APPLE__ sl_program = p = glCreateProgram(); f = glCreateShader(GL_FRAGMENT_SHADER_ARB); fs = textFileRead("fire.frag"); const char *ff = fs; glShaderSource(f, 1, &ff, NULL); free(fs); glCompileShader(f); printShaderInfoLog(f); glAttachShader(p, f); glLinkProgram(p); printProgramInfoLog(p); glUseProgram(p); sl_n_particles_loc = glGetUniformLocation(p, "n_particles"); sl_particles_loc = glGetUniformLocation(p, "particles"); #else glCreateProgramObjectARB = (PFNGLCREATEPROGRAMOBJECTARBPROC) SDL_GL_GetProcAddress("glCreateProgramObjectARB"); glCreateShaderObjectARB = (PFNGLCREATESHADEROBJECTARBPROC) SDL_GL_GetProcAddress("glCreateShaderObjectARB"); glShaderSourceARB = (PFNGLSHADERSOURCEARBPROC) SDL_GL_GetProcAddress("glShaderSourceARB"); glCompileShaderARB = (PFNGLCOMPILESHADERARBPROC) SDL_GL_GetProcAddress("glCompileShaderARB"); glAttachObjectARB = (PFNGLATTACHOBJECTARBPROC) SDL_GL_GetProcAddress("glAttachObjectARB"); glLinkProgramARB = (PFNGLLINKPROGRAMARBPROC) SDL_GL_GetProcAddress("glLinkProgramARB"); glUseProgramObjectARB = (PFNGLUSEPROGRAMOBJECTARBPROC) SDL_GL_GetProcAddress("glUseProgramObjectARB"); glGetObjectParameterivARB = (PFNGLGETOBJECTPARAMETERIVARBPROC) SDL_GL_GetProcAddress("glGetObjectParameterivARB"); glGetInfoLogARB = (PFNGLGETINFOLOGARBPROC) SDL_GL_GetProcAddress("glGetInfoLogARB"); glGetUniformLocationARB = (PFNGLGETUNIFORMLOCATIONPROC) SDL_GL_GetProcAddress("glGetUniformLocationARB"); glUniform1iARB = (PFNGLUNIFORM1IPROC) SDL_GL_GetProcAddress("glUniform1iARB"); glUniform2fvARB = (PFNGLUNIFORM2FVPROC) SDL_GL_GetProcAddress("glUniform2fvARB"); sl_program = p = glCreateProgramObjectARB(); f = glCreateShaderObjectARB(GL_FRAGMENT_SHADER_ARB); fs = textFileRead("fire.frag"); const char *ff = fs; glShaderSourceARB(f, 1, &ff, NULL); free(fs); glCompileShaderARB(f); glAttachObjectARB(p, f); glLinkProgramARB(p); printInfoLog(p); glUseProgramObjectARB(p); sl_n_particles_loc = glGetUniformLocationARB(p, "n_particles"); sl_particles_loc = glGetUniformLocationARB(p, "particles"); #endif glEnable(GL_BLEND); glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); // glEnable(GL_TEXTURE_2D); } /* initialize sdl screen*/ int Visualizer::init_sdl() { SDL_Surface *screen; if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER) < 0) { fprintf(stderr, "Unable to initialize SDL: %s\n", SDL_GetError()); return(-1); } SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1); SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 16); SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 8); SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 8); SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 8); SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE, 8); if (!(screen = SDL_SetVideoMode(_window_width, _window_height, 0, SDL_SWSURFACE | SDL_RESIZABLE | SDL_OPENGL))) { fprintf(stderr, "Unable to set video mode: %s\n", SDL_GetError()); return(-1); } SDL_WM_SetCaption("Realtime Visualizer", NULL); do_gl_projection(); return(0); } void Visualizer::do_gl_projection() { double aspect = double(_window_width)/_window_height; // set up coordinate space glMatrixMode( GL_PROJECTION ); glLoadIdentity(); glOrtho(-aspect, aspect, -1, 1, -1, 1); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); } //----------------------------------------------------------------------------- // Name: reshapeFunc( ) // Desc: called when window size changes //----------------------------------------------------------------------------- void Visualizer::reshapeFunc( const SDL_ResizeEvent & resize_e ) { int w = resize_e.w, h = resize_e.h; // save the new window size _window_width = w; _window_height = h; // map the view port to the client area glViewport( 0, 0, w, h ); SDL_SetVideoMode(_window_width, _window_height, 0, SDL_SWSURFACE | SDL_RESIZABLE | SDL_OPENGL); do_gl_projection(); } int Visualizer::keyboard_event(const SDL_KeyboardEvent & keyboard_e) { int key = keyboard_e.keysym.sym; if (key == SDLK_ESCAPE || key == SDLK_q) { return SDL_QUIT; } return 0; } //----------------------------------------------------------------------------- // Name: render( ) // Desc: callback function invoked to draw the client area //----------------------------------------------------------------------------- void Visualizer::render(double tick_time) { // local state static GLfloat zrot = 0.0f, c = 0.0f; glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT ); glPushMatrix(); #ifdef __APPLE__ glUseProgram(sl_program); #else glUseProgramObjectARB(sl_program); #endif GLint n_particles = _fountain->_particles.size(); int MAX_PARTICLES = 400; GLfloat particles[800]; n_particles = min(n_particles, MAX_PARTICLES); int index = 0; for (int j = 0; j < n_particles ; j++) { index = n_particles - 1 - j; particles[2*j] = _fountain->_particles[index].x; particles[2*j+1] = _fountain->_particles[index].y; } int count = 0; while(true) { if (count + 8 >= n_particles*2) { if (count == 0) { glUniform1iARB(sl_n_particles_loc, n_particles); glUniform2fvARB(sl_particles_loc, n_particles, particles); glBegin(GL_QUADS); glTexCoord2f(-1,-1); glVertex2f(-1,-1); glTexCoord2f(1,-1); glVertex2f(1,-1); glTexCoord2f(1,1); glVertex2f(1,1); glTexCoord2f(-1,1); glVertex2f(-1,1); glEnd(); } break; } glUniform1iARB(sl_n_particles_loc, 4); glUniform2fvARB(sl_particles_loc, 4, particles + count); GLfloat minx, maxx, miny, maxy; minx = particles[count]; maxx = particles[count]; miny = particles[count+1]; maxy = particles[count+1]; for (int i = 0; i < 4; i++) { GLfloat x = particles[count+2*i]; GLfloat y = particles[count+2*i + 1]; minx = min(minx, x); miny = min(miny, y); maxx = max(maxx, x); maxy = max(maxy, y); } float d_x = .2; float d_y = .2; minx -= d_x; maxx += d_x; miny -= d_y; maxy += d_y; glBegin(GL_QUADS); glTexCoord2f(minx,miny); glVertex2f (minx,miny); glTexCoord2f(maxx,miny); glVertex2f (maxx,miny); glTexCoord2f(maxx,maxy); glVertex2f (maxx,maxy); glTexCoord2f(minx,maxy); glVertex2f (minx,maxy); glEnd(); count+=2; } glUseProgramObjectARB(0); //_fountain->draw(); draw_phase_loop(); draw_circle_spectrum(); draw_spectrum(); // pop glPopMatrix(); // flush! //glFlush( ); // swap the double buffer SDL_GL_SwapBuffers( ); } void Visualizer::update(double tick_time) { _fountain->update(tick_time, _buffer_size/2, _spectrum_buffer_1, _spectrum_buffer_2); } void Visualizer::draw_phase_loop() { for (int j = 3; j > 0; j--) { glLineWidth( j ); glBegin( GL_LINE_STRIP ); // loop through global buffer for( int i = 0; i < _buffer_size/2; i++ ) { glColor4f( 1,1,1, .1); glVertex2f( .5 * _audio_buffer_2[i + _buffer_size/2] + .7, .5 * _audio_buffer_1[i] + .2); } for( int i = 0; i < _buffer_size/2; i++ ) { glColor4f( 1,1,1, .1); glVertex2f( .5 * _audio_buffer_1[i] + .7, .5 * _audio_buffer_1[i+ _buffer_size/2] + .2); } glEnd(); } } void Visualizer::draw_circle_spectrum() { //glEnable(GL_POINT_SMOOTH); /* It KILLS fragment shader performance */ //glHint(GL_POINT_SMOOTH_HINT, GL_NICEST); glPointSize(5); glBegin( GL_POINTS ); for( int i = 0; i < _buffer_size/8; i++ ) { // set the color glColor4f( 1,0,0, .5); // set the next vertex glVertex2f( .2 * sin(PI * 8 * i/_buffer_size) - .8 , 4 * _spectrum_buffer_1[i] + .2 * cos(PI * 8 * i/_buffer_size) + .2); } for( int i = 0; i < _buffer_size/8; i++ ) { // set the color glColor4f( 1,0,0, .5); // set the next vertex glVertex2f( - .2 * sin(PI * 8 * i/_buffer_size) - .8 , 4 * _spectrum_buffer_1[i] + .2 * cos(PI * 8 * i/_buffer_size) + .2); } glEnd(); } void Visualizer::draw_spectrum() { glBegin( GL_LINE_STRIP ); for( int i = 0; i < _buffer_size/2; i++ ) { // set the color glColor3f( 5 * _spectrum_buffer_1[i], 1 - 10 * _spectrum_buffer_1[i], 20*_spectrum_buffer_1[i] ); // set the next vertex glVertex2f( - log(i+1)/log(_buffer_size/2), 5*_spectrum_buffer_1[i] - .9); // increment x } glEnd(); glBegin( GL_LINE_STRIP ); for( int i = 0; i < _buffer_size/2; i++ ) { // set the color glColor3f( 5 * _spectrum_buffer_1[i], 1 - 10 * _spectrum_buffer_1[i], 20*_spectrum_buffer_1[i] ); // set the next vertex glVertex2f( log(i+1)/log(_buffer_size/2), 5*_spectrum_buffer_1[i] - .9); // increment x } // done glEnd(); } // quick hack to count fps int fps_count = 0; double fps_time = 0; void count_frame(double time_elapsed) { fps_time += time_elapsed; fps_count++; if(fps_time > 1) { fps_time -= 1; cout << "fps = " << fps_count << "\n"; fps_count = 0; } } TickTimer fps_tt; int Visualizer::main_loop() { bool quit = false; SDL_Event event; TickTimer tick_timer; while (!quit) { if (SDL_PollEvent(&event)) { switch (event.type) { // case SDL_MOUSEBUTTONDOWN: // mouse_down_event(event.button); // break; // case SDL_MOUSEBUTTONUP: // mouse_up_event(event.button); // break; // case SDL_MOUSEMOTION: // mouse_motion_event(event.motion); // break ; // if( event.type == SDL_VIDEORESIZE case SDL_VIDEORESIZE: reshapeFunc(event.resize); break; case SDL_KEYDOWN: //case SDL_KEYUP: if (keyboard_event(event.key) == SDL_QUIT) quit = true; break; case SDL_QUIT: quit = true; break; } } else { fps_tt.tick(); double time_elapsed = tick_timer.tick(); update(time_elapsed); render(time_elapsed); count_frame(time_elapsed); double sleep_time = 1.0/TARGET_FPS - fps_tt.tick(); if (sleep_time > 0) usleep(sleep_time * 1000000); } } clean_up(); return(0); }