#include "synth_engine.h"
#include "synth_math.h"
#include "lowpass.h"
#include "filter.h"
#include <string.h>
float
sin_sample(float amp, float freq, unsigned long long phase, unsigned int sample_rate)
{
return amp * sinf(2.0 * M_PI * freq * ((float)phase / (float)sample_rate));
}
float
saw_sample(float amp, float freq, unsigned long long phase, unsigned int sample_rate)
{
return amp * (0.17 * (1.0 - (2.0 * M_PI * freq * fmod((float)phase, (float)(sample_rate / (freq)))) / (float)sample_rate));
}
float
sawX_sample(float amp, float freq, float sm, unsigned long long phase, unsigned int sample_rate)
{
float dOutput = 0.0;
for (float n = 1.0; n < sm; n++)
dOutput += (sinf(n * 2.0 * M_PI * freq * ((float)phase / (float)sample_rate))) / n;
return 0.5 * amp * dOutput;
}
float
sqr_sample(float amp, float freq, float duty_cycle, unsigned long long phase, unsigned int sample_rate)
{
if (duty_cycle < 0.0001 || duty_cycle > 0.9999) {
duty_cycle = 0.5;
}
return (fmod((float)phase / (float)sample_rate, 1.0 / freq) < duty_cycle * (1.0 / freq)) ? amp : -amp;
}
float
gen0(float f, unsigned long long phase, float x, unsigned int sample_rate)
{
return sin_sample(1, f, phase, sample_rate);
/* return sqr_sample(0.1, f, 0.3, phase, sample_rate) */
/* + sqr_sample(0.1, f * 3.0 / 2.0 , 0.5, phase, sample_rate) */
/* + saw_sample(0.3, f, phase, sample_rate) */
/* + sin_sample(0.1, f, phase, sample_rate) */
/* + sin_sample(0.1, f * 5, phase, sample_rate) */
/* /\* + sin_sample(0.1, freq * 50 * 1021, phase, sample_rate) *\/ */
/* /\* + sin_sample(0.1, freq * 50 * 3531021, phase, sample_rate) *\/ */
/* + sin_sample(0.1, f * 7, phase, sample_rate); */
}
float
gen1(float f, unsigned long long phase, float x, unsigned int sample_rate)
{
return saw_sample(1, f, phase, sample_rate);
/* return sawX_sample(0.5, f, 5, phase, sample_rate) */
/* + saw_sample(0.3, 2 * f / 5, phase, sample_rate) */
/* + sin_sample(0.2, f * 5.0 / 7.0 , phase, sample_rate); */
}
float
gen2(float f, unsigned long long phase, float x, unsigned int sample_rate)
{
/* return sin_sample(0.5, f * sqrt(2) , phase, sample_rate) */
/* + sin_sample(0.5, f, phase, sample_rate); */
return sawX_sample(1, f, 15, phase, sample_rate);
}
float
gen3(float f, unsigned long long phase, float x, unsigned int sample_rate)
{
return sqr_sample(1, f, .5, phase, sample_rate);
/* return sawX_sample(0.7, f, 5, phase, sample_rate) */
/* + sin_sample(0.3, 4.0/17.0*f, phase, sample_rate); */
/* return saw_sample(0.5, f * (1 + sqrt(5)) / 2, phase, sample_rate) */
/* + sin_sample(0.3, f * x, phase, sample_rate) */
/* + sqr_sample(0.2, f * x, 0.2 * x * x, phase, sample_rate); */
}
float
lfo(float f, unsigned long long phase, unsigned int sample_rate) {
return sin_sample(1, f, phase, sample_rate);
}
int
notes_active(synth_t *synth)
{
int flag = 0;
for (int i = 0; i < MIDI_NOTES; i++) {
if (!synth->midi_note[i].active)
continue;
if (!adsr_amplitude(&synth->adsr,
(float)synth->midi_note[i].noteOn,
(float)synth->midi_note[i].noteOff,
(float)synth->midi_note[i].elapsed)
&& synth->midi_note[i].noteOff != 0) {
synth->midi_note[i].freq = 0;
synth->midi_note[i].channel = -1;
synth->midi_note[i].noteOn = -1;
synth->midi_note[i].noteOff = -1;
synth->midi_note[i].velocity = -1;
synth->midi_note[i].elapsed = -1;
synth->midi_note[i].active = 0;
} else {
flag++;
}
}
return flag;
}
float
make_sample(unsigned long long phase, void *synthData, unsigned int sample_rate, int viz)
{
synth_t *synth = (synth_t*)synthData;
float sample = 0;
/* should never be 0 if we are here */
/* Notes */
float n = notes_active(synth);
float rms = 0;
for (int i = 0; i < MIDI_NOTES; i++) {
if (!synth->midi_note[i].active)
continue;
rms += synth->midi_note[i].velocity * synth->midi_note[i].velocity;
}
rms = sqrt(rms / n);
for (int i = 0; i < MIDI_NOTES; i++) {
if (!synth->midi_note[i].active)
continue;
sample += 0.2 * rms
* adsr_amplitude(&synth->adsr,
synth->midi_note[i].noteOn,
synth->midi_note[i].noteOff,
synth->midi_note[i].elapsed)
* synth->gen[synth->geni](synth->midi_note[i].freq + synth->freq_offset, synth->midi_note[i].elapsed, synth->x, sample_rate);
}
/* filter */
if (synth->filter) {
// ALLL THE FILTERS
float cutoff = round(synth->cutoff) + 1;
cutoff = cutoff + cutoff * (synth->lfo.amp) * lfo(synth->lfo.freq, synth->lfo.elapsed, sample_rate);
if (cutoff == 0) cutoff = 0.001;
LowPass_Update(synth->resonance, cutoff, sample_rate);
sample = LowPass_Filter(sample);
update_bw_low_pass_filter(synth->fff, SAMPLE_RATE,cutoff, synth->resonance);
sample = bw_low_pass(synth->fff, sample);
}
sample = synth->gain * sample;
// band stop for high freqs
sample = bw_band_stop(synth->fff2, sample);
if (synth->clamp) sample = clamp(sample);
return sample;
}
void
add_to_viz(synth_t *synth, float sample)
{
synth->viz.wave[synth->viz.wi++] = sample;
if (synth->viz.wi >= VIZ_BUF) {
synth->viz.wi = 0;
}
}
int
sound_gen(const void *inputBuffer, void *outputBuffer,
unsigned long framesPerBuffer,
const PaStreamCallbackTimeInfo* timeInfo,
PaStreamCallbackFlags statusFlags,
void *synthData)
{
synth_t *synth = (synth_t*)synthData;
float *out = (float*)outputBuffer;
(void) timeInfo;
(void) statusFlags;
(void) inputBuffer;
float s = 0.0f;
for( unsigned long i=0; i<framesPerBuffer; i++ ) {
if (!synth->active) {
*out++ = 0.0f;
*out++ = 0.0f;
add_to_viz(synth, 0.0f);
synth->lfo.elapsed = 0;
continue;
}
if (!notes_active(synth)) {
synth->active = 0;
*out++ = 0.0f;
*out++ = 0.0f;
add_to_viz(synth, 0.0f);
continue;
}
s = make_sample(synth->n.elapsed, synth, SAMPLE_RATE, 0);
*out++ = s;
*out++ = s;
synth->lfo.elapsed++;
synth->adsr.elapsed++;
synth->n.elapsed++;
for (int i = 0; i < MIDI_NOTES; i++) {
if (!synth->midi_note[i].active)
continue;
synth->midi_note[i].elapsed++;
}
// viz
add_to_viz(synth, s);
}
return paContinue;
}
static void
StreamFinished( void* synthData )
{
synth_t *synth = (synth_t *) synthData;
}
void
init_synth(synth_t * synth)
{
synth->freq_offset = 0;
synth->gain = 1;
synth->x = 1;
synth->adsr.a = 0.0;
synth->adsr.peak = 1.0f;
synth->adsr.d = 0.3;
synth->adsr.s = 0.7;
synth->adsr.r = 0.4;
synth->adsr.elapsed = 0;
synth->lfo.freq = 1.0f;
synth->lfo.amp = 0.0f;
synth->lfo.elapsed = 0;
for (int i =0; i<MIDI_NOTES; i++) {
synth->midi_note[i].freq = 0;
synth->midi_note[i].channel = -1;
synth->midi_note[i].noteOn = -1;
synth->midi_note[i].noteOff = -1;
synth->midi_note[i].velocity = -1;
synth->midi_note[i].elapsed = -1;
synth->midi_note[i].active = 0;
synth->midi_note[i].adsr = &(synth->adsr);
}
synth->n.freq = 0;
synth->n.noteOn = 0;
synth->n.noteOff = 1;
synth->n.key = 0;
synth->n.elapsed = 0;
synth->octave = 3;
synth->poly = 0;
synth->multi = 0;
synth->filter = 1;
synth->cutoff = 22000.0f;
synth->resonance = 1.0f;
synth->clamp = 1;
synth->gen[0] = gen0;
synth->gen[1] = gen1;
synth->gen[2] = gen2;
synth->gen[3] = gen3;
synth->geni = 0;
synth->active = 0;
synth->viz.sample_rate_divider = 1;
synth->viz.wi = 0;
LowPass_Init();
synth->fff = create_bw_low_pass_filter(2, SAMPLE_RATE, 400);
synth->fff2 = create_bw_band_stop_filter(8, SAMPLE_RATE, 15000, 22000);
Pa_Initialize();
int i;
const PaDeviceInfo *deviceInfo;
for( i=0; i< Pa_GetDeviceCount(); i++ ) {
deviceInfo = Pa_GetDeviceInfo( i );
if (!strcmp("HyperX Cloud II Wireless: USB Audio (hw:2,0)", deviceInfo->name)) break;
printf("dev: %s || %f\n", deviceInfo->name, deviceInfo->defaultSampleRate);
//if (!strcmp("HDA Intel PCH: ALC1220 Analog (hw:0,0)", deviceInfo->name)) break;
}
PaStreamParameters outputParameters;
outputParameters.device = Pa_GetDefaultOutputDevice(); /* default output device */
printf("-------\nSelected device: %s\n-------\n", Pa_GetDeviceInfo(outputParameters.device)->name);
outputParameters.channelCount = 2; /* stereo output */
outputParameters.sampleFormat = paFloat32; /* 32 bit floating point output */
outputParameters.suggestedLatency = Pa_GetDeviceInfo( outputParameters.device )->defaultLowOutputLatency;
outputParameters.hostApiSpecificStreamInfo = NULL;
Pa_OpenStream(&(synth->stream),
NULL, /* no input */
&outputParameters,
SAMPLE_RATE,
FRAMES_PER_BUFFER,
paClipOff, /* we won't output out of range samples so don't bother clipping them */
sound_gen,
synth );
Pa_SetStreamFinishedCallback(synth->stream, &StreamFinished);
Pa_StartStream(synth->stream);
}
void
free_synth(synth_t * synth)
{
Pa_StopStream( synth->stream );
Pa_CloseStream( synth->stream );
Pa_Terminate();
free_bw_low_pass(synth->fff);
free_bw_band_stop(synth->fff2);
}