#include #include #include #include "synth/globals.h" #include "synthapp.h" #include "synthframe.h" using namespace std; // PortAudio callback for audio I/O bool firstPACallback = true; static int paCallback( const void *inputBuffer, void *outputBuffer, unsigned long framesPerBuffer, const PaStreamCallbackTimeInfo* timeInfo, PaStreamCallbackFlags statusFlags, void *userData ) { SynthApp *app = (SynthApp*) userData; if(firstPACallback) { printf("Buffer size is %ld frames\n", framesPerBuffer); firstPACallback = false; } PmEvent events[16]; int numEvents = Pm_Read(app->pmStream, events, 16); 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 int note = Pm_MessageData1(msg); app->synth.noteOff(ch, note); } else if(type == 0x9) { // Note On int note = Pm_MessageData1(msg); int vel = Pm_MessageData2(msg); app->synth.noteOn(ch, note, vel); } else if(type == 0xB) { // Controller Message int cc = Pm_MessageData1(msg); int val = Pm_MessageData2(msg); app->synth.control(ch, cc, val); } } float *out = (float*) outputBuffer; for(int i = 0; i < framesPerBuffer; i++) { frame f = app->synth.tick(); *out++ = f.left; *out++ = f.right; } return 0; } void handlePMError(PmError err) { 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) { 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(); putchar('\n'); printf("Select MIDI input device:\n\n"); for(int i = 0; i < pmDeviceCount; ++i) { const PmDeviceInfo* deviceInfo = Pm_GetDeviceInfo(i); if(!deviceInfo->input) { continue; } printf("%d. %s\n", i, deviceInfo->name); } putchar('\n'); int pmDeviceIndex; for(;;) { // loop for input until valid choice is made printf("Number: "); char input[4]; fgets(input, 4, stdin); //const char* input = "1"; try { pmDeviceIndex = stoi(input); if( pmDeviceIndex >= 0 && pmDeviceIndex < pmDeviceCount && Pm_GetDeviceInfo(pmDeviceIndex)->input) { break; } } catch(...) { } // stoi() throws an exeption for invalid integers printf("Invalid selection\n"); } putchar('\n'); PaHostApiIndex paDeviceCount = Pa_GetDeviceCount(); printf("Select audio output device:\n\n"); for(int i = 0; i < paDeviceCount; ++i) { const PaDeviceInfo* deviceInfo = Pa_GetDeviceInfo(i); if(deviceInfo->maxOutputChannels < 2) { continue; } printf("%d. %s (%s)\n", i, deviceInfo->name, Pa_GetHostApiInfo(deviceInfo->hostApi)->name); } putchar('\n'); PaHostApiIndex paDeviceIndex; for(;;) { // loop for input until valid choice is made printf("Number: "); char input[4]; fgets(input, 4, stdin); //const char* input = "5"; try { paDeviceIndex = stoi(input); if( paDeviceIndex >= 0 && paDeviceIndex < paDeviceCount && Pa_GetDeviceInfo(paDeviceIndex)->maxOutputChannels >= 2) { break; } } catch(...) { } // stoi() throws an exeption for invalid integers printf("Invalid selection\n"); } // Open MIDI input pmErr = Pm_OpenInput( &pmStream, pmDeviceIndex, NULL, 8, NULL, NULL); if(pmErr != pmNoError) { handlePMError(pmErr); } const PaDeviceInfo* deviceInfo = Pa_GetDeviceInfo(paDeviceIndex); putchar('\n'); printf("Default sample rate is %.0f Hz\n", deviceInfo->defaultSampleRate); printf("Default low input latency is %.0f samples\n", deviceInfo->defaultSampleRate * deviceInfo->defaultLowOutputLatency); putchar('\n'); // Configure/start audio output stream PaStreamParameters paStreamParams; paStreamParams.device = paDeviceIndex; paStreamParams.channelCount = 2; paStreamParams.sampleFormat = paFloat32; paStreamParams.suggestedLatency = deviceInfo->defaultLowOutputLatency; paStreamParams.hostApiSpecificStreamInfo = NULL; paErr = Pa_OpenStream( &paStream, NULL, // no input channels &paStreamParams, // stereo output deviceInfo->defaultSampleRate, 0, // frames per buffer paNoFlag, paCallback, this); if(paErr != paNoError) { handlePAError(paErr); } paErr = Pa_StartStream(paStream); if(paErr != paNoError) { handlePAError(paErr); } const PaStreamInfo *paStreamInfo = Pa_GetStreamInfo(paStream); printf("Reported latency is %d frames\n", (int) round(SAMPLE_RATE * paStreamInfo->outputLatency)); wxFrame *frame = new SynthFrame(); frame->Show(true); return true; } int SynthApp::OnExit() { 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(); printf("\nTerminating\n"); return 0; } wxIMPLEMENT_APP(SynthApp); #ifdef WIN32 int main(int argc, char** argv) { HMODULE module = GetModuleHandle(NULL); LPSTR lpCmdLine = (LPSTR) calloc(1, 1); return WinMain(module, NULL, lpCmdLine, SW_SHOW); } #endif