File: smp.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 (378 lines) | stat: -rw-r--r-- 10,763 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
/*
 * June 30, 1992
 * Copyright 1992 Leigh Smith And Sundry Contributors
 * This source code is freely redistributable and may be used for
 * any purpose.  This copyright notice must be maintained. 
 * Leigh Smith And Sundry Contributors are not responsible for 
 * the consequences of using this software.
 */

/*
 * Sound Tools SampleVision file format driver.
 * Output is always in little-endian (80x86/VAX) order.
 * 
 * Derived from: Sound Tools skeleton handler file.
 *
 * Add: Loop point verbose info.  It's a start, anyway.
 */

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

#define NAMELEN    30		/* Size of Samplevision name */
#define COMMENTLEN 60		/* Size of Samplevision comment, not shared */
#define MIDI_UNITY 60		/* MIDI note number to play sample at unity */

/* The header preceeding the sample data */
struct smpheader {
	char Id[18];		/* File identifier */
	char version[4];	/* File version */
	char comments[COMMENTLEN];	/* User comments */
	char name[NAMELEN + 1];	/* Sample Name, left justified */
};
#define HEADERSIZE (sizeof(struct smpheader) - 1)	/* -1 for name's \0 */

/* Samplevision loop definition structure */
struct loop {
	ULONG start; /* Sample count into sample data, not byte count */
	ULONG end;   /* end point */
	char type;	     /* 0 = loop off, 1 = forward, 2 = forw/back */
	short count;	     /* No of times to loop */
};

/* Samplevision marker definition structure */
struct marker {
	char name[10];		/* Ascii Marker name */
	ULONG position;	/* Sample Number, not byte number */
};

/* The trailer following the sample data */
struct smptrailer {
	struct loop loops[8];		/* loops */
	struct marker markers[8];	/* markers */
	char MIDInote;			/* for unity pitch playback */
	ULONG rate;			/* in hertz */
	ULONG SMPTEoffset;		/* in subframes - huh? */
	ULONG CycleSize;		/* sample count in one cycle of the */
					/* sampled sound -1 if unknown */
};

/* Private data for SMP file */
typedef struct smpstuff {
  ULONG NoOfSamps;		/* Sample data count in words */
  /* comment memory resides in private data because it's small */
  char comment[COMMENTLEN + NAMELEN + 3];
} *smp_t;

char *SVmagic = "SOUND SAMPLE DATA ", *SVvers = "2.1 ";

/*
 * Read the SampleVision trailer structure.
 * Returns 1 if everything was read ok, 0 if there was an error.
 */
static int readtrailer(ft, trailer)
ft_t ft;
struct smptrailer *trailer;
{
	int i;

	rshort(ft);			/* read reserved word */
	for(i = 0; i < 8; i++) {	/* read the 8 loops */
		trailer->loops[i].start = rlong(ft);
		ft->loops[i].start = trailer->loops[i].start;
		trailer->loops[i].end = rlong(ft);
		ft->loops[i].length = 
			trailer->loops[i].end - trailer->loops[i].start;
		trailer->loops[i].type = getc(ft->fp);
		ft->loops[i].type = trailer->loops[8].type;
		trailer->loops[i].count = rshort(ft);
		ft->loops[8].count = trailer->loops[8].count;
	}
	for(i = 0; i < 8; i++) {	/* read the 8 markers */
		if (fread(trailer->markers[i].name, 1, 10, ft->fp) != 10)
			return(0);
		trailer->markers[i].position = rlong(ft);
	}
	trailer->MIDInote = getc(ft->fp);
	trailer->rate = rlong(ft);
	trailer->SMPTEoffset = rlong(ft);
	trailer->CycleSize = rlong(ft);
	return(1);
}

/*
 * set the trailer data - loops and markers, to reasonably benign values
 */
