#include #include #include #include #include "synthapp.h" #include "synthframe.h" using namespace std; // PortAudio callback for audio I/O static int paCallback( const void *inputBuffer, void *outputBuffer, unsigned long framesPerBuffer, const PaStreamCallbackTimeInfo* timeInfo, PaStreamCallbackFlags statusFlags, void *userData ) { Synth *state = (Synth*) userData; float *out = (float*)outputBuffer; for(int i = 0; i < framesPerBuffer; i++) { float value = 0; *out++ = value; *out++ = value; } return 0; } // PortTimer callback for MIDI I/O static void ptCallback(PtTimestamp timestamp, void* userData) { SynthApp* app = (SynthApp*) userData; PmEvent events[8]; int numEvents = Pm_Read(app->pmStream, events, 8); for(int i = 0; i < numEvents; ++i) { PmMessage msg = events[i].message; int type = (msg >> 4) & 0xF; int ch = msg & 0xF; if(type == 0x8 || (type == 0x9 && Pm_MessageData2(msg) == 0)) { // Note Off } else if(type == 0x9) { // Note On int note = Pm_MessageData1(msg); int vel = Pm_MessageData2(msg); } else if(type == 0xB) { // Controller Message int cc = Pm_MessageData1(msg); int val = Pm_MessageData2(msg); if(cc == 1 || cc == 74) { // Sound Controller 5: Brightness } } cout << "MIDI:"; for(int j = 0; j < 24; j += 8) { cout << " " << hex << ((msg >> j) & 0xFF); } cout << endl; } } void handlePMError(PmError err) { Pt_Stop(); Pa_Terminate(); Pm_Terminate(); cerr << "PortMidi error" << endl; cerr << "Error number: " << err << endl; cerr << "Error message: " << Pm_GetErrorText(err) << endl; exit(err); } void handlePAError(PaError err) { Pt_Stop(); Pa_Terminate(); Pm_Terminate(); cerr << "PortAudio error" << endl; cerr << "Error number: " << err << endl; cerr << "Error message: " << Pa_GetErrorText(err) << endl; exit(err); } bool SynthApp::OnInit() { // Initialise PortMIDI and PortAudio libraries PmError pmErr = Pm_Initialize(); if(pmErr != pmNoError) { handlePMError(pmErr); } PaError paErr = Pa_Initialize(); if(paErr != paNoError) { handlePAError(paErr); } int pmDeviceCount = Pm_CountDevices(); cout << "Select MIDI input device:" << endl; for(int i = 0; i < pmDeviceCount; ++i) { const PmDeviceInfo* deviceInfo = Pm_GetDeviceInfo(i); if(!deviceInfo->input) { continue; } cout << i << ". " << deviceInfo->name << endl; } int pmDeviceIndex; for(;;) { // loop for input until valid choice is made string input; getline(cin, input); try { pmDeviceIndex = stoi(input); if( pmDeviceIndex >= 0 && pmDeviceIndex < pmDeviceCount && Pm_GetDeviceInfo(pmDeviceIndex)->input) { break; } } catch(...) { } // stoi() throws an exeption for invalid integers cout << "Invalid selection" << endl; } PaHostApiIndex paDeviceCount = Pa_GetDeviceCount(); cout << "Select audio output device:" << endl; for(int i = 0; i < paDeviceCount; ++i) { const PaDeviceInfo* deviceInfo = Pa_GetDeviceInfo(i); if(deviceInfo->maxOutputChannels < 2) { continue; } cout << i << ". " << deviceInfo->name << " (" << Pa_GetHostApiInfo(deviceInfo->hostApi)->name << ")" << endl; } PaHostApiIndex paDeviceIndex; for(;;) { // loop for input until valid choice is made string input; getline(cin, input); try { paDeviceIndex = stoi(input); if( paDeviceIndex >= 0 && paDeviceIndex < paDeviceCount && Pa_GetDeviceInfo(paDeviceIndex)->maxOutputChannels >= 2) { break; } } catch(...) { } // stoi() throws an exeption for invalid integers cout << "Invalid selection" << endl; } // Start MIDI timer and open MIDI input Pt_Start(1, ptCallback, this); PmError pmError = Pm_OpenInput( &pmStream, pmDeviceIndex, NULL, 8, NULL, NULL); if(pmErr != pmNoError) { handlePMError(pmErr); } // Configure/start audio output stream PaStreamParameters paStreamParams; paStreamParams.device = paDeviceIndex; paStreamParams.channelCount = 2; paStreamParams.sampleFormat = paFloat32; paStreamParams.suggestedLatency = 512.0 / SAMPLE_RATE; paStreamParams.hostApiSpecificStreamInfo = NULL; paErr = Pa_OpenStream( &paStream, NULL, // no input channels &paStreamParams, // stereo output SAMPLE_RATE, 0, // frames per buffer paNoFlag, paCallback, &synth); if(paErr != paNoError) { handlePAError(paErr); } paErr = Pa_StartStream(paStream); if(paErr != paNoError) { handlePAError(paErr); } const PaStreamInfo *paStreamInfo = Pa_GetStreamInfo(paStream); cout << "Latency: " << round(1000 * paStreamInfo->outputLatency) << " ms" << endl; wxFrame *frame = new SynthFrame(); frame->Show(true); return true; } int SynthApp::OnExit() { Pt_Stop(); PaError paErr = Pa_StopStream(paStream); if(paErr != paNoError) { handlePAError(paErr); } paErr = Pa_CloseStream(paStream); if(paErr != paNoError) { handlePAError(paErr); } PmError pmErr = Pm_Close(pmStream); if(pmErr != pmNoError) { handlePMError(pmErr); } Pa_Terminate(); cout << "Terminating" << endl; return 0; } wxIMPLEMENT_APP(SynthApp);