Skip to content

Instantly share code, notes, and snippets.

@xspager
Last active January 8, 2022 17:32
Show Gist options
  • Save xspager/e39aacf1256970daa2db22112671e65f to your computer and use it in GitHub Desktop.
Save xspager/e39aacf1256970daa2db22112671e65f to your computer and use it in GitHub Desktop.
Reads a FLAC file metadata, only the first block for now
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <stdint.h>
#include <string.h>
#include <assert.h>
#define BUFFER_SIZE 4096
typedef struct {
uint16_t min_block_size;
uint16_t max_block_size;
uint32_t min_frame_size;
uint32_t max_frame_size;
uint32_t samplerate;
uint8_t channels;
uint8_t bits_per_sample;
uint64_t total_samples;
uint8_t md5sum[16];
} metadata_block_streaminfo;
enum BLOCK_TYPE {
STREAMINFO = 0,
PADDING,
APPLICATION,
SEEKTABLE,
VORBIS_COMMENT,
CUESHEET,
PICTURE,
};
void populate_metadata_block_streaminfo(uint8_t buffer[], metadata_block_streaminfo *streaminfo)
{
streaminfo->min_block_size = (buffer[0] << 8) + buffer[1];
streaminfo->max_block_size = (buffer[2] << 8) + buffer[3];
streaminfo->min_frame_size = (buffer[4] << 16) + (buffer[5] << 8) + buffer[6];
streaminfo->max_frame_size = (buffer[7] << 16) + (buffer[8] << 8) + buffer[9];
// buffer[12] -> 4 bits from sample rate + 3 bits channels + 1 bit per sample
streaminfo->samplerate = (buffer[10] << 12) + (buffer[11] << 4) + ((buffer[12] & 0xF0) >> 4);
streaminfo->channels = ((buffer[12] & 0x0E) >> 1) +1;
// buffer[13] -> 4 bits for bits per sample and 4 for total sample rates
streaminfo->bits_per_sample = ((buffer[12] & 0x01) << 4) + ((buffer[13] & 0xF0) >> 4) + 1;
streaminfo->total_samples = ((buffer[13] & 0x0F) << 28) + (buffer[14] << 24) + (buffer[15] << 16) + (buffer[16] << 8) + buffer[17];
memcpy(streaminfo->md5sum, &buffer[18], 16);
}
void print_metadata_block_streaminfo(metadata_block_streaminfo *streaminfo)
{
printf(" Min block size: %i\n", streaminfo->min_block_size);
printf(" Max block size: %i\n", streaminfo->max_block_size);
printf(" Min frame size: %i\n", streaminfo->min_frame_size);
printf(" Max frame size: %i\n", streaminfo->max_frame_size);
printf(" Sample rate: %i\n", streaminfo->samplerate);
printf(" Channels: %i\n", streaminfo->channels);
printf(" Bits per sample: %i\n", streaminfo->bits_per_sample);
printf(" Total samples: %llu\n", streaminfo->total_samples);
printf(" MD5Sum: %02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x\n",
streaminfo->md5sum[0],
streaminfo->md5sum[1],
streaminfo->md5sum[2],
streaminfo->md5sum[3],
streaminfo->md5sum[4],
streaminfo->md5sum[5],
streaminfo->md5sum[6],
streaminfo->md5sum[7],
streaminfo->md5sum[8],
streaminfo->md5sum[9],
streaminfo->md5sum[10],
streaminfo->md5sum[11],
streaminfo->md5sum[12],
streaminfo->md5sum[13],
streaminfo->md5sum[14],
streaminfo->md5sum[15]
);
}
void print_metadata_block_vorbis_comment(uint8_t buffer[], int block_size)
{
uint8_t s_buf[BUFFER_SIZE] = { 0 };
uint32_t offset;
uint32_t vendor_string_lenght = buffer[0] + (buffer[1] << 8) + (buffer[2] << 16) + (buffer[3] << 24);
assert(vendor_string_lenght < BUFFER_SIZE); // don't care about the spec. limit to BUFFER_SIZE
offset = 4;
memcpy(s_buf, &buffer[offset], vendor_string_lenght);
s_buf[vendor_string_lenght] = '\0';
offset += vendor_string_lenght;
uint32_t comments_len = buffer[offset] + (buffer[offset+1] << 8) + (buffer[offset+2] << 16) + (buffer[offset+3] << 24);
offset += 4;
//printf(" Vendor string lenght: %d\n", vendor_string_lenght);
printf(" Vendor: %s\n", s_buf);
printf(" Comments len: %d\n", comments_len);
for(int i=0; i < comments_len; i++) {
uint32_t this_comment_lenght = buffer[offset] + (buffer[offset+1] << 8) + (buffer[offset+2] << 16) + (buffer[offset+3] << 24);
offset+=4;
//printf("this comment length = %i\n", this_comment_lenght);
assert(this_comment_lenght < BUFFER_SIZE);
bzero(s_buf, BUFFER_SIZE);
memcpy(s_buf, &buffer[offset], this_comment_lenght);
//s_buf[this_comment_lenght] = '\0';
printf(" comment[%02d] len=%i : %s\n", i, this_comment_lenght, s_buf);
offset+=this_comment_lenght;
}
printf("final offset = %i", offset);
}
int main(int argc, char *argv[])
{
char *filename;
FILE *file;
char marker[4];
uint8_t buffer[BUFFER_SIZE];
metadata_block_streaminfo streaminfo;
if(argc < 2) {
printf("FLAC filename missing\n");
exit(1);
}
filename = argv[1];
file = fopen(filename, "rb");
if(!file) {
printf("can't open file %s\n", filename);
exit(1);
}
fread(&marker, sizeof(marker), 1, file);
if(ferror(file)) {
printf("error reading the file\n");
perror("%s\n");
exit(1);
}
if(strncmp("fLaC", marker, 4) != 0) {
printf("file %s is not a FLAC file\n", filename);
printf("%s\n", marker);
}
uint32_t block_header;
for(int i=0; i<10; i++){
printf("Reading metadata block: %d\n", i);
fread(&block_header, sizeof(block_header), 1, file);
int is_last_block = block_header & 0x80000000; // 1000 0000 00...
printf(" Block header in hex: 0x%x\n", block_header);
uint8_t block_type = block_header & (0x7F000000) >> 24; // 0111 1111 00..
printf(" Block header type: %i\n", block_type);
uint32_t block_size = (block_header & 0xFFFFFF00) >> 24;
printf(" Block header size: %i\n", block_size);
assert(block_size < 65535);
switch(block_type) {
case STREAMINFO:
assert(block_size == 34);
fread(buffer, (sizeof(uint8_t) * 34), 1, file);
populate_metadata_block_streaminfo(buffer, &streaminfo);
print_metadata_block_streaminfo(&streaminfo);
break;
case VORBIS_COMMENT:
assert(block_size < BUFFER_SIZE);
//fread(buffer, block_size, 1, file);
fread(buffer, 400, 1, file);
printf("block_size = %i", block_size);
print_metadata_block_vorbis_comment(buffer, block_size);
break;
case SEEKTABLE:
default:
assert(block_size < BUFFER_SIZE);
// read the bytes anyway
fread(buffer, (sizeof(uint8_t) * block_size), 1, file);
break;
}
if(is_last_block) break;
printf("\n");
}
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment