Mercurial > audlegacy-plugins
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 } |