#include #include #include #include #include #include #include #include #include #include /* * This programs allows you to play audio CDs on a RRD42 under * ULTRIX V4.2. First is you need write access to the raw device * (/dev/rrz?c) and you need to set the environment variable CDROM * to the RRD42 drive (ie. setenv CDROM /dev/rrz4c). * * This programs allows to * play the entire cd cdp play * play from a specific cdp play n * track to the end * pause the cd cdp pause * unpause (resume) the cd cdp resume * list tracks cdp query * eject the cd cdp eject * show cd status cdp status * * This program is the result of a 4 hour hacking session trying * to understand how to get the RRD42 to play. */ char *channel_selections[] = { "Muted", "Channel 0", "Channel 1", "Channels 0 & 1" }; int mode = CDROM_MSF_FORMAT; struct cd_toc toc; struct cd_toc_entry *fte, *lte; struct cd_toc_header th; struct cd_playback pb; struct cd_playback_status ps; char *cdrom; main(argc, argv) int argc; char **argv; { int fd; if ((cdrom = getenv("CDROM")) == NULL) { fprintf(stderr, "CDROM environment variable not set\n"); exit(EX_USAGE); } fd = open(cdrom, O_RDONLY, 0); if (fd < 0) { fprintf(stderr, "%s: %s\n", cdrom, strerror(errno)); exit(EX_UNAVAILABLE); } cdrom_init(fd); if (argc == 2) { if (strcasecmp("pause", argv[1]) == 0) { if (ioctl(fd, CDROM_PAUSE_PLAY, NULL) < 0) { fprintf(stderr, "%s: pause: %s\n", cdrom, strerror(errno)); exit(EX_UNAVAILABLE); } exit(EX_OK); } else if (strcasecmp("play", argv[1]) == 0) { exit(cdrom_play(fd, -1, -1)); } else if (strcasecmp("query", argv[1]) == 0) { exit(cdrom_query(fd)); } else if (strcasecmp("status", argv[1]) == 0) { exit(cdrom_status(fd)); } else if (strcasecmp("resume", argv[1]) == 0) { if (ioctl(fd, CDROM_RESUME_PLAY, NULL) < 0) { fprintf(stderr, "%s: resume: %s\n", cdrom, strerror(errno)); exit(EX_UNAVAILABLE); } exit(EX_OK); } else if (strcasecmp("eject", argv[1]) == 0) { if (ioctl(fd, CDROM_EJECT_CADDY, NULL) < 0) { fprintf(stderr, "%s: eject: %s\n", cdrom, strerror(errno)); exit(EX_UNAVAILABLE); } exit(EX_OK); } } else if (argc == 3) { if (strcasecmp("play", argv[1]) == 0) { int track; if (sscanf(argv[2], "%i", &track) == 1) { exit(cdrom_play(fd, track, -1)); } } } fprintf(stderr, "%s: unrecognized or illegal command or option\n", cdrom); fprintf(stderr, "usage: %s { query, pause, resume, eject, play [track], status }\n", argv[0]); exit(EX_USAGE); } cdrom_status(fd) int fd; { int m = 0, f = 0, s = 0, idx, status = ps.ps_audio_status; if (ps.ps_audio_status) printf("Audio status = %s%s%s%s%s%s\n", (ps.ps_audio_status & PS_PLAY_IN_PROGRESS) ? "\n\tPlay In Progress" : "", (ps.ps_audio_status & PS_PLAY_PAUSED) ? "\n\tPause In Progress" : "", (ps.ps_audio_status & PS_MUTING_ON) ? "\n\tMuting On" : "", (ps.ps_audio_status & PS_PLAY_COMPLETED) ? "\n\tPlay Completed" : "", (ps.ps_audio_status & PS_PLAY_ERROR) ? "\n\tError Occurred During Play" : "", (ps.ps_audio_status & PS_PLAY_NOT_REQUESTED) ? "\n\tAudio Play Not Requested" : "" ); printf("Channel %d Selection = %s, volume = %1.2f%%\n", 0, channel_selections[ps.ps_chan0_select], (100.0 * ps.ps_chan0_volume) / CDROM_MAX_VOLUME); printf("Channel %d Selection = %s, volume = %1.2f%%\n", 1, channel_selections[ps.ps_chan1_select], (100.0 * ps.ps_chan1_volume) / CDROM_MAX_VOLUME); printf("Channel %d Selection = %s, volume = %1.2f%%\n", 2, channel_selections[ps.ps_chan2_select], (100.0 * ps.ps_chan2_volume) / CDROM_MAX_VOLUME); printf("Channel %d Selection = %s, volume = %1.2f%%\n", 3, channel_selections[ps.ps_chan3_select], (100.0 * ps.ps_chan3_volume) / CDROM_MAX_VOLUME); printf("\n"); m = 0; s = 0; fte = (struct cd_toc_entry *) (toc.toc_buffer + sizeof(th)); lte = fte + (th.th_ending_track - th.th_starting_track) + 1; idx = 0; while ((ps.ps_audio_status & PS_PLAY_COMPLETED) == 0) { static struct timeval timeout = { 0, 1000000 / 10 }; pb.pb_alloc_length = sizeof(ps); pb.pb_buffer = (caddr_t) &ps; if (ioctl(fd, CDROM_PLAYBACK_STATUS, &pb) < 0) { fprintf(stderr, "%s: play: %s\n", cdrom, strerror(errno)); exit(EX_UNAVAILABLE); } if (status != ps.ps_audio_status) { printf("\nAudio status = %s%s%s%s%s%s%s\n", (ps.ps_audio_status & PS_PLAY_IN_PROGRESS) ? "\n\tPlay In Progress" : "", (ps.ps_audio_status & PS_PLAY_PAUSED) ? "\n\tPause In Progress" : "", (ps.ps_audio_status & PS_MUTING_ON) ? "\n\tMuting On" : "", (ps.ps_audio_status & PS_PLAY_COMPLETED) ? "\n\tPlay Completed" : "", (ps.ps_audio_status & PS_PLAY_ERROR) ? "\n\tError Occurred During Play" : "", (ps.ps_audio_status & PS_PLAY_NOT_REQUESTED) ? "\n\tAudio Play Not Requested" : "", ps.ps_audio_status ? "" : "" ); status = ps.ps_audio_status; if (ps.ps_audio_status & PS_PLAY_COMPLETED) break; } if (ps.ps_lbamsf == CDROM_LBA_FORMAT) { printf("Address = %d (%s)\r", (ps.ps_lba.addr0) + (ps.ps_lba.addr1 << 8) + (ps.ps_lba.addr2 << 16) + (ps.ps_lba.addr3 << 24), "LBA"); fflush(stdout); } else { if (m != ps.ps_msf.m_units || s != ps.ps_msf.s_units) { int dm, tdm, ds, tds, fs, fm; while (ps.ps_msf.m_units > fte[idx+1].te_msf.m_units || (ps.ps_msf.m_units == fte[idx+1].te_msf.m_units && ps.ps_msf.s_units > fte[idx+1].te_msf.s_units)) { idx++; } fs = ps.ps_msf.s_units - fte[idx].te_msf.s_units; fm = ps.ps_msf.m_units - fte[idx].te_msf.m_units; if (fs < 0) fm -= 1, fs += 60; ds = fte[idx+1].te_msf.s_units - ps.ps_msf.s_units; dm = fte[idx+1].te_msf.m_units - ps.ps_msf.m_units; if (ds < 0) dm -= 1, ds += 60; tds = lte->te_msf.s_units - ps.ps_msf.s_units; tdm = lte->te_msf.m_units - ps.ps_msf.m_units; if (tds < 0) tdm -= 1, tds += 60; printf("Track %-3d %2d:%02d (-%d:%02d) %3.2f%% -- %3d %2d:%02d (-%d:%02d) %3.2f%%\r", fte[idx].te_track_number, fm, fs, dm, ds, 100.0 - 100.0 * (ds + dm * 60) / ((fte[idx+1].te_msf.m_units - fte[idx].te_msf.m_units) * 60 + fte[idx+1].te_msf.s_units - fte[idx].te_msf.s_units), fte[idx].te_track_number - th.th_ending_track, ps.ps_msf.m_units, ps.ps_msf.s_units, tdm, tds, 100.0 - 100.0 * (tds + tdm * 60) / ((lte->te_msf.m_units - fte->te_msf.m_units) * 60 + lte->te_msf.s_units - fte->te_msf.s_units)); m = ps.ps_msf.m_units; s = ps.ps_msf.s_units; fflush(stdout); } } select(0, 0, 0, 0, &timeout); } return EX_OK; } cdrom_init(fd) int fd; { if (ioctl(fd, CDROM_SET_ADDRESS_FORMAT, &mode) < 0) { fprintf(stderr, "%s: set addr mode: %s\n", cdrom, strerror(errno)); } pb.pb_alloc_length = sizeof(ps); pb.pb_buffer = (caddr_t) &ps; if (ioctl(fd, CDROM_PLAYBACK_STATUS, &pb) < 0) { fprintf(stderr, "%s: play: %s\n", cdrom, strerror(errno)); exit(EX_UNAVAILABLE); } if (ioctl(fd, CDROM_TOC_HEADER, &th) < 0) { fprintf(stderr, "%s: toc hdr: %s\n", cdrom, strerror(errno)); exit(EX_UNAVAILABLE); } toc.toc_address_format = mode; toc.toc_starting_track = th.th_starting_track; toc.toc_alloc_length = th.th_data_len0 + 256 * th.th_data_len1 + sizeof(th); toc.toc_buffer = (caddr_t) malloc(toc.toc_alloc_length); bzero(toc.toc_buffer, toc.toc_alloc_length); if (ioctl(fd, CDROM_SET_ADDRESS_FORMAT, &mode) < 0) { fprintf(stderr, "%s: set addr mode: %s\n", cdrom, strerror(errno)); } if (ioctl(fd, CDROM_TOC_ENTRYS, &toc) < 0) { fprintf(stderr, "%s: toc entry: %s\n", cdrom, strerror(errno)); exit(EX_UNAVAILABLE); } fte = (struct cd_toc_entry *) (toc.toc_buffer + sizeof(th)); lte = fte + (th.th_ending_track - th.th_starting_track) + 1; } cdrom_play(fd, start, end) int fd; int start; int end; { if ((start > 0) && (start > th.th_ending_track || start < th.th_starting_track)) { fprintf(stderr, "Starting track (%d) be with the range of %d to %d\n", start, th.th_starting_track, th.th_ending_track); return EX_USAGE; } if ((end > 0) && (end > th.th_ending_track || end < th.th_ending_track)) { fprintf(stderr, "Ending track (%d) be with the range of %d to %d\n", end, th.th_starting_track, th.th_ending_track); return EX_USAGE; } if (start < 0) start = th.th_starting_track; if (end < 0) end = th.th_ending_track; fte += (start - th.th_starting_track); lte += (end - th.th_ending_track); if (toc.toc_address_format == CDROM_LBA_FORMAT) { struct cd_play_audio pa; pa.pa_lba = fte->te_lba.addr0 + (fte->te_lba.addr1 << 8) + (fte->te_lba.addr2 << 16) + (fte->te_lba.addr3 << 24); pa.pa_length = lte->te_lba.addr0 + (lte->te_lba.addr1 << 8) + (lte->te_lba.addr2 << 16) + (lte->te_lba.addr3 << 24); pa.pa_length -= pa.pa_lba; printf("pa_lba = %d, pa_length = %d\n", pa.pa_lba, pa.pa_length); if (ioctl(fd, CDROM_PLAY_AUDIO, &pa) < 0) { fprintf(stderr, "%s: play: %s\n", cdrom, strerror(errno)); return EX_OSERR; } } else { struct cd_play_audio_msf msf; msf.msf_starting_M_unit = fte->te_msf.m_units; msf.msf_starting_S_unit = fte->te_msf.s_units; msf.msf_starting_F_unit = fte->te_msf.f_units; msf.msf_ending_M_unit = lte->te_msf.m_units; msf.msf_ending_S_unit = lte->te_msf.s_units; msf.msf_ending_F_unit = lte->te_msf.f_units; if (msf.msf_ending_F_unit > 0) { msf.msf_ending_F_unit--; } else { msf.msf_ending_F_unit = 74; if (msf.msf_ending_S_unit > 0) { msf.msf_ending_S_unit--; } else { msf.msf_ending_S_unit = 59; msf.msf_ending_M_unit--; } } if (ioctl(fd, CDROM_PLAY_AUDIO_MSF, &msf) < 0) { fprintf(stderr, "%s: play msf: %s\n", cdrom, strerror(errno)); return EX_OSERR; } } return EX_OK; } cdrom_query(fd) int fd; { struct cd_toc_entry *te = fte; printf("Total time = %d:%02d (%d track%s)\n\n", lte->te_msf.m_units, lte->te_msf.s_units, lte - fte, lte - fte == 1 ? "" : "s"); for (te = fte; te < lte; te++) { int dm, ds; dm = te[1].te_msf.m_units - te->te_msf.m_units; ds = te[1].te_msf.s_units - te->te_msf.s_units; if (ds < 0) dm -= 1, ds += 60; printf("Track %2d: starting at %2d:%02d, duration %2d:%02d\n", te->te_track_number, te->te_msf.m_units, te->te_msf.s_units, dm, ds); #if 0 printf("\twith%s pre-emphasis, copy %s, %s track, %d channel.\n", (te->te_control & CDROM_AUDIO_PREMPH) ? "" : "out", (te->te_control & CDROM_COPY_PERMITTED) ? "allowed" : "prohibited", (te->te_control & CDROM_DATA_TRACK) ? "data" : "audio", (te->te_control & CDROM_FOUR_CHAN_AUDIO) ? 4 : 2); #endif } }