File: voc.c

package info (click to toggle)
sox 12.16-6
  • links: PTS
  • area: main
  • in suites: potato
  • size: 1,180 kB
  • ctags: 1,466
  • sloc: ansic: 16,658; sh: 2,071; makefile: 126
file content (588 lines) | stat: -rw-r--r-- 17,304 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
/*
 * 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 
 * the consequences of using this software.
 *
 * September 8, 1993
 * Copyright 1993 T. Allen Grider - for changes to support block type 9
 * and word sized samples.  Same caveats and disclaimer as above.
 *
 * February 22, 1996
 * by Chris Bagwell (cbagwell@sprynet.com)
 * 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.
 */

/*
 * Sound Tools Sound Blaster VOC handler sources.
 */

/*------------------------------------------------------------------------
The following is taken from the Audio File Formats FAQ dated 2-Jan-1995
and submitted by Guido van Rossum <guido@cwi.nl>.
--------------------------------------------------------------------------
Creative Voice (VOC) file format
--------------------------------

From: galt@dsd.es.com

(byte numbers are hex!)

    HEADER (bytes 00-19)
    Series of DATA BLOCKS (bytes 1A+) [Must end w/ Terminator Block]

- ---------------------------------------------------------------

HEADER:
-------
     byte #     Description
     ------     ------------------------------------------
     00-12      "Creative Voice File"
     13         1A (eof to abort printing of file)
     14-15      Offset of first datablock in .voc file (std 1A 00
                in Intel Notation)
     16-17      Version number (minor,major) (VOC-HDR puts 0A 01)
     18-19      2's Comp of Ver. # + 1234h (VOC-HDR puts 29 11)

- ---------------------------------------------------------------

DATA BLOCK:
-----------

   Data Block:  TYPE(1-byte), SIZE(3-bytes), INFO(0+ bytes)
   NOTE: Terminator Block is an exception -- it has only the TYPE byte.

      TYPE   Description     Size (3-byte int)   Info
      ----   -----------     -----------------   -----------------------
      00     Terminator      (NONE)              (NONE)
      01     Sound data      2+length of data    *
      02     Sound continue  length of data      Voice Data
      03     Silence         3                   **
      04     Marker          2                   Marker# (2 bytes)
      05     ASCII           length of string    null terminated string
      06     Repeat          2                   Count# (2 bytes)
      07     End repeat      0                   (NONE)
      08     Extended        4                   ***

      *Sound Info Format:       **Silence Info Format:
       ---------------------      ----------------------------
       00   Sample Rate           00-01  Length of silence - 1
       01   Compression Type      02     Sample Rate
       02+  Voice Data

    ***Extended Info Format:
       ---------------------
       00-01  Time Constant: Mono: 65536 - (256000000/sample_rate)
                             Stereo: 65536 - (25600000/(2*sample_rate))
       02     Pack
       03     Mode: 0 = mono
                    1 = stereo


  Marker#           -- Driver keeps the most recent marker in a status byte
  Count#            -- Number of repetitions + 1
                         Count# may be 1 to FFFE for 0 - FFFD repetitions
                         or FFFF for endless repetitions
  Sample Rate       -- SR byte = 256-(1000000/sample_rate)
  Length of silence -- in units of sampling cycle
  Compression Type  -- of voice data
                         8-bits    = 0
                         4-bits    = 1
                         2.6-bits  = 2
                         2-bits    = 3
                         Multi DAC = 3+(# of channels) [interesting--
                                       this isn't in the developer's manual]

Detailed description of new data blocks (VOC files version 1.20 and above):

        (Source is fax from Barry Boone at Creative Labs, 405/742-6622)

BLOCK 8 - digitized sound attribute extension, must preceed block 1.
          Used to define stereo, 8 bit audio
        BYTE bBlockID;       // = 8
        BYTE nBlockLen[3];   // 3 byte length
        WORD wTimeConstant;  // time constant = same as block 1
        BYTE bPackMethod;    // same as in block 1
        BYTE bVoiceMode;     // 0-mono, 1-stereo

        Data is stored left, right

BLOCK 9 - data block that supersedes blocks 1 and 8.  
          Used for stereo, 16 bit.

        BYTE bBlockID;          // = 9
        BYTE nBlockLen[3];      // length 12 plus length of sound
        DWORD dwSamplesPerSec;  // samples per second, not time const.
        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 
                                // have a size of 16 bytes

        Valid values of wFormat are:

                0x0000  8-bit unsigned PCM
                0x0001  Creative 8-bit to 4-bit ADPCM
                0x0002  Creative 8-bit to 3-bit ADPCM
                0x0003  Creative 8-bit to 2-bit ADPCM
                0x0004  16-bit signed PCM
                0x0006  CCITT a-Law
                0x0007  CCITT u-Law
                0x02000 Creative 16-bit to 4-bit ADPCM

        Data is stored left, right

------------------------------------------------------------------------*/

