Mercurial > repos > blastem
diff flac.c @ 2298:9d68799f945b
Added basic FLAC seek implementation and added support for FLAC tracks in CUE sheets
author | Michael Pavone <pavone@retrodev.com> |
---|---|
date | Thu, 09 Mar 2023 22:49:42 -0800 |
parents | 789802d99629 |
children |
line wrap: on
line diff
--- a/flac.c Sun Feb 19 22:00:29 2023 -0800 +++ b/flac.c Thu Mar 09 22:49:42 2023 -0800 @@ -17,6 +17,11 @@ f->offset = relative ? f->offset + offset : offset; } +static uint32_t tell_buffer(flac_file *f) +{ + return f->offset; +} + static uint8_t read_byte_file(flac_file *f) { int result = fgetc(f->read_data); @@ -31,6 +36,11 @@ fseek(f->read_data, offset, relative ? SEEK_CUR : SEEK_SET); } +static uint32_t tell_file(flac_file *f) +{ + return ftell(f->read_data); +} + static void read_chars(flac_file *f, char *dest, uint32_t count) { for (; count > 0; --count) @@ -46,6 +56,19 @@ return ret; } +static uint64_t read64(flac_file *f) +{ + uint64_t value = ((uint64_t)f->read_byte(f)) << 56; + value |= ((uint64_t)f->read_byte(f)) << 48; + value |= ((uint64_t)f->read_byte(f)) << 40; + value |= ((uint64_t)f->read_byte(f)) << 32; + value |= ((uint64_t)f->read_byte(f)) << 24; + value |= f->read_byte(f) << 16; + value |= f->read_byte(f) << 8; + value |= f->read_byte(f); + return value; +} + static uint32_t read_bits(flac_file *f, uint32_t num_bits) { uint32_t ret = 0; @@ -127,6 +150,18 @@ f->seek(f, 16, 1);//MD5 } +static void parse_seektable(flac_file *f, uint32_t size) +{ + f->num_seekpoints = size / 18; + f->seekpoints = calloc(f->num_seekpoints, sizeof(flac_seekpoint)); + for (uint32_t i = 0; i < f->num_seekpoints; i++) + { + f->seekpoints[i].sample_number = read64(f); + f->seekpoints[i].offset = read64(f); + f->seekpoints[i].sample_count = read16(f); + } +} + static uint8_t parse_header(flac_file *f) { char id[4]; @@ -139,10 +174,13 @@ read_meta_block_header(f, &header); if (header.type == STREAMINFO) { parse_streaminfo(f); + } else if (header.type == SEEKTABLE) { + parse_seektable(f, header.size); } else { f->seek(f, header.size, 1); } } while (!header.is_last); + f->first_frame_offset = f->tell(f); return 1; } @@ -152,6 +190,7 @@ f->read_data = buffer; f->read_byte = read_byte_buffer; f->seek = seek_buffer; + f->tell = tell_buffer; f->buffer_size = size; if (parse_header(f)) { return f; @@ -166,6 +205,7 @@ f->read_data = file; f->read_byte = read_byte_file; f->seek = seek_file; + f->tell = tell_file; if (parse_header(f)) { return f; } @@ -186,7 +226,7 @@ mask >>= 1; length++; } - uint64_t value = byte + (mask - 1); + uint64_t value = byte & (mask - 1); for (uint8_t i = 0; i < length; i++) { value <<= 6; @@ -208,7 +248,7 @@ mask >>= 1; length++; } - uint32_t value = byte + (mask - 1); + uint32_t value = byte & (mask - 1); for (uint8_t i = 0; i < length; i++) { value <<= 6; @@ -580,3 +620,38 @@ return 1; } + +void flac_seek(flac_file *f, uint64_t sample_number) +{ + if (sample_number >= f->frame_start_sample && sample_number < f->frame_start_sample + f->frame_block_size) { + f->frame_sample_pos = sample_number - f->frame_start_sample; + return; + } + uint32_t best_seekpoint = f->num_seekpoints + 1; + if (f->num_seekpoints) { + uint64_t best_diff; + for (uint32_t i = 0; i < f->num_seekpoints; i++) + { + if (f->seekpoints[i].sample_number > sample_number) { + continue; + } + uint64_t diff = sample_number - f->seekpoints[i].sample_number; + if (best_seekpoint > f->num_seekpoints || diff < best_diff) { + best_seekpoint = i; + best_diff = diff; + } + } + } + //TODO: more efficient seeking + if (best_seekpoint > f->num_seekpoints) { + f->seek(f, f->first_frame_offset, 0); + } else if (f->seekpoints[best_seekpoint].sample_number > f->frame_start_sample || f->frame_start_sample > sample_number){ + f->seek(f, f->seekpoints[best_seekpoint].offset + f->first_frame_offset, 0); + } + do { + if (!decode_frame(f)) { + return; + } + } while ((f->frame_start_sample + f->frame_block_size) <= sample_number); + f->frame_sample_pos = sample_number - f->frame_start_sample; +}