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
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
"/" PACKAGE
"/op";
54 static LIST_HEAD(op_head
);
55 static struct output_plugin
*op
= NULL
;
56 static sample_format_t current_sf
= 0;
58 /* volume is between 0 and volume_max */
61 static void add_plugin(struct output_plugin
*plugin
)
63 struct list_head
*item
= op_head
.next
;
65 while (item
!= &op_head
) {
66 struct output_plugin
*o
= container_of(item
, struct output_plugin
, node
);
68 if (plugin
->priority
< o
->priority
)
74 list_add_tail(&plugin
->node
, item
);
77 void op_load_plugins(void)
82 dir
= opendir(plugin_dir
);
84 warn("couldn't open directory `%s': %s\n", plugin_dir
, strerror(errno
));
87 while ((d
= readdir(dir
)) != NULL
) {
89 struct output_plugin
*plug
;
94 if (d
->d_name
[0] == '.')
96 ext
= strrchr(d
->d_name
, '.');
99 if (strcmp(ext
, ".so"))
102 snprintf(filename
, sizeof(filename
), "%s/%s", plugin_dir
, d
->d_name
);
104 so
= dlopen(filename
, RTLD_NOW
);
106 warn("%s\n", dlerror());
110 plug
= xnew(struct output_plugin
, 1);
113 if (!(plug
->pcm_ops
= dlsym(so
, sym
)))
116 sym
= "op_pcm_options";
117 if (!(plug
->pcm_options
= dlsym(so
, sym
)))
121 symptr
= dlsym(so
, sym
);
124 plug
->priority
= *(int *)symptr
;
126 plug
->mixer_ops
= dlsym(so
, "op_mixer_ops");
127 plug
->mixer_options
= dlsym(so
, "op_mixer_options");
129 if (plug
->mixer_ops
== NULL
|| plug
->mixer_options
== NULL
) {
130 plug
->mixer_ops
= NULL
;
131 plug
->mixer_options
= NULL
;
134 plug
->name
= xstrndup(d
->d_name
, ext
- d
->d_name
);
136 plug
->pcm_initialized
= 0;
137 plug
->mixer_initialized
= 0;
138 plug
->mixer_open
= 0;
143 warn("%s: symbol %s not found\n", filename
, sym
);
150 static void init_plugin(struct output_plugin
*o
)
152 if (!o
->mixer_initialized
&& o
->mixer_ops
) {
153 if (o
->mixer_ops
->init() == 0) {
154 o
->mixer_initialized
= 1;
156 d_print("could not initialize mixer `%s'\n", o
->name
);
159 if (!o
->pcm_initialized
) {
160 if (o
->pcm_ops
->init() == 0) {
161 o
->pcm_initialized
= 1;
163 d_print("could not initialize pcm `%s'\n", o
->name
);
168 void op_init_plugins(void)
170 struct output_plugin
*o
;
172 list_for_each_entry(o
, &op_head
, node
)
176 void op_exit_plugins(void)
178 struct output_plugin
*o
;
180 list_for_each_entry(o
, &op_head
, node
) {
181 if (o
->mixer_initialized
&& o
->mixer_ops
)
182 o
->mixer_ops
->exit();
183 if (o
->pcm_initialized
)
188 static void close_mixer(void)
191 if (op
&& op
->mixer_open
) {
192 BUG_ON(op
->mixer_ops
== NULL
);
193 op
->mixer_ops
->close();
198 static void open_mixer(void)
203 BUG_ON(op
->mixer_open
);
204 if (op
->mixer_ops
&& op
->mixer_initialized
) {
207 rc
= op
->mixer_ops
->open(&volume_max
);
216 int op_select(const char *name
)
218 struct output_plugin
*o
;
220 list_for_each_entry(o
, &op_head
, node
) {
221 if (strcasecmp(name
, o
->name
) == 0) {
222 /* try to initialize if not initialized yet */
225 if (!o
->pcm_initialized
)
226 return -OP_ERROR_NOT_INITIALIZED
;
234 return -OP_ERROR_NO_PLUGIN
;
237 int op_select_any(void)
239 struct output_plugin
*o
;
240 int rc
= -OP_ERROR_NO_PLUGIN
;
242 list_for_each_entry(o
, &op_head
, node
) {
243 if (!o
->pcm_initialized
)
246 rc
= op_select(o
->name
);
253 int op_open(sample_format_t sf
)
258 return -OP_ERROR_NOT_INITIALIZED
;
260 rc
= op
->pcm_ops
->open(sf
);
266 int op_set_sf(sample_format_t sf
)
270 if (current_sf
!= sf
) {
273 d_print("op_close returned %d\n", rc
);
287 int op_second_size(void)
289 return sf_get_rate(current_sf
) * sf_get_frame_size(current_sf
);
294 if (op
->pcm_ops
->drop
== NULL
)
295 return -OP_ERROR_NOT_SUPPORTED
;
296 return op
->pcm_ops
->drop();
302 return op
->pcm_ops
->close();
305 int op_write(const char *buffer
, int count
)
309 rc
= op
->pcm_ops
->write(buffer
, count
);
315 if (op
->pcm_ops
->pause
== NULL
)
317 return op
->pcm_ops
->pause();
322 if (op
->pcm_ops
->unpause
== NULL
)
324 return op
->pcm_ops
->unpause();
327 int op_buffer_space(void)
331 rc
= op
->pcm_ops
->buffer_space();
335 int op_set_volume(int left
, int right
)
338 return -OP_ERROR_NOT_INITIALIZED
;
340 return -OP_ERROR_NOT_SUPPORTED
;
341 return op
->mixer_ops
->set_volume(left
, right
);
344 int op_get_volume(int *left
, int *right
)
347 return -OP_ERROR_NOT_INITIALIZED
;
349 return -OP_ERROR_NOT_SUPPORTED
;
350 return op
->mixer_ops
->get_volume(left
, right
);
353 #define OP_OPT_ID(plugin_idx, is_mixer, option_idx) \
354 (((plugin_idx) << 16) | ((is_mixer) << 15) | (option_idx))
356 static struct output_plugin
*find_plugin(int idx
)
358 struct output_plugin
*o
;
360 list_for_each_entry(o
, &op_head
, node
) {
368 int op_set_option(unsigned int id
, const 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 const struct mixer_plugin_ops
*mo
= o
->mixer_ops
;
380 int rc
= mo
->set_option(oid
, val
);
382 if (rc
== 0 && op
&& op
->mixer_ops
== mo
) {
383 /* option of the current op was set
384 * try to reopen the mixer */
390 return o
->pcm_ops
->set_option(oid
, val
);
393 int op_get_option(unsigned int id
, char **val
)
395 unsigned int pid
= id
>> 16;
396 unsigned int mix
= id
& 0x8000;
397 unsigned int oid
= id
& 0x7fff;
398 const struct output_plugin
*o
= find_plugin(pid
);
401 return -OP_ERROR_NOT_OPTION
;
404 return o
->mixer_ops
->get_option(oid
, val
);
405 return o
->pcm_ops
->get_option(oid
, val
);
408 int op_for_each_option(void (*cb
)(unsigned int id
, const char *key
))
410 struct output_plugin
*o
;
411 unsigned int pid
, oid
;
415 list_for_each_entry(o
, &op_head
, node
) {
418 for (oid
= 0; o
->pcm_options
[oid
]; oid
++) {
419 snprintf(key
, sizeof(key
), "dsp.%s.%s",
421 o
->pcm_options
[oid
]);
422 cb(OP_OPT_ID(pid
, 0, oid
), key
);
424 if (o
->mixer_ops
== NULL
)
426 for (oid
= 0; o
->mixer_options
[oid
]; oid
++) {
427 snprintf(key
, sizeof(key
), "mixer.%s.%s",
429 o
->mixer_options
[oid
]);
430 cb(OP_OPT_ID(pid
, 1, oid
), key
);
436 char *op_get_error_msg(int rc
, const char *arg
)
442 snprintf(buffer
, sizeof(buffer
), "%s: %s", arg
, strerror(errno
));
444 case OP_ERROR_NO_PLUGIN
:
445 snprintf(buffer
, sizeof(buffer
),
446 "%s: no such plugin", arg
);
448 case OP_ERROR_NOT_INITIALIZED
:
449 snprintf(buffer
, sizeof(buffer
),
450 "%s: couldn't initialize required output plugin", arg
);
452 case OP_ERROR_NOT_SUPPORTED
:
453 snprintf(buffer
, sizeof(buffer
),
454 "%s: function not supported", arg
);
456 case OP_ERROR_SAMPLE_FORMAT
:
457 snprintf(buffer
, sizeof(buffer
),
458 "%s: sample format not supported", arg
);
460 case OP_ERROR_NOT_OPTION
:
461 snprintf(buffer
, sizeof(buffer
),
462 "%s: no such option", arg
);
464 case OP_ERROR_INTERNAL
:
465 snprintf(buffer
, sizeof(buffer
), "%s: internal error", arg
);
467 case OP_ERROR_SUCCESS
:
469 snprintf(buffer
, sizeof(buffer
),
470 "%s: this is not an error (%d), this is a bug",
474 return xstrdup(buffer
);
477 void op_dump_plugins(void)
479 struct output_plugin
*o
;
481 printf("\nOutput Plugins: %s\n", plugin_dir
);
482 list_for_each_entry(o
, &op_head
, node
) {
483 printf(" %s\n", o
->name
);
487 char *op_get_current(void)
490 return xstrdup(op
->name
);