xfreecd

Audio CD player for X
git clone https://www.brianlane.com/git/xfreecd
Log | Files | Refs | README | LICENSE

cd_control.c (11629B)


      1 /* -----------------------------------------------------------------------
      2    cd_control process
      3 
      4    part of XfreeCD
      5 
      6    Copyright (C) 1998 Brian C. Lane
      7    nexus@tatoosh.com
      8    http://www.tatoosh.com/nexus
      9 
     10    This program is free software; you can redistribute it and/or
     11    modify it under the terms of the GNU General Public License
     12    as published by the Free Software Foundation; either version 2
     13    of the License, or (at your option) any later version.
     14 
     15    This program is distributed in the hope that it will be useful,
     16    but WITHOUT ANY WARRANTY; without even the implied warranty of
     17    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     18    GNU General Public License for more details.
     19 
     20    You should have received a copy of the GNU General Public License
     21    along with this program; if not, write to the Free Software
     22    Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
     23    
     24    ==========================[ HISTORY ]==================================
     25    06/22/98   Bug in track length calculation. It was waiting until track
     26               #2 before calculating the lengths (holdover from the change
     27 	      to 0 offset from 1 offset). FIXED.
     28 
     29    05/08/98   Added the control and init code from old version of xfreecd
     30               Added software control of the EJECT on CLOSE state - using
     31 	      the CD_SET_EJECT command and an argument of 0 to disable
     32 	      eject on exit and 1 to enable eject on exit.
     33 
     34    05/03/98   Wrote the synchronization code and basic switch statment.
     35               Testing different shutdown situations. No hungup child
     36 	      processes yet.
     37 
     38 	      Things this process needs to do:
     39 	      x 1. Get the data on the current CD and send it to the
     40 	           parent, in response to a request by the parent.
     41 	      x 2. Play a CD
     42 	      x 3. Pause a CD
     43 	      x 4. Resume a CD
     44 	      x 5. Go to next track
     45 	      x 6. Go to Previous track
     46 	      x 8. Eject a CD
     47 	      x 9. Close the CD tray
     48 	     10. Switch CDs for a CD changer
     49 	     11. Seek forward (may need a kernel patch?)
     50 	     12. Seek backwards (may need a kernel patch?)
     51 	     13. Return error/status codes (like if a play is tried and
     52 	         there is no CD in the player).
     53 	     14. Shuffle play (on one Cd or among multiple CDs)
     54 	     15. Repeat Song
     55 	     16. Repeat whole CD, repeat group of CDs
     56 
     57 
     58    ----------------------------------------------------------------------- */
     59 #include <stdio.h>
     60 #include <fcntl.h>
     61 #include <sys/ioctl.h>
     62 #include <sys/types.h>
     63 #include <sys/socket.h>
     64 #include <unistd.h>
     65 #include <gtk/gtk.h>
     66 #include "child_sync.h"
     67 #include "cd_control.h"
     68 #include "cddb.h"
     69 
     70 int cd_control( int );
     71 int readline( register int, register char *, register int );
     72 
     73 #undef DEBUG1
     74 #undef DEBUG3
     75 
     76 /*
     77    Fire up the cd_control process.
     78    Create a socket pair.
     79    Fork a child process
     80    Return the parent's file descriptor for the socket or -1 + errno
     81    fill in childpid with the child's PID
     82    Child calls cd_control 
     83 */
     84 int start_cd_control( int *childpid )
     85 {
     86   int  fd[2];
     87 
     88   if( socketpair( AF_UNIX, SOCK_STREAM, 0, fd ) < 0 )
     89     return(-1);
     90 
     91   if( ( *childpid = fork() ) < 0 )
     92   {
     93     perror("start_cd_control, cannot fork");
     94     return(-1);
     95   } else if( *childpid == 0 ) {
     96     close( fd[0] );
     97 
     98     /* Synchronize with the parent, exit if it fails */
     99     if( parent_sync( fd[1] ) == 0 )
    100       cd_control( fd[1] );
    101     
    102     close( fd[1] );
    103     exit(0);
    104   } 
    105   close( fd[1] );
    106 
    107   if( child_sync( fd[0] ) == 0 )
    108     return(fd[0]);  
    109 
    110   return(-1);
    111 }
    112 
    113 
    114 /*
    115    Load a new CD, read all of its info into the cdinfo array
    116 */
    117 int cdrom_init( struct CDINFO *cdinfo, struct cdrom_volctrl *volctrl, char *cd_device )
    118 {
    119   int  fd;
    120   int  x;
    121 
    122   /* Try and open the /dev/cdrom device */
    123   if( ( fd = open( cd_device, O_RDONLY ) ) < 0 )
    124     {
    125 #ifdef DEBUG3
    126       g_print( "device = %s\n", cd_device);
    127 #endif
    128 
    129       perror("CDROM device" );
    130       return -1;
    131     }
    132   
    133   /* Read the cdrom's header information */
    134   if( ioctl( fd, CDROMREADTOCHDR, &cdinfo->tochdr ) != 0 )
    135     {
    136       perror("cdrom_init(CDROMREADTOCHDR-2)" );
    137       return -1;
    138     }
    139 
    140   /* Get the time on the end of the last track */
    141   cdinfo->leadout.cdte_track  = CDROM_LEADOUT;
    142   cdinfo->leadout.cdte_format = CDROM_MSF;
    143 
    144   if( ioctl( fd, CDROMREADTOCENTRY, &cdinfo->leadout ) != 0 )
    145     {
    146       perror( "cdrom_init(CDROMREADTOCENTRY-2)" );
    147       return -1;
    148     }
    149 
    150   /* Calculate the total length of the CD */
    151   cdinfo->cd_length = cdinfo->leadout.cdte_addr.msf.second \
    152                      + (cdinfo->leadout.cdte_addr.msf.minute * 60);
    153 
    154   /* Read the current CD volume level - different from soundcard volume */
    155   if( ioctl( fd, CDROMVOLREAD, volctrl ) == 0 )
    156     { 
    157       cdinfo->volume = volctrl->channel0;
    158     } else {
    159       perror( "cdrom_init(CDROMVOLREAD-2)" );
    160       return -1;
    161     }
    162 
    163   /* Fill in the tracks[] array with info on all the tracks */
    164   for( x = 0; x < cdinfo->tochdr.cdth_trk1; x++ )
    165     {
    166       /* Read the info for the current track */
    167       cdinfo->track[x].te.cdte_track  = x+1;
    168       cdinfo->track[x].te.cdte_format = CDROM_MSF;
    169       if( ioctl( fd, CDROMREADTOCENTRY, &cdinfo->track[x].te ) != 0 )
    170 	{
    171 	  perror( "set_track_length(CDROMREADTOCENTRY)" );
    172 	  return -1;
    173 	}
    174 
    175       cdinfo->track[x].frame_offset = cdinfo->track[x].te.cdte_addr.msf.minute * 60 + cdinfo->track[x].te.cdte_addr.msf.second;
    176 
    177       /* Convert seconds to frames */
    178       cdinfo->track[x].frame_offset *= 75;
    179       cdinfo->track[x].frame_offset += cdinfo->track[x].te.cdte_addr.msf.frame;
    180 
    181       /* 
    182        * Set the length of the previous track
    183        *  Accomplished by taking the start of the current track - start
    184        *  of the previous track.
    185        */
    186       if( x > 0 )
    187 	{
    188 	  /* Set length to the start of the current one */
    189 	  cdinfo->track[x-1].length = (cdinfo->track[x].te.cdte_addr.msf.minute * 60) + cdinfo->track[x].te.cdte_addr.msf.second;
    190 
    191 	  /* Subtract the start of the previous track */
    192 	  cdinfo->track[x-1].length = cdinfo->track[x-1].length - ((cdinfo->track[x-1].te.cdte_addr.msf.minute * 60) + cdinfo->track[x-1].te.cdte_addr.msf.second);
    193 
    194 #ifdef DEBUG1
    195 	  /* Show some debug data on the tracks we find */
    196 	  g_print("track %d - length = %d seconds\n", x-1, cdinfo->track[x-1].length );
    197 #endif
    198 	}
    199     }
    200 
    201   /* 
    202    * Set the length of the last track
    203    * This is accomplished by subtracting the last track start from the
    204    * length of the CD.
    205    */
    206   x--;
    207   cdinfo->track[x].length = cdinfo->cd_length - ((cdinfo->track[x].te.cdte_addr.msf.minute * 60) + cdinfo->track[x].te.cdte_addr.msf.second );
    208 
    209 #ifdef DEBUG1
    210   /* Show debug data on the last track */
    211   g_print("track %d - length = %d seconds\n", x, cdinfo->track[x].length );
    212   
    213   g_print("New CD loaded: ");
    214   g_print("%d tracks, cd_length = %d, ", cdinfo->tochdr.cdth_trk1, cdinfo->cd_length );
    215   g_print("volume = %d\n", cdinfo->volume );
    216 #endif		
    217 
    218   /* Calculate the discid for this CD */
    219   cdinfo->discid = cddb_discid( cdinfo );
    220 
    221   cdinfo->revision = -1;
    222   cdinfo->extd = NULL;
    223 
    224 
    225   /* All is well */
    226   return fd;
    227 }
    228 
    229 
    230 /* ----------------------------------------------------------------------
    231    Control the low-level audio CD player
    232 
    233    Wait for commands from the parent, execute the command and send back
    234    a response if one is needed
    235 
    236    A command is 2 bytes of data - a command and an optional argument.
    237    The Commands are defined in cd_control.h as CD_*
    238 
    239    To init the cd, if status is called the first time it will try to init
    240    the CD. If this fails it sets cd_fd to -1, so status won't repeatedly
    241    try the CD. After the initial failure only a play will try.
    242 
    243    ----------------------------------------------------------------------- */
    244 int cd_control( int control_fd )
    245 {
    246   char   cmnd[2],
    247          cd_device[80];                 /* Device to talk to */
    248   struct cdrom_volctrl volctrl;  	/* Volume control 		 */
    249   struct CDINFO cdinfo;                 /* All the info on the CD        */
    250   int  fd_cd=0;
    251 
    252   /* Default cdrom device */
    253   strcpy( cd_device, "/dev/cdrom" );
    254 
    255   for(;;)
    256   {
    257     /* Get the command from the parent process */
    258     if( read( control_fd, &cmnd, 2 ) != 2 )
    259       return(-1);
    260 
    261     switch( cmnd[0] )
    262       {
    263       case CD_DIAG :
    264 	puts("cd_control diagnostic\n");
    265 	break;
    266 
    267       case CD_STATUS :
    268 	if( fd_cd > 0 )
    269 	  {
    270 	    cdinfo.sc.cdsc_format = CDROM_MSF;     /* Info in MM:SS:FF format*/
    271 	    if( ioctl( fd_cd, CDROMSUBCHNL, &cdinfo.sc ) != 0 )
    272 	      {
    273 		/* perror( "cd_control(CDROMSUBCHNL-2)" ); */
    274 		cdinfo.sc.cdsc_audiostatus = CDROM_AUDIO_NO_STATUS;
    275 	      }
    276   	  } else {
    277 	    cdinfo.sc.cdsc_audiostatus = CDROM_AUDIO_NO_STATUS;
    278 	  }
    279 
    280 	/* Send the new data back to the main process */
    281 	write( control_fd, &cdinfo, sizeof( struct CDINFO ) );
    282 	break;
    283 
    284 
    285       case CD_PLAY :
    286 	/* If we are not open yet, try to open it first */
    287 	if( fd_cd < 1 )
    288 	  {
    289 	    fd_cd = cdrom_init( &cdinfo, &volctrl, cd_device );
    290 	    
    291 	    /* Start playing at the beginning*/
    292 	    cmnd[1] = cdinfo.tochdr.cdth_trk0;
    293 	  }
    294 
    295 	if( fd_cd > 0 )
    296 	  {
    297 	    /* Setup the CD to play the indicated track */
    298 	    cdinfo.ti.cdti_trk0 = cmnd[1];
    299 	    cdinfo.ti.cdti_trk1 = cdinfo.tochdr.cdth_trk1;
    300 	    cdinfo.ti.cdti_ind0 = cdinfo.ti.cdti_ind1 = 0;
    301 
    302 	    /* Stop playing previous track and start playing the next */
    303 	    /* Try this without the stop and see what happens... */
    304 	    if( ioctl( fd_cd, CDROMSTOP ) != 0 )
    305 	      {
    306 		perror( "cd_control(CDROMSTOP-1)" );
    307 	      } else if( ioctl( fd_cd, CDROMPLAYTRKIND, &cdinfo.ti ) != 0 ) {
    308 		perror( "cd_control(CDROMPLAYTRKIND-1)" );
    309 	      }
    310 	  }
    311 	break;
    312 
    313       case CD_PAUSE :
    314 	if( fd_cd > 0 )
    315 	  {
    316 	    if( ioctl( fd_cd, CDROMPAUSE ) != 0 )
    317 	      perror( "cd_control(CDROMPAUSE)" );
    318 	  }
    319 	break;
    320 
    321       case CD_RESUME :
    322 	if( fd_cd > 0 )
    323 	  {
    324 	    if( ioctl( fd_cd, CDROMRESUME ) != 0 )
    325 	      perror( "cd_control(CDROMRESUME)" );
    326 	  }
    327 	break;
    328 
    329       case CD_STOP :
    330 	if( fd_cd > 0 )
    331 	  {
    332 	    if( ioctl( fd_cd, CDROMSTOP ) != 0 )
    333 	      perror( "cd_control(CDROMSTOP-2)" );
    334 
    335 	    close( fd_cd );
    336 	    fd_cd = 0;
    337 	  }
    338 	break;
    339 
    340       case CD_VOLUME :
    341 	if( fd_cd > 0 )
    342 	  {
    343 	    cdinfo.volume = (unsigned char) cmnd[1];
    344 
    345 	    volctrl.channel0 = cdinfo.volume;
    346 	    volctrl.channel1 = cdinfo.volume;
    347 	    if( ioctl( fd_cd, CDROMVOLCTRL, &volctrl ) != 0 )
    348 	      perror( "cd_control(CDROMVOLCTRL-3)" );
    349 	  }
    350 	break;
    351 
    352       case CD_EJECT :
    353 	if( (fd_cd > 0) )
    354 	  {
    355 	    /* Stop playing, then eject the CD */
    356 	    if( ioctl( fd_cd, CDROMSTOP ) != 0 )
    357 	      {
    358 		perror( "cd_control(CDROMSTOP-3)" );
    359 	      } else  if( ioctl( fd_cd, CDROMEJECT ) != 0 ) {
    360 		perror( "cd_control(CDROMEJECT)" );
    361 	      }
    362 	    close( fd_cd );
    363 	    fd_cd = 0;
    364 	  }
    365 	break;
    366 
    367       case CD_SET_EJECT :
    368 	if( fd_cd > 0 )
    369 	  {
    370 	    /* Set the software configurable eject on close state */
    371 	    if( ioctl( fd_cd, CDROMEJECT_SW, cmnd[1] ) != 0 )
    372 	      perror("cd_control(CDROMEJECT_SW)");
    373 	  }
    374 	break;
    375 
    376 	/* Set the default device, close the current device and open the
    377 	   new one */
    378       case CD_SET_DEVICE :
    379 
    380 #ifdef DEBUG3
    381 	printf("got CD_SET_DEVICE\n");
    382 #endif
    383 
    384 	/*
    385 	   This routine is different from the others, it reads a string from
    386 	   the sending process after receiving the 2 byte command
    387 	*/
    388 	if( readline( control_fd, cd_device, 80 ) < 0 )
    389 	  {
    390 	    perror("readline - 2");	    
    391 	  }
    392 
    393 	/* Strip trailing CR */
    394 	if( cd_device[strlen(cd_device)-1] == '\n' )
    395 	  cd_device[strlen(cd_device)-1] = 0;
    396 
    397 #ifdef DEBUG3
    398 	printf("Setting cd_control cd_device = %s\n", cd_device );
    399 #endif
    400 	/* If a device isn't open, then open one */
    401 	if( fd_cd < 1 )
    402 	  fd_cd = cdrom_init( &cdinfo, &volctrl, cd_device );
    403 
    404 	break;
    405 
    406 
    407       case CD_QUIT :
    408 	if( fd_cd )
    409 	  close( fd_cd );
    410 	return(0);
    411 	break;
    412       }
    413   }
    414   return(0);
    415 }