-
Notifications
You must be signed in to change notification settings - Fork 0
/
channel.c
133 lines (107 loc) · 3.59 KB
/
channel.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
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
#include <stdbool.h>
#include <stdlib.h>
#include "util.h"
#include "synth.h"
#include "sound.h"
#include "channel.h"
typedef struct {
List free_node;
List channel_node;
float velocity;
char note;
bool pressed;
} WavegenStatus;
extern Synth synth;
WavegenStatus wavegen_status[SYNTH_WAVEGEN_COUNT - 1];
List free_wavegens_first, free_wavegens_last;
static WavegenStatus *alloc_wavegen(void) {
List *node = free_wavegens_first.next;
if (node == &free_wavegens_last) return NULL;
list_delete(node);
return container_of(node, WavegenStatus, free_node);
}
static void free_wavegen(WavegenStatus *self) {
list_move_before(&self->free_node, &free_wavegens_last);
}
static Wavegen *get_wavegen_state(WavegenStatus *ws) {
if (ws < wavegen_status || ws >= endof(wavegen_status)) exit(1);
return &synth.wavegens[ws - wavegen_status];
}
static WavegenStatus *channel_get_note_wavegen(Channel *self, char note) {
for (List *item = self->status_list.next; item; item = item->next) {
WavegenStatus *ws = container_of(item, WavegenStatus, channel_node);
if (ws->note == note) return ws;
}
return NULL;
}
static void channel_update_wavegen(Channel *self, WavegenStatus *ws, bool set_envelope) {
Wavegen *w;
if (!self->program) return;
w = get_wavegen_state(ws);
w->freq = freq_from_note(ws->note + self->pitch_bend);
w->velocity = 10000ul * ws->velocity * self->gain;
w->shape = self->program->shape;
if (set_envelope) {
wavegen_set_vol_envelope(w, ws->pressed ?
self->program->press_envelope : self->program->release_envelope
);
}
}
void channel_note_off(Channel *self, char note, float velocity) {
WavegenStatus *ws;
ws = channel_get_note_wavegen(self, note);
if (!ws) return;
ws->pressed = false;
channel_update_wavegen(self, ws, !self->sustain);
free_wavegen(ws);
}
void channel_note_on(Channel *self, char note, float velocity) {
WavegenStatus *ws;
if (velocity == 0) {
channel_note_off(self, note, 127);
return;
}
ws = channel_get_note_wavegen(self, note);
if (!ws) {
ws = alloc_wavegen();
if (!ws) {
warn(); /* no free wavegens */
return;
}
}
list_move_after(&ws->channel_node, &self->status_list);
ws->note = note;
ws->velocity = velocity;
ws->pressed = true;
channel_update_wavegen(self, ws, true);
}
void channel_update_wavegens(Channel *self) {
for (List *item = self->status_list.next; item; item = item->next) {
WavegenStatus *ws = container_of(item, WavegenStatus, channel_node);
channel_update_wavegen(self, ws, false);
}
}
void channel_set_sustain(Channel *self, bool enabled) {
bool release = self->sustain && !enabled;
self->sustain = enabled;
if (release) {
for (List *item = self->status_list.next; item; item = item->next) {
WavegenStatus *ws = container_of(item, WavegenStatus, channel_node);
channel_update_wavegen(self, ws, !ws->pressed);
}
}
}
void channel_all_notes_off(Channel *self, bool reset_envelopes) {
for (List *item = self->status_list.next; item; item = item->next) {
WavegenStatus *ws = container_of(item, WavegenStatus, channel_node);
ws->pressed = false;
channel_update_wavegen(self, ws, true);
}
}
void reset_channels(void) {
list_move_after(&free_wavegens_last, &free_wavegens_first);
for (WavegenStatus *ws = wavegen_status; ws < endof(wavegen_status); ws++) {
list_delete(&ws->channel_node);
free_wavegen(ws);
}
}