1 /* New Generation Sound Functions.
2 Copyright (C) 2006 Sebastian Freundt
4 This file is part of SXEmacs
6 SXEmacs is free software: you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation, either version 3 of the License, or
9 (at your option) any later version.
11 SXEmacs is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with this program. If not, see <http://www.gnu.org/licenses/>. */
20 /* Synched up with: Not in FSF. */
22 #ifndef INCLUDED_sound_h_
23 #define INCLUDED_sound_h_
26 #include "semaphore.h"
28 #include "events/event-queue.h"
31 extern Lisp_Object Q_device, Q_keep_open, Q_server, Q_client;
34 typedef enum audio_drivers audio_driver;
35 typedef enum audio_states audio_state;
36 typedef enum media_thread_states media_thread_state;
37 typedef enum media_thread_play_states media_thread_play_state;
38 typedef struct ad_meths ad_meths;
39 typedef struct Lisp_Audio_Device Lisp_Audio_Device;
41 typedef enum media_thread_states media_thread_state_t;
42 typedef enum media_thread_play_states media_thread_play_state_t;
43 typedef struct audio_job_s *audio_job_t;
45 typedef void ad_device_data;
46 typedef ad_device_data*(*ad_create_fun)(Lisp_Object options);
47 typedef void(*ad_finish_fun)(ad_device_data*);
48 typedef void(*ad_print_fun)(Lisp_Object device, Lisp_Object, int);
49 typedef Lisp_Object(*ad_mark_fun)(ad_device_data *device_data);
50 typedef int(*ad_play_fun)(audio_job_t);
51 typedef int(*ad_record_fun)(audio_job_t);
64 NUMBER_OF_AUDIO_DRIVERS
72 NUMBER_OF_AUDIO_STATES
76 #ifdef ALL_DEBUG_FLAGS
77 #undef SOUND_DEBUG_FLAG
78 #define SOUND_DEBUG_FLAG
81 #define __SOUND_DEBUG__(args...) fprintf(stderr, "SOUND " args)
82 #ifndef SOUND_DEBUG_FLAG
83 #define SOUND_DEBUG(args...)
85 #define SOUND_DEBUG(args...) __SOUND_DEBUG__(args)
87 #define SOUND_DEBUG_DEV(args...) SOUND_DEBUG("[audio-device]: " args)
88 #define SOUND_DEBUG_MT(args...) SOUND_DEBUG("[media-thread]: " args)
89 #define SOUND_DEBUG_MW(args...) SOUND_DEBUG("[media-workers]: " args)
90 #define SOUND_DEBUG_MW_ENQ(args...) \
91 SOUND_DEBUG_MW("[enqueue]: st:0x%x, " args)
92 #define SOUND_DEBUG_MW_DEQ(args...) \
93 SOUND_DEBUG_MW("[dequeue]: st:0x%x, " args)
94 #define SOUND_DEBUG_PT(args...) SOUND_DEBUG("[pthread]: " args)
95 #define SOUND_DEBUG_AJ(args...) SOUND_DEBUG("[audio-job]: " args)
96 #define SOUND_CRITICAL(args...) __SOUND_DEBUG__("CRITICAL: " args)
98 /* now device driver dependent stuff */
100 ad_create_fun create;
101 ad_finish_fun finish;
105 ad_record_fun record;
108 extern Lisp_Object Vdefault_audio_device;
110 struct Lisp_Audio_Device {
111 struct lcrecord_header lheader;
114 const ad_meths *meths;
115 ad_device_data *device_data;
116 pthread_mutex_t device_data_mtx;
119 DECLARE_LRECORD(audio_device, Lisp_Audio_Device);
120 #define XAUDIO_DEVICE(x) XRECORD(x, audio_device, Lisp_Audio_Device)
121 #define XSETAUDIO_DEVICE(x, p) XSETRECORD(x, p, audio_device)
122 #define wrap_audio_device(p) wrap_object(p)
123 #define AUDIO_DEVICEP(x) RECORDP(x, audio_device)
124 #define CHECK_AUDIO_DEVICE(x) CHECK_RECORD(x, audio_device)
125 #define CONCHECK_AUDIO_DEVICE(x) CONCHECK_RECORD(x, audio_device)
127 #define audio_device_driver(ad) ((ad)->driver)
128 #define audio_device_state(ad) ((ad)->state)
129 #define audio_device_data(ad) ((ad)->device_data)
130 #define audio_device_set_meths(_ad, _m) ((_ad)->meths = _m)
131 #define audio_device_meths(_ad) ((_ad)->meths)
132 #define audio_device_meth(_ad, _m) (audio_device_meths(_ad)->_m)
133 #define XAUDIO_DEVICE_DRIVER(x) (audio_device_driver(XAUDIO_DEVICE(x)))
134 #define XAUDIO_DEVICE_STATE(x) (audio_device_state(XAUDIO_DEVICE(x)))
135 #define XAUDIO_DEVICE_DATA(x) (audio_device_data(XAUDIO_DEVICE(x)))
136 #define XAUDIO_DEVICE_SET_METHS(_d, _m) (audio_device_set_meths(_d, _m))
137 #define XAUDIO_DEVICE_METHS(_d) (audio_device_meths(XAUDIO_DEVICE(_d)))
138 #define XAUDIO_DEVICE_METH(_d, _m) (audio_device_meth(XAUDIO_DEVICE(_d), _m))
140 extern audio_driver decode_audio_type(Lisp_Object);
141 extern audio_driver decode_audio_device(Lisp_Object);
142 extern void *get_audio_device_data(Lisp_Object);
143 extern Lisp_Audio_Device *get_audio_device(Lisp_Object);
145 EXFUN(Fmake_audio_device, MANY);
146 EXFUN(Faudio_device_p, 1);
148 EXFUN(Fdelete_audio_device, 1); /* too dangerous at the moment */
151 #define DECLARE_AUDIO_DEVICE(_name) \
152 extern ad_meths _name[1]
153 #define DECLARE_AUDIO_DEVICE_SIMPLE_METHS(_name) \
154 static ad_device_data *_name##_create(Lisp_Object); \
155 static void _name##_finish(ad_device_data*); \
156 static void _name##_print(Lisp_Object, Lisp_Object, int); \
157 static Lisp_Object _name##_mark(ad_device_data*); \
158 static int _name##_play(audio_job_t); \
159 static int _name##_record(audio_job_t); \
160 /* we currently have no record support so define a nop here */ \
162 _name##_record(audio_job_t SXE_UNUSED(foo)) \
167 #define DEFINE_AUDIO_DEVICE_CUSTOM(_na, _cr, _fi, _pr, _ma, _pl, _re) \
168 ad_meths _na[1] = { { (_cr), (_fi), (_pr), (_ma), (_pl), (_re) } }
169 #define DEFINE_AUDIO_DEVICE(_name, _crea, _fini, _prin, _mark, _play, _reco) \
170 DEFINE_AUDIO_DEVICE_CUSTOM((_name), \
177 #define DEFINE_AUDIO_DEVICE_SIMPLE(_name) \
178 DEFINE_AUDIO_DEVICE(_name, \
179 create, finish, print, mark, play, record)
180 #define DEFINE_AUDIO_DEVICE_EMPTY(_name) \
181 DEFINE_AUDIO_DEVICE_CUSTOM((_name), \
182 NULL, NULL, NULL, NULL, NULL, NULL)
183 #define DEFINE_AUDIO_DEVICE_EMPTY_BUT_PLAY(_name, _play) \
184 DEFINE_AUDIO_DEVICE_CUSTOM((_name), \
185 NULL, NULL, NULL, NULL, (_play), NULL)
186 #define SET_AUDIO_DEVICE_FUNCTION_CUSTOM(_device, _slot, _function) \
187 (_device->_slot = _function)
188 #define SET_AUDIO_DEVICE_FUNCTION(_device, _function) \
189 SET_AUDIO_DEVICE_FUNCTION_CUSTOM(_device, \
191 _device##_##_function)
194 /* maximal number of channels, 8ch should be okay */
195 #define SOUND_MAX_CHANNELS 8
196 /* maximal sample width */
197 #define SOUND_MAX_SAMPLE_WIDTH sizeof(uint32_t)
198 /* maximal sampling frequency, could be 96 kHz though */
199 #define SOUND_MAX_SAMPLE_FREQ 48000
200 /* 1 sec of sound at extreme values */
201 #define SOUND_MAX_AUDIO_FRAME_SIZE \
202 SOUND_MAX_SAMPLE_FREQ * SOUND_MAX_SAMPLE_WIDTH * SOUND_MAX_CHANNELS
205 * Threads are the containers for the streams. Streams are stored
206 * (along with devices) inside threads, while substreams are stored inside
207 * subthreads. In source/sink language, a thread is the cable to plug a source
208 * (stream) to a sink (device).
213 * ,-----> | Thread | <-----,
222 * first | | up | last
224 * +==========+ next +==========+ next +==========+
225 * |subthread1| <-----> |subthread2| <-----> |subthread3|
226 * +----------+ prev +----------+ prev +----------+
227 * |substream1| |substream2| |substream3|
228 * |pthread_t1| |pthread_t2| |pthread_t3|
229 * |privdata1 | |privdata2 | |privdata3 |
230 * +==========+ +==========+ +==========+
232 * Note: It is yet _not_ possible to specify different devices for each
233 * subthread. This will require another split of the device structure into
234 * a device+subdevice tree.
237 /* some defs for the pthreaded version */
239 enum media_thread_states {
243 NUMBER_OF_MEDIA_THREAD_STATES
246 enum media_thread_play_states {
251 NUMBER_OF_MEDIA_THREAD_PLAY_STATES
255 /* data and handling info */
258 media_substream *substream;
260 /* state information */
261 media_thread_state state;
262 media_thread_play_state play_state;
263 #if defined(HAVE_THREADS)
266 #if defined(EF_USE_ASYNEQ)
270 /* anonymous data, used by the various backends */
271 /* do not use it, it is not freed by gc */
272 void *job_device_data;
273 void *job_stream_data;
277 size_t buffer_alloc_size;
279 /* audio-specific job options */
284 uint8_t chanvol[MEDIA_MAX_AUDIO_CHANNELS];
287 /* communication objects */
289 Lisp_Object sentinel;
292 #define MTPSTATE_REACT_TIME 20000 /* in usec */
294 enum sxe_finalise_conds {
295 SXE_SMPH_SHALL_START,
296 SXE_SMPH_HAVE_STARTED,
297 SXE_SMPH_SHALL_FINISH,
298 SXE_SMPH_HAVE_FINISHED
301 #define audio_job(_x) (((audio_job_t)worker_job_data(_x)))
302 #define audio_job_stream(_x) (audio_job(_x)->stream)
303 #define audio_job_device(_x) (audio_job(_x)->device)
304 #define audio_job_sentinel(_x) (audio_job(_x)->sentinel)
305 #define audio_job_device_data(_aj) (_aj)->job_device_data
306 #define audio_job_stream_data(_aj) (_aj)->job_stream_data
307 #define audio_job_queue(_aj) (_aj)->queue
308 #define XAUDIO_JOB(_x) ((audio_job_t)(XWORKER_JOB_DATA(_x)))
309 #define XAUDIO_JOB_STREAM(_x) audio_job_stream(XWORKER_JOB(_x))
310 #define XAUDIO_JOB_DEVICE(_x) audio_job_device(XWORKER_JOB(_x))
311 #define XAUDIO_JOB_SENTINEL(_x) audio_job_sentinel(XWORKER_JOB(_x))
312 #define XAUDIO_JOB_QUEUE(_x) (audio_job_queue(XAUDIO_JOB(_x)))
313 #define AUDIO_JOBP(_x) \
314 (WORKER_JOBP(_x) && XWORKER_JOB_HANDLER(_x) == &audio_job_handler)
315 #define CHECK_AUDIO_JOB(_x) \
317 if (!AUDIO_JOBP(_x)) \
318 dead_wrong_type_argument(Qaudio_jobp, _x); \
320 #define CONCHECK_AUDIO_JOB(_x) \
322 if (!AUDIO_JOBP(_x)) \
323 return wrong_type_argument(Qaudio_jobp, _x); \
327 #define SXE_WORKERS_SMPH_INIT(_mt) \
328 SOUND_DEBUG_MW("initialising state semaphore: 0x%x\n", \
329 (unsigned int)(_mt)); \
330 SXE_MSEMAPH_INIT(&(_mt)->state_smph);
331 #define SXE_WORKERS_SMPH_FINI(_mt) \
332 SOUND_DEBUG_MW("finishing state semaphore: 0x%x\n", \
333 (unsigned int)(_mt)); \
334 SXE_MSEMAPH_FINI(&(_mt)->state_smph);
335 #define SXE_WORKERS_SMPH_LOCK(_mt) \
336 SOUND_DEBUG_MW("locking mutex of state semaphore: 0x%x\n", \
337 (unsigned int)(_mt)); \
338 SXE_MSEMAPH_LOCK(&(_mt)->state_smph);
339 #define SXE_WORKERS_SMPH_UNLOCK(_mt) \
340 SOUND_DEBUG_MW("unlocking mutex of state semaphore: 0x%x\n", \
341 (unsigned int)(_mt)); \
342 SXE_MSEMAPH_UNLOCK(&(_mt)->state_smph);
344 #define SXE_WORKERS_SMPH_SIGNAL(_mt, _state) \
345 SOUND_DEBUG_MW("signalling "#_state": 0x%x\n", \
346 (unsigned int)(_mt)); \
347 SXE_MSEMAPH_SIGNAL(&(_mt)->state_smph, _state); \
348 SOUND_DEBUG_MW("signalled "#_state": 0x%x\n", \
349 (unsigned int)(_mt));
350 #define SXE_WORKERS_SMPH_BROADCAST(_mt, _state) \
351 SOUND_DEBUG_MW("broadcasting "#_state": 0x%x\n", \
352 (unsigned int)(_mt)); \
353 SXE_MSEMAPH_BROADCAST(&(_mt)->state_smph, _state); \
354 SOUND_DEBUG_MW("broadcast "#_state": 0x%x\n", \
355 (unsigned int)(_mt));
356 #define SXE_WORKERS_SMPH_WAIT(_mt, _state) \
357 SOUND_DEBUG_MW("waiting for "#_state": 0x%x\n", \
358 (unsigned int)(_mt)); \
359 SXE_MSEMAPH_WAIT(&(_mt)->state_smph, _state); \
360 SOUND_DEBUG_MW("ACK, got "#_state": 0x%x\n", \
361 (unsigned int)(_mt));
363 #define SXE_WORKERS_SMPH_SYNCH(_mt, _state) \
364 SOUND_DEBUG_MW("waiting for "#_state": 0x%x\n", \
365 (unsigned int)(_mt)); \
366 SXE_MSEMAPH_SYNCH(&(_mt)->state_smph, _state); \
367 SOUND_DEBUG_MW("ACK, got "#_state": 0x%x\n", \
368 (unsigned int)(_mt));
369 #define WITH_SXE_WORKERS_SMPH_SYNCH(_mt, _state, args...) \
370 SOUND_DEBUG_MW("waiting for "#_state": 0x%x\n", \
371 (unsigned int)(_mt)); \
372 WITH_SXE_MSEMAPH_SYNCH(&(_mt)->state_smph, _state, args); \
373 SOUND_DEBUG_MW("ACK, got "#_state": 0x%x\n", \
374 (unsigned int)(_mt));
375 #define SXE_WORKERS_SMPH_TRIGGER(_mt, _state) \
376 SOUND_DEBUG_MW("triggering "#_state": 0x%x\n", \
377 (unsigned int)(_mt)); \
378 SXE_MSEMAPH_TRIGGER(&(_mt)->state_smph, _state); \
379 SOUND_DEBUG_MW("triggered "#_state": 0x%x\n", \
380 (unsigned int)(_mt));
381 #define SXE_WORKERS_SMPH_TRIGGER_ALL(_mt, _state) \
382 SOUND_DEBUG_MW("triggering all "#_state": 0x%x\n", \
383 (unsigned int)(_mt)); \
384 SXE_MSEMAPH_TRIGGER_ALL(&(_mt)->state_smph, _state); \
385 SOUND_DEBUG_MW("triggered all "#_state": 0x%x\n", \
386 (unsigned int)(_mt));
389 /* convenience macros, dunno how sensible they are */
390 #define SOUND_UNPACK_MT(aj, dev, ms, mss, ad, cad, mtap) \
391 /* unpack the media thread */ \
392 dev = (aj)->device; \
393 ms = XMEDIA_STREAM((aj)->stream); \
394 mss = aj->substream; \
396 /* unpack device */ \
397 ad = get_audio_device(dev); \
398 cad = get_audio_device_data(dev); \
400 /* cannot use incomplete or corrupt audio devices */ \
401 if ((ad) == NULL || cad == NULL || \
402 media_stream_meth((ms), rewind) == NULL || \
403 media_stream_meth((ms), read) == NULL || \
404 XAUDIO_DEVICE_DRIVER(dev) != MYSELF) \
407 /* cannot use most devices to play non-audio stuff */ \
408 if (media_substream_type(mss) != MTYPE_AUDIO) \
410 mtap = media_substream_type_properties(mss).aprops;
414 /* for the communication with (async'ly) running audio jobs */
415 typedef struct audio_job_event_s *audio_job_event_t;
416 typedef enum audio_job_event_kinds audio_job_event_kind_t;
417 typedef enum audio_job_change_states audio_job_change_state_t;
418 typedef int audio_job_change_volume_t;
419 typedef float audio_job_change_rate_t;
420 typedef union audio_job_event_args_u *audio_job_event_args_t;
422 enum audio_job_change_states {
427 no_audio_job_change_states
430 union audio_job_event_args_u {
431 audio_job_change_state_t state_args;
432 audio_job_change_volume_t volume_args;
433 audio_job_change_rate_t rate_args;
436 enum audio_job_event_kinds {
440 no_audio_job_event_kinds
443 struct audio_job_event_s {
444 audio_job_event_kind_t kind;
445 union audio_job_event_args_u args;
449 #define audio_job_event_kind(_x) ((_x)->kind)
450 #define audio_job_event_args(_x) ((_x)->args)
451 #define audio_job_event_fau(_x) ((_x)->free_after_use)
453 /* some often used events */
454 extern struct audio_job_event_s pause_event;
455 extern struct audio_job_event_s resume_event;
456 extern struct audio_job_event_s start_event;
457 extern struct audio_job_event_s stop_event;
458 extern struct audio_job_event_s change_volume_event;
459 extern struct audio_job_event_s change_rate_event;
461 extern_inline audio_job_event_t
462 make_audio_job_event(audio_job_event_kind_t kind);
464 extern_inline audio_job_event_t
465 make_audio_job_event(audio_job_event_kind_t kind)
467 audio_job_event_t aje = xnew(struct audio_job_event_s);
468 audio_job_event_kind(aje) = kind;
469 audio_job_event_fau(aje) = 1;
474 free_audio_job_event(audio_job_event_t aje)
476 if (audio_job_event_fau(aje)) {
482 #endif /* EF_USE_ASYNEQ */
484 #endif /* INCLUDED_sound_h_ */