comparison src/Output/arts/audio.c @ 0:13389e613d67 trunk

[svn] - initial import of audacious-plugins tree (lots to do)
author nenolod
date Mon, 18 Sep 2006 01:11:49 -0700
parents
children
comparison
equal deleted inserted replaced
-1:000000000000 0:13389e613d67
1 /*
2 * aRts ouput plugin for xmms
3 *
4 * Copyright (C) 2000,2003 Haavard Kvaalen <havardk@xmms.org>
5 *
6 * Licenced under GNU GPL version 2.
7 *
8 * Audacious port by Giacomo Lozito from develia.org
9 *
10 */
11
12 #include "arts.h"
13 #include "arts_helper/arts_helper.h"
14 #include <errno.h>
15 #include <signal.h>
16 #include <pthread.h>
17
18 #include <sys/types.h>
19 #include <sys/socket.h>
20 #include <sys/wait.h>
21 #include <unistd.h>
22
23 static gboolean going, paused, helper_failed;
24 static guint64 written;
25 static struct params_info input_params, output_params;
26 static int helperfd;
27 static pid_t helper_pid;
28
29 static int (*arts_convert_func)(void **data, int length);
30 struct arts_config artsxmms_cfg;
31
32 struct {
33 int left, right;
34 } volume = {100, 100};
35
36
37 typedef struct format_info {
38 AFormat format;
39 long frequency;
40 int channels;
41 long bps;
42 } format_info_t;
43
44 static format_info_t input;
45 /* static format_info_t effect; */
46 /* static format_info_t output; */
47
48
49 void artsxmms_tell_audio(AFormat * fmt, gint * srate, gint * nch)
50 {
51 (*fmt) = input.format;
52 (*srate) = input.frequency;
53 (*nch) = input.channels;
54 }
55
56
57 void artsxmms_init(void)
58 {
59 ConfigDb *db;
60
61 memset(&artsxmms_cfg, 0, sizeof (artsxmms_cfg));
62
63 artsxmms_cfg.buffer_size = 400;
64
65 db = bmp_cfg_db_open();
66 bmp_cfg_db_get_int(db, "arts", "buffer_size",
67 &artsxmms_cfg.buffer_size);
68 bmp_cfg_db_close(db);
69 }
70
71
72 static int read_all(int fd, void *buf, size_t count)
73 {
74 size_t left = count;
75 int r;
76 do {
77 r = read(fd, buf, left);
78 if (r < 0)
79 return -1;
80 left -= r;
81 buf = (char *)buf + r;
82 } while (left > 0 && r > 0);
83 return count - left;
84 }
85
86 static int write_all(int fd, const void *buf, size_t count)
87 {
88 size_t left = count;
89 int w;
90 do {
91 w = write(fd, buf, left);
92 if (w < 0)
93 return -1;
94 left -= w;
95 buf = (char *)buf + w;
96 } while (left > 0 && w > 0);
97 /* g_message("wrote: %d", count - left); */
98 return count - left;
99 }
100
101 static int wait_for_helper(int fd)
102 {
103 struct timeval timeout;
104 fd_set rdfs;
105 int sr;
106
107 FD_ZERO(&rdfs);
108 FD_SET(fd, &rdfs);
109
110 timeout.tv_sec = 10;
111 timeout.tv_usec = 0;
112
113 sr = select(fd + 1, &rdfs, NULL, NULL, &timeout);
114 if (sr < 0) {
115 g_message("wait_for_helper(): select failed: %s",
116 strerror(errno));
117 return -1;
118 } else if (!sr) {
119 g_message("wait_for_helper(): Timed out waiting for helper");
120 return -1;
121 }
122 return 0;
123 }
124
125 static int xx;
126
127 static int helper_cmd_data(int cmd, int idata, void* ptr, int data_length)
128 {
129 static pthread_mutex_t artsm = PTHREAD_MUTEX_INITIALIZER;
130 struct command out;
131 struct response in;
132 int status;
133
134 out.cmd = cmd;
135 out.data = idata;
136 out.data_length = data_length;
137 xx++;
138
139 if (helper_failed)
140 goto failed;
141
142 pthread_mutex_lock(&artsm);
143 /* fprintf(stderr, "Sending %d; ", out.cmd); */
144 if (write_all(helperfd, &out, sizeof (out)) != sizeof (out))
145 goto failed;
146 if (data_length > 0)
147 if (write_all(helperfd, ptr, data_length) != data_length)
148 goto failed;
149
150 if (wait_for_helper(helperfd)) {
151 g_message("waiting failed: %d", cmd);
152 goto failed;
153 }
154
155 if (read_all(helperfd, &in, sizeof (in)) != sizeof (in))
156 {
157 g_message("read failed: %d", cmd);
158 goto failed;
159 }
160
161 /* fprintf(stderr, "%d complete\n", out.cmd); */
162 pthread_mutex_unlock(&artsm);
163
164 if (in.status)
165 return -in.status;
166 return in.data;
167
168 failed:
169 g_message("helper_cmd_data(): failed");
170 helper_failed = TRUE;
171 if (helper_pid && waitpid(helper_pid, &status, WNOHANG)) {
172 if (status)
173 g_message("helper terminated abnormally: %d", status);
174 else
175 g_message("helper terminated normally");
176 helper_pid = 0;
177 } else if (helper_pid)
178 g_message("helper has not terminated");
179 pthread_mutex_unlock(&artsm);
180 return -STATUS_FAILED;
181 }
182
183 static int helper_cmd(int cmd, int idata)
184 {
185 return helper_cmd_data(cmd, idata, NULL, 0);
186 }
187
188 static int artsxmms_helper_init(struct params_info *params)
189 {
190 int ret;
191 struct init_data id;
192
193 id.version = HELPER_VERSION;
194 id.resolution = params->resolution;
195 id.rate= params->frequency;
196 id.nchannels = params->channels;
197 id.buffer_time = artsxmms_cfg.buffer_size;
198
199 ret = helper_cmd_data(CMD_INIT, 0, &id, sizeof (id));
200 if (ret) {
201 g_message("Init failed: %d", -ret);
202 return -1;
203 }
204
205 return 0;
206 }
207
208 static void artsxmms_set_params(struct params_info *params, AFormat fmt, int rate, int nch)
209 {
210 params->format = fmt;
211 params->frequency = rate;
212 params->channels = nch;
213
214 params->bps = rate * nch;
215 params->resolution = 8;
216 if (!(fmt == FMT_U8 || fmt == FMT_S8))
217 {
218 params->bps *= 2;
219 params->resolution = 16;
220 }
221 }
222
223 int artsxmms_get_written_time(void)
224 {
225 if (!going)
226 return 0;
227
228 return (written * 1000) / output_params.bps;
229 }
230
231 int artsxmms_get_output_time(void)
232 {
233 int time;
234
235 if (!going)
236 return 0;
237 if (helper_failed)
238 return -2;
239
240 time = artsxmms_get_written_time();
241 time -= helper_cmd(CMD_GET_OUTPUT_LATENCY, 0);
242
243 if (time < 0)
244 return 0;
245 return time;
246 }
247
248 int artsxmms_playing(void)
249 {
250 if (!going)
251 return FALSE;
252
253 if (!paused)
254 {
255 if (helper_cmd(CMD_QUERY_PLAYING, 0) <= 0)
256 return FALSE;
257 return TRUE;
258 }
259
260 return TRUE;
261 }
262
263 int artsxmms_free(void)
264 {
265 int space;
266
267 if (!going)
268 return 0;
269
270 space = helper_cmd(CMD_FREE, 0);
271 if (space < 0)
272 return 0;
273
274 return space;
275 }
276
277 void artsxmms_write(gpointer ptr, int length)
278 {
279 AFormat new_format;
280 int new_frequency, new_channels;
281 EffectPlugin *ep;
282
283 new_format = input_params.format;
284 new_frequency = input_params.frequency;
285 new_channels = input_params.channels;
286
287 ep = get_current_effect_plugin();
288 if (effects_enabled() && ep && ep->query_format)
289 ep->query_format(&new_format, &new_frequency, &new_channels);
290
291 if (new_format != output_params.format ||
292 new_frequency != output_params.frequency ||
293 new_channels != output_params.channels)
294 {
295 /*
296 * The effect plugins has changed the format of the stream.
297 */
298
299 guint64 offset = (written * 1000) / output_params.bps;
300 artsxmms_set_params(&output_params, new_format,
301 new_frequency, new_channels);
302 arts_convert_func = arts_get_convert_func(output_params.format);
303
304 written = (offset * output_params.bps) / 1000;
305
306 artsxmms_helper_init(&output_params);
307 }
308
309 /*
310 * Doing the effect plugin processing here adds some latency,
311 * but the alternative is just too frigging hairy.
312 */
313
314 if (effects_enabled() && ep && ep->mod_samples)
315 length = ep->mod_samples(&ptr, length, input_params.format,
316 input_params.frequency,
317 input_params.channels);
318
319 if (arts_convert_func)
320 arts_convert_func(ptr, length);
321
322 helper_cmd_data(CMD_WRITE, 0, ptr, length);
323 written += length;
324 }
325
326 void artsxmms_close(void)
327 {
328 int status;
329 going = 0;
330 /* g_message("sending quit cmd"); */
331 if (!helper_cmd(CMD_QUIT, 0)) {
332 waitpid(helper_pid, &status, 0);
333 if (status)
334 g_message("artsxmms_close(): Child exited abnormally: %d",
335 status);
336 }
337 }
338
339 void artsxmms_flush(int time)
340 {
341 /*
342 * Argh, no way to flush the stream from the C api.
343 */
344 written = (time / 10) * (output_params.bps / 100);
345
346 }
347
348 void artsxmms_pause(short p)
349 {
350 paused = p;
351 helper_cmd(CMD_PAUSE, p);
352 }
353
354 static int artsxmms_start_helper()
355 {
356 int sockets[2];
357
358 if (socketpair(AF_UNIX, SOCK_STREAM, 0, sockets) < 0)
359 {
360 g_message("artsxmms_start_helper(): "
361 "Failed to create socketpair: %s", strerror(errno));
362 return -1;
363 }
364
365 if ((helper_pid = fork()) == 0)
366 {
367 /* Child */
368 char sockfdstr[10];
369 close(sockets[1]);
370 sprintf(sockfdstr, "%d", sockets[0]);
371 execlp("audacious-arts-helper", "audacious-arts-helper",
372 sockfdstr, NULL);
373 g_warning("artsxmms_start_helper(): "
374 "Failed to start audacious-arts-helper: %s", strerror(errno));
375 close(sockets[0]);
376 _exit(1);
377 }
378 close(sockets[0]);
379 helperfd = sockets[1];
380
381 if (helper_pid < 0)
382 {
383 g_message("artsxmms_start_helper(): "
384 "Failed to fork() helper process: %s", strerror(errno));
385 close(sockets[1]);
386 return -1;
387 }
388
389 return 0;
390 }
391
392 int artsxmms_open(AFormat fmt, int rate, int nch)
393 {
394 if (artsxmms_start_helper() < 0)
395 return 0;
396
397 artsxmms_set_params(&input_params, fmt, rate, nch);
398 artsxmms_set_params(&output_params, fmt, rate, nch);
399
400 arts_convert_func = arts_get_convert_func(output_params.format);
401
402 written = 0;
403 paused = 0;
404 helper_failed = FALSE;
405
406 if (artsxmms_helper_init(&output_params)) {
407 artsxmms_close();
408 return 0;
409 }
410 artsxmms_set_volume(volume.left, volume.right);
411
412 going = 1;
413 return 1;
414 }
415
416 void artsxmms_get_volume(int *l, int *r)
417 {
418 *l = volume.left;
419 *r = volume.right;
420 }
421
422 void artsxmms_set_volume(int l, int r)
423 {
424 int vol[2];
425 volume.left = l;
426 volume.right = r;
427 vol[0] = l;
428 vol[1] = r;
429 helper_cmd_data(CMD_SET_VOLUME, 0, vol, sizeof(vol));
430 }