Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 8 additions & 8 deletions include/osc.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,16 @@ typedef struct {
Wavetable *wt; // pointer to this oscillator's wavetable
} Osc;

Osc *osc_create(Waveform type, size_t length, double freq);
void osc_set_freq(Osc *osc, double freq);
void osc_destroy(Osc *osc);
Osc *Osc_create(Waveform type, size_t length, double freq);
void Osc_set_freq(Osc *osc, double freq);
void Osc_destroy(Osc *osc);

typedef struct {
Vec *vec;
} OscVec;

OscVec *oscvec_create(void);
void oscvec_push(OscVec *ov, Osc *osc);
Osc *oscvec_get(const OscVec *ov, size_t index);
size_t oscvec_size(const OscVec *ov);
void oscvec_destroy(OscVec *ov);
OscVec *OscVec_create(void);
void OscVec_push(OscVec *ov, Osc *osc);
Osc *OscVec_get(const OscVec *ov, size_t index);
size_t OscVec_size(const OscVec *ov);
void OscVec_destroy(OscVec *ov);
12 changes: 6 additions & 6 deletions include/vec.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,9 @@ typedef struct {
ElemDestroyFunc destroy_func; // Function pointer for element deletion
} Vec;

Vec *vec_create(size_t element_size, ElemDestroyFunc destroy_func);
void vec_push_back(Vec *vec, const void *element);
void vec_get(const Vec *vec, size_t index, void *out_element);
void vec_set(Vec *vec, size_t index, const void *element);
void vec_pop_back(Vec *vec);
void vec_destroy(Vec *vec);
Vec *Vec_create(size_t element_size, ElemDestroyFunc destroy_func);
void Vec_push_back(Vec *vec, const void *element);
void Vec_get(const Vec *vec, size_t index, void *out_element);
void Vec_set(Vec *vec, size_t index, const void *element);
void Vec_pop_back(Vec *vec);
void Vec_destroy(Vec *vec);
6 changes: 3 additions & 3 deletions include/wavetable.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,9 @@ typedef struct {
Waveform type;
} Wavetable;

Wavetable *wavetable_create(Waveform type, size_t length);
void wavetable_set_custom(Wavetable *wt, float *data, size_t length);
void wavetable_destroy(Wavetable *wt);
Wavetable *Wavetable_create(Waveform type, size_t length);
void Wavetable_set_custom(Wavetable *wt, float *data, size_t length);
void Wavetable_destroy(Wavetable *wt);

