Update screen title instead of xterm title if TERM is "screen"
[cmus.git] / output.c
blobf87fee7317421b23faa2fd50bcb20b60cc5cdc8a
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 <prog.h>
25 #include <utils.h>
26 #include <xmalloc.h>
27 #include <list.h>
28 #include <debug.h>
29 #include <config.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 "/" 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 */
59 int volume_max = 0;
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)
69 break;
70 item = item->next;
73 /* add before item */
74 list_add_tail(&plugin->node, item);
77 void op_load_plugins(void)
79 DIR *dir;
80 struct dirent *d;
82 dir = opendir(plugin_dir);
83 if (dir == NULL) {
84 warn("couldn't open directory `%s': %s\n", plugin_dir, strerror(errno));
85 return;
87 while ((d = readdir(dir)) != NULL) {
88 char filename[256];
89 struct output_plugin *plug;
90 void *so, *symptr;
91 char *ext;
92 const char *sym;
94 if (d->d_name[0] == '.')
95 continue;
96 ext = strrchr(d->d_name, '.');
97 if (ext == NULL)
98 continue;
99 if (strcmp(ext, ".so"))
100 continue;
102 snprintf(filename, sizeof(filename), "%s/%s", plugin_dir, d->d_name);
104 so = dlopen(filename, RTLD_NOW);
105 if (so == NULL) {
106 warn("%s\n", dlerror());
107 continue;
110 plug = xnew(struct output_plugin, 1);
112 sym = "op_pcm_ops";
113 if (!(plug->pcm_ops = dlsym(so, sym)))
114 goto sym_err;
116 sym = "op_pcm_options";
117 if (!(plug->pcm_options = dlsym(so, sym)))
118 goto sym_err;
120 sym = "op_priority";
121 symptr = dlsym(so, sym);
122 if (symptr == NULL)
123 goto sym_err;
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);
135 plug->handle = so;
136 plug->pcm_initialized = 0;
137 plug->mixer_initialized = 0;
138 plug->mixer_open = 0;
140 add_plugin(plug);
141 continue;
142 sym_err:
143 warn("%s: symbol %s not found\n", filename, sym);
144 free(plug);
145 dlclose(so);
147 closedir(dir);
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;
155 } else {
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;
162 } else {
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)
173 init_plugin(o);
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)
184 o->pcm_ops->exit();
188 static void close_mixer(void)
190 volume_max = 0;
191 if (op && op->mixer_open) {
192 BUG_ON(op->mixer_ops == NULL);
193 op->mixer_ops->close();
194 op->mixer_open = 0;
198 static void open_mixer(void)
200 if (op == NULL)
201 return;
203 BUG_ON(op->mixer_open);
204 if (op->mixer_ops && op->mixer_initialized) {
205 int rc;
207 rc = op->mixer_ops->open(&volume_max);
208 if (rc == 0) {
209 op->mixer_open = 1;
210 } else {
211 volume_max = 0;
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 */
223 init_plugin(o);
225 if (!o->pcm_initialized)
226 return -OP_ERROR_NOT_INITIALIZED;
228 close_mixer();
229 op = o;
230 open_mixer();
231 return 0;
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)
244 continue;
246 rc = op_select(o->name);
247 if (rc == 0)
248 break;
250 return rc;
253 int op_open(sample_format_t sf)
255 int rc;
257 if (op == NULL)
258 return -OP_ERROR_NOT_INITIALIZED;
259 current_sf = sf;
260 rc = op->pcm_ops->open(sf);
261 if (rc)
262 current_sf = 0;
263 return rc;
266 int op_set_sf(sample_format_t sf)
268 int rc = 0;
270 if (current_sf != sf) {
271 rc = op_close();
272 if (rc) {
273 d_print("op_close returned %d\n", rc);
274 return rc;
276 rc = op_open(sf);
277 if (rc) {
278 return rc;
279 } else {
280 current_sf = sf;
281 return 1;
284 return 0;
287 int op_second_size(void)
289 return sf_get_rate(current_sf) * sf_get_frame_size(current_sf);
292 int op_drop(void)
294 if (op->pcm_ops->drop == NULL)
295 return -OP_ERROR_NOT_SUPPORTED;
296 return op->pcm_ops->drop();
299 int op_close(void)
301 current_sf = 0;
302 return op->pcm_ops->close();
305 int op_write(const char *buffer, int count)
307 int rc;
309 rc = op->pcm_ops->write(buffer, count);
310 return rc;
313 int op_pause(void)
315 if (op->pcm_ops->pause == NULL)
316 return 0;
317 return op->pcm_ops->pause();
320 int op_unpause(void)
322 if (op->pcm_ops->unpause == NULL)
323 return 0;
324 return op->pcm_ops->unpause();
327 int op_buffer_space(void)
329 int rc;
331 rc = op->pcm_ops->buffer_space();
332 return rc;
335 int op_set_volume(int left, int right)
337 if (op == NULL)
338 return -OP_ERROR_NOT_INITIALIZED;
339 if (!op->mixer_open)
340 return -OP_ERROR_NOT_SUPPORTED;
341 return op->mixer_ops->set_volume(left, right);
344 int op_get_volume(int *left, int *right)
346 if (op == NULL)
347 return -OP_ERROR_NOT_INITIALIZED;
348 if (!op->mixer_open)
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) {
361 if (idx == 0)
362 return o;
363 idx--;
365 return NULL;
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);
375 if (o == NULL)
376 return -OP_ERROR_NOT_OPTION;
378 if (mix) {
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 */
385 close_mixer();
386 open_mixer();
388 return rc;
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);
400 if (o == NULL)
401 return -OP_ERROR_NOT_OPTION;
403 if (mix)
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;
412 char key[64];
414 pid = -1;
415 list_for_each_entry(o, &op_head, node) {
416 pid++;
418 for (oid = 0; o->pcm_options[oid]; oid++) {
419 snprintf(key, sizeof(key), "dsp.%s.%s",
420 o->name,
421 o->pcm_options[oid]);
422 cb(OP_OPT_ID(pid, 0, oid), key);
424 if (o->mixer_ops == NULL)
425 continue;
426 for (oid = 0; o->mixer_options[oid]; oid++) {
427 snprintf(key, sizeof(key), "mixer.%s.%s",
428 o->name,
429 o->mixer_options[oid]);
430 cb(OP_OPT_ID(pid, 1, oid), key);
433 return 0;
436 char *op_get_error_msg(int rc, const char *arg)
438 char buffer[1024];
440 switch (-rc) {
441 case OP_ERROR_ERRNO:
442 snprintf(buffer, sizeof(buffer), "%s: %s", arg, strerror(errno));
443 break;
444 case OP_ERROR_NO_PLUGIN:
445 snprintf(buffer, sizeof(buffer),
446 "%s: no such plugin", arg);
447 break;
448 case OP_ERROR_NOT_INITIALIZED:
449 snprintf(buffer, sizeof(buffer),
450 "%s: couldn't initialize required output plugin", arg);
451 break;
452 case OP_ERROR_NOT_SUPPORTED:
453 snprintf(buffer, sizeof(buffer),
454 "%s: function not supported", arg);
455 break;
456 case OP_ERROR_SAMPLE_FORMAT:
457 snprintf(buffer, sizeof(buffer),
458 "%s: sample format not supported", arg);
459 break;
460 case OP_ERROR_NOT_OPTION:
461 snprintf(buffer, sizeof(buffer),
462 "%s: no such option", arg);
463 break;
464 case OP_ERROR_INTERNAL:
465 snprintf(buffer, sizeof(buffer), "%s: internal error", arg);
466 break;
467 case OP_ERROR_SUCCESS:
468 default:
469 snprintf(buffer, sizeof(buffer),
470 "%s: this is not an error (%d), this is a bug",
471 arg, rc);
472 break;
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)
489 if (op)
490 return xstrdup(op->name);
491 return NULL;