]> git.sesse.net Git - vlc/blob - modules/audio_output/sndio.c
aout: report drift via parameter rather than callback
[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_object_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 Open (vlc_object_t *obj)
61 {
62     audio_output_t *aout = (audio_output_t *)obj;
63     aout_sys_t *sys = malloc (sizeof (*sys));
64     if (unlikely(sys == NULL))
65         return VLC_EGENERIC;
66
67     sys->hdl = sio_open (NULL, SIO_PLAY, 0 /* blocking */);
68     if (sys->hdl == NULL)
69     {
70         msg_Err (obj, "cannot create audio playback stream");
71         free (sys);
72         return VLC_EGENERIC;
73     }
74     aout->sys = sys;
75
76     struct sio_par par;
77     sio_initpar (&par);
78     par.bits = 16;
79     par.bps = par.bits >> 3;
80     par.sig = 1;
81     par.le = SIO_LE_NATIVE;
82     par.pchan = aout_FormatNbChannels (&aout->format);
83     par.rate = aout->format.i_rate;
84     par.xrun = SIO_SYNC;
85
86     if (!sio_setpar (sys->hdl, &par) || !sio_getpar (sys->hdl, &par))
87     {
88         msg_Err (obj, "cannot negotiate audio playback parameters");
89         goto error;
90     }
91
92     if (par.bps != par.bits >> 3)
93     {
94         msg_Err (obj, "unsupported audio sample format (%u bits in %u bytes)",
95                  par.bits, par.bps);
96         goto error;
97     }
98
99     audio_format_t f;
100
101     switch (par.bits)
102     {
103         case 8:
104             f.i_format = par.sig ? VLC_CODEC_S8 : VLC_CODEC_U8;
105             break;
106         case 16:
107             f.i_format = par.sig ? (par.le ? VLC_CODEC_S16L : VLC_CODEC_S16B)
108                                  : (par.le ? VLC_CODEC_U16L : VLC_CODEC_U16B);
109             break;
110         case 24:
111             f.i_format = par.sig ? (par.le ? VLC_CODEC_S24L : VLC_CODEC_S24B)
112                                  : (par.le ? VLC_CODEC_U24L : VLC_CODEC_U24B);
113             break;
114         case 32:
115             f.i_format = par.sig ? (par.le ? VLC_CODEC_S32L : VLC_CODEC_S32B)
116                                  : (par.le ? VLC_CODEC_U32L : VLC_CODEC_U32B);
117             break;
118         default:
119             msg_Err (obj, "unsupported audio sample format (%u bits)",
120                      par.bits);
121             goto error;
122     }
123
124     f.i_rate = par.rate;
125
126     /* Channel map */
127     unsigned chans;
128     switch (par.pchan)
129     {
130         case 1:
131             chans = AOUT_CHAN_CENTER;
132             break;
133         case 2:
134             chans = AOUT_CHANS_STEREO;
135             break;
136         case 4:
137             chans = AOUT_CHANS_4_0;
138             break;
139         case 6:
140             chans = AOUT_CHANS_5_1;
141             break;
142         case 8:
143             chans = AOUT_CHANS_7_1;
144             break;
145         default:
146             msg_Err (aout, "unknown %u channels map", par.pchan);
147             goto error;
148     }
149
150     f.i_original_channels = f.i_physical_channels = chans;
151     aout_FormatPrepare (&f);
152
153     aout->format = f;
154     aout->sys = sys;
155     aout->pf_play = Play;
156     aout->pf_pause = Pause;
157     aout->pf_flush  = NULL; /* sndio sucks! */
158     if (sio_onvol(sys->hdl, VolumeChanged, aout))
159     {
160         aout->volume_set = VolumeSet;
161         aout->mute_set = MuteSet;
162     }
163     else
164     {
165         aout->volume_set = NULL;
166         aout->mute_set = NULL;
167     }
168
169     sio_start (sys->hdl);
170     return VLC_SUCCESS;
171
172 error:
173     Close (obj);
174     return VLC_EGENERIC;
175 }
176
177 static void Close (vlc_object_t *obj)
178 {
179     audio_output_t *aout = (audio_output_t *)obj;
180     aout_sys_t *sys = aout->sys;
181
182     sio_close (sys->hdl);
183     free (sys);
184 }
185
186 static void Play (audio_output_t *aout, block_t *block,
187                   mtime_t *restrict drift)
188 {
189     aout_sys_t *sys = aout->sys;
190     struct sio_par par;
191
192     if (sio_getpar (sys->hdl, &par) == 0)
193     {
194         mtime_t delay = par.bufsz * CLOCK_FREQ / aout->format.i_rate;
195
196         *drift = mdate () + delay - block->i_pts;
197     }
198
199     while (block->i_buffer > 0 && !sio_eof (sys->hdl))
200     {
201         size_t bytes = sio_write (sys->hdl, block->p_buffer, block->i_buffer);
202
203         block->p_buffer += bytes;
204         block->i_buffer -= bytes;
205         /* Note that i_nb_samples and i_pts are corrupted here. */
206     }
207     block_Release (block);
208 }
209
210 static void Pause (audio_output_t *aout, bool pause, mtime_t date)
211 {
212     aout_sys_t *sys = aout->sys;
213
214     if (pause)
215         sio_stop (sys->hdl);
216     else
217         sio_start (sys->hdl);
218     (void) date;
219 }
220
221 static void VolumeChanged (void *arg, unsigned volume)
222 {
223     audio_output_t *aout = arg;
224     float fvol = (float)volume / (float)SIO_MAXVOL;
225
226     aout_VolumeReport (aout, fvol);
227     aout_MuteReport (aout, volume == 0);
228     if (volume) /* remember last non-zero volume to unmute later */
229         aout->sys->volume = volume;
230 }
231
232 static int VolumeSet (audio_output_t *aout, float fvol)
233 {
234     aout_sys_t *sys = aout->sys;
235     unsigned volume = lroundf (fvol * SIO_MAXVOL);
236
237     if (!sys->mute && !sio_setvol (sys->hdl, volume))
238         return -1;
239     sys->volume = volume;
240     return 0;
241 }
242
243 static int MuteSet (audio_output_t *aout, bool mute)
244 {
245     aout_sys_t *sys = aout->sys;
246
247     if (!sio_setvol (sys->hdl, mute ? 0 : sys->volume))
248         return -1;
249
250     sys->mute = mute;
251     return 0;
252 }