You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 

226 lines
6.4 KiB

#include <cstdlib>
#include <cstdio>
#include <cmath>
#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