aboutsummaryrefslogtreecommitdiffstats
path: root/src/decoder/_flac_common.c
diff options
context:
space:
mode:
authorMax Kellermann <max@duempel.org>2008-10-26 11:29:25 +0100
committerMax Kellermann <max@duempel.org>2008-10-26 11:29:25 +0100
commite11355f47d545fe523b019481415b1347aecd4bd (patch)
treef178cd838be280d0517dc0e5910c36cb96a2a80e /src/decoder/_flac_common.c
parentcbc71191f0ed75c5fafad5c387f009c2139a7bed (diff)
downloadmpd-e11355f47d545fe523b019481415b1347aecd4bd.tar.gz
mpd-e11355f47d545fe523b019481415b1347aecd4bd.tar.xz
mpd-e11355f47d545fe523b019481415b1347aecd4bd.zip
renamed src/inputPlugins/ to src/decoder/
These plugins are not input plugins, they are decoder plugins. No CamelCase in the directory name.
Diffstat (limited to 'src/decoder/_flac_common.c')
-rw-r--r--src/decoder/_flac_common.c323
1 files changed, 323 insertions, 0 deletions
diff --git a/src/decoder/_flac_common.c b/src/decoder/_flac_common.c
new file mode 100644
index 000000000..db43e0003
--- /dev/null
+++ b/src/decoder/_flac_common.c
@@ -0,0 +1,323 @@
+/* the Music Player Daemon (MPD)
+ * Copyright (C) 2003-2007 by Warren Dukes (warren.dukes@gmail.com)
+ * 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 "_flac_common.h"
+#include "../log.h"
+
+#include <FLAC/format.h>
+#include <FLAC/metadata.h>
+
+void init_FlacData(FlacData * data, struct decoder * decoder,
+ InputStream * inStream)
+{
+ data->time = 0;
+ data->position = 0;
+ data->bitRate = 0;
+ data->decoder = decoder;
+ data->inStream = inStream;
+ data->replayGainInfo = NULL;
+ data->tag = NULL;
+}
+
+static int flacFindVorbisCommentFloat(const FLAC__StreamMetadata * block,
+ const 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 *p = &(block->data.vorbis_comment.
+ comments[offset].entry[pos]);
+ tmp = p[len];
+ p[len] = '\0';
+ *fl = (float)atof((char *)p);
+ p[len] = tmp;
+
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+/* replaygain stuff by AliasMrJones */
+static void flacParseReplayGain(const FLAC__StreamMetadata * block,
+ FlacData * data)
+{
+ 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 const char *VORBIS_COMMENT_DISC_KEY = "discnumber";
+
+static unsigned int commentMatchesAddToTag(const
+ FLAC__StreamMetadata_VorbisComment_Entry
+ * entry, unsigned int itemType,
+ struct tag ** tag)
+{
+ const char *str;
+ size_t slen;
+ int vlen;
+
+ switch (itemType) {
+ case TAG_ITEM_TRACK:
+ str = VORBIS_COMMENT_TRACK_KEY;
+ break;
+ case TAG_ITEM_DISC:
+ str = VORBIS_COMMENT_DISC_KEY;
+ break;
+ default:
+ str = mpdTagItemKeys[itemType];
+ }
+ slen = strlen(str);
+ vlen = entry->length - slen - 1;
+
+ if ((vlen > 0) && (0 == strncasecmp(str, (char *)entry->entry, slen))
+ && (*(entry->entry + slen) == '=')) {
+ if (!*tag)
+ *tag = tag_new();
+
+ tag_add_item_n(*tag, itemType,
+ (char *)(entry->entry + slen + 1), vlen);
+
+ return 1;
+ }
+
+ return 0;
+}
+
+struct tag *copyVorbisCommentBlockToMpdTag(const FLAC__StreamMetadata * block,
+ struct tag * 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)
+{
+ const FLAC__StreamMetadata_StreamInfo *si = &(block->data.stream_info);
+
+ switch (block->type) {
+ case FLAC__METADATA_TYPE_STREAMINFO:
+ data->audio_format.bits = (int8_t)si->bits_per_sample;
+ data->audio_format.sample_rate = si->sample_rate;
+ data->audio_format.channels = (int8_t)si->channels;
+ data->total_time = ((float)si->total_samples) / (si->sample_rate);
+ break;
+ case FLAC__METADATA_TYPE_VORBIS_COMMENT:
+ flacParseReplayGain(block, data);
+ default:
+ break;
+ }
+}
+
+void flac_error_common_cb(const char *plugin,
+ const FLAC__StreamDecoderErrorStatus status,
+ mpd_unused FlacData * data)
+{
+ if (decoder_get_command(data->decoder) == DECODE_COMMAND_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);
+ }
+}
+
+static void flac_convert_stereo16(int16_t *dest,
+ const FLAC__int32 * const buf[],
+ unsigned int position, unsigned int end)
+{
+ for (; position < end; ++position) {
+ *dest++ = buf[0][position];
+ *dest++ = buf[1][position];
+ }
+}
+
+static void
+flac_convert_16(int16_t *dest,
+ unsigned int num_channels,
+ const FLAC__int32 * const buf[],
+ unsigned int position, unsigned int end)
+{
+ unsigned int c_chan;
+
+ for (; position < end; ++position)
+ for (c_chan = 0; c_chan < num_channels; c_chan++)
+ *dest++ = buf[c_chan][position];
+}
+
+/**
+ * Note: this function also handles 24 bit files!
+ */
+static void
+flac_convert_32(int32_t *dest,
+ unsigned int num_channels,
+ const FLAC__int32 * const buf[],
+ unsigned int position, unsigned int end)
+{
+ unsigned int c_chan;
+
+ for (; position < end; ++position)
+ for (c_chan = 0; c_chan < num_channels; c_chan++)
+ *dest++ = buf[c_chan][position];
+}
+
+static void
+flac_convert_8(int8_t *dest,
+ unsigned int num_channels,
+ const FLAC__int32 * const buf[],
+ unsigned int position, unsigned int end)
+{
+ unsigned int c_chan;
+
+ for (; position < end; ++position)
+ for (c_chan = 0; c_chan < num_channels; c_chan++)
+ *dest++ = buf[c_chan][position];
+}
+
+static void flac_convert(unsigned char *dest,
+ unsigned int num_channels,
+ unsigned int bytes_per_sample,
+ const FLAC__int32 * const buf[],
+ unsigned int position, unsigned int end)
+{
+ switch (bytes_per_sample) {
+ case 2:
+ if (num_channels == 2)
+ flac_convert_stereo16((int16_t*)dest, buf,
+ position, end);
+ else
+ flac_convert_16((int16_t*)dest, num_channels, buf,
+ position, end);
+ break;
+
+ case 4:
+ flac_convert_32((int32_t*)dest, num_channels, buf,
+ position, end);
+ break;
+
+ case 1:
+ flac_convert_8((int8_t*)dest, num_channels, buf,
+ position, end);
+ break;
+ }
+}
+
+FLAC__StreamDecoderWriteStatus
+flac_common_write(FlacData *data, const FLAC__Frame * frame,
+ const FLAC__int32 *const buf[])
+{
+ unsigned int c_samp;
+ const unsigned int num_channels = frame->header.channels;
+ const unsigned int bytes_per_sample =
+ audio_format_sample_size(&data->audio_format);
+ const unsigned int bytes_per_channel =
+ bytes_per_sample * frame->header.channels;
+ const unsigned int max_samples = FLAC_CHUNK_SIZE / bytes_per_channel;
+ unsigned int num_samples;
+ enum decoder_command cmd;
+
+ if (bytes_per_sample != 1 && bytes_per_sample != 2 &&
+ bytes_per_sample != 4)
+ /* exotic unsupported bit rate */
+ return FLAC__STREAM_DECODER_WRITE_STATUS_ABORT;
+
+ for (c_samp = 0; c_samp < frame->header.blocksize;
+ c_samp += num_samples) {
+ num_samples = frame->header.blocksize - c_samp;
+ if (num_samples > max_samples)
+ num_samples = max_samples;
+
+ flac_convert(data->chunk,
+ num_channels, bytes_per_sample, buf,
+ c_samp, c_samp + num_samples);
+
+ cmd = decoder_data(data->decoder, data->inStream,
+ 1, data->chunk,
+ num_samples * bytes_per_channel,
+ data->time, data->bitRate,
+ data->replayGainInfo);
+ switch (cmd) {
+ case DECODE_COMMAND_NONE:
+ case DECODE_COMMAND_START:
+ break;
+
+ case DECODE_COMMAND_STOP:
+ return
+ FLAC__STREAM_DECODER_WRITE_STATUS_ABORT;
+
+ case DECODE_COMMAND_SEEK:
+ return
+ FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE;
+ }
+ }
+
+ return FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE;
+}