1 /* sound-esd.c - play a sound over ESD
3 Copyright (C) 2006 Sebastian Freundt
5 This file is part of SXEmacs
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.
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.
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/>. */
21 /* Synched up with: Not in FSF. */
36 #include "sound-esd.h"
38 /* the name given to ESD - I think this should identify ourselves */
39 #define ESD_NAME "SXEmacs"
41 #define MYSELF ADRIVER_ESD
45 #define __ESD_DEBUG__(args...) fprintf(stderr, "ESD " args)
46 #ifndef ESD_DEBUG_FLAG
47 #define ESD_DEBUG(args...)
49 #define ESD_DEBUG(args...) __ESD_DEBUG__(args)
51 #define ESD_DEBUG_HW(args...) ESD_DEBUG("[hardware]: " args)
52 #define ESD_DEBUG_S(args...) ESD_DEBUG("[stream]: " args)
53 #define ESD_DEBUG_COE(args...) ESD_DEBUG("[coerce]: " args)
54 #define ESD_DEBUG_AJ(args...) ESD_DEBUG("[audio-job]: " args)
55 #define ESD_CRITICAL(args...) __ESD_DEBUG__("CRITICAL: " args)
58 DECLARE_AUDIO_DEVICE_SIMPLE_METHS(sound_esd);
59 DEFINE_AUDIO_DEVICE_SIMPLE(sound_esd);
63 sound_esd_mark(ad_device_data *devdata)
65 sound_esd_data_t *sed = devdata;
70 mark_object(sed->server);
76 sound_esd_print(Lisp_Object device, Lisp_Object pcfun, int ef)
78 sound_esd_data_t *sed = NULL;
80 sed = get_audio_device_data(device);
81 /* cannot use incomplete or corrupt audio devices */
82 if (XAUDIO_DEVICE_DRIVER(device) != MYSELF || sed == NULL) {
83 write_c_string(" VOID", pcfun);
84 /* now that we are here, mark AO device as dead */
85 XAUDIO_DEVICE_STATE(device) = ASTATE_DEAD;
89 /* info about the connected output plugin */
90 write_c_string(" :device ", pcfun);
91 if (NILP(sed->server))
92 write_c_string("#default", pcfun);
94 print_internal(sed->server, pcfun, ef);
101 sound_esd_close_device(sound_esd_data_t *sed)
105 ESD_DEBUG_HW("socket closed\n");
111 sound_esd_init_socket(sound_esd_data_t *sed, sound_esd_aj_data_t *sesd)
113 /* convert header information into ESD flags */
115 sesd->flags = ESD_STREAM | ESD_PLAY;
116 sesd->flags |= ESD_BITS16;
117 sesd->samplewidth = 16;
118 sesd->channels = sesd->mtap->channels;
119 sesd->framesize = sesd->channels * sizeof(int16_t);
120 sesd->samplerate = sesd->mtap->samplerate;
122 switch (sesd->mtap->channels) {
124 sesd->flags |= ESD_MONO;
127 sesd->flags |= ESD_STEREO;
130 sesd->flags |= ESD_STEREO;
131 ADD_MEDIA_SAMPLE_EFFECT(
132 sesd->coe_chain, sesd->coe_ch_cnt,
133 MEDIA_SAMPLE_EFFECT(sxe_mse_5ch_to_2ch), NULL);
136 message(GETTEXT("audio-esd: "
137 "%d channels - only 1 or 2 supported"),
138 sesd->mtap->channels);
145 static ad_device_data *
146 sound_esd_create(Lisp_Object esd_options)
149 sound_esd_data_t *sed = NULL;
150 /* option keywords */
151 Lisp_Object opt_server;
152 Lisp_Object opt_port;
155 opt_server = Fplist_get(esd_options, intern(":server"), Qnil);
156 if (!NILP(opt_server) && !STRINGP(opt_server)) {
157 wrong_type_argument(Qstringp, opt_server);
161 opt_port = Fplist_get(esd_options, intern(":port"), Qnil);
162 if (!NILP(opt_port) && !NATNUMP(opt_port)) {
163 wrong_type_argument(Qnatnump, opt_port);
167 /* initialise and fill */
168 sed = xnew_and_zero(sound_esd_data_t);
169 sed->server = opt_server;
171 return (ad_device_data*)sed;
175 sound_esd_finish(ad_device_data *data)
177 sound_esd_data_t *sed = data;
181 sound_esd_close_device(sed);
183 ESD_DEBUG("audio-device finished.\n");
190 sound_esd_change_volume(audio_job_t aj, audio_job_event_args_t args)
192 SXE_MUTEX_LOCK(&aj->mtx);
193 aj->volume = args->volume_args;
194 SXE_MUTEX_UNLOCK(&aj->mtx);
198 sound_esd_change_rate(audio_job_t aj, audio_job_event_args_t args)
200 SXE_MUTEX_LOCK(&aj->mtx);
201 aj->ratetrafo = args->rate_args;
202 SXE_MUTEX_UNLOCK(&aj->mtx);
206 sound_esd_change_state(audio_job_t aj, audio_job_event_args_t args)
208 SXE_MUTEX_LOCK(&aj->mtx);
209 switch (args->state_args) {
211 ESD_DEBUG_AJ("->pause state\n");
212 aj->play_state = MTPSTATE_PAUSE;
215 ESD_DEBUG_AJ("->resume state\n");
216 aj->play_state = MTPSTATE_RUN;
219 ESD_DEBUG_AJ("->start state\n");
222 ESD_DEBUG_AJ("->stop state\n");
223 aj->play_state = MTPSTATE_STOP;
225 case no_audio_job_change_states:
227 ESD_DEBUG_AJ("->unknown state\n");
230 SXE_MUTEX_UNLOCK(&aj->mtx);
234 sound_esd_handle_aj_events(audio_job_t aj)
235 __attribute__((always_inline));
237 sound_esd_handle_aj_events(audio_job_t aj)
239 sound_esd_aj_data_t *sasd;
240 audio_job_event_t ev = NULL;
243 assert(audio_job_queue(aj));
246 SXE_MUTEX_LOCK(&aj->mtx);
247 sasd = audio_job_device_data(aj);
248 if ((ev = eq_noseeum_dequeue(audio_job_queue(aj))) == NULL) {
249 SXE_MUTEX_UNLOCK(&aj->mtx);
252 SXE_MUTEX_UNLOCK(&aj->mtx);
254 ESD_DEBUG_AJ("Event 0x%lx\n", (long unsigned int)ev);
255 switch (audio_job_event_kind(ev)) {
256 case aj_change_state:
257 ESD_DEBUG_AJ("change state event\n");
258 sound_esd_change_state(aj, &audio_job_event_args(ev));
260 case aj_change_volume:
261 ESD_DEBUG_AJ("change volume event\n");
262 sound_esd_change_volume(aj, &audio_job_event_args(ev));
265 ESD_DEBUG_AJ("change rate event\n");
266 sound_esd_change_rate(aj, &audio_job_event_args(ev));
269 case no_audio_job_event_kinds:
271 ESD_CRITICAL("unknown event\n");
274 free_audio_job_event(ev);
276 #endif /* EF_USE_ASYNEQ */
279 sound_esd_play(audio_job_t aj)
282 Lisp_Media_Stream *ms;
283 media_substream *mss;
285 media_thread_play_state mtp;
288 Lisp_Audio_Device *lad = NULL;
289 sound_esd_data_t *sed = NULL;
293 sxe_media_sample_t *tmpbuf;
294 /* esd socket stuff */
297 char *hoststr = NULL;
298 /* subthread stuff */
299 sound_esd_aj_data_t _sesd, *sesd = &_sesd;
300 sxe_mse_volume_args _volargs, *volargs = &_volargs;
301 sxe_mse_rerate_args _rrargs, *rrargs = &_rrargs;
303 int alloced_myself = 0;
305 /* unpack the media thread */
306 SOUND_UNPACK_MT(aj, device, ms, mss, lad, sed, sesd->mtap);
308 /* this lock is totally unnecessary */
312 sesd->samplerate = sesd->samplewidth = sesd->channels = 0;
314 sesd->coe_ch_cnt = 0;
316 /* init the socket */
317 sound_esd_init_socket(sed, sesd);
319 /* hm, use the given options */
320 hoststr = (NILP(sed->server) ? NULL : (char*)XSTRING_DATA(sed->server));
321 sesd->sock = esd_play_stream(
322 sesd->flags, sesd->samplerate, hoststr, "SXEmacs");
326 /* rewind the stream */
327 media_stream_meth(ms, rewind)(mss);
329 /* the volume effect */
330 ADD_MEDIA_SAMPLE_EFFECT(
331 sesd->coe_chain, sesd->coe_ch_cnt,
332 MEDIA_SAMPLE_EFFECT(sxe_mse_volume), volargs);
333 volargs->num_channels = sesd->channels;
335 /* the rerate effect */
336 ADD_MEDIA_SAMPLE_EFFECT(
337 sesd->coe_chain, sesd->coe_ch_cnt,
338 MEDIA_SAMPLE_EFFECT(sxe_mse_rerate), rrargs);
339 rrargs->num_channels = sesd->channels;
340 rrargs->srcrate = rrargs->tgtrate = 1;
342 ESD_DEBUG_COE("have %d coerce functions in my chain.\n",
345 XAUDIO_DEVICE_STATE(device) = ASTATE_ALIVE;
347 /* play chunks of the stream */
348 SXE_MUTEX_LOCK(&aj->mtx);
349 if (aj->buffer_alloc_size < SOUND_MAX_AUDIO_FRAME_SIZE) {
351 aj->buffer = xmalloc_atomic(SOUND_MAX_AUDIO_FRAME_SIZE);
352 aj->buffer_alloc_size = SOUND_MAX_AUDIO_FRAME_SIZE;
354 SXE_MUTEX_UNLOCK(&aj->mtx);
355 tmpbuf = (sxe_media_sample_t*)aj->buffer;
356 resolution = (sesd->mtap->samplerate * MTPSTATE_REACT_TIME) / 1000000;
358 while (aj->play_state != MTPSTATE_STOP) {
361 /* handle job events */
362 if (audio_job_queue(aj)) {
363 sound_esd_handle_aj_events(aj);
367 SXE_MUTEX_LOCK(&aj->mtx);
368 mtp = aj->play_state;
369 SXE_MUTEX_UNLOCK(&aj->mtx);
372 len = media_stream_meth(ms, read)(
373 mss, aj->buffer, resolution);
375 ESD_DEBUG_S("finished\n");
376 SXE_MUTEX_LOCK(&aj->mtx);
377 aj->play_state = MTPSTATE_STOP;
378 SXE_MUTEX_UNLOCK(&aj->mtx);
382 /* set up the volume args */
383 volargs->volume[0] = volargs->volume[1] =
385 /* set up the rerate args */
386 rrargs->tweak = aj->ratetrafo;
389 tmplen becomes the number of samples */
390 tmplen = sesd->channels*len;
391 for (i = 0; i < sesd->coe_ch_cnt; i++) {
392 ESD_DEBUG_COE("calling coerce "
393 "%d on b:0x%x l:%d\n",
394 i, (unsigned int)tmpbuf, tmplen);
395 tmplen = CALL_MEDIA_SAMPLE_EFFECT(
397 tmpbuf, tmpbuf, tmplen);
400 /* bring back to S16 */
401 MEDIA_SAMPLE_FORMAT_DOWNSAMPLE(sxe_msf_S16)(
402 aj->buffer, aj->buffer, tmplen);
405 sesd->sock, aj->buffer,
406 tmplen*sizeof(int16_t));
408 ESD_DEBUG_S("writing to socket failed: %d.\n",
410 SXE_MUTEX_LOCK(&aj->mtx);
411 aj->play_state = MTPSTATE_STOP;
412 SXE_MUTEX_UNLOCK(&aj->mtx);
416 ESD_DEBUG("sleeping for %d\n", resolution);
420 case MTPSTATE_UNKNOWN:
422 case NUMBER_OF_MEDIA_THREAD_PLAY_STATES:
424 ESD_DEBUG("ACK, quit\n");
425 SXE_MUTEX_LOCK(&aj->mtx);
426 aj->play_state = MTPSTATE_STOP;
427 SXE_MUTEX_UNLOCK(&aj->mtx);
432 /* close and shutdown */
433 SXE_MUTEX_LOCK(&aj->mtx);
434 if (alloced_myself && aj->buffer) {
438 aj->buffer_alloc_size = 0;
439 SXE_MUTEX_UNLOCK(&aj->mtx);
441 sound_esd_close_device(sed);
442 /* we better close the socket now */
450 /* sound-esd.c ends here */