# HG changeset patch # User Michael Pavone # Date 1678430982 28800 # Node ID 9d68799f945bdaaca5751352cfd4e713e37620c4 # Parent e6b2b2341c68e6523c8e049af428dfdd08de42c8 Added basic FLAC seek implementation and added support for FLAC tracks in CUE sheets diff -r e6b2b2341c68 -r 9d68799f945b cdimage.c --- a/cdimage.c Sun Feb 19 22:00:29 2023 -0800 +++ b/cdimage.c Thu Mar 09 22:49:42 2023 -0800 @@ -111,17 +111,21 @@ if (track) { lba -= media->tracks[track - 1].end_lba; } - if (media->tracks[track].has_subcodes) { - if (!media->tmp_buffer) { - media->tmp_buffer = calloc(1, 96); + if (media->tracks[track].flac) { + flac_seek(media->tracks[track].flac, (media->tracks[track].file_offset + lba * media->tracks[track].sector_bytes) / 4); + } else { + if (media->tracks[track].has_subcodes) { + if (!media->tmp_buffer) { + media->tmp_buffer = calloc(1, 96); + } + fseek(media->tracks[track].f, media->tracks[track].file_offset + (lba + 1) * media->tracks[track].sector_bytes - 96, SEEK_SET); + int bytes = fread(media->tmp_buffer, 1, 96, media->tracks[track].f); + if (bytes != 96) { + fprintf(stderr, "Only read %d subcode bytes\n", bytes); + } } - fseek(media->tracks[track].f, media->tracks[track].file_offset + (lba + 1) * media->tracks[track].sector_bytes - 96, SEEK_SET); - int bytes = fread(media->tmp_buffer, 1, 96, media->tracks[track].f); - if (bytes != 96) { - fprintf(stderr, "Only read %d subcode bytes\n", bytes); - } + fseek(media->tracks[track].f, media->tracks[track].file_offset + lba * media->tracks[track].sector_bytes, SEEK_SET); } - fseek(media->tracks[track].f, media->tracks[track].file_offset + lba * media->tracks[track].sector_bytes, SEEK_SET); } } return track; @@ -156,12 +160,23 @@ return 0; } else if ((media->tracks[media->cur_track].sector_bytes < 2352 && offset < 16) || offset > (media->tracks[media->cur_track].sector_bytes + 16)) { return fake_read(media->cur_sector, offset); + } else if (media->tracks[media->cur_track].flac) { + if (offset & 3) { + return media->byte_storage[(offset & 3) - 1]; + } else { + int16_t samples[2]; + flac_get_sample(media->tracks[media->cur_track].flac, samples, 2); + media->byte_storage[0] = samples[0] >> 8; + media->byte_storage[1] = samples[1]; + media->byte_storage[2] = samples[1] >> 8; + return samples[0]; + } } else { if (media->tracks[media->cur_track].need_swap) { if (offset & 1) { - return media->byte_storage; + return media->byte_storage[0]; } - media->byte_storage = fgetc(media->tracks[media->cur_track].f); + media->byte_storage[0] = fgetc(media->tracks[media->cur_track].f); } return fgetc(media->tracks[media->cur_track].f); } @@ -198,6 +213,7 @@ int track = -1; uint8_t audio_byte_swap = 0; FILE *f = NULL; + flac_file *flac = NULL; int track_of_file = -1; uint8_t has_index_0 = 0; uint32_t extra_offset = 0; @@ -215,6 +231,7 @@ warning("Expected track %d, but found track %d in CUE sheet\n", track + 1, file_track); } tracks[track].f = f; + tracks[track].flac = flac; cmd = cmd_start(end); @@ -259,11 +276,12 @@ memcpy(fname + dirlen + 1, cmd, end-cmd); fname[dirlen + 1 + (end-cmd)] = 0; } + flac = NULL; f = fopen(fname, "rb"); if (!f) { fatal_error("Failed to open %s specified by FILE command in CUE sheet %s.%s\n", fname, media->name, media->extension); } - free(fname); + track_of_file = -1; for (end++; *end && *end != '\n' && *end != '\r'; end++) { @@ -276,19 +294,29 @@ } else if (startswith(end, "WAVE")) { audio_byte_swap = 0; wave_header wave; - if (!wave_read_header(f, &wave)) { - fatal_error("Wave file %s specified by cute sheet %s.%s is not valid\n", fname, media->name, media->extension); + if (wave_read_header(f, &wave)) { + if (wave.audio_format != 1 || wave.num_channels != 2 || wave.sample_rate != 44100 || wave.bits_per_sample != 16) { + warning("BlastEm only suports WAVE tracks in 16-bit stereo PCM format at 44100 hz, file %s does not match\n", fname); + } + extra_offset = wave.format_header.size + sizeof(wave.data_header) + sizeof(wave.chunk); + } else { + fseek(f, 0, SEEK_SET); + flac = flac_file_from_file(f); + if (!flac) { + fatal_error("WAVE file %s in cue sheet %s.%s is neither a valid WAVE nor a valid FLAC file\n", fname, media->name, media->extension); + } + if (flac->sample_rate != 44100 || flac->bits_per_sample != 16 || flac->channels != 2) { + warning("FLAC files in a CUE sheet should match CD audio specs, %s does not\n", fname); + } + } - if (wave.audio_format != 1 || wave.num_channels != 2 || wave.sample_rate != 44100 || wave.bits_per_sample != 16) { - warning("BlastEm only suports WAVE tracks in 16-bit stereo PCM format at 44100 hz, file %s does not match\n", fname); - } - extra_offset = wave.format_header.size + sizeof(wave.data_header) + sizeof(wave.chunk); } else { warning("Unsupported FILE type in CUE sheet. Only BINARY and MOTOROLA are supported\n"); } break; } } + free(fname); } } } else if (track >= 0) { @@ -344,10 +372,22 @@ for (int track = 0; track < media->num_tracks; track++) { if (track == media->num_tracks - 1 && tracks[track].f) { uint32_t start_lba =tracks[track].fake_pregap ? tracks[track].start_lba : tracks[track].pregap_lba; - tracks[track].end_lba = start_lba + (file_size(tracks[track].f) - tracks[track].file_offset)/ tracks[track].sector_bytes; + uint32_t fsize; + if (tracks[track].flac) { + fsize = tracks[track].flac->total_samples * 4; + } else { + fsize = file_size(tracks[track].f); + } + tracks[track].end_lba = start_lba + (fsize - tracks[track].file_offset)/ tracks[track].sector_bytes; } else if (tracks[track].f != f) { uint32_t start_lba =tracks[track-1].fake_pregap ? tracks[track-1].start_lba : tracks[track-1].pregap_lba; - tracks[track-1].end_lba = start_lba + (file_size(tracks[track-1].f) - tracks[track-1].file_offset)/ tracks[track-1].sector_bytes; + uint32_t fsize; + if (tracks[track-1].flac) { + fsize = tracks[track-1].flac->total_samples * 4; + } else { + fsize = file_size(tracks[track-1].f); + } + tracks[track-1].end_lba = start_lba + (fsize - tracks[track-1].file_offset)/ tracks[track-1].sector_bytes; offset = tracks[track-1].end_lba; } if (!tracks[track].fake_pregap) { @@ -359,7 +399,7 @@ //replace cue sheet with first sector free(media->buffer); media->buffer = calloc(2048, 1); - if (tracks[0].type == TRACK_DATA && tracks[0].sector_bytes == 2352) { + if (tracks[0].type == TRACK_DATA && tracks[0].sector_bytes == 2352 && !tracks[0].flac) { // if the first track is a data track, don't trust the CUE sheet and look at the MM:SS:FF from first sector uint8_t msf[3]; fseek(tracks[0].f, 12, SEEK_SET); @@ -588,10 +628,12 @@ save_int32(buf, 0); } save_int8(buf, media->in_fake_pregap); - save_int8(buf, media->byte_storage); + save_int8(buf, media->byte_storage[0]); if (media->tmp_buffer) { save_buffer8(buf, media->tmp_buffer, 96); } + save_int8(buf, media->byte_storage[1]); + save_int8(buf, media->byte_storage[2]); } void cdimage_deserialize(deserialize_buffer *buf, void *vmedia) @@ -607,8 +649,12 @@ fseek(media->tracks[media->cur_track].f, seekpos, SEEK_SET); } media->in_fake_pregap = load_int8(buf); - media->byte_storage = load_int8(buf); + media->byte_storage[0] = load_int8(buf); if (media->tmp_buffer) { load_buffer8(buf, media->tmp_buffer, 96); } + if (buf->size - buf->cur_pos >= 2) { + media->byte_storage[1] = load_int8(buf); + media->byte_storage[2] = load_int8(buf); + } } diff -r e6b2b2341c68 -r 9d68799f945b flac.c --- 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; +} diff -r e6b2b2341c68 -r 9d68799f945b flac.h --- a/flac.h Sun Feb 19 22:00:29 2023 -0800 +++ b/flac.h Thu Mar 09 22:49:42 2023 -0800 @@ -6,43 +6,54 @@ typedef struct flac_file flac_file; -typedef uint8_t (*flac_read)(flac_file *f); -typedef void (*flac_seek)(flac_file *f, uint32_t offset, uint8_t relative); +typedef uint8_t (*flac_read_fun)(flac_file *f); +typedef void (*flac_seek_fun)(flac_file *f, uint32_t offset, uint8_t relative); +typedef uint32_t (*flac_tell_fun)(flac_file *f); typedef struct { uint32_t allocated_samples; int32_t *decoded; } flac_subframe; -struct flac_file { - uint64_t total_samples; - uint64_t frame_start_sample; - void *read_data; - flac_read read_byte; - flac_seek seek; - flac_subframe *subframes; - uint32_t offset; - uint32_t buffer_size; - - uint32_t frame_sample_pos; - uint32_t remaining_frame_samples; +typedef struct { + uint64_t sample_number; + uint64_t offset; + uint16_t sample_count; +} flac_seekpoint; - uint32_t sample_rate; - uint32_t frame_sample_rate; - uint32_t frame_block_size; - uint8_t bits_per_sample; - uint8_t frame_bits_per_sample; - uint8_t channels; - uint8_t frame_channels; - uint8_t frame_joint_stereo; - uint8_t subframe_alloc; +struct flac_file { + uint64_t total_samples; + uint64_t frame_start_sample; + void *read_data; + flac_read_fun read_byte; + flac_seek_fun seek; + flac_tell_fun tell; + flac_subframe *subframes; + flac_seekpoint *seekpoints; + uint32_t num_seekpoints; + uint32_t offset; + uint32_t buffer_size; + uint32_t first_frame_offset; - uint8_t cur_byte; - uint8_t bits; + uint32_t frame_sample_pos; + + uint32_t sample_rate; + uint32_t frame_sample_rate; + uint32_t frame_block_size; + uint8_t bits_per_sample; + uint8_t frame_bits_per_sample; + uint8_t channels; + uint8_t frame_channels; + uint8_t frame_joint_stereo; + uint8_t subframe_alloc; + + uint8_t cur_byte; + uint8_t bits; }; flac_file *flac_file_from_buffer(void *buffer, uint32_t size); flac_file *flac_file_from_file(FILE *file); uint8_t flac_get_sample(flac_file *f, int16_t *out, uint8_t desired_channels); +void flac_seek(flac_file *f, uint64_t sample_number); #endif //FLAC_H_ diff -r e6b2b2341c68 -r 9d68799f945b system.h --- a/system.h Sun Feb 19 22:00:29 2023 -0800 +++ b/system.h Thu Mar 09 22:49:42 2023 -0800 @@ -3,6 +3,7 @@ #include #include #include +#include "flac.h" typedef struct system_header system_header; typedef struct system_media system_media; @@ -111,6 +112,7 @@ typedef struct { FILE *f; + flac_file *flac; uint32_t file_offset; uint32_t fake_pregap; uint32_t pregap_lba; @@ -142,7 +144,7 @@ uint32_t cur_sector; media_type type; uint8_t in_fake_pregap; - uint8_t byte_storage; + uint8_t byte_storage[3]; }; #define OPT_ADDRESS_LOG (1U << 31U)