Esempio n. 1
0
FLAC__bool populate_seekpoint_values(const char *filename, FLAC__StreamMetadata *block, FLAC__bool *needs_write)
{
	FLAC__StreamDecoder *decoder;
	ClientData client_data;
	FLAC__bool ok = true;

	FLAC__ASSERT(0 != block);
	FLAC__ASSERT(block->type == FLAC__METADATA_TYPE_SEEKTABLE);

	client_data.seektable_template = &block->data.seek_table;
	client_data.samples_written = 0;
	/* client_data.audio_offset must be determined later */
	client_data.first_seekpoint_to_check = 0;
	client_data.error_occurred = false;

	decoder = FLAC__stream_decoder_new();

	if(0 == decoder) {
		fprintf(stderr, "%s: ERROR (--add-seekpoint) creating the decoder instance\n", filename);
		return false;
	}

	FLAC__stream_decoder_set_md5_checking(decoder, false);
	FLAC__stream_decoder_set_metadata_ignore_all(decoder);

	if(FLAC__stream_decoder_init_file(decoder, filename, write_callback_, /*metadata_callback=*/0, error_callback_, &client_data) != FLAC__STREAM_DECODER_INIT_STATUS_OK) {
		fprintf(stderr, "%s: ERROR (--add-seekpoint) initializing the decoder instance (%s)\n", filename, FLAC__stream_decoder_get_resolved_state_string(decoder));
		ok = false;
	}

	if(ok && !FLAC__stream_decoder_process_until_end_of_metadata(decoder)) {
		fprintf(stderr, "%s: ERROR (--add-seekpoint) decoding file (%s)\n", filename, FLAC__stream_decoder_get_resolved_state_string(decoder));
		ok = false;
	}

	if(ok && !FLAC__stream_decoder_get_decode_position(decoder, &client_data.audio_offset)) {
		fprintf(stderr, "%s: ERROR (--add-seekpoint) decoding file\n", filename);
		ok = false;
	}
	client_data.last_offset = client_data.audio_offset;

	if(ok && !FLAC__stream_decoder_process_until_end_of_stream(decoder)) {
		fprintf(stderr, "%s: ERROR (--add-seekpoint) decoding file (%s)\n", filename, FLAC__stream_decoder_get_resolved_state_string(decoder));
		ok = false;
	}

	if(ok && client_data.error_occurred) {
		fprintf(stderr, "%s: ERROR (--add-seekpoint) decoding file (%u:%s)\n", filename, (unsigned)client_data.error_status, FLAC__StreamDecoderErrorStatusString[client_data.error_status]);
		ok = false;
	}

	*needs_write = true;
	FLAC__stream_decoder_delete(decoder);
	return ok;
}
FLAC_API const char *FLAC__seekable_stream_decoder_get_resolved_state_string(const FLAC__SeekableStreamDecoder *decoder)
{
	if(decoder->protected_->state != FLAC__SEEKABLE_STREAM_DECODER_STREAM_DECODER_ERROR)
		return FLAC__SeekableStreamDecoderStateString[decoder->protected_->state];
	else
		return FLAC__stream_decoder_get_resolved_state_string(decoder->private_->stream_decoder);
}
Esempio n. 3
0
FLAC__bool FLAC_plugin__decoder_init(FLAC__StreamDecoder *decoder, const char *filename, FLAC__int64 filesize, stream_data_struct *stream_data, output_config_t *config)
{
	FLAC__StreamDecoderInitStatus init_status;

	FLAC__ASSERT(decoder);
	FLAC_plugin__decoder_finish(decoder);
	/* init decoder */
	FLAC__stream_decoder_set_md5_checking(decoder, false);
	FLAC__stream_decoder_set_metadata_ignore_all(decoder);
	FLAC__stream_decoder_set_metadata_respond(decoder, FLAC__METADATA_TYPE_STREAMINFO);
	FLAC__stream_decoder_set_metadata_respond(decoder, FLAC__METADATA_TYPE_VORBIS_COMMENT);

	if ((init_status = FLAC__stream_decoder_init_file(decoder, filename, write_callback, metadata_callback, error_callback, /*client_data=*/stream_data)) != FLAC__STREAM_DECODER_INIT_STATUS_OK)
	{
		FLAC_plugin__show_error("Error while initializing decoder (%s [%s]).", FLAC__StreamDecoderInitStatusString[init_status], FLAC__stream_decoder_get_resolved_state_string(decoder));
		return false;
	}
	/* process */
	cfg = *config;
	wide_samples_in_reservoir_ = 0;
	stream_data->is_playing = false;
	stream_data->abort_flag = false;
	stream_data->has_replaygain = false;

	if (!FLAC__stream_decoder_process_until_end_of_metadata(decoder))
	{
		FLAC_plugin__show_error("Error while processing metadata (%s).", FLAC__stream_decoder_get_resolved_state_string(decoder));
		return false;
	}
	/* check results */
	if (stream_data->abort_flag) return false;                /* metadata callback already popped up the error dialog */
	/* init replaygain */
	stream_data->output_bits_per_sample = stream_data->has_replaygain && cfg.replaygain.enable ?
		cfg.resolution.replaygain.bps_out :
		cfg.resolution.normal.dither_24_to_16 ? min(stream_data->bits_per_sample, 16) : stream_data->bits_per_sample;

	if (stream_data->has_replaygain && cfg.replaygain.enable && cfg.resolution.replaygain.dither)
		FLAC__replaygain_synthesis__init_dither_context(&stream_data->dither_context, stream_data->bits_per_sample, cfg.resolution.replaygain.noise_shaping);
	/* more inits */
	stream_data->eof = false;
	stream_data->seek_to = -1;
	stream_data->is_playing = true;
	stream_data->average_bps = (unsigned)(filesize / (125.*(double)(FLAC__int64)stream_data->total_samples/(double)stream_data->sample_rate));
	
	bh_index_last_w = 0;
	bh_index_last_o = BITRATE_HIST_SIZE;
	decode_position = 0;
	decode_position_last = 0;
	written_time_last = 0;

	return true;
}
Esempio n. 4
0
static gboolean
xmms_flac_init (xmms_xform_t *xform)
{
	xmms_flac_data_t *data;
	xmms_sample_format_t sample_fmt;
	FLAC__bool retval;
#if !defined(FLAC_API_VERSION_CURRENT) || FLAC_API_VERSION_CURRENT <= 7
	FLAC__StreamDecoderState init_status;
#else
	FLAC__StreamDecoderInitStatus init_status;
#endif
	gint filesize;
	const gchar *metakey;

	g_return_val_if_fail (xform, FALSE);

	data = g_new0 (xmms_flac_data_t, 1);

	xmms_xform_private_data_set (xform, data);

	data->flacdecoder = FLAC__stream_decoder_new ();

	/* we don't need to explicitly tell the decoder to respond to
	 * FLAC__METADATA_TYPE_STREAMINFO here, it always does.
	 */
#if !defined(FLAC_API_VERSION_CURRENT) || FLAC_API_VERSION_CURRENT <= 7
	FLAC__seekable_stream_decoder_set_metadata_respond (data->flacdecoder,
	                                                    FLAC__METADATA_TYPE_VORBIS_COMMENT);
	FLAC__seekable_stream_decoder_set_eof_callback (data->flacdecoder,
	                                                flac_callback_eof);
	FLAC__seekable_stream_decoder_set_read_callback (data->flacdecoder,
	                                                 flac_callback_read);
	FLAC__seekable_stream_decoder_set_seek_callback (data->flacdecoder,
	                                                 flac_callback_seek);
	FLAC__seekable_stream_decoder_set_tell_callback (data->flacdecoder,
	                                                 flac_callback_tell);
	FLAC__seekable_stream_decoder_set_write_callback (data->flacdecoder,
	                                                  flac_callback_write);
	FLAC__seekable_stream_decoder_set_error_callback (data->flacdecoder,
	                                                  flac_callback_error);
	FLAC__seekable_stream_decoder_set_length_callback (data->flacdecoder,
	                                                   flac_callback_length);
	FLAC__seekable_stream_decoder_set_metadata_callback (data->flacdecoder,
	                                                     flac_callback_metadata);

	FLAC__seekable_stream_decoder_set_client_data (data->flacdecoder, xform);

	init_status = FLAC__seekable_stream_decoder_init (data->flacdecoder);

	if (init_status != FLAC__SEEKABLE_STREAM_DECODER_OK) {
		const gchar *errmsg = FLAC__seekable_stream_decoder_get_resolved_state_string (data->flacdecoder);
		XMMS_DBG ("FLAC init failed: %s", errmsg);
		goto err;
	}
#else
	FLAC__stream_decoder_set_metadata_respond (data->flacdecoder,
	                                           FLAC__METADATA_TYPE_VORBIS_COMMENT);
	FLAC__stream_decoder_set_metadata_respond (data->flacdecoder,
	                                           FLAC__METADATA_TYPE_PICTURE);

	init_status =
		FLAC__stream_decoder_init_stream (data->flacdecoder,
		                                  flac_callback_read,
		                                  flac_callback_seek,
		                                  flac_callback_tell,
		                                  flac_callback_length,
		                                  flac_callback_eof,
		                                  flac_callback_write,
		                                  flac_callback_metadata,
		                                  flac_callback_error,
		                                  xform);

	if (init_status != FLAC__STREAM_DECODER_INIT_STATUS_OK) {
		XMMS_DBG ("FLAC init failed: %s",
		          FLAC__stream_decoder_get_resolved_state_string (data->flacdecoder));
		goto err;
	}
#endif

	retval = FLAC__stream_decoder_process_until_end_of_metadata (data->flacdecoder);
	if (!retval)
		goto err;

	if (data->vorbiscomment) {
		handle_comments (xform, data);
	}

	metakey = XMMS_MEDIALIB_ENTRY_PROPERTY_BITRATE;
	xmms_xform_metadata_set_int (xform, metakey, (gint) data->bit_rate);

	metakey = XMMS_MEDIALIB_ENTRY_PROPERTY_SIZE;
	if (xmms_xform_metadata_get_int (xform, metakey, &filesize)) {
		gint32 val = (gint32) data->total_samples / data->sample_rate * 1000;

		metakey = XMMS_MEDIALIB_ENTRY_PROPERTY_DURATION;
		xmms_xform_metadata_set_int (xform, metakey, val);
	}

	if (data->bits_per_sample == 8) {
		sample_fmt = XMMS_SAMPLE_FORMAT_S8;
	} else if (data->bits_per_sample == 16) {
		sample_fmt = XMMS_SAMPLE_FORMAT_S16;
	} else if (data->bits_per_sample == 24) {
		sample_fmt = XMMS_SAMPLE_FORMAT_S32;
	} else if (data->bits_per_sample == 32) {
		sample_fmt = XMMS_SAMPLE_FORMAT_S32;
	} else {
		goto err;
	}

	xmms_xform_outdata_type_add (xform,
	                             XMMS_STREAM_TYPE_MIMETYPE,
	                             "audio/pcm",
	                             XMMS_STREAM_TYPE_FMT_FORMAT,
	                             sample_fmt,
	                             XMMS_STREAM_TYPE_FMT_CHANNELS,
	                             data->channels,
	                             XMMS_STREAM_TYPE_FMT_SAMPLERATE,
	                             data->sample_rate,
	                             XMMS_STREAM_TYPE_END);

	data->buffer = g_string_new (NULL);

	return TRUE;

err:

	FLAC__stream_decoder_finish (data->flacdecoder);
	FLAC__stream_decoder_delete (data->flacdecoder);
	g_free (data);
	xmms_xform_private_data_set (xform, NULL);

	return FALSE;

}
Esempio n. 5
0
/* read mode:
 * 0 - no read after seek
 * 1 - read 2 frames
 * 2 - read until end
 */
