From c3e6687281538467d2890536fcd0f3b7d6451367 Mon Sep 17 00:00:00 2001 From: HiveBeats Date: Sun, 18 Jun 2023 14:51:14 +0400 Subject: [PATCH] feat: explicit oscillator-related code --- .vscode/tasks.json | 1 + build.sh | 2 +- main.c | 192 +-------------------------------------------- oscillator.c | 153 ++++++++++++++++++++++++++++++++++++ oscillator.h | 33 ++++++++ settings.h | 18 +++++ 6 files changed, 208 insertions(+), 191 deletions(-) create mode 100644 oscillator.c create mode 100644 oscillator.h create mode 100644 settings.h diff --git a/.vscode/tasks.json b/.vscode/tasks.json index eba6acd..e43101c 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -11,6 +11,7 @@ "${file}", "${fileDirname}/utils.c", "${fileDirname}/ring_buffer.c", + "${fileDirname}/oscillator.c", "${fileDirname}/parser.c", "-lm", "-lraylib", diff --git a/build.sh b/build.sh index 0c4a7a5..80fa68b 100644 --- a/build.sh +++ b/build.sh @@ -1,3 +1,3 @@ #!/bin/bash CC="${CXX:-cc}" -$CC -Wall -std=c11 ./main.c ./utils.c ./ring_buffer.c ./parser.c -lm -lraylib -o ./bin/main +$CC -Wall -std=c11 ./main.c ./utils.c ./ring_buffer.c ./oscillator.c ./parser.c -lm -lraylib -o ./bin/main diff --git a/main.c b/main.c index 4632b3c..476f130 100644 --- a/main.c +++ b/main.c @@ -6,199 +6,11 @@ #include "parser.h" #include "utils.h" #include "ring_buffer.h" +#include "settings.h" +#include "oscillator.h" #include "raylib.h" -#define SAMPLE_RATE 48000.f -#define BPM 120.f -#define BEAT_DURATION 60.f/BPM -#define PITCH_STANDARD 440.f -#define VOLUME 0.5f -#define ATTACK_MS 100.f -#define STREAM_BUFFER_SIZE 4096 - -#define SYNTH_PI 3.1415926535f -#define SYNTH_VOLUME 0.5f - -#define WINDOW_WIDTH 640 -#define WINDOW_HEIGHT 480 - - -//------------------------------------------------------------------------------------ -// Oscillator -//------------------------------------------------------------------------------------ - -typedef enum { - Sine, - Triangle, - Saw, - Square -} OscillatorType; - -typedef struct OscillatorParameter { - OscillatorType osc; - float freq; -} OscillatorParameter; - -typedef struct OscillatorParameterList { - OscillatorParameter* array; - size_t count; -} OscillatorParameterList; - -typedef struct OscillatorGenerationParameter { - OscillatorParameterList oscillators; - float sample; -} OscillatorGenerationParameter; - -static SynthSound get_init_samples(float duration) { - size_t sample_count = (size_t)(duration * SAMPLE_RATE); - float* samples = malloc(sizeof(float) * sample_count); - - for (double i = 0.0; i < duration * SAMPLE_RATE; i++) { - samples[(int)i] = i; - } - - SynthSound res = { - .samples = samples, - .sample_count = sample_count - }; - - return res; -} - -static float pos(float hz, float x) { - return fmodf(hz * x / SAMPLE_RATE, 1); -} - -float sineosc(float hz, float x) { - return sinf(x * (2.f * SYNTH_PI * hz / SAMPLE_RATE)); -} - -static float sign(float v) { - return (v > 0.0) ? 1.f : -1.f; -} - -float squareosc(float hz, float x) { - return sign(sineosc(hz, x)); -} - -float triangleosc(float hz, float x) { - return 1.f - fabsf(pos(hz, x) - 0.5f) * 4.f; -} - -float sawosc(float hz, float x) { - return pos(hz, x) * 2.f - 1.f; -} - -float multiosc(OscillatorGenerationParameter param) { - float osc_sample = 0.f; - for (size_t i = 0; i < param.oscillators.count; i++) { - OscillatorParameter osc = param.oscillators.array[i]; - switch (osc.osc) { - case Sine: - osc_sample += sineosc(osc.freq, param.sample); - break; - case Triangle: - osc_sample += triangleosc(osc.freq, param.sample); - break; - case Square: - osc_sample += squareosc(osc.freq, param.sample); - break; - case Saw: - osc_sample += sawosc(osc.freq, param.sample); - break; - } - } - - return osc_sample; -} - -static SynthSound freq(float duration, OscillatorParameterList osc) { - SynthSound samples = get_init_samples(duration); - // SynthSound attack = get_attack_samples(); - - float* output = malloc(sizeof(float) * samples.sample_count); - for (int i = 0; i < samples.sample_count; i++) { - float sample = samples.samples[i]; - OscillatorGenerationParameter param = { - .oscillators = osc, - .sample = sample - }; - output[i] = multiosc(param) * VOLUME; - } - - // create attack and release - /* - let adsrLength = Seq.length output - let attackArray = attack |> Seq.take adsrLength - let release = Seq.rev attackArray - */ - /* - todo: I will change the ADSR approach to an explicit ADSR module(with it's own state) - size_t adsr_length = samples.sample_count; - float *attackArray = NULL, *releaseArray = NULL; - - if (adsr_length > 0) { - //todo: calloc - attackArray = malloc(sizeof(float) * adsr_length); - size_t attack_length = attack.sample_count < adsr_length - ? attack.sample_count - : adsr_length; - - memcpy(attackArray, attack.samples, attack_length); - //todo: calloc - releaseArray = malloc(sizeof(float) * adsr_length); - memcpy(releaseArray, attackArray, attack_length); - reverse_array(releaseArray, 0, adsr_length); - } - */ - - // if (samples.sample_count > 1024) { - // samples.sample_count = 1024; - // } - // //todo: move to somewhere - // for (size_t i = 0; i < 1024; i++) { - // synth_sound.samples[i] = 0.0f; - // } - - // for (size_t i = 0; i < samples.sample_count; i++) { - // synth_sound.samples[i] = output[i]; - // } - // synth_sound.sample_count = samples.sample_count; - - - // return zipped array - SynthSound res = { - .samples = output, - .sample_count = samples.sample_count - }; - - return res; -} - -/* -static SynthSound get_attack_samples() { - float attack_time = 0.001 * ATTACK_MS; - size_t sample_count = (size_t)(attack_time * SAMPLE_RATE); - float* attack = malloc(sizeof(float) * sample_count); - float samples_to_rise = SAMPLE_RATE * attack_time; - float rising_delta = 1.0 / samples_to_rise; - float i = 0.0; - - for (int j = 0; j < sample_count; j++) { - i += rising_delta; - attack[j] = fmin(i, 1.0); - } - - SynthSound res = { - .samples = attack, - .sample_count = sample_count - }; - - return res; -} -*/ - //------------------------------------------------------------------------------------ // Synth //------------------------------------------------------------------------------------ diff --git a/oscillator.c b/oscillator.c new file mode 100644 index 0000000..693a867 --- /dev/null +++ b/oscillator.c @@ -0,0 +1,153 @@ +#include "oscillator.h" +#include "settings.h" +#include "math.h" +#include "stdlib.h" + +static SynthSound get_init_samples(float duration) { + size_t sample_count = (size_t)(duration * SAMPLE_RATE); + float* samples = malloc(sizeof(float) * sample_count); + + for (double i = 0.0; i < duration * SAMPLE_RATE; i++) { + samples[(int)i] = i; + } + + SynthSound res = { + .samples = samples, + .sample_count = sample_count + }; + + return res; +} + +static float pos(float hz, float x) { + return fmodf(hz * x / SAMPLE_RATE, 1); +} + +static float sineosc(float hz, float x) { + return sinf(x * (2.f * SYNTH_PI * hz / SAMPLE_RATE)); +} + +static float sign(float v) { + return (v > 0.0) ? 1.f : -1.f; +} + +static float squareosc(float hz, float x) { + return sign(sineosc(hz, x)); +} + +static float triangleosc(float hz, float x) { + return 1.f - fabsf(pos(hz, x) - 0.5f) * 4.f; +} + +static float sawosc(float hz, float x) { + return pos(hz, x) * 2.f - 1.f; +} + +float multiosc(OscillatorGenerationParameter param) { + float osc_sample = 0.f; + for (size_t i = 0; i < param.oscillators.count; i++) { + OscillatorParameter osc = param.oscillators.array[i]; + switch (osc.osc) { + case Sine: + osc_sample += sineosc(osc.freq, param.sample); + break; + case Triangle: + osc_sample += triangleosc(osc.freq, param.sample); + break; + case Square: + osc_sample += squareosc(osc.freq, param.sample); + break; + case Saw: + osc_sample += sawosc(osc.freq, param.sample); + break; + } + } + + return osc_sample; +} + +SynthSound freq(float duration, OscillatorParameterList osc) { + SynthSound samples = get_init_samples(duration); + // SynthSound attack = get_attack_samples(); + + float* output = malloc(sizeof(float) * samples.sample_count); + for (int i = 0; i < samples.sample_count; i++) { + float sample = samples.samples[i]; + OscillatorGenerationParameter param = { + .oscillators = osc, + .sample = sample + }; + output[i] = multiosc(param) * VOLUME; + } + + // create attack and release + /* + let adsrLength = Seq.length output + let attackArray = attack |> Seq.take adsrLength + let release = Seq.rev attackArray + */ + /* + todo: I will change the ADSR approach to an explicit ADSR module(with it's own state) + size_t adsr_length = samples.sample_count; + float *attackArray = NULL, *releaseArray = NULL; + + if (adsr_length > 0) { + //todo: calloc + attackArray = malloc(sizeof(float) * adsr_length); + size_t attack_length = attack.sample_count < adsr_length + ? attack.sample_count + : adsr_length; + + memcpy(attackArray, attack.samples, attack_length); + //todo: calloc + releaseArray = malloc(sizeof(float) * adsr_length); + memcpy(releaseArray, attackArray, attack_length); + reverse_array(releaseArray, 0, adsr_length); + } + */ + + // if (samples.sample_count > 1024) { + // samples.sample_count = 1024; + // } + // //todo: move to somewhere + // for (size_t i = 0; i < 1024; i++) { + // synth_sound.samples[i] = 0.0f; + // } + + // for (size_t i = 0; i < samples.sample_count; i++) { + // synth_sound.samples[i] = output[i]; + // } + // synth_sound.sample_count = samples.sample_count; + + + // return zipped array + SynthSound res = { + .samples = output, + .sample_count = samples.sample_count + }; + + return res; +} + +/* +static SynthSound get_attack_samples() { + float attack_time = 0.001 * ATTACK_MS; + size_t sample_count = (size_t)(attack_time * SAMPLE_RATE); + float* attack = malloc(sizeof(float) * sample_count); + float samples_to_rise = SAMPLE_RATE * attack_time; + float rising_delta = 1.0 / samples_to_rise; + float i = 0.0; + + for (int j = 0; j < sample_count; j++) { + i += rising_delta; + attack[j] = fmin(i, 1.0); + } + + SynthSound res = { + .samples = attack, + .sample_count = sample_count + }; + + return res; +} +*/ \ No newline at end of file diff --git a/oscillator.h b/oscillator.h new file mode 100644 index 0000000..536cc0d --- /dev/null +++ b/oscillator.h @@ -0,0 +1,33 @@ +#ifndef OSCILLATOR_H +#define OSCILLATOR_H + +#include "utils.h" + +typedef enum { + Sine, + Triangle, + Saw, + Square +} OscillatorType; + +typedef struct OscillatorParameter { + OscillatorType osc; + float freq; +} OscillatorParameter; + +typedef struct OscillatorParameterList { + OscillatorParameter* array; + size_t count; +} OscillatorParameterList; + +typedef struct OscillatorGenerationParameter { + OscillatorParameterList oscillators; + float sample; +} OscillatorGenerationParameter; + +float multiosc(OscillatorGenerationParameter param); +SynthSound freq(float duration, OscillatorParameterList osc); + + + +#endif \ No newline at end of file diff --git a/settings.h b/settings.h new file mode 100644 index 0000000..e4f600a --- /dev/null +++ b/settings.h @@ -0,0 +1,18 @@ +#ifndef SETTINGS_H +#define SETTINGS_H + +#define SAMPLE_RATE 48000.f +#define BPM 120.f +#define BEAT_DURATION 60.f/BPM +#define PITCH_STANDARD 440.f +#define VOLUME 0.5f +#define ATTACK_MS 100.f +#define STREAM_BUFFER_SIZE 4096 + +#define SYNTH_PI 3.1415926535f +#define SYNTH_VOLUME 0.5f + +#define WINDOW_WIDTH 640 +#define WINDOW_HEIGHT 480 + +#endif \ No newline at end of file