1
2 /*
3 # Sfront, a SAOL to C translator
4 # This file: merged linux/freebsd audio driver for sfront
5 # Copyright (C) 2000 Bertrand Petit
6 #
7 # Copyright (c) 1999-2006, Regents of the University of California
8 # All rights reserved.
9 #
10 # Redistribution and use in source and binary forms, with or without
11 # modification, are permitted provided that the following conditions are
12 # met:
13 #
14 # Redistributions of source code must retain the above copyright
15 # notice, this list of conditions and the following disclaimer.
16 #
17 # Redistributions in binary form must reproduce the above copyright
18 # notice, this list of conditions and the following disclaimer in the
19 # documentation and/or other materials provided with the distribution.
20 #
21 # Neither the name of the University of California, Berkeley nor the
22 # names of its contributors may be used to endorse or promote products
23 # derived from this software without specific prior written permission.
24 #
25 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
26 # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
27 # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
28 # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
29 # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
30 # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
31 # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
32 # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
33 # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
34 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
35 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
36 #
37 # Maintainer: John Lazzaro, lazzaro@cs.berkeley.edu
38 */
39
40
41 #define ASYSIO_LINUX 0
42 #define ASYSIO_FREEBSD 1
43
44 #define ASYSIO_OSTYPE ASYSIO_LINUX
45
46 /****************************************************************/
47 /****************************************************************/
48 /* linux audio driver for sfront */
49 /****************************************************************/
50
51 /**************************************************/
52 /* define flags for fifo mode, and for a timer to */
53 /* catch SAOL infinite loops */
54 /**************************************************/
55
56 #if (defined(ASYS_HASOUTPUT) && (ASYSIO_OSTYPE == ASYSIO_LINUX) && \
57 defined(CSYS_CDRIVER_LINMIDI) && (ASYS_TIMEOPTION == ASYS_TIMESYNC) && \
58 !defined(ASYS_HASINPUT))
59 #define ASYSIO_USEFIFO 1
60 #endif
61
62 #if (defined(ASYS_HASOUTPUT) && (ASYSIO_OSTYPE == ASYSIO_LINUX) && \
63 (ASYS_TIMEOPTION != ASYS_TIMESYNC))
64 #define ASYSIO_USEFIFO 1
65 #endif
66
67 #ifndef ASYSIO_USEFIFO
68 #define ASYSIO_USEFIFO 0
69 #endif
70
71 /**************************************************/
72 /* include headers, based on flags set above */
73 /**************************************************/
74
75 #include <unistd.h>
76 #include <sys/ioctl.h>
77 #include <fcntl.h>
78
79 #if (ASYSIO_OSTYPE == ASYSIO_LINUX)
80 #include <sys/soundcard.h>
81 #include <endian.h>
82 #endif
83
84 #if (ASYSIO_OSTYPE == ASYSIO_FREEBSD)
85 #include <machine/soundcard.h>
86 #include <machine/endian.h>
87 #endif
88
89 #include <signal.h>
90 #include <sys/time.h>
91
92 #if ASYSIO_USEFIFO
93 #include <sched.h>
94 #if (ASYS_TIMEOPTION == ASYS_TIMESYNC)
95 #include <time.h>
96 #endif
97 #endif
98
99 /******************************/
100 /* other constant definitions */
101 /******************************/
102
103 #ifndef ASYSIO_DSPDEV
104 #define ASYSIO_DSPDEV "/dev/dsp"
105 #endif
106
107 /* determines native audio format */
108
109 #if (BYTE_ORDER == BIG_ENDIAN)
110 # define ASYSIO_AFORMAT AFMT_S16_BE
111 #else
112 # if (BYTE_ORDER == LITTLE_ENDIAN)
113 # define ASYSIO_AFORMAT AFMT_S16_LE
114 # else
115 # error "BYTE_ORDER not defined?"
116 # endif
117 #endif
118
119 /* codes for IO types */
120
121 #define ASYSIO_I 0
122 #define ASYSIO_O 1
123 #define ASYSIO_IO 2
124
125 /* minimum fragment size */
126
127 #define ASYSIO_FRAGMIN 16
128 #define ASYSIO_LOGFRAGMIN 4
129
130 /* number of silence buffers */
131
132 #define ASYSO_LNUMBUFF 4
133
134 /* maximum number of I/O retries before termination */
135
136 #define ASYSIO_MAXRETRY 256
137
138 #if ASYSIO_USEFIFO /* SCHED_FIFO constants for ksync() */
139
140 #if (ASYS_TIMEOPTION == ASYS_TIMESYNC)
141 #define ASYSIO_SYNC_TIMEOUT 5 /* idle time to leave SCHED_FIFO */
142 #define ASYSIO_SYNC_ACTIVE 0 /* machine states for noteon timeout */
143 #define ASYSIO_SYNC_WAITING 1
144 #define ASYSIO_SYNC_SCHEDOTHER 2
145 #else
146 #define ASYSIO_MAXBLOCK ((int)EV(KRATE))*2 /* max wait tor let SCHED_OTHERs run */
147 #endif
148
149 #endif
150
151 /************************/
152 /* variable definitions */
153 /************************/
154
155 int asysio_fd; /* device pointer */
156 int asysio_srate; /* sampling rate */
157 int asysio_channels; /* number of channels */
158 int asysio_size; /* # samples in a buffer */
159 int asysio_bsize; /* actual # bytes in a buffer */
160 int asysio_requested_bsize; /* requested # bytes in a buffer */
161 int asysio_input; /* 1 if ASYSIO */
162 int asysio_blocktime; /* time (in bytes) blocked in kcycle */
163
164 struct count_info asysio_ptr; /* for GET{I,O}*PTR ioctl calls */
165 struct audio_buf_info asysio_info; /* for GET{I,O}SPACE ioctl calls */
166
167 #if defined(ASYS_HASOUTPUT)
168 short * asyso_buf = NULL; /* output buffer */
169 int asysio_puts; /* total number of putbufs */
170 int asysio_reset; /* flags an overrun */
171 #endif
172
173 #if defined(ASYS_HASINPUT)
174 short * asysi_buf = NULL; /* input buffer */
175 struct audio_buf_info asysi_info; /* input dma status */
176 #endif
177
178 sigset_t asysio_iloop_mask; /* for masking off iloop interrupt */
179 struct sigaction asysio_iloop_action; /* for setting up iloop interrupt */
180 struct itimerval asysio_iloop_timer; /* for setting up iloop timer */
181
182 #if ASYSIO_USEFIFO
183 int asysio_fifo; /* can get into sched_fifo mode */
184 struct sched_param asysio_fifoparam; /* param block for fifo mode */
185 struct sched_param asysio_otherparam; /* param block for other mode */
186
187 #if (ASYS_TIMEOPTION == ASYS_TIMESYNC)
188
189 /* state machine variables for noteon timeout */
190 int asysio_sync_state;
191 time_t asysio_sync_waitstart;
192 extern int csysi_newnote; /* from linmidi */
193
194 #else
195
196 /* state to detect long periods w/o blocking */
197 int asysio_sync_noblock; /* how many acycles since last block */
198 struct timespec asysio_sync_sleeptime; /* time to wait during forced block */
199
200 #endif
201
202 #endif
203
204 #if defined(ASYS_KSYNC) /* ksync() state */
205 struct count_info asysio_sync_ptr;
206 int asysio_sync_target, asysio_sync_incr;
207 float asysio_sync_cpuscale;
208 #endif
209
210 /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
211 /* shutdown routines */
212 /*______________________________________________________________*/
213
214
215 /****************************************************************/
216 /* shuts down soundcard */
217 /****************************************************************/
218
asysio_shutdown(void)219 void asysio_shutdown(void)
220
221 {
222 if (ioctl(asysio_fd, SNDCTL_DSP_SYNC, 0) == -1)
223 {
224 fprintf(stderr, "\nSoundcard Error: SNDCTL_DSP_SYNC Ioctl Problem\n");
225 fprintf(stderr, "Errno Message: %s\n\n", strerror(errno));
226 }
227 close(asysio_fd);
228
229 /* so that a slow exit doesn't trigger timer */
230
231 asysio_iloop_timer.it_value.tv_sec = 0;
232 asysio_iloop_timer.it_value.tv_usec = 0;
233 asysio_iloop_timer.it_interval.tv_sec = 0;
234 asysio_iloop_timer.it_interval.tv_usec = 0;
235
236 if (setitimer(ITIMER_PROF, &asysio_iloop_timer, NULL) < 0)
237 {
238 fprintf(stderr, "\nSoundcard Driver Error:\n\n");
239 fprintf(stderr, " Couldn't set up ITIMER_PROF timer.\n");
240 fprintf(stderr, " Errno Message: %s\n\n", strerror(errno));
241 }
242 }
243
244
245 #if (defined(ASYS_HASOUTPUT)&&(!defined(ASYS_HASINPUT)))
246
247 /****************************************************************/
248 /* shuts down audio output */
249 /****************************************************************/
250
asys_oshutdown(void)251 void asys_oshutdown(void)
252
253 {
254 asysio_shutdown();
255 if (asyso_buf != NULL)
256 free(asyso_buf);
257 }
258
259 #endif
260
261 #if (!defined(ASYS_HASOUTPUT)&&(defined(ASYS_HASINPUT)))
262
263 /****************************************************************/
264 /* shuts down audio input */
265 /****************************************************************/
266
asys_ishutdown(void)267 void asys_ishutdown(void)
268
269 {
270 asysio_shutdown();
271 if (asysi_buf != NULL)
272 free(asysi_buf);
273 }
274
275 #endif
276
277
278 #if (defined(ASYS_HASOUTPUT)&&(defined(ASYS_HASINPUT)))
279
280 /****************************************************************/
281 /* shuts down audio input and output */
282 /****************************************************************/
283
asys_ioshutdown(void)284 void asys_ioshutdown(void)
285
286 {
287 asysio_shutdown();
288 if (asyso_buf != NULL)
289 free(asyso_buf);
290 if (asysi_buf != NULL)
291 free(asysi_buf);
292 }
293
294 #endif
295
296
297 /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
298 /* initialization routines */
299 /*______________________________________________________________*/
300
301
302 /****************************************************************/
303 /* generic error-checking ioctl wrapper */
304 /****************************************************************/
305
306 #define ASYSIO_IOCTL_CALL(x,y,z) do { if (ioctl(x,y,z) == -1){\
307 fprintf(stderr, " Error: %s Ioctl Problem\n", #y ); \
308 fprintf(stderr, " Errno Message: %s\n\n", strerror(errno));\
309 close(asysio_fd); return ASYS_ERROR;}} while (0)
310
311 #define ASYSIO_ERROR_RETURN(x) do {\
312 fprintf(stderr, " Error: %s.\n", x);\
313 fprintf(stderr, " Errno Message: %s\n\n", strerror(errno));\
314 close(asysio_fd);\
315 return ASYS_ERROR; } while (0)
316
317 #define ASYSIO_ERROR_RETURN_NOERRNO(x) do {\
318 fprintf(stderr, " Error: %s.\n", x);\
319 close(asysio_fd);\
320 return ASYS_ERROR; } while (0)
321
322
323 /****************************************************************/
324 /* opens the soudcard device */
325 /****************************************************************/
326
asysio_opendevice(int dir,int toption)327 int asysio_opendevice(int dir, int toption)
328
329 {
330
331 switch(dir) {
332 case ASYSIO_I:
333 asysio_fd = open(ASYSIO_DSPDEV, O_RDONLY, 0);
334 asysio_input = 1;
335 break;
336 case ASYSIO_O:
337 asysio_fd = open(ASYSIO_DSPDEV, O_WRONLY, 0);
338 asysio_input = 0;
339 break;
340 case ASYSIO_IO:
341 asysio_fd = open(ASYSIO_DSPDEV, O_RDWR, 0);
342 asysio_input = 1;
343 break;
344 default:
345 fprintf(stderr, " Error: Unexpected dir parameter value in \n");
346 fprintf(stderr, " asysio_setup.\n\n");
347 return ASYS_ERROR;
348 }
349
350 if (asysio_fd == -1)
351 {
352 fprintf(stderr, " Error: Can't open device %s (%s)\n\n", ASYSIO_DSPDEV,
353 strerror(errno));
354 return ASYS_ERROR;
355 }
356 return ASYS_DONE;
357
358 }
359
360 /****************************************************************/
361 /* signal handler for trapping SAOL infinite loops */
362 /****************************************************************/
363
asysio_iloop_handler(int signum)364 void asysio_iloop_handler(int signum)
365 {
366 fprintf(stderr, " Error: Either\n\n");
367 fprintf(stderr, " [1] The SAOL program has an infinite loop in it, or\n");
368 fprintf(stderr, " [2] Content is too complex for real-time work.\n\n");
369 fprintf(stderr, " Exiting program ...\n\n");
370 exit(0);
371 }
372
373
374 /****************************************************************/
375 /* initializes iloop (heartbeat) interrupt */
376 /****************************************************************/
377
asysio_initiloop(void)378 int asysio_initiloop(void)
379
380 {
381
382 /*********************************************************/
383 /* set up signal handler for infinite-loop (iloop) timer */
384 /*********************************************************/
385
386 if (sigemptyset(&asysio_iloop_action.sa_mask) < 0)
387 ASYSIO_ERROR_RETURN("Couldn't run sigemptyset(iloop) OS call");
388
389 /* infinite-loop timer wins over midi overrun timer */
390
391 if (sigaddset(&asysio_iloop_action.sa_mask, SIGALRM) < 0)
392 ASYSIO_ERROR_RETURN("Couldn't run sigaddset(iloop) OS call");
393
394 asysio_iloop_action.sa_flags = SA_RESTART;
395 asysio_iloop_action.sa_handler = asysio_iloop_handler;
396
397 if (sigaction(SIGPROF, &asysio_iloop_action, NULL) < 0)
398 ASYSIO_ERROR_RETURN("Couldn't set up SIGPROF signal handler");
399
400
401 /************************/
402 /* set up timer and arm */
403 /************************/
404
405 asysio_iloop_timer.it_value.tv_sec = 3;
406 asysio_iloop_timer.it_value.tv_usec = 0;
407 asysio_iloop_timer.it_interval.tv_sec = 0;
408 asysio_iloop_timer.it_interval.tv_usec = 0;
409
410 if (setitimer(ITIMER_PROF, &asysio_iloop_timer, NULL) < 0)
411 ASYSIO_ERROR_RETURN("Couldn't set up ITIMER_PROF timer");
412
413 return ASYS_DONE;
414 }
415
416 /****************************************************************/
417 /* initializes sched_fifo */
418 /****************************************************************/
419
asysio_initscheduler(void)420 int asysio_initscheduler(void)
421
422 {
423
424 #if ASYSIO_USEFIFO
425
426 /*******************************/
427 /* set up sched_fifo variables */
428 /*******************************/
429
430 memset(&asysio_otherparam, 0, sizeof(struct sched_param));
431 memset(&asysio_fifoparam, 0, sizeof(struct sched_param));
432
433 if ((asysio_fifoparam.sched_priority =
434 sched_get_priority_max(SCHED_FIFO)) < 0)
435 ASYSIO_ERROR_RETURN("Couldn't get scheduling priority");
436
437 asysio_fifoparam.sched_priority--;
438
439 /********************************/
440 /* try to enter sched-fifo mode */
441 /********************************/
442
443 asysio_fifo = !sched_setscheduler(0, SCHED_FIFO, &asysio_fifoparam);
444
445 #endif
446
447 return ASYS_DONE;
448 }
449
450 /****************************************************************/
451 /* prints startup screen */
452 /****************************************************************/
453
asysio_screenwriter(void)454 int asysio_screenwriter(void)
455
456 {
457 int i, found;
458 int haslinmidi = 0;
459 float actual_latency;
460
461
462 fprintf(stderr, "%s ", (ASYS_LATENCYTYPE == ASYS_HIGHLATENCY)?
463 "Streaming" : "Interactive");
464
465 fprintf(stderr, "%s Audio ", (asysio_channels == 2) ? "Stereo" : "Mono");
466
467 #if defined(ASYS_HASOUTPUT)
468 fprintf(stderr, "%s", asysio_input ? "Input/Output" : "Output");
469 #else
470 fprintf(stderr, "Input");
471 #endif
472
473 found = i = 0;
474 while (i < csys_sfront_argc)
475 {
476 if (!(strcmp(csys_sfront_argv[i],"-bitc") &&
477 strcmp(csys_sfront_argv[i],"-bit") &&
478 strcmp(csys_sfront_argv[i],"-orc")))
479 {
480 i++;
481 fprintf(stderr, " for %s", csys_sfront_argv[i]);
482 found = 1;
483 break;
484 }
485 i++;
486 }
487 if (!found)
488 fprintf(stderr, " for UNKNOWN");
489
490
491 i = 0;
492 while (i < csys_sfront_argc)
493 {
494 if (!strcmp(csys_sfront_argv[i],"-cin"))
495 {
496 i++;
497 fprintf(stderr, " (-cin %s)", csys_sfront_argv[i]);
498 break;
499 }
500 i++;
501 }
502 fprintf(stderr, "\n\n");
503
504
505 #if defined(CSYS_CDRIVER_LINMIDI)
506
507 haslinmidi = 1;
508
509 #endif
510
511 #if (defined(CSYS_CDRIVER_LINMIDI) || defined(CSYS_CDRIVER_ALSAMIDI)\
512 || defined(CSYS_CDRIVER_ALSASEQ) || defined(CSYS_CDRIVER_ASCII))
513
514 /* list midi presets available */
515
516 fprintf(stderr,
517 "MIDI Preset Numbers (use MIDI controller to select):\n\n");
518
519 for (i = 0; i < CSYS_PRESETNUM; i++)
520 {
521 fprintf(stderr, "%3i. %s",
522 csys_presets[i].preset,
523 csys_instr[csys_presets[i].index].name);
524 if ((i&1))
525 fprintf(stderr, "\n");
526 else
527 {
528 fprintf(stderr, "\t\t");
529 if (i == (CSYS_PRESETNUM-1))
530 fprintf(stderr, "\n");
531 }
532 }
533 fprintf(stderr, "\n");
534
535 #endif
536
537 #if defined(CSYS_CDRIVER_ASCII)
538
539 fprintf(stderr,
540 "To play tunes on ASCII keyboard: a-z for notes, 0-9 for MIDI presets,\n");
541 fprintf(stderr,
542 "cntrl-C exits. If autorepeat interferes, exit and run 'xset -r' (in X).\n\n");
543
544 #endif
545
546 /* diagnose best flags to use, and if they are used */
547
548 #ifdef ASYS_HASOUTPUT
549
550 if ((ASYS_LATENCYTYPE == ASYS_HIGHLATENCY) || asysio_input ||
551 (!haslinmidi))
552 {
553 if (geteuid() || (ASYS_TIMEOPTION == ASYS_TIMESYNC))
554 {
555 fprintf(stderr, "For best results, make these changes:\n");
556 fprintf(stderr, "\n");
557 if (ASYS_TIMEOPTION == ASYS_TIMESYNC)
558 fprintf(stderr, " * Remove sfront -timesync flag\n");
559 if (geteuid())
560 fprintf(stderr, " * Run sa.c executable as root.\n");
561 fprintf(stderr, "\n");
562 }
563 }
564 else
565 {
566 fprintf(stderr, "This application runs best as root (%s), with:\n",
567 !geteuid() ? "which you are": "which you aren't");
568 fprintf(stderr, "\n");
569 fprintf(stderr, " [1] Sfront -playback flag. Good audio quality, keeps\n");
570 fprintf(stderr, " the mouse/kbd alive");
571 fprintf(stderr, "%s.\n", (ASYS_TIMEOPTION == ASYS_PLAYBACK) ?
572 " (currently chosen)":"");
573 fprintf(stderr, " [2] Sfront -timesync flag. Better quality, console\n");
574 fprintf(stderr, " freezes during MIDI input");
575 fprintf(stderr, "%s.\n", (ASYS_TIMEOPTION == ASYS_TIMESYNC) ?
576 " (currently chosen)":"");
577 fprintf(stderr, "\n");
578 }
579
580 #endif
581
582 /* latency information */
583
584 #if (defined(ASYS_HASOUTPUT))
585
586 ASYSIO_IOCTL_CALL(asysio_fd, SNDCTL_DSP_GETOSPACE, &asysio_info);
587
588 fprintf(stderr, "If audio artifacts still occur, try");
589
590 actual_latency = ATIME*ASYSO_LNUMBUFF*(asysio_size >> (asysio_channels - 1));
591
592 if (asysio_info.fragstotal < ASYSO_LNUMBUFF)
593 {
594 fprintf(stderr, " sfront -latency %f flag, and see\n",
595 0.5F*actual_latency);
596 }
597 else
598 {
599 fprintf(stderr, " sfront -latency %f flag, and see\n",
600 2.0F*actual_latency);
601
602 }
603 if (ASYS_LATENCYTYPE == ASYS_LOWLATENCY)
604 fprintf(stderr, "http://www.cs.berkeley.edu/"
605 "~lazzaro/sa/sfman/user/use/index.html#interact\n") ;
606 else
607 fprintf(stderr, "http://www.cs.berkeley.edu/"
608 "~lazzaro/sa/sfman/user/use/index.html#stream\n") ;
609
610 fprintf(stderr, "\n");
611
612 if ((asysio_bsize != ASYSIO_FRAGMIN) &&
613 (asysio_bsize == asysio_requested_bsize) &&
614 (ASYS_LATENCYTYPE == ASYS_LOWLATENCY))
615 {
616 fprintf(stderr, "If interactive response is slow, try ");
617 fprintf(stderr, "sfront -latency %f flag.\n", 0.5F*actual_latency);
618 fprintf(stderr, "\n");
619 }
620
621 #endif
622
623 fprintf(stderr,
624 "USE AT YOUR OWN RISK. Running as root may damage your file system,\n");
625 fprintf(stderr,
626 "and network use may result in a malicious attack on your machine.\n\n");
627
628 #if (ASYSIO_USEFIFO && (ASYS_TIMEOPTION == ASYS_TIMESYNC))
629
630 if (!geteuid())
631 {
632 fprintf(stderr,
633 "NOTE: Mouse and keyboard are frozen for %i seconds after a MIDI\n",
634 ASYSIO_SYNC_TIMEOUT);
635 fprintf(stderr,
636 "NoteOn or NoteOff is received. Do not be alarmed.\n");
637 }
638
639 #endif
640
641 if (NSYS_NET)
642 fprintf(stderr, "Network status: Contacting SIP server\n");
643
644 return ASYS_DONE;
645
646 }
647
648
649 /****************************************************************/
650 /* setup operations common to input and output */
651 /****************************************************************/
652
asysio_setup(int srate,int channels,int dir,int toption)653 int asysio_setup(int srate, int channels, int dir, int toption)
654
655 {
656 int i, j, maxfrag;
657
658 /******************/
659 /* open soundcard */
660 /******************/
661
662 if (asysio_opendevice(dir, toption) == ASYS_ERROR)
663 return ASYS_ERROR;
664
665 /**************************************/
666 /* set up bidirectional I/O if needed */
667 /**************************************/
668
669 if (dir == ASYSIO_IO)
670 {
671
672 #if (ASYSIO_OSTYPE != ASYSIO_FREEBSD)
673
674 ASYSIO_IOCTL_CALL(asysio_fd, SNDCTL_DSP_SETDUPLEX, 0);
675
676 #endif
677
678 ASYSIO_IOCTL_CALL(asysio_fd, SNDCTL_DSP_GETCAPS, &j);
679
680 if (!(j & DSP_CAP_DUPLEX))
681 ASYSIO_ERROR_RETURN_NOERRNO("Sound card can't do bidirectional audio");
682 }
683
684 /************************/
685 /* range check channels */
686 /************************/
687
688 if (channels > 2)
689 ASYSIO_ERROR_RETURN_NOERRNO("Sound card can't handle > 2 channels");
690
691 /*********************/
692 /* set fragment size */
693 /*********************/
694
695 j = ASYSIO_LOGFRAGMIN;
696 i = ASYSIO_FRAGMIN >> channels; /* only works for channels = 1, 2 */
697
698 /* find closest power-of-two fragment size to latency request */
699
700 while (2*ATIME*i*ASYSO_LNUMBUFF < ASYS_LATENCY)
701 {
702 i <<= 1;
703 j++;
704 }
705 if ((ATIME*2*i*ASYSO_LNUMBUFF - ASYS_LATENCY) <
706 (ASYS_LATENCY - ATIME*i*ASYSO_LNUMBUFF))
707 {
708 i <<= 1;
709 j++;
710 }
711
712 asysio_requested_bsize = 2*i*channels;
713
714 maxfrag = (ASYS_TIMEOPTION != ASYS_TIMESYNC) ? ASYSO_LNUMBUFF :
715 ASYSO_LNUMBUFF + ((ACYCLE/i) + 1);
716
717 j |= (maxfrag << 16);
718 ASYSIO_IOCTL_CALL(asysio_fd, SNDCTL_DSP_SETFRAGMENT, &j);
719
720 /********************/
721 /* set audio format */
722 /********************/
723
724 j = ASYSIO_AFORMAT;
725 ASYSIO_IOCTL_CALL(asysio_fd, SNDCTL_DSP_SETFMT, &j);
726
727 if (j != ASYSIO_AFORMAT)
728 ASYSIO_ERROR_RETURN_NOERRNO("Soundcard can't handle native shorts");
729
730 /****************************************************/
731 /* set number of channels -- later add channels > 2 */
732 /****************************************************/
733
734 asysio_channels = channels--;
735 ASYSIO_IOCTL_CALL(asysio_fd, SNDCTL_DSP_STEREO, &channels);
736
737 if (channels != (asysio_channels-1))
738 ASYSIO_ERROR_RETURN_NOERRNO("Soundcard can't handle number of channels");
739
740 /*********************/
741 /* set sampling rate */
742 /*********************/
743
744 asysio_srate = srate;
745 ASYSIO_IOCTL_CALL(asysio_fd, SNDCTL_DSP_SPEED, &srate);
746
747 if (abs(asysio_srate - srate) > 1000)
748 ASYSIO_ERROR_RETURN_NOERRNO("Soundcard can't handle sampling rate");
749
750 /******************************/
751 /* compute actual buffer size */
752 /******************************/
753
754 ASYSIO_IOCTL_CALL(asysio_fd, SNDCTL_DSP_GETBLKSIZE, &asysio_bsize);
755 asysio_size = asysio_bsize >> 1;
756
757 /*************************/
758 /* print out info screen */
759 /*************************/
760
761 if (asysio_screenwriter() == ASYS_ERROR)
762 return ASYS_ERROR;
763
764 /*********************************/
765 /* set SCHED_FIFO if appropriate */
766 /*********************************/
767
768 if (asysio_initscheduler() == ASYS_ERROR)
769 return ASYS_ERROR;
770
771 /**********************************/
772 /* set up iloop (heartbeat) timer */
773 /**********************************/
774
775 if (asysio_initiloop() == ASYS_ERROR)
776 return ASYS_ERROR;
777
778 return ASYS_DONE;
779 }
780
781
782
783 #if (defined(ASYS_HASOUTPUT) && !defined(ASYS_HASINPUT))
784
785 /****************************************************************/
786 /* sets up audio output for a given srate/channels */
787 /****************************************************************/
788
asys_osetup(int srate,int ochannels,int osample,char * oname,int toption)789 int asys_osetup(int srate, int ochannels, int osample,
790 char * oname, int toption)
791
792 {
793 if (asysio_setup(srate, ochannels, ASYSIO_O, toption) == ASYS_ERROR)
794 return ASYS_ERROR;
795
796 if (!(asyso_buf = (short *)calloc(asysio_size, sizeof(short))))
797 ASYSIO_ERROR_RETURN("Can't allocate output buffer");
798
799 return ASYS_DONE;
800 }
801
802 #endif
803
804
805 #if (!defined(ASYS_HASOUTPUT) && defined(ASYS_HASINPUT))
806
807 /****************************************************************/
808 /* sets up audio input for a given srate/channels */
809 /****************************************************************/
810
asys_isetup(int srate,int ichannels,int isample,char * iname,int toption)811 int asys_isetup(int srate, int ichannels, int isample,
812 char * iname, int toption)
813
814 {
815 if (asysio_setup(srate, ichannels, ASYSIO_I, toption) == ASYS_ERROR)
816 return ASYS_ERROR;
817 if (!(asysi_buf = (short *)malloc(asysio_bsize)))
818 ASYSIO_ERROR_RETURN("Can't allocate input buffer");
819
820 return ASYS_DONE;
821 }
822
823 #endif
824
825
826 #if (defined(ASYS_HASOUTPUT) && defined(ASYS_HASINPUT))
827
828 /****************************************************************/
829 /* sets up audio input and output for a given srate/channels */
830 /****************************************************************/
831
asys_iosetup(int srate,int ichannels,int ochannels,int isample,int osample,char * iname,char * oname,int toption)832 int asys_iosetup(int srate, int ichannels, int ochannels,
833 int isample, int osample,
834 char * iname, char * oname, int toption)
835
836
837 {
838
839 if (ichannels != ochannels)
840 ASYSIO_ERROR_RETURN_NOERRNO
841 ("Soundcard needs SAOL inchannels == outchannels");
842
843 if (asysio_setup(srate, ichannels, ASYSIO_IO, toption) == ASYS_ERROR)
844 return ASYS_ERROR;
845
846 if (!(asysi_buf = (short *)malloc(asysio_bsize)))
847 ASYSIO_ERROR_RETURN("Can't allocate input buffer");
848
849 if (!(asyso_buf = (short *)calloc(asysio_size, sizeof(short))))
850 ASYSIO_ERROR_RETURN("Can't allocate output buffer");
851
852 return ASYS_DONE;
853 }
854
855 #endif
856
857
858 /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
859 /* input, output, and recovery routines */
860 /*______________________________________________________________*/
861
862
863 #if defined(ASYS_HASINPUT)
864
865 /****************************************************************/
866 /* gets one frame of audio from input */
867 /****************************************************************/
868
asys_getbuf(ASYS_ITYPE * asys_ibuf[],int * isize)869 int asys_getbuf(ASYS_ITYPE * asys_ibuf[], int * isize)
870
871 {
872 int diffcompute, starttime;
873 int size, recv, bptr, retry;
874
875 *isize = asysio_size;
876
877 if (*asys_ibuf == NULL)
878 *asys_ibuf = asysi_buf;
879
880 ASYSIO_IOCTL_CALL(asysio_fd, SNDCTL_DSP_GETISPACE, &asysio_info);
881
882 #if defined(ASYS_HASOUTPUT)
883
884 if (diffcompute = (asysio_info.bytes < asysio_bsize))
885 {
886 ASYSIO_IOCTL_CALL(asysio_fd, SNDCTL_DSP_GETOPTR, &asysio_ptr);
887 starttime = asysio_ptr.bytes;
888 }
889
890 #endif
891
892 retry = bptr = 0;
893 size = asysio_bsize;
894
895 while ((recv = read(asysio_fd, &((*asys_ibuf)[bptr]), size)) != size)
896 {
897 if (++retry > ASYSIO_MAXRETRY)
898 ASYSIO_ERROR_RETURN("Too many I/O retries -- asys_getbuf");
899
900 if (recv < 0) /* errors */
901 {
902 if ((errno == EAGAIN) || (errno == EINTR))
903 continue;
904 else
905 ASYSIO_ERROR_RETURN("Read error on output audio device");
906 }
907 else
908 {
909 bptr += recv; /* partial read */
910 size -= recv;
911 }
912 }
913
914 #if defined(ASYS_HASOUTPUT)
915
916 if (diffcompute)
917 {
918 ASYSIO_IOCTL_CALL(asysio_fd, SNDCTL_DSP_GETOPTR, &asysio_ptr);
919 asysio_blocktime += (asysio_ptr.bytes - starttime);
920 }
921
922 #endif
923
924 return ASYS_DONE;
925 }
926
927 #endif
928
929
930 #if defined(ASYS_HASOUTPUT)
931
932 /****************************************************************/
933 /* sends one frame of audio to output */
934 /****************************************************************/
935
asys_putbuf(ASYS_OTYPE * asys_obuf[],int * osize)936 int asys_putbuf(ASYS_OTYPE * asys_obuf[], int * osize)
937
938
939 {
940 int size, sent, bptr, retry;
941 int diffcompute, starttime;
942
943 size = (*osize)*2;
944
945
946 if (asysio_reset)
947 return ASYS_DONE;
948
949 ASYSIO_IOCTL_CALL(asysio_fd, SNDCTL_DSP_GETOSPACE, &asysio_info);
950
951 asysio_reset = (++asysio_puts > ASYSO_LNUMBUFF) &&
952 (asysio_info.fragments == asysio_info.fragstotal);
953 if (asysio_reset)
954 return ASYS_DONE;
955
956 #if (ASYS_TIMEOPTION != ASYS_TIMESYNC)
957
958 if (diffcompute = (asysio_info.bytes < size))
959 {
960 ASYSIO_IOCTL_CALL(asysio_fd, SNDCTL_DSP_GETOPTR, &asysio_ptr);
961 starttime = asysio_ptr.bytes;
962 }
963
964 #endif
965
966 retry = bptr = 0;
967 while ((sent = write(asysio_fd, &((*asys_obuf)[bptr]), size)) != size)
968 {
969 if (++retry > ASYSIO_MAXRETRY)
970 ASYSIO_ERROR_RETURN("Too many I/O retries -- asys_putbuf");
971
972 if (sent < 0) /* errors */
973 {
974 if ((errno == EAGAIN) || (errno == EINTR))
975 continue;
976 else
977 ASYSIO_ERROR_RETURN("Write error on output audio device");
978 }
979 else
980 {
981 bptr += sent; /* partial write */
982 size -= sent;
983 }
984 }
985
986 #if (ASYS_TIMEOPTION != ASYS_TIMESYNC)
987
988 if (diffcompute)
989 {
990 ASYSIO_IOCTL_CALL(asysio_fd, SNDCTL_DSP_GETOPTR, &asysio_ptr);
991 asysio_blocktime += (asysio_ptr.bytes - starttime);
992 }
993
994 #endif
995
996 *osize = asysio_size;
997 return ASYS_DONE;
998 }
999
1000
1001 /****************************************************************/
1002 /* creates buffer, and generates starting silence */
1003 /****************************************************************/
1004
asys_preamble(ASYS_OTYPE * asys_obuf[],int * osize)1005 int asys_preamble(ASYS_OTYPE * asys_obuf[], int * osize)
1006
1007 {
1008 int i;
1009
1010 *asys_obuf = asyso_buf;
1011 *osize = asysio_size;
1012
1013 for(i = 0; i < ASYSO_LNUMBUFF; i++)
1014 if (asys_putbuf(asys_obuf, osize) == ASYS_ERROR)
1015 return ASYS_ERROR;
1016
1017 return ASYS_DONE;
1018 }
1019
1020
1021 /****************************************************************/
1022 /* recovers from an overrun */
1023 /****************************************************************/
1024
asysio_recover(void)1025 int asysio_recover(void)
1026
1027 {
1028 int size, recv, bptr, retry;
1029 int i;
1030
1031 asysio_reset = 0;
1032
1033 memset(asyso_buf, 0, asysio_bsize);
1034
1035 /*************************/
1036 /* flush input if needed */
1037 /*************************/
1038
1039 #if defined(ASYS_HASINPUT)
1040
1041 ASYSIO_IOCTL_CALL(asysio_fd, SNDCTL_DSP_GETISPACE, &asysi_info);
1042
1043 while (asysi_info.fragments > 0)
1044 {
1045 retry = bptr = 0;
1046 size = asysio_bsize;
1047
1048 while ((recv = read(asysio_fd, &(asysi_buf[bptr]), size)) != size)
1049 {
1050 if (++retry > ASYSIO_MAXRETRY)
1051 ASYSIO_ERROR_RETURN("Too many I/O retries -- asysio_recover");
1052
1053 if (recv < 0) /* errors */
1054 {
1055 if ((errno == EAGAIN) || (errno == EINTR))
1056 continue;
1057 else
1058 ASYSIO_ERROR_RETURN("Read error on output audio device");
1059 }
1060 else
1061 {
1062 bptr += recv; /* partial read */
1063 size -= recv;
1064 }
1065 }
1066
1067 ASYSIO_IOCTL_CALL(asysio_fd, SNDCTL_DSP_GETISPACE, &asysi_info);
1068 }
1069
1070 ibusidx = 0;
1071 if (asys_getbuf(&asys_ibuf, &EV(asys_isize))==ASYS_ERROR)
1072 return ASYS_ERROR;
1073
1074 #endif
1075
1076 /**************************************/
1077 /* fill latency interval with silence */
1078 /**************************************/
1079
1080 asysio_puts = 0;
1081 for(i = 0; i < ASYSO_LNUMBUFF; i++)
1082 if (asys_putbuf(&asyso_buf, &asysio_size) == ASYS_ERROR)
1083 return ASYS_ERROR;
1084
1085 return ASYS_DONE;
1086
1087 }
1088
1089
1090 #endif
1091
1092
1093 /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
1094 /* ksync() system for time synchronization */
1095 /*_________________________________________________________*/
1096
1097 #if defined(ASYS_KSYNC)
1098
1099 /***********************************************************/
1100 /* initializes k-rate boundaries sync */
1101 /***********************************************************/
1102
ksyncinit()1103 void ksyncinit()
1104
1105 {
1106 asysio_sync_target = asysio_sync_incr = ACYCLE*asysio_channels*2;
1107 asysio_sync_cpuscale = 1.0F/asysio_sync_incr;
1108
1109 /* for -timesync, set up SCHED_FIFO watchdog state machine */
1110
1111 #if (ASYSIO_USEFIFO && (ASYS_TIMEOPTION == ASYS_TIMESYNC))
1112
1113 if (asysio_fifo)
1114
1115 {
1116 asysio_sync_state = ASYSIO_SYNC_SCHEDOTHER;
1117 if (sched_setscheduler(0, SCHED_OTHER, &asysio_otherparam))
1118 epr(0,NULL,NULL,"internal error -- sched_other unavailable");
1119 }
1120
1121 #endif
1122
1123 /* elsewise, set up SCHED_FIFO monitor to force blocking */
1124
1125 #if (ASYSIO_USEFIFO && (ASYS_TIMEOPTION != ASYS_TIMESYNC))
1126
1127 asysio_sync_noblock = 0;
1128 asysio_sync_sleeptime.tv_sec = 0;
1129 asysio_sync_sleeptime.tv_nsec = 2000001; /* 2ms + epsilon forces block */
1130
1131 #endif
1132
1133 }
1134
1135
1136 /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
1137 /* different ksync()s for -timesync and -playback */
1138 /*__________________________________________________________*/
1139
1140
1141 #if (ASYS_TIMEOPTION != ASYS_TIMESYNC)
1142
1143 /***********************************************************/
1144 /* synchronizes on k-rate boundaries */
1145 /***********************************************************/
1146
ksync()1147 float ksync()
1148
1149 {
1150 float ret;
1151 int comptime;
1152
1153 if (asysio_reset)
1154 {
1155 if (asysio_recover()==ASYS_ERROR)
1156 epr(0,NULL,NULL, "Soundcard error -- failed recovery.");
1157 asysio_sync_target = asysio_sync_incr;
1158 ret = 1.0F;
1159 }
1160 else
1161 {
1162
1163 ASYSIO_IOCTL_CALL(asysio_fd, SNDCTL_DSP_GETOPTR, &asysio_sync_ptr);
1164
1165 if (asysio_sync_target == asysio_sync_incr)
1166 ret = 0.0F;
1167 else
1168 {
1169 comptime = asysio_sync_ptr.bytes - asysio_blocktime;
1170 if (comptime > asysio_sync_target)
1171 ret = 1.0F;
1172 else
1173 ret = (asysio_sync_cpuscale*
1174 (asysio_sync_incr - (asysio_sync_target - comptime)));
1175 }
1176
1177 if ((asysio_sync_target = asysio_sync_incr + asysio_sync_ptr.bytes) < 0)
1178 epr(0,NULL,NULL,"Soundcard error -- rollover.");
1179 }
1180
1181 /* reset infinite-loop timer */
1182
1183 if (setitimer(ITIMER_PROF, &asysio_iloop_timer, NULL) < 0)
1184 {
1185 fprintf(stderr, " Runtime Errno Message: %s\n", strerror(errno));
1186 epr(0,NULL,NULL, "Soundcard error -- Couldn't reset ITIMER_PROF");
1187 }
1188
1189 #if ASYSIO_USEFIFO
1190
1191 if (asysio_fifo)
1192 {
1193 /* let other processes run if pending too long */
1194
1195 if (asysio_blocktime)
1196 asysio_sync_noblock = 0;
1197 else
1198 asysio_sync_noblock++;
1199
1200 if (asysio_sync_noblock > ASYSIO_MAXBLOCK)
1201 {
1202 nanosleep(&asysio_sync_sleeptime, NULL);
1203 asysio_sync_noblock = 0;
1204 }
1205 }
1206
1207 #endif
1208
1209 asysio_blocktime = 0;
1210 return ret;
1211 }
1212
1213 #endif
1214
1215
1216 #if (ASYS_TIMEOPTION == ASYS_TIMESYNC)
1217
1218 /***********************************************************/
1219 /* synchronizes on k-rate boundaries */
1220 /***********************************************************/
1221
ksync()1222 float ksync()
1223
1224 {
1225 float ret;
1226 int comptime;
1227
1228 if (!asysio_reset)
1229 {
1230 ASYSIO_IOCTL_CALL(asysio_fd, SNDCTL_DSP_GETOPTR, &asysio_sync_ptr);
1231 if (asysio_sync_ptr.bytes > asysio_sync_target)
1232 {
1233 comptime = asysio_sync_ptr.bytes - asysio_blocktime;
1234 if (comptime < asysio_sync_target)
1235 ret = (asysio_sync_cpuscale*
1236 (asysio_sync_incr - (asysio_sync_target - comptime)));
1237 else
1238 ret = 1.0F;
1239 ret = (asysio_sync_target != asysio_sync_incr) ? ret : 0.0F;
1240 }
1241 else
1242 {
1243 comptime = asysio_sync_ptr.bytes - asysio_blocktime;
1244 ret = (asysio_sync_cpuscale*
1245 (asysio_sync_incr - (asysio_sync_target - comptime)));
1246 asysio_reset = asysio_input &&
1247 ((asysio_sync_target-asysio_sync_ptr.bytes) == asysio_sync_incr);
1248 while ((asysio_sync_ptr.bytes < asysio_sync_target) && !asysio_reset)
1249 {
1250 ASYSIO_IOCTL_CALL(asysio_fd, SNDCTL_DSP_GETOSPACE, &asysio_info);
1251 asysio_reset = (asysio_info.fragments == asysio_info.fragstotal);
1252 ASYSIO_IOCTL_CALL(asysio_fd, SNDCTL_DSP_GETOPTR,&asysio_sync_ptr);
1253 }
1254 }
1255 }
1256 if (asysio_reset)
1257 {
1258 if (asysio_recover()==ASYS_ERROR)
1259 epr(0,NULL,NULL,"Sound driver error -- failed recovery.");
1260 ASYSIO_IOCTL_CALL(asysio_fd, SNDCTL_DSP_GETOPTR, &asysio_sync_ptr);
1261 asysio_sync_target = asysio_sync_ptr.bytes;
1262 ret = 1.0F;
1263 }
1264 if ((asysio_sync_target += asysio_sync_incr) < 0)
1265 epr(0,NULL,NULL,"Sound driver error -- rollover.");
1266
1267 /* reset infinite-loop timer */
1268
1269 if (setitimer(ITIMER_PROF, &asysio_iloop_timer, NULL) < 0)
1270 {
1271 fprintf(stderr, " Runtime Errno Message: %s\n", strerror(errno));
1272 epr(0,NULL,NULL, "Soundcard error -- Couldn't reset ITIMER_PROF");
1273 }
1274
1275 #if ASYSIO_USEFIFO
1276
1277 if (asysio_fifo)
1278 {
1279 switch (asysio_sync_state) {
1280 case ASYSIO_SYNC_ACTIVE:
1281 if (!csysi_newnote)
1282 {
1283 asysio_sync_state = ASYSIO_SYNC_WAITING;
1284 asysio_sync_waitstart = time(NULL);
1285 }
1286 break;
1287 case ASYSIO_SYNC_WAITING:
1288 if (csysi_newnote)
1289 asysio_sync_state = ASYSIO_SYNC_ACTIVE;
1290 else
1291 if ((time(NULL) - asysio_sync_waitstart) >= ASYSIO_SYNC_TIMEOUT)
1292 {
1293 asysio_sync_state = ASYSIO_SYNC_SCHEDOTHER;
1294 if (sched_setscheduler(0, SCHED_OTHER, &asysio_otherparam))
1295 epr(0,NULL,NULL,"internal error -- sched_other unavailable");
1296 }
1297 break;
1298 case ASYSIO_SYNC_SCHEDOTHER:
1299 if (csysi_newnote)
1300 {
1301 asysio_sync_state = ASYSIO_SYNC_ACTIVE;
1302 if (sched_setscheduler(0, SCHED_FIFO, &asysio_fifoparam))
1303 fprintf(stderr, " Note: Process no longer root, "
1304 "improved audio quality no longer possible.\n");
1305 }
1306 break;
1307 }
1308 }
1309
1310 #endif
1311
1312 asysio_blocktime = 0;
1313 return ret;
1314 }
1315
1316 #endif
1317
1318 #endif /* ASYS_KSYNC */
1319
1320 #undef ASYSIO_IOCTL_CALL
1321 #undef ASYSIO_ERROR_RETURN
1322 #undef ASYSIO_ERROR_RETURN_NOERRNO
1323 #undef ASYSIO_LINUX
1324 #undef ASYSIO_FREEBSD
1325 #undef ASYSIO_OSTYPE
1326 #undef ASYSIO_DSPDEV
1327 #undef ASYSIO_AFORMAT
1328 #undef ASYSIO_I
1329 #undef ASYSIO_O
1330 #undef ASYSIO_IO
1331 #undef ASYSIO_FRAGMIN
1332 #undef ASYSIO_LOGFRAGMIN
1333 #undef ASYSO_LNUMBUFF
1334 #undef ASYSIO_MAXRETRY
1335
1336 /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
1337 /* end of soundcard driver */
1338 /*______________________________________________________________*/
1339
1340
1341