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 }