#include "synth_engine.h" #include "synth_math.h" #include "lowpass.h" #include "filter.h" #include 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; iactive) { *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; imidi_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); }