1 /* libSoX SampleVision file format handler.
2  * Output is always in little-endian (80x86/VAX) order.
3  *
4  * Derived from: libSoX skeleton handler file.
5  *
6  * Add: Loop point verbose info.  It's a start, anyway.
7  */
8 
9 /*
10  * June 30, 1992
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.
16  */
17 
18 #include "sox_i.h"
19 #include <string.h>
20 #include <errno.h>
21 
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 */
26 
27 /* The header preceeding the sample data */
28 struct smpheader {
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 */
33 };
34 #define HEADERSIZE (sizeof(struct smpheader) - 1)       /* -1 for name's \0 */
35 
36 /* Samplevision loop definition structure */
37 struct loop {
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 */
42 };
43 
44 /* Samplevision marker definition structure */
45 struct marker {
46         char name[MARKERLEN + 1]; /* Ascii Marker name */
47         uint32_t position;        /* Sample Number, not byte number */
48 };
49 
50 /* The trailer following the sample data */
51 struct smptrailer {
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 */
59 };
60 
61 /* Private data for SMP file */
62 typedef struct {
63   uint64_t NoOfSamps;           /* Sample data count in words */
64   uint64_t dataStart;
65   /* comment memory resides in private data because it's small */
66   char comment[COMMENTLEN + NAMELEN + 3];
67 } priv_t;
68 
69 static char const *SVmagic = "SOUND SAMPLE DATA ", *SVvers = "2.1 ";
70 
71 /*
72  * Read the SampleVision trailer structure.
73  * Returns 1 if everything was read ok, 0 if there was an error.
74  */
readtrailer(sox_format_t * ft,struct smptrailer * trailer)75 static int readtrailer(sox_format_t * ft, struct smptrailer *trailer)
76 {
77         int i;
78         uint16_t trash16;
79 
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;
91         }
92         for(i = 0; i < 8; i++) {        /* read the 8 markers */
93                 if (lsx_readbuf(ft, trailer->markers[i].name, MARKERLEN) != MARKERLEN)
94                 {
95                     lsx_fail_errno(ft,SOX_EHDR,"EOF in SMP");
96                     return(SOX_EOF);
97                 }
98                 trailer->markers[i].name[MARKERLEN] = 0;
99                 lsx_readdw(ft, &(trailer->markers[i].position));
100         }
101         lsx_readsb(ft, &(trailer->MIDInote));
102         lsx_readdw(ft, &(trailer->rate));
103         lsx_readdw(ft, &(trailer->SMPTEoffset));
104         lsx_readdw(ft, &(trailer->CycleSize));
105         return(SOX_SUCCESS);
106 }
107 
108 /*
109  * set the trailer data - loops and markers, to reasonably benign values
110  */
settrailer(sox_format_t * ft,struct smptrailer * trailer,sox_rate_t rate)111 static void settrailer(sox_format_t * ft, struct smptrailer *trailer, sox_rate_t rate)
112 {
113         int i;
114 
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
118                     ? 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
122                     ? 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;
126             } else {
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;
133             }
134         }
135         for(i = 0; i < 8; i++) {        /* write the 8 markers */
136                 strcpy(trailer->markers[i].name, "          ");
137                 trailer->markers[i].position = ~0u;
138         }
139         trailer->MIDInote = MIDI_UNITY;         /* Unity play back */
140         trailer->rate = rate;
141         trailer->SMPTEoffset = 0;
142         trailer->CycleSize = ~0u;
143 }
144 
145 /*
146  * Write the SampleVision trailer structure.
147  * Returns 1 if everything was written ok, 0 if there was an error.
148  */
writetrailer(sox_format_t * ft,struct smptrailer * trailer)149 static int writetrailer(sox_format_t * ft, struct smptrailer *trailer)
150 {
151         int i;
152 
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);
159         }
160         for(i = 0; i < 8; i++) {        /* write the 8 markers */
161                 if (lsx_writes(ft, trailer->markers[i].name) == SOX_EOF)
162                 {
163                     lsx_fail_errno(ft,SOX_EHDR,"EOF in SMP");
164                     return(SOX_EOF);
165                 }
166                 lsx_writedw(ft, trailer->markers[i].position);
167         }
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);
172         return(SOX_SUCCESS);
173 }
174 
sox_smpseek(sox_format_t * ft,uint64_t offset)175 static int sox_smpseek(sox_format_t * ft, uint64_t offset)
176 {
177     uint64_t new_offset;
178     size_t channel_block, alignment;
179     priv_t * smp = (priv_t *) ft->priv;
180 
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.
188      */
189     if (alignment != 0)
190         new_offset += (channel_block - alignment);
191     new_offset += smp->dataStart;
192 
193     ft->sox_errno = lsx_seeki(ft, (off_t)new_offset, SEEK_SET);
194 
195     if( ft->sox_errno == SOX_SUCCESS )
196         smp->NoOfSamps = ft->signal.length - (new_offset / (ft->encoding.bits_per_sample >> 3));
197 
198     return(ft->sox_errno);
199 }
200 /*
201  * Do anything required before you start reading samples.
202  * Read file header.
203  *      Find out sampling rate,
204  *      size and encoding of samples,
205  *      mono/stereo/quad.
206  */
sox_smpstartread(sox_format_t * ft)207 static int sox_smpstartread(sox_format_t * ft)
208 {
209         priv_t * smp = (priv_t *) ft->priv;
210         int namelen, commentlen;
211         off_t samplestart;
212         size_t i;
213         unsigned dw;
214         struct smpheader header;
215         struct smptrailer trailer;
216 
217         /* If you need to seek around the input file. */
218         if (! ft->seekable)
219         {
220                 lsx_fail_errno(ft,SOX_EOF,"SMP input file must be a file, not a pipe");
221                 return(SOX_EOF);
222         }
223 
224         /* Read SampleVision header */
225         if (lsx_readbuf(ft, &header, HEADERSIZE) != HEADERSIZE)
226         {
227                 lsx_fail_errno(ft,SOX_EHDR,"unexpected EOF in SMP header");
228                 return(SOX_EOF);
229         }
230         if (strncmp(header.Id, SVmagic, (size_t)17) != 0)
231         {
232                 lsx_fail_errno(ft,SOX_EHDR,"SMP header does not begin with magic word %s", SVmagic);
233                 return(SOX_EOF);
234         }
235         if (strncmp(header.version, SVvers, (size_t)4) != 0)
236         {
237                 lsx_fail_errno(ft,SOX_EHDR,"SMP header is not version %s", SVvers);
238                 return(SOX_EOF);
239         }
240 
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--)
246           ;
247         for (commentlen = COMMENTLEN-1;
248             commentlen >= 0 && header.comments[commentlen] == ' '; commentlen--)
249           ;
250         sprintf(smp->comment, "%.*s: %.*s", namelen+1, header.name,
251                 commentlen+1, header.comments);
252         sox_append_comments(&ft->oob.comments, smp->comment);
253 
254         /* Extract out the sample size (always intel format) */
255         lsx_readdw(ft, &dw);
256         smp->NoOfSamps = dw;
257         /* mark the start of the sample data */
258         samplestart = lsx_tell(ft);
259 
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)
263         {
264                 lsx_fail_errno(ft,errno,"SMP unable to seek to trailer");
265                 return(SOX_EOF);
266         }
267         if (readtrailer(ft, &trailer))
268         {
269                 lsx_fail_errno(ft,SOX_EHDR,"unexpected EOF in SMP trailer");
270                 return(SOX_EOF);
271         }
272 
273         /* seek back to the beginning of the data */
274         if (lsx_seeki(ft, (off_t)samplestart, 0) == -1)
275         {
276                 lsx_fail_errno(ft,errno,"SMP unable to seek back to start of sample data");
277                 return(SOX_EOF);
278         }
279 
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;
286 
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;
296                 }
297         }
298         lsx_report("MIDI Note number: %d", trailer.MIDInote);
299 
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;
310         }
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;
315         else
316                 ft->oob.instr.loopmode = SOX_LOOP_NONE;
317 
318         return(SOX_SUCCESS);
319 }
320 
321 /*
322  * Read up to len samples from file.
323  * Convert to signed longs.
324  * Place in buf[].
325  * Return number of samples read.
326  */
sox_smpread(sox_format_t * ft,sox_sample_t * buf,size_t len)327 static size_t sox_smpread(sox_format_t * ft, sox_sample_t *buf, size_t len)
328 {
329         priv_t * smp = (priv_t *) ft->priv;
330         unsigned short datum;
331         size_t done = 0;
332 
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,);
337         }
338         return done;
339 }
340 
sox_smpstartwrite(sox_format_t * ft)341 static int sox_smpstartwrite(sox_format_t * ft)
342 {
343         priv_t * smp = (priv_t *) ft->priv;
344         struct smpheader header;
345         char * comment = lsx_cat_comments(ft->oob.comments);
346 
347         /* If you have to seek around the output file */
348         if (! ft->seekable)
349         {
350                 lsx_fail_errno(ft,SOX_EOF,"Output .smp file must be a file, not a pipe");
351                 return(SOX_EOF);
352         }
353 
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);
358         free(comment);
359 
360         /* Write file header */
361         if(lsx_writebuf(ft, &header, HEADERSIZE) != HEADERSIZE)
362         {
363             lsx_fail_errno(ft,errno,"SMP: Can't write header completely");
364             return(SOX_EOF);
365         }
366         lsx_writedw(ft, 0);      /* write as zero length for now, update later */
367         smp->NoOfSamps = 0;
368 
369         return(SOX_SUCCESS);
370 }
371 
sox_smpwrite(sox_format_t * ft,const sox_sample_t * buf,size_t len)372 static size_t sox_smpwrite(sox_format_t * ft, const sox_sample_t *buf, size_t len)
373 {
374         priv_t * smp = (priv_t *) ft->priv;
375         int datum;
376         size_t done = 0;
377 
378         while(done < len) {
379                 SOX_SAMPLE_LOCALS;
380                 datum = (int) SOX_SAMPLE_TO_SIGNED_16BIT(*buf++, ft->clips);
381                 lsx_writew(ft, (uint16_t)datum);
382                 smp->NoOfSamps++;
383                 done++;
384         }
385 
386         return(done);
387 }
388 
sox_smpstopwrite(sox_format_t * ft)389 static int sox_smpstopwrite(sox_format_t * ft)
390 {
391         priv_t * smp = (priv_t *) ft->priv;
392         struct smptrailer trailer;
393 
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)
398         {
399                 lsx_fail_errno(ft,errno,"SMP unable to seek back to save size");
400                 return(SOX_EOF);
401         }
402         lsx_writedw(ft, smp->NoOfSamps > UINT_MAX ? UINT_MAX : (unsigned)smp->NoOfSamps);
403 
404         return(SOX_SUCCESS);
405 }
406 
LSX_FORMAT_HANDLER(smp)407 LSX_FORMAT_HANDLER(smp)
408 {
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)
416   };
417   return &handler;
418 }
419