Skip to content

Instantly share code, notes, and snippets.

@kuzux
Created January 14, 2023 19:23
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save kuzux/d6d86f405195973419b9ee80a623b2b2 to your computer and use it in GitHub Desktop.
Save kuzux/d6d86f405195973419b9ee80a623b2b2 to your computer and use it in GitHub Desktop.
#include <stddef.h>
#include <stdint.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
typedef struct {
uint8_t* buf;
int curr;
int len;
} stream_t;
typedef struct {
// all unchanged from the header, ids of these things
uint8_t id;
uint8_t level;
uint8_t bitrate;
uint8_t samplerate;
} frame_header_t;
// returns read byte
// or negative on error (eof)
int read_next_byte(stream_t* stream, uint8_t* byte) {
if(stream->curr == stream->len) return -1;
*byte = stream->buf[stream->curr++];
return (int)*byte;
}
// returns header position
// or negative on error (eof)
int read_next_frame(stream_t* stream, frame_header_t* new_frame) {
uint8_t byte = 0x00;
while(true) {
uint8_t byte1;
int pos = stream->curr;
if(read_next_byte(stream, &byte1) < 0) break;
if(byte1 != 0xFF) continue;
uint8_t byte2;
if(read_next_byte(stream, &byte2) < 0) break;
if(byte2 & 0XF0 != 0xF0) continue;
uint8_t byte3, byte4;
if(read_next_byte(stream, &byte3) < 0) break;
if(read_next_byte(stream, &byte4) < 0) break;
new_frame->id = (byte2 >> 3) & 0x01;
new_frame->level = (byte2 >> 1) & 0x03;
new_frame->bitrate = (byte3 >> 4) & 0x0F;
new_frame->samplerate = (byte3 >> 2) & 0x03;
return pos;
}
return -1;
}
// returns 0 on success
// negative on unsupported frame
int is_frame_supported(const frame_header_t* frame) {
// only supporting MPEG I Level 3
if(frame->id != 0x1) return -1;
if(frame->level != 0x1) return -2;
// invalid bitrate & samplerates
if(frame->bitrate == 0xF) return -3;
if(frame->samplerate == 0x3) return -4;
return 0;
}
// returns location of the TAG header
// or negative on error (not found)
int id3v1_header_location(stream_t* stream) {
while(true) {
int pos = stream->curr;
uint8_t byte1, byte2, byte3;
if(read_next_byte(stream, &byte1) < 0) break;
if(byte1 != 'T') {
continue;
}
if(read_next_byte(stream, &byte2) < 0) break;
if(byte2 != 'A') {
stream->curr--;
continue;
}
if(read_next_byte(stream, &byte3) < 0) break;
if(byte3 != 'G') {
stream->curr--;
continue;
}
return pos;
}
return -1;
}
int main(int argc, char** argv) {
assert(argc == 2);
char* filename = argv[1];
FILE* fp = fopen(filename, "rb");
assert(fp);
fseek(fp, 0, SEEK_END);
size_t filelen = ftell(fp);
fseek(fp, 0, SEEK_SET);
printf("%zu bytes in file\n", filelen);
uint8_t* buf = malloc(filelen);
size_t bytes_read = fread(buf, 1, filelen, fp);
assert(bytes_read == filelen);
stream_t stream = { buf, 0, filelen };
int tag_pos = id3v1_header_location(&stream);
stream.curr = 0;
if(tag_pos > 0) {
printf("id3 header found at %d\n", tag_pos);
stream.len = tag_pos;
} else {
printf("id3 header not found\n");
}
size_t num_frames = 0;
size_t valid_frames = 0;
while(true) {
frame_header_t header;
int pos = read_next_frame(&stream, &header);
if(pos < 0) break;
num_frames++;
if(is_frame_supported(&header) < 0) continue;
// printf("frame %zu at pos %d\n", num_frames, pos);
valid_frames++;
}
printf("%zu frames in total, %zu valid\n", num_frames, valid_frames);
free(buf);
fclose(fp);
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment