diff options
Diffstat (limited to '')
-rw-r--r-- | src/inputPlugins/_flac_common.c | 195 | ||||
-rw-r--r-- | src/inputPlugins/_flac_common.h | 77 | ||||
-rw-r--r-- | src/inputPlugins/_ogg_common.c | 65 | ||||
-rw-r--r-- | src/inputPlugins/_ogg_common.h | 35 | ||||
-rw-r--r-- | src/inputPlugins/aac_plugin.c | 2 | ||||
-rw-r--r-- | src/inputPlugins/audiofile_plugin.c | 2 | ||||
-rw-r--r-- | src/inputPlugins/flac_plugin.c | 258 | ||||
-rw-r--r-- | src/inputPlugins/mod_plugin.c | 2 | ||||
-rw-r--r-- | src/inputPlugins/mp3_plugin.c | 2 | ||||
-rw-r--r-- | src/inputPlugins/mp4_plugin.c | 2 | ||||
-rw-r--r-- | src/inputPlugins/mpc_plugin.c | 2 | ||||
-rw-r--r-- | src/inputPlugins/oggflac_plugin.c | 426 | ||||
-rw-r--r-- | src/inputPlugins/oggvorbis_plugin.c (renamed from src/inputPlugins/ogg_plugin.c) | 126 |
13 files changed, 908 insertions, 286 deletions
diff --git a/src/inputPlugins/_flac_common.c b/src/inputPlugins/_flac_common.c new file mode 100644 index 000000000..f6a470ed2 --- /dev/null +++ b/src/inputPlugins/_flac_common.c @@ -0,0 +1,195 @@ +/* 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 + * + * Common data structures and functions used by FLAC and OggFLAC + * (c) 2005 by Eric Wong <normalperson@yhbt.net> + * + * 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" + +#if defined(HAVE_FLAC) || defined(HAVE_OGGFLAC) + +#include "_flac_common.h" + +#include "../log.h" +#include "../tag.h" +#include "../inputStream.h" +#include "../outputBuffer.h" +#include "../decode.h" +#include "../replayGain.h" + +#include <stdio.h> +#include <string.h> +#include <unistd.h> +#include <FLAC/format.h> +#include <FLAC/metadata.h> + +void init_FlacData (FlacData * data, OutputBuffer * cb, + DecoderControl * dc, InputStream * inStream) +{ + data->chunk_length = 0; + data->time = 0; + data->position = 0; + data->bitRate = 0; + data->cb = cb; + data->dc = dc; + data->inStream = inStream; + data->replayGainInfo = NULL; + data->tag = NULL; +} + +static 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) { + size_t pos = strlen(cmnt)+1; /* 1 is for '=' */ + int len = block->data.vorbis_comment.comments[offset].length + -pos; + if(len > 0) { + unsigned char tmp; + unsigned char * dup = &(block->data.vorbis_comment. + comments[offset].entry[pos]); + tmp = dup[len]; + dup[len] = '\0'; + *fl = atof((char *)dup); + dup[len] = tmp; + + return 1; + } + } + + return 0; +} + +/* replaygain stuff by AliasMrJones */ +static void flacParseReplayGain(const FLAC__StreamMetadata *block, + FlacData * data) { + unsigned int found = 0; + + if (data->replayGainInfo) + freeReplayGainInfo(data->replayGainInfo); + + data->replayGainInfo = newReplayGainInfo(); + + found &= flacFindVorbisCommentFloat(block,"replaygain_album_gain", + &data->replayGainInfo->albumGain); + found &= flacFindVorbisCommentFloat(block,"replaygain_album_peak", + &data->replayGainInfo->albumPeak); + found &= flacFindVorbisCommentFloat(block,"replaygain_track_gain", + &data->replayGainInfo->trackGain); + found &= flacFindVorbisCommentFloat(block,"replaygain_track_peak", + &data->replayGainInfo->trackPeak); + + if (!found) { + freeReplayGainInfo(data->replayGainInfo); + data->replayGainInfo = NULL; + } +} + +/* tracknumber is used in VCs, MPD uses "track" ..., all the other + * tag names match */ +static const char * VORBIS_COMMENT_TRACK_KEY = "tracknumber"; + +static unsigned int commentMatchesAddToTag( + const FLAC__StreamMetadata_VorbisComment_Entry * entry, + unsigned int itemType, + MpdTag ** tag) +{ + const char * str = (itemType == TAG_ITEM_TRACK) ? + VORBIS_COMMENT_TRACK_KEY : mpdTagItemKeys[itemType]; + size_t slen = strlen(str); + int vlen = entry->length - slen - 1; + + if ((vlen > 0) && (0 == strncasecmp(str,(char *)entry->entry, slen)) + && (*(entry->entry + slen) == '=')) { + if (!*tag) + *tag = newMpdTag(); + + addItemToMpdTagWithLen(*tag, itemType, + (char *)(entry->entry+slen + 1), vlen); + + return 1; + } + + return 0; +} + +MpdTag * copyVorbisCommentBlockToMpdTag(const FLAC__StreamMetadata * block, + MpdTag * tag) +{ + unsigned int i, j; + FLAC__StreamMetadata_VorbisComment_Entry *comments; + + comments = block->data.vorbis_comment.comments; + + for (i = block->data.vorbis_comment.num_comments; i != 0; --i) { + for (j = TAG_NUM_OF_ITEM_TYPES; j--; ) { + if (commentMatchesAddToTag(comments, j, &tag)) + break; + } + comments++; + } + + return tag; +} + +void flac_metadata_common_cb(const FLAC__StreamMetadata *block, FlacData *data) +{ + DecoderControl *dc = data->dc; + const FLAC__StreamMetadata_StreamInfo *si = &(block->data.stream_info); + + switch(block->type) { + case FLAC__METADATA_TYPE_STREAMINFO: + dc->audioFormat.bits = si->bits_per_sample; + dc->audioFormat.sampleRate = si->sample_rate; + dc->audioFormat.channels = si->channels; + dc->totalTime = ((float)si->total_samples) / (si->sample_rate); + getOutputAudioFormat(&(dc->audioFormat), + &(data->cb->audioFormat)); + break; + case FLAC__METADATA_TYPE_VORBIS_COMMENT: + flacParseReplayGain(block,data); + default: + break; + } +} + +void flac_error_common_cb( const char * plugin, + const FLAC__StreamDecoderErrorStatus status, + FlacData *data) +{ + if(data->dc->stop) return; + + switch(status) { + case FLAC__STREAM_DECODER_ERROR_STATUS_LOST_SYNC: + ERROR("%s lost sync\n", plugin); + break; + case FLAC__STREAM_DECODER_ERROR_STATUS_BAD_HEADER: + ERROR("bad %s header\n", plugin); + break; + case FLAC__STREAM_DECODER_ERROR_STATUS_FRAME_CRC_MISMATCH: + ERROR("%s crc mismatch\n", plugin); + break; + default: + ERROR("unknown %s error\n",plugin); + } +} + +#endif /* HAVE_FLAC || HAVE_OGGFLAC */ diff --git a/src/inputPlugins/_flac_common.h b/src/inputPlugins/_flac_common.h new file mode 100644 index 000000000..830d3bfc0 --- /dev/null +++ b/src/inputPlugins/_flac_common.h @@ -0,0 +1,77 @@ +/* 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 + * + * Common data structures and functions used by FLAC and OggFLAC + * (c) 2005 by Eric Wong <normalperson@yhbt.net> + * + * 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_COMMON_H +#define _FLAC_COMMON_H + +#include "../inputPlugin.h" + +#if defined(HAVE_FLAC) || defined(HAVE_OGGFLAC) + +#include "../tag.h" +#include "../inputStream.h" +#include "../outputBuffer.h" +#include "../decode.h" +#include <FLAC/seekable_stream_decoder.h> +#include <FLAC/metadata.h> + +#define FLAC_CHUNK_SIZE 4080 + +typedef struct { + unsigned char chunk[FLAC_CHUNK_SIZE]; + int chunk_length; + float time; + int bitRate; + FLAC__uint64 position; + OutputBuffer * cb; + DecoderControl * dc; + InputStream * inStream; + ReplayGainInfo * replayGainInfo; + MpdTag * tag; +} FlacData; + +/* initializes a given FlacData struct */ +void init_FlacData (FlacData * data, OutputBuffer * cb, + DecoderControl * dc, InputStream * inStream); +void flac_metadata_common_cb( const FLAC__StreamMetadata *block, + FlacData *data); +void flac_error_common_cb( const char * plugin, + FLAC__StreamDecoderErrorStatus status, + FlacData *data); + +MpdTag * copyVorbisCommentBlockToMpdTag(const FLAC__StreamMetadata * block, + MpdTag * tag); + +static inline int flacSendChunk(FlacData * data) +{ + if (sendDataToOutputBuffer(data->cb, NULL, data->dc, 1, data->chunk, + data->chunk_length, data->time, data->bitRate, + data->replayGainInfo) == OUTPUT_BUFFER_DC_STOP) + return -1; + + return 0; +} + + +#endif /* HAVE_FLAC || HAVE_OGGFLAC */ + +#endif /* _FLAC_COMMON_H */ + diff --git a/src/inputPlugins/_ogg_common.c b/src/inputPlugins/_ogg_common.c new file mode 100644 index 000000000..fb0bb19e0 --- /dev/null +++ b/src/inputPlugins/_ogg_common.c @@ -0,0 +1,65 @@ +/* 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 + * + * Common functions used for Ogg data streams (Ogg-Vorbis and OggFLAC) + * (c) 2005 by Eric Wong <normalperson@yhbt.net> + * + * 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" + +#if defined(HAVE_OGGFLAC) || defined(HAVE_OGGVORBIS) + +#include "../utils.h" +#include "_ogg_common.h" + +#include <string.h> + +ogg_stream_type ogg_stream_type_detect(InputStream * inStream) +{ + /* oggflac detection based on code in ogg123 and this post + * http://lists.xiph.org/pipermail/flac/2004-December/000393.html + * ogg123 trunk still doesn't have this patch as of June 2005 */ + unsigned char buf[41]; + size_t r, to_read = 41; + + seekInputStream(inStream, 0, SEEK_SET); + + while (to_read) { + r = readFromInputStream(inStream, buf, 1, to_read); + if (r < 0) + break; + to_read -= r; + if (!r && !inputStreamAtEOF(inStream)) + my_usleep(10000); + else + break; + } + + seekInputStream(inStream, 0, SEEK_SET); + + if (r >= 32 && memcmp(buf, "OggS", 4) == 0 && ( + (memcmp(buf+29, "FLAC", 4) == 0 + && memcmp(buf+37, "fLaC", 4) == 0) + || (memcmp(buf+28, "FLAC", 4) == 0) + || (memcmp(buf+28, "fLaC", 4) == 0))) { + return FLAC; + } + return VORBIS; +} + +#endif /* defined(HAVE_OGGFLAC || defined(HAVE_OGGVORBIS) */ + diff --git a/src/inputPlugins/_ogg_common.h b/src/inputPlugins/_ogg_common.h new file mode 100644 index 000000000..4cd7fda41 --- /dev/null +++ b/src/inputPlugins/_ogg_common.h @@ -0,0 +1,35 @@ +/* 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 + * + * Common functions used for Ogg data streams (Ogg-Vorbis and OggFLAC) + * (c) 2005 by Eric Wong <normalperson@yhbt.net> + * + * 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 _OGG_COMMON_H +#define _OGG_COMMON_H + +#include "../inputPlugin.h" + +#if defined(HAVE_OGGFLAC) || defined(HAVE_OGGVORBIS) + +typedef enum _ogg_stream_type { VORBIS, FLAC } ogg_stream_type; + +ogg_stream_type ogg_stream_type_detect(InputStream * inStream); + +#endif /* defined(HAVE_OGGFLAC || defined(HAVE_OGGVORBIS) */ + +#endif /* _OGG_COMMON_H */ diff --git a/src/inputPlugins/aac_plugin.c b/src/inputPlugins/aac_plugin.c index e368f3089..3fb5f4742 100644 --- a/src/inputPlugins/aac_plugin.c +++ b/src/inputPlugins/aac_plugin.c @@ -418,6 +418,7 @@ InputPlugin aacPlugin = { "aac", NULL, + NULL, NULL, NULL, aac_decode, @@ -435,6 +436,7 @@ InputPlugin aacPlugin = NULL, NULL, NULL, + NULL, NULL, NULL, 0, diff --git a/src/inputPlugins/audiofile_plugin.c b/src/inputPlugins/audiofile_plugin.c index adc279b60..d212eaf47 100644 --- a/src/inputPlugins/audiofile_plugin.c +++ b/src/inputPlugins/audiofile_plugin.c @@ -163,6 +163,7 @@ InputPlugin audiofilePlugin = NULL, NULL, NULL, + NULL, audiofile_decode, audiofileTagDup, INPUT_PLUGIN_STREAM_FILE, @@ -176,6 +177,7 @@ InputPlugin audiofilePlugin = { NULL, NULL, + NULL, NULL, NULL, NULL, diff --git a/src/inputPlugins/flac_plugin.c b/src/inputPlugins/flac_plugin.c index 455394888..242dc6f22 100644 --- a/src/inputPlugins/flac_plugin.c +++ b/src/inputPlugins/flac_plugin.c @@ -20,6 +20,8 @@ #ifdef HAVE_FLAC +#include "_flac_common.h" + #include "../utils.h" #include "../log.h" #include "../pcm_utils.h" @@ -34,22 +36,8 @@ #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; - ReplayGainInfo * replayGainInfo; -} 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); @@ -75,14 +63,7 @@ int flac_decode(OutputBuffer * cb, DecoderControl * dc, InputStream * inStream) int status = 1; int ret =0; - data.chunk_length = 0; - data.time = 0; - data.position = 0; - data.bitRate = 0; - data.cb = cb; - data.dc = dc; - data.inStream = inStream; - data.replayGainInfo = NULL; + init_FlacData(&data, cb, dc, inStream); if(!(flacDec = FLAC__seekable_stream_decoder_new())) { ret = -1; @@ -176,19 +157,16 @@ int flac_decode(OutputBuffer * cb, DecoderControl * dc, InputStream * inStream) dc->seek = 0; } */ - if(dc->stop) { - dc->state = DECODE_STATE_STOP; - dc->stop = 0; - } - else dc->state = DECODE_STATE_STOP; + dc->state = DECODE_STATE_STOP; + dc->stop = 0; fail: if(data.replayGainInfo) freeReplayGainInfo(data.replayGainInfo); - closeInputStream(inStream); - if(flacDec) FLAC__seekable_stream_decoder_delete(flacDec); + closeInputStream(inStream); + return ret; } @@ -250,35 +228,17 @@ FLAC__SeekableStreamDecoderLengthStatus flacLength( } FLAC__bool flacEOF(const FLAC__SeekableStreamDecoder * flacDec, void * fdata) { - FlacData * data = (FlacData *) fdata; - - switch(inputStreamAtEOF(data->inStream)) { - case 1: + FlacData * data = (FlacData *) fdata; + + if (inputStreamAtEOF(data->inStream) == 1) return true; - default: - return false; - } + 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\n"); - break; - case FLAC__STREAM_DECODER_ERROR_STATUS_BAD_HEADER: - ERROR("bad header\n"); - break; - case FLAC__STREAM_DECODER_ERROR_STATUS_FRAME_CRC_MISMATCH: - ERROR("crc mismatch\n"); - break; - default: - ERROR("unknown flac error\n"); - } + flac_error_common_cb("flac",status,(FlacData *) fdata); } void flacPrintErroredState(FLAC__SeekableStreamDecoderState state) @@ -312,93 +272,10 @@ void flacPrintErroredState(FLAC__SeekableStreamDecoderState state) } } -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 = 0; - - if(NULL != data->replayGainInfo) { - freeReplayGainInfo(data->replayGainInfo); - data->replayGainInfo = NULL; - } - - data->replayGainInfo = newReplayGainInfo(); - - found &= flacFindVorbisCommentFloat(block,"replaygain_album_gain", - &data->replayGainInfo->albumGain); - found &= flacFindVorbisCommentFloat(block,"replaygain_album_peak", - &data->replayGainInfo->albumPeak); - found &= flacFindVorbisCommentFloat(block,"replaygain_track_gain", - &data->replayGainInfo->trackGain); - found &= flacFindVorbisCommentFloat(block,"replaygain_track_peak", - &data->replayGainInfo->trackPeak); - - if(!found) { - freeReplayGainInfo(data->replayGainInfo); - data->replayGainInfo = NULL; - } -} - 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) { - switch(sendDataToOutputBuffer(data->cb, NULL, data->dc, 1, data->chunk, - data->chunk_length, data->time, data->bitRate, - data->replayGainInfo)) - { - case OUTPUT_BUFFER_DC_STOP: - return -1; - default: - return 0; - } - - return 0; + flac_metadata_common_cb(block, (FlacData *)vdata); } FLAC__StreamDecoderWriteStatus flacWrite(const FLAC__SeekableStreamDecoder *dec, @@ -447,79 +324,6 @@ FLAC__StreamDecoderWriteStatus flacWrite(const FLAC__SeekableStreamDecoder *dec, return FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE; } -static int commentMatchesAddToTag( - char * str, - FLAC__StreamMetadata_VorbisComment_Entry * entry, - int itemType, - MpdTag ** tag) -{ - int slen = strlen(str); - int vlen = entry->length - slen; - - if( vlen <= 0 ) return 0; - - if( 0 == strncasecmp(str, entry->entry, slen) ) { - if(*tag == NULL) *tag = newMpdTag(); - addItemToMpdTagWithLen(*tag, itemType, - entry->entry+slen, vlen); - return 1; - } - - return 0; -} - - -static MpdTag * copyVorbisCommentBlockToMpdTag(FLAC__StreamMetadata * block, - MpdTag * tag) -{ - int i; - - for(i = 0; i < block->data.vorbis_comment.num_comments; i++) { - if(commentMatchesAddToTag( - "artist=", - block->data.vorbis_comment.comments+i, - TAG_ITEM_ARTIST, - &tag)); - else if(commentMatchesAddToTag( - "title=", - block->data.vorbis_comment.comments+i, - TAG_ITEM_TITLE, - &tag)); - else if(commentMatchesAddToTag( - "album=", - block->data.vorbis_comment.comments+i, - TAG_ITEM_ALBUM, - &tag)); - else if(commentMatchesAddToTag( - "tracknumber=", - block->data.vorbis_comment.comments+i, - TAG_ITEM_TRACK, - &tag)); - else if(commentMatchesAddToTag( - "genre=", - block->data.vorbis_comment.comments+i, - TAG_ITEM_GENRE, - &tag)); - else if(commentMatchesAddToTag( - "date=", - block->data.vorbis_comment.comments+i, - TAG_ITEM_DATE, - &tag)); - else if(commentMatchesAddToTag( - "composer=", - block->data.vorbis_comment.comments+i, - TAG_ITEM_COMPOSER, - &tag)); - else if(commentMatchesAddToTag( - "performer=", - block->data.vorbis_comment.comments+i, - TAG_ITEM_PERFORMER, - &tag)); - } - - return tag; -} - MpdTag * flacMetadataDup(char * file, int * vorbisCommentFound) { MpdTag * ret = NULL; FLAC__Metadata_SimpleIterator * it; @@ -594,30 +398,32 @@ char * flac_mime_types[] = {"application/x-flac", NULL}; InputPlugin flacPlugin = { - "flac", - NULL, + "flac", + NULL, NULL, - flac_decode, NULL, - flacTagDup, - INPUT_PLUGIN_STREAM_URL | INPUT_PLUGIN_STREAM_FILE, - flacSuffixes, - flac_mime_types + flac_decode, + NULL, + flacTagDup, + INPUT_PLUGIN_STREAM_URL | INPUT_PLUGIN_STREAM_FILE, + flacSuffixes, + flac_mime_types }; -#else +#else /* !HAVE_FLAC */ InputPlugin flacPlugin = -{ - NULL, - NULL, - NULL, - NULL, +{ + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + 0, NULL, NULL, - 0, - NULL, - NULL, }; -#endif +#endif /* HAVE_FLAC */ diff --git a/src/inputPlugins/mod_plugin.c b/src/inputPlugins/mod_plugin.c index 547f4af4a..8b5a245a0 100644 --- a/src/inputPlugins/mod_plugin.c +++ b/src/inputPlugins/mod_plugin.c @@ -259,6 +259,7 @@ InputPlugin modPlugin = NULL, mod_finishMikMod, NULL, + NULL, mod_decode, modTagDup, INPUT_PLUGIN_STREAM_FILE, @@ -273,6 +274,7 @@ InputPlugin modPlugin = NULL, NULL, NULL, + NULL, NULL, NULL, NULL, diff --git a/src/inputPlugins/mp3_plugin.c b/src/inputPlugins/mp3_plugin.c index 0657f25b1..8a73ed85d 100644 --- a/src/inputPlugins/mp3_plugin.c +++ b/src/inputPlugins/mp3_plugin.c @@ -820,6 +820,7 @@ InputPlugin mp3Plugin = "mp3", NULL, NULL, + NULL, mp3_decode, NULL, mp3_tagDup, @@ -837,6 +838,7 @@ InputPlugin mp3Plugin = NULL, NULL, NULL, + NULL, 0, NULL, NULL diff --git a/src/inputPlugins/mp4_plugin.c b/src/inputPlugins/mp4_plugin.c index a1af848c5..e41c054c0 100644 --- a/src/inputPlugins/mp4_plugin.c +++ b/src/inputPlugins/mp4_plugin.c @@ -431,6 +431,7 @@ InputPlugin mp4Plugin = NULL, NULL, NULL, + NULL, mp4_decode, mp4TagDup, INPUT_PLUGIN_STREAM_FILE, @@ -447,6 +448,7 @@ InputPlugin mp4Plugin = NULL, NULL, NULL, + NULL, NULL, 0, NULL, diff --git a/src/inputPlugins/mpc_plugin.c b/src/inputPlugins/mpc_plugin.c index 335c5c368..01a165c44 100644 --- a/src/inputPlugins/mpc_plugin.c +++ b/src/inputPlugins/mpc_plugin.c @@ -336,6 +336,7 @@ InputPlugin mpcPlugin = "mpc", NULL, NULL, + NULL, mpc_decode, NULL, mpcTagDup, @@ -351,6 +352,7 @@ InputPlugin mpcPlugin = NULL, NULL, NULL, + NULL, NULL, NULL, NULL, diff --git a/src/inputPlugins/oggflac_plugin.c b/src/inputPlugins/oggflac_plugin.c new file mode 100644 index 000000000..25f9a3cdb --- /dev/null +++ b/src/inputPlugins/oggflac_plugin.c @@ -0,0 +1,426 @@ +/* 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 + * + * OggFLAC support (half-stolen from flac_plugin.c :)) + * (c) 2005 by Eric Wong <normalperson@yhbt.net> + * + * 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_OGGFLAC + +#include "_flac_common.h" +#include "_ogg_common.h" + +#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 <OggFLAC/seekable_stream_decoder.h> +#include <FLAC/metadata.h> + +static void oggflac_cleanup(InputStream * inStream, + FlacData * data, + OggFLAC__SeekableStreamDecoder * decoder) +{ + if (data->replayGainInfo) + freeReplayGainInfo(data->replayGainInfo); + if (decoder) + OggFLAC__seekable_stream_decoder_delete(decoder); + closeInputStream(inStream); +} + +static OggFLAC__SeekableStreamDecoderReadStatus of_read_cb( + const OggFLAC__SeekableStreamDecoder * decoder, + FLAC__byte buf[], unsigned * bytes, void * fdata) { + FlacData * data = (FlacData *) fdata; + size_t r; + + while (1) { + r = readFromInputStream(data->inStream,(void *)buf,1,*bytes); + if (r == 0 && !inputStreamAtEOF(data->inStream) && + !data->dc->stop) + my_usleep(10000); + else + break; + } + *bytes = r; + + if (r == 0 && !inputStreamAtEOF(data->inStream) && !data->dc->stop) + return OggFLAC__SEEKABLE_STREAM_DECODER_READ_STATUS_ERROR; + + return OggFLAC__SEEKABLE_STREAM_DECODER_READ_STATUS_OK; +} + +static OggFLAC__SeekableStreamDecoderSeekStatus of_seek_cb( + const OggFLAC__SeekableStreamDecoder * decoder, + FLAC__uint64 offset, void * fdata) +{ + FlacData * data = (FlacData *) fdata; + + if(seekInputStream(data->inStream,offset,SEEK_SET)<0) { + return OggFLAC__SEEKABLE_STREAM_DECODER_SEEK_STATUS_ERROR; + } + + return OggFLAC__SEEKABLE_STREAM_DECODER_SEEK_STATUS_OK; +} + +static OggFLAC__SeekableStreamDecoderTellStatus of_tell_cb( + const OggFLAC__SeekableStreamDecoder * decoder, + FLAC__uint64 * offset, void * fdata) +{ + FlacData * data = (FlacData *) fdata; + + *offset = (long)(data->inStream->offset); + + return OggFLAC__SEEKABLE_STREAM_DECODER_TELL_STATUS_OK; +} + +static OggFLAC__SeekableStreamDecoderLengthStatus of_length_cb( + const OggFLAC__SeekableStreamDecoder * decoder, + FLAC__uint64 * length, void * fdata) +{ + FlacData * data = (FlacData *) fdata; + + *length = (size_t)(data->inStream->size); + + return OggFLAC__SEEKABLE_STREAM_DECODER_LENGTH_STATUS_OK; +} + +static FLAC__bool of_EOF_cb(const OggFLAC__SeekableStreamDecoder * decoder, + void * fdata) { + FlacData * data = (FlacData *) fdata; + + if (inputStreamAtEOF(data->inStream) == 1) + return true; + return false; +} + +static void of_error_cb(const OggFLAC__SeekableStreamDecoder * decoder, + FLAC__StreamDecoderErrorStatus status, void *fdata) +{ + flac_error_common_cb("oggflac",status,(FlacData *) fdata); +} + +static void oggflacPrintErroredState(OggFLAC__SeekableStreamDecoderState state) +{ + switch(state) { + case OggFLAC__SEEKABLE_STREAM_DECODER_MEMORY_ALLOCATION_ERROR: + ERROR("oggflac allocation error\n"); + break; + case OggFLAC__SEEKABLE_STREAM_DECODER_READ_ERROR: + ERROR("oggflac read error\n"); + break; + case OggFLAC__SEEKABLE_STREAM_DECODER_SEEK_ERROR: + ERROR("oggflac seek error\n"); + break; + case OggFLAC__SEEKABLE_STREAM_DECODER_STREAM_DECODER_ERROR: + ERROR("oggflac seekable stream error\n"); + break; + case OggFLAC__SEEKABLE_STREAM_DECODER_ALREADY_INITIALIZED: + ERROR("oggflac decoder already initialized\n"); + break; + case OggFLAC__SEEKABLE_STREAM_DECODER_INVALID_CALLBACK: + ERROR("invalid oggflac callback\n"); + break; + case OggFLAC__SEEKABLE_STREAM_DECODER_UNINITIALIZED: + ERROR("oggflac decoder uninitialized\n"); + break; + case OggFLAC__SEEKABLE_STREAM_DECODER_OK: + case OggFLAC__SEEKABLE_STREAM_DECODER_SEEKING: + case OggFLAC__SEEKABLE_STREAM_DECODER_END_OF_STREAM: + break; + } +} + +static FLAC__StreamDecoderWriteStatus oggflacWrite( + const OggFLAC__SeekableStreamDecoder *decoder, + 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; + const int bytesPerSample = data->dc->audioFormat.bits/8; + + timeChange = ((float)samples)/frame->header.sample_rate; + data->time+= timeChange; + + /* ogg123 uses a complicated method of calculating bitrate + * with averaging which I'm not too fond of. + * (waste of memory/CPU cycles, especially given this is _lossless_) + * a get_decode_position() is not available in OggFLAC, either + * + * this does not give an accurate bitrate: + * (bytes_last_read was set in the read callback) + data->bitRate = ((8.0 * data->bytes_last_read * + frame->header.sample_rate) + /((float)samples * 1000)) + 0.5; + */ + + 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; +} + +/* used by TagDup */ +static void of_metadata_dup_cb( + const OggFLAC__SeekableStreamDecoder * decoder, + const FLAC__StreamMetadata *block, void *vdata) +{ + FlacData * data = (FlacData *)vdata; + + switch(block->type) { + case FLAC__METADATA_TYPE_STREAMINFO: + if (!data->tag) data->tag = newMpdTag(); + data->tag->time = ((float)block->data.stream_info. + total_samples) / + block->data.stream_info.sample_rate + + 0.5; + return; + case FLAC__METADATA_TYPE_VORBIS_COMMENT: + copyVorbisCommentBlockToMpdTag(block,data->tag); + default: + break; + } +} + +/* used by decode */ +static void of_metadata_decode_cb(const OggFLAC__SeekableStreamDecoder * dec, + const FLAC__StreamMetadata *block, void *vdata) +{ + flac_metadata_common_cb(block, (FlacData *)vdata); +} + +static OggFLAC__SeekableStreamDecoder * full_decoder_init_and_read_metadata( + FlacData * data, + unsigned int metadata_only) +{ + OggFLAC__SeekableStreamDecoder * decoder = NULL; + unsigned int s = 1; + + if (!(decoder = OggFLAC__seekable_stream_decoder_new())) + return NULL; + + if (metadata_only) { + s &= OggFLAC__seekable_stream_decoder_set_metadata_callback( + decoder, of_metadata_dup_cb); + s &= OggFLAC__seekable_stream_decoder_set_metadata_respond( + decoder, + FLAC__METADATA_TYPE_STREAMINFO); + } else { + s &= OggFLAC__seekable_stream_decoder_set_metadata_callback( + decoder, of_metadata_decode_cb); + } + + s &= OggFLAC__seekable_stream_decoder_set_read_callback(decoder, + of_read_cb); + s &= OggFLAC__seekable_stream_decoder_set_seek_callback(decoder, + of_seek_cb); + s &= OggFLAC__seekable_stream_decoder_set_tell_callback(decoder, + of_tell_cb); + s &= OggFLAC__seekable_stream_decoder_set_length_callback(decoder, + of_length_cb); + s &= OggFLAC__seekable_stream_decoder_set_eof_callback(decoder, + of_EOF_cb); + s &= OggFLAC__seekable_stream_decoder_set_write_callback(decoder, + oggflacWrite); + s &= OggFLAC__seekable_stream_decoder_set_metadata_respond(decoder, + FLAC__METADATA_TYPE_VORBIS_COMMENT); + s &= OggFLAC__seekable_stream_decoder_set_error_callback(decoder, + of_error_cb); + s &= OggFLAC__seekable_stream_decoder_set_client_data(decoder, + (void *)data); + + if (!s) { + ERROR("oggflac problem before init()\n"); + goto fail; + } + if (OggFLAC__seekable_stream_decoder_init(decoder) != + OggFLAC__SEEKABLE_STREAM_DECODER_OK) + { + ERROR("oggflac problem doing init()\n"); + goto fail; + } + if (!OggFLAC__seekable_stream_decoder_process_until_end_of_metadata( + decoder)) { + ERROR("oggflac problem reading metadata\n"); + goto fail; + } + + return decoder; + +fail: + oggflacPrintErroredState( + OggFLAC__seekable_stream_decoder_get_state(decoder)); + OggFLAC__seekable_stream_decoder_delete(decoder); + return NULL; +} + +/* public functions: */ +static MpdTag * oggflac_TagDup(char * file) +{ + InputStream inStream; + OggFLAC__SeekableStreamDecoder * decoder; + FlacData data; + + if (openInputStream(&inStream, file) < 0) + return NULL; + if (ogg_stream_type_detect(&inStream) != FLAC) { + closeInputStream(&inStream); + return NULL; + } + + init_FlacData(&data, NULL, NULL, &inStream); + + /* errors here won't matter, + * data.tag will be set or unset, that's all we care about */ + decoder = full_decoder_init_and_read_metadata(&data,1); + + oggflac_cleanup(&inStream, &data, decoder); + + return data.tag; +} + +static unsigned int oggflac_try_decode(InputStream * inStream) +{ + return (ogg_stream_type_detect(inStream) == FLAC) ? 1 : 0; +} + +static int oggflac_decode(OutputBuffer * cb, DecoderControl * dc, + InputStream * inStream) +{ + OggFLAC__SeekableStreamDecoder * decoder = NULL; + FlacData data; + int ret = 0; + + init_FlacData(&data, cb, dc, inStream); + + if(!(decoder = full_decoder_init_and_read_metadata(&data,0))){ + ret = -1; + goto fail; + } + + dc->state = DECODE_STATE_DECODE; + + while(1) { + OggFLAC__seekable_stream_decoder_process_single(decoder); + if(OggFLAC__seekable_stream_decoder_get_state(decoder)!= + OggFLAC__SEEKABLE_STREAM_DECODER_OK) + { + break; + } + if(dc->seek) { + FLAC__uint64 sampleToSeek = dc->seekWhere* + dc->audioFormat.sampleRate+0.5; + if(OggFLAC__seekable_stream_decoder_seek_absolute( + decoder, sampleToSeek)) + { + clearOutputBuffer(cb); + data.time = ((float)sampleToSeek)/ + dc->audioFormat.sampleRate; + data.position = 0; + } + else dc->seekError = 1; + dc->seek = 0; + } + } + + if(!dc->stop) { + oggflacPrintErroredState( + OggFLAC__seekable_stream_decoder_get_state(decoder)); + OggFLAC__seekable_stream_decoder_finish(decoder); + } + /* send last little bit */ + if(data.chunk_length>0 && !dc->stop) { + flacSendChunk(&data); + flushOutputBuffer(data.cb); + } + + dc->state = DECODE_STATE_STOP; + dc->stop = 0; + +fail: + oggflac_cleanup(inStream, &data, decoder); + + return ret; +} + +static char * oggflac_Suffixes[] = {"ogg", NULL}; +static char * oggflac_mime_types[] = {"application/ogg", NULL}; + +InputPlugin oggflacPlugin = +{ + "oggflac", + NULL, + NULL, + oggflac_try_decode, + oggflac_decode, + NULL, + oggflac_TagDup, + INPUT_PLUGIN_STREAM_URL | INPUT_PLUGIN_STREAM_FILE, + oggflac_Suffixes, + oggflac_mime_types +}; + +#else /* !HAVE_FLAC */ + +InputPlugin oggflacPlugin = +{ + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + 0, + NULL, + NULL, +}; + +#endif /* HAVE_OGGFLAC */ + diff --git a/src/inputPlugins/ogg_plugin.c b/src/inputPlugins/oggvorbis_plugin.c index abadca388..63bac411e 100644 --- a/src/inputPlugins/ogg_plugin.c +++ b/src/inputPlugins/oggvorbis_plugin.c @@ -16,9 +16,13 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +/* TODO 'ogg' should probably be replaced with 'oggvorbis' in all instances */ + #include "../inputPlugin.h" -#ifdef HAVE_OGG +#ifdef HAVE_OGGVORBIS + +#include "_ogg_common.h" #include "../utils.h" #include "../audio.h" @@ -99,7 +103,7 @@ long ogg_tell_cb(void * vdata) { return (long)(data->inStream->offset); } -char * ogg_parseComment(char * comment, char * needle) { +static inline char * ogg_parseComment(char * comment, char * needle) { int len = strlen(needle); if(strncasecmp(comment, needle, len) == 0 && *(comment+len) == '=') { @@ -150,48 +154,40 @@ void ogg_getReplayGainInfo(char ** comments, ReplayGainInfo ** infoPtr) { } } -MpdTag * oggCommentsParse(char ** comments) { - MpdTag * ret = NULL; - char * temp; +static const char * VORBIS_COMMENT_TRACK_KEY = "tracknumber"; + +static inline unsigned int ogg_parseCommentAddToTag(char * comment, + unsigned int itemType, MpdTag ** tag) +{ + const char * needle = (itemType == TAG_ITEM_TRACK) ? + VORBIS_COMMENT_TRACK_KEY : mpdTagItemKeys[itemType]; + unsigned int len = strlen(needle); + + if(strncasecmp(comment, needle, len) == 0 && *(comment+len) == '=') { + if (!*tag) + *tag = newMpdTag(); + + addItemToMpdTag(*tag, itemType, comment+len+1); + + return 1; + } + + return 0; +} +static MpdTag * oggCommentsParse(char ** comments) { + MpdTag * tag = NULL; + while(*comments) { - if((temp = ogg_parseComment(*comments,"artist"))) { - if(!ret) ret = newMpdTag(); - addItemToMpdTag(ret, TAG_ITEM_ARTIST, temp); - } - else if((temp = ogg_parseComment(*comments,"title"))) { - if(!ret) ret = newMpdTag(); - addItemToMpdTag(ret, TAG_ITEM_TITLE, temp); - } - else if((temp = ogg_parseComment(*comments,"album"))) { - if(!ret) ret = newMpdTag(); - addItemToMpdTag(ret, TAG_ITEM_ALBUM, temp); - } - else if((temp = ogg_parseComment(*comments,"tracknumber"))) { - if(!ret) ret = newMpdTag(); - addItemToMpdTag(ret, TAG_ITEM_TRACK, temp); - } - else if((temp = ogg_parseComment(*comments,"genre"))) { - if(!ret) ret = newMpdTag(); - addItemToMpdTag(ret, TAG_ITEM_GENRE, temp); - } - else if((temp = ogg_parseComment(*comments,"date"))) { - if(!ret) ret = newMpdTag(); - addItemToMpdTag(ret, TAG_ITEM_DATE, temp); - } - else if((temp = ogg_parseComment(*comments,"composer"))) { - if(!ret) ret = newMpdTag(); - addItemToMpdTag(ret, TAG_ITEM_COMPOSER, temp); - } - else if((temp = ogg_parseComment(*comments,"performer"))) { - if(!ret) ret = newMpdTag(); - addItemToMpdTag(ret, TAG_ITEM_PERFORMER, temp); + unsigned int j; + for (j = TAG_NUM_OF_ITEM_TYPES; j--; ) { + if (ogg_parseCommentAddToTag(*comments, j, &tag)) + break; } - comments++; } - - return ret; + + return tag; } void putOggCommentsIntoOutputBuffer(OutputBuffer * cb, char * streamName, @@ -220,7 +216,9 @@ void putOggCommentsIntoOutputBuffer(OutputBuffer * cb, char * streamName, freeMpdTag(tag); } -int ogg_decode(OutputBuffer * cb, DecoderControl * dc, InputStream * inStream) +/* public */ +int oggvorbis_decode(OutputBuffer * cb, DecoderControl * dc, + InputStream * inStream) { OggVorbis_File vf; ov_callbacks callbacks; @@ -360,7 +358,7 @@ int ogg_decode(OutputBuffer * cb, DecoderControl * dc, InputStream * inStream) return 0; } -MpdTag * oggTagDup(char * file) { +MpdTag * oggvorbis_TagDup(char * file) { MpdTag * ret = NULL; FILE * fp; OggVorbis_File vf; @@ -386,35 +384,43 @@ MpdTag * oggTagDup(char * file) { return ret; } -char * oggSuffixes[] = {"ogg", NULL}; -char * oggMimeTypes[] = {"application/ogg", NULL}; +static unsigned int oggvorbis_try_decode(InputStream * inStream) +{ + return (ogg_stream_type_detect(inStream) == VORBIS) ? 1 : 0; +} + -InputPlugin oggPlugin = +static char * oggvorbis_Suffixes[] = {"ogg", NULL}; +static char * oggvorbis_MimeTypes[] = {"application/ogg", NULL}; + +InputPlugin oggvorbisPlugin = { - "ogg", + "oggvorbis", + NULL, NULL, + oggvorbis_try_decode, + oggvorbis_decode, NULL, - ogg_decode, - NULL, - oggTagDup, - INPUT_PLUGIN_STREAM_URL | INPUT_PLUGIN_STREAM_FILE, - oggSuffixes, - oggMimeTypes + oggvorbis_TagDup, + INPUT_PLUGIN_STREAM_URL | INPUT_PLUGIN_STREAM_FILE, + oggvorbis_Suffixes, + oggvorbis_MimeTypes }; -#else +#else /* !HAVE_OGGVORBIS */ -InputPlugin oggPlugin = +InputPlugin oggvorbisPlugin = { NULL, NULL, NULL, - NULL, - NULL, - NULL, - 0, - NULL, - NULL + NULL, + NULL, + NULL, + NULL, + 0, + NULL, + NULL, }; -#endif +#endif /* HAVE_OGGVORBIS */ |