Hello, I'm currently doing a little music player using libflac and libao. What
I've currently done works as it should, but I have a problem where only one
metadata block is detected, even if there are more (it doesn't have the last
attribute set to true).

This is using flac 1.3.2 on Gentoo amd64.

The main code file is attached, it mainly follows the examples given with the
libflac and libao doc (using the stream_decoder with all the relevant
callbacks; including metadata, the one causing me problems here).
And here's what metaflac --list gives me on a test file:

METADATA block #0
  type: 0 (STREAMINFO)
  is last: false
  length: 34
  minimum blocksize: 4608 samples
  maximum blocksize: 4608 samples
  minimum framesize: 2291 bytes
  maximum framesize: 10321 bytes
  sample_rate: 44100 Hz
  channels: 2
  bits-per-sample: 16
  total samples: 8800596
  MD5 signature: 9e471211972413ceb8febcdc77ef9d68
METADATA block #1
  type: 4 (VORBIS_COMMENT)
  is last: false
  length: 439
  vendor string: Lavf57.83.100
  comments: 14
    comment[0]: REPLAYGAIN_ALGORITHM=ITU-R BS.1770
    comment[1]: REPLAYGAIN_REFERENCE_LOUDNESS=-18.00
    comment[2]: REPLAYGAIN_TRACK_GAIN=8.53 dB
    comment[3]: REPLAYGAIN_TRACK_PEAK=0.218024
    comment[4]: REPLAYGAIN_ALBUM_GAIN=-0.03 dB
    comment[5]: REPLAYGAIN_ALBUM_PEAK=0.695334
    comment[6]: encoder=Lavf57.83.100
    comment[7]: ARTIST=Hirilorn
    comment[8]: TITLE=Return of the Druids
    comment[9]: TRACKNUMBER=01
    comment[10]: TRACKTOTAL=9
    comment[11]: ALBUMARTIST=Hirilorn / Nasav
    comment[12]: ALBUM=A Hymn to the Ancient Souls / Umr at tawil
    comment[13]: DATE=2000
METADATA block #2
  type: 1 (PADDING)
  is last: true
  length: 8192


If anyone has the solution or some clues, I'd be glad to hear it.

Regards,
Hadrien Lacour


PS: sorry for posting this also on f...@xiph.org, I confused the two lists
#include <pthread.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>

#include <FLAC/stream_decoder.h>
#include <ao/ao.h>

#include "misc.h"


typedef struct
{
	ao_sample_format aofmt;
	int outfd;
} flac_ctx;


void flac_metadata_callback(const FLAC__StreamDecoder *decoder,
	const FLAC__StreamMetadata *metadata, void *client_data)
{
	(void)decoder;
	flac_ctx *ctx = client_data;

	fprintf(stderr, "Metadata block, type: %d, last: %s\n", metadata->type,
		metadata->is_last ? "True" : "False");
	if(metadata->type == FLAC__METADATA_TYPE_STREAMINFO)
	{
		ctx->aofmt.bits = metadata->data.stream_info.bits_per_sample;
		ctx->aofmt.byte_format = AO_FMT_NATIVE;
		ctx->aofmt.channels = metadata->data.stream_info.channels;
		ctx->aofmt.matrix = NULL; /*TODO: read from VorbisComments?*/
		ctx->aofmt.rate = metadata->data.stream_info.sample_rate;
	}
	else if(metadata->type == FLAC__METADATA_TYPE_VORBIS_COMMENT)
	{
		fprintf(stderr, "Vorbis comment:\n");
		const FLAC__StreamMetadata_VorbisComment vc =
			metadata->data.vorbis_comment;
		for(size_t i = 0; i < vc.num_comments; ++i)
		{
			const char *data = (const char *)vc.comments[i].entry;
			const char *val = strchr(data, '=');
			fprintf(stderr, "	 %*s: %s\n", (int)(val - data), data, val + 1);
		}
	}
}

void flac_error_callback(const FLAC__StreamDecoder *decoder,
	FLAC__StreamDecoderErrorStatus status, void *client_data)
{
	(void)decoder, (void)client_data;

	fprintf(stderr, "FLAC decoding error: %s\n",
		FLAC__StreamDecoderErrorStatusString[status]);
}

