From 30424cb3e9cf500165621802f65d552218e080f3 Mon Sep 17 00:00:00 2001
From: Warren Dukes <warren.dukes@gmail.com>
Date: Mon, 31 May 2004 01:54:10 +0000
Subject: flac_plugin

git-svn-id: https://svn.musicpd.org/mpd/trunk@1246 09075e82-0dd4-0310-85a5-a0d7c8717e4f
---
 src/Makefile.am                |   7 +-
 src/flac_decode.c              | 441 -------------------------------
 src/flac_decode.h              |  31 ---
 src/inputPlugin.c              |   2 +
 src/inputPlugins/flac_plugin.c | 580 +++++++++++++++++++++++++++++++++++++++++
 src/inputPlugins/mp3_plugin.c  |   1 +
 src/inputPlugins/ogg_plugin.c  |   1 +
 src/tag.c                      | 114 --------
 8 files changed, 588 insertions(+), 589 deletions(-)
 delete mode 100644 src/flac_decode.c
 delete mode 100644 src/flac_decode.h
 create mode 100644 src/inputPlugins/flac_plugin.c

diff --git a/src/Makefile.am b/src/Makefile.am
index ead005df4..39706f692 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -1,11 +1,12 @@
 bin_PROGRAMS = mpd
 SUBDIRS = $(ID3_SUBDIR) $(MAD_SUBDIR) $(MP4FF_SUBDIR)
 
-mpd_inputPlugins = inputPlugins/mp3_plugin.c inputPlugins/ogg_plugin.c
+mpd_inputPlugins = inputPlugins/mp3_plugin.c inputPlugins/ogg_plugin.c \
+	inputPlugins/flac_plugin.c
 
 mpd_headers = buffer2array.h interface.h command.h playlist.h ls.h \
 	song.h list.h directory.h tables.h utils.h path.h \
-	tag.h player.h listen.h conf.h volume.h flac_decode.h \
+	tag.h player.h listen.h conf.h volume.h \
 	audio.h playerData.h stats.h myfprintf.h sig_handlers.h decode.h log.h \
 	audiofile_decode.h charConv.h permission.h mpd_types.h pcm_utils.h \
 	mp4_decode.h aac_decode.h signal_check.h utf8.h inputStream.h \
@@ -13,7 +14,7 @@ mpd_headers = buffer2array.h interface.h command.h playlist.h ls.h \
 	inputPlugin.h
 mpd_SOURCES = main.c buffer2array.c interface.c command.c playlist.c ls.c \
 	song.c list.c directory.c tables.c utils.c path.c \
-	tag.c player.c listen.c conf.c volume.c flac_decode.c \
+	tag.c player.c listen.c conf.c volume.c \
 	audio.c playerData.c stats.c myfprintf.c sig_handlers.c decode.c log.c \
 	audiofile_decode.c charConv.c permission.c pcm_utils.c mp4_decode.c \
 	aac_decode.c signal_check.c utf8.c inputStream.c outputBuffer.c \
