353b0778c78ef6aa0a454e507c4addb938d0b0a1
[sxemacs] / src / media / sound-jack.c
1 /* sound-jack.c - play a sound over the Jack Audio Server
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-jack.h"
37
38 Lisp_Object Qjack;
39
40 #define MYSELF ADRIVER_JACK
41 #define SAMPLE_MAX_32BIT  2147483647.0f
42 #define SAMPLE_MAX_24BIT  8388607.0f
43 #define SAMPLE_MAX_16BIT  32767.0f
44 #define SAMPLE_MAX_8BIT   255.0f
45
46 static JMP_BUF jack_server_sig;
47 static int sound_jack_process(jack_nframes_t, void*);
48 static void sound_jack_shutdown_cbfun(void*);
49 static void sound_jack_error(const char*);
50 static void sound_jack_silence(float**, int, sound_jack_aj_data_t*);
51 static int sound_jack_write(float**, jack_nframes_t, audio_job_t);
52 static size_t demux_internal();
53
54 #define __JACK_DEBUG__(args...)         fprintf(stderr, "JACK " args)
55 #ifndef JACK_DEBUG_FLAG
56 #define JACK_DEBUG(args...)
57 #else
58 #define JACK_DEBUG(args...)             __JACK_DEBUG__(args)
59 #endif
60 #define JACK_DEBUG_S(args...)           JACK_DEBUG("[stream]: " args)
61 #define JACK_DEBUG_AJ(args...)          JACK_DEBUG("[audio-job]: " args)
62 #define JACK_CRITICAL(args...)          __JACK_DEBUG__("CRITICAL: " args)
63
64 \f
65 DECLARE_AUDIO_DEVICE_SIMPLE_METHS(sound_jack);
66 DEFINE_AUDIO_DEVICE_SIMPLE(sound_jack);
67
68 \f
69 static Lisp_Object
70 sound_jack_mark(ad_device_data *devdata)
71 {
72         sound_jack_data_t *sjd = devdata;
73
74         mark_object(sjd->options);
75         mark_object(sjd->server);
76
77         return Qnil;
78 }
79
80 static void
81 sound_jack_print(Lisp_Object device, Lisp_Object pcfun, int ef)
82 {
83         sound_jack_data_t *sjd = NULL;
84
85         sjd = get_audio_device_data(device);
86         /* cannot use incomplete or corrupt audio devices */
87         if (XAUDIO_DEVICE_DRIVER(device) != MYSELF || sjd == NULL) {
88                 write_c_string(" VOID", pcfun);
89                 /* now that we are here, mark AO device as dead */
90                 XAUDIO_DEVICE_STATE(device) = ASTATE_DEAD;
91                 return;
92         }
93
94         /* info about the connected output plugin */
95         write_c_string(" :server ", pcfun);
96         if (NILP(sjd->server))
97                 write_c_string("#default", pcfun);
98         else
99                 print_internal(sjd->server, pcfun, ef);
100
101         write_c_string(" :client ", pcfun);
102         if (NILP(sjd->client))
103                 write_c_string("SXEmacs", pcfun);
104         else
105                 print_internal(sjd->client, pcfun, ef);
106
107         return;
108 }
109
110 \f
111 static ad_device_data *
112 sound_jack_create(Lisp_Object jack_options)
113 {
114         /* result */
115         sound_jack_data_t *sjd = NULL;
116         /* option keywords */
117         Lisp_Object opt_server = Qnil;
118         Lisp_Object opt_client = Qnil;
119
120         /* parse options */
121         opt_server = Fplist_get(jack_options, intern(":server"), Qnil);
122         if (!NILP(opt_server) && !STRINGP(opt_server)) {
123                 wrong_type_argument(Qstringp, opt_server);
124                 return NULL;
125         }
126
127         opt_client = Fplist_get(jack_options, intern(":client"), Qnil);
128         if (!NILP(opt_client) && !STRINGP(opt_client)) {
129                 wrong_type_argument(Qstringp, opt_client);
130                 return NULL;
131         }
132
133         /* initialise and fill */
134         sjd = xnew_and_zero(sound_jack_data_t);
135         sjd->options = jack_options;
136         sjd->server = opt_server;
137         sjd->client = opt_client;
138         sjd->num_ports = 0;
139
140         return (ad_device_data*)sjd;
141 }
142
143 static void
144 sound_jack_finish(ad_device_data *data)
145 {
146         sound_jack_data_t *sjd = data;
147         if (sjd != NULL) {
148                 ;
149         }
150
151         return;
152 }
153
154 \f
155 static ad_device_data *
156 sound_jack_subthread_create(void)
157 {
158         /* result */
159         sound_jack_aj_data_t *sjsd = NULL;
160         /* jack stuff */
161         jack_client_t *client = NULL;
162         int port_flags = 0;
163         const char **ports = NULL;
164         int i;
165
166         /* Create a new playback client */
167         client = jack_client_open("SXEmacs", 0, NULL);
168         if (!client) {
169                 message(GETTEXT("audio-jack: "
170                                 "cannot open server."));
171                 return NULL;
172         }
173
174         /* initialise and fill */
175         sjsd = xnew_and_zero(sound_jack_aj_data_t);
176         sjsd->client = client;
177
178         /* list matching ports */
179         port_flags |= JackPortIsInput;
180         port_flags |= JackPortIsPhysical;
181         ports = jack_get_ports(client, NULL, NULL, port_flags);
182         for (sjsd->num_ports = 0; ports && ports[sjsd->num_ports];
183              sjsd->num_ports++);        /* just count */
184         if (!sjsd->num_ports) {
185                 message(GETTEXT("audio-jack: "
186                                 "no physical ports available."));
187                 jack_client_close(client);
188                 xfree(sjsd);
189                 return NULL;
190         }
191
192         JACK_DEBUG("initialised %d ports\n", sjsd->num_ports);
193
194         /* if (mtap->channels > sjsd->num_ports); */
195
196         /* create out output ports */
197         for (i = 0; i < sjsd->num_ports; i++) {
198                 char pname[30];
199                 int sz = snprintf(pname, sizeof(pname), "SXEmacs out_%d", i);
200                 assert(sz>=0 && sz<sizeof(pname));
201                 sjsd->ports[i] = jack_port_register(
202                         client, pname,
203                         JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0);
204                 if (!sjsd->ports[i]) {
205                         message(GETTEXT("audio-jack: "
206                                         "not enough ports available."));
207                         jack_client_close(client);
208                         xfree(sjsd);
209                         return NULL;
210                 }
211         }
212
213         sjsd->port_ptrs = ports;
214
215         return (ad_device_data*)sjsd;
216 }
217
218 static int
219 sound_jack_play(audio_job_t aj)
220 {
221         /* stream stuff */
222         mtype_audio_properties *mtap;
223         Lisp_Media_Stream *ms;
224         media_substream *mss;
225         /* device stuff */
226         Lisp_Object device;
227         Lisp_Audio_Device *lad = NULL;
228         sound_jack_data_t *sjd = NULL;
229         sound_jack_aj_data_t *sjsd = NULL;
230         int i;
231         int code =  1;
232
233         SOUND_UNPACK_MT(aj, device, ms, mss, lad, sjd, mtap);
234
235         /* get jack device */
236         JACK_DEBUG("creating subthread.\n");
237         if ((sjsd = sound_jack_subthread_create()) == NULL) {
238                 message(GETTEXT("audio-jack: "
239                                 "cannot create connection to jack server."));
240                 return 0;
241         }
242
243         /* initialise our callback semaphore */
244         SXE_SEMAPH_INIT(&(sjsd->sem));
245
246         if (SETJMP(jack_server_sig)) {
247                 JACK_CRITICAL("Caught a lethal signal.\n");
248                 warn_when_safe(
249                         Qjack, Qerror,
250                         GETTEXT("Jack daemon died or "
251                                 "send us a lethal signal! "
252                                 "This instance might have become a total mess! "
253                                 "Consider a restart."));
254                 return 1;
255         }
256
257         /* rewind the stream */
258         media_stream_meth(ms, rewind)(mss);
259         SXE_MUTEX_LOCK(&aj->mtx);
260         aj->buffer = xmalloc_atomic(SOUND_MAX_AUDIO_FRAME_SIZE);
261         aj->buffer_alloc_size = SOUND_MAX_AUDIO_FRAME_SIZE;
262         aj->resolution = (mtap->samplerate * MTPSTATE_REACT_TIME) / 1000000;
263
264         audio_job_device_data(aj) = sjsd;
265         sjsd->samplerate = mtap->samplerate;
266         sjsd->framesize = (sjsd->channels = mtap->channels) * sizeof(int32_t);
267         sjsd->demux_fun = demux_internal;
268
269         sjsd->ringbufcnt = SOUND_MAX_AUDIO_FRAME_SIZE >> 10;
270         sjsd->readpos = sjsd->writepos = sjsd->overfill = 0;
271         SXE_MUTEX_UNLOCK(&aj->mtx);
272
273         JACK_DEBUG("setting callback.\n");
274         jack_set_process_callback(sjsd->client, sound_jack_process, aj);
275         jack_set_error_function(sound_jack_error);
276         jack_on_shutdown(sjsd->client, sound_jack_shutdown_cbfun, aj);
277
278         /* now activate */
279         if (jack_activate(sjsd->client)) {
280                 message(GETTEXT("audio-jack: "
281                                 "activate failed."));
282                 goto finish;
283         }
284
285         for (i = 0; i < sjsd->num_ports; i++) {
286                 if (jack_connect(sjsd->client,
287                                  jack_port_name(sjsd->ports[i]),
288                                  sjsd->port_ptrs[i])) {
289                         message(GETTEXT("audio-jack: "
290                                         "connecting failed."));
291                         goto finish;
292                 }
293         }
294
295         JACK_DEBUG("SEMAPHORE WAIT: 0x%x@0x%x@0x%x\n",
296                    (unsigned int)&(sjsd->sem),
297                    (unsigned int)sjsd,
298                    (unsigned int)aj);
299         SXE_SEMAPH_SYNCH(&(sjsd->sem));
300         code = 0;
301         /* close and shutdown */
302 finish:
303         JACK_DEBUG("finish.\n");
304         if (sjsd->client) {
305                 jack_client_t *cli = sjsd->client;
306                 JACK_DEBUG("DISC.\n");
307                 sjsd->client = NULL;
308                 for (i = 0; i < sjsd->num_ports; i++) {
309                         jack_disconnect(cli,
310                                         jack_port_name(sjsd->ports[i]),
311                                         sjsd->port_ptrs[i]);
312                 }
313                 JACK_DEBUG("CLOSE.\n");
314                 jack_client_close(cli);
315         }
316
317         SXE_SEMAPH_FINI(&(sjsd->sem));
318
319         SXE_MUTEX_LOCK(&aj->mtx);
320         if (aj->buffer)
321                 xfree(aj->buffer);
322         aj->buffer = NULL;
323
324         if (sjsd)
325                 xfree(sjsd);
326         sjsd = NULL;
327
328         aj->state = MTSTATE_FINISHED;
329         audio_job_device_data(aj) = NULL;
330         SXE_MUTEX_UNLOCK(&aj->mtx);
331
332         return code;
333 }
334
335 \f
336 /* pull one channel out of a multi-channel stream */
337 static size_t
338 demux_internal(float *dst, char *src,
339                size_t n, int chan, sound_jack_aj_data_t *f);
340 static size_t
341 demux_internal(float *dst, char *src,
342                size_t n, int chan, sound_jack_aj_data_t *f)
343 {
344         /* dst: destination buffer */
345         /* src: source buffer */
346         /* n: number of samples */
347         /* chan: channel to frob */
348         int off = sizeof(int32_t)*chan;
349         int sr = f->samplerate;
350         int fs = f->framesize;
351
352         for (sxe_index_t i = 0; i < n; i++) {
353                 /* compute frame number to serve */
354                 int frame = i * sr/48000 * f->ratetrafo;
355                 int srcp = frame*fs + off;
356                 dst[i] = ((float)(*(int32_t*)(src+srcp)) / SAMPLE_MAX_24BIT)
357                         * f->fvol;
358         }
359
360         /* actually read frames were n * samplerate / 48000 */
361         return n * sr/48000 * f->ratetrafo;
362 }
363
364 /**
365  * error callback
366  **/
367 static void
368 sound_jack_error(const char *errmsg)
369 {
370         JACK_CRITICAL("Strange things happened.\n"
371                       "Error message: %s\n",
372                       errmsg);
373
374         LONGJMP(jack_server_sig, 1);
375
376         return;
377 }
378
379 /* shutdown callback */
380 static void
381 sound_jack_shutdown_cbfun(void *arg)
382 {
383         JACK_CRITICAL("Shutdown: %p\n", arg);
384
385         LONGJMP(jack_server_sig, 1);
386
387         return;
388 }
389
390 #ifdef EF_USE_ASYNEQ
391 static inline void
392 sound_jack_change_volume(audio_job_t aj, audio_job_event_args_t args)
393 {
394         SXE_MUTEX_LOCK(&aj->mtx);
395         aj->volume = args->volume_args;
396         SXE_MUTEX_UNLOCK(&aj->mtx);
397 }
398
399 static inline void
400 sound_jack_change_rate(audio_job_t aj, audio_job_event_args_t args)
401 {
402         SXE_MUTEX_LOCK(&aj->mtx);
403         aj->ratetrafo = args->rate_args;
404         SXE_MUTEX_UNLOCK(&aj->mtx);
405 }
406
407 static inline void
408 sound_jack_change_state(audio_job_t aj, audio_job_event_args_t args)
409 {
410         SXE_MUTEX_LOCK(&aj->mtx);
411         switch (args->state_args) {
412         case aj_pause:
413                 JACK_DEBUG_AJ("->pause state\n");
414                 aj->play_state = MTPSTATE_PAUSE;
415                 break;
416         case aj_resume:
417                 JACK_DEBUG_AJ("->resume state\n");
418                 aj->play_state = MTPSTATE_RUN;
419                 break;
420         case aj_start:
421                 JACK_DEBUG_AJ("->start state\n");
422                 aj->play_state = MTPSTATE_RUN;
423                 break;
424         case aj_stop:
425                 JACK_DEBUG_AJ("->stop state\n");
426                 aj->play_state = MTPSTATE_STOP;
427                 break;
428
429         case no_audio_job_change_states:
430         default:
431                 JACK_DEBUG_AJ("->unknown state\n");
432                 break;
433         }
434         SXE_MUTEX_UNLOCK(&aj->mtx);
435 }
436
437 static inline void
438 sound_jack_handle_aj_events(audio_job_t aj)
439         __attribute__((always_inline));
440 static inline void
441 sound_jack_handle_aj_events(audio_job_t aj)
442 {
443         sound_jack_aj_data_t *sasd;
444         audio_job_event_t ev = NULL;
445
446 #if 0
447         assert(audio_job_queue(aj));
448 #endif
449
450         SXE_MUTEX_LOCK(&aj->mtx);
451         sasd = audio_job_device_data(aj);
452         SXE_SET_UNUSED(sasd);
453
454         if ((ev = eq_noseeum_dequeue(audio_job_queue(aj))) == NULL) {
455                 SXE_MUTEX_UNLOCK(&aj->mtx);
456                 return;
457         }
458         SXE_MUTEX_UNLOCK(&aj->mtx);
459
460         JACK_DEBUG_AJ("Event 0x%lx\n", (long unsigned int)ev);
461         switch (audio_job_event_kind(ev)) {
462         case aj_change_state:
463                 JACK_DEBUG_AJ("change state event\n");
464                 sound_jack_change_state(aj, &audio_job_event_args(ev));
465                 break;
466         case aj_change_volume:
467                 JACK_DEBUG_AJ("change volume event\n");
468                 sound_jack_change_volume(aj, &audio_job_event_args(ev));
469                 break;
470         case aj_change_rate:
471                 JACK_DEBUG_AJ("change rate event\n");
472                 sound_jack_change_rate(aj, &audio_job_event_args(ev));
473                 break;
474
475         case no_audio_job_event_kinds:
476         default:
477                 JACK_CRITICAL("unknown event %d\n", audio_job_event_kind(ev));
478                 break;
479         }
480         free_audio_job_event(ev);
481 }
482 #endif  /* EF_USE_ASYNEQ */
483
484 /**
485  * JACK Callback function
486  * - nframes number of frames to fill into buffers
487  * - arg contains the media subthread to operate on
488  */
489 static int
490 sound_jack_process(jack_nframes_t nframes, void *userdata)
491 {
492         audio_job_t aj = NULL;
493         sound_jack_aj_data_t *sjsd = NULL;
494         media_substream *mss = NULL;
495         char *buffer = NULL;
496         media_thread_play_state mtp;
497         float *bufs[MAX_CHANS];
498         int curvol;
499
500         aj = userdata;
501         SXE_MUTEX_LOCK(&aj->mtx);
502         mss = aj->substream;
503         SXE_SET_UNUSED(mss);
504
505         sjsd = audio_job_device_data(aj);
506         buffer = aj->buffer;
507         SXE_SET_UNUSED(buffer);
508
509 #ifdef EF_USE_ASYNEQ
510         /* stuff on the event queue? */
511         if (audio_job_queue(aj)) {
512                 sound_jack_handle_aj_events(aj);
513         }
514 #endif
515
516         /* Set the playback volume of the stream */
517         if ((curvol = aj->volume) != sjsd->volume) {
518                 sjsd->fvol = (float)curvol / MEDIA_SAMPLE_VOLUME_NORM;
519                 sjsd->volume = curvol;
520         }
521
522         if (aj->ratetrafo != sjsd->ratetrafo) {
523                 sjsd->ratetrafo = aj->ratetrafo;
524         }
525
526         for (int i = 0; i < sjsd->num_ports; i++) {
527                 bufs[i] = jack_port_get_buffer(sjsd->ports[i], nframes);
528         }
529
530         mtp = aj->play_state;
531         switch (mtp) {
532         case MTPSTATE_RUN:
533                 sound_jack_write(bufs, nframes, aj);
534                 break;
535         case MTPSTATE_PAUSE:
536                 sound_jack_silence(bufs, nframes, sjsd);
537                 break;
538
539         case MTPSTATE_UNKNOWN:
540         case MTPSTATE_STOP:
541         case NUMBER_OF_MEDIA_THREAD_PLAY_STATES:
542         default:
543                 JACK_DEBUG_S("DRAIN. MTP state:%d\n", mtp);
544                 SXE_SEMAPH_TRIGGER(&(sjsd->sem));
545                 break;
546         }
547         SXE_MUTEX_UNLOCK(&aj->mtx);
548         return 0;
549 }
550
551 /**
552  * fill the buffers with silence
553  * - bufs num_bufs float buffers, each will contain the data of one channel
554  * - cnt number of samples in each buffer
555  * - num_bufs number of buffers
556  */
557 static void
558 sound_jack_silence(float **bufs, int cnt, sound_jack_aj_data_t *sjsd)
559 {
560         int i, j;
561         for (i = 0; i < cnt; i++)
562                 for (j = 0; j < sjsd->num_ports; j++)
563                         bufs[j][i] = 0.0f;
564 }
565
566 static int
567 sound_jack_write(float **bufs, jack_nframes_t nframes, audio_job_t aj)
568 {
569         sound_jack_aj_data_t *sjsd = audio_job_device_data(aj);
570         size_t len = 0, tmplen = 0, tmpcnt = 0;
571         sound_jack_demux_fun dmx = sjsd->demux_fun;
572         ms_read_fun __read = media_stream_meth(aj->substream->up, read);
573
574         /* cleanup buffer, move the frame overfill to the beginning */
575         if (sjsd->writepos < aj->buffer_alloc_size >> 1 &&
576             sjsd->underrun < 4) {
577                 memcpy(aj->buffer, aj->buffer+sjsd->readpos,
578                        sjsd->writepos - sjsd->readpos);
579                 sjsd->writepos -= sjsd->readpos;
580                 sjsd->readpos = 0;
581
582                 len = __read(aj->substream, aj->buffer+sjsd->writepos, nframes);
583                 if (len == -1UL) {
584                         len = 0;
585                 }
586
587                 sjsd->overfill += len;
588                 sjsd->writepos += len*sjsd->framesize;
589
590                 /* increase underrun counter to detect the end */
591                 if (len == 0)
592                         sjsd->underrun++;
593         } else if (sjsd->overfill > nframes << 2 ||
594                    sjsd->underrun >= 4) {
595                 JACK_DEBUG("having a rest\n");
596         } else {
597                 JACK_DEBUG("resetting write position.\n");
598                 memcpy(aj->buffer, aj->buffer+sjsd->readpos,
599                        sjsd->writepos - sjsd->readpos);
600                 sjsd->writepos -= sjsd->readpos;
601                 sjsd->readpos = 0;
602         }
603
604         JACK_DEBUG_S("req:%d p, got:%d p, readpos %d, writepos %d, "
605                      "overfill: %d, framesize: %d, sr: %d\n",
606                      nframes, len, sjsd->readpos, sjsd->writepos,
607                      sjsd->overfill, sjsd->framesize, sjsd->samplerate);
608
609         /* care for buffer underruns */
610         if (nframes > sjsd->overfill)
611                 tmplen = sjsd->overfill;
612         else
613                 tmplen = nframes;
614
615         /* output, i loops over the number of ports we have to fill,
616          * while j loops over the actual channels */
617         for (int i = 0, j = 0; i < sjsd->num_ports; i++) {
618                 tmpcnt = dmx(
619                         bufs[i], aj->buffer+sjsd->readpos, tmplen, j, sjsd);
620                 if (j < sjsd->channels) {
621                         j++;
622                 }
623         }
624         /* fill the rest with silence */
625         tmplen = nframes - tmplen;
626         if (tmplen > 0) {
627                 JACK_DEBUG_S("fill up with silence\n");
628                 sound_jack_silence(bufs, tmplen, sjsd);
629         }
630
631         /* modify readposition and overfill */
632         sjsd->readpos += tmpcnt * sjsd->framesize;
633         sjsd->overfill -= tmpcnt;
634
635         /* detect end of track */
636         if (sjsd->underrun >= 4 && sjsd->overfill < 64)
637                 aj->play_state = MTPSTATE_STOP;
638
639         return 0;
640 }
641
642
643 #undef MYSELF
644
645 /* sound-jack.c ends here */