bool interleave16(const FLAC__int32 * const restrict inbuf[],
	size_t nbchannel, size_t nbsample, int outfd)
{
	static FLAC__int16 interleave_buf[BUFSIZ * 2];

	switch(nbchannel)
	{
		case 2: ; /* Let the compiler optimize the most common case */
			const FLAC__int32 *restrict inbufp_l = inbuf[0],
				*restrict inbufp_r = inbuf[1];

			for(size_t to_write, i = 0; i < nbsample; i += to_write)
			{
				to_write = u64min(nbsample - i, BUFSIZ);
				FLAC__int16 *restrict outbufp = interleave_buf;

				for(size_t n = 0; n < to_write; ++n)
				{
					*(outbufp++) = *(inbufp_l++);
					*(outbufp++) = *(inbufp_r++);
				}
				const size_t nbyte = to_write * sizeof(FLAC__int16) * 2;
				if(!write_full(outfd, interleave_buf, nbyte))
					return false;
			}
			break;

		default: ;
			const FLAC__int32 *restrict inbufp[nbchannel];
			memcpy((void *)inbufp, (const void *)inbuf,
				nbchannel * sizeof(FLAC__int32 *));

			for(size_t to_write, i = 0; i < nbsample; i += to_write)
			{
				to_write = u64min(nbsample - i, BUFSIZ);
				FLAC__int16 *restrict outbufp = interleave_buf;

				for(size_t n = 0; n < to_write; ++n)
				{
					for(size_t chan = 0; chan < nbchannel; ++chan)
						*(outbufp++) = *(inbufp[chan]++);
				}
				const size_t nbyte = to_write * sizeof(FLAC__int16) *
					nbchannel;
				if(!write_full(outfd, interleave_buf, nbyte))
					return false;
			}
			break;
	}
	return true;
}

FLAC__StreamDecoderWriteStatus flac_write_callback(
	const FLAC__StreamDecoder *decoder, const FLAC__Frame *frame,
	const FLAC__int32 * const buffer[], void *client_data)
{
	(void)decoder;
	flac_ctx *ctx = client_data;
	const size_t nbchannel = ctx->aofmt.channels,
		nbsample = frame->header.blocksize;

	switch(ctx->aofmt.bits)
	{
		case 16:
			if(!interleave16(buffer, nbchannel, nbsample, ctx->outfd))
				return FLAC__STREAM_DECODER_WRITE_STATUS_ABORT;
			break;

		default:
			fprintf(stderr, "Only 16 bit FLAC is supported\n");
			return FLAC__STREAM_DECODER_WRITE_STATUS_ABORT;
	}
	return FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE;
}

ao_device * ao_init(ao_sample_format aofmt, const char *aodrivername)
{
	ao_initialize();
	int aodriver = aodrivername ? ao_driver_id(aodrivername) :
		ao_default_driver_id();
	ao_info *aoinfo = ao_driver_info(aodriver);
	printf("Using driver \"%s\"\n", aoinfo->name);
	ao_device *ret = ao_open_live(aodriver, &aofmt, NULL);
	if(ret == NULL)
		exit(EXIT_FAILURE);
	return ret;
}

FLAC__StreamDecoder * flac_init(const char *path, flac_ctx *ctx)
{
	FLAC__StreamDecoder *ret = 0;
	FLAC__StreamDecoderInitStatus init_status;

	if((ret = FLAC__stream_decoder_new()) == NULL)
		die("Couldn't create flac decoder");

	FLAC__stream_decoder_set_md5_checking(ret, true);

	init_status = FLAC__stream_decoder_init_file(ret, path,
		flac_write_callback, flac_metadata_callback, flac_error_callback,
		ctx);
	if(init_status != FLAC__STREAM_DECODER_INIT_STATUS_OK)
	{
		die("Flac decoder initialization error: %s\n",
			FLAC__StreamDecoderInitStatusString[init_status]);
	}
	return ret;
}

void flac_decoder_state_error(FLAC__StreamDecoder *decoder)
{
	fprintf(stderr, "FLAC decoder error: %s\n",
		FLAC__StreamDecoderStateString[
			FLAC__stream_decoder_get_state(decoder)]);
}

void * decoder_routine(void *arg)
{
	FLAC__StreamDecoder *decoder = arg;
	FLAC__bool *ret = malloc(sizeof(FLAC__bool));
	*ret = FLAC__stream_decoder_process_until_end_of_stream(decoder);
	return ret;
}


int main(int argc, char **argv)
{
	int pipefd[2];
	errchk(pipe(pipefd) == 0, NULL);
	flac_ctx ctx = {.outfd = pipefd[1]};

	FLAC__StreamDecoder *decoder = flac_init(argv[1], &ctx);
	if(!FLAC__stream_decoder_process_until_end_of_metadata(decoder))
	{
		flac_decoder_state_error(decoder);
		return EXIT_SUCCESS;
	}
	ao_device *aodev = ao_init(ctx.aofmt, argc > 2 ? argv[2] : NULL);

	pthread_t decoder_thread;
	pthread_create(&decoder_thread, NULL, decoder_routine, decoder);

	char pcm_buf[BUFSIZ * 2];
	ssize_t ret;
	while((ret = read(pipefd[0], pcm_buf, BUFSIZ)) > 0)
		ao_play(aodev, pcm_buf, ret);

	FLAC__bool *decoder_status;
	pthread_join(decoder_thread, (void **)&decoder_status);
	if(!*decoder_status)
		flac_decoder_state_error(decoder);
	free(decoder_status);

	ao_close(aodev);
	ao_shutdown();
	errchk(ret == 0, "Pipe read");
	return EXIT_SUCCESS;
}
_______________________________________________
flac-dev mailing list
flac-dev@xiph.org
http://lists.xiph.org/mailman/listinfo/flac-dev

Reply via email to