1 /*
2  * MOC - music on console
3  *
4  * SNDIO sound driver for MOC by Alexander Polakov.
5  * Copyright (C) 2011 Alexander Polakov <polachok@gmail.com>
6  *
7  * This program is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 2 of the License, or
10  * (at your option) any later version.
11  *
12  */
13 
14 #ifdef HAVE_CONFIG_H
15 # include "config.h"
16 #endif
17 
18 #ifdef HAVE_SNDIO_H
19 # include <sndio.h>
20 #endif
21 
22 #include <assert.h>
23 
24 #include "common.h"
25 #include "audio.h"
26 #include "log.h"
27 
28 #define PCT_TO_SIO(pct)	((127 * (pct) + 50) / 100)
29 #define SIO_TO_PCT(vol)	((100 * (vol) + 64) / 127)
30 
31 static struct sio_hdl *hdl = NULL;
32 static int curvol = 100;
33 static struct sound_params params = { 0, 0, 0 };
34 
35 static void sndio_close ();
36 
volume_cb(void * addr ATTR_UNUSED,unsigned vol)37 static void volume_cb (void *addr ATTR_UNUSED, unsigned vol)
38 {
39 	curvol = SIO_TO_PCT(vol);
40 }
41 
sndio_init(struct output_driver_caps * caps)42 static int sndio_init (struct output_driver_caps *caps)
43 {
44 	assert (caps != NULL);
45 
46 	caps->formats = SFMT_S8 | SFMT_U8 | SFMT_U16 | SFMT_S16 | SFMT_NE;
47 	caps->min_channels = 1;
48 	caps->max_channels = 2;
49 
50 	return 1;
51 }
52 
sndio_shutdown()53 static void sndio_shutdown ()
54 {
55 	if (hdl)
56 		sndio_close ();
57 }
58 
59 /* Return 0 on failure. */
sndio_open(struct sound_params * sound_params)60 static int sndio_open (struct sound_params *sound_params)
61 {
62 	struct sio_par par;
63 
64 	assert (hdl == NULL);
65 
66 	if ((hdl = sio_open (NULL, SIO_PLAY, 0)) == NULL)
67 		return 0;
68 
69 	params = *sound_params;
70 	sio_initpar (&par);
71 	/* Add volume change callback. */
72 	sio_onvol (hdl, volume_cb, NULL);
73 	par.rate = sound_params->rate;
74 	par.pchan = sound_params->channels;
75 	par.bits = (((sound_params->fmt & SFMT_S8) ||
76 	             (sound_params->fmt & SFMT_U8)) ? 8 : 16);
77 	par.le = SIO_LE_NATIVE;
78 	par.sig = (((sound_params->fmt & SFMT_S16) ||
79 	            (sound_params->fmt & SFMT_S8)) ? 1 : 0);
80 	par.round = par.rate / 8;
81 	par.appbufsz = par.round * 2;
82 	logit ("rate %d pchan %d bits %d sign %d",
83 	        par.rate, par.pchan, par.bits, par.sig);
84 
85 	if (!sio_setpar (hdl, &par) || !sio_getpar (hdl, &par)
86 	                            || !sio_start (hdl)) {
87 		logit ("Failed to set sndio parameters.");
88 		sio_close (hdl);
89 		hdl = NULL;
90 		return 0;
91 	}
92 	sio_setvol (hdl, PCT_TO_SIO(curvol));
93 
94 	return 1;
95 }
96 
97 /* Return the number of bytes played, or -1 on error. */
sndio_play(const char * buff,const size_t size)98 static int sndio_play (const char *buff, const size_t size)
99 {
100 	int count;
101 
102 	assert (hdl != NULL);
103 
104 	count = (int) sio_write (hdl, buff, size);
105 	if (!count && sio_eof (hdl))
106 		return -1;
107 
108 	return count;
109 }
110 
sndio_close()111 static void sndio_close ()
112 {
113 	assert (hdl != NULL);
114 
115 	sio_stop (hdl);
116 	sio_close (hdl);
117 	hdl = NULL;
118 }
119 
sndio_read_mixer()120 static int sndio_read_mixer ()
121 {
122 	return curvol;
123 }
124 
sndio_set_mixer(int vol)125 static void sndio_set_mixer (int vol)
126 {
127 	if (hdl != NULL)
128 		sio_setvol (hdl, PCT_TO_SIO (vol));
129 }
130 
sndio_get_buff_fill()131 static int sndio_get_buff_fill ()
132 {
133 	assert (hdl != NULL);
134 
135 	/* Since we cannot stop SNDIO playing the samples already in
136 	 * its buffer, there will never be anything left unheard. */
137 
138 	return 0;
139 }
140 
sndio_reset()141 static int sndio_reset ()
142 {
143 	assert (hdl != NULL);
144 
145 	/* SNDIO will continue to play the samples already in its buffer
146 	 * regardless of what we do, so there's nothing we can do. */
147 
148 	return 1;
149 }
150 
sndio_get_rate()151 static int sndio_get_rate ()
152 {
153 	assert (hdl != NULL);
154 
155 	return params.rate;
156 }
157 
sndio_toggle_mixer_channel()158 static void sndio_toggle_mixer_channel ()
159 {
160 	assert (hdl != NULL);
161 }
162 
sndio_get_mixer_channel_name()163 static char *sndio_get_mixer_channel_name ()
164 {
165 	return xstrdup ("moc");
166 }
167 
sndio_funcs(struct hw_funcs * funcs)168 void sndio_funcs (struct hw_funcs *funcs)
169 {
170 	funcs->init = sndio_init;
171 	funcs->shutdown = sndio_shutdown;
172 	funcs->open = sndio_open;
173 	funcs->close = sndio_close;
174 	funcs->play = sndio_play;
175 	funcs->read_mixer = sndio_read_mixer;
176 	funcs->set_mixer = sndio_set_mixer;
177 	funcs->get_buff_fill = sndio_get_buff_fill;
178 	funcs->reset = sndio_reset;
179 	funcs->get_rate = sndio_get_rate;
180 	funcs->toggle_mixer_channel = sndio_toggle_mixer_channel;
181 	funcs->get_mixer_channel_name = sndio_get_mixer_channel_name;
182 }
183