Mercurial > repos > blastem
comparison 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 |
comparison
equal
deleted
inserted
replaced
2297:e6b2b2341c68 | 2298:9d68799f945b |
---|---|
15 static void seek_buffer(flac_file *f, uint32_t offset, uint8_t relative) | 15 static void seek_buffer(flac_file *f, uint32_t offset, uint8_t relative) |
16 { | 16 { |
17 f->offset = relative ? f->offset + offset : offset; | 17 f->offset = relative ? f->offset + offset : offset; |
18 } | 18 } |
19 | 19 |
20 static uint32_t tell_buffer(flac_file *f) | |
21 { | |
22 return f->offset; | |
23 } | |
24 | |
20 static uint8_t read_byte_file(flac_file *f) | 25 static uint8_t read_byte_file(flac_file *f) |
21 { | 26 { |
22 int result = fgetc(f->read_data); | 27 int result = fgetc(f->read_data); |
23 if (result == EOF) { | 28 if (result == EOF) { |
24 return 0; | 29 return 0; |
29 static void seek_file(flac_file *f, uint32_t offset, uint8_t relative) | 34 static void seek_file(flac_file *f, uint32_t offset, uint8_t relative) |
30 { | 35 { |
31 fseek(f->read_data, offset, relative ? SEEK_CUR : SEEK_SET); | 36 fseek(f->read_data, offset, relative ? SEEK_CUR : SEEK_SET); |
32 } | 37 } |
33 | 38 |
39 static uint32_t tell_file(flac_file *f) | |
40 { | |
41 return ftell(f->read_data); | |
42 } | |
43 | |
34 static void read_chars(flac_file *f, char *dest, uint32_t count) | 44 static void read_chars(flac_file *f, char *dest, uint32_t count) |
35 { | 45 { |
36 for (; count > 0; --count) | 46 for (; count > 0; --count) |
37 { | 47 { |
38 *(dest++) = f->read_byte(f); | 48 *(dest++) = f->read_byte(f); |
42 static uint16_t read16(flac_file *f) | 52 static uint16_t read16(flac_file *f) |
43 { | 53 { |
44 uint16_t ret = f->read_byte(f) << 8; | 54 uint16_t ret = f->read_byte(f) << 8; |
45 ret |= f->read_byte(f); | 55 ret |= f->read_byte(f); |
46 return ret; | 56 return ret; |
57 } | |
58 | |
59 static uint64_t read64(flac_file *f) | |
60 { | |
61 uint64_t value = ((uint64_t)f->read_byte(f)) << 56; | |
62 value |= ((uint64_t)f->read_byte(f)) << 48; | |
63 value |= ((uint64_t)f->read_byte(f)) << 40; | |
64 value |= ((uint64_t)f->read_byte(f)) << 32; | |
65 value |= ((uint64_t)f->read_byte(f)) << 24; | |
66 value |= f->read_byte(f) << 16; | |
67 value |= f->read_byte(f) << 8; | |
68 value |= f->read_byte(f); | |
69 return value; | |
47 } | 70 } |
48 | 71 |
49 static uint32_t read_bits(flac_file *f, uint32_t num_bits) | 72 static uint32_t read_bits(flac_file *f, uint32_t num_bits) |
50 { | 73 { |
51 uint32_t ret = 0; | 74 uint32_t ret = 0; |
125 f->bits_per_sample = read_bits(f, 5) + 1; | 148 f->bits_per_sample = read_bits(f, 5) + 1; |
126 f->total_samples = read_bits64(f, 36); | 149 f->total_samples = read_bits64(f, 36); |
127 f->seek(f, 16, 1);//MD5 | 150 f->seek(f, 16, 1);//MD5 |
128 } | 151 } |
129 | 152 |
153 static void parse_seektable(flac_file *f, uint32_t size) | |
154 { | |
155 f->num_seekpoints = size / 18; | |
156 f->seekpoints = calloc(f->num_seekpoints, sizeof(flac_seekpoint)); | |
157 for (uint32_t i = 0; i < f->num_seekpoints; i++) | |
158 { | |
159 f->seekpoints[i].sample_number = read64(f); | |
160 f->seekpoints[i].offset = read64(f); | |
161 f->seekpoints[i].sample_count = read16(f); | |
162 } | |
163 } | |
164 | |
130 static uint8_t parse_header(flac_file *f) | 165 static uint8_t parse_header(flac_file *f) |
131 { | 166 { |
132 char id[4]; | 167 char id[4]; |
133 read_chars(f, id, sizeof(id)); | 168 read_chars(f, id, sizeof(id)); |
134 if (memcmp("fLaC", id, sizeof(id))) { | 169 if (memcmp("fLaC", id, sizeof(id))) { |
137 meta_block_header header; | 172 meta_block_header header; |
138 do { | 173 do { |
139 read_meta_block_header(f, &header); | 174 read_meta_block_header(f, &header); |
140 if (header.type == STREAMINFO) { | 175 if (header.type == STREAMINFO) { |
141 parse_streaminfo(f); | 176 parse_streaminfo(f); |
177 } else if (header.type == SEEKTABLE) { | |
178 parse_seektable(f, header.size); | |
142 } else { | 179 } else { |
143 f->seek(f, header.size, 1); | 180 f->seek(f, header.size, 1); |
144 } | 181 } |
145 } while (!header.is_last); | 182 } while (!header.is_last); |
183 f->first_frame_offset = f->tell(f); | |
146 return 1; | 184 return 1; |
147 } | 185 } |
148 | 186 |
149 flac_file *flac_file_from_buffer(void *buffer, uint32_t size) | 187 flac_file *flac_file_from_buffer(void *buffer, uint32_t size) |
150 { | 188 { |
151 flac_file *f = calloc(1, sizeof(flac_file)); | 189 flac_file *f = calloc(1, sizeof(flac_file)); |
152 f->read_data = buffer; | 190 f->read_data = buffer; |
153 f->read_byte = read_byte_buffer; | 191 f->read_byte = read_byte_buffer; |
154 f->seek = seek_buffer; | 192 f->seek = seek_buffer; |
193 f->tell = tell_buffer; | |
155 f->buffer_size = size; | 194 f->buffer_size = size; |
156 if (parse_header(f)) { | 195 if (parse_header(f)) { |
157 return f; | 196 return f; |
158 } | 197 } |
159 free(f); | 198 free(f); |
164 { | 203 { |
165 flac_file *f = calloc(1, sizeof(flac_file)); | 204 flac_file *f = calloc(1, sizeof(flac_file)); |
166 f->read_data = file; | 205 f->read_data = file; |
167 f->read_byte = read_byte_file; | 206 f->read_byte = read_byte_file; |
168 f->seek = seek_file; | 207 f->seek = seek_file; |
208 f->tell = tell_file; | |
169 if (parse_header(f)) { | 209 if (parse_header(f)) { |
170 return f; | 210 return f; |
171 } | 211 } |
172 free(f); | 212 free(f); |
173 return NULL; | 213 return NULL; |
184 while (byte & mask) | 224 while (byte & mask) |
185 { | 225 { |
186 mask >>= 1; | 226 mask >>= 1; |
187 length++; | 227 length++; |
188 } | 228 } |
189 uint64_t value = byte + (mask - 1); | 229 uint64_t value = byte & (mask - 1); |
190 for (uint8_t i = 0; i < length; i++) | 230 for (uint8_t i = 0; i < length; i++) |
191 { | 231 { |
192 value <<= 6; | 232 value <<= 6; |
193 value |= f->read_byte(f) & 0x3F; | 233 value |= f->read_byte(f) & 0x3F; |
194 } | 234 } |
206 while (byte & mask) | 246 while (byte & mask) |
207 { | 247 { |
208 mask >>= 1; | 248 mask >>= 1; |
209 length++; | 249 length++; |
210 } | 250 } |
211 uint32_t value = byte + (mask - 1); | 251 uint32_t value = byte & (mask - 1); |
212 for (uint8_t i = 0; i < length; i++) | 252 for (uint8_t i = 0; i < length; i++) |
213 { | 253 { |
214 value <<= 6; | 254 value <<= 6; |
215 value |= f->read_byte(f) & 0x3F; | 255 value |= f->read_byte(f) & 0x3F; |
216 } | 256 } |
578 } | 618 } |
579 f->frame_sample_pos++; | 619 f->frame_sample_pos++; |
580 | 620 |
581 return 1; | 621 return 1; |
582 } | 622 } |
623 | |
624 void flac_seek(flac_file *f, uint64_t sample_number) | |
625 { | |
626 if (sample_number >= f->frame_start_sample && sample_number < f->frame_start_sample + f->frame_block_size) { | |
627 f->frame_sample_pos = sample_number - f->frame_start_sample; | |
628 return; | |
629 } | |
630 uint32_t best_seekpoint = f->num_seekpoints + 1; | |
631 if (f->num_seekpoints) { | |
632 uint64_t best_diff; | |
633 for (uint32_t i = 0; i < f->num_seekpoints; i++) | |
634 { | |
635 if (f->seekpoints[i].sample_number > sample_number) { | |
636 continue; | |
637 } | |
638 uint64_t diff = sample_number - f->seekpoints[i].sample_number; | |
639 if (best_seekpoint > f->num_seekpoints || diff < best_diff) { | |
640 best_seekpoint = i; | |
641 best_diff = diff; | |
642 } | |
643 } | |
644 } | |
645 //TODO: more efficient seeking | |
646 if (best_seekpoint > f->num_seekpoints) { | |
647 f->seek(f, f->first_frame_offset, 0); | |
648 } else if (f->seekpoints[best_seekpoint].sample_number > f->frame_start_sample || f->frame_start_sample > sample_number){ | |
649 f->seek(f, f->seekpoints[best_seekpoint].offset + f->first_frame_offset, 0); | |
650 } | |
651 do { | |
652 if (!decode_frame(f)) { | |
653 return; | |
654 } | |
655 } while ((f->frame_start_sample + f->frame_block_size) <= sample_number); | |
656 f->frame_sample_pos = sample_number - f->frame_start_sample; | |
657 } |