summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorgramanas <anastasis.gramm2@gmail.com>2023-06-27 22:37:53 +0300
committergramanas <anastasis.gramm2@gmail.com>2023-06-27 22:37:53 +0300
commit60c82a72fedd719f69c1f1de896aca00784a2881 (patch)
tree3679e0886fc12aa8eeeab60260347ee7a0c5ae0e /src
parente77d4d42cacd21bf80e4f47cba2fe85f5a5b0991 (diff)
downloadsynth-project-60c82a72fedd719f69c1f1de896aca00784a2881.tar.gz
synth-project-60c82a72fedd719f69c1f1de896aca00784a2881.tar.bz2
synth-project-60c82a72fedd719f69c1f1de896aca00784a2881.zip
Changes
Diffstat (limited to 'src')
-rw-r--r--src/Makefile.am6
-rw-r--r--src/adsr.c2
-rw-r--r--src/control.c68
-rw-r--r--src/control.h81
-rw-r--r--src/midi.c20
-rw-r--r--src/sound.c53
-rw-r--r--src/sound.h16
-rw-r--r--src/synth_engine.c276
-rw-r--r--src/synth_engine.h15
-rw-r--r--src/synth_gui.c82
-rw-r--r--src/synth_math.h8
11 files changed, 526 insertions, 101 deletions
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 <stdio.h>
+
+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 <string.h>
@@ -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 <portaudio.h>
+#include <string.h>
+
+#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 <string.h>
@@ -37,9 +39,38 @@ sqr_sample(float amp, float freq, float duty_cycle, unsigned long long phase, un
}
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;
}
@@ -181,6 +236,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)
{
float *out = (float*)outputBuffer + 2 * 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; i<framesPerBuffer; i++ ) {
- //interpolate_changes(i);
- get_frame(outputBuffer, synth, i);
+ // use iget inside
+ get_frame(buffer, synth, i);
+ }
+ // process buffer
+ if (synth->multi) process(buffer);
+
+ // output buffer
+ for( unsigned long i=0; i<framesPerBuffer * 2; i += 2 ) {
+ // use iget inside
+ *out++ = buffer[i];
+ *out++ = buffer[i+1];
+ }
+ // finalize_changes();
+ for (int i = 0; i < synth->cci; 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;
}