Skip to content

Instantly share code, notes, and snippets.

@nu774
Created March 26, 2025 07:17
Show Gist options
  • Save nu774/d82ffd0b0c637b34fb3369cdc779e1cb to your computer and use it in GitHub Desktop.
Save nu774/d82ffd0b0c637b34fb3369cdc779e1cb to your computer and use it in GitHub Desktop.
scan chained oggflac file with libFLAC 1.5+
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <FLAC/stream_decoder.h>
#include <FLAC/format.h>
typedef struct context_t {
FLAC__StreamMetadata_StreamInfo si;
FLAC__StreamDecoderErrorStatus err;
char **vc;
} context_t;
FLAC__StreamDecoderWriteStatus
write_callback(const FLAC__StreamDecoder *decoder, const FLAC__Frame *frame,
const FLAC__int32 *const buffer[], void *client_data) {
return FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE;
}
void error_callback(const FLAC__StreamDecoder *decoder,
FLAC__StreamDecoderErrorStatus status, void *client_data) {
((context_t *)client_data)->err = status;
}
void metadata_callback(const FLAC__StreamDecoder *decoder,
const FLAC__StreamMetadata *metadata,
void *client_data) {
context_t *ctx = (context_t *)client_data;
switch (metadata->type) {
case FLAC__METADATA_TYPE_STREAMINFO: {
ctx->si = metadata->data.stream_info;
break;
}
case FLAC__METADATA_TYPE_VORBIS_COMMENT: {
FLAC__StreamMetadata_VorbisComment vc = metadata->data.vorbis_comment;
unsigned ptr_area_size = (vc.num_comments + 1)* sizeof(char*);
int all_len = 0;
for (int i = 0; i < vc.num_comments; ++i) {
all_len += vc.comments[i].length + 1;
}
void *buffer = malloc(ptr_area_size + all_len);
ctx->vc = buffer;
char *bp = (char*)buffer + ptr_area_size;
for (int i = 0; i < vc.num_comments; ++i) {
memcpy(bp, vc.comments[i].entry, vc.comments[i].length);
bp[vc.comments[i].length] = 0;
ctx->vc[i] = bp;
bp += vc.comments[i].length + 1;
}
ctx->vc[vc.num_comments] = 0;
break;
}
default:
break;
}
}
int main(int argc, char **argv) {
if (argc == 1) {
fprintf(stderr, "usage: %s FILE\n", argv[0]);
return 1;
}
FLAC__StreamDecoder *decoder = FLAC__stream_decoder_new();
context_t ctx;
memset(&ctx, 0, sizeof ctx);
FLAC__StreamDecoderInitStatus res = FLAC__stream_decoder_init_ogg_file(
decoder, argv[1], write_callback, metadata_callback, error_callback,
&ctx);
if (res != FLAC__STREAM_DECODER_INIT_STATUS_OK) {
fprintf(stderr, "ERROR: cannot open %s: error=%d", argv[1], res);
FLAC__stream_decoder_delete(decoder);
return 2;
}
FLAC__stream_decoder_set_decode_chained_stream(decoder, 1);
FLAC__stream_decoder_set_metadata_respond(decoder,
FLAC__METADATA_TYPE_STREAMINFO);
FLAC__stream_decoder_set_metadata_respond(decoder,
FLAC__METADATA_TYPE_VORBIS_COMMENT);
for (int track = 1;; ++track) {
memset(&ctx, 0, sizeof ctx);
if (!FLAC__stream_decoder_process_until_end_of_metadata(decoder) || ctx.err)
break;
printf("stream #%d sample rate: %d, channels: %d, bits: %d, duration: %g\n",
track,
ctx.si.sample_rate, ctx.si.channels, ctx.si.bits_per_sample,
(double)ctx.si.total_samples / ctx.si.sample_rate);
if (ctx.vc) {
for (int i = 0; ctx.vc[i] != NULL; i++) {
printf("%s\n", ctx.vc[i]);
}
free(ctx.vc);
}
if (!FLAC__stream_decoder_skip_single_link(decoder))
break;
if (FLAC__stream_decoder_get_state(decoder) == FLAC__STREAM_DECODER_END_OF_STREAM)
break;
putchar('\n');
}
FLAC__stream_decoder_delete(decoder);
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment