From 60c82a72fedd719f69c1f1de896aca00784a2881 Mon Sep 17 00:00:00 2001 From: gramanas Date: Tue, 27 Jun 2023 22:37:53 +0300 Subject: Changes --- src/Makefile.am | 6 +- src/adsr.c | 2 +- src/control.c | 68 +++++++++++++ src/control.h | 81 ++++++++++++++++ src/midi.c | 20 ++-- src/sound.c | 53 ++++++++++ src/sound.h | 16 ++++ src/synth_engine.c | 276 ++++++++++++++++++++++++++++++++++++++--------------- src/synth_engine.h | 15 ++- src/synth_gui.c | 82 ++++++++++++++-- src/synth_math.h | 8 +- 11 files changed, 526 insertions(+), 101 deletions(-) create mode 100644 src/control.c create mode 100644 src/control.h create mode 100644 src/sound.c create mode 100644 src/sound.h diff --git a/src/Makefile.am b/src/Makefile.am index 0b69262..ab17d84 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -3,6 +3,8 @@ bin_PROGRAMS = synth common_sources = adsr.c \ adsr.h \ + control.c \ + control.h \ filter.c \ filter.h \ lowpass.c \ @@ -12,6 +14,8 @@ common_sources = adsr.c \ midi.h \ notes.h \ raygui.h \ + sound.c \ + sound.h \ synth_engine.c \ synth_engine.h \ synth_gui.c \ @@ -31,7 +35,7 @@ common_sources = adsr.c \ AM_CFLAGS = -O3 -march=native -fno-math-errno -funroll-loops -flto -pthread synth_SOURCES = synth.c $(common_sources) -synth_LDADD = -lportaudio -lrt -lm -lasound -lraylib -lportmidi -ljack +synth_LDADD = -lportaudio -lrt -lm -lasound -lraylib -lportmidi -ljack -lfftw3f #cookbook_SOURCES = cookbook.c $(common_sources) #cookbook_SOURCES = cookbook.c $(common_sources) #cook_SOURCES = cook.c $(common_sources) diff --git a/src/adsr.c b/src/adsr.c index 8ef2ba9..4336bb5 100644 --- a/src/adsr.c +++ b/src/adsr.c @@ -43,5 +43,5 @@ adsr_amplitude(adsr_t *adsr, float noteOn, float noteOff, unsigned long long ela if (mod <= 0.000) mod = 0.0; - return clamp(mod); + return clamp(mod, -1, 1); } diff --git a/src/control.c b/src/control.c new file mode 100644 index 0000000..d952f7f --- /dev/null +++ b/src/control.c @@ -0,0 +1,68 @@ +#include "control.h" +#include + +int +cc_step(cc_t *cc, int steps) +{ + float tmp = cc->mod; + cc->mod += steps * cc->step; + + if (cc->value+cc->mod > cc->max) { + cc->mod = 0; + cc->target = cc->max; + } else if (cc->value+cc->mod < cc->min) { + cc->mod = 0; + cc->target = cc->min; + } + + return cc->mod != tmp; +} + +void +cc_prep(cc_t *cc) +{ + if (cc->mod) { + cc->target = cc->value + cc->mod; + } +} + +void +cc_fix(cc_t *cc) +{ + cc->value = cc->target; + cc->mod = 0; +} + +void +cc_reset(cc_t *cc) +{ + cc->target = cc->def; +} + +float +cc_iget(cc_t *cc, unsigned int i, unsigned int max) +{ + /* + v/t_s: value/time start + v/t_e: value/time end + y = m*x + b + m = (v_e - v_s) / (t_e - t_s) + b = v_s - m * t_s + */ + + float m = (cc->target - cc->value) / (max - 0); + + float b = cc->value /* + m * 0 */; + + return m * i + b; +} + +const char * +cc_to_str(cc_t *cc) +{ + static char buf[128]; + + snprintf(buf, sizeof buf, "[%s:v:%.2f_m:%.2f_t:%.2f]", cc->name, cc->value, cc->mod, cc->target); + + return buf; +} diff --git a/src/control.h b/src/control.h new file mode 100644 index 0000000..8eee97b --- /dev/null +++ b/src/control.h @@ -0,0 +1,81 @@ +#ifndef CONTROL_H +#define CONTROL_H + +/** + Control Component + + The midi implementation polls the midi device every whenever and gets to + react to midi events. Any and all changes should not happen directly to the + active `value`, but rather to the `mod`. This way the sound engine can + smoothly interpolate the changed value while generating samples. + + The sound engine fills a buffer with samples. The buffer has BUFFER_SIZE. + Each call of the sound callback will insert BUFFER_SIZE samples in the + buffer. Before we get the samples, we poll the synth's ccs for any changes. + This will set the target value to the `value + mod`, and the sound engine + will interpolate the `value` from value to target . It should also reset the + mod since other midi events might trigger until the next buffer generation. +*/ + +typedef struct cc_t { + char name[64]; + int midi_cc; + float min, max; + float step; + float def; + float value; /* active value (start for interpolation) */ + float mod; /* stores the modified value before it is set as target */ + float target; /* target value (end for interpolation) */ +} cc_t; + +#ifndef CC +#define CC2(NAME, MIN, MAX, STEP, DEF) \ + (cc_t) { \ + .name = NAME, \ + .midi_cc = -1, \ + .min = MIN, \ + .max = MAX, \ + .step = STEP, \ + .def = DEF, \ + .value = DEF, \ + .mod = 0, \ + .target = DEF \ + }; +#define CC(SYNTH, NAME, MIN, MAX, STEP, DEF) \ + strcpy(SYNTH.name, NAME); \ + SYNTH.midi_cc = -1; \ + SYNTH.min = MIN; \ + SYNTH.max = MAX; \ + SYNTH.step = STEP; \ + SYNTH.def = DEF; \ + SYNTH.value = DEF; \ + SYNTH.mod = 0; \ + SYNTH.target = DEF; \ + synth->ccs[synth->cci++] = &SYNTH; +#endif + +/** + Step the value of the target cc for `steps`. + `steps` can be positive or negative. + + The change will occur in the mod value of the cc. + + returns 1 if the value changed, 0 otherwise + */ +int cc_step(cc_t *cc, int steps); + +/** + Reset the cc to defaults + */ +void cc_reset(cc_t *cc); + +/** + Get the interpolated value from 0 to `max` in position `i` + */ +float cc_iget(cc_t *cc, unsigned int i, unsigned int max); +void cc_prep(cc_t *cc); +void cc_fix(cc_t *cc); + +const char * cc_to_str(cc_t *cc); + +#endif /* CONTROL_H */ diff --git a/src/midi.c b/src/midi.c index 0bd44a8..d3e2adb 100644 --- a/src/midi.c +++ b/src/midi.c @@ -1,5 +1,6 @@ #include "midi.h" #include "notes.h" +#include "control.h" #include @@ -13,7 +14,7 @@ void midi_decode(uint32_t msg, synth_t * synth) { switch (message) { case 0x08: - printf("Note Off: channel=%d, note=%d, velocity=%d\n", channel, data1, data2); + // printf("Note Off: channel=%d, note=%d, velocity=%d\n", channel, data1, data2); if (synth->n.key == data1) { synth->n.noteOff = Pa_GetStreamTime(synth->stream); } @@ -22,7 +23,7 @@ void midi_decode(uint32_t msg, synth_t * synth) { break; case 0x09: - printf("Note On: channel=%d, note=%d, velocity=%d\n", channel, data1, data2); + // printf("Note On: channel=%d, note=%d, velocity=%d\n", channel, data1, data2); synth->n.key = data1; synth->n.freq = notes[data1 % 12][(data1 / 12) % 8]; synth->n.noteOn = Pa_GetStreamTime(synth->stream); @@ -47,11 +48,13 @@ void midi_decode(uint32_t msg, synth_t * synth) { printf("Aftertouch: channel=%d, note=%d, pressure=%d\n", channel, data1, data2); break; case 0x0B: - printf("Control Change: channel=%d, controller=%d, value=%d\n", channel, data1, data2); + // printf("Control Change: channel=%d, controller=%d, value=%d\n", channel, data1, data2); int x = data2 < 64 ? 1 : -1; switch (data1) { case 0: synth->adsr.a = synth->adsr.a + (x * 0.01); + //synth->freq_offset = synth->freq_offset + (x*5); + //cc_step(&synth->cc_cutoff, x); break; case 1: synth->adsr.d = synth->adsr.d + (x * 0.01); @@ -63,15 +66,18 @@ void midi_decode(uint32_t msg, synth_t * synth) { synth->adsr.r = synth->adsr.r + (x * 0.01); break; case 4: + cc_step(&synth->cc_cutoff, x); synth->cutoff = synth->cutoff + (x * (10 * log10(synth->cutoff))); break; case 5: - synth->resonance = synth->resonance + (x * 0.02); + cc_step(&synth->cc_resonance, x); break; case 6: - synth->lfo.freq = synth->lfo.freq + (x * 0.1); + cc_step(&synth->cc_pitch, x); + //synth->lfo.freq = synth->lfo.freq + (x * 0.1); break; case 7: + cc_reset(&synth->cc_pitch); synth->lfo.amp = synth->lfo.amp + (x * 0.002); break; break; @@ -137,7 +143,9 @@ init_midi(midi_t *m, synth_t *synth) info = Pm_GetDeviceInfo(i); printf("%d: %s [input: %d output: %d opened: %d is_virt:%d] (interf: %s) -- %d\n", i, info->name, info->input, info->output, info->opened, info->is_virtual, info->interf, Pm_GetDefaultInputDeviceID()); //if (!strcmp("MPK225 MIDI", info->name) && !info->input) break; - if (!strcmp("MPK225 Port A", info->name) && info->input == 1) break; + //if (!strcmp("MPK225 Port A", info->name) && info->input == 1) break; + //if (!strcmp("CH345 MIDI 1", info->name) && info->input == 1) break; + if (!strcmp("Midi Through Port-0", info->name) && info->input == 1) break; } Pt_Start(1, midiCallback, m); diff --git a/src/sound.c b/src/sound.c new file mode 100644 index 0000000..49de899 --- /dev/null +++ b/src/sound.c @@ -0,0 +1,53 @@ +#include "sound.h" + + +static void +StreamFinished( void* synthData ) +{ + synth_t *synth = (synth_t *) synthData; +} + +void +init_sound(synth_t * synth, PaStreamCallback *streamCallback) +{ + 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; + if (!strcmp("pulse", deviceInfo->name)) break; + } + + + PaStreamParameters outputParameters; + outputParameters.device = i; 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 */ + streamCallback, + synth ); + + Pa_SetStreamFinishedCallback(synth->stream, &StreamFinished); + Pa_StartStream(synth->stream); +} + +void +destroy_sound(synth_t * synth) +{ + Pa_StopStream( synth->stream ); + Pa_CloseStream( synth->stream ); + Pa_Terminate(); +} diff --git a/src/sound.h b/src/sound.h new file mode 100644 index 0000000..9e0c066 --- /dev/null +++ b/src/sound.h @@ -0,0 +1,16 @@ +#ifndef SOUND_H +#define SOUND_H + +#include +#include + +#include "synth_engine.h" + +/* #define SAMPLE_RATE (44100) */ +/* #define FRAMES_PER_BUFFER (256) */ + +void init_sound(synth_t * synth, PaStreamCallback *streamCallback); + +void destroy_sound(synth_t * synth); + +#endif /* SOUND_H */ diff --git a/src/synth_engine.c b/src/synth_engine.c index 49c3168..9dc6457 100644 --- a/src/synth_engine.c +++ b/src/synth_engine.c @@ -2,6 +2,8 @@ #include "synth_math.h" #include "lowpass.h" #include "filter.h" +#include "control.h" +#include "sound.h" #include @@ -36,10 +38,39 @@ sqr_sample(float amp, float freq, float duty_cycle, unsigned long long phase, un return (fmod((float)phase / (float)sample_rate, 1.0 / freq) < duty_cycle * (1.0 / freq)) ? amp : -amp; } +float +gen10(float f, unsigned long long phase, float x, unsigned int sample_rate) +{ + return fmodf(phase, sample_rate/f) / (sample_rate/f); +} + +float +gen1110(float f, unsigned long long phase, float x, unsigned int sample_rate) +{ +float amplitude = 0.5; // Set the initial amplitude to 0.5 +float period = 1.0 / f; // Calculate the period of the waveform +float angular_frequency = 2.0 * M_PI * f; // Calculate the angular frequency + +// Calculate the phase angle +float phase_angle = fmodf(angular_frequency * phase / sample_rate, 2.0 * M_PI); + +// Generate the sample value using the sine function +float sample = amplitude * sinf(phase_angle); + +return sample; +} +float +gen110(float f, unsigned long long phase, float x, unsigned int sample_rate) +{ + float a = fmodf(phase, sample_rate/f); + return a > (sample_rate/f) / 2.0f ? 0.9999f : -0.9999f; +} + float gen0(float f, unsigned long long phase, float x, unsigned int sample_rate) { return sin_sample(1, f, phase, sample_rate); + //return chirp(phase, f); /* 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) */ @@ -53,10 +84,10 @@ gen0(float f, unsigned long long phase, float x, unsigned int 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); */ + //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 @@ -113,16 +144,17 @@ notes_active(synth_t *synth) return flag; } +float cur_freq, next_freq; + float -make_sample(unsigned long long phase, void *synthData, unsigned int sample_rate, int viz) +make_sample(void *synthData, unsigned int sample_rate, int frame) { synth_t *synth = (synth_t*)synthData; - float sample = 0; + float sample = 0.0f; - /* should never be 0 if we are here */ - - /* Notes */ float n = notes_active(synth); + if (n == 0) return sample; + float rms = 0; for (int i = 0; i < MIDI_NOTES; i++) { if (!synth->midi_note[i].active) @@ -137,26 +169,49 @@ make_sample(unsigned long long phase, void *synthData, unsigned int sample_rate, 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); + float adsr = adsr_amplitude(&synth->adsr, + synth->midi_note[i].noteOn, + synth->midi_note[i].noteOff, + synth->midi_note[i].elapsed); + + float targ_freq = synth->midi_note[i].freq * cc_iget(&synth->cc_pitch, frame, FRAMES_PER_BUFFER); + float targ_freq_with_repitch = synth->midi_note[i].freq * cc_iget(&synth->cc_pitch, frame, FRAMES_PER_BUFFER); + + float synth_sample = synth->gen[synth->geni](targ_freq, + synth->midi_note[i].elapsed, + synth->x, + sample_rate); + + /* if (targ_freq != targ_freq_with_repitch) { */ + /* unsigned long long tmp = synth->midi_note[i].elapsed; */ + /* float next_sample = synth->gen[synth->geni](targ_freq_with_repitch, */ + /* tmp, */ + /* synth->x, */ + /* sample_rate); */ + /* while (synth_sample - next_sample > 0.001 && next_sample - synth_sample > 0.001) { */ + /* next_sample = synth->gen[synth->geni](targ_freq_with_repitch, */ + /* ++tmp, */ + /* synth->x, */ + /* sample_rate); */ + /* } */ + /* synth_sample = next_sample; */ + /* } */ + + sample += 0.2 * rms * adsr * synth_sample; } /* 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); + float cutoff = cc_iget(&synth->cc_cutoff, frame, FRAMES_PER_BUFFER); + float reso = round(cc_iget(&synth->cc_resonance, frame, FRAMES_PER_BUFFER)); + //cutoff = cutoff + cutoff * (synth->lfo.amp) * lfo(cc_iget(&synth->cc_lfo_freq, frame, FRAMES_PER_BUFFER), synth->lfo.elapsed, sample_rate); if (cutoff == 0) cutoff = 0.001; - LowPass_Update(synth->resonance, cutoff, sample_rate); + LowPass_Update(reso, cutoff, sample_rate); sample = LowPass_Filter(sample); - update_bw_low_pass_filter(synth->fff, SAMPLE_RATE,cutoff, synth->resonance); + update_bw_low_pass_filter(synth->fff, SAMPLE_RATE,cutoff, reso); sample = bw_low_pass(synth->fff, sample); } @@ -165,7 +220,7 @@ make_sample(unsigned long long phase, void *synthData, unsigned int sample_rate, // band stop for high freqs sample = bw_band_stop(synth->fff2, sample); - if (synth->clamp) sample = clamp(sample); + if (synth->clamp) sample = clamp(sample, -1, 1); return sample; } @@ -180,6 +235,20 @@ add_to_viz(synth_t *synth, float sample) } } +void +increment_synth(synth_t *synth) +{ + 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++; + } +} + void get_frame(void *outputBuffer, synth_t *synth, int i) { @@ -205,23 +274,89 @@ get_frame(void *outputBuffer, synth_t *synth, int i) return; } - s = make_sample(synth->n.elapsed, synth, SAMPLE_RATE, 0); + s = make_sample(synth, SAMPLE_RATE, i); *out++ = s; *out++ = s; - synth->lfo.elapsed++; - synth->adsr.elapsed++; - synth->n.elapsed++; + // move time + increment_synth(synth); - for (int i = 0; i < MIDI_NOTES; i++) { - if (!synth->midi_note[i].active) - continue; + // viz + add_to_viz(synth, s); +} - synth->midi_note[i].elapsed++; +#include "fftw3.h" + +int +process(float * buffer) +{ + int len = FRAMES_PER_BUFFER; + + float buf[FRAMES_PER_BUFFER]; + + for( unsigned long i=0; i < len * 2; i += 2 ) { + buf[i / 2] = buffer[i]; + } + + fftwf_complex* frequency_signal = (fftwf_complex*)fftwf_malloc(sizeof(fftwf_complex) * (len / 2 + 1)); + //double* time_signal = (double*)fftwf_malloc(sizeof(double) * len); + + fftwf_plan forward_plan = fftwf_plan_dft_r2c_1d(len, buf, frequency_signal, FFTW_ESTIMATE); + fftwf_plan backward_plan = fftwf_plan_dft_c2r_1d(len, frequency_signal, buf, FFTW_ESTIMATE); + + fftwf_execute(forward_plan); + + /* for (int i=0; i < (len / 2 + 1); i++ ) { */ + /* if (frequency_signal[i][0] > 1) { */ + /* frequency_signal[i][0] = 0; */ + /* } */ + /* if (frequency_signal[i][1] > 1) { */ + /* frequency_signal[i][1] = 0; */ + /* } */ + /* printf("%f %f | ", sqrt(frequency_signal[i][0] * frequency_signal[i][0] + frequency_signal[i][1] * frequency_signal[i][1]), atan2(frequency_signal[i][1], frequency_signal[i][0])); */ + + /* float f = atan2(frequency_signal[i][1], frequency_signal[i][0]); */ + /* if (f > M_PI / 2 && f < 2 * M_PI / 3) { */ + /* frequency_signal[i][0] = 0.0; */ + /* frequency_signal[i][1] = 0.0; */ + /* } */ + /* } */ + + fftwf_execute(backward_plan); + + for (unsigned long i=0; i < len * 2; i += 2) { + buffer[i] = buf[i / 2] / len ; + buffer[i + 1] = buf[i / 2] / len ; } - // viz - add_to_viz(synth, s); + fftwf_destroy_plan(forward_plan); + fftwf_destroy_plan(backward_plan); + fftwf_free(frequency_signal); + //fftwf_free(time_signal); + + /* for( unsigned long i=0; i < len * 2; i += 2 ) { */ + /* buf[i / 2] = buffer[i]; */ + /* } */ + /* float kernel[7] = {.01, .88, .21, .36, .21, .13, .01}; */ + /* float res[len + 7 - 1]; */ + /* convole(buf, kernel, len, 7, res); */ + + /* int N = (len + 7 - 1); */ + /* int M = len; */ + + /* float ratio = (float) N / M; */ + + /* for (int i = 0; i < M; i++) { */ + /* int index = (int)(i * ratio); */ + /* buf[i] = res[index]; */ + /* } */ + + /* for (unsigned long i=0; i < len * 2; i += 2) { */ + /* buffer[i] = buf[i / 2] ; */ + /* buffer[i + 1] = buf[i / 2] ; */ + /* } */ + + return 0; } int @@ -233,30 +368,49 @@ sound_gen(const void *inputBuffer, void *outputBuffer, { synth_t *synth = (synth_t*)synthData; float *out = (float*)outputBuffer; + + float buffer[2 * FRAMES_PER_BUFFER]; (void) timeInfo; (void) statusFlags; (void) inputBuffer; // get_changes(); + for (int i = 0; i < synth->cci; i++) { + cc_prep(synth->ccs[i]); + } + // fill buffer for( unsigned long i=0; imulti) process(buffer); + + // output buffer + for( unsigned long i=0; icci; i++) { + cc_fix(synth->ccs[i]); } - return paContinue; } - -static void -StreamFinished( void* synthData ) -{ - synth_t *synth = (synth_t *) synthData; -} - void init_synth(synth_t * synth) { + synth->cci = 0; + CC(synth->cc_cutoff, "cutoff", 10, 5000, 30, 5000); + CC(synth->cc_resonance, "resonance", 1, 10, .02, 1); + CC(synth->cc_lfo_freq, "lfo_freq", .01, 10, .02, .1); + CC(synth->cc_pitch, "pitch", -3, 4, 0.01, 1); + + synth->modi = 0; + synth->freq_offset = 0; synth->gain = 1; synth->x = 1; @@ -295,14 +449,13 @@ init_synth(synth_t * synth) 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->geni = 2; synth->active = 0; @@ -313,46 +466,13 @@ init_synth(synth_t * synth) 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 = i; 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); + init_sound(synth, sound_gen); } void free_synth(synth_t * synth) { - Pa_StopStream( synth->stream ); - Pa_CloseStream( synth->stream ); - Pa_Terminate(); - + destroy_sound(synth); free_bw_low_pass(synth->fff); free_bw_band_stop(synth->fff2); diff --git a/src/synth_engine.h b/src/synth_engine.h index 55dc04e..0c8145f 100644 --- a/src/synth_engine.h +++ b/src/synth_engine.h @@ -9,10 +9,11 @@ #include "notes.h" #include "filter.h" #include "adsr.h" +#include "control.h" //#define SAMPLE_RATE (44100) #define SAMPLE_RATE (48000) -#define FRAMES_PER_BUFFER (210) +#define FRAMES_PER_BUFFER (256) #define VIZ_BUF 1024 @@ -58,6 +59,14 @@ typedef struct viz_t { typedef struct { PaStream *stream; + + cc_t * ccs[128]; + int cci; + + cc_t cc_pitch; + cc_t cc_cutoff; + cc_t cc_resonance; + cc_t cc_lfo_freq; float freq_offset; float gain; @@ -77,11 +86,13 @@ typedef struct { int filter; int clamp; + int modifiers[16]; + int modi; + float (*gen[4]) (float freq, unsigned long long phase, float x, unsigned int sample_rate); int geni; float cutoff; - float resonance; BWLowPass* fff; BWBandStop* fff2; diff --git a/src/synth_gui.c b/src/synth_gui.c index 4f9f5a3..510075b 100644 --- a/src/synth_gui.c +++ b/src/synth_gui.c @@ -36,7 +36,7 @@ keyboard(void *synthData, PaStream *stream) set_note(synth, note, Pa_GetStreamTime(stream), keys[i]); //printf("Note On : %s[%d] %fHz\n", int_to_note(i % 12), (synth->octave + (i / 12)) % 8, note); - synth->midi_note[i].freq = 16.35 * pow(2, (synth->octave + i / 12.0)); //notes[i % 12][(synth->octave + (i / 12)) % 8]; + synth->midi_note[i].freq = 16.35160 * pow(2, (synth->octave + i / 12.0)); //notes[i % 12][(synth->octave + (i / 12)) % 8]; //synth->midi_note[i].freq = notes[i % 12][(synth->octave + (i / 12)) % 8]; synth->midi_note[i].channel = -1; synth->midi_note[i].noteOn = Pa_GetStreamTime(synth->stream); @@ -58,6 +58,63 @@ keyboard(void *synthData, PaStream *stream) } } } + + int mods[] = {KEY_Z, KEY_X, KEY_C, KEY_V, KEY_B, KEY_N, KEY_M, 0}; + synth->modi = 0; + for (int i = 0; mods[i]; i++) { + if (IsKeyDown(mods[i])) { + synth->modifiers[synth->modi++] = mods[i]; + } + } + + if (IsKeyDown(265)) { // up + for (int i = 0; i < synth->modi; i++) { + if (synth->modifiers[i] == KEY_Z) { + cc_step(&synth->cc_cutoff, 1); + } + if (synth->modifiers[i] == KEY_X) { + cc_step(&synth->cc_resonance, 1); + } + if (synth->modifiers[i] == KEY_C) { + cc_step(&synth->cc_pitch, 1); + } + if (synth->modifiers[i] == KEY_V) { + cc_step(&synth->cc_lfo_freq, 1); + } + } + } + if (IsKeyDown(264)) { // down + for (int i = 0; i < synth->modi; i++) { + if (synth->modifiers[i] == KEY_Z) { + cc_step(&synth->cc_cutoff, -1); + } + if (synth->modifiers[i] == KEY_X) { + cc_step(&synth->cc_resonance, -1); + } + if (synth->modifiers[i] == KEY_C) { + cc_step(&synth->cc_pitch, -1); + } + if (synth->modifiers[i] == KEY_V) { + cc_step(&synth->cc_lfo_freq, -1); + } + } + } + if (IsKeyDown(KEY_ENTER)) { // down + for (int i = 0; i < synth->modi; i++) { + if (synth->modifiers[i] == KEY_Z) { + cc_reset(&synth->cc_cutoff); + } + if (synth->modifiers[i] == KEY_X) { + cc_reset(&synth->cc_resonance); + } + if (synth->modifiers[i] == KEY_C) { + cc_reset(&synth->cc_pitch); + } + if (synth->modifiers[i] == KEY_V) { + cc_reset(&synth->cc_lfo_freq); + } + } + } } void @@ -78,12 +135,12 @@ draw_adsr_sliders(synth_t * synth, int x, int y, int width, int height, int offs synth->adsr.r = GuiSlider((Rectangle){ x, y + count++ * (height + offset), width, height }, "R: ", buf, synth->adsr.r , -0.001f, 3.0f); snprintf(buf, sizeof buf, "%.3f", synth->cutoff); synth->cutoff = GuiSliderBar((Rectangle){ x, y + count++ * (height + offset), width, height }, "fC: ", buf, synth->cutoff , 0.0f, 11000.0f); - snprintf(buf, sizeof buf, "%.3f", synth->resonance); - synth->resonance = GuiSliderBar((Rectangle){ x, y + count++ * (height + offset), width, height }, "fR: ", buf, synth->resonance , 0.001f, 5.0f); + /* snprintf(buf, sizeof buf, "%.3f", synth->resonance); */ + /* synth->resonance = GuiSliderBar((Rectangle){ x, y + count++ * (height + offset), width, height }, "fR: ", buf, synth->resonance , 0.001f, 5.0f); */ snprintf(buf, sizeof buf, "%.3f", synth->lfo.freq); synth->lfo.freq = GuiSliderBar((Rectangle){ x, y + count++ * (height + offset), width, height }, "lfo freq: ", buf, synth->lfo.freq , 0.001f, 10.0f); snprintf(buf, sizeof buf, "%.3f", synth->lfo.amp); - synth->lfo.amp = GuiSliderBar((Rectangle){ x, y + count++ * (height + offset), width, height }, "lfo amprrrrrr: ", buf, synth->lfo.amp , 0.0f, 0.93f); + synth->lfo.amp = GuiSliderBar((Rectangle){ x, y + count++ * (height + offset), width, height }, "lfo amp: ", buf, synth->lfo.amp , 0.0f, 0.93f); } void @@ -149,11 +206,18 @@ rayrun(void *synthData) draw_adsr_sliders(synth, 30, 20, 256, 20, 4); - if ( GuiButton((Rectangle){ WIDTH / 2 - 108, 150 - 42 - 6 - 6, 216, 6 }, "")) { - synth->freq_offset = 0; - } - snprintf(buf, sizeof buf, "%.1f", synth->freq_offset); - synth->freq_offset = GuiSlider((Rectangle){ WIDTH / 2 - 108, 150 - 42, 216, 24 }, "fine", buf, synth->freq_offset , -20.0f, 20.0f); + /* if ( GuiButton((Rectangle){ WIDTH / 2 - 108, 150 - 42 - 6 - 6, 216, 6 }, "")) { */ + /* synth->freq_offset = 0; */ + /* } */ + snprintf(buf, sizeof buf, "extra pitch %.4f", synth->cc_pitch.value); + DrawText(buf, WIDTH / 2 - 108, 150 - 82, 20, LIGHTGRAY); + snprintf(buf, sizeof buf, "filter cutoff %.1f", synth->cc_cutoff.value); + DrawText(buf, WIDTH / 2 - 108, 150 - 42, 20, LIGHTGRAY); + snprintf(buf, sizeof buf, "filter reso %.4f", synth->cc_resonance.value); + DrawText(buf, WIDTH / 2 - 108, 150 - 62, 20, LIGHTGRAY); + /* snprintf(buf, sizeof buf, "freq offset %.1f", synth->freq_offset); */ + /* DrawText(buf, WIDTH / 2 - 108, 150 - 42, 20, LIGHTGRAY); */ + /* synth->freq_offset = GuiSlider((Rectangle){ WIDTH / 2 - 108, 150 - 42, 216, 24 }, "fine", buf, synth->freq_offset , -20.0f, 20.0f); */ if ( GuiButton((Rectangle){ WIDTH / 2 - 108, 150 - 6 - 6, 216, 6 }, "")) { synth->gain = 1; diff --git a/src/synth_math.h b/src/synth_math.h index 74dec4c..2dd9f83 100644 --- a/src/synth_math.h +++ b/src/synth_math.h @@ -18,14 +18,14 @@ gcd(long long a, long long b) static long long lcm(long long a, long long b) { - return (a / gcd(a, b)) * b; + return (a / gcd(a, b)) * b; } static float -clamp(float f) +clamp(float f, int min, int max) { - if (f <= -1) return -0.9999; - if (f >= 1) return 0.9999; + if (f <= min) return min + 0.0001; + if (f >= max) return max - 0.0001; return f; } -- cgit v1.2.3