typedef struct {
Vec *vec;
Expand Down
76 changes: 38 additions & 38 deletions src/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,14 @@
#include "vec.h"

// Global mutex to protect oscillator state.
static pthread_mutex_t osc_mutex = PTHREAD_MUTEX_INITIALIZER;
static pthread_mutex_t Osc_mutex = PTHREAD_MUTEX_INITIALIZER;

// The write callback: libsoundio calls this when it needs more audio samples.
// Now we use each oscillator's own wavetable length.
static void write_callback(struct SoundIoOutStream *outstream, int frame_count_min,
int frame_count_max) {
(void)frame_count_min;
Vec *osc_vec = (Vec *)outstream->userdata;
Vec *Osc_vec = (Vec *)outstream->userdata;
int frames_left = frame_count_max;

while (frames_left > 0) {
Expand All @@ -35,31 +35,31 @@ static void write_callback(struct SoundIoOutStream *outstream, int frame_count_m
for (int frame = 0; frame < frame_count; frame++) {
float sample = 0.0f;

pthread_mutex_lock(&osc_mutex);
pthread_mutex_lock(&Osc_mutex);
// Mix output from each oscillator.
for (size_t i = 0; i < osc_vec->size; i++) {
for (size_t i = 0; i < Osc_vec->size; i++) {
// Retrieve the Osc* stored at index i.
Osc *osc = ((Osc **)(osc_vec->data))[i];
Osc *osc = ((Osc **)(Osc_vec->data))[i];
// Use the actual wavetable length for indexing.
size_t wt_length = osc->wt->length;
double pos = osc->phase;
int index0 = (int)pos;
int index1 = (index0 + 1) % wt_length;
double frac = pos - index0;
float osc_sample =
float Osc_sample =
(float)((1.0 - frac) * osc->wt->data[index0] + frac * osc->wt->data[index1]);
sample += osc_sample;
sample += Osc_sample;

// Increment phase and wrap around using the actual wavetable length.
osc->phase += osc->phase_inc;
if (osc->phase >= wt_length)
osc->phase -= wt_length;
}
pthread_mutex_unlock(&osc_mutex);
pthread_mutex_unlock(&Osc_mutex);

// Average the mixed sample.
if (osc_vec->size > 0)
sample /= osc_vec->size;
if (Osc_vec->size > 0)
sample /= Osc_vec->size;

// Write the sample to all output channels.
for (int ch = 0; ch < outstream->layout.channel_count; ch++) {
Expand All @@ -81,23 +81,23 @@ int main(int argc, char **argv) {
(void)argc;
(void)argv;
// Create a vector for Osc pointers.
// We pass osc_destroy as the element destroy function so that each Osc is properly freed.
Vec *osc_vec = vec_create(sizeof(Osc *), (ElemDestroyFunc)osc_destroy);
// We pass Osc_destroy as the element destroy function so that each Osc is properly freed.
Vec *Osc_vec = Vec_create(sizeof(Osc *), (ElemDestroyFunc)Osc_destroy);
double freq0 = 440.0, freq1 = 550.0, freq2 = 660.0;

// Create oscillators with different wavetable resolutions.
// For example:
// - A high-resolution sine (1024 samples),
// - A medium-resolution saw (256 samples),
// - A low-resolution square (16 samples) for a bitcrushed effect.
Osc *tempA = osc_create(WAVEFORM_SINE, 1024, freq0);
Osc *tempB = osc_create(WAVEFORM_SAW, 256, freq1);
Osc *tempC = osc_create(WAVEFORM_SQUARE, 16, freq2);
Osc *tempA = Osc_create(WAVEFORM_SINE, 1024, freq0);
Osc *tempB = Osc_create(WAVEFORM_SAW, 256, freq1);
Osc *tempC = Osc_create(WAVEFORM_SQUARE, 16, freq2);

// Push the Osc pointers into the vector.
vec_push_back(osc_vec, &tempA);
vec_push_back(osc_vec, &tempB);
vec_push_back(osc_vec, &tempC);
Vec_push_back(Osc_vec, &tempA);
Vec_push_back(Osc_vec, &tempB);
Vec_push_back(Osc_vec, &tempC);

// Initialize libsoundio.
struct SoundIo *soundio = soundio_create();
Expand Down Expand Up @@ -148,7 +148,7 @@ int main(int argc, char **argv) {
}

// Set our oscillator vector as userdata and assign the callback.
outstream->userdata = osc_vec;
outstream->userdata = Osc_vec;
outstream->write_callback = write_callback;

// Open and start the output stream.
Expand Down Expand Up @@ -178,50 +178,50 @@ int main(int argc, char **argv) {

// Adjust oscillator 0 with UP/DOWN keys.
if (IsKeyPressed(KEY_UP)) {
pthread_mutex_lock(&osc_mutex);
pthread_mutex_lock(&Osc_mutex);
freq0 += 10.0;
osc_set_freq(((Osc **)(osc_vec->data))[0], freq0);
pthread_mutex_unlock(&osc_mutex);
Osc_set_freq(((Osc **)(Osc_vec->data))[0], freq0);
pthread_mutex_unlock(&Osc_mutex);
}
if (IsKeyPressed(KEY_DOWN)) {
pthread_mutex_lock(&osc_mutex);
pthread_mutex_lock(&Osc_mutex);
freq0 -= 10.0;
if (freq0 < 1.0)
freq0 = 1.0;
osc_set_freq(((Osc **)(osc_vec->data))[0], freq0);
pthread_mutex_unlock(&osc_mutex);
Osc_set_freq(((Osc **)(Osc_vec->data))[0], freq0);
pthread_mutex_unlock(&Osc_mutex);
}

// Adjust oscillator 1 with RIGHT/LEFT keys.
if (IsKeyPressed(KEY_RIGHT)) {
pthread_mutex_lock(&osc_mutex);
pthread_mutex_lock(&Osc_mutex);
freq1 += 10.0;
osc_set_freq(((Osc **)(osc_vec->data))[1], freq1);
pthread_mutex_unlock(&osc_mutex);
Osc_set_freq(((Osc **)(Osc_vec->data))[1], freq1);
pthread_mutex_unlock(&Osc_mutex);
}
if (IsKeyPressed(KEY_LEFT)) {
pthread_mutex_lock(&osc_mutex);
pthread_mutex_lock(&Osc_mutex);
freq1 -= 10.0;
if (freq1 < 1.0)
freq1 = 1.0;
osc_set_freq(((Osc **)(osc_vec->data))[1], freq1);
pthread_mutex_unlock(&osc_mutex);
Osc_set_freq(((Osc **)(Osc_vec->data))[1], freq1);
pthread_mutex_unlock(&Osc_mutex);
}

// Adjust oscillator 2 with W/S keys.
if (IsKeyPressed(KEY_W)) {
pthread_mutex_lock(&osc_mutex);
pthread_mutex_lock(&Osc_mutex);
freq2 += 10.0;
osc_set_freq(((Osc **)(osc_vec->data))[2], freq2);
pthread_mutex_unlock(&osc_mutex);
Osc_set_freq(((Osc **)(Osc_vec->data))[2], freq2);
pthread_mutex_unlock(&Osc_mutex);
}
if (IsKeyPressed(KEY_S)) {
pthread_mutex_lock(&osc_mutex);
pthread_mutex_lock(&Osc_mutex);
freq2 -= 10.0;
if (freq2 < 1.0)
freq2 = 1.0;
osc_set_freq(((Osc **)(osc_vec->data))[2], freq2);
pthread_mutex_unlock(&osc_mutex);
Osc_set_freq(((Osc **)(Osc_vec->data))[2], freq2);
pthread_mutex_unlock(&Osc_mutex);
}

EndDrawing();
Expand All @@ -232,7 +232,7 @@ int main(int argc, char **argv) {
soundio_outstream_destroy(outstream);
soundio_device_unref(device);
soundio_destroy(soundio);
vec_destroy(osc_vec);
Vec_destroy(Osc_vec);

return 0;
}
24 changes: 12 additions & 12 deletions src/osc.c
Original file line number Diff line number Diff line change
Expand Up @@ -3,39 +3,39 @@
#include <assert.h>
#include <stdlib.h>

Osc *osc_create(Waveform type, size_t length, double freq) {
Osc *Osc_create(Waveform type, size_t length, double freq) {
Osc *osc = malloc(sizeof(Osc));
osc->phase = 0.0;
osc->phase_inc = (TABLE_SIZE * freq) / SAMPLE_RATE;
osc->wt = wavetable_create(type, length);
osc->wt = Wavetable_create(type, length);
return osc;
}

void osc_set_freq(Osc *osc, double freq) {
void Osc_set_freq(Osc *osc, double freq) {
assert(freq > 0);
assert(freq < SAMPLE_RATE / 2.0); // nyquist
osc->phase_inc = (TABLE_SIZE * freq) / SAMPLE_RATE;
}

void osc_destroy(Osc *osc) {
void Osc_destroy(Osc *osc) {
assert(osc);
wavetable_destroy(osc->wt);
Wavetable_destroy(osc->wt);
free(osc);
}

OscVec *oscvec_create(void) {
OscVec *OscVec_create(void) {
OscVec *ov = malloc(sizeof(OscVec));
ov->vec = vec_create(sizeof(Osc *), (ElemDestroyFunc)osc_destroy);
ov->vec = Vec_create(sizeof(Osc *), (ElemDestroyFunc)Osc_destroy);
return ov;
}

void oscvec_push(OscVec *ov, Osc *osc) { vec_push_back(ov->vec, &osc); }
void OscVec_push(OscVec *ov, Osc *osc) { Vec_push_back(ov->vec, &osc); }

Osc *oscvec_get(const OscVec *ov, size_t index) { return ((Osc **)(ov->vec->data))[index]; }
Osc *OscVec_get(const OscVec *ov, size_t index) { return ((Osc **)(ov->vec->data))[index]; }

size_t oscvec_size(const OscVec *ov) { return ov->vec->size; }
size_t OscVec_size(const OscVec *ov) { return ov->vec->size; }

void oscvec_destroy(OscVec *ov) {
vec_destroy(ov->vec);
void OscVec_destroy(OscVec *ov) {
Vec_destroy(ov->vec);
free(ov);
}
12 changes: 6 additions & 6 deletions src/vec.c
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
#include <stdlib.h>
#include <string.h>

Vec *vec_create(size_t element_size, ElemDestroyFunc destroy_func) {
Vec *Vec_create(size_t element_size, ElemDestroyFunc destroy_func) {
assert(element_size > 0);

Vec *vec = malloc(sizeof(Vec));
Expand All @@ -26,7 +26,7 @@ Vec *vec_create(size_t element_size, ElemDestroyFunc destroy_func) {
return vec;
}

void vec_destroy(Vec *vec) {
void Vec_destroy(Vec *vec) {
assert(vec);

if (vec->destroy_func) {
Expand All @@ -39,7 +39,7 @@ void vec_destroy(Vec *vec) {
free(vec);
}

void vec_push_back(Vec *vec, const void *element) {
void Vec_push_back(Vec *vec, const void *element) {
assert(vec);
assert(element);

Expand All @@ -57,22 +57,22 @@ void vec_push_back(Vec *vec, const void *element) {
vec->size++;
}

void vec_get(const Vec *vec, size_t index, void *out_element) {
void Vec_get(const Vec *vec, size_t index, void *out_element) {
cr_assert(vec);
cr_assert(index < vec->size);

memcpy(out_element, (char *)vec->data + index * vec->element_size, vec->element_size);
}

void vec_set(Vec *vec, size_t index, const void *element) {
void Vec_set(Vec *vec, size_t index, const void *element) {
assert(vec);
assert(element);
assert(index < vec->size);

memcpy((char *)vec->data + index * vec->element_size, element, vec->element_size);
}

void vec_pop_back(Vec *vec) {
void Vec_pop_back(Vec *vec) {
assert(vec);
assert(vec->size > 0);

Expand Down
10 changes: 5 additions & 5 deletions src/wavetable.c
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
#include <stdio.h>
#include <stdlib.h>

Wavetable *wavetable_create(Waveform type, size_t length) {
Wavetable *Wavetable_create(Waveform type, size_t length) {
cr_assert(length > 0);

Wavetable *wt = malloc(sizeof(Wavetable));
Expand Down Expand Up @@ -50,19 +50,19 @@ Wavetable *wavetable_create(Waveform type, size_t length) {
return wt;
}

void wavetable_destroy(Wavetable *wt) {
void Wavetable_destroy(Wavetable *wt) {
cr_assert(wt);
free(wt->data);
free(wt);
}

WtVec *WtVec_create(void) {
WtVec *ov = malloc(sizeof(WtVec));
ov->vec = vec_create(sizeof(Wavetable *), (ElemDestroyFunc)wavetable_destroy);
ov->vec = Vec_create(sizeof(Wavetable *), (ElemDestroyFunc)Wavetable_destroy);
return ov;
}

void WtVec_push(WtVec *ov, Wavetable *osc) { vec_push_back(ov->vec, &osc); }
void WtVec_push(WtVec *ov, Wavetable *osc) { Vec_push_back(ov->vec, &osc); }

Wavetable *WtVec_get(const WtVec *ov, size_t index) {
return ((Wavetable **)(ov->vec->data))[index];
Expand All @@ -71,6 +71,6 @@ Wavetable *WtVec_get(const WtVec *ov, size_t index) {
size_t WtVec_size(const WtVec *ov) { return ov->vec->size; }

void WtVec_destroy(WtVec *ov) {
vec_destroy(ov->vec);
Vec_destroy(ov->vec);
free(ov);
}
8 changes: 4 additions & 4 deletions tests/test_osc.c
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@
Test(oscillator, create_and_frequency) {
double freq = 440.0;
size_t wt_length = 1024;
Osc *osc = osc_create(WAVEFORM_SINE, wt_length, freq);
cr_assert_not_null(osc, "osc_create returned NULL");
Osc *osc = Osc_create(WAVEFORM_SINE, wt_length, freq);
cr_assert_not_null(osc, "Osc_create returned NULL");
cr_assert_float_eq(osc->phase, 0.0, 0.0001, "Initial phase should be 0");

double expected_phase_inc = (TABLE_SIZE * freq) / SAMPLE_RATE;
Expand All @@ -18,9 +18,9 @@ Test(oscillator, create_and_frequency) {
cr_assert_eq(osc->wt->length, wt_length, "Wavetable length incorrect");

/* Update oscillator frequency */
osc_set_freq(osc, 880.0);
Osc_set_freq(osc, 880.0);
double new_expected_phase_inc = (TABLE_SIZE * 880.0) / SAMPLE_RATE;
cr_assert_float_eq(osc->phase_inc, new_expected_phase_inc, 0.0001, "Phase increment after frequency update incorrect");

osc_destroy(osc);
Osc_destroy(osc);
}
Loading