shithub: sox

Download patch

ref: 34d83c1c8e6dfa635a57c3cd07b54c222b6f39f7
parent: b91c9d5d7ce73a1b90794fa008fc2dc4cde53886
author: cbagwell <cbagwell>
date: Thu Oct 25 14:08:12 EDT 2001

Fixed size and encoding problem when writing hdrs of 16-bit stereo files

--- a/Changelog
+++ b/Changelog
@@ -24,6 +24,8 @@
     forces strictest alignment.
   o ima_rw was miscompiling on alpha's because of a header ordering
     problem.
+  o Erik de Castro Lopo pointed out that when writing 16-bit VOC files
+    the headers did not contain the correct length or encoding type.
 
 sox-12.17.2
 -----------
--- a/src/voc.c
+++ b/src/voc.c
@@ -1,8 +1,8 @@
 /*
  * Copyright 1991 Lance Norskog And Sundry Contributors
  * This source code is freely redistributable and may be used for
- * any purpose.  This copyright notice must be maintained. 
- * Lance Norskog And Sundry Contributors are not responsible for 
+ * any purpose.  This copyright notice must be maintained.
+ * Lance Norskog And Sundry Contributors are not responsible for
  * the consequences of using this software.
  *
  * September 8, 1993
@@ -11,7 +11,7 @@
  *
  * February 22, 1996
  * by Chris Bagwell (cbagwell@sprynet.com)
- * Added support for block type 8 (extended) which allows for 8-bit stereo 
+ * Added support for block type 8 (extended) which allows for 8-bit stereo
  * files.  Added support for saving stereo files and 16-bit files.
  * Added VOC format info from audio format FAQ so I don't have to keep
  * looking around for it.
@@ -111,7 +111,7 @@
 
         Data is stored left, right
 
-BLOCK 9 - data block that supersedes blocks 1 and 8.  
+BLOCK 9 - data block that supersedes blocks 1 and 8.
           Used for stereo, 16 bit.
 
         BYTE bBlockID;          // = 9
@@ -120,7 +120,7 @@
         BYTE bBitsPerSample;    // e.g., 8 or 16
         BYTE bChannels;         // 1 for mono, 2 for stereo
         WORD wFormat;           // see below
-        BYTE reserved[4];       // pad to make block w/o data 
+        BYTE reserved[4];       // pad to make block w/o data
                                 // have a size of 16 bytes
 
         Valid values of wFormat are:
@@ -143,148 +143,148 @@
 
 /* Private data for VOC file */
 typedef struct vocstuff {
-	LONG	rest;			/* bytes remaining in current block */
-	LONG	rate;			/* rate code (byte) of this chunk */
-	int		silent;		/* sound or silence? */
-	LONG	srate;			/* rate code (byte) of silence */
-	LONG	blockseek;		/* start of current output block */
-	LONG	samples;		/* number of samples output */
-	int		size;		/* word length of data */
-	unsigned char	channels;	/* number of sound channels */
-	int     extended;       /* Has an extended block been read? */
+        LONG    rest;                   /* bytes remaining in current block */
+        LONG    rate;                   /* rate code (byte) of this chunk */
+        int             silent;         /* sound or silence? */
+        LONG    srate;                  /* rate code (byte) of silence */
+        LONG    blockseek;              /* start of current output block */
+        LONG    samples;                /* number of samples output */
+        int             size;           /* word length of data */
+        unsigned char   channels;       /* number of sound channels */
+        int     extended;       /* Has an extended block been read? */
 } *vs_t;
 
-#define	VOC_TERM	0
-#define	VOC_DATA	1
-#define	VOC_CONT	2
-#define	VOC_SILENCE	3
-#define	VOC_MARKER	4
-#define	VOC_TEXT	5
-#define	VOC_LOOP	6
-#define	VOC_LOOPEND	7
+#define VOC_TERM        0
+#define VOC_DATA        1
+#define VOC_CONT        2
+#define VOC_SILENCE     3
+#define VOC_MARKER      4
+#define VOC_TEXT        5
+#define VOC_LOOP        6
+#define VOC_LOOPEND     7
 #define VOC_EXTENDED    8
-#define VOC_DATA_16	9
+#define VOC_DATA_16     9
 
-#define	min(a, b)	(((a) < (b)) ? (a) : (b))
+#define min(a, b)       (((a) < (b)) ? (a) : (b))
 
 static int getblock(ft_t);
 static void blockstart(ft_t);
 static void blockstop(ft_t);
 
