Merge remote-tracking branch 'origin/master' into for-steve
[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(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         if ((ev = eq_noseeum_dequeue(audio_job_queue(aj))) == NULL) {
257                 SXE_MUTEX_UNLOCK(&aj->mtx);
258                 return;
259         }
260         SXE_MUTEX_UNLOCK(&aj->mtx);
261
262         AO_DEBUG_AJ("Event 0x%lx\n", (long unsigned int)ev);
263         switch (audio_job_event_kind(ev)) {
264         case aj_change_state:
265                 AO_DEBUG_AJ("change state event\n");
266                 sound_ao_change_state(aj, &audio_job_event_args(ev));
267                 break;
268         case aj_change_volume:
269                 AO_DEBUG_AJ("change volume event\n");
270                 sound_ao_change_volume(aj, &audio_job_event_args(ev));
271                 break;
272         case aj_change_rate:
273                 AO_DEBUG_AJ("change rate event\n");
274                 sound_ao_change_rate(aj, &audio_job_event_args(ev));
275                 break;
276
277         case no_audio_job_event_kinds:
278         default:
279                 AO_CRITICAL("unknown event\n");
280                 break;
281         }
282         free_audio_job_event(ev);
283 }
284 #endif  /* EF_USE_ASYNEQ */
285 static int
286 sound_ao_play(audio_job_t aj)
287 {
288         /* stream stuff */
289         Lisp_Media_Stream *ms;
290         media_substream *mss;
291         /* thread stuff */
292         media_thread_play_state mtp;
293         /* device stuff */
294         Lisp_Object device;
295         Lisp_Audio_Device *lad = NULL;
296         sound_ao_data_t *saod = NULL;
297         /* libao stuff */
298         ao_sample_format *format;
299         ao_option *options;
300         int driver;
301         /* buffering */
302         int resolution;
303         /* subthread stuff */
304         sound_ao_aj_data_t _sasd, *sasd = &_sasd;
305         sxe_mse_volume_args _volargs, *volargs = &_volargs;
306         sxe_mse_rerate_args _rrargs, *rrargs = &_rrargs;
307         /* cache stuff */
308         int alloced_myself = 0;
309
310         SOUND_UNPACK_MT(aj, device, ms, mss, lad, saod, sasd->mtap);
311
312         /* setup for the driver */
313         driver = saod->driver_id;
314         format = saod->fmt;
315         options = saod->options;
316
317         /* setup format specs from audio props */
318         format->channels = sasd->mtap->channels;
319         format->rate = sasd->mtap->samplerate;
320         format->bits = 16; /* HARDCODED, was: mtap->samplewidth; */
321         format->byte_format = AO_FMT_LITTLE; /* hmpf */
322
323         /* open the driver */
324         sasd->dev = ao_open_live(driver, format, options);
325         if (sasd->dev == NULL) {
326                 message(GETTEXT("audio: Unsupported device."));
327                 XAUDIO_DEVICE_STATE(device) = ASTATE_DEAD;
328                 return 0;
329         }
330
331         /* fill the subthread data structure */
332         sasd->msf = MEDIA_SAMPLE_FORMAT(sxe_msf_S16);
333         sasd->framesize = sasd->mtap->channels * sizeof(int16_t);
334         sasd->coe_ch_cnt = 0;
335         audio_job_device_data(aj) = sasd;
336
337         /* the volume effect */
338         ADD_MEDIA_SAMPLE_EFFECT(
339                 sasd->coe_chain, sasd->coe_ch_cnt,
340                 MEDIA_SAMPLE_EFFECT(sxe_mse_volume), volargs);
341         volargs->num_channels = sasd->mtap->channels;
342         sasd->volargs = volargs;
343
344         /* the rerate effect */
345         ADD_MEDIA_SAMPLE_EFFECT(
346                 sasd->coe_chain, sasd->coe_ch_cnt,
347                 MEDIA_SAMPLE_EFFECT(sxe_mse_rerate), rrargs);
348         rrargs->num_channels = sasd->mtap->channels;
349         rrargs->srcrate = rrargs->tgtrate = 1;
350         sasd->rrargs = rrargs;
351
352         /* rewind it ... */
353         media_stream_meth(ms, rewind)(mss);
354
355         XAUDIO_DEVICE_STATE(device) = ASTATE_ALIVE;
356
357         /* ... and play it */
358         SXE_MUTEX_LOCK(&aj->mtx);
359         mtp = aj->play_state;
360         if (aj->buffer_alloc_size < SOUND_MAX_AUDIO_FRAME_SIZE) {
361                 alloced_myself = 1;
362                 aj->buffer = xmalloc_atomic(SOUND_MAX_AUDIO_FRAME_SIZE);
363                 aj->buffer_alloc_size = SOUND_MAX_AUDIO_FRAME_SIZE;
364         }
365         SXE_MUTEX_UNLOCK(&aj->mtx);
366         resolution = (sasd->mtap->samplerate * MTPSTATE_REACT_TIME) / 1000000;
367
368         while (mtp != MTPSTATE_STOP) {
369
370 #ifdef EF_USE_ASYNEQ
371                 /* events are there? */
372                 if (audio_job_queue(aj)) {
373                         sound_ao_handle_aj_events(aj);
374                 }
375 #endif
376
377                 SXE_MUTEX_LOCK(&aj->mtx);
378                 switch (aj->play_state) {
379                 case MTPSTATE_RUN:
380                         if (!ao_push(aj, resolution))
381                                 aj->play_state = MTPSTATE_STOP;
382                         break;
383                 case MTPSTATE_PAUSE:
384                   /* must sleep resolution outside of lock */
385
386                 case MTPSTATE_UNKNOWN:
387                 case MTPSTATE_STOP:
388                 case NUMBER_OF_MEDIA_THREAD_PLAY_STATES:
389                 default:
390                         AO_DEBUG("ACK, quit\n");
391                         aj->play_state = MTPSTATE_STOP;
392                         break;
393                 }
394                 mtp = aj->play_state;
395                 SXE_MUTEX_UNLOCK(&aj->mtx);
396                 if (mtp == MTPSTATE_PAUSE) {
397                         AO_DEBUG("sleeping for %d\n", resolution);
398                         usleep(resolution);
399                         break;
400                 }
401         }
402
403         /* -- Close and shutdown -- */
404         SXE_MUTEX_LOCK(&aj->mtx);
405         if (alloced_myself && aj->buffer) {
406                 xfree(aj->buffer);
407         }
408         aj->buffer = NULL;
409         aj->buffer_alloc_size = 0;
410         SXE_MUTEX_UNLOCK(&aj->mtx);
411
412         /* sasd is always != NULL here per its initialization */
413         if (sasd->dev) {
414                 ao_close(sasd->dev);
415                 sasd->dev = NULL;
416         }
417         sasd = NULL;
418
419         return 1;
420 }
421
422 #undef MYSELF
423
424 /* sound-ao.c ends here */