ftp.nice.ch/pub/next/tools/cdrom/playcd.1.3.s.tar.gz#/playcd-1.3/playcd.c

This is playcd.c in view mode; [Download] [Up]

/* -*-C-*-
*******************************************************************************
*
* File:         play3401.c
* RCS:          /usr/local/lib/cvs/play3401/play3401.c,v 1.3 1994/03/03 19:10:36 cedman Exp
* Description:  Play Toshiba 3401
* Author:       Carl Edman
* Created:      Wed May  5 21:34:47 1993
* Modified:     Thu Mar  3 13:48:25 1994 (Carl Edman) cedman@capitalist.princeton.edu
* Modified:     Sat Apr 08 1995 (Juergen Sell) js@icem.de
                + support the NEC player (3Xi that is, if it matters)
                + force search for drive to begin at /dev/sd2 hardcoded
                  (this should become a paramter)
* Modified      Sat Apr 08 1995 js
                + support -d<number> parameter to set start search device
                  to /dev/sd<number>
* Modified      Wed Apr 12 1995 js
                + added -l option to produce frames-listing
                + incorporated toshiba and nec support in a single source
* Language:     C
* Package:      N/A
* Status:       Alpha
*
* (C) Copyright 1993, but otherwise this file is perfect freeware.
*
*******************************************************************************
*/

/* define exactly one of the following */
#if (0) /* Toshiba 3401 */
#define TOSHIBA
#define NO_TRACK (0xaa)
#define OPCODE (0x28)
#define CONTROL (1<<6)
#define DEVICE "TOSHIBA CD-ROM XM-3401TA" /* or try "TOSHIBA CD-ROM XM-4101TA" */
#define LBA (BCD((rp+offset)/(75*60))<<24)+(BCD(((rp+offset)/75)%60)<<16)+(BCD((rp+offset)%75)<<8)
#else if (0) /* nec 3x */
#define NO_TRACK (0xA2)
#define OPCODE (0xD4)
#define CONTROL (0)
#define DEVICE "NEC CD-ROM DRIVE:500"
#define LBA (rp+offset)
#endif

#include <ctype.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <libc.h>
#include <signal.h>
#include <errno.h>
#include <sys/file.h>
#include <sys/types.h>
#include <sys/resource.h>
#include <sound/sound.h>
#include <sound/sounddriver.h>
#include <sound/utilsound.h>
#include "scsicommands.h"

#define BCD(c) (((c)%10)+((((c)/10)%10)<<4))
#define BLOCKSIZE (2352)
#define BUFBLOCK (6*75)
#define READBLOCK (27)
#define TRACKNO (100)
#define GETNO() for(c=0;isascii(*d) && isdigit(*d);d++) c= (c*10)+(*d-'0')

struct Mode
   {
   u_int data_length:8,
         medium_type:8,
         dev_parameter:8,
         bdescriptor_length:8;
   u_int density:8,
         nblocks:24;
   u_int mbz1:8,
         block_length:24;
   };

struct Toc_track_descriptor
   {
   u_int mbz1:8,
         adr:4,
         control:4,
         track_number:8,
         mbz2:8;
   u_int address;
   };

struct Toc
   {
   u_int toc_data_length:16,
         first:8,
         last:8;
   struct Toc_track_descriptor track[TRACKNO];
   };

volatile int done,fillup;
volatile int readp,playp;
port_t devicePort,ownerPort,streamPort,replyPort;
condition_t sync_threads;
mutex_t position;
cthread_t play_t;
char *buf;
struct Toc toc;
int errflg=0,dontejectflg=0,recordflg=0,playflg=0;

extern int getopt(int ac,char *av[],char *opts);
extern int optind;
extern char *optarg;

