Security fixes
[sxemacs] / src / media / sound-arts.c
1 /* sound-arts.c - play a sound over the arts
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-arts.h"
37
38 Lisp_Object Qarts;
39
40 #define MYSELF ADRIVER_ARTS
41
42 #define __ARTS_DEBUG__(args...)         fprintf(stderr, "ARTS " args)
43 #ifndef ARTS_DEBUG_FLAG
44 #define ARTS_DEBUG(args...)
45 #else
46 #define ARTS_DEBUG(args...)             __ARTS_DEBUG__(args)
47 #endif
48 #define ARTS_DEBUG_C(args...)           ARTS_DEBUG("[connection]: " args)
49 #define ARTS_DEBUG_S(args...)           ARTS_DEBUG("[stream]: " args)
50 #define ARTS_DEBUG_COE(args...)         ARTS_DEBUG("[coerce]: " args)
51 #define ARTS_DEBUG_AJ(args...)          ARTS_DEBUG("[audio-job]: " args)
52 #define ARTS_CRITICAL(args...)          __ARTS_DEBUG__("CRITICAL: " args)
53
54 \f
55 DECLARE_AUDIO_DEVICE_SIMPLE_METHS(sound_arts);
56 DEFINE_AUDIO_DEVICE_SIMPLE(sound_arts);
57
58 \f
59 static Lisp_Object
60 sound_arts_mark(ad_device_data *devdata)
61 {
62         sound_arts_data_t *sad = devdata;
63
64         sad = NULL;;
65
66         return Qnil;
67 }
68
69 static void
70 sound_arts_print(Lisp_Object device, Lisp_Object pcfun, int ef)
71 {
72         sound_arts_data_t *sad = NULL;
73
74         sad = get_audio_device_data(device);
75
76         /* cannot use incomplete or corrupt audio devices */
77         if (XAUDIO_DEVICE_DRIVER(device) != MYSELF || sad == NULL) {
78                 write_c_string(" VOID", pcfun);
79                 /* now that we are here, mark AO device as dead */
80                 XAUDIO_DEVICE_STATE(device) = ASTATE_DEAD;
81                 return;
82         }
83
84         /* info about the connected output plugin */
85
86
87         return;
88 }
89
90 \f
91 static ad_device_data *
92 sound_arts_create(Lisp_Object arts_options)
93 {
94         sound_arts_data_t *sad;
95         Lisp_Object opt_server = Qnil;
96
97         /* parse options */
98         opt_server = Fplist_get(arts_options, intern(":server"), Qnil);
99         if (!NILP(opt_server) && !STRINGP(opt_server)) {
100                 wrong_type_argument(Qstringp, opt_server);
101                 return NULL;
102         }
103
104         /* -- initialise -- */
105         sad = xnew_and_zero(sound_arts_data_t);
106         SXE_MUTEX_INIT(&sad->mtx);
107
108         ARTS_DEBUG_C("created: 0x%x\n", (unsigned int)sad);
109
110         return sad;
111 }
112
113 static void
114 sound_arts_finish(ad_device_data *data)
115 {
116         ARTS_DEBUG("finishing ARTS: 0x%lx\n",
117                    (long unsigned int)data);
118
119         SXE_MUTEX_FINI(&((sound_arts_data_t*)data)->mtx);
120         ARTS_DEBUG("audio-device finished.\n");
121
122         return;
123 }
124 \f
125 #ifdef EF_USE_ASYNEQ
126 static inline void
127 sound_arts_change_volume(audio_job_t aj, audio_job_event_args_t args)
128 {
129         SXE_MUTEX_LOCK(&aj->mtx);
130         aj->volume = args->volume_args;
131         SXE_MUTEX_UNLOCK(&aj->mtx);
132 }
133
134 static inline void
135 sound_arts_change_rate(audio_job_t aj, audio_job_event_args_t args)
136 {
137         SXE_MUTEX_LOCK(&aj->mtx);
138         aj->ratetrafo = args->rate_args;
139         SXE_MUTEX_UNLOCK(&aj->mtx);
140 }
141
142 static inline void
143 sound_arts_change_state(audio_job_t aj, audio_job_event_args_t args)
144 {
145         SXE_MUTEX_LOCK(&aj->mtx);
146         switch (args->state_args) {
147         case aj_pause:
148                 ARTS_DEBUG_AJ("->pause state\n");
149                 aj->play_state = MTPSTATE_PAUSE;
150                 break;
151         case aj_resume:
152                 ARTS_DEBUG_AJ("->resume state\n");
153                 aj->play_state = MTPSTATE_RUN;
154                 break;
155         case aj_start:
156                 ARTS_DEBUG_AJ("->start state\n");
157                 break;
158         case aj_stop:
159                 ARTS_DEBUG_AJ("->stop state\n");
160                 aj->play_state = MTPSTATE_STOP;
161                 break;
162
163         case no_audio_job_change_states:
164         default:
165                 ARTS_DEBUG_AJ("->unknown state\n");
166                 break;
167         }
168         SXE_MUTEX_UNLOCK(&aj->mtx);
169 }
170
171 static inline void
172 sound_arts_handle_aj_events(audio_job_t aj)
173         __attribute__((always_inline));
174 static inline void
175 sound_arts_handle_aj_events(audio_job_t aj)
176 {
177         sound_arts_aj_data_t *sasd;
178         audio_job_event_t ev = NULL;
179
180 #if 0
181         assert(audio_job_queue(aj));
182 #endif
183
184         SXE_MUTEX_LOCK(&aj->mtx);
185         sasd = audio_job_device_data(aj);
186         if ((ev = eq_noseeum_dequeue(audio_job_queue(aj))) == NULL) {
187                 SXE_MUTEX_UNLOCK(&aj->mtx);
188                 return;
189         }
190         SXE_MUTEX_UNLOCK(&aj->mtx);
191
192         ARTS_DEBUG_AJ("Event 0x%lx\n", (long unsigned int)ev);
193         switch (audio_job_event_kind(ev)) {
194         case aj_change_state:
195                 ARTS_DEBUG_AJ("change state event\n");
196                 sound_arts_change_state(aj, &audio_job_event_args(ev));
197                 break;
198         case aj_change_volume:
199                 ARTS_DEBUG_AJ("change volume event\n");
200                 sound_arts_change_volume(aj, &audio_job_event_args(ev));
201                 break;
202         case aj_change_rate:
203                 ARTS_DEBUG_AJ("change rate event\n");
204                 sound_arts_change_rate(aj, &audio_job_event_args(ev));
205                 break;
206         case no_audio_job_event_kinds:
207         default:
208                 ARTS_CRITICAL("unknown event\n");
209                 break;
210         }
211         free_audio_job_event(ev);
212 }
213 #endif  /* EF_USE_ASYNEQ */
214 \f
215 static int
216 arts_push(audio_job_t aj, int nframes)
217 {
218         size_t len = 0, tmplen = 0;
219         sxe_media_sample_t *tmpbuf;
220         sound_arts_aj_data_t *sasd = audio_job_device_data(aj);
221         int i;
222
223         tmpbuf = (sxe_media_sample_t*)(aj->buffer);
224         len = media_stream_meth(aj->substream->up, read)(
225                 aj->substream, aj->buffer, nframes);
226
227         /* set up the volume args */
228         sasd->volargs->volume[0] = sasd->volargs->volume[1] =
229                 aj->volume;
230         /* set up the rerate args */
231         sasd->rrargs->tweak = aj->ratetrafo;
232
233         /* coerce the stuff */
234         tmplen = sasd->mtap->channels*len;
235         for (i = 0; i < sasd->coe_ch_cnt; i++) {
236                 ARTS_DEBUG_COE("calling coerce "
237                               "%d on b:0x%x l:%d\n",
238                               i, (unsigned int)tmpbuf, tmplen);
239                 tmplen = CALL_MEDIA_SAMPLE_EFFECT(
240                         sasd->coe_chain, i, tmpbuf, tmpbuf, tmplen);
241         }
242         /* bring back to S16 or U8 */
243         MEDIA_SAMPLE_FORMAT_DOWNSAMPLE(sasd->msf)(
244                 aj->buffer, aj->buffer, tmplen);
245
246         ARTS_DEBUG_S("writing %u samples...\n", tmplen);
247         i = 0;
248         tmplen *= sasd->framesize/sasd->mtap->channels;
249         while (i < 500 &&       /* very arbitrary choice */
250                !(len = arts_write(sasd->as, aj->buffer, tmplen))) {
251                 i++;
252                 usleep(100);
253         }
254         ARTS_DEBUG_S("ACK (written %u)\n", len);
255
256         return len;
257 }
258
259 static int
260 sound_arts_play(audio_job_t aj)
261 {
262         /* stream stuff */
263         Lisp_Media_Stream *ms;
264         media_substream *mss;
265         /* thread stuff */
266         media_thread_play_state mtp;
267         /* device stuff */
268         Lisp_Object device;
269         Lisp_Audio_Device *lad = NULL;
270         sound_arts_data_t *sad = NULL;
271         /* buffering */
272         int resolution;
273         /* subthread stuff */
274         sound_arts_aj_data_t _sasd, *sasd = &_sasd;
275         sxe_mse_volume_args _volargs, *volargs = &_volargs;
276         sxe_mse_rerate_args _rrargs, *rrargs = &_rrargs;
277         /* cache stuff */
278         int alloced_myself = 0;
279
280         SOUND_UNPACK_MT(aj, device, ms, mss, lad, sad, sasd->mtap);
281
282         SXE_MUTEX_LOCK(&sad->mtx);
283         if (sad->lock) {
284                 ARTS_DEBUG_C("Device locked.\n");
285                 message(GETTEXT("audio-arts: "
286                                 "Device locked."));
287                 /* this lock is probably unnecessary */
288                 SXE_MUTEX_UNLOCK(&sad->mtx);
289                 return 0;
290         }
291
292         sad->lock = 1;
293
294         /* trigger arts */
295         if (arts_init() == 0) {
296                 char tmp[48];
297                 int sz = snprintf(tmp, sizeof(tmp), "SXEmacs%lx", pthread_self());
298                 assert(sz>=0 && sz<sizeof(tmp));
299                 sasd->as = arts_play_stream(sasd->mtap->samplerate,
300                                             16 /* HARDCODED */,
301                                             sasd->mtap->channels, tmp);
302                 arts_stream_set(sasd->as, ARTS_P_BLOCKING, 0);
303         } else {
304                 message(GETTEXT("Connecting to aRts daemon failed."));
305                 sad->lock = 0;
306                 SXE_MUTEX_UNLOCK(&sad->mtx);
307                 return 0;
308         }
309
310         /* fill the subthread data structure */
311         sasd->msf = MEDIA_SAMPLE_FORMAT(sxe_msf_S16);
312         sasd->framesize = sasd->mtap->channels * sizeof(int16_t);
313         sasd->coe_ch_cnt = 0;
314         audio_job_device_data(aj) = sasd;
315
316         /* the volume effect */
317         ADD_MEDIA_SAMPLE_EFFECT(
318                 sasd->coe_chain, sasd->coe_ch_cnt,
319                 MEDIA_SAMPLE_EFFECT(sxe_mse_volume), volargs);
320         volargs->num_channels = sasd->mtap->channels;
321         sasd->volargs = volargs;
322
323         /* the rerate effect */
324         ADD_MEDIA_SAMPLE_EFFECT(
325                 sasd->coe_chain, sasd->coe_ch_cnt,
326                 MEDIA_SAMPLE_EFFECT(sxe_mse_rerate), rrargs);
327         rrargs->num_channels = sasd->mtap->channels;
328         rrargs->srcrate = rrargs->tgtrate = 1;
329         sasd->rrargs = rrargs;
330
331         /* rewind it ... */
332         media_stream_meth(ms, rewind)(mss);
333
334         if (aj->buffer_alloc_size < SOUND_MAX_AUDIO_FRAME_SIZE) {
335                 alloced_myself = 1;
336                 aj->buffer = xmalloc_atomic(SOUND_MAX_AUDIO_FRAME_SIZE);
337                 aj->buffer_alloc_size = SOUND_MAX_AUDIO_FRAME_SIZE;
338         }
339         resolution = (sasd->mtap->samplerate * MTPSTATE_REACT_TIME) / 1000000;
340
341         while (aj->play_state != MTPSTATE_STOP) {
342
343 #ifdef EF_USE_ASYNEQ
344                 if (audio_job_queue(aj)) {
345                         sound_arts_handle_aj_events(aj);
346                 }
347 #endif
348
349                 SXE_MUTEX_LOCK(&sad->mtx);
350                 mtp = aj->play_state;
351                 SXE_MUTEX_UNLOCK(&sad->mtx);
352                 switch (mtp) {
353                 case MTPSTATE_RUN:
354                         if (!arts_push(aj, resolution))
355                                 aj->play_state = MTPSTATE_STOP;
356                         break;
357                 case MTPSTATE_PAUSE:
358                         ARTS_DEBUG("sleeping for %d\n", resolution);
359                         usleep(resolution);
360                         break;
361
362                 case MTPSTATE_UNKNOWN:
363                 case MTPSTATE_STOP:
364                 case NUMBER_OF_MEDIA_THREAD_PLAY_STATES:
365                 default:
366                         ARTS_DEBUG("ACK, quit\n");
367                         SXE_MUTEX_LOCK(&sad->mtx);
368                         aj->play_state = MTPSTATE_STOP;
369                         SXE_MUTEX_UNLOCK(&sad->mtx);
370                         break;
371                 }
372         }
373
374         /* -- Close and shutdown -- */
375         SXE_MUTEX_LOCK(&sad->mtx);
376         if (alloced_myself && aj->buffer) {
377                 xfree(aj->buffer);
378         }
379         aj->buffer = NULL;
380         aj->buffer_alloc_size = 0;
381         SXE_MUTEX_UNLOCK(&sad->mtx);
382
383         if (sasd && sasd->as) {
384                 arts_close_stream(sasd->as);
385                 sasd->as = NULL;
386         }
387         sasd = NULL;
388
389         arts_free();
390         sad->lock = 0;
391         SXE_MUTEX_UNLOCK(&sad->mtx);
392         return 1;
393 }
394
395 #undef MYSELF
396
397 /* sound-arts.c ends here */