-
Notifications
You must be signed in to change notification settings - Fork 4
/
synth_keys.c
101 lines (88 loc) · 4.06 KB
/
synth_keys.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
#include "demo_common.h"
static const uint8_t scale[] = {36, 38, 40, 43, 45, 60, 50, 52};
static const uint8_t scale2[] = {33, 31, 26, 31, 29, 33, 31, 29};
static CT_Smush rnd;
static void init_voice(CTSS_Synth *synth, CTSS_DSPStack *stack, float freq);
static int render_synth(const void *in,
void *out,
unsigned long frames,
const PaStreamCallbackTimeInfo *timeInfo,
PaStreamCallbackFlags status,
void *data);
static void trigger_note(AppState *app, char ch);
int main(int argc, char *argv[]) {
AppState app;
ct_smush_init(&rnd, 0xdecafbad);
ctss_init(&app.synth, 8);
ctss_add_global_lfo(&app.synth, ctss_osc("lfo1", ctss_process_osc_sin, 0.0f,
HZ_TO_RAD(0.1f), 0.5f, 1.0f));
for (uint8_t i = 0; i < app.synth.numStacks; i++) {
init_voice(&app.synth, &app.synth.stacks[i], ctss_notes[scale[0]]);
}
app.pitch = 28;
app.callback = render_synth;
app.handler = trigger_note;
app.channels = 1;
app.isNewNote = 0;
return demo(&app);
}
static void init_voice(CTSS_Synth *synth, CTSS_DSPStack *stack, float freq) {
ctss_init_stack(stack);
CTSS_DSPNode *env = ctss_adsr("env", synth->lfo[0]);
ctss_configure_adsr(env, 2.0f, 0.7f, 1.5f, 0.5f, 0.5f, true);
CTSS_DSPNode *lfo = ctss_osc("lfoPitch", ctss_process_osc_square, 0.0f,
HZ_TO_RAD(2.0f), HZ_TO_RAD(1.0f), 0.0f);
CTSS_DSPNode *lfo2 = ctss_osc("lfoGain", ctss_process_osc_saw, 0.0f,
HZ_TO_RAD(6.666666f), 0.4f, 0.6f);
CTSS_DSPNode *osc1 = ctss_osc("osc1", ctss_process_osc_saw, 0.0f,
HZ_TO_RAD(freq * 0.51f), 0.35f, 0.0f);
ctss_set_osc_lfo(osc1, lfo, 1.0f);
CTSS_DSPNode *osc2 = ctss_osc("osc2", ctss_process_osc_saw, 0.0f,
HZ_TO_RAD(freq), 0.35f, 0.0f);
ctss_set_osc_lfo(osc2, lfo, 1.0f);
ctss_set_osc_env(osc2, env, HZ_TO_RAD(10.0f));
CTSS_DSPNode *sum = ctss_op4("sum", osc1, env, osc2, env, ctss_process_madd);
CTSS_DSPNode *gate = ctss_op2("gate", sum, lfo2, ctss_process_mult);
// CTSS_DSPNode *filter = ctss_filter_iir("filter", IIR_LP, gate, NULL,
// ct_smush_minmax(&rnd, 300.0f, 1200.0f),
// 0.85f);
CTSS_DSPNode *filter =
ctss_filter_formant("filter", ct_smush(&rnd) % 5, gate);
CTSS_DSPNode *delay = ctss_delay("delay", filter, SAMPLE_RATE >> 2, 0.4f, 1);
CTSS_DSPNode *filter2 =
ctss_filter_iir("filter2", IIR_HP, delay, NULL, 1800.0f, 0.0f);
CTSS_DSPNode *delay2 =
ctss_delay("delay2", filter2, (uint32_t)(SAMPLE_RATE * 0.333f), 0.4, 1);
CTSS_DSPNode *delay3 =
ctss_delay("delay3", delay2, (uint32_t)(SAMPLE_RATE * 0.4781f), 0.4f, 1);
CTSS_DSPNode *sum2 = ctss_op4_const("sum2", filter, 0.2f, delay3, 0.8f,
ctss_process_madd_const);
CTSS_DSPNode *nodes[] = {env, lfo, lfo2, osc1, osc2, sum, gate,
filter, delay, filter2, delay2, delay3, sum2};
ctss_build_stack(stack, nodes, 13);
}
static int render_synth(const void *in,
void *out,
unsigned long frames,
const PaStreamCallbackTimeInfo *timeInfo,
PaStreamCallbackFlags status,
void *data) {
AppState *app = (AppState *)data;
if (app->isNewNote) {
float freq = ctss_notes[scale[app->noteID % 8]];
float freq2 = ctss_notes[scale2[(app->noteID % 8)] + 12];
CTSS_DSPStack *s = &app->synth.stacks[app->voiceID];
ctss_reset_adsr(NODE_ID(s, "env"));
NODE_ID_STATE(CTSS_OscState, s, "osc1")->freq = HZ_TO_RAD(freq2);
NODE_ID_STATE(CTSS_OscState, s, "osc2")->freq = HZ_TO_RAD(freq);
ctss_activate_stack(s);
app->noteID++;
app->voiceID = (app->voiceID + 1) % app->synth.numStacks;
app->isNewNote = 0;
}
ctss_update_mix_mono_f32(&app->synth, ctss_mixdown_f32, frames, (float *)out);
return 0;
}
static void trigger_note(AppState *app, char ch) {
app->isNewNote = 1;
}