Last active
August 13, 2020 12:05
-
-
Save boochow/6f0767cc18888152612bbff4085ec6fb to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* File: smplefm.cpp | |
* | |
* Simple FM Synthesis | |
* | |
*/ | |
#include "userosc.h" | |
typedef struct State { | |
float w0; | |
float phase; | |
float lfoz; | |
float mod_int; | |
float mod_val; | |
uint8_t flags; | |
} State; | |
enum { | |
k_flags_none = 0, | |
k_flag_reset = 1<<0, | |
}; | |
const float modfreq[16] = {1./4, 1./2, 2./3, 1, 4./3, 3./2, 5./3, 2., \ | |
3, 4, 5, 6, 8, 9, 12, 16}; | |
static State s_state; | |
void OSC_INIT(uint32_t platform, uint32_t api) | |
{ | |
s_state.w0 = 0.f; | |
s_state.phase = 0.f; | |
s_state.lfoz = 0.f; | |
s_state.mod_int = 0.f; | |
s_state.mod_val = 1.; | |
s_state.flags = k_flags_none; | |
} | |
__fast_inline float fill_buf(q31_t * __restrict y, const q31_t * y_e, \ | |
float phase, const float w0, float lfo, const float lfo_inc) { | |
const float mod_val = s_state.mod_val; | |
const float mod_int = s_state.mod_int; | |
for (; y != y_e; ) { | |
float coeff = mod_int + (1 - mod_int) * lfo; | |
float mod = 1.f - coeff * osc_sinf(phase * mod_val); | |
float sig = osc_sinf(phase + mod); | |
*(y++) = f32_to_q31(sig); | |
phase += w0; | |
if (phase > 12.0) {phase -= 12;} | |
lfo += lfo_inc; | |
} | |
return phase; | |
} | |
void OSC_CYCLE(const user_osc_param_t * const params, | |
int32_t *yn, | |
const uint32_t frames) | |
{ | |
const uint8_t flags = s_state.flags; | |
s_state.flags = k_flags_none; | |
float phase = (flags & k_flag_reset) ? 0.f : s_state.phase; | |
const float w0 = s_state.w0 = osc_w0f_for_note((params->pitch)>>8, | |
params->pitch & 0xFF); | |
const float lfo = q31_to_f32(params->shape_lfo); | |
const float lfo_inc = (lfo - s_state.lfoz) / frames; | |
s_state.phase = fill_buf((q31_t *) yn, (q31_t *) yn + frames, | |
phase, w0, s_state.lfoz, lfo_inc); | |
s_state.lfoz = lfo; | |
} | |
void OSC_NOTEON(const user_osc_param_t * const params) | |
{ | |
s_state.flags |= k_flag_reset; | |
} | |
void OSC_NOTEOFF(const user_osc_param_t * const params) | |
{ | |
(void)params; | |
} | |
void OSC_PARAM(uint16_t index, uint16_t value) | |
{ | |
const float valf = param_val_to_f32(value); | |
switch (index) { | |
case k_user_osc_param_id1: | |
case k_user_osc_param_id2: | |
case k_user_osc_param_id3: | |
case k_user_osc_param_id4: | |
case k_user_osc_param_id5: | |
case k_user_osc_param_id6: | |
break; | |
case k_user_osc_param_shape: | |
s_state.mod_int = valf; | |
break; | |
case k_user_osc_param_shiftshape: | |
s_state.mod_val = modfreq[value >> 6]; | |
break; | |
default: | |
break; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
SImple FM oscillator for Korg logue SDK.
blog (in Japanese) => https://blog.boochow.com/article/nts1-making-osc-2.html