void settrailer(ft, trailer, rate)
ft_t ft;
struct smptrailer *trailer;
unsigned int rate;
{
	int i;

	for(i = 0; i < 8; i++) {	/* copy the 8 loops */
	    if (ft->loops[i].type != 0) {
		trailer->loops[i].start = ft->loops[i].start;
		/* to mark it as not set */
		trailer->loops[i].end = ft->loops[i].start + ft->loops[i].length;
		trailer->loops[i].type = ft->loops[i].type;
		trailer->loops[i].count = ft->loops[i].count;	
	    } else {
		/* set first loop start as FFFFFFFF */
		trailer->loops[i].start = ~0;	
		/* to mark it as not set */
		trailer->loops[i].end = 0;	
		trailer->loops[i].type = 0;
		trailer->loops[i].count = 0;
	    }
	}
	for(i = 0; i < 8; i++) {	/* write the 8 markers */
		strcpy(trailer->markers[i].name, "          ");
		trailer->markers[i].position = ~0;
	}
	trailer->MIDInote = MIDI_UNITY;		/* Unity play back */
	trailer->rate = rate;
	trailer->SMPTEoffset = 0;
	trailer->CycleSize = -1;
}

/*
 * Write the SampleVision trailer structure.
 * Returns 1 if everything was written ok, 0 if there was an error.
 */
static int writetrailer(ft, trailer)
ft_t ft;
struct smptrailer *trailer;
{
	int i;

	wshort(ft, 0);			/* write the reserved word */
	for(i = 0; i < 8; i++) {	/* write the 8 loops */
		wlong(ft, trailer->loops[i].start);
		wlong(ft, trailer->loops[i].end);
		putc(trailer->loops[i].type, ft->fp);
		wshort(ft, trailer->loops[i].count);
	}
	for(i = 0; i < 8; i++) {	/* write the 8 markers */
		if (fwrite(trailer->markers[i].name, 1, 10, ft->fp) != 10)
			return(0);
		wlong(ft, trailer->markers[i].position);
	}
	putc(trailer->MIDInote, ft->fp);
	wlong(ft, trailer->rate);
	wlong(ft, trailer->SMPTEoffset);
	wlong(ft, trailer->CycleSize);
	return(1);
}

/*
 * Do anything required before you start reading samples.
 * Read file header. 
 *	Find out sampling rate, 
 *	size and style of samples, 
 *	mono/stereo/quad.
 */
void smpstartread(ft) 
ft_t ft;
{
	smp_t smp = (smp_t) ft->priv;
	int littlendian = 1;
	char *endptr;
	int i;
	int namelen, commentlen;
	LONG samplestart;
	struct smpheader header;
	struct smptrailer trailer;

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

	/* If you need to seek around the input file. */
	if (! ft->seekable)
		fail("SMP input file must be a file, not a pipe");

	/* Read SampleVision header */
	if (fread((char *) &header, 1, HEADERSIZE, ft->fp) != HEADERSIZE)
		fail("unexpected EOF in SMP header");
	if (strncmp(header.Id, SVmagic, 17) != 0)
		fail("SMP header does not begin with magic word %s\n", SVmagic);
	if (strncmp(header.version, SVvers, 4) != 0)
		fail("SMP header is not version %s\n", SVvers);

	/* Format the sample name and comments to a single comment */
	/* string. We decrement the counters till we encounter non */
        /* padding space chars, so the *lengths* are low by one */
        for (namelen = NAMELEN-1;
            namelen >= 0 && header.name[namelen] == ' '; namelen--)
	  ;
        for (commentlen = COMMENTLEN-1;
            commentlen >= 0 && header.comments[commentlen] == ' '; commentlen--)
	  ;
	sprintf(smp->comment, "%.*s: %.*s", namelen+1, header.name,
		commentlen+1, header.comments);
	ft->comment = smp->comment;

	report("SampleVision file name and comments: %s", ft->comment);
	/* Extract out the sample size (always intel format) */
	smp->NoOfSamps = rlong(ft);
	/* mark the start of the sample data */
	samplestart = ftell(ft->fp);

	/* seek from the current position (the start of sample data) by */
	/* NoOfSamps * 2 */
	if (fseek(ft->fp, smp->NoOfSamps * 2L, 1) == -1)
		fail("SMP unable to seek to trailer");
	if (!readtrailer(ft, &trailer))
		fail("unexpected EOF in SMP trailer");

	/* seek back to the beginning of the data */
	if (fseek(ft->fp, samplestart, 0) == -1) 
		fail("SMP unable to seek back to start of sample data");

	ft->info.rate = (int) trailer.rate;
	ft->info.size = WORD;
	ft->info.style = SIGN2;
	ft->info.channels = 1;

	if (verbose) {
		fprintf(stderr, "SampleVision trailer:\n");
		for(i = 0; i < 8; i++) if (1 || trailer.loops[i].count) {
#ifdef __alpha__
			fprintf(stderr, "Loop %d: start: %6d", i, trailer.loops[i].start);
			fprintf(stderr, " end:   %6d", trailer.loops[i].end);
#else
			fprintf(stderr, "Loop %d: start: %6ld", i, trailer.loops[i].start);
			fprintf(stderr, " end:   %6ld", trailer.loops[i].end);
#endif
			fprintf(stderr, " count: %6d", trailer.loops[i].count);
			fprintf(stderr, " type:  ");
			switch(trailer.loops[i].type) {
				case 0: fprintf(stderr, "off\n"); break;
				case 1: fprintf(stderr, "forward\n"); break;
				case 2: fprintf(stderr, "forward/backward\n"); break;
			}
		}
		fprintf(stderr, "MIDI Note number: %d\n\n", trailer.MIDInote);
	}
	ft->instr.nloops = 0;
	for(i = 0; i < 8; i++) 
		if (trailer.loops[i].type) 
			ft->instr.nloops++;
	for(i = 0; i < ft->instr.nloops; i++) {
		ft->loops[i].type = trailer.loops[i].type;
		ft->loops[i].count = trailer.loops[i].count;
		ft->loops[i].start = trailer.loops[i].start;
		ft->loops[i].length = trailer.loops[i].end 
			- trailer.loops[i].start;
	}
	ft->instr.MIDIlow = ft->instr.MIDIhi =
		ft->instr.MIDInote = trailer.MIDInote;
	if (ft->instr.nloops > 0)
		ft->instr.loopmode = LOOP_8;
	else
		ft->instr.loopmode = LOOP_NONE;
}

