1 #include <rtosc/rtosc.h>
2 #include <rtosc/thread-link.h>
3 #include <rtosc/ports.h>
4 #include <rtosc/miditable.h>
5 #include <string.h>
6 #include <cmath>
7 #include "synth.h"
8 #include <rtosc/port-sugar.h>
9 #include <rtosc/subtree-serialize.h>
10
11 using namespace rtosc;
12
13 float Fs = 0.0f;
14
15 ThreadLink bToU(1024,1024);
16 ThreadLink uToB(1024,1024);
17
18 #define rObject Adsr
19
20 Ports Adsr::ports = {
21 rParamF(av, rLinear(-1,1), "attack value"),
22 rParamF(dv, rLinear(-1,1), "decay value"),
23 rParamF(sv, rLinear(-1,1), "sustain value"),
24 rParamF(rv, rLinear(-1,1), "release value"),
25
26 rParamF(at, rLog(1e-3,10), "attach time"),
27 rParamF(dt, rLog(1e-3,10), "decay time"),
28 rParamF(rt, rLog(1e-3,10), "release time"),
29 };
30
31 //sawtooth generator
oscil(float freq)32 float oscil(float freq)
33 {
34 static float phase = 0.0f;
35 phase += freq/Fs;
36 if(phase > 1.0f)
37 phase -= 1.0f;
38
39 return phase;
40 }
41
sample(void)42 inline float Synth::sample(void)
43 {
44 return oscil(freq*(1+frq_env(gate))/2.0f)*(1+amp_env(gate))/2.0f;
45 }
46
interp(float a,float b,float pos)47 float interp(float a, float b, float pos)
48 {
49 return (1.0f-pos)*a+pos*b;
50 }
51
52 //Linear ADSR
operator ()(bool gate)53 float Adsr::operator()(bool gate)
54 {
55 time += 1.0/Fs;
56
57 if(gate == false && pgate == true)
58 relval = (*this)(true);
59 if(gate != pgate)
60 time = 0.0f;
61 pgate = gate;
62
63 float reltime = time;
64 if(gate) {
65 if(at > reltime) //Attack
66 return interp(av, dv, reltime/at);
67 reltime -= at;
68 if(dt > reltime) //Decay
69 return interp(dv, sv, reltime/dt);
70 return sv; //Sustain
71 }
72 if(rt > reltime) //Release
73 return interp(relval, rv, reltime/rt);
74
75 return rv;
76 }
77
78 MidiTable midi(Synth::ports);
79 char serial[2048];
80 size_t serial_size;
81
82 Synth s;
83 void process_control(unsigned char control[3]);
84
85 #undef rObject
86 #define rObject Synth
87
88 rtosc::Ports Synth::ports = {
89 rRecur(amp_env, "amplitude envelope"),
90 rRecur(frq_env, "frequency envelope"),
91 rParamF(freq, rLog(1,1e3), "note frequency"),
92 rToggle(gate, "Note enable"),
93 midi.registerPort(),
94 midi.learnPort(),
95 midi.unlearnPort(),
96 {"echo:s", ":internal\0", NULL, [](const char *msg, RtData &d)
__anon9feae3690102() 97 {
98 d.reply(rtosc_argument(msg, 0).s);
99 }},
100 {"save", ":internal\0", NULL, [](const char *, RtData &data)
__anon9feae3690202() 101 {
102 fprintf(stderr, "saving...\n");
103 serial_size = subtree_serialize(serial, sizeof(serial),
104 data.obj, &Synth::ports);
105 }},
106 {"load", ":internal\0", NULL, [](const char *, RtData &data)
__anon9feae3690302() 107 {
108 memset(data.loc, 0, data.loc_size);
109 fprintf(stderr, "loading...\n");
110 subtree_deserialize(serial, serial_size, data.obj, &Synth::ports, data);
111 }},
112 };
113
114 Ports *root_ports = &Synth::ports;
115
116
117 float &freq = s.freq;
118 bool &gate = s.gate;
119
event_cb(msg_t m)120 void event_cb(msg_t m)
121 {
122 char buffer[1024];
123 rtosc::RtData d;
124 d.loc = buffer;
125 d.loc_size = 1024;
126 d.obj = (void*) &s;
127 Synth::ports.dispatch(m+1, d);
128 bToU.raw_write(m);
129 puts("event-cb");
130 if(rtosc_type(m,0) == 'f')
131 printf("%s -> %f\n", m, rtosc_argument(m,0).f);
132 if(rtosc_type(m,0) == 'i')
133 printf("%s -> %d\n", m, rtosc_argument(m,0).i);
134 }
135
modify_cb(const char * action,const char * path,const char *,int ch,int cc)136 void modify_cb(const char *action, const char *path, const char *, int ch, int cc)
137 {
138 if(!strcmp(action, "ADD") || !strcmp(action, "REPLACE"))
139 bToU.write("/midi/add", "sii", path, ch, cc);
140 else if(!strcmp(action, "DEL"))
141 bToU.write("/midi/remove", "s", path);
142 }
143
144 #include <err.h>
synth_init(void)145 void synth_init(void)
146 {
147 printf("%p\n", Adsr::ports["dv"]->metadata);
148 printf("'%d'\n", Adsr::ports["dv"]->metadata[0]);
149 if(strlen(Adsr::ports["dv"]->metadata)<3)
150 errx(1,"bad metadata");
151 midi.event_cb = event_cb;
152 midi.modify_cb = modify_cb;
153 }
154
process_control(unsigned char ctl[3])155 void process_control(unsigned char ctl[3])
156 {
157 midi.process(ctl[0]&0x0F, ctl[1], ctl[2]);
158 }
159
160 class DispatchData:public rtosc::RtData
161 {
162 public:
DispatchData(void)163 DispatchData(void)
164 {
165 memset(buffer, 0, 1024);
166 loc = buffer;
167 loc_size = 1024;
168 obj = &s;
169 }
170 char buffer[1024];
171
reply(const char * path,const char * args,...)172 void reply(const char *path, const char *args, ...)
173 {
174 va_list va;
175 va_start(va,args);
176 const size_t len =
177 rtosc_vmessage(bToU.buffer(),bToU.buffer_size(),path,args,va);
178 if(len)
179 bToU.raw_write(bToU.buffer());
180 va_end(va);
181 }
182
reply(const char * msg)183 void reply(const char *msg)
184 {
185 bToU.raw_write(msg);
186 }
187
broadcast(const char * path,const char * args,...)188 void broadcast(const char *path, const char *args, ...)
189 {
190 bToU.write("/broadcast","");
191 va_list va;
192 va_start(va,args);
193 const size_t len =
194 rtosc_vmessage(bToU.buffer(),bToU.buffer_size(),path,args,va);
195 if(len)
196 bToU.raw_write(bToU.buffer());
197 va_end(va);
198 }
199
200
broadcast(const char * msg)201 void broadcast(const char *msg)
202 {
203 bToU.write("/broadcast","");
204 bToU.raw_write(msg);
205 }
206
207 };
208
process_output(float * smps,unsigned nframes)209 void process_output(float *smps, unsigned nframes)
210 {
211 DispatchData d;
212 while(uToB.hasNext()) {
213 Synth::ports.dispatch(uToB.read()+1, d);
214 fprintf(stderr, "backend '%s'\n", uToB.peak());
215 }
216
217 for(unsigned i=0; i<nframes; ++i)
218 smps[i] = s.sample();
219 }
220