Building an Audio CD Player, Listing 4

### Listing 4. Jcd_Drive_ix86-Linux.c

    #include <sys/types.h>                   /*   1 */
    #include <sys/stat.h>                    /*   2 */
    #include <fcntl.h>                       /*   3 */
    #include <linux/cdrom.h>                 /*   4 */
    #include <sys/ioctl.h>                   /*   5 */
    #include <unistd.h>                      /*   6 */
    #include <stdlib.h>                      /*   7 */
    #include <stdio.h>                       /*   8 */
    #include <time.h>                        /*   9 */
    #include "Jcd_Drive.h"                   /*  10 */
    /**                                          12
      Jcd - Java CD Audio Player                 13
      Michael Hamilton (michael@actrix.gen.nz).  14
      All rights reserved.                       15
     */                                      /*  16 */
     * This code could be far more integrated into
     * the Java code, but I may want to used it
     * with python (my prefered langauge), so I'm
     * avoiding this for now.  Ignoring supporting
     * multi-disc players for now, but the java
     * class allows for it in the future.        22
     * If using kaffe generate .h with
     * "kaffeh Jcd/Drive"                        24
     */                                      /*  26 */
    #ifdef TRUE                              /*  28 */
    #undef TRUE                              /*  29 */
    #undef FALSE                             /*  30 */
    #endif                                   /*  31 */
    #define TRUE  1                          /*  33 */
    #define FALSE 0                          /*  34 */
    /*                                           36
     * Javah and Kaffeh differ over the type of long
     * in Java-1.1.1 this is int32_t             38
     */
    #ifndef KAFFE                            /*  39 */
    #  define Java_Int long  /* Java 1.0 */  /*  40 */
    #else                                    /*  41 */
    #  define Java_Int jint  /* kaffeh */    /*  42 */
    #endif                                   /*  43 */
    #define FRAMES_PER_SECOND \
            Jcd_Drive_FRAMES_PER_SECOND      /*  46 */
    #define MAX_DEVICE_LEN 512               /*  47 */
    #define MAX_ERROR_LEN 512                /*  48 */
    #define FRAME_ADDRESS(mins, secs, frames) \
      (((mins) * FRAMES_PER_SECOND * 60) +    \
            ((secs) * FRAMES_PER_SECOND) + (frames))
    static int debug;                        /*  53 */
    static void
      yield_player(struct HJcd_Drive *drive);/*  55 */
    static void
      take_player(struct HJcd_Drive *drive); /*  56 */
    static void take_player(struct HJcd_Drive *drive)
    {                                        /*  59 */
      /*                                         60
       * If not already open, open the device.   61
       */                                    /*  62 */
      if (unhand(drive)->fd == -1) {         /*  63 */
        char device_name[MAX_DEVICE_LEN + 1];/*  64 */
        javaString2CString(
          unhand(drive)->device_name,device_name,
          MAX_DEVICE_LEN);                   /*  65 */
        if (device_name == '\0') {           /*  66 */
          char *device = getenv("CDROM");    /*  67 */
          if (device == NULL)                /*  68 */
            strcpy(device_name, "/dev/cdrom");
        }                                    /*  70 */
        unhand(drive)->device_flags |=
          (getenv("SBPCD") != NULL) ?        /*  71 */
          Jcd_Drive_FLAG_STOP_PLAY :         /*  72 */
          Jcd_Drive_FLAG_NONE;               /*  73 */
        unhand(drive)->fd =open(device_name,O_RDONLY);
        if (debug)                           /*  77 */
          fprintf(stderr,
                  "Openned %s flags=%d fd=%d\n ",
                  device_name,               /*  79 */
                  unhand(drive)->device_flags,
                  unhand(drive)->fd);        /*  81 */
      }                                      /*  82 */
    }                                        /*  83 */
    static void yield_player(struct HJcd_Drive *drive)
    {                                        /*  87 */
     /*                                          88
      * Close the device, make it available to others.
      */                                     /*  90 */
      if (unhand(drive)->fd != -1) {         /*  91 */
        close(unhand(drive)->fd);            /*  92 */
      }                                      /*  93 */
      unhand(drive)->fd               = -1;  /*  95 */
      unhand(drive)->current_track    = 1;   /*  96 */
      unhand(drive)->current_index    = 1;   /*  97 */
      unhand(drive)->number_of_tracks = 0;   /*  98 */
      unhand(drive)->audio_status     =
        Jcd_Drive_STATUS_INVALID;            /*  99 */
      if (debug)                             /* 101 */
        fprintf(stderr, "closed cd device\n");
    }                                        /* 103 */
    static Java_Int
    new_status(struct HJcd_Drive *drive)     /* 105 */
    {                                        /* 106 */
      * Try to obtain the device and query its status.
      * May cause the tray to close with some drivers.
      */                                     /* 110 */
      struct cdrom_subchnl ch;               /* 111 */
      long stat;                             /* 112 */
      unhand(drive)->current_track   = 1;    /* 114 */
      unhand(drive)->current_index   = 1;    /* 115 */
      unhand(drive)->current_address = 0;    /* 116 */
      if (unhand(drive)->fd == -1) {
                         /* See if there's a new CD */
        take_player(drive);                  /* 120 */
        if (unhand(drive)->fd != -1) {       /* 121 */
          struct cdrom_tochdr tinfo;         /* 122 */
          stat = ioctl(unhand(drive)->fd,
                       CDROMREADTOCHDR,
                       &tinfo);              /* 123 */
          if (stat == -1) {                  /* 124 */
            yield_player(drive);             /* 125 */
            unhand(drive)->audio_status =
              Jcd_Drive_STATUS_INVALID;      /* 126 */
            return (Java_Int)
              unhand(drive)->audio_status;   /* 127 */
          }                                  /* 128 */
          unhand(drive)->number_of_tracks =
            tinfo.cdth_trk1;                 /* 129 */
          if (debug)                         /* 130 */
            fprintf(stderr,                  /* 131 */
                    "number_of_tracks=%d\n",
                    unhand(drive)->number_of_tracks);
        }                                    /* 134 */
      }                                      /* 135 */
      ch.cdsc_format = CDROM_MSF;            /* 137 */
      stat = ioctl(unhand(drive)->fd,
                   CDROMSUBCHNL,
                   &ch);                     /* 139 */
      if (stat == -1) { /* Assume no CD in drive */
        yield_player(drive);                 /* 141 */
        unhand(drive)->audio_status =
          Jcd_Drive_STATUS_INVALID;          /* 142 */
        return (Java_Int) unhand(drive)->audio_status;
      }                                      /* 144 */
      unhand(drive)->current_track = ch.cdsc_trk;
      unhand(drive)->current_index = ch.cdsc_ind;
      unhand(drive)->current_address =
        FRAME_ADDRESS(ch.cdsc_absaddr.msf.minute,
                      ch.cdsc_absaddr.msf.second,
                      ch.cdsc_absaddr.msf.frame);
      unhand(drive)->audio_status =          /* 153 */
        ch.cdsc_audiostatus;                 /* 154 */
      return (Java_Int) unhand(drive)->audio_status;
    }                                        /* 157 */
    static int is_open(struct HJcd_Drive *drive)
    {                                        /* 160 */
      if (unhand(drive)->fd == -1) {         /* 161 */
        return FALSE;                        /* 163 */
      }                                      /* 164 */
      return TRUE;                           /* 165 */
    }                                        /* 166 */
    static int                               /* 168 */
    is_available_now(struct HJcd_Drive *drive)
    {                                        /* 169 */
      new_status(drive);                     /* 170 */
      return is_open(drive);                 /* 171 */
    }                                        /* 172 */
    void Jcd_Drive_initDrive(struct HJcd_Drive *drive)
    {                                        /* 175 */
      fprintf(stderr, "Initializing cd drive...\n");
      debug = getenv("JCD_DEBUG") != NULL;   /* 177 */
      new_status(drive);                     /* 178 */
    }                                        /* 179 */
    Java_Int                                 /* 181 */
    Jcd_Drive_status(struct HJcd_Drive *drive)
    {                                        /* 182 */
      if (unhand(drive)->fd == -1) {         /* 183 */
        return unhand(drive)->audio_status;  /* 184 */
      }                                      /* 185 */
      else {                                 /* 186 */
        return new_status(drive);            /* 187 */
      }                                      /* 188 */
    }                                        /* 189 */
    Java_Int                                 /* 191 */
    Jcd_Drive_currentTrack(struct HJcd_Drive *drive)
    {                                        /* 192 */
      if (!is_available_now(drive)) { return 1; }
      return unhand(drive)->current_track;   /* 194 */
    }                                        /* 195 */
    Java_Int                                 /* 197 */
    Jcd_Drive_currentIndex(struct HJcd_Drive *drive)
    {   /* Must call current track first */  /* 198 */
      if (!is_available_now(drive)) { return 1; }
      return unhand(drive)->current_index;   /* 200 */
    }                                        /* 201 */
    Java_Int                                 /* 204 */
    Jcd_Drive_currentAddress(struct HJcd_Drive *drive)
    {                                        /* 205 */
      if (!is_available_now(drive)) { return 0; }
      return unhand(drive)->current_address; /* 207 */
    }                                        /* 208 */
    Java_Int
    Jcd_Drive_numberOfTracks(struct HJcd_Drive *drive)
    {                                        /* 212 */
      if (!is_open(drive)) { return 0; }     /* 213 */
      return unhand(drive)->number_of_tracks;/* 214 */
    }                                        /* 215 */
    Java_Int                                 /* 218 */
    Jcd_Drive_trackAddress(struct HJcd_Drive *drive,
                           Java_Int track)
    {                                        /* 219 */
      struct cdrom_tocentry tocentry;        /* 220 */
      if (!is_open(drive)) { return 0; }     /* 222 */
      tocentry.cdte_track = track;           /* 223 */
      tocentry.cdte_format = CDROM_MSF;      /* 224 */
      if ((ioctl(unhand(drive)->fd,
                 CDROMREADTOCENTRY,
                 &tocentry)) == -1) {
        if (debug)                           /* 226 */
          fprintf(stderr, "tae=%d\n", track);/* 227 */
        SignalError(0,
                    "Jcd/TrackAddressException",
                    strerror(errno));        /* 228 */
        return 0;                            /* 229 */
      }                                      /* 230 */
      return                                 /* 231 */
        FRAME_ADDRESS(tocentry.cdte_addr.msf.minute,
                      tocentry.cdte_addr.msf.second,
                      tocentry.cdte_addr.msf.frame);
    }                                        /* 234 */
    Java_Int
    Jcd_Drive_trackLength(struct HJcd_Drive *drive,
                          Java_Int n)        /* 236 */
    {                                        /* 237 */
      int starts_at =Jcd_Drive_trackAddress(drive, n);
      int start_of_next =                    /* 239 */
        (n >= unhand(drive)->number_of_tracks)
        ? Jcd_Drive_cdEndAddress(drive)      /* 241 */
        : Jcd_Drive_trackAddress(drive, n + 1);
      return start_of_next - starts_at;      /* 243 */
    }                                        /* 244 */
    Java_Int                                 /* 247 */
    Jcd_Drive_cdEndAddress(struct HJcd_Drive *drive)
    {                                        /* 248 */
      if (!is_open(drive)) { return 0; }     /* 249 */
      return                                 /* 250 */
        Jcd_Drive_trackAddress(drive, CDROM_LEADOUT);
    }                                        /* 251 */
    static int cddbSum(int n)                /* 253 */
    {   /* Sum the digits */                 /* 254 */
      int s = 0;                             /* 255 */
      while (n != 0) {                       /* 256 */
        s += n % 10;                         /* 257 */
        n = n / 10;                          /* 258 */
      }                                      /* 259 */
      return s;                              /* 260 */
    }                                        /* 261 */
    struct Hjava_lang_String *Jcd_Drive_cddbID(
      struct HJcd_Drive *drive)              /* 263 */
    {                                        /* 264 */
      /* see http://sunsite.unc.edu~/cddb/xjcd/ */
      char id[10] = "00000000";              /* 266 */
      int i;                                 /* 267 */
      int t = 0;                             /* 268 */
      int n = 0;                             /* 269 */
      if (!is_open(drive))
        { return makeJavaString(id, 8); }    /* 271 */
      for (i = 1;
           i
           i++) {                            /* 272 */
        n += cddbSum(Jcd_Drive_trackAddress(drive, i)
                     / FRAMES_PER_SECOND);
        t += (Jcd_Drive_trackLength(drive, i) /
              FRAMES_PER_SECOND) ;           /* 274 */
      }                                      /* 275 */
      i = ((n % 0xff)
           << 24 | t
           << 8 | (unhand(drive)->number_of_tracks));
      sprintf(id, "%08x", i);                /* 277 */
      return makeJavaString(id, 8);          /* 278 */
    }                                        /* 279 */
    void Jcd_Drive_play(struct HJcd_Drive *drive,
                            Java_Int start_track,
                            Java_Int start_index,
                            Java_Int end_track,
                            Java_Int end_index)
    {                                        /* 286 */
      struct cdrom_ti ti;                    /* 287 */
      long stat;                             /* 288 */
      unhand(drive)->current_track = 1;      /* 290 */
      if (!is_available_now(drive)) { return; }
      if (debug)                             /* 294 */
        fprintf(stderr,                      /* 295 */
                "play %d %d %d\n",           /* 296 */
                start_track,
                end_track,
                unhand(drive)->number_of_tracks);
      if (end_track == 0) {
        /* Kludge for first call to play. */ /* 299 */
        end_track = unhand(drive)->number_of_tracks;
      }                                      /* 301 */
      if (start_track
        start_track > unhand(drive)->number_of_tracks)
        {
          SignalError(0,
             "Jcd/DriveException",
             "Play: start track out of range.");
          return;                            /* 305 */
      }                                      /* 306 */
      if (end_track
          end_track > unhand(drive)->number_of_tracks)
        {
          SignalError(0,
                      "Jcd/DriveException",
                      "Play: end track out of range.");
          return;                            /* 310 */
        }                                    /* 311 */
      if (!is_open(drive)) {                 /* 313 */
        return;                              /* 314 */
      }                                      /* 315 */
      if ((unhand(drive)->device_flags &
           Jcd_Drive_FLAG_STOP_PLAY)) {      /* 317 */
           /* Must issue stop before play. */
        Jcd_Drive_stop(drive);               /* 318 */
      }                                      /* 319 */
      if (debug)                             /* 321 */
        fprintf(stderr, "try play %d\n", start_track);
      ti.cdti_trk0 = start_track;            /* 324 */
      ti.cdti_ind0 = start_index;            /* 325 */
      ti.cdti_trk1 = end_track;              /* 326 */
      ti.cdti_ind1 = end_index;              /* 327 */
      if (ti.cdti_ind1 == 0) {               /* 329 */
        /* Doesn't seem to be a way of specifying end
         * index.  But this seems to work.      331
         */                                  /* 332 */
        ti.cdti_ind1 =unhand(drive)->number_of_tracks;
      }                                      /* 334 */
      stat = ioctl(unhand(drive)->fd,
                   CDROMPLAYTRKIND,
                   &ti);                     /* 336 */
      if (stat
        SignalError(0, "Jcd/PlayException",
                    strerror(errno));        /* 338 */
        return;                              /* 339 */
      }                                      /* 340 */
      unhand(drive)->current_track = start_track;
      if (debug)                             /* 344 */
        fprintf(stderr, "playing %d\n",      /* 346 */
                unhand(drive)->current_track);
    }                                        /* 346 */
    void Jcd_Drive_stop(struct HJcd_Drive *drive)
    {                                        /* 350 */
      if (!is_available_now(drive)) { return; }
      if (ioctl(unhand(drive)->fd, CDROMSTOP) == -1) {
        SignalError(0, "Jcd/StopException",
                    strerror(errno));        /* 354 */
      }                                      /* 355 */
    }                                        /* 356 */
    void Jcd_Drive_pause(struct HJcd_Drive *drive)
    {                                        /* 360 */
      if (!is_available_now(drive)) { return; }
      if (unhand(drive)->audio_status !=
          Jcd_Drive_STATUS_PLAY) {           /* 363 */
        SignalError(0, "Jcd/PauseException",
                    "Pause: drive isn't playing.");
        return;                              /* 365 */
      }                                      /* 366 */
      if (ioctl(unhand(drive)->fd,CDROMPAUSE) == -1) {
        SignalError(0, "Jcd/PauseException",
                    strerror(errno));        /* 369 */
      }                                      /* 370 */
    }                                        /* 371 */
    void Jcd_Drive_resume(struct HJcd_Drive *drive)
    {                                        /* 375 */
      if (!is_available_now(drive)) { return; }
      if (unhand(drive)->audio_status !=
          Jcd_Drive_STATUS_PAUSED) {         /* 378 */
        SignalError(0, "Jcd/ResumeException",
                    "Resume: drive isn't paused.");
        return;                              /* 380 */
      }                                      /* 381 */
      if (ioctl(unhand(drive)->fd,CDROMRESUME) == -1){
        SignalError(0, "Jcd/ResumeException",
                    strerror(errno));        /* 384 */
      }                                      /* 385 */
    }                                        /* 386 */
    void Jcd_Drive_eject(struct HJcd_Drive *drive)
    {                                        /* 390 */
      long retract = unhand(drive)->fd == -1;
      if (!is_available_now(drive)) { return; }
      if (retract) { return; }               /* 396 */
      Jcd_Drive_stop(drive);                 /* 399 */
      if (ioctl(unhand(drive)->fd,CDROMEJECT) == -1) {
        SignalError(0, "Jcd/EjectException",
                    strerror(errno));        /* 402 */
      }                                      /* 403 */
      yield_player(drive);                   /* 405 */
    }                                        /* 406 */
    Java_Int                                 /* 409 */
    Jcd_Drive_volume(struct HJcd_Drive *drive)
    {                                        /* 410 */
      struct cdrom_volctrl vol;              /* 411 */
      if (!is_available_now(drive)) { return; }
      if (ioctl(unhand(drive)->fd, CDROMVOLREAD,
                &vol) == -1) {               /* 415 */
        SignalError(0, "Jcd/VolumeException",
                    strerror(errno));        /* 416 */
        return 0;                            /* 417 */
      }                                      /* 418 */
      return vol.channel0;                   /* 419 */
    }                                        /* 420 */
    void Jcd_Drive_setVolume(struct HJcd_Drive *drive,
                             Java_Int volume)
    {                                        /* 423 */
      struct cdrom_volctrl vol;              /* 424 */
      if (!is_available_now(drive)) { return; }
      if (volume
        SignalError(0, "Jcd/SetVolumeException",
                    "Volume out of range."); /* 429 */
        return;                              /* 430 */
      }                                      /* 431 */
      vol.channel0 = volume;                 /* 433 */
      vol.channel1 = volume;                 /* 434 */
      vol.channel2 = volume;                 /* 435 */
      vol.channel3 = volume;                 /* 436 */
      if (ioctl(unhand(drive)->fd, CDROMVOLCTRL,
                &vol) == -1) {               /* 438 */
        SignalError(0, "Jcd/SetVolumeException",
                    strerror(errno));        /* 439 */
      }                                      /* 440 */
    }                                        /* 441 */
    struct Hjava_lang_String *               /* 444 */
    Jcd_Drive_productCode(struct HJcd_Drive *drive)
    {                                        /* 445 */
      char upc[9] = "00000000";              /* 446 */
      if (!is_available_now(drive)) {
        return makeJavaString(upc, 8);       /* 448 */
      }                                      /* 449 */
      if (ioctl(unhand(drive)->fd, CDROM_GET_UPC, upc)
          == -1) {                           /* 450 */
        SignalError(0, "Jcd/ProductCodeException",
                    strerror(errno));        /* 451 */
      }                                      /* 452 */
      return makeJavaString(upc, 8);         /* 453 */
    }                                        /* 454 */