Compiler & warning related updates/fixes from Nelson
[sxemacs] / src / media / sound-ao.c
1 /* sound-ao.c - play a sound over the libao devices
2
3    Copyright (C) 2006 Sebastian Freundt
4
5 This file is part of SXEmacs
6
7 SXEmacs 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 3 of the License, or
10 (at your option) any later version.
11
12 SXEmacs is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 GNU General Public License for more details.
16
17 You should have received a copy of the GNU General Public License
18 along with this program.  If not, see <http://www.gnu.org/licenses/>. */
19
20
21 /* Synched up with: Not in FSF. */
22
23 #ifdef HAVE_CONFIG_H
24 #include <config.h>
25 #endif
26
27 #include "lisp.h"
28
29 #include <unistd.h>
30 #include <fcntl.h>
31 #include <stdio.h>
32 #include <errno.h>
33 #include <string.h>
34
35 #include "media.h"
36 #include "sound-ao.h"
37
38 Lisp_Object Qao;
39
40 #define MYSELF ADRIVER_AO
41
42 #define __AO_DEBUG__(args...)           fprintf(stderr, "AO " args)
43 #ifndef AO_DEBUG_FLAG
44 #define AO_DEBUG(args...)
45 #else
46 #define AO_DEBUG(args...)               __AO_DEBUG__(args)
47 #endif
48 #define AO_DEBUG_C(args...)             AO_DEBUG("[connection]: " args)
49 #define AO_DEBUG_S(args...)             AO_DEBUG("[stream]: " args)
50 #define AO_DEBUG_COE(args...)           AO_DEBUG("[coerce]: " args)
51 #define AO_DEBUG_AJ(args...)            AO_DEBUG("[audio-job]: " args)
52 #define AO_CRITICAL(args...)            __AO_DEBUG__("CRITICAL: " args)
53
54 \f
55 DECLARE_AUDIO_DEVICE_SIMPLE_METHS(sound_ao);
56 DEFINE_AUDIO_DEVICE_SIMPLE(sound_ao);
57
58 \f
59 static Lisp_Object
60 sound_ao_mark(ad_device_data *devdata)
61 {
62         return Qnil;
63 }
64
65 static void
66 sound_ao_print(Lisp_Object device, Lisp_Object pcfun, int ef)
67 {
68         sound_ao_data_t *saod = NULL;
69
70         saod = get_audio_device_data(device);
71         /* cannot use AO on incomplete or corrupt audio devices */
72         if (XAUDIO_DEVICE_DRIVER(device) != MYSELF || saod == NULL) {
73                 write_c_string("VOID", pcfun);
74                 /* now that we are here, mark AO device as dead */
75                 XAUDIO_DEVICE_STATE(device) = ASTATE_DEAD;
76                 return;
77         }
78
79         /* info about the connected output plugin */
80         write_c_string(" :driver \"", pcfun);
81         write_c_string(ao_driver_info(saod->driver_id)->short_name, pcfun);
82         write_c_string("\"", pcfun);
83
84         if (ef);
85         return;
86 }
87
88 static ad_device_data *
89 sound_ao_create(Lisp_Object ao_options)
90 {
91         int driver;
92         ao_device *device;
93         ao_option *options;
94         ao_sample_format *fmt;
95         /* result */
96         sound_ao_data_t *aod;
97         /* option keywords */
98         Lisp_Object opt_driver;
99         char *optext_driver = NULL;
100
101         /* parse options */
102         opt_driver = Fplist_get(ao_options, intern(":driver"), Qnil);
103         if (!NILP(opt_driver) && !STRINGP(opt_driver)) {
104                 wrong_type_argument(Qstringp, opt_driver);
105                 return NULL;
106         } else if (STRINGP(opt_driver))
107                 optext_driver = (char*)XSTRING_DATA(opt_driver);
108
109         /* -- initialise -- */
110         ao_initialize();
111         fmt = xmalloc_and_zero(sizeof(ao_sample_format));
112
113         /* -- Setup for driver -- */
114         if (optext_driver != NULL)
115                 driver = ao_driver_id(optext_driver);
116         else
117                 driver = ao_default_driver_id();
118
119         /* just some generics */
120         fmt->channels = 2;
121         fmt->rate = 44100;
122         fmt->bits = 16;
123         fmt->byte_format = AO_FMT_LITTLE;
124
125         options = NULL;
126
127         /* -- Open driver -- */
128         device = ao_open_live(driver, fmt, options);
129         if (device == NULL) {
130                 message(GETTEXT("audio-ao: Unsupported driver."));
131                 xfree(fmt);
132                 aod = NULL;
133         } else {
134                 aod = xnew_and_zero(sound_ao_data_t);
135
136                 aod->ad = device;
137                 aod->options = NULL;
138                 aod->fmt = fmt;
139                 aod->driver_id = driver;
140         }
141
142         return aod;
143 }
144
145 static void
146 sound_ao_finish(ad_device_data *data)
147 {
148         sound_ao_data_t *ao_dev = data;
149         if (ao_dev != NULL) {
150                 ao_close(ao_dev->ad);
151                 xfree(ao_dev->fmt);
152                 ao_shutdown();
153         }
154         return;
155 }
156
157 \f
158 static int
159 ao_push(audio_job_t aj, int nframes)
160 {
161         size_t len = 0, tmplen = 0;
162         sxe_media_sample_t *tmpbuf;
163         sound_ao_aj_data_t *sasd = audio_job_device_data(aj);
164         int i;
165
166         tmpbuf = (sxe_media_sample_t*)(aj->buffer);
167         len = media_stream_meth(aj->substream->up, read)(
168                 aj->substream, aj->buffer, nframes);
169
170         /* set up the volume args */
171         sasd->volargs->volume[0] = sasd->volargs->volume[1] = aj->volume;
172         /* set up the rerate args */
173         sasd->rrargs->tweak = aj->ratetrafo;
174
175         /* coerce the stuff */
176         tmplen = sasd->mtap->channels*len;
177         for (i = 0; i < sasd->coe_ch_cnt; i++) {
178                 AO_DEBUG_COE("calling coerce "
179                               "%d on b:0x%x l:%d\n",
180                               i, (unsigned int)tmpbuf, tmplen);
181                 tmplen = CALL_MEDIA_SAMPLE_EFFECT(
182                         sasd->coe_chain, i, tmpbuf, tmpbuf, tmplen);
183         }
184         /* bring back to S16 or U8 */
185         MEDIA_SAMPLE_FORMAT_DOWNSAMPLE(sasd->msf)(
186                 aj->buffer, aj->buffer, tmplen);
187
188         ao_play(sasd->dev, aj->buffer,
189                 tmplen * sasd->framesize / sasd->mtap->channels);
190
191         return tmplen;
192 }
193
194 \f
195 #ifdef EF_USE_ASYNEQ
196 static inline void
197 sound_ao_change_volume(audio_job_t aj, audio_job_event_args_t args)
198 {
199         SXE_MUTEX_LOCK(&aj->mtx);
200         aj->volume = args->volume_args;
201         SXE_MUTEX_UNLOCK(&aj->mtx);
202 }
203
204 static inline void
205 sound_ao_change_rate(audio_job_t aj, audio_job_event_args_t args)
206 {
207         SXE_MUTEX_LOCK(&aj->mtx);
208         aj->ratetrafo = args->rate_args;
209         SXE_MUTEX_UNLOCK(&aj->mtx);
210 }
211
212 static inline void
213 sound_ao_change_state(audio_job_t aj, audio_job_event_args_t args)
214 {
215         SXE_MUTEX_LOCK(&aj->mtx);
216         switch (args->state_args) {
217         case aj_pause:
218                 AO_DEBUG_AJ("->pause state\n");
219                 aj->play_state = MTPSTATE_PAUSE;
220                 break;
221         case aj_resume:
222                 AO_DEBUG_AJ("->resume state\n");
223                 aj->play_state = MTPSTATE_RUN;
224                 break;
225         case aj_start:
226                 AO_DEBUG_AJ("->start state\n");
227                 break;
228         case aj_stop:
229                 AO_DEBUG_AJ("->stop state\n");
230                 aj->play_state = MTPSTATE_STOP;
231                 break;
232
233         case no_audio_job_change_states:
234         default:
235                 AO_DEBUG_AJ("->unknown state\n");
236                 break;
237         }
238         SXE_MUTEX_UNLOCK(&aj->mtx);
239 }
240
241 static inline void
242 sound_ao_handle_aj_events(audio_job_t aj)
243         __attribute__((always_inline));
244 static inline void
245 sound_ao_handle_aj_events(audio_job_t aj)
246 {
247         sound_ao_aj_data_t *sasd;
248         audio_job_event_t ev = NULL;
249
250 #if 0
251         assert(audio_job_queue(aj));
252 #endif
253
254         SXE_MUTEX_LOCK(&aj->mtx);
255         sasd = audio_job_device_data(aj);
256         SXE_SET_UNUSED(sasd);
257
258         if ((ev = eq_noseeum_dequeue(audio_job_queue(aj))) == NULL) {
259                 SXE_MUTEX_UNLOCK(&aj->mtx);
260                 return;
261         }
262         SXE_MUTEX_UNLOCK(&aj->mtx);
263
264         AO_DEBUG_AJ("Event 0x%lx\n", (long unsigned int)ev);
265         switch (audio_job_event_kind(ev)) {
266         case aj_change_state:
267                 AO_DEBUG_AJ("change state event\n");
268                 sound_ao_change_state(aj, &audio_job_event_args(ev));
269                 break;
270         case aj_change_volume:
271                 AO_DEBUG_AJ("change volume event\n");
272                 sound_ao_change_volume(aj, &audio_job_event_args(ev));
273                 break;
274         case aj_change_rate:
275                 AO_DEBUG_AJ("change rate event\n");
276                 sound_ao_change_rate(aj, &audio_job_event_args(ev));
277                 break;
278
279         case no_audio_job_event_kinds:
280         default:
281                 AO_CRITICAL("unknown event\n");
282                 break;
283         }
284         free_audio_job_event(ev);
285 }
286 #endif  /* EF_USE_ASYNEQ */
287 static int
288 sound_ao_play(audio_job_t aj)
289 {
290         /* stream stuff */
291         Lisp_Media_Stream *ms;
292         media_substream *mss;
293         /* thread stuff */
294         media_thread_play_state mtp;
295         /* device stuff */
296         Lisp_Object device;
297         Lisp_Audio_Device *lad = NULL;
298         sound_ao_data_t *saod = NULL;
299         /* libao stuff */
300         ao_sample_format *format;
301         ao_option *options;
302         int driver;
303         /* buffering */
304         int resolution;
305         /* subthread stuff */
306         sound_ao_aj_data_t _sasd, *sasd = &_sasd;
307         sxe_mse_volume_args _volargs, *volargs = &_volargs;
308         sxe_mse_rerate_args _rrargs, *rrargs = &_rrargs;
309         /* cache stuff */
310         int alloced_myself = 0;
311
312         SOUND_UNPACK_MT(aj, device, ms, mss, lad, saod, sasd->mtap);
313
314         /* setup for the driver */
315         driver = saod->driver_id;
316         format = saod->fmt;
317         options = saod->options;
318
319         /* setup format specs from audio props */
320         format->channels = sasd->mtap->channels;
321         format->rate = sasd->mtap->samplerate;
322         format->bits = 16; /* HARDCODED, was: mtap->samplewidth; */
323         format->byte_format = AO_FMT_LITTLE; /* hmpf */
324
325         /* open the driver */
326         sasd->dev = ao_open_live(driver, format, options);
327         if (sasd->dev == NULL) {
328                 message(GETTEXT("audio: Unsupported device."));
329                 XAUDIO_DEVICE_STATE(device) = ASTATE_DEAD;
330                 return 0;
331         }
332
333         /* fill the subthread data structure */
334         sasd->msf = MEDIA_SAMPLE_FORMAT(sxe_msf_S16);
335         sasd->framesize = sasd->mtap->channels * sizeof(int16_t);
336         sasd->coe_ch_cnt = 0;
337         audio_job_device_data(aj) = sasd;
338
339         /* the volume effect */
340         ADD_MEDIA_SAMPLE_EFFECT(
341                 sasd->coe_chain, sasd->coe_ch_cnt,
342                 MEDIA_SAMPLE_EFFECT(sxe_mse_volume), volargs);
343         volargs->num_channels = sasd->mtap->channels;
344         sasd->volargs = volargs;
345
346         /* the rerate effect */
347         ADD_MEDIA_SAMPLE_EFFECT(
348                 sasd->coe_chain, sasd->coe_ch_cnt,
349                 MEDIA_SAMPLE_EFFECT(sxe_mse_rerate), rrargs);
350         rrargs->num_channels = sasd->mtap->channels;
351         rrargs->srcrate = rrargs->tgtrate = 1;
352         sasd->rrargs = rrargs;
353
354         /* rewind it ... */
355         media_stream_meth(ms, rewind)(mss);
356
357         XAUDIO_DEVICE_STATE(device) = ASTATE_ALIVE;
358
359         /* ... and play it */
360         SXE_MUTEX_LOCK(&aj->mtx);
361         mtp = aj->play_state;
362         if (aj->buffer_alloc_size < SOUND_MAX_AUDIO_FRAME_SIZE) {
363                 alloced_myself = 1;
364                 aj->buffer = xmalloc_atomic(SOUND_MAX_AUDIO_FRAME_SIZE);
365                 aj->buffer_alloc_size = SOUND_MAX_AUDIO_FRAME_SIZE;
366         }
367         SXE_MUTEX_UNLOCK(&aj->mtx);
368         resolution = (sasd->mtap->samplerate * MTPSTATE_REACT_TIME) / 1000000;
369
370         while (mtp != MTPSTATE_STOP) {
371
372 #ifdef EF_USE_ASYNEQ
373                 /* events are there? */
374                 if (audio_job_queue(aj)) {
375                         sound_ao_handle_aj_events(aj);
376                 }
377 #endif
378
379                 SXE_MUTEX_LOCK(&aj->mtx);
380                 switch (aj->play_state) {
381                 case MTPSTATE_RUN:
382                         if (!ao_push(aj, resolution))
383                                 aj->play_state = MTPSTATE_STOP;
384                         break;
385                 case MTPSTATE_PAUSE:
386                   /* must sleep resolution outside of lock */
387
388                 case MTPSTATE_UNKNOWN:
389                 case MTPSTATE_STOP:
390                 case NUMBER_OF_MEDIA_THREAD_PLAY_STATES:
391                 default:
392                         AO_DEBUG("ACK, quit\n");
393                         aj->play_state = MTPSTATE_STOP;
394                         break;
395                 }
396                 mtp = aj->play_state;
397                 SXE_MUTEX_UNLOCK(&aj->mtx);
398                 if (mtp == MTPSTATE_PAUSE) {
399                         AO_DEBUG("sleeping for %d\n", resolution);
400                         usleep(resolution);
401                         break;
402                 }
403         }
404
405         /* -- Close and shutdown -- */
406         SXE_MUTEX_LOCK(&aj->mtx);
407         if (alloced_myself && aj->buffer) {
408                 xfree(aj->buffer);
409         }
410         aj->buffer = NULL;
411         aj->buffer_alloc_size = 0;
412         SXE_MUTEX_UNLOCK(&aj->mtx);
413
414         /* sasd is always != NULL here per its initialization */
415         if (sasd->dev) {
416                 ao_close(sasd->dev);
417                 sasd->dev = NULL;
418         }
419         sasd = NULL;
420
421         return 1;
422 }
423
424 #undef MYSELF
425
426 /* sound-ao.c ends here */