#include "RtAudio.h" #include "RtMidi.h" #include "Delay.h" #include "KSString.h" #include "KSInstrument.h" #include "JCRev.h" #include "osc/OscReceivedElements.h" #include "osc/OscPacketListener.h" #include "osc/OscOutboundPacketStream.h" #include "ip/UdpSocket.h" #include #include #include #include #include #include "calories_def.h" #include "util_thread.h" using namespace std; #define SAMPLE double #define MY_PI 3.14159265358979 #define MY_SRATE 44100.0 //#define ADDRESS "127.0.0.1" #define PORT 7000 #define OUTPUT_BUFFER_SIZE 1024 //Rtmidi variable and interrupt fn bool done; static void finish( int ignore ){ done = true; } //Global audio buffer SAMPLE* g_audioBuffer; //Global OSC variables int numberOfTransmitSockets; UdpTransmitSocket** transmitSockets; //Struct for storing sound generation classes struct SoundFieldGens { KSInstrument** ksInstruments; Delay** delayLines; stk::JCRev** reverbs; int numberOfTransmitSockets; }; SoundFieldGens* soundFieldGens; //Keep track of ip addresses char* selfIpAddr; char** ipAddrs; #include "SoundfieldPacketListener.h" void * osc_listener( void * ) { // instantiate listener SoundFieldPacketListener listener; UdpListeningReceiveSocket receiveSocket( IpEndpointName( IpEndpointName::ANY_ADDRESS, PORT ), &listener ); cerr << "OSC listener started on port: " << PORT << "..." << endl; // go! receiveSocket.RunUntilSigInt(); return NULL; } // Callback function int tick( void *outputBuffer, void *inputBuffer, unsigned int nBufferFrames, double streamTime, RtAudioStreamStatus status, void *soundFieldGensPointer ) { //output warning if stream status is not good (0 means no errors). if ( status ) std::cout << "Stream over/underflow detected." << std::endl; //recast output buffer pointer. Use register keyword to try to allocate for fast access. register SAMPLE *oSamples = (SAMPLE *) outputBuffer; //recast string instrument pointer SoundFieldGens* soundFieldGens = (SoundFieldGens*) soundFieldGensPointer; SAMPLE currentSample = 0.0; //Set values in output buffer for (unsigned int i = 0; i < nBufferFrames; i++) { currentSample = 0.0; for( int i = 0; i < numberOfTransmitSockets; i++) currentSample += 0.25*soundFieldGens->reverbs[i]->tick( soundFieldGens->delayLines[i]->tick(soundFieldGens->ksInstruments[i]->tick() ) ); currentSample += soundFieldGens->ksInstruments[numberOfTransmitSockets]->tick(); *oSamples++ = currentSample; } return 0; } int main(int argc, char *argv[]) { // Karplus-Strong Instrument variables ------------------------------------------ int numberOfVoices = 30; //Maximum number of voices generated at once int numberOfStrings = 88; //Number of strings to populate. double lowFreq = 27.5; //The lowest frequency to be made available on our instrument. double feedbackGain = 0.99; //Set the feedback gain to be used for all strings soundFieldGens = new SoundFieldGens; // JCRev parameter variables ---------------------------------------------------- double t60 = 2.0; // Utility variables ------------------------------------------------------------ XThread thread; string input = ""; // OSC variables ---------------------------------------------------------------- char buffer[OUTPUT_BUFFER_SIZE]; osc::OutboundPacketStream packetStream( buffer, OUTPUT_BUFFER_SIZE ); // RtMidi Variables ------------------------------------------------------------- RtMidiIn *midiin = 0; std::vector message; int nBytes, i; double stamp; unsigned int port = 0; // Rtmidi constructor try { midiin = new RtMidiIn(); } catch ( RtError &error ) { error.printMessage(); exit( EXIT_FAILURE ); } // Don't ignore sysex, timing, or active sensing messages. midiin->ignoreTypes( false, false, false ); // Install an interrupt handler function. done = false; (void) signal(SIGINT, finish); // RtAudio Parameter variables -------------------------------------------------- unsigned int inChannels = 0, outChannels = 1, fs = 44100, bufferFrames = 256, iDevice = 0, oDevice = 0, iOffset = 0, oOffset = 0; //Construct RtAudio object and make sure at least one output device is present RtAudio adac; if ( adac.getDeviceCount() < 1 ) { cerr << "\nNo audio devices found!\n"; exit( 1 ); } // Let RtAudio print messages to stderr. adac.showWarnings( true ); // Set the input and output parameter variables RtAudio::StreamParameters oParams; //Output if ( oDevice == 0 ) oParams.deviceId = adac.getDefaultOutputDevice(); else oParams.deviceId = oDevice - 1; oParams.nChannels = outChannels; oParams.firstChannel = oOffset; //options struct for setting stream options if desired RtAudio::StreamOptions options; try { adac.openStream( &oParams, NULL, RTAUDIO_FLOAT64, fs, &bufferFrames, &tick, (void*) soundFieldGens, &options ); } catch ( RtError& err ) { err.printMessage(); exit( 1 ); } cout << "Please enter your IP address" << endl; getline(cin, input); selfIpAddr = new char[25]; strcpy(selfIpAddr, input.c_str()); cout << "Please enter the number of computers you wish to send to" << endl; getline(cin, input); numberOfTransmitSockets = atoi(input.c_str()); transmitSockets = new UdpTransmitSocket*[numberOfTransmitSockets]; ipAddrs = new char*[numberOfTransmitSockets]; soundFieldGens->ksInstruments = new KSInstrument*[numberOfTransmitSockets+1]; for( int i=0; i < numberOfTransmitSockets+1; i++) soundFieldGens->ksInstruments[i] = new KSInstrument(numberOfStrings, numberOfVoices, lowFreq, feedbackGain, MY_SRATE); //We will be receiving from the same number of computers we send to // Initialize a delayline and reverb for each of these. soundFieldGens->delayLines = new Delay*[numberOfTransmitSockets]; soundFieldGens->reverbs = new stk::JCRev*[numberOfTransmitSockets]; for( int i = 0; i < numberOfTransmitSockets; i++) soundFieldGens->reverbs[i] = new stk::JCRev(t60); cout << "Enter the other computers' IP addresses and distances (in feet)." << endl; for( int i = 0; i < numberOfTransmitSockets; i++) { cout << "IP Address " << (i+1) << ": " ; getline(cin, input); transmitSockets[i] = new UdpTransmitSocket( IpEndpointName( input.c_str(), PORT ) ); ipAddrs[i] = new char[25]; strcpy(ipAddrs[i], input.c_str()); cout << "Approximate Distance (a double type value) " << (i+1) << ": "; getline(cin, input); soundFieldGens->delayLines[i] = new Delay( ceil((atof(input.c_str()) / 1000.0 * MY_SRATE)) + 2 , (atof(input.c_str()) / 1000.0 * MY_SRATE) ); } // Try to open the audio stream try { adac.startStream(); } catch ( RtError& err ) { err.printMessage(); goto cleanup; } // Try to open midi input port try { midiin->openPort( port ); } catch ( RtError &error ) { error.printMessage(); goto cleanup; } // start listener thread.start( osc_listener ); // wait for MIDI messages here while ( !done ) { stamp = midiin->getMessage( &message ); nBytes = message.size(); if (nBytes > 0 ) { //If the message is coming from a key and is in the range of instantiated notes if ( (int)message[0] == 144 && (int)message[1] < numberOfStrings ) { if ((int)message[2] == 0) //If we receive a 0 velocity message, stop playing that note { //Stop on the local instrument soundFieldGens->ksInstruments[numberOfTransmitSockets]->markInactive((int)message[1]); //Send stop message to all transmitSockets (a couple times to be safe) packetStream.Clear(); packetStream << osc::BeginMessage( "/noteOff" ) << selfIpAddr << (int)message[1] << osc::EndMessage; for( int i = 0; i < numberOfTransmitSockets; i++ ) { transmitSockets[i]->Send( packetStream.Data(), packetStream.Size() ); transmitSockets[i]->Send( packetStream.Data(), packetStream.Size() ); transmitSockets[i]->Send( packetStream.Data(), packetStream.Size() ); } } else //Otherwise, pluck that string { //Pluck on local instrument soundFieldGens->ksInstruments[numberOfTransmitSockets]->pluckString((int)message[1], (int)message[2]); //Send pluck message to all transmitSockets packetStream.Clear(); packetStream << osc::BeginMessage( "/noteOn" ) << selfIpAddr << (int)message[1] << (int)message[2] << osc::EndMessage; for( int i = 0; i < numberOfTransmitSockets; i++ ) transmitSockets[i]->Send( packetStream.Data(), packetStream.Size() ); } } else if ( (int)message[0] == 128 && (int)message[1] < numberOfStrings ) //noteOff { //Stop on the local instrument soundFieldGens->ksInstruments[numberOfTransmitSockets]->markInactive((int)message[1]); //Send stop message to all transmitSockets (a couple times to be safe) packetStream.Clear(); packetStream << osc::BeginMessage( "/noteOff" ) << selfIpAddr << (int)message[1] << osc::EndMessage; for( int i = 0; i < numberOfTransmitSockets; i++ ) { transmitSockets[i]->Send( packetStream.Data(), packetStream.Size() ); transmitSockets[i]->Send( packetStream.Data(), packetStream.Size() ); transmitSockets[i]->Send( packetStream.Data(), packetStream.Size() ); } } } usleep( 10000 ); } // If we get here, stop the stream. try { adac.stopStream(); } catch( RtError& err ) { err.printMessage(); } cleanup: if ( adac.isStreamOpen() ) { adac.closeStream(); for (int i = 0; i < numberOfTransmitSockets; i++) delete transmitSockets[i]; delete []transmitSockets; for (int i = 0; i < numberOfTransmitSockets; i++) { delete soundFieldGens->ksInstruments[i]; delete soundFieldGens->delayLines[i]; delete soundFieldGens->reverbs[i]; } delete soundFieldGens->ksInstruments[numberOfTransmitSockets]; delete soundFieldGens->ksInstruments; delete soundFieldGens->delayLines; delete soundFieldGens->reverbs; } delete soundFieldGens; delete midiin; return 0; }