#include "st.h"
#include <string.h>

/* 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 */
	int		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_EXTENDED    8
#define VOC_DATA_16	9

#define	min(a, b)	(((a) < (b)) ? (a) : (b))

void getblock();
void blockstart(P1(ft_t));
void blockstop(P1(ft_t));

void vocstartread(ft) 
ft_t ft;
{
	char header[20];
	vs_t v = (vs_t) ft->priv;
	int sbseek;
	int littlendian = 1;
	char *endptr;

	endptr = (char *) &littlendian;
	/* VOC is in Little Endian format.  Swap bytes read in on */
	/* Big Endian mahcines.				          */
	if (!*endptr)
	{
		ft->swap = ft->swap ? 0 : 1;
	}

	if (! ft->seekable)
		fail("VOC input file must be a file, not a pipe");
	if (fread(header, 1, 20, ft->fp) != 20)
		fail("unexpected EOF in VOC header");
	if (strncmp(header, "Creative Voice File\032", 19))
		fail("VOC file header incorrect");

	sbseek = rshort(ft);
	fseek(ft->fp, sbseek, 0);

	v->rate = -1;
	v->rest = 0;
	v->extended = 0;
	getblock(ft);
	if (v->rate == -1)
		fail("Input .voc file had no sound!");

	ft->info.size = v->size;
	ft->info.style = UNSIGNED;
	if (v->size == WORD)
	    ft->info.style = SIGN2;
	if (ft->info.channels == -1)
		ft->info.channels = v->channels;
}

LONG vocread(ft, buf, len) 
ft_t ft;
LONG *buf, len;
{
	vs_t v = (vs_t) ft->priv;
	int done = 0;
	
	if (v->rest == 0)
		getblock(ft);
	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++) {
			LONG datum;
			switch(v->size)
			{
			    case BYTE:
				if ((datum = getc(ft->fp)) == EOF) {
				    warn("VOC input: short file");
				    v->rest = 0;
				    return done;
				}
				datum ^= 0x80;	/* convert to signed */
				*buf++ = LEFT(datum, 24);
				break;
			    case WORD:
				datum = rshort(ft);
				if (feof(ft->fp))
				{
				    warn("VOC input: short file");
				    v->rest = 0;
				    return done;
				}
				*buf++ = LEFT(datum, 16);
				v->rest--; /* Processed 2 bytes so update */
				break;
			}	
		}
	}
	return done;
}

/* nothing to do */
void vocstopread(ft) 
ft_t ft;
{
}

/* When saving samples in VOC format the following outline is followed:
 * 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 
 * VOC_DATA_16 header.
 *
 * This approach will cause the output to be an its most basic format
 * 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.)
 */
void vocstartwrite(ft) 
ft_t ft;
{
	vs_t v = (vs_t) ft->priv;
	int littlendian = 1;
	char *endptr;

	endptr = (char *) &littlendian;
	/* VOC is in Little Endian format.  Swap whats read */
	/* in on Big Endian machines.			    */
	if (!*endptr)
	{
		ft->swap = ft->swap ? 0 : 1;
	}

	if (! ft->seekable)
		fail("Output .voc file must be a file, not a pipe");

	v->samples = 0;

	/* File format name and a ^Z (aborts printing under DOS) */
	(void) fwrite("Creative Voice File\032\032", 1, 20, ft->fp);
	wshort(ft, 26);			/* size of header */
	wshort(ft, 0x10a);              /* major/minor version number */
	wshort(ft, 0x1129);		/* checksum of version number */

	if (ft->info.size == BYTE)
	  ft->info.style = UNSIGNED;
	else
	  ft->info.style = SIGN2;
	if (ft->info.channels == -1)
		ft->info.channels = 1;
}