int interpret_duration(const char *str,int *sf,int *ef)
   {
   int c,i;
   const char *d;

   for(i=0,d=str;*d;d++) if (*d=='-') i++;   
   if (i>1) return -1;

   d=str;

   if (*d=='#')
      {
      d++;
      GETNO();
      for(i=0;i<TRACKNO;i++) if (toc.track[i].track_number==c) break;
      if (i>=TRACKNO) return -1;
      *sf=toc.track[i].address;
      if (*d=='+') d++;
      }
   else
      {
      *sf=toc.track[0].address;
      }

   if (*d && *d!='-')
      {
      GETNO();
      if (*d==':')
         {
         d++;
         *sf+=c*60*75;
         GETNO(); 
	 if (c>=60) return -1;
         *sf+=c*75;
         if (*d==':')
            {
            d++;
            GETNO(); if (c>=75) return -1;
            *sf+=c;
            }
         }
      else
         {
         *sf+=c;
         }
      }

   if (!*d) d=str; else if (*d=='-') d++; else return -1;
   
   if (*d=='#')
      {
      d++;
      GETNO();
      for(i=0;i<TRACKNO;i++) if (toc.track[i].track_number==c) break;
      if (i>=TRACKNO) return -1;
      if (*d=='+')
         {
         *ef=toc.track[i].address;
         d++;
         }
      else
         {
         *ef=toc.track[i+1].address;
         }
      }
   else if (*d=='\0')
      {
      for(i=0;i<TRACKNO;i++) if (toc.track[i].track_number==NO_TRACK) break;
      if (i>=TRACKNO) return -1;
      *ef=toc.track[i].address;
      }
   else
      {
      *ef=toc.track[0].address;
      }

   if (*d && *d!='-')
      {
      GETNO();
      if (*d==':')
         {
         d++;
         *ef+=c*60*75;
         GETNO(); if (c>=60) return -1;
         *ef+=c*75;
         if (*d==':')
            {
            d++;
            GETNO(); if (c>=75) return -1;
            *ef+=c+1;
            }
         else
            {
            *ef+=75;
            }
         }
      else
         {
         *ef+=c+1;
         }
      }

   if (*d && *d!=-1) return -1;
   if (*sf<toc.track[0].address) return -1;
   for(i=0;i<TRACKNO;i++) if (toc.track[i].track_number==NO_TRACK) break;
   if (i>=TRACKNO) return -1;
   if (*ef>toc.track[i].address) return -1;
   return (*sf<*ef);
   } 

void play_suspend(int sig)
   {
   snddriver_stream_control(streamPort,0,SNDDRIVER_PAUSE_STREAM);
   signal(sig,SIG_DFL);
   raise(sig);
   }

void play_continue(int sig)
   {
   signal(SIGTSTP,play_suspend);
   snddriver_stream_control(streamPort,0,SNDDRIVER_RESUME_STREAM);
   }

void setdone(int sig)
   {
   snddriver_stream_control(streamPort,0,SNDDRIVER_ABORT_STREAM);
   done=1;
   }

void play_completed(void *arg,int tag)
   {
   playp=tag;
   }

any_t play_thread(any_t arg)
   {
   int rp,pp,tp,ep;
   unsigned short *p,*e,*r;
   int protocol=0;
   int err;
   snddriver_handlers_t handlers = { 0,0,0,play_completed,0,0,0,0,0,0,0,0 };
   msg_header_t *replyMsg = 0;

   if (playflg)
      {
      replyMsg=malloc(MSG_SIZE_MAX);
      port_allocate(task_self(),&replyPort);
      SNDAcquire(SND_ACCESS_OUT,10,0,0,0,0,&devicePort,&ownerPort);
      snddriver_stream_setup(devicePort,ownerPort,SNDDRIVER_STREAM_TO_SNDOUT_44,vm_page_size/2,2,176400,352800,&protocol,&streamPort);
      }
   
   mutex_lock(position);
   tp=readp;
   while(!done)
      {
      while (!done && ((fillup && (readp<playp+(BUFBLOCK*3)/4))||
                       (!fillup && (readp<=tp))))
         condition_wait(sync_threads,position);
      fillup=0;
      rp=readp; pp=playp;
      mutex_unlock(position);
      ep=tp;
      while (tp<rp)
         {
         ep=tp+75;
         if (ep>rp) ep=rp;
         if (tp%BUFBLOCK+(ep-tp)>BUFBLOCK) ep=tp+BUFBLOCK-(tp%BUFBLOCK);
         p=(unsigned short *)(buf+((tp%BUFBLOCK)*BLOCKSIZE));
         e=(unsigned short *)(buf+((((ep-1)%BUFBLOCK)+1)*BLOCKSIZE));
         for(r=p;r<e;r++) *r=NXSwapShort(*r);

         if (playflg)
            err=snddriver_stream_start_writing(streamPort,
                                               p,e-p,
                                               ep,
                                               0,0,
                                               0,1,0,0,0,0,
                                               replyPort);
         if (recordflg) write(1,p,(e-p)*sizeof(*p));
         tp=ep;
         }
      
      mutex_lock(position);
      if (playflg)
         {
         replyMsg->msg_size=MSG_SIZE_MAX;
         replyMsg->msg_local_port=replyPort;
         while (msg_receive(replyMsg, RCV_TIMEOUT,0)==RCV_SUCCESS)
            {
            err=snddriver_reply_handler(replyMsg,&handlers);
            if (playp>=tp) fillup=1;
            condition_broadcast(sync_threads);
            }
         }
      else
         {
         playp=ep;
         condition_broadcast(sync_threads);
         }
      }
   done=1;
   mutex_unlock(position);
   
   if (playflg)
      {
      free(replyMsg);
      port_deallocate(task_self(),replyPort);
      SNDRelease(SND_ACCESS_OUT,devicePort,ownerPort);
      }
   
   return arg;
   }

int main(int ac,char *av[])
   {
   int c;
   int startframe,endframe;
   struct Mode page,opage;
   int aDeviceId= 0;
   int listFlag= 0;
   

   while((c=getopt(ac,av,"leprd:"))!=EOF) switch(c)
      {
    case 'l':
      listFlag++;
      break;
    case 'e':
      dontejectflg++;
      break;
    case 'p':
      playflg++;
      break;
    case 'r':
      recordflg++;
      break;
    case 'd':
	if (!sscanf( optarg, "%d", &aDeviceId) ) errflg++;
	
	break;
	
    case '?':
    default:
      errflg++;
      }

   if (geteuid())
      {
      fprintf(stderr,"%s was not installed SUID root.\n",av[0]);
      exit(1);
      }

   if (errflg)
      {
      fprintf(stderr,"Usage: %s [-l] [-e] [-r] [-p] [-d<n>] duration ...\n",av[0]);
      exit(2);
      }
   
   if (playflg==0 && recordflg==0)
      playflg++;

   position=mutex_alloc();
   
   sync_threads=condition_alloc();
   
   signal(SIGHUP ,setdone); signal(SIGINT ,setdone); signal(SIGQUIT,setdone);
   signal(SIGILL ,setdone); signal(SIGTRAP,setdone); signal(SIGIOT ,setdone);
   signal(SIGEMT ,setdone); signal(SIGFPE ,setdone); signal(SIGBUS ,setdone);
   signal(SIGSEGV,setdone); signal(SIGSYS ,setdone); signal(SIGPIPE,setdone);
   signal(SIGTERM,setdone); signal(SIGXCPU,setdone); signal(SIGXFSZ,setdone);

   signal(SIGTSTP,play_suspend); signal(SIGCONT,play_continue);
   
   scsisettimeout(10); //js origianl 10
   if (scsigetdev(DEVICE, aDeviceId)==-1) //js, begin at /dev/sd2
      {
      fprintf(stderr,"Can't find CD player -- this version of %s is compiled for %s only.\n",av[0], DEVICE); //js
      exit(3);
      }

   if (scsiread6s(&opage,sizeof(opage),0x1a,0,0,0,sizeof(opage),0))
      {
      fprintf(stderr,"Can't read mode page.\n");
      exit(4);
      }
   
   opage.data_length=0;
   page=opage;
   page.density=0x82;
   page.block_length=0x930;
#ifdef TOSHIBA   
    if (scsiwrite6s(&page,sizeof(page),0x15,0,0,0,sizeof(page),0)) 
       { 
       fprintf(stderr,"Can't write mode page.\n"); 
       exit(5); 
       } 
#endif   
   buf=malloc(BUFBLOCK*BLOCKSIZE);
   done=0;
   fillup=1;

   if (scsiread10(&toc,sizeof(toc),0x43,0,0,0,1,0,0,0,sizeof(toc),0))
      {
      fprintf(stderr,"Can't read CDs table of contents.\n");
      goto end;
      }
   
   for(c=0;c<TRACKNO;c++)
      {
      int i=toc.track[c].address;
      toc.track[c].address=(i&0xff)+75*((i>>8)&0xff)+75*60*((i>>16)&0xff);
      if (listFlag /* > 0 */) {
	  if (toc.track[c].track_number != 0) fprintf(stderr, "[%d] %d\n", toc.track[c].track_number, toc.track[c].address/75);
         }
      
      
      }

   setpriority(PRIO_PROCESS,0,-10);
   
   playp=readp=0;
   play_t=cthread_fork(play_thread,0);
      
    if (recordflg)
       {
       SNDSoundStruct sndheader;
       sndheader.magic=SND_MAGIC;
       sndheader.dataLocation=28;
       sndheader.dataSize=0;    /* a lie, but /usr/bin/sndplay dosen't care */
       sndheader.dataFormat=SND_FORMAT_LINEAR_16;
       sndheader.samplingRate=SND_RATE_HIGH;
       sndheader.channelCount=2;
       sndheader.info[0]=0;
       sndheader.info[1]=0;
       sndheader.info[2]=0;
       sndheader.info[3]=0;
       write(1,&sndheader,28);  /* write 28-byte header */
       }

   for(c=optind;(c<ac) && !done;c++)
      {
      int offset,rp,pp,ep;
      if (!strcmp(av[c],"repeat"))
         {
         if (c==optind)
            {
            fprintf(stderr,"No durations before repeat\n");
            continue;
            }
         c=optind;
         }
      if (interpret_duration(av[c],&startframe,&endframe)==-1)
         {
	 fprintf(stderr,"Bad duration specificitation: %s\n",av[c]);
         continue;
         }

      mutex_lock(position);
      offset=startframe-readp;
      startframe-=offset;
      endframe-=offset;
      
      while(!done && readp<endframe)
         {
         while ((playp+BUFBLOCK<=readp) && !done) condition_wait(sync_threads,position);
         rp=readp; pp=playp;
         
         ep=rp+READBLOCK;
         if (ep>endframe) ep=endframe;
         if (rp%BUFBLOCK+(ep-rp)>BUFBLOCK) ep=rp+BUFBLOCK-(rp%BUFBLOCK);
         if (pp+BUFBLOCK<ep) ep=pp+BUFBLOCK;

         if (ep>rp)
            {
            mutex_unlock(position);
	    
            if (scsiread10(buf+(rp%BUFBLOCK)*BLOCKSIZE,(ep-rp)*BLOCKSIZE,OPCODE,0,0,0,0,0,LBA,0,ep-rp,CONTROL)) goto end;
            mutex_lock(position);
            rp=readp=ep;
            condition_broadcast(sync_threads);
            }
         }
      mutex_unlock(position);
      }
 end:
   done=1;
   condition_broadcast(sync_threads);
   cthread_join(play_t);
#ifdef TOSHIBA
    if (scsiwrite6s(&opage,sizeof(opage),0x15,0,0,0,sizeof(opage),0)) 
       { 
       fprintf(stderr,"Can't reset mode page.\n"); 
       } 
#endif
   if (scsiclose(!dontejectflg))
      {
      fprintf(stderr,"Can't eject CD.\n");
      }
   exit(0);
   }

These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.