home *** CD-ROM | disk | FTP | other *** search
/ AmigActive 3 / AACD03.BIN / AACD / Sound / SoX / Source / smp.c < prev    next >
C/C++ Source or Header  |  1999-07-18  |  11KB  |  379 lines

  1. /*
  2.  * June 30, 1992
  3.  * Copyright 1992 Leigh Smith 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.  * Leigh Smith And Sundry Contributors are not responsible for 
  7.  * the consequences of using this software.
  8.  */
  9.  
  10. /*
  11.  * Sound Tools SampleVision file format driver.
  12.  * Output is always in little-endian (80x86/VAX) order.
  13.  * 
  14.  * Derived from: Sound Tools skeleton handler file.
  15.  *
  16.  * Add: Loop point verbose info.  It's a start, anyway.
  17.  */
  18.  
  19. #include "st.h"
  20. #include <string.h>
  21.  
  22. #define NAMELEN    30        /* Size of Samplevision name */
  23. #define COMMENTLEN 60        /* Size of Samplevision comment, not shared */
  24. #define MIDI_UNITY 60        /* MIDI note number to play sample at unity */
  25.  
  26. /* The header preceeding the sample data */
  27. struct smpheader {
  28.     char Id[18];        /* File identifier */
  29.     char version[4];    /* File version */
  30.     char comments[COMMENTLEN];    /* User comments */
  31.     char name[NAMELEN + 1];    /* Sample Name, left justified */
  32. };
  33. #define HEADERSIZE (sizeof(struct smpheader) - 1)    /* -1 for name's \0 */
  34.  
  35. /* Samplevision loop definition structure */
  36. struct loop {
  37.     ULONG start; /* Sample count into sample data, not byte count */
  38.     ULONG end;   /* end point */
  39.     char type;         /* 0 = loop off, 1 = forward, 2 = forw/back */
  40.     short count;         /* No of times to loop */
  41. };
  42.  
  43. /* Samplevision marker definition structure */
  44. struct marker {
  45.     char name[10];        /* Ascii Marker name */
  46.     ULONG position;    /* Sample Number, not byte number */
  47. };
  48.  
  49. /* The trailer following the sample data */
  50. struct smptrailer {
  51.     struct loop loops[8];        /* loops */
  52.     struct marker markers[8];    /* markers */
  53.     char MIDInote;            /* for unity pitch playback */
  54.     ULONG rate;            /* in hertz */
  55.     ULONG SMPTEoffset;        /* in subframes - huh? */
  56.     ULONG CycleSize;        /* sample count in one cycle of the */
  57.                     /* sampled sound -1 if unknown */
  58. };
  59.  
  60. /* Private data for SMP file */
  61. typedef struct smpstuff {
  62.   ULONG NoOfSamps;        /* Sample data count in words */
  63.   /* comment memory resides in private data because it's small */
  64.   char comment[COMMENTLEN + NAMELEN + 3];
  65. } *smp_t;
  66.  
  67. char *SVmagic = "SOUND SAMPLE DATA ", *SVvers = "2.1 ";
  68.  
  69. /*
  70.  * Read the SampleVision trailer structure.
  71.  * Returns 1 if everything was read ok, 0 if there was an error.
  72.  */
  73. static int readtrailer(ft, trailer)
  74. ft_t ft;
  75. struct smptrailer *trailer;
  76. {
  77.     int i;
  78.  
  79.     rshort(ft);            /* read reserved word */
  80.     for(i = 0; i < 8; i++) {    /* read the 8 loops */
  81.         trailer->loops[i].start = rlong(ft);
  82.         ft->loops[i].start = trailer->loops[i].start;
  83.         trailer->loops[i].end = rlong(ft);
  84.         ft->loops[i].length = 
  85.             trailer->loops[i].end - trailer->loops[i].start;
  86.         trailer->loops[i].type = getc(ft->fp);
  87.         ft->loops[i].type = trailer->loops[8].type;
  88.         trailer->loops[i].count = rshort(ft);
  89.         ft->loops[8].count = trailer->loops[8].count;
  90.     }
  91.     for(i = 0; i < 8; i++) {    /* read the 8 markers */
  92.         if (fread(trailer->markers[i].name, 1, 10, ft->fp) != 10)
  93.             return(0);
  94.         trailer->markers[i].position = rlong(ft);
  95.     }
  96.     trailer->MIDInote = getc(ft->fp);
  97.     trailer->rate = rlong(ft);
  98.     trailer->SMPTEoffset = rlong(ft);
  99.     trailer->CycleSize = rlong(ft);
  100.     return(1);
  101. }
  102.  
  103. /*
  104.  * set the trailer data - loops and markers, to reasonably benign values
  105.  */
  106. void settrailer(ft, trailer, rate)
  107. ft_t ft;
  108. struct smptrailer *trailer;
  109. unsigned int rate;
  110. {
  111.     int i;
  112.  
  113.     for(i = 0; i < 8; i++) {    /* copy the 8 loops */
  114.         if (ft->loops[i].type != 0) {
  115.         trailer->loops[i].start = ft->loops[i].start;
  116.         /* to mark it as not set */
  117.         trailer->loops[i].end = ft->loops[i].start + ft->loops[i].length;
  118.         trailer->loops[i].type = ft->loops[i].type;
  119.         trailer->loops[i].count = ft->loops[i].count;    
  120.         } else {
  121.         /* set first loop start as FFFFFFFF */
  122.         trailer->loops[i].start = ~0;    
  123.         /* to mark it as not set */
  124.         trailer->loops[i].end = 0;    
  125.         trailer->loops[i].type = 0;
  126.         trailer->loops[i].count = 0;
  127.         }
  128.     }
  129.     for(i = 0; i < 8; i++) {    /* write the 8 markers */
  130.         strcpy(trailer->markers[i].name, "          ");
  131.         trailer->markers[i].position = ~0;
  132.     }
  133.     trailer->MIDInote = MIDI_UNITY;        /* Unity play back */
  134.     trailer->rate = rate;
  135.     trailer->SMPTEoffset = 0;
  136.     trailer->CycleSize = -1;
  137. }
  138.  
  139. /*
  140.  * Write the SampleVision trailer structure.
  141.  * Returns 1 if everything was written ok, 0 if there was an error.
  142.  */
  143. static int writetrailer(ft, trailer)
  144. ft_t ft;
  145. struct smptrailer *trailer;
  146. {
  147.     int i;
  148.  
  149.     wshort(ft, 0);            /* write the reserved word */
  150.     for(i = 0; i < 8; i++) {    /* write the 8 loops */
  151.         wlong(ft, trailer->loops[i].start);
  152.         wlong(ft, trailer->loops[i].end);
  153.         putc(trailer->loops[i].type, ft->fp);
  154.         wshort(ft, trailer->loops[i].count);
  155.     }
  156.     for(i = 0; i < 8; i++) {    /* write the 8 markers */
  157.         if (fwrite(trailer->markers[i].name, 1, 10, ft->fp) != 10)
  158.             return(0);
  159.         wlong(ft, trailer->markers[i].position);
  160.     }
  161.     putc(trailer->MIDInote, ft->fp);
  162.     wlong(ft, trailer->rate);
  163.     wlong(ft, trailer->SMPTEoffset);
  164.     wlong(ft, trailer->CycleSize);
  165.     return(1);
  166. }
  167.  
  168. /*
  169.  * Do anything required before you start reading samples.
  170.  * Read file header. 
  171.  *    Find out sampling rate, 
  172.  *    size and style of samples, 
  173.  *    mono/stereo/quad.
  174.  */
  175. void smpstartread(ft) 
  176. ft_t ft;
  177. {
  178.     smp_t smp = (smp_t) ft->priv;
  179.     int littlendian = 1;
  180.     char *endptr;
  181.     int i;
  182.     int namelen, commentlen;
  183.     LONG samplestart;
  184.     struct smpheader header;
  185.     struct smptrailer trailer;
  186.  
  187.     endptr = (char *) &littlendian;
  188.     /* SMP is in Little Endian format.  Swap whats read in on */
  189.     /* Big Endian machines.                              */
  190.     if (!*endptr)
  191.     {
  192.         ft->swap = ft->swap ? 0 : 1;
  193.     }
  194.  
  195.     /* If you need to seek around the input file. */
  196.     if (! ft->seekable)
  197.         fail("SMP input file must be a file, not a pipe");
  198.  
  199.     /* Read SampleVision header */
  200.     if (fread((char *) &header, 1, HEADERSIZE, ft->fp) != HEADERSIZE)
  201.         fail("unexpected EOF in SMP header");
  202.     if (strncmp(header.Id, SVmagic, 17) != 0)
  203.         fail("SMP header does not begin with magic word %s\n", SVmagic);
  204.     if (strncmp(header.version, SVvers, 4) != 0)
  205.         fail("SMP header is not version %s\n", SVvers);
  206.  
  207.     /* Format the sample name and comments to a single comment */
  208.     /* string. We decrement the counters till we encounter non */
  209.         /* padding space chars, so the *lengths* are low by one */
  210.         for (namelen = NAMELEN-1;
  211.             namelen >= 0 && header.name[namelen] == ' '; namelen--)
  212.       ;
  213.         for (commentlen = COMMENTLEN-1;
  214.             commentlen >= 0 && header.comments[commentlen] == ' '; commentlen--)
  215.       ;
  216.     sprintf(smp->comment, "%.*s: %.*s", namelen+1, header.name,
  217.         commentlen+1, header.comments);
  218.     ft->comment = smp->comment;
  219.  
  220.     report("SampleVision file name and comments: %s", ft->comment);
  221.     /* Extract out the sample size (always intel format) */
  222.     smp->NoOfSamps = rlong(ft);
  223.     /* mark the start of the sample data */
  224.     samplestart = ftell(ft->fp);
  225.  
  226.     /* seek from the current position (the start of sample data) by */
  227.     /* NoOfSamps * 2 */
  228.     if (fseek(ft->fp, smp->NoOfSamps * 2L, 1) == -1)
  229.         fail("SMP unable to seek to trailer");
  230.     if (!readtrailer(ft, &trailer))
  231.         fail("unexpected EOF in SMP trailer");
  232.  
  233.     /* seek back to the beginning of the data */
  234.     if (fseek(ft->fp, samplestart, 0) == -1) 
  235.         fail("SMP unable to seek back to start of sample data");
  236.  
  237.     ft->info.rate = (int) trailer.rate;
  238.     ft->info.size = WORD;
  239.     ft->info.style = SIGN2;
  240.     ft->info.channels = 1;
  241.  
  242.     if (verbose) {
  243.         fprintf(stderr, "SampleVision trailer:\n");
  244.         for(i = 0; i < 8; i++) if (1 || trailer.loops[i].count) {
  245. #ifdef __alpha__
  246.             fprintf(stderr, "Loop %d: start: %6d", i, trailer.loops[i].start);
  247.             fprintf(stderr, " end:   %6d", trailer.loops[i].end);
  248. #else
  249.             fprintf(stderr, "Loop %d: start: %6ld", i, trailer.loops[i].start);
  250.             fprintf(stderr, " end:   %6ld", trailer.loops[i].end);
  251. #endif
  252.             fprintf(stderr, " count: %6d", trailer.loops[i].count);
  253.             fprintf(stderr, " type:  ");
  254.             switch(trailer.loops[i].type) {
  255.                 case 0: fprintf(stderr, "off\n"); break;
  256.                 case 1: fprintf(stderr, "forward\n"); break;
  257.                 case 2: fprintf(stderr, "forward/backward\n"); break;
  258.             }
  259.         }
  260.         fprintf(stderr, "MIDI Note number: %d\n\n", trailer.MIDInote);
  261.     }
  262.     ft->instr.nloops = 0;
  263.     for(i = 0; i < 8; i++) 
  264.         if (trailer.loops[i].type) 
  265.             ft->instr.nloops++;
  266.     for(i = 0; i < ft->instr.nloops; i++) {
  267.         ft->loops[i].type = trailer.loops[i].type;
  268.         ft->loops[i].count = trailer.loops[i].count;
  269.         ft->loops[i].start = trailer.loops[i].start;
  270.         ft->loops[i].length = trailer.loops[i].end 
  271.             - trailer.loops[i].start;
  272.     }
  273.     ft->instr.MIDIlow = ft->instr.MIDIhi =
  274.         ft->instr.MIDInote = trailer.MIDInote;
  275.     if (ft->instr.nloops > 0)
  276.         ft->instr.loopmode = LOOP_8;
  277.     else
  278.         ft->instr.loopmode = LOOP_NONE;
  279. }
  280.  
  281. /*
  282.  * Read up to len samples from file.
  283.  * Convert to signed longs.
  284.  * Place in buf[].
  285.  * Return number of samples read.
  286.  */
  287. LONG smpread(ft, buf, len) 
  288. ft_t ft;
  289. LONG *buf, len;
  290. {
  291.     smp_t smp = (smp_t) ft->priv;
  292.     LONG datum;
  293.     int done = 0;
  294.     
  295.     for(; done < len && smp->NoOfSamps; done++, smp->NoOfSamps--) {
  296.         datum = rshort(ft);
  297.         /* scale signed up to long's range */
  298.         *buf++ = LEFT(datum, 16);
  299.     }
  300.     return done;
  301. }
  302.  
  303. /*
  304.  * Do anything required when you stop reading samples.  
  305.  * Don't close input file! 
  306.  */
  307. void smpstopread(ft) 
  308. ft_t ft;
  309. {
  310. }
  311.  
  312. void smpstartwrite(ft) 
  313. ft_t ft;
  314. {
  315.     int littlendian = 1;
  316.     char *endptr;
  317.  
  318.     smp_t smp = (smp_t) ft->priv;
  319.     struct smpheader header;
  320.  
  321.     endptr = (char *) &littlendian;
  322.     /* SMP is in Little Endian format.  Swap whats read in on */
  323.     /* Big Endian machines.                              */
  324.     if (!*endptr)
  325.     {
  326.         ft->swap = ft->swap ? 0 : 1;
  327.     }
  328.  
  329.     /* If you have to seek around the output file */
  330.     if (! ft->seekable)
  331.         fail("Output .smp file must be a file, not a pipe");
  332.  
  333.     /* If your format specifies any of the following info. */
  334.     ft->info.size = WORD;
  335.     ft->info.style = SIGN2;
  336.     ft->info.channels = 1;
  337.  
  338.     strcpy(header.Id, SVmagic);
  339.     strcpy(header.version, SVvers);
  340.     sprintf(header.comments, "%-*s", COMMENTLEN, "Converted using Sox.");
  341.     sprintf(header.name, "%-*.*s", NAMELEN, NAMELEN, ft->comment);
  342.  
  343.     /* Write file header */
  344.     if(fwrite(&header, 1, HEADERSIZE, ft->fp) != HEADERSIZE)
  345.         fail("SMP: Can't write header completely");
  346.     wlong(ft, 0);    /* write as zero length for now, update later */
  347.     smp->NoOfSamps = 0;
  348. }
  349.  
  350. void smpwrite(ft, buf, len) 
  351. ft_t ft;
  352. LONG *buf, len;
  353. {
  354.     smp_t smp = (smp_t) ft->priv;
  355.     register int datum;
  356.  
  357.     while(len--) {
  358.         datum = (int) RIGHT(*buf++, 16);
  359.         wshort(ft, datum);
  360.         smp->NoOfSamps++;
  361.     }
  362.     /* If you cannot write out all of the supplied samples, */
  363.     /*    fail("SMP: Can't write all samples to %s", ft->filename); */
  364. }
  365.  
  366. void smpstopwrite(ft) 
  367. ft_t ft;
  368. {
  369.     smp_t smp = (smp_t) ft->priv;
  370.     struct smptrailer trailer;
  371.  
  372.     /* Assign the trailer data */
  373.     settrailer(ft, &trailer, ft->info.rate);
  374.     writetrailer(ft, &trailer);
  375.     if (fseek(ft->fp, 112, 0) == -1)
  376.         fail("SMP unable to seek back to save size");
  377.     wlong(ft, smp->NoOfSamps);
  378. }
  379.