Mercurial > audlegacy
annotate Plugins/Output/esd/audio.c @ 1635:a3a6f657f895 trunk
[svn] - fork the OSS plugin for CoreAudio work
author | nenolod |
---|---|
date | Thu, 07 Sep 2006 09:11:46 -0700 |
parents | 705d4c089fce |
children |
rev | line source |
---|---|
61 | 1 /* BMP - Cross-platform multimedia player |
2 * Copyright (C) 2003-2004 BMP development team. | |
3 * | |
4 * Based on XMMS: | |
5 * Copyright (C) 1998-2003 XMMS development team. | |
6 * | |
7 * This program is free software; you can redistribute it and/or modify | |
8 * it under the terms of the GNU General Public License as published by | |
9 * the Free Software Foundation; either version 2 of the License, or | |
10 * (at your option) any later version. | |
11 * | |
12 * This program is distributed in the hope that it will be useful, | |
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
15 * GNU General Public License for more details. | |
16 * | |
17 * You should have received a copy of the GNU General Public License | |
18 * along with this program; if not, write to the Free Software | |
1459 | 19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
61 | 20 */ |
21 | |
22 #include <glib.h> | |
23 #include <stdlib.h> | |
24 #include <string.h> | |
25 #include <esd.h> | |
26 | |
27 #include <unistd.h> | |
28 | |
29 #include <libaudacious/util.h> | |
30 | |
31 #include "esdout.h" | |
32 | |
33 | |
34 static gint fd = 0; | |
35 static gpointer buffer; | |
36 static gboolean going = FALSE, paused = FALSE, prebuffer, remove_prebuffer; | |
37 static gint buffer_size, prebuffer_size, blk_size = 4096; | |
38 static gint rd_index = 0, wr_index = 0; | |
39 static gint output_time_offset = 0; | |
40 static guint64 written = 0, output_bytes = 0; | |
41 static gint bps, ebps; | |
42 static gint flush; | |
43 static gint format, channels, frequency, latency; | |
44 static esd_format_t esd_format; | |
45 static gint input_bps, input_format, input_frequency, input_channels; | |
46 static GThread *buffer_thread; | |
47 static gboolean realtime = FALSE; | |
48 static void *(*esd_translate) (void *, gint); | |
488
cb0f36590d98
[svn] ESD output plugin enhancements by max_verem -at- yahoo.com.
nenolod
parents:
61
diff
changeset
|
49 static int player_id_unique = 0; |
61 | 50 |
51 static gint | |
52 get_latency(void) | |
53 { | |
54 int fd, amount = 0; | |
55 | |
56 #ifndef HAVE_ESD_GET_LATENCY | |
57 esd_server_info_t *info; | |
58 #endif | |
59 | |
60 fd = esd_open_sound(esd_cfg.hostname); | |
61 | |
62 if (fd == -1) | |
63 return 0; | |
64 | |
65 #ifdef HAVE_ESD_GET_LATENCY | |
66 amount = esd_get_latency(fd); | |
67 #else | |
68 info = esd_get_server_info(fd); | |
69 if (info) { | |
70 if (info->format & ESD_STEREO) { | |
71 if (info->format & ESD_BITS16) | |
72 amount = (44100 * (ESD_BUF_SIZE + 64)) / info->rate; | |
73 else | |
74 amount = (44100 * (ESD_BUF_SIZE + 128)) / info->rate; | |
75 } | |
76 else { | |
77 if (info->format & ESD_BITS16) | |
78 amount = (2 * 44100 * (ESD_BUF_SIZE + 128)) / info->rate; | |
79 else | |
80 amount = (2 * 44100 * (ESD_BUF_SIZE + 256)) / info->rate; | |
81 } | |
82 free(info); | |
83 } | |
84 amount += ESD_BUF_SIZE * 2; | |
85 #endif | |
86 esd_close(fd); | |
87 return amount; | |
88 } | |
89 | |
90 static void * | |
91 esd_stou8(void *data, gint length) | |
92 { | |
93 int len = length; | |
94 unsigned char *dat = (unsigned char *) data; | |
95 while (len-- > 0) | |
96 *dat++ ^= 0x80; | |
97 return data; | |
98 } | |
99 | |
100 static void * | |
101 esd_utos16sw(void *data, gint length) | |
102 { | |
103 int len = length; | |
104 short *dat = data; | |
105 while (len > 0) { | |
106 *dat = GUINT16_SWAP_LE_BE(*dat) ^ 0x8000; | |
107 dat++; | |
108 len -= 2; | |
109 } | |
110 return data; | |
111 } | |
112 | |
113 static void * | |
114 esd_utos16(void *data, gint length) | |
115 { | |
116 int len = length; | |
117 short *dat = data; | |
118 while (len > 0) { | |
119 *dat ^= 0x8000; | |
120 dat++; | |
121 len -= 2; | |
122 } | |
123 return data; | |
124 } | |
125 | |
126 static void * | |
127 esd_16sw(void *data, gint length) | |
128 { | |
129 int len = length; | |
130 short *dat = data; | |
131 while (len > 0) { | |
132 *dat = GUINT16_SWAP_LE_BE(*dat); | |
133 dat++; | |
134 len -= 2; | |
135 } | |
136 return data; | |
137 } | |
138 | |
139 static void | |
140 esdout_setup_format(AFormat fmt, gint rate, gint nch) | |
141 { | |
142 gboolean swap_sign = FALSE; | |
143 gboolean swap_16 = FALSE; | |
144 | |
145 format = fmt; | |
146 frequency = rate; | |
147 channels = nch; | |
148 switch (fmt) { | |
149 case FMT_S8: | |
150 swap_sign = TRUE; | |
151 case FMT_U8: | |
152 esd_format = ESD_BITS8; | |
153 break; | |
154 case FMT_U16_LE: | |
155 case FMT_U16_BE: | |
156 case FMT_U16_NE: | |
157 swap_sign = TRUE; | |
158 case FMT_S16_LE: | |
159 case FMT_S16_BE: | |
160 case FMT_S16_NE: | |
161 esd_format = ESD_BITS16; | |
162 break; | |
163 } | |
164 | |
165 #if (G_BYTE_ORDER == G_BIG_ENDIAN) | |
166 if (fmt == FMT_U16_LE || fmt == FMT_S16_LE) | |
167 #else | |
168 if (fmt == FMT_U16_BE || fmt == FMT_S16_BE) | |
169 #endif | |
170 swap_16 = TRUE; | |
171 | |
172 esd_translate = (void *(*)()) NULL; | |
173 if (esd_format == ESD_BITS8) { | |
174 if (swap_sign == TRUE) | |
175 esd_translate = esd_stou8; | |
176 } | |
177 else { | |
178 if (swap_sign == TRUE) { | |
179 if (swap_16 == TRUE) | |
180 esd_translate = esd_utos16sw; | |
181 else | |
182 esd_translate = esd_utos16; | |
183 } | |
184 else { | |
185 if (swap_16 == TRUE) | |
186 esd_translate = esd_16sw; | |
187 } | |
188 } | |
189 | |
190 bps = rate * nch; | |
191 if (esd_format == ESD_BITS16) | |
192 bps *= 2; | |
193 if (nch == 1) | |
194 esd_format |= ESD_MONO; | |
195 else | |
196 esd_format |= ESD_STEREO; | |
197 esd_format |= ESD_STREAM | ESD_PLAY; | |
198 | |
199 latency = ((get_latency() * frequency) / 44100) * channels; | |
200 if (format != FMT_U8 && format != FMT_S8) | |
201 latency *= 2; | |
202 } | |
203 | |
204 | |
205 gint | |
206 esdout_get_written_time(void) | |
207 { | |
208 if (!going) | |
209 return 0; | |
210 return (gint) ((written * 1000) / input_bps); | |
211 } | |
212 | |
213 gint | |
214 esdout_get_output_time(void) | |
215 { | |
216 guint64 bytes; | |
217 | |
218 if (!fd || !going) | |
219 return 0; | |
220 | |
221 bytes = output_bytes; | |
222 if (!paused) | |
223 bytes -= (bytes < latency ? bytes : latency); | |
224 | |
225 return output_time_offset + (gint) ((bytes * 1000) / ebps); | |
226 } | |
227 | |
228 gint | |
229 esdout_used(void) | |
230 { | |
231 if (realtime) | |
232 return 0; | |
233 else { | |
234 if (wr_index >= rd_index) | |
235 return wr_index - rd_index; | |
236 return buffer_size - (rd_index - wr_index); | |
237 } | |
238 } | |
239 | |
240 gint | |
241 esdout_playing(void) | |
242 { | |
243 if (!going) | |
244 return FALSE; | |
245 if (!esdout_used()) | |
246 return FALSE; | |
247 | |
248 return TRUE; | |
249 } | |
250 | |
251 gint | |
252 esdout_free(void) | |
253 { | |
254 if (!realtime) { | |
255 if (remove_prebuffer && prebuffer) { | |
256 prebuffer = FALSE; | |
257 remove_prebuffer = FALSE; | |
258 } | |
259 if (prebuffer) | |
260 remove_prebuffer = TRUE; | |
261 | |
262 if (rd_index > wr_index) | |
263 return (rd_index - wr_index) - 1; | |
264 return (buffer_size - (wr_index - rd_index)) - 1; | |
265 } | |
266 else { | |
267 if (paused) | |
268 return 0; | |
269 else | |
270 return 1000000; | |
271 } | |
272 } | |
273 | |
274 static void | |
275 esdout_write_audio(gpointer data, gint length) | |
276 { | |
277 AFormat new_format; | |
278 gint new_frequency, new_channels; | |
279 EffectPlugin *ep; | |
280 | |
281 new_format = input_format; | |
282 new_frequency = input_frequency; | |
283 new_channels = input_channels; | |
284 | |
285 ep = get_current_effect_plugin(); | |
286 if (effects_enabled() && ep && ep->query_format) { | |
287 ep->query_format(&new_format, &new_frequency, &new_channels); | |
288 } | |
289 | |
290 if (new_format != format || new_frequency != frequency | |
291 || new_channels != channels) { | |
292 output_time_offset += (gint) ((output_bytes * 1000) / ebps); | |
293 output_bytes = 0; | |
294 esdout_setup_format(new_format, new_frequency, new_channels); | |
295 frequency = new_frequency; | |
296 channels = new_channels; | |
297 esd_close(fd); | |
298 esdout_set_audio_params(); | |
299 } | |
300 if (effects_enabled() && ep && ep->mod_samples) | |
301 length = | |
302 ep->mod_samples(&data, length, input_format, input_frequency, | |
303 input_channels); | |
304 if (esd_translate) | |
305 output_bytes += write(fd, esd_translate(data, length), length); | |
306 else | |
307 output_bytes += write(fd, data, length); | |
308 } | |
309 | |
310 | |
311 void | |
312 esdout_write(gpointer ptr, gint length) | |
313 { | |
314 gint cnt, off = 0; | |
315 | |
316 if (!realtime) { | |
317 remove_prebuffer = FALSE; | |
318 | |
319 written += length; | |
320 while (length > 0) { | |
321 cnt = MIN(length, buffer_size - wr_index); | |
322 memcpy((gchar *) buffer + wr_index, (gchar *) ptr + off, cnt); | |
323 wr_index = (wr_index + cnt) % buffer_size; | |
324 length -= cnt; | |
325 off += cnt; | |
326 | |
327 } | |
328 } | |
329 else { | |
330 if (paused) | |
331 return; | |
332 esdout_write_audio(ptr, length); | |
333 written += length; | |
334 | |
335 } | |
336 | |
337 } | |
338 | |
339 void | |
340 esdout_close(void) | |
341 { | |
342 if (!going) | |
343 return; | |
344 | |
345 going = 0; | |
346 | |
347 if (!realtime) | |
348 g_thread_join(buffer_thread); | |
349 else | |
350 esd_close(fd); | |
351 | |
352 wr_index = 0; | |
353 rd_index = 0; | |
354 g_free(esd_cfg.playername); | |
355 esd_cfg.playername = NULL; | |
356 } | |
357 | |
358 void | |
359 esdout_flush(gint time) | |
360 { | |
361 if (!realtime) { | |
362 flush = time; | |
363 while (flush != -1) | |
364 g_usleep(10000); | |
365 } | |
366 else { | |
367 output_time_offset = time; | |
368 written = (guint64) (time / 10) * (guint64) (input_bps / 100); | |
369 output_bytes = 0; | |
370 } | |
371 } | |
372 | |
373 void | |
374 esdout_pause(short p) | |
375 { | |
376 paused = p; | |
377 } | |
378 | |
379 gpointer | |
380 esdout_loop(gpointer arg) | |
381 { | |
382 gint length, cnt; | |
383 | |
384 | |
385 while (going) { | |
386 if (esdout_used() > prebuffer_size) | |
387 prebuffer = FALSE; | |
388 if (esdout_used() > 0 && !paused && !prebuffer) { | |
389 length = MIN(blk_size, esdout_used()); | |
390 while (length > 0) { | |
391 cnt = MIN(length, buffer_size - rd_index); | |
392 esdout_write_audio((gchar *) buffer + rd_index, cnt); | |
393 rd_index = (rd_index + cnt) % buffer_size; | |
394 length -= cnt; | |
395 } | |
396 } | |
397 else | |
398 g_usleep(10000); | |
399 | |
400 if (flush != -1) { | |
401 output_time_offset = flush; | |
402 written = (guint64) (flush / 10) * (guint64) (input_bps / 100); | |
403 rd_index = wr_index = output_bytes = 0; | |
404 flush = -1; | |
405 prebuffer = TRUE; | |
406 } | |
407 | |
408 } | |
409 | |
410 esd_close(fd); | |
411 g_free(buffer); | |
412 return NULL; | |
413 } | |
414 | |
415 void | |
416 esdout_set_audio_params(void) | |
417 { | |
418 fd = esd_play_stream(esd_format, frequency, | |
419 esd_cfg.hostname, esd_cfg.playername); | |
420 /* Set the stream's mixer */ | |
421 if (fd != -1) | |
422 esdout_mixer_init(); | |
423 ebps = frequency * channels; | |
424 if (format == FMT_U16_BE || format == FMT_U16_LE || | |
425 format == FMT_S16_BE || format == FMT_S16_LE || | |
426 format == FMT_S16_NE || format == FMT_U16_NE) | |
427 ebps *= 2; | |
428 } | |
429 | |
430 gint | |
431 esdout_open(AFormat fmt, gint rate, gint nch) | |
432 { | |
433 esdout_setup_format(fmt, rate, nch); | |
434 | |
435 input_format = format; | |
436 input_channels = channels; | |
437 input_frequency = frequency; | |
438 input_bps = bps; | |
439 | |
440 realtime = xmms_check_realtime_priority(); | |
441 | |
442 if (!realtime) { | |
443 buffer_size = (esd_cfg.buffer_size * input_bps) / 1000; | |
444 if (buffer_size < 8192) | |
445 buffer_size = 8192; | |
446 prebuffer_size = (buffer_size * esd_cfg.prebuffer) / 100; | |
447 if (buffer_size - prebuffer_size < 4096) | |
448 prebuffer_size = buffer_size - 4096; | |
449 | |
450 buffer = g_malloc0(buffer_size); | |
451 } | |
452 flush = -1; | |
453 prebuffer = 1; | |
454 wr_index = rd_index = output_time_offset = written = output_bytes = 0; | |
455 paused = FALSE; | |
456 remove_prebuffer = FALSE; | |
457 | |
488
cb0f36590d98
[svn] ESD output plugin enhancements by max_verem -at- yahoo.com.
nenolod
parents:
61
diff
changeset
|
458 esd_cfg.playername = g_strdup_printf("xmms - plugin (%d) [%d]", getpid(), player_id_unique++); |
61 | 459 |
460 if (esd_cfg.hostname) | |
461 g_free(esd_cfg.hostname); | |
462 if (esd_cfg.use_remote) | |
463 esd_cfg.hostname = | |
464 g_strdup_printf("%s:%d", esd_cfg.server, esd_cfg.port); | |
465 else | |
466 esd_cfg.hostname = NULL; | |
467 | |
468 esdout_set_audio_params(); | |
469 if (fd == -1) { | |
470 g_free(esd_cfg.playername); | |
471 esd_cfg.playername = NULL; | |
472 g_free(buffer); | |
473 return 0; | |
474 } | |
475 going = 1; | |
476 | |
477 if (!realtime) | |
478 buffer_thread = g_thread_create(esdout_loop, NULL, TRUE, NULL); | |
479 return 1; | |
480 } | |
517
55dc40ff1aff
[svn] Add tell_audio(AFormat *, gint *, gint *), for retrieving audio
nenolod
parents:
488
diff
changeset
|
481 |
55dc40ff1aff
[svn] Add tell_audio(AFormat *, gint *, gint *), for retrieving audio
nenolod
parents:
488
diff
changeset
|
482 void |
55dc40ff1aff
[svn] Add tell_audio(AFormat *, gint *, gint *), for retrieving audio
nenolod
parents:
488
diff
changeset
|
483 esdout_tell(AFormat * fmt, gint * rate, gint * nch) |
55dc40ff1aff
[svn] Add tell_audio(AFormat *, gint *, gint *), for retrieving audio
nenolod
parents:
488
diff
changeset
|
484 { |
55dc40ff1aff
[svn] Add tell_audio(AFormat *, gint *, gint *), for retrieving audio
nenolod
parents:
488
diff
changeset
|
485 (*fmt) = format; |
55dc40ff1aff
[svn] Add tell_audio(AFormat *, gint *, gint *), for retrieving audio
nenolod
parents:
488
diff
changeset
|
486 (*rate) = frequency; |
55dc40ff1aff
[svn] Add tell_audio(AFormat *, gint *, gint *), for retrieving audio
nenolod
parents:
488
diff
changeset
|
487 (*nch) = channels; |
55dc40ff1aff
[svn] Add tell_audio(AFormat *, gint *, gint *), for retrieving audio
nenolod
parents:
488
diff
changeset
|
488 } |