Mercurial > audlegacy
comparison Input/wav/wav.c @ 0:cb178e5ad177 trunk
[svn] Import audacious source.
author | nenolod |
---|---|
date | Mon, 24 Oct 2005 03:06:47 -0700 |
parents | |
children |
comparison
equal
deleted
inserted
replaced
-1:000000000000 | 0:cb178e5ad177 |
---|---|
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 | |
19 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. | |
20 */ | |
21 | |
22 #include "wav.h" | |
23 | |
24 #include <glib.h> | |
25 #include <glib/gi18n.h> | |
26 #include <string.h> | |
27 | |
28 #include <libaudacious/util.h> | |
29 #include <libaudacious/titlestring.h> | |
30 #include "audacious/output.h" | |
31 | |
32 | |
33 InputPlugin wav_ip = { | |
34 NULL, | |
35 NULL, | |
36 NULL, /* Description */ | |
37 wav_init, | |
38 NULL, | |
39 NULL, | |
40 is_our_file, | |
41 NULL, | |
42 play_file, | |
43 stop, | |
44 wav_pause, | |
45 seek, | |
46 NULL, | |
47 get_time, | |
48 NULL, | |
49 NULL, | |
50 NULL, | |
51 NULL, | |
52 NULL, | |
53 NULL, | |
54 NULL, | |
55 get_song_info, | |
56 NULL, /* file_info_box */ | |
57 NULL | |
58 }; | |
59 | |
60 WaveFile *wav_file = NULL; | |
61 static GThread *decode_thread; | |
62 static gboolean audio_error = FALSE; | |
63 | |
64 InputPlugin * | |
65 get_iplugin_info(void) | |
66 { | |
67 wav_ip.description = g_strdup_printf(_("WAV Audio Plugin")); | |
68 return &wav_ip; | |
69 } | |
70 | |
71 static void | |
72 wav_init(void) | |
73 { | |
74 /* empty */ | |
75 } | |
76 | |
77 /* needed for is_our_file() */ | |
78 static gint | |
79 read_n_bytes(VFSFile * file, guint8 * buf, gint n) | |
80 { | |
81 if (vfs_fread(buf, 1, n, file) != n) { | |
82 return FALSE; | |
83 } | |
84 return TRUE; | |
85 } | |
86 | |
87 static guint32 | |
88 convert_to_header(guint8 * buf) | |
89 { | |
90 | |
91 return (buf[0] << 24) + (buf[1] << 16) + (buf[2] << 8) + buf[3]; | |
92 } | |
93 | |
94 static guint32 | |
95 convert_to_long(guint8 * buf) | |
96 { | |
97 | |
98 return (buf[3] << 24) + (buf[2] << 16) + (buf[1] << 8) + buf[0]; | |
99 } | |
100 | |
101 static guint16 | |
102 read_wav_id(gchar * filename) | |
103 { | |
104 VFSFile *file; | |
105 guint16 wavid; | |
106 guint8 buf[4]; | |
107 guint32 head; | |
108 glong seek; | |
109 | |
110 if (!(file = vfs_fopen(filename, "rb"))) { /* Could not open file */ | |
111 return 0; | |
112 } | |
113 if (!(read_n_bytes(file, buf, 4))) { | |
114 vfs_fclose(file); | |
115 return 0; | |
116 } | |
117 head = convert_to_header(buf); | |
118 if (head == ('R' << 24) + ('I' << 16) + ('F' << 8) + 'F') { /* Found a riff -- maybe WAVE */ | |
119 if (vfs_fseek(file, 4, SEEK_CUR) != 0) { /* some error occured */ | |
120 vfs_fclose(file); | |
121 return 0; | |
122 } | |
123 if (!(read_n_bytes(file, buf, 4))) { | |
124 vfs_fclose(file); | |
125 return 0; | |
126 } | |
127 head = convert_to_header(buf); | |
128 if (head == ('W' << 24) + ('A' << 16) + ('V' << 8) + 'E') { /* Found a WAVE */ | |
129 seek = 0; | |
130 do { /* we'll be looking for the fmt-chunk which comes before the data-chunk */ | |
131 /* A chunk consists of an header identifier (4 bytes), the length of the chunk | |
132 (4 bytes), and the chunkdata itself, padded to be an even number of bytes. | |
133 We'll skip all chunks until we find the "data"-one which could contain | |
134 mpeg-data */ | |
135 if (seek != 0) { | |
136 if (vfs_fseek(file, seek, SEEK_CUR) != 0) { /* some error occured */ | |
137 vfs_fclose(file); | |
138 return 0; | |
139 } | |
140 } | |
141 if (!(read_n_bytes(file, buf, 4))) { | |
142 vfs_fclose(file); | |
143 return 0; | |
144 } | |
145 head = convert_to_header(buf); | |
146 if (!(read_n_bytes(file, buf, 4))) { | |
147 vfs_fclose(file); | |
148 return 0; | |
149 } | |
150 seek = convert_to_long(buf); | |
151 seek = seek + (seek % 2); /* Has to be even (padding) */ | |
152 if (seek >= 2 | |
153 && head == ('f' << 24) + ('m' << 16) + ('t' << 8) + ' ') { | |
154 if (!(read_n_bytes(file, buf, 2))) { | |
155 vfs_fclose(file); | |
156 return 0; | |
157 } | |
158 wavid = buf[0] + 256 * buf[1]; | |
159 seek -= 2; | |
160 /* we could go on looking for other things, but all we wanted was the wavid */ | |
161 vfs_fclose(file); | |
162 return wavid; | |
163 } | |
164 } | |
165 while (head != ('d' << 24) + ('a' << 16) + ('t' << 8) + 'a'); | |
166 /* it's RIFF WAVE */ | |
167 } | |
168 /* it's RIFF */ | |
169 } | |
170 /* it's not even RIFF */ | |
171 vfs_fclose(file); | |
172 return 0; | |
173 } | |
174 | |
175 static const gchar * | |
176 get_extension(const gchar * filename) | |
177 { | |
178 const gchar *ext = strrchr(filename, '.'); | |
179 return ext ? ext + 1 : NULL; | |
180 } | |
181 | |
182 static gboolean | |
183 is_our_file(gchar * filename) | |
184 { | |
185 gchar *ext; | |
186 | |
187 ext = strrchr(filename, '.'); | |
188 if (ext) | |
189 if (!strcasecmp(ext, ".wav")) | |
190 if (read_wav_id(filename) == WAVE_FORMAT_PCM) | |
191 return TRUE; | |
192 return FALSE; | |
193 } | |
194 | |
195 | |
196 static gchar * | |
197 get_title(const gchar * filename) | |
198 { | |
199 TitleInput *input; | |
200 gchar *title; | |
201 | |
202 input = bmp_title_input_new(); | |
203 | |
204 input->file_name = g_path_get_basename(filename); | |
205 input->file_ext = get_extension(filename); | |
206 input->file_path = g_path_get_dirname(filename); | |
207 | |
208 if (!(title = xmms_get_titlestring(xmms_get_gentitle_format(), input))) | |
209 title = g_strdup(input->file_name); | |
210 | |
211 g_free(input->file_path); | |
212 g_free(input->file_name); | |
213 g_free(input); | |
214 | |
215 return title; | |
216 } | |
217 | |
218 static gint | |
219 read_le_long(VFSFile * file, glong * ret) | |
220 { | |
221 guchar buf[4]; | |
222 | |
223 if (vfs_fread(buf, 1, 4, file) != 4) | |
224 return 0; | |
225 | |
226 *ret = (buf[3] << 24) | (buf[2] << 16) | (buf[1] << 8) | buf[0]; | |
227 return TRUE; | |
228 } | |
229 | |
230 #define read_le_ulong(file,ret) read_le_long(file,(long*)ret) | |
231 | |
232 static int | |
233 read_le_short(VFSFile * file, gshort * ret) | |
234 { | |
235 guchar buf[2]; | |
236 | |
237 if (vfs_fread(buf, 1, 2, file) != 2) | |
238 return 0; | |
239 | |
240 *ret = (buf[1] << 8) | buf[0]; | |
241 return TRUE; | |
242 } | |
243 | |
244 static gpointer | |
245 play_loop(gpointer arg) | |
246 { | |
247 gchar data[2048 * 2]; | |
248 gint bytes, blk_size, rate; | |
249 gint actual_read; | |
250 | |
251 blk_size = 512 * (wav_file->bits_per_sample / 8) * wav_file->channels; | |
252 rate = | |
253 wav_file->samples_per_sec * wav_file->channels * | |
254 (wav_file->bits_per_sample / 8); | |
255 while (wav_file->going) { | |
256 if (!wav_file->eof) { | |
257 bytes = blk_size; | |
258 if (wav_file->length - wav_file->position < bytes) | |
259 bytes = wav_file->length - wav_file->position; | |
260 if (bytes > 0) { | |
261 actual_read = vfs_fread(data, 1, bytes, wav_file->file); | |
262 | |
263 if (actual_read == 0) { | |
264 wav_file->eof = 1; | |
265 wav_ip.output->buffer_free(); | |
266 wav_ip.output->buffer_free(); | |
267 } | |
268 else { | |
269 if (wav_file->seek_to == -1) | |
270 produce_audio(wav_ip.output->written_time(), | |
271 (wav_file->bits_per_sample == | |
272 16) ? FMT_S16_LE : FMT_U8, | |
273 wav_file->channels, bytes, data, | |
274 &wav_file->going); | |
275 wav_file->position += actual_read; | |
276 } | |
277 } | |
278 else { | |
279 wav_file->eof = TRUE; | |
280 wav_ip.output->buffer_free(); | |
281 wav_ip.output->buffer_free(); | |
282 xmms_usleep(10000); | |
283 } | |
284 } | |
285 else | |
286 xmms_usleep(10000); | |
287 if (wav_file->seek_to != -1) { | |
288 wav_file->position = wav_file->seek_to * rate; | |
289 vfs_fseek(wav_file->file, | |
290 wav_file->position + wav_file->data_offset, SEEK_SET); | |
291 wav_ip.output->flush(wav_file->seek_to * 1000); | |
292 wav_file->seek_to = -1; | |
293 } | |
294 | |
295 } | |
296 vfs_fclose(wav_file->file); | |
297 | |
298 g_thread_exit(NULL); | |
299 return NULL; | |
300 } | |
301 | |
302 static void | |
303 play_file(gchar * filename) | |
304 { | |
305 gchar magic[4], *name; | |
306 gulong len; | |
307 gint rate; | |
308 | |
309 audio_error = FALSE; | |
310 | |
311 wav_file = g_new0(WaveFile, 1); | |
312 if ((wav_file->file = vfs_fopen(filename, "rb"))) { | |
313 vfs_fread(magic, 1, 4, wav_file->file); | |
314 if (strncmp(magic, "RIFF", 4)) { | |
315 vfs_fclose(wav_file->file); | |
316 g_free(wav_file); | |
317 wav_file = NULL; | |
318 return; | |
319 } | |
320 read_le_ulong(wav_file->file, &len); | |
321 vfs_fread(magic, 1, 4, wav_file->file); | |
322 if (strncmp(magic, "WAVE", 4)) { | |
323 vfs_fclose(wav_file->file); | |
324 g_free(wav_file); | |
325 wav_file = NULL; | |
326 return; | |
327 } | |
328 for (;;) { | |
329 vfs_fread(magic, 1, 4, wav_file->file); | |
330 if (!read_le_ulong(wav_file->file, &len)) { | |
331 vfs_fclose(wav_file->file); | |
332 g_free(wav_file); | |
333 wav_file = NULL; | |
334 return; | |
335 } | |
336 if (!strncmp("fmt ", magic, 4)) | |
337 break; | |
338 vfs_fseek(wav_file->file, len, SEEK_CUR); | |
339 } | |
340 if (len < 16) { | |
341 vfs_fclose(wav_file->file); | |
342 g_free(wav_file); | |
343 wav_file = NULL; | |
344 return; | |
345 } | |
346 read_le_short(wav_file->file, &wav_file->format_tag); | |
347 switch (wav_file->format_tag) { | |
348 case WAVE_FORMAT_UNKNOWN: | |
349 case WAVE_FORMAT_ALAW: | |
350 case WAVE_FORMAT_MULAW: | |
351 case WAVE_FORMAT_ADPCM: | |
352 case WAVE_FORMAT_OKI_ADPCM: | |
353 case WAVE_FORMAT_DIGISTD: | |
354 case WAVE_FORMAT_DIGIFIX: | |
355 case IBM_FORMAT_MULAW: | |
356 case IBM_FORMAT_ALAW: | |
357 case IBM_FORMAT_ADPCM: | |
358 vfs_fclose(wav_file->file); | |
359 g_free(wav_file); | |
360 wav_file = NULL; | |
361 return; | |
362 } | |
363 read_le_short(wav_file->file, &wav_file->channels); | |
364 read_le_long(wav_file->file, &wav_file->samples_per_sec); | |
365 read_le_long(wav_file->file, &wav_file->avg_bytes_per_sec); | |
366 read_le_short(wav_file->file, &wav_file->block_align); | |
367 read_le_short(wav_file->file, &wav_file->bits_per_sample); | |
368 if (wav_file->bits_per_sample != 8 && wav_file->bits_per_sample != 16) { | |
369 vfs_fclose(wav_file->file); | |
370 g_free(wav_file); | |
371 wav_file = NULL; | |
372 return; | |
373 } | |
374 len -= 16; | |
375 if (len) | |
376 vfs_fseek(wav_file->file, len, SEEK_CUR); | |
377 | |
378 for (;;) { | |
379 vfs_fread(magic, 4, 1, wav_file->file); | |
380 | |
381 if (!read_le_ulong(wav_file->file, &len)) { | |
382 vfs_fclose(wav_file->file); | |
383 g_free(wav_file); | |
384 wav_file = NULL; | |
385 return; | |
386 } | |
387 if (!strncmp("data", magic, 4)) | |
388 break; | |
389 vfs_fseek(wav_file->file, len, SEEK_CUR); | |
390 } | |
391 wav_file->data_offset = vfs_ftell(wav_file->file); | |
392 wav_file->length = len; | |
393 | |
394 wav_file->position = 0; | |
395 wav_file->going = 1; | |
396 | |
397 if (wav_ip.output-> | |
398 open_audio((wav_file->bits_per_sample == | |
399 16) ? FMT_S16_LE : FMT_U8, | |
400 wav_file->samples_per_sec, wav_file->channels) == 0) { | |
401 audio_error = TRUE; | |
402 vfs_fclose(wav_file->file); | |
403 g_free(wav_file); | |
404 wav_file = NULL; | |
405 return; | |
406 } | |
407 name = get_title(filename); | |
408 rate = | |
409 wav_file->samples_per_sec * wav_file->channels * | |
410 (wav_file->bits_per_sample / 8); | |
411 wav_ip.set_info(name, 1000 * (wav_file->length / rate), 8 * rate, | |
412 wav_file->samples_per_sec, wav_file->channels); | |
413 g_free(name); | |
414 wav_file->seek_to = -1; | |
415 decode_thread = g_thread_create(play_loop, NULL, TRUE, NULL); | |
416 } | |
417 } | |
418 | |
419 static void | |
420 stop(void) | |
421 { | |
422 if (wav_file && wav_file->going) { | |
423 wav_file->going = 0; | |
424 g_thread_join(decode_thread); | |
425 wav_ip.output->close_audio(); | |
426 g_free(wav_file); | |
427 wav_file = NULL; | |
428 } | |
429 } | |
430 | |
431 static void | |
432 wav_pause(gshort p) | |
433 { | |
434 wav_ip.output->pause(p); | |
435 } | |
436 | |
437 static void | |
438 seek(gint time) | |
439 { | |
440 wav_file->seek_to = time; | |
441 | |
442 wav_file->eof = FALSE; | |
443 | |
444 while (wav_file->seek_to != -1) | |
445 xmms_usleep(10000); | |
446 } | |
447 | |
448 static int | |
449 get_time(void) | |
450 { | |
451 if (audio_error) | |
452 return -2; | |
453 if (!wav_file) | |
454 return -1; | |
455 if (!wav_file->going | |
456 || (wav_file->eof && !wav_ip.output->buffer_playing())) | |
457 return -1; | |
458 else { | |
459 return wav_ip.output->output_time(); | |
460 } | |
461 } | |
462 | |
463 static void | |
464 get_song_info(gchar * filename, gchar ** title, gint * length) | |
465 { | |
466 gchar magic[4]; | |
467 gulong len; | |
468 gint rate; | |
469 WaveFile *wav_file; | |
470 | |
471 wav_file = g_malloc(sizeof(WaveFile)); | |
472 memset(wav_file, 0, sizeof(WaveFile)); | |
473 if (!(wav_file->file = vfs_fopen(filename, "rb"))) | |
474 return; | |
475 | |
476 vfs_fread(magic, 1, 4, wav_file->file); | |
477 if (strncmp(magic, "RIFF", 4)) { | |
478 vfs_fclose(wav_file->file); | |
479 g_free(wav_file); | |
480 wav_file = NULL; | |
481 return; | |
482 } | |
483 read_le_ulong(wav_file->file, &len); | |
484 vfs_fread(magic, 1, 4, wav_file->file); | |
485 if (strncmp(magic, "WAVE", 4)) { | |
486 vfs_fclose(wav_file->file); | |
487 g_free(wav_file); | |
488 wav_file = NULL; | |
489 return; | |
490 } | |
491 for (;;) { | |
492 vfs_fread(magic, 1, 4, wav_file->file); | |
493 if (!read_le_ulong(wav_file->file, &len)) { | |
494 vfs_fclose(wav_file->file); | |
495 g_free(wav_file); | |
496 wav_file = NULL; | |
497 return; | |
498 } | |
499 if (!strncmp("fmt ", magic, 4)) | |
500 break; | |
501 vfs_fseek(wav_file->file, len, SEEK_CUR); | |
502 } | |
503 if (len < 16) { | |
504 vfs_fclose(wav_file->file); | |
505 g_free(wav_file); | |
506 wav_file = NULL; | |
507 return; | |
508 } | |
509 read_le_short(wav_file->file, &wav_file->format_tag); | |
510 switch (wav_file->format_tag) { | |
511 case WAVE_FORMAT_UNKNOWN: | |
512 case WAVE_FORMAT_ALAW: | |
513 case WAVE_FORMAT_MULAW: | |
514 case WAVE_FORMAT_ADPCM: | |
515 case WAVE_FORMAT_OKI_ADPCM: | |
516 case WAVE_FORMAT_DIGISTD: | |
517 case WAVE_FORMAT_DIGIFIX: | |
518 case IBM_FORMAT_MULAW: | |
519 case IBM_FORMAT_ALAW: | |
520 case IBM_FORMAT_ADPCM: | |
521 vfs_fclose(wav_file->file); | |
522 g_free(wav_file); | |
523 wav_file = NULL; | |
524 return; | |
525 } | |
526 read_le_short(wav_file->file, &wav_file->channels); | |
527 read_le_long(wav_file->file, &wav_file->samples_per_sec); | |
528 read_le_long(wav_file->file, &wav_file->avg_bytes_per_sec); | |
529 read_le_short(wav_file->file, &wav_file->block_align); | |
530 read_le_short(wav_file->file, &wav_file->bits_per_sample); | |
531 if (wav_file->bits_per_sample != 8 && wav_file->bits_per_sample != 16) { | |
532 vfs_fclose(wav_file->file); | |
533 g_free(wav_file); | |
534 wav_file = NULL; | |
535 return; | |
536 } | |
537 len -= 16; | |
538 if (len) | |
539 vfs_fseek(wav_file->file, len, SEEK_CUR); | |
540 | |
541 for (;;) { | |
542 vfs_fread(magic, 4, 1, wav_file->file); | |
543 | |
544 if (!read_le_ulong(wav_file->file, &len)) { | |
545 vfs_fclose(wav_file->file); | |
546 g_free(wav_file); | |
547 wav_file = NULL; | |
548 return; | |
549 } | |
550 if (!strncmp("data", magic, 4)) | |
551 break; | |
552 vfs_fseek(wav_file->file, len, SEEK_CUR); | |
553 } | |
554 rate = | |
555 wav_file->samples_per_sec * wav_file->channels * | |
556 (wav_file->bits_per_sample / 8); | |
557 (*length) = 1000 * (len / rate); | |
558 (*title) = get_title(filename); | |
559 | |
560 vfs_fclose(wav_file->file); | |
561 g_free(wav_file); | |
562 wav_file = NULL; | |
563 } |