static FLAC__bool seek_barrage(FLAC__bool is_ogg, const char *filename, off_t filesize, unsigned count, FLAC__int64 total_samples, unsigned read_mode)
{
	FLAC__StreamDecoder *decoder;
	DecoderClientData decoder_client_data;
	unsigned i;
	long int n;

	decoder_client_data.got_data = false;
	decoder_client_data.total_samples = 0;
	decoder_client_data.quiet = false;
	decoder_client_data.ignore_errors = false;
	decoder_client_data.error_occurred = false;

	printf("\n+++ seek test: FLAC__StreamDecoder (%s FLAC, read_mode=%u)\n\n", is_ogg? "Ogg":"native", read_mode);

	decoder = FLAC__stream_decoder_new();
	if(0 == decoder)
		return die_("FLAC__stream_decoder_new() FAILED, returned NULL\n");

	if(is_ogg) {
		if(FLAC__stream_decoder_init_ogg_file(decoder, filename, write_callback_, metadata_callback_, error_callback_, &decoder_client_data) != FLAC__STREAM_DECODER_INIT_STATUS_OK)
			return die_s_("FLAC__stream_decoder_init_file() FAILED", decoder);
	}
	else {
		if(FLAC__stream_decoder_init_file(decoder, filename, write_callback_, metadata_callback_, error_callback_, &decoder_client_data) != FLAC__STREAM_DECODER_INIT_STATUS_OK)
			return die_s_("FLAC__stream_decoder_init_file() FAILED", decoder);
	}

	if(!FLAC__stream_decoder_process_until_end_of_metadata(decoder))
		return die_s_("FLAC__stream_decoder_process_until_end_of_metadata() FAILED", decoder);

	if(!is_ogg) { /* not necessary to do this for Ogg because of its seeking method */
	/* process until end of stream to make sure we can still seek in that state */
		decoder_client_data.quiet = true;
		if(!FLAC__stream_decoder_process_until_end_of_stream(decoder))
			return die_s_("FLAC__stream_decoder_process_until_end_of_stream() FAILED", decoder);
		decoder_client_data.quiet = false;

		printf("stream decoder state is %s\n", FLAC__stream_decoder_get_resolved_state_string(decoder));
		if(FLAC__stream_decoder_get_state(decoder) != FLAC__STREAM_DECODER_END_OF_STREAM)
			return die_s_("expected FLAC__STREAM_DECODER_END_OF_STREAM", decoder);
	}

#ifdef _MSC_VER
	printf("file's total_samples is %I64u\n", decoder_client_data.total_samples);
#else
	printf("file's total_samples is %llu\n", (unsigned long long)decoder_client_data.total_samples);
#endif
#if !defined _MSC_VER && !defined __MINGW32__ && !defined __EMX__
	if (decoder_client_data.total_samples > (FLAC__uint64)RAND_MAX) {
		printf("ERROR: must be total_samples < %u\n", (unsigned)RAND_MAX);
		return false;
	}
#endif
	n = (long int)decoder_client_data.total_samples;

	if(n == 0 && total_samples >= 0)
		n = (long int)total_samples;

	/* if we don't have a total samples count, just guess based on the file size */
	/* @@@ for is_ogg we should get it from last page's granulepos */
	if(n == 0) {
		/* 8 would imply no compression, 9 guarantees that we will get some samples off the end of the stream to test that case */
		n = 9 * filesize / (decoder_client_data.channels * decoder_client_data.bits_per_sample);
#if !defined _MSC_VER && !defined __MINGW32__
		if(n > RAND_MAX)
			n = RAND_MAX;
#endif
	}

	printf("Begin seek barrage, count=%u\n", count);

	for (i = 0; !stop_signal_ && (count == 0 || i < count); i++) {
		FLAC__uint64 pos;

		/* for the first 10, seek to the first 10 samples */
		if (n >= 10 && i < 10) {
			pos = i;
		}
		/* for the second 10, seek to the last 10 samples */
		else if (n >= 10 && i < 20) {
			pos = n - 1 - (i-10);
		}
		/* for the third 10, seek past the end and make sure we fail properly as expected */
		else if (i < 30) {
			pos = n + (i-20);
		}
		else {
#if !defined _MSC_VER && !defined __MINGW32__
			pos = (FLAC__uint64)(random() % n);
#else
			/* RAND_MAX is only 32767 in my MSVC */
			pos = (FLAC__uint64)((rand()<<15|rand()) % n);
#endif
		}

#ifdef _MSC_VER
		printf("seek(%I64u)... ", pos);
#else
		printf("seek(%llu)... ", (unsigned long long)pos);
#endif
		fflush(stdout);
		if(!FLAC__stream_decoder_seek_absolute(decoder, pos)) {
			if(pos >= (FLAC__uint64)n)
				printf("seek past end failed as expected... ");
			else if(decoder_client_data.total_samples == 0 && total_samples <= 0)
				printf("seek failed, assuming it was past EOF... ");
			else
				return die_s_("FLAC__stream_decoder_seek_absolute() FAILED", decoder);
			if(!FLAC__stream_decoder_flush(decoder))
				return die_s_("FLAC__stream_decoder_flush() FAILED", decoder);
		}
		else if(read_mode == 1) {
			printf("decode_frame... ");
			fflush(stdout);
			if(!FLAC__stream_decoder_process_single(decoder))
				return die_s_("FLAC__stream_decoder_process_single() FAILED", decoder);

			printf("decode_frame... ");
			fflush(stdout);
			if(!FLAC__stream_decoder_process_single(decoder))
				return die_s_("FLAC__stream_decoder_process_single() FAILED", decoder);
		}
		else if(read_mode == 2) {
			printf("decode_all... ");
			fflush(stdout);
			decoder_client_data.quiet = true;
			if(!FLAC__stream_decoder_process_until_end_of_stream(decoder))
				return die_s_("FLAC__stream_decoder_process_until_end_of_stream() FAILED", decoder);
			decoder_client_data.quiet = false;
		}

		printf("OK\n");
		fflush(stdout);
	}

	if(FLAC__stream_decoder_get_state(decoder) != FLAC__STREAM_DECODER_UNINITIALIZED) {
		if(!FLAC__stream_decoder_finish(decoder))
			return die_s_("FLAC__stream_decoder_finish() FAILED", decoder);
	}

	FLAC__stream_decoder_delete(decoder);
	printf("\nPASSED!\n");

	return true;
}
Esempio n. 6
0
unsigned FLAC_plugin__decode(FLAC__StreamDecoder *decoder, stream_data_struct *stream_data, char *sample_buffer)
{
	/* fill reservoir */
	while (wide_samples_in_reservoir_ < SAMPLES_PER_WRITE)
	{
		if (FLAC__stream_decoder_get_state(decoder) == FLAC__STREAM_DECODER_END_OF_STREAM)
		{
			stream_data->eof = true;
			break;
		}
		else if (!FLAC__stream_decoder_process_single(decoder))
		{
			FLAC_plugin__show_error("Error while processing frame (%s).", FLAC__stream_decoder_get_resolved_state_string(decoder));
			stream_data->eof = true;
			break;
		}
		if (!FLAC__stream_decoder_get_decode_position(decoder, &decode_position))
			decode_position = 0;
	}
	/* output samples */
	if (wide_samples_in_reservoir_ > 0)
	{
		const unsigned n = min(wide_samples_in_reservoir_, SAMPLES_PER_WRITE);
		const unsigned channels = stream_data->channels;
		unsigned i;
		int bytes;

		if (cfg.replaygain.enable && stream_data->has_replaygain)
		{
			bytes = FLAC__replaygain_synthesis__apply_gain(
				sample_buffer,
				true, /* little_endian_data_out */
				stream_data->output_bits_per_sample == 8, /* unsigned_data_out */
				reservoir__,
				n,
				channels,
				stream_data->bits_per_sample,
				stream_data->output_bits_per_sample,
				stream_data->replay_scale,
				cfg.replaygain.hard_limit,
				cfg.resolution.replaygain.dither,
				&stream_data->dither_context
			);
		}
		else
		{
			bytes = FLAC__plugin_common__pack_pcm_signed_little_endian(
				sample_buffer,
				reservoir__,
				n,
				channels,
				stream_data->bits_per_sample,
				stream_data->output_bits_per_sample
			);
		}

		wide_samples_in_reservoir_ -= n;
		for (i = 0; i < channels; i++)
			memmove(&reservoir_[i][0], &reservoir_[i][n], sizeof(reservoir_[0][0]) * wide_samples_in_reservoir_);

		return bytes;
	}
	else
	{
		stream_data->eof = true;
		return 0;
	}
}
Esempio n. 7
0
int32_t MV_PlayFLAC
(
 char *ptr,
 uint32_t ptrlength,
 int32_t   loopstart,
 int32_t   loopend,
 int32_t   pitchoffset,
 int32_t   vol,
 int32_t   left,
 int32_t   right,
 int32_t   priority,
 uint32_t callbackval
 )