-int st_vocstartread(ft) 
+int st_vocstartread(ft)
 ft_t ft;
 {
-	char header[20];
-	vs_t v = (vs_t) ft->priv;
-	unsigned short sbseek;
-	int rc;
+        char header[20];
+        vs_t v = (vs_t) ft->priv;
+        unsigned short sbseek;
+        int rc;
 
-	/* VOC is in Little Endian format.  Swap bytes read in on */
-	/* Big Endian mahcines.				          */
-	if (ST_IS_BIGENDIAN)
-	{
-		ft->swap = ft->swap ? 0 : 1;
-	}
+        /* VOC is in Little Endian format.  Swap bytes read in on */
+        /* Big Endian mahcines.                                   */
+        if (ST_IS_BIGENDIAN)
+        {
+                ft->swap = ft->swap ? 0 : 1;
+        }
 
 
-	if (! ft->seekable)
-	{
-		st_fail_errno(ft,ST_EOF,"VOC input file must be a file, not a pipe");
-		return(ST_EOF);
-	}
+        if (! ft->seekable)
+        {
+                st_fail_errno(ft,ST_EOF,"VOC input file must be a file, not a pipe");
+                return(ST_EOF);
+        }
 
-	if (fread(header, 1, 20, ft->fp) != 20)
-	{
-		st_fail_errno(ft,ST_EHDR,"unexpected EOF in VOC header");
-		return(ST_EOF);
-	}
-	if (strncmp(header, "Creative Voice File\032", 19))
-	{
-		st_fail_errno(ft,ST_EHDR,"VOC file header incorrect");
-		return(ST_EOF);
-	}
+        if (fread(header, 1, 20, ft->fp) != 20)
+        {
+                st_fail_errno(ft,ST_EHDR,"unexpected EOF in VOC header");
+                return(ST_EOF);
+        }
+        if (strncmp(header, "Creative Voice File\032", 19))
+        {
+                st_fail_errno(ft,ST_EHDR,"VOC file header incorrect");
+                return(ST_EOF);
+        }
 
-	st_readw(ft, &sbseek);
-	fseek(ft->fp, sbseek, 0);
+        st_readw(ft, &sbseek);
+        fseek(ft->fp, sbseek, 0);
 
-	v->rate = -1;
-	v->rest = 0;
-	v->extended = 0;
-	rc = getblock(ft);
-	if (rc)
-	    return rc;
-	if (v->rate == -1)
-	{
-		st_fail_errno(ft,ST_EOF,"Input .voc file had no sound!");
-		return(ST_EOF);
-	}
+        v->rate = -1;
+        v->rest = 0;
+        v->extended = 0;
+        rc = getblock(ft);
+        if (rc)
+            return rc;
+        if (v->rate == -1)
+        {
+                st_fail_errno(ft,ST_EOF,"Input .voc file had no sound!");
+                return(ST_EOF);
+        }
 
-	ft->info.size = v->size;
-	ft->info.encoding = ST_ENCODING_UNSIGNED;
-	if (v->size == ST_SIZE_WORD)
-	    ft->info.encoding = ST_ENCODING_SIGN2;
-	if (ft->info.channels == -1)
-		ft->info.channels = v->channels;
+        ft->info.size = v->size;
+        ft->info.encoding = ST_ENCODING_UNSIGNED;
+        if (v->size == ST_SIZE_WORD)
+            ft->info.encoding = ST_ENCODING_SIGN2;
+        if (ft->info.channels == -1)
+                ft->info.channels = v->channels;
 
-	return(ST_SUCCESS);
+        return(ST_SUCCESS);
 }
 
