--- libcdplay.c.orig 2003-04-10 19:19:44.000000000 +0200 +++ libcdplay.c 2008-06-24 01:07:56.000000000 +0200 @@ -1,484 +1,186 @@ -/* $Revision: 1.4 $ */ -/* - * This is libcdplay, a Linux-specific CD-ROM playing library. - * - * This code is (C) 1998-2001 David E. Smith - * and released under the GNU GPL. See `COPYING' for details. +/*- + * Copyright (c) 2008 Pietro Cerutti * - * (Old versions were under the LGPL. This is no longer the case.) + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + + +/* + * This is a wrapper around libcdaudio */ #include "libcdplay.h" -#include "sha.h" -#include "mbo.h" -#include "dcd.h" -#include "base64.h" -#include -#include -#include -#include -#include -#include -#include #include -#include +#include +static int cd_desc; +static struct disc_status status; +static struct disc_info info; +static char disc_id[9]; -#define DISC_MAGIC 42 -static int cdrive; /* fd for the cd, natch */ -static int disc_present = DISC_MAGIC; /* ugly hack for cd_present() */ -static char discid[33]; - -static void int_cd_discid(); - -/* Plays just the one track specified by trknum. */ -u_char cd_play_track (u_char trknum) { - struct cdrom_ti trkidx; - #ifdef DEBUG - fprintf (stderr,"Entering cd_play_track (%i)\n", trknum); - #endif - if (!cd_present()) return -1; - memset (&trkidx, 0, sizeof (struct cdrom_ti)); - trkidx.cdti_trk0 = trkidx.cdti_trk1 = trknum; - if (cd_data_track(trknum)) return -1; -#ifdef OLD_STOP_BEHAVIOUR - if(ioctl (cdrive, CDROMSTOP) == -1) - fprintf(stderr, "CDROMSTOP failed: %s\n", strerror(errno)); -#else - if(ioctl (cdrive, CDROMSTART) == -1) - fprintf(stderr, "CDROMSTART failed: %s\n", strerror(errno)); -#endif - if(ioctl (cdrive, CDROMPLAYTRKIND, &trkidx) == -1) { - struct cdrom_tocentry toce0, toce1; - struct cdrom_msf play; - toce0.cdte_format = CDROM_MSF; - toce0.cdte_track = trknum; - if(ioctl (cdrive, CDROMREADTOCENTRY, &toce0) == -1) - fprintf(stderr, "CDROMREADTOCENTRY[1] failed: %s\n", strerror(errno)); - toce1.cdte_format = CDROM_MSF; - toce1.cdte_track = ((trknum == cd_last_track()) ? CDROM_LEADOUT : (trknum+1)); - if(ioctl (cdrive, CDROMREADTOCENTRY, &toce1) == -1) { - fprintf(stderr, "CDROMREADTOCENTRY[2] (CDROM_LEADOUT) failed: %s\n", - strerror(errno)); - } - play.cdmsf_min0=toce0.cdte_addr.msf.minute; - play.cdmsf_sec0=toce0.cdte_addr.msf.second; - play.cdmsf_frame0=toce0.cdte_addr.msf.frame; - play.cdmsf_min1=toce1.cdte_addr.msf.minute; - play.cdmsf_sec1=toce1.cdte_addr.msf.second; - play.cdmsf_frame1=toce1.cdte_addr.msf.frame; - if(ioctl (cdrive, CDROMPLAYMSF, &play) == -1) - fprintf(stderr, "CDROMPLAYMSF failed: %s\n", strerror(errno)); - } - return trknum; -} /* cd_play_track */ - -/* Plays a sequential list of tracks, from trk1 to trk2. Unused. ;) */ -u_char cd_play_sequence (u_char trk1, u_char trk2) { - struct cdrom_ti trkidx; - #ifdef DEBUG - fprintf (stderr, "Entering cd_play_sequence (%i to %i)\n", trk1, trk2); - #endif - if (!cd_present()) return -1; - memset (&trkidx, 0, sizeof (struct cdrom_ti)); - trkidx.cdti_trk0 = trk1; - trkidx.cdti_trk1 = trk2; -#ifdef OLD_STOP_BEHAVIOUR - if(ioctl (cdrive, CDROMSTOP) == -1) - fprintf(stderr, "CDROMSTOP failed: %s\n", strerror(errno)); -#else - if(ioctl (cdrive, CDROMSTART) == -1) - fprintf(stderr, "CDROMSTART failed: %s\n", strerror(errno)); -#endif - if(ioctl (cdrive, CDROMPLAYTRKIND, &trkidx) == -1) { - struct cdrom_tocentry toce0, toce1; - struct cdrom_msf play; - toce0.cdte_format = CDROM_MSF; - toce0.cdte_track = trk1; - if(ioctl (cdrive, CDROMREADTOCENTRY, &toce0) == -1) - fprintf(stderr, "CDROMREADTOCENTRY[1] failed: %s\n", strerror(errno)); - toce1.cdte_format = CDROM_MSF; - toce1.cdte_track = ((trk2 == cd_last_track()) ? CDROM_LEADOUT : (trk2+1)); - if(ioctl (cdrive, CDROMREADTOCENTRY, &toce1) == -1) { - fprintf(stderr, "CDROMREADTOCENTRY[2] (CDROM_LEADOUT) failed: %s\n", - strerror(errno)); - } - play.cdmsf_min0=toce0.cdte_addr.msf.minute; - play.cdmsf_sec0=toce0.cdte_addr.msf.second; - play.cdmsf_frame0=toce0.cdte_addr.msf.frame; - play.cdmsf_min1=toce1.cdte_addr.msf.minute; - play.cdmsf_sec1=toce1.cdte_addr.msf.second; - play.cdmsf_frame1=toce1.cdte_addr.msf.frame; - if(ioctl (cdrive, CDROMPLAYMSF, &play) == -1) - fprintf(stderr, "CDROMPLAYMSF failed: %s\n", strerror(errno)); - } - return trk2; -} /* play_seq */ - -/* Plays from trknum to the end of the disc */ -u_char cd_play_disc (u_char trknum) { - struct cdrom_ti trkidx; - #ifdef DEBUG - fprintf (stderr,"Entering cd_play_disc (%i)\n", trknum); - #endif - if (!cd_present()) return -1; - memset (&trkidx, 0, sizeof(struct cdrom_ti)); - trkidx.cdti_trk0 = trknum; - trkidx.cdti_trk1 = cd_last_track(); -#ifdef OLD_STOP_BEHAVIOUR - if(ioctl(cdrive, CDROMSTOP) == -1) /* has to be stopped to reset */ - fprintf(stderr, "CDROMSTOP failed: %s\n", strerror(errno)); -#else - if(ioctl(cdrive, CDROMSTART) == -1) - fprintf(stderr, "CDROMSTART failed: %s\n", strerror(errno)); -#endif - if(ioctl(cdrive, CDROMPLAYTRKIND, &trkidx) == -1) { - struct cdrom_tocentry toce0, toce1; - struct cdrom_msf play; - toce0.cdte_format = CDROM_MSF; - toce0.cdte_track = trknum; - if(ioctl (cdrive, CDROMREADTOCENTRY, &toce0) == -1) - fprintf(stderr, "CDROMREADTOCENTRY[1] failed: %s\n", strerror(errno)); - toce1.cdte_format = CDROM_MSF; - toce1.cdte_track = CDROM_LEADOUT; - if(ioctl (cdrive, CDROMREADTOCENTRY, &toce1) == -1) { - fprintf(stderr, "CDROMREADTOCENTRY[2] (CDROM_LEADOUT) failed: %s\n", - strerror(errno)); - } - play.cdmsf_min0=toce0.cdte_addr.msf.minute; - play.cdmsf_sec0=toce0.cdte_addr.msf.second; - play.cdmsf_frame0=toce0.cdte_addr.msf.frame; - play.cdmsf_min1=toce1.cdte_addr.msf.minute; - play.cdmsf_sec1=toce1.cdte_addr.msf.second; - play.cdmsf_frame1=toce1.cdte_addr.msf.frame; - if(ioctl (cdrive, CDROMPLAYMSF, &play) == -1) - fprintf(stderr, "CDROMPLAYMSF failed: %s\n", strerror(errno)); - } - return trknum; -} /* play_track */ - - -/* This doesn't do what you think. That's why you shouldn't use it... */ -int cd_active() { - struct cdrom_subchnl subchnl; - #if DEBUG > 1 - fprintf (stderr, "Entering cd_active()\n"); - #endif - memset (&subchnl, '\0', sizeof (struct cdrom_subchnl)); - subchnl.cdsc_format=CDROM_MSF; - if(ioctl (cdrive, CDROMSUBCHNL, &subchnl) == -1) - fprintf(stderr, "CDROMSUBCHNL failed: %s\n", strerror(errno)); - #ifdef DEBUG - fprintf (stderr, "subchannel status is %i\n", subchnl.cdsc_audiostatus); - #endif - switch (subchnl.cdsc_audiostatus) { - case CDROM_AUDIO_INVALID: return TRUE; - case CDROM_AUDIO_PLAY: - case CDROM_AUDIO_PAUSED: return TRUE; /* no, it's NOT a typo */ - default: return FALSE; - } - return FALSE; -} - -/* return the track that's playing (or zero) */ -u_char cd_current_track() { - struct cdrom_subchnl subchnl; - #ifdef DEBUG - fprintf (stderr, "Calling cd_current_track()... "); - #endif - if (!cd_present()) return 0; - memset (&subchnl, '\0', sizeof(struct cdrom_subchnl)); - subchnl.cdsc_format = CDROM_MSF; - if(ioctl(cdrive, CDROMSUBCHNL, &subchnl) == -1) - fprintf(stderr, "CDROMSUBCHNL failed: %s\n", strerror(errno)); - if (FALSE == cd_active()) return 0; - #ifdef DEBUG - fprintf (stderr, "returning %i\n", subchnl.cdsc_trk); - #endif - return subchnl.cdsc_trk; -} /* current_track */ - -unsigned long raw_track_length (u_char trknum) { - long frames = 0; - struct cdrom_tocentry toce; - if (!cd_present()) return 0; - toce.cdte_format = CDROM_MSF; - toce.cdte_track = (trknum == cd_last_track()) ? CDROM_LEADOUT : trknum+1; - if(ioctl (cdrive, CDROMREADTOCENTRY, &toce) == -1) - fprintf(stderr, "CDROMREADTOCENTRY[3] failed: %s\n", strerror(errno)); - frames = (toce.cdte_addr.msf.minute) * CD_FRAMES * 60; - frames += (toce.cdte_addr.msf.second) * CD_FRAMES; - frames += toce.cdte_addr.msf.frame; - return frames; -} /* raw */ - -unsigned long cd_track_length_frames (u_char trknum) { - long frames = raw_track_length(trknum); - long prevframes = raw_track_length (trknum-1); - if (!cd_present()) return 0; - frames -= prevframes; - #if DEBUG > 1 - fprintf (stderr, "cd_track_length_frames: track %i is %lu frames\n", - trknum, frames); - #endif - return frames; -} - -/* returns length of track trknum, rounded to nearest second */ -int cd_track_length (u_char trknum) { - long frames = cd_track_length_frames(trknum); - int seconds = frames / CD_FRAMES; - if (!cd_present()) return 0; - if ((frames % CD_FRAMES) > (CD_FRAMES / 2)) seconds++; - #ifdef DEBUG - fprintf (stderr, "cd_track_length: track %i is %i seconds\n", - trknum, seconds); - #endif - return seconds; -} /* track_length */ - -unsigned long cd_disc_length_frames (void) { - long frames = raw_track_length(cd_last_track()); - #if DEBUG > 1 - fprintf (stderr, "cd_disc_length_frames: %lu\n", frames); - #endif - if (!cd_present()) return 0; - return frames; -} - -int cd_disc_length (void) { - long frames = cd_disc_length_frames(); - int seconds = frames / CD_FRAMES; - if (!cd_present()) return 0; - if ((frames % CD_FRAMES) > (CD_FRAMES / 2)) seconds++; - #ifdef DEBUG - fprintf (stderr, "cd_disc_length: %i\n", seconds); - #endif - return seconds; -} - -int cd_hw_status (void) { - struct cdrom_subchnl cdsc; - memset (&cdsc, '\0', sizeof (struct cdrom_subchnl)); - cdsc.cdsc_format = CDROM_MSF; - if(ioctl (cdrive, CDROMSUBCHNL, &cdsc) == -1) - fprintf(stderr, "CDROMSUBCHNL failed: %s\n", strerror(errno)); - return cdsc.cdsc_audiostatus; -} -/* The status codes are defined in cdrom.h. They may include: - CDROM_AUDIO_PLAY (cd playing) - CDROM_AUDIO_PAUSED - CDROM_AUDIO_ERROR - CDROM_AUDIO_COMPLETED (done with last request, drive stopped) - CDROM_AUDIO_NO_STATUS (drive ready) - - Unless you know what you're doing, you probably don't want to use this. -*/ - -/* this was Othmar's, but I sorta reversed it. */ -int cd_present(void) { - int status; - if (disc_present != DISC_MAGIC) status = disc_present; - else { - status = ioctl(cdrive, CDROM_DRIVE_STATUS); - disc_present = status; - } - #ifdef DEBUG - // fprintf (stderr, "cd_present(), status is %i\n", status); - #endif - if ((status == CDS_DISC_OK) || (status == CDS_NO_INFO)) return TRUE; - /* returning TRUE if the drive doesn't support the status ioctl is, - perhaps, risky, but one can only wonder... */ - /* other status codes: no disc, tray open, drive not ready */ - return FALSE; -} - -/* toggle pause state */ -void cd_pause (void) { - int pause = cd_hw_status(); - #ifdef DEBUG - fprintf (stderr, "Entering cd_pause. Current status is %i\n", pause); - #endif - switch (pause) { - case CDROM_AUDIO_PAUSED: - if(ioctl (cdrive, CDROMRESUME) == -1) - fprintf(stderr, "CDROMRESUME failed: %s\n", strerror(errno)); - break; - case CDROM_AUDIO_PLAY: - if(ioctl (cdrive, CDROMPAUSE) == -1) - fprintf(stderr, "CDROMPAUSE failed: %s\n", strerror(errno)); - break; - default: cd_play_disc(cd_first_track()); break; - /* the above was a really cool idea from Ronald Tol */ - /* Any state other than CDROM_AUDIO_PLAY implies that we're not */ - /* playing right now, so what the hell? :) linux/cdrom.h lists */ - /* all the possible states, and this seems to make sense... */ - } -} - - -int cd_paused (void) { - int pause = cd_hw_status(); - #ifdef DEBUG - fprintf (stderr, "Entering cd_paused\n"); - #endif - if (CDROM_AUDIO_PAUSED == pause) return TRUE; - return FALSE; -} - - -void cd_stop (void) { - #ifdef DEBUG - fprintf (stderr, "Entering cd_stop()\n"); - #endif - if(ioctl (cdrive, CDROMSTOP) == -1) - fprintf(stderr, "CDROMSTOP failed: %s\n", strerror(errno)); -} - -void cd_eject (void) { - #ifdef DEBUG - fprintf (stderr, "Entering cd_eject()\n"); - #endif - if (ioctl(cdrive,CDROM_DRIVE_STATUS) == CDS_TRAY_OPEN) - ioctl(cdrive,CDROMCLOSETRAY); - else ioctl (cdrive, CDROMEJECT); -} - -u_char cd_first_track (void) { - struct cdrom_tochdr tochdr; - #ifdef DEBUG - fprintf (stderr, "Calling cd_first_track()... "); - #endif - if (!cd_present()) return 0; - if(ioctl (cdrive, CDROMREADTOCHDR, &tochdr) == -1) - fprintf(stderr, "CDROMREADTOCHDR failed: %s\n", strerror(errno)); - #ifdef DEBUG - fprintf (stderr, "returning %i\n", tochdr.cdth_trk0); - #endif - return tochdr.cdth_trk0; -} - -u_char cd_last_track (void) { - struct cdrom_tochdr tochdr; - #ifdef DEBUG - fprintf (stderr, "Calling cd_last_track()... "); - #endif - if (!cd_present()) return 0; - if(ioctl (cdrive, CDROMREADTOCHDR, &tochdr) == -1) - fprintf(stderr, "CDROMREADTOCHDR failed: %s\n", strerror(errno)); - #ifdef DEBUG - fprintf (stderr, "returning %i\n", tochdr.cdth_trk1); - #endif - return tochdr.cdth_trk1; -} - -int cd_data_track (u_char trknum) { - struct cdrom_tocentry toce; - int i; - if (!cd_present()) return -1; - memset (&toce, '\0', sizeof(struct cdrom_tocentry)); - toce.cdte_format = CDROM_MSF; - toce.cdte_track = trknum; - ioctl(cdrive, CDROMREADTOCENTRY, &toce); - /* if(ioctl (cdrive, CDROMREADTOCENTRY, &toce) == -1) */ - /* fprintf(stderr, "CDROMREADTOCENTRY[4] failed: %s\n", strerror(errno)); */ - /* apparently this fails randomly on some drives for no good reason. */ - i = (toce.cdte_ctrl & CDROM_DATA_TRACK ? TRUE : FALSE); - #ifdef DEBUG - fprintf (stderr, "toce.cdte_ctrl is %i\n", toce.cdte_ctrl); - fprintf (stderr, "cd_data_track(%i) returning %i\n", trknum, i); - #endif - return i; -} - -/* Hey, wow, it's the mandatory `initialize the drive' function. */ -int cd_init_player (char *device) { - #ifdef DEBUG - fprintf(stderr, "Calling cd_init_player(%s). Debugging ON.\n", device); - #endif - - cdrive = open(device, O_RDONLY | O_NONBLOCK); - if (!cdrive) return -1; - - if (!cd_present()) return -1; - - int_cd_discid(); - mbo_init(device); - return cdrive; -} - -/* never use this either. */ -int cd_fd (void) { - return cdrive; -} - -/* Okay, this is just f'n silly. musicbrainz has a "Get CD Index" - * function, but it won't work unless you've already retrieved - * the information from the network. So I have to keep all the - * "Get CD Index ID" code here. - */ +int +cd_init_player(char *device) +{ + cd_desc = cd_init_device(device); + mbo_init(device); + cd_stat(cd_desc, &info); + return (cd_desc); +} + +u_char +dcd_play_track(u_char trknum) +{ + return (cd_play_sequence(trknum, trknum)); +} + +u_char +cd_play_disc(u_char trknum) +{ + return (cd_play_sequence(trknum, info.disc_total_tracks)); +} + +u_char +cd_play_sequence(u_char trk1, u_char trk2) +{ + return (cd_play_track(cd_desc, (int) trk1, (int) trk2) ? -1 : trk1); +} + +u_char +cd_current_track(void) +{ + cd_poll(cd_desc, &status); + return (status.status_current_track); +} + +unsigned long +cd_disc_length_frames(void) +{ + cd_poll(cd_desc, &status); + return (status.status_disc_time.frames); +} + +unsigned long +cd_track_length_frames(u_char trknum) +{ + return (info.disc_track[trknum-1].track_length.frames); +} + +int +cd_disc_length(void) +{ + return (info.disc_length.minutes * 60 + + info.disc_length.seconds); +} + +int +cd_track_length(u_char trknum) +{ + return (info.disc_track[trknum-1].track_length.minutes * 60 + + info.disc_track[trknum-1].track_length.seconds); +} -char *cd_discid(void) { - return discid; +int +cd_track_time(void) +{ + cd_poll(cd_desc, &status); + return (status.status_track_time.minutes * 60 + status.status_track_time.seconds); } -static void int_cd_discid (void) { - SHA_INFO sha; - unsigned char digest[20]; - unsigned long size; - char temp[9]; - int i, lba; - int trackz = cd_last_track(); - struct cdrom_tocentry toce; - - if (!cd_present()) return;; - - #if DEBUG > 1 - fprintf (stderr, "Entering cd_discid()\n"); - #endif - - memset (discid, '\0', sizeof(discid)); - memset (&toce, '\0', sizeof(toce)); - - sha_init(&sha); - sprintf (temp, "%02X", cd_first_track()); - sha_update(&sha, (unsigned char *)temp, strlen(temp)); - sprintf (temp, "%02X", cd_last_track()); - sha_update(&sha, (unsigned char *)temp, strlen(temp)); - - toce.cdte_track = CDROM_LEADOUT; - toce.cdte_format = CDROM_LBA; - if(ioctl(cdrive, CDROMREADTOCENTRY, &toce) == -1) - fprintf(stderr, "CDROMREADTOCENTRY[5] failed: %s\n", strerror(errno)); - lba = toce.cdte_addr.lba + 150; - sprintf (temp, "%08X", lba); - sha_update(&sha, (unsigned char *)temp, strlen(temp)); - - for (i=1; i < 100; i++) { - if (i <= trackz) { - toce.cdte_format = CDROM_LBA; - toce.cdte_track = i; - if(ioctl (cdrive, CDROMREADTOCENTRY, &toce) == -1) - fprintf(stderr, "CDROMREADTOCENTRY[6] failed: %s\n", strerror(errno)); - lba = toce.cdte_addr.lba + 150; - sprintf(temp, "%08X", lba); - } - else sprintf (temp, "%08X", 0); - sha_update(&sha, (unsigned char *)temp, strlen(temp)); - } - sha_final(digest, &sha); - #if DEBUG > 1 - fprintf (stderr, "SHA digest finalized.\n"); - #endif - bin_to_base64(discid, digest, 20, size); - #ifdef DEBUG - fprintf (stderr, "cd_discid returning %s\n", discid); - #endif +int +cd_data_track(u_char trknum) +{ + return (info.disc_track[trknum-1].track_type == CDAUDIO_TRACK_DATA); } -/* there used to be more stuff here, but it's been more-or-less - subsumed by the musicbrainz crapulence. */ +u_char +cd_first_track(void) +{ + return (1); +} + +u_char +cd_last_track(void) +{ + return (info.disc_total_tracks); +} -/* char *cd_discid(void); */ -/* char *cd_subid(void); */ +int +cd_active(void) +{ + cd_poll(cd_desc, &status); + return (status.status_mode == CDAUDIO_PLAYING || + status.status_mode == CDAUDIO_PAUSED); +} + +int +cd_paused(void) +{ + cd_poll(cd_desc, &status); + return (status.status_mode == CDAUDIO_PAUSED); +} + +int +cd_present(void) +{ + return (info.disc_present); +} + +int +cd_hw_status(void) +{ + return 0; +} + +char * +cd_discid(void) +{ + snprintf(disc_id, sizeof(disc_id), "%0x", cddb_discid(cd_desc)); + return (disc_id); +} + +void +dcd_stop(void) +{ + cd_stop(cd_desc); +} + +void +dcd_pause(void) +{ + if(cd_paused()) + cd_resume(cd_desc); + else + cd_pause(cd_desc); +} + +void +dcd_eject(void) +{ + cd_eject(cd_desc); +}