1
/*
2
 * Copyright 2008-2011 Various Authors
3
 * Copyright 2006 Johannes Weißl
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 "op.h"
20
#include "xmalloc.h"
21
#include "utils.h"
22
#include "misc.h"
23
#include "debug.h"
24
25
/*
26
 * <ao/ao.h> uses FILE but doesn't include stdio.h.
27
 * Also we use snprintf().
28
 */
29
#include <stdio.h>
30
#include <strings.h>
31
#include <ao/ao.h>
32
33
#ifdef AO_EBADFORMAT
34
#define AO_API_1
35
#endif
36
37
static ao_device *libao_device;
38
static char *wav_dir = NULL;
39
static int wav_counter = 1;
40
static int is_wav = 0;
41
42
/* configuration */
43
static char *libao_driver = NULL;
44
static int libao_buffer_space = 16384;
45
46
47
static int op_ao_init(void)
48
{
49
	/* ignore config value */
50
	wav_counter = 1;
51
52
	ao_initialize();
53
	return 0;
54
}
55
56
static int op_ao_exit(void)
57
{
58
	free(libao_driver);
59
	ao_shutdown();
60
	return 0;
61
}
62
63
/* http://www.xiph.org/ao/doc/ao_sample_format.html */
64
static const struct {
65
	channel_position_t pos;
66
	const char *str;
67
} ao_channel_mapping[] = {
68
	{ CHANNEL_POSITION_LEFT,			"L" },
69
	{ CHANNEL_POSITION_RIGHT,			"R" },
70
	{ CHANNEL_POSITION_CENTER,			"C" },
71
	{ CHANNEL_POSITION_MONO,			"M" },
72
	{ CHANNEL_POSITION_FRONT_LEFT_OF_CENTER,	"CL" },
73
	{ CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER,	"CR" },
74
	{ CHANNEL_POSITION_REAR_LEFT,			"BL" },
75
	{ CHANNEL_POSITION_REAR_RIGHT,			"BR" },
76
	{ CHANNEL_POSITION_REAR_CENTER,			"BC" },
77
	{ CHANNEL_POSITION_SIDE_LEFT,			"SL" },
78
	{ CHANNEL_POSITION_SIDE_RIGHT,			"SR" },
79
	{ CHANNEL_POSITION_LFE,				"LFE" },
80
	{ CHANNEL_POSITION_INVALID,			"X" },
81
};
82
83
#ifdef AO_API_1
84
static char *ao_channel_matrix(int channels, const channel_position_t *map)
85
{
86
	int i, j;
87
	char buf[256] = "";
88
89
	if (!map || !channel_map_valid(map))
90
		return NULL;
91
92
	for (i = 0; i < channels; i++) {
93
		const channel_position_t pos = map[i];
94
		int found = 0;
95
		for (j = 0; j < N_ELEMENTS(ao_channel_mapping); j++) {
96
			if (pos == ao_channel_mapping[j].pos) {
97
				strcat(buf, ao_channel_mapping[j].str);
98
				strcat(buf, ",");
99
				found = 1;
100
				break;
101
			}
102
		}
103
		if (!found)
104
			strcat(buf, "M,");
105
	}
106
	buf[strlen(buf)-1] = '\0';
107
108
	return xstrdup(buf);
109
}
110
#endif
111
112
static int op_ao_open(sample_format_t sf, const channel_position_t *channel_map)
113
{
114
	ao_sample_format format = {
115
		.bits        = sf_get_bits(sf),
116
		.rate        = sf_get_rate(sf),
117
		.channels    = sf_get_channels(sf),
118
		.byte_format = sf_get_bigendian(sf) ? AO_FMT_BIG : AO_FMT_LITTLE,
119
#ifdef AO_API_1
120
		.matrix      = ao_channel_matrix(sf_get_channels(sf), channel_map)
121
#endif
122
	};
123
	int driver;
124
125
	if (libao_driver == NULL) {
126
		driver = ao_default_driver_id();
127
	} else {
128
		driver = ao_driver_id(libao_driver);
129
		is_wav = strcasecmp(libao_driver, "wav") == 0;
130
	}
131
	if (driver == -1) {
132
		errno = ENODEV;
133
		return -OP_ERROR_ERRNO;
134
	}
135
136
	if (is_wav) {
137
		char file[512];
138
139
		if (wav_dir == NULL)
140
			wav_dir = xstrdup(home_dir);
141
		snprintf(file, sizeof(file), "%s/%02d.wav", wav_dir, wav_counter);
142
		libao_device = ao_open_file(driver, file, 0, &format, NULL);
143
	} else {
144
		libao_device = ao_open_live(driver, &format, NULL);
145
	}
146
147
	if (libao_device == NULL) {
148
		switch (errno) {
149
		case AO_ENODRIVER:
150
		case AO_ENOTFILE:
151
		case AO_ENOTLIVE:
152
		case AO_EOPENDEVICE:
153
			errno = ENODEV;
154
			return -OP_ERROR_ERRNO;
155
		case AO_EBADOPTION:
156
			errno = EINVAL;
157
			return -OP_ERROR_ERRNO;
158
		case AO_EOPENFILE:
159
			errno = EACCES;
160
			return -OP_ERROR_ERRNO;
161
		case AO_EFILEEXISTS:
162
			errno = EEXIST;
163
			return -OP_ERROR_ERRNO;
164
		case AO_EFAIL:
165
		default:
166
			return -OP_ERROR_INTERNAL;
167
		}
168
	}
169
170
#ifdef AO_API_1
171
	d_print("channel matrix: %s\n", format.matrix ? format.matrix : "default");
172
#endif
173
	return 0;
174
}
175
176
static int op_ao_close(void)
177
{
178
	ao_close(libao_device);
179
	if (is_wav)
180
		wav_counter++;
181
	return 0;
182
}
183
184
static int op_ao_write(const char *buffer, int count)
185
{
186
	if (ao_play(libao_device, (void *)buffer, count) == 0)
187
		return -1;
188
	return count;
189
}
190
191
static int op_ao_buffer_space(void)
192
{
193
	if (is_wav)
194
		return 128 * 1024;
195
	return libao_buffer_space;
196
}
197
198
static int op_ao_set_option(int key, const char *val)
199
{
200
	long int ival;
201
202
	switch (key) {
203
	case 0:
204
		if (str_to_int(val, &ival) || ival < 4096) {
205
			errno = EINVAL;
206
			return -OP_ERROR_ERRNO;
207
		}
208
		libao_buffer_space = ival;
209
		break;
210
	case 1:
211
		free(libao_driver);
212
		libao_driver = NULL;
213
		if (val[0])
214
			libao_driver = xstrdup(val);
215
		break;
216
	case 2:
217
		if (str_to_int(val, &ival)) {
218
			errno = EINVAL;
219
			return -OP_ERROR_ERRNO;
220
		}
221
		wav_counter = ival;
222
		break;
223
	case 3:
224
		free(wav_dir);
225
		wav_dir = xstrdup(val);
226
		break;
227
	default:
228
		return -OP_ERROR_NOT_OPTION;
229
	}
230
	return 0;
231
}
232
233
static int op_ao_get_option(int key, char **val)
234
{
235
	switch (key) {
236
	case 0:
237
		*val = xnew(char, 22);
238
		snprintf(*val, 22, "%d", libao_buffer_space);
239
		break;
240
	case 1:
241
		if (libao_driver)
242
			*val = xstrdup(libao_driver);
243
		break;
244
	case 2:
245
		*val = xnew(char, 22);
246
		snprintf(*val, 22, "%d", wav_counter);
247
		break;
248
	case 3:
249
		if (wav_dir == NULL)
250
			wav_dir = xstrdup(home_dir);
251
		*val = expand_filename(wav_dir);
252
		break;
253
	default:
254
		return -OP_ERROR_NOT_OPTION;
255
	}
256
	return 0;
257
}
258
259
const struct output_plugin_ops op_pcm_ops = {
260
	.init = op_ao_init,
261
	.exit = op_ao_exit,
262
	.open = op_ao_open,
263
	.close = op_ao_close,
264
	.write = op_ao_write,
265
	.buffer_space = op_ao_buffer_space,
266
	.set_option = op_ao_set_option,
267
	.get_option = op_ao_get_option
268
};
269
270
const char * const op_pcm_options[] = {
271
	"buffer_size",
272
	"driver",
273
	"wav_counter",
274
	"wav_dir",
275
	NULL
276
};
277
278
const int op_priority = 3;