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
;
57 /* volume is between 0 and volume_max */
62 static void add_plugin(struct output_plugin
*plugin
)
64 struct list_head
*item
= op_head
.next
;
66 while (item
!= &op_head
) {
67 struct output_plugin
*o
= container_of(item
, struct output_plugin
, node
);
69 if (plugin
->priority
< o
->priority
)
75 list_add_tail(&plugin
->node
, item
);
78 void op_load_plugins(void)
83 dir
= opendir(plugin_dir
);
85 error_msg("couldn't open directory `%s': %s", plugin_dir
, strerror(errno
));
88 while ((d
= readdir(dir
)) != NULL
) {
90 struct output_plugin
*plug
;
95 if (d
->d_name
[0] == '.')
97 ext
= strrchr(d
->d_name
, '.');
100 if (strcmp(ext
, ".so"))
103 snprintf(filename
, sizeof(filename
), "%s/%s", plugin_dir
, d
->d_name
);
105 so
= dlopen(filename
, RTLD_NOW
);
107 error_msg("%s", dlerror());
111 plug
= xnew(struct output_plugin
, 1);
114 if (!(plug
->pcm_ops
= dlsym(so
, sym
)))
117 sym
= "op_pcm_options";
118 if (!(plug
->pcm_options
= dlsym(so
, sym
)))
122 symptr
= dlsym(so
, sym
);
125 plug
->priority
= *(int *)symptr
;
127 plug
->mixer_ops
= dlsym(so
, "op_mixer_ops");
128 plug
->mixer_options
= dlsym(so
, "op_mixer_options");
130 if (plug
->mixer_ops
== NULL
|| plug
->mixer_options
== NULL
) {
131 plug
->mixer_ops
= NULL
;
132 plug
->mixer_options
= NULL
;
135 plug
->name
= xstrndup(d
->d_name
, ext
- d
->d_name
);
137 plug
->pcm_initialized
= 0;
138 plug
->mixer_initialized
= 0;
139 plug
->mixer_open
= 0;
144 error_msg("%s: symbol %s not found", filename
, sym
);
151 static void init_plugin(struct output_plugin
*o
)
153 if (!o
->mixer_initialized
&& o
->mixer_ops
) {
154 if (o
->mixer_ops
->init() == 0) {
155 d_print("initialized mixer for %s\n", o
->name
);
156 o
->mixer_initialized
= 1;
158 d_print("could not initialize mixer `%s'\n", o
->name
);
161 if (!o
->pcm_initialized
) {
162 if (o
->pcm_ops
->init() == 0) {
163 d_print("initialized pcm for %s\n", o
->name
);
164 o
->pcm_initialized
= 1;
166 d_print("could not initialize pcm `%s'\n", o
->name
);
171 void op_exit_plugins(void)
173 struct output_plugin
*o
;
175 list_for_each_entry(o
, &op_head
, node
) {
176 if (o
->mixer_initialized
&& o
->mixer_ops
)
177 o
->mixer_ops
->exit();
178 if (o
->pcm_initialized
)
183 void mixer_close(void)
186 if (op
&& op
->mixer_open
) {
187 BUG_ON(op
->mixer_ops
== NULL
);
188 op
->mixer_ops
->close();
193 void mixer_open(void)
198 BUG_ON(op
->mixer_open
);
199 if (op
->mixer_ops
&& op
->mixer_initialized
) {
202 rc
= op
->mixer_ops
->open(&volume_max
);
212 static int select_plugin(struct output_plugin
*o
)
214 /* try to initialize if not initialized yet */
217 if (!o
->pcm_initialized
)
218 return -OP_ERROR_NOT_INITIALIZED
;
223 int op_select(const char *name
)
225 struct output_plugin
*o
;
227 list_for_each_entry(o
, &op_head
, node
) {
228 if (strcasecmp(name
, o
->name
) == 0)
229 return select_plugin(o
);
231 return -OP_ERROR_NO_PLUGIN
;
234 int op_select_any(void)
236 struct output_plugin
*o
;
237 int rc
= -OP_ERROR_NO_PLUGIN
;
239 list_for_each_entry(o
, &op_head
, node
) {
240 rc
= select_plugin(o
);
247 int op_open(sample_format_t sf
)
250 return -OP_ERROR_NOT_INITIALIZED
;
251 return op
->pcm_ops
->open(sf
);
256 if (op
->pcm_ops
->drop
== NULL
)
257 return -OP_ERROR_NOT_SUPPORTED
;
258 return op
->pcm_ops
->drop();
263 return op
->pcm_ops
->close();
266 int op_write(const char *buffer
, int count
)
270 rc
= op
->pcm_ops
->write(buffer
, count
);
276 if (op
->pcm_ops
->pause
== NULL
)
278 return op
->pcm_ops
->pause();
283 if (op
->pcm_ops
->unpause
== NULL
)
285 return op
->pcm_ops
->unpause();
288 int op_buffer_space(void)
292 rc
= op
->pcm_ops
->buffer_space();
296 int mixer_set_volume(int left
, int right
)
299 return -OP_ERROR_NOT_INITIALIZED
;
301 return -OP_ERROR_NOT_OPEN
;
302 return op
->mixer_ops
->set_volume(left
, right
);
305 int mixer_read_volume(void)
308 return -OP_ERROR_NOT_INITIALIZED
;
310 return -OP_ERROR_NOT_OPEN
;
311 return op
->mixer_ops
->get_volume(&volume_l
, &volume_r
);
314 int mixer_get_fds(int *fds
)
317 return -OP_ERROR_NOT_INITIALIZED
;
318 if (!op
->mixer_ops
->get_fds
)
319 return -OP_ERROR_NOT_SUPPORTED
;
321 return -OP_ERROR_NOT_OPEN
;
322 return op
->mixer_ops
->get_fds(fds
);
325 #define OP_OPT_ID(plugin_idx, is_mixer, option_idx) \
326 (((plugin_idx) << 16) | ((is_mixer) << 15) | (option_idx))
328 static struct output_plugin
*find_plugin(int idx
)
330 struct output_plugin
*o
;
332 list_for_each_entry(o
, &op_head
, node
) {
342 int op_set_option(unsigned int id
, const char *val
)
344 unsigned int pid
= id
>> 16;
345 unsigned int mix
= id
& 0x8000;
346 unsigned int oid
= id
& 0x7fff;
347 const struct output_plugin
*o
= find_plugin(pid
);
350 return -OP_ERROR_NOT_OPTION
;
353 const struct mixer_plugin_ops
*mo
= o
->mixer_ops
;
354 int rc
= mo
->set_option(oid
, val
);
356 if (rc
== 0 && op
&& op
->mixer_ops
== mo
) {
357 /* option of the current op was set
358 * try to reopen the mixer */
365 return o
->pcm_ops
->set_option(oid
, val
);
368 int op_get_option(unsigned int id
, char **val
)
370 unsigned int pid
= id
>> 16;
371 unsigned int mix
= id
& 0x8000;
372 unsigned int oid
= id
& 0x7fff;
373 const struct output_plugin
*o
= find_plugin(pid
);
376 return -OP_ERROR_NOT_OPTION
;
379 return o
->mixer_ops
->get_option(oid
, val
);
380 return o
->pcm_ops
->get_option(oid
, val
);
383 int op_for_each_option(void (*cb
)(unsigned int id
, const char *key
))
385 struct output_plugin
*o
;
386 unsigned int pid
, oid
;
390 list_for_each_entry(o
, &op_head
, node
) {
393 for (oid
= 0; o
->pcm_options
[oid
]; oid
++) {
394 snprintf(key
, sizeof(key
), "dsp.%s.%s",
396 o
->pcm_options
[oid
]);
397 cb(OP_OPT_ID(pid
, 0, oid
), key
);
399 if (o
->mixer_ops
== NULL
)
401 for (oid
= 0; o
->mixer_options
[oid
]; oid
++) {
402 snprintf(key
, sizeof(key
), "mixer.%s.%s",
404 o
->mixer_options
[oid
]);
405 cb(OP_OPT_ID(pid
, 1, oid
), key
);
411 char *op_get_error_msg(int rc
, const char *arg
)
417 snprintf(buffer
, sizeof(buffer
), "%s: %s", arg
, strerror(errno
));
419 case OP_ERROR_NO_PLUGIN
:
420 snprintf(buffer
, sizeof(buffer
),
421 "%s: no such plugin", arg
);
423 case OP_ERROR_NOT_INITIALIZED
:
424 snprintf(buffer
, sizeof(buffer
),
425 "%s: couldn't initialize required output plugin", arg
);
427 case OP_ERROR_NOT_SUPPORTED
:
428 snprintf(buffer
, sizeof(buffer
),
429 "%s: function not supported", arg
);
431 case OP_ERROR_NOT_OPEN
:
432 snprintf(buffer
, sizeof(buffer
),
433 "%s: mixer is not open", arg
);
435 case OP_ERROR_SAMPLE_FORMAT
:
436 snprintf(buffer
, sizeof(buffer
),
437 "%s: sample format not supported", arg
);
439 case OP_ERROR_NOT_OPTION
:
440 snprintf(buffer
, sizeof(buffer
),
441 "%s: no such option", arg
);
443 case OP_ERROR_INTERNAL
:
444 snprintf(buffer
, sizeof(buffer
), "%s: internal error", arg
);
446 case OP_ERROR_SUCCESS
:
448 snprintf(buffer
, sizeof(buffer
),
449 "%s: this is not an error (%d), this is a bug",
453 return xstrdup(buffer
);
456 void op_dump_plugins(void)
458 struct output_plugin
*o
;
460 printf("\nOutput Plugins: %s\n", plugin_dir
);
461 list_for_each_entry(o
, &op_head
, node
) {
462 printf(" %s\n", o
->name
);
466 char *op_get_current(void)
469 return xstrdup(op
->name
);