-LONG st_vocread(ft, buf, len) 
+LONG st_vocread(ft, buf, len)
 ft_t ft;
 LONG *buf, len;
 {
-	vs_t v = (vs_t) ft->priv;
-	int done = 0;
-	int rc;
-	unsigned short us;
-	unsigned char uc;
-	
-	if (v->rest == 0)
-	{
-		rc = getblock(ft);
-		if (rc)
-		    return 0;
-	}
+        vs_t v = (vs_t) ft->priv;
+        int done = 0;
+        int rc;
+        unsigned short us;
+        unsigned char uc;
 
-	if (v->rest == 0)
-		return 0;
+        if (v->rest == 0)
+        {
+                rc = getblock(ft);
+                if (rc)
+                    return 0;
+        }
 
-	if (v->silent) {
-		/* Fill in silence */
-		for(;v->rest && (done < len); v->rest--, done++)
-			*buf++ = 0x80000000L;
-	} else {
-		for(;v->rest && (done < len); v->rest--, done++) {
-			switch(v->size)
-			{
-			    case ST_SIZE_BYTE:
-				if (st_readb(ft, &uc) == ST_EOF) {
-				    st_warn("VOC input: short file");
-				    v->rest = 0;
-				    return done;
-				}
-				uc ^= 0x80;	/* convert to signed */
-				*buf++ = LEFT(uc, 24);
-				break;
-			    case ST_SIZE_WORD:
-				st_readw(ft, &us);
-				if (feof(ft->fp))
-				{
-				    st_warn("VOC input: short file");
-				    v->rest = 0;
-				    return done;
-				}
-				*buf++ = LEFT(us, 16);
-				v->rest--; /* Processed 2 bytes so update */
-				break;
-			}	
-		}
-	}
-	return done;
-}
+        if (v->rest == 0)
+                return 0;
+
+        if (v->silent) {
+                /* Fill in silence */
+                for(;v->rest && (done < len); v->rest--, done++)
+                        *buf++ = 0x80000000L;
+        } else {
+                for(;v->rest && (done < len); v->rest--, done++) {
+                        switch(v->size)
+                        {
+                            case ST_SIZE_BYTE:
+                                if (st_readb(ft, &uc) == ST_EOF) {
+                                    st_warn("VOC input: short file");
+                                    v->rest = 0;
+                                    return done;
+                                }
+                                uc ^= 0x80;     /* convert to signed */
+                                *buf++ = LEFT(uc, 24);
+                                break;
+                            case ST_SIZE_WORD:
+                                st_readw(ft, &us);
+                                if (feof(ft->fp))
+                                {
+                                    st_warn("VOC input: short file");
+                                    v->rest = 0;
+                                    return done;
+                                }
+                                *buf++ = LEFT(us, 16);
+                                v->rest--; /* Processed 2 bytes so update */
+                                break;
+                        }
+                }
+        }
+        return done;
+}
 
 /* nothing to do */
