1
/*
2
 * Copyright 2008-2011 Various Authors
3
 * Copyright 2010-2011 Philipp 'ph3-der-loewe' Schafft
4
 * Copyright 2006 Johannes Weißl
5
 *
6
 * This program is free software; you can redistribute it and/or
7
 * modify it under the terms of the GNU General Public License as
8
 * published by the Free Software Foundation; either version 2 of the
9
 * License, or (at your option) any later version.
10
 *
11
 * This program is distributed in the hope that it will be useful, but
12
 * WITHOUT ANY WARRANTY; without even the implied warranty of
13
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14
 * General Public License for more details.
15
 *
16
 * You should have received a copy of the GNU General Public License
17
 * along with this program; if not, see <http://www.gnu.org/licenses/>.
18
 */
19
20
#include <roaraudio.h>
21
22
#include "op.h"
23
#include "mixer.h"
24
#include "xmalloc.h"
25
#include "utils.h"
26
#include "misc.h"
27
#include "debug.h"
28
29
// we do not use native 2^16-1 here as they use signed ints with 16 bit
30
// so we use 2^(16-1)-1 here.
31
#define MIXER_BASE_VOLUME 32767
32
33
static struct roar_connection con;
34
static roar_vs_t *vss = NULL;
35
static int err;
36
static sample_format_t format;
37
38
/* configuration */
39
static char *host = NULL;
40
static char *role = NULL;
41
42
static inline void _err_to_errno(void)
43
{
44
	roar_err_set(err);
45
	roar_err_to_errno();
46
}
47
48
static int op_roar_dummy(void)
49
{
50
	return 0;
51
}
52
53
#if DEBUG > 1
54
static ssize_t op_roar_debug_write(struct roar_vio_calls *vio, void *buf_, size_t count)
55
{
56
	char *buf = (char *) buf_;
57
	int len = count;
58
	if (len > 0 && buf[len-1] == '\n')
59
		len--;
60
	if (len > 0)
61
		d_print("%*s\n", len, buf);
62
	return count;
63
}
64
65
static struct roar_vio_calls op_roar_debug_cbs = {
66
	.write = op_roar_debug_write
67
};
68
#endif
69
70
static int op_roar_init(void)
71
{
72
#if DEBUG > 1
73
	roar_debug_set_stderr_mode(ROAR_DEBUG_MODE_VIO);
74
	roar_debug_set_stderr_vio(&op_roar_debug_cbs);
75
#else
76
	roar_debug_set_stderr_mode(ROAR_DEBUG_MODE_SYSLOG);
77
#endif
78
	return 0;
79
}
80
81
static int op_roar_exit(void)
82
{
83
	free(host);
84
	free(role);
85
86
	return 0;
87
}
88
89
static int _set_role(void)
90
{
91
	int roleid = ROAR_ROLE_UNKNOWN;
92
93
	if (role == NULL)
94
		return 0;
95
96
	roleid = roar_str2role(role);
97
98
	if (roleid == ROAR_ROLE_UNKNOWN) {
99
		// TODO: warn if role is invalid.
100
		return 0;
101
	}
102
103
	if (roar_vs_role(vss, roleid, &err) == -1) {
104
		return -OP_ERROR_ERRNO;
105
	}
106
107
	return 0;
108
}
109
110
static int op_roar_open(sample_format_t sf, const channel_position_t *channel_map)
111
{
112
	struct roar_audio_info info;
113
	int ret;
114
115
	memset(&info, 0, sizeof(info));
116
117
	ROAR_DBG("op_roar_open(*) = ?");
118
119
	format = sf;
120
121
	info.rate = sf_get_rate(sf);
122
	info.channels = sf_get_channels(sf);
123
	info.bits = sf_get_bits(sf);
124
125
	if (sf_get_bigendian(sf)) {
126
		if (sf_get_signed(sf)) {
127
			info.codec = ROAR_CODEC_PCM_S_BE;
128
		} else {
129
			info.codec = ROAR_CODEC_PCM_U_BE;
130
		}
131
	} else {
132
		if (sf_get_signed(sf)) {
133
			info.codec = ROAR_CODEC_PCM_S_LE;
134
		} else {
135
			info.codec = ROAR_CODEC_PCM_U_LE;
136
		}
137
	}
138
139
	ROAR_DBG("op_roar_open(*) = ?");
140
141
	if (roar_libroar_set_server(host) == -1) {
142
		ROAR_DBG("op_roar_open(*) = ?");
143
144
		roar_err_to_errno();
145
		return -OP_ERROR_ERRNO;
146
	}
147
148
	if ( roar_simple_connect2(&con, NULL, "C* Music Player (cmus)", ROAR_ENUM_FLAG_NONBLOCK, 0) == -1 ) {
149
		ROAR_DBG("op_roar_open(*) = ?");
150
151
		roar_err_to_errno();
152
		return -OP_ERROR_ERRNO;
153
	}
154
155
	vss = roar_vs_new_from_con(&con, &err);
156
	if (vss == NULL) {
157
		ROAR_DBG("op_roar_open(*) = ?");
158
159
		roar_disconnect(&con);
160
161
		_err_to_errno();
162
		return -OP_ERROR_ERRNO;
163
	}
164
165
	if (roar_vs_stream(vss, &info, ROAR_DIR_PLAY, &err) == -1) {
166
		ROAR_DBG("op_roar_open(*) = ?");
167
168
		roar_disconnect(&con);
169
170
		_err_to_errno();
171
		return -OP_ERROR_ERRNO;
172
	}
173
174
	ROAR_DBG("op_roar_open(*) = ?");
175
176
	if (roar_vs_buffer(vss, 2048*8, &err) == -1) {
177
		roar_vs_close(vss, ROAR_VS_TRUE, NULL);
178
		roar_disconnect(&con);
179
		_err_to_errno();
180
		return -OP_ERROR_ERRNO;
181
	}
182
183
	ROAR_DBG("op_roar_open(*) = ?");
184
185
	ret = _set_role();
186
	if (ret != 0) {
187
		roar_vs_close(vss, ROAR_VS_TRUE, NULL);
188
		roar_disconnect(&con);
189
		_err_to_errno();
190
		return ret;
191
	}
192
193
	ROAR_DBG("op_roar_open(*) = ?");
194
195
	roar_vs_blocking(vss, ROAR_VS_FALSE, &err);
196
197
	ROAR_DBG("op_roar_open(*) = 0");
198
199
	return 0;
200
}
201
202
static int op_roar_close(void)
203
{
204
	roar_vs_close(vss, ROAR_VS_FALSE, &err);
205
	roar_disconnect(&con);
206
	return 0;
207
}
208
209
static int op_roar_drop(void)
210
{
211
	roar_vs_reset_buffer(vss, ROAR_VS_TRUE, ROAR_VS_TRUE, &err);
212
	return 0;
213
}
214
215
static int op_roar_write(const char *buffer, int count)
216
{
217
	int ret;
218
	int i;
219
	ret = roar_vs_write(vss, buffer, count, &err);
220
	for (i = 0; i < 8; i++)
221
		roar_vs_iterate(vss, ROAR_VS_NOWAIT, NULL);
222
	return ret;
223
}
224
225
static int op_roar_buffer_space(void)
226
{
227
	ssize_t ret;
228
	int i;
229
	int fs = sf_get_frame_size(format);
230
231
	for (i = 0; i < 8; i++)
232
		roar_vs_iterate(vss, ROAR_VS_NOWAIT, NULL);
233
234
	ret = roar_vs_get_avail_write(vss, &err);
235
236
	ret -= ret % fs;
237
238
	return ret;
239
}
240
241
static int op_roar_pause(void) {
242
	if (roar_vs_pause(vss, ROAR_VS_TRUE, &err) == -1) {
243
		_err_to_errno();
244
		return -OP_ERROR_ERRNO;
245
	}
246
	return 0;
247
}
248
static int op_roar_unpause(void) {
249
	if (roar_vs_pause(vss, ROAR_VS_FALSE, &err) == -1) {
250
		_err_to_errno();
251
		return -OP_ERROR_ERRNO;
252
	}
253
	return 0;
254
}
255
256
257
static int op_roar_set_option(int key, const char *val)
258
{
259
	switch (key) {
260
	case 0:
261
		free(host);
262
		host = xstrdup(val);
263
		break;
264
	case 1:
265
		free(role);
266
		role = xstrdup(val);
267
		_set_role();
268
		break;
269
	default:
270
		return -OP_ERROR_NOT_OPTION;
271
	}
272
	return 0;
273
}
274
275
static int op_roar_get_option(int key, char **val)
276
{
277
	switch (key) {
278
	case 0:
279
		if (host != NULL)
280
			*val = xstrdup(host);
281
		break;
282
	case 1:
283
		if (role != NULL)
284
			*val = xstrdup(role);
285
		break;
286
	default:
287
		return -OP_ERROR_NOT_OPTION;
288
	}
289
	return 0;
290
}
291
292
static int op_roar_mixer_open(int *volume_max)
293
{
294
	*volume_max = MIXER_BASE_VOLUME;
295
	return 0;
296
}
297
298
static int op_roar_mixer_set_volume(int l, int r)
299
{
300
	float lf, rf;
301
302
	if (vss == NULL)
303
		return -OP_ERROR_NOT_OPEN;
304
305
	lf = (float)l / (float)MIXER_BASE_VOLUME;
306
	rf = (float)r / (float)MIXER_BASE_VOLUME;
307
308
	if (roar_vs_volume_stereo(vss, lf, rf, &err) == -1) {
309
		_err_to_errno();
310
		return -OP_ERROR_ERRNO;
311
	}
312
313
	return 0;
314
}
315
static int op_roar_mixer_get_volume(int *l, int *r)
316
{
317
	float lf, rf;
318
319
	if (vss == NULL)
320
		return -OP_ERROR_NOT_OPEN;
321
322
	if (roar_vs_volume_get(vss, &lf, &rf, &err) == -1) {
323
		_err_to_errno();
324
		return -OP_ERROR_ERRNO;
325
	}
326
327
	lf *= (float)MIXER_BASE_VOLUME;
328
	rf *= (float)MIXER_BASE_VOLUME;
329
330
	*l = lf;
331
	*r = rf;
332
333
	return 0;
334
}
335
336
static int op_roar_mixer_set_option(int key, const char *val)
337
{
338
	return -OP_ERROR_NOT_OPTION;
339
}
340
static int op_roar_mixer_get_option(int key, char **val)
341
{
342
	return -OP_ERROR_NOT_OPTION;
343
}
344
345
const struct output_plugin_ops op_pcm_ops = {
346
	.init = op_roar_init,
347
	.exit = op_roar_exit,
348
	.open = op_roar_open,
349
	.close = op_roar_close,
350
	.drop = op_roar_drop,
351
	.write = op_roar_write,
352
	.buffer_space = op_roar_buffer_space,
353
	.pause = op_roar_pause,
354
	.unpause = op_roar_unpause,
355
	.set_option = op_roar_set_option,
356
	.get_option = op_roar_get_option
357
};
358
359
const char * const op_pcm_options[] = {
360
	"server",
361
	"role",
362
	NULL
363
};
364
365
const struct mixer_plugin_ops op_mixer_ops = {
366
	.init = op_roar_dummy,
367
	.exit = op_roar_dummy,
368
	.open = op_roar_mixer_open,
369
	.close = op_roar_dummy,
370
	.get_fds = NULL,
371
	.set_volume = op_roar_mixer_set_volume,
372
	.get_volume = op_roar_mixer_get_volume,
373
	.set_option = op_roar_mixer_set_option,
374
	.get_option = op_roar_mixer_get_option
375
};
376
377
const char * const op_mixer_options[] = {
378
	NULL
379
};
380
381
const int op_priority = -3;