mubox.c (4685B)
1 /* 2 Primitive audio synth 3 4 - 6-voice polyphony, 1 noise channel, 1 kick channel 5 - only triangle osc, only freq, vol and decay ctls 6 - controlled via stdin 7 */ 8 9 #include <u.h> 10 #include <libc.h> 11 #include <bio.h> 12 #include <thread.h> 13 14 #define MaxArgs 32 15 #define MaxVoices 6 16 #define BSize 441 17 18 typedef struct Voice Voice; 19 struct Voice { 20 double stime; 21 double volume; 22 double amplitude; 23 double decay; 24 double freq; 25 double bend; 26 }; 27 28 enum { 29 VKick = MaxVoices, 30 VNoise = MaxVoices + 1, 31 }; 32 33 Voice v[MaxVoices + 2], vv, *vp; 34 double T, mix[2]; 35 Biobuf *in; 36 int out; 37 38 int realtime; 39 40 /* 41 * Frequencies for equal-tempered scale 42 * from https://pages.mtu.edu/~suits/notefreqs.html 43 */ 44 45 struct { 46 char *s; 47 double f; 48 } notes[12] = { 49 {"C-", 261.63}, 50 {"C#", 277.18}, 51 {"D-", 293.66}, 52 {"D#", 311.13}, 53 {"E-", 329.63}, 54 {"F-", 349.23}, 55 {"F#", 369.99}, 56 {"G-", 392.0 }, 57 {"G#", 415.30}, 58 {"A-", 440.0 }, 59 {"A#", 466.16}, 60 {"B-", 493.88}, 61 }; 62 63 double 64 ms2decay(double x) 65 { 66 if (x <= 0) return 1; 67 return 1/(x*44.1); 68 } 69 70 Voice * 71 newvoice(double freq) 72 { 73 static int n = 0; 74 n = (n + 1) % MaxVoices; 75 v[n] = vv; 76 v[n].amplitude = 1; 77 v[n].stime = T; 78 v[n].freq = freq; 79 return &v[n]; 80 } 81 82 int 83 note(char *s) 84 { 85 int i, oct; 86 if ((s[2] < '0') || (s[2] > '9')) return -1; 87 oct = atoi(s+2) - 4; 88 for (i = 0; i < 12; i++) { 89 if (strncmp(s, notes[i].s, 2) == 0) { 90 double freq = notes[i].f; 91 while (oct < 0) { 92 freq = freq / 2; 93 oct++; 94 } 95 while (oct > 0) { 96 freq = freq * 2; 97 oct--; 98 } 99 vp = newvoice(freq); 100 return 0; 101 } 102 } 103 return -1; 104 } 105 106 void 107 cmd(char *s) 108 { 109 double f; 110 if (note(s) != 0) switch(s[0]) { 111 /* noise channel commands */ 112 case 'h': 113 v[VNoise].amplitude = 1; 114 v[VNoise].decay = ms2decay(25); 115 break; 116 case 'H': 117 v[VNoise].amplitude = 1; 118 v[VNoise].decay = ms2decay(100); 119 break; 120 case 'N': 121 f = atof(s+1); 122 v[VNoise].volume = f; 123 v[VNoise].amplitude = 1; 124 break; 125 case 'n': 126 f = atof(s+1); 127 v[VNoise].decay = ms2decay(f); 128 v[VNoise].amplitude = 1; 129 break; 130 131 /* kick channel commands */ 132 case 'k': 133 v[VKick].stime = T; 134 v[VKick].freq = 220; 135 v[VKick].amplitude = 1; 136 break; 137 case 'K': 138 v[VKick].stime = T; 139 v[VKick].freq = 440; 140 v[VKick].amplitude = 1; 141 break; 142 143 /* voice channel commands */ 144 case 'f': // freq 145 f = atof(s+1); 146 vp->freq = f; 147 vv.freq = f; 148 break; 149 case 'b': // bend 150 f = atof(s+1); 151 vp->bend = f; 152 vv.bend = f; 153 break; 154 case 'd': // decay; 155 f = atof(s+1); 156 vp->decay = ms2decay(f); 157 vv.decay = ms2decay(f); 158 break; 159 case 'v': 160 f = atof(s+1); 161 vp = newvoice(f); 162 break; 163 } 164 } 165 166 void 167 threadread(void *) 168 { 169 for (;;) { 170 char *s = Brdstr(in, '\n', 1); 171 if (s == nil) threadexitsall(nil); 172 char *args[MaxArgs]; 173 int n = tokenize(s, args, MaxArgs); 174 int i; 175 for (i = 0; i < n; i++) cmd(args[i]); 176 free(s); 177 } 178 } 179 180 double 181 tri(double T) 182 { 183 static double env[5] = { 0.0, 1.0, 0.0, -1.0, 0.0 }; 184 T = 4.0 * (T - floor(T)); 185 int i = floor(T); 186 double X = T - i; 187 return env[i+1] * X + env[i] * (1 - X); 188 } 189 190 void 191 voice(Voice *v) 192 { 193 double x = tri((T - v->stime) * v->freq) * v->amplitude * v->volume; 194 mix[0] += x; 195 mix[1] += x; 196 } 197 198 void 199 noise(void) 200 { 201 mix[0] += (1 - frand() * 2) * v[VNoise].amplitude * v[VNoise].volume; 202 mix[1] += (1 - frand() * 2) * v[VNoise].amplitude * v[VNoise].volume; 203 } 204 205 void 206 kick(void) 207 { 208 double x = cos(2.0 * PI * (T - v[VKick].stime) * v[VKick].freq) * 209 v[VKick].amplitude * v[VKick].volume; 210 mix[0] += x; 211 mix[1] += x; 212 } 213 214 void 215 modvoice(Voice *v) 216 { 217 v->amplitude -= v->decay; 218 v->freq *= v->bend; 219 if (v->amplitude < 0) v->amplitude = 0; 220 if (v->freq < 0) v->freq = 0; 221 } 222 223 void 224 output(void) 225 { 226 s16int buf[BSize * 2]; 227 int i, j; 228 for (i = 0; i < BSize; i++) { 229 mix[0] = 0; 230 mix[1] = 0; 231 for (j = 0; j < MaxVoices; j++) voice(&v[j]); 232 noise(); 233 kick(); 234 for (j = 0; j < MaxVoices + 2; j++) modvoice(&v[j]); 235 buf[i * 2] = 0x7fff * (mix[0] / 8); 236 buf[i * 2 + 1] = 0x7fff * (mix[1] / 8); 237 T += 1.0/44100.0; 238 } 239 240 write(out, buf, BSize * 4); 241 } 242 243 void 244 usage(void) 245 { 246 fprint(2, "usage: %s [-r]\n", argv0); 247 threadexitsall("usage"); 248 } 249 250 void 251 threadmain(int argc, char **argv) 252 { 253 int i; 254 ARGBEGIN { 255 case 'r': 256 realtime = 1; 257 break; 258 default: 259 usage(); 260 } ARGEND; 261 if (argc != 0) usage(); 262 for (i = 0; i < MaxVoices; i++) { 263 v[i] = (Voice) { 264 0, 265 1, 266 0, 267 0, 268 0, 269 1, 270 }; 271 } 272 v[VKick] = (Voice) { 273 0, 274 1, 275 0, 276 0.0001, 277 220, 278 0.9995, 279 }; 280 v[VNoise] = (Voice) { 281 0, 282 1, 283 0, 284 0.01, 285 0, 286 0, 287 }; 288 vv = (Voice) { 289 0, 290 1, 291 1, 292 ms2decay(500), 293 0, 294 1, 295 }; 296 297 in = Bfdopen(0, OREAD); 298 out = 1; 299 vp = &v[0]; 300 proccreate(threadread, nil, 1024 * 1024 * 64); 301 302 vlong t = nsec() / 1000000; 303 for (;;) { 304 output(); 305 t += 10; 306 if (realtime != 0) sleep(t - (nsec() / 1000000)); 307 } 308 }