summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorgramanas <anastasis.gramm2@gmail.com>2023-07-07 01:09:12 +0300
committergramanas <anastasis.gramm2@gmail.com>2023-07-07 01:09:12 +0300
commitc8cd7f9298de876f2046fddd2e322a63c421a505 (patch)
tree1b764fed928592f8f3465e9ef28552b451efad8a /src
parentf170fb058a07175e6e753f2a6d20283dc7200a88 (diff)
downloadsynth-project-c8cd7f9298de876f2046fddd2e322a63c421a505.tar.gz
synth-project-c8cd7f9298de876f2046fddd2e322a63c421a505.tar.bz2
synth-project-c8cd7f9298de876f2046fddd2e322a63c421a505.zip
suppa
Diffstat (limited to 'src')
-rw-r--r--src/Makefile.am9
-rw-r--r--src/adsr.c49
-rw-r--r--src/adsr.h1
-rw-r--r--src/control.h12
-rw-r--r--src/midi.c1
-rw-r--r--src/osc.c97
-rw-r--r--src/osc.h72
-rw-r--r--src/osc_sin.c22
-rw-r--r--src/osc_sound.c34
-rw-r--r--src/osc_tri.c30
-rw-r--r--src/osc_weird.c25
-rw-r--r--src/synth_common.h15
-rw-r--r--src/synth_engine.c96
-rw-r--r--src/synth_engine.h18
-rw-r--r--src/synth_gui.c37
-rw-r--r--src/wavetable.c182
-rw-r--r--src/wavetable.h24
17 files changed, 642 insertions, 82 deletions
diff --git a/src/Makefile.am b/src/Makefile.am
index 0ef06f4..f96d14e 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -13,9 +13,16 @@ common_sources = adsr.c \
midi.c \
midi.h \
notes.h \
+ osc.c \
+ osc.h \
+ osc_tri.c \
+ osc_sin.c \
+ osc_weird.c \
+ osc_sound.c \
raygui.h \
sound.c \
sound.h \
+ synth_common.h \
synth_engine.c \
synth_engine.h \
synth_gui.c \
@@ -37,7 +44,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 -lfftw3f
+synth_LDADD = -lportaudio -lrt -lm -lasound -lraylib -lportmidi -ljack -lfftw3f -lsndfile
#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 4336bb5..439d7b1 100644
--- a/src/adsr.c
+++ b/src/adsr.c
@@ -45,3 +45,52 @@ adsr_amplitude(adsr_t *adsr, float noteOn, float noteOff, unsigned long long ela
return clamp(mod, -1, 1);
}
+
+float
+fix_adsr(adsr_t *adsr, float noteOn, float noteOff, unsigned long long elapsed, unsigned long long noteOffSample)
+{
+ // convert to samples
+ unsigned long long attack = adsr->a * SAMPLE_RATE + 1;
+ unsigned long long decay = adsr->d * SAMPLE_RATE + 1;
+ unsigned long long sustain = adsr->s * SAMPLE_RATE + 1;
+ unsigned long long release = adsr->r * SAMPLE_RATE + 1;
+
+ float mod = 0.0f;
+
+ // if note is pressed
+ if (noteOn != 0 && noteOff == 0) {
+ if (elapsed < attack) {
+ // we are in the attack phase
+ mod = ((float)elapsed / attack) * adsr->peak;
+ }
+ else if (/* elapsed >= attack && */ elapsed < attack + decay) {
+ // we are in the decay phase
+ mod = ((float)(elapsed - attack) / decay) * (adsr->s - adsr->peak) + adsr->peak;
+ }
+ else {// sustain
+ mod = adsr->s;
+ }
+ }
+ else { // note is not pressed
+ if (elapsed < noteOffSample + release) {
+ // we are on the release
+
+ // reuse previous block to find starting value
+ if (noteOffSample < attack) {
+ mod = ((float)noteOffSample / attack) * adsr->peak;
+ }
+ else if (/* elapsed >= attack && */ noteOffSample < attack + decay) {
+ mod = ((float)(noteOffSample - attack) / decay) * (adsr->s - adsr->peak) + adsr->peak;
+ }
+ else {
+ mod = adsr->s;
+ }
+
+ mod = (0 - mod) / (float)release * (elapsed - noteOffSample) + mod;
+ }
+ //printf("el: %ld, att: %ld MOD: %f, on %f, off %f\n", elapsed, attack, mod, noteOn, noteOff);
+ }
+
+
+ return mod;
+}
diff --git a/src/adsr.h b/src/adsr.h
index 713c95f..0f740dd 100644
--- a/src/adsr.h
+++ b/src/adsr.h
@@ -11,5 +11,6 @@ typedef struct adsr_t {
} adsr_t;
float adsr_amplitude(adsr_t *adsr, float noteOn, float noteOff, unsigned long long elapsed);
+float fix_adsr(adsr_t *adsr, float noteOn, float noteOff, unsigned long long elapsed, unsigned long long noteOffSample);
#endif /* ADSR_H */
diff --git a/src/control.h b/src/control.h
index 8eee97b..45e87ed 100644
--- a/src/control.h
+++ b/src/control.h
@@ -29,18 +29,6 @@ typedef struct cc_t {
} 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; \
diff --git a/src/midi.c b/src/midi.c
index 8452bcc..b53f365 100644
--- a/src/midi.c
+++ b/src/midi.c
@@ -16,6 +16,7 @@ void midi_decode(uint32_t msg, synth_t * synth) {
case 0x08:
// printf("Note Off: channel=%d, note=%d, velocity=%d\n", channel, data1, data2);
synth->midi_note[data1].noteOff = Pa_GetStreamTime(synth->stream);
+ synth->midi_note[data1].noteOffSample = synth->midi_note[data1].elapsed;
break;
case 0x09:
// printf("Note On: channel=%d, note=%d, velocity=%d\n", channel, data1, data2);
diff --git a/src/osc.c b/src/osc.c
new file mode 100644
index 0000000..295c83d
--- /dev/null
+++ b/src/osc.c
@@ -0,0 +1,97 @@
+#include "synth_common.h"
+#include "osc.h"
+
+#include <sndfile.h>
+#include <stdlib.h>
+#include <stddef.h>
+
+
+float
+osc_interpolate(float offset, float start, float end) {
+ // linear interpolation between start and end
+ float m = (end - start) / (1 - 0);
+ float b = start /* + m * 0 */;
+
+ // the value to pick
+ float x = offset - (int)offset;
+
+ return m * x + b;
+}
+
+int
+osc_next_index(osc_t * osc, float offset)
+{
+ if (offset >= osc->len) offset = 0;
+
+ int current_index = (int)offset;
+ return current_index + 1 >= osc->len ? 0 : current_index + 1;
+}
+
+
+float
+osc_next_offset(osc_t * osc, float f, float offset)
+{
+ if (offset >= osc->len) offset = 0;
+
+ if (osc->type == SAMPLE) {
+ // this makes A2 be the base ???
+ // A2 = 220.0 Hz is base ???
+ // get this from osc*
+ f = f / (16.35160 * pow(2, (2 + 5 / 12.0)));
+ }
+
+ float step = f * ((float)osc->len / SAMPLE_RATE);
+
+ offset += step;
+ if (offset >= osc->len) {
+ offset -= osc->len;
+ }
+
+ return offset;
+}
+
+int
+osc_load_wav(osc_t * osc, const char * path)
+{
+ SNDFILE* file;
+ SF_INFO fileInfo;
+ int numSamplesRead;
+
+ // Open the WAV file
+ file = sf_open(path, SFM_READ, &fileInfo);
+ if (!file) {
+ printf("Failed to open the WAV file: %s\n", sf_strerror(NULL));
+ return -1;
+ }
+
+ printf("Opened %s: \n", path);
+ printf("frames: %ld sr: %d chan: %d format: %d sections: %d seekable: %d\n", fileInfo.frames, fileInfo.samplerate, fileInfo.channels, fileInfo.format, fileInfo.sections, fileInfo.seekable);
+
+ // Ensure the WAV file has only one channel
+ if (fileInfo.channels != 1) {
+ printf("Only mono WAV files are supported.\n");
+ sf_close(file);
+ return -1;
+ }
+
+ osc->len = fileInfo.frames;
+ osc->end = fileInfo.frames;
+ osc->data = (float *) malloc(sizeof(float) * fileInfo.frames);
+
+ // Read the WAV file into the buffer
+ numSamplesRead = sf_readf_float(file, osc->data, fileInfo.frames);
+
+ /* float max = -1000; */
+ /* float min = 1000; */
+ /* for (int i = 0; i < numSamplesRead; i++) { */
+ /* float s = osc_sound_data[i]; */
+ /* // printf("Sample %d: %f\n", i, s); */
+ /* if (s < min) min = s; */
+ /* if (s > max) max = s; */
+ /* } */
+ /* printf("Min: %f Max: %f\n", min, max); */
+
+ // Close the WAV file
+ sf_close(file);
+ return 0;
+}
diff --git a/src/osc.h b/src/osc.h
new file mode 100644
index 0000000..3915e5a
--- /dev/null
+++ b/src/osc.h
@@ -0,0 +1,72 @@
+#ifndef OSC_H
+#define OSC_H
+
+#include <math.h>
+
+/**
+ * The main idea of the osc is the following:
+ * The root of the sound is a periodic wave function.
+ * This can be a stored period of a wave in a wavetable
+ * or a function like sin(). In order to play a sound with
+ * the wave an external float offset is kept that points to the
+ * next sample, a function is provided to advance this offset
+ * a calucalted step amount, depending on the desired frequency.
+ * Linear interpolation is used so that a float offset can work
+ * and provide autosmoothing.
+ */
+
+enum osc_type {
+ WAVE,
+ SAMPLE
+};
+
+typedef struct osc_t {
+ char name[16];
+ float * data;
+ long len;
+ long start;
+ long end;
+ enum osc_type type;
+} osc_t;
+
+/* Initializes the wavetables and functions for the oscilators */
+void init_osc();
+
+float osc_interpolate(float offset, float start, float end);
+float osc_next_offset(osc_t * osc, float f, float offset);
+int osc_next_index(osc_t * osc, float offset);
+
+int osc_load_wav(osc_t * osc, const char * path);
+
+/***************/
+/* OSCILATORS */
+/***************/
+
+#define OSC_COMMON(osc) \
+ void \
+ set_##osc##_start(long start) \
+ { OSC_##osc.start = start; } \
+ void \
+ set_##osc##_end(long end) \
+ { OSC_##osc.end = end; }
+
+#define OSC_COMMON_H(osc) \
+ void \
+ set_##osc##_start(long start); \
+ void \
+ set_##osc##_end(long end); \
+ float \
+ osc_##osc(float offset); \
+ float \
+ osc_##osc##_next(float f, float offset);
+
+/*****************/
+/* osc functions */
+/*****************/
+
+OSC_COMMON_H(tri)
+OSC_COMMON_H(sin)
+OSC_COMMON_H(weird)
+OSC_COMMON_H(sound)
+
+#endif /* OSC_H */
diff --git a/src/osc_sin.c b/src/osc_sin.c
new file mode 100644
index 0000000..66478d2
--- /dev/null
+++ b/src/osc_sin.c
@@ -0,0 +1,22 @@
+#include "osc.h"
+
+osc_t OSC_sin = {
+ .name = "f_sin",
+ .len = 20000,
+ .start = 0,
+ .end = 20000,
+};
+
+float
+osc_sin(float offset)
+{
+ return osc_interpolate(offset,
+ sin(2 * M_PI * (int)offset / OSC_sin.len),
+ sin(2 * M_PI * osc_next_index(&OSC_sin, offset) / OSC_sin.len));
+}
+
+float
+osc_sin_next(float f, float offset)
+{
+ return osc_next_offset(&OSC_sin, f, offset);
+}
diff --git a/src/osc_sound.c b/src/osc_sound.c
new file mode 100644
index 0000000..dd4eda5
--- /dev/null
+++ b/src/osc_sound.c
@@ -0,0 +1,34 @@
+#include "osc.h"
+
+#include <stdlib.h>
+
+osc_t OSC_sound = {
+ .name = "w_sound",
+ .data = 0,
+ .len = 0,
+ .start = 0,
+ .end = 0,
+ .type = SAMPLE,
+};
+
+OSC_COMMON(sound)
+
+float
+osc_sound(float offset)
+{
+ if (!OSC_sound.len) {
+ //osc_load_wav(&OSC_sound, "/home/gramanas/code/synth-project/waves/test2.wav");
+ osc_load_wav(&OSC_sound, "/home/gramanas/code/synth-project/waves/test_lick.wav");
+ /* osc_load_wav(&OSC_sound, "/mnt/data/music/samples/velvet/Drums/Kick/Kick Coldwire 1.wav"); */
+ }
+
+ return osc_interpolate(offset,
+ OSC_sound.data[(int)offset],
+ OSC_sound.data[osc_next_index(&OSC_sound, offset)]);
+}
+
+float
+osc_sound_next(float f, float offset)
+{
+ return osc_next_offset(&OSC_sound, f, offset);
+}
diff --git a/src/osc_tri.c b/src/osc_tri.c
new file mode 100644
index 0000000..c4a56f5
--- /dev/null
+++ b/src/osc_tri.c
@@ -0,0 +1,30 @@
+#include "osc.h"
+
+osc_t OSC_tri = {
+ .name = "w_triangle",
+ .start = 0,
+ .end = 2,
+ .len = 2,
+};
+
+float
+tri(int index)
+{
+ if (index == 0) return -1.0f;
+ else if (index == 1) return 1.0f;
+ else return 0.0f;
+}
+
+float
+osc_tri(float offset)
+{
+ return osc_interpolate(offset,
+ tri((int)offset),
+ tri(osc_next_index(&OSC_tri, offset)));
+}
+
+float
+osc_tri_next(float f, float offset)
+{
+ return osc_next_offset(&OSC_tri, f, offset);
+}
diff --git a/src/osc_weird.c b/src/osc_weird.c
new file mode 100644
index 0000000..143cc23
--- /dev/null
+++ b/src/osc_weird.c
@@ -0,0 +1,25 @@
+#include "osc.h"
+
+float osc_weird_fun(int index)
+{
+ return ((float)(index * index * index) / (200 * 200 * 200)) * 2 - 1;
+}
+
+osc_t OSC_weird = {
+ .name = "f_weird",
+ .len = 200
+};
+
+float
+osc_weird(float offset)
+{
+ return osc_interpolate(offset,
+ osc_weird_fun((int)offset),
+ osc_weird_fun(osc_next_index(&OSC_weird, offset)));
+}
+
+float
+osc_weird_next(float f, float offset)
+{
+ return osc_next_offset(&OSC_weird, f, offset);
+}
diff --git a/src/synth_common.h b/src/synth_common.h
new file mode 100644
index 0000000..ee10331
--- /dev/null
+++ b/src/synth_common.h
@@ -0,0 +1,15 @@
+#ifndef SYNTH_COMMON_H
+#define SYNTH_COMMON_H
+
+//#define SAMPLE_RATE (44100)
+#define SAMPLE_RATE (48000)
+#define FRAMES_PER_BUFFER (256)
+
+#define VIZ_BUF 1024
+
+#define WIDTH 1024
+#define HEIGHT 600
+
+#define MIDI_NOTES 128
+
+#endif /* SYNTH_COMMON_H */
diff --git a/src/synth_engine.c b/src/synth_engine.c
index 9dbf2c5..5118c7d 100644
--- a/src/synth_engine.c
+++ b/src/synth_engine.c
@@ -5,6 +5,7 @@
#include "control.h"
#include "sound.h"
#include "wavetable.h"
+#include "osc.h"
#include <string.h>
@@ -46,21 +47,6 @@ gen10(float f, unsigned long long phase, float x, unsigned int sample_rate)
}
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);
@@ -105,13 +91,32 @@ gen3(float f, midi_note_t * midi_note, float x, unsigned int sample_rate)
{
//return sqr_sample(1, f, .5, midi_note->elapsed, sample_rate);
- return wvt_next(wvt_saw, f, sample_rate, &midi_note->wvt_index);
+ //return wvt_next(wvt_sound, f, sample_rate, &midi_note->wvt_index);
+ float sample = osc_sound(midi_note->wvt_index);
+ midi_note->wvt_index = osc_sound_next(f, midi_note->wvt_index);
+ return sample;
/* return sawX_sample(0.7, f, 5, midi_note->elapsed, sample_rate) */
/* + sin_sample(0.3, 4.0/17.0*f, midi_note->elapsed, sample_rate); */
- /* return saw_sample(0.5, f * (1 + sqrt(5)) / 2, midi_note->elapsed, sample_rate) */
- /* + sin_sample(0.3, f * x, midi_note->elapsed, sample_rate) */
- /* + sqr_sample(0.2, f * x, 0.2 * x * x, midi_note->elapsed, sample_rate); */
+}
+
+float
+gen4(float f, midi_note_t * midi_note, float x, unsigned int sample_rate)
+{
+ return wvt_next(wvt_tri, f, sample_rate, &midi_note->wvt_index);
+}
+
+float
+gen5(float f, midi_note_t * midi_note, float x, unsigned int sample_rate)
+{
+ return 0.8 * wvt_next(wvt_saw, f, sample_rate, &midi_note->wvt_index) +
+ .2 * sin_sample(1, f/2, midi_note->elapsed, sample_rate);
+}
+
+float
+gen6(float f, midi_note_t * midi_note, float x, unsigned int sample_rate)
+{
+ return wvt_next(wvt_sqr, f, sample_rate, &midi_note->wvt_index);
}
float
@@ -128,18 +133,21 @@ notes_active(synth_t *synth)
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)
+ if (!fix_adsr(&synth->adsr,
+ (float)synth->midi_note[i].noteOn,
+ (float)synth->midi_note[i].noteOff,
+ synth->midi_note[i].elapsed,
+ synth->midi_note[i].noteOffSample)
&& 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].wvt_index = 0;
+ synth->midi_note[i].lfo_index = 0;
synth->midi_note[i].velocity = -1;
synth->midi_note[i].elapsed = -1;
+ synth->midi_note[i].noteOffSample = 0;
synth->midi_note[i].active = 0;
} else {
flag++;
@@ -149,7 +157,7 @@ notes_active(synth_t *synth)
return flag;
}
-float cur_freq, next_freq;
+float lfo_index;
float
make_sample(void *synthData, unsigned int sample_rate, int frame)
@@ -174,34 +182,23 @@ make_sample(void *synthData, unsigned int sample_rate, int frame)
if (!synth->midi_note[i].active)
continue;
- float adsr = adsr_amplitude(&synth->adsr,
+ float adsr = fix_adsr(&synth->adsr,
synth->midi_note[i].noteOn,
synth->midi_note[i].noteOff,
- synth->midi_note[i].elapsed);
+ synth->midi_note[i].elapsed,
+ synth->midi_note[i].noteOffSample);
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);
+ targ_freq = targ_freq + targ_freq *
+ cc_iget(&synth->cc_lfo_amp, frame, FRAMES_PER_BUFFER) *
+ // (1.05946309436/24.0) *
+ (wvt_next(wvt_tri, (wvt_next(wvt_sound, 2 * cc_iget(&synth->cc_lfo_freq, frame, FRAMES_PER_BUFFER), SAMPLE_RATE, &lfo_index) / 2.0 + 0.5) * cc_iget(&synth->cc_lfo_freq, frame, FRAMES_PER_BUFFER), SAMPLE_RATE, &synth->midi_note[i].lfo_index) / 2.0 + 0.5);
float synth_sample = synth->gen[synth->geni](targ_freq,
&synth->midi_note[i],
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;
}
@@ -265,8 +262,7 @@ get_frame(void *outputBuffer, synth_t *synth, int i)
*out++ = 0.0f;
add_to_viz(synth, 0.0f);
-
- synth->lfo.elapsed = 0;
+ lfo_index = 0;
return;
}
@@ -275,6 +271,7 @@ get_frame(void *outputBuffer, synth_t *synth, int i)
*out++ = 0.0f;
*out++ = 0.0f;
add_to_viz(synth, 0.0f);
+ lfo_index = 0;
return;
}
@@ -410,7 +407,9 @@ 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_lfo_freq, "lfo_freq", .01, 10, .02, 1);
+ CC(synth->cc_lfo_freq, "lfo_freq", 1, 1000, 2, 1);
+ CC(synth->cc_lfo_amp, "lfo_amp", 0, 1, .01, 0);
CC(synth->cc_pitch, "pitch", -3, 4, 0.01, 1);
synth->modi = 0;
@@ -438,6 +437,8 @@ init_synth(synth_t * synth)
synth->midi_note[i].velocity = -1;
synth->midi_note[i].elapsed = -1;
synth->midi_note[i].active = 0;
+ synth->midi_note[i].wvt_index = 0;
+ synth->midi_note[i].lfo_index = 0;
synth->midi_note[i].adsr = &(synth->adsr);
}
@@ -453,7 +454,10 @@ init_synth(synth_t * synth)
synth->gen[1] = gen1;
synth->gen[2] = gen2;
synth->gen[3] = gen3;
- synth->geni = 2;
+ synth->gen[4] = gen4;
+ synth->gen[5] = gen5;
+ synth->gen[6] = gen6;
+ synth->geni = 3;
synth->active = 0;
diff --git a/src/synth_engine.h b/src/synth_engine.h
index a1f3d4a..38ff740 100644
--- a/src/synth_engine.h
+++ b/src/synth_engine.h
@@ -6,21 +6,12 @@
#include <math.h>
#include <portaudio.h>
+#include "synth_common.h"
#include "notes.h"
#include "filter.h"
#include "adsr.h"
#include "control.h"
-//#define SAMPLE_RATE (44100)
-#define SAMPLE_RATE (48000)
-#define FRAMES_PER_BUFFER (256)
-
-#define VIZ_BUF 1024
-
-#define WIDTH 1024
-#define HEIGHT 600
-
-#define MIDI_NOTES 128
#ifndef M_PI
#define M_PI (3.14159265)
@@ -38,8 +29,10 @@ typedef struct midi_note_t {
PaTime noteOn;
PaTime noteOff;
float velocity; // normalized
- size_t wvt_index;
+ float wvt_index;
+ float lfo_index;
unsigned long long elapsed;
+ unsigned long long noteOffSample;
adsr_t * adsr;
int active;
} midi_note_t;
@@ -60,6 +53,7 @@ typedef struct {
cc_t cc_cutoff;
cc_t cc_resonance;
cc_t cc_lfo_freq;
+ cc_t cc_lfo_amp;
float freq_offset;
float gain;
@@ -81,7 +75,7 @@ typedef struct {
int modifiers[16];
int modi;
- float (*gen[4]) (float freq, midi_note_t * midi_note, float x, unsigned int sample_rate);
+ float (*gen[7]) (float freq, midi_note_t * midi_note, float x, unsigned int sample_rate);
int geni;
float cutoff;
diff --git a/src/synth_gui.c b/src/synth_gui.c
index ee4ae05..ca1acc9 100644
--- a/src/synth_gui.c
+++ b/src/synth_gui.c
@@ -23,6 +23,7 @@ keyboard(void *synthData, PaStream *stream)
synth->midi_note[i].velocity = 1.0;
synth->midi_note[i].elapsed = 0;
synth->midi_note[i].active = 1;
+
//synth->adsr.elapsed = 0;
synth->active = 1;
}
@@ -30,6 +31,7 @@ keyboard(void *synthData, PaStream *stream)
for (int i = 0; keys[i]; i++) {
if (IsKeyReleased(keys[i])) {
synth->midi_note[i].noteOff = Pa_GetStreamTime(synth->stream);
+ synth->midi_note[i].noteOffSample = synth->midi_note[i].elapsed;
note = notes[i % 12][(synth->octave + (i / 12)) % 8];
}
}
@@ -56,6 +58,9 @@ keyboard(void *synthData, PaStream *stream)
if (synth->modifiers[i] == KEY_V) {
cc_step(&synth->cc_lfo_freq, 1);
}
+ if (synth->modifiers[i] == KEY_B) {
+ cc_step(&synth->cc_lfo_amp, 1);
+ }
}
}
if (IsKeyDown(264)) { // down
@@ -72,6 +77,9 @@ keyboard(void *synthData, PaStream *stream)
if (synth->modifiers[i] == KEY_V) {
cc_step(&synth->cc_lfo_freq, -1);
}
+ if (synth->modifiers[i] == KEY_B) {
+ cc_step(&synth->cc_lfo_amp, -1);
+ }
}
}
if (IsKeyDown(KEY_ENTER)) { // down
@@ -88,6 +96,9 @@ keyboard(void *synthData, PaStream *stream)
if (synth->modifiers[i] == KEY_V) {
cc_reset(&synth->cc_lfo_freq);
}
+ if (synth->modifiers[i] == KEY_B) {
+ cc_reset(&synth->cc_lfo_amp);
+ }
}
}
}
@@ -131,21 +142,21 @@ draw_signals(synth_t * synth, int x, int y, int width, int height)
DrawCircle(i , y + height / 2 + floor(250 * synth->viz.wave[i - x]), point_radius, MAGENTA);
}
- float adsr_duration = synth->adsr.a + synth->adsr.d + (synth->adsr.a + synth->adsr.d + synth->adsr.r) / 3.0 + synth->adsr.r;
- for (int i = x; i < WIDTH - x; i++) {
- for (int j = 0; j < MIDI_NOTES; j++) {
- if (!synth->midi_note[j].active)
- continue;
+/* float adsr_duration = synth->adsr.a + synth->adsr.d + (synth->adsr.a + synth->adsr.d + synth->adsr.r) / 3.0 + synth->adsr.r; */
+/* for (int i = x; i < WIDTH - x; i++) { */
+/* for (int j = 0; j < MIDI_NOTES; j++) { */
+/* if (!synth->midi_note[j].active) */
+/* continue; */
- DrawCircle(i , y + height - 1 + 4.5*floor((WIDTH / 24) * - adsr_amplitude(&synth->adsr, synth->midi_note[j].noteOn, synth->midi_note[j].noteOff, (i - x) * (adsr_duration * SAMPLE_RATE) / WIDTH)), point_radius, BLUE);
- }
- }
+/* DrawCircle(i , y + height - 1 + 4.5*floor((WIDTH / 24) * - fix_adsr(&synth->adsr, synth->midi_note[j].noteOn, synth->midi_note[j].noteOff, (i - x) * (adsr_duration * SAMPLE_RATE) / WIDTH, synth->midi_note[j].noteOffSample)), point_radius, BLUE); */
+/* } */
+/* } */
for (int i = x; i < WIDTH - x; i++) {
for (int j = 0; j < MIDI_NOTES; j++) {
if (!synth->midi_note[j].active)
continue;
- DrawCircle(i , y + height - 1 + 4.5*floor((WIDTH / 24) * - adsr_amplitude(&synth->adsr, synth->midi_note[j].noteOn, synth->midi_note[j].noteOff, synth->midi_note[j].elapsed)), point_radius, GREEN);
+ DrawCircle(i , y + height - 1 + 4.5*floor((WIDTH / 24) * - fix_adsr(&synth->adsr, synth->midi_note[j].noteOn, synth->midi_note[j].noteOff, synth->midi_note[j].elapsed, synth->midi_note[j].noteOffSample)), point_radius, GREEN);
}
}
}
@@ -188,6 +199,10 @@ rayrun(void *synthData)
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, "lfo amp %.4f", synth->cc_lfo_amp.value);
+ DrawText(buf, WIDTH / 2 - 108, 150 - 22, 20, LIGHTGRAY);
+ snprintf(buf, sizeof buf, "lfo freq %.4f", synth->cc_lfo_freq.value);
+ DrawText(buf, WIDTH / 2 - 108, 150 - 102, 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); */
@@ -210,7 +225,7 @@ rayrun(void *synthData)
GuiSpinner((Rectangle){ WIDTH - 100 - 50 , 50, 100, 24 }, "oct: ", &(synth->octave), 0, 7, 0);
snprintf(buf, sizeof buf, "generator %d --> ", synth->geni);
- GuiSpinner((Rectangle){ WIDTH - 100 - 50 , 50 + 24 + 6, 100, 24 }, buf, &(synth->geni), 0, 3, 0);
+ GuiSpinner((Rectangle){ WIDTH - 100 - 50 , 50 + 24 + 6, 100, 24 }, buf, &(synth->geni), 0, 6, 0);
synth->clamp = GuiToggle((Rectangle){ WIDTH - 100 - 50, 50 + 24 + 6 + 24 + 6, 100, 24 }, "clamp", synth->clamp);
@@ -218,7 +233,7 @@ rayrun(void *synthData)
// signals
draw_signals(synth, 20, 390, WIDTH - 2*20, 200);
- DrawText("THE SYNTH!!!!!!!!!!!!!!!!!!1", WIDTH / 2 - 100, 50, 20, LIGHTGRAY);
+ //DrawText("THE SYNTH!!!!!!!!!!!!!!!!!!1", WIDTH / 2 - 100, 50, 20, LIGHTGRAY);
DrawText("KEYBOARD: Q .. ] TOGGLE MULTI: ENTER", WIDTH / 2 -300, HEIGHT - 300 - 50, 20, LIGHTGRAY);
snprintf(buf, sizeof buf, "%f stream time: %f", synth->viz.wave[0], Pa_GetStreamTime(synth->stream));
DrawText(buf, WIDTH / 2 -300, HEIGHT - 300, 20, LIGHTGRAY);
diff --git a/src/wavetable.c b/src/wavetable.c
new file mode 100644
index 0000000..ab3308a
--- /dev/null
+++ b/src/wavetable.c
@@ -0,0 +1,182 @@
+#include "wavetable.h"
+#include <sndfile.h>
+
+static wave_t wvt_sine_data;
+static wave_t wvt_saw_data;
+static wave_t wvt_digisaw_data;
+static wave_t wvt_tri_data;
+static wave_t wvt_sqr_data;
+static wave_t wvt_sound_data;
+
+
+void
+wvt_sine_init(size_t size)
+{
+ wvt_sine_data.size = size;
+ wvt_sine_data.data = (float *) malloc(sizeof(float) * size);
+ for (int i = 0; i < size; i++) {
+ wvt_sine_data.data[i] = sinf(2 * M_PI * i / size);
+ }
+}
+
+void
+wvt_saw_init(size_t size)
+{
+ wvt_saw_data.size = size;
+ wvt_saw_data.data = (float *) malloc(sizeof(float) * size);
+ for (int i = 0; i < size; i++) {
+ float dOutput = 0.0;
+ for (float n = 1.0; n < 25; n++)
+ dOutput += sinf(n * 2.0 * M_PI * i / size) / n;
+ wvt_saw_data.data[i] = 0.6 * dOutput;
+ }
+}
+
+void
+wvt_digisaw_init(size_t size)
+{
+ wvt_digisaw_data.size = size;
+ wvt_digisaw_data.data = (float *) malloc(sizeof(float) * size);
+ for (int i = 0; i < size; i++) {
+ wvt_digisaw_data.data[i] = (float)i / size;
+ }
+}
+
+void
+wvt_sqr_init(size_t size)
+{
+ wvt_sqr_data.size = size;
+ wvt_sqr_data.data = (float *) malloc(sizeof(float) * size);
+ for (int i = 0; i < size; i++) {
+ if (i < size / 2) {
+ wvt_sqr_data.data[i] = -1.0f;
+ } else {
+ wvt_sqr_data.data[i] = 1.0f;
+ }
+ }
+}
+
+void
+wvt_sound_init(char * path)
+{
+ SNDFILE* file;
+ SF_INFO fileInfo;
+ int numSamplesRead;
+
+ // Open the WAV file
+ file = sf_open(path, SFM_READ, &fileInfo);
+ if (!file) {
+ printf("Failed to open the WAV file: %s\n", sf_strerror(NULL));
+ return;
+ }
+ printf("frames: %ld sr: %d chan: %d format: %d sections: %d seekable: %d\n", fileInfo.frames, fileInfo.samplerate, fileInfo.channels, fileInfo.format, fileInfo.sections, fileInfo.seekable);
+ // Ensure the WAV file has only one channel
+ if (fileInfo.channels != 1) {
+ printf("Only mono WAV files are supported.\n");
+ sf_close(file);
+ return;
+ }
+
+ wvt_sound_data.size = fileInfo.frames ;
+ wvt_sound_data.data = (float *) malloc(sizeof(float) * fileInfo.frames);
+
+ // Read the WAV file into the buffer
+ numSamplesRead = sf_readf_float(file, wvt_sound_data.data, fileInfo.frames);
+
+ float max = -1000;
+ float min = 1000;
+ for (int i = 0; i < numSamplesRead; i++) {
+ float s = wvt_sound_data.data[i];
+ // printf("Sample %d: %f\n", i, s);
+ if (s < min) min = s;
+ if (s > max) max = s;
+ }
+ printf("Min: %f Max: %f\n", min, max);
+
+ // Close the WAV file
+ sf_close(file);
+}
+
+void
+wvt_init()
+{
+ wvt_sine_init(44100);
+ wvt_saw_init(128);
+ wvt_digisaw_init(48000);
+ wvt_sqr_init(48000);
+ wvt_tri_data.size = 2;
+ wvt_tri_data.data = (float *) malloc(sizeof(float) * 2);
+ wvt_tri_data.data[0] = -1.0f;
+ wvt_tri_data.data[1] = 1.0f;
+ wvt_sound_init("/home/gramanas/code/synth-project/waves/test1.wav");
+}
+
+
+float
+wvt_gen(wave_t * wave, float * index, float freq, int sample_rate)
+{
+ if (*index >= wave->size) *index = 0;
+
+ int current_index = (int)*index;
+ int next_index = current_index + 1 >= wave->size ? 0 : current_index + 1;
+
+ // linear interpolation between current_index and next_index
+ float m = (wave->data[next_index] - wave->data[current_index]) / (1 - 0);
+ float b = wave->data[current_index] /* + m * 0 */;
+
+ // the value to pick
+ float x = *index - current_index;
+
+ float sample = m * x + b;
+ float step = freq * ((float)wave->size / sample_rate);
+
+ *index += step;
+ if (*index >= wave->size) {
+ *index -= wave->size;
+ }
+
+ return sample;
+}
+
+float
+wvt_next(float (* wvt_fun)(float *, float, int), float freq, int sample_rate, float * wvt_index)
+{
+ float sample = wvt_fun(wvt_index, freq, sample_rate);
+ return sample;
+}
+
+float
+wvt_saw(float * index, float freq, int sample_rate)
+{
+ return wvt_gen(&wvt_saw_data, index, freq, sample_rate);
+}
+
+float
+wvt_digisaw(float * index, float freq, int sample_rate)
+{
+ return wvt_gen(&wvt_digisaw_data, index, freq, sample_rate);
+}
+
+float
+wvt_sine(float * index, float freq, int sample_rate)
+{
+ return wvt_gen(&wvt_sine_data, index, freq, sample_rate);
+}
+
+float
+wvt_tri(float * index, float freq, int sample_rate)
+{
+ return wvt_gen(&wvt_tri_data, index, freq, sample_rate);
+}
+
+float
+wvt_sqr(float * index, float freq, int sample_rate)
+{
+ return wvt_gen(&wvt_sqr_data, index, freq, sample_rate);
+}
+
+float
+wvt_sound(float * index, float freq, int sample_rate)
+{
+ return wvt_gen(&wvt_sound_data, index, freq, sample_rate);
+}
diff --git a/src/wavetable.h b/src/wavetable.h
new file mode 100644
index 0000000..46174b4
--- /dev/null
+++ b/src/wavetable.h
@@ -0,0 +1,24 @@
+#ifndef WAVETABLE_H
+#define WAVETABLE_H
+
+#include "synth_engine.h"
+
+#define WVT_MAX 44100
+
+typedef struct wave_t {
+ float * data;
+ size_t size;
+} wave_t;
+
+float wvt_sine(float *, float, int);
+float wvt_saw(float *, float, int);
+float wvt_digisaw(float *, float, int);
+float wvt_tri(float *, float, int);
+float wvt_sqr(float *, float, int);
+float wvt_sound(float *, float, int);
+
+float wvt_next(float (* wvt_fun)(float *, float, int), float freq, int sample_rate, float * wvt_index);
+
+void wvt_init();
+
+#endif /* WAVETABLE_H */