|
|
|
@ -3,6 +3,8 @@ |
|
|
|
|
#include <iostream> |
|
|
|
|
|
|
|
|
|
#include "portaudio.h" |
|
|
|
|
#include "portmidi.h" |
|
|
|
|
#include "porttime.h" |
|
|
|
|
|
|
|
|
|
#include "oscillator.h" |
|
|
|
|
#include "svf.h" |
|
|
|
@ -10,14 +12,20 @@ |
|
|
|
|
using namespace std; |
|
|
|
|
|
|
|
|
|
#define SAMPLE_RATE 48000 |
|
|
|
|
#define NUM_OSCS 7 |
|
|
|
|
|
|
|
|
|
float noteToFreq(int note) { |
|
|
|
|
return 440 * pow(2, (note - 69) / 12.0); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
typedef struct { |
|
|
|
|
Oscillator oscs[8]; |
|
|
|
|
Oscillator oscs[NUM_OSCS]; |
|
|
|
|
SVFx2 filter; |
|
|
|
|
float time; |
|
|
|
|
PmStream* pmStream; |
|
|
|
|
} State; |
|
|
|
|
|
|
|
|
|
static int audioCallback( |
|
|
|
|
static int paCallback( |
|
|
|
|
const void *inputBuffer, |
|
|
|
|
void *outputBuffer, |
|
|
|
|
unsigned long framesPerBuffer, |
|
|
|
@ -30,12 +38,12 @@ static int audioCallback( |
|
|
|
|
|
|
|
|
|
for(int i = 0; i < framesPerBuffer; i++) { |
|
|
|
|
float value = 0; |
|
|
|
|
for(int j = 0; j < 8; ++j) { |
|
|
|
|
for(int j = 0; j < NUM_OSCS; ++j) { |
|
|
|
|
value += state->oscs[j].tick(); |
|
|
|
|
} |
|
|
|
|
value *= 0.125; |
|
|
|
|
value /= NUM_OSCS; |
|
|
|
|
value *= 0.5; |
|
|
|
|
state->filter.setFrequency(0.21 + 0.20 * sin(M_PI * state->time)); |
|
|
|
|
state->filter.setFrequency(0.21 + 0.20 * cos(M_PI * state->time)); |
|
|
|
|
value = state->filter.tick(value); |
|
|
|
|
*out++ = value; |
|
|
|
|
*out++ = value; |
|
|
|
@ -45,17 +53,61 @@ static int audioCallback( |
|
|
|
|
return 0; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static void ptCallback(PtTimestamp timestamp, void* userData) { |
|
|
|
|
State* state = (State*) userData; |
|
|
|
|
PmEvent events[8]; |
|
|
|
|
int numEvents = Pm_Read(state->pmStream, events, 8); |
|
|
|
|
for(int i = 0; i < numEvents; ++i) { |
|
|
|
|
PmMessage msg = events[i].message; |
|
|
|
|
|
|
|
|
|
int cmd = (msg >> 4) & 0xF; |
|
|
|
|
int chan = msg & 0xF; |
|
|
|
|
|
|
|
|
|
if(cmd == 0x9) { |
|
|
|
|
int vel = (msg >> 16) & 0x7F; |
|
|
|
|
if(vel > 0) { |
|
|
|
|
int note = (msg >> 8) & 0x7F; |
|
|
|
|
float centreFreq = noteToFreq(note); |
|
|
|
|
float spreadCoeff = 0.004; |
|
|
|
|
for(int osc = 0, pos = -3; pos <= 3; ++osc, ++pos) { |
|
|
|
|
float freq = centreFreq * (1 + pos * spreadCoeff); |
|
|
|
|
state->oscs[osc].phaseStep = (freq * 2 * M_PI) / SAMPLE_RATE; |
|
|
|
|
} |
|
|
|
|
state->time = 0; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
cout << "MIDI:"; |
|
|
|
|
for(int j = 0; j < 24; j += 8) { |
|
|
|
|
cout << " " << hex << ((msg >> j) & 0xFF); |
|
|
|
|
|
|
|
|
|
} |
|
|
|
|
cout << endl; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
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); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static State state; |
|
|
|
|
int main(void) { |
|
|
|
|
for(int i = 0; i < 8; ++i) { |
|
|
|
|
static State state; |
|
|
|
|
for(int i = 0; i < 7; ++i) { |
|
|
|
|
float freq = 440 + i; |
|
|
|
|
state.oscs[i].phaseStep = (freq * 2 * M_PI) / SAMPLE_RATE; |
|
|
|
|
state.oscs[i].phase = PIx2 * (float) rand() / RAND_MAX; |
|
|
|
@ -63,78 +115,121 @@ int main(void) { |
|
|
|
|
state.filter.setMode(SVFx2::MODE_LP); |
|
|
|
|
state.filter.setQ(1); |
|
|
|
|
state.time = 0; |
|
|
|
|
|
|
|
|
|
PaStream *stream; |
|
|
|
|
PaError err; |
|
|
|
|
|
|
|
|
|
err = Pa_Initialize(); |
|
|
|
|
if(err != paNoError) { |
|
|
|
|
handlePAError(err); |
|
|
|
|
PmError pmErr = Pm_Initialize(); |
|
|
|
|
if(pmErr != pmNoError) { |
|
|
|
|
handlePMError(pmErr); |
|
|
|
|
} |
|
|
|
|
PaError paErr = Pa_Initialize(); |
|
|
|
|
if(paErr != paNoError) { |
|
|
|
|
handlePAError(paErr); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
PaHostApiIndex deviceCount = Pa_GetDeviceCount(); |
|
|
|
|
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(;;) { |
|
|
|
|
string input; |
|
|
|
|
getline(cin, input); |
|
|
|
|
try { |
|
|
|
|
pmDeviceIndex = stoi(input); |
|
|
|
|
if(pmDeviceIndex >= 0 && pmDeviceIndex < pmDeviceCount) { |
|
|
|
|
break; |
|
|
|
|
} |
|
|
|
|
} catch(...) { } |
|
|
|
|
cout << "Invalid selection" << endl;
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
PaHostApiIndex paDeviceCount = Pa_GetDeviceCount(); |
|
|
|
|
cout << "Select audio output device:" << endl; |
|
|
|
|
for(int i = 0; i < deviceCount; ++i) { |
|
|
|
|
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; |
|
|
|
|
} |
|
|
|
|
string input; |
|
|
|
|
PaHostApiIndex deviceIndex; |
|
|
|
|
PaHostApiIndex paDeviceIndex; |
|
|
|
|
for(;;) { |
|
|
|
|
string input; |
|
|
|
|
getline(cin, input); |
|
|
|
|
try { |
|
|
|
|
deviceIndex = stoi(input); |
|
|
|
|
if(deviceIndex >= 0 && deviceIndex < deviceCount) { |
|
|
|
|
paDeviceIndex = stoi(input); |
|
|
|
|
if(paDeviceIndex >= 0 && paDeviceIndex < paDeviceCount) { |
|
|
|
|
break; |
|
|
|
|
} |
|
|
|
|
} catch(...) { } |
|
|
|
|
cout << "Invalid selection" << endl;
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
Pt_Start(1, ptCallback, &state); |
|
|
|
|
|
|
|
|
|
PmError pmError = Pm_OpenInput( |
|
|
|
|
&state.pmStream, |
|
|
|
|
pmDeviceIndex, |
|
|
|
|
NULL, |
|
|
|
|
256, |
|
|
|
|
NULL, |
|
|
|
|
NULL); |
|
|
|
|
if(pmErr != pmNoError) { |
|
|
|
|
handlePMError(pmErr); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/* Open an audio I/O stream. */ |
|
|
|
|
PaStreamParameters streamParams; |
|
|
|
|
streamParams.device = deviceIndex; |
|
|
|
|
streamParams.channelCount = 2; |
|
|
|
|
streamParams.sampleFormat = paFloat32; |
|
|
|
|
streamParams.suggestedLatency = 0; |
|
|
|
|
streamParams.hostApiSpecificStreamInfo = NULL; |
|
|
|
|
err = Pa_OpenStream( |
|
|
|
|
&stream, |
|
|
|
|
PaStream *paStream; |
|
|
|
|
PaStreamParameters paStreamParams; |
|
|
|
|
paStreamParams.device = paDeviceIndex; |
|
|
|
|
paStreamParams.channelCount = 2; |
|
|
|
|
paStreamParams.sampleFormat = paFloat32; |
|
|
|
|
paStreamParams.suggestedLatency = 0; |
|
|
|
|
paStreamParams.hostApiSpecificStreamInfo = NULL; |
|
|
|
|
paErr = Pa_OpenStream( |
|
|
|
|
&paStream, |
|
|
|
|
NULL, /* no input channels */ |
|
|
|
|
&streamParams, /* stereo output */ |
|
|
|
|
&paStreamParams, /* stereo output */ |
|
|
|
|
SAMPLE_RATE, |
|
|
|
|
0, /* frames per buffer */ |
|
|
|
|
0, /* frames per buffer */ |
|
|
|
|
paNoFlag, |
|
|
|
|
audioCallback, |
|
|
|
|
paCallback, |
|
|
|
|
&state); |
|
|
|
|
if(err != paNoError) { |
|
|
|
|
handlePAError(err); |
|
|
|
|
if(paErr != paNoError) { |
|
|
|
|
handlePAError(paErr); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
err = Pa_StartStream(stream); |
|
|
|
|
if(err != paNoError) {
|
|
|
|
|
handlePAError(err); |
|
|
|
|
paErr = Pa_StartStream(paStream); |
|
|
|
|
if(paErr != paNoError) {
|
|
|
|
|
handlePAError(paErr); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
const PaStreamInfo *streamInfo = Pa_GetStreamInfo(stream); |
|
|
|
|
cout << "Latency: " << round(1000 * streamInfo->outputLatency) << " ms" << endl; |
|
|
|
|
const PaStreamInfo *paStreamInfo = Pa_GetStreamInfo(paStream); |
|
|
|
|
cout << "Latency: " << round(1000 * paStreamInfo->outputLatency) << " ms" << endl; |
|
|
|
|
|
|
|
|
|
/* Wait for Return key */ |
|
|
|
|
cout << "Running - press Enter to exit" << endl; |
|
|
|
|
getchar(); |
|
|
|
|
|
|
|
|
|
err = Pa_StopStream(stream); |
|
|
|
|
if(err != paNoError) { |
|
|
|
|
handlePAError(err); |
|
|
|
|
paErr = Pa_StopStream(paStream); |
|
|
|
|
if(paErr != paNoError) { |
|
|
|
|
handlePAError(paErr); |
|
|
|
|
} |
|
|
|
|
err = Pa_CloseStream(stream); |
|
|
|
|
if(err != paNoError) { |
|
|
|
|
handlePAError(err); |
|
|
|
|
paErr = Pa_CloseStream(paStream); |
|
|
|
|
if(paErr != paNoError) { |
|
|
|
|
handlePAError(paErr); |
|
|
|
|
} |
|
|
|
|
pmErr = Pm_Close(state.pmStream); |
|
|
|
|
if(pmErr != pmNoError) { |
|
|
|
|
handlePMError(pmErr); |
|
|
|
|
} |
|
|
|
|
Pa_Terminate(); |
|
|
|
|
cout << "Terminating" << endl; |
|
|
|
|
|
|
|
|
|
return err; |
|
|
|
|
return 0; |
|
|
|
|
} |