From c8cd7f9298de876f2046fddd2e322a63c421a505 Mon Sep 17 00:00:00 2001 From: gramanas Date: Fri, 7 Jul 2023 01:09:12 +0300 Subject: suppa --- src/Makefile.am | 9 ++- src/adsr.c | 49 +++++++++++++++ src/adsr.h | 1 + src/control.h | 12 ---- src/midi.c | 1 + src/osc.c | 97 ++++++++++++++++++++++++++++ src/osc.h | 72 +++++++++++++++++++++ src/osc_sin.c | 22 +++++++ src/osc_sound.c | 34 ++++++++++ src/osc_tri.c | 30 +++++++++ src/osc_weird.c | 25 ++++++++ src/synth_common.h | 15 +++++ src/synth_engine.c | 96 ++++++++++++++-------------- src/synth_engine.h | 18 ++---- src/synth_gui.c | 37 +++++++---- src/wavetable.c | 182 +++++++++++++++++++++++++++++++++++++++++++++++++++++ src/wavetable.h | 24 +++++++ 17 files changed, 642 insertions(+), 82 deletions(-) create mode 100644 src/osc.c create mode 100644 src/osc.h create mode 100644 src/osc_sin.c create mode 100644 src/osc_sound.c create mode 100644 src/osc_tri.c create mode 100644 src/osc_weird.c create mode 100644 src/synth_common.h create mode 100644 src/wavetable.c create mode 100644 src/wavetable.h 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 +#include +#include + + +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 + +/** + * 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 + +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 @@ -45,21 +46,6 @@ 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) { @@ -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 #include +#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 + +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 */ -- cgit v1.2.3