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

  1. /*
  2.  * September 25, 1991
  3.  * Copyright 1991 Guido van Rossum 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.  * Guido van Rossum And Sundry Contributors are not responsible for 
  7.  * the consequences of using this software.
  8.  */
  9.  
  10. /*
  11.  * Sound Tools SGI/Amiga AIFF format.
  12.  * Used by SGI on 4D/35 and Indigo.
  13.  * This is a subformat of the EA-IFF-85 format.
  14.  * This is related to the IFF format used by the Amiga.
  15.  * But, apparently, not the same.
  16.  *
  17.  * Jan 93: new version from Guido Van Rossum that 
  18.  * correctly skips unwanted sections.
  19.  *
  20.  * Jan 94: add loop & marker support
  21.  * Jul 97: added comments I/O by Leigh Smith
  22.  * Nov 97: added verbose chunk comments
  23.  *
  24.  * June 1, 1998 - Chris Bagwell (cbagwell@sprynet.com)
  25.  *   Fixed compile warnings reported by Kjetil Torgrim Homme
  26.  *   <kjetilho@ifi.uio.no>
  27.  *
  28.  * Sept 9, 1998 - fixed loop markers.
  29.  *
  30.  * Feb. 9, 1999 - Small fix to work with invalid headers that include
  31.  *   a INST block with markers that equal 0.  It should ingore those.
  32.  *   Also fix endian problems when ran on Intel machines.  The check
  33.  *   for endianness was being performed AFTER reading the header instead
  34.  *   of before reading it.
  35.  *
  36.  */
  37.  
  38. #include <math.h>
  39. #include <stdlib.h>
  40. #include <string.h>
  41. #include <stdio.h>
  42.  
  43. #ifdef HAVE_UNISTD_H
  44. #include <unistd.h>    /* For SEEK_* defines if not found in stdio */
  45. #endif
  46.  
  47. #ifdef HAVE_MALLOC_H
  48. #include <malloc.h>
  49. #endif
  50.  
  51. #include "st.h"
  52.  
  53. /* Private data used by writer */
  54. struct aiffpriv {
  55.     ULONG nsamples;    /* number of 1-channel samples read or written */
  56. };
  57.  
  58. double read_ieee_extended();
  59. LONG rawread(P3(ft_t, LONG *, LONG));
  60. void aiffwriteheader(P2(ft_t, LONG));
  61. void rawwrite(P3(ft_t, LONG *, LONG));
  62. void write_ieee_extended(P2(ft_t, double));
  63. double ConvertFromIeeeExtended();
  64. void ConvertToIeeeExtended(P2(double, char *));
  65. void textChunk(P3(char **text, char *chunkDescription, ft_t ft));
  66. void reportInstrument(P1(ft_t ft));
  67.  
  68. void aiffstartread(ft) 
  69. ft_t ft;
  70. {
  71.     struct aiffpriv *p = (struct aiffpriv *) ft->priv;
  72.     char buf[5];
  73.     ULONG totalsize;
  74.     LONG chunksize;
  75.     int channels = 0;
  76.     ULONG frames;
  77.     int bits = 0;
  78.     double rate = 0.0;
  79.     ULONG offset = 0;
  80.     ULONG blocksize = 0;
  81.     int littlendian = 1;
  82.     char *endptr;
  83.     int foundcomm = 0, foundmark = 0, foundinstr = 0;
  84.     struct mark {
  85.         int id, position;
  86.         char name[40]; 
  87.     } marks[32];
  88.     int i, j;
  89.     LONG nmarks = 0;
  90.     LONG sustainLoopBegin = 0, sustainLoopEnd = 0,
  91.          releaseLoopBegin = 0, releaseLoopEnd = 0;
  92.     LONG seekto = 0L, ssndsize = 0L;
  93.     char *author;
  94.     char *copyright;
  95.     char *nametext;
  96.  
  97.     /* Needed because of rawread() */
  98.     rawstartread(ft);
  99.  
  100.     /* AIFF is in Big Endian format.  Swap whats read in on Little */
  101.     /* Endian machines.                                            */
  102.     endptr = (char *) &littlendian;
  103.     if (*endptr)
  104.     {
  105.         ft->swap = ft->swap ? 0 : 1;
  106.     }
  107.  
  108.     /* FORM chunk */
  109.     if (fread(buf, 1, 4, ft->fp) != 4 || strncmp(buf, "FORM", 4) != 0)
  110.         fail("AIFF header does not begin with magic word 'FORM'");
  111.     totalsize = rlong(ft);
  112.     if (fread(buf, 1, 4, ft->fp) != 4 || strncmp(buf, "AIFF", 4) != 0)
  113.         fail("AIFF 'FORM' chunk does not specify 'AIFF' as type");
  114.  
  115.     
  116.     /* Skip everything but the COMM chunk and the SSND chunk */
  117.     /* The SSND chunk must be the last in the file */
  118.     while (1) {
  119.         if (fread(buf, 1, 4, ft->fp) != 4)
  120.         {
  121.             if (ssndsize > 0)
  122.                 break;
  123.             else
  124.                 fail("Missing SSND chunk in AIFF file");
  125.         }
  126.         if (strncmp(buf, "COMM", 4) == 0) {
  127.             /* COMM chunk */
  128.             chunksize = rlong(ft);
  129.             if (chunksize != 18)
  130.                 fail("AIFF COMM chunk has bad size");
  131.             channels = rshort(ft);
  132.             frames = rlong(ft);
  133.             bits = rshort(ft);
  134.             rate = read_ieee_extended(ft);
  135.             foundcomm = 1;
  136.         }
  137.         else if (strncmp(buf, "SSND", 4) == 0) {
  138.             /* SSND chunk */
  139.             chunksize = rlong(ft);
  140.             offset = rlong(ft);
  141.             blocksize = rlong(ft);
  142.             chunksize -= 8;
  143.             ssndsize = chunksize;
  144.             /* if can't seek, just do sound now */
  145.             if (!ft->seekable)
  146.                 break;
  147.             /* else, seek to end of sound and hunt for more */
  148.             seekto = ftell(ft->fp);
  149.             fseek(ft->fp, chunksize, SEEK_CUR); 
  150.         }
  151.         else if (strncmp(buf, "MARK", 4) == 0) {
  152.             /* MARK chunk */
  153.             chunksize = rlong(ft);
  154.             nmarks = rshort(ft);
  155.             chunksize -= 2;
  156.             for(i = 0; i < nmarks; i++) {
  157.                 int len;
  158.  
  159.                 marks[i].id = rshort(ft);
  160.                 marks[i].position = rlong(ft);
  161.                 chunksize -= 6;
  162.                 len = getc(ft->fp);
  163.                 chunksize -= len + 1;
  164.                 for(j = 0; j < len ; j++) 
  165.                     marks[i].name[j] = getc(ft->fp);
  166.                 marks[i].name[j] = 0;
  167.                 if ((len & 1) == 0) {
  168.                     chunksize--;
  169.                     getc(ft->fp);
  170.                 }
  171.             }
  172.             /* HA HA!  Sound Designer (and others) makes */
  173.             /* bogus files. It spits out bogus chunksize */
  174.             /* for MARK field */
  175.             while(chunksize-- > 0)
  176.                 getc(ft->fp);
  177.             foundmark = 1;
  178.         }
  179.         else if (strncmp(buf, "INST", 4) == 0) {
  180.             /* INST chunk */
  181.             chunksize = rlong(ft);
  182.             ft->instr.MIDInote = getc(ft->fp);
  183.             getc(ft->fp);                /* detune */
  184.             ft->instr.MIDIlow = getc(ft->fp);
  185.             ft->instr.MIDIhi = getc(ft->fp);
  186.             getc(ft->fp);            /* low velocity */
  187.             getc(ft->fp);            /* hi  velocity */
  188.             rshort(ft);                /* gain */
  189.             ft->loops[0].type = rshort(ft); /* sustain loop */
  190.             sustainLoopBegin = rshort(ft);     /* begin marker */
  191.             sustainLoopEnd = rshort(ft);    /* end marker */
  192.             ft->loops[1].type = rshort(ft); /* release loop */
  193.             releaseLoopBegin = rshort(ft);  /* begin marker */
  194.             releaseLoopEnd = rshort(ft);    /* end marker */
  195.  
  196.             /* At least one known program generates an INST */
  197.             /* block with everything zeroed out (meaning    */
  198.             /* no Loops used).  In this case it should just */
  199.             /* be ingorned.                        */
  200.             if (sustainLoopBegin == 0 && releaseLoopBegin == 0)
  201.                 foundinstr = 0;
  202.             else
  203.                 foundinstr = 1;
  204.         }
  205.         else if (strncmp(buf, "APPL", 4) == 0) {
  206.             chunksize = rlong(ft);
  207.             while(chunksize-- > 0)
  208.                 getc(ft->fp);
  209.         }
  210.         else if (strncmp(buf, "ALCH", 4) == 0) {
  211.             /* I think this is bogus and gets grabbed by APPL */
  212.             /* INST chunk */
  213.             rlong(ft);        /* ENVS - jeez! */
  214.             chunksize = rlong(ft);
  215.             while(chunksize-- > 0)
  216.                 getc(ft->fp);
  217.         }
  218.         else if (strncmp(buf, "ANNO", 4) == 0) {
  219.             /* Old form of comment chunk */
  220.             chunksize = rlong(ft);
  221.             /* allocate enough memory to hold the comment */
  222.             ft->comment = (char *) malloc((size_t) chunksize);
  223.             if (ft->comment == NULL)
  224.               fail("AIFF: Couldn't allocate ANNO header");
  225.             if (fread(ft->comment, 1, chunksize, ft->fp) != chunksize)
  226.               fail("AIFF: Unexpected EOF in ANNO header");
  227.         }
  228.         else if (strncmp(buf, "AUTH", 4) == 0) {
  229.           /* Author chunk */
  230.           textChunk(&author, "Author:", ft);
  231.           free(author);
  232.         }
  233.         else if (strncmp(buf, "NAME", 4) == 0) {
  234.           /* Name chunk */
  235.           textChunk(&nametext, "Name:", ft);
  236.           free(nametext);
  237.         }
  238.         else if (strncmp(buf, "(c) ", 4) == 0) {
  239.           /* Copyright chunk */
  240.           textChunk(©right, "Copyright:", ft);
  241.           free(copyright);
  242.         }
  243.         else {
  244.             buf[4] = 0;
  245.             /* bogus file, probably from the Mac */
  246.             if ((buf[0] < 'A' || buf[0] > 'Z') ||
  247.                 (buf[1] < 'A' || buf[1] > 'Z') ||
  248.                 (buf[2] < 'A' || buf[2] > 'Z') ||
  249.                 (buf[3] < 'A' || buf[3] > 'Z'))
  250.                 break;
  251.             if (feof(ft->fp))
  252.                 break;
  253.             report("AIFFstartread: ignoring '%s' chunk\n", buf);
  254.             chunksize = rlong(ft);
  255.             if (feof(ft->fp))
  256.                 break;
  257.             /* Skip the chunk using getc() so we may read
  258.                from a pipe */
  259.             while (chunksize-- > 0) {
  260.                 if (getc(ft->fp) == EOF)
  261.                     break;
  262.             }
  263.         }
  264.         if (feof(ft->fp))
  265.             break;
  266.     }
  267.  
  268.     /* 
  269.      * if a pipe, we lose all chunks after sound.  
  270.      * Like, say, instrument loops. 
  271.      */
  272.     if (ft->seekable)
  273.     {
  274.         if (seekto > 0)
  275.             fseek(ft->fp, seekto, SEEK_SET);
  276.         else
  277.             fail("AIFF: no sound data on input file");
  278.     }
  279.     /* SSND chunk just read */
  280.     if (blocksize != 0)
  281.         fail("AIFF header specifies nonzero blocksize?!?!");
  282.     while ((LONG) (--offset) >= 0) {
  283.         if (getc(ft->fp) == EOF)
  284.             fail("unexpected EOF while skipping AIFF offset");
  285.     }
  286.  
  287.     if (foundcomm) {
  288.         ft->info.channels = channels;
  289.         ft->info.rate = rate;
  290.         ft->info.style = SIGN2;
  291.         switch (bits) {
  292.         case 8:
  293.             ft->info.size = BYTE;
  294.             break;
  295.         case 16:
  296.             ft->info.size = WORD;
  297.             break;
  298.         default:
  299.             fail("unsupported sample size in AIFF header: %d", bits);
  300.             /*NOTREACHED*/
  301.         }
  302.     } else  {
  303.         if ((ft->info.channels == -1)
  304.             || (ft->info.rate == -1)
  305.             || (ft->info.style == -1)
  306.             || (ft->info.size == -1)) {
  307.           report("You must specify # channels, sample rate, signed/unsigned,\n");
  308.           report("and 8/16 on the command line.");
  309.           fail("Bogus AIFF file: no COMM section.");
  310.         }
  311.  
  312.     }
  313.  
  314.     p->nsamples = ssndsize / ft->info.size;    /* leave out channels */
  315.  
  316.     if (foundmark && !foundinstr)
  317.         fail("Bogus AIFF file: MARKers but no INSTrument.");
  318.     if (!foundmark && foundinstr)
  319.         fail("Bogus AIFF file: INSTrument but no MARKers.");
  320.     if (foundmark && foundinstr) {
  321.         int i;
  322.         int slbIndex = 0, sleIndex = 0;
  323.         int rlbIndex = 0, rleIndex = 0;
  324.  
  325.         /* find our loop markers and save their marker indexes */
  326.         for(i = 0; i < nmarks; i++) { 
  327.           if(marks[i].id == sustainLoopBegin)
  328.             slbIndex = i;
  329.           if(marks[i].id == sustainLoopEnd)
  330.             sleIndex = i;
  331.           if(marks[i].id == releaseLoopBegin)
  332.             rlbIndex = i;
  333.           if(marks[i].id == releaseLoopEnd)
  334.             rleIndex = i;
  335.         }
  336.  
  337.         ft->instr.nloops = 0;
  338.         if (ft->loops[0].type != 0) {
  339.             ft->loops[0].start = marks[slbIndex].position;
  340.             ft->loops[0].length = 
  341.                 marks[sleIndex].position - marks[slbIndex].position;
  342.             /* really the loop count should be infinite */
  343.             ft->loops[0].count = 1;    
  344.             ft->instr.loopmode = LOOP_SUSTAIN_DECAY | ft->loops[0].type;
  345.             ft->instr.nloops++;
  346.         }
  347.         if (ft->loops[1].type != 0) {
  348.             ft->loops[1].start = marks[rlbIndex].position;
  349.             ft->loops[1].length = 
  350.                 marks[rleIndex].position - marks[rlbIndex].position;
  351.             /* really the loop count should be infinite */
  352.             ft->loops[1].count = 1;
  353.             ft->instr.loopmode = LOOP_SUSTAIN_DECAY | ft->loops[1].type;
  354.             ft->instr.nloops++;
  355.         } 
  356.     }
  357.     if (verbose)
  358.       reportInstrument(ft);
  359. }
  360.  
  361. /* print out the MIDI key allocations, loop points, directions etc */
  362. void reportInstrument(ft)
  363. ft_t ft;
  364. {
  365.   int loopNum;
  366.  
  367.   if(ft->instr.nloops > 0)
  368.     fprintf(stderr, "AIFF Loop markers:\n");
  369.   for(loopNum  = 0; loopNum < ft->instr.nloops; loopNum++) {
  370.     if (ft->loops[loopNum].count) {
  371.       fprintf(stderr, "Loop %d: start: %6d", loopNum, ft->loops[loopNum].start);
  372.       fprintf(stderr, " end:   %6d", 
  373.           ft->loops[loopNum].start + ft->loops[loopNum].length);
  374.       fprintf(stderr, " count: %6d", ft->loops[loopNum].count);
  375.       fprintf(stderr, " type:  ");
  376.       switch(ft->loops[loopNum].type & ~LOOP_SUSTAIN_DECAY) {
  377.       case 0: fprintf(stderr, "off\n"); break;
  378.       case 1: fprintf(stderr, "forward\n"); break;
  379.       case 2: fprintf(stderr, "forward/backward\n"); break;
  380.       }
  381.     }
  382.   }
  383.   fprintf(stderr, "Unity MIDI Note: %d\n", ft->instr.MIDInote);
  384.   fprintf(stderr, "Low   MIDI Note: %d\n", ft->instr.MIDIlow);
  385.   fprintf(stderr, "High  MIDI Note: %d\n", ft->instr.MIDIhi);
  386. }
  387.  
  388. /* Process a text chunk, allocate memory, display it if verbose and return */
  389. void textChunk(text, chunkDescription, ft) 
  390. char **text;
  391. char *chunkDescription;
  392. ft_t ft;
  393. {
  394.   LONG chunksize = rlong(ft);
  395.   /* allocate enough memory to hold the text including a terminating \0 */
  396.   *text = (char *) malloc((size_t) chunksize + 1);
  397.   if (*text == NULL)
  398.     fail("AIFF: Couldn't allocate %s header", chunkDescription);
  399.   if (fread(*text, 1, chunksize, ft->fp) != chunksize)
  400.     fail("AIFF: Unexpected EOF in %s header", chunkDescription);
  401.   *(*text + chunksize) = '\0';
  402.   if(verbose) {
  403.     printf("%-10s   \"%s\"\n", chunkDescription, *text);
  404.   }
  405. }
  406.  
  407. LONG aiffread(ft, buf, len)
  408. ft_t ft;
  409. LONG *buf, len;
  410. {
  411.     struct aiffpriv *p = (struct aiffpriv *) ft->priv;
  412.  
  413.     /* just read what's left of SSND chunk */
  414.     if (len > p->nsamples)
  415.         len = p->nsamples;
  416.     rawread(ft, buf, len);
  417.     p->nsamples -= len;
  418.     return len;
  419. }
  420.  
  421. void aiffstopread(ft) 
  422. ft_t ft;
  423. {
  424.     char buf[5];
  425.     ULONG chunksize;
  426.  
  427.     if (!ft->seekable)
  428.     {
  429.         while (! feof(ft->fp)) 
  430.         {
  431.         if (fread(buf, 1, 4, ft->fp) != 4)
  432.             break;
  433.  
  434.         chunksize = rlong(ft);
  435.         if (feof(ft->fp))
  436.             break;
  437.         buf[4] = '\0';
  438.         warn("Ignoring AIFF tail chunk: '%s', %d bytes long\n", 
  439.             buf, chunksize);
  440.         if (! strcmp(buf, "MARK") || ! strcmp(buf, "INST"))
  441.             warn("    You're stripping MIDI/loop info!\n");
  442.         while ((LONG) (--chunksize) >= 0) 
  443.         {
  444.             if (getc(ft->fp) == EOF)
  445.                 break;
  446.         }
  447.         }
  448.     }
  449.  
  450.     /* Needed because of rawwrite() */
  451.     rawstopread(ft);
  452.  
  453.     return;
  454. }
  455.  
  456. /* When writing, the header is supposed to contain the number of
  457.    samples and data bytes written.
  458.    Since we don't know how many samples there are until we're done,
  459.    we first write the header with an very large number,
  460.    and at the end we rewind the file and write the header again
  461.    with the right number.  This only works if the file is seekable;
  462.    if it is not, the very large size remains in the header.
  463.    Strictly spoken this is not legal, but the playaiff utility
  464.    will still be able to play the resulting file. */
  465.  
  466. void aiffstartwrite(ft)
  467. ft_t ft;
  468. {
  469.     struct aiffpriv *p = (struct aiffpriv *) ft->priv;
  470.     int littlendian;
  471.     char *endptr;
  472.  
  473.     /* Needed because rawwrite() */
  474.     rawstartwrite(ft);
  475.  
  476.     /* AIFF is in Big Endian format.  Swap whats read in on Little */
  477.     /* Endian machines.                                            */
  478.     littlendian = 1;
  479.     endptr = (char *) &littlendian;
  480.     if (*endptr)
  481.     {
  482.         ft->swap = ft->swap ? 0 : 1;
  483.     }
  484.  
  485.     p->nsamples = 0;
  486.     if (ft->info.style == ULAW && ft->info.size == BYTE) {
  487.         report("expanding 8-bit u-law to 16 bits");
  488.         ft->info.size = WORD;
  489.     }
  490.     ft->info.style = SIGN2; /* We have a fixed style */
  491.  
  492.     /* Compute the "very large number" so that a maximum number
  493.        of samples can be transmitted through a pipe without the
  494.        risk of causing overflow when calculating the number of bytes.
  495.        At 48 kHz, 16 bits stereo, this gives ~3 hours of music.
  496.        Sorry, the AIFF format does not provide for an "infinite"
  497.        number of samples. */
  498.     aiffwriteheader(ft, 0x7f000000L / (ft->info.size*ft->info.channels));
  499. }
  500.  
  501. void aiffwrite(ft, buf, len)
  502. ft_t ft;
  503. LONG *buf, len;
  504. {
  505.     struct aiffpriv *p = (struct aiffpriv *) ft->priv;
  506.     p->nsamples += len;
  507.     rawwrite(ft, buf, len);
  508. }
  509.  
  510. void
  511. aiffstopwrite(ft)
  512. ft_t ft;
  513. {
  514.     struct aiffpriv *p = (struct aiffpriv *) ft->priv;
  515.  
  516.     /* Needed because of rawwrite().  Call now to flush
  517.      * buffer now before seeking around below.
  518.      */
  519.     rawstopwrite(ft);
  520.  
  521.     if (!ft->seekable)
  522.         return;
  523.     if (fseek(ft->fp, 0L, SEEK_SET) != 0)
  524.         fail("can't rewind output file to rewrite AIFF header");
  525.     aiffwriteheader(ft, p->nsamples / ft->info.channels);
  526. }
  527.  
  528. void aiffwriteheader(ft, nframes)
  529. ft_t ft;
  530. LONG nframes;
  531. {
  532.     int hsize =
  533.         8 /*COMM hdr*/ + 18 /*COMM chunk*/ +
  534.         8 /*SSND hdr*/ + 12 /*SSND chunk*/;
  535.     int bits = 0;
  536.     int i;
  537.     int comment_size;
  538.  
  539.     hsize += 8 + 2 + 16*ft->instr.nloops;    /* MARK chunk */
  540.     hsize += 20;                /* INST chunk */
  541.  
  542.     if (ft->info.style == SIGN2 && ft->info.size == BYTE)
  543.         bits = 8;
  544.     else if (ft->info.style == SIGN2 && ft->info.size == WORD)
  545.         bits = 16;
  546.     else
  547.         fail("unsupported output style/size for AIFF header");
  548.  
  549.     fputs("FORM", ft->fp); /* IFF header */
  550.     wlong(ft, hsize + nframes * ft->info.size * ft->info.channels); /* file size */
  551.     fputs("AIFF", ft->fp); /* File type */
  552.  
  553.     /* ANNO chunk -- holds comments text, however this is */
  554.     /* discouraged by Apple in preference to a COMT comments */
  555.     /* chunk, which holds a timestamp and marker id */
  556.     if (ft->comment)
  557.     {
  558.       fputs("ANNO", ft->fp);
  559.       /* Must put an even number of characters out.  True 68k processors
  560.        * OS's seem to require this 
  561.        */
  562.       comment_size = strlen(ft->comment);
  563.       wlong(ft, (LONG)(((comment_size % 2) == 0) ? comment_size : comment_size + 1)); /* ANNO chunk size, the No of chars */
  564.       fputs(ft->comment, ft->fp);
  565.       if (comment_size % 2 == 1)
  566.         fputs(" ", ft->fp);
  567.     }
  568.  
  569.     /* COMM chunk -- describes encoding (and #frames) */
  570.     fputs("COMM", ft->fp);
  571.     wlong(ft, (LONG) 18); /* COMM chunk size */
  572.     wshort(ft, ft->info.channels); /* nchannels */
  573.     wlong(ft, nframes); /* number of frames */
  574.     wshort(ft, bits); /* sample width, in bits */
  575.     write_ieee_extended(ft, (double)ft->info.rate);
  576.  
  577.     /* MARK chunk -- set markers */
  578.     if (ft->instr.nloops) {
  579.         fputs("MARK", ft->fp);
  580.         if (ft->instr.nloops > 2)
  581.             ft->instr.nloops = 2;
  582.         wlong(ft, 2 + 16*ft->instr.nloops);
  583.         wshort(ft, ft->instr.nloops);
  584.  
  585.         for(i = 0; i < ft->instr.nloops; i++) {
  586.             wshort(ft, i + 1);
  587.             wlong(ft, ft->loops[i].start);
  588.             fputc(0, ft->fp);
  589.             fputc(0, ft->fp);
  590.             wshort(ft, i*2 + 1);
  591.             wlong(ft, ft->loops[i].start + ft->loops[i].length);
  592.             fputc(0, ft->fp);
  593.             fputc(0, ft->fp);
  594.             }
  595.  
  596.         fputs("INST", ft->fp);
  597.         wlong(ft, 20);
  598.         /* random MIDI shit that we default on */
  599.         fputc(ft->instr.MIDInote, ft->fp);
  600.         fputc(0, ft->fp);            /* detune */
  601.         fputc(ft->instr.MIDIlow, ft->fp);
  602.         fputc(ft->instr.MIDIhi, ft->fp);
  603.         fputc(1, ft->fp);            /* low velocity */
  604.         fputc(127, ft->fp);            /* hi  velocity */
  605.         wshort(ft, 0);                /* gain */
  606.  
  607.         /* sustain loop */
  608.         wshort(ft, ft->loops[0].type);
  609.         wshort(ft, 1);                /* marker 1 */
  610.         wshort(ft, 3);                /* marker 3 */
  611.         /* release loop, if there */
  612.         if (ft->instr.nloops == 2) {
  613.             wshort(ft, ft->loops[1].type);
  614.             wshort(ft, 2);            /* marker 2 */
  615.             wshort(ft, 4);            /* marker 4 */
  616.         } else {
  617.             wshort(ft, 0);            /* no release loop */
  618.             wshort(ft, 0);
  619.             wshort(ft, 0);
  620.         }
  621.     }
  622.  
  623.     /* SSND chunk -- describes data */
  624.     fputs("SSND", ft->fp);
  625.     /* chunk size */
  626.     wlong(ft, 8 + nframes * ft->info.channels * ft->info.size); 
  627.     wlong(ft, (LONG) 0); /* offset */
  628.     wlong(ft, (LONG) 0); /* block size */
  629. }
  630.  
  631. double read_ieee_extended(ft)
  632. ft_t ft;
  633. {
  634.     char buf[10];
  635.     if (fread(buf, 1, 10, ft->fp) != 10)
  636.         fail("EOF while reading IEEE extended number");
  637.     return ConvertFromIeeeExtended(buf);
  638. }
  639.  
  640. void write_ieee_extended(ft, x)
  641. ft_t ft;
  642. double x;
  643. {
  644.     char buf[10];
  645.     ConvertToIeeeExtended(x, buf);
  646.     /*
  647.     report("converted %g to %o %o %o %o %o %o %o %o %o %o",
  648.         x,
  649.         buf[0], buf[1], buf[2], buf[3], buf[4],
  650.         buf[5], buf[6], buf[7], buf[8], buf[9]);
  651.     */
  652.     (void) fwrite(buf, 1, 10, ft->fp);
  653. }
  654.  
  655.  
  656. /*
  657.  * C O N V E R T   T O   I E E E   E X T E N D E D
  658.  */
  659.  
  660. /* Copyright (C) 1988-1991 Apple Computer, Inc.
  661.  * All rights reserved.
  662.  *
  663.  * Machine-independent I/O routines for IEEE floating-point numbers.
  664.  *
  665.  * NaN's and infinities are converted to HUGE_VAL or HUGE, which
  666.  * happens to be infinity on IEEE machines.  Unfortunately, it is
  667.  * impossible to preserve NaN's in a machine-independent way.
  668.  * Infinities are, however, preserved on IEEE machines.
  669.  *
  670.  * These routines have been tested on the following machines:
  671.  *    Apple Macintosh, MPW 3.1 C compiler
  672.  *    Apple Macintosh, THINK C compiler
  673.  *    Silicon Graphics IRIS, MIPS compiler
  674.  *    Cray X/MP and Y/MP
  675.  *    Digital Equipment VAX
  676.  *
  677.  *
  678.  * Implemented by Malcolm Slaney and Ken Turkowski.
  679.  *
  680.  * Malcolm Slaney contributions during 1988-1990 include big- and little-
  681.  * endian file I/O, conversion to and from Motorola's extended 80-bit
  682.  * floating-point format, and conversions to and from IEEE single-
  683.  * precision floating-point format.
  684.  *
  685.  * In 1991, Ken Turkowski implemented the conversions to and from
  686.  * IEEE double-precision format, added more precision to the extended
  687.  * conversions, and accommodated conversions involving +/- infinity,
  688.  * NaN's, and denormalized numbers.
  689.  */
  690.  
  691. #ifndef HUGE_VAL
  692. # define HUGE_VAL HUGE
  693. #endif /*HUGE_VAL*/
  694.  
  695. # define FloatToUnsigned(f)      ((ULONG)(((LONG)(f - 2147483648.0)) + 2147483647L) + 1)
  696.  
  697. void ConvertToIeeeExtended(num, bytes)
  698. double num;
  699. char *bytes;
  700. {
  701.     int    sign;
  702.     int expon;
  703.     double fMant, fsMant;
  704.     ULONG hiMant, loMant;
  705.  
  706.     if (num < 0) {
  707.         sign = 0x8000;
  708.         num *= -1;
  709.     } else {
  710.         sign = 0;
  711.     }
  712.  
  713.     if (num == 0) {
  714.         expon = 0; hiMant = 0; loMant = 0;
  715.     }
  716.     else {
  717.         fMant = frexp(num, &expon);
  718.         if ((expon > 16384) || !(fMant < 1)) {    /* Infinity or NaN */
  719.             expon = sign|0x7FFF; hiMant = 0; loMant = 0; /* infinity */
  720.         }
  721.         else {    /* Finite */
  722.             expon += 16382;
  723.             if (expon < 0) {    /* denormalized */
  724.                 fMant = ldexp(fMant, expon);
  725.                 expon = 0;
  726.             }
  727.             expon |= sign;
  728.             fMant = ldexp(fMant, 32);          
  729.             fsMant = floor(fMant); 
  730.             hiMant = FloatToUnsigned(fsMant);
  731.             fMant = ldexp(fMant - fsMant, 32); 
  732.             fsMant = floor(fMant); 
  733.             loMant = FloatToUnsigned(fsMant);
  734.         }
  735.     }
  736.     
  737.     bytes[0] = expon >> 8;
  738.     bytes[1] = expon;
  739.     bytes[2] = hiMant >> 24;
  740.     bytes[3] = hiMant >> 16;
  741.     bytes[4] = hiMant >> 8;
  742.     bytes[5] = hiMant;
  743.     bytes[6] = loMant >> 24;
  744.     bytes[7] = loMant >> 16;
  745.     bytes[8] = loMant >> 8;
  746.     bytes[9] = loMant;
  747. }
  748.  
  749.  
  750. /*
  751.  * C O N V E R T   F R O M   I E E E   E X T E N D E D  
  752.  */
  753.  
  754. /* 
  755.  * Copyright (C) 1988-1991 Apple Computer, Inc.
  756.  * All rights reserved.
  757.  *
  758.  * Machine-independent I/O routines for IEEE floating-point numbers.
  759.  *
  760.  * NaN's and infinities are converted to HUGE_VAL or HUGE, which
  761.  * happens to be infinity on IEEE machines.  Unfortunately, it is
  762.  * impossible to preserve NaN's in a machine-independent way.
  763.  * Infinities are, however, preserved on IEEE machines.
  764.  *
  765.  * These routines have been tested on the following machines:
  766.  *    Apple Macintosh, MPW 3.1 C compiler
  767.  *    Apple Macintosh, THINK C compiler
  768.  *    Silicon Graphics IRIS, MIPS compiler
  769.  *    Cray X/MP and Y/MP
  770.  *    Digital Equipment VAX
  771.  *
  772.  *
  773.  * Implemented by Malcolm Slaney and Ken Turkowski.
  774.  *
  775.  * Malcolm Slaney contributions during 1988-1990 include big- and little-
  776.  * endian file I/O, conversion to and from Motorola's extended 80-bit
  777.  * floating-point format, and conversions to and from IEEE single-
  778.  * precision floating-point format.
  779.  *
  780.  * In 1991, Ken Turkowski implemented the conversions to and from
  781.  * IEEE double-precision format, added more precision to the extended
  782.  * conversions, and accommodated conversions involving +/- infinity,
  783.  * NaN's, and denormalized numbers.
  784.  */
  785.  
  786. #ifndef HUGE_VAL
  787. # define HUGE_VAL HUGE
  788. #endif /*HUGE_VAL*/
  789.  
  790. # define UnsignedToFloat(u)         (((double)((LONG)(u - 2147483647L - 1))) + 2147483648.0)
  791.  
  792. /****************************************************************
  793.  * Extended precision IEEE floating-point conversion routine.
  794.  ****************************************************************/
  795.  
  796. double ConvertFromIeeeExtended(bytes)
  797. unsigned char *bytes;    /* LCN */
  798. {
  799.     double    f;
  800.     int    expon;
  801.     ULONG hiMant, loMant;
  802.     
  803.     expon = ((bytes[0] & 0x7F) << 8) | (bytes[1] & 0xFF);
  804.     hiMant    =    ((ULONG)(bytes[2] & 0xFF) << 24)
  805.             |    ((ULONG)(bytes[3] & 0xFF) << 16)
  806.             |    ((ULONG)(bytes[4] & 0xFF) << 8)
  807.             |    ((ULONG)(bytes[5] & 0xFF));
  808.     loMant    =    ((ULONG)(bytes[6] & 0xFF) << 24)
  809.             |    ((ULONG)(bytes[7] & 0xFF) << 16)
  810.             |    ((ULONG)(bytes[8] & 0xFF) << 8)
  811.             |    ((ULONG)(bytes[9] & 0xFF));
  812.  
  813.     if (expon == 0 && hiMant == 0 && loMant == 0) {
  814.         f = 0;
  815.     }
  816.     else {
  817.         if (expon == 0x7FFF) {    /* Infinity or NaN */
  818.             f = HUGE_VAL;
  819.         }
  820.         else {
  821.             expon -= 16383;
  822.             f  = ldexp(UnsignedToFloat(hiMant), expon-=31);
  823.             f += ldexp(UnsignedToFloat(loMant), expon-=32);
  824.         }
  825.     }
  826.  
  827.     if (bytes[0] & 0x80)
  828.         return -f;
  829.     else
  830.         return f;
  831. }
  832.  
  833.