# HG changeset patch # User Michael Pavone # Date 1676869966 28800 # Node ID 789802d99629ae0a819d4d41e0ca12d345e819cd # Parent eb45ad9d8a3fec471313e4bd39b71a3baf53ba1b Add basic FLAC decoder and add FLAC playback support to the media player diff -r eb45ad9d8a3f -r 789802d99629 Makefile --- a/Makefile Fri Feb 10 23:17:43 2023 -0800 +++ b/Makefile Sun Feb 19 21:12:46 2023 -0800 @@ -196,7 +196,7 @@ endif endif endif -AUDIOOBJS=ym2612.o psg.o wave.o vgm.o event_log.o render_audio.o rf5c164.o oscilloscope.o +AUDIOOBJS=ym2612.o psg.o wave.o flac.o vgm.o event_log.o render_audio.o rf5c164.o oscilloscope.o CONFIGOBJS=config.o tern.o util.o paths.o NUKLEAROBJS=$(FONT) nuklear_ui/blastem_nuklear.o nuklear_ui/sfnt.o RENDEROBJS=ppm.o controller_info.o diff -r eb45ad9d8a3f -r 789802d99629 flac.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flac.c Sun Feb 19 21:12:46 2023 -0800 @@ -0,0 +1,582 @@ +#include +#include +#include +#include "flac.h" + +static uint8_t read_byte_buffer(flac_file *f) +{ + if (f->offset >= f->buffer_size) { + return 0; + } + uint8_t *buf = f->read_data; + return buf[f->offset++]; +} + +static void seek_buffer(flac_file *f, uint32_t offset, uint8_t relative) +{ + f->offset = relative ? f->offset + offset : offset; +} + +static uint8_t read_byte_file(flac_file *f) +{ + int result = fgetc(f->read_data); + if (result == EOF) { + return 0; + } + return result; +} + +static void seek_file(flac_file *f, uint32_t offset, uint8_t relative) +{ + fseek(f->read_data, offset, relative ? SEEK_CUR : SEEK_SET); +} + +static void read_chars(flac_file *f, char *dest, uint32_t count) +{ + for (; count > 0; --count) + { + *(dest++) = f->read_byte(f); + } +} + +static uint16_t read16(flac_file *f) +{ + uint16_t ret = f->read_byte(f) << 8; + ret |= f->read_byte(f); + return ret; +} + +static uint32_t read_bits(flac_file *f, uint32_t num_bits) +{ + uint32_t ret = 0; + while (num_bits) + { + if (!f->bits) { + f->cur_byte = f->read_byte(f); + f->bits = 8; + } + uint32_t new_bits = f->bits; + if (new_bits > num_bits) { + new_bits = num_bits; + } + ret <<= new_bits; + uint32_t mask = (1 << new_bits) - 1; + ret |= (f->cur_byte >> (f->bits - new_bits)) & mask; + f->bits -= new_bits; + num_bits -= new_bits; + } + return ret; +} + +static uint64_t read_bits64(flac_file *f, uint64_t num_bits) +{ + uint64_t ret = 0; + while (num_bits) + { + if (!f->bits) { + f->cur_byte = f->read_byte(f); + f->bits = 8; + } + uint64_t new_bits = f->bits; + if (new_bits > num_bits) { + new_bits = num_bits; + } + ret <<= new_bits; + uint64_t mask = (1 << new_bits) - 1; + ret |= f->cur_byte & mask; + f->cur_byte >>= new_bits; + f->bits -= new_bits; + num_bits -= new_bits; + } + return ret; +} + +typedef struct { + uint32_t size; + uint8_t type; + uint8_t is_last; +} meta_block_header; + +enum { + STREAMINFO, + PADDING, + APPLICATION, + SEEKTABLE, + VORBIS_COMMENT, + CUESHEET, + PICTURE +}; + +static void read_meta_block_header(flac_file *f, meta_block_header *dest) +{ + dest->is_last = read_bits(f, 1); + dest->type = read_bits(f, 7); + dest->size = read_bits(f, 24); +} + +static void parse_streaminfo(flac_file *f) +{ + read16(f);//min block size + read16(f);//max block size + read_bits(f, 24);//min frame size + read_bits(f, 24);//max frame size + f->sample_rate = read_bits(f, 20); + f->channels = read_bits(f, 3) + 1; + f->bits_per_sample = read_bits(f, 5) + 1; + f->total_samples = read_bits64(f, 36); + f->seek(f, 16, 1);//MD5 +} + +static uint8_t parse_header(flac_file *f) +{ + char id[4]; + read_chars(f, id, sizeof(id)); + if (memcmp("fLaC", id, sizeof(id))) { + return 0; + } + meta_block_header header; + do { + read_meta_block_header(f, &header); + if (header.type == STREAMINFO) { + parse_streaminfo(f); + } else { + f->seek(f, header.size, 1); + } + } while (!header.is_last); + return 1; +} + +flac_file *flac_file_from_buffer(void *buffer, uint32_t size) +{ + flac_file *f = calloc(1, sizeof(flac_file)); + f->read_data = buffer; + f->read_byte = read_byte_buffer; + f->seek = seek_buffer; + f->buffer_size = size; + if (parse_header(f)) { + return f; + } + free(f); + return NULL; +} + +flac_file *flac_file_from_file(FILE *file) +{ + flac_file *f = calloc(1, sizeof(flac_file)); + f->read_data = file; + f->read_byte = read_byte_file; + f->seek = seek_file; + if (parse_header(f)) { + return f; + } + free(f); + return NULL; +} + +static uint64_t read_utf64(flac_file *f) +{ + uint8_t byte = f->read_byte(f); + if (!(byte & 0x80)) { + return byte; + } + uint8_t mask = 0x40; + uint8_t length = 0; + while (byte & mask) + { + mask >>= 1; + length++; + } + uint64_t value = byte + (mask - 1); + for (uint8_t i = 0; i < length; i++) + { + value <<= 6; + value |= f->read_byte(f) & 0x3F; + } + return value; +} + +static uint32_t read_utf32(flac_file *f) +{ + uint8_t byte = f->read_byte(f); + if (!(byte & 0x80)) { + return byte; + } + uint8_t mask = 0x40; + uint8_t length = 0; + while (byte & mask) + { + mask >>= 1; + length++; + } + uint32_t value = byte + (mask - 1); + for (uint8_t i = 0; i < length; i++) + { + value <<= 6; + value |= f->read_byte(f) & 0x3F; + } + return value; +} + +static uint8_t parse_frame_header(flac_file *f) +{ + uint16_t sync = read_bits(f, 14); + if (sync != 0x3FFE) { + fprintf(stderr, "Invalid sync FLAC sync pattern: %X\n", sync); + return 0; + } + read_bits(f, 1);//reserved + uint8_t block_size_strategy = read_bits(f, 1); + uint8_t block_size_code = read_bits(f, 4); + uint8_t sample_rate_code = read_bits(f, 4); + uint8_t channels = read_bits(f, 4); + uint8_t joint_stereo = 0; + if (channels > 7) { + joint_stereo = (channels & 7) + 1; + channels = 2; + } else { + ++channels; + } + f->frame_channels = channels; + f->frame_joint_stereo = joint_stereo; + uint8_t bits_per_sample_code = read_bits(f, 3); + if (!bits_per_sample_code) { + f->frame_bits_per_sample = f->bits_per_sample; + } else if (bits_per_sample_code < 3) { + f->frame_bits_per_sample = 4 + 4 * bits_per_sample_code; + } else { + f->frame_bits_per_sample = 4 * bits_per_sample_code; + } + read_bits(f, 1);//reserved + uint32_t block_num; + if (block_size_strategy) { + f->frame_start_sample = read_utf64(f); + } else { + block_num = read_utf32(f); + } + uint32_t block_size = 0; + switch (block_size_code) + { + case 0: + fputs("Detected reserved block size 0", stderr); + return 0; + case 1: + block_size = 192; + break; + case 6: + block_size = f->read_byte(f) + 1; + break; + case 7: + block_size = read16(f) + 1; + break; + default: + if (block_size_code < 8) { + block_size = 576 * (1 << (block_size_code - 2)); + } else { + block_size = 256 * (1 << (block_size_code - 8)); + } + break; + } + f->frame_block_size = block_size; + if (!block_size_strategy) { + f->frame_start_sample = ((uint64_t)block_num) * ((uint64_t)block_size); + } + uint32_t sample_rate; + switch (sample_rate_code) + { + case 15: + fputs("Invalid frame header sample rate", stderr); + case 0: + sample_rate = f->sample_rate; + break; + case 1: + sample_rate = 88200; + break; + case 2: + sample_rate = 176400; + break; + case 3: + sample_rate = 192000; + break; + case 4: + sample_rate = 8000; + break; + case 5: + sample_rate = 16000; + break; + case 6: + sample_rate = 22050; + break; + case 7: + sample_rate = 44100; + break; + case 8: + sample_rate = 32000; + break; + case 9: + sample_rate = 44100; + break; + case 10: + sample_rate = 48000; + break; + case 11: + sample_rate = 96000; + break; + case 12: + sample_rate = f->read_byte(f) * 1000; + break; + case 13: + sample_rate = read16(f); + break; + case 14: + sample_rate = read16(f) * 10; + break; + } + f->frame_sample_rate = sample_rate; + f->read_byte(f);//CRC-8 + return 1; +} + +enum { + SUBFRAME_CONSTANT, + SUBFRAME_VERBATIM, + SUBFRAME_FIXED = 8, + SUBFRAME_LPC = 0x20, +}; + +static int32_t sign_extend(uint32_t value, uint32_t bits) +{ + if (value & (1 << (bits - 1))) { + value |= ~((1 << bits) - 1); + } + return value; +} + +static int32_t signed_sample(uint32_t sample_bits, uint32_t sample, uint8_t wasted_bits) +{ + sample <<= wasted_bits; + sample = sign_extend(sample, sample_bits); + return sample; +} + +static void decode_residuals(flac_file *f, flac_subframe *sub, int64_t *coefficients, uint32_t order, int64_t shift) +{ + uint8_t residual_method = read_bits(f, 2); + uint8_t rice_param_bits = residual_method ? 5 : 4; + uint32_t partition_count = 1 << read_bits(f, 4); + uint32_t cur = order; + uint32_t partition_size = f->frame_block_size / partition_count; + for (uint32_t partition = 0; partition < partition_count; partition++) + { + uint32_t rice_param = read_bits(f, rice_param_bits); + if (rice_param == (1 << rice_param_bits) - 1) { + //escape code, residuals are unencoded + rice_param = read_bits(f, rice_param_bits); + for (uint32_t end = partition ? cur + partition_size : partition_size; cur < end; cur++) + { + int64_t prediction = 0; + for (uint32_t i = 0; i < order; i++) + { + prediction += ((int64_t)sub->decoded[cur - 1 - i]) * coefficients[i]; + } + if (shift) { + prediction >>= shift; + } + prediction += sign_extend(read_bits(f, rice_param), rice_param); + sub->decoded[cur] = prediction; + } + } else { + for (uint32_t end = partition ? cur + partition_size : partition_size; cur < end; cur++) + { + int64_t prediction = 0; + for (uint32_t i = 0; i < order; i++) + { + prediction += ((int64_t)sub->decoded[cur - 1 - i]) * coefficients[i]; + } + if (shift) { + prediction >>= shift; + } + uint32_t residual = 0; + while (!read_bits(f, 1)) + { + ++residual; + } + residual <<= rice_param; + residual |= read_bits(f, rice_param); + if (residual & 1) { + sub->decoded[cur] = prediction - (residual >> 1) - 1; + } else { + sub->decoded[cur] = prediction + (residual >> 1); + } + } + } + } +} + +static void decode_subframe(flac_file *f, flac_subframe *sub) +{ + if (f->frame_block_size > sub->allocated_samples) { + sub->decoded = realloc(sub->decoded, sizeof(int32_t) * f->frame_block_size); + sub->allocated_samples = f->frame_block_size ; + } + int64_t prediction_coefficients[32]; + read_bits(f, 1);//reserved + uint8_t type = read_bits(f, 6); + uint8_t has_wasted_bits = read_bits(f, 1); + uint8_t wasted_bits = 0; + if (has_wasted_bits) { + ++wasted_bits; + while (!read_bits(f, 1)) + { + ++wasted_bits; + } + } + uint32_t sample_bits = f->frame_bits_per_sample - wasted_bits; + if (f->frame_joint_stereo) { + int channel = sub - f->subframes; + if (f->frame_joint_stereo == 2 && !channel || (channel && f->frame_joint_stereo != 2)) { + sample_bits++; + } + } + if (type == SUBFRAME_CONSTANT) { + int32_t sample = signed_sample(sample_bits, read_bits(f, sample_bits), wasted_bits); + for (uint32_t i = 0; i < f->frame_block_size; i++) + { + sub->decoded[i] = sample; + } + } else if (type == SUBFRAME_VERBATIM) { + for (uint32_t i = 0; i < f->frame_block_size; i++) + { + sub->decoded[i] = signed_sample(sample_bits, read_bits(f, sample_bits), wasted_bits); + } + } else if (type & SUBFRAME_LPC) { + uint32_t order = (type & 0x1F) + 1; + for (uint32_t i = 0; i < order; i++) + { + sub->decoded[i] = signed_sample(sample_bits, read_bits(f, sample_bits), wasted_bits); + } + uint32_t coefficient_bits = read_bits(f, 4) + 1; + int64_t shift_bits = read_bits(f, 5); + for (uint32_t i = 0; i < order; i++) + { + prediction_coefficients[i] = sign_extend(read_bits(f, coefficient_bits), coefficient_bits); + } + decode_residuals(f, sub, prediction_coefficients, order, shift_bits); + } else if (type & SUBFRAME_FIXED) { + uint32_t order = type & 7; + for (uint32_t i = 0; i < order; i++) + { + sub->decoded[i] = signed_sample(sample_bits, read_bits(f, sample_bits), wasted_bits); + } + switch (order) + { + case 1: + prediction_coefficients[0] = 1; + break; + case 2: + prediction_coefficients[0] = 2; + prediction_coefficients[1] = -1; + break; + case 3: + prediction_coefficients[0] = 3; + prediction_coefficients[1] = -3; + prediction_coefficients[2] = 1; + break; + case 4: + prediction_coefficients[0] = 4; + prediction_coefficients[1] = -6; + prediction_coefficients[2] = 4; + prediction_coefficients[3] = -1; + break; + } + decode_residuals(f, sub, prediction_coefficients, order, 0); + } else { + fprintf(stderr, "Invalid subframe type %X\n", type); + } +} + +static uint8_t decode_frame(flac_file *f) +{ + if (!parse_frame_header(f)) { + return 0; + } + if (f->frame_channels > f->subframe_alloc) { + f->subframes = realloc(f->subframes, sizeof(flac_subframe) * f->frame_channels); + memset(f->subframes + f->subframe_alloc, 0, sizeof(flac_subframe) * (f->frame_channels - f->subframe_alloc)); + f->subframe_alloc = f->frame_channels; + } + for (uint8_t channel = 0; channel < f->frame_channels; channel++) + { + decode_subframe(f, f->subframes + channel); + } + f->bits = 0; + read16(f);//Frame footer CRC-16 + f->frame_sample_pos = 0; + return 1; +} + +uint8_t flac_get_sample(flac_file *f, int16_t *out, uint8_t desired_channels) +{ + if (f->frame_sample_pos == f->frame_block_size) { + if (!decode_frame(f)) { + return 0; + } + } + uint8_t copy_channels; + if (f->frame_channels == 1 && desired_channels > 1) { + int16_t sample = f->subframes->decoded[f->frame_sample_pos]; + *(out++) = sample; + *(out++) = sample; + copy_channels = 2; + } else { + int32_t left, right, mid, diff; + switch (f->frame_joint_stereo) + { + case 0: + copy_channels = desired_channels; + if (copy_channels > f->frame_channels) { + copy_channels = f->frame_channels; + } + for (uint8_t i = 0; i < copy_channels; i++) + { + *(out++) = f->subframes[i].decoded[f->frame_sample_pos]; + } + break; + case 1: + //left-side + copy_channels = 2; + *(out++) = left = f->subframes[0].decoded[f->frame_sample_pos]; + if (desired_channels > 1) { + *(out++) = left + f->subframes[1].decoded[f->frame_sample_pos]; + } + break; + case 2: + //side-right + copy_channels = 2; + right = f->subframes[1].decoded[f->frame_sample_pos]; + left = right + f->subframes[0].decoded[f->frame_sample_pos]; + *(out++) = left; + if (desired_channels > 1) { + *(out++) = right; + } + break; + case 3: + //mid-side + copy_channels = 2; + mid = f->subframes[0].decoded[f->frame_sample_pos]; + diff = f->subframes[1].decoded[f->frame_sample_pos]; + left = (diff + 2 * mid) >> 1; + *(out++) = left; + if (desired_channels > 1) { + *(out++) = left - diff; + } + break; + } + } + for (uint8_t i = copy_channels; i < desired_channels; i++) + { + *(out++) = 0; + } + f->frame_sample_pos++; + + return 1; +} diff -r eb45ad9d8a3f -r 789802d99629 flac.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/flac.h Sun Feb 19 21:12:46 2023 -0800 @@ -0,0 +1,48 @@ +#ifndef FLAC_H_ +#define FLAC_H_ + +#include +#include + +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 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; + + 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); + +#endif //FLAC_H_ diff -r eb45ad9d8a3f -r 789802d99629 mediaplayer.c --- a/mediaplayer.c Fri Feb 10 23:17:43 2023 -0800 +++ b/mediaplayer.c Sun Feb 19 21:12:46 2023 -0800 @@ -443,7 +443,16 @@ void flac_frame(media_player *player) { - render_sleep_ms(15); + for (uint32_t remaining_samples = player->flac->sample_rate / 60; remaining_samples > 0; remaining_samples--) + { + int16_t samples[2]; + if (flac_get_sample(player->flac, samples, 2)) { + render_put_stereo_sample(player->audio, samples[0], samples[1]); + } else { + player->state = STATE_PAUSED; + return; + } + } } void vgm_init(media_player *player, uint32_t opts) @@ -571,6 +580,14 @@ free(player->wave); } +static void flac_player_init(media_player *player) +{ + player->flac = flac_file_from_buffer(player->media->buffer, player->media->size); + if (player->flac) { + player->audio = render_audio_source("Audio File", player->flac->sample_rate, 1, 2); + } +} + static void resume_player(system_header *system) { media_player *player = (media_player *)system; @@ -689,6 +706,9 @@ case AUDIO_WAVE: wave_player_init(player); break; + case AUDIO_FLAC: + flac_player_init(player); + break; } return player; diff -r eb45ad9d8a3f -r 789802d99629 mediaplayer.h --- a/mediaplayer.h Fri Feb 10 23:17:43 2023 -0800 +++ b/mediaplayer.h Sun Feb 19 21:12:46 2023 -0800 @@ -5,6 +5,7 @@ #include "system.h" #include "vgm.h" #include "wave.h" +#include "flac.h" #include "render_audio.h" typedef struct chip_info chip_info; @@ -28,6 +29,7 @@ vgm_extended_header *vgm_ext; data_block *ym_seek_block; wave_header *wave; + flac_file *flac; audio_source *audio; chip_info *chips; uint32_t num_chips;