// Emily Graber // 256a final fall, 2014 // ImprovArt #include #include #include "rtmidi/RtMidi.h" #include "x-audio.h" #include "y-fluidsynth.h" #include "midi_monster.h" #include #include #include #include using namespace std; #define ON_OFF_BYTE 0 #define PITCH_BYTE 1 #define VELOCITY_BYTE 2 #define NOTE_ON 144 #define NOTE_OFF 128 //global variables unsigned int srate = 44100; unsigned int g_frameSize = 256; unsigned int g_channels = 2; // dont change this from 2...fluidsynth synthesizes 2 channels SAMPLE* g_buffer; YFluidSynth* g_synth; RtMidiIn *midiin; MidiMonster *mm; GLfloat g_windowWidth = 1280; GLfloat g_windowHeight = 720; long g_last_width = g_windowWidth; long g_last_height = g_windowHeight; bool g_fullscreen = false; float g_t = 0; //prototypes bool chooseMidiPort( RtMidiIn *rtmidi ); void midi_callback( double deltatime, std::vector< unsigned char > *message, void * ); void initGlut( int argc, char ** argv ); void initialize_graphics(); void reshapeFunc( int w, int h ); void keyboardFunc( unsigned char key, int x, int y ); void specialFunc( int key, int x, int y ); void mouseFunc( int button, int state, int x, int y ); void idleFunc(); void displayFunc(); bool initAudio(); bool startAudio(); void audio_callback( SAMPLE * buffer, unsigned int numFrames, void * userData ); void exitImprov(); ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // main ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // program entry point int main( int argc, char ** argv ){ // Minimal command-line check. //if ( argc > 2 ) usage(); try { // RtMidiIn constructor midiin = new RtMidiIn(); // Call function to select port. if ( chooseMidiPort( midiin ) == false ) exitImprov(); // Set our callback function. This should be done immediately after // opening the port to avoid having incoming messages written to the // queue instead of sent to the callback function. midiin->setCallback( &midi_callback ); // Don't ignore sysex, timing, or active sensing messages. midiin->ignoreTypes( false, false, false ); std::cout << "\nReading MIDI input ... press to quit.\n"; //char input; //std::cin.get(input); } catch ( RtMidiError &error ) { error.printMessage(); } // init glut initGlut( argc, argv ); // init gfx; initialize_graphics(); // init audio initAudio(); // start audio startAudio(); // let GLUT handle the current thread from here glutMainLoop(); return 0; } ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // RtMidi stuff ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// bool chooseMidiPort( RtMidiIn *rtmidi ) { std::cout << "\nWould you like to open a virtual input port? [y/N] "; std::string keyHit; std::getline( std::cin, keyHit ); if ( keyHit == "y" ) { rtmidi->openVirtualPort(); return true; } std::string portName ; unsigned int i = 0, nPorts = rtmidi->getPortCount(); if ( nPorts == 0 ) { std::cout << "No input ports available!" << std::endl; return false; } if ( nPorts == 1 ) { std::cout << "\nOpening " << rtmidi->getPortName() << std::endl; } else { for ( i=0; igetPortName(i); std::cout << " Input port #" << i << ": " << portName << '\n'; } do { std::cout << "\nChoose a port number: "; std::cin >> i; } while ( i >= nPorts ); std::getline( std::cin, keyHit ); // used to clear out stdin } rtmidi->openPort( i ); return true; } void midi_callback( double deltatime, std::vector< unsigned char > *message, void * ) { unsigned int nBytes = message->size(); if ( nBytes == 1 ){ return; //if((int)message->at(0) == 254 || (int)message->at(0) == 255 ) // those messages 255 or 254 are just the midi ticker } for ( unsigned int i=0; i 0 ) std::cout << "stamp = " << deltatime << std::endl; mm->store_midi_in(message) // int pitch = (int)message->at(PITCH_BYTE); // Byte 0 = 254, stamp = 0.200047 // Byte 0 = 254, stamp = 0.200005 // Byte 0 = 254, stamp = 0.200052 // Byte 0 = 144, Byte 1 = 60, Byte 2 = 92, stamp = 0.132738 // Byte 0 = 128, Byte 1 = 60, Byte 2 = 67, stamp = 0.159947 } ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // OpenGL stuff ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // our init glut void initGlut( int argc, char ** argv ) { // initialize GLUT glutInit( &argc, (char **)argv ); // double buffer, use rgb color, enable depth buffer glutInitDisplayMode( GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH ); // initialize the window size glutInitWindowSize( g_windowWidth, g_windowHeight ); // set the window postion glutInitWindowPosition( 100, 100 ); // create the window glutCreateWindow( "ImprovArt"); // set the idle function - called when idle glutIdleFunc( idleFunc ); // set the display function - called when redrawing glutDisplayFunc( displayFunc ); // set the reshape function - called when client area changes glutReshapeFunc( reshapeFunc ); // set the keyboard function - called on keyboard events glutKeyboardFunc( keyboardFunc ); // set the mouse function - called on mouse stuff glutMouseFunc( mouseFunc ); // for arrow keys, etc glutSpecialFunc (specialFunc ); } void initialize_graphics() { // // reset time // XGfx::resetCurrentTime(); // // set simulation speed // XGfx::setDeltaFactor( 1.0f ); // // get the first // XGfx::getCurrentTime( true ); // // random // XFun::srand(); // set the GL clear color - use when the color buffer is cleared glClearColor( 0, 0, 0, 1.0f ); // set the shading model to 'smooth' glShadeModel( GL_SMOOTH ); // enable depth glEnable( GL_DEPTH_TEST ); // set the front faces of polygons glFrontFace( GL_CCW ); // set fill mode glPolygonMode( GL_FRONT_AND_BACK, GL_FILL ); // enable lighting glEnable( GL_LIGHTING ); // enable lighting for front glLightModeli( GL_FRONT_AND_BACK, GL_TRUE ); // material have diffuse and ambient lighting glColorMaterial( GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE ); // enable color glEnable( GL_COLOR_MATERIAL ); // normalize (for scaling) glEnable( GL_NORMALIZE ); // line width glLineWidth( 1 ); // enable light 0 glEnable( GL_LIGHT0 ); // clear the color buffer once glClear( GL_COLOR_BUFFER_BIT ); } //----------------------------------------------------------------------------- // Name: reshapeFunc( ) // Desc: called when window size changes //----------------------------------------------------------------------------- void reshapeFunc( int w, int h ) { // save the new window size g_windowWidth = w; g_windowHeight = h; // map the view port to the client area glViewport( 0, 0, w, h ); // set the matrix mode to project glMatrixMode( GL_PROJECTION ); // load the identity matrix glLoadIdentity( ); // set the matrix mode to modelview glMatrixMode( GL_MODELVIEW ); // load the identity matrix glLoadIdentity( ); } //----------------------------------------------------------------------------- // Name: keyboardFunc( ) // Desc: key event //----------------------------------------------------------------------------- void keyboardFunc( unsigned char key, int x, int y ) { // system keys (handled first) switch( key ) { case 'q': { exitImprov(); break; } case 's': { // toggle fullscreen // check fullscreen if( !g_fullscreen ){ g_last_width = g_windowWidth; g_last_height = g_windowHeight; glutFullScreen(); } else glutReshapeWindow( g_last_width, g_last_height ); // toggle variable value g_fullscreen = !g_fullscreen; } break; } // do a reshape since viewEyeY might have changed reshapeFunc( g_windowWidth, g_windowHeight ); // post redisplay glutPostRedisplay( ); } //----------------------------------------------------------------------------- // Name: specialFunc( ) // Desc: handles arrow stuff //----------------------------------------------------------------------------- void specialFunc( int key, int x, int y ) { // check bool handled = false; // if not handled if( !handled ) { switch( key ) { case GLUT_KEY_LEFT: //cout << "heloo" << endl; break; case GLUT_KEY_RIGHT: break; case GLUT_KEY_UP: break; case GLUT_KEY_DOWN: break; } } } //----------------------------------------------------------------------------- // Name: mouseFunc( ) // Desc: handles mouse stuff //----------------------------------------------------------------------------- void mouseFunc( int button, int state, int x, int y ) { if( button == GLUT_LEFT_BUTTON ) { // when left mouse button is down if( state == GLUT_DOWN ) { } else { } } else if ( button == GLUT_RIGHT_BUTTON ) { // when right mouse button down if( state == GLUT_DOWN ) { } else { } } else { } glutPostRedisplay( ); } //----------------------------------------------------------------------------- // Name: idleFunc( ) // Desc: callback from GLUT //----------------------------------------------------------------------------- void idleFunc() { // render the scene glutPostRedisplay( ); } //----------------------------------------------------------------------------- // Name: displayFunc( ) // Desc: callback function invoked to draw the client area //----------------------------------------------------------------------------- void displayFunc() { static float z = 0; // set the GL clear color - use when the color buffer is cleared glClearColor( 0, 0, 0, 1.0f ); // clear the color and depth buffers glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT ); // enable depth test glEnable( GL_DEPTH_TEST ); // save state glPushMatrix(); if((int)g_t % 1000 < 200){ glColor3f(0.1, 0.1, z); glutSolidTeapot(.5); z+=0.05; } // pop state glPopMatrix(); // // draw any HUD here // Globals::hud->project(); // Globals::hud->updateAll( Globals::sim->delta() ); // Globals::hud->drawAll(); // flush gl commands glFlush(); // swap the buffers glutSwapBuffers(); } ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // RtAudio stuff ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // initialize audio bool initAudio() { // initialize if( !XAudioIO::init( 0, 0, srate, g_frameSize, 2, audio_callback, NULL ) ) { // done return false; } // instantiate a YFluidsynth g_synth = new YFluidSynth(); g_synth->init( srate, 32 ); g_synth->load( "data/sfonts/rocking8m11e.sf2", "" ); g_synth->programChange( 0, 100 ); // allocate audio buffer g_buffer = new SAMPLE[g_frameSize*g_channels]; return true; } // start audio bool startAudio() { // start the audio if( !XAudioIO::start() ) { // done return false; } return true; } // audio callback // int callme( void * outputBuffer, void * inputBuffer, unsigned int numFrames, // double streamTime, RtAudioStreamStatus status, void * data ) void audio_callback( SAMPLE * buffer, unsigned int numFrames, void * userData ) { // hacks to test fluidsynth static int oldpitch = 62; g_synth->synthesize2( g_buffer, numFrames ); for(int i = 0; i < numFrames; i++){ buffer[g_channels*i] = 0.2 * sin(2*3.14159*440*g_t/(1.0*srate)) + g_buffer[g_channels*i]; // interleave samples for other channels for(int j = 1; j < g_channels; j++) buffer[g_channels*i + j] = buffer[g_channels*i]; // update the global time keeper g_t += 1.0; } // go to the midi_monster and find the next note to be played. ...base it on g_t? // hacks to test fluidsynth if((int)g_t == 0)g_synth->noteOn(0, oldpitch, 127); if((int)g_t % 1000 == 0){ g_synth->noteOff( 0, oldpitch ); g_synth->noteOn( 0, oldpitch, 127 ); } } void exitImprov(){ delete midiin; exit(0); // if( audio.isStreamOpen() ) // audio.closeStream(); }