home *** CD-ROM | disk | FTP | other *** search
/ Simtel MSDOS - Coast to Coast / simteldosarchivecoasttocoast2.iso / windows3 / mtlabsnd.zip / SOX.C < prev    next >
Text File  |  1993-04-26  |  17KB  |  681 lines

  1. /*
  2.  * July 5, 1991
  3.  * Copyright 1991 Lance Norskog And Sundry Contributors
  4.  * This source code is freely redistributable and may be used for
  5.  * any purpose.  This copyright notice must be maintained. 
  6.  * Lance Norskog And Sundry Contributors are not responsible for 
  7.  * the consequences of using this software.
  8.  */
  9.  
  10. #include "st.h"
  11. #include <sys/types.h>
  12. #include <sys/stat.h>
  13. #include <varargs.h>
  14. #include <ctype.h>
  15. #include <string.h>
  16.  
  17. /*
  18.  * SOX main program.
  19.  *
  20.  * Rewrite for new nicer option syntax.  July 13, 1991.
  21.  * Rewrite for separate effects library.  Sep. 15, 1991.
  22.  */
  23.  
  24. float volume = 1.0;    /* expansion coefficient */
  25. int dovolume = 0;
  26. int clipped = 0;    /* Volume change clipping errors */
  27.  
  28. float amplitude = 1.0;    /* Largest sample so far in intermediate buffer */
  29.  
  30. int writing = 0;    /* are we writing to a file? */
  31.  
  32. /* export flags */
  33. int verbose = 0;    /* be noisy on stderr */
  34. int summary = 0;    /* just print summary of information */
  35.  
  36. long ibuf[BUFSIZ];    /* Intermediate processing buffer */
  37. long obuf[BUFSIZ];    /* Intermediate processing buffer */
  38.  
  39.  
  40. long volumechange();
  41.  
  42. #ifdef    DOS
  43. char writebuf[BUFSIZ];    /* output write buffer */
  44. #endif
  45.  
  46. void    gettype(), report(), geteffect(), checkeffect();
  47.  
  48. struct soundstream informat, outformat;
  49.  
  50. char *myname;
  51. extern errno;
  52. extern char *sys_errlist[];
  53.  
  54. ft_t ft;
  55. struct effect eff;
  56. char *ifile, *ofile, *itype, *otype;
  57. extern char *optarg;
  58. extern int optind;
  59.  
  60. main(n, args)
  61. int n;
  62. char **args;
  63. {
  64.     myname = args[0];
  65.     init();
  66.     
  67.     ifile = ofile = NULL;
  68.  
  69.     /* Get input format options */
  70.     ft = &informat;
  71.     doopts(n, args);
  72.     /* Get input file */
  73.     if (optind >= n)
  74.         usage("No input file?");
  75.     ifile = args[optind];
  76.     if (! strcmp(ifile, "-"))
  77.         ft->fp = stdin;
  78.     else if ((ft->fp = fopen(ifile, READBINARY)) == NULL)
  79.         fail("Can't open input file '%s': %s", 
  80.             ifile, sys_errlist[errno]);
  81.     ft->filename = ifile;
  82.     optind++;
  83.  
  84.     /* Let -e allow no output file, just do an effect */
  85.     if (optind < n) {
  86.         if (strcmp(args[optind], "-e")) {
  87.         /* Get output format options */
  88.         ft = &outformat;
  89.         doopts(n, args);
  90.         /* Get output file */
  91.         if (optind >= n)
  92.             usage("No output file?");
  93.         ofile = args[optind];
  94.         ft->filename = ofile;
  95.         /*
  96.          * There are two choices here:
  97.          *    1) stomp the old file - normal shell "> file" behavior
  98.          *    2) fail if the old file already exists - csh mode
  99.          */
  100.         if (! strcmp(ofile, "-"))
  101.             ft->fp = stdout;
  102.         else {
  103. #ifdef    unix
  104.              /*    
  105.              * Remove old file if it's a text file, but 
  106.               * preserve Unix /dev/sound files.  I'm not sure
  107.              * this needs to be here, but it's not hurting
  108.              * anything.
  109.              */
  110.             if ((ft->fp = fopen(ofile, WRITEBINARY)) && 
  111.                    (filetype(fileno(ft->fp)) == S_IFREG)) {
  112.                 fclose(ft->fp);
  113.                 unlink(ofile);
  114.                 creat(ofile, 0666);
  115.                 ft->fp = fopen(ofile, WRITEBINARY);
  116.             }
  117. #else
  118.             ft->fp = fopen(ofile, WRITEBINARY);
  119. #endif
  120.             if (ft->fp == NULL)
  121.                 fail("Can't open output file '%s': %s", 
  122.                     ofile, sys_errlist[errno]);
  123. #ifdef    DOS
  124.             if (setvbuf (ft->fp,writebuf,_IOFBF,sizeof(writebuf)))
  125.                 fail("Can't set write buffer");
  126. #endif
  127.         }
  128.         writing = 1;
  129.         }
  130.         optind++;
  131.     }
  132.  
  133.     /* ??? */
  134. /*
  135.     if ((optind < n) && !writing && !eff.name)
  136.         fail("Can't do an effect without an output file!");
  137. */
  138.  
  139.     /* Get effect name */
  140.     if (optind < n) {
  141.         eff.name = args[optind];
  142.         optind++;
  143.         geteffect(&eff);
  144.         (* eff.h->getopts)(&eff, n - optind, &args[optind]);
  145.     }
  146.  
  147.     /* 
  148.      * If we haven't specifically set an output file 
  149.      * don't write a file; we could be doing a summary
  150.      * or a format check.
  151.      */
  152. /*
  153.     if (! ofile)
  154.         usage("Must be given an output file name");
  155. */
  156.     if (! ofile)
  157.         writing = 0;
  158.     /* Check global arguments */
  159.     if (volume <= 0.0)
  160.         fail("Volume must be greater than 0.0");
  161.     informat.seekable  = (filetype(fileno(informat.fp)) == S_IFREG);
  162.     outformat.seekable = (filetype(fileno(outformat.fp)) == S_IFREG); 
  163.  
  164.     /* If file types have not been set with -t, set from file names. */
  165.     if (! informat.filetype) {
  166.         if (informat.filetype = strrchr(ifile, '/'))
  167.             informat.filetype++;
  168.         else
  169.             informat.filetype = ifile;
  170.         if (informat.filetype = strrchr(informat.filetype, '.'))
  171.             informat.filetype++;
  172.     }
  173.     if (writing && ! outformat.filetype) {
  174.         if (outformat.filetype = strrchr(ofile, '/'))
  175.             outformat.filetype++;
  176.         else
  177.             outformat.filetype = ofile;
  178.         if (outformat.filetype = strrchr(outformat.filetype, '.'))
  179.             outformat.filetype++;
  180.     }
  181.  
  182.     process();
  183.     statistics();
  184.     exit(0);
  185. }
  186.  
  187. doopts(n, args)
  188. int n;
  189. char **args;
  190. {
  191.     int c;
  192.     char *str;
  193.  
  194.     while ((c = getopt(n, args, "r:v:t:c:suUAbwlfdDxSV")) != -1) {
  195.         switch(c) {
  196.         case 't':
  197.             if (! ft) usage("-t");
  198.             ft->filetype = optarg;
  199.             if (ft->filetype[0] == '.')
  200.                 ft->filetype++;
  201.             break;
  202.  
  203.         case 'r':
  204.             if (! ft) usage("-r");
  205.             str = optarg;
  206.             if ((! sscanf(str, "%d", &ft->info.rate)) ||
  207.                     (ft->info.rate <= 0))
  208.                 fail("-r must be given a positive integer");
  209.             break;
  210.         case 'v':
  211.             if (! ft) usage("-v");
  212.             str = optarg;
  213.             if ((! sscanf(str, "%e", &volume)) ||
  214.                     (volume <= 0))
  215.                 fail("Volume value '%s' is not a number",
  216.                     optarg);
  217.             dovolume = 1;
  218.             break;
  219.  
  220.         case 'c':
  221.             if (! ft) usage("-c");
  222.             str = optarg;
  223.             if (! sscanf(str, "%d", &ft->info.channels))
  224.                 fail("-c must be given a number");
  225.             break;
  226.         case 'b':
  227.             if (! ft) usage("-b");
  228.             ft->info.size = BYTE;
  229.             break;
  230.         case 'w':
  231.             if (! ft) usage("-w");
  232.             ft->info.size = WORD;
  233.             break;
  234.         case 'l':
  235.             if (! ft) usage("-l");
  236.             ft->info.size = LONG;
  237.             break;
  238.         case 'f':
  239.             if (! ft) usage("-f");
  240.             ft->info.size = FLOAT;
  241.             break;
  242.         case 'd':
  243.             if (! ft) usage("-d");
  244.             ft->info.size = DOUBLE;
  245.             break;
  246.         case 'D':
  247.             if (! ft) usage("-D");
  248.             ft->info.size = IEEE;
  249.             break;
  250.  
  251.         case 's':
  252.             if (! ft) usage("-s");
  253.             ft->info.style = SIGN2;
  254.             break;
  255.         case 'u':
  256.             if (! ft) usage("-u");
  257.             ft->info.style = UNSIGNED;
  258.             break;
  259.         case 'U':
  260.             if (! ft) usage("-U");
  261.             ft->info.style = ULAW;
  262.             break;
  263.         case 'A':
  264.             if (! ft) usage("-A");
  265.             ft->info.style = ALAW;
  266.             break;
  267.         
  268.         case 'x':
  269.             if (! ft) usage("-x");
  270.             ft->swap = 1;
  271.             break;
  272.         
  273. /*  stat effect does this ?
  274.         case 'S':
  275.             summary = 1;
  276.             break;
  277. */
  278.         case 'V':
  279.             verbose = 1;
  280.             break;
  281.         }
  282.     }
  283. }
  284.  
  285. init() {
  286.  
  287.     /* init files */
  288.     informat.info.rate      = outformat.info.rate  = 0.0;
  289.     informat.info.size      = outformat.info.size  = -1;
  290.     informat.info.style     = outformat.info.style = -1;
  291.     informat.info.channels  = outformat.info.channels = -1;
  292.     informat.swap      = 0;
  293.     informat.filetype  = outformat.filetype  = (char *) 0;
  294.     informat.fp        = stdin;
  295.     outformat.fp       = stdout;
  296.     informat.filename  = "input";
  297.     outformat.filename = "output";
  298. }
  299.  
  300. /* 
  301.  * Process input file -> effect -> output file
  302.  *    one buffer at a time
  303.  */
  304.  
  305. process() {
  306.     long isamp, osamp, istart;
  307.     long i, idone, odone;
  308.  
  309.     gettype(&informat);
  310.     if (writing)
  311.         gettype(&outformat);
  312.     /* Read and write starters can change their formats. */
  313.     (* informat.h->startread)(&informat);
  314.     checkformat(&informat);
  315.     report("Input file: using sample rate %d\n\tsize %s, style %s, %d %s",
  316.         informat.info.rate, sizes[informat.info.size], 
  317.         styles[informat.info.style], informat.info.channels, 
  318.         (informat.info.channels > 1) ? "channels" : "channel");
  319.     if (writing) {
  320.         copyformat(&informat, &outformat);
  321.         (* outformat.h->startwrite)(&outformat);
  322.         checkformat(&outformat);
  323.         cmpformats(&informat, &outformat);
  324.     report("Output file: using sample rate %d\n\tsize %s, style %s, %d %s",
  325.         outformat.info.rate, sizes[outformat.info.size], 
  326.         styles[outformat.info.style], outformat.info.channels, 
  327.         (outformat.info.channels > 1) ? "channels" : "channel");
  328.     }
  329.     /* Very Important: 
  330.      * Effect fabrication and start is called AFTER files open.
  331.      * Effect may write out data beforehand, and
  332.      * some formats don't know their sample rate until now.
  333.      */
  334.     checkeffect();
  335.     /* inform effect about signal information */
  336.     eff.ininfo = informat.info;
  337.     eff.outinfo = outformat.info;
  338.     (* eff.h->start)(&eff);
  339.     istart = 0;
  340.     while((isamp = (*informat.h->read)(&informat,&ibuf[istart],
  341.             (long) BUFSIZ-istart))>0) {
  342.         long *ib = ibuf;
  343.  
  344.         isamp += istart;
  345.         /* Do volume before effect or after?  idunno */
  346.         if (dovolume) for (i = 0; i < isamp; i++)
  347.             ibuf[i] = volumechange(ibuf[i]);
  348.         osamp = sizeof(obuf) / sizeof(long);
  349.         /* Effect (i.e. rate change) may do different sizes I and O */
  350.         while (isamp) {
  351.             idone = isamp;
  352.             odone = osamp;
  353.             (* eff.h->flow)(&eff, ib, obuf, &idone, &odone);
  354.             /* 
  355.              * kludge!     
  356.              * Effect is stuck.  Start over with new buffer.
  357.              * This can drop samples at end of file. 
  358.              * No effects currently do this, but it could happen.
  359.              */
  360.             if (idone == 0) {
  361.                 int i;
  362.                 for(i = isamp - 1; i; i--)
  363.                     ibuf[i] = ib[i];
  364.                 istart = isamp;
  365.                 isamp = 0;
  366.                 break;
  367.             }
  368.             if (writing && (odone > 0)) 
  369.                 (* outformat.h->write)(&outformat, obuf, (long) odone);
  370.             isamp -= idone;
  371.             ib += idone;
  372.         }
  373.     }
  374.     /* Drain effect out */
  375.     if (writing) {
  376.         odone = sizeof(obuf) / sizeof(long);
  377.         (* eff.h->drain)(&eff, obuf, &odone);
  378.         if (odone > 0)
  379.             (* outformat.h->write)(&outformat, obuf, (long) odone);
  380.         /* keep calling it until it returns a partial buffer */
  381.         while (odone == (sizeof(obuf) / sizeof(long))) {
  382.             (* eff.h->drain)(&eff, obuf, &odone);
  383.             if (odone)
  384.              (* outformat.h->write)(&outformat, obuf, (long) odone);
  385.         }
  386.     }
  387.     /* Very Important: 
  388.      * Effect stop is called BEFORE files close.
  389.      * Effect may write out more data after. 
  390.      */
  391.     (* eff.h->stop)(&eff);
  392.     (* informat.h->stopread)(&informat);
  393.     fclose(informat.fp);
  394.     if (writing)
  395.         (* outformat.h->stopwrite)(&outformat);
  396.     if (writing)
  397.         fclose(outformat.fp);
  398. }
  399.  
  400. /*
  401.  * Check that we have a known format suffix string.
  402.  */
  403. void
  404. gettype(formp)
  405. ft_t formp;
  406. {
  407.     char **list;
  408.     int i;
  409.     extern format_t formats[];
  410.  
  411.     if (! formp->filetype)
  412. fail("Must give file type for %s file, either as suffix or with -t option",
  413. formp->filename);
  414.     for(i = 0; formats[i].names; i++) {
  415.         for(list = formats[i].names; *list; list++) {
  416.             char *s1 = *list, *s2 = formp->filetype;
  417.             if (! strcmpcase(s1, s2))
  418.                 break;    /* not a match */
  419.         }
  420.         if (! *list)
  421.             continue;
  422.         /* Found it! */
  423.         formp->h = &formats[i];
  424.         return;
  425.     }
  426.     if (! strcmpcase(formp->filetype, "snd")) {
  427.         verbose = 1;
  428.         report("File type '%s' is used to name several different formats.", formp->filetype);
  429.         report("If the file came from a Macintosh, it is probably");
  430.         report("a .ub file with a sample rate of 11025 (or possibly 5012 or 22050).");
  431.         report("Use the sequence '-t .ub -r 11025 file.snd'");
  432.         report("If it came from a PC, it's probably a Soundtool file.");
  433.         report("Use the sequence '-t .sndt file.snd'");
  434.         report("If it came from a NeXT, it's probably a .au file.");
  435.         fail("Use the sequence '-t .au file.snd'\n");
  436.     }
  437.     fail("File type '%s' of %s file is not known!",
  438.         formp->filetype, formp->filename);
  439. }
  440.  
  441. copyformat(ft, ft2)
  442. ft_t ft, ft2;
  443. {
  444.     int noise = 0;
  445.     if (ft2->info.rate == 0.0) {
  446.         ft2->info.rate = ft->info.rate;
  447.         noise = 1;
  448.     }
  449.     if (outformat.info.size == -1) {
  450.         ft2->info.size = ft->info.size;
  451.         noise = 1;
  452.     }
  453.     if (outformat.info.style == -1) {
  454.         ft2->info.style = ft->info.style;
  455.         noise = 1;
  456.     }
  457.     if (outformat.info.channels == -1) {
  458.         ft2->info.channels = ft->info.channels;
  459.         noise = 1;
  460.     }
  461.     return noise;
  462. }
  463.  
  464. cmpformats(ft, ft2)
  465. ft_t ft, ft2;
  466. {
  467.     int noise = 0;
  468.     float abs;
  469.  
  470. }
  471.  
  472. /* check that all settings have been given */
  473. checkformat(ft) 
  474. ft_t ft;
  475. {
  476.     if (ft->info.rate == 0.0)
  477.         fail("Sampling rate for %s file was not given\n", ft->filename);
  478.     if ((ft->info.rate < 100) || (ft->info.rate > 50000))
  479.         fail("Sampling rate %d for %s file is bogus\n", 
  480.             ft->info.rate, ft->filename);
  481.     if (ft->info.size == -1)
  482.         fail("Data size was not given for %s file\nUse one of -b/-w/-l/-f/-d/-D", ft->filename);
  483.     if (ft->info.style == -1)
  484.         fail("Data style was not given for %s file\nUse one of -s/-u/-U/-A", ft->filename);
  485.     /* it's so common, might as well default */
  486.     if (ft->info.channels == -1)
  487.         ft->info.channels = 1;
  488.     /*    fail("Number of output channels was not given for %s file",
  489.             ft->filename); */
  490. }
  491.  
  492. /*
  493.  * If no effect given, decide what it should be.
  494.  */
  495. void
  496. checkeffect(effp)
  497. eff_t effp;
  498. {
  499.     int already = (eff.name != (char *) 0);
  500.     char *rate = 0, *chan = 0;
  501.     int i;
  502.  
  503.     for (i = 0; effects[i].name; i++) {
  504.         if (!chan && (effects[i].flags & EFF_CHAN))
  505.             chan = effects[i].name;
  506.         if (! rate && (effects[i].flags & EFF_RATE))
  507.             rate = effects[i].name;
  508.     }
  509.  
  510.     if (eff.name && ! writing)
  511.         return;
  512.  
  513.     /* 
  514.      * Require mixdown for channel mismatch.
  515.      * XXX Doesn't handle channel expansion.  Need an effect for this.
  516.      * Require one of the rate-changers on a rate change.
  517.      * Override a rate change by explicitly giving 'copy' command.
  518.      */
  519.     if (informat.info.channels != outformat.info.channels) {
  520.         if (eff.name && !(eff.h->flags & EFF_CHAN))
  521.             fail("Need to do change number of channels first.  Try the '%s' effect.", chan);
  522.         if (! eff.name) {
  523.             eff.name = chan;
  524.             report(
  525. "Changing %d input channels to %d output channels with '%s' effect\n",
  526.             informat.info.channels, outformat.info.channels, chan);
  527.             geteffect(&eff);
  528.         }
  529.     } 
  530.     /* 
  531.      * Be liberal on rate difference errors.
  532.      * Note that the SPARC 8000-8192 problem
  533.      * comes in just under the wire.  XXX
  534.      *
  535.       * Bogus.  Should just do a percentage.
  536.      */
  537.     if (abs(informat.info.rate - outformat.info.rate) > 200) {
  538.         if (eff.name && !(eff.h->flags & EFF_RATE))
  539.             fail("Need to do rate change first.  Try the '%s' effect.", 
  540.             rate);
  541.         if (! eff.name) {
  542.             eff.name = rate;
  543.             report(
  544. "Changing sample rate %d to rate %d via noisy 'rate' effect\n",
  545.             informat.info.rate, outformat.info.rate);
  546.             geteffect(&eff);
  547.         }
  548.     }
  549.     /* don't need to change anything */
  550.     if (! eff.name)
  551.         eff.name = "copy";
  552.     if (! already) {
  553.         geteffect(&eff);
  554.         /* give default opts for manufactured effect */
  555.         (eff.h->getopts)(&eff, 0, (char *) 0);
  556.     }
  557. }
  558.  
  559. /*
  560.  * Check that we have a known effect name.
  561.  */
  562. void
  563. geteffect(effp)
  564. eff_t effp;
  565. {
  566.     int i;
  567.  
  568.     for(i = 0; effects[i].name; i++) {
  569.         char *s1 = effects[i].name, *s2 = effp->name;
  570.         while(*s1 && *s2 && (tolower(*s1) == tolower(*s2)))
  571.             s1++, s2++;
  572.         if (*s1 || *s2)
  573.             continue;    /* not a match */
  574.         /* Found it! */
  575.         effp->h = &effects[i];
  576.         return;
  577.     }
  578.     /* Guido Van Rossum fix */
  579.     fprintf(stderr, "Known effects:");
  580.     for (i = 0; effects[i].name; i++)
  581.         fprintf(stderr, "\t%s\n", effects[i].name);
  582.     fail("\nEffect '%s' is not known!", effp->name);
  583. }
  584.  
  585. /* Guido Van Rossum fix */
  586. statistics() {
  587.     if (dovolume && clipped > 0)
  588.         report("Volume change clipped %d samples", clipped);
  589. }
  590.  
  591. long volumechange(y)
  592. long y;
  593. {
  594.     double y1;
  595.  
  596.     y1 = y * volume;
  597.     if (y1 < -2147483647.0) {
  598.         y1 = -2147483647.0;
  599.         clipped++;
  600.     }
  601.     else if (y1 > 2147483647.0) {
  602.         y1 = 2147483647.0;
  603.         clipped++;
  604.     }
  605.  
  606.     return y1;
  607. }
  608.  
  609. filetype(fd)
  610. int fd;
  611. {
  612.     struct stat st;
  613.  
  614.     fstat(fd, &st);
  615.  
  616.     return st.st_mode & S_IFMT;
  617. }
  618.  
  619. char *usagestr = 
  620. "[ -V -S ] [ fopts ] ifile [ fopts ] ofile [ effect [ effopts ] ]\nfopts: -r rate -v volume -c channels -s/-u/-U/-A -b/-w/-l/-f/-d/-D -x\neffects and effopts: various";
  621.  
  622. usage(opt)
  623. char *opt;
  624. {
  625. #ifndef    DOS
  626.     /* single-threaded machines don't really need this */
  627.     fprintf(stderr, "%s: ", myname);
  628. #endif
  629.     fprintf(stderr, "Usage: %s", usagestr);
  630.     if (opt)
  631.         fprintf(stderr, "\nFailed at: %s\n", opt);
  632.     exit(1);
  633. }
  634.  
  635. void
  636. report(va_alist) 
  637. va_dcl
  638. {
  639.     va_list args;
  640.     char *fmt;
  641.  
  642.     if (! verbose)
  643.         return;
  644. #ifndef    DOS
  645.     /* single-threaded machines don't really need this */
  646.     fprintf(stderr, "%s: ", myname);
  647. #endif
  648.     va_start(args);
  649.     fmt = va_arg(args, char *);
  650.     vfprintf(stderr, fmt, args);
  651.     va_end(args);
  652.     fprintf(stderr, "\n");
  653. }
  654.  
  655. fail(va_alist) 
  656. va_dcl
  657. {
  658.     va_list args;
  659.     char *fmt;
  660.  
  661. #ifndef    DOS
  662.     /* single-threaded machines don't really need this */
  663.     fprintf(stderr, "%s: ", myname);
  664. #endif
  665.     va_start(args);
  666.     fmt = va_arg(args, char *);
  667.     vfprintf(stderr, fmt, args);
  668.     va_end(args);
  669.     fprintf(stderr, "\n");
  670.     exit(2);
  671. }
  672.  
  673.  
  674. strcmpcase(s1, s2)
  675. char *s1, *s2;
  676. {
  677.     while(*s1 && *s2 && (tolower(*s1) == tolower(*s2)))
  678.         s1++, s2++;
  679.     return *s1 - *s2;
  680. }
  681.