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