diff 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
line wrap: on
line diff
--- 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);
+	}
 }