void vocwrite(ft, buf, len) 
ft_t ft;
LONG *buf, len;
{
	vs_t v = (vs_t) ft->priv;
	unsigned char uc;
	int sw;
	
	if (v->samples == 0) {
	  /* No silence packing yet. */
	  v->silent = 0;
	  blockstart(ft);
	}
	v->samples += len;
	while(len--) {
	  if (ft->info.size == BYTE) {
	    uc = RIGHT(*buf++, 24);
	    uc ^= 0x80;
	    putc(uc, ft->fp);
	  } else {
		sw = (int) RIGHT(*buf++, 16);
	    wshort(ft,sw);
          }
	}
}

void vocstopwrite(ft) 
ft_t ft;
{
	blockstop(ft);
}

/* Voc-file handlers */

/* Read next block header, save info, leave position at start of data */
void
getblock(ft)
ft_t ft;
{
	vs_t v = (vs_t) ft->priv;
	unsigned char uc, block;
	ULONG sblen;
	LONG new_rate;
	int i;

	v->silent = 0;
	while (v->rest == 0) {
		if (feof(ft->fp))
			return;
		block = getc(ft->fp);
		if (block == VOC_TERM)
			return;
		if (feof(ft->fp))
			return;
		/* 
		 * Size is an 24-bit value.  Currently there is no util 
		 * func to read this so do it this cross-platform way
		 *
		 */
		uc = getc(ft->fp);
		sblen = uc;
		uc = getc(ft->fp);
		sblen |= ((LONG) uc) << 8;
		uc = getc(ft->fp);
		sblen |= ((LONG) uc) << 16;
		switch(block) {
		case VOC_DATA: 
		        uc = getc(ft->fp);
			/* When DATA block preceeded by an EXTENDED     */
			/* block, the DATA blocks rate value is invalid */
		        if (!v->extended) {
			  if (uc == 0)
			    fail("File %s: Sample rate is zero?");
			  if ((v->rate != -1) && (uc != v->rate))
			    fail("File %s: sample rate codes differ: %d != %d",
				 ft->filename,v->rate, uc);
			  v->rate = uc;
			  ft->info.rate = 1000000.0/(256 - v->rate);
			  v->channels = 1;
			}
			uc = getc(ft->fp);
			if (uc != 0)
			  fail("File %s: only interpret 8-bit data!",
			       ft->filename);
			v->extended = 0;
			v->rest = sblen - 2;
			v->size = BYTE;
			return;
		case VOC_DATA_16:
			new_rate = rlong(ft);
			if (new_rate == 0)
			    fail("File %s: Sample rate is zero?",ft->filename);
			if ((v->rate != -1) && (new_rate != v->rate))
			    fail("File %s: sample rate codes differ: %d != %d",
				ft->filename, v->rate, new_rate);
			v->rate = new_rate;
			ft->info.rate = new_rate;
			uc = getc(ft->fp);
			switch (uc)
			{
			    case 8:	v->size = BYTE; break;
			    case 16:	v->size = WORD; break;
			    default:	fail("Don't understand size %d", uc);
			}
			v->channels = getc(ft->fp);
			getc(ft->fp);	/* unknown */
			getc(ft->fp);	/* notused */
			getc(ft->fp);	/* notused */
			getc(ft->fp);	/* notused */
			getc(ft->fp);	/* notused */
			getc(ft->fp);	/* notused */
			v->rest = sblen - 12;
			return;
		case VOC_CONT: 
			v->rest = sblen;
			return;
		case VOC_SILENCE: 
			{
			unsigned short period;

			period = rshort(ft);
			uc = getc(ft->fp);
			if (uc == 0)
				fail("File %s: Silence sample rate is zero");
			/* 
			 * 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;
			}
		case VOC_MARKER:
			uc = getc(ft->fp);
			uc = getc(ft->fp);
			/* Falling! Falling! */
		case VOC_TEXT:
			{
			int i;
			/* Could add to comment in SF? */
			for(i = 0; i < sblen; i++)
				getc(ft->fp);
			}
			continue;	/* get next block */
		case VOC_LOOP:
		case VOC_LOOPEND:
			report("File %s: skipping repeat loop");
			for(i = 0; i < sblen; i++)
				getc(ft->fp);
			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;
			new_rate = rshort(ft);
			if (new_rate == 0)
			   fail("File %s: Sample rate is zero?");
			if ((v->rate != -1) && (new_rate != v->rate))
			   fail("File %s: sample rate codes differ: %d != %d",
					ft->filename, v->rate, new_rate);
			v->rate = new_rate;
			uc = getc(ft->fp);
			if (uc != 0)
				fail("File %s: only interpret 8-bit data!",
					ft->filename);
			uc = getc(ft->fp);
			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:
			report("File %s: skipping unknown block code %d",
				ft->filename, block);
			for(i = 0; i < sblen; i++)
				getc(ft->fp);
		}
	}
}

/* Start an output block. */
void blockstart(ft)
ft_t ft;
{
	vs_t v = (vs_t) ft->priv;

	v->blockseek = ftell(ft->fp);
	if (v->silent) {
		putc(VOC_SILENCE, ft->fp);	/* Silence block code */
		putc(0, ft->fp);		/* Period length */
		putc(0, ft->fp);		/* Period length */
		putc((int) v->rate, ft->fp);		/* Rate code */
	} else {
	  if (ft->info.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) {
	      putc(VOC_EXTENDED, ft->fp);	/* Voice Extended block code */
	      putc(4, ft->fp);                /* block length = 4 */
	      putc(0, ft->fp);                /* block length = 4 */
	      putc(0, ft->fp);                /* block length = 4 */
		  v->rate = 65536L - (256000000.0/(2*(float)ft->info.rate));
	      wshort(ft,v->rate);	/* Rate code */
	      putc(0, ft->fp);                /* File is not packed */
	      putc(1, ft->fp);                /* samples are in stereo */
	    }
	    putc(VOC_DATA, ft->fp);		/* Voice Data block code */
	    putc(0, ft->fp);		/* block length (for now) */
	    putc(0, ft->fp);		/* block length (for now) */
	    putc(0, ft->fp);		/* block length (for now) */
	    v->rate = 256 - (1000000.0/(float)ft->info.rate);
	    putc((int) v->rate, ft->fp);/* Rate code */
	    putc(0, ft->fp);		/* 8-bit raw data */
	} else {
	    putc(VOC_DATA_16, ft->fp);		/* Voice Data block code */
	    putc(0, ft->fp);		/* block length (for now) */
	    putc(0, ft->fp);		/* block length (for now) */
	    putc(0, ft->fp);		/* block length (for now) */
	    v->rate = ft->info.rate;
	    wlong(ft, v->rate);	/* Rate code */
	    putc(16, ft->fp);		/* Sample Size */
	    putc(ft->info.channels, ft->fp);	/* Sample Size */
	    putc(0, ft->fp);		/* Unknown */
	    putc(0, ft->fp);		/* Unused */
	    putc(0, ft->fp);		/* Unused */
	    putc(0, ft->fp);		/* Unused */
	    putc(0, ft->fp);		/* Unused */
	    putc(0, ft->fp);		/* Unused */
	  }
	}
}

/* End the current data or silence block. */
void blockstop(ft) 
ft_t ft;
{
	vs_t v = (vs_t) ft->priv;
	LONG datum;

	putc(0, ft->fp);			/* 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) {
		wshort(ft, v->samples);
	} else {
	  if (ft->info.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;
		putc((int)datum, ft->fp);       /* low byte of length */
		datum = (v->samples >> 8) & 0xff;
		putc((int)datum, ft->fp);  /* middle byte of length */
		datum = (v->samples >> 16) & 0xff;
		putc((int)datum, ft->fp); /* high byte of length */
	}
}