1 /* libSoX SampleVision file format handler.
2 * Output is always in little-endian (80x86/VAX) order.
4 * Derived from: libSoX skeleton handler file.
6 * Add: Loop point verbose info. It's a start, anyway.
11 * Copyright 1992 Leigh Smith And Sundry Contributors
12 * This source code is freely redistributable and may be used for
13 * any purpose. This copyright notice must be maintained.
14 * Leigh Smith And Sundry Contributors are not responsible for
15 * the consequences of using this software.
22 #define NAMELEN 30 /* Size of Samplevision name */
23 #define COMMENTLEN 60 /* Size of Samplevision comment, not shared */
24 #define MIDI_UNITY 60 /* MIDI note number to play sample at unity */
25 #define MARKERLEN (size_t)10 /* Size of Marker name */
27 /* The header preceeding the sample data */
29 char Id
[18]; /* File identifier */
30 char version
[4]; /* File version */
31 char comments
[COMMENTLEN
]; /* User comments */
32 char name
[NAMELEN
+ 1]; /* Sample Name, left justified */
34 #define HEADERSIZE (sizeof(struct smpheader) - 1) /* -1 for name's \0 */
36 /* Samplevision loop definition structure */
38 uint32_t start
; /* Sample count into sample data, not byte count */
39 uint32_t end
; /* end point */
40 unsigned char type
; /* 0 = loop off, 1 = forward, 2 = forw/back */
41 unsigned short count
; /* No of times to loop */
44 /* Samplevision marker definition structure */
46 char name
[MARKERLEN
+ 1]; /* Ascii Marker name */
47 uint32_t position
; /* Sample Number, not byte number */
50 /* The trailer following the sample data */
52 struct loop loops
[8]; /* loops */
53 struct marker markers
[8]; /* markers */
54 int8_t MIDInote
; /* for unity pitch playback */
55 uint32_t rate
; /* in hertz */
56 uint32_t SMPTEoffset
; /* in subframes - huh? */
57 uint32_t CycleSize
; /* sample count in one cycle of the */
58 /* sampled sound -1 if unknown */
61 /* Private data for SMP file */
63 uint64_t NoOfSamps
; /* Sample data count in words */
65 /* comment memory resides in private data because it's small */
66 char comment
[COMMENTLEN
+ NAMELEN
+ 3];
69 static char const *SVmagic
= "SOUND SAMPLE DATA ", *SVvers
= "2.1 ";
72 * Read the SampleVision trailer structure.
73 * Returns 1 if everything was read ok, 0 if there was an error.
75 static int readtrailer(sox_format_t
* ft
, struct smptrailer
*trailer
)
80 lsx_readw(ft
, &trash16
); /* read reserved word */
81 for(i
= 0; i
< 8; i
++) { /* read the 8 loops */
82 lsx_readdw(ft
, &(trailer
->loops
[i
].start
));
83 ft
->oob
.loops
[i
].start
= trailer
->loops
[i
].start
;
84 lsx_readdw(ft
, &(trailer
->loops
[i
].end
));
85 ft
->oob
.loops
[i
].length
=
86 trailer
->loops
[i
].end
- trailer
->loops
[i
].start
;
87 lsx_readb(ft
, &(trailer
->loops
[i
].type
));
88 ft
->oob
.loops
[i
].type
= trailer
->loops
[i
].type
;
89 lsx_readw(ft
, &(trailer
->loops
[i
].count
));
90 ft
->oob
.loops
[i
].count
= trailer
->loops
[i
].count
;
92 for(i
= 0; i
< 8; i
++) { /* read the 8 markers */
93 if (lsx_readbuf(ft
, trailer
->markers
[i
].name
, MARKERLEN
) != MARKERLEN
)
95 lsx_fail_errno(ft
,SOX_EHDR
,"EOF in SMP");
98 trailer
->markers
[i
].name
[MARKERLEN
] = 0;
99 lsx_readdw(ft
, &(trailer
->markers
[i
].position
));
101 lsx_readsb(ft
, &(trailer
->MIDInote
));
102 lsx_readdw(ft
, &(trailer
->rate
));
103 lsx_readdw(ft
, &(trailer
->SMPTEoffset
));
104 lsx_readdw(ft
, &(trailer
->CycleSize
));
109 * set the trailer data - loops and markers, to reasonably benign values
111 static void settrailer(sox_format_t
* ft
, struct smptrailer
*trailer
, sox_rate_t rate
)
115 for(i
= 0; i
< 8; i
++) { /* copy the 8 loops */
116 if (ft
->oob
.loops
[i
].type
!= 0) {
117 trailer
->loops
[i
].start
= ft
->oob
.loops
[i
].start
> UINT_MAX
119 : ft
->oob
.loops
[i
].start
;
120 /* to mark it as not set */
121 trailer
->loops
[i
].end
= ft
->oob
.loops
[i
].start
+ ft
->oob
.loops
[i
].length
> UINT_MAX
123 : ft
->oob
.loops
[i
].start
+ ft
->oob
.loops
[i
].length
;
124 trailer
->loops
[i
].type
= ft
->oob
.loops
[i
].type
;
125 trailer
->loops
[i
].count
= ft
->oob
.loops
[i
].count
;
127 /* set first loop start as FFFFFFFF */
128 trailer
->loops
[i
].start
= ~0u;
129 /* to mark it as not set */
130 trailer
->loops
[i
].end
= 0;
131 trailer
->loops
[i
].type
= 0;
132 trailer
->loops
[i
].count
= 0;
135 for(i
= 0; i
< 8; i
++) { /* write the 8 markers */
136 strcpy(trailer
->markers
[i
].name
, " ");
137 trailer
->markers
[i
].position
= ~0u;
139 trailer
->MIDInote
= MIDI_UNITY
; /* Unity play back */
140 trailer
->rate
= rate
;
141 trailer
->SMPTEoffset
= 0;
142 trailer
->CycleSize
= ~0u;
146 * Write the SampleVision trailer structure.
147 * Returns 1 if everything was written ok, 0 if there was an error.
149 static int writetrailer(sox_format_t
* ft
, struct smptrailer
*trailer
)
153 lsx_writew(ft
, 0); /* write the reserved word */
154 for(i
= 0; i
< 8; i
++) { /* write the 8 loops */
155 lsx_writedw(ft
, trailer
->loops
[i
].start
);
156 lsx_writedw(ft
, trailer
->loops
[i
].end
);
157 lsx_writeb(ft
, trailer
->loops
[i
].type
);
158 lsx_writew(ft
, trailer
->loops
[i
].count
);
160 for(i
= 0; i
< 8; i
++) { /* write the 8 markers */
161 if (lsx_writes(ft
, trailer
->markers
[i
].name
) == SOX_EOF
)
163 lsx_fail_errno(ft
,SOX_EHDR
,"EOF in SMP");
166 lsx_writedw(ft
, trailer
->markers
[i
].position
);
168 lsx_writeb(ft
, (uint8_t)(trailer
->MIDInote
));
169 lsx_writedw(ft
, trailer
->rate
);
170 lsx_writedw(ft
, trailer
->SMPTEoffset
);
171 lsx_writedw(ft
, trailer
->CycleSize
);
175 static int sox_smpseek(sox_format_t
* ft
, uint64_t offset
)
178 size_t channel_block
, alignment
;
179 priv_t
* smp
= (priv_t
*) ft
->priv
;
181 new_offset
= offset
* (ft
->encoding
.bits_per_sample
>> 3);
182 /* Make sure request aligns to a channel block (ie left+right) */
183 channel_block
= ft
->signal
.channels
* (ft
->encoding
.bits_per_sample
>> 3);
184 alignment
= new_offset
% channel_block
;
185 /* Most common mistaken is to compute something like
186 * "skip everthing upto and including this sample" so
187 * advance to next sample block in this case.
190 new_offset
+= (channel_block
- alignment
);
191 new_offset
+= smp
->dataStart
;
193 ft
->sox_errno
= lsx_seeki(ft
, (off_t
)new_offset
, SEEK_SET
);
195 if( ft
->sox_errno
== SOX_SUCCESS
)
196 smp
->NoOfSamps
= ft
->signal
.length
- (new_offset
/ (ft
->encoding
.bits_per_sample
>> 3));
198 return(ft
->sox_errno
);
201 * Do anything required before you start reading samples.
203 * Find out sampling rate,
204 * size and encoding of samples,
207 static int sox_smpstartread(sox_format_t
* ft
)
209 priv_t
* smp
= (priv_t
*) ft
->priv
;
210 int namelen
, commentlen
;
214 struct smpheader header
;
215 struct smptrailer trailer
;
217 /* If you need to seek around the input file. */
220 lsx_fail_errno(ft
,SOX_EOF
,"SMP input file must be a file, not a pipe");
224 /* Read SampleVision header */
225 if (lsx_readbuf(ft
, &header
, HEADERSIZE
) != HEADERSIZE
)
227 lsx_fail_errno(ft
,SOX_EHDR
,"unexpected EOF in SMP header");
230 if (strncmp(header
.Id
, SVmagic
, (size_t)17) != 0)
232 lsx_fail_errno(ft
,SOX_EHDR
,"SMP header does not begin with magic word %s", SVmagic
);
235 if (strncmp(header
.version
, SVvers
, (size_t)4) != 0)
237 lsx_fail_errno(ft
,SOX_EHDR
,"SMP header is not version %s", SVvers
);
241 /* Format the sample name and comments to a single comment */
242 /* string. We decrement the counters till we encounter non */
243 /* padding space chars, so the *lengths* are low by one */
244 for (namelen
= NAMELEN
-1;
245 namelen
>= 0 && header
.name
[namelen
] == ' '; namelen
--)
247 for (commentlen
= COMMENTLEN
-1;
248 commentlen
>= 0 && header
.comments
[commentlen
] == ' '; commentlen
--)
250 sprintf(smp
->comment
, "%.*s: %.*s", namelen
+1, header
.name
,
251 commentlen
+1, header
.comments
);
252 sox_append_comments(&ft
->oob
.comments
, smp
->comment
);
254 /* Extract out the sample size (always intel format) */
257 /* mark the start of the sample data */
258 samplestart
= lsx_tell(ft
);
260 /* seek from the current position (the start of sample data) by */
261 /* NoOfSamps * sizeof(int16_t) */
262 if (lsx_seeki(ft
, (off_t
)(smp
->NoOfSamps
* 2), 1) == -1)
264 lsx_fail_errno(ft
,errno
,"SMP unable to seek to trailer");
267 if (readtrailer(ft
, &trailer
))
269 lsx_fail_errno(ft
,SOX_EHDR
,"unexpected EOF in SMP trailer");
273 /* seek back to the beginning of the data */
274 if (lsx_seeki(ft
, (off_t
)samplestart
, 0) == -1)
276 lsx_fail_errno(ft
,errno
,"SMP unable to seek back to start of sample data");
280 ft
->signal
.rate
= (int) trailer
.rate
;
281 ft
->encoding
.bits_per_sample
= 16;
282 ft
->encoding
.encoding
= SOX_ENCODING_SIGN2
;
283 ft
->signal
.channels
= 1;
284 smp
->dataStart
= samplestart
;
285 ft
->signal
.length
= smp
->NoOfSamps
;
287 lsx_report("SampleVision trailer:");
288 for(i
= 0; i
< 8; i
++) if (1 || trailer
.loops
[i
].count
) {
289 lsx_report("Loop %lu: start: %6d", (unsigned long)i
, trailer
.loops
[i
].start
);
290 lsx_report(" end: %6d", trailer
.loops
[i
].end
);
291 lsx_report(" count: %6d", trailer
.loops
[i
].count
);
292 switch(trailer
.loops
[i
].type
) {
293 case 0: lsx_report("type: off"); break;
294 case 1: lsx_report("type: forward"); break;
295 case 2: lsx_report("type: forward/backward"); break;
298 lsx_report("MIDI Note number: %d", trailer
.MIDInote
);
300 ft
->oob
.instr
.nloops
= 0;
301 for(i
= 0; i
< 8; i
++)
302 if (trailer
.loops
[i
].type
)
303 ft
->oob
.instr
.nloops
++;
304 for(i
= 0; i
< ft
->oob
.instr
.nloops
; i
++) {
305 ft
->oob
.loops
[i
].type
= trailer
.loops
[i
].type
;
306 ft
->oob
.loops
[i
].count
= trailer
.loops
[i
].count
;
307 ft
->oob
.loops
[i
].start
= trailer
.loops
[i
].start
;
308 ft
->oob
.loops
[i
].length
= trailer
.loops
[i
].end
309 - trailer
.loops
[i
].start
;
311 ft
->oob
.instr
.MIDIlow
= ft
->oob
.instr
.MIDIhi
=
312 ft
->oob
.instr
.MIDInote
= trailer
.MIDInote
;
313 if (ft
->oob
.instr
.nloops
> 0)
314 ft
->oob
.instr
.loopmode
= SOX_LOOP_8
;
316 ft
->oob
.instr
.loopmode
= SOX_LOOP_NONE
;
322 * Read up to len samples from file.
323 * Convert to signed longs.
325 * Return number of samples read.
327 static size_t sox_smpread(sox_format_t
* ft
, sox_sample_t
*buf
, size_t len
)
329 priv_t
* smp
= (priv_t
*) ft
->priv
;
330 unsigned short datum
;
333 for(; done
< len
&& smp
->NoOfSamps
; done
++, smp
->NoOfSamps
--) {
334 lsx_readw(ft
, &datum
);
335 /* scale signed up to long's range */
336 *buf
++ = SOX_SIGNED_16BIT_TO_SAMPLE(datum
,);
341 static int sox_smpstartwrite(sox_format_t
* ft
)
343 priv_t
* smp
= (priv_t
*) ft
->priv
;
344 struct smpheader header
;
345 char * comment
= lsx_cat_comments(ft
->oob
.comments
);
347 /* If you have to seek around the output file */
350 lsx_fail_errno(ft
,SOX_EOF
,"Output .smp file must be a file, not a pipe");
354 memcpy(header
.Id
, SVmagic
, sizeof(header
.Id
));
355 memcpy(header
.version
, SVvers
, sizeof(header
.version
));
356 sprintf(header
.comments
, "%-*s", COMMENTLEN
- 1, "Converted using Sox.");
357 sprintf(header
.name
, "%-*.*s", NAMELEN
, NAMELEN
, comment
);
360 /* Write file header */
361 if(lsx_writebuf(ft
, &header
, HEADERSIZE
) != HEADERSIZE
)
363 lsx_fail_errno(ft
,errno
,"SMP: Can't write header completely");
366 lsx_writedw(ft
, 0); /* write as zero length for now, update later */
372 static size_t sox_smpwrite(sox_format_t
* ft
, const sox_sample_t
*buf
, size_t len
)
374 priv_t
* smp
= (priv_t
*) ft
->priv
;
380 datum
= (int) SOX_SAMPLE_TO_SIGNED_16BIT(*buf
++, ft
->clips
);
381 lsx_writew(ft
, (uint16_t)datum
);
389 static int sox_smpstopwrite(sox_format_t
* ft
)
391 priv_t
* smp
= (priv_t
*) ft
->priv
;
392 struct smptrailer trailer
;
394 /* Assign the trailer data */
395 settrailer(ft
, &trailer
, ft
->signal
.rate
);
396 writetrailer(ft
, &trailer
);
397 if (lsx_seeki(ft
, (off_t
)112, 0) == -1)
399 lsx_fail_errno(ft
,errno
,"SMP unable to seek back to save size");
402 lsx_writedw(ft
, smp
->NoOfSamps
> UINT_MAX
? UINT_MAX
: (unsigned)smp
->NoOfSamps
);
407 LSX_FORMAT_HANDLER(smp
)
409 static char const * const names
[] = {"smp", NULL
};
410 static unsigned const write_encodings
[] = {SOX_ENCODING_SIGN2
, 16, 0, 0};
411 static sox_format_handler_t handler
= {SOX_LIB_VERSION_CODE
,
412 "Turtle Beach SampleVision", names
, SOX_FILE_LIT_END
| SOX_FILE_MONO
,
413 sox_smpstartread
, sox_smpread
, NULL
,
414 sox_smpstartwrite
, sox_smpwrite
, sox_smpstopwrite
,
415 sox_smpseek
, write_encodings
, NULL
, sizeof(priv_t
)