/* read_cd.c - CD read routines. * */ #include #include #include #include #include #include #include #include #include #include #include #include #include "util.h" #define READ 0x28 #define READ_CD_DA 0xD8 #define READ_TOC 0x43 #define MAX_BLOCKS 12 /* number of CD blocks processed at a time */ typedef struct { short toc_data_length; unchar first_track_number; unchar last_track_number; } toc_header; typedef struct { unchar reserved1; unsigned adr: 4; unsigned four_ch_audio: 1; unsigned data_track: 1; unsigned copy_prohibited: 1; unsigned pre_emphasis: 1; unchar track_number; unchar reserved2; unchar reserved3; unchar m; unchar s; unchar f; } toc_entry; typedef struct { toc_header th; toc_entry te[100]; } toc_t; typedef struct { unchar reserved1; unchar medium_type; unsigned reserved2: 1; unsigned host_application_code: 6; unchar block_descriptor_length; } select_header; typedef struct { unchar density_code; unsigned number_of_blocks: 24; unchar reserved; unsigned block_length: 24; } block_descriptor; static boolean_t read_toc(dsreq_t *dsp, toc_t *tocp) { fillg1cmd(dsp, (unchar *)CMDBUF(dsp), READ_TOC, 2, 0, 0, 0, 0, 0, B2(sizeof(tocp[0])), 0); filldsreq(dsp, (unchar *)tocp, sizeof(tocp[0]), DSRQ_READ|DSRQ_SENSE); doscsireq(getfd(dsp), dsp); return check_status(dsp, "Read TOC"); } static ulong_t read_binary(dsreq_t *dsp, ulong_t start, void *buf, ulong_t blocks) { ulong_t blocks_read; fillg1cmd(dsp, (unchar *)CMDBUF(dsp), READ, 0, B4(start), 0, B2(blocks), 0); filldsreq(dsp, (unchar *)buf, blocks*CDROM_BLOCKSIZE, DSRQ_READ|DSRQ_SENSE); doscsireq(getfd(dsp), dsp); blocks_read = DATASENT(dsp) / CDROM_BLOCKSIZE; if (blocks_read != blocks) fprintf(stderr, "Tried to read %u blocks at %u (%.2u:%.2u:%.2u), received %u blocks=%u bytes.\n", blocks, start, MINUTE(start), SECOND(start), FRAME(start), blocks_read, DATASENT(dsp)); (void) check_status(dsp, "Read"); return blocks_read; } static ulong_t read_cd_da(dsreq_t *dsp, ulong_t start, void *buf, ulong_t blocks) { ulong_t blocks_read; unchar *cmdbuf = (unchar *)CMDBUF(dsp); cmdbuf[0] = READ_CD_DA; cmdbuf[1] = 0; cmdbuf[2] = (unchar)(start >> 24); cmdbuf[3] = (unchar)(start >> 16); cmdbuf[4] = (unchar)(start >> 8); cmdbuf[5] = (unchar)start; cmdbuf[6] = (unchar)(blocks >> 24); cmdbuf[7] = (unchar)(blocks >> 16); cmdbuf[8] = (unchar)(blocks >> 8); cmdbuf[9] = (unchar)blocks; cmdbuf[10] = 0; cmdbuf[11] = 0; CMDLEN(dsp) = 12; filldsreq(dsp, (unchar *)buf, blocks*CDDA_BLOCKSIZE, DSRQ_READ|DSRQ_SENSE); do doscsireq(getfd(dsp), dsp); while ((STATUS(dsp) == STA_CHECK) && (ADDSENSECODE(SENSEBUF(dsp)) == 0xBF)); blocks_read = DATASENT(dsp) / CDDA_BLOCKSIZE; if (blocks_read != blocks) fprintf(stderr, "Tried to read %u blocks at %u (%.2u:%.2u:%.2u), received %u blocks=%u bytes.\n", blocks, start, MINUTE(start), SECOND(start), FRAME(start), blocks_read, DATASENT(dsp)); (void) check_status(dsp, "Read CD DA"); return blocks_read; } static boolean_t process_audio_track(dsreq_t *dsp, ALport aport, int tn, ulong_t start, ulong_t end, boolean_t play, boolean_t write_to_file, AFfilesetup filesetup) { AFfilehandle file; struct { select_header sh; block_descriptor bd; } mode_arg; bzero(&mode_arg, sizeof mode_arg); mode_arg.sh.block_descriptor_length = sizeof mode_arg.bd; mode_arg.bd.block_length = CDDA_BLOCKSIZE; mode_select(dsp, (caddr_t)&mode_arg, sizeof mode_arg); if (write_to_file) { char fname[] = "nn.aifc"; fname[0] = tn / 10 + '0'; fname[1] = tn % 10 + '0'; file = AFopenfile(fname, "w", filesetup); } while (start < end) { ulong_t data[CDDA_BLOCKSIZE*MAX_BLOCKS/sizeof(ulong_t)]; ulong_t blocks = end - start; ulong_t blocks_read; if (blocks > MAX_BLOCKS) blocks = MAX_BLOCKS; blocks_read = read_cd_da(dsp, start, data, blocks); if (blocks_read == 0) return B_TRUE; start += blocks_read; /* byte-swap the data. */ { ulong_t *ptr = data; ulong_t *end = ptr + CDDA_BLOCKSIZE*blocks_read/sizeof(ulong_t); do *ptr = ((*ptr >> 8) & 0x00FF00FF) | ((*ptr << 8) & 0xFF00FF00); while (++ptr < end); } if (play) { if (ALwritesamps(aport, data, (int)(CDDA_BLOCKSIZE*blocks_read/2)) != 0) { perror("ALwritesamps"); return B_TRUE; } } if (write_to_file) if (AFwriteframes(file, AF_DEFAULT_TRACK, data, (int)(CDDA_BLOCKSIZE*blocks_read/4)) != CDDA_BLOCKSIZE*blocks_read/4) { perror("AFwriteframes"); return B_TRUE; } } if (write_to_file) AFclosefile(file); return B_FALSE; } static boolean_t process_data_track(dsreq_t *dsp, boolean_t stdoutw, int tn, ulong_t start, ulong_t end) { int file; char fname[] = "nn.img"; struct { select_header sh; block_descriptor bd; } mode_arg; bzero(&mode_arg, sizeof mode_arg); mode_arg.sh.block_descriptor_length = sizeof mode_arg.bd; mode_arg.bd.block_length = CDROM_BLOCKSIZE; mode_select(dsp, (caddr_t)&mode_arg, sizeof mode_arg); if (stdoutw) file = 1; else { fname[0] = tn / 10 + '0'; fname[1] = tn % 10 + '0'; file = creat(fname, 0444); if (file < 0) { perror(fname); return B_TRUE; } } while (start < end) { ulong_t data[CDROM_BLOCKSIZE*MAX_BLOCKS/sizeof(ulong_t)]; ulong_t blocks = end - start; ulong_t blocks_read; if (blocks > MAX_BLOCKS) blocks = MAX_BLOCKS; blocks_read = read_binary(dsp, start, data, blocks); if (blocks_read == 0) return B_TRUE; start += blocks_read; if (write(file, data, CDROM_BLOCKSIZE*blocks_read) != CDROM_BLOCKSIZE*blocks_read) { perror(fname); return B_TRUE; } } if (!stdoutw) close(file); return B_FALSE; } boolean_t read_cd(dsreq_t *dsp, uint_t verbose, boolean_t audio, boolean_t binary, boolean_t play, boolean_t stdoutw, boolean_t write_to_file, boolean_t *track_selected) { toc_t toc; int i; ulong_t t1_addr, start, end, length, total_audio = 0, total_binary = 0; ALport aport; AFfilesetup filesetup; if (wait_ready(dsp, verbose)) return B_TRUE; if (read_toc(dsp, &toc)) return B_TRUE; for (i = 0; i < (toc.th.toc_data_length - 2) / sizeof(toc.te[0]); i++) { int tn = toc.te[i].track_number; start = MSF2INT(toc.te[i].m, toc.te[i].s, toc.te[i].f); end = MSF2INT(toc.te[i+1].m, toc.te[i+1].s, toc.te[i+1].f); length = end - start; if (verbose > 0) { if (tn > 99) printf("Lead-out at %.2u:%.2u:%.2u.\n", toc.te[i].m, toc.te[i].s, toc.te[i].f); else { printf("Track %2u at %.2u:%.2u:%.2u, length %.2u:%.2u:%.2u, ", tn, toc.te[i].m, toc.te[i].s, toc.te[i].f, MINUTE(length), SECOND(length), FRAME(length)); if (toc.te[i].data_track) printf("digital data.\n"); else printf("%u-ch DA %s pre-emphasis.\n", toc.te[i].four_ch_audio? 4 : 2, toc.te[i].pre_emphasis? "WITH" : "without"); } } if ((tn < 100) && track_selected[tn]) { if (toc.te[i].data_track == 0) { if (toc.te[i].four_ch_audio == 0) { if (audio) total_audio += length; else track_selected[tn] = B_FALSE; } else { printf("Warning - unable to process 4-channel Digital Audio.\n"); track_selected[tn] = B_FALSE; } } else { if (binary) total_binary += length; else track_selected[tn] = B_FALSE; } } } if (total_audio == 0) play = B_FALSE; if (verbose > 0) { printf("Total audio time selected=%.2u:%.2u:%.2u.\n", MINUTE(total_audio), SECOND(total_audio), FRAME(total_audio)); printf("Total binary blocks selected=%u.\n", total_binary); } if (write_to_file || play) { if (total_audio > 0) { if (play) { long pvbuf[2]; aport = ALopenport("ReadCDDA", "w", (ALconfig) 0); if (aport == 0) { perror("ALopenport"); return B_TRUE; } pvbuf[0] = AL_OUTPUT_RATE; pvbuf[1] = AL_RATE_44100; if (ALsetparams(AL_DEFAULT_DEVICE, pvbuf, 2) != 0) { perror("ALsetparams"); return B_TRUE; } } if (write_to_file) { filesetup = AFnewfilesetup(); AFinitfilefmt(filesetup, AF_FILE_AIFFC); AFinitchannels(filesetup, AF_DEFAULT_TRACK, 2); AFinitrate(filesetup, AF_DEFAULT_TRACK, 44100.0); AFinitsampfmt(filesetup, AF_DEFAULT_TRACK, AF_SAMPFMT_TWOSCOMP, 16); } } t1_addr = MSF2INT(toc.te[0].m, toc.te[0].s, toc.te[0].f); for (i = 0; i < (toc.th.toc_data_length - 2) / sizeof(toc.te[0]) - 1; i++) if ((toc.te[i].track_number <= 99) && track_selected[toc.te[i].track_number]) { start = MSF2INT(toc.te[i].m, toc.te[i].s, toc.te[i].f) - t1_addr; end = MSF2INT(toc.te[i+1].m, toc.te[i+1].s, toc.te[i+1].f) - t1_addr; if (verbose > 0) printf("Processing track %2u.\n", toc.te[i].track_number); if (toc.te[i].data_track == 0) { if (process_audio_track(dsp, aport, toc.te[i].track_number, start, end, play, write_to_file, filesetup)) return B_TRUE; } else if (write_to_file) if (process_data_track(dsp, stdoutw, toc.te[i].track_number, start, end)) return B_TRUE; } } return B_FALSE; }