home *** CD-ROM | disk | FTP | other *** search
/ Emulator Universe CD / emulatoruniversecd1998.iso / CPC / Utils / PC2Tape / DECODER.C next >
Encoding:
C/C++ Source or Header  |  1996-11-26  |  15.1 KB  |  596 lines

  1. /* AIFF decoder, (c) Pierre Guerrier 1996 */
  2. /*    e-mail: guerrier@ecoledoc.ibp.fr    */
  3. /*       see attached "read_me" file      */
  4. /*  version 1.1,  released november 1996  */
  5.  
  6.  
  7. #include <fcntl.h>
  8. #include <stdio.h>
  9. #include <stdlib.h>
  10. #include <string.h>
  11.  
  12. #define MSDOS   1
  13. /* change this value to 1 if you are compiling for DOS */
  14.  
  15.  
  16. #define kNo   0    /* nothing under way */
  17. #define kArm  1    /* leader caught */
  18. #define kHead 2    /* syncs found */
  19. #define kData 3
  20. #define kNULL (-300)
  21.  
  22. #define kHeadSync (0x2c | (127<<9))
  23. #define kDataSync (0x16 | (127<<9))
  24. #define kHeadSize 256
  25. #define kDataSize 2048
  26. #define kCRCSizeH 2
  27. #define kCRCSizeD 16    /* approximate trailer size */
  28. #define kCRCpoly  4129  /* used for binary long division in CRC */
  29. #define kLeadSize 2000 /* leader size in "one" bits */
  30. #define kTreshold 500  /* duration limit between a 0 and a 1, in CPU clocks */
  31. #define kTopOut   50   /* <<<<---- problematic !! */
  32. #define kTopIn    3
  33.  
  34. FILE *Source;
  35. FILE *Target;
  36.  
  37. /* interface and option globals */
  38.  
  39. long Counter = 0;    
  40. int Right = 0;       /* use first channel */
  41. int Polarity = 0;    /* no sign change */
  42. int Step = 1;
  43. int Ratio;
  44. int SlowRate = 4;    /* default = SW1 */
  45. int Verbose = 0;
  46.  
  47. /* globals for delta->bit->byte levels state machines */
  48.  
  49. int WMin;
  50. int W0Low;
  51. int W0High;
  52. int W1Low;
  53. int W1High;
  54. int WTopIn;
  55. int WTopOut;
  56. int WLine;
  57.  
  58. unsigned int window = 0;
  59. int bitPt  = 0;
  60. int bytePt = 0;
  61. int engaged = kNo;
  62. int accumulator = 0;
  63.  
  64. /* globals for byte->file level state machine */
  65.  
  66. char currentFile[17];
  67. unsigned char BlockBuff[kDataSize+kCRCSizeD];
  68. int currentBlock;
  69. unsigned int currentSize = 2048;
  70. unsigned int currentSize2 = 2048;
  71. int closeOnNext = 0;
  72. int isOpen = 0;
  73. int BlockReady = 0;
  74.  
  75.  
  76.  
  77.  
  78.  
  79.  
  80.  
  81. /* delta->bits->bytes engine */
  82.  
  83.  
  84. int sendDelta(int delta)
  85. {
  86.  int retVal;
  87.  
  88.  window = (window<<1) | (delta > WLine);
  89.   /* shifting window */
  90.  
  91.  switch (engaged)   /* finite state machine DSP :-) */
  92.   {
  93.   case kArm:
  94.    if ((window&0xFFFF)==kHeadSync)
  95.          {
  96.          engaged = kHead;
  97.          printf("\nSample %d: Found a HEADER Sync !\n", Counter);
  98.          }
  99.    if ((window&0xFFFF)==kDataSync)
  100.          {
  101.          engaged = kData;
  102.          printf("\nSample %d: Found a BLOCK Sync !\n", Counter);
  103.          }
  104.    bitPt  = 0;
  105.    bytePt = 0;
  106.    retVal = kNULL;
  107.    accumulator--;
  108.    if ((accumulator < 0) && (engaged == kArm))
  109.      { engaged = kNo; accumulator = 0; if (Verbose) printf("Disarmed...\n"); }
  110.    break;
  111.  
  112.   case kHead: case kData:
  113.    bitPt++;
  114.    if (bitPt == 8)
  115.      {
  116.      bitPt = 0;
  117.      bytePt++;
  118.      retVal = window & 255;
  119.      if ( ((engaged == kHead) && (bytePt == (kHeadSize+kCRCSizeH))) ||
  120.           ((engaged == kData) && (bytePt == (currentSize+kCRCSizeD))) )
  121.            { engaged = kNo; accumulator = 0; }
  122.      }
  123.     else
  124.      retVal = kNULL;
  125.    break;
  126.  
  127.   case kNo:        /* leader management */
  128.    retVal = kNULL;
  129.    if ((window  & 0xFFFF) == 0xFFFF) accumulator++;
  130.    if (accumulator > kLeadSize)
  131.       { engaged = kArm;
  132.         if (Verbose) printf("Sample %d: Found a leader ...", Counter);
  133.         accumulator = 8*kHeadSize; }
  134.    break;
  135.   }
  136.  
  137.  return(retVal);
  138. }
  139.  
  140.  
  141.  
  142. /* bytes->files engine */
  143.  
  144. unsigned int CRCupdate(unsigned int CRC, unsigned char new)
  145. {
  146.  unsigned int aux = CRC ^ (new << 8);
  147.  int i;
  148.  
  149.  for(i=0; i<8; i++)
  150.    if (aux & 0x8000)
  151.        aux = (aux <<= 1) ^ kCRCpoly;
  152.      else
  153.        aux <<= 1;
  154.  
  155.  return(aux);
  156. }
  157.  
  158.  
  159. unsigned int CRCheck(unsigned char* buffer, unsigned int size)
  160. {
  161. int chunks;
  162. int error = 0;
  163. int i,j, offset1,offset2;
  164. unsigned int CRC;
  165.  
  166.  
  167.  chunks = size >> 8;
  168.  
  169.  for(i=0; i<chunks; i++)
  170.   {
  171.   CRC = 0xFFFF;
  172.   offset1 = 256*i;
  173.   offset2 = 258*i;
  174.   for(j=0; j<256; j++)
  175.     CRC = CRCupdate(CRC, buffer[offset1+j] = buffer[offset2+j]);
  176.   CRCupdate(CRC, 0);
  177.   CRCupdate(CRC, 0);
  178.   if ((~CRC & 0xFFFF) != (buffer[offset2+257]+256*buffer[offset2+256])) error = 1;
  179.   }
  180.  
  181.  return(error);
  182. }
  183.  
  184.  
  185.  
  186. void Tape2DOS()
  187. {
  188. unsigned char AuxBuff[128];
  189. unsigned int CRC = 0;
  190. char nameBuff[12];
  191. char prevFile[17];
  192. int newBlock,firstBlock;
  193. int i,j,s;
  194. char c,x;
  195.  
  196.  for(i=0; i<16; i++) prevFile[i] = currentFile[i];
  197.  for(i=0; i<16; i++) currentFile[i] = BlockBuff[i];
  198.  prevFile[16] = 0;
  199.  newBlock = BlockBuff[16];
  200.  closeOnNext = BlockBuff[17];
  201.  currentSize2 = currentSize = BlockBuff[19]+256*BlockBuff[20];
  202.  firstBlock = BlockBuff[23];
  203.  if (CRCheck(BlockBuff, kHeadSize))
  204.   fprintf(stderr,"    ****    Incorrect HEADER checksum !\n"); 
  205.  
  206.  if (Verbose)
  207.     printf("            Found block %d of file %s\n", newBlock, currentFile);
  208.  if (!firstBlock && (newBlock != (currentBlock+1)) )
  209.     fprintf(stderr,"    ****    Missed some blocks of file %s !\n", currentFile);
  210.  
  211.  if (currentSize > kDataSize)
  212.      {
  213.      currentSize = kDataSize;
  214.      fprintf(stderr,"    ****    Block size error !\n");
  215.      }
  216.    else
  217.      if (Verbose) printf("            Data block size %d bytes\n", currentSize);
  218.  i = currentSize & 0xFF00;   /* padding */
  219.  if (currentSize & 0xFF) i+= 256;
  220.  currentSize = i;
  221.  currentBlock = newBlock;
  222.  BlockReady = 1;
  223.  
  224.  s = strlen(currentFile);  /* name conversion function */
  225.  j = 0;
  226.  for(i=0; i<11; i++)
  227.   {
  228.   if (j<s)
  229.    {
  230.    c = toupper(currentFile[j++]);
  231.    if (c == '.')
  232.       {
  233.       if (i>7)  i--;
  234.          else { x = '_'; j--; }
  235.       }
  236.      else
  237.       { if (!isalnum(c)) x = '#'; else x = c; }
  238.    }
  239.    else x = '_';
  240.   nameBuff[i] = x;
  241.   }
  242.  for(j=10; nameBuff[j] == '_'; j--) ;
  243.  nameBuff[j+1] = 0;
  244.  
  245.  if (strncmp(currentFile, prevFile, 16))
  246.       { /* the file names differ */
  247.       if (isOpen)
  248.         {
  249.         if (closeOnNext)
  250.           {                     /* fine completion */
  251.           closeOnNext = 0;
  252.           fclose(Target);
  253.           isOpen = 0;
  254.           printf("    >>>>    File %s terminated...\n", prevFile);
  255.           }
  256.          else
  257.           {
  258.           fclose(Target);       /* we missed the end of prev file */
  259.           fprintf(stderr,"    ****    File %s did not terminate properly !\n", currentFile);
  260.           }
  261.         }
  262.       printf("    >>>>    Saving tape file %s as disc file %s\n", currentFile, nameBuff);
  263.       Target = fopen(nameBuff, "wb");
  264.       isOpen = 1;    
  265.       }
  266.     else /* the file names are identical */
  267.       goto end; /* the header has already been created */
  268.  
  269.  if (Verbose)
  270.    printf("            Indicated total size %d bytes\n", BlockBuff[24]+256*BlockBuff[25]);
  271.  if (BlockBuff[18] & 0xF0) goto end;  /* No header for ASCII files */
  272.  if (Verbose)
  273.   {
  274.   printf("            File is not ASCII, creating AMSDOS header:\n");
  275.   switch((BlockBuff[18]&0x0F)>>1)
  276.    {
  277.    case 0:
  278.       printf("            Locomotive BASIC file\n");
  279.       break;
  280.    case 1:
  281.       printf("            Binary File, origin %d, entry %d\n",
  282.                            BlockBuff[21]+256*BlockBuff[22],
  283.                            BlockBuff[26]+256*BlockBuff[27]);
  284.       break;
  285.    case 2:
  286.       printf("            Binary Screen Image\n");
  287.       break;
  288.    case 3:
  289.       printf("    ****    Inconsistent file typing: ASCII\n");
  290.       break;
  291.    }
  292.   }
  293.  
  294.  for(i=0; i<128; i++) AuxBuff[i] = 0;
  295.  for(i=0; i<11; i++)
  296.     if (BlockBuff[i]) AuxBuff[i+1] = BlockBuff[i];
  297.                else   AuxBuff[i+1] = ' ';
  298.  
  299.  for(i=18; i<28; i++) AuxBuff[i] = BlockBuff[i];
  300.  AuxBuff[23] = 255;
  301.  AuxBuff[64] = BlockBuff[24];
  302.  AuxBuff[65] = BlockBuff[25];
  303.  
  304.  for (i=0; i<67; i++) CRC += AuxBuff[i];
  305.  AuxBuff[67] = CRC & 255;
  306.  AuxBuff[68] = (CRC >> 8) & 255;
  307.  for (i=0; i<128; i++) fputc(AuxBuff[i], Target);
  308.  
  309. end:
  310. }
  311.  
  312.  
  313. void saveData()
  314. {
  315.  int i;
  316.  
  317.  if (CRCheck(BlockBuff, currentSize))
  318.    fprintf(stderr, "    ****    Incorrect DATA checksum !\n");
  319.  
  320.  for(i=0; i<currentSize2; i++)
  321.    fputc(BlockBuff[i], Target);
  322.  printf("            Saving block %d of file %s\n", currentBlock, currentFile);
  323.  BlockReady = 0;
  324. }
  325.  
  326. void rescueData()
  327. {
  328.  int i;
  329.  
  330.  Target = fopen("Rescued.Blocks", "ab");
  331.  
  332.  if (CRCheck(BlockBuff, kDataSize))
  333.    fprintf(stderr, "    ****    Incorrect DATA checksum !\n");
  334.  
  335.  for(i=0; i<kDataSize; i++)
  336.         fputc(BlockBuff[i], Target);
  337.  fprintf(stderr, "    ****    One data block rescued !\n");
  338.  fclose(Target);
  339. }
  340.  
  341. void SendByte(unsigned char newByte)
  342. {
  343.  BlockBuff[bytePt-1] = newByte;
  344.  
  345.  if (engaged == kNo) /* the byte received is the last of a block or header */
  346.    {                 /* bytePt still holds correct count */
  347.    if (bytePt == (currentSize+kCRCSizeD))
  348.        {if (isOpen && BlockReady) saveData(); else rescueData(); }
  349.                                /* if data found without header */
  350.    if (bytePt == (kHeadSize+kCRCSizeH)) Tape2DOS();
  351.    }
  352. }
  353.  
  354.  
  355.  
  356.  
  357.  
  358. /* wave->delta engine and calling point for other levels */
  359.  
  360.  
  361. int getNext()
  362. {
  363.  int buffer, retVal;
  364.  
  365.  Counter++;
  366.  if (Step) fseek(Source, Step, 1);
  367.  if ((buffer = fgetc(Source)) == EOF) 
  368.        {
  369.        if (Verbose) printf("%d samples scanned. Offset %d\n",Counter, ftell(Source));
  370.        return(kNULL);
  371.        }
  372.  if (buffer>>7) retVal = buffer-256; else retVal = buffer;
  373.  if (Polarity) retVal = -retVal;
  374.  return(retVal);
  375.  
  376. long remind = 0;
  377.  
  378. int filter(int s0, int s1, int delta)
  379. {
  380.       if (delta<W0Low)
  381.          {
  382.          if ((engaged != kNo) && ( ((s0<0)&&(s1>=0)) || ((!s0)&&(s1>0)) ))
  383.             { if (! (delta<WMin))
  384.                    {
  385.                    if (Verbose)
  386.      fprintf(stderr,"    ****    Narrow strobe in data, sample %d\n", Counter);
  387.                    return(1);
  388.                    }
  389.                 else return(0);
  390.             }
  391.           else return(0);
  392.          }
  393.       else if (delta<W0High) return( ((s0<0)&&(s1>=0)) || ((!s0)&&(s1>0)) );
  394.       else if (delta<W1Low) 
  395.               {  /* shitty case */
  396.               if ( ((s0<0)&&(s1>=0)) || ((!s0)&&(s1>0)) )
  397.                 {
  398.                 if ((engaged != kNo) && Verbose)  /* not so important */
  399.                    printf("    ****    Bad strobe in data at sample %d\n", Counter);
  400.                 return(1);
  401.                 }
  402.                else return(0);
  403.               }
  404.       else if (delta<W1High) return( ((s0<0)&&(s1>=0)) || ((!s0)&&(s1>0)) );
  405.       else if ((engaged != kNo) || remind )
  406.          {
  407.          if (delta>WTopIn)
  408.               {
  409.               if (Verbose)
  410.                 fprintf(stderr,"    ****    Wide strobe in data, samples %d-%d\n", remind, Counter);
  411.               remind = 0;
  412.               return(1); }
  413.           else { if (!remind) remind = Counter;  /* begin wide arch */
  414.                  return(0); }
  415.          }
  416.       else return(delta > WTopOut);
  417. }
  418.  
  419.  
  420. void doProcess()
  421. {
  422.  int  i, last, new, delta, resByte;
  423.  
  424.  for(i=0; i<17; i++)  currentFile[i] = 0;
  425.  delta = 1;
  426.  
  427.  while ((new = getNext()) != kNULL)
  428.   {
  429.   if ( filter(new, last, delta) )  /* raising slope inversion */
  430.      {
  431.      resByte = sendDelta(delta);              /* bit->bytes/blocks   */
  432.      if (resByte != kNULL) SendByte(resByte); /* bytes/blocks->files */
  433.      delta = 1;
  434.      }
  435.     else
  436.      delta++;
  437.  
  438.   last = new;
  439.   }
  440.  
  441.  if (isOpen)
  442.    {
  443.    if ((engaged == kNo) || (engaged == kArm))  /* not in data */
  444.      {
  445.      if (closeOnNext && !BlockReady)  /* all fine */
  446.        { if (Verbose) printf("            File %s terminated properly.\n",currentFile);}
  447.       else                  /* missed some blocks */
  448.        {
  449.        fprintf(stderr,"    ****    File %s did not terminate properly !\n", currentFile);
  450.        rescueData();
  451.        }
  452.      }
  453.     else        /* in data */
  454.      {
  455.      fprintf(stderr,"    ****    File %s did not terminate properly !\n", currentFile);
  456.      rescueData();
  457.      }
  458.    }
  459.   else
  460.    if (engaged == kData) rescueData();
  461. }
  462.  
  463.  
  464. void makeWindows()
  465. {
  466.  WLine = (SlowRate*kTreshold)/Ratio;  /* (CPU clocks/one)/(ticks/sample) -> sample/one */
  467.  WMin = WLine/2;           /* WMin = 2, WTopIn = 15, WTopOut = 250 */
  468.  W0Low = WLine/2 +1;       /* WLine = 5, W0Low = 3, W0High = 6, W1Low = 7, W1High = 10 */
  469.  W0High = WLine+1;
  470.  W1Low = (3*WLine)/2;
  471.  W1High = WLine*2;
  472.  WTopIn = kTopIn*WLine;
  473.  WTopOut = kTopOut*WLine;
  474. }
  475.  
  476.  
  477.    /* data formatting service routine */
  478.  
  479.  
  480. int checkAIFF()
  481. {
  482.  int  i;
  483.  unsigned char buffer[256];
  484.  char *aux;
  485.  unsigned char bits,channels,exponent,mantissa;
  486.  long rate, nyquist = (9600/SlowRate)*4;
  487.  
  488.  for(i=0; i<255; i++)
  489.       { buffer[i] = fgetc(Source);  if (buffer[i] == '\0') buffer[i] = ' ';}
  490.  buffer[i] = '\0';
  491.  if ( (strstr(buffer, "AIFF") != NULL) ||
  492.         ((strstr(buffer, "AIFC") != NULL) && (strstr(buffer, "NONE") != NULL)) )
  493.     {
  494.     aux = strstr(buffer, "COMM");
  495.     channels = *(aux+9);
  496.     bits = *(aux+15);
  497.     exponent = *(aux+17);
  498.     mantissa = *(aux+18);
  499.     rate = (long)mantissa << (exponent-6);    /* result is approx. Hz */
  500.  
  501.     if (Verbose)
  502.       printf("File is %d-bit, %d-channeled, %d Hz\n",(int)bits,(int)channels, rate);
  503.     if (rate < nyquist)
  504.         fprintf(stderr, "Unable to process file with optimal Nyquist frequency\n");
  505.       else
  506.         Step *= rate/nyquist;                 /* will filter high frequencies */
  507.               /* number of CPC clock cycles per sample after Step-decimation: */
  508.     Ratio = (int)( ((long)Step * 3993600L) / rate);
  509.     Step *= channels;
  510.     if (bits>8) Step *= 2;
  511.     if (Verbose)
  512.       printf("Adopting sample step = %d\n", Step);
  513.     Step--;   /* because fgetc will move 1 step forward ..  */
  514.    
  515.     if ((channels == 1) && Right) printf("File is mono, ignoring -r flag\n");
  516.     fseek ( Source, ((unsigned long)strstr(buffer, "SSND")-(unsigned long)buffer)+16, 0 );    
  517.     if ((channels > 1) && Right) 
  518.         { fgetc(Source); if (bits>8) fgetc(Source);}
  519.     return(1);
  520.     }
  521.   else
  522.      return(0);
  523. }
  524.  
  525.  
  526.  
  527. /* shell interface system below */
  528.  
  529.  
  530. void doFlags(char *theFlags)
  531. {
  532.  int  i,j;
  533.  char t;
  534.  
  535.  j = strlen(theFlags);
  536.  for(i=1; i<j; i++)
  537.    {t = tolower(theFlags[i]);
  538.     if (t=='r') Right = 1;      /* use second audio channel */
  539.       else
  540.     if (t=='v') Verbose = 1;
  541.       else
  542.     if (t=='p') Polarity = 1;  /* change signs */
  543.       else
  544.     if ((t >= '1') & (t < '9')) SlowRate = (int)(t-'0');
  545.       else
  546.     { fprintf(stderr,"Unknown option flag: -%c\n",t); exit(4); }
  547.    }
  548. }
  549.  
  550.  
  551. void doFile(char *theFile)
  552. {
  553.  char SourceName[256];
  554.  int i;
  555.  
  556.  #if MSDOS == 1
  557.  sprintf (SourceName, "%s.aif", theFile);
  558.  #else
  559.  sprintf (SourceName, "%s.aiff", theFile);
  560.  #endif
  561.  
  562.  if ((Source = fopen(SourceName,"rb")) == NULL)
  563.    { fprintf(stderr, "Unable to open %s, trying %s\n", SourceName, theFile);
  564.      if ((Source = fopen(theFile,"rb")) == NULL)
  565.         { fprintf(stderr, "Unable to open %s\n", theFile);
  566.           exit(3); } }
  567.      
  568.  if (checkAIFF())
  569.      { makeWindows(); doProcess(); }
  570.     else
  571.      { fprintf(stderr,"File %s is not valid uncompressed AIFF !\n", theFile); exit(2); }
  572.  fclose(Source);
  573. }
  574.  
  575.  
  576. int main(int argc, char **argv)
  577. {
  578.  #if MSDOS == 1
  579.  _fmode = O_BINARY;
  580.  #endif
  581.  
  582.   printf("AIFF Decoder 1.1, (c) 1996 Pierre Guerrier\n");
  583.  
  584.  switch(argc) 
  585.  {
  586.   case 2:  doFile(argv[1]); exit(0); break;
  587.   case 3: 
  588.     if (argv[1][0] == '-') {doFlags(argv[1]); doFile(argv[2]); exit(0); break;}
  589.   default:
  590.     fprintf(stderr,"Usage: %s [-rpvn] file\n   where n is in range 1..8\n", argv[0]); 
  591.     exit(1);
  592.     break;   
  593.  }
  594. }
  595.