cmus-remote: Read command results
[cmus.git] / output.c
blobe7cad5efb35641bc10d811e4fbeb0217cb1b1a73
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 "config/libdir.h"
31 #include <string.h>
32 #include <stdlib.h>
33 #include <sys/types.h>
34 #include <dirent.h>
35 #include <dlfcn.h>
37 struct output_plugin {
38 struct list_head node;
39 char *name;
40 void *handle;
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;
46 int priority;
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 */
58 int volume_max = 0;
59 int volume_l = -1;
60 int volume_r = -1;
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)
70 break;
71 item = item->next;
74 /* add before item */
75 list_add_tail(&plugin->node, item);
78 void op_load_plugins(void)
80 DIR *dir;
81 struct dirent *d;
83 dir = opendir(plugin_dir);
84 if (dir == NULL) {
85 error_msg("couldn't open directory `%s': %s", plugin_dir, strerror(errno));
86 return;
88 while ((d = readdir(dir)) != NULL) {
89 char filename[256];
90 struct output_plugin *plug;
91 void *so, *symptr;
92 char *ext;
93 const char *sym;
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 sym = "op_pcm_ops";
114 if (!(plug->pcm_ops = dlsym(so, sym)))
115 goto sym_err;
117 sym = "op_pcm_options";
118 if (!(plug->pcm_options = dlsym(so, sym)))
119 goto sym_err;
121 sym = "op_priority";
122 symptr = dlsym(so, sym);
123 if (symptr == NULL)
124 goto sym_err;
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);
136 plug->handle = so;
137 plug->pcm_initialized = 0;
138 plug->mixer_initialized = 0;
139 plug->mixer_open = 0;
141 add_plugin(plug);
142 continue;
143 sym_err:
144 error_msg("%s: symbol %s not found", filename, sym);
145 free(plug);
146 dlclose(so);
148 closedir(dir);
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;
157 } else {
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;
165 } else {
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)
179 o->pcm_ops->exit();
183 void mixer_close(void)
185 volume_max = 0;
186 if (op && op->mixer_open) {
187 BUG_ON(op->mixer_ops == NULL);
188 op->mixer_ops->close();
189 op->mixer_open = 0;
193 void mixer_open(void)
195 if (op == NULL)
196 return;
198 BUG_ON(op->mixer_open);
199 if (op->mixer_ops && op->mixer_initialized) {
200 int rc;
202 rc = op->mixer_ops->open(&volume_max);
203 if (rc == 0) {
204 op->mixer_open = 1;
205 mixer_read_volume();
206 } else {
207 volume_max = 0;
212 static int select_plugin(struct output_plugin *o)
214 /* try to initialize if not initialized yet */
215 init_plugin(o);
217 if (!o->pcm_initialized)
218 return -OP_ERROR_NOT_INITIALIZED;
219 op = o;
220 return 0;
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);
241 if (rc == 0)
242 break;
244 return rc;
247 int op_open(sample_format_t sf)
249 if (op == NULL)
250 return -OP_ERROR_NOT_INITIALIZED;
251 return op->pcm_ops->open(sf);
254 int op_drop(void)
256 if (op->pcm_ops->drop == NULL)
257 return -OP_ERROR_NOT_SUPPORTED;
258 return op->pcm_ops->drop();
261 int op_close(void)
263 return op->pcm_ops->close();
266 int op_write(const char *buffer, int count)
268 int rc;
270 rc = op->pcm_ops->write(buffer, count);
271 return rc;
274 int op_pause(void)
276 if (op->pcm_ops->pause == NULL)
277 return 0;
278 return op->pcm_ops->pause();
281 int op_unpause(void)
283 if (op->pcm_ops->unpause == NULL)
284 return 0;
285 return op->pcm_ops->unpause();
288 int op_buffer_space(void)
290 int rc;
292 rc = op->pcm_ops->buffer_space();
293 return rc;
296 int mixer_set_volume(int left, int right)
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->set_volume(left, right);
305 int mixer_read_volume(void)
307 if (op == NULL)
308 return -OP_ERROR_NOT_INITIALIZED;
309 if (!op->mixer_open)
310 return -OP_ERROR_NOT_OPEN;
311 return op->mixer_ops->get_volume(&volume_l, &volume_r);
314 int mixer_get_fds(int *fds)
316 if (op == NULL)
317 return -OP_ERROR_NOT_INITIALIZED;
318 if (!op->mixer_open)
319 return -OP_ERROR_NOT_OPEN;
320 if (!op->mixer_ops->get_fds)
321 return -OP_ERROR_NOT_SUPPORTED;
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) {
333 if (idx == 0)
334 return o;
335 idx--;
337 return NULL;
340 extern int soft_vol;
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);
349 if (o == NULL)
350 return -OP_ERROR_NOT_OPTION;
352 if (mix) {
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 */
359 mixer_close();
360 if (!soft_vol)
361 mixer_open();
363 return rc;
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);
375 if (o == NULL)
376 return -OP_ERROR_NOT_OPTION;
378 if (mix)
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;
387 char key[64];
389 pid = -1;
390 list_for_each_entry(o, &op_head, node) {
391 pid++;
393 for (oid = 0; o->pcm_options[oid]; oid++) {
394 snprintf(key, sizeof(key), "dsp.%s.%s",
395 o->name,
396 o->pcm_options[oid]);
397 cb(OP_OPT_ID(pid, 0, oid), key);
399 if (o->mixer_ops == NULL)
400 continue;
401 for (oid = 0; o->mixer_options[oid]; oid++) {
402 snprintf(key, sizeof(key), "mixer.%s.%s",
403 o->name,
404 o->mixer_options[oid]);
405 cb(OP_OPT_ID(pid, 1, oid), key);
408 return 0;
411 char *op_get_error_msg(int rc, const char *arg)
413 char buffer[1024];
415 switch (-rc) {
416 case OP_ERROR_ERRNO:
417 snprintf(buffer, sizeof(buffer), "%s: %s", arg, strerror(errno));
418 break;
419 case OP_ERROR_NO_PLUGIN:
420 snprintf(buffer, sizeof(buffer),
421 "%s: no such plugin", arg);
422 break;
423 case OP_ERROR_NOT_INITIALIZED:
424 snprintf(buffer, sizeof(buffer),
425 "%s: couldn't initialize required output plugin", arg);
426 break;
427 case OP_ERROR_NOT_SUPPORTED:
428 snprintf(buffer, sizeof(buffer),
429 "%s: function not supported", arg);
430 break;
431 case OP_ERROR_NOT_OPEN:
432 snprintf(buffer, sizeof(buffer),
433 "%s: mixer is not open", arg);
434 break;
435 case OP_ERROR_SAMPLE_FORMAT:
436 snprintf(buffer, sizeof(buffer),
437 "%s: sample format not supported", arg);
438 break;
439 case OP_ERROR_NOT_OPTION:
440 snprintf(buffer, sizeof(buffer),
441 "%s: no such option", arg);
442 break;
443 case OP_ERROR_INTERNAL:
444 snprintf(buffer, sizeof(buffer), "%s: internal error", arg);
445 break;
446 case OP_ERROR_SUCCESS:
447 default:
448 snprintf(buffer, sizeof(buffer),
449 "%s: this is not an error (%d), this is a bug",
450 arg, rc);
451 break;
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)
468 if (op)
469 return xstrdup(op->name);
470 return NULL;