/*
 * Read up to len samples from file.
 * Convert to signed longs.
 * Place in buf[].
 * Return number of samples read.
 */
LONG smpread(ft, buf, len) 
ft_t ft;
LONG *buf, len;
{
	smp_t smp = (smp_t) ft->priv;
	LONG datum;
	int done = 0;
	
	for(; done < len && smp->NoOfSamps; done++, smp->NoOfSamps--) {
		datum = rshort(ft);
		/* scale signed up to long's range */
		*buf++ = LEFT(datum, 16);
	}
	return done;
}

/*
 * Do anything required when you stop reading samples.  
 * Don't close input file! 
 */
void smpstopread(ft) 
ft_t ft;
{
}

void smpstartwrite(ft) 
ft_t ft;
{
	int littlendian = 1;
	char *endptr;

	smp_t smp = (smp_t) ft->priv;
	struct smpheader header;

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

	/* If you have to seek around the output file */
	if (! ft->seekable)
		fail("Output .smp file must be a file, not a pipe");

	/* If your format specifies any of the following info. */
	ft->info.size = WORD;
	ft->info.style = SIGN2;
	ft->info.channels = 1;

	strcpy(header.Id, SVmagic);
	strcpy(header.version, SVvers);
	sprintf(header.comments, "%-*s", COMMENTLEN, "Converted using Sox.");
	sprintf(header.name, "%-*.*s", NAMELEN, NAMELEN, ft->comment);

	/* Write file header */
	if(fwrite(&header, 1, HEADERSIZE, ft->fp) != HEADERSIZE)
		fail("SMP: Can't write header completely");
	wlong(ft, 0);	/* write as zero length for now, update later */
	smp->NoOfSamps = 0;
}

void smpwrite(ft, buf, len) 
ft_t ft;
LONG *buf, len;
{
	smp_t smp = (smp_t) ft->priv;
	register int datum;

	while(len--) {
		datum = (int) RIGHT(*buf++, 16);
		wshort(ft, datum);
		smp->NoOfSamps++;
	}
	/* If you cannot write out all of the supplied samples, */
	/*	fail("SMP: Can't write all samples to %s", ft->filename); */
}

void smpstopwrite(ft) 
ft_t ft;
{
	smp_t smp = (smp_t) ft->priv;
	struct smptrailer trailer;

	/* Assign the trailer data */
	settrailer(ft, &trailer, ft->info.rate);
	writetrailer(ft, &trailer);
	if (fseek(ft->fp, 112, 0) == -1)
		fail("SMP unable to seek back to save size");
	wlong(ft, smp->NoOfSamps);
}