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