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);
65 NUMBER_OF_AUDIO_DRIVERS
73 NUMBER_OF_AUDIO_STATES
77 #ifdef ALL_DEBUG_FLAGS
78 #undef SOUND_DEBUG_FLAG
79 #define SOUND_DEBUG_FLAG
82 #define __SOUND_DEBUG__(args...) fprintf(stderr, "SOUND " args)
83 #ifndef SOUND_DEBUG_FLAG
84 #define SOUND_DEBUG(args...)
86 #define SOUND_DEBUG(args...) __SOUND_DEBUG__(args)
88 #define SOUND_DEBUG_DEV(args...) SOUND_DEBUG("[audio-device]: " args)
89 #define SOUND_DEBUG_MT(args...) SOUND_DEBUG("[media-thread]: " args)
90 #define SOUND_DEBUG_MW(args...) SOUND_DEBUG("[media-workers]: " args)
91 #define SOUND_DEBUG_MW_ENQ(args...) \
92 SOUND_DEBUG_MW("[enqueue]: st:0x%x, " args)
93 #define SOUND_DEBUG_MW_DEQ(args...) \
94 SOUND_DEBUG_MW("[dequeue]: st:0x%x, " args)
95 #define SOUND_DEBUG_PT(args...) SOUND_DEBUG("[pthread]: " args)
96 #define SOUND_DEBUG_AJ(args...) SOUND_DEBUG("[audio-job]: " args)
97 #define SOUND_CRITICAL(args...) __SOUND_DEBUG__("CRITICAL: " args)
99 /* now device driver dependent stuff */
101 ad_create_fun create;
102 ad_finish_fun finish;
106 ad_record_fun record;
109 extern Lisp_Object Vdefault_audio_device;
111 struct Lisp_Audio_Device {
112 struct lcrecord_header lheader;
115 const ad_meths *meths;
116 ad_device_data *device_data;
117 pthread_mutex_t device_data_mtx;
120 DECLARE_LRECORD(audio_device, Lisp_Audio_Device);
121 #define XAUDIO_DEVICE(x) XRECORD(x, audio_device, Lisp_Audio_Device)
122 #define XSETAUDIO_DEVICE(x, p) XSETRECORD(x, p, audio_device)
123 #define wrap_audio_device(p) wrap_object(p)
124 #define AUDIO_DEVICEP(x) RECORDP(x, audio_device)
125 #define CHECK_AUDIO_DEVICE(x) CHECK_RECORD(x, audio_device)
126 #define CONCHECK_AUDIO_DEVICE(x) CONCHECK_RECORD(x, audio_device)
128 #define audio_device_driver(ad) ((ad)->driver)
129 #define audio_device_state(ad) ((ad)->state)
130 #define audio_device_data(ad) ((ad)->device_data)
131 #define audio_device_set_meths(_ad, _m) ((_ad)->meths = _m)
132 #define audio_device_meths(_ad) ((_ad)->meths)
133 #define audio_device_meth(_ad, _m) (audio_device_meths(_ad)->_m)
134 #define XAUDIO_DEVICE_DRIVER(x) (audio_device_driver(XAUDIO_DEVICE(x)))
135 #define XAUDIO_DEVICE_STATE(x) (audio_device_state(XAUDIO_DEVICE(x)))
136 #define XAUDIO_DEVICE_DATA(x) (audio_device_data(XAUDIO_DEVICE(x)))
137 #define XAUDIO_DEVICE_SET_METHS(_d, _m) (audio_device_set_meths(_d, _m))
138 #define XAUDIO_DEVICE_METHS(_d) (audio_device_meths(XAUDIO_DEVICE(_d)))
139 #define XAUDIO_DEVICE_METH(_d, _m) (audio_device_meth(XAUDIO_DEVICE(_d), _m))
141 extern audio_driver decode_audio_type(Lisp_Object);
142 extern audio_driver decode_audio_device(Lisp_Object);
143 extern void *get_audio_device_data(Lisp_Object);
144 extern Lisp_Audio_Device *get_audio_device(Lisp_Object);
146 EXFUN(Fmake_audio_device, MANY);
147 EXFUN(Faudio_device_p, 1);
149 EXFUN(Fdelete_audio_device, 1); /* too dangerous at the moment */
152 #define DECLARE_AUDIO_DEVICE(_name) \
153 extern ad_meths _name[1]
154 #define DECLARE_AUDIO_DEVICE_SIMPLE_METHS(_name) \
155 static ad_device_data *_name##_create(Lisp_Object); \
156 static void _name##_finish(ad_device_data*); \
157 static void _name##_print(Lisp_Object, Lisp_Object, int); \
158 static Lisp_Object _name##_mark(ad_device_data*); \
159 static int _name##_play(audio_job_t); \
160 static int _name##_record(audio_job_t); \
161 /* we currently have no record support so define a nop here */ \
163 _name##_record(audio_job_t UNUSED(foo)) \
168 #define DEFINE_AUDIO_DEVICE_CUSTOM(_na, _cr, _fi, _pr, _ma, _pl, _re) \
169 ad_meths _na[1] = { { (_cr), (_fi), (_pr), (_ma), (_pl), (_re) } }
170 #define DEFINE_AUDIO_DEVICE(_name, _crea, _fini, _prin, _mark, _play, _reco) \
171 DEFINE_AUDIO_DEVICE_CUSTOM((_name), \
178 #define DEFINE_AUDIO_DEVICE_SIMPLE(_name) \
179 DEFINE_AUDIO_DEVICE(_name, \
180 create, finish, print, mark, play, record)
181 #define DEFINE_AUDIO_DEVICE_EMPTY(_name) \
182 DEFINE_AUDIO_DEVICE_CUSTOM((_name), \
183 NULL, NULL, NULL, NULL, NULL, NULL)
184 #define DEFINE_AUDIO_DEVICE_EMPTY_BUT_PLAY(_name, _play) \
185 DEFINE_AUDIO_DEVICE_CUSTOM((_name), \
186 NULL, NULL, NULL, NULL, (_play), NULL)
187 #define SET_AUDIO_DEVICE_FUNCTION_CUSTOM(_device, _slot, _function) \
188 (_device->_slot = _function)
189 #define SET_AUDIO_DEVICE_FUNCTION(_device, _function) \
190 SET_AUDIO_DEVICE_FUNCTION_CUSTOM(_device, \
192 _device##_##_function)
195 /* maximal number of channels, 8ch should be okay */
196 #define SOUND_MAX_CHANNELS 8
197 /* maximal sample width */
198 #define SOUND_MAX_SAMPLE_WIDTH sizeof(uint32_t)
199 /* maximal sampling frequency, could be 96 kHz though */
200 #define SOUND_MAX_SAMPLE_FREQ 48000
201 /* 1 sec of sound at extreme values */
202 #define SOUND_MAX_AUDIO_FRAME_SIZE \
203 SOUND_MAX_SAMPLE_FREQ * SOUND_MAX_SAMPLE_WIDTH * SOUND_MAX_CHANNELS
206 * Threads are the containers for the streams. Streams are stored
207 * (along with devices) inside threads, while substreams are stored inside
208 * subthreads. In source/sink language, a thread is the cable to plug a source
209 * (stream) to a sink (device).
214 * ,-----> | Thread | <-----,
223 * first | | up | last
225 * +==========+ next +==========+ next +==========+
226 * |subthread1| <-----> |subthread2| <-----> |subthread3|
227 * +----------+ prev +----------+ prev +----------+
228 * |substream1| |substream2| |substream3|
229 * |pthread_t1| |pthread_t2| |pthread_t3|
230 * |privdata1 | |privdata2 | |privdata3 |
231 * +==========+ +==========+ +==========+
233 * Note: It is yet _not_ possible to specify different devices for each
234 * subthread. This will require another split of the device structure into
235 * a device+subdevice tree.
238 /* some defs for the pthreaded version */
240 enum media_thread_states {
244 NUMBER_OF_MEDIA_THREAD_STATES
247 enum media_thread_play_states {
252 NUMBER_OF_MEDIA_THREAD_PLAY_STATES
256 /* data and handling info */
259 media_substream *substream;
261 /* state information */
262 media_thread_state state;
263 media_thread_play_state play_state;
264 #if defined(HAVE_THREADS)
267 #if defined(EF_USE_ASYNEQ)
271 /* anonymous data, used by the various backends */
272 /* do not use it, it is not freed by gc */
273 void *job_device_data;
274 void *job_stream_data;
278 size_t buffer_alloc_size;
280 /* audio-specific job options */
285 uint8_t chanvol[MEDIA_MAX_AUDIO_CHANNELS];
288 /* communication objects */
290 Lisp_Object sentinel;
293 #define MTPSTATE_REACT_TIME 20000 /* in usec */
295 enum sxe_finalise_conds {
296 SXE_SMPH_SHALL_START,
297 SXE_SMPH_HAVE_STARTED,
298 SXE_SMPH_SHALL_FINISH,
299 SXE_SMPH_HAVE_FINISHED
302 #define audio_job(_x) (((audio_job_t)worker_job_data(_x)))
303 #define audio_job_stream(_x) (audio_job(_x)->stream)
304 #define audio_job_device(_x) (audio_job(_x)->device)
305 #define audio_job_sentinel(_x) (audio_job(_x)->sentinel)
306 #define audio_job_device_data(_aj) (_aj)->job_device_data
307 #define audio_job_stream_data(_aj) (_aj)->job_stream_data
308 #define audio_job_queue(_aj) (_aj)->queue
309 #define XAUDIO_JOB(_x) ((audio_job_t)(XWORKER_JOB_DATA(_x)))
310 #define XAUDIO_JOB_STREAM(_x) audio_job_stream(XWORKER_JOB(_x))
311 #define XAUDIO_JOB_DEVICE(_x) audio_job_device(XWORKER_JOB(_x))
312 #define XAUDIO_JOB_SENTINEL(_x) audio_job_sentinel(XWORKER_JOB(_x))
313 #define XAUDIO_JOB_QUEUE(_x) (audio_job_queue(XAUDIO_JOB(_x)))
314 #define AUDIO_JOBP(_x) \
315 (WORKER_JOBP(_x) && XWORKER_JOB_HANDLER(_x) == &audio_job_handler)
316 #define CHECK_AUDIO_JOB(_x) \
318 if (!AUDIO_JOBP(_x)) \
319 dead_wrong_type_argument(Qaudio_jobp, _x); \
321 #define CONCHECK_AUDIO_JOB(_x) \
323 if (!AUDIO_JOBP(_x)) \
324 return wrong_type_argument(Qaudio_jobp, _x); \
328 #define SXE_WORKERS_SMPH_INIT(_mt) \
329 SOUND_DEBUG_MW("initialising state semaphore: 0x%x\n", \
330 (unsigned int)(_mt)); \
331 SXE_MSEMAPH_INIT(&(_mt)->state_smph);
332 #define SXE_WORKERS_SMPH_FINI(_mt) \
333 SOUND_DEBUG_MW("finishing state semaphore: 0x%x\n", \
334 (unsigned int)(_mt)); \
335 SXE_MSEMAPH_FINI(&(_mt)->state_smph);
336 #define SXE_WORKERS_SMPH_LOCK(_mt) \
337 SOUND_DEBUG_MW("locking mutex of state semaphore: 0x%x\n", \
338 (unsigned int)(_mt)); \
339 SXE_MSEMAPH_LOCK(&(_mt)->state_smph);
340 #define SXE_WORKERS_SMPH_UNLOCK(_mt) \
341 SOUND_DEBUG_MW("unlocking mutex of state semaphore: 0x%x\n", \
342 (unsigned int)(_mt)); \
343 SXE_MSEMAPH_UNLOCK(&(_mt)->state_smph);
345 #define SXE_WORKERS_SMPH_SIGNAL(_mt, _state) \
346 SOUND_DEBUG_MW("signalling "#_state": 0x%x\n", \
347 (unsigned int)(_mt)); \
348 SXE_MSEMAPH_SIGNAL(&(_mt)->state_smph, _state); \
349 SOUND_DEBUG_MW("signalled "#_state": 0x%x\n", \
350 (unsigned int)(_mt));
351 #define SXE_WORKERS_SMPH_BROADCAST(_mt, _state) \
352 SOUND_DEBUG_MW("broadcasting "#_state": 0x%x\n", \
353 (unsigned int)(_mt)); \
354 SXE_MSEMAPH_BROADCAST(&(_mt)->state_smph, _state); \
355 SOUND_DEBUG_MW("broadcast "#_state": 0x%x\n", \
356 (unsigned int)(_mt));
357 #define SXE_WORKERS_SMPH_WAIT(_mt, _state) \
358 SOUND_DEBUG_MW("waiting for "#_state": 0x%x\n", \
359 (unsigned int)(_mt)); \
360 SXE_MSEMAPH_WAIT(&(_mt)->state_smph, _state); \
361 SOUND_DEBUG_MW("ACK, got "#_state": 0x%x\n", \
362 (unsigned int)(_mt));
364 #define SXE_WORKERS_SMPH_SYNCH(_mt, _state) \
365 SOUND_DEBUG_MW("waiting for "#_state": 0x%x\n", \
366 (unsigned int)(_mt)); \
367 SXE_MSEMAPH_SYNCH(&(_mt)->state_smph, _state); \
368 SOUND_DEBUG_MW("ACK, got "#_state": 0x%x\n", \
369 (unsigned int)(_mt));
370 #define WITH_SXE_WORKERS_SMPH_SYNCH(_mt, _state, args...) \
371 SOUND_DEBUG_MW("waiting for "#_state": 0x%x\n", \
372 (unsigned int)(_mt)); \
373 WITH_SXE_MSEMAPH_SYNCH(&(_mt)->state_smph, _state, args); \
374 SOUND_DEBUG_MW("ACK, got "#_state": 0x%x\n", \
375 (unsigned int)(_mt));
376 #define SXE_WORKERS_SMPH_TRIGGER(_mt, _state) \
377 SOUND_DEBUG_MW("triggering "#_state": 0x%x\n", \
378 (unsigned int)(_mt)); \
379 SXE_MSEMAPH_TRIGGER(&(_mt)->state_smph, _state); \
380 SOUND_DEBUG_MW("triggered "#_state": 0x%x\n", \
381 (unsigned int)(_mt));
382 #define SXE_WORKERS_SMPH_TRIGGER_ALL(_mt, _state) \
383 SOUND_DEBUG_MW("triggering all "#_state": 0x%x\n", \
384 (unsigned int)(_mt)); \
385 SXE_MSEMAPH_TRIGGER_ALL(&(_mt)->state_smph, _state); \
386 SOUND_DEBUG_MW("triggered all "#_state": 0x%x\n", \
387 (unsigned int)(_mt));
390 /* convenience macros, dunno how sensible they are */
391 #define SOUND_UNPACK_MT(aj, dev, ms, mss, ad, cad, mtap) \
392 /* unpack the media thread */ \
393 dev = (aj)->device; \
394 ms = XMEDIA_STREAM((aj)->stream); \
395 mss = aj->substream; \
397 /* unpack device */ \
398 ad = get_audio_device(dev); \
399 cad = get_audio_device_data(dev); \
401 /* cannot use incomplete or corrupt audio devices */ \
402 if ((ad) == NULL || cad == NULL || \
403 media_stream_meth((ms), rewind) == NULL || \
404 media_stream_meth((ms), read) == NULL || \
405 XAUDIO_DEVICE_DRIVER(dev) != MYSELF) \
408 /* cannot use most devices to play non-audio stuff */ \
409 if (media_substream_type(mss) != MTYPE_AUDIO) \
411 mtap = media_substream_type_properties(mss).aprops;
415 /* for the communication with (async'ly) running audio jobs */
416 typedef struct audio_job_event_s *audio_job_event_t;
417 typedef enum audio_job_event_kinds audio_job_event_kind_t;
418 typedef enum audio_job_change_states audio_job_change_state_t;
419 typedef int audio_job_change_volume_t;
420 typedef float audio_job_change_rate_t;
421 typedef union audio_job_event_args_u *audio_job_event_args_t;
423 enum audio_job_change_states {
428 no_audio_job_change_states
431 union audio_job_event_args_u {
432 audio_job_change_state_t state_args;
433 audio_job_change_volume_t volume_args;
434 audio_job_change_rate_t rate_args;
437 enum audio_job_event_kinds {
441 no_audio_job_event_kinds
444 struct audio_job_event_s {
445 audio_job_event_kind_t kind;
446 union audio_job_event_args_u args;
450 #define audio_job_event_kind(_x) ((_x)->kind)
451 #define audio_job_event_args(_x) ((_x)->args)
452 #define audio_job_event_fau(_x) ((_x)->free_after_use)
454 /* some often used events */
455 extern struct audio_job_event_s pause_event;
456 extern struct audio_job_event_s resume_event;
457 extern struct audio_job_event_s start_event;
458 extern struct audio_job_event_s stop_event;
459 extern struct audio_job_event_s change_volume_event;
460 extern struct audio_job_event_s change_rate_event;
462 extern_inline audio_job_event_t
463 make_audio_job_event(audio_job_event_kind_t kind);
465 extern_inline audio_job_event_t
466 make_audio_job_event(audio_job_event_kind_t kind)
468 audio_job_event_t aje = xnew(struct audio_job_event_s);
469 audio_job_event_kind(aje) = kind;
470 audio_job_event_fau(aje) = 1;
475 free_audio_job_event(audio_job_event_t aje)
477 if (audio_job_event_fau(aje)) {
483 #endif /* EF_USE_ASYNEQ */
485 #endif /* INCLUDED_sound_h_ */