-int st_vocstopread(ft) 
+int st_vocstopread(ft)
 ft_t ft;
 {
     return(ST_SUCCESS);
@@ -294,7 +294,7 @@
  * If an 8-bit mono sample then use a VOC_DATA header.
  * If an 8-bit stereo sample then use a VOC_EXTENDED header followed
  * by a VOC_DATA header.
- * If a 16-bit sample (either stereo or mono) then save with a 
+ * If a 16-bit sample (either stereo or mono) then save with a
  * VOC_DATA_16 header.
  *
  * This approach will cause the output to be an its most basic format
@@ -301,76 +301,76 @@
  * which will work with the oldest software (eg. an 8-bit mono sample
  * will be able to be played with a really old SB VOC player.)
  */
-int st_vocstartwrite(ft) 
+int st_vocstartwrite(ft)
 ft_t ft;
 {
-	vs_t v = (vs_t) ft->priv;
+        vs_t v = (vs_t) ft->priv;
 
-	/* VOC is in Little Endian format.  Swap whats read */
-	/* in on Big Endian machines.			    */
-	if (ST_IS_BIGENDIAN)
-	{
-		ft->swap = ft->swap ? 0 : 1;
-	}
+        /* VOC is in Little Endian format.  Swap whats read */
+        /* in on Big Endian machines.                       */
+        if (ST_IS_BIGENDIAN)
+        {
+                ft->swap = ft->swap ? 0 : 1;
+        }
 
-	if (! ft->seekable)
-	{
-		st_fail_errno(ft,ST_EOF,"Output .voc file must be a file, not a pipe");
-		return(ST_EOF);
-	}
+        if (! ft->seekable)
+        {
+                st_fail_errno(ft,ST_EOF,"Output .voc file must be a file, not a pipe");
+                return(ST_EOF);
+        }
 
-	v->samples = 0;
+        v->samples = 0;
 
-	/* File format name and a ^Z (aborts printing under DOS) */
-	st_writes(ft, "Creative Voice File\032");
-	st_writew(ft, 26);			/* size of header */
-	st_writew(ft, 0x10a);              /* major/minor version number */
-	st_writew(ft, 0x1129);		/* checksum of version number */
+        /* File format name and a ^Z (aborts printing under DOS) */
+        st_writes(ft, "Creative Voice File\032");
+        st_writew(ft, 26);                      /* size of header */
+        st_writew(ft, 0x10a);              /* major/minor version number */
+        st_writew(ft, 0x1129);          /* checksum of version number */
 
-	if (ft->info.size == ST_SIZE_BYTE)
-	  ft->info.encoding = ST_ENCODING_UNSIGNED;
-	else
-	  ft->info.encoding = ST_ENCODING_SIGN2;
-	if (ft->info.channels == -1)
-		ft->info.channels = 1;
+        if (ft->info.size == ST_SIZE_BYTE)
+          ft->info.encoding = ST_ENCODING_UNSIGNED;
+        else
+          ft->info.encoding = ST_ENCODING_SIGN2;
+        if (ft->info.channels == -1)
+                ft->info.channels = 1;
 
-	return(ST_SUCCESS);
+        return(ST_SUCCESS);
 }
 
-LONG st_vocwrite(ft, buf, len) 
+LONG st_vocwrite(ft, buf, len)
 ft_t ft;
 LONG *buf, len;
 {
-	vs_t v = (vs_t) ft->priv;
-	unsigned char uc;
-	int sw;
-	LONG done = 0;
-	
-	if (v->samples == 0) {
-	  /* No silence packing yet. */
-	  v->silent = 0;
-	  blockstart(ft);
-	}
-	v->samples += len;
-	while(done < len) {
-	  if (ft->info.size == ST_SIZE_BYTE) {
-	    uc = RIGHT(*buf++, 24);
-	    uc ^= 0x80;
-	    st_writeb(ft, uc);
-	  } else {
-		sw = (int) RIGHT(*buf++, 16);
-	    st_writew(ft,sw);
+        vs_t v = (vs_t) ft->priv;
+        unsigned char uc;
+        int sw;
+        LONG done = 0;
+
+        if (v->samples == 0) {
+          /* No silence packing yet. */
+          v->silent = 0;
+          blockstart(ft);
+        }
+        v->samples += len;
+        while(done < len) {
+          if (ft->info.size == ST_SIZE_BYTE) {
+            uc = RIGHT(*buf++, 24);
+            uc ^= 0x80;
+            st_writeb(ft, uc);
+          } else {
+                sw = (int) RIGHT(*buf++, 16);
+            st_writew(ft,sw);
           }
-	  done++;
-	}
-	return done;
+          done++;
+        }
+        return done;
 }
 
-int st_vocstopwrite(ft) 
+int st_vocstopwrite(ft)
 ft_t ft;
 {
-	blockstop(ft);
-	return(ST_SUCCESS);
+        blockstop(ft);
+        return(ST_SUCCESS);
 }
 
 /* Voc-file handlers */
@@ -380,271 +380,270 @@
 getblock(ft)
 ft_t ft;
 {
-	vs_t v = (vs_t) ft->priv;
-	unsigned char uc, block;
-	ULONG sblen;
-	unsigned short new_rate_short;
-	ULONG new_rate_long;
-	int i;
-	ULONG trash;
+        vs_t v = (vs_t) ft->priv;
+        unsigned char uc, block;
+        ULONG sblen;
+        unsigned short new_rate_short;
+        ULONG new_rate_long;
+        int i;
+        ULONG trash;
 
-	v->silent = 0;
-	while (v->rest == 0) {
-		if (feof(ft->fp))
-			return ST_SUCCESS;
-		st_readb(ft, &block);
-		if (block == VOC_TERM)
-			return ST_SUCCESS;
-		if (feof(ft->fp))
-			return ST_SUCCESS;
-		/* 
-		 * Size is an 24-bit value.  Currently there is no util 
-		 * func to read this so do it this cross-platform way
-		 *
-		 */
-		st_readb(ft, &uc);
-		sblen = uc;
-		st_readb(ft, &uc);
-		sblen |= ((LONG) uc) << 8;
-		st_readb(ft, &uc);
-		sblen |= ((LONG) uc) << 16;
-		switch(block) {
-		case VOC_DATA: 
-			st_readb(ft, &uc);
-			/* When DATA block preceeded by an EXTENDED     */
-			/* block, the DATA blocks rate value is invalid */
-		        if (!v->extended) {
-			  if (uc == 0)
-			  {
-			    st_fail_errno(ft,ST_EFMT,"File %s: Sample rate is zero?");
-			    return(ST_EOF);
-			  }
-			  if ((v->rate != -1) && (uc != v->rate))
-			  {
-			    st_fail_errno(ft,ST_EFMT,"File %s: sample rate codes differ: %d != %d",
-				 ft->filename,v->rate, uc);
-			    return(ST_EOF);
-			  }
-			  v->rate = uc;
-			  ft->info.rate = 1000000.0/(256 - v->rate);
-			  v->channels = 1;
-			}
-			st_readb(ft, &uc);
-			if (uc != 0)
-			{
-			  st_fail_errno(ft,ST_EFMT,"File %s: only interpret 8-bit data!",
-			       ft->filename);
-			  return(ST_EOF);
-			}
-			v->extended = 0;
-			v->rest = sblen - 2;
-			v->size = ST_SIZE_BYTE;
-			return (ST_SUCCESS);
-		case VOC_DATA_16:
-			st_readdw(ft, &new_rate_long);
-			if (new_rate_long == 0)
-			{
-			    st_fail_errno(ft,ST_EFMT,"File %s: Sample rate is zero?",ft->filename);
-			    return(ST_EOF);
-			}
-			if ((v->rate != -1) && (new_rate_long != v->rate))
-			{
-			    st_fail_errno(ft,ST_EFMT,"File %s: sample rate codes differ: %d != %d",
-				ft->filename, v->rate, new_rate_long);
-			    return(ST_EOF);
-			}
-			v->rate = new_rate_long;
-			ft->info.rate = new_rate_long;
-			st_readb(ft, &uc);
-			switch (uc)
-			{
-			    case 8:	v->size = ST_SIZE_BYTE; break;
-			    case 16:	v->size = ST_SIZE_WORD; break;
-			    default:	
-					st_fail_errno(ft,ST_EFMT,"Don't understand size %d", uc);
-					return(ST_EOF);
-			}
-			st_readb(ft, &(v->channels));
-			st_readb(ft, (unsigned char *)&trash); /* unknown */
-			st_readb(ft, (unsigned char *)&trash); /* notused */
-			st_readb(ft, (unsigned char *)&trash); /* notused */
-			st_readb(ft, (unsigned char *)&trash); /* notused */
-			st_readb(ft, (unsigned char *)&trash); /* notused */
-			st_readb(ft, (unsigned char *)&trash); /* notused */
-			v->rest = sblen - 12;
-			return (ST_SUCCESS);
-		case VOC_CONT: 
-			v->rest = sblen;
-			return (ST_SUCCESS);
-		case VOC_SILENCE: 
-			{
-			unsigned short period;
+        v->silent = 0;
+        while (v->rest == 0) {
+                if (feof(ft->fp))
+                        return ST_SUCCESS;
+                st_readb(ft, &block);
+                if (block == VOC_TERM)
+                        return ST_SUCCESS;
+                if (feof(ft->fp))
+                        return ST_SUCCESS;
+                /*
+                 * Size is an 24-bit value.  Currently there is no util
+                 * func to read this so do it this cross-platform way
+                 *
+                 */
+                st_readb(ft, &uc);
+                sblen = uc;
+                st_readb(ft, &uc);
+                sblen |= ((LONG) uc) << 8;
+                st_readb(ft, &uc);
+                sblen |= ((LONG) uc) << 16;
+                switch(block) {
+                case VOC_DATA:
+                        st_readb(ft, &uc);
+                        /* When DATA block preceeded by an EXTENDED     */
+                        /* block, the DATA blocks rate value is invalid */
+                        if (!v->extended) {
+                          if (uc == 0)
+                          {
+                            st_fail_errno(ft,ST_EFMT,"File %s: Sample rate is zero?");
+                            return(ST_EOF);
+                          }
+                          if ((v->rate != -1) && (uc != v->rate))
+                          {
+                            st_fail_errno(ft,ST_EFMT,"File %s: sample rate codes differ: %d != %d",
+                                 ft->filename,v->rate, uc);
+                            return(ST_EOF);
+                          }
+                          v->rate = uc;
+                          ft->info.rate = 1000000.0/(256 - v->rate);
+                          v->channels = 1;
+                        }
+                        st_readb(ft, &uc);
+                        if (uc != 0)
+                        {
+                          st_fail_errno(ft,ST_EFMT,"File %s: only interpret 8-bit data!",
+                               ft->filename);
+                          return(ST_EOF);
+                        }
+                        v->extended = 0;
+                        v->rest = sblen - 2;
+                        v->size = ST_SIZE_BYTE;
+                        return (ST_SUCCESS);
+                case VOC_DATA_16:
+                        st_readdw(ft, &new_rate_long);
+                        if (new_rate_long == 0)
+                        {
+                            st_fail_errno(ft,ST_EFMT,"File %s: Sample rate is zero?",ft->filename);
+                            return(ST_EOF);
+                        }
+                        if ((v->rate != -1) && (new_rate_long != v->rate))
+                        {
+                            st_fail_errno(ft,ST_EFMT,"File %s: sample rate codes differ: %d != %d",
+                                ft->filename, v->rate, new_rate_long);
+                            return(ST_EOF);
+                        }
+                        v->rate = new_rate_long;
+                        ft->info.rate = new_rate_long;
+                        st_readb(ft, &uc);
+                        switch (uc)
+                        {
+                            case 8:     v->size = ST_SIZE_BYTE; break;
+                            case 16:    v->size = ST_SIZE_WORD; break;
+                            default:
+                                        st_fail_errno(ft,ST_EFMT,"Don't understand size %d", uc);
+                                        return(ST_EOF);
+                        }
+                        st_readb(ft, &(v->channels));
+                        st_readb(ft, (unsigned char *)&trash); /* unknown */
+                        st_readb(ft, (unsigned char *)&trash); /* notused */
+                        st_readb(ft, (unsigned char *)&trash); /* notused */
+                        st_readb(ft, (unsigned char *)&trash); /* notused */
+                        st_readb(ft, (unsigned char *)&trash); /* notused */
+                        st_readb(ft, (unsigned char *)&trash); /* notused */
+                        v->rest = sblen - 12;
+                        return (ST_SUCCESS);
+                case VOC_CONT:
+                        v->rest = sblen;
+                        return (ST_SUCCESS);
+                case VOC_SILENCE:
+                        {
+                        unsigned short period;
 
-			st_readw(ft, &period);
-			st_readb(ft, &uc);
-			if (uc == 0)
-			{
-				st_fail_errno(ft,ST_EFMT,"File %s: Silence sample rate is zero");
-				return(ST_EOF);
-			}
-			/* 
-			 * Some silence-packed files have gratuitously
-			 * different sample rate codes in silence.
-			 * Adjust period.
-			 */
-			if ((v->rate != -1) && (uc != v->rate))
-				period = (period * (256 - uc))/(256 - v->rate);
-			else
-				v->rate = uc;
-			v->rest = period;
-			v->silent = 1;
-			return (ST_SUCCESS);
-			}
-		case VOC_MARKER:
-			st_readb(ft, &uc);
-			st_readb(ft, &uc);
-			/* Falling! Falling! */
-		case VOC_TEXT:
-			{
-			int i;
-			/* Could add to comment in SF? */
-			for(i = 0; i < sblen; i++)
-			    st_readb(ft, (unsigned char *)&trash);
-			}
-			continue;	/* get next block */
-		case VOC_LOOP:
-		case VOC_LOOPEND:
-			st_report("File %s: skipping repeat loop");
-			for(i = 0; i < sblen; i++)
-			    st_readb(ft, (unsigned char *)&trash);
-			break;
-		case VOC_EXTENDED:
-			/* An Extended block is followed by a data block */
-			/* Set this byte so we know to use the rate      */
-			/* value from the extended block and not the     */
-			/* data block.					 */
-			v->extended = 1;
-			st_readw(ft, &new_rate_short);
-			if (new_rate_short == 0)
-			{
-			   st_fail_errno(ft,ST_EFMT,"File %s: Sample rate is zero?");
-			   return(ST_EOF);
-			}
-			if ((v->rate != -1) && (new_rate_short != v->rate))
-			{
-			   st_fail_errno(ft,ST_EFMT,"File %s: sample rate codes differ: %d != %d",
-					ft->filename, v->rate, new_rate_short);
-			   return(ST_EOF);
-			}
-			v->rate = new_rate_short;
-			st_readb(ft, &uc);
-			if (uc != 0)
-			{
-				st_fail_errno(ft,ST_EFMT,"File %s: only interpret 8-bit data!",
-					ft->filename);
-				return(ST_EOF);
-			}
-			st_readb(ft, &uc);
-			if (uc)
-				ft->info.channels = 2;  /* Stereo */
-			/* Needed number of channels before finishing
-			   compute for rate */
-			ft->info.rate = (256000000L/(65536L - v->rate))/ft->info.channels;
-			/* An extended block must be followed by a data */
-			/* block to be valid so loop back to top so it  */
-			/* can be grabed.				*/
-			continue;
-		default:
-			st_report("File %s: skipping unknown block code %d",
-				ft->filename, block);
-			for(i = 0; i < sblen; i++)
-			    st_readb(ft, (unsigned char *)&trash);
-		}
-	}
-	return ST_SUCCESS;
-}
+                        st_readw(ft, &period);
+                        st_readb(ft, &uc);
+                        if (uc == 0)
+                        {
+                                st_fail_errno(ft,ST_EFMT,"File %s: Silence sample rate is zero");
+                                return(ST_EOF);
+                        }
+                        /*
+                         * Some silence-packed files have gratuitously
+                         * different sample rate codes in silence.
+                         * Adjust period.
+                         */
+                        if ((v->rate != -1) && (uc != v->rate))
+                                period = (period * (256 - uc))/(256 - v->rate);
+                        else
+                                v->rate = uc;
+                        v->rest = period;
+                        v->silent = 1;
+                        return (ST_SUCCESS);
+                        }
+                case VOC_MARKER:
+                        st_readb(ft, &uc);
+                        st_readb(ft, &uc);
+                        /* Falling! Falling! */
+                case VOC_TEXT:
+                        {
+                        int i;
+                        /* Could add to comment in SF? */
+                        for(i = 0; i < sblen; i++)
+                            st_readb(ft, (unsigned char *)&trash);
+                        }
+                        continue;       /* get next block */
+                case VOC_LOOP:
+                case VOC_LOOPEND:
+                        st_report("File %s: skipping repeat loop");
+                        for(i = 0; i < sblen; i++)
+                            st_readb(ft, (unsigned char *)&trash);
+                        break;
+                case VOC_EXTENDED:
+                        /* An Extended block is followed by a data block */
+                        /* Set this byte so we know to use the rate      */
+                        /* value from the extended block and not the     */
+                        /* data block.                                   */
+                        v->extended = 1;
+                        st_readw(ft, &new_rate_short);
+                        if (new_rate_short == 0)
+                        {
+                           st_fail_errno(ft,ST_EFMT,"File %s: Sample rate is zero?");
+                           return(ST_EOF);
+                        }
+                        if ((v->rate != -1) && (new_rate_short != v->rate))
+                        {
+                           st_fail_errno(ft,ST_EFMT,"File %s: sample rate codes differ: %d != %d",
+                                        ft->filename, v->rate, new_rate_short);
+                           return(ST_EOF);
+                        }
+                        v->rate = new_rate_short;
+                        st_readb(ft, &uc);
+                        if (uc != 0)
+                        {
+                                st_fail_errno(ft,ST_EFMT,"File %s: only interpret 8-bit data!",
+                                        ft->filename);
+                                return(ST_EOF);
+                        }
+                        st_readb(ft, &uc);
+                        if (uc)
+                                ft->info.channels = 2;  /* Stereo */
+                        /* Needed number of channels before finishing
+                           compute for rate */
+                        ft->info.rate = (256000000L/(65536L - v->rate))/ft->info.channels;
+                        /* An extended block must be followed by a data */
+                        /* block to be valid so loop back to top so it  */
+                        /* can be grabed.                               */
+                        continue;
+                default:
+                        st_report("File %s: skipping unknown block code %d",
+                                ft->filename, block);
+                        for(i = 0; i < sblen; i++)
+                            st_readb(ft, (unsigned char *)&trash);
+                }
+        }
+        return ST_SUCCESS;
+}
 
 /* Start an output block. */
 static void blockstart(ft)
 ft_t ft;
 {
-	vs_t v = (vs_t) ft->priv;
+        vs_t v = (vs_t) ft->priv;
 
-	v->blockseek = ftell(ft->fp);
-	if (v->silent) {
-		st_writeb(ft, VOC_SILENCE);	/* Silence block code */
-		st_writeb(ft, 0);		/* Period length */
-		st_writeb(ft, 0);		/* Period length */
-		st_writeb(ft, v->rate);		/* Rate code */
-	} else {
-	  if (ft->info.size == ST_SIZE_BYTE) {
-	    /* 8-bit sample section.  By always setting the correct     */
-	    /* rate value in the DATA block (even when its preceeded    */
-	    /* by an EXTENDED block) old software can still play stereo */
-	    /* files in mono by just skipping over the EXTENDED block.  */
-	    /* Prehaps the rate should be doubled though to make up for */
-	    /* double amount of samples for a given time????            */
-	    if (ft->info.channels > 1) {
-	      st_writeb(ft, VOC_EXTENDED);	/* Voice Extended block code */
-	      st_writeb(ft, 4);                /* block length = 4 */
-	      st_writeb(ft, 0);                /* block length = 4 */
-	      st_writeb(ft, 0);                /* block length = 4 */
-		  v->rate = 65536L - (256000000.0/(2*(float)ft->info.rate));
-	      st_writew(ft,v->rate);	/* Rate code */
-	      st_writeb(ft, 0);         /* File is not packed */
-	      st_writeb(ft, 1);         /* samples are in stereo */
-	    }
-	    st_writeb(ft, VOC_DATA);	/* Voice Data block code */
-	    st_writeb(ft, 0);		/* block length (for now) */
-	    st_writeb(ft, 0);		/* block length (for now) */
-	    st_writeb(ft, 0);		/* block length (for now) */
-	    v->rate = 256 - (1000000.0/(float)ft->info.rate);
-	    st_writeb(ft, (int) v->rate);/* Rate code */
-	    st_writeb(ft, 0);		/* 8-bit raw data */
-	} else {
-	    st_writeb(ft, VOC_DATA_16); /* Voice Data block code */
-	    st_writeb(ft, 0);		/* block length (for now) */
-	    st_writeb(ft, 0);		/* block length (for now) */
-	    st_writeb(ft, 0);		/* block length (for now) */
-	    v->rate = ft->info.rate;
-	    st_writedw(ft, v->rate);	/* Rate code */
-	    st_writeb(ft, 16);		/* Sample Size */
-	    st_writeb(ft, ft->info.channels);	/* Sample Size */
-	    st_writeb(ft, 0);		/* Unknown */
-	    st_writeb(ft, 0);		/* Unused */
-	    st_writeb(ft, 0);		/* Unused */
-	    st_writeb(ft, 0);		/* Unused */
-	    st_writeb(ft, 0);		/* Unused */
-	    st_writeb(ft, 0);		/* Unused */
-	  }
-	}
+        v->blockseek = ftell(ft->fp);
+        if (v->silent) {
+                st_writeb(ft, VOC_SILENCE);     /* Silence block code */
+                st_writeb(ft, 0);               /* Period length */
+                st_writeb(ft, 0);               /* Period length */
+                st_writeb(ft, v->rate);         /* Rate code */
+        } else {
+          if (ft->info.size == ST_SIZE_BYTE) {
+            /* 8-bit sample section.  By always setting the correct     */
+            /* rate value in the DATA block (even when its preceeded    */
+            /* by an EXTENDED block) old software can still play stereo */
+            /* files in mono by just skipping over the EXTENDED block.  */
+            /* Prehaps the rate should be doubled though to make up for */
+            /* double amount of samples for a given time????            */
+            if (ft->info.channels > 1) {
+              st_writeb(ft, VOC_EXTENDED);      /* Voice Extended block code */
+              st_writeb(ft, 4);                /* block length = 4 */
+              st_writeb(ft, 0);                /* block length = 4 */
+              st_writeb(ft, 0);                /* block length = 4 */
+                  v->rate = 65536L - (256000000.0/(2*(float)ft->info.rate));
+              st_writew(ft,v->rate);    /* Rate code */
+              st_writeb(ft, 0);         /* File is not packed */
+              st_writeb(ft, 1);         /* samples are in stereo */
+            }
+            st_writeb(ft, VOC_DATA);    /* Voice Data block code */
+            st_writeb(ft, 0);           /* block length (for now) */
+            st_writeb(ft, 0);           /* block length (for now) */
+            st_writeb(ft, 0);           /* block length (for now) */
+            v->rate = 256 - (1000000.0/(float)ft->info.rate);
+            st_writeb(ft, (int) v->rate);/* Rate code */
+            st_writeb(ft, 0);           /* 8-bit raw data */
+        } else {
+            st_writeb(ft, VOC_DATA_16); /* Voice Data block code */
+            st_writeb(ft, 0);           /* block length (for now) */
+            st_writeb(ft, 0);           /* block length (for now) */
+            st_writeb(ft, 0);           /* block length (for now) */
+            v->rate = ft->info.rate;
+            st_writedw(ft, v->rate);    /* Rate code */
+            st_writeb(ft, 16);          /* Sample Size */
+            st_writeb(ft, ft->info.channels);   /* Sample Size */
+            st_writew(ft, 0x0004);      /* Encoding */
+            st_writeb(ft, 0);           /* Unused */
+            st_writeb(ft, 0);           /* Unused */
+            st_writeb(ft, 0);           /* Unused */
+            st_writeb(ft, 0);           /* Unused */
+          }
+        }
 }
 
 /* End the current data or silence block. */
-static void blockstop(ft) 
+static void blockstop(ft)
 ft_t ft;
 {
-	vs_t v = (vs_t) ft->priv;
-	LONG datum;
+        vs_t v = (vs_t) ft->priv;
+        LONG datum;
 
-	st_writeb(ft, 0);			/* End of file block code */
-	fseek(ft->fp, v->blockseek, 0);		/* seek back to block length */
-	fseek(ft->fp, 1, 1);			/* seek forward one */
-	if (v->silent) {
-		st_writew(ft, v->samples);
-	} else {
-	  if (ft->info.size == ST_SIZE_BYTE) {
-	    if (ft->info.channels > 1) {
-	      fseek(ft->fp, 8, 1); /* forward 7 + 1 for new block header */
-	    }
-	  }
-	        v->samples += 2;		/* adjustment: SBDK pp. 3-5 */
-		datum = (v->samples) & 0xff;
-		st_writeb(ft, (int)datum);       /* low byte of length */
-		datum = (v->samples >> 8) & 0xff;
-		st_writeb(ft, (int)datum);  /* middle byte of length */
-		datum = (v->samples >> 16) & 0xff;
-		st_writeb(ft, (int)datum); /* high byte of length */
-	}
+        st_writeb(ft, 0);                       /* End of file block code */
+        fseek(ft->fp, v->blockseek, 0);         /* seek back to block length */
+        fseek(ft->fp, 1, 1);                    /* seek forward one */
+        if (v->silent) {
+                st_writew(ft, v->samples);
+        } else {
+          if (ft->info.size == ST_SIZE_BYTE) {
+            if (ft->info.channels > 1) {
+              fseek(ft->fp, 8, 1); /* forward 7 + 1 for new block header */
+            }
+          }
+                v->samples += 2;                /* adjustment: SBDK pp. 3-5 */
+                datum = (v->samples * ft->info.size) & 0xff;
+                st_writeb(ft, (int)datum);       /* low byte of length */
+                datum = ((v->samples * ft->info.size) >> 8) & 0xff;
+                st_writeb(ft, (int)datum);  /* middle byte of length */
+                datum = ((v->samples  * ft->info.size)>> 16) & 0xff;
+                st_writeb(ft, (int)datum); /* high byte of length */
+        }
 }