{
   VoiceNode   *voice;
   flac_data * fd = 0;
   FLAC__Metadata_Chain* metadata_chain;

   UNREFERENCED_PARAMETER(loopend);

   if ( !MV_Installed )
   {
      MV_SetErrorCode( MV_NotInstalled );
      return MV_Error;
   }

   fd = (flac_data *) malloc( sizeof(flac_data) );
   if (!fd) {
      MV_SetErrorCode( MV_InvalidFLACFile );
      return MV_Error;
   }

   memset(fd, 0, sizeof(flac_data));
   fd->ptr = ptr;
   fd->pos = 0;
   fd->blocksize = 0;
   fd->length = ptrlength;

   fd->block = NULL;

   fd->stream = FLAC__stream_decoder_new();
   fd->sample_pos = 0;

    FLAC__stream_decoder_set_metadata_ignore_all(fd->stream);

   if (FLAC__stream_decoder_init_stream(fd->stream,
            read_flac_stream,
            seek_flac_stream,
            tell_flac_stream,
            length_flac_stream,
            eof_flac_stream,
            write_flac_stream,
            /*metadata_flac_stream*/ NULL,
            error_flac_stream,
            (void*) fd) != FLAC__STREAM_DECODER_INIT_STATUS_OK) {
      MV_Printf("MV_PlayFLAC: %s\n", FLAC__stream_decoder_get_resolved_state_string(fd->stream));
      MV_SetErrorCode( MV_InvalidFLACFile );
      return MV_Error;
   }

   // Request a voice from the voice pool
   voice = MV_AllocVoice( priority );
   if ( voice == NULL )
   {
      FLAC__stream_decoder_finish(fd->stream);
      FLAC__stream_decoder_delete(fd->stream);
      free(fd);
      MV_SetErrorCode( MV_NoVoices );
      return MV_Error;
   }

   fd->owner = voice;

   voice->wavetype    = FLAC;
   voice->extra       = (void*)fd;
   voice->GetSound    = MV_GetNextFLACBlock;
   voice->NextBlock   = fd->block;
   voice->DemandFeed  = NULL;
   voice->LoopCount   = 0;
   voice->BlockLength = 0;
   voice->PitchScale  = PITCH_GetScale( pitchoffset );
   voice->next        = NULL;
   voice->prev        = NULL;
   voice->priority    = priority;
   voice->callbackval = callbackval;

   voice->Playing     = TRUE;
   voice->Paused      = FALSE;

   voice->LoopStart   = 0;
   voice->LoopEnd     = 0;
   voice->LoopSize    = (loopstart >= 0 ? 1 : 0);

    // parse metadata
    // loop parsing designed with multiple repetitions in mind
    // In retrospect, it may be possible to MV_GetVorbisCommentLoops(voice, (vorbis_comment *) tags->data.vorbis_comment)
    // but libvorbisfile may be confused by the signedness of char* vs FLAC__byte* and this code does not depend on HAVE_VORBIS.
    metadata_chain = FLAC__metadata_chain_new();
    if (metadata_chain != NULL)
    {
        if (FLAC__metadata_chain_read_with_callbacks(metadata_chain, fd, flac_callbacks))
        {
            FLAC__Metadata_Iterator* metadata_iterator = FLAC__metadata_iterator_new();
            if (metadata_iterator != NULL)
            {
                char *vc_loopstart = NULL;
                char *vc_loopend = NULL;
                char *vc_looplength = NULL;

                FLAC__metadata_iterator_init(metadata_iterator, metadata_chain);

                do
                {
                    FLAC__StreamMetadata *tags = FLAC__metadata_iterator_get_block(metadata_iterator);

                    if (tags->type == FLAC__METADATA_TYPE_STREAMINFO)
                    {
                        const FLAC__StreamMetadata_StreamInfo *info = &tags->data.stream_info;

                        if (info->channels != 1 && info->channels != 2) {
                           FLAC__metadata_object_delete(tags);
                           FLAC__metadata_iterator_delete(metadata_iterator);
                          // FLAC__metadata_chain_delete(metadata_chain);
                           FLAC__stream_decoder_finish(fd->stream);
                           FLAC__stream_decoder_delete(fd->stream);
                           free(fd);
                           MV_SetErrorCode( MV_InvalidFLACFile );
                           return MV_Error;
                        }

                        voice->channels     = info->channels;
                        voice->bits         = info->bits_per_sample;
                        voice->SamplingRate = info->sample_rate;
                    }

                    // load loop tags from metadata
                    if (tags->type == FLAC__METADATA_TYPE_VORBIS_COMMENT)
                    {
                        FLAC__uint32 comment;
                        uint8_t loopTagCount;
                        for (comment = 0; comment < tags->data.vorbis_comment.num_comments; ++comment)
                        {
                            const char *entry = (const char *) tags->data.vorbis_comment.comments[comment].entry;
                            if (entry != NULL && entry[0] != '\0')
                            {
                                const char *value = strchr(entry,'=');
                                const size_t field = value-entry;
                                value += 1;

                                for (loopTagCount = 0; loopTagCount < loopStartTagCount && vc_loopstart == NULL; ++loopTagCount)
                                    if (strncasecmp(entry, loopStartTags[loopTagCount], field) == 0)
                                        vc_loopstart = strdup(value);

                                for (loopTagCount = 0; loopTagCount < loopEndTagCount && vc_loopend == NULL; ++loopTagCount)
                                    if (strncasecmp(entry, loopEndTags[loopTagCount], field) == 0)
                                        vc_loopend = strdup(value);

                                for (loopTagCount = 0; loopTagCount < loopLengthTagCount && vc_looplength == NULL; ++loopTagCount)
                                    if (strncasecmp(entry, loopLengthTags[loopTagCount], field) == 0)
                                        vc_looplength = strdup(value);
                            }
                        }
                    }

                    FLAC__metadata_object_delete(tags); // If it wasn't for this I would assign pointers instead of strdup().
                }
                while (FLAC__metadata_iterator_next(metadata_iterator));

                if (vc_loopstart != NULL)
                {
                    {
                        const FLAC__int64 flac_loopstart = atol(vc_loopstart);
                        if (flac_loopstart >= 0) // a loop starting at 0 is valid
                        {
                            voice->LoopStart = (const char *) (intptr_t) flac_loopstart;
                            voice->LoopSize = 1;
                        }
                    }
                    free(vc_loopstart);
                }
                if (vc_loopend != NULL)
                {
                    if (voice->LoopSize > 0)
                    {
                        const FLAC__int64 flac_loopend = atol(vc_loopend);
                        if (flac_loopend > 0) // a loop ending at 0 is invalid
                            voice->LoopEnd = (const char *) (intptr_t) flac_loopend;
                    }
                    free(vc_loopend);
                }
                if (vc_looplength != NULL)
                {
                    if (voice->LoopSize > 0 && voice->LoopEnd == 0)
                    {
                        const FLAC__int64 flac_looplength = atol(vc_looplength);
                        if (flac_looplength > 0) // a loop of length 0 is invalid
                            voice->LoopEnd = (const char *) ((intptr_t) flac_looplength + (intptr_t) voice->LoopStart);
                    }
                    free(vc_looplength);
                }

                FLAC__metadata_iterator_delete(metadata_iterator);
            }
            else
                MV_Printf("Error allocating FLAC__Metadata_Iterator!\n");
        }
        else
            MV_Printf("%s\n", FLAC__Metadata_ChainStatusString[FLAC__metadata_chain_status(metadata_chain)]);

       // FLAC__metadata_chain_delete(metadata_chain); // when run with GDB, this throws SIGTRAP about freed heap memory being modified
    }
    else
        MV_Printf("Error allocating FLAC__Metadata_Chain!\n");

   // CODEDUP multivoc.c MV_SetVoicePitch
   voice->RateScale    = ( voice->SamplingRate * voice->PitchScale ) / MV_MixRate;
   voice->FixedPointBufferSize = ( voice->RateScale * MV_MIXBUFFERSIZE ) -
      voice->RateScale;
   MV_SetVoiceMixMode( voice );

   MV_SetVoiceVolume( voice, vol, left, right );
   MV_PlayVoice( voice );

   return voice->handle;
}