Last active
January 8, 2022 17:32
-
-
Save xspager/e39aacf1256970daa2db22112671e65f to your computer and use it in GitHub Desktop.
Reads a FLAC file metadata, only the first block for now
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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