Added rudimentary MIDI support

main
Thor 1 year ago
parent 97a41f0ca3
commit 391d96b572
  1. 2
      Makefile
  2. 183
      main.cpp

@ -1,5 +1,5 @@
CPPFLAGS=-std=c++11 -Ofast
LDLIBS=-lstdc++ -lportaudio
LDLIBS=-lstdc++ -lportaudio -lportmidi
all: main

@ -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;
}
Loading…
Cancel
Save