diff Input/wav/wav.c @ 0:cb178e5ad177 trunk

[svn] Import audacious source.
author nenolod
date Mon, 24 Oct 2005 03:06:47 -0700
parents
children
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Input/wav/wav.c	Mon Oct 24 03:06:47 2005 -0700
@@ -0,0 +1,563 @@
+/*  BMP - Cross-platform multimedia player
+ *  Copyright (C) 2003-2004  BMP development team.
+ *
+ *  Based on XMMS:
+ *  Copyright (C) 1998-2003  XMMS development team.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "wav.h"
+
+#include <glib.h>
+#include <glib/gi18n.h>
+#include <string.h>
+
+#include <libaudacious/util.h>
+#include <libaudacious/titlestring.h>
+#include "audacious/output.h"
+
+
+InputPlugin wav_ip = {
+    NULL,
+    NULL,
+    NULL,                       /* Description */
+    wav_init,
+    NULL,
+    NULL,
+    is_our_file,
+    NULL,
+    play_file,
+    stop,
+    wav_pause,
+    seek,
+    NULL,
+    get_time,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    NULL,
+    get_song_info,
+    NULL,                       /* file_info_box */
+    NULL
+};
+
+WaveFile *wav_file = NULL;
+static GThread *decode_thread;
+static gboolean audio_error = FALSE;
+
+InputPlugin *
+get_iplugin_info(void)
+{
+    wav_ip.description = g_strdup_printf(_("WAV Audio Plugin"));
+    return &wav_ip;
+}
+
+static void
+wav_init(void)
+{
+    /* empty */
+}
+
+/* needed for is_our_file() */
+static gint
+read_n_bytes(VFSFile * file, guint8 * buf, gint n)
+{
+    if (vfs_fread(buf, 1, n, file) != n) {
+        return FALSE;
+    }
+    return TRUE;
+}
+
+static guint32
+convert_to_header(guint8 * buf)
+{
+
+    return (buf[0] << 24) + (buf[1] << 16) + (buf[2] << 8) + buf[3];
+}
+
+static guint32
+convert_to_long(guint8 * buf)
+{
+
+    return (buf[3] << 24) + (buf[2] << 16) + (buf[1] << 8) + buf[0];
+}
+
+static guint16
+read_wav_id(gchar * filename)
+{
+    VFSFile *file;
+    guint16 wavid;
+    guint8 buf[4];
+    guint32 head;
+    glong seek;
+
+    if (!(file = vfs_fopen(filename, "rb"))) {  /* Could not open file */
+        return 0;
+    }
+    if (!(read_n_bytes(file, buf, 4))) {
+        vfs_fclose(file);
+        return 0;
+    }
+    head = convert_to_header(buf);
+    if (head == ('R' << 24) + ('I' << 16) + ('F' << 8) + 'F') { /* Found a riff -- maybe WAVE */
+        if (vfs_fseek(file, 4, SEEK_CUR) != 0) {    /* some error occured */
+            vfs_fclose(file);
+            return 0;
+        }
+        if (!(read_n_bytes(file, buf, 4))) {
+            vfs_fclose(file);
+            return 0;
+        }
+        head = convert_to_header(buf);
+        if (head == ('W' << 24) + ('A' << 16) + ('V' << 8) + 'E') { /* Found a WAVE */
+            seek = 0;
+            do {                /* we'll be looking for the fmt-chunk which comes before the data-chunk */
+                /* A chunk consists of an header identifier (4 bytes), the length of the chunk
+                   (4 bytes), and the chunkdata itself, padded to be an even number of bytes.
+                   We'll skip all chunks until we find the "data"-one which could contain
+                   mpeg-data */
+                if (seek != 0) {
+                    if (vfs_fseek(file, seek, SEEK_CUR) != 0) { /* some error occured */
+                        vfs_fclose(file);
+                        return 0;
+                    }
+                }
+                if (!(read_n_bytes(file, buf, 4))) {
+                    vfs_fclose(file);
+                    return 0;
+                }
+                head = convert_to_header(buf);
+                if (!(read_n_bytes(file, buf, 4))) {
+                    vfs_fclose(file);
+                    return 0;
+                }
+                seek = convert_to_long(buf);
+                seek = seek + (seek % 2);   /* Has to be even (padding) */
+                if (seek >= 2
+                    && head == ('f' << 24) + ('m' << 16) + ('t' << 8) + ' ') {
+                    if (!(read_n_bytes(file, buf, 2))) {
+                        vfs_fclose(file);
+                        return 0;
+                    }
+                    wavid = buf[0] + 256 * buf[1];
+                    seek -= 2;
+                    /* we could go on looking for other things, but all we wanted was the wavid */
+                    vfs_fclose(file);
+                    return wavid;
+                }
+            }
+            while (head != ('d' << 24) + ('a' << 16) + ('t' << 8) + 'a');
+            /* it's RIFF WAVE */
+        }
+        /* it's RIFF */
+    }
+    /* it's not even RIFF */
+    vfs_fclose(file);
+    return 0;
+}
+
+static const gchar *
+get_extension(const gchar * filename)
+{
+    const gchar *ext = strrchr(filename, '.');
+    return ext ? ext + 1 : NULL;
+}
+
+static gboolean
+is_our_file(gchar * filename)
+{
+    gchar *ext;
+
+    ext = strrchr(filename, '.');
+    if (ext)
+        if (!strcasecmp(ext, ".wav"))
+            if (read_wav_id(filename) == WAVE_FORMAT_PCM)
+                return TRUE;
+    return FALSE;
+}
+
+
+static gchar *
+get_title(const gchar * filename)
+{
+    TitleInput *input;
+    gchar *title;
+
+    input = bmp_title_input_new();
+
+    input->file_name = g_path_get_basename(filename);
+    input->file_ext = get_extension(filename);
+    input->file_path = g_path_get_dirname(filename);
+
+    if (!(title = xmms_get_titlestring(xmms_get_gentitle_format(), input)))
+        title = g_strdup(input->file_name);
+
+    g_free(input->file_path);
+    g_free(input->file_name);
+    g_free(input);
+
+    return title;
+}
+
+static gint
+read_le_long(VFSFile * file, glong * ret)
+{
+    guchar buf[4];
+
+    if (vfs_fread(buf, 1, 4, file) != 4)
+        return 0;
+
+    *ret = (buf[3] << 24) | (buf[2] << 16) | (buf[1] << 8) | buf[0];
+    return TRUE;
+}
+
+#define read_le_ulong(file,ret) read_le_long(file,(long*)ret)
+
+static int
+read_le_short(VFSFile * file, gshort * ret)
+{
+    guchar buf[2];
+
+    if (vfs_fread(buf, 1, 2, file) != 2)
+        return 0;
+
+    *ret = (buf[1] << 8) | buf[0];
+    return TRUE;
+}
+
+static gpointer
+play_loop(gpointer arg)
+{
+    gchar data[2048 * 2];
+    gint bytes, blk_size, rate;
+    gint actual_read;
+
+    blk_size = 512 * (wav_file->bits_per_sample / 8) * wav_file->channels;
+    rate =
+        wav_file->samples_per_sec * wav_file->channels *
+        (wav_file->bits_per_sample / 8);
+    while (wav_file->going) {
+        if (!wav_file->eof) {
+            bytes = blk_size;
+            if (wav_file->length - wav_file->position < bytes)
+                bytes = wav_file->length - wav_file->position;
+            if (bytes > 0) {
+                actual_read = vfs_fread(data, 1, bytes, wav_file->file);
+
+                if (actual_read == 0) {
+                    wav_file->eof = 1;
+                    wav_ip.output->buffer_free();
+                    wav_ip.output->buffer_free();
+                }
+                else {
+                    if (wav_file->seek_to == -1)
+                        produce_audio(wav_ip.output->written_time(),
+                                      (wav_file->bits_per_sample ==
+                                       16) ? FMT_S16_LE : FMT_U8,
+                                      wav_file->channels, bytes, data,
+                                      &wav_file->going);
+                    wav_file->position += actual_read;
+                }
+            }
+            else {
+                wav_file->eof = TRUE;
+                wav_ip.output->buffer_free();
+                wav_ip.output->buffer_free();
+                xmms_usleep(10000);
+            }
+        }
+        else
+            xmms_usleep(10000);
+        if (wav_file->seek_to != -1) {
+            wav_file->position = wav_file->seek_to * rate;
+            vfs_fseek(wav_file->file,
+                      wav_file->position + wav_file->data_offset, SEEK_SET);
+            wav_ip.output->flush(wav_file->seek_to * 1000);
+            wav_file->seek_to = -1;
+        }
+
+    }
+    vfs_fclose(wav_file->file);
+
+    g_thread_exit(NULL);
+    return NULL;
+}
+
+static void
+play_file(gchar * filename)
+{
+    gchar magic[4], *name;
+    gulong len;
+    gint rate;
+
+    audio_error = FALSE;
+
+    wav_file = g_new0(WaveFile, 1);
+    if ((wav_file->file = vfs_fopen(filename, "rb"))) {
+        vfs_fread(magic, 1, 4, wav_file->file);
+        if (strncmp(magic, "RIFF", 4)) {
+            vfs_fclose(wav_file->file);
+            g_free(wav_file);
+            wav_file = NULL;
+            return;
+        }
+        read_le_ulong(wav_file->file, &len);
+        vfs_fread(magic, 1, 4, wav_file->file);
+        if (strncmp(magic, "WAVE", 4)) {
+            vfs_fclose(wav_file->file);
+            g_free(wav_file);
+            wav_file = NULL;
+            return;
+        }
+        for (;;) {
+            vfs_fread(magic, 1, 4, wav_file->file);
+            if (!read_le_ulong(wav_file->file, &len)) {
+                vfs_fclose(wav_file->file);
+                g_free(wav_file);
+                wav_file = NULL;
+                return;
+            }
+            if (!strncmp("fmt ", magic, 4))
+                break;
+            vfs_fseek(wav_file->file, len, SEEK_CUR);
+        }
+        if (len < 16) {
+            vfs_fclose(wav_file->file);
+            g_free(wav_file);
+            wav_file = NULL;
+            return;
+        }
+        read_le_short(wav_file->file, &wav_file->format_tag);
+        switch (wav_file->format_tag) {
+        case WAVE_FORMAT_UNKNOWN:
+        case WAVE_FORMAT_ALAW:
+        case WAVE_FORMAT_MULAW:
+        case WAVE_FORMAT_ADPCM:
+        case WAVE_FORMAT_OKI_ADPCM:
+        case WAVE_FORMAT_DIGISTD:
+        case WAVE_FORMAT_DIGIFIX:
+        case IBM_FORMAT_MULAW:
+        case IBM_FORMAT_ALAW:
+        case IBM_FORMAT_ADPCM:
+            vfs_fclose(wav_file->file);
+            g_free(wav_file);
+            wav_file = NULL;
+            return;
+        }
+        read_le_short(wav_file->file, &wav_file->channels);
+        read_le_long(wav_file->file, &wav_file->samples_per_sec);
+        read_le_long(wav_file->file, &wav_file->avg_bytes_per_sec);
+        read_le_short(wav_file->file, &wav_file->block_align);
+        read_le_short(wav_file->file, &wav_file->bits_per_sample);
+        if (wav_file->bits_per_sample != 8 && wav_file->bits_per_sample != 16) {
+            vfs_fclose(wav_file->file);
+            g_free(wav_file);
+            wav_file = NULL;
+            return;
+        }
+        len -= 16;
+        if (len)
+            vfs_fseek(wav_file->file, len, SEEK_CUR);
+
+        for (;;) {
+            vfs_fread(magic, 4, 1, wav_file->file);
+
+            if (!read_le_ulong(wav_file->file, &len)) {
+                vfs_fclose(wav_file->file);
+                g_free(wav_file);
+                wav_file = NULL;
+                return;
+            }
+            if (!strncmp("data", magic, 4))
+                break;
+            vfs_fseek(wav_file->file, len, SEEK_CUR);
+        }
+        wav_file->data_offset = vfs_ftell(wav_file->file);
+        wav_file->length = len;
+
+        wav_file->position = 0;
+        wav_file->going = 1;
+
+        if (wav_ip.output->
+            open_audio((wav_file->bits_per_sample ==
+                        16) ? FMT_S16_LE : FMT_U8,
+                       wav_file->samples_per_sec, wav_file->channels) == 0) {
+            audio_error = TRUE;
+            vfs_fclose(wav_file->file);
+            g_free(wav_file);
+            wav_file = NULL;
+            return;
+        }
+        name = get_title(filename);
+        rate =
+            wav_file->samples_per_sec * wav_file->channels *
+            (wav_file->bits_per_sample / 8);
+        wav_ip.set_info(name, 1000 * (wav_file->length / rate), 8 * rate,
+                        wav_file->samples_per_sec, wav_file->channels);
+        g_free(name);
+        wav_file->seek_to = -1;
+        decode_thread = g_thread_create(play_loop, NULL, TRUE, NULL);
+    }
+}
+
+static void
+stop(void)
+{
+    if (wav_file && wav_file->going) {
+        wav_file->going = 0;
+        g_thread_join(decode_thread);
+        wav_ip.output->close_audio();
+        g_free(wav_file);
+        wav_file = NULL;
+    }
+}
+
+static void
+wav_pause(gshort p)
+{
+    wav_ip.output->pause(p);
+}
+
+static void
+seek(gint time)
+{
+    wav_file->seek_to = time;
+
+    wav_file->eof = FALSE;
+
+    while (wav_file->seek_to != -1)
+        xmms_usleep(10000);
+}
+
+static int
+get_time(void)
+{
+    if (audio_error)
+        return -2;
+    if (!wav_file)
+        return -1;
+    if (!wav_file->going
+        || (wav_file->eof && !wav_ip.output->buffer_playing()))
+        return -1;
+    else {
+        return wav_ip.output->output_time();
+    }
+}
+
+static void
+get_song_info(gchar * filename, gchar ** title, gint * length)
+{
+    gchar magic[4];
+    gulong len;
+    gint rate;
+    WaveFile *wav_file;
+
+    wav_file = g_malloc(sizeof(WaveFile));
+    memset(wav_file, 0, sizeof(WaveFile));
+    if (!(wav_file->file = vfs_fopen(filename, "rb")))
+        return;
+
+    vfs_fread(magic, 1, 4, wav_file->file);
+    if (strncmp(magic, "RIFF", 4)) {
+        vfs_fclose(wav_file->file);
+        g_free(wav_file);
+        wav_file = NULL;
+        return;
+    }
+    read_le_ulong(wav_file->file, &len);
+    vfs_fread(magic, 1, 4, wav_file->file);
+    if (strncmp(magic, "WAVE", 4)) {
+        vfs_fclose(wav_file->file);
+        g_free(wav_file);
+        wav_file = NULL;
+        return;
+    }
+    for (;;) {
+        vfs_fread(magic, 1, 4, wav_file->file);
+        if (!read_le_ulong(wav_file->file, &len)) {
+            vfs_fclose(wav_file->file);
+            g_free(wav_file);
+            wav_file = NULL;
+            return;
+        }
+        if (!strncmp("fmt ", magic, 4))
+            break;
+        vfs_fseek(wav_file->file, len, SEEK_CUR);
+    }
+    if (len < 16) {
+        vfs_fclose(wav_file->file);
+        g_free(wav_file);
+        wav_file = NULL;
+        return;
+    }
+    read_le_short(wav_file->file, &wav_file->format_tag);
+    switch (wav_file->format_tag) {
+    case WAVE_FORMAT_UNKNOWN:
+    case WAVE_FORMAT_ALAW:
+    case WAVE_FORMAT_MULAW:
+    case WAVE_FORMAT_ADPCM:
+    case WAVE_FORMAT_OKI_ADPCM:
+    case WAVE_FORMAT_DIGISTD:
+    case WAVE_FORMAT_DIGIFIX:
+    case IBM_FORMAT_MULAW:
+    case IBM_FORMAT_ALAW:
+    case IBM_FORMAT_ADPCM:
+        vfs_fclose(wav_file->file);
+        g_free(wav_file);
+        wav_file = NULL;
+        return;
+    }
+    read_le_short(wav_file->file, &wav_file->channels);
+    read_le_long(wav_file->file, &wav_file->samples_per_sec);
+    read_le_long(wav_file->file, &wav_file->avg_bytes_per_sec);
+    read_le_short(wav_file->file, &wav_file->block_align);
+    read_le_short(wav_file->file, &wav_file->bits_per_sample);
+    if (wav_file->bits_per_sample != 8 && wav_file->bits_per_sample != 16) {
+        vfs_fclose(wav_file->file);
+        g_free(wav_file);
+        wav_file = NULL;
+        return;
+    }
+    len -= 16;
+    if (len)
+        vfs_fseek(wav_file->file, len, SEEK_CUR);
+
+    for (;;) {
+        vfs_fread(magic, 4, 1, wav_file->file);
+
+        if (!read_le_ulong(wav_file->file, &len)) {
+            vfs_fclose(wav_file->file);
+            g_free(wav_file);
+            wav_file = NULL;
+            return;
+        }
+        if (!strncmp("data", magic, 4))
+            break;
+        vfs_fseek(wav_file->file, len, SEEK_CUR);
+    }
+    rate =
+        wav_file->samples_per_sec * wav_file->channels *
+        (wav_file->bits_per_sample / 8);
+    (*length) = 1000 * (len / rate);
+    (*title) = get_title(filename);
+
+    vfs_fclose(wav_file->file);
+    g_free(wav_file);
+    wav_file = NULL;
+}