diff src/wavpack/libwavpack.cxx @ 109:38ce41606f10 trunk

[svn] - wavpack input plugin -- under construction
author nenolod
date Tue, 24 Oct 2006 19:03:53 -0700
parents
children 5745352e0c88
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/wavpack/libwavpack.cxx	Tue Oct 24 19:03:53 2006 -0700
@@ -0,0 +1,411 @@
+#include <assert.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <wavpack/wputils.h>
+extern "C" {
+#include <audacious/plugin.h>
+#include <audacious/configdb.h>
+#include <audacious/titlestring.h>
+#include <audacious/util.h>
+}
+#include <glib.h>
+#include <gtk/gtk.h>
+#include <iconv.h>
+#include <math.h>
+#include "equalizer.h"
+#include "tags.h"
+#ifndef M_LN10
+#define M_LN10   2.3025850929940456840179914546843642
+#endif
+
+#define DBG(format, args...) fprintf(stderr, format, ## args)
+#define BUFFER_SIZE 256 // read buffer size, in samples
+
+extern "C" InputPlugin * get_iplugin_info(void);
+static void wv_load_config();
+static int wv_is_our_file(char *);
+static void wv_play(char *);
+static void wv_stop(void);
+static void wv_pause(short);
+static void wv_seek(int);
+static int wv_get_time(void);
+static void wv_get_song_info(char *, char **, int *);
+static char *generate_title(const char *, WavpackContext *ctx);
+static double isSeek;
+static short paused;
+static bool killDecodeThread;
+static bool AudioError;
+static GThread *thread_handle;
+static gboolean EQ_on;
+
+// in ui.cpp
+void wv_configure();
+void wv_about_box(void);
+void wv_file_info_box(char *);
+extern gboolean clipPreventionEnabled;
+extern gboolean dynBitrateEnabled;
+extern gboolean replaygainEnabled;
+extern gboolean albumReplaygainEnabled;
+extern gboolean openedAudio;
+
+InputPlugin mod = {
+    NULL,                       //handle
+    NULL,                       //filename
+    NULL,
+    wv_load_config,
+    wv_about_box,
+    wv_configure,
+    wv_is_our_file,
+    NULL,                       //no use
+    wv_play,
+    wv_stop,
+    wv_pause,
+    wv_seek,
+    NULL,                       //set eq
+    wv_get_time,
+    NULL,                       //get volume
+    NULL,                       //set volume
+    NULL,                       //cleanup
+    NULL,                       //obsolete
+    NULL,                       //add_vis
+    NULL,
+    NULL,
+    wv_get_song_info,
+    wv_file_info_box,          //info box
+    NULL,                       //output
+};
+
+class WavpackDecoder
+{
+public:
+    InputPlugin *mod;
+    int32_t *input;
+    int16_t *output;
+    int sample_rate;
+    int num_channels;
+    WavpackContext *ctx;
+    char error_buff[4096]; // TODO: fixme!
+
+    WavpackDecoder(InputPlugin *mod) : mod(mod)
+    {
+        ctx = NULL;
+        input = NULL;
+        output = NULL;
+    }
+
+    ~WavpackDecoder()
+    {
+        if (input != NULL) {
+            free(input);
+            input = NULL;
+        }
+        if (output != NULL) {
+            free(output);
+            output = NULL;
+        }
+        if (ctx != NULL) {
+            WavpackCloseFile(ctx);
+            ctx = NULL;
+        }
+    }
+
+    bool attach(const char *filename)
+    {
+        ctx = WavpackOpenFileInput(filename, error_buff, OPEN_TAGS | OPEN_WVC, 0);
+
+        if (ctx == NULL) {
+            return false;
+        }
+
+        sample_rate = WavpackGetSampleRate(ctx);
+        num_channels = WavpackGetNumChannels(ctx);
+        input = (int32_t *)calloc(BUFFER_SIZE, num_channels * sizeof(int32_t));
+        output = (int16_t *)calloc(BUFFER_SIZE, num_channels * sizeof(int16_t));
+        mod->set_info(generate_title(filename, ctx),
+                      (int) (WavpackGetNumSamples(ctx) / sample_rate) * 1000,
+                      (int) WavpackGetAverageBitrate(ctx, num_channels),
+                      (int) sample_rate, num_channels);
+        return true;
+    }
+
+    bool open_audio()
+    {
+        return mod->output->open_audio(FMT_S16_LE, sample_rate, num_channels);
+    }
+
+    void process_buffer(size_t num_samples)
+    {
+        for (int i = 0; i < num_samples * num_channels; i++) {
+            output[i] = input[i];
+        }
+        produce_audio(mod->output->get_output_time(), FMT_S16_LE, 
+		sample_rate, num_channels, output, 
+		num_samples * num_channels * sizeof(int16_t));
+    }
+};
+
+extern "C" InputPlugin *
+get_iplugin_info(void)
+{
+    mod.description =
+        g_strdup_printf(("Wavpack Decoder Plugin %s"), VERSION);
+    return &mod;
+}
+
+static int
+wv_is_our_file(char *filename)
+{
+    char *ext;
+
+    ext = strrchr(filename, '.');
+    if (ext) {
+        if (!strcasecmp(ext, ".wv")) {
+            return TRUE;
+        }
+    }
+    return FALSE;
+}
+
+void
+load_tag(ape_tag *tag, WavpackContext *ctx) 
+{
+    memset(tag, 0, sizeof(ape_tag));
+    WavpackGetTagItem(ctx, "Album", tag->album, sizeof(tag->album));
+    WavpackGetTagItem(ctx, "Artist", tag->artist, sizeof(tag->artist));
+    WavpackGetTagItem(ctx, "Comment", tag->comment, sizeof(tag->comment));
+    WavpackGetTagItem(ctx, "Genre", tag->genre, sizeof(tag->genre));
+    WavpackGetTagItem(ctx, "Title", tag->title, sizeof(tag->title));
+    WavpackGetTagItem(ctx, "Track", tag->track, sizeof(tag->track));
+    WavpackGetTagItem(ctx, "Year", tag->year, sizeof(tag->year));
+}
+
+static char *
+convertUTF8toLocale(char *utf8)
+{
+    // note - opens a new iconv descriptor for each call
+    // will have to find a way to reuse the descriptor if this turns
+    // out to be too slow
+    iconv_t idesc = iconv_open("", "UTF-8");
+    if (idesc == (iconv_t) -1) {
+        perror("iconv_open failed");
+        return g_strdup(utf8);
+    }
+
+    size_t in_left = strlen(utf8);
+    size_t out_left = 2 * in_left + 1;
+    char *buf = (char *)g_malloc(out_left);
+    char *in = utf8;
+    char *out = buf;
+
+    memset(buf, 0, out_left);
+    size_t err = iconv(idesc, &in, &in_left, &out, &out_left);
+    iconv_close(idesc);
+    return buf;
+}
+
+static void *
+end_thread()
+{
+    return 0;
+}
+
+static void *
+DecodeThread(void *a)
+{
+    ape_tag tag;
+    char *filename = (char *) a;
+    int bps_updateCounter = 0;
+    int bps;
+    int i;
+    WavpackDecoder d(&mod);
+
+    if (!d.attach(filename)) {
+        printf("wavpack: Error opening file: \"%s\"\n", filename);
+        killDecodeThread = true;
+        return end_thread();
+    }
+    bps = WavpackGetBytesPerSample(d.ctx) * d.num_channels;
+    DBG("reading %s at %d rate with %d channels\n", filename, d.sample_rate, d.num_channels);
+
+    if (!d.open_audio()) {
+        DBG("error opening xmms audio channel\n");
+        killDecodeThread = true;
+        AudioError = true;
+        openedAudio = false;
+    }
+    else {
+        DBG("opened xmms audio channel\n");
+        openedAudio = true;
+    }
+    unsigned status;
+    char *display = generate_title(filename, d.ctx);
+    int length = (int) (1000 * WavpackGetNumSamples(d.ctx));
+
+    while (!killDecodeThread) {
+        if (isSeek != -1) {
+            DBG("seeking to position %d\n", isSeek);
+            WavpackSeekSample(d.ctx, isSeek * d.sample_rate);
+            isSeek = -1;
+        }
+        if (paused == 0
+            && (mod.output->buffer_free() >=
+                (1152 * 2 *
+                 (16 / 8)) << (mod.output->buffer_playing()? 1 : 0))) {
+            status =
+                WavpackUnpackSamples(d.ctx, d.input, BUFFER_SIZE);
+            if (status == (unsigned) (-1)) {
+                printf("wavpack: Error decoding file.\n");
+                break;
+            }
+            else if (status == 0) {
+                killDecodeThread = true;
+                break;
+            }
+            else {
+                d.process_buffer(status);
+            }
+        }
+        else {
+            xmms_usleep(10000);
+        }
+    }
+    return end_thread();
+}
+
+static void
+wv_play(char *filename)
+{
+    paused = 0;
+    isSeek = -1;
+    killDecodeThread = false;
+    AudioError = false;
+    thread_handle = g_thread_create(DecodeThread, (void *) filename, TRUE, NULL);
+    return;
+}
+
+static char *
+generate_title(const char *fn, WavpackContext *ctx)
+{
+    static char *displaytitle = NULL;
+    ape_tag tag;
+    TitleInput *ti;
+
+    ti = (TitleInput *) g_malloc0(sizeof(TitleInput));
+    ti->__size = XMMS_TITLEINPUT_SIZE;
+    ti->__version = XMMS_TITLEINPUT_VERSION;
+
+    ti->file_name = g_strdup(g_basename(fn));
+    ti->file_ext = "wv";
+
+    load_tag(&tag, ctx);
+
+    // xmms doesn't support unicode...
+    ti->track_name = convertUTF8toLocale(tag.title);
+    ti->performer = convertUTF8toLocale(tag.artist);
+    ti->album_name = convertUTF8toLocale(tag.album);
+    ti->date = convertUTF8toLocale(tag.year);
+    ti->track_number = atoi(tag.track);
+    if (ti->track_number < 0)
+        ti->track_number = 0;
+    ti->year = atoi(tag.year);
+    if (ti->year < 0)
+        ti->year = 0;
+    ti->genre = convertUTF8toLocale(tag.genre);
+    ti->comment = convertUTF8toLocale(tag.comment);
+
+    displaytitle = xmms_get_titlestring(xmms_get_gentitle_format(), ti);
+    if (!displaytitle || *displaytitle == '\0'
+        || (strlen(tag.title) == 0 && strlen(tag.artist) == 0))
+        displaytitle = ti->file_name;
+    g_free(ti->track_name);
+    g_free(ti->performer);
+    g_free(ti->album_name);
+    g_free(ti->genre);
+    g_free(ti->comment);
+    g_free(ti);
+
+    return displaytitle;
+}
+
+static void
+wv_get_song_info(char *filename, char **title, int *length)
+{
+    assert(filename != NULL);
+    char error_buff[4096]; // TODO: fixme!
+    WavpackContext *ctx = WavpackOpenFileInput(filename, error_buff, OPEN_TAGS | OPEN_WVC, 0);
+    if (ctx == NULL) {
+        printf("wavpack: Error opening file: \"%s: %s\"\n", filename, error_buff);
+        return;
+    }
+    int sample_rate = WavpackGetSampleRate(ctx);
+    int num_channels = WavpackGetNumChannels(ctx);
+    DBG("reading %s at %d rate with %d channels\n", filename, sample_rate, num_channels);
+
+    *length = (int)(WavpackGetNumSamples(ctx) / sample_rate) * 1000,
+    *title = generate_title(filename, ctx);
+    DBG("title for %s = %s\n", filename, *title);
+    WavpackCloseFile(ctx);
+}
+
+static int
+wv_get_time(void)
+{
+    if (!mod.output)
+        return -1;
+    if (AudioError)
+        return -2;
+    if (killDecodeThread && !mod.output->buffer_playing())
+        return -1;
+    return mod.output->output_time();
+}
+
+
+static void
+wv_seek(int sec)
+{
+    isSeek = sec;
+    mod.output->flush((int) (1000 * isSeek));
+}
+
+static void
+wv_pause(short pause)
+{
+    mod.output->pause(paused = pause);
+}
+
+static void
+wv_stop(void)
+{
+    killDecodeThread = true;
+    if (thread_handle != 0) {
+        g_thread_join(thread_handle);
+        if (openedAudio) {
+            mod.output->buffer_free();
+            mod.output->close_audio();
+        }
+        openedAudio = false;
+        if (AudioError)
+            printf("Could not open Audio\n");
+    }
+
+}
+
+static void
+wv_load_config()
+{
+    ConfigDb *cfg;
+
+    cfg = bmp_cfg_db_open();
+
+    bmp_cfg_db_get_bool(cfg, "wavpack", "clip_prevention",
+                          &clipPreventionEnabled);
+    bmp_cfg_db_get_bool(cfg, "wavpack", "album_replaygain",
+                          &albumReplaygainEnabled);
+    bmp_cfg_db_get_bool(cfg, "wavpack", "dyn_bitrate", &dynBitrateEnabled);
+    bmp_cfg_db_get_bool(cfg, "wavpack", "replaygain", &replaygainEnabled);
+    bmp_cfg_db_close(cfg);
+
+    openedAudio = false;
+}