diff src/flacng/seekable_stream_callbacks.c @ 930:2f742d127b3e trunk

[svn] - initial import of flacng from audacious-flacng-0.012
author nenolod
date Mon, 09 Apr 2007 10:55:23 -0700
parents
children b6c95e2a14f4
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/flacng/seekable_stream_callbacks.c	Mon Apr 09 10:55:23 2007 -0700
@@ -0,0 +1,338 @@
+/*
+ *  A FLAC decoder plugin for the Audacious Media Player
+ *  Copyright (C) 2005 Ralf Ertzinger
+ *
+ *  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 <string.h>
+#include <FLAC/all.h>
+#include <glib.h>
+#include <glib/gi18n.h>
+#include <audacious/vfs.h>
+#include "flacng.h"
+#include "tools.h"
+#include "seekable_stream_callbacks.h"
+#include "debug.h"
+
+/* === */
+
+FLAC__StreamDecoderReadStatus read_callback(const FLAC__StreamDecoder *decoder, FLAC__byte buffer[], unsigned *bytes, void *client_data) {
+
+    callback_info* info;
+    gint to_read;
+    size_t read;
+
+    _ENTER;
+
+    info = (callback_info*) client_data;
+    _DEBUG("Using callback_info %s", info->name);
+
+    if (NULL == info->input_stream) {
+        _ERROR("Trying to read data from an uninitialized file!");
+        _LEAVE FLAC__STREAM_DECODER_READ_STATUS_ABORT;
+    }
+
+    if (0 <= info->read_max) {
+        to_read = MIN(*bytes, info->read_max);
+        _DEBUG("Reading restricted to %d bytes", info->read_max);
+    } else {
+        to_read = *bytes;
+    }
+
+    if (0 == to_read) {
+        _LEAVE FLAC__STREAM_DECODER_READ_STATUS_END_OF_STREAM;
+    }
+
+    read = vfs_fread(buffer, 1, to_read, info->input_stream);
+
+    if ((0 < read) && (0 < info->read_max)) {
+        info->read_max -= read;
+    }
+
+    _DEBUG("Wanted %d bytes, got %d bytes", *bytes, read);
+    *bytes = read;
+
+    switch(read) {
+        case -1:
+            _ERROR("Error while reading from stream!");
+            _LEAVE FLAC__STREAM_DECODER_READ_STATUS_ABORT;
+            break;
+
+        case 0:
+            _DEBUG("Stream reached EOF");
+            _LEAVE FLAC__STREAM_DECODER_READ_STATUS_END_OF_STREAM;
+            break;
+
+        default:
+            _LEAVE FLAC__STREAM_DECODER_READ_STATUS_CONTINUE;
+    }
+}
+
+/* --- */
+
+FLAC__StreamDecoderSeekStatus seek_callback(const FLAC__StreamDecoder *decoder, FLAC__uint64 absolute_byte_offset, void *client_data) {
+
+    callback_info* info;
+
+    _ENTER;
+
+    info = (callback_info*) client_data;
+    _DEBUG("Using callback_info %s", info->name);
+
+    _DEBUG("Seeking to %lld", absolute_byte_offset);
+
+    if (0 != vfs_fseek(info->input_stream, absolute_byte_offset, SEEK_SET)) {
+        _ERROR("Could not seek to %lld!", absolute_byte_offset);
+        _LEAVE FLAC__STREAM_DECODER_SEEK_STATUS_ERROR;
+    }
+
+    _LEAVE FLAC__STREAM_DECODER_SEEK_STATUS_OK;
+}
+
+/* --- */
+
+FLAC__StreamDecoderTellStatus tell_callback(const FLAC__StreamDecoder *decoder, FLAC__uint64 *absolute_byte_offset, void *client_data) {
+
+    callback_info* info;
+    glong position;
+
+    _ENTER;
+
+    info = (callback_info*) client_data;
+    _DEBUG("Using callback_info %s", info->name);
+
+    if (-1 == (position = vfs_ftell(info->input_stream))) {
+        fprintf(stderr, "Could not tell current position!");
+        return FLAC__STREAM_DECODER_TELL_STATUS_ERROR;
+    }
+
+    _DEBUG("Current position: %d", position);
+
+    *absolute_byte_offset = position;
+
+    _LEAVE FLAC__STREAM_DECODER_TELL_STATUS_OK;
+}
+
+/* --- */
+
+FLAC__bool eof_callback(const FLAC__StreamDecoder *decoder, void *client_data) {
+
+    callback_info* info;
+    gboolean eof;
+
+    _ENTER;
+
+    info = (callback_info*) client_data;
+    _DEBUG("Using callback_info %s", info->name);
+
+    eof = vfs_feof(info->input_stream);
+
+    _LEAVE eof;
+}
+
+/* --- */
+
+FLAC__StreamDecoderLengthStatus length_callback(const FLAC__StreamDecoder *decoder, FLAC__uint64 *stream_length, void *client_data) {
+
+    callback_info* info;
+    size_t size;
+
+    _ENTER;
+
+    info = (callback_info*) client_data;
+    _DEBUG("Using callback_info %s", info->name);
+
+    *stream_length = 0;
+
+    _LEAVE FLAC__STREAM_DECODER_LENGTH_STATUS_OK;
+}
+
+/* --- */
+
+FLAC__StreamDecoderWriteStatus write_callback(const FLAC__StreamDecoder *decoder, const FLAC__Frame *frame, const FLAC__int32 * const buffer[], void *client_data) {
+
+    glong i;
+    gshort j;
+    gint32 sample;
+    callback_info* info;
+
+    _ENTER;
+
+    info = (callback_info*) client_data;
+    _DEBUG("Using callback_info %s", info->name);
+
+    _DEBUG("Frame decoded: %d samples per channel, %d channels, %d bps",
+            frame->header.blocksize, frame->header.channels, frame->header.bits_per_sample);
+
+    /*
+     * Check if there is more data decoded than we have space
+     * for. This _should_ not happen given how our buffer is sized,
+     * but you never know.
+     */
+    if (info->buffer_free < (frame->header.blocksize * frame->header.channels)) {
+        _ERROR("BUG! Too much data decoded from stream!");
+        _LEAVE FLAC__STREAM_DECODER_WRITE_STATUS_ABORT;
+    }
+
+    if ((frame->header.bits_per_sample != 8) &&
+        (frame->header.bits_per_sample != 16) &&
+        (frame->header.bits_per_sample != 24)) {
+        _ERROR("Unsupported bitrate found in stream: %d!", frame->header.bits_per_sample);
+        _LEAVE FLAC__STREAM_DECODER_WRITE_STATUS_ABORT;
+    }
+
+    /*
+     * Copy the frame metadata, will be compared to stream
+     * metadata later
+     * This also describes the format of the current buffer content.
+     */
+    info->frame.channels = frame->header.channels;
+    info->frame.samplerate = frame->header.sample_rate;
+    info->frame.bits_per_sample = frame->header.bits_per_sample;
+
+    for (i=0; i < frame->header.blocksize; i++) {
+        for (j=0; j < frame->header.channels; j++) {
+            *(info->write_pointer++) = buffer[j][i];
+            info->buffer_free -= 1;
+            info->buffer_used += 1;
+        }
+    }
+
+    _DEBUG("free space in buffer after copying: %d samples", info->buffer_free);
+
+    _LEAVE FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE;
+}
+
+/* --- */
+
+void error_callback(const FLAC__StreamDecoder *decoder, FLAC__StreamDecoderErrorStatus status, void *client_data) {
+
+    _ENTER;
+
+    _ERROR("FLAC decoder error callback was called: %d", status);
+
+    _LEAVE;
+
+}
+
+/* --- */
+
+void metadata_callback(const FLAC__StreamDecoder *decoder, const FLAC__StreamMetadata *metadata, void *client_data) {
+
+    callback_info* info;
+    gint i;
+    FLAC__StreamMetadata_VorbisComment_Entry* entry;
+    FLAC__StreamMetadata* metadata_copy;
+    gchar* key;
+    gchar* value;
+    int artist_offset;
+
+    _ENTER;
+
+    info = (callback_info*) client_data;
+    _DEBUG("Using callback_info %s", info->name);
+
+    /*
+     * We have found a metadata block. Enable unrestricted reading
+     */
+    info->read_max = -1;
+
+    if (FLAC__METADATA_TYPE_STREAMINFO == metadata->type) {
+        /*
+         * Basic stream information. Sample rate, channels and stuff
+         */
+        _DEBUG("FLAC__METADATA_TYPE_STREAMINFO found");
+
+        info->stream.samples = metadata->data.stream_info.total_samples;
+        _DEBUG("total_samples=%lld", metadata->data.stream_info.total_samples);
+        info->stream.bits_per_sample = metadata->data.stream_info.bits_per_sample;
+        _DEBUG("bits_per_sample=%d", metadata->data.stream_info.bits_per_sample);
+        info->stream.channels = metadata->data.stream_info.channels;
+        _DEBUG("channels=%d", metadata->data.stream_info.channels);
+        info->stream.samplerate = metadata->data.stream_info.sample_rate;
+        _DEBUG("sample_rate=%d", metadata->data.stream_info.sample_rate);
+
+        info->metadata_changed = TRUE;
+    }
+
+    if (FLAC__METADATA_TYPE_VORBIS_COMMENT == metadata->type) {
+        /*
+         * We will possibly need to modify some of the entries
+         * in the metadata field, so we make a copy of it
+         * first.
+         * The original structure must not be modified.
+         */
+        metadata_copy = FLAC__metadata_object_clone(metadata);
+
+        /*
+         * A vorbis type comment field.
+         */
+        _DEBUG("FLAC__METADATA_TYPE_VORBIS_COMMENT found");
+        _DEBUG("Vorbis comment contains %d fields", metadata_copy->data.vorbis_comment.num_comments);
+        _DEBUG("Vendor string: %s", metadata_copy->data.vorbis_comment.vendor_string.entry);
+
+        /*
+         * Find an ARTIST field
+         */
+        if (0 <= (artist_offset = FLAC__metadata_object_vorbiscomment_find_entry_from(metadata_copy, 0, "ARTIST"))) {
+            _DEBUG("ARTIST field found @ %d: %s", artist_offset,
+                    metadata_copy->data.vorbis_comment.comments[artist_offset].entry);
+        }
+
+
+        /*
+         * Enumerate the comment entries
+         */
+        entry = metadata_copy->data.vorbis_comment.comments;
+        for (i=0; i < metadata_copy->data.vorbis_comment.num_comments; i++,entry++) {
+            _DEBUG("Comment[%d]: %s", i, entry->entry);
+
+            /*
+             * Try and parse the comment.
+             * If FLAC__metadata_object_vorbiscomment_entry_to_name_value_pair() succeeds,
+             * it allocates memory for the key and value which we have to take
+             * care of.
+             */
+            if (false == FLAC__metadata_object_vorbiscomment_entry_to_name_value_pair(*entry, &key, &value)) {
+                _DEBUG("Could not parse comment");
+            } else {
+                _DEBUG("Key: <%s>, Value <%s>", key, value);
+                add_comment(info, key, value);
+                free(key);
+                free(value);
+            }
+        }
+
+        /*
+         * Free our metadata copy
+         */
+        FLAC__metadata_object_delete(metadata_copy);
+
+        info->metadata_changed = TRUE;
+    }
+
+    if (FLAC__METADATA_TYPE_SEEKTABLE == metadata->type) {
+        /*
+         * We have found a seektable, which means that we can seek
+         * without telling FLAC the length of the file (which we can not
+         * do, since Audacious lacks the functions for that)
+         */
+        _DEBUG("FLAC__METADATA_TYPE_SEEKTABLE found");
+        info->stream.has_seektable = TRUE;
+    }
+
+    _LEAVE;
+}