add libsndfile directory
[ardour.git] / libs / libsndfile / src / svx.c
1 /*
2 ** Copyright (C) 1999-2004 Erik de Castro Lopo <erikd@mega-nerd.com>
3 **
4 ** This program is free software; you can redistribute it and/or modify
5 ** it under the terms of the GNU Lesser General Public License as published by
6 ** the Free Software Foundation; either version 2.1 of the License, or
7 ** (at your option) any later version.
8 **
9 ** This program is distributed in the hope that it will be useful,
10 ** but WITHOUT ANY WARRANTY; without even the implied warranty of
11 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12 ** GNU Lesser General Public License for more details.
13 **
14 ** You should have received a copy of the GNU Lesser General Public License
15 ** along with this program; if not, write to the Free Software
16 ** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
17 */
18
19 #include "sfconfig.h"
20
21 #include <stdio.h>
22 #include <string.h>
23 #include <ctype.h>
24 #include <stdarg.h>
25
26 #include "sndfile.h"
27 #include "sfendian.h"
28 #include "common.h"
29
30
31 /*------------------------------------------------------------------------------
32  * Macros to handle big/little endian issues.
33 */
34
35 #define FORM_MARKER     (MAKE_MARKER ('F', 'O', 'R', 'M'))
36 #define SVX8_MARKER     (MAKE_MARKER ('8', 'S', 'V', 'X'))
37 #define SV16_MARKER     (MAKE_MARKER ('1', '6', 'S', 'V'))
38 #define VHDR_MARKER     (MAKE_MARKER ('V', 'H', 'D', 'R'))
39 #define BODY_MARKER     (MAKE_MARKER ('B', 'O', 'D', 'Y'))
40
41 #define ATAK_MARKER     (MAKE_MARKER ('A', 'T', 'A', 'K'))
42 #define RLSE_MARKER     (MAKE_MARKER ('R', 'L', 'S', 'E'))
43
44 #define c_MARKER        (MAKE_MARKER ('(', 'c', ')', ' '))
45 #define NAME_MARKER     (MAKE_MARKER ('N', 'A', 'M', 'E'))
46 #define AUTH_MARKER     (MAKE_MARKER ('A', 'U', 'T', 'H'))
47 #define ANNO_MARKER     (MAKE_MARKER ('A', 'N', 'N', 'O'))
48 #define CHAN_MARKER     (MAKE_MARKER ('C', 'H', 'A', 'N'))
49
50 /*------------------------------------------------------------------------------
51  * Typedefs for file chunks.
52 */
53
54 typedef struct
55 {       unsigned int    oneShotHiSamples, repeatHiSamples, samplesPerHiCycle ;
56         unsigned short  samplesPerSec ;
57         unsigned char   octave, compression ;
58         unsigned int    volume ;
59 } VHDR_CHUNK ;
60
61 enum {
62         HAVE_FORM       = 0x01,
63
64         HAVE_SVX        = 0x02,
65         HAVE_VHDR       = 0x04,
66         HAVE_BODY       = 0x08
67 } ;
68
69 /*------------------------------------------------------------------------------
70  * Private static functions.
71 */
72
73 static int      svx_close       (SF_PRIVATE *psf) ;
74 static int      svx_write_header (SF_PRIVATE *psf, int calc_length) ;
75 static int      svx_read_header (SF_PRIVATE *psf) ;
76
77 /*------------------------------------------------------------------------------
78 ** Public function.
79 */
80
81 int
82 svx_open        (SF_PRIVATE *psf)
83 {       int error ;
84
85         if (psf->mode == SFM_READ || (psf->mode == SFM_RDWR && psf->filelength > 0))
86         {       if ((error = svx_read_header (psf)))
87                         return error ;
88
89                 psf->endian = SF_ENDIAN_BIG ;                   /* All SVX files are big endian. */
90
91                 psf->blockwidth = psf->sf.channels * psf->bytewidth ;
92                 if (psf->blockwidth)
93                         psf->sf.frames = psf->datalength / psf->blockwidth ;
94
95                 psf_fseek (psf, psf->dataoffset, SEEK_SET) ;
96                 } ;
97
98         if (psf->mode == SFM_WRITE || psf->mode == SFM_RDWR)
99         {       if (psf->is_pipe)
100                         return SFE_NO_PIPE_WRITE ;
101
102                 if ((psf->sf.format & SF_FORMAT_TYPEMASK) != SF_FORMAT_SVX)
103                         return  SFE_BAD_OPEN_FORMAT ;
104
105                 psf->endian = psf->sf.format & SF_FORMAT_ENDMASK ;
106
107                 if (psf->endian == SF_ENDIAN_LITTLE || (CPU_IS_LITTLE_ENDIAN && psf->endian == SF_ENDIAN_CPU))
108                         return SFE_BAD_ENDIAN ;
109
110                 psf->endian = SF_ENDIAN_BIG ;                   /* All SVX files are big endian. */
111
112                 error = svx_write_header (psf, SF_FALSE) ;
113                 if (error)
114                         return error ;
115
116                 psf->write_header = svx_write_header ;
117                 } ;
118
119         psf->container_close = svx_close ;
120
121         if ((error = pcm_init (psf)))
122                 return error ;
123
124         return 0 ;
125 } /* svx_open */
126
127 /*------------------------------------------------------------------------------
128 */
129
130 static int
131 svx_read_header (SF_PRIVATE *psf)
132 {       VHDR_CHUNK              vhdr ;
133         unsigned int    FORMsize, vhdrsize, dword, marker ;
134         int                             filetype = 0, parsestage = 0, done = 0 ;
135         int                     bytecount = 0, channels ;
136
137         memset (&vhdr, 0, sizeof (vhdr)) ;
138         psf_binheader_readf (psf, "p", 0) ;
139
140         /* Set default number of channels. Currently can't handle stereo SVX files. */
141         psf->sf.channels = 1 ;
142
143         psf->sf.format = SF_FORMAT_SVX ;
144
145         while (! done)
146         {       psf_binheader_readf (psf, "m", &marker) ;
147                 switch (marker)
148                 {       case FORM_MARKER :
149                                         if (parsestage)
150                                                 return SFE_SVX_NO_FORM ;
151
152                                         psf_binheader_readf (psf, "E4", &FORMsize) ;
153
154                                         if (FORMsize != psf->filelength - 2 * sizeof (dword))
155                                         {       dword = psf->filelength - 2 * sizeof (dword) ;
156                                                 psf_log_printf (psf, "FORM : %d (should be %d)\n", FORMsize, dword) ;
157                                                 FORMsize = dword ;
158                                                 }
159                                         else
160                                                 psf_log_printf (psf, "FORM : %d\n", FORMsize) ;
161                                         parsestage |= HAVE_FORM ;
162                                         break ;
163
164                         case SVX8_MARKER :
165                         case SV16_MARKER :
166                                         if (! (parsestage & HAVE_FORM))
167                                                 return SFE_SVX_NO_FORM ;
168                                         filetype = marker ;
169                                         psf_log_printf (psf, " %M\n", marker) ;
170                                         parsestage |= HAVE_SVX ;
171                                         break ;
172
173                         case VHDR_MARKER :
174                                         if (! (parsestage & (HAVE_FORM | HAVE_SVX)))
175                                                 return SFE_SVX_NO_FORM ;
176
177                                         psf_binheader_readf (psf, "E4", &vhdrsize) ;
178
179                                         psf_log_printf (psf, " VHDR : %d\n", vhdrsize) ;
180
181                                         psf_binheader_readf (psf, "E4442114", &(vhdr.oneShotHiSamples), &(vhdr.repeatHiSamples),
182                                                 &(vhdr.samplesPerHiCycle), &(vhdr.samplesPerSec), &(vhdr.octave), &(vhdr.compression),
183                                                 &(vhdr.volume)) ;
184
185                                         psf_log_printf (psf, "  OneShotHiSamples  : %d\n", vhdr.oneShotHiSamples) ;
186                                         psf_log_printf (psf, "  RepeatHiSamples   : %d\n", vhdr.repeatHiSamples) ;
187                                         psf_log_printf (psf, "  samplesPerHiCycle : %d\n", vhdr.samplesPerHiCycle) ;
188                                         psf_log_printf (psf, "  Sample Rate       : %d\n", vhdr.samplesPerSec) ;
189                                         psf_log_printf (psf, "  Octave            : %d\n", vhdr.octave) ;
190
191                                         psf_log_printf (psf, "  Compression       : %d => ", vhdr.compression) ;
192
193                                         switch (vhdr.compression)
194                                         {       case 0 : psf_log_printf (psf, "None.\n") ;
195                                                                 break ;
196                                                 case 1 : psf_log_printf (psf, "Fibonacci delta\n") ;
197                                                                 break ;
198                                                 case 2 : psf_log_printf (psf, "Exponential delta\n") ;
199                                                                 break ;
200                                                 } ;
201
202                                         psf_log_printf (psf, "  Volume            : %d\n", vhdr.volume) ;
203
204                                         psf->sf.samplerate      = vhdr.samplesPerSec ;
205
206                                         if (filetype == SVX8_MARKER)
207                                         {       psf->sf.format |= SF_FORMAT_PCM_S8 ;
208                                                 psf->bytewidth = 1 ;
209                                                 }
210                                         else if (filetype == SV16_MARKER)
211                                         {       psf->sf.format |= SF_FORMAT_PCM_16 ;
212                                                 psf->bytewidth = 2 ;
213                                                 } ;
214
215                                         parsestage |= HAVE_VHDR ;
216                                         break ;
217
218                         case BODY_MARKER :
219                                         if (! (parsestage & HAVE_VHDR))
220                                                 return SFE_SVX_NO_BODY ;
221
222                                         psf_binheader_readf (psf, "E4", &dword) ;
223                                         psf->datalength = dword ;
224
225                                         psf->dataoffset = psf_ftell (psf) ;
226
227                                         if (psf->datalength > psf->filelength - psf->dataoffset)
228                                         {       psf_log_printf (psf, " BODY : %D (should be %D)\n", psf->datalength, psf->filelength - psf->dataoffset) ;
229                                                 psf->datalength = psf->filelength - psf->dataoffset ;
230                                                 }
231                                         else
232                                                 psf_log_printf (psf, " BODY : %D\n", psf->datalength) ;
233
234                                         parsestage |= HAVE_BODY ;
235
236                                         if (! psf->sf.seekable)
237                                                 break ;
238
239                                         psf_fseek (psf, psf->datalength, SEEK_CUR) ;
240                                         break ;
241
242                         case NAME_MARKER :
243                                         if (! (parsestage & HAVE_SVX))
244                                                 return SFE_SVX_NO_FORM ;
245
246                                         psf_binheader_readf (psf, "E4", &dword) ;
247
248                                         psf_log_printf (psf, " %M : %d\n", marker, dword) ;
249
250                                         if (strlen (psf->filename) != dword)
251                                         {       if (dword > sizeof (psf->filename) - 1)
252                                                         return SFE_SVX_BAD_NAME_LENGTH ;
253
254                                                 psf_binheader_readf (psf, "b", psf->filename, dword) ;
255                                                 psf->filename [dword] = 0 ;
256                                                 }
257                                         else
258                                                 psf_binheader_readf (psf, "j", dword) ;
259                                         break ;
260
261                         case ANNO_MARKER :
262                                         if (! (parsestage & HAVE_SVX))
263                                                 return SFE_SVX_NO_FORM ;
264
265                                         psf_binheader_readf (psf, "E4", &dword) ;
266
267                                         psf_log_printf (psf, " %M : %d\n", marker, dword) ;
268
269                                         psf_binheader_readf (psf, "j", dword) ;
270                                         break ;
271
272                         case CHAN_MARKER :
273                                         if (! (parsestage & HAVE_SVX))
274                                                 return SFE_SVX_NO_FORM ;
275
276                                         psf_binheader_readf (psf, "E4", &dword) ;
277
278                                         psf_log_printf (psf, " %M : %d\n", marker, dword) ;
279
280                                         bytecount += psf_binheader_readf (psf, "E4", &channels) ;
281                                         psf->sf.channels = channels ;
282
283                                         psf_log_printf (psf, "  Channels : %d\n", channels) ;
284
285                                         psf_binheader_readf (psf, "j", dword - bytecount) ;
286                                         break ;
287
288
289                         case AUTH_MARKER :
290                         case c_MARKER :
291                                         if (! (parsestage & HAVE_SVX))
292                                                 return SFE_SVX_NO_FORM ;
293
294                                         psf_binheader_readf (psf, "E4", &dword) ;
295
296                                         psf_log_printf (psf, " %M : %d\n", marker, dword) ;
297
298                                         psf_binheader_readf (psf, "j", dword) ;
299                                         break ;
300
301                         default :
302                                         if (isprint ((marker >> 24) & 0xFF) && isprint ((marker >> 16) & 0xFF)
303                                                 && isprint ((marker >> 8) & 0xFF) && isprint (marker & 0xFF))
304                                         {       psf_binheader_readf (psf, "E4", &dword) ;
305
306                                                 psf_log_printf (psf, "%M : %d (unknown marker)\n", marker, dword) ;
307
308                                                 psf_binheader_readf (psf, "j", dword) ;
309                                                 break ;
310                                                 } ;
311                                         if ((dword = psf_ftell (psf)) & 0x03)
312                                         {       psf_log_printf (psf, "  Unknown chunk marker at position %d. Resynching.\n", dword - 4) ;
313
314                                                 psf_binheader_readf (psf, "j", -3) ;
315                                                 break ;
316                                                 } ;
317                                         psf_log_printf (psf, "*** Unknown chunk marker : %X. Exiting parser.\n", marker) ;
318                                         done = 1 ;
319                         } ;     /* switch (marker) */
320
321                 if (! psf->sf.seekable && (parsestage & HAVE_BODY))
322                         break ;
323
324                 if (psf_ftell (psf) >= psf->filelength - SIGNED_SIZEOF (dword))
325                         break ;
326                 } ; /* while (1) */
327
328         if (vhdr.compression)
329                 return SFE_SVX_BAD_COMP ;
330
331         if (psf->dataoffset <= 0)
332                 return SFE_SVX_NO_DATA ;
333
334         return 0 ;
335 } /* svx_read_header */
336
337 static int
338 svx_close (SF_PRIVATE *psf)
339 {
340         if (psf->mode == SFM_WRITE || psf->mode == SFM_RDWR)
341                 svx_write_header (psf, SF_TRUE) ;
342
343         return 0 ;
344 } /* svx_close */
345
346 static int
347 svx_write_header (SF_PRIVATE *psf, int calc_length)
348 {       static  char    annotation      [] = "libsndfile by Erik de Castro Lopo\0\0\0" ;
349         sf_count_t      current ;
350
351         current = psf_ftell (psf) ;
352
353         if (calc_length)
354         {       psf->filelength = psf_get_filelen (psf) ;
355
356                 psf->datalength = psf->filelength - psf->dataoffset ;
357
358                 if (psf->dataend)
359                         psf->datalength -= psf->filelength - psf->dataend ;
360
361                 psf->sf.frames = psf->datalength / (psf->bytewidth * psf->sf.channels) ;
362                 } ;
363
364         psf->header [0] = 0 ;
365         psf->headindex = 0 ;
366         psf_fseek (psf, 0, SEEK_SET) ;
367
368         /* FORM marker and FORM size. */
369         psf_binheader_writef (psf, "Etm8", FORM_MARKER, (psf->filelength < 8) ?
370                         psf->filelength * 0 : psf->filelength - 8) ;
371
372         psf_binheader_writef (psf, "m", (psf->bytewidth == 1) ? SVX8_MARKER : SV16_MARKER) ;
373
374         /* VHDR chunk. */
375         psf_binheader_writef (psf, "Em4", VHDR_MARKER, sizeof (VHDR_CHUNK)) ;
376         /* VHDR : oneShotHiSamples, repeatHiSamples, samplesPerHiCycle */
377         psf_binheader_writef (psf, "E444", psf->sf.frames, 0, 0) ;
378         /* VHDR : samplesPerSec, octave, compression */
379         psf_binheader_writef (psf, "E211", psf->sf.samplerate, 1, 0) ;
380         /* VHDR : volume */
381         psf_binheader_writef (psf, "E4", (psf->bytewidth == 1) ? 0xFF : 0xFFFF) ;
382
383         /* Filename and annotation strings. */
384         psf_binheader_writef (psf, "Emsms", NAME_MARKER, psf->filename, ANNO_MARKER, annotation) ;
385
386         /* BODY marker and size. */
387         psf_binheader_writef (psf, "Etm8", BODY_MARKER, (psf->datalength < 0) ?
388                         psf->datalength * 0 : psf->datalength) ;
389
390         psf_fwrite (psf->header, psf->headindex, 1, psf) ;
391
392         if (psf->error)
393                 return psf->error ;
394
395         psf->dataoffset = psf->headindex ;
396
397         if (current > 0)
398                 psf_fseek (psf, current, SEEK_SET) ;
399
400         return psf->error ;
401 } /* svx_write_header */
402
403
404 /*
405 ** Do not edit or modify anything in this comment block.
406 ** The arch-tag line is a file identity tag for the GNU Arch 
407 ** revision control system.
408 **
409 ** arch-tag: a80ab6fb-7d75-4d32-a6b0-0061a3f05d95
410 */