Mercurial > repos > blastem
comparison cdimage.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 | efc75ea79164 |
children | 9f0c67e5c50a |
comparison
equal
deleted
inserted
replaced
2297:e6b2b2341c68 | 2298:9d68799f945b |
---|---|
109 media->cur_track = track; | 109 media->cur_track = track; |
110 if (!media->in_fake_pregap) { | 110 if (!media->in_fake_pregap) { |
111 if (track) { | 111 if (track) { |
112 lba -= media->tracks[track - 1].end_lba; | 112 lba -= media->tracks[track - 1].end_lba; |
113 } | 113 } |
114 if (media->tracks[track].has_subcodes) { | 114 if (media->tracks[track].flac) { |
115 if (!media->tmp_buffer) { | 115 flac_seek(media->tracks[track].flac, (media->tracks[track].file_offset + lba * media->tracks[track].sector_bytes) / 4); |
116 media->tmp_buffer = calloc(1, 96); | 116 } else { |
117 } | 117 if (media->tracks[track].has_subcodes) { |
118 fseek(media->tracks[track].f, media->tracks[track].file_offset + (lba + 1) * media->tracks[track].sector_bytes - 96, SEEK_SET); | 118 if (!media->tmp_buffer) { |
119 int bytes = fread(media->tmp_buffer, 1, 96, media->tracks[track].f); | 119 media->tmp_buffer = calloc(1, 96); |
120 if (bytes != 96) { | 120 } |
121 fprintf(stderr, "Only read %d subcode bytes\n", bytes); | 121 fseek(media->tracks[track].f, media->tracks[track].file_offset + (lba + 1) * media->tracks[track].sector_bytes - 96, SEEK_SET); |
122 } | 122 int bytes = fread(media->tmp_buffer, 1, 96, media->tracks[track].f); |
123 } | 123 if (bytes != 96) { |
124 fseek(media->tracks[track].f, media->tracks[track].file_offset + lba * media->tracks[track].sector_bytes, SEEK_SET); | 124 fprintf(stderr, "Only read %d subcode bytes\n", bytes); |
125 } | |
126 } | |
127 fseek(media->tracks[track].f, media->tracks[track].file_offset + lba * media->tracks[track].sector_bytes, SEEK_SET); | |
128 } | |
125 } | 129 } |
126 } | 130 } |
127 return track; | 131 return track; |
128 } | 132 } |
129 | 133 |
154 return fake_read(media->cur_sector, offset); | 158 return fake_read(media->cur_sector, offset); |
155 } else if (media->in_fake_pregap == FAKE_AUDIO) { | 159 } else if (media->in_fake_pregap == FAKE_AUDIO) { |
156 return 0; | 160 return 0; |
157 } else if ((media->tracks[media->cur_track].sector_bytes < 2352 && offset < 16) || offset > (media->tracks[media->cur_track].sector_bytes + 16)) { | 161 } else if ((media->tracks[media->cur_track].sector_bytes < 2352 && offset < 16) || offset > (media->tracks[media->cur_track].sector_bytes + 16)) { |
158 return fake_read(media->cur_sector, offset); | 162 return fake_read(media->cur_sector, offset); |
163 } else if (media->tracks[media->cur_track].flac) { | |
164 if (offset & 3) { | |
165 return media->byte_storage[(offset & 3) - 1]; | |
166 } else { | |
167 int16_t samples[2]; | |
168 flac_get_sample(media->tracks[media->cur_track].flac, samples, 2); | |
169 media->byte_storage[0] = samples[0] >> 8; | |
170 media->byte_storage[1] = samples[1]; | |
171 media->byte_storage[2] = samples[1] >> 8; | |
172 return samples[0]; | |
173 } | |
159 } else { | 174 } else { |
160 if (media->tracks[media->cur_track].need_swap) { | 175 if (media->tracks[media->cur_track].need_swap) { |
161 if (offset & 1) { | 176 if (offset & 1) { |
162 return media->byte_storage; | 177 return media->byte_storage[0]; |
163 } | 178 } |
164 media->byte_storage = fgetc(media->tracks[media->cur_track].f); | 179 media->byte_storage[0] = fgetc(media->tracks[media->cur_track].f); |
165 } | 180 } |
166 return fgetc(media->tracks[media->cur_track].f); | 181 return fgetc(media->tracks[media->cur_track].f); |
167 } | 182 } |
168 } | 183 } |
169 | 184 |
196 media->tracks = tracks; | 211 media->tracks = tracks; |
197 line = media->buffer; | 212 line = media->buffer; |
198 int track = -1; | 213 int track = -1; |
199 uint8_t audio_byte_swap = 0; | 214 uint8_t audio_byte_swap = 0; |
200 FILE *f = NULL; | 215 FILE *f = NULL; |
216 flac_file *flac = NULL; | |
201 int track_of_file = -1; | 217 int track_of_file = -1; |
202 uint8_t has_index_0 = 0; | 218 uint8_t has_index_0 = 0; |
203 uint32_t extra_offset = 0; | 219 uint32_t extra_offset = 0; |
204 do { | 220 do { |
205 char *cmd = cmd_start(line); | 221 char *cmd = cmd_start(line); |
213 int file_track = strtol(cmd, &end, 10); | 229 int file_track = strtol(cmd, &end, 10); |
214 if (file_track != (track + 1)) { | 230 if (file_track != (track + 1)) { |
215 warning("Expected track %d, but found track %d in CUE sheet\n", track + 1, file_track); | 231 warning("Expected track %d, but found track %d in CUE sheet\n", track + 1, file_track); |
216 } | 232 } |
217 tracks[track].f = f; | 233 tracks[track].f = f; |
234 tracks[track].flac = flac; | |
218 | 235 |
219 | 236 |
220 cmd = cmd_start(end); | 237 cmd = cmd_start(end); |
221 if (*cmd) { | 238 if (*cmd) { |
222 if (startswith(cmd, "AUDIO")) { | 239 if (startswith(cmd, "AUDIO")) { |
257 memcpy(fname, media->dir, dirlen); | 274 memcpy(fname, media->dir, dirlen); |
258 fname[dirlen] = PATH_SEP[0]; | 275 fname[dirlen] = PATH_SEP[0]; |
259 memcpy(fname + dirlen + 1, cmd, end-cmd); | 276 memcpy(fname + dirlen + 1, cmd, end-cmd); |
260 fname[dirlen + 1 + (end-cmd)] = 0; | 277 fname[dirlen + 1 + (end-cmd)] = 0; |
261 } | 278 } |
279 flac = NULL; | |
262 f = fopen(fname, "rb"); | 280 f = fopen(fname, "rb"); |
263 if (!f) { | 281 if (!f) { |
264 fatal_error("Failed to open %s specified by FILE command in CUE sheet %s.%s\n", fname, media->name, media->extension); | 282 fatal_error("Failed to open %s specified by FILE command in CUE sheet %s.%s\n", fname, media->name, media->extension); |
265 } | 283 } |
266 free(fname); | 284 |
267 track_of_file = -1; | 285 track_of_file = -1; |
268 for (end++; *end && *end != '\n' && *end != '\r'; end++) | 286 for (end++; *end && *end != '\n' && *end != '\r'; end++) |
269 { | 287 { |
270 if (!isspace(*end)) { | 288 if (!isspace(*end)) { |
271 extra_offset = 0; | 289 extra_offset = 0; |
274 } else if (startswith(end, "MOTOROLA")) { | 292 } else if (startswith(end, "MOTOROLA")) { |
275 audio_byte_swap = 1; | 293 audio_byte_swap = 1; |
276 } else if (startswith(end, "WAVE")) { | 294 } else if (startswith(end, "WAVE")) { |
277 audio_byte_swap = 0; | 295 audio_byte_swap = 0; |
278 wave_header wave; | 296 wave_header wave; |
279 if (!wave_read_header(f, &wave)) { | 297 if (wave_read_header(f, &wave)) { |
280 fatal_error("Wave file %s specified by cute sheet %s.%s is not valid\n", fname, media->name, media->extension); | 298 if (wave.audio_format != 1 || wave.num_channels != 2 || wave.sample_rate != 44100 || wave.bits_per_sample != 16) { |
299 warning("BlastEm only suports WAVE tracks in 16-bit stereo PCM format at 44100 hz, file %s does not match\n", fname); | |
300 } | |
301 extra_offset = wave.format_header.size + sizeof(wave.data_header) + sizeof(wave.chunk); | |
302 } else { | |
303 fseek(f, 0, SEEK_SET); | |
304 flac = flac_file_from_file(f); | |
305 if (!flac) { | |
306 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); | |
307 } | |
308 if (flac->sample_rate != 44100 || flac->bits_per_sample != 16 || flac->channels != 2) { | |
309 warning("FLAC files in a CUE sheet should match CD audio specs, %s does not\n", fname); | |
310 } | |
311 | |
281 } | 312 } |
282 if (wave.audio_format != 1 || wave.num_channels != 2 || wave.sample_rate != 44100 || wave.bits_per_sample != 16) { | |
283 warning("BlastEm only suports WAVE tracks in 16-bit stereo PCM format at 44100 hz, file %s does not match\n", fname); | |
284 } | |
285 extra_offset = wave.format_header.size + sizeof(wave.data_header) + sizeof(wave.chunk); | |
286 } else { | 313 } else { |
287 warning("Unsupported FILE type in CUE sheet. Only BINARY and MOTOROLA are supported\n"); | 314 warning("Unsupported FILE type in CUE sheet. Only BINARY and MOTOROLA are supported\n"); |
288 } | 315 } |
289 break; | 316 break; |
290 } | 317 } |
291 } | 318 } |
319 free(fname); | |
292 } | 320 } |
293 } | 321 } |
294 } else if (track >= 0) { | 322 } else if (track >= 0) { |
295 if (startswith(cmd, "PREGAP ")) { | 323 if (startswith(cmd, "PREGAP ")) { |
296 tracks[track].fake_pregap = timecode_to_lba(cmd + 7); | 324 tracks[track].fake_pregap = timecode_to_lba(cmd + 7); |
342 f = tracks[0].f; | 370 f = tracks[0].f; |
343 uint32_t offset = 0; | 371 uint32_t offset = 0; |
344 for (int track = 0; track < media->num_tracks; track++) { | 372 for (int track = 0; track < media->num_tracks; track++) { |
345 if (track == media->num_tracks - 1 && tracks[track].f) { | 373 if (track == media->num_tracks - 1 && tracks[track].f) { |
346 uint32_t start_lba =tracks[track].fake_pregap ? tracks[track].start_lba : tracks[track].pregap_lba; | 374 uint32_t start_lba =tracks[track].fake_pregap ? tracks[track].start_lba : tracks[track].pregap_lba; |
347 tracks[track].end_lba = start_lba + (file_size(tracks[track].f) - tracks[track].file_offset)/ tracks[track].sector_bytes; | 375 uint32_t fsize; |
376 if (tracks[track].flac) { | |
377 fsize = tracks[track].flac->total_samples * 4; | |
378 } else { | |
379 fsize = file_size(tracks[track].f); | |
380 } | |
381 tracks[track].end_lba = start_lba + (fsize - tracks[track].file_offset)/ tracks[track].sector_bytes; | |
348 } else if (tracks[track].f != f) { | 382 } else if (tracks[track].f != f) { |
349 uint32_t start_lba =tracks[track-1].fake_pregap ? tracks[track-1].start_lba : tracks[track-1].pregap_lba; | 383 uint32_t start_lba =tracks[track-1].fake_pregap ? tracks[track-1].start_lba : tracks[track-1].pregap_lba; |
350 tracks[track-1].end_lba = start_lba + (file_size(tracks[track-1].f) - tracks[track-1].file_offset)/ tracks[track-1].sector_bytes; | 384 uint32_t fsize; |
385 if (tracks[track-1].flac) { | |
386 fsize = tracks[track-1].flac->total_samples * 4; | |
387 } else { | |
388 fsize = file_size(tracks[track-1].f); | |
389 } | |
390 tracks[track-1].end_lba = start_lba + (fsize - tracks[track-1].file_offset)/ tracks[track-1].sector_bytes; | |
351 offset = tracks[track-1].end_lba; | 391 offset = tracks[track-1].end_lba; |
352 } | 392 } |
353 if (!tracks[track].fake_pregap) { | 393 if (!tracks[track].fake_pregap) { |
354 tracks[track].pregap_lba += offset; | 394 tracks[track].pregap_lba += offset; |
355 } | 395 } |
357 tracks[track].end_lba += offset; | 397 tracks[track].end_lba += offset; |
358 } | 398 } |
359 //replace cue sheet with first sector | 399 //replace cue sheet with first sector |
360 free(media->buffer); | 400 free(media->buffer); |
361 media->buffer = calloc(2048, 1); | 401 media->buffer = calloc(2048, 1); |
362 if (tracks[0].type == TRACK_DATA && tracks[0].sector_bytes == 2352) { | 402 if (tracks[0].type == TRACK_DATA && tracks[0].sector_bytes == 2352 && !tracks[0].flac) { |
363 // if the first track is a data track, don't trust the CUE sheet and look at the MM:SS:FF from first sector | 403 // if the first track is a data track, don't trust the CUE sheet and look at the MM:SS:FF from first sector |
364 uint8_t msf[3]; | 404 uint8_t msf[3]; |
365 fseek(tracks[0].f, 12, SEEK_SET); | 405 fseek(tracks[0].f, 12, SEEK_SET); |
366 if (sizeof(msf) == fread(msf, 1, sizeof(msf), tracks[0].f)) { | 406 if (sizeof(msf) == fread(msf, 1, sizeof(msf), tracks[0].f)) { |
367 tracks[0].fake_pregap = msf[2] + (msf[0] * 60 + msf[1]) * 75; | 407 tracks[0].fake_pregap = msf[2] + (msf[0] * 60 + msf[1]) * 75; |
586 save_int32(buf, ftell(media->tracks[media->cur_track].f)); | 626 save_int32(buf, ftell(media->tracks[media->cur_track].f)); |
587 } else { | 627 } else { |
588 save_int32(buf, 0); | 628 save_int32(buf, 0); |
589 } | 629 } |
590 save_int8(buf, media->in_fake_pregap); | 630 save_int8(buf, media->in_fake_pregap); |
591 save_int8(buf, media->byte_storage); | 631 save_int8(buf, media->byte_storage[0]); |
592 if (media->tmp_buffer) { | 632 if (media->tmp_buffer) { |
593 save_buffer8(buf, media->tmp_buffer, 96); | 633 save_buffer8(buf, media->tmp_buffer, 96); |
594 } | 634 } |
635 save_int8(buf, media->byte_storage[1]); | |
636 save_int8(buf, media->byte_storage[2]); | |
595 } | 637 } |
596 | 638 |
597 void cdimage_deserialize(deserialize_buffer *buf, void *vmedia) | 639 void cdimage_deserialize(deserialize_buffer *buf, void *vmedia) |
598 { | 640 { |
599 system_media *media = vmedia; | 641 system_media *media = vmedia; |
605 uint32_t seekpos = load_int32(buf); | 647 uint32_t seekpos = load_int32(buf); |
606 if (media->cur_track < media->num_tracks && media->tracks[media->cur_track].f) { | 648 if (media->cur_track < media->num_tracks && media->tracks[media->cur_track].f) { |
607 fseek(media->tracks[media->cur_track].f, seekpos, SEEK_SET); | 649 fseek(media->tracks[media->cur_track].f, seekpos, SEEK_SET); |
608 } | 650 } |
609 media->in_fake_pregap = load_int8(buf); | 651 media->in_fake_pregap = load_int8(buf); |
610 media->byte_storage = load_int8(buf); | 652 media->byte_storage[0] = load_int8(buf); |
611 if (media->tmp_buffer) { | 653 if (media->tmp_buffer) { |
612 load_buffer8(buf, media->tmp_buffer, 96); | 654 load_buffer8(buf, media->tmp_buffer, 96); |
613 } | 655 } |
614 } | 656 if (buf->size - buf->cur_pos >= 2) { |
657 media->byte_storage[1] = load_int8(buf); | |
658 media->byte_storage[2] = load_int8(buf); | |
659 } | |
660 } |