forked from klang-modular/synthapp
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.
140 lines
3.5 KiB
140 lines
3.5 KiB
#include <cstdlib>
|
|
#include <cmath>
|
|
#include <iostream>
|
|
|
|
#include "portaudio.h"
|
|
|
|
#include "oscillator.h"
|
|
#include "svf.h"
|
|
|
|
using namespace std;
|
|
|
|
#define SAMPLE_RATE 48000
|
|
|
|
typedef struct {
|
|
Oscillator oscs[8];
|
|
SVFx2 filter;
|
|
float time;
|
|
} State;
|
|
|
|
static int audioCallback(
|
|
const void *inputBuffer,
|
|
void *outputBuffer,
|
|
unsigned long framesPerBuffer,
|
|
const PaStreamCallbackTimeInfo* timeInfo,
|
|
PaStreamCallbackFlags statusFlags,
|
|
void *userData ) {
|
|
|
|
State *state = (State*) userData;
|
|
float *out = (float*)outputBuffer;
|
|
|
|
for(int i = 0; i < framesPerBuffer; i++) {
|
|
float value = 0;
|
|
for(int j = 0; j < 8; ++j) {
|
|
value += state->oscs[j].tick();
|
|
}
|
|
value *= 0.125;
|
|
value *= 0.5;
|
|
state->filter.setFrequency(0.21 + 0.20 * sin(M_PI * state->time));
|
|
value = state->filter.tick(value);
|
|
*out++ = value;
|
|
*out++ = value;
|
|
state->time += 1.0 / SAMPLE_RATE;
|
|
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
void handlePAError(PaError err) {
|
|
Pa_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) {
|
|
float freq = 440 + i;
|
|
state.oscs[i].phaseStep = (freq * 2 * M_PI) / SAMPLE_RATE;
|
|
state.oscs[i].phase = PIx2 * (float) rand() / RAND_MAX;
|
|
}
|
|
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);
|
|
}
|
|
|
|
PaHostApiIndex deviceCount = Pa_GetDeviceCount();
|
|
cout << "Select audio output device:" << endl;
|
|
for(int i = 0; i < deviceCount; ++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;
|
|
for(;;) {
|
|
getline(cin, input);
|
|
try {
|
|
deviceIndex = stoi(input);
|
|
if(deviceIndex >= 0 && deviceIndex < deviceCount) {
|
|
break;
|
|
}
|
|
} catch(...) { }
|
|
cout << "Invalid selection" << endl;
|
|
}
|
|
|
|
/* 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,
|
|
NULL, /* no input channels */
|
|
&streamParams, /* stereo output */
|
|
SAMPLE_RATE,
|
|
0, /* frames per buffer */
|
|
paNoFlag,
|
|
audioCallback,
|
|
&state);
|
|
if(err != paNoError) {
|
|
handlePAError(err);
|
|
}
|
|
|
|
err = Pa_StartStream(stream);
|
|
if(err != paNoError) {
|
|
handlePAError(err);
|
|
}
|
|
|
|
const PaStreamInfo *streamInfo = Pa_GetStreamInfo(stream);
|
|
cout << "Latency: " << round(1000 * streamInfo->outputLatency) << " ms" << endl;
|
|
|
|
/* Wait for Return key */
|
|
getchar();
|
|
|
|
err = Pa_StopStream(stream);
|
|
if(err != paNoError) {
|
|
handlePAError(err);
|
|
}
|
|
err = Pa_CloseStream(stream);
|
|
if(err != paNoError) {
|
|
handlePAError(err);
|
|
}
|
|
Pa_Terminate();
|
|
cout << "Terminating" << endl;
|
|
|
|
return err;
|
|
} |