diff --git a/src/flac_decode.c b/src/flac_decode.c
deleted file mode 100644
index a5af3e132..000000000
--- a/src/flac_decode.c
+++ /dev/null
@@ -1,441 +0,0 @@
-/* the Music Player Daemon (MPD)
- * (c)2003-2004 by Warren Dukes (shank@mercury.chem.pitt.edu)
- * This project's homepage is: http://www.musicpd.org
- *
- * 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 "flac_decode.h"
-
-#ifdef HAVE_FLAC
-
-#include "utils.h"
-#include "log.h"
-#include "pcm_utils.h"
-#include "inputStream.h"
-#include "outputBuffer.h"
-#include "replayGain.h"
-#include "audio.h"
-
-#include <stdio.h>
-#include <string.h>
-#include <unistd.h>
-#include <FLAC/seekable_stream_decoder.h>
-#include <FLAC/metadata.h>
-
-typedef struct {
-#define FLAC_CHUNK_SIZE 4080
-	unsigned char chunk[FLAC_CHUNK_SIZE];
-	int chunk_length;
-	float time;
-	int bitRate;
-	FLAC__uint64 position;
-	OutputBuffer * cb;
-	DecoderControl * dc;
-        InputStream inStream;
-        float replayGainScale;
-} FlacData;
-
-/* this code is based on flac123, from flac-tools */
-
-int flacSendChunk(FlacData * data);
-void flacError(const FLAC__SeekableStreamDecoder *, 
-                FLAC__StreamDecoderErrorStatus, void *);
-void flacPrintErroredState(FLAC__SeekableStreamDecoderState state, char * file);
-void flacMetadata(const FLAC__SeekableStreamDecoder *, 
-                const FLAC__StreamMetadata *, void *);
-FLAC__StreamDecoderWriteStatus flacWrite(const FLAC__SeekableStreamDecoder *,
-                const FLAC__Frame *, const FLAC__int32 * const buf[], void *);
-FLAC__SeekableStreamDecoderReadStatus flacRead(
-                const FLAC__SeekableStreamDecoder *, FLAC__byte buf[],
-                unsigned *, void *);
-FLAC__SeekableStreamDecoderSeekStatus flacSeek(
-                const FLAC__SeekableStreamDecoder *, FLAC__uint64, void *);
-FLAC__SeekableStreamDecoderTellStatus flacTell(
-                const FLAC__SeekableStreamDecoder *, FLAC__uint64 *, void *);
-FLAC__SeekableStreamDecoderLengthStatus flacLength(
-                const FLAC__SeekableStreamDecoder *, FLAC__uint64 *, void *);
-FLAC__bool flacEOF(const FLAC__SeekableStreamDecoder *, void *);
-
-int flac_decode(OutputBuffer * cb, DecoderControl *dc) {
-	FLAC__SeekableStreamDecoder * flacDec;
-	FlacData data;
-	int status = 1;
-
-	data.chunk_length = 0;
-	data.time = 0;
-	data.position = 0;
-	data.bitRate = 0;
-	data.cb = cb;
-	data.dc = dc;
-        data.replayGainScale = 1.0;
-
-        if(openInputStream(&(data.inStream),dc->file)<0) {
-                ERROR("unable to open flac: %s\n",dc->file);
-                return -1;
-        }
-	
-	if(!(flacDec = FLAC__seekable_stream_decoder_new())) return -1;
-	/*status&=FLAC__file_decoder_set_md5_checking(flacDec,1);*/
-	status&=FLAC__seekable_stream_decoder_set_read_callback(flacDec,
-                        flacRead);
-	status&=FLAC__seekable_stream_decoder_set_seek_callback(flacDec,
-                        flacSeek);
-	status&=FLAC__seekable_stream_decoder_set_tell_callback(flacDec,
-                        flacTell);
-	status&=FLAC__seekable_stream_decoder_set_length_callback(flacDec,
-                        flacLength);
-	status&=FLAC__seekable_stream_decoder_set_eof_callback(flacDec,flacEOF);
-	status&=FLAC__seekable_stream_decoder_set_write_callback(flacDec,
-                        flacWrite);
-	status&=FLAC__seekable_stream_decoder_set_metadata_callback(flacDec,
-                        flacMetadata);
-	status&=FLAC__seekable_stream_decoder_set_error_callback(flacDec,
-                        flacError);
-	status&=FLAC__seekable_stream_decoder_set_client_data(flacDec,
-                        (void *)&data);
-	if(!status) {
-		ERROR("flac problem before init(): %s\n",dc->file);
-		flacPrintErroredState(
-                        FLAC__seekable_stream_decoder_get_state(flacDec),
-                                        dc->file);
-		FLAC__seekable_stream_decoder_delete(flacDec);
-		return -1;
-	}
-
-	if(FLAC__seekable_stream_decoder_init(flacDec)!=
-			FLAC__STREAM_DECODER_SEARCH_FOR_METADATA) 
-	{
-		ERROR("flac problem doing init(): %s\n",dc->file);
-		flacPrintErroredState(
-                        FLAC__seekable_stream_decoder_get_state(flacDec),
-                                        dc->file);
-		FLAC__seekable_stream_decoder_delete(flacDec);
-		return -1;
-	}
-
-	if(!FLAC__seekable_stream_decoder_process_until_end_of_metadata(flacDec)) {
-		ERROR("flac problem reading metadata: %s\n", dc->file);
-		flacPrintErroredState(
-                        FLAC__seekable_stream_decoder_get_state(flacDec), 
-                                        dc->file);
-		FLAC__seekable_stream_decoder_delete(flacDec);
-		return -1;
-	}
-
-	dc->state = DECODE_STATE_DECODE;
-
-	while(1) {
-		FLAC__seekable_stream_decoder_process_single(flacDec);
-		if(FLAC__seekable_stream_decoder_get_state(flacDec)!=
-				FLAC__SEEKABLE_STREAM_DECODER_OK)
-		{
-			break;
-		}
-		if(dc->seek) {
-			FLAC__uint64 sampleToSeek = dc->seekWhere*
-					dc->audioFormat.sampleRate+0.5;
-			if(FLAC__seekable_stream_decoder_seek_absolute(flacDec,
-						sampleToSeek))
-			{
-                                clearOutputBuffer(cb);
-				data.time = ((float)sampleToSeek)/
-					dc->audioFormat.sampleRate;
-				data.position = 0;
-			}
-                        else dc->seekError = 1;
-			dc->seek = 0;
-		}
-	}
-	/* I don't think we need this bit here! -shank */
-	/*FLAC__file_decoder_process_until_end_of_file(flacDec);*/
-	if(!dc->stop) {
-		flacPrintErroredState(
-                        FLAC__seekable_stream_decoder_get_state(flacDec), 
-                                        dc->file);
-		FLAC__seekable_stream_decoder_finish(flacDec);
-	}
-	FLAC__seekable_stream_decoder_delete(flacDec);
-	/* send last little bit */
-	if(data.chunk_length>0 && !dc->stop) {
-		flacSendChunk(&data);
-		flushOutputBuffer(data.cb);
-	}
-
-	/*if(dc->seek) {
-                dc->seekError = 1;
-                dc->seek = 0;
-        } */
-	
-	if(dc->stop) {
-		dc->state = DECODE_STATE_STOP;
-		dc->stop = 0;
-	}
-	else dc->state = DECODE_STATE_STOP;
-
-	return 0;
-}
-
-FLAC__SeekableStreamDecoderReadStatus flacRead(
-                const FLAC__SeekableStreamDecoder * flacDec, FLAC__byte buf[],
-                unsigned * bytes, void * fdata) {
-	FlacData * data = (FlacData *) fdata;
-
-        *bytes = readFromInputStream(&(data->inStream),(void *)buf,1,*bytes);
-
-        if(*bytes==0) return FLAC__SEEKABLE_STREAM_DECODER_READ_STATUS_ERROR;
-        
-        return FLAC__SEEKABLE_STREAM_DECODER_READ_STATUS_OK;
-}
-
-FLAC__SeekableStreamDecoderSeekStatus flacSeek(
-                const FLAC__SeekableStreamDecoder * flacDec, 
-                FLAC__uint64 offset, void * fdata) 
-{
-	FlacData * data = (FlacData *) fdata;
-
-        if(seekInputStream(&(data->inStream),offset,SEEK_SET)<0) {
-                return FLAC__SEEKABLE_STREAM_DECODER_SEEK_STATUS_ERROR;
-        }
-
-        return FLAC__SEEKABLE_STREAM_DECODER_SEEK_STATUS_OK;
-}
-
-FLAC__SeekableStreamDecoderTellStatus flacTell(
-                const FLAC__SeekableStreamDecoder * flacDec, 
-                FLAC__uint64 * offset, void * fdata) 
-{
-	FlacData * data = (FlacData *) fdata;
-
-        *offset = data->inStream.offset;
-
-        return FLAC__SEEKABLE_STREAM_DECODER_TELL_STATUS_OK;
-}
-
-FLAC__SeekableStreamDecoderLengthStatus flacLength(
-                const FLAC__SeekableStreamDecoder * flacDec, 
-                FLAC__uint64 * length, void * fdata)
-{
-	FlacData * data = (FlacData *) fdata;
-
-        *length = data->inStream.size;
-
-        return FLAC__SEEKABLE_STREAM_DECODER_LENGTH_STATUS_OK;
-}
-
-FLAC__bool flacEOF(const FLAC__SeekableStreamDecoder * flacDec, void * fdata) {
-	FlacData * data = (FlacData *) fdata;
-
-        switch(inputStreamAtEOF(&(data->inStream))) {
-        case 1:
-                return true;
-        default:
-                return false;
-        }
-}
-
-void flacError(const FLAC__SeekableStreamDecoder *dec, 
-                FLAC__StreamDecoderErrorStatus status, void *fdata) 
-{
-	FlacData * data = (FlacData *) fdata;
-	if(data->dc->stop) return;
-
-	switch(status) {
-	case FLAC__STREAM_DECODER_ERROR_STATUS_LOST_SYNC:
-		ERROR("flac lost sync: %s\n",data->dc->file);
-		break;
-	case FLAC__STREAM_DECODER_ERROR_STATUS_BAD_HEADER:
-		ERROR("bad header %s\n",data->dc->file);
-		break;
-	case FLAC__STREAM_DECODER_ERROR_STATUS_FRAME_CRC_MISMATCH:
-		ERROR("crc mismatch %s\n",data->dc->file);
-		break;
-	default:
-		ERROR("unknow flac error %s\n",data->dc->file);
-	}
-}
-
-void flacPrintErroredState(FLAC__SeekableStreamDecoderState state, 
-                char * file) 
-{
-	switch(state) {
-	case FLAC__SEEKABLE_STREAM_DECODER_MEMORY_ALLOCATION_ERROR:
-		ERROR("flac allocation error\n");
-		break;
-	case FLAC__SEEKABLE_STREAM_DECODER_READ_ERROR:
-		ERROR("flac read error: %s\n",file);
-		break;
-	case FLAC__SEEKABLE_STREAM_DECODER_SEEK_ERROR:
-		ERROR("flac seek error: %s\n",file);
-		break;
-	case FLAC__SEEKABLE_STREAM_DECODER_STREAM_DECODER_ERROR:
-		ERROR("flac seekable stream error: %s\n",file);
-		break;
-	case FLAC__SEEKABLE_STREAM_DECODER_ALREADY_INITIALIZED:
-		ERROR("flac decoder already initilaized: %s\n",file);
-		break;
-	case FLAC__SEEKABLE_STREAM_DECODER_INVALID_CALLBACK:
-		ERROR("invalid flac callback\n");
-		break;
-	case FLAC__SEEKABLE_STREAM_DECODER_UNINITIALIZED:
-		ERROR("flac decoder uninitialized: %s\n",file);
-		break;
-	case FLAC__SEEKABLE_STREAM_DECODER_OK:
-	case FLAC__SEEKABLE_STREAM_DECODER_SEEKING:
-	case FLAC__SEEKABLE_STREAM_DECODER_END_OF_STREAM:
-		break;
-	}
-}
-
-int flacFindVorbisCommentFloat(const FLAC__StreamMetadata * block, char * cmnt, 
-                float * fl)
-{
-        int offset = FLAC__metadata_object_vorbiscomment_find_entry_from(
-                        block,0,cmnt);
-
-        if(offset >= 0) {
-                int pos = strlen(cmnt)+1; /* 1 is for '=' */
-                int len = block->data.vorbis_comment.comments[offset].length
-                                -pos;
-                if(len > 0) {
-                        char * dup = malloc(len+1);
-                        memcpy(dup,&(block->data.vorbis_comment.comments[offset].entry[pos]),len);
-                        dup[len] = '\0';
-                        *fl = atof(dup);
-                        free(dup);
-                        return 1;
-                }
-        }
-
-        return 0;
-}
-
-/* replaygain stuff by AliasMrJones */
-void flacParseReplayGain(const FLAC__StreamMetadata *block, FlacData * data) {
-        int found;
-        float gain = 0.0;
-        float peak = 0.0;
-        int state = getReplayGainState();
-
-        if(state == REPLAYGAIN_OFF) return;
-
-        found = flacFindVorbisCommentFloat(block,"replaygain_album_gain",&gain);
-        if(found) {
-                flacFindVorbisCommentFloat(block,"replaygain_album_peak",
-                                &peak);
-        }
-
-        if(!found || state == REPLAYGAIN_TRACK) {
-                if(flacFindVorbisCommentFloat(block,"replaygain_track_gain",
-                                &gain))
-                {
-                        peak = 0.0;
-                        flacFindVorbisCommentFloat(block,
-                                        "replaygain_track_peak",&peak);
-                }
-        }
-
-        data->replayGainScale = computeReplayGainScale(gain,peak);
-}
-
-void flacMetadata(const FLAC__SeekableStreamDecoder *dec, 
-                const FLAC__StreamMetadata *block, void *vdata) 
-{
-	FlacData * data = (FlacData *)vdata;
-
-        switch(block->type) {
-        case FLAC__METADATA_TYPE_STREAMINFO:
-		data->dc->audioFormat.bits = 
-                                block->data.stream_info.bits_per_sample;
-		data->dc->audioFormat.sampleRate = 
-                                block->data.stream_info.sample_rate;
-		data->dc->audioFormat.channels = 
-                                block->data.stream_info.channels;
-		data->dc->totalTime = 
-                                ((float)block->data.stream_info.total_samples)/
-			        data->dc->audioFormat.sampleRate;
-                getOutputAudioFormat(&(data->dc->audioFormat),
-                                &(data->cb->audioFormat));
-                break;
-        case FLAC__METADATA_TYPE_VORBIS_COMMENT:
-                flacParseReplayGain(block,data);
-        default:
-                break;
-        }
-}
-
-int flacSendChunk(FlacData * data) {
-        doReplayGain(data->chunk,data->chunk_length,&(data->dc->audioFormat),
-                        data->replayGainScale);
-
-	switch(sendDataToOutputBuffer(data->cb, NULL, data->dc, 1, data->chunk,
-			data->chunk_length, data->time, data->bitRate)) 
-	{
-	case OUTPUT_BUFFER_DC_STOP:
-		return -1;
-	default:
-		return 0;
-	}
-
-	return 0;
-}
-
-FLAC__StreamDecoderWriteStatus flacWrite(const FLAC__SeekableStreamDecoder *dec,
-                const FLAC__Frame *frame, const FLAC__int32 * const buf[], 
-                void * vdata) 
-{
-	FlacData * data = (FlacData *)vdata;
-	FLAC__uint32 samples = frame->header.blocksize;
-	FLAC__uint16 u16;
-	unsigned char * uc;
-	int c_samp, c_chan, d_samp;
-	int i;
-	float timeChange;
-	FLAC__uint64 newPosition = 0;
-	
-	timeChange = ((float)samples)/frame->header.sample_rate;
-	data->time+= timeChange;
-
-	FLAC__seekable_stream_decoder_get_decode_position(dec,&newPosition);
-	if(data->position) {
-		data->bitRate = ((newPosition-data->position)*8.0/timeChange)
-				/1000+0.5;
-	}
-	data->position = newPosition;
-
-	for(c_samp = d_samp = 0; c_samp < frame->header.blocksize; c_samp++) {
-		for(c_chan = 0; c_chan < frame->header.channels; 
-				c_chan++, d_samp++) {
-			u16 = buf[c_chan][c_samp];
-			uc = (unsigned char *)&u16;
-			for(i=0;i<(data->dc->audioFormat.bits/8);i++) {
-				if(data->chunk_length>=FLAC_CHUNK_SIZE) {
-					if(flacSendChunk(data)<0) {
-						return FLAC__STREAM_DECODER_WRITE_STATUS_ABORT;
-					}
-					data->chunk_length = 0;
-					if(data->dc->seek) {
-						return FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE;
-					}
-				}
-				data->chunk[data->chunk_length++] = *(uc++);
-			}
-		}
-	}
-
-	return FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE;
-}
-
-#endif
-/* vim:set shiftwidth=8 tabstop=8 expandtab: */
diff --git a/src/flac_decode.h b/src/flac_decode.h
deleted file mode 100644
index c7956ca12..000000000
--- a/src/flac_decode.h
+++ /dev/null
@@ -1,31 +0,0 @@
-/* the Music Player Daemon (MPD)
- * (c)2003-2004 by Warren Dukes (shank@mercury.chem.pitt.edu)
- * This project's homepage is: http://www.musicpd.org
- *
- * 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
- */
-
-#ifndef FLAC_DECODE_H
-#define FLAC_DECODE_H
-
-#include "../config.h"
-
-#include "playerData.h"
-
-#include <stdio.h>
-
-int flac_decode(OutputBuffer * cb, DecoderControl * dc);
-
-#endif
-/* vim:set shiftwidth=8 tabstop=8 expandtab: */
diff --git a/src/inputPlugin.c b/src/inputPlugin.c
index d90a0e944..325ebfcfd 100644
--- a/src/inputPlugin.c
+++ b/src/inputPlugin.c
@@ -69,6 +69,7 @@ InputPlugin * getInputPluginFromName(char * name) {
 
 extern InputPlugin mp3Plugin;
 extern InputPlugin oggPlugin;
+extern InputPlugin flacPlugin;
 
 void initInputPlugins() {
 	inputPlugin_list = makeList(NULL);
@@ -76,6 +77,7 @@ void initInputPlugins() {
 	/* load plugins here */
 	loadInputPlugin(&mp3Plugin);
 	loadInputPlugin(&oggPlugin);
+	loadInputPlugin(&flacPlugin);
 }
 
 void finishInputPlugins() {
diff --git a/src/inputPlugins/flac_plugin.c b/src/inputPlugins/flac_plugin.c
new file mode 100644
index 000000000..23e2bcfea
--- /dev/null
+++ b/src/inputPlugins/flac_plugin.c
@@ -0,0 +1,580 @@
+/* the Music Player Daemon (MPD)
+ * (c)2003-2004 by Warren Dukes (shank@mercury.chem.pitt.edu)
+ * This project's homepage is: http://www.musicpd.org
+ *
+ * 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 "../inputPlugin.h"
+
+#ifdef HAVE_FLAC
+
+#include "../utils.h"
+#include "../log.h"
+#include "../pcm_utils.h"
+#include "../inputStream.h"
+#include "../outputBuffer.h"
+#include "../replayGain.h"
+#include "../audio.h"
+#include "../path.h"
+
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <FLAC/seekable_stream_decoder.h>
+#include <FLAC/metadata.h>
+
+typedef struct {
+#define FLAC_CHUNK_SIZE 4080
+	unsigned char chunk[FLAC_CHUNK_SIZE];
+	int chunk_length;
+	float time;
+	int bitRate;
+	FLAC__uint64 position;
+	OutputBuffer * cb;
+	DecoderControl * dc;
+        InputStream inStream;
+        float replayGainScale;
+} FlacData;
+
+/* this code is based on flac123, from flac-tools */
+
+int flacSendChunk(FlacData * data);
+void flacError(const FLAC__SeekableStreamDecoder *, 
+                FLAC__StreamDecoderErrorStatus, void *);
+void flacPrintErroredState(FLAC__SeekableStreamDecoderState state, char * file);
+void flacMetadata(const FLAC__SeekableStreamDecoder *, 
+                const FLAC__StreamMetadata *, void *);
+FLAC__StreamDecoderWriteStatus flacWrite(const FLAC__SeekableStreamDecoder *,
+                const FLAC__Frame *, const FLAC__int32 * const buf[], void *);
+FLAC__SeekableStreamDecoderReadStatus flacRead(
+                const FLAC__SeekableStreamDecoder *, FLAC__byte buf[],
+                unsigned *, void *);
+FLAC__SeekableStreamDecoderSeekStatus flacSeek(
+                const FLAC__SeekableStreamDecoder *, FLAC__uint64, void *);
+FLAC__SeekableStreamDecoderTellStatus flacTell(
+                const FLAC__SeekableStreamDecoder *, FLAC__uint64 *, void *);
+FLAC__SeekableStreamDecoderLengthStatus flacLength(
+                const FLAC__SeekableStreamDecoder *, FLAC__uint64 *, void *);
+FLAC__bool flacEOF(const FLAC__SeekableStreamDecoder *, void *);
+
+int flac_decode(OutputBuffer * cb, DecoderControl *dc) {
+	FLAC__SeekableStreamDecoder * flacDec;
+	FlacData data;
+	int status = 1;
+
+	data.chunk_length = 0;
+	data.time = 0;
+	data.position = 0;
+	data.bitRate = 0;
+	data.cb = cb;
+	data.dc = dc;
+        data.replayGainScale = 1.0;
+
+        if(openInputStream(&(data.inStream),dc->file)<0) {
+                ERROR("unable to open flac: %s\n",dc->file);
+                return -1;
+        }
+	
+	if(!(flacDec = FLAC__seekable_stream_decoder_new())) return -1;
+	/*status&=FLAC__file_decoder_set_md5_checking(flacDec,1);*/
+	status&=FLAC__seekable_stream_decoder_set_read_callback(flacDec,
+                        flacRead);
+	status&=FLAC__seekable_stream_decoder_set_seek_callback(flacDec,
+                        flacSeek);
+	status&=FLAC__seekable_stream_decoder_set_tell_callback(flacDec,
+                        flacTell);
+	status&=FLAC__seekable_stream_decoder_set_length_callback(flacDec,
+                        flacLength);
+	status&=FLAC__seekable_stream_decoder_set_eof_callback(flacDec,flacEOF);
+	status&=FLAC__seekable_stream_decoder_set_write_callback(flacDec,
+                        flacWrite);
+	status&=FLAC__seekable_stream_decoder_set_metadata_callback(flacDec,
+                        flacMetadata);
+	status&=FLAC__seekable_stream_decoder_set_error_callback(flacDec,
+                        flacError);
+	status&=FLAC__seekable_stream_decoder_set_client_data(flacDec,
+                        (void *)&data);
+	if(!status) {
+		ERROR("flac problem before init(): %s\n",dc->file);
+		flacPrintErroredState(
+                        FLAC__seekable_stream_decoder_get_state(flacDec),
+                                        dc->file);
+		FLAC__seekable_stream_decoder_delete(flacDec);
+		return -1;
+	}
+
+	if(FLAC__seekable_stream_decoder_init(flacDec)!=
+			FLAC__STREAM_DECODER_SEARCH_FOR_METADATA) 
+	{
+		ERROR("flac problem doing init(): %s\n",dc->file);
+		flacPrintErroredState(
+                        FLAC__seekable_stream_decoder_get_state(flacDec),
+                                        dc->file);
+		FLAC__seekable_stream_decoder_delete(flacDec);
+		return -1;
+	}
+
+	if(!FLAC__seekable_stream_decoder_process_until_end_of_metadata(flacDec)) {
+		ERROR("flac problem reading metadata: %s\n", dc->file);
+		flacPrintErroredState(
+                        FLAC__seekable_stream_decoder_get_state(flacDec), 
+                                        dc->file);
+		FLAC__seekable_stream_decoder_delete(flacDec);
+		return -1;
+	}
+
+	dc->state = DECODE_STATE_DECODE;
+
+	while(1) {
+		FLAC__seekable_stream_decoder_process_single(flacDec);
+		if(FLAC__seekable_stream_decoder_get_state(flacDec)!=
+				FLAC__SEEKABLE_STREAM_DECODER_OK)
+		{
+			break;
+		}
+		if(dc->seek) {
+			FLAC__uint64 sampleToSeek = dc->seekWhere*
+					dc->audioFormat.sampleRate+0.5;
+			if(FLAC__seekable_stream_decoder_seek_absolute(flacDec,
+						sampleToSeek))
+			{
+                                clearOutputBuffer(cb);
+				data.time = ((float)sampleToSeek)/
+					dc->audioFormat.sampleRate;
+				data.position = 0;
+			}
+                        else dc->seekError = 1;
+			dc->seek = 0;
+		}
+	}
+	/* I don't think we need this bit here! -shank */
+	/*FLAC__file_decoder_process_until_end_of_file(flacDec);*/
+	if(!dc->stop) {
+		flacPrintErroredState(
+                        FLAC__seekable_stream_decoder_get_state(flacDec), 
+                                        dc->file);
+		FLAC__seekable_stream_decoder_finish(flacDec);
+	}
+	FLAC__seekable_stream_decoder_delete(flacDec);
+	/* send last little bit */
+	if(data.chunk_length>0 && !dc->stop) {
+		flacSendChunk(&data);
+		flushOutputBuffer(data.cb);
+	}
+
+	/*if(dc->seek) {
+                dc->seekError = 1;
+                dc->seek = 0;
+        } */
+	
+	if(dc->stop) {
+		dc->state = DECODE_STATE_STOP;
+		dc->stop = 0;
+	}
+	else dc->state = DECODE_STATE_STOP;
+
+	return 0;
+}
+
+FLAC__SeekableStreamDecoderReadStatus flacRead(
+                const FLAC__SeekableStreamDecoder * flacDec, FLAC__byte buf[],
+                unsigned * bytes, void * fdata) {
+	FlacData * data = (FlacData *) fdata;
+
+        *bytes = readFromInputStream(&(data->inStream),(void *)buf,1,*bytes);
+
+        if(*bytes==0) return FLAC__SEEKABLE_STREAM_DECODER_READ_STATUS_ERROR;
+        
+        return FLAC__SEEKABLE_STREAM_DECODER_READ_STATUS_OK;
+}
+
+FLAC__SeekableStreamDecoderSeekStatus flacSeek(
+                const FLAC__SeekableStreamDecoder * flacDec, 
+                FLAC__uint64 offset, void * fdata) 
+{
+	FlacData * data = (FlacData *) fdata;
+
+        if(seekInputStream(&(data->inStream),offset,SEEK_SET)<0) {
+                return FLAC__SEEKABLE_STREAM_DECODER_SEEK_STATUS_ERROR;
+        }
+
+        return FLAC__SEEKABLE_STREAM_DECODER_SEEK_STATUS_OK;
+}
+
+FLAC__SeekableStreamDecoderTellStatus flacTell(
+                const FLAC__SeekableStreamDecoder * flacDec, 
+                FLAC__uint64 * offset, void * fdata) 
+{
+	FlacData * data = (FlacData *) fdata;
+
+        *offset = data->inStream.offset;
+
+        return FLAC__SEEKABLE_STREAM_DECODER_TELL_STATUS_OK;
+}
+
+FLAC__SeekableStreamDecoderLengthStatus flacLength(
+                const FLAC__SeekableStreamDecoder * flacDec, 
+                FLAC__uint64 * length, void * fdata)
+{
+	FlacData * data = (FlacData *) fdata;
+
+        *length = data->inStream.size;
+
+        return FLAC__SEEKABLE_STREAM_DECODER_LENGTH_STATUS_OK;
+}
+
+FLAC__bool flacEOF(const FLAC__SeekableStreamDecoder * flacDec, void * fdata) {
+	FlacData * data = (FlacData *) fdata;
+
+        switch(inputStreamAtEOF(&(data->inStream))) {
+        case 1:
+                return true;
+        default:
+                return false;
+        }
+}
+
+void flacError(const FLAC__SeekableStreamDecoder *dec, 
+                FLAC__StreamDecoderErrorStatus status, void *fdata) 
+{
+	FlacData * data = (FlacData *) fdata;
+	if(data->dc->stop) return;
+
+	switch(status) {
+	case FLAC__STREAM_DECODER_ERROR_STATUS_LOST_SYNC:
+		ERROR("flac lost sync: %s\n",data->dc->file);
+		break;
+	case FLAC__STREAM_DECODER_ERROR_STATUS_BAD_HEADER:
+		ERROR("bad header %s\n",data->dc->file);
+		break;
+	case FLAC__STREAM_DECODER_ERROR_STATUS_FRAME_CRC_MISMATCH:
+		ERROR("crc mismatch %s\n",data->dc->file);
+		break;
+	default:
+		ERROR("unknow flac error %s\n",data->dc->file);
+	}
+}
+
+void flacPrintErroredState(FLAC__SeekableStreamDecoderState state, 
+                char * file) 
+{
+	switch(state) {
+	case FLAC__SEEKABLE_STREAM_DECODER_MEMORY_ALLOCATION_ERROR:
+		ERROR("flac allocation error\n");
+		break;
+	case FLAC__SEEKABLE_STREAM_DECODER_READ_ERROR:
+		ERROR("flac read error: %s\n",file);
+		break;
+	case FLAC__SEEKABLE_STREAM_DECODER_SEEK_ERROR:
+		ERROR("flac seek error: %s\n",file);
+		break;
+	case FLAC__SEEKABLE_STREAM_DECODER_STREAM_DECODER_ERROR:
+		ERROR("flac seekable stream error: %s\n",file);
+		break;
+	case FLAC__SEEKABLE_STREAM_DECODER_ALREADY_INITIALIZED:
+		ERROR("flac decoder already initilaized: %s\n",file);
+		break;
+	case FLAC__SEEKABLE_STREAM_DECODER_INVALID_CALLBACK:
+		ERROR("invalid flac callback\n");
+		break;
+	case FLAC__SEEKABLE_STREAM_DECODER_UNINITIALIZED:
+		ERROR("flac decoder uninitialized: %s\n",file);
+		break;
+	case FLAC__SEEKABLE_STREAM_DECODER_OK:
+	case FLAC__SEEKABLE_STREAM_DECODER_SEEKING:
+	case FLAC__SEEKABLE_STREAM_DECODER_END_OF_STREAM:
+		break;
+	}
+}
+
+int flacFindVorbisCommentFloat(const FLAC__StreamMetadata * block, char * cmnt, 
+                float * fl)
+{
+        int offset = FLAC__metadata_object_vorbiscomment_find_entry_from(
+                        block,0,cmnt);
+
+        if(offset >= 0) {
+                int pos = strlen(cmnt)+1; /* 1 is for '=' */
+                int len = block->data.vorbis_comment.comments[offset].length
+                                -pos;
+                if(len > 0) {
+                        char * dup = malloc(len+1);
+                        memcpy(dup,&(block->data.vorbis_comment.comments[offset].entry[pos]),len);
+                        dup[len] = '\0';
+                        *fl = atof(dup);
+                        free(dup);
+                        return 1;
+                }
+        }
+
+        return 0;
+}
+
+/* replaygain stuff by AliasMrJones */
+void flacParseReplayGain(const FLAC__StreamMetadata *block, FlacData * data) {
+        int found;
+        float gain = 0.0;
+        float peak = 0.0;
+        int state = getReplayGainState();
+
+        if(state == REPLAYGAIN_OFF) return;
+
+        found = flacFindVorbisCommentFloat(block,"replaygain_album_gain",&gain);
+        if(found) {
+                flacFindVorbisCommentFloat(block,"replaygain_album_peak",
+                                &peak);
+        }
+
+        if(!found || state == REPLAYGAIN_TRACK) {
+                if(flacFindVorbisCommentFloat(block,"replaygain_track_gain",
+                                &gain))
+                {
+                        peak = 0.0;
+                        flacFindVorbisCommentFloat(block,
+                                        "replaygain_track_peak",&peak);
+                }
+        }
+
+        data->replayGainScale = computeReplayGainScale(gain,peak);
+}
+
+void flacMetadata(const FLAC__SeekableStreamDecoder *dec, 
+                const FLAC__StreamMetadata *block, void *vdata) 
+{
+	FlacData * data = (FlacData *)vdata;
+
+        switch(block->type) {
+        case FLAC__METADATA_TYPE_STREAMINFO:
+		data->dc->audioFormat.bits = 
+                                block->data.stream_info.bits_per_sample;
+		data->dc->audioFormat.sampleRate = 
+                                block->data.stream_info.sample_rate;
+		data->dc->audioFormat.channels = 
+                                block->data.stream_info.channels;
+		data->dc->totalTime = 
+                                ((float)block->data.stream_info.total_samples)/
+			        data->dc->audioFormat.sampleRate;
+                getOutputAudioFormat(&(data->dc->audioFormat),
+                                &(data->cb->audioFormat));
+                break;
+        case FLAC__METADATA_TYPE_VORBIS_COMMENT:
+                flacParseReplayGain(block,data);
+        default:
+                break;
+        }
+}
+
+int flacSendChunk(FlacData * data) {
+        doReplayGain(data->chunk,data->chunk_length,&(data->dc->audioFormat),
+                        data->replayGainScale);
+
+	switch(sendDataToOutputBuffer(data->cb, NULL, data->dc, 1, data->chunk,
+			data->chunk_length, data->time, data->bitRate)) 
+	{
+	case OUTPUT_BUFFER_DC_STOP:
+		return -1;
+	default:
+		return 0;
+	}
+
+	return 0;
+}
+
+FLAC__StreamDecoderWriteStatus flacWrite(const FLAC__SeekableStreamDecoder *dec,
+                const FLAC__Frame *frame, const FLAC__int32 * const buf[], 
+                void * vdata) 
+{
+	FlacData * data = (FlacData *)vdata;
+	FLAC__uint32 samples = frame->header.blocksize;
+	FLAC__uint16 u16;
+	unsigned char * uc;
+	int c_samp, c_chan, d_samp;
+	int i;
+	float timeChange;
+	FLAC__uint64 newPosition = 0;
+	
+	timeChange = ((float)samples)/frame->header.sample_rate;
+	data->time+= timeChange;
+
+	FLAC__seekable_stream_decoder_get_decode_position(dec,&newPosition);
+	if(data->position) {
+		data->bitRate = ((newPosition-data->position)*8.0/timeChange)
+				/1000+0.5;
+	}
+	data->position = newPosition;
+
+	for(c_samp = d_samp = 0; c_samp < frame->header.blocksize; c_samp++) {
+		for(c_chan = 0; c_chan < frame->header.channels; 
+				c_chan++, d_samp++) {
+			u16 = buf[c_chan][c_samp];
+			uc = (unsigned char *)&u16;
+			for(i=0;i<(data->dc->audioFormat.bits/8);i++) {
+				if(data->chunk_length>=FLAC_CHUNK_SIZE) {
+					if(flacSendChunk(data)<0) {
+						return FLAC__STREAM_DECODER_WRITE_STATUS_ABORT;
+					}
+					data->chunk_length = 0;
+					if(data->dc->seek) {
+						return FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE;
+					}
+				}
+				data->chunk[data->chunk_length++] = *(uc++);
+			}
+		}
+	}
+
+	return FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE;
+}
+
+MpdTag * flacMetadataDup(char * utf8file, int * vorbisCommentFound) {
+	MpdTag * ret = NULL;
+	FLAC__Metadata_SimpleIterator * it;
+	FLAC__StreamMetadata * block = NULL;
+	int offset;
+	int len, pos;
+
+	*vorbisCommentFound = 0;
+
+	it = FLAC__metadata_simple_iterator_new();
+	if(!FLAC__metadata_simple_iterator_init(it,rmp2amp(utf8ToFsCharset(utf8file)),1,0)) {
+		FLAC__metadata_simple_iterator_delete(it);
+		return ret;
+	}
+	
+	do {
+		block = FLAC__metadata_simple_iterator_get_block(it);
+		if(!block) break;
+		if(block->type == FLAC__METADATA_TYPE_VORBIS_COMMENT) {
+			char * dup;
+
+			offset = FLAC__metadata_object_vorbiscomment_find_entry_from(block,0,"artist");
+			if(offset>=0) {
+				*vorbisCommentFound = 1;
+				if(!ret) ret = newMpdTag();
+				pos = strlen("artist=");
+				len = block->data.vorbis_comment.comments[offset].length-pos;
+				if(len>0) {
+					dup = malloc(len+1);
+					memcpy(dup,&(block->data.vorbis_comment.comments[offset].entry[pos]),len);
+					dup[len] = '\0';
+					stripReturnChar(dup);
+					ret->artist = dup;
+				}
+			}
+			offset = FLAC__metadata_object_vorbiscomment_find_entry_from(block,0,"album");
+			if(offset>=0) {
+				*vorbisCommentFound = 1;
+				if(!ret) ret = newMpdTag();
+				pos = strlen("album=");
+				len = block->data.vorbis_comment.comments[offset].length-pos;
+				if(len>0) {
+					dup = malloc(len+1);
+					memcpy(dup,&(block->data.vorbis_comment.comments[offset].entry[pos]),len);
+					dup[len] = '\0';
+					stripReturnChar(dup);
+					ret->album = dup;
+				}
+			}
+			offset = FLAC__metadata_object_vorbiscomment_find_entry_from(block,0,"title");
+			if(offset>=0) {
+				*vorbisCommentFound = 1;
+				if(!ret) ret = newMpdTag();
+				pos = strlen("title=");
+				len = block->data.vorbis_comment.comments[offset].length-pos;
+				if(len>0) {
+					dup = malloc(len+1);
+					memcpy(dup,&(block->data.vorbis_comment.comments[offset].entry[pos]),len);
+					dup[len] = '\0';
+					stripReturnChar(dup);
+					ret->title = dup;
+				}
+			}
+			offset = FLAC__metadata_object_vorbiscomment_find_entry_from(block,0,"tracknumber");
+			if(offset>=0) {
+				*vorbisCommentFound = 1;
+				if(!ret) ret = newMpdTag();
+				pos = strlen("tracknumber=");
+				len = block->data.vorbis_comment.comments[offset].length-pos;
+				if(len>0) {
+					dup = malloc(len+1);
+					memcpy(dup,&(block->data.vorbis_comment.comments[offset].entry[pos]),len);
+					dup[len] = '\0';
+					stripReturnChar(dup);
+					ret->track = dup;
+				}
+			}
+		}
+		else if(block->type == FLAC__METADATA_TYPE_STREAMINFO) {
+			if(!ret) ret = newMpdTag();
+			ret->time = ((float)block->data.stream_info.
+					total_samples) /
+					block->data.stream_info.sample_rate +
+					0.5;
+		}
+		FLAC__metadata_object_delete(block);
+	} while(FLAC__metadata_simple_iterator_next(it));
+
+	FLAC__metadata_simple_iterator_delete(it);
+	return ret;
+}
+
+MpdTag * flacTagDup(char * utf8file) {
+	MpdTag * ret = NULL;
+	int foundVorbisComment = 0;
+
+	ret = flacMetadataDup(utf8file,&foundVorbisComment);
+	if(!ret) return NULL;
+	if(!foundVorbisComment) {
+		MpdTag * temp = id3Dup(utf8file);
+		if(temp) {
+			temp->time = ret->time;
+			freeMpdTag(ret);
+			ret = temp;
+		}
+	}
+
+	if(ret) validateUtf8Tag(ret);
+
+	return ret;
+}
+
+char * flacSuffixes[] = {"flac", NULL};
+
+InputPlugin flacPlugin = 
+{
+        "flac",
+        NULL,
+        flac_decode,
+        flacTagDup,
+        INPUT_PLUGIN_STREAM_FILE,
+        flacSuffixes,
+        NULL
+};
+
+#else
+
+InputPlugin flacPlugin =
+{       
+        NULL,
+        NULL,
+        NULL,
+        NULL,
+        0,
+        NULL,
+        NULL,
+};
+
+#endif
+/* vim:set shiftwidth=8 tabstop=8 expandtab: */
diff --git a/src/inputPlugins/mp3_plugin.c b/src/inputPlugins/mp3_plugin.c
index 5d34a1068..594f17876 100644
--- a/src/inputPlugins/mp3_plugin.c
+++ b/src/inputPlugins/mp3_plugin.c
@@ -651,6 +651,7 @@ InputPlugin mp3Plugin =
 	NULL,
 	NULL,
 	NULL,
+	NULL,
 	0,
 	NULL,
 	NULL
diff --git a/src/inputPlugins/ogg_plugin.c b/src/inputPlugins/ogg_plugin.c
index af617378e..4f44af4c2 100644
--- a/src/inputPlugins/ogg_plugin.c
+++ b/src/inputPlugins/ogg_plugin.c
@@ -353,6 +353,7 @@ InputPlugin oggPlugin =
 
 InputPlugin oggPlugin = 
 {
+	NULL,
         NULL,
         NULL,
         NULL,
diff --git a/src/tag.c b/src/tag.c
index c540fa06e..493b1a19d 100644
--- a/src/tag.c
+++ b/src/tag.c
@@ -279,120 +279,6 @@ MpdTag * mp4TagDup(char * utf8file) {
 }
 #endif
 
-#ifdef HAVE_FLAC
-MpdTag * flacMetadataDup(char * utf8file, int * vorbisCommentFound) {
-	MpdTag * ret = NULL;
-	FLAC__Metadata_SimpleIterator * it;
-	FLAC__StreamMetadata * block = NULL;
-	int offset;
-	int len, pos;
-
-	*vorbisCommentFound = 0;
-
-	it = FLAC__metadata_simple_iterator_new();
-	if(!FLAC__metadata_simple_iterator_init(it,rmp2amp(utf8ToFsCharset(utf8file)),1,0)) {
-		FLAC__metadata_simple_iterator_delete(it);
-		return ret;
-	}
-	
-	do {
-		block = FLAC__metadata_simple_iterator_get_block(it);
-		if(!block) break;
-		if(block->type == FLAC__METADATA_TYPE_VORBIS_COMMENT) {
-			char * dup;
-
-			offset = FLAC__metadata_object_vorbiscomment_find_entry_from(block,0,"artist");
-			if(offset>=0) {
-				*vorbisCommentFound = 1;
-				if(!ret) ret = newMpdTag();
-				pos = strlen("artist=");
-				len = block->data.vorbis_comment.comments[offset].length-pos;
-				if(len>0) {
-					dup = malloc(len+1);
-					memcpy(dup,&(block->data.vorbis_comment.comments[offset].entry[pos]),len);
-					dup[len] = '\0';
-					stripReturnChar(dup);
-					ret->artist = dup;
-				}
-			}
-			offset = FLAC__metadata_object_vorbiscomment_find_entry_from(block,0,"album");
-			if(offset>=0) {
-				*vorbisCommentFound = 1;
-				if(!ret) ret = newMpdTag();
-				pos = strlen("album=");
-				len = block->data.vorbis_comment.comments[offset].length-pos;
-				if(len>0) {
-					dup = malloc(len+1);
-					memcpy(dup,&(block->data.vorbis_comment.comments[offset].entry[pos]),len);
-					dup[len] = '\0';
-					stripReturnChar(dup);
-					ret->album = dup;
-				}
-			}
-			offset = FLAC__metadata_object_vorbiscomment_find_entry_from(block,0,"title");
-			if(offset>=0) {
-				*vorbisCommentFound = 1;
-				if(!ret) ret = newMpdTag();
-				pos = strlen("title=");
-				len = block->data.vorbis_comment.comments[offset].length-pos;
-				if(len>0) {
-					dup = malloc(len+1);
-					memcpy(dup,&(block->data.vorbis_comment.comments[offset].entry[pos]),len);
-					dup[len] = '\0';
-					stripReturnChar(dup);
-					ret->title = dup;
-				}
-			}
-			offset = FLAC__metadata_object_vorbiscomment_find_entry_from(block,0,"tracknumber");
-			if(offset>=0) {
-				*vorbisCommentFound = 1;
-				if(!ret) ret = newMpdTag();
-				pos = strlen("tracknumber=");
-				len = block->data.vorbis_comment.comments[offset].length-pos;
-				if(len>0) {
-					dup = malloc(len+1);
-					memcpy(dup,&(block->data.vorbis_comment.comments[offset].entry[pos]),len);
-					dup[len] = '\0';
-					stripReturnChar(dup);
-					ret->track = dup;
-				}
-			}
-		}
-		else if(block->type == FLAC__METADATA_TYPE_STREAMINFO) {
-			if(!ret) ret = newMpdTag();
-			ret->time = ((float)block->data.stream_info.
-					total_samples) /
-					block->data.stream_info.sample_rate +
-					0.5;
-		}
-		FLAC__metadata_object_delete(block);
-	} while(FLAC__metadata_simple_iterator_next(it));
-
-	FLAC__metadata_simple_iterator_delete(it);
-	return ret;
-}
-
-MpdTag * flacTagDup(char * utf8file) {
-	MpdTag * ret = NULL;
-	int foundVorbisComment = 0;
-
-	ret = flacMetadataDup(utf8file,&foundVorbisComment);
-	if(!ret) return NULL;
-	if(!foundVorbisComment) {
-		MpdTag * temp = id3Dup(utf8file);
-		if(temp) {
-			temp->time = ret->time;
-			freeMpdTag(ret);
-			ret = temp;
-		}
-	}
-
-	if(ret) validateUtf8Tag(ret);
-
-	return ret;
-}
-#endif
-
 MpdTag * newMpdTag() {
 	MpdTag * ret = malloc(sizeof(MpdTag));
 	ret->album = NULL;
-- 
cgit v1.2.3