1 /*
2  * Copyright (c) 2008 Alexandre Ratchov <alex at 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 <string.h>
18 #include <sndio.h>
19 #include <ao/ao.h>
20 #include <ao/plugin.h>
21 
22 static char *ao_sndio_options[] = {
23   "verbose",
24   "quiet",
25   "matrix",
26   "debug",
27   "dev",
28   "id"
29 };
30 
31 ao_info ao_sndio_info = {
32   AO_TYPE_LIVE,
33   "sndio audio output",
34   "sndio",
35   "Alexandre Ratchov <alex at caoua.org>",
36   "Outputs to the sndio library",
37   AO_FMT_NATIVE,
38   30,
39   ao_sndio_options,
40   sizeof(ao_sndio_options)/sizeof(*ao_sndio_options)
41 };
42 
43 typedef struct ao_sndio_internal
44 {
45   struct sio_hdl *hdl;
46   char *dev;
47   int id;
48 } ao_sndio_internal;
49 
ao_plugin_test()50 int ao_plugin_test()
51 {
52   struct sio_hdl *hdl;
53 
54   hdl = sio_open(NULL, SIO_PLAY, 0);
55   if (hdl == NULL)
56     return 0;
57   sio_close(hdl);
58   return 1;
59 }
60 
ao_plugin_driver_info(void)61 ao_info *ao_plugin_driver_info(void)
62 {
63   return &ao_sndio_info;
64 }
65 
ao_plugin_device_init(ao_device * device)66 int ao_plugin_device_init(ao_device *device)
67 {
68   ao_sndio_internal *internal;
69   internal = (ao_sndio_internal *) calloc(1,sizeof(*internal));
70   internal->id=-1;
71   device->internal = internal;
72   device->output_matrix_order = AO_OUTPUT_MATRIX_FIXED;
73   return 1;
74 }
75 
ao_plugin_set_option(ao_device * device,const char * key,const char * value)76 int ao_plugin_set_option(ao_device *device, const char *key, const char *value)
77 {
78   ao_sndio_internal *internal = (ao_sndio_internal *) device->internal;
79 
80   if (!strcmp(key, "dev")) {
81     if (internal->dev)
82       free (internal->dev);
83     if(!value){
84       internal->dev=NULL;
85     }else{
86       if (!(internal->dev = strdup(value)))
87         return 0;
88     }
89   }
90   if (!strcmp(key, "id")) {
91     if(internal->dev)
92       free (internal->dev);
93     internal->dev=NULL;
94     internal->id=atoi(value);
95   }
96   return 1;
97 }
98 
ao_plugin_open(ao_device * device,ao_sample_format * format)99 int ao_plugin_open(ao_device *device, ao_sample_format *format)
100 {
101   ao_sndio_internal *internal = (ao_sndio_internal *) device->internal;
102   struct sio_hdl *hdl;
103   struct sio_par par;
104 
105   if(!internal->dev && internal->id>=0){
106     char buf[80];
107     sprintf(buf,"sun:%d",internal->id);
108     internal->dev = strdup(buf);
109   }
110 
111   hdl = sio_open(internal->dev, SIO_PLAY, 0);
112   if (hdl == NULL)
113     return 0;
114   internal->hdl = hdl;
115 
116   sio_initpar(&par);
117   par.sig = 1;
118   if (format->bits > 8)
119     par.le = device->client_byte_format == AO_FMT_LITTLE ? 1 : 0;
120   par.bits = format->bits;
121   par.rate = format->rate;
122   par.pchan = device->output_channels;
123   if (!sio_setpar(hdl, &par))
124     return 0;
125   if (!sio_getpar(hdl, &par))
126     return 0;
127   if (par.bits != format->bits)
128     return 0;
129   device->driver_byte_format = par.le ? AO_FMT_LITTLE : AO_FMT_BIG;
130   if (!sio_start(hdl))
131     return 0;
132 
133   if(!device->inter_matrix){
134     /* set up matrix such that users are warned about > stereo playback */
135     if(device->output_channels<=2)
136       device->inter_matrix=strdup("L,R");
137     //else no matrix, which results in a warning
138   }
139 
140   return 1;
141 }
142 
ao_plugin_play(ao_device * device,const char * output_samples,uint_32 num_bytes)143 int ao_plugin_play(ao_device *device, const char *output_samples, uint_32 num_bytes)
144 {
145   ao_sndio_internal *internal = (ao_sndio_internal *) device->internal;
146   struct sio_hdl *hdl = internal->hdl;
147 
148   if (!sio_write(hdl, output_samples, num_bytes))
149     return 0;
150   return 1;
151 }
152 
ao_plugin_close(ao_device * device)153 int ao_plugin_close(ao_device *device)
154 {
155   ao_sndio_internal *internal = (ao_sndio_internal *) device->internal;
156   struct sio_hdl *hdl = internal->hdl;
157 
158   if(hdl){
159     sio_close(hdl);
160     internal->hdl = NULL;
161   }
162   return 1;
163 }
164 
ao_plugin_device_clear(ao_device * device)165 void ao_plugin_device_clear(ao_device *device)
166 {
167   ao_sndio_internal *internal = (ao_sndio_internal *) device->internal;
168   struct sio_hdl *hdl = internal->hdl;
169 
170   if(hdl)
171     sio_close(hdl);
172   if(internal->dev)
173     free(internal->dev);
174   free(internal);
175   device->internal=NULL;
176 }
177