2 * Copyright 2004-2006 Timo Hirvonen
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License as
6 * published by the Free Software Foundation; either version 2 of the
7 * License, or (at your option) any later version.
9 * This program is distributed in the hope that it will be useful, but
10 * WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
28 #include "ui_curses.h"
29 #include "config/libdir.h"
33 #include <sys/types.h>
37 struct output_plugin
{
38 struct list_head node
;
42 const struct output_plugin_ops
*pcm_ops
;
43 const struct mixer_plugin_ops
*mixer_ops
;
44 const char * const *pcm_options
;
45 const char * const *mixer_options
;
48 unsigned int pcm_initialized
: 1;
49 unsigned int mixer_initialized
: 1;
50 unsigned int mixer_open
: 1;
53 static const char * const plugin_dir
= LIBDIR
"/cmus/op";
54 static LIST_HEAD(op_head
);
55 static struct output_plugin
*op
= NULL
;
61 /* volume is between 0 and volume_max */
64 static void add_plugin(struct output_plugin
*plugin
)
66 struct list_head
*item
= op_head
.next
;
68 while (item
!= &op_head
) {
69 struct output_plugin
*o
= container_of(item
, struct output_plugin
, node
);
71 if (plugin
->priority
< o
->priority
)
77 list_add_tail(&plugin
->node
, item
);
80 void op_load_plugins(void)
85 dir
= opendir(plugin_dir
);
87 error_msg("couldn't open directory `%s': %s", plugin_dir
, strerror(errno
));
90 while ((d
= readdir(dir
)) != NULL
) {
92 struct output_plugin
*plug
;
97 if (d
->d_name
[0] == '.')
99 ext
= strrchr(d
->d_name
, '.');
102 if (strcmp(ext
, ".so"))
105 snprintf(filename
, sizeof(filename
), "%s/%s", plugin_dir
, d
->d_name
);
107 so
= dlopen(filename
, RTLD_NOW
);
109 error_msg("%s", dlerror());
113 plug
= xnew(struct output_plugin
, 1);
116 if (!(plug
->pcm_ops
= dlsym(so
, sym
)))
119 sym
= "op_pcm_options";
120 if (!(plug
->pcm_options
= dlsym(so
, sym
)))
124 symptr
= dlsym(so
, sym
);
127 plug
->priority
= *(int *)symptr
;
129 plug
->mixer_ops
= dlsym(so
, "op_mixer_ops");
130 plug
->mixer_options
= dlsym(so
, "op_mixer_options");
132 if (plug
->mixer_ops
== NULL
|| plug
->mixer_options
== NULL
) {
133 plug
->mixer_ops
= NULL
;
134 plug
->mixer_options
= NULL
;
137 plug
->name
= xstrndup(d
->d_name
, ext
- d
->d_name
);
139 plug
->pcm_initialized
= 0;
140 plug
->mixer_initialized
= 0;
141 plug
->mixer_open
= 0;
146 error_msg("%s: symbol %s not found", filename
, sym
);
153 static void init_plugin(struct output_plugin
*o
)
155 if (!o
->mixer_initialized
&& o
->mixer_ops
) {
156 if (o
->mixer_ops
->init() == 0) {
157 d_print("initialized mixer for %s\n", o
->name
);
158 o
->mixer_initialized
= 1;
160 d_print("could not initialize mixer `%s'\n", o
->name
);
163 if (!o
->pcm_initialized
) {
164 if (o
->pcm_ops
->init() == 0) {
165 d_print("initialized pcm for %s\n", o
->name
);
166 o
->pcm_initialized
= 1;
168 d_print("could not initialize pcm `%s'\n", o
->name
);
173 void op_exit_plugins(void)
175 struct output_plugin
*o
;
177 list_for_each_entry(o
, &op_head
, node
) {
178 if (o
->mixer_initialized
&& o
->mixer_ops
)
179 o
->mixer_ops
->exit();
180 if (o
->pcm_initialized
)
185 static void close_mixer(void)
188 if (op
&& op
->mixer_open
) {
189 BUG_ON(op
->mixer_ops
== NULL
);
190 op
->mixer_ops
->close();
195 static void open_mixer(void)
205 BUG_ON(op
->mixer_open
);
206 if (op
->mixer_ops
&& op
->mixer_initialized
) {
209 rc
= op
->mixer_ops
->open(&volume_max
);
218 static int select_plugin(struct output_plugin
*o
)
220 /* try to initialize if not initialized yet */
223 if (!o
->pcm_initialized
)
224 return -OP_ERROR_NOT_INITIALIZED
;
232 int op_select(const char *name
)
234 struct output_plugin
*o
;
236 list_for_each_entry(o
, &op_head
, node
) {
237 if (strcasecmp(name
, o
->name
) == 0)
238 return select_plugin(o
);
240 return -OP_ERROR_NO_PLUGIN
;
243 int op_select_any(void)
245 struct output_plugin
*o
;
246 int rc
= -OP_ERROR_NO_PLUGIN
;
248 list_for_each_entry(o
, &op_head
, node
) {
249 rc
= select_plugin(o
);
256 int op_open(sample_format_t sf
)
259 return -OP_ERROR_NOT_INITIALIZED
;
260 return op
->pcm_ops
->open(sf
);
265 if (op
->pcm_ops
->drop
== NULL
)
266 return -OP_ERROR_NOT_SUPPORTED
;
267 return op
->pcm_ops
->drop();
272 return op
->pcm_ops
->close();
275 int op_write(const char *buffer
, int count
)
279 rc
= op
->pcm_ops
->write(buffer
, count
);
285 if (op
->pcm_ops
->pause
== NULL
)
287 return op
->pcm_ops
->pause();
292 if (op
->pcm_ops
->unpause
== NULL
)
294 return op
->pcm_ops
->unpause();
297 int op_buffer_space(void)
301 rc
= op
->pcm_ops
->buffer_space();
305 int op_set_volume(int left
, int right
)
313 return -OP_ERROR_NOT_INITIALIZED
;
315 return -OP_ERROR_NOT_SUPPORTED
;
316 return op
->mixer_ops
->set_volume(left
, right
);
319 int op_get_volume(int *left
, int *right
)
327 return -OP_ERROR_NOT_INITIALIZED
;
329 return -OP_ERROR_NOT_SUPPORTED
;
330 return op
->mixer_ops
->get_volume(left
, right
);
333 void op_set_soft_vol(int soft
)
340 #define OP_OPT_ID(plugin_idx, is_mixer, option_idx) \
341 (((plugin_idx) << 16) | ((is_mixer) << 15) | (option_idx))
343 static struct output_plugin
*find_plugin(int idx
)
345 struct output_plugin
*o
;
347 list_for_each_entry(o
, &op_head
, node
) {
355 int op_set_option(unsigned int id
, const char *val
)
357 unsigned int pid
= id
>> 16;
358 unsigned int mix
= id
& 0x8000;
359 unsigned int oid
= id
& 0x7fff;
360 const struct output_plugin
*o
= find_plugin(pid
);
363 return -OP_ERROR_NOT_OPTION
;
366 const struct mixer_plugin_ops
*mo
= o
->mixer_ops
;
367 int rc
= mo
->set_option(oid
, val
);
369 if (rc
== 0 && op
&& op
->mixer_ops
== mo
) {
370 /* option of the current op was set
371 * try to reopen the mixer */
377 return o
->pcm_ops
->set_option(oid
, val
);
380 int op_get_option(unsigned int id
, char **val
)
382 unsigned int pid
= id
>> 16;
383 unsigned int mix
= id
& 0x8000;
384 unsigned int oid
= id
& 0x7fff;
385 const struct output_plugin
*o
= find_plugin(pid
);
388 return -OP_ERROR_NOT_OPTION
;
391 return o
->mixer_ops
->get_option(oid
, val
);
392 return o
->pcm_ops
->get_option(oid
, val
);
395 int op_for_each_option(void (*cb
)(unsigned int id
, const char *key
))
397 struct output_plugin
*o
;
398 unsigned int pid
, oid
;
402 list_for_each_entry(o
, &op_head
, node
) {
405 for (oid
= 0; o
->pcm_options
[oid
]; oid
++) {
406 snprintf(key
, sizeof(key
), "dsp.%s.%s",
408 o
->pcm_options
[oid
]);
409 cb(OP_OPT_ID(pid
, 0, oid
), key
);
411 if (o
->mixer_ops
== NULL
)
413 for (oid
= 0; o
->mixer_options
[oid
]; oid
++) {
414 snprintf(key
, sizeof(key
), "mixer.%s.%s",
416 o
->mixer_options
[oid
]);
417 cb(OP_OPT_ID(pid
, 1, oid
), key
);
423 char *op_get_error_msg(int rc
, const char *arg
)
429 snprintf(buffer
, sizeof(buffer
), "%s: %s", arg
, strerror(errno
));
431 case OP_ERROR_NO_PLUGIN
:
432 snprintf(buffer
, sizeof(buffer
),
433 "%s: no such plugin", arg
);
435 case OP_ERROR_NOT_INITIALIZED
:
436 snprintf(buffer
, sizeof(buffer
),
437 "%s: couldn't initialize required output plugin", arg
);
439 case OP_ERROR_NOT_SUPPORTED
:
440 snprintf(buffer
, sizeof(buffer
),
441 "%s: function not supported", arg
);
443 case OP_ERROR_SAMPLE_FORMAT
:
444 snprintf(buffer
, sizeof(buffer
),
445 "%s: sample format not supported", arg
);
447 case OP_ERROR_NOT_OPTION
:
448 snprintf(buffer
, sizeof(buffer
),
449 "%s: no such option", arg
);
451 case OP_ERROR_INTERNAL
:
452 snprintf(buffer
, sizeof(buffer
), "%s: internal error", arg
);
454 case OP_ERROR_SUCCESS
:
456 snprintf(buffer
, sizeof(buffer
),
457 "%s: this is not an error (%d), this is a bug",
461 return xstrdup(buffer
);
464 void op_dump_plugins(void)
466 struct output_plugin
*o
;
468 printf("\nOutput Plugins: %s\n", plugin_dir
);
469 list_for_each_entry(o
, &op_head
, node
) {
470 printf(" %s\n", o
->name
);
474 char *op_get_current(void)
477 return xstrdup(op
->name
);