File: | src/usr.bin/aucat/aucat.c |
Warning: | line 604, column 3 Value stored to 'ocnt' is never read |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
1 | /* |
2 | * Copyright (c) 2008-2014 Alexandre Ratchov <alex@caoua.org> |
3 | * |
4 | * Permission to use, copy, modify, and distribute this software for any |
5 | * purpose with or without fee is hereby granted, provided that the above |
6 | * copyright notice and this permission notice appear in all copies. |
7 | * |
8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |
9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR |
11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN |
13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF |
14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
15 | */ |
16 | |
17 | #include <err.h> |
18 | #include <errno(*__errno()).h> |
19 | #include <limits.h> |
20 | #include <poll.h> |
21 | #include <signal.h> |
22 | #include <sndio.h> |
23 | #include <stdlib.h> |
24 | #include <string.h> |
25 | #include <unistd.h> |
26 | #include "abuf.h" |
27 | #include "afile.h" |
28 | #include "dsp.h" |
29 | #include "sysex.h" |
30 | #include "utils.h" |
31 | |
32 | /* |
33 | * masks to extract command and channel of status byte |
34 | */ |
35 | #define MIDI_CMDMASK0xf0 0xf0 |
36 | #define MIDI_CHANMASK0x0f 0x0f |
37 | |
38 | /* |
39 | * MIDI status bytes of voice messages |
40 | */ |
41 | #define MIDI_NOFF0x80 0x80 /* note off */ |
42 | #define MIDI_NON0x90 0x90 /* note on */ |
43 | #define MIDI_KAT0xa0 0xa0 /* key after touch */ |
44 | #define MIDI_CTL0xb0 0xb0 /* controller */ |
45 | #define MIDI_PC0xc0 0xc0 /* program change */ |
46 | #define MIDI_CAT0xd0 0xd0 /* channel after touch */ |
47 | #define MIDI_BEND0xe0 0xe0 /* pitch bend */ |
48 | #define MIDI_ACK0xfe 0xfe /* active sensing message */ |
49 | |
50 | /* |
51 | * MIDI controller numbers |
52 | */ |
53 | #define MIDI_CTL_VOL7 7 |
54 | |
55 | /* |
56 | * Max coarse value |
57 | */ |
58 | #define MIDI_MAXCTL127 127 |
59 | |
60 | /* |
61 | * MIDI status bytes for sysex |
62 | */ |
63 | #define MIDI_SX_START0xf0 0xf0 |
64 | #define MIDI_SX_STOP0xf7 0xf7 |
65 | |
66 | /* |
67 | * audio device defaults |
68 | */ |
69 | #define DEFAULT_RATE48000 48000 |
70 | #define DEFAULT_BUFSZ_MS200 200 |
71 | |
72 | struct slot { |
73 | struct slot *next; /* next on the play/rec list */ |
74 | int vol; /* dynamic range */ |
75 | int volctl; /* volume in the 0..127 range */ |
76 | struct abuf buf; /* file i/o buffer */ |
77 | int bpf; /* bytes per frame */ |
78 | int cmin, cmax; /* file channel range */ |
79 | struct cmap cmap; /* channel mapper state */ |
80 | struct resamp resamp; /* resampler state */ |
81 | struct conv conv; /* format encoder state */ |
82 | int join; /* channel join factor */ |
83 | int expand; /* channel expand factor */ |
84 | void *resampbuf, *convbuf; /* conversion tmp buffers */ |
85 | int dup; /* mono-to-stereo and alike */ |
86 | int round; /* slot-side block size */ |
87 | int mode; /* MODE_{PLAY,REC} */ |
88 | #define SLOT_CFG0 0 /* buffers not allocated yet */ |
89 | #define SLOT_INIT1 1 /* not trying to do anything */ |
90 | #define SLOT_RUN2 2 /* playing/recording */ |
91 | #define SLOT_STOP3 3 /* draining (play only) */ |
92 | int pstate; /* one of above */ |
93 | long long skip; /* frames to skip at the beginning */ |
94 | long long pos; /* start position (at device rate) */ |
95 | struct afile afile; /* file desc & friends */ |
96 | }; |
97 | |
98 | /* |
99 | * device properties |
100 | */ |
101 | unsigned int dev_mode; /* bitmap of SIO_{PLAY,REC} */ |
102 | unsigned int dev_bufsz; /* device buffer size */ |
103 | unsigned int dev_round; /* device block size */ |
104 | int dev_rate; /* device sample rate (Hz) */ |
105 | unsigned int dev_pchan, dev_rchan; /* play & rec channels count */ |
106 | adata_t *dev_pbuf, *dev_rbuf; /* play & rec buffers */ |
107 | long long dev_pos; /* last MMC position in frames */ |
108 | #define DEV_STOP0 0 /* stopped */ |
109 | #define DEV_START1 1 /* started */ |
110 | unsigned int dev_pstate; /* one of above */ |
111 | char *dev_name; /* device sndio(7) name */ |
112 | char *dev_port; /* control port sndio(7) name */ |
113 | struct sio_hdl *dev_sh; /* device handle */ |
114 | struct mio_hdl *dev_mh; /* MIDI control port handle */ |
115 | unsigned int dev_volctl = MIDI_MAXCTL127; /* master volume */ |
116 | |
117 | /* |
118 | * MIDI parser state |
119 | */ |
120 | #define MIDI_MSGMAX32 32 /* max size of MIDI msg */ |
121 | unsigned char dev_msg[MIDI_MSGMAX32]; /* parsed input message */ |
122 | unsigned int dev_mst; /* input MIDI running status */ |
123 | unsigned int dev_mused; /* bytes used in ``msg'' */ |
124 | unsigned int dev_midx; /* current ``msg'' size */ |
125 | unsigned int dev_mlen; /* expected ``msg'' length */ |
126 | unsigned int dev_prime; /* blocks to write to start */ |
127 | |
128 | unsigned int log_level = 1; |
129 | volatile sig_atomic_t quit_flag = 0; |
130 | struct slot *slot_list = NULL((void*)0); |
131 | |
132 | /* |
133 | * length of voice and common MIDI messages (status byte included) |
134 | */ |
135 | const unsigned int voice_len[] = { 3, 3, 3, 3, 2, 2, 3 }; |
136 | const unsigned int common_len[] = { 0, 2, 3, 2, 0, 0, 1, 1 }; |
137 | |
138 | char usagestr[] = "usage: aucat [-dn] [-b size] " |
139 | "[-c min:max] [-e enc] [-f device] [-g position]\n\t" |
140 | "[-h fmt] [-i file] [-j flag] [-o file] [-p position] [-q port]\n\t" |
141 | "[-r rate] [-v volume]\n"; |
142 | |
143 | static void * |
144 | allocbuf(int nfr, int nch) |
145 | { |
146 | size_t fsize; |
147 | |
148 | if (nch < 0 || nch > NCHAN_MAX64) { |
149 | log_puts("allocbuf: bogus channel count\n"); |
150 | panic(); |
151 | } |
152 | fsize = nch * sizeof(adata_t); |
153 | return reallocarray(NULL((void*)0), nfr, fsize); |
154 | } |
155 | |
156 | static void |
157 | slot_log(struct slot *s) |
158 | { |
159 | #ifdef DEBUG1 |
160 | static char *pstates[] = { |
161 | "cfg", "ini", "run", "stp" |
162 | }; |
163 | #endif |
164 | log_puts(s->afile.path); |
165 | #ifdef DEBUG1 |
166 | if (log_level >= 3) { |
167 | log_puts(",pst="); |
168 | log_puts(pstates[s->pstate]); |
169 | } |
170 | #endif |
171 | } |
172 | |
173 | static void |
174 | slot_flush(struct slot *s) |
175 | { |
176 | int count, n; |
177 | unsigned char *data; |
178 | |
179 | for (;;) { |
180 | data = abuf_rgetblk(&s->buf, &count); |
181 | if (count == 0) |
182 | break; |
183 | n = afile_write(&s->afile, data, count); |
184 | if (n == 0) { |
185 | slot_log(s); |
186 | log_puts(": can't write, disabled\n"); |
187 | s->pstate = SLOT_INIT1; |
188 | return; |
189 | } |
190 | abuf_rdiscard(&s->buf, n); |
191 | } |
192 | } |
193 | |
194 | static void |
195 | slot_fill(struct slot *s) |
196 | { |
197 | int count, n; |
198 | unsigned char *data; |
199 | |
200 | for (;;) { |
201 | data = abuf_wgetblk(&s->buf, &count); |
202 | if (count == 0) |
203 | break; |
204 | n = afile_read(&s->afile, data, count); |
205 | if (n == 0) { |
206 | #ifdef DEBUG1 |
207 | if (log_level >= 3) { |
208 | slot_log(s); |
209 | log_puts(": eof reached, stopping\n"); |
210 | } |
211 | #endif |
212 | s->pstate = SLOT_STOP3; |
213 | break; |
214 | } |
215 | abuf_wcommit(&s->buf, n); |
216 | } |
217 | } |
218 | |
219 | static int |
220 | slot_new(char *path, int mode, struct aparams *par, int hdr, |
221 | int cmin, int cmax, int rate, int dup, int vol, long long pos) |
222 | { |
223 | struct slot *s; |
224 | |
225 | s = xmalloc(sizeof(struct slot)); |
226 | if (!afile_open(&s->afile, path, hdr, |
227 | mode == SIO_PLAY1 ? AFILE_FREAD1 : AFILE_FWRITE2, |
228 | par, rate, cmax - cmin + 1)) { |
229 | xfree(s); |
230 | return 0; |
231 | } |
232 | s->cmin = cmin; |
233 | s->cmax = cmin + s->afile.nch - 1; |
234 | s->dup = dup; |
235 | s->vol = MIDI_TO_ADATA(vol)(aparams_ctltovol[vol] << (16 - 16)); |
236 | s->mode = mode; |
237 | s->pstate = SLOT_CFG0; |
238 | s->pos = pos; |
239 | if (log_level >= 2) { |
240 | slot_log(s); |
241 | log_puts(": "); |
242 | log_puts(s->mode == SIO_PLAY1 ? "play" : "rec"); |
243 | log_puts(", chan "); |
244 | log_putu(s->cmin); |
245 | log_puts(":"); |
246 | log_putu(s->cmax); |
247 | log_puts(", "); |
248 | log_putu(s->afile.rate); |
249 | log_puts("Hz, "); |
250 | switch (s->afile.fmt) { |
251 | case AFILE_FMT_PCM0: |
252 | aparams_log(&s->afile.par); |
253 | break; |
254 | case AFILE_FMT_ULAW1: |
255 | log_puts("ulaw"); |
256 | break; |
257 | case AFILE_FMT_ALAW2: |
258 | log_puts("alaw"); |
259 | break; |
260 | case AFILE_FMT_FLOAT3: |
261 | log_puts("f32le"); |
262 | break; |
263 | } |
264 | if (s->mode == SIO_PLAY1 && s->afile.endpos >= 0) { |
265 | log_puts(", bytes "); |
266 | log_puti(s->afile.startpos); |
267 | log_puts(".."); |
268 | log_puti(s->afile.endpos); |
269 | } |
270 | if (s->mode == SIO_PLAY1) { |
271 | log_puts(", vol "); |
272 | log_puti(s->vol); |
273 | } |
274 | log_puts("\n"); |
275 | } |
276 | s->next = slot_list; |
277 | slot_list = s; |
278 | return 1; |
279 | } |
280 | |
281 | static void |
282 | slot_init(struct slot *s) |
283 | { |
284 | unsigned int slot_nch, bufsz; |
285 | |
286 | #ifdef DEBUG1 |
287 | if (s->pstate != SLOT_CFG0) { |
288 | slot_log(s); |
289 | log_puts(": slot_init: wrong state\n"); |
290 | panic(); |
291 | } |
292 | #endif |
293 | s->bpf = s->afile.par.bps * (s->cmax - s->cmin + 1); |
294 | s->round = ((long long)dev_round * s->afile.rate + |
295 | dev_rate - 1) / dev_rate; |
296 | |
297 | bufsz = s->round * (dev_bufsz / dev_round); |
298 | bufsz -= bufsz % s->round; |
299 | if (bufsz == 0) |
300 | bufsz = s->round; |
301 | abuf_init(&s->buf, bufsz * s->bpf); |
302 | #ifdef DEBUG1 |
303 | if (log_level >= 3) { |
304 | slot_log(s); |
305 | log_puts(": allocated "); |
306 | log_putu(bufsz); |
307 | log_puts(" frame buffer\n"); |
308 | } |
309 | #endif |
310 | |
311 | slot_nch = s->cmax - s->cmin + 1; |
312 | s->convbuf = NULL((void*)0); |
313 | s->resampbuf = NULL((void*)0); |
314 | s->join = 1; |
315 | s->expand = 1; |
316 | if (s->mode & SIO_PLAY1) { |
317 | if (s->dup) { |
318 | if (dev_pchan > slot_nch) |
319 | s->expand = dev_pchan / slot_nch; |
320 | else if (dev_pchan < slot_nch) |
321 | s->join = slot_nch / dev_pchan; |
322 | } |
323 | cmap_init(&s->cmap, |
324 | s->cmin, s->cmax, |
325 | s->cmin, s->cmax, |
326 | 0, dev_pchan - 1, |
327 | 0, dev_pchan - 1); |
328 | if (s->afile.fmt != AFILE_FMT_PCM0 || |
329 | !aparams_native(&s->afile.par)) { |
330 | dec_init(&s->conv, &s->afile.par, slot_nch); |
331 | s->convbuf = allocbuf(s->round, slot_nch); |
332 | } |
333 | if (s->afile.rate != dev_rate) { |
334 | resamp_init(&s->resamp, s->afile.rate, dev_rate, |
335 | slot_nch); |
336 | s->resampbuf = allocbuf(dev_round, slot_nch); |
337 | } |
338 | } |
339 | if (s->mode & SIO_REC2) { |
340 | if (s->dup) { |
341 | if (dev_rchan > slot_nch) |
342 | s->join = dev_rchan / slot_nch; |
343 | else if (dev_rchan < slot_nch) |
344 | s->expand = slot_nch / dev_rchan; |
345 | } |
346 | cmap_init(&s->cmap, |
347 | 0, dev_rchan - 1, |
348 | 0, dev_rchan - 1, |
349 | s->cmin, s->cmax, |
350 | s->cmin, s->cmax); |
351 | if (s->afile.rate != dev_rate) { |
352 | resamp_init(&s->resamp, dev_rate, s->afile.rate, |
353 | slot_nch); |
354 | s->resampbuf = allocbuf(dev_round, slot_nch); |
355 | } |
356 | if (!aparams_native(&s->afile.par)) { |
357 | enc_init(&s->conv, &s->afile.par, slot_nch); |
358 | s->convbuf = allocbuf(s->round, slot_nch); |
359 | } |
360 | |
361 | /* |
362 | * cmap_copy() doesn't write samples in all channels, |
363 | * for instance when mono->stereo conversion is |
364 | * disabled. So we have to prefill cmap_copy() output |
365 | * with silence. |
366 | */ |
367 | if (s->resampbuf) { |
368 | memset(s->resampbuf, 0, |
369 | dev_round * slot_nch * sizeof(adata_t)); |
370 | } else if (s->convbuf) { |
371 | memset(s->convbuf, 0, |
372 | s->round * slot_nch * sizeof(adata_t)); |
373 | } else { |
374 | memset(s->buf.data, 0, |
375 | bufsz * slot_nch * sizeof(adata_t)); |
376 | } |
377 | } |
378 | s->pstate = SLOT_INIT1; |
379 | #ifdef DEBUG1 |
380 | if (log_level >= 3) { |
381 | slot_log(s); |
382 | log_puts(": chain initialized\n"); |
383 | } |
384 | #endif |
385 | } |
386 | |
387 | static void |
388 | slot_start(struct slot *s, long long pos) |
389 | { |
390 | #ifdef DEBUG1 |
391 | if (s->pstate != SLOT_INIT1) { |
392 | slot_log(s); |
393 | log_puts(": slot_start: wrong state\n"); |
394 | panic(); |
395 | } |
396 | #endif |
397 | pos -= s->pos; |
398 | if (pos < 0) { |
399 | s->skip = -pos; |
400 | pos = 0; |
401 | } else |
402 | s->skip = 0; |
403 | |
404 | /* |
405 | * convert pos to slot sample rate |
406 | * |
407 | * At this stage, we could adjust s->resamp.diff to get |
408 | * sub-frame accuracy. |
409 | */ |
410 | pos = pos * s->afile.rate / dev_rate; |
411 | |
412 | if (!afile_seek(&s->afile, pos * s->bpf)) { |
413 | s->pstate = SLOT_INIT1; |
414 | return; |
415 | } |
416 | s->pstate = SLOT_RUN2; |
417 | if (s->mode & SIO_PLAY1) |
418 | slot_fill(s); |
419 | #ifdef DEBUG1 |
420 | if (log_level >= 2) { |
421 | slot_log(s); |
422 | log_puts(": started\n"); |
423 | } |
424 | #endif |
425 | } |
426 | |
427 | static void |
428 | slot_stop(struct slot *s) |
429 | { |
430 | if (s->pstate == SLOT_INIT1) |
431 | return; |
432 | if (s->mode & SIO_REC2) |
433 | slot_flush(s); |
434 | if (s->mode & SIO_PLAY1) |
435 | s->buf.used = s->buf.start = 0; |
436 | s->pstate = SLOT_INIT1; |
437 | #ifdef DEBUG1 |
438 | if (log_level >= 2) { |
439 | slot_log(s); |
440 | log_puts(": stopped\n"); |
441 | } |
442 | #endif |
443 | } |
444 | |
445 | static void |
446 | slot_del(struct slot *s) |
447 | { |
448 | struct slot **ps; |
449 | |
450 | if (s->pstate != SLOT_CFG0) { |
451 | slot_stop(s); |
452 | afile_close(&s->afile); |
453 | #ifdef DEBUG1 |
454 | if (log_level >= 3) { |
455 | slot_log(s); |
456 | log_puts(": closed\n"); |
457 | } |
458 | #endif |
459 | abuf_done(&s->buf); |
460 | if (s->resampbuf) |
461 | xfree(s->resampbuf); |
462 | if (s->convbuf) |
463 | xfree(s->convbuf); |
464 | } |
465 | for (ps = &slot_list; *ps != s; ps = &(*ps)->next) |
466 | ; /* nothing */ |
467 | *ps = s->next; |
468 | xfree(s); |
469 | } |
470 | |
471 | static void |
472 | slot_getcnt(struct slot *s, int *icnt, int *ocnt) |
473 | { |
474 | int cnt; |
475 | |
476 | if (s->resampbuf) |
477 | resamp_getcnt(&s->resamp, icnt, ocnt); |
478 | else { |
479 | cnt = (*icnt < *ocnt) ? *icnt : *ocnt; |
480 | *icnt = cnt; |
481 | *ocnt = cnt; |
482 | } |
483 | } |
484 | |
485 | static void |
486 | play_filt_resamp(struct slot *s, void *res_in, void *out, int icnt, int ocnt) |
487 | { |
488 | int i, offs, vol, nch; |
489 | void *in; |
490 | |
491 | if (s->resampbuf) { |
492 | resamp_do(&s->resamp, res_in, s->resampbuf, icnt, ocnt); |
493 | in = s->resampbuf; |
494 | } else |
495 | in = res_in; |
496 | |
497 | nch = s->cmap.nch; |
498 | vol = s->vol / s->join; /* XXX */ |
499 | cmap_add(&s->cmap, in, out, vol, ocnt); |
500 | |
501 | offs = 0; |
502 | for (i = s->join - 1; i > 0; i--) { |
503 | offs += nch; |
504 | cmap_add(&s->cmap, (adata_t *)in + offs, out, vol, ocnt); |
505 | } |
506 | offs = 0; |
507 | for (i = s->expand - 1; i > 0; i--) { |
508 | offs += nch; |
509 | cmap_add(&s->cmap, in, (adata_t *)out + offs, vol, ocnt); |
510 | } |
511 | } |
512 | |
513 | static void |
514 | play_filt_dec(struct slot *s, void *in, void *out, int icnt, int ocnt) |
515 | { |
516 | void *tmp; |
517 | |
518 | tmp = s->convbuf; |
519 | if (tmp) { |
520 | switch (s->afile.fmt) { |
521 | case AFILE_FMT_PCM0: |
522 | dec_do(&s->conv, in, tmp, icnt); |
523 | break; |
524 | case AFILE_FMT_ULAW1: |
525 | dec_do_ulaw(&s->conv, in, tmp, icnt, 0); |
526 | break; |
527 | case AFILE_FMT_ALAW2: |
528 | dec_do_ulaw(&s->conv, in, tmp, icnt, 1); |
529 | break; |
530 | case AFILE_FMT_FLOAT3: |
531 | dec_do_float(&s->conv, in, tmp, icnt); |
532 | break; |
533 | } |
534 | } else |
535 | tmp = in; |
536 | play_filt_resamp(s, tmp, out, icnt, ocnt); |
537 | } |
538 | |
539 | /* |
540 | * Mix as many as possible frames (but not more than a block) from the |
541 | * slot buffer to the given location. Return the number of frames mixed |
542 | * in the output buffer |
543 | */ |
544 | static int |
545 | slot_mix_badd(struct slot *s, adata_t *odata) |
546 | { |
547 | adata_t *idata; |
548 | int len, icnt, ocnt, otodo, odone; |
549 | |
550 | odone = 0; |
551 | otodo = dev_round; |
552 | if (s->skip > 0) { |
553 | ocnt = otodo; |
554 | if (ocnt > s->skip) |
555 | ocnt = s->skip; |
556 | s->skip -= ocnt; |
557 | odata += dev_pchan * ocnt; |
558 | otodo -= ocnt; |
559 | odone += ocnt; |
560 | } |
561 | while (otodo > 0) { |
562 | idata = (adata_t *)abuf_rgetblk(&s->buf, &len); |
563 | icnt = len / s->bpf; |
564 | if (icnt > s->round) |
565 | icnt = s->round; |
566 | ocnt = otodo; |
567 | slot_getcnt(s, &icnt, &ocnt); |
568 | if (icnt == 0) |
569 | break; |
570 | play_filt_dec(s, idata, odata, icnt, ocnt); |
571 | abuf_rdiscard(&s->buf, icnt * s->bpf); |
572 | otodo -= ocnt; |
573 | odone += ocnt; |
574 | odata += ocnt * dev_pchan; |
575 | } |
576 | return odone; |
577 | } |
578 | |
579 | static void |
580 | rec_filt_resamp(struct slot *s, void *in, void *res_out, int icnt, int ocnt) |
581 | { |
582 | int i, vol, offs, nch; |
583 | void *out = res_out; |
584 | |
585 | out = (s->resampbuf) ? s->resampbuf : res_out; |
586 | |
587 | nch = s->cmap.nch; |
588 | vol = ADATA_UNIT(1 << (16 - 1)) / s->join; |
589 | cmap_copy(&s->cmap, in, out, vol, icnt); |
590 | |
591 | offs = 0; |
592 | for (i = s->join - 1; i > 0; i--) { |
593 | offs += nch; |
594 | cmap_add(&s->cmap, (adata_t *)in + offs, out, vol, icnt); |
595 | } |
596 | offs = 0; |
597 | for (i = s->expand - 1; i > 0; i--) { |
598 | offs += nch; |
599 | cmap_copy(&s->cmap, in, (adata_t *)out + offs, vol, icnt); |
600 | } |
601 | if (s->resampbuf) |
602 | resamp_do(&s->resamp, s->resampbuf, res_out, icnt, ocnt); |
603 | else |
604 | ocnt = icnt; |
Value stored to 'ocnt' is never read | |
605 | } |
606 | |
607 | static void |
608 | rec_filt_enc(struct slot *s, void *in, void *out, int icnt, int ocnt) |
609 | { |
610 | void *tmp; |
611 | |
612 | tmp = s->convbuf; |
613 | rec_filt_resamp(s, in, tmp ? tmp : out, icnt, ocnt); |
614 | if (tmp) |
615 | enc_do(&s->conv, tmp, out, ocnt); |
616 | } |
617 | |
618 | /* |
619 | * Copy "todo" frames from the given buffer to the slot buffer, |
620 | * but not more than a block. |
621 | */ |
622 | static void |
623 | slot_sub_bcopy(struct slot *s, adata_t *idata, int itodo) |
624 | { |
625 | adata_t *odata; |
626 | int len, icnt, ocnt; |
627 | |
628 | if (s->skip > 0) { |
629 | icnt = itodo; |
630 | if (icnt > s->skip) |
631 | icnt = s->skip; |
632 | s->skip -= icnt; |
633 | idata += dev_rchan * icnt; |
634 | itodo -= icnt; |
635 | } |
636 | |
637 | while (itodo > 0) { |
638 | odata = (adata_t *)abuf_wgetblk(&s->buf, &len); |
639 | ocnt = len / s->bpf; |
640 | if (ocnt > s->round) |
641 | ocnt = s->round; |
642 | icnt = itodo; |
643 | slot_getcnt(s, &icnt, &ocnt); |
644 | if (ocnt == 0) |
645 | break; |
646 | rec_filt_enc(s, idata, odata, icnt, ocnt); |
647 | abuf_wcommit(&s->buf, ocnt * s->bpf); |
648 | itodo -= icnt; |
649 | idata += icnt * dev_rchan; |
650 | } |
651 | } |
652 | |
653 | static int |
654 | dev_open(char *dev, int mode, int bufsz, char *port) |
655 | { |
656 | int rate, pmax, rmax; |
657 | struct sio_par par; |
658 | struct slot *s; |
659 | |
660 | if (port) { |
661 | dev_port = port; |
662 | dev_mh = mio_open(dev_port, MIO_IN8, 0); |
663 | if (dev_mh == NULL((void*)0)) { |
664 | log_puts(port); |
665 | log_puts(": couldn't open midi port\n"); |
666 | return 0; |
667 | } |
668 | } else |
669 | dev_mh = NULL((void*)0); |
670 | |
671 | dev_name = dev; |
672 | dev_sh = sio_open(dev, mode, 0); |
673 | if (dev_sh == NULL((void*)0)) { |
674 | log_puts(dev_name); |
675 | log_puts(": couldn't open audio device\n"); |
676 | return 0; |
677 | } |
678 | |
679 | rate = pmax = rmax = 0; |
680 | for (s = slot_list; s != NULL((void*)0); s = s->next) { |
681 | if (s->afile.rate > rate) |
682 | rate = s->afile.rate; |
683 | if (s->mode == SIO_PLAY1) { |
684 | if (s->cmax > pmax) |
685 | pmax = s->cmax; |
686 | } |
687 | if (s->mode == SIO_REC2) { |
688 | if (s->cmax > rmax) |
689 | rmax = s->cmax; |
690 | } |
691 | } |
692 | sio_initpar(&par); |
693 | par.bits = ADATA_BITS16; |
694 | par.bps = sizeof(adata_t); |
695 | par.msb = 0; |
696 | par.le = SIO_LE_NATIVE1; |
697 | par.rate = rate; |
698 | if (mode & SIO_PLAY1) |
699 | par.pchan = pmax + 1; |
700 | if (mode & SIO_REC2) |
701 | par.rchan = rmax + 1; |
702 | par.appbufsz = bufsz > 0 ? bufsz : rate * DEFAULT_BUFSZ_MS200 / 1000; |
703 | if (!sio_setpar(dev_sh, &par) || !sio_getpar(dev_sh, &par)) { |
704 | log_puts(dev_name); |
705 | log_puts(": couldn't set audio params\n"); |
706 | return 0; |
707 | } |
708 | if (par.bits != ADATA_BITS16 || |
709 | par.bps != sizeof(adata_t) || |
710 | (par.bps > 1 && par.le != SIO_LE_NATIVE1) || |
711 | (par.bps * 8 > par.bits && par.msb)) { |
712 | log_puts(dev_name); |
713 | log_puts(": unsupported audio params\n"); |
714 | return 0; |
715 | } |
716 | dev_mode = mode; |
717 | dev_rate = par.rate; |
718 | dev_bufsz = par.bufsz; |
719 | dev_round = par.round; |
720 | if (mode & SIO_PLAY1) { |
721 | dev_pchan = par.pchan; |
722 | dev_pbuf = allocbuf(dev_round, dev_pchan); |
723 | } |
724 | if (mode & SIO_REC2) { |
725 | dev_rchan = par.rchan; |
726 | dev_rbuf = allocbuf(dev_round, dev_rchan); |
727 | } |
728 | dev_pstate = DEV_STOP0; |
729 | if (log_level >= 2) { |
730 | log_puts(dev_name); |
731 | log_puts(": "); |
732 | log_putu(dev_rate); |
733 | log_puts("Hz"); |
734 | if (dev_mode & SIO_PLAY1) { |
735 | log_puts(", play 0:"); |
736 | log_puti(dev_pchan - 1); |
737 | } |
738 | if (dev_mode & SIO_REC2) { |
739 | log_puts(", rec 0:"); |
740 | log_puti(dev_rchan - 1); |
741 | } |
742 | log_puts(", "); |
743 | log_putu(dev_bufsz / dev_round); |
744 | log_puts(" blocks of "); |
745 | log_putu(dev_round); |
746 | log_puts(" frames\n"); |
747 | } |
748 | return 1; |
749 | } |
750 | |
751 | static void |
752 | dev_close(void) |
753 | { |
754 | sio_close(dev_sh); |
755 | if (dev_mh) |
756 | mio_close(dev_mh); |
757 | if (dev_mode & SIO_PLAY1) |
758 | xfree(dev_pbuf); |
759 | if (dev_mode & SIO_REC2) |
760 | xfree(dev_rbuf); |
761 | } |
762 | |
763 | static void |
764 | dev_master(int val) |
765 | { |
766 | struct slot *s; |
767 | int mastervol, slotvol; |
768 | |
769 | mastervol = MIDI_TO_ADATA(dev_volctl)(aparams_ctltovol[dev_volctl] << (16 - 16)); |
770 | for (s = slot_list; s != NULL((void*)0); s = s->next) { |
771 | slotvol = MIDI_TO_ADATA(val)(aparams_ctltovol[val] << (16 - 16)); |
772 | s->vol = ADATA_MUL(mastervol, slotvol)(((int)(mastervol) * (int)(slotvol)) >> (16 - 1)); |
773 | } |
774 | #ifdef DEBUG1 |
775 | if (log_level >= 3) { |
776 | log_puts("master volume set to "); |
777 | log_putu(val); |
778 | log_puts("\n"); |
779 | } |
780 | #endif |
781 | } |
782 | |
783 | static void |
784 | dev_slotvol(int midich, int val) |
785 | { |
786 | struct slot *s; |
787 | int mastervol, slotvol; |
788 | |
789 | for (s = slot_list; s != NULL((void*)0); s = s->next) { |
790 | if (midich == 0) { |
791 | mastervol = MIDI_TO_ADATA(dev_volctl)(aparams_ctltovol[dev_volctl] << (16 - 16)); |
792 | slotvol = MIDI_TO_ADATA(val)(aparams_ctltovol[val] << (16 - 16)); |
793 | s->vol = ADATA_MUL(mastervol, slotvol)(((int)(mastervol) * (int)(slotvol)) >> (16 - 1)); |
794 | #ifdef DEBUG1 |
795 | if (log_level >= 3) { |
796 | slot_log(s); |
797 | log_puts(": volume set to "); |
798 | log_putu(val); |
799 | log_puts("\n"); |
800 | } |
801 | #endif |
802 | break; |
803 | } |
804 | } |
805 | } |
806 | |
807 | /* |
808 | * start all slots simultaneously |
809 | */ |
810 | static void |
811 | dev_mmcstart(void) |
812 | { |
813 | struct slot *s; |
814 | |
815 | if (dev_pstate == DEV_STOP0) { |
816 | dev_pstate = DEV_START1; |
817 | for (s = slot_list; s != NULL((void*)0); s = s->next) |
818 | slot_start(s, dev_pos); |
819 | dev_prime = (dev_mode & SIO_PLAY1) ? dev_bufsz / dev_round : 0; |
820 | sio_start(dev_sh); |
821 | if (log_level >= 2) |
822 | log_puts("started\n"); |
823 | } else { |
824 | #ifdef DEBUG1 |
825 | if (log_level >= 3) |
826 | log_puts("ignoring mmc start\n"); |
827 | #endif |
828 | } |
829 | } |
830 | |
831 | /* |
832 | * stop all slots simultaneously |
833 | */ |
834 | static void |
835 | dev_mmcstop(void) |
836 | { |
837 | struct slot *s; |
838 | |
839 | if (dev_pstate == DEV_START1) { |
840 | dev_pstate = DEV_STOP0; |
841 | for (s = slot_list; s != NULL((void*)0); s = s->next) |
842 | slot_stop(s); |
843 | sio_stop(dev_sh); |
844 | if (log_level >= 2) |
845 | log_puts("stopped\n"); |
846 | } else { |
847 | #ifdef DEBUG1 |
848 | if (log_level >= 3) |
849 | log_puts("ignored mmc stop\n"); |
850 | #endif |
851 | } |
852 | } |
853 | |
854 | /* |
855 | * relocate all slots simultaneously |
856 | */ |
857 | static void |
858 | dev_mmcloc(int hr, int min, int sec, int fr, int cent, int fps) |
859 | { |
860 | long long pos; |
861 | |
862 | pos = (long long)dev_rate * hr * 3600 + |
863 | (long long)dev_rate * min * 60 + |
864 | (long long)dev_rate * sec + |
865 | (long long)dev_rate * fr / fps + |
866 | (long long)dev_rate * cent / (100 * fps); |
867 | if (dev_pos == pos) |
868 | return; |
869 | dev_pos = pos; |
870 | if (log_level >= 2) { |
871 | log_puts("relocated to "); |
872 | log_putu(hr); |
873 | log_puts(":"); |
874 | log_putu(min); |
875 | log_puts(":"); |
876 | log_putu(sec); |
877 | log_puts("."); |
878 | log_putu(fr); |
879 | log_puts("."); |
880 | log_putu(cent); |
881 | log_puts(" at "); |
882 | log_putu(fps); |
883 | log_puts("fps\n"); |
884 | } |
885 | if (dev_pstate == DEV_START1) { |
886 | dev_mmcstop(); |
887 | dev_mmcstart(); |
888 | } |
889 | } |
890 | |
891 | static void |
892 | dev_imsg(unsigned char *msg, unsigned int len) |
893 | { |
894 | struct sysex *x; |
895 | unsigned int fps, chan; |
896 | |
897 | if ((msg[0] & MIDI_CMDMASK0xf0) == MIDI_CTL0xb0 && msg[1] == MIDI_CTL_VOL7) { |
898 | chan = msg[0] & MIDI_CHANMASK0x0f; |
899 | dev_slotvol(chan, msg[2]); |
900 | return; |
901 | } |
902 | x = (struct sysex *)msg; |
903 | if (x->start != SYSEX_START0xf0) |
904 | return; |
905 | if (len < SYSEX_SIZE(empty)(5 + sizeof(struct sysex_empty))) |
906 | return; |
907 | if (x->type != SYSEX_TYPE_RT0x7f) |
908 | return; |
909 | if (x->id0 == SYSEX_CONTROL0x04 && x->id1 == SYSEX_MASTER0x01) { |
910 | if (len == SYSEX_SIZE(master)(5 + sizeof(struct sysex_master))) |
911 | dev_master(x->u.master.coarse); |
912 | return; |
913 | } |
914 | if (x->id0 != SYSEX_MMC0x06) |
915 | return; |
916 | switch (x->id1) { |
917 | case SYSEX_MMC_STOP0x01: |
918 | if (len != SYSEX_SIZE(stop)(5 + sizeof(struct sysex_stop))) |
919 | return; |
920 | dev_mmcstop(); |
921 | break; |
922 | case SYSEX_MMC_START0x02: |
923 | if (len != SYSEX_SIZE(start)(5 + sizeof(struct sysex_start))) |
924 | return; |
925 | dev_mmcstart(); |
926 | break; |
927 | case SYSEX_MMC_LOC0x44: |
928 | if (len != SYSEX_SIZE(loc)(5 + sizeof(struct sysex_loc)) || |
929 | x->u.loc.len != SYSEX_MMC_LOC_LEN0x06 || |
930 | x->u.loc.cmd != SYSEX_MMC_LOC_CMD0x01) |
931 | return; |
932 | switch (x->u.loc.hr >> 5) { |
933 | case MTC_FPS_240: |
934 | fps = 24; |
935 | break; |
936 | case MTC_FPS_251: |
937 | fps = 25; |
938 | break; |
939 | case MTC_FPS_303: |
940 | fps = 30; |
941 | break; |
942 | default: |
943 | dev_mmcstop(); |
944 | return; |
945 | } |
946 | dev_mmcloc(x->u.loc.hr & 0x1f, |
947 | x->u.loc.min, |
948 | x->u.loc.sec, |
949 | x->u.loc.fr, |
950 | x->u.loc.cent, |
951 | fps); |
952 | break; |
953 | } |
954 | } |
955 | |
956 | /* |
957 | * parse the given data chunk and call imsg() for each message |
958 | */ |
959 | static void |
960 | midi_in(unsigned char *idata, int icount) |
961 | { |
962 | int i; |
963 | unsigned char c; |
964 | |
965 | for (i = 0; i < icount; i++) { |
966 | c = *idata++; |
967 | if (c >= 0xf8) { |
968 | /* we don't use real-time events */ |
969 | } else if (c == SYSEX_END0xf7) { |
970 | if (dev_mst == SYSEX_START0xf0) { |
971 | dev_msg[dev_midx++] = c; |
972 | dev_imsg(dev_msg, dev_midx); |
973 | } |
974 | dev_mst = 0; |
975 | dev_midx = 0; |
976 | } else if (c >= 0xf0) { |
977 | dev_msg[0] = c; |
978 | dev_mlen = common_len[c & 7]; |
979 | dev_mst = c; |
980 | dev_midx = 1; |
981 | } else if (c >= 0x80) { |
982 | dev_msg[0] = c; |
983 | dev_mlen = voice_len[(c >> 4) & 7]; |
984 | dev_mst = c; |
985 | dev_midx = 1; |
986 | } else if (dev_mst) { |
987 | if (dev_midx == 0 && dev_mst != SYSEX_START0xf0) |
988 | dev_msg[dev_midx++] = dev_mst; |
989 | dev_msg[dev_midx++] = c; |
990 | if (dev_midx == dev_mlen) { |
991 | dev_imsg(dev_msg, dev_midx); |
992 | if (dev_mst >= 0xf0) |
993 | dev_mst = 0; |
994 | dev_midx = 0; |
995 | } else if (dev_midx == MIDI_MSGMAX32) { |
996 | /* sysex too long */ |
997 | dev_mst = 0; |
998 | } |
999 | } |
1000 | } |
1001 | } |
1002 | |
1003 | static int |
1004 | slot_list_mix(unsigned int round, unsigned int pchan, adata_t *pbuf) |
1005 | { |
1006 | unsigned int done, n; |
1007 | struct slot *s; |
1008 | |
1009 | memset(pbuf, 0, pchan * round * sizeof(adata_t)); |
1010 | done = 0; |
1011 | for (s = slot_list; s != NULL((void*)0); s = s->next) { |
1012 | if (s->pstate == SLOT_INIT1 || !(s->mode & SIO_PLAY1)) |
1013 | continue; |
1014 | if (s->pstate == SLOT_STOP3 && s->buf.used < s->bpf) { |
1015 | #ifdef DEBUG1 |
1016 | if (log_level >= 3) { |
1017 | slot_log(s); |
1018 | log_puts(": drained, done\n"); |
1019 | } |
1020 | #endif |
1021 | slot_stop(s); |
1022 | continue; |
1023 | } |
1024 | n = slot_mix_badd(s, dev_pbuf); |
1025 | if (n > done) |
1026 | done = n; |
1027 | } |
1028 | return done; |
1029 | } |
1030 | |
1031 | static int |
1032 | slot_list_copy(unsigned int count, unsigned int rchan, adata_t *rbuf) |
1033 | { |
1034 | unsigned int done; |
1035 | struct slot *s; |
1036 | |
1037 | done = 0; |
1038 | for (s = slot_list; s != NULL((void*)0); s = s->next) { |
1039 | if (s->pstate == SLOT_INIT1 || !(s->mode & SIO_REC2)) |
1040 | continue; |
1041 | slot_sub_bcopy(s, rbuf, count); |
1042 | done = count; |
1043 | } |
1044 | return done; |
1045 | } |
1046 | |
1047 | static void |
1048 | slot_list_iodo(void) |
1049 | { |
1050 | struct slot *s; |
1051 | |
1052 | for (s = slot_list; s != NULL((void*)0); s = s->next) { |
1053 | if (s->pstate != SLOT_RUN2) |
1054 | continue; |
1055 | if ((s->mode & SIO_PLAY1) && |
1056 | (s->buf.used < s->round * s->bpf)) |
1057 | slot_fill(s); |
1058 | if ((s->mode & SIO_REC2) && |
1059 | (s->buf.len - s->buf.used < s->round * s->bpf)) |
1060 | slot_flush(s); |
1061 | } |
1062 | } |
1063 | |
1064 | static int |
1065 | offline(void) |
1066 | { |
1067 | unsigned int todo; |
1068 | int rate, cmax; |
1069 | struct slot *s; |
1070 | |
1071 | if (pledge("stdio", NULL((void*)0)) == -1) |
1072 | err(1, "pledge"); |
1073 | |
1074 | rate = cmax = 0; |
1075 | for (s = slot_list; s != NULL((void*)0); s = s->next) { |
1076 | if (s->afile.rate > rate) |
1077 | rate = s->afile.rate; |
1078 | if (s->cmax > cmax) |
1079 | cmax = s->cmax; |
1080 | } |
1081 | dev_sh = NULL((void*)0); |
1082 | dev_name = "offline"; |
1083 | dev_mode = SIO_PLAY1 | SIO_REC2; |
1084 | dev_rate = rate; |
1085 | dev_bufsz = rate; |
1086 | dev_round = rate; |
1087 | dev_pchan = dev_rchan = cmax + 1; |
1088 | dev_pbuf = dev_rbuf = allocbuf(dev_round, dev_pchan); |
1089 | dev_pstate = DEV_STOP0; |
1090 | for (s = slot_list; s != NULL((void*)0); s = s->next) |
1091 | slot_init(s); |
1092 | for (s = slot_list; s != NULL((void*)0); s = s->next) |
1093 | slot_start(s, 0); |
1094 | for (;;) { |
1095 | todo = slot_list_mix(dev_round, dev_pchan, dev_pbuf); |
1096 | if (todo == 0) |
1097 | break; |
1098 | slot_list_copy(todo, dev_pchan, dev_pbuf); |
1099 | slot_list_iodo(); |
1100 | } |
1101 | xfree(dev_pbuf); |
1102 | while (slot_list) |
1103 | slot_del(slot_list); |
1104 | return 1; |
1105 | } |
1106 | |
1107 | static int |
1108 | playrec_cycle(void) |
1109 | { |
1110 | unsigned int n, todo; |
1111 | unsigned char *p; |
1112 | int pcnt, rcnt; |
1113 | |
1114 | #ifdef DEBUG1 |
1115 | if (log_level >= 4) { |
1116 | log_puts(dev_name); |
1117 | log_puts(": cycle, prime = "); |
1118 | log_putu(dev_prime); |
1119 | log_puts("\n"); |
1120 | } |
1121 | #endif |
1122 | pcnt = rcnt = 0; |
1123 | if (dev_mode & SIO_REC2) { |
1124 | if (dev_prime > 0) |
1125 | dev_prime--; |
1126 | else { |
1127 | todo = dev_round * dev_rchan * sizeof(adata_t); |
1128 | p = (unsigned char *)dev_rbuf; |
1129 | while (todo > 0) { |
1130 | n = sio_read(dev_sh, p, todo); |
1131 | if (n == 0) { |
1132 | log_puts(dev_name); |
1133 | log_puts(": failed to read " |
1134 | "from device\n"); |
1135 | return 0; |
1136 | } |
1137 | p += n; |
1138 | todo -= n; |
1139 | } |
1140 | rcnt = slot_list_copy(dev_round, dev_rchan, dev_rbuf); |
1141 | } |
1142 | } |
1143 | if (dev_mode & SIO_PLAY1) { |
1144 | pcnt = slot_list_mix(dev_round, dev_pchan, dev_pbuf); |
1145 | todo = sizeof(adata_t) * dev_pchan * dev_round; |
1146 | n = sio_write(dev_sh, dev_pbuf, todo); |
1147 | if (n == 0) { |
1148 | log_puts(dev_name); |
1149 | log_puts(": failed to write to device\n"); |
1150 | return 0; |
1151 | } |
1152 | } |
1153 | slot_list_iodo(); |
1154 | return pcnt > 0 || rcnt > 0; |
1155 | } |
1156 | |
1157 | static void |
1158 | sigint(int s) |
1159 | { |
1160 | if (quit_flag) |
1161 | _exit(1); |
1162 | quit_flag = 1; |
1163 | } |
1164 | |
1165 | static int |
1166 | playrec(char *dev, int mode, int bufsz, char *port) |
1167 | { |
1168 | #define MIDIBUFSZ0x100 0x100 |
1169 | unsigned char mbuf[MIDIBUFSZ0x100]; |
1170 | struct sigaction sa; |
1171 | struct pollfd *pfds; |
1172 | struct slot *s; |
1173 | int n, ns, nm, ev; |
1174 | |
1175 | if (!dev_open(dev, mode, bufsz, port)) |
1176 | return 0; |
1177 | if (pledge("stdio audio", NULL((void*)0)) == -1) |
1178 | err(1, "pledge"); |
1179 | |
1180 | n = sio_nfds(dev_sh); |
1181 | if (dev_mh) |
1182 | n += mio_nfds(dev_mh); |
1183 | pfds = reallocarray(NULL((void*)0), n, sizeof(struct pollfd)); |
1184 | if (pfds == NULL((void*)0)) |
1185 | err(1, "malloc"); |
1186 | |
1187 | for (s = slot_list; s != NULL((void*)0); s = s->next) |
1188 | slot_init(s); |
1189 | if (dev_mh == NULL((void*)0)) |
1190 | dev_mmcstart(); |
1191 | else { |
1192 | if (log_level >= 2) |
1193 | log_puts("ready, waiting for mmc messages\n"); |
1194 | } |
1195 | |
1196 | quit_flag = 0; |
1197 | sigfillset(&sa.sa_mask); |
1198 | sa.sa_flags = SA_RESTART0x0002; |
1199 | sa.sa_handler__sigaction_u.__sa_handler = sigint; |
1200 | sigaction(SIGINT2, &sa, NULL((void*)0)); |
1201 | sigaction(SIGTERM15, &sa, NULL((void*)0)); |
1202 | sigaction(SIGHUP1, &sa, NULL((void*)0)); |
1203 | while (!quit_flag) { |
1204 | if (dev_pstate == DEV_START1) { |
1205 | ev = 0; |
1206 | if (mode & SIO_PLAY1) |
1207 | ev |= POLLOUT0x0004; |
1208 | if (mode & SIO_REC2) |
1209 | ev |= POLLIN0x0001; |
1210 | ns = sio_pollfd(dev_sh, pfds, ev); |
1211 | } else |
1212 | ns = 0; |
1213 | if (dev_mh) |
1214 | nm = mio_pollfd(dev_mh, pfds + ns, POLLIN0x0001); |
1215 | else |
1216 | nm = 0; |
1217 | if (poll(pfds, ns + nm, -1) == -1) { |
1218 | if (errno(*__errno()) == EINTR4) |
1219 | continue; |
1220 | log_puts("poll failed\n"); |
1221 | panic(); |
1222 | } |
1223 | if (dev_pstate == DEV_START1) { |
1224 | ev = sio_revents(dev_sh, pfds); |
1225 | if (ev & POLLHUP0x0010) { |
1226 | log_puts(dev); |
1227 | log_puts(": audio device gone, stopping\n"); |
1228 | break; |
1229 | } |
1230 | if (ev & (POLLIN0x0001 | POLLOUT0x0004)) { |
1231 | if (!playrec_cycle() && dev_mh == NULL((void*)0)) |
1232 | break; |
1233 | } |
1234 | } |
1235 | if (dev_mh) { |
1236 | ev = mio_revents(dev_mh, pfds + ns); |
1237 | if (ev & POLLHUP0x0010) { |
1238 | log_puts(dev_port); |
1239 | log_puts(": midi port gone, stopping\n"); |
1240 | break; |
1241 | } |
1242 | if (ev & POLLIN0x0001) { |
1243 | n = mio_read(dev_mh, mbuf, MIDIBUFSZ0x100); |
1244 | midi_in(mbuf, n); |
1245 | } |
1246 | } |
1247 | } |
1248 | sigfillset(&sa.sa_mask); |
1249 | sa.sa_flags = SA_RESTART0x0002; |
1250 | sa.sa_handler__sigaction_u.__sa_handler = SIG_DFL(void (*)(int))0; |
1251 | sigaction(SIGINT2, &sa, NULL((void*)0)); |
1252 | sigaction(SIGTERM15, &sa, NULL((void*)0)); |
1253 | sigaction(SIGHUP1, &sa, NULL((void*)0)); |
1254 | |
1255 | if (dev_pstate == DEV_START1) |
1256 | dev_mmcstop(); |
1257 | xfree(pfds); |
1258 | dev_close(); |
1259 | while (slot_list) |
1260 | slot_del(slot_list); |
1261 | return 1; |
1262 | } |
1263 | |
1264 | static int |
1265 | opt_onoff(char *s, int *flag) |
1266 | { |
1267 | if (strcmp("off", s) == 0) { |
1268 | *flag = 0; |
1269 | return 1; |
1270 | } |
1271 | if (strcmp("on", s) == 0) { |
1272 | *flag = 1; |
1273 | return 1; |
1274 | } |
1275 | log_puts(s); |
1276 | log_puts(": on/off expected\n"); |
1277 | return 0; |
1278 | } |
1279 | |
1280 | static int |
1281 | opt_enc(char *s, struct aparams *par) |
1282 | { |
1283 | int len; |
1284 | |
1285 | len = aparams_strtoenc(par, s); |
1286 | if (len == 0 || s[len] != '\0') { |
1287 | log_puts(s); |
1288 | log_puts(": bad encoding\n"); |
1289 | return 0; |
1290 | } |
1291 | return 1; |
1292 | } |
1293 | |
1294 | static int |
1295 | opt_hdr(char *s, int *hdr) |
1296 | { |
1297 | if (strcmp("auto", s) == 0) { |
1298 | *hdr = AFILE_HDR_AUTO0; |
1299 | return 1; |
1300 | } |
1301 | if (strcmp("raw", s) == 0) { |
1302 | *hdr = AFILE_HDR_RAW1; |
1303 | return 1; |
1304 | } |
1305 | if (strcmp("wav", s) == 0) { |
1306 | *hdr = AFILE_HDR_WAV2; |
1307 | return 1; |
1308 | } |
1309 | if (strcmp("aiff", s) == 0) { |
1310 | *hdr = AFILE_HDR_AIFF3; |
1311 | return 1; |
1312 | } |
1313 | if (strcmp("au", s) == 0) { |
1314 | *hdr = AFILE_HDR_AU4; |
1315 | return 1; |
1316 | } |
1317 | log_puts(s); |
1318 | log_puts(": bad header type\n"); |
1319 | return 0; |
1320 | } |
1321 | |
1322 | static int |
1323 | opt_ch(char *s, int *rcmin, int *rcmax) |
1324 | { |
1325 | char *next, *end; |
1326 | long cmin, cmax; |
1327 | |
1328 | errno(*__errno()) = 0; |
1329 | cmin = strtol(s, &next, 10); |
1330 | if (next == s || *next != ':') |
1331 | goto failed; |
1332 | cmax = strtol(++next, &end, 10); |
1333 | if (end == next || *end != '\0') |
1334 | goto failed; |
1335 | if (cmin < 0 || cmax < cmin || cmax >= NCHAN_MAX64) |
1336 | goto failed; |
1337 | *rcmin = cmin; |
1338 | *rcmax = cmax; |
1339 | return 1; |
1340 | failed: |
1341 | log_puts(s); |
1342 | log_puts(": channel range expected\n"); |
1343 | return 0; |
1344 | } |
1345 | |
1346 | static int |
1347 | opt_num(char *s, int min, int max, int *num) |
1348 | { |
1349 | const char *errstr; |
1350 | |
1351 | *num = strtonum(s, min, max, &errstr); |
1352 | if (errstr) { |
1353 | log_puts(s); |
1354 | log_puts(": expected integer between "); |
1355 | log_puti(min); |
1356 | log_puts(" and "); |
1357 | log_puti(max); |
1358 | log_puts("\n"); |
1359 | return 0; |
1360 | } |
1361 | return 1; |
1362 | } |
1363 | |
1364 | static int |
1365 | opt_pos(char *s, long long *pos) |
1366 | { |
1367 | const char *errstr; |
1368 | |
1369 | *pos = strtonum(s, 0, LLONG_MAX9223372036854775807LL, &errstr); |
1370 | if (errstr) { |
1371 | log_puts(s); |
1372 | log_puts(": positive number of samples expected\n"); |
1373 | return 0; |
1374 | } |
1375 | return 1; |
1376 | } |
1377 | |
1378 | int |
1379 | main(int argc, char **argv) |
1380 | { |
1381 | int dup, cmin, cmax, rate, vol, bufsz, hdr, mode; |
1382 | char *port, *dev; |
1383 | struct aparams par; |
1384 | int n_flag, c; |
1385 | long long pos; |
1386 | |
1387 | if (pledge("stdio rpath wpath cpath inet unix dns audio", NULL((void*)0)) == -1) |
1388 | err(1, "pledge"); |
1389 | |
1390 | vol = 127; |
1391 | dup = 0; |
1392 | bufsz = 0; |
1393 | rate = DEFAULT_RATE48000; |
1394 | cmin = 0; |
1395 | cmax = 1; |
1396 | aparams_init(&par); |
1397 | hdr = AFILE_HDR_AUTO0; |
1398 | n_flag = 0; |
1399 | port = NULL((void*)0); |
1400 | dev = NULL((void*)0); |
1401 | mode = 0; |
1402 | pos = 0; |
1403 | |
1404 | while ((c = getopt(argc, argv, |
1405 | "b:c:de:f:g:h:i:j:no:p:q:r:t:v:")) != -1) { |
1406 | switch (c) { |
1407 | case 'b': |
1408 | if (!opt_num(optarg, 1, RATE_MAX192000, &bufsz)) |
1409 | return 1; |
1410 | break; |
1411 | case 'c': |
1412 | if (!opt_ch(optarg, &cmin, &cmax)) |
1413 | return 1; |
1414 | break; |
1415 | case 'd': |
1416 | log_level++; |
1417 | break; |
1418 | case 'e': |
1419 | if (!opt_enc(optarg, &par)) |
1420 | return 1; |
1421 | break; |
1422 | case 'f': |
1423 | dev = optarg; |
1424 | break; |
1425 | case 'g': |
1426 | if (!opt_pos(optarg, &dev_pos)) |
1427 | return 1; |
1428 | break; |
1429 | case 'h': |
1430 | if (!opt_hdr(optarg, &hdr)) |
1431 | return 1; |
1432 | break; |
1433 | case 'i': |
1434 | if (!slot_new(optarg, SIO_PLAY1, |
1435 | &par, hdr, cmin, cmax, rate, dup, vol, pos)) |
1436 | return 1; |
1437 | mode |= SIO_PLAY1; |
1438 | break; |
1439 | case 'j': |
1440 | if (!opt_onoff(optarg, &dup)) |
1441 | return 1; |
1442 | break; |
1443 | case 'n': |
1444 | n_flag = 1; |
1445 | break; |
1446 | case 'o': |
1447 | if (!slot_new(optarg, SIO_REC2, |
1448 | &par, hdr, cmin, cmax, rate, dup, 0, pos)) |
1449 | return 1; |
1450 | mode |= SIO_REC2; |
1451 | break; |
1452 | case 'p': |
1453 | if (!opt_pos(optarg, &pos)) |
1454 | return 1; |
1455 | break; |
1456 | case 'q': |
1457 | port = optarg; |
1458 | break; |
1459 | case 'r': |
1460 | if (!opt_num(optarg, RATE_MIN4000, RATE_MAX192000, &rate)) |
1461 | return 1; |
1462 | break; |
1463 | case 'v': |
1464 | if (!opt_num(optarg, 0, MIDI_MAXCTL127, &vol)) |
1465 | return 1; |
1466 | break; |
1467 | default: |
1468 | goto bad_usage; |
1469 | } |
1470 | } |
1471 | argc -= optind; |
1472 | argv += optind; |
1473 | if (argc != 0) { |
1474 | bad_usage: |
1475 | log_puts(usagestr); |
1476 | return 1; |
1477 | } |
1478 | if (n_flag) { |
1479 | if (dev != NULL((void*)0) || port != NULL((void*)0)) { |
1480 | log_puts("-f and -q make no sense in off-line mode\n"); |
1481 | return 1; |
1482 | } |
1483 | if (mode != (SIO_PLAY1 | SIO_REC2)) { |
1484 | log_puts("both -i and -o required\n"); |
1485 | return 1; |
1486 | } |
1487 | if (!offline()) |
1488 | return 1; |
1489 | } else { |
1490 | if (dev == NULL((void*)0)) |
1491 | dev = SIO_DEVANY"default"; |
1492 | if (mode == 0) { |
1493 | log_puts("at least -i or -o required\n"); |
1494 | return 1; |
1495 | } |
1496 | if (!playrec(dev, mode, bufsz, port)) |
1497 | return 1; |
1498 | } |
1499 | return 0; |
1500 | } |