Mercurial > audlegacy-plugins
comparison src/jack/jack.c @ 12:3da1b8942b8b trunk
[svn] - remove src/Input src/Output src/Effect src/General src/Visualization src/Container
author | nenolod |
---|---|
date | Mon, 18 Sep 2006 03:14:20 -0700 |
parents | src/Output/jack/jack.c@6303e3a8a6b8 |
children | f6887767487c |
comparison
equal
deleted
inserted
replaced
11:cff1d04026ae | 12:3da1b8942b8b |
---|---|
1 /* xmms - jack output plugin | |
2 * Copyright 2002 Chris Morgan<cmorgan@alum.wpi.edu> | |
3 * | |
4 * audacious port (2005) by Giacomo Lozito from develia.org | |
5 * | |
6 * This code maps xmms calls into the jack translation library | |
7 */ | |
8 | |
9 #include "audacious/configdb.h" | |
10 #include "audacious/util.h" | |
11 #include <dlfcn.h> | |
12 #include <gtk/gtk.h> | |
13 #include <glib/gi18n.h> | |
14 #include <stdio.h> | |
15 #include "config.h" | |
16 #include "bio2jack.h" /* includes for the bio2jack library */ | |
17 #include "jack.h" | |
18 #include "xconvert.h" /* xmms rate conversion header file */ | |
19 #include <string.h> | |
20 | |
21 | |
22 | |
23 /* set to 1 for verbose output */ | |
24 #define VERBOSE_OUTPUT 0 | |
25 | |
26 jackconfig jack_cfg; | |
27 | |
28 #define OUTFILE stderr | |
29 | |
30 #define TRACE(...) \ | |
31 if(jack_cfg.isTraceEnabled) { \ | |
32 fprintf(OUTFILE, "%s:", __FUNCTION__), \ | |
33 fprintf(OUTFILE, __VA_ARGS__), \ | |
34 fflush(OUTFILE); \ | |
35 } | |
36 | |
37 #define ERR(...) \ | |
38 if(jack_cfg.isTraceEnabled) { \ | |
39 fprintf(OUTFILE, "ERR: %s:", __FUNCTION__), \ | |
40 fprintf(OUTFILE, __VA_ARGS__), \ | |
41 fflush(OUTFILE); \ | |
42 } | |
43 | |
44 | |
45 static int driver = 0; /* handle to the jack output device */ | |
46 | |
47 typedef struct format_info { | |
48 AFormat format; | |
49 long frequency; | |
50 int channels; | |
51 long bps; | |
52 } format_info_t; | |
53 | |
54 static format_info_t input; | |
55 static format_info_t effect; | |
56 static format_info_t output; | |
57 | |
58 static convert_freq_func_t freq_convert; /* rate convert function */ | |
59 static struct xmms_convert_buffers *convertb; /* convert buffer */ | |
60 | |
61 #define MAKE_FUNCPTR(f) static typeof(f) * fp_##f = NULL; | |
62 MAKE_FUNCPTR(xmms_convert_buffers_new); | |
63 MAKE_FUNCPTR(xmms_convert_buffers_destroy); | |
64 MAKE_FUNCPTR(xmms_convert_get_frequency_func); | |
65 void *xmmslibhandle; /* handle to the dlopen'ed libxmms.so */ | |
66 | |
67 static int isXmmsFrequencyAvailable = 0; | |
68 | |
69 static gboolean output_opened; /* true if we have a connection to jack */ | |
70 | |
71 static GtkWidget *dialog, *button, *label; | |
72 | |
73 void jack_set_volume(int l, int r); | |
74 | |
75 /* Giacomo's note: removed the destructor from the original xmms-jack, cause | |
76 destructors + thread join + NPTL currently leads to problems; solved this | |
77 by adding a cleanup function callback for output plugins in Audacious, this | |
78 is used to close the JACK connection and to perform a correct shutdown */ | |
79 void jack_cleanup(void) | |
80 { | |
81 int errval; | |
82 TRACE("cleanup\n"); | |
83 | |
84 if((errval = JACK_Close(driver))) | |
85 ERR("error closing device, errval of %d\n", errval); | |
86 | |
87 /* only clean this up if we have the function to call */ | |
88 if(isXmmsFrequencyAvailable) | |
89 { | |
90 fp_xmms_convert_buffers_destroy(convertb); /* clean up the rate conversion buffers */ | |
91 dlclose(xmmslibhandle); | |
92 } | |
93 | |
94 return; | |
95 } | |
96 | |
97 | |
98 void jack_sample_rate_error(void) | |
99 { | |
100 dialog = gtk_dialog_new(); | |
101 gtk_window_set_title(GTK_WINDOW(dialog), ("Sample rate mismatch")); | |
102 gtk_container_border_width(GTK_CONTAINER(dialog), 5); | |
103 label = gtk_label_new(( | |
104 "Xmms is asking for a sample rate that differs from\n " | |
105 "that of the jack server. Xmms 1.2.8 or later\n" | |
106 "contains resampling routines that xmms-jack will\n" | |
107 "dynamically load and use to perform resampling.\n" | |
108 "Or you can restart the jack server\n" | |
109 "with a sample rate that matches the one that\n" | |
110 "xmms desires. -r is the option for the jack\n" | |
111 "alsa driver so -r 44100 or -r 48000 should do\n\n" | |
112 "Chris Morgan <cmorgan@alum.wpi.edu>\n")); | |
113 | |
114 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->vbox), label, TRUE, TRUE, 0); | |
115 gtk_widget_show(label); | |
116 | |
117 button = gtk_button_new_with_label((" Close ")); | |
118 gtk_signal_connect_object(GTK_OBJECT(button), "clicked", GTK_SIGNAL_FUNC(gtk_widget_destroy), GTK_OBJECT(dialog)); | |
119 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(dialog)->action_area), button, FALSE, FALSE, 0); | |
120 gtk_widget_show(button); | |
121 | |
122 gtk_widget_show(dialog); | |
123 gtk_widget_grab_focus(button); | |
124 } | |
125 | |
126 | |
127 /* Return the number of milliseconds of audio data that has been */ | |
128 /* written out to the device */ | |
129 gint jack_get_written_time(void) | |
130 { | |
131 long return_val; | |
132 return_val = JACK_GetPosition(driver, MILLISECONDS, WRITTEN); | |
133 | |
134 TRACE("returning %ld milliseconds\n", return_val); | |
135 return return_val; | |
136 } | |
137 | |
138 | |
139 /* Return the current number of milliseconds of audio data that has */ | |
140 /* been played out of the audio device, not including the buffer */ | |
141 gint jack_get_output_time(void) | |
142 { | |
143 gint return_val; | |
144 | |
145 /* don't try to get any values if the device is still closed */ | |
146 if(JACK_GetState(driver) == CLOSED) | |
147 return_val = 0; | |
148 else | |
149 return_val = JACK_GetPosition(driver, MILLISECONDS, PLAYED); /* get played position in terms of milliseconds */ | |
150 | |
151 TRACE("returning %d milliseconds\n", return_val); | |
152 return return_val; | |
153 } | |
154 | |
155 | |
156 /* returns TRUE if we are currently playing */ | |
157 /* NOTE: this was confusing at first BUT, if the device is open and there */ | |
158 /* is no more audio to be played, then the device is NOT PLAYING */ | |
159 gint jack_playing(void) | |
160 { | |
161 gint return_val; | |
162 | |
163 /* If we are playing see if we ACTUALLY have something to play */ | |
164 if(JACK_GetState(driver) == PLAYING) | |
165 { | |
166 /* If we have zero bytes stored, we are done playing */ | |
167 if(JACK_GetBytesStored(driver) == 0) | |
168 return_val = FALSE; | |
169 else | |
170 return_val = TRUE; | |
171 } | |
172 else | |
173 return_val = FALSE; | |
174 | |
175 TRACE("returning %d\n", return_val); | |
176 return return_val; | |
177 } | |
178 | |
179 | |
180 void jack_set_port_connection_mode() | |
181 { | |
182 /* setup the port connection mode that determines how bio2jack will connect ports */ | |
183 enum JACK_PORT_CONNECTION_MODE mode; | |
184 | |
185 if(strcmp(jack_cfg.port_connection_mode, "CONNECT_ALL") == 0) | |
186 mode = CONNECT_ALL; | |
187 else if(strcmp(jack_cfg.port_connection_mode, "CONNECT_OUTPUT") == 0) | |
188 mode = CONNECT_OUTPUT; | |
189 else if(strcmp(jack_cfg.port_connection_mode, "CONNECT_NONE") == 0) | |
190 mode = CONNECT_NONE; | |
191 else | |
192 { | |
193 TRACE("Defaulting to CONNECT_ALL"); | |
194 mode = CONNECT_ALL; | |
195 } | |
196 JACK_SetPortConnectionMode(mode); | |
197 } | |
198 | |
199 /* Initialize necessary things */ | |
200 void jack_init(void) | |
201 { | |
202 /* read the isTraceEnabled setting from the config file */ | |
203 ConfigDb *cfgfile; | |
204 | |
205 cfgfile = bmp_cfg_db_open(); | |
206 if (!cfgfile) | |
207 { | |
208 jack_cfg.isTraceEnabled = FALSE; | |
209 jack_cfg.port_connection_mode = "CONNECT_ALL"; /* default to connect all */ | |
210 jack_cfg.volume_left = 25; /* set default volume to 25 % */ | |
211 jack_cfg.volume_right = 25; | |
212 } else | |
213 { | |
214 bmp_cfg_db_get_bool(cfgfile, "jack", "isTraceEnabled", &jack_cfg.isTraceEnabled); | |
215 if(!bmp_cfg_db_get_string(cfgfile, "jack", "port_connection_mode", &jack_cfg.port_connection_mode)) | |
216 jack_cfg.port_connection_mode = "CONNECT_ALL"; | |
217 if(!bmp_cfg_db_get_int(cfgfile, "jack", "volume_left", &jack_cfg.volume_left)) | |
218 jack_cfg.volume_left = 25; | |
219 if(!bmp_cfg_db_get_int(cfgfile, "jack", "volume_right", &jack_cfg.volume_right)) | |
220 jack_cfg.volume_right = 25; | |
221 } | |
222 | |
223 bmp_cfg_db_close(cfgfile); | |
224 | |
225 TRACE("initializing\n"); | |
226 JACK_Init(); /* initialize the driver */ | |
227 | |
228 /* set the bio2jack name so users will see xmms-jack in their */ | |
229 /* jack client list */ | |
230 JACK_SetClientName("audacious-jack"); | |
231 | |
232 /* set the port connection mode */ | |
233 jack_set_port_connection_mode(); | |
234 | |
235 /* XXX unportable to 2.x */ | |
236 xmmslibhandle = dlopen("libaudacious.so", RTLD_NOW); | |
237 if(xmmslibhandle) | |
238 { | |
239 fp_xmms_convert_buffers_new = dlsym(xmmslibhandle, "xmms_convert_buffers_new"); | |
240 fp_xmms_convert_buffers_destroy = dlsym(xmmslibhandle, "xmms_convert_buffers_destroy"); | |
241 fp_xmms_convert_get_frequency_func = dlsym(xmmslibhandle, "xmms_convert_get_frequency_func"); | |
242 | |
243 if(!fp_xmms_convert_buffers_new) | |
244 { | |
245 TRACE("fp_xmms_convert_buffers_new couldn't be dlsym'ed\n"); | |
246 TRACE("dlerror: %s\n", dlerror()); | |
247 } | |
248 | |
249 if(!fp_xmms_convert_buffers_destroy) | |
250 { | |
251 TRACE("fp_xmms_convert_buffers_destroy couldn't be dlsym'ed\n"); | |
252 TRACE("dlerror: %s\n", dlerror()); | |
253 } | |
254 | |
255 if(!fp_xmms_convert_get_frequency_func) | |
256 { | |
257 TRACE("fp_xmms_get_frequency_func couldn't be dlsym'ed\n"); | |
258 TRACE("dlerror: %s\n", dlerror()); | |
259 } | |
260 | |
261 if(!fp_xmms_convert_buffers_new || !fp_xmms_convert_buffers_destroy || | |
262 !fp_xmms_convert_get_frequency_func) | |
263 { | |
264 dlclose(xmmslibhandle); /* close the library, no need to keep it open */ | |
265 TRACE("One or more frequency convertion functions are missing, upgrade to xmms >=1.2.8\n"); | |
266 } else | |
267 { | |
268 TRACE("Found frequency convertion functions, setting isXmmsFrequencyAvailable to 1\n"); | |
269 isXmmsFrequencyAvailable = 1; | |
270 } | |
271 } else | |
272 { | |
273 TRACE("unable to dlopen '%s'\n", "libaudacious.so"); | |
274 } | |
275 | |
276 /* only initialize this stuff if we have the functions available */ | |
277 if(isXmmsFrequencyAvailable) | |
278 { | |
279 convertb = fp_xmms_convert_buffers_new (); | |
280 freq_convert = fp_xmms_convert_get_frequency_func(FMT_S16_LE, 2); | |
281 } | |
282 | |
283 output_opened = FALSE; | |
284 } | |
285 | |
286 | |
287 /* Return the amount of data that can be written to the device */ | |
288 gint jack_free(void) | |
289 { | |
290 unsigned long return_val = JACK_GetBytesFreeSpace(driver); | |
291 unsigned long tmp; | |
292 | |
293 /* adjust for frequency differences, otherwise xmms could send us */ | |
294 /* as much data as we have free, then we go to convert this to */ | |
295 /* the output frequency and won't have enough space, so adjust */ | |
296 /* by the ratio of the two */ | |
297 if(effect.frequency != output.frequency) | |
298 { | |
299 tmp = return_val; | |
300 return_val = (return_val * effect.frequency) / output.frequency; | |
301 TRACE("adjusting from %ld to %ld free bytes to compensate for frequency differences\n", tmp, return_val); | |
302 } | |
303 | |
304 if(return_val > G_MAXINT) | |
305 { | |
306 TRACE("Warning: return_val > G_MAXINT\n"); | |
307 return_val = G_MAXINT; | |
308 } | |
309 | |
310 TRACE("free space of %ld bytes\n", return_val); | |
311 | |
312 return return_val; | |
313 } | |
314 | |
315 | |
316 /* Close the device */ | |
317 void jack_close(void) | |
318 { | |
319 ConfigDb *cfgfile; | |
320 | |
321 cfgfile = bmp_cfg_db_open(); | |
322 bmp_cfg_db_set_int(cfgfile, "jack", "volume_left", jack_cfg.volume_left); /* stores the volume setting */ | |
323 bmp_cfg_db_set_int(cfgfile, "jack", "volume_right", jack_cfg.volume_right); | |
324 bmp_cfg_db_close(cfgfile); | |
325 | |
326 TRACE("\n"); | |
327 | |
328 JACK_Reset(driver); /* flush buffers, reset position and set state to STOPPED */ | |
329 TRACE("resetting driver, not closing now, destructor will close for us\n"); | |
330 } | |
331 | |
332 | |
333 /* Open the device up */ | |
334 gint jack_open(AFormat fmt, gint sample_rate, gint num_channels) | |
335 { | |
336 int bits_per_sample; | |
337 int retval; | |
338 unsigned long rate; | |
339 | |
340 TRACE("fmt == %d, sample_rate == %d, num_channels == %d\n", | |
341 fmt, sample_rate, num_channels); | |
342 | |
343 if((fmt == FMT_U8) || (fmt == FMT_S8)) | |
344 { | |
345 bits_per_sample = 8; | |
346 } else | |
347 { | |
348 bits_per_sample = 16; | |
349 } | |
350 | |
351 /* record some useful information */ | |
352 input.format = fmt; | |
353 input.frequency = sample_rate; | |
354 input.bps = bits_per_sample * sample_rate * num_channels; | |
355 input.channels = num_channels; | |
356 | |
357 /* setup the effect as matching the input format */ | |
358 effect.format = input.format; | |
359 effect.frequency = input.frequency; | |
360 effect.channels = input.channels; | |
361 effect.bps = input.bps; | |
362 | |
363 /* if we are already opened then don't open again */ | |
364 if(output_opened) | |
365 { | |
366 /* if something has changed we should close and re-open the connect to jack */ | |
367 if((output.channels != input.channels) || | |
368 (output.frequency != input.frequency) || | |
369 (output.format != input.format)) | |
370 { | |
371 TRACE("output.channels is %d, jack_open called with %d channels\n", output.channels, input.channels); | |
372 TRACE("output.frequency is %ld, jack_open called with %ld\n", output.frequency, input.frequency); | |
373 TRACE("output.format is %d, jack_open called with %d\n", output.format, input.format); | |
374 jack_close(); | |
375 } else | |
376 { | |
377 TRACE("output_opened is TRUE and no options changed, not reopening\n"); | |
378 return 1; | |
379 } | |
380 } | |
381 | |
382 /* try to open the jack device with the requested rate at first */ | |
383 output.frequency = input.frequency; | |
384 output.bps = input.bps; | |
385 output.channels = input.channels; | |
386 output.format = input.format; | |
387 | |
388 rate = output.frequency; | |
389 retval = JACK_Open(&driver, bits_per_sample, &rate, output.channels); | |
390 output.frequency = rate; /* avoid compile warning as output.frequency differs in type | |
391 from what JACK_Open() wants for the type of the rate parameter */ | |
392 if((retval == ERR_RATE_MISMATCH) && isXmmsFrequencyAvailable) | |
393 { | |
394 TRACE("xmms(input) wants rate of '%ld', jacks rate(output) is '%ld', opening at jack rate\n", input.frequency, output.frequency); | |
395 | |
396 /* open the jack device with true jack's rate, return 0 upon failure */ | |
397 retval = JACK_Open(&driver, bits_per_sample, &rate, output.channels); | |
398 output.frequency = rate; /* avoid compile warning as output.frequency differs in type | |
399 from what JACK_Open() wants for the type of the rate parameter */ | |
400 if(retval) | |
401 { | |
402 TRACE("failed to open jack with JACK_Open(), error %d\n", retval); | |
403 return 0; | |
404 } | |
405 TRACE("success!!\n"); | |
406 } else if((retval == ERR_RATE_MISMATCH) && !isXmmsFrequencyAvailable) | |
407 { | |
408 TRACE("JACK_Open(), sample rate mismatch with no resampling routines available\n"); | |
409 | |
410 jack_sample_rate_error(); /* notify the user that we can't resample */ | |
411 | |
412 return 0; | |
413 } else if(retval != ERR_SUCCESS) | |
414 { | |
415 TRACE("failed to open jack with JACK_Open(), error %d\n", retval); | |
416 return 0; | |
417 } | |
418 | |
419 jack_set_volume(jack_cfg.volume_left, jack_cfg.volume_right); /* sets the volume to stored value */ | |
420 output_opened = TRUE; | |
421 | |
422 return 1; | |
423 } | |
424 | |
425 | |
426 /* write some audio out to the device */ | |
427 void jack_write(gpointer ptr, gint length) | |
428 { | |
429 long written; | |
430 EffectPlugin *ep; | |
431 AFormat new_format; | |
432 int new_frequency, new_channels; | |
433 long positionMS; | |
434 | |
435 TRACE("starting length of %d\n", length); | |
436 | |
437 /* copy the current values into temporary values */ | |
438 new_format = input.format; | |
439 new_frequency = input.frequency; | |
440 new_channels = input.channels; | |
441 | |
442 /* query xmms for the current plugin */ | |
443 ep = get_current_effect_plugin(); | |
444 if(effects_enabled() && ep && ep->query_format) | |
445 { | |
446 ep->query_format(&new_format, &new_frequency, &new_channels); | |
447 } | |
448 | |
449 /* if the format has changed take this into account by modifying */ | |
450 /* the time offset and reopening the device with the new format settings */ | |
451 if (new_format != effect.format || | |
452 new_frequency != effect.frequency || | |
453 new_channels != effect.channels) | |
454 { | |
455 TRACE("format changed, storing new values and opening/closing jack\n"); | |
456 TRACE("effect.format == %d, new_format == %d, effect.frequency == %ld, new_frequency == %d, effect.channels == %d, new_channels = %d\n", | |
457 effect.format, new_format, effect.frequency, new_frequency, effect.channels, new_channels); | |
458 | |
459 positionMS = JACK_GetPosition(driver, MILLISECONDS, PLAYED); | |
460 | |
461 jack_close(); | |
462 jack_open(new_format, new_frequency, new_channels); | |
463 | |
464 /* restore the position after the open and close */ | |
465 JACK_SetState(driver, PAUSED); | |
466 JACK_SetPosition(driver, MILLISECONDS, positionMS); | |
467 JACK_SetState(driver, PLAYING); | |
468 } | |
469 | |
470 /* if effects are enabled and we have a plugin, run the current */ | |
471 /* samples through the plugin */ | |
472 if (effects_enabled() && ep && ep->mod_samples) | |
473 { | |
474 length = ep->mod_samples(&ptr, length, | |
475 input.format, | |
476 input.frequency, | |
477 input.channels); | |
478 TRACE("effects_enabled(), length is now %d\n", length); | |
479 } | |
480 | |
481 TRACE("effect.frequency == %ld, input.frequency == %ld, output.frequency == %ld\n", | |
482 effect.frequency, input.frequency, output.frequency); | |
483 | |
484 /* if we need rate conversion, perform it here */ | |
485 if((effect.frequency != output.frequency) && isXmmsFrequencyAvailable) | |
486 { | |
487 TRACE("performing rate conversion from '%ld'(effect) to '%ld'(output)\n", effect.frequency, output.frequency); | |
488 length = freq_convert (convertb, &ptr, length, effect.frequency, output.frequency); | |
489 } | |
490 | |
491 TRACE("length = %d\n", length); | |
492 /* loop until we have written all the data out to the jack device */ | |
493 /* this is due to xmms' audio driver api */ | |
494 char *buf = (char*)ptr; | |
495 while(length > 0) | |
496 { | |
497 TRACE("writing %d bytes\n", length); | |
498 written = JACK_Write(driver, (unsigned char*)buf, length); | |
499 length-=written; | |
500 buf+=written; | |
501 } | |
502 TRACE("finished\n"); | |
503 } | |
504 | |
505 | |
506 /* Flush any output currently buffered */ | |
507 /* and set the number of bytes written based on ms_offset_time, */ | |
508 /* the number of milliseconds of offset passed in */ | |
509 /* This is done so the driver itself keeps track of */ | |
510 /* current playing position of the mp3 */ | |
511 void jack_flush(gint ms_offset_time) | |
512 { | |
513 TRACE("setting values for ms_offset_time of %d\n", ms_offset_time); | |
514 | |
515 JACK_Reset(driver); /* flush buffers and set state to STOPPED */ | |
516 | |
517 /* update the internal driver values to correspond to the input time given */ | |
518 JACK_SetPosition(driver, MILLISECONDS, ms_offset_time); | |
519 | |
520 JACK_SetState(driver, PLAYING); | |
521 } | |
522 | |
523 | |
524 /* Pause the jack device */ | |
525 void jack_pause(short p) | |
526 { | |
527 TRACE("p == %d\n", p); | |
528 | |
529 /* pause the device if p is non-zero, unpause the device if p is zero and */ | |
530 /* we are currently paused */ | |
531 if(p) | |
532 JACK_SetState(driver, PAUSED); | |
533 else if(JACK_GetState(driver) == PAUSED) | |
534 JACK_SetState(driver, PLAYING); | |
535 } | |
536 | |
537 | |
538 /* Set the volume */ | |
539 void jack_set_volume(int l, int r) | |
540 { | |
541 if(output.channels == 1) | |
542 { | |
543 TRACE("l(%d)\n", l); | |
544 } else if(output.channels > 1) | |
545 { | |
546 TRACE("l(%d), r(%d)\n", l, r); | |
547 } | |
548 | |
549 if(output.channels > 0) { | |
550 JACK_SetVolumeForChannel(driver, 0, l); | |
551 jack_cfg.volume_left = l; | |
552 } | |
553 if(output.channels > 1) { | |
554 JACK_SetVolumeForChannel(driver, 1, r); | |
555 jack_cfg.volume_right = r; | |
556 } | |
557 } | |
558 | |
559 | |
560 /* Get the current volume setting */ | |
561 void jack_get_volume(int *l, int *r) | |
562 { | |
563 unsigned int _l, _r; | |
564 | |
565 if(output.channels > 0) | |
566 { | |
567 JACK_GetVolumeForChannel(driver, 0, &_l); | |
568 (*l) = _l; | |
569 } | |
570 if(output.channels > 1) | |
571 { | |
572 JACK_GetVolumeForChannel(driver, 1, &_r); | |
573 (*r) = _r; | |
574 } | |
575 | |
576 #if VERBOSE_OUTPUT | |
577 if(output.channels == 1) | |
578 TRACE("l(%d)\n", *l); | |
579 else if(output.channels > 1) | |
580 TRACE("l(%d), r(%d)\n", *l, *r); | |
581 #endif | |
582 } | |
583 | |
584 | |
585 void jack_about(void) | |
586 { | |
587 static GtkWidget *aboutbox; | |
588 | |
589 if (!aboutbox) | |
590 { | |
591 aboutbox = xmms_show_message( | |
592 _("About JACK Output Plugin 0.15"), | |
593 _("XMMS jack Driver 0.15\n\n" | |
594 "xmms-jack.sf.net\nChris Morgan<cmorgan@alum.wpi.edu>\n\n" | |
595 "Audacious port by\nGiacomo Lozito from develia.org"), | |
596 _("Ok"), FALSE, NULL, NULL); | |
597 g_signal_connect(GTK_OBJECT(aboutbox), "destroy", | |
598 (GCallback)gtk_widget_destroyed, &aboutbox); | |
599 } | |
600 } | |
601 | |
602 static void | |
603 jack_tell_audio(AFormat * fmt, gint * srate, gint * nch) | |
604 { | |
605 (*fmt) = input.format; | |
606 (*srate) = input.frequency; | |
607 (*nch) = input.channels; | |
608 } | |
609 | |
610 OutputPlugin jack_op = | |
611 { | |
612 NULL, | |
613 NULL, | |
614 "JACK Output Plugin 0.15", | |
615 jack_init, | |
616 jack_cleanup, | |
617 jack_about, | |
618 jack_configure, | |
619 jack_get_volume, | |
620 jack_set_volume, | |
621 jack_open, | |
622 jack_write, | |
623 jack_close, | |
624 jack_flush, | |
625 jack_pause, | |
626 jack_free, | |
627 jack_playing, | |
628 jack_get_output_time, | |
629 jack_get_written_time, | |
630 jack_tell_audio | |
631 }; | |
632 | |
633 | |
634 OutputPlugin *get_oplugin_info(void) | |
635 { | |
636 return &jack_op; | |
637 } |