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 }