]> git.sesse.net Git - vlc/blob - modules/audio_output/sndio.c
aout: add distinct start/stop callbacks (refs #4787, refs #7601)
[vlc] / modules / audio_output / sndio.c
1 /*****************************************************************************
2  * sndio.c : sndio plugin for VLC
3  *****************************************************************************
4  * Copyright (C) 2012 RĂ©mi Denis-Courmont
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU Lesser General Public License as published by
8  * the Free Software Foundation; either version 2.1 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public License
17  * along with this program; if not, write to the Free Software Foundation,
18  * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
19  *****************************************************************************/
20
21 #ifdef HAVE_CONFIG_H
22 # include "config.h"
23 #endif
24
25 #include <math.h>
26 #include <assert.h>
27
28 #include <vlc_common.h>
29 #include <vlc_plugin.h>
30 #include <vlc_aout.h>
31
32 #include <sndio.h>
33
34 static int Open (vlc_object_t *);
35 static void Close (vlc_objec_t *);
36
37 vlc_module_begin ()
38     set_shortname ("sndio")
39     set_description (N_("OpenBSD sndio audio output"))
40     set_category (CAT_AUDIO)
41     set_subcategory (SUBCAT_AUDIO_AOUT)
42     set_capability ("audio output", 120)
43     set_callbacks (Open, Close)
44 vlc_module_end ()
45
46 static void Play (audio_output_t *, block_t *, mtime_t *);
47 static void Pause (audio_output_t *, bool, mtime_t);
48 static int VolumeSet (audio_output_t *, float);
49 static int MuteSet (audio_output_t *, bool);
50 static void VolumeChanged (void *, unsigned);
51
52 struct aout_sys_t
53 {
54     struct sio_hdl *hdl;
55     unsigned volume;
56     bool mute;
57 };
58
59 /** Initializes an sndio playback stream */
60 static int Start (audio_output_t *aout, audio_sample_format_t *restrict fmt)
61 {
62     aout_sys_t *sys = aout->sys;
63
64     sys->hdl = sio_open (NULL, SIO_PLAY, 0 /* blocking */);
65     if (sys->hdl == NULL)
66     {
67         msg_Err (obj, "cannot create audio playback stream");
68         free (sys);
69         return VLC_EGENERIC;
70     }
71     aout->sys = sys;
72
73     struct sio_par par;
74     sio_initpar (&par);
75     par.bits = 16;
76     par.bps = par.bits >> 3;
77     par.sig = 1;
78     par.le = SIO_LE_NATIVE;
79     par.pchan = aout_FormatNbChannels (fmt);
80     par.rate = fmt->i_rate;
81     par.xrun = SIO_SYNC;
82
83     if (!sio_setpar (sys->hdl, &par) || !sio_getpar (sys->hdl, &par))
84     {
85         msg_Err (obj, "cannot negotiate audio playback parameters");
86         goto error;
87     }
88
89     if (par.bps != par.bits >> 3)
90     {
91         msg_Err (obj, "unsupported audio sample format (%u bits in %u bytes)",
92                  par.bits, par.bps);
93         goto error;
94     }
95
96     switch (par.bits)
97     {
98         case 8:
99             fmt->i_format = par.sig ? VLC_CODEC_S8 : VLC_CODEC_U8;
100             break;
101         case 16:
102             fmt->i_format = par.sig
103                                  ? (par.le ? VLC_CODEC_S16L : VLC_CODEC_S16B)
104                                  : (par.le ? VLC_CODEC_U16L : VLC_CODEC_U16B);
105             break;
106         case 24:
107             fmt->i_format = par.sig
108                                  ? (par.le ? VLC_CODEC_S24L : VLC_CODEC_S24B)
109                                  : (par.le ? VLC_CODEC_U24L : VLC_CODEC_U24B);
110             break;
111         case 32:
112             fmt->i_format = par.sig
113                                  ? (par.le ? VLC_CODEC_S32L : VLC_CODEC_S32B)
114                                  : (par.le ? VLC_CODEC_U32L : VLC_CODEC_U32B);
115             break;
116         default:
117             msg_Err (obj, "unsupported audio sample format (%u bits)",
118                      par.bits);
119             goto error;
120     }
121
122     fmt->i_rate = par.rate;
123
124     /* Channel map */
125     unsigned chans;
126     switch (par.pchan)
127     {
128         case 1:
129             chans = AOUT_CHAN_CENTER;
130             break;
131         case 2:
132             chans = AOUT_CHANS_STEREO;
133             break;
134         case 4:
135             chans = AOUT_CHANS_4_0;
136             break;
137         case 6:
138             chans = AOUT_CHANS_5_1;
139             break;
140         case 8:
141             chans = AOUT_CHANS_7_1;
142             break;
143         default:
144             msg_Err (aout, "unknown %u channels map", par.pchan);
145             goto error;
146     }
147
148     fmt->i_original_channels = fmt->i_physical_channels = chans;
149     aout_FormatPrepare (fmt);
150
151     aout->sys = sys;
152     aout->play = Play;
153     aout->pause = Pause;
154     aout->flush  = NULL; /* sndio sucks! */
155     if (sio_onvol(sys->hdl, VolumeChanged, aout))
156     {
157         aout->volume_set = VolumeSet;
158         aout->mute_set = MuteSet;
159     }
160     else
161     {
162         aout->volume_set = NULL;
163         aout->mute_set = NULL;
164     }
165
166     sio_start (sys->hdl);
167     return VLC_SUCCESS;
168
169 error:
170     sio_close (sys->hdl);
171     return VLC_EGENERIC;
172 }
173
174 static void Close (vlc_object_t *obj)
175 {
176     audio_output_t *aout = (audio_output_t *)obj;
177     aout_sys_t *sys = aout->sys;
178
179     sio_close (sys->hdl);
180 }
181
182 static void Play (audio_output_t *aout, block_t *block,
183                   mtime_t *restrict drift)
184 {
185     aout_sys_t *sys = aout->sys;
186     struct sio_par par;
187
188     if (sio_getpar (sys->hdl, &par) == 0)
189     {
190         mtime_t delay = par.bufsz * CLOCK_FREQ / aout->format.i_rate;
191
192         *drift = mdate () + delay - block->i_pts;
193     }
194
195     while (block->i_buffer > 0 && !sio_eof (sys->hdl))
196     {
197         size_t bytes = sio_write (sys->hdl, block->p_buffer, block->i_buffer);
198
199         block->p_buffer += bytes;
200         block->i_buffer -= bytes;
201         /* Note that i_nb_samples and i_pts are corrupted here. */
202     }
203     block_Release (block);
204 }
205
206 static void Pause (audio_output_t *aout, bool pause, mtime_t date)
207 {
208     aout_sys_t *sys = aout->sys;
209
210     if (pause)
211         sio_stop (sys->hdl);
212     else
213         sio_start (sys->hdl);
214     (void) date;
215 }
216
217 static void VolumeChanged (void *arg, unsigned volume)
218 {
219     audio_output_t *aout = arg;
220     float fvol = (float)volume / (float)SIO_MAXVOL;
221
222     aout_VolumeReport (aout, fvol);
223     aout_MuteReport (aout, volume == 0);
224     if (volume) /* remember last non-zero volume to unmute later */
225         aout->sys->volume = volume;
226 }
227
228 static int VolumeSet (audio_output_t *aout, float fvol)
229 {
230     aout_sys_t *sys = aout->sys;
231     unsigned volume = lroundf (fvol * SIO_MAXVOL);
232
233     if (!sys->mute && !sio_setvol (sys->hdl, volume))
234         return -1;
235     sys->volume = volume;
236     return 0;
237 }
238
239 static int MuteSet (audio_output_t *aout, bool mute)
240 {
241     aout_sys_t *sys = aout->sys;
242
243     if (!sio_setvol (sys->hdl, mute ? 0 : sys->volume))
244         return -1;
245
246     sys->mute = mute;
247     return 0;
248 }
249
250 static int Open (vlc_object_t *obj)
251 {
252     audio_output_t *aout = (audio_output_t *)obj;
253     aout_sys_t *sys = malloc (sizeof (*sys));
254     if (unlikely(sys == NULL))
255         return VLC_ENOMEM;
256
257     aout->sys = sys;
258     aout->start = Start;
259     aout->stop = Stop;
260     /* FIXME: set volume/mute here */
261     return VLC_SUCCESS;
262 }
263
264 static int Close (vlc_object_t *obj)
265 {
266     audio_output_t *aout = (audio_output_t *)obj;
267     aout_sys_t *sys = aout->sys;
268
269     free (sys);
270 }
271