rate: add some sanity checking
[sox.git] / src / 8svx.c
blob3b7af8c960a82ca7293c865e4a5700a3d3418db3
1 /* Amiga 8SVX format handler: W V Neisius, February 1992 */
3 #include "sox_i.h"
5 #include <errno.h>
6 #include <string.h>
7 #include <stdlib.h>
8 #include <stdio.h>
10 #define BUFLEN 512
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;
21 static void svxwriteheader(sox_format_t *, size_t);
23 /*======================================================================*/
24 /* 8SVXSTARTREAD */
25 /*======================================================================*/
27 static int startread(sox_format_t * ft)
29 priv_t * p = (priv_t * ) ft->priv;
31 char buf[12];
32 char *chunk_buf;
34 uint32_t totalsize;
35 uint32_t chunksize;
37 uint32_t channels;
38 unsigned short rate;
40 if (! ft->seekable)
42 lsx_fail_errno(ft,SOX_EINVAL,"8svx input file must be a file, not a pipe");
43 return (SOX_EOF);
45 rate = 0;
46 channels = 1;
48 /* read FORM chunk */
49 if (lsx_reads(ft, buf, (size_t)4) == SOX_EOF || strncmp(buf, "FORM", (size_t)4) != 0)
51 lsx_fail_errno(ft, SOX_EHDR, "Header did not begin with magic word `FORM'");
52 return(SOX_EOF);
54 lsx_readdw(ft, &totalsize);
55 if (lsx_reads(ft, buf, (size_t)4) == SOX_EOF || strncmp(buf, "8SVX", (size_t)4) != 0)
57 lsx_fail_errno(ft, SOX_EHDR, "'FORM' chunk does not specify `8SVX' as type");
58 return(SOX_EOF);
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)
67 lsx_fail_errno(ft, SOX_EHDR, "VHDR chunk has bad size");
68 return(SOX_EOF);
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)
76 lsx_fail_errno(ft, SOX_EFMT, "Unsupported data compression");
77 return(SOX_EOF);
79 lsx_seeki(ft,(off_t)4,SEEK_CUR);
80 continue;
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)
91 lsx_fail_errno(ft, SOX_EHDR, "Couldn't read all of header");
92 return(SOX_EOF);
94 chunk_buf[chunksize] = '\0';
95 lsx_debug("%s",chunk_buf);
96 free(chunk_buf);
98 continue;
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)
109 lsx_fail_errno(ft, SOX_EHDR, "Couldn't read all of header");
110 return(SOX_EOF);
112 chunk_buf[chunksize] = '\0';
113 lsx_debug("%s",chunk_buf);
114 free(chunk_buf);
116 continue;
119 if (strncmp(buf,"CHAN",(size_t)4) == 0) {
120 lsx_readdw(ft, &chunksize);
121 if (chunksize != 4)
123 lsx_fail_errno(ft, SOX_EHDR, "Couldn't read all of header");
124 return(SOX_EOF);
126 lsx_readdw(ft, &channels);
127 channels = (channels & 0x01) +
128 ((channels & 0x02) >> 1) +
129 ((channels & 0x04) >> 2) +
130 ((channels & 0x08) >> 3);
132 continue;
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;
144 if (rate == 0)
146 lsx_fail_errno(ft, SOX_EHDR, "Invalid sample rate");
147 return(SOX_EOF);
149 if (strncmp(buf,"BODY",(size_t)4) != 0)
151 lsx_fail_errno(ft, SOX_EHDR, "BODY chunk not found");
152 return(SOX_EOF);
154 lsx_readdw(ft, &(p->nsamples));
155 p->left = p->nsamples;
156 p->ch0_pos = lsx_tell(ft);
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;
164 return(SOX_SUCCESS);
167 /*======================================================================*/
168 /* 8SVXREAD */
169 /*======================================================================*/
170 static size_t read_samples(sox_format_t * ft, sox_sample_t *buf, size_t nsamp)
172 size_t done = 0;
174 priv_t * p = (priv_t * ) ft->priv;
175 size_t frames = nsamp / ft->signal.channels;
176 unsigned width = p->nsamples / ft->signal.channels;
178 if (p->left < frames)
179 frames = p->left;
181 while (done != frames) {
182 size_t chunk = frames - done;
183 size_t i;
184 unsigned ch;
186 if (chunk > BUFLEN)
187 chunk = BUFLEN;
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;
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);
202 done += chunk;
203 p->left -= chunk * ft->signal.channels;
204 p->ch0_pos += chunk;
206 return done * ft->signal.channels;
209 /*======================================================================*/
210 /* 8SVXSTARTWRITE */
211 /*======================================================================*/
212 static int startwrite(sox_format_t * ft)
214 priv_t * p = (priv_t * ) ft->priv;
215 size_t i;
217 /* open channel output files */
218 for (i = 0; i < ft->signal.channels; i++) {
219 if ((p->tmp[i] = lsx_tmpfile()) == NULL)
221 lsx_fail_errno(ft,errno,"Can't open channel output file");
222 return(SOX_EOF);
226 p->nsamples = 0;
227 return(SOX_SUCCESS);
230 /*======================================================================*/
231 /* 8SVXWRITE */
232 /*======================================================================*/
234 static size_t write_samples(sox_format_t * ft, const sox_sample_t *buf, size_t len)
236 priv_t * p = (priv_t * ) ft->priv;
237 SOX_SAMPLE_LOCALS;
239 unsigned char datum;
240 size_t done = 0, i;
242 p->nsamples += len;
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]);
249 done += ft->signal.channels;
251 return (done);
254 /*======================================================================*/
255 /* 8SVXSTOPWRITE */
256 /*======================================================================*/
258 static int stopwrite(sox_format_t * ft)
260 priv_t * p = (priv_t * ) ft->priv;
262 size_t i, len;
263 char svxbuf[512];
265 svxwriteheader(ft, (size_t) p->nsamples);
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))
272 lsx_fail_errno (ft,errno,"Can't rewind channel output file %lu",(unsigned long)i);
273 return(SOX_EOF);
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;
282 fclose (p->tmp[i]);
285 /* add a pad byte if BODY size is odd */
286 if(p->nsamples % 2 != 0)
287 lsx_writeb(ft, '\0');
289 return(SOX_SUCCESS);
292 /*======================================================================*/
293 /* 8SVXWRITEHEADER */
294 /*======================================================================*/
295 #define SVXHEADERSIZE 100
296 static void svxwriteheader(sox_format_t * ft, size_t nsamples)
298 size_t formsize = nsamples + SVXHEADERSIZE - 8;
300 /* FORM size must be even */
301 if(formsize % 2 != 0) formsize++;
303 lsx_writes(ft, "FORM");
304 lsx_writedw(ft, (unsigned) formsize); /* size of file */
305 lsx_writes(ft, "8SVX"); /* File type */
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 */
317 lsx_writes(ft, "ANNO");
318 lsx_writedw(ft, 32); /* length of block */
319 lsx_writes(ft, "File created by Sound Exchange ");
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);
326 lsx_writes(ft, "BODY");
327 lsx_writedw(ft, (unsigned) nsamples); /* samples in file */
330 LSX_FORMAT_HANDLER(svx)
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)
341 return &handler;