Initial git import
[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[16];
297                 snprintf(tmp, 15, "SXEmacs%lx", pthread_self());
298                 sasd->as = arts_play_stream(sasd->mtap->samplerate,
299                                             16 /* HARDCODED */,
300                                             sasd->mtap->channels, tmp);
301                 arts_stream_set(sasd->as, ARTS_P_BLOCKING, 0);
302         } else {
303                 message(GETTEXT("Connecting to aRts daemon failed."));
304                 sad->lock = 0;
305                 SXE_MUTEX_UNLOCK(&sad->mtx);
306                 return 0;
307         }
308
309         /* fill the subthread data structure */
310         sasd->msf = MEDIA_SAMPLE_FORMAT(sxe_msf_S16);
311         sasd->framesize = sasd->mtap->channels * sizeof(int16_t);
312         sasd->coe_ch_cnt = 0;
313         audio_job_device_data(aj) = sasd;
314
315         /* the volume effect */
316         ADD_MEDIA_SAMPLE_EFFECT(
317                 sasd->coe_chain, sasd->coe_ch_cnt,
318                 MEDIA_SAMPLE_EFFECT(sxe_mse_volume), volargs);
319         volargs->num_channels = sasd->mtap->channels;
320         sasd->volargs = volargs;
321
322         /* the rerate effect */
323         ADD_MEDIA_SAMPLE_EFFECT(
324                 sasd->coe_chain, sasd->coe_ch_cnt,
325                 MEDIA_SAMPLE_EFFECT(sxe_mse_rerate), rrargs);
326         rrargs->num_channels = sasd->mtap->channels;
327         rrargs->srcrate = rrargs->tgtrate = 1;
328         sasd->rrargs = rrargs;
329
330         /* rewind it ... */
331         media_stream_meth(ms, rewind)(mss);
332
333         if (aj->buffer_alloc_size < SOUND_MAX_AUDIO_FRAME_SIZE) {
334                 alloced_myself = 1;
335                 aj->buffer = xmalloc_atomic(SOUND_MAX_AUDIO_FRAME_SIZE);
336                 aj->buffer_alloc_size = SOUND_MAX_AUDIO_FRAME_SIZE;
337         }
338         resolution = (sasd->mtap->samplerate * MTPSTATE_REACT_TIME) / 1000000;
339
340         while (aj->play_state != MTPSTATE_STOP) {
341
342 #ifdef EF_USE_ASYNEQ
343                 if (audio_job_queue(aj)) {
344                         sound_arts_handle_aj_events(aj);
345                 }
346 #endif
347
348                 SXE_MUTEX_LOCK(&sad->mtx);
349                 mtp = aj->play_state;
350                 SXE_MUTEX_UNLOCK(&sad->mtx);
351                 switch (mtp) {
352                 case MTPSTATE_RUN:
353                         if (!arts_push(aj, resolution))
354                                 aj->play_state = MTPSTATE_STOP;
355                         break;
356                 case MTPSTATE_PAUSE:
357                         ARTS_DEBUG("sleeping for %d\n", resolution);
358                         usleep(resolution);
359                         break;
360
361                 case MTPSTATE_UNKNOWN:
362                 case MTPSTATE_STOP:
363                 case NUMBER_OF_MEDIA_THREAD_PLAY_STATES:
364                 default:
365                         ARTS_DEBUG("ACK, quit\n");
366                         SXE_MUTEX_LOCK(&sad->mtx);
367                         aj->play_state = MTPSTATE_STOP;
368                         SXE_MUTEX_UNLOCK(&sad->mtx);
369                         break;
370                 }
371         }
372
373         /* -- Close and shutdown -- */
374         SXE_MUTEX_LOCK(&sad->mtx);
375         if (alloced_myself && aj->buffer) {
376                 xfree(aj->buffer);
377         }
378         aj->buffer = NULL;
379         aj->buffer_alloc_size = 0;
380         SXE_MUTEX_UNLOCK(&sad->mtx);
381
382         if (sasd && sasd->as) {
383                 arts_close_stream(sasd->as);
384                 sasd->as = NULL;
385         }
386         sasd = NULL;
387
388         arts_free();
389         sad->lock = 0;
390         SXE_MUTEX_UNLOCK(&sad->mtx);
391         return 1;
392 }
393
394 #undef MYSELF
395
396 /* sound-arts.c ends here */