1 /*
2  * Copyright 2008-2013 Various Authors
3  * Copyright 2004-2006 Timo Hirvonen
4  *
5  * This program is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU General Public License as
7  * published by the Free Software Foundation; either version 2 of the
8  * License, or (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful, but
11  * WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, see <http://www.gnu.org/licenses/>.
17  */
18 
19 #include "output.h"
20 #include "op.h"
21 #include "mixer.h"
22 #include "sf.h"
23 #include "utils.h"
24 #include "xmalloc.h"
25 #include "list.h"
26 #include "debug.h"
27 #include "ui_curses.h"
28 #include "options.h"
29 #include "xstrjoin.h"
30 #include "misc.h"
31 
32 #include <string.h>
33 #include <strings.h>
34 #include <stdio.h>
35 #include <stdlib.h>
36 #include <stdbool.h>
37 #include <sys/types.h>
38 #include <dirent.h>
39 #include <dlfcn.h>
40 
41 struct output_plugin {
42 	struct list_head node;
43 	char *name;
44 	void *handle;
45 
46 	const struct output_plugin_ops *pcm_ops;
47 	const struct mixer_plugin_ops *mixer_ops;
48 	const struct output_plugin_opt *pcm_options;
49 	const struct mixer_plugin_opt *mixer_options;
50 	int priority;
51 
52 	unsigned int pcm_initialized : 1;
53 	unsigned int mixer_initialized : 1;
54 	unsigned int mixer_open : 1;
55 };
56 
57 static const char *plugin_dir;
58 static LIST_HEAD(op_head);
59 static struct output_plugin *op = NULL;
60 
61 /* volume is between 0 and volume_max */
62 int volume_max = 0;
63 int volume_l = -1;
64 int volume_r = -1;
65 
add_plugin(struct output_plugin * plugin)66 static void add_plugin(struct output_plugin *plugin)
67 {
68 	struct list_head *item = op_head.next;
69 
70 	while (item != &op_head) {
71 		struct output_plugin *o = container_of(item, struct output_plugin, node);
72 
73 		if (plugin->priority < o->priority)
74 			break;
75 		item = item->next;
76 	}
77 
78 	/* add before item */
79 	list_add_tail(&plugin->node, item);
80 }
81 
op_load_plugins(void)82 void op_load_plugins(void)
83 {
84 	DIR *dir;
85 	struct dirent *d;
86 
87 	plugin_dir = xstrjoin(cmus_lib_dir, "/op");
88 	dir = opendir(plugin_dir);
89 	if (dir == NULL) {
90 		error_msg("couldn't open directory `%s': %s", plugin_dir, strerror(errno));
91 		return;
92 	}
93 	while ((d = (struct dirent *) readdir(dir)) != NULL) {
94 		char filename[512];
95 		struct output_plugin *plug;
96 		void *so, *symptr;
97 		char *ext;
98 		const unsigned *abi_version_ptr;
99 		bool err = false;
100 
101 		if (d->d_name[0] == '.')
102 			continue;
103 		ext = strrchr(d->d_name, '.');
104 		if (ext == NULL)
105 			continue;
106 		if (strcmp(ext, ".so"))
107 			continue;
108 
109 		snprintf(filename, sizeof(filename), "%s/%s", plugin_dir, d->d_name);
110 
111 		so = dlopen(filename, RTLD_NOW);
112 		if (so == NULL) {
113 			d_print("%s: %s\n", filename, dlerror());
114 			continue;
115 		}
116 
117 		plug = xnew(struct output_plugin, 1);
118 
119 		plug->pcm_ops = dlsym(so, "op_pcm_ops");
120 		plug->pcm_options = dlsym(so, "op_pcm_options");
121 		symptr = dlsym(so, "op_priority");
122 		abi_version_ptr = dlsym(so, "op_abi_version");
123 		if (!plug->pcm_ops || !plug->pcm_options || !symptr) {
124 			error_msg("%s: missing symbol", filename);
125 			err = true;
126 		}
127 		if (!abi_version_ptr || *abi_version_ptr != OP_ABI_VERSION) {
128 			error_msg("%s: incompatible plugin version", filename);
129 			err = true;
130 		}
131 		if (err) {
132 			free(plug);
133 			dlclose(so);
134 			continue;
135 		}
136 		plug->priority = *(int *)symptr;
137 
138 		plug->mixer_ops = dlsym(so, "op_mixer_ops");
139 		plug->mixer_options = dlsym(so, "op_mixer_options");
140 		if (plug->mixer_ops == NULL || plug->mixer_options == NULL) {
141 			plug->mixer_ops = NULL;
142 			plug->mixer_options = NULL;
143 		}
144 
145 		plug->name = xstrndup(d->d_name, ext - d->d_name);
146 		plug->handle = so;
147 		plug->pcm_initialized = 0;
148 		plug->mixer_initialized = 0;
149 		plug->mixer_open = 0;
150 
151 		add_plugin(plug);
152 	}
153 	closedir(dir);
154 }
155 
init_plugin(struct output_plugin * o)156 static void init_plugin(struct output_plugin *o)
157 {
158 	if (!o->mixer_initialized && o->mixer_ops) {
159 		if (o->mixer_ops->init() == 0) {
160 			d_print("initialized mixer for %s\n", o->name);
161 			o->mixer_initialized = 1;
162 		} else {
163 			d_print("could not initialize mixer `%s'\n", o->name);
164 		}
165 	}
166 	if (!o->pcm_initialized) {
167 		if (o->pcm_ops->init() == 0) {
168 			d_print("initialized pcm for %s\n", o->name);
169 			o->pcm_initialized = 1;
170 		} else {
171 			d_print("could not initialize pcm `%s'\n", o->name);
172 		}
173 	}
174 }
175 
op_exit_plugins(void)176 void op_exit_plugins(void)
177 {
178 	struct output_plugin *o;
179 
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();
185 	}
186 }
187 
mixer_close(void)188 void mixer_close(void)
189 {
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;
195 	}
196 }
197 
mixer_open(void)198 void mixer_open(void)
199 {
200 	if (op == NULL)
201 		return;
202 
203 	BUG_ON(op->mixer_open);
204 	if (op->mixer_ops && op->mixer_initialized) {
205 		int rc;
206 
207 		rc = op->mixer_ops->open(&volume_max);
208 		if (rc == 0) {
209 			op->mixer_open = 1;
210 			mixer_read_volume();
211 		} else {
212 			volume_max = 0;
213 		}
214 	}
215 }
216 
select_plugin(struct output_plugin * o)217 static int select_plugin(struct output_plugin *o)
218 {
219 	/* try to initialize if not initialized yet */
220 	init_plugin(o);
221 
222 	if (!o->pcm_initialized)
223 		return -OP_ERROR_NOT_INITIALIZED;
224 	op = o;
225 	return 0;
226 }
227 
op_select(const char * name)228 int op_select(const char *name)
229 {
230 	struct output_plugin *o;
231 
232 	list_for_each_entry(o, &op_head, node) {
233 		if (strcasecmp(name, o->name) == 0)
234 			return select_plugin(o);
235 	}
236 	return -OP_ERROR_NO_PLUGIN;
237 }
238 
op_select_any(void)239 int op_select_any(void)
240 {
241 	struct output_plugin *o;
242 	int rc = -OP_ERROR_NO_PLUGIN;
243 	sample_format_t sf = sf_channels(2) | sf_rate(44100) | sf_bits(16) | sf_signed(1);
244 
245 	list_for_each_entry(o, &op_head, node) {
246 		rc = select_plugin(o);
247 		if (rc != 0)
248 			continue;
249 		rc = o->pcm_ops->open(sf, NULL);
250 		if (rc == 0) {
251 			o->pcm_ops->close();
252 			break;
253 		}
254 	}
255 	return rc;
256 }
257 
op_open(sample_format_t sf,const channel_position_t * channel_map)258 int op_open(sample_format_t sf, const channel_position_t *channel_map)
259 {
260 	if (op == NULL)
261 		return -OP_ERROR_NOT_INITIALIZED;
262 	return op->pcm_ops->open(sf, channel_map);
263 }
264 
op_drop(void)265 int op_drop(void)
266 {
267 	if (op->pcm_ops->drop == NULL)
268 		return -OP_ERROR_NOT_SUPPORTED;
269 	return op->pcm_ops->drop();
270 }
271 
op_close(void)272 int op_close(void)
273 {
274 	return op->pcm_ops->close();
275 }
276 
op_write(const char * buffer,int count)277 int op_write(const char *buffer, int count)
278 {
279 	return op->pcm_ops->write(buffer, count);
280 }
281 
op_pause(void)282 int op_pause(void)
283 {
284 	if (op->pcm_ops->pause == NULL)
285 		return 0;
286 	return op->pcm_ops->pause();
287 }
288 
op_unpause(void)289 int op_unpause(void)
290 {
291 	if (op->pcm_ops->unpause == NULL)
292 		return 0;
293 	return op->pcm_ops->unpause();
294 }
295 
op_buffer_space(void)296 int op_buffer_space(void)
297 {
298 	return op->pcm_ops->buffer_space();
299 }
300 
mixer_set_volume(int left,int right)301 int mixer_set_volume(int left, int right)
302 {
303 	if (op == NULL)
304 		return -OP_ERROR_NOT_INITIALIZED;
305 	if (!op->mixer_open)
306 		return -OP_ERROR_NOT_OPEN;
307 	return op->mixer_ops->set_volume(left, right);
308 }
309 
mixer_read_volume(void)310 int mixer_read_volume(void)
311 {
312 	if (op == NULL)
313 		return -OP_ERROR_NOT_INITIALIZED;
314 	if (!op->mixer_open)
315 		return -OP_ERROR_NOT_OPEN;
316 	return op->mixer_ops->get_volume(&volume_l, &volume_r);
317 }
318 
mixer_get_fds(int * fds)319 int mixer_get_fds(int *fds)
320 {
321 	if (op == NULL)
322 		return -OP_ERROR_NOT_INITIALIZED;
323 	if (!op->mixer_open)
324 		return -OP_ERROR_NOT_OPEN;
325 	if (!op->mixer_ops->get_fds)
326 		return -OP_ERROR_NOT_SUPPORTED;
327 	return op->mixer_ops->get_fds(fds);
328 }
329 
330 extern int soft_vol;
331 
option_error(int rc)332 static void option_error(int rc)
333 {
334 	char *msg = op_get_error_msg(rc, "setting option");
335 	error_msg("%s", msg);
336 	free(msg);
337 }
338 
set_dsp_option(void * data,const char * val)339 static void set_dsp_option(void *data, const char *val)
340 {
341 	const struct output_plugin_opt *o = data;
342 	int rc;
343 
344 	rc = o->set(val);
345 	if (rc)
346 		option_error(rc);
347 }
348 
option_of_current_mixer(const struct mixer_plugin_opt * opt)349 static bool option_of_current_mixer(const struct mixer_plugin_opt *opt)
350 {
351 	const struct mixer_plugin_opt *mpo;
352 
353 	if (!op)
354 		return false;
355 	for (mpo = op->mixer_options; mpo && mpo->name; mpo++) {
356 		if (mpo == opt)
357 			return true;
358 	}
359 	return false;
360 }
361 
set_mixer_option(void * data,const char * val)362 static void set_mixer_option(void *data, const char *val)
363 {
364 	const struct mixer_plugin_opt *o = data;
365 	int rc;
366 
367 	rc = o->set(val);
368 	if (rc) {
369 		option_error(rc);
370 	} else if (option_of_current_mixer(o)) {
371 		/* option of the current op was set
372 		 * try to reopen the mixer */
373 		mixer_close();
374 		if (!soft_vol)
375 			mixer_open();
376 	}
377 }
378 
get_dsp_option(void * data,char * buf,size_t size)379 static void get_dsp_option(void *data, char *buf, size_t size)
380 {
381 	const struct output_plugin_opt *o = data;
382 	char *val = NULL;
383 
384 	o->get(&val);
385 	if (val) {
386 		strscpy(buf, val, size);
387 		free(val);
388 	}
389 }
390 
get_mixer_option(void * data,char * buf,size_t size)391 static void get_mixer_option(void *data, char *buf, size_t size)
392 {
393 	const struct mixer_plugin_opt *o = data;
394 	char *val = NULL;
395 
396 	o->get(&val);
397 	if (val) {
398 		strscpy(buf, val, size);
399 		free(val);
400 	}
401 }
402 
op_add_options(void)403 void op_add_options(void)
404 {
405 	struct output_plugin *o;
406 	const struct output_plugin_opt *opo;
407 	const struct mixer_plugin_opt *mpo;
408 	char key[64];
409 
410 	list_for_each_entry(o, &op_head, node) {
411 		for (opo = o->pcm_options; opo->name; opo++) {
412 			snprintf(key, sizeof(key), "dsp.%s.%s", o->name,
413 					opo->name);
414 			option_add(xstrdup(key), opo, get_dsp_option,
415 					set_dsp_option, NULL, 0);
416 		}
417 		for (mpo = o->mixer_options; mpo && mpo->name; mpo++) {
418 			snprintf(key, sizeof(key), "mixer.%s.%s", o->name,
419 					mpo->name);
420 			option_add(xstrdup(key), mpo, get_mixer_option,
421 					set_mixer_option, NULL, 0);
422 		}
423 	}
424 }
425 
op_get_error_msg(int rc,const char * arg)426 char *op_get_error_msg(int rc, const char *arg)
427 {
428 	char buffer[1024];
429 
430 	switch (-rc) {
431 	case OP_ERROR_ERRNO:
432 		snprintf(buffer, sizeof(buffer), "%s: %s", arg, strerror(errno));
433 		break;
434 	case OP_ERROR_NO_PLUGIN:
435 		snprintf(buffer, sizeof(buffer),
436 				"%s: no such plugin", arg);
437 		break;
438 	case OP_ERROR_NOT_INITIALIZED:
439 		snprintf(buffer, sizeof(buffer),
440 				"%s: couldn't initialize required output plugin", arg);
441 		break;
442 	case OP_ERROR_NOT_SUPPORTED:
443 		snprintf(buffer, sizeof(buffer),
444 				"%s: function not supported", arg);
445 		break;
446 	case OP_ERROR_NOT_OPEN:
447 		snprintf(buffer, sizeof(buffer),
448 				"%s: mixer is not open", arg);
449 		break;
450 	case OP_ERROR_SAMPLE_FORMAT:
451 		snprintf(buffer, sizeof(buffer),
452 				"%s: sample format not supported", arg);
453 		break;
454 	case OP_ERROR_NOT_OPTION:
455 		snprintf(buffer, sizeof(buffer),
456 				"%s: no such option", arg);
457 		break;
458 	case OP_ERROR_INTERNAL:
459 		snprintf(buffer, sizeof(buffer), "%s: internal error", arg);
460 		break;
461 	case OP_ERROR_SUCCESS:
462 	default:
463 		snprintf(buffer, sizeof(buffer),
464 				"%s: this is not an error (%d), this is a bug",
465 				arg, rc);
466 		break;
467 	}
468 	return xstrdup(buffer);
469 }
470 
op_dump_plugins(void)471 void op_dump_plugins(void)
472 {
473 	struct output_plugin *o;
474 
475 	printf("\nOutput Plugins: %s\n", plugin_dir);
476 	list_for_each_entry(o, &op_head, node) {
477 		printf("  %s\n", o->name);
478 	}
479 }
480 
op_get_current(void)481 const char *op_get_current(void)
482 {
483 	if (op)
484 		return op->name;
485 	return NULL;
486 }
487