1 /* Amiga 8SVX format handler: W V Neisius, February 1992 */
2 
3 #include "sox_i.h"
4 
5 #include <errno.h>
6 #include <string.h>
7 #include <stdlib.h>
8 #include <stdio.h>
9 
10 #define BUFLEN 512
11 
12 /* Private data used by writer */
13 typedef struct{
14   uint32_t nsamples;
15   uint32_t left;
16   off_t ch0_pos;
17   sox_uint8_t buf[4][BUFLEN];
18   FILE* tmp[4];
19 } priv_t;
20 
21 static void svxwriteheader(sox_format_t *, size_t);
22 
23 /*======================================================================*/
24 /*                         8SVXSTARTREAD                                */
25 /*======================================================================*/
26 
startread(sox_format_t * ft)27 static int startread(sox_format_t * ft)
28 {
29         priv_t * p = (priv_t * ) ft->priv;
30 
31         char buf[12];
32         char *chunk_buf;
33 
34         uint32_t totalsize;
35         uint32_t chunksize;
36 
37         uint32_t channels;
38         unsigned short rate;
39 
40         if (! ft->seekable)
41         {
42                 lsx_fail_errno(ft,SOX_EINVAL,"8svx input file must be a file, not a pipe");
43                 return (SOX_EOF);
44         }
45         rate = 0;
46         channels = 1;
47 
48         /* read FORM chunk */
49         if (lsx_reads(ft, buf, (size_t)4) == SOX_EOF || strncmp(buf, "FORM", (size_t)4) != 0)
50         {
51                 lsx_fail_errno(ft, SOX_EHDR, "Header did not begin with magic word `FORM'");
52                 return(SOX_EOF);
53         }
54         lsx_readdw(ft, &totalsize);
55         if (lsx_reads(ft, buf, (size_t)4) == SOX_EOF || strncmp(buf, "8SVX", (size_t)4) != 0)
56         {
57                 lsx_fail_errno(ft, SOX_EHDR, "'FORM' chunk does not specify `8SVX' as type");
58                 return(SOX_EOF);
59         }
60 
61         /* read chunks until 'BODY' (or end) */
62         while (lsx_reads(ft, buf, (size_t)4) == SOX_SUCCESS && strncmp(buf,"BODY",(size_t)4) != 0) {
63                 if (strncmp(buf,"VHDR",(size_t)4) == 0) {
64                         lsx_readdw(ft, &chunksize);
65                         if (chunksize != 20)
66                         {
67                                 lsx_fail_errno(ft, SOX_EHDR, "VHDR chunk has bad size");
68                                 return(SOX_EOF);
69                         }
70                         lsx_seeki(ft,(off_t)12,SEEK_CUR);
71                         lsx_readw(ft, &rate);
72                         lsx_seeki(ft,(off_t)1,SEEK_CUR);
73                         lsx_readbuf(ft, buf,(size_t)1);
74                         if (buf[0] != 0)
75                         {
76                                 lsx_fail_errno(ft, SOX_EFMT, "Unsupported data compression");
77                                 return(SOX_EOF);
78                         }
79                         lsx_seeki(ft,(off_t)4,SEEK_CUR);
80                         continue;
81                 }
82 
83                 if (strncmp(buf,"ANNO",(size_t)4) == 0) {
84                         lsx_readdw(ft, &chunksize);
85                         if (chunksize & 1)
86                                 chunksize++;
87                         chunk_buf = lsx_malloc(chunksize + (size_t)2);
88                         if (lsx_readbuf(ft, chunk_buf,(size_t)chunksize)
89                                         != chunksize)
90                         {
91                                 lsx_fail_errno(ft, SOX_EHDR, "Couldn't read all of header");
92                                 return(SOX_EOF);
93                         }
94                         chunk_buf[chunksize] = '\0';
95                         lsx_debug("%s",chunk_buf);
96                         free(chunk_buf);
97 
98                         continue;
99                 }
100 
101                 if (strncmp(buf,"NAME",(size_t)4) == 0) {
102                         lsx_readdw(ft, &chunksize);
103                         if (chunksize & 1)
104                                 chunksize++;
105                         chunk_buf = lsx_malloc(chunksize + (size_t)1);
106                         if (lsx_readbuf(ft, chunk_buf,(size_t)chunksize)
107                                         != chunksize)
108                         {
109                                 lsx_fail_errno(ft, SOX_EHDR, "Couldn't read all of header");
110                                 return(SOX_EOF);
111                         }
112                         chunk_buf[chunksize] = '\0';
113                         lsx_debug("%s",chunk_buf);
114                         free(chunk_buf);
115 
116                         continue;
117                 }
118 
119                 if (strncmp(buf,"CHAN",(size_t)4) == 0) {
120                         lsx_readdw(ft, &chunksize);
121                         if (chunksize != 4)
122                         {
123                                 lsx_fail_errno(ft, SOX_EHDR, "Couldn't read all of header");
124                                 return(SOX_EOF);
125                         }
126                         lsx_readdw(ft, &channels);
127                         channels = (channels & 0x01) +
128                                         ((channels & 0x02) >> 1) +
129                                         ((channels & 0x04) >> 2) +
130                                         ((channels & 0x08) >> 3);
131 
132                         continue;
133                 }
134 
135                 /* some other kind of chunk */
136                 lsx_readdw(ft, &chunksize);
137                 if (chunksize & 1)
138                         chunksize++;
139                 lsx_seeki(ft,(off_t)chunksize,SEEK_CUR);
140                 continue;
141 
142         }
143 
144         if (rate == 0)
145         {
146                 lsx_fail_errno(ft, SOX_EHDR, "Invalid sample rate");
147                 return(SOX_EOF);
148         }
149         if (strncmp(buf,"BODY",(size_t)4) != 0)
150         {
151                 lsx_fail_errno(ft, SOX_EHDR, "BODY chunk not found");
152                 return(SOX_EOF);
153         }
154         lsx_readdw(ft, &(p->nsamples));
155         p->left = p->nsamples;
156         p->ch0_pos = lsx_tell(ft);
157 
158         ft->signal.length = p->nsamples;
159         ft->signal.channels = channels;
160         ft->signal.rate = rate;
161         ft->encoding.encoding = SOX_ENCODING_SIGN2;
162         ft->encoding.bits_per_sample = 8;
163 
164         return(SOX_SUCCESS);
165 }
166 
167 /*======================================================================*/
168 /*                         8SVXREAD                                     */
169 /*======================================================================*/
read_samples(sox_format_t * ft,sox_sample_t * buf,size_t nsamp)170 static size_t read_samples(sox_format_t * ft, sox_sample_t *buf, size_t nsamp)
171 {
172     size_t done = 0;
173 
174     priv_t * p = (priv_t * ) ft->priv;
175     size_t frames = nsamp / ft->signal.channels;
176     unsigned width = p->nsamples / ft->signal.channels;
177 
178     if (p->left < frames)
179         frames = p->left;
180 
181     while (done != frames) {
182         size_t chunk = frames - done;
183         size_t i;
184         unsigned ch;
185 
186         if (chunk > BUFLEN)
187             chunk = BUFLEN;
188 
189         for (ch = 0; ch != ft->signal.channels; ch++) {
190             if (lsx_seeki(ft, p->ch0_pos + ch * width, SEEK_SET) ||
191                 chunk != lsx_readbuf(ft, p->buf[ch], chunk))
192                 return done * ft->signal.channels;
193         }
194 
195         for (i = 0; i != chunk; i++) {
196             for (ch = 0; ch != ft->signal.channels; ch++) {
197                 /* scale signed up to long's range */
198                 *buf++ = SOX_SIGNED_8BIT_TO_SAMPLE(p->buf[ch][i], dummy);
199             }
200         }
201 
202         done += chunk;
203         p->left -= chunk * ft->signal.channels;
204         p->ch0_pos += chunk;
205     }
206     return done * ft->signal.channels;
207 }
208 
209 /*======================================================================*/
210 /*                         8SVXSTARTWRITE                               */
211 /*======================================================================*/
startwrite(sox_format_t * ft)212 static int startwrite(sox_format_t * ft)
213 {
214         priv_t * p = (priv_t * ) ft->priv;
215         size_t i;
216 
217         /* open channel output files */
218         for (i = 0; i < ft->signal.channels; i++) {
219                 if ((p->tmp[i] = lsx_tmpfile()) == NULL)
220                 {
221                         lsx_fail_errno(ft,errno,"Can't open channel output file");
222                         return(SOX_EOF);
223                 }
224         }
225 
226         p->nsamples = 0;
227         return(SOX_SUCCESS);
228 }
229 
230 /*======================================================================*/
231 /*                         8SVXWRITE                                    */
232 /*======================================================================*/
233 
write_samples(sox_format_t * ft,const sox_sample_t * buf,size_t len)234 static size_t write_samples(sox_format_t * ft, const sox_sample_t *buf, size_t len)
235 {
236         priv_t * p = (priv_t * ) ft->priv;
237         SOX_SAMPLE_LOCALS;
238 
239         unsigned char datum;
240         size_t done = 0, i;
241 
242         p->nsamples += len;
243 
244         while(done < len) {
245                 for (i = 0; i < ft->signal.channels; i++) {
246                         datum = SOX_SAMPLE_TO_SIGNED_8BIT(*buf++, ft->clips);
247                         putc(datum, p->tmp[i]);
248                 }
249                 done += ft->signal.channels;
250         }
251         return (done);
252 }
253 
254 /*======================================================================*/
255 /*                         8SVXSTOPWRITE                                */
256 /*======================================================================*/
257 
stopwrite(sox_format_t * ft)258 static int stopwrite(sox_format_t * ft)
259 {
260         priv_t * p = (priv_t * ) ft->priv;
261 
262         size_t i, len;
263         char svxbuf[512];
264 
265         svxwriteheader(ft, (size_t) p->nsamples);
266 
267         /* append all channel pieces to channel 0 */
268         /* close temp files */
269         for (i = 0; i < ft->signal.channels; i++) {
270                 if (fseeko(p->tmp[i], (off_t)0, 0))
271                 {
272                         lsx_fail_errno (ft,errno,"Can't rewind channel output file %lu",(unsigned long)i);
273                         return(SOX_EOF);
274                 }
275                 while (!feof(p->tmp[i])) {
276                         len = fread(svxbuf, (size_t) 1, (size_t) 512, p->tmp[i]);
277                         if (lsx_writebuf(ft, svxbuf, len) != len) {
278                           lsx_fail_errno (ft,errno,"Can't write channel output file %lu",(unsigned long)i);
279                           return SOX_EOF;
280                         }
281                 }
282                 fclose (p->tmp[i]);
283         }
284 
285         /* add a pad byte if BODY size is odd */
286         if(p->nsamples % 2 != 0)
287             lsx_writeb(ft, '\0');
288 
289         return(SOX_SUCCESS);
290 }
291 
292 /*======================================================================*/
293 /*                         8SVXWRITEHEADER                              */
294 /*======================================================================*/
295 #define SVXHEADERSIZE 100
svxwriteheader(sox_format_t * ft,size_t nsamples)296 static void svxwriteheader(sox_format_t * ft, size_t nsamples)
297 {
298         size_t formsize =  nsamples + SVXHEADERSIZE - 8;
299 
300         /* FORM size must be even */
301         if(formsize % 2 != 0) formsize++;
302 
303         lsx_writes(ft, "FORM");
304         lsx_writedw(ft, (unsigned) formsize);  /* size of file */
305         lsx_writes(ft, "8SVX"); /* File type */
306 
307         lsx_writes(ft, "VHDR");
308         lsx_writedw(ft, 20); /* number of bytes to follow */
309         lsx_writedw(ft, (unsigned) nsamples/ft->signal.channels);  /* samples, 1-shot */
310         lsx_writedw(ft, 0);  /* samples, repeat */
311         lsx_writedw(ft, 0);  /* samples per repeat cycle */
312         lsx_writew(ft, min(65535, (unsigned)(ft->signal.rate + .5)));
313         lsx_writeb(ft,1); /* number of octabes */
314         lsx_writeb(ft,0); /* data compression (none) */
315         lsx_writew(ft,1); lsx_writew(ft,0); /* volume */
316 
317         lsx_writes(ft, "ANNO");
318         lsx_writedw(ft, 32); /* length of block */
319         lsx_writes(ft, "File created by Sound Exchange  ");
320 
321         lsx_writes(ft, "CHAN");
322         lsx_writedw(ft, 4);
323         lsx_writedw(ft, (ft->signal.channels == 2) ? 6u :
324                    (ft->signal.channels == 4) ? 15u : 2u);
325 
326         lsx_writes(ft, "BODY");
327         lsx_writedw(ft, (unsigned) nsamples); /* samples in file */
328 }
329 
LSX_FORMAT_HANDLER(svx)330 LSX_FORMAT_HANDLER(svx)
331 {
332   static char const * const names[] = {"8svx", NULL};
333   static unsigned const write_encodings[] = {SOX_ENCODING_SIGN2, 8, 0, 0};
334   static sox_format_handler_t const handler = {SOX_LIB_VERSION_CODE,
335     "Amiga audio format (a subformat of the Interchange File Format)",
336     names, SOX_FILE_BIG_END|SOX_FILE_MONO|SOX_FILE_STEREO|SOX_FILE_QUAD,
337     startread, read_samples, NULL,
338     startwrite, write_samples, stopwrite,
339     NULL, write_encodings, NULL, sizeof(priv_t)
340   };
341   return &handler;
342 }
343