home *** CD-ROM | disk | FTP | other *** search
/ Crawly Crypt Collection 1 / crawlyvol1.bin / ste / wave_20 / wave.c < prev    next >
C/C++ Source or Header  |  1993-07-30  |  15KB  |  464 lines

  1. /**************************************************************
  2. *       This program will play a "wave" file on an ATARI TT   *
  3. *       This program should also run on an STe and Falcon     *
  4. *       but since I only own an ST and a TT it is only        *
  5. *       warranteed on a TT.                                   *
  6. *                                                             *
  7. *       THIS PROGRAM IS NOT MINT/MULTI-TOS FRIENDLY           *
  8. *       THIS PROGRAM WILL RUN UNDER MINT/MULTI-TOS BUT        *
  9. *       MULTI_TASKING WILL BE SUSPENDED WHILE PLAYING THE     *
  10. *       SAMPLE. MULTI_TASKING RESUMES WHEN THE SAMPLE IS      *
  11. *       FINISHED PLAYING.                                     *
  12. *                                                             *
  13. *       I know how to fix this and it will be implemented     *
  14. *       as soon as I can get around to it.                    *
  15. *                                                             *
  16. *  Algorithm:                                                 *
  17. *      open file                                              *
  18. *      Read the header of the file.                           *
  19. *      if the header is not valid report that and exit        *
  20. *      If file is an "RIFF" file convert to motorola integers *
  21. *      allocate memory (ST-RAM) for PCM data                  *
  22. *      Read the PCM data                                      *
  23. *      close the file                                         *
  24. *      If sample is 8 bit then convert data to ATARI format   *
  25. *      play the Sample                                        *
  26. *      wait until sample is completed                         *
  27. *      free memory                                            *
  28. *      exit                                                   *
  29. *                                                             *
  30. *                                                             *
  31. *  Change History:                                            *
  32. *                                                             *
  33. *  12/10/92    Added ability to accept filenames from stdin      *
  34. *  12/13/92 Corrected Error msgs relating to memory allocs    *
  35. *  05/20/93 Added capability to skip over undesired "chunks"  *
  36. *  07/06/93 Added capability to play partial files instead    *
  37. *           of reporting a file error                         *
  38. *  07/30/92 Corrected reading from stdin to handle empty lines*
  39. *                                                             *
  40. **************************************************************/
  41. /*
  42.     The correct method of reading a WAV file is to
  43.     read in the RIFF/RIFX chunk id and size along with
  44.     the WAVE identifier.
  45.     while not the end of the file (WAV files can contain multiple samples)
  46.     Begin
  47.       Read in the next chunk identifier
  48.       while the identifier is not "fmt\0"
  49.       Begin       (typically this section willnever be used)
  50.         report type of chunk encountered.
  51.         skip this chunk.
  52.         read in the next chunk identifier
  53.         End (ckID not "fmt\0")
  54.       read in the "fmt\0" chunk data
  55.       Read in the next chunk identifier
  56.       while the identifier is not "data"
  57.       Begin
  58.         report type of chunk encountered.
  59.         skip this chunk.
  60.         read in the next chunk identifier
  61.         End (ckID not "data")
  62.       read in the data
  63.       one second of sample at a time
  64.       Begin
  65.         re-sample the data (if needed otherwise copy)
  66.         play the 1 second
  67.         End (One second at a time)
  68.       End (Not end of file)
  69. */
  70.  
  71. /*
  72.   Things to be added:
  73.     1) take filenames from STDIN
  74.     2) optionally supress messages to stdout/stderr
  75. */
  76.  
  77. #include <stdio.h>
  78. #include <stdlib.h>
  79. #include <string.h>
  80. #include <tos.h>
  81. #include "stereo.h"
  82. #include "riff.h"
  83.  
  84. #define FALSE 0
  85. #define TRUE  1
  86. #define MAX_FNAME_LENGTH 132
  87. #define FROM_START_OF_FILE 0
  88. #define FROM_END_OF_FILE   2
  89. void FatalError (char * id) ;
  90. void MSC_cnvrt_int(INTEGER * i) ;
  91. void MSC_cnvrt_long(LONG * i) ;
  92. void Convert_MSC_ATARI_8_Bit(unsigned char * c, unsigned long i) ;
  93. long Play_PCM_Sample() ;
  94. void process_file(char * fname) ;
  95. int  locate_chunk(char * id_fmt, CHK_DEF * Chk, FILE * fp) ;
  96.  
  97. char cmd[132] ;
  98. char *id_riff = "RIFF";
  99. char *id_rifx = "RIFX";
  100. char *id_wave = "WAVE";
  101. char *id_fmt  = "fmt ";
  102. char *id_data = "data" ;
  103. WAVE_DEFINITION  *wave ;
  104. PCM_Samples       origSample ;
  105. int               motorola_fmt ;
  106.  
  107. main(argc, argv)
  108. int argc ;
  109. char *argv[] ;
  110. {
  111. unsigned long   i ;
  112. char            fname[MAX_FNAME_LENGTH] ;
  113.  
  114.   strcpy(cmd, argv[0]) ;
  115.   fname[0] = '\0' ;
  116.   if (argc > 1)
  117.     for (i = 1; i < argc; i++) 
  118.     {
  119.       if (argv[i][0] != '-')
  120.       {
  121.         printf("%s -", argv[i]) ;
  122.         process_file(argv[i]) ;
  123.       }
  124.     }
  125.   else
  126.   while (fgets(fname, MAX_FNAME_LENGTH, stdin) != NULL)
  127.   {
  128.     if (strlen(fname) > 1)
  129.       process_file(fname) ;
  130.   }
  131.   return 0 ;
  132. }
  133.  
  134. void process_file(char * fname)
  135. {
  136. long            num_read ;
  137. unsigned long   nSamples ;
  138. double          Sample_length ;
  139. long            BytesPerSample ;
  140. long            file_size ;
  141. long            file_pos ;
  142. PCM_Samples     Dest ;
  143. WAVE_FILE       riffChk ;
  144. CHK_DEF         fmtChk ;
  145. FILE          * fp ;
  146.  
  147.   motorola_fmt = FALSE ;
  148.   if (strcmp(fname,"-")==0) 
  149.   { 
  150.     fp = stdin;  
  151.     fname = "<stdin>"; 
  152.   }
  153.   else 
  154.     fp = fopen(fname,"rb");
  155.  
  156.   fseek(fp,0l, FROM_END_OF_FILE) ;
  157.   file_size = ftell(fp) ;
  158.   fseek(fp,0l, FROM_START_OF_FILE) ;
  159.   if (fp == NULL)
  160.     FatalError("could not open file.") ;    
  161.  
  162.   wave = (WAVE_DEFINITION *) malloc(sizeof(WAVE_DEFINITION)) ;
  163.   if (!wave)
  164.     FatalError("not enough memory to read '*.WAV' file header");
  165.  
  166.   num_read = fread(&riffChk, 1, sizeof(WAVE_FILE), fp) ;
  167.   if (num_read != sizeof(WAVE_FILE))
  168.     FatalError("WAV 'riff' header read failed");
  169.  
  170.   if (strncmp(riffChk.riffStr, id_riff, 4))
  171.   {
  172.     if (strncmp(riffChk.riffStr, id_rifx, 4))
  173.       FatalError("not an RIFF/RIFX file");
  174.     else
  175.       motorola_fmt = TRUE ;
  176.   }
  177.   if (strncmp(riffChk.waveStr, id_wave, 4))
  178.     FatalError("not a WAVE file");
  179.  
  180. /*
  181.    We now are going to have to possible loop through
  182.    many "chunks" of information to locate the format
  183.    "fmt" chunk for this WAVe file.
  184. */
  185.   if (locate_chunk(id_fmt, &fmtChk, fp) == -1)
  186.   {
  187.     FatalError("EOF encountered before 'fmt' chunk") ;
  188.   }
  189.  
  190.   num_read = fread(&(wave->hdr),1, sizeof(WAVE_HDR), fp) ;
  191.   if (num_read != sizeof(WAVE_HDR))
  192.     FatalError("WAVE header read failed");
  193.  
  194.   if (locate_chunk(id_data, &(wave->data), fp) == -1)
  195.   {
  196.     FatalError("EOF encountered before 'data' chunk") ;
  197.   }
  198. /*
  199.   because some sample wave files are incomplete the next few
  200.   lines will make adjustments to try and fix that.
  201. */
  202.   file_pos = ftell(fp) ;
  203.   if ((file_pos + wave->data.ckSize) > file_size)
  204.   {
  205.     wave->data.ckSize = file_size - file_pos ;
  206.   }
  207. /* do we need to change the integer storage format to motorola? */
  208.   if (motorola_fmt == FALSE)
  209.   {
  210.     MSC_cnvrt_long(&riffChk.ckSize) ;
  211. /*
  212.     MSC_cnvrt_long(&fmtChk.ckSize) ;
  213. */
  214.     MSC_cnvrt_int(&wave->hdr.formatTag) ;
  215.     MSC_cnvrt_int(&wave->hdr.nChannels) ;
  216.     MSC_cnvrt_long(&wave->hdr.nSamplesPerSec) ;
  217.     MSC_cnvrt_long(&wave->hdr.nAvgBytesPerSec) ;
  218.     MSC_cnvrt_int(&wave->hdr.nBlockAlign) ;
  219.     MSC_cnvrt_int(&wave->hdr.nBitsPerSample) ;
  220. /*
  221.     MSC_cnvrt_long(&wave->data.ckSize) ;
  222. */
  223.   }
  224.   if (wave->hdr.nChannels > 2)
  225.     FatalError("can not accept more than 2 channels in file") ;
  226.  
  227.  
  228. /* 
  229.     Since the STe/TT only support 8 bit PCM codes
  230.     We need to determine the correct amount of memory.
  231.     We also must take into consideration that that WAVE
  232.     file may not be sampled at the same rate the STe/TT
  233.     can play it back. So we may need to peform a little
  234.     DSP to reconstruct the sample so the Atari can play it
  235.     correctly.
  236. */
  237.  
  238. /* Determine memory requirements */
  239.   BytesPerSample = (wave->hdr.nBitsPerSample + 7) / 8 ;
  240.   if (BytesPerSample > 2)
  241.     FatalError("can not accept more than 2 bytes per sample") ;
  242.  
  243.   if ( wave->hdr.nSamplesPerSec <= SSS_RATE_50kHz )
  244.     wave->Target_SpS = SSS_RATE_50kHz ;
  245.   if ( wave->hdr.nSamplesPerSec <= SSS_RATE_25kHz )
  246.     wave->Target_SpS = SSS_RATE_25kHz ;
  247.   if ( wave->hdr.nSamplesPerSec <= SSS_RATE_12kHz )
  248.     wave->Target_SpS = SSS_RATE_12kHz ;
  249.   if ( wave->hdr.nSamplesPerSec <= SSS_RATE_6kHz )
  250.     wave->Target_SpS = SSS_RATE_6kHz ;
  251.  
  252. /*
  253.    determine the number of seconds the sample will play for 
  254. */
  255.   nSamples = (wave->data.ckSize / wave->hdr.nChannels)/ BytesPerSample ;
  256.   Sample_length = (double)nSamples / (double)wave->hdr.nSamplesPerSec ;
  257.  
  258.   printf("WAVE file contents:\n") ;
  259.   printf("  %d bit", wave->hdr.nBitsPerSample) ;
  260.   if (wave->hdr.nChannels == 2)
  261.     printf(" Stereo\n") ;
  262.   else
  263.     printf(" Mono\n") ;
  264.   printf("Sample Rate:\n   Original : %ld\n   Playback :%ld\n",
  265.          wave->hdr.nSamplesPerSec, wave->Target_SpS) ;
  266.   printf("  %lf seconds playing time\n", Sample_length) ;
  267.  
  268. /*
  269.    determine the number of bytes required to hold
  270.    sample at a rate that the Atari can play back.
  271. */
  272.   wave->required_memory = (unsigned long)((double)wave->Target_SpS * Sample_length) ;
  273.   wave->required_memory = wave->required_memory * wave->hdr.nChannels ;
  274.  
  275. /* Allocate memory for the PCM Data */
  276.   wave->PCM.Mono8 = (unsigned char *)Mxalloc((wave->required_memory + 256), 0) ;
  277.  
  278.   if (!wave->PCM.Mono8)
  279.     FatalError("not enough memory to play sample") ;
  280.  
  281.   if (wave->Target_SpS == wave->hdr.nSamplesPerSec)
  282.   { /* no psuedo DSP algorithm required */
  283.     num_read = fread(wave->PCM.Mono8,1, wave->data.ckSize, fp) ;
  284.     if (num_read != wave->data.ckSize)
  285.       FatalError("error reading WAVE data") ;
  286.   }
  287.   else
  288. /*
  289.   determine the amount of memory required to read in the
  290.   entire WAVE file. Use TT-RAM if available (mode = 3)
  291. */
  292.   {
  293.     origSample.Mono8 = (unsigned char *)Mxalloc(wave->data.ckSize,3) ;
  294.     if (!origSample.Mono8)
  295.       FatalError("not enough memory to read in sample");
  296.  
  297.     Dest.Mono8 = wave->PCM.Mono8 ;
  298.     num_read = fread(origSample.Mono8,1, wave->data.ckSize, fp) ;
  299.     if (num_read != wave->data.ckSize)
  300.     {
  301.       FatalError("error reading source of WAVE data") ;
  302.       wave->required_memory = (long)((float)wave->required_memory 
  303.                                    * (float)(num_read)/(float)wave->data.ckSize) ;
  304.       wave->data.ckSize = num_read ;
  305.     }
  306.  
  307. /*
  308.    If we only had object oriented programming this next section
  309.    would have only one line
  310. */
  311.     switch (wave->hdr.nChannels)
  312.     {
  313.       case 1 : switch ((int)BytesPerSample)
  314.                {
  315.                  case 1: Resample8bitMono(&Dest, &origSample, wave->required_memory, wave->data.ckSize) ;
  316.                          break ;
  317.                  case 2: Resample16bitMono(&Dest, &origSample, wave->required_memory, wave->data.ckSize) ;
  318.                          break ;
  319.                  default:
  320.                    FatalError("can not convert this # bytes per sample") ;
  321.                }
  322.                break ;
  323.       case 2 : switch ((int)BytesPerSample)
  324.                {
  325.                  case 1: Resample8bitStereo(&Dest, &origSample, wave->required_memory, wave->data.ckSize) ;
  326.                          break ;
  327.                  case 2: Resample16bitStereo(&Dest, &origSample, wave->required_memory, wave->data.ckSize) ;
  328.                          break ;
  329.                  default:
  330.                    FatalError("can not convert this # bytes per sample") ;
  331.                }
  332.                break ;
  333.       default :
  334.          FatalError("Incorrect # of channels") ;
  335.     }
  336.   }
  337.   Supexec(Play_PCM_Sample) ;
  338.  
  339.   free(wave->PCM.Mono8) ;
  340.   free(wave) ;
  341.   free(origSample.Mono8) ;
  342.   fclose(fp) ;
  343. }
  344.  
  345.  
  346. /***********************************/
  347. void FatalError (identifier)
  348.        char *identifier;
  349. {
  350.   if (wave != NULL)
  351.   {
  352.     if (wave->PCM.Mono8 != NULL)
  353.       free(wave->PCM.Mono8) ;
  354.     free(wave) ;
  355.   if (origSample.Mono8 != NULL)
  356.     free(origSample.Mono8) ;
  357.   }
  358.   fprintf(stderr, "Fatal Error: %s: %s\n",cmd, identifier);
  359.   exit(-1);
  360.   return ;
  361. }
  362.  
  363.  
  364. /*
  365.   The next two routines convert an integer and a long
  366.   that is stored in Intel format to Motorola format
  367. */
  368. void MSC_cnvrt_int(i)
  369. INTEGER * i ;
  370. {
  371. char tmp ;
  372.  
  373.   tmp = i->MSC[0] ;
  374.   i->MSC[0] = i->MSC[1] ;
  375.   i->MSC[1] = tmp ;
  376.  
  377. void MSC_cnvrt_long(LONG * i) 
  378. {
  379. char tmp ;
  380.  
  381.   tmp = i->MSC[0] ;
  382.   i->MSC[0] = i->MSC[3] ;
  383.   i->MSC[3] = tmp ;
  384.   tmp = i->MSC[1] ;
  385.   i->MSC[1] = i->MSC[2] ;
  386.   i->MSC[2] = tmp ;
  387. }
  388.  
  389. /*
  390.   Since the following routine accesses the hardware registers
  391.   it must be executed in supervisory mode.
  392. */
  393. long Play_PCM_Sample()
  394. {
  395. SOUND_REGS    *hardware = (SOUND_REGS *)0xff8900l ;
  396. unsigned int   mode ;
  397. unsigned long  tmp ;
  398.  
  399. /*
  400.   hardware = (SOUND_REGS *)malloc(sizeof(SOUND_REGS)) ;
  401. */
  402.   switch (wave->Target_SpS)
  403.   {
  404.     case SSS_RATE_6kHz  : mode = SSS_RATE_06258Hz ; /* 0x0 */
  405.                           break ;
  406.     case SSS_RATE_12kHz : mode = SSS_RATE_12517Hz ; /* 0x1 */
  407.                           break ;
  408.     case SSS_RATE_25kHz : mode = SSS_RATE_25033Hz ; /* 0x2 */
  409.                           break ;
  410.     case SSS_RATE_50kHz : mode = SSS_RATE_50066Hz ; /* 0x3 */
  411.                           break ;
  412.     default : FatalError("can not play sample rate") ;
  413.   }
  414.  
  415.   if (wave->hdr.nChannels == 1)
  416.     mode = mode | SSS_MONO ;
  417.  
  418.   tmp = (unsigned long)wave->PCM.Mono8 + wave->required_memory ;
  419.  
  420.   hardware->Frame_base_high = ((unsigned long) wave->PCM.Mono8 >> 16) & 0x3f ;
  421.   hardware->Frame_base_med = ((unsigned long) wave->PCM.Mono8 >> 8) & 0xff ;
  422.   hardware->Frame_base_low = (unsigned long) wave->PCM.Mono8 & 0xfe ;
  423.  
  424.   hardware->Frame_end_high = ((unsigned long) tmp >> 16) & 0x3f ;
  425.   hardware->Frame_end_med = ((unsigned long) tmp >> 8) & 0xff ;
  426.   hardware->Frame_end_low = (unsigned long) tmp & 0xfe ;
  427.  
  428.   hardware->Mode_cntrl = mode ;
  429.   hardware->DMA_cntrl = 0x01 ;
  430.  
  431. /* wait for the sample to complete */
  432.   while(hardware->DMA_cntrl != 0) ;
  433.  
  434.   return 0l ;
  435.  
  436. }
  437.  
  438. int locate_chunk(char * id_fmt, CHK_DEF * Chk, FILE * fp)
  439. {
  440. int good_bad ;
  441. int num_read ;
  442. char temp[5] ;
  443.  
  444.   good_bad = 0 ; /* false */
  445.   do
  446.   {
  447.     temp[4] = '\0' ;
  448.     num_read = fread(Chk, 1, sizeof(CHK_DEF), fp) ;
  449.     if (num_read != sizeof(CHK_DEF))
  450.       return -1 ;
  451.     if (motorola_fmt == FALSE)
  452.       MSC_cnvrt_long(&Chk->ckSize) ;
  453.     strncpy(temp, Chk->ckStr, 4) ;
  454.     printf("Found Chunk of type : %s\n", temp) ;
  455.     if (strncmp(id_fmt, Chk->ckStr, 4) != 0)
  456.     {
  457.       fseek(fp, Chk->ckSize, 1) ;
  458.     }
  459.     else
  460.       good_bad = 1 ;  /* true */
  461.   } while (good_bad == 0) ;
  462.   return 0 ;
  463. }