commit 78394dd926f80c146c3723510c104af978b1cfaa
parent efb5a262f0aeca23923e607bb80513e6003d3f4b
Author: Alexandre Bique <bique.alexandre@gmail.com>
Date: Thu, 23 Oct 2014 19:05:07 +0200
thyns: note on, note off
Diffstat:
6 files changed, 129 insertions(+), 37 deletions(-)
diff --git a/examples/thyns/dlist.h b/examples/thyns/dlist.h
@@ -0,0 +1,28 @@
+#ifndef DLIST_H
+# define DLIST_H
+
+# define thyns_dlist_push_back(Head, Item) \
+ do { \
+ if (!(Head)) { \
+ (Head) = (Item); \
+ (Item)->next = (Item); \
+ (Item)->prev = (Item); \
+ } else { \
+ (Item)->next = (Head); \
+ (Item)->prev = (Head)->prev; \
+ (Item)->prev->next = (Item); \
+ (Item)->next->prev = (Item); \
+ } \
+ } while (0)
+
+# define thyns_dlist_remove(Head, Item) \
+ do { \
+ (Item)->next->prev = (Item)->prev; \
+ (Item)->prev->next = (Item)->next; \
+ (Item)->next = NULL; \
+ (Item)->prev = NULL; \
+ if ((Head) == (Item)) \
+ (Head) = NULL; \
+ } while (0)
+
+#endif /* !DLIST_H */
diff --git a/examples/thyns/env.h b/examples/thyns/env.h
@@ -31,12 +31,12 @@ struct thyns_env
static inline void thyns_env_init(struct thyns_env * restrict env)
{
env->state = THYNS_ENV_IDLE;
+ env->v = 0;
}
static inline void thyns_env_restart(struct thyns_env * restrict env)
{
env->state = THYNS_ENV_ATTACK;
- env->v = 0;
}
static inline double thyns_env_step(struct thyns_env * restrict env)
diff --git a/examples/thyns/filt.h b/examples/thyns/filt.h
@@ -10,6 +10,9 @@
struct thyns_filt
{
+ uint32_t sr; // sample rate
+ double pi_sr; // M_PI / sample_rate
+
double cutoff; // in hz
double resonance;
@@ -24,21 +27,23 @@ struct thyns_filt
float y4; // delayed feedback
};
-static inline void thyns_filt_init(struct thyns_filt *filt)
+static inline void
+thyns_filt_init(struct thyns_filt *filt, uint32_t sr)
{
memset(filt, 0, sizeof (*filt));
+ filt->sr = sr;
+ filt->pi_sr = M_PI / sr;
}
-static inline void thyns_filt_set_cutoff(struct thyns_filt *filt,
- double cutoff,
- double pi_sr)
+static inline void
+thyns_filt_set_cutoff(struct thyns_filt *filt, double cutoff)
{
- filt->g = tan(pi_sr * cutoff);
+ filt->g = tan(filt->pi_sr * cutoff);
filt->g_div = 1.0 / (1.0 + filt->g);
}
-static inline double thyns_filt_step(struct thyns_filt *filt,
- double in)
+static inline double
+thyns_filt_step(struct thyns_filt *filt, double in)
{
double x0 = in - filt->resonance * filt->y4;
double y1 = (filt->g * tanh(x0) + filt->iceq1) * filt->g_div;
diff --git a/examples/thyns/osc.h b/examples/thyns/osc.h
@@ -14,17 +14,24 @@ enum thyns_osc_type
struct thyns_osc
{
+ uint32_t sr; // sample rate
+ double pi_sr; // M_PI / sample_rate
+
enum thyns_osc_type type;
double pwm; // 0..1
+ double tune;
double freq;
double angle_ramp;
double angle;
double phase;
};
-static inline void thyns_osc_init(struct thyns_osc *osc)
+static inline void
+ thyns_osc_init(struct thyns_osc *osc, uint32_t sr)
{
+ osc->sr = sr;
+ osc->pi_sr = M_PI / sr;
osc->type = THYNS_OSC_NONE;
osc->pwm = 0.5;
osc->freq = 0;
@@ -32,14 +39,15 @@ static inline void thyns_osc_init(struct thyns_osc *osc)
osc->phase = 0;
}
-static inline void thyns_osc_set_freq(struct thyns_osc *osc,
- double freq,
- double pi_sr)
+static inline void
+thyns_osc_set_freq(struct thyns_osc *osc, double freq)
{
- osc->angle_ramp = 2 * pi_sr * freq;
+ osc->freq = freq;
+ osc->angle_ramp = 2 * osc->pi_sr * freq * pow(2, osc->tune);
}
-static inline double thyns_osc_step(struct thyns_osc *osc)
+static inline double
+thyns_osc_step(struct thyns_osc *osc)
{
osc->angle += fmod(osc->angle + osc->angle_ramp, 2 * M_PI);
double angle = fmod(osc->angle + osc->phase, 2 * M_PI);
diff --git a/examples/thyns/thyns.h b/examples/thyns/thyns.h
@@ -4,34 +4,35 @@
# include <clap/clap.h>
# include "voice.h"
+# include "dlist.h"
# define THYNS_VOICE_COUNT 32
struct thyns
{
- uint32_t sr; // sample rate
+ uint32_t sr; // sample rate
double pi_sr; // M_PI / sample_rate
uint64_t steady_time;
- struct thyns_voice *running;
+ struct thyns_voice *singing;
struct thyns_voice *idle;
+ struct thyns_voice *keys[0x80];
struct thyns_voice buffer[THYNS_VOICE_COUNT];
};
static inline void thyns_init(struct thyns *thyns, uint32_t sr)
{
+ memset(thyns, 0, sizeof (*thyns));
+
thyns->sr = sr;
thyns->pi_sr = M_PI / sr;
- thyns->running = NULL;
- thyns->idle = thyns->buffer;
for (uint32_t i = 0; i < THYNS_VOICE_COUNT; ++i) {
thyns_voice_init(thyns->buffer + i, sr);
- thyns->buffer[i].next = thyns->buffer + i + 1;
+ thyns_dlist_push_back(thyns->idle, thyns->buffer + i);
}
- thyns->buffer[THYNS_VOICE_COUNT - 1].next = NULL;
}
static double thyns_step(struct thyns *thyns,
@@ -39,7 +40,7 @@ static double thyns_step(struct thyns *thyns,
{
double out = 0;
struct thyns_voice *prev = NULL;
- struct thyns_voice *v = thyns->running;
+ struct thyns_voice *v = thyns->singing;
while (v) {
out += thyns_voice_step(v);
@@ -54,7 +55,7 @@ static double thyns_step(struct thyns *thyns,
} else {
v->next = thyns->idle;
thyns->idle = v;
- v = thyns->running;
+ v = thyns->singing;
}
} else {
prev = v;
@@ -65,18 +66,49 @@ static double thyns_step(struct thyns *thyns,
return out;
}
-static inline struct thyns_voice *
-thyns_find_voice(struct thyns *thyns, uint8_t note)
+static inline void
+thyns_note_on(struct thyns *thyns,
+ uint8_t key,
+ float pitch)
+{
+ struct thyns_voice *voice = NULL;
+
+ assert(key < 0x80);
+ if (thyns->keys[key]) {
+ voice = thyns->keys[key];
+ } else {
+ if (thyns->idle) {
+ voice = thyns->idle;
+ thyns_dlist_remove(thyns->idle, voice);
+ } else {
+ voice = thyns->singing;
+ thyns_dlist_remove(thyns->singing, voice);
+ thyns->keys[voice->key] = NULL;
+ }
+ thyns_dlist_push_back(thyns->singing, voice);
+ thyns->keys[key] = voice;
+ }
+
+ thyns_voice_start_note(thyns->keys[key], key, pitch);
+}
+
+static inline void
+thyns_note_off(struct thyns *thyns,
+ uint8_t key)
{
- // XXX
- return NULL;
}
-static inline void thyns_handle_event(struct thyns *thyns,
- struct clap_event *ev)
+static inline void
+thyns_handle_event(struct thyns *thyns,
+ struct clap_event *ev)
{
switch (ev->type) {
case CLAP_EVENT_NOTE_ON:
+ thyns_note_on(thyns, ev->note.key, ev->note.pitch);
+ break;
+
+ case CLAP_EVENT_NOTE_OFF:
+ thyns_note_off(thyns, ev->note.key);
break;
default:
@@ -105,7 +137,7 @@ static inline void thyns_process(struct thyns *thyns,
process->output[0][i] = thyns_step(thyns, process);
}
- process->need_processing = thyns->running;
+ process->need_processing = thyns->singing;
}
#endif /* !THYNS_H */
diff --git a/examples/thyns/voice.h b/examples/thyns/voice.h
@@ -7,12 +7,14 @@
struct thyns_voice
{
+ struct thyns_voice *prev;
struct thyns_voice *next;
uint32_t sr; // sample rate
double pi_sr; // M_PI / sample_rate
- float freq;
+ uint32_t key;
+ float freq;
// osc part
struct thyns_osc osc1;
@@ -29,19 +31,23 @@ struct thyns_voice
double amp;
};
-static void thyns_voice_init(struct thyns_voice *voice, uint32_t sr)
+static inline void
+thyns_voice_init(struct thyns_voice *voice, uint32_t sr)
{
+ voice->prev = NULL;
+ voice->next = NULL;
+
voice->sr = sr;
voice->pi_sr = M_PI / sr;
// osc
- thyns_osc_init(&voice->osc1);
- thyns_osc_init(&voice->osc2);
+ thyns_osc_init(&voice->osc1, sr);
+ thyns_osc_init(&voice->osc2, sr);
voice->osc_mix = 0;
// filter
- thyns_filt_init(&voice->filt);
- thyns_filt_set_cutoff(&voice->filt, 1000, voice->pi_sr);
+ thyns_filt_init(&voice->filt, sr);
+ thyns_filt_set_cutoff(&voice->filt, 1000);
thyns_env_init(&voice->filt_env);
voice->filt_env_depth = 0;
@@ -50,7 +56,20 @@ static void thyns_voice_init(struct thyns_voice *voice, uint32_t sr)
voice->amp = 0.7;
}
-double thyns_voice_step(struct thyns_voice *voice)
+static inline void
+thyns_voice_start_note(struct thyns_voice *voice,
+ uint32_t key,
+ float freq)
+{
+ thyns_osc_set_freq(&voice->osc1, freq);
+ thyns_osc_set_freq(&voice->osc2, freq);
+
+ thyns_env_restart(&voice->filt_env);
+ thyns_env_restart(&voice->amp_env);
+}
+
+static inline double
+thyns_voice_step(struct thyns_voice *voice)
{
double osc1 = thyns_osc_step(&voice->osc1);
double osc2 = thyns_osc_step(&voice->osc2);
@@ -58,7 +77,7 @@ double thyns_voice_step(struct thyns_voice *voice)
double fenv = thyns_env_step(&voice->filt_env);
double cutoff = exp(log(voice->filt.cutoff) + fenv);
- thyns_filt_set_cutoff(&voice->filt, cutoff, voice->pi_sr);
+ thyns_filt_set_cutoff(&voice->filt, cutoff);
double filtered = thyns_filt_step(&voice->filt, oscm);
return filtered * voice->amp * thyns_env_step(&voice->amp_env);