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"
30 #include "config/libdir.h"
34 #include <sys/types.h>
38 struct output_plugin
{
39 struct list_head node
;
43 const struct output_plugin_ops
*pcm_ops
;
44 const struct mixer_plugin_ops
*mixer_ops
;
45 const char * const *pcm_options
;
46 const char * const *mixer_options
;
49 unsigned int pcm_initialized
: 1;
50 unsigned int mixer_initialized
: 1;
51 unsigned int mixer_open
: 1;
54 static const char * const plugin_dir
= LIBDIR
"/cmus/op";
55 static LIST_HEAD(op_head
);
56 static struct output_plugin
*op
= NULL
;
58 /* volume is between 0 and volume_max */
63 static void add_plugin(struct output_plugin
*plugin
)
65 struct list_head
*item
= op_head
.next
;
67 while (item
!= &op_head
) {
68 struct output_plugin
*o
= container_of(item
, struct output_plugin
, node
);
70 if (plugin
->priority
< o
->priority
)
76 list_add_tail(&plugin
->node
, item
);
79 void op_load_plugins(void)
84 dir
= opendir(plugin_dir
);
86 error_msg("couldn't open directory `%s': %s", plugin_dir
, strerror(errno
));
89 while ((d
= readdir(dir
)) != NULL
) {
91 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);
113 plug
->pcm_ops
= dlsym(so
, "op_pcm_ops");
114 plug
->pcm_options
= dlsym(so
, "op_pcm_options");
115 symptr
= dlsym(so
, "op_priority");
116 if (!plug
->pcm_ops
|| !plug
->pcm_options
|| !symptr
) {
117 error_msg("%s: missing symbol", filename
);
122 plug
->priority
= *(int *)symptr
;
124 plug
->mixer_ops
= dlsym(so
, "op_mixer_ops");
125 plug
->mixer_options
= dlsym(so
, "op_mixer_options");
126 if (plug
->mixer_ops
== NULL
|| plug
->mixer_options
== NULL
) {
127 plug
->mixer_ops
= NULL
;
128 plug
->mixer_options
= NULL
;
131 plug
->name
= xstrndup(d
->d_name
, ext
- d
->d_name
);
133 plug
->pcm_initialized
= 0;
134 plug
->mixer_initialized
= 0;
135 plug
->mixer_open
= 0;
142 static void init_plugin(struct output_plugin
*o
)
144 if (!o
->mixer_initialized
&& o
->mixer_ops
) {
145 if (o
->mixer_ops
->init() == 0) {
146 d_print("initialized mixer for %s\n", o
->name
);
147 o
->mixer_initialized
= 1;
149 d_print("could not initialize mixer `%s'\n", o
->name
);
152 if (!o
->pcm_initialized
) {
153 if (o
->pcm_ops
->init() == 0) {
154 d_print("initialized pcm for %s\n", o
->name
);
155 o
->pcm_initialized
= 1;
157 d_print("could not initialize pcm `%s'\n", o
->name
);
162 void op_exit_plugins(void)
164 struct output_plugin
*o
;
166 list_for_each_entry(o
, &op_head
, node
) {
167 if (o
->mixer_initialized
&& o
->mixer_ops
)
168 o
->mixer_ops
->exit();
169 if (o
->pcm_initialized
)
174 void mixer_close(void)
177 if (op
&& op
->mixer_open
) {
178 BUG_ON(op
->mixer_ops
== NULL
);
179 op
->mixer_ops
->close();
184 void mixer_open(void)
189 BUG_ON(op
->mixer_open
);
190 if (op
->mixer_ops
&& op
->mixer_initialized
) {
193 rc
= op
->mixer_ops
->open(&volume_max
);
203 static int select_plugin(struct output_plugin
*o
)
205 /* try to initialize if not initialized yet */
208 if (!o
->pcm_initialized
)
209 return -OP_ERROR_NOT_INITIALIZED
;
214 int op_select(const char *name
)
216 struct output_plugin
*o
;
218 list_for_each_entry(o
, &op_head
, node
) {
219 if (strcasecmp(name
, o
->name
) == 0)
220 return select_plugin(o
);
222 return -OP_ERROR_NO_PLUGIN
;
225 int op_select_any(void)
227 struct output_plugin
*o
;
228 int rc
= -OP_ERROR_NO_PLUGIN
;
230 list_for_each_entry(o
, &op_head
, node
) {
231 rc
= select_plugin(o
);
238 int op_open(sample_format_t sf
)
241 return -OP_ERROR_NOT_INITIALIZED
;
242 return op
->pcm_ops
->open(sf
);
247 if (op
->pcm_ops
->drop
== NULL
)
248 return -OP_ERROR_NOT_SUPPORTED
;
249 return op
->pcm_ops
->drop();
254 return op
->pcm_ops
->close();
257 int op_write(const char *buffer
, int count
)
261 rc
= op
->pcm_ops
->write(buffer
, count
);
267 if (op
->pcm_ops
->pause
== NULL
)
269 return op
->pcm_ops
->pause();
274 if (op
->pcm_ops
->unpause
== NULL
)
276 return op
->pcm_ops
->unpause();
279 int op_buffer_space(void)
283 rc
= op
->pcm_ops
->buffer_space();
287 int mixer_set_volume(int left
, int right
)
290 return -OP_ERROR_NOT_INITIALIZED
;
292 return -OP_ERROR_NOT_OPEN
;
293 return op
->mixer_ops
->set_volume(left
, right
);
296 int mixer_read_volume(void)
299 return -OP_ERROR_NOT_INITIALIZED
;
301 return -OP_ERROR_NOT_OPEN
;
302 return op
->mixer_ops
->get_volume(&volume_l
, &volume_r
);
305 int mixer_get_fds(int *fds
)
308 return -OP_ERROR_NOT_INITIALIZED
;
310 return -OP_ERROR_NOT_OPEN
;
311 if (!op
->mixer_ops
->get_fds
)
312 return -OP_ERROR_NOT_SUPPORTED
;
313 return op
->mixer_ops
->get_fds(fds
);
316 static struct output_plugin
*find_plugin(int idx
)
318 struct output_plugin
*o
;
320 list_for_each_entry(o
, &op_head
, node
) {
330 static void option_error(int rc
)
332 char *msg
= op_get_error_msg(rc
, "setting option");
333 error_msg("%s", msg
);
337 static void set_dsp_option(unsigned int id
, const char *val
)
339 const struct output_plugin
*o
= find_plugin(id
>> 16);
342 rc
= o
->pcm_ops
->set_option(id
& 0xffff, val
);
347 static void set_mixer_option(unsigned int id
, const char *val
)
349 const struct output_plugin
*o
= find_plugin(id
>> 16);
352 rc
= o
->mixer_ops
->set_option(id
& 0xffff, val
);
357 if (op
&& op
->mixer_ops
== o
->mixer_ops
) {
358 /* option of the current op was set
359 * try to reopen the mixer */
366 static void get_dsp_option(unsigned int id
, char *buf
)
368 const struct output_plugin
*o
= find_plugin(id
>> 16);
371 o
->pcm_ops
->get_option(id
& 0xffff, &val
);
378 static void get_mixer_option(unsigned int id
, char *buf
)
380 const struct output_plugin
*o
= find_plugin(id
>> 16);
383 o
->mixer_ops
->get_option(id
& 0xffff, &val
);
390 void op_add_options(void)
392 struct output_plugin
*o
;
393 unsigned int oid
, pid
= 0;
396 list_for_each_entry(o
, &op_head
, node
) {
397 for (oid
= 0; o
->pcm_options
[oid
]; oid
++) {
398 snprintf(key
, sizeof(key
), "dsp.%s.%s",
400 o
->pcm_options
[oid
]);
401 option_add(xstrdup(key
), (pid
<< 16) | oid
, get_dsp_option
, set_dsp_option
, NULL
);
403 for (oid
= 0; o
->mixer_ops
&& o
->mixer_options
[oid
]; oid
++) {
404 snprintf(key
, sizeof(key
), "mixer.%s.%s",
406 o
->mixer_options
[oid
]);
407 option_add(xstrdup(key
), (pid
<< 16) | oid
, get_mixer_option
, set_mixer_option
, NULL
);
413 char *op_get_error_msg(int rc
, const char *arg
)
419 snprintf(buffer
, sizeof(buffer
), "%s: %s", arg
, strerror(errno
));
421 case OP_ERROR_NO_PLUGIN
:
422 snprintf(buffer
, sizeof(buffer
),
423 "%s: no such plugin", arg
);
425 case OP_ERROR_NOT_INITIALIZED
:
426 snprintf(buffer
, sizeof(buffer
),
427 "%s: couldn't initialize required output plugin", arg
);
429 case OP_ERROR_NOT_SUPPORTED
:
430 snprintf(buffer
, sizeof(buffer
),
431 "%s: function not supported", arg
);
433 case OP_ERROR_NOT_OPEN
:
434 snprintf(buffer
, sizeof(buffer
),
435 "%s: mixer is not open", arg
);
437 case OP_ERROR_SAMPLE_FORMAT
:
438 snprintf(buffer
, sizeof(buffer
),
439 "%s: sample format not supported", arg
);
441 case OP_ERROR_NOT_OPTION
:
442 snprintf(buffer
, sizeof(buffer
),
443 "%s: no such option", arg
);
445 case OP_ERROR_INTERNAL
:
446 snprintf(buffer
, sizeof(buffer
), "%s: internal error", arg
);
448 case OP_ERROR_SUCCESS
:
450 snprintf(buffer
, sizeof(buffer
),
451 "%s: this is not an error (%d), this is a bug",
455 return xstrdup(buffer
);
458 void op_dump_plugins(void)
460 struct output_plugin
*o
;
462 printf("\nOutput Plugins: %s\n", plugin_dir
);
463 list_for_each_entry(o
, &op_head
, node
) {
464 printf(" %s\n", o
->name
);
468 const char *op_get_current(void)