home *** CD-ROM | disk | FTP | other *** search
/ Computer Installation Guide - Dragon Clan Series / CD2.iso / Audio / CDDA2WAV / CDDA2WAV.C < prev    next >
Encoding:
C/C++ Source or Header  |  1994-06-14  |  20.0 KB  |  796 lines

  1. /*
  2.  * CDDA2WAV
  3.  *
  4.  * LAST CHANGE:
  5.  *   18.12.93 - first version,    OK
  6.  *   01.01.94 - generalized & clean up HE
  7.  *   13.06.94 - released HE
  8.  */
  9.  
  10. #include <stdio.h>
  11. #include <stdlib.h>
  12. #include <string.h>
  13.  
  14. #include "uti.h"
  15. #include "aspidef.h"
  16.  
  17.  
  18. /* possible values for UNDERSAMPLING: 1, 2, 3, 4, 6, 7, 12, 14, 28, 49 */
  19. /*                      44 22 14 11  8  6  3.7  3  KHz */
  20. #define UNDERSAMPLING    2
  21.  
  22.  
  23. #define NSECTORS    1
  24. #define MAXTRK    30
  25.  
  26. #define CB_CDDASECTOR    2368
  27. #define CB_QSUBCHANNEL    16
  28. #define CB_CDROMSECTOR    2048
  29. #define CB_AUDIO    (CB_CDDASECTOR-CB_QSUBCHANNEL)
  30.  
  31. typedef struct TOC {
  32.   BYTE _reserved1;
  33.   BYTE bFlags;
  34.   BYTE bTrack;
  35.   BYTE _reserved2;
  36.   DWORD dwStartSector;
  37. } TOC;
  38.  
  39. static BYTE bufferCdRom [CB_CDROMSECTOR];
  40. static BYTE bufferCdda [NSECTORS * CB_CDDASECTOR];
  41. static BYTE bufferAudio [NSECTORS * CB_AUDIO];
  42.  
  43. static BYTE g_track=0xff, g_index=0xff;
  44. static BYTE g_minute=0xff, g_seconds=0xff;
  45.  
  46. static int nTocEntries;
  47. static TOC g_toc [MAXTRK]; /* 100 */
  48.  
  49. void EnableAudioMode (BOOL fAudioMode);
  50.  
  51. /*
  52.  * ---------------------------------------------------------------------
  53.  *  definitions for RIFF output (from Windows MMSYSTEM)
  54.  * ---------------------------------------------------------------------
  55.  */
  56.  
  57. typedef DWORD FOURCC;    /* a four character code */
  58.  
  59. typedef struct CHUNKHDR {
  60.   FOURCC ckid;        /* chunk ID */
  61.   DWORD dwSize;     /* chunk size */
  62. } CHUNKHDR;
  63.  
  64. #if    0
  65. /* general waveform format structure (information common to all formats) */
  66. typedef struct waveformat_tag {
  67.   WORD wFormatTag;    /* format type */
  68.   WORD nChannels;    /* number of channels (i.e. mono, stereo, etc.) */
  69.   DWORD nSamplesPerSec; /* sample rate */
  70.   DWORD nAvgBytesPerSec;/* for buffer size estimation */
  71.   WORD nBlockAlign;    /* block size of data */
  72. } WAVEFORMAT;
  73. typedef WAVEFORMAT     *PWAVEFORMAT;
  74. #endif
  75.  
  76. /* flags for 'wFormatTag' field of WAVEFORMAT */
  77. #define WAVE_FORMAT_PCM 1
  78.  
  79. /* specific waveform format structure for PCM data */
  80. typedef struct pcmwaveformat_tag {
  81.   WORD wFormatTag;    /* format type */
  82.   WORD nChannels;    /* number of channels (i.e. mono, stereo, etc.) */
  83.   DWORD nSamplesPerSec; /* sample rate */
  84.   DWORD nAvgBytesPerSec;/* for buffer size estimation */
  85.   WORD nBlockAlign;    /* block size of data */
  86.   WORD wBitsPerSample;
  87. } PCMWAVEFORMAT;
  88. typedef PCMWAVEFORMAT *PPCMWAVEFORMAT;
  89.  
  90.  
  91. /* MMIO macros */
  92. #define mmioFOURCC(ch0, ch1, ch2, ch3) \
  93.   ((DWORD)(BYTE)(ch0) | ((DWORD)(BYTE)(ch1) << 8) | \
  94.   ((DWORD)(BYTE)(ch2) << 16) | ((DWORD)(BYTE)(ch3) << 24))
  95.  
  96. #define FOURCC_RIFF    mmioFOURCC ('R', 'I', 'F', 'F')
  97. #define FOURCC_LIST    mmioFOURCC ('L', 'I', 'S', 'T')
  98. #define FOURCC_WAVE    mmioFOURCC ('W', 'A', 'V', 'E')
  99. #define FOURCC_FMT    mmioFOURCC ('f', 'm', 't', ' ')
  100. #define FOURCC_DATA    mmioFOURCC ('d', 'a', 't', 'a')
  101.  
  102.  
  103. /* simplified header for standard WAV files */
  104. typedef struct WAVEHDR {
  105.   CHUNKHDR chkRiff;
  106.   FOURCC fccWave;
  107.   CHUNKHDR chkFmt;
  108.   WORD wFormatTag;    /* format type */
  109.   WORD nChannels;    /* number of channels (i.e. mono, stereo, etc.) */
  110.   DWORD nSamplesPerSec; /* sample rate */
  111.   DWORD nAvgBytesPerSec;/* for buffer size estimation */
  112.   WORD nBlockAlign;    /* block size of data */
  113.   WORD wBitsPerSample;
  114.   CHUNKHDR chkData;
  115. } WAVEHDR;
  116.  
  117. #define IS_STD_WAV_HEADER(waveHdr) ( \
  118.   waveHdr.chkRiff.ckid == FOURCC_RIFF && \
  119.   waveHdr.fccWave == FOURCC_WAVE && \
  120.   waveHdr.chkFmt.ckid == FOURCC_FMT && \
  121.   waveHdr.chkData.ckid == FOURCC_DATA && \
  122.   waveHdr.wFormatTag == WAVE_FORMAT_PCM)
  123.  
  124.  
  125.  
  126. /*
  127.  * ASPI
  128.  */
  129.  
  130. static SRB_IO srb;
  131. static FILE *audio;
  132.  
  133.  
  134. BOOL AspiPrintError (int err)
  135. {
  136.   if (err == ASPIE_SUCCESS) return FALSE;
  137.   printf ("cdrom: %s\n", AspiGetErrorText (err));
  138.   return TRUE;
  139. }
  140.  
  141. /* actually reverse */
  142. VOID *Swap (VOID *p, int size)
  143. {
  144.   char *pc = p;
  145.   char tmp;
  146.  
  147.   if (size == 4) {
  148.     tmp = pc [0];
  149.     pc [0] = pc [3];
  150.     pc [3] = tmp;
  151.     tmp = pc [1];
  152.     pc [1] = pc [2];
  153.     pc [2] = tmp;
  154.   } else {
  155.     tmp = pc [0];
  156.     pc [0] = pc [1];
  157.     pc [1] = tmp;
  158.   }
  159.   return p;
  160. }
  161.  
  162. static WAVEHDR waveHdr;
  163. static DWORD nBytesDone = 0;
  164.  
  165. /* define size-related entries in wave header, update and close file */
  166. VOID CloseAudio (void)
  167. {
  168.   DWORD cbData = nBytesDone;/* / 4
  169.                  / (44100 / waveHdr.nSamplesPerSec)
  170.                  * waveHdr.nChannels 
  171.                  * ((waveHdr.wBitsPerSample + 7) / 8);*/
  172.  
  173.   fseek(audio, 0L, SEEK_SET);
  174.  
  175.   waveHdr.chkRiff.dwSize = cbData + sizeof(WAVEHDR) - sizeof(CHUNKHDR) ;
  176.   waveHdr.chkData.dwSize = cbData;
  177.  
  178.   fwrite (&waveHdr, sizeof (waveHdr), 1, audio);
  179.  
  180.   fclose (audio);
  181. }
  182.  
  183.  
  184. void Cleanup (void)
  185. {
  186.   CloseAudio ();
  187.   EnableAudioMode (FALSE);
  188. }
  189.  
  190.  
  191. void FatalError (char *szMessage, ...)
  192. {
  193.   vprintf (szMessage, (char *)(&szMessage + 1));
  194.   Cleanup();
  195.   exit (1);
  196. }
  197.  
  198.  
  199. void OpenAudio (char *fname, unsigned rate, int nBitsPerSample, int channels)
  200. {
  201.   audio = fopen (fname, "wb");
  202.  
  203.   waveHdr.chkRiff.ckid = FOURCC_RIFF;
  204.   waveHdr.fccWave = FOURCC_WAVE;
  205.   waveHdr.chkFmt.ckid = FOURCC_FMT;
  206.   waveHdr.chkFmt.dwSize = sizeof (PCMWAVEFORMAT);
  207.   waveHdr.wFormatTag = WAVE_FORMAT_PCM;
  208.   waveHdr.nChannels = channels;
  209.   waveHdr.nSamplesPerSec = rate;
  210.   waveHdr.nBlockAlign = channels * ((nBitsPerSample + 7) / 8);
  211.   waveHdr.nAvgBytesPerSec = waveHdr.nBlockAlign * waveHdr.nSamplesPerSec;
  212.   waveHdr.wBitsPerSample = nBitsPerSample;
  213.   waveHdr.chkData.ckid = FOURCC_DATA;
  214.  
  215.   fwrite (&waveHdr, sizeof (waveHdr), 1, audio);
  216. }
  217.  
  218.  
  219. void OpenCdRom (void)
  220. {
  221.   int targetId = 0;
  222.  
  223.   while (targetId < 7 && AspiGetDeviceType (0, targetId, 0) != DTC_CDROM)
  224.     targetId++;
  225.   if (targetId == 7)
  226.     FatalError ("No CD-ROM drive found.\n");
  227.  
  228.   memzero (srb);
  229.   srb.srb.bCmd = SRBC_EXECIO;
  230.   srb.srb.bAdapterId = 0;
  231.   srb.srb.bFlags = 0*SRBF_READ;
  232.   srb.bTargetId = (BYTE)targetId;
  233.   srb.bLun = 0;
  234.   srb.lpData = NULL;
  235.   srb.cbData = 0;
  236.   srb.cbSense = 14;
  237.   srb.cCdb = 6;
  238. }
  239.  
  240.  
  241. void ReadCdRom (LONG lSector)
  242. {
  243.   int err;
  244.   static BYTE cmd [10] = {0x28, 0, 0, 0, 0, 0, 0, 0, 0, 0};
  245.  
  246.   cmd [2] = (BYTE)(lSector >> 24);
  247.   cmd [3] = (BYTE)((lSector >> 16) & 0xFF);
  248.   cmd [4] = (BYTE)((lSector >> 8) & 0xFF);
  249.   cmd [5] = (BYTE)(lSector & 0xFF);
  250.   cmd [8] = (BYTE)NSECTORS;
  251.  
  252.   srb.lpData = bufferCdda;
  253.   srb.cbData = NSECTORS * CB_CDDASECTOR;
  254.   srb.cCdb = 10;
  255.   memcpy (&srb.cdb, cmd, 10);
  256.  
  257.   err = AspiRequestWait (&srb.srb);
  258.   if (AspiPrintError (err))
  259.     FatalError ("Read CD-ROM failed");
  260. }
  261.  
  262. static int quiet = 0;
  263.  
  264. void UpdateTrackData (short p_num)
  265. {
  266.   if (!quiet) printf ("\ntrack: %.2X, ", p_num);
  267.   g_track = (char)p_num;
  268. }
  269.  
  270.  
  271. void UpdateIndexData (short p_num)
  272. {
  273.   if (!quiet) printf ("index: %.2X\n", p_num);
  274.   g_index = (char)p_num;
  275. }
  276.  
  277.  
  278. void UpdateTimeData (short p_min, short p_sec)
  279. {
  280.   if (!quiet) printf ("time: %.2X:%.2X\r", p_min, p_sec);
  281.   g_minute = (char)p_min;
  282.   g_seconds = (char)p_sec;
  283. }
  284.  
  285.  
  286. static int waitforsignal = 0;
  287.  
  288. void SaveBuffer (unsigned rate, int nBitsPerSample, int channels, 
  289.                  LONG SecsToDo, DWORD *BytesDone)
  290. {
  291.   short i;
  292.   char *pSrc = (char *)bufferCdda;
  293.   BYTE *pDst = (BYTE *)bufferAudio;
  294.   short nSamplesLeftInSector = ((CB_CDDASECTOR-CB_QSUBCHANNEL)/4);
  295.   unsigned undersampling = 44100 / rate;
  296.   int sh_bits = 16 - nBitsPerSample;
  297.   unsigned BytesToDo = (SecsToDo > NSECTORS ? NSECTORS : SecsToDo) * CB_CDDASECTOR;
  298.   BYTE *pStart = pDst;
  299.   static int any_signal = 0;
  300.   char *pSrcStop = pSrc + BytesToDo;
  301.  
  302.   while (pSrc < pSrcStop) {
  303.     long lsum = 0;
  304.     long rsum = 0;
  305.  
  306.     for (i=0; i < undersampling; i++) {
  307.       /* LSB l, MSB l, LSB r, MSB r, ... */
  308.       lsum += *((signed short *)pSrc)++;
  309.       rsum += *((signed short *)pSrc)++;
  310.     }
  311.     lsum /= undersampling;
  312.     rsum /= undersampling;
  313.  
  314.     if (channels == 1) {
  315.       short sum;       /* mono section */
  316.       sum = (lsum + rsum) >> 1;
  317.       if (nBitsPerSample == 8) {
  318.         if ((BYTE)(sum >> 8) != 0) {
  319.           if (!any_signal) pStart = pDst;
  320.           any_signal = 1;
  321.         }
  322.         *pDst++ = (BYTE)(sum >> 8) + (1 << 7);    /* ok */
  323.         } else {
  324.         sum >>= sh_bits;
  325.         if (sum != 0) {
  326.           if (!any_signal) pStart = pDst;
  327.           any_signal = 1;
  328.         }
  329.         *((short *)pDst)++ = sum; /* ok */
  330.           }
  331.     } else {
  332.       /* stereo section */
  333.       if (nBitsPerSample == 8) {
  334.         if ((short)(lsum >> 8) != 0 || 
  335.         (short)(rsum >> 8) != 0) {
  336.           if (!any_signal) pStart = pDst;
  337.           any_signal = 1;
  338.         }
  339.         *pDst++ = (BYTE)(((short) lsum) >> 8) + (1 << 7);
  340.         *pDst++ = (BYTE)(((short) rsum) >> 8) + (1 << 7);    /* ok */
  341.         } else {
  342.         if ((short)(lsum >> sh_bits) != 0 || 
  343.         (short)(rsum >> sh_bits) != 0) {
  344.           if (!any_signal) pStart = pDst;
  345.           any_signal = 1;
  346.         }
  347.         *((short *)pDst)++ = (((short) lsum) >> sh_bits);
  348.         *((short *)pDst)++ = (((short) rsum) >> sh_bits); /* ok */
  349.           }
  350.     }
  351.  
  352.     nSamplesLeftInSector -= undersampling;
  353.     if (nSamplesLeftInSector == 0) {
  354.  
  355.       /* analyze Q-sub channel data */
  356.       if (pSrc [1] == 1) {
  357.         if (((BYTE *)pSrc) [2] != g_track)
  358.             UpdateTrackData (((BYTE *)pSrc) [2]);
  359.           if (((BYTE *)pSrc) [3] != g_index)
  360.             UpdateIndexData (((BYTE *)pSrc) [3]);
  361.           if (((BYTE *)pSrc) [5] != g_seconds ||
  362.               ((BYTE *)pSrc) [4] != g_minute)
  363.             UpdateTimeData (((BYTE *)pSrc) [4], ((BYTE *)pSrc) [5]);
  364.       }
  365.  
  366.       /* skip Q-sub channel data */
  367.       pSrc += CB_QSUBCHANNEL;
  368.       nSamplesLeftInSector = ((CB_CDDASECTOR - CB_QSUBCHANNEL)/4);
  369.     }
  370.   }
  371.   if (!waitforsignal) pStart = (BYTE *)bufferAudio;
  372.  
  373.   if (!waitforsignal || any_signal) {
  374.     fwrite (pStart, pDst - pStart, 1, audio);
  375.     *BytesDone += pDst - pStart;
  376.   }
  377. }
  378.  
  379.  
  380. void EnableAudioMode (BOOL fAudioMode)
  381. {
  382.   int err;
  383.   static BYTE mode [12] = {0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 0, 0};
  384.   static BYTE cmd [6] = {0x15, 0x10, 0, 0, sizeof (mode), 0};
  385.  
  386.   if (fAudioMode) {
  387.     mode [4] = 0x82;
  388.     mode [10] = (CB_CDDASECTOR >> 8);
  389.     mode [11] = (CB_CDDASECTOR & 0xFF);
  390.   } else {
  391.     mode [4] = 0x00;
  392.     mode [10] = (CB_CDROMSECTOR >> 8);
  393.     mode [11] = (CB_CDROMSECTOR & 0xFF);
  394.   }
  395.  
  396.   srb.lpData = mode;
  397.   srb.cbData = sizeof (mode);
  398.   srb.cCdb = 6;
  399.   memcpy (&srb.cdb, cmd, 6);
  400.   err = AspiRequestWait (&srb.srb);
  401.   if (AspiPrintError (err)) {
  402.     if (err == ASPIE_UNITATTENTION)
  403.       err = AspiRequestWait (&srb.srb);
  404.     if (AspiPrintError (err))
  405.       FatalError ("Enable audio mode failed");
  406.   }
  407. }
  408.  
  409.  
  410. void ReadToc (void)
  411. {
  412.   int err;
  413.   static BYTE cmd [10] = {
  414.     0x43, 0, 0, 0, 0, 0, 1, CB_CDROMSECTOR >> 8, CB_CDROMSECTOR & 0xFF, 0
  415.   };
  416.  
  417.   srb.lpData = bufferCdRom;
  418.   srb.cbData = CB_CDROMSECTOR;
  419.   srb.cCdb = 10;
  420.   memcpy (&srb.cdb, cmd, 10);
  421.  
  422.   err = AspiRequestWait (&srb.srb);
  423.   if (AspiPrintError (err))
  424.     FatalError ("Read TOC failed.");
  425.  
  426.   nTocEntries = ((bufferCdRom [0] << 8) + bufferCdRom [1] - 2) / 8;
  427.   memcpy (g_toc, bufferCdRom + 4, 8 * nTocEntries);
  428. }
  429.  
  430.  
  431. void DisplayToc (void)
  432. {
  433.   int i;
  434.   DWORD dw;
  435.   unsigned mins;
  436.   float secnds;
  437.   extern double fmod(double,double);
  438.  
  439.   dw = g_toc [nTocEntries-1].dwStartSector;
  440.   Swap (&dw, 4);
  441.   mins= dw / (75L*60);
  442.   secnds= fmod(dw , 60*75) / 75.0;
  443.   printf ("Table of Contents: total tracks:%d, (total time %u:%05.2f)\n",
  444.           nTocEntries-1, mins, secnds);
  445.   for (i=0; i<nTocEntries; i++) {
  446.     if (g_toc [i].bFlags & 4) continue;    /* skip nonaudio tracks */
  447.     if (g_toc [i].bTrack <= MAXTRK) {
  448.       DWORD dw2 = g_toc [i].dwStartSector;
  449.       dw = g_toc [i+1].dwStartSector;
  450.       Swap (&dw, 4);
  451.       Swap (&dw2, 4);
  452.       mins= (dw - dw2) / (75L*60);
  453.       secnds= fmod(dw-dw2 , 60*75) / 75.0;
  454.       printf (" %d.(%u:%05.2f)", g_toc [i].bTrack,mins,secnds);
  455.     }
  456.     if (i < nTocEntries-1)
  457.        if ((i+1) % 5 == 0) printf("\n");
  458.        else putchar (',');
  459.   }
  460.   printf ("\n");
  461. }
  462.  
  463.  
  464. LONG GetStartSector (int p_track)
  465. {
  466.   int i;
  467.  
  468.   for (i=0; i<nTocEntries; i++) {
  469.     if (g_toc [i].bTrack == p_track) {
  470.       DWORD dw = g_toc [i].dwStartSector;
  471.       if (g_toc [i].bFlags & 4)
  472.     return -1;
  473.       Swap (&dw, 4);
  474.       return dw;
  475.     }
  476.   }
  477.  
  478.   return -1;
  479. }
  480.  
  481.  
  482. LONG GetEndSector (int p_track)
  483. {
  484.   int i;
  485.  
  486.   for (i=1; i<nTocEntries; i++) {
  487.     if (g_toc [i-1].bTrack == p_track) {
  488.       DWORD dw = g_toc [i].dwStartSector;
  489.       if (g_toc [i].bFlags & 4)
  490.     return -1;
  491.       Swap (&dw, 4);
  492.       return dw-1;
  493.     }
  494.   }
  495.  
  496.   return -1;
  497. }
  498.  
  499.  
  500.  
  501. int FirstTrack (void)
  502. {
  503.   int i;
  504.   
  505.   for (i=0; i<nTocEntries; i++) {
  506.     if (g_toc [i].bTrack != 0xAA &&
  507.     !(g_toc [i].bFlags & 4))
  508.       return g_toc [i].bTrack;
  509.   }
  510.   return 0;
  511. }
  512.  
  513.  
  514. LONG NextTrack (int p_offset)
  515. {
  516.   int track = (g_track >> 4) * 10 + (g_track & 15);
  517.   long res;
  518.  
  519.   track += p_offset;
  520.   res = GetStartSector (track);
  521.   return res == -1 ? GetStartSector (track - p_offset) : res;
  522. }
  523.  
  524.  
  525. void usage(void)
  526. {
  527.     fprintf(stderr, "cdda2wav [-c chans] [-s] [-m] [-b bits] [-r rate] [-t track] [-o offset]\n");
  528.     fprintf(stderr, "         [wavfile.wav]\n");
  529.     fprintf(stderr, "cdda2wav copies parts from audio cd's directly to WAV files.\n");
  530.     fprintf(stderr, "It requires a toshiba XM3401 scsi cdrom drive and an aspi driver to work.\n");
  531.     fprintf(stderr, "options: -c channels : set 1 for mono, or 2 for stereo recording.\n");
  532.     fprintf(stderr, "         -s          : set to stereo recording.\n");
  533.     fprintf(stderr, "         -m          : set to mono recording.\n");
  534.     fprintf(stderr, "         -b bits     : set bits per sample per channel (8, 12 or 16 bits).\n");
  535.     fprintf(stderr, "         -r rate     : set rate in samples per second. Possible values are:\n");
  536.     fprintf(stderr, "            44100,22050,14700,11025,7350,3675,3150,1575,900 Hertz.\n");
  537.     fprintf(stderr, "         -t track    : select start track.\n");
  538.     fprintf(stderr, "         -o offset   : start 'offset' sectors behind start track.\n");
  539.     fprintf(stderr, "                       one sector equivalents 1/75 second.\n");
  540.     fprintf(stderr, "         -d duration : set recording time in seconds or 0 for whole track.\n");
  541.     fprintf(stderr, "         -w          : wait for signal, then start recording.\n");
  542.     fprintf(stderr, "         -q          : quiet operation, no screen output.\n");
  543.     fprintf(stderr, "defaults: mono, 16 bit, 22050 Hz, track 1, no offset, 16 seconds, 'audio.wav'\n"
  544.                   "          don't wait for signal, not quiet\n");
  545.   exit(1);
  546. }
  547.  
  548. int main (int argc, char *argv [])
  549. {
  550.   int args = 0;
  551.   LONG lSector;
  552.   LONG lSector_p1;
  553.   LONG sector_offset = 0;
  554.   LONG nSectorsToDo = (1200 / NSECTORS) * NSECTORS;
  555.   LONG req_space;
  556.   LONG time = nSectorsToDo / 75;
  557.   int track = 1;
  558.   int    channels = 1;
  559.   unsigned rate = 44100 / UNDERSAMPLING;
  560.   int bits = 16;
  561.   char fname[100] = "audio.wav";
  562. #if    defined    (MSDOS) || defined(__MSDOS__)
  563. #include <conio.h>
  564.   int key_pressed = 0;
  565. #endif
  566.  
  567.   /* command options parsing */
  568.   while (--argc) {
  569.     args++;
  570.     /* channels */
  571.     if (!strncmp("-c",argv[args],2)) {
  572.       if (strlen (argv[args]) > 2) {
  573.     channels = atoi(argv[args]+2);
  574.     continue;
  575.       } else {
  576.         args++;
  577.         if (--argc) {
  578.       channels = atoi(argv[args]);
  579.       continue;
  580.     } else usage();
  581.       }
  582.     }
  583.     /* bits */
  584.     if (!strncmp("-b",argv[args],2)) {
  585.       if (strlen (argv[args]) > 2) {
  586.     bits = atoi(argv[args]+2);
  587.     continue;
  588.       } else {
  589.         args++;
  590.         if (--argc) {
  591.       bits = atoi(argv[args]);
  592.       continue;
  593.     } else usage();
  594.       }
  595.     }
  596.     /* rate */
  597.     if (!strncmp("-r",argv[args],2)) {
  598.       if (strlen (argv[args]) > 2) {
  599.     rate = atoi(argv[args]+2);
  600.     continue;
  601.       } else {
  602.         args++;
  603.         if (--argc) {
  604.       rate = atoi(argv[args]);
  605.       continue;
  606.     } else usage();
  607.       }
  608.     }
  609.     /* start track */
  610.     if (!strncmp("-t",argv[args],2)) {
  611.       if (strlen (argv[args]) > 2) {
  612.     track = atoi(argv[args]+2);
  613.     continue;
  614.       } else {
  615.         args++;
  616.         if (--argc) {
  617.       track = atoi(argv[args]);
  618.       continue;
  619.         } else usage();
  620.       }
  621.     }
  622.     /* recording time */
  623.     if (!strncmp("-d",argv[args],2)) {
  624.       if (strlen (argv[args]) > 2) {
  625.     time = atoi(argv[args]+2);
  626.     continue;
  627.       } else {
  628.         args++;
  629.         if (--argc) {
  630.       time = atoi(argv[args]);
  631.       continue;
  632.     } else usage();
  633.       }
  634.     }
  635.     /* sector offset */
  636.     if (!strncmp("-o",argv[args],2)) {
  637.       if (strlen (argv[args]) > 2) {
  638.     sector_offset = atol(argv[args]+2);
  639.     continue;
  640.       } else {
  641.         args++;
  642.         if (--argc) {
  643.       sector_offset = atol(argv[args]);
  644.       continue;
  645.     } else usage();
  646.       }
  647.     }
  648.     /* stereo */
  649.     if (!strncmp("-s",argv[args],2)) {
  650.       channels = 2;
  651.       continue;
  652.     }
  653.     /* mono */
  654.     if (!strncmp("-m",argv[args],2)) {
  655.       channels = 1;
  656.       continue;
  657.     }
  658.     /* wait for signal */
  659.     if (!strncmp("-w",argv[args],2)) {
  660.       waitforsignal = 1;
  661.       continue;
  662.     }
  663.     /* quiet */
  664.     if (!strncmp("-q",argv[args],2)) {
  665.       quiet = 1;
  666.       continue;
  667.     }
  668.     /* other options unknown */
  669.     if (*argv[args] == '-') usage();
  670.  
  671.       /* filename given */
  672.       strcpy(fname,argv[args]);
  673.  
  674.       /* should be the last parameter */
  675.       if (argc > 1) usage();
  676.   }
  677.  
  678.   /* check all parameters */
  679.   if (channels != 1 && channels != 2) usage();
  680.   if (bits != 8 && bits != 12 && bits != 16) usage();
  681.   if (44100 % rate != 0) usage();
  682.   if (track < 1 || track > 50) usage();
  683.   if (time < 0 || time > 4440) usage();
  684.  
  685.  
  686.  
  687.   if (not AspiInit ())
  688.     FatalError ("Unable to open ASPI device.\n");
  689.  
  690.   atexit (Cleanup);
  691.  
  692.   OpenCdRom ();
  693.   EnableAudioMode (TRUE);
  694.   ReadToc ();
  695.   if (!quiet) DisplayToc ();
  696.  
  697.   if (!FirstTrack ())
  698.     FatalError ("This is no audio disk");
  699.  
  700.   lSector = GetStartSector (track);
  701.   lSector_p1 = GetEndSector (track) + 1;
  702.   if (lSector < 0)
  703.     FatalError ("track %d not found\n", track);
  704.  
  705.   lSector += sector_offset;
  706.   /* check against end sector of track */
  707.   if (lSector >= lSector_p1) {
  708.     printf("sector offset exceeds track size (ignored)\n");
  709.     lSector -= sector_offset;
  710.   }
  711.  
  712.   if (time == 0) {
  713.     /* set time to track time */
  714.     time = ((lSector_p1 - lSector)) / 75;
  715.   }
  716.  
  717.   remove(fname);
  718. #if    defined    (MSDOS) || defined(__MSDOS__)
  719.   /* calculate required disk space */
  720.   req_space = sizeof(WAVEHDR) + (bits + 7)/8 * channels * (LONG)rate * time;
  721.  
  722.   /* check disk space */
  723.   {
  724. #include <dos.h>
  725. #include <ctype.h>
  726.      LONG avail;
  727.      unsigned drive;
  728. #ifdef TURBOC
  729.      struct dfree d;
  730. #else
  731.      struct diskfree_t d;
  732. #endif
  733.  
  734.      if (fname[1] != ':') {
  735.        /* get default */
  736.        drive = 0;
  737.      } else {
  738.        /* get specified drive */
  739.        drive = toupper(fname[0]) - '@';
  740.      }
  741.  
  742. #ifdef TURBOC
  743.      getdfree(drive, &d);
  744.      avail = (long) d.df_avail
  745.            * (long) d.df_bsec
  746.            * (long) d.df_sclus;
  747. #else
  748.      _dos_getdiskfree(drive, &d);
  749.      avail = (long) d.avail_clusters
  750.            * (long) d.bytes_per_sector
  751.            * (long) d.sectors_per_cluster;
  752. #endif
  753.  
  754.      if (req_space > avail) {
  755.        if (!quiet) fprintf(stderr, "unsufficient disk space! reducing recording length.\n");
  756.        time = (avail - sizeof(WAVEHDR)) / 
  757.                ((bits + 7)/8 * channels * (LONG)rate);
  758.        req_space = sizeof(WAVEHDR) + (bits + 7)/8 * channels * (LONG)rate * time;
  759.        if (!quiet) fprintf(stderr, "maximum possible are %ld seconds <=> %ld Bytes.\n",
  760.                time,req_space);
  761.      }
  762.   }
  763. #endif
  764.  
  765.   /* calculate # of sectors to read */
  766.   nSectorsToDo = time * 75;
  767.  
  768.   if (!quiet) printf("recording %lu seconds %s with %d bits @ %u Hz ->'%s'...\n"
  769.                               ,time ,channels == 1 ? "mono":"stereo", bits, rate, fname);
  770.  
  771.   OpenAudio (fname, rate, bits, channels);
  772.  
  773.   while (nSectorsToDo > 0 
  774. #if    defined    (MSDOS) || defined(__MSDOS__)
  775.          && !(key_pressed = kbhit())
  776. #endif
  777.                          ) {
  778.     ReadCdRom (lSector);
  779.     lSector += NSECTORS;
  780.     SaveBuffer (rate, bits, channels, nSectorsToDo, &nBytesDone);
  781.     nSectorsToDo -= NSECTORS;
  782.   }
  783.  
  784. #if    defined    (MSDOS) || defined(__MSDOS__)
  785.   if (key_pressed) {
  786.     getch();
  787.     if (!quiet) printf("\naborted.\n");
  788.   }
  789. #endif
  790.  
  791.   CloseAudio ();
  792.   EnableAudioMode (FALSE);
  793.  
  794.   return 0;
  795. }
  796.