Handle streams separately in tree_add_track()
[cmus.git] / output.c
blobcf15cfc3261b0723075d8a1b1c87c47e5dd471a8
1 /*
2 * Copyright 2004-2006 Timo Hirvonen
3 *
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
17 * 02111-1307, USA.
20 #include "output.h"
21 #include "op.h"
22 #include "mixer.h"
23 #include "sf.h"
24 #include "utils.h"
25 #include "xmalloc.h"
26 #include "list.h"
27 #include "debug.h"
28 #include "ui_curses.h"
29 #include "options.h"
30 #include "config/libdir.h"
32 #include <string.h>
33 #include <stdlib.h>
34 #include <sys/types.h>
35 #include <dirent.h>
36 #include <dlfcn.h>
38 struct output_plugin {
39 struct list_head node;
40 char *name;
41 void *handle;
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;
47 int priority;
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 */
59 int volume_max = 0;
60 int volume_l = -1;
61 int volume_r = -1;
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)
71 break;
72 item = item->next;
75 /* add before item */
76 list_add_tail(&plugin->node, item);
79 void op_load_plugins(void)
81 DIR *dir;
82 struct dirent *d;
84 dir = opendir(plugin_dir);
85 if (dir == NULL) {
86 error_msg("couldn't open directory `%s': %s", plugin_dir, strerror(errno));
87 return;
89 while ((d = readdir(dir)) != NULL) {
90 char filename[256];
91 struct output_plugin *plug;
92 void *so, *symptr;
93 char *ext;
95 if (d->d_name[0] == '.')
96 continue;
97 ext = strrchr(d->d_name, '.');
98 if (ext == NULL)
99 continue;
100 if (strcmp(ext, ".so"))
101 continue;
103 snprintf(filename, sizeof(filename), "%s/%s", plugin_dir, d->d_name);
105 so = dlopen(filename, RTLD_NOW);
106 if (so == NULL) {
107 error_msg("%s", dlerror());
108 continue;
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);
118 free(plug);
119 dlclose(so);
120 continue;
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);
132 plug->handle = so;
133 plug->pcm_initialized = 0;
134 plug->mixer_initialized = 0;
135 plug->mixer_open = 0;
137 add_plugin(plug);
139 closedir(dir);
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;
148 } else {
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;
156 } else {
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)
170 o->pcm_ops->exit();
174 void mixer_close(void)
176 volume_max = 0;
177 if (op && op->mixer_open) {
178 BUG_ON(op->mixer_ops == NULL);
179 op->mixer_ops->close();
180 op->mixer_open = 0;
184 void mixer_open(void)
186 if (op == NULL)
187 return;
189 BUG_ON(op->mixer_open);
190 if (op->mixer_ops && op->mixer_initialized) {
191 int rc;
193 rc = op->mixer_ops->open(&volume_max);
194 if (rc == 0) {
195 op->mixer_open = 1;
196 mixer_read_volume();
197 } else {
198 volume_max = 0;
203 static int select_plugin(struct output_plugin *o)
205 /* try to initialize if not initialized yet */
206 init_plugin(o);
208 if (!o->pcm_initialized)
209 return -OP_ERROR_NOT_INITIALIZED;
210 op = o;
211 return 0;
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);
232 if (rc == 0)
233 break;
235 return rc;
238 int op_open(sample_format_t sf)
240 if (op == NULL)
241 return -OP_ERROR_NOT_INITIALIZED;
242 return op->pcm_ops->open(sf);
245 int op_drop(void)
247 if (op->pcm_ops->drop == NULL)
248 return -OP_ERROR_NOT_SUPPORTED;
249 return op->pcm_ops->drop();
252 int op_close(void)
254 return op->pcm_ops->close();
257 int op_write(const char *buffer, int count)
259 int rc;
261 rc = op->pcm_ops->write(buffer, count);
262 return rc;
265 int op_pause(void)
267 if (op->pcm_ops->pause == NULL)
268 return 0;
269 return op->pcm_ops->pause();
272 int op_unpause(void)
274 if (op->pcm_ops->unpause == NULL)
275 return 0;
276 return op->pcm_ops->unpause();
279 int op_buffer_space(void)
281 int rc;
283 rc = op->pcm_ops->buffer_space();
284 return rc;
287 int mixer_set_volume(int left, int right)
289 if (op == NULL)
290 return -OP_ERROR_NOT_INITIALIZED;
291 if (!op->mixer_open)
292 return -OP_ERROR_NOT_OPEN;
293 return op->mixer_ops->set_volume(left, right);
296 int mixer_read_volume(void)
298 if (op == NULL)
299 return -OP_ERROR_NOT_INITIALIZED;
300 if (!op->mixer_open)
301 return -OP_ERROR_NOT_OPEN;
302 return op->mixer_ops->get_volume(&volume_l, &volume_r);
305 int mixer_get_fds(int *fds)
307 if (op == NULL)
308 return -OP_ERROR_NOT_INITIALIZED;
309 if (!op->mixer_open)
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) {
321 if (idx == 0)
322 return o;
323 idx--;
325 return NULL;
328 extern int soft_vol;
330 static void option_error(int rc)
332 char *msg = op_get_error_msg(rc, "setting option");
333 error_msg("%s", msg);
334 free(msg);
337 static void set_dsp_option(unsigned int id, const char *val)
339 const struct output_plugin *o = find_plugin(id >> 16);
340 int rc;
342 rc = o->pcm_ops->set_option(id & 0xffff, val);
343 if (rc)
344 option_error(rc);
347 static void set_mixer_option(unsigned int id, const char *val)
349 const struct output_plugin *o = find_plugin(id >> 16);
350 int rc;
352 rc = o->mixer_ops->set_option(id & 0xffff, val);
353 if (rc) {
354 option_error(rc);
355 return;
357 if (op && op->mixer_ops == o->mixer_ops) {
358 /* option of the current op was set
359 * try to reopen the mixer */
360 mixer_close();
361 if (!soft_vol)
362 mixer_open();
366 static void get_dsp_option(unsigned int id, char *buf)
368 const struct output_plugin *o = find_plugin(id >> 16);
369 char *val = NULL;
371 o->pcm_ops->get_option(id & 0xffff, &val);
372 if (val) {
373 strcpy(buf, val);
374 free(val);
378 static void get_mixer_option(unsigned int id, char *buf)
380 const struct output_plugin *o = find_plugin(id >> 16);
381 char *val = NULL;
383 o->mixer_ops->get_option(id & 0xffff, &val);
384 if (val) {
385 strcpy(buf, val);
386 free(val);
390 void op_add_options(void)
392 struct output_plugin *o;
393 unsigned int oid, pid = 0;
394 char key[64];
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",
399 o->name,
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",
405 o->name,
406 o->mixer_options[oid]);
407 option_add(xstrdup(key), (pid << 16) | oid, get_mixer_option, set_mixer_option, NULL);
409 pid++;
413 char *op_get_error_msg(int rc, const char *arg)
415 char buffer[1024];
417 switch (-rc) {
418 case OP_ERROR_ERRNO:
419 snprintf(buffer, sizeof(buffer), "%s: %s", arg, strerror(errno));
420 break;
421 case OP_ERROR_NO_PLUGIN:
422 snprintf(buffer, sizeof(buffer),
423 "%s: no such plugin", arg);
424 break;
425 case OP_ERROR_NOT_INITIALIZED:
426 snprintf(buffer, sizeof(buffer),
427 "%s: couldn't initialize required output plugin", arg);
428 break;
429 case OP_ERROR_NOT_SUPPORTED:
430 snprintf(buffer, sizeof(buffer),
431 "%s: function not supported", arg);
432 break;
433 case OP_ERROR_NOT_OPEN:
434 snprintf(buffer, sizeof(buffer),
435 "%s: mixer is not open", arg);
436 break;
437 case OP_ERROR_SAMPLE_FORMAT:
438 snprintf(buffer, sizeof(buffer),
439 "%s: sample format not supported", arg);
440 break;
441 case OP_ERROR_NOT_OPTION:
442 snprintf(buffer, sizeof(buffer),
443 "%s: no such option", arg);
444 break;
445 case OP_ERROR_INTERNAL:
446 snprintf(buffer, sizeof(buffer), "%s: internal error", arg);
447 break;
448 case OP_ERROR_SUCCESS:
449 default:
450 snprintf(buffer, sizeof(buffer),
451 "%s: this is not an error (%d), this is a bug",
452 arg, rc);
453 break;
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)
470 if (op)
471 return op->name;
472 return NULL;