Programmatic Oscillators
Sine, cosine, saw and square oscillators with minimal binary footprint.
– Created: May 22, 2023 UTC
– Edited: July 28, 2024 UTC
– Tags: Programming, Music, C
Wanted to have some beep bops for my 4mb-jam entry, so I ended up with implementations of three oscillators that would require minimal amount of calculations.
Idea is to precalculate initial state offline and only have short frame generation procedure in final binary. Thinking about using it in tracker format of sorts that would have runtime generated samples.
Inspirations are taken from musicdsp.
Sine/Cosine
/* Intended to be executed offline with values then embedded in the binary.
* By having usage of glibc sin and cos functions strictly offline it's easier to have it freestanding
*/
struct sinewave {
float f, s, c;
} init_sinewave(float frequency, float phase, float amplitude) {
struct sinewave r;
r.f = 2.f * sinf((float)M_PI * frequency / AUDIO_FRAME_RATE);
r.s = amplitude * sinf(phase);
r.c = amplitude * cosf(phase);
return r;
}
/* Use `s` for sine value and `c` for cosine on caller side */
void pump_sinewave(struct sinewave *wave) {
wave->s -= wave->f * wave->c;
wave->c += wave->f * wave->s;
}
Square
/* Implemented over sinewave */
struct sqrtwave {
struct sinewave w;
union {
float f;
uint32_t u;
} v;
} init_sqrtwave(float frequency, float phase, float amplitude) {
struct sqrtwave r;
union {
float f;
uint32_t u;
} v, a;
r.w = init_sinewave(frequency, phase, 1.f);
v.f = r.w.s;
a.f = amplitude;
r.v.u = (a.u & 0x7fffffff) | (v.u & 0x80000000);
return r;
}
/* Use floating point bit representation to infer sign, all other bits are set to amplitude */
void pump_sqrtwave(struct sqrtwave *wave) {
union {
float f;
uint32_t u;
} v;
pump_sinewave(&wave->w);
v.f = wave->w.s;
wave->v.u = (wave->v.u & 0x7fffffff) | (v.u & 0x80000000);
}
Saw
struct sawtwave {
float v, a, i;
} init_sawtwave(float frequency, float phase, float amplitude) {
struct sawtwave r;
r.v = sinf(phase) * amplitude;
r.a = amplitude;
r.i = 2.f * frequency / AUDIO_FRAME_RATE * amplitude;
return r;
}
/* Simple amplitude overflow check with truncation */
void pump_sawtwave(struct sawtwave *wave) {
wave->v += wave->i;
if (wave->v > wave->a)
wave->v -= wave->a * 2.f;
}
Edits
- Fixed initial value based on phase in sqrtwave, proper range for sawtwave.
- Added waveform gifs.