add new sigc++2 directory
[ardour.git] / libs / libsndfile / src / mat5.c
1 /*
2 ** Copyright (C) 2002-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        <fcntl.h>
23 #include        <string.h>
24 #include        <ctype.h>
25
26 #include        "sndfile.h"
27 #include        "sfendian.h"
28 #include        "common.h"
29 #include        "float_cast.h"
30
31 /*------------------------------------------------------------------------------
32 ** Information on how to decode and encode this file was obtained in a PDF
33 ** file which I found on http://www.wotsit.org/.
34 ** Also did a lot of testing with GNU Octave but do not have access to
35 ** Matlab (tm) and so could not test it there.
36 */
37
38 /*------------------------------------------------------------------------------
39 ** Macros to handle big/little endian issues.
40 */
41
42 #define MATL_MARKER     (MAKE_MARKER ('M', 'A', 'T', 'L'))
43
44 #define IM_MARKER       (('I' << 8) + 'M')
45 #define MI_MARKER       (('M' << 8) + 'I')
46
47 /*------------------------------------------------------------------------------
48 ** Enums and typedefs.
49 */
50
51 enum
52 {       MAT5_TYPE_SCHAR                 = 0x1,
53         MAT5_TYPE_UCHAR                 = 0x2,
54         MAT5_TYPE_INT16                 = 0x3,
55         MAT5_TYPE_UINT16                = 0x4,
56         MAT5_TYPE_INT32                 = 0x5,
57         MAT5_TYPE_UINT32                = 0x6,
58         MAT5_TYPE_FLOAT                 = 0x7,
59         MAT5_TYPE_DOUBLE                = 0x9,
60         MAT5_TYPE_ARRAY                 = 0xE,
61
62         MAT5_TYPE_COMP_USHORT   = 0x00020004,
63         MAT5_TYPE_COMP_UINT             = 0x00040006
64 } ;
65
66 typedef struct
67 {       sf_count_t      size ;
68         int                     rows, cols ;
69         char            name [32] ;
70 } MAT5_MATRIX ;
71
72 /*------------------------------------------------------------------------------
73 ** Private static functions.
74 */
75
76 static int              mat5_close              (SF_PRIVATE *psf) ;
77
78 static int              mat5_write_header (SF_PRIVATE *psf, int calc_length) ;
79 static int              mat5_read_header (SF_PRIVATE *psf) ;
80
81 /*------------------------------------------------------------------------------
82 ** Public function.
83 */
84
85 int
86 mat5_open       (SF_PRIVATE *psf)
87 {       int             subformat, error = 0 ;
88
89         if (psf->mode == SFM_READ || (psf->mode == SFM_RDWR && psf->filelength > 0))
90         {       if ((error = mat5_read_header (psf)))
91                         return error ;
92                 } ;
93
94         if ((psf->sf.format & SF_FORMAT_TYPEMASK) != SF_FORMAT_MAT5)
95                 return  SFE_BAD_OPEN_FORMAT ;
96
97         subformat = psf->sf.format & SF_FORMAT_SUBMASK ;
98
99         if (psf->mode == SFM_WRITE || psf->mode == SFM_RDWR)
100         {       if (psf->is_pipe)
101                         return SFE_NO_PIPE_WRITE ;
102
103                 psf->endian = psf->sf.format & SF_FORMAT_ENDMASK ;
104                 if (CPU_IS_LITTLE_ENDIAN && (psf->endian == SF_ENDIAN_CPU || psf->endian == 0))
105                         psf->endian = SF_ENDIAN_LITTLE ;
106                 else if (CPU_IS_BIG_ENDIAN && (psf->endian == SF_ENDIAN_CPU || psf->endian == 0))
107                         psf->endian = SF_ENDIAN_BIG ;
108
109                 if ((error = mat5_write_header (psf, SF_FALSE)))
110                         return error ;
111
112                 psf->write_header = mat5_write_header ;
113                 } ;
114
115         psf->container_close = mat5_close ;
116
117         psf->blockwidth = psf->bytewidth * psf->sf.channels ;
118
119         switch (subformat)
120         {       case SF_FORMAT_PCM_U8 :
121                 case SF_FORMAT_PCM_16 :
122                 case SF_FORMAT_PCM_32 :
123                                 error = pcm_init (psf) ;
124                                 break ;
125
126                 case SF_FORMAT_FLOAT :
127                                 error = float32_init (psf) ;
128                                 break ;
129
130                 case SF_FORMAT_DOUBLE :
131                                 error = double64_init (psf) ;
132                                 break ;
133
134                 default : break ;
135                 } ;
136
137         return error ;
138 } /* mat5_open */
139
140 /*------------------------------------------------------------------------------
141 */
142
143 static int
144 mat5_close      (SF_PRIVATE *psf)
145 {
146         if (psf->mode == SFM_WRITE || psf->mode == SFM_RDWR)
147                 mat5_write_header (psf, SF_TRUE) ;
148
149         return 0 ;
150 } /* mat5_close */
151
152 /*------------------------------------------------------------------------------
153 */
154
155 static int
156 mat5_write_header (SF_PRIVATE *psf, int calc_length)
157 {       static const char       *filename = "MATLAB 5.0 MAT-file, written by " PACKAGE "-" VERSION ", " ;
158         static const char       *sr_name = "samplerate\0\0\0\0\0\0\0\0\0\0\0" ;
159         static const char       *wd_name = "wavedata\0" ;
160         sf_count_t      current, datasize ;
161         int                     encoding ;
162
163         current = psf_ftell (psf) ;
164
165         if (calc_length)
166         {       psf_fseek (psf, 0, SEEK_END) ;
167                 psf->filelength = psf_ftell (psf) ;
168                 psf_fseek (psf, 0, SEEK_SET) ;
169
170                 psf->datalength = psf->filelength - psf->dataoffset ;
171                 if (psf->dataend)
172                         psf->datalength -= psf->filelength - psf->dataend ;
173
174                 psf->sf.frames = psf->datalength / (psf->bytewidth * psf->sf.channels) ;
175                 } ;
176
177         switch (psf->sf.format & SF_FORMAT_SUBMASK)
178         {       case SF_FORMAT_PCM_U8 :
179                                 encoding = MAT5_TYPE_UCHAR ;
180                                 break ;
181
182                 case SF_FORMAT_PCM_16 :
183                                 encoding = MAT5_TYPE_INT16 ;
184                                 break ;
185
186                 case SF_FORMAT_PCM_32 :
187                                 encoding = MAT5_TYPE_INT32 ;
188                                 break ;
189
190                 case SF_FORMAT_FLOAT :
191                                 encoding = MAT5_TYPE_FLOAT ;
192                                 break ;
193
194                 case SF_FORMAT_DOUBLE :
195                                 encoding = MAT5_TYPE_DOUBLE ;
196                                 break ;
197
198                 default :
199                                 return SFE_BAD_OPEN_FORMAT ;
200                 } ;
201
202         /* Reset the current header length to zero. */
203         psf->header [0] = 0 ;
204         psf->headindex = 0 ;
205         psf_fseek (psf, 0, SEEK_SET) ;
206
207         psf_get_date_str (psf->u.cbuf, sizeof (psf->u.scbuf)) ;
208         psf_binheader_writef (psf, "bb", filename, strlen (filename), psf->u.cbuf, strlen (psf->u.cbuf) + 1) ;
209
210         memset (psf->u.scbuf, ' ', 124 - psf->headindex) ;
211         psf_binheader_writef (psf, "b", psf->u.scbuf, 124 - psf->headindex) ;
212
213         psf->rwf_endian = psf->endian ;
214
215         if (psf->rwf_endian == SF_ENDIAN_BIG)
216                 psf_binheader_writef (psf, "2b", 0x0100, "MI", 2) ;
217         else
218                 psf_binheader_writef (psf, "2b", 0x0100, "IM", 2) ;
219
220         psf_binheader_writef (psf, "444444", MAT5_TYPE_ARRAY, 64, MAT5_TYPE_UINT32, 8, 6, 0) ;
221         psf_binheader_writef (psf, "4444", MAT5_TYPE_INT32, 8, 1, 1) ;
222         psf_binheader_writef (psf, "44b", MAT5_TYPE_SCHAR, strlen (sr_name), sr_name, 16) ;
223
224         if (psf->sf.samplerate > 0xFFFF)
225                 psf_binheader_writef (psf, "44", MAT5_TYPE_COMP_UINT, psf->sf.samplerate) ;
226         else
227         {       unsigned short samplerate = psf->sf.samplerate ;
228
229                 psf_binheader_writef (psf, "422", MAT5_TYPE_COMP_USHORT, samplerate, 0) ;
230                 } ;
231
232         datasize = psf->sf.frames * psf->sf.channels * psf->bytewidth ;
233
234         psf_binheader_writef (psf, "t484444", MAT5_TYPE_ARRAY, datasize + 64, MAT5_TYPE_UINT32, 8, 6, 0) ;
235         psf_binheader_writef (psf, "t4448", MAT5_TYPE_INT32, 8, psf->sf.channels, psf->sf.frames) ;
236         psf_binheader_writef (psf, "44b", MAT5_TYPE_SCHAR, strlen (wd_name), wd_name, strlen (wd_name)) ;
237
238         datasize = psf->sf.frames * psf->sf.channels * psf->bytewidth ;
239         if (datasize > 0x7FFFFFFF)
240                 datasize = 0x7FFFFFFF ;
241
242         psf_binheader_writef (psf, "t48", encoding, datasize) ;
243
244         /* Header construction complete so write it out. */
245         psf_fwrite (psf->header, psf->headindex, 1, psf) ;
246
247         if (psf->error)
248                 return psf->error ;
249
250         psf->dataoffset = psf->headindex ;
251
252         if (current > 0)
253                 psf_fseek (psf, current, SEEK_SET) ;
254
255         return psf->error ;
256 } /* mat5_write_header */
257
258 static int
259 mat5_read_header (SF_PRIVATE *psf)
260 {       char    name [32] ;
261         short   version, endian ;
262         int             type, size, flags1, flags2, rows, cols ;
263
264         psf_binheader_readf (psf, "pb", 0, psf->u.cbuf, 124) ;
265
266         psf->u.scbuf [125] = 0 ;
267
268         if (strlen (psf->u.cbuf) >= 124)
269                 return SFE_UNIMPLEMENTED ;
270
271         if (strstr (psf->u.cbuf, "MATLAB 5.0 MAT-file") == psf->u.cbuf)
272                 psf_log_printf (psf, "%s\n", psf->u.scbuf) ;
273
274
275         psf_binheader_readf (psf, "E22", &version, &endian) ;
276
277         if (endian == MI_MARKER)
278         {       psf->endian = psf->rwf_endian = SF_ENDIAN_BIG ;
279                 if (CPU_IS_LITTLE_ENDIAN) version = ENDSWAP_SHORT (version) ;
280                 }
281         else if (endian == IM_MARKER)
282         {       psf->endian = psf->rwf_endian = SF_ENDIAN_LITTLE ;
283                 if (CPU_IS_BIG_ENDIAN) version = ENDSWAP_SHORT (version) ;
284                 }
285         else
286                 return SFE_MAT5_BAD_ENDIAN ;
287
288         if ((CPU_IS_LITTLE_ENDIAN && endian == IM_MARKER) ||
289                         (CPU_IS_BIG_ENDIAN && endian == MI_MARKER))
290                 version = ENDSWAP_SHORT (version) ;
291
292         psf_log_printf (psf, "Version : 0x%04X\n", version) ;
293         psf_log_printf (psf, "Endian  : 0x%04X => %s\n", endian,
294                                 (psf->endian == SF_ENDIAN_LITTLE) ? "Little" : "Big") ;
295
296         /*========================================================*/
297         psf_binheader_readf (psf, "44", &type, &size) ;
298         psf_log_printf (psf, "Block\n Type : %X    Size : %d\n", type, size) ;
299
300         if (type != MAT5_TYPE_ARRAY)
301                 return SFE_MAT5_NO_BLOCK ;
302
303         psf_binheader_readf (psf, "44", &type, &size) ;
304         psf_log_printf (psf, "    Type : %X    Size : %d\n", type, size) ;
305
306         if (type != MAT5_TYPE_UINT32)
307                 return SFE_MAT5_NO_BLOCK ;
308
309         psf_binheader_readf (psf, "44", &flags1, &flags2) ;
310         psf_log_printf (psf, "    Flg1 : %X    Flg2 : %d\n", flags1, flags2) ;
311
312         psf_binheader_readf (psf, "44", &type, &size) ;
313         psf_log_printf (psf, "    Type : %X    Size : %d\n", type, size) ;
314
315         if (type != MAT5_TYPE_INT32)
316                 return SFE_MAT5_NO_BLOCK ;
317
318         psf_binheader_readf (psf, "44", &rows, &cols) ;
319         psf_log_printf (psf, "    Rows : %X    Cols : %d\n", rows, cols) ;
320
321         if (rows != 1 || cols != 1)
322                 return SFE_MAT5_SAMPLE_RATE ;
323
324         psf_binheader_readf (psf, "4", &type) ;
325
326         if (type == MAT5_TYPE_SCHAR)
327         {       psf_binheader_readf (psf, "4", &size) ;
328                 psf_log_printf (psf, "    Type : %X    Size : %d\n", type, size) ;
329                 if (size > SIGNED_SIZEOF (name) - 1)
330                 {       psf_log_printf (psf, "Error : Bad name length.\n") ;
331                         return SFE_MAT5_NO_BLOCK ;
332                         } ;
333
334                 psf_binheader_readf (psf, "bj", name, size, (8 - (size % 8)) % 8) ;
335                 name [size] = 0 ;
336                 }
337         else if ((type & 0xFFFF) == MAT5_TYPE_SCHAR)
338         {       size = type >> 16 ;
339                 if (size > 4)
340                 {       psf_log_printf (psf, "Error : Bad name length.\n") ;
341                         return SFE_MAT5_NO_BLOCK ;
342                         } ;
343
344                 psf_log_printf (psf, "    Type : %X\n", type) ;
345                 psf_binheader_readf (psf, "4", &name) ;
346                 name [size] = 0 ;
347                 }
348         else
349                 return SFE_MAT5_NO_BLOCK ;
350
351         psf_log_printf (psf, "    Name : %s\n", name) ;
352
353         /*-----------------------------------------*/
354
355         psf_binheader_readf (psf, "44", &type, &size) ;
356
357         switch (type)
358         {       case MAT5_TYPE_DOUBLE :
359                                 {       double  samplerate ;
360
361                                         psf_binheader_readf (psf, "d", &samplerate) ;
362                                         LSF_SNPRINTF (name, sizeof (name), "%f\n", samplerate) ;
363                                         psf_log_printf (psf, "    Val  : %s\n", name) ;
364
365                                         psf->sf.samplerate = lrint (samplerate) ;
366                                         } ;
367                                 break ;
368
369                 case MAT5_TYPE_COMP_USHORT :
370                                 {       unsigned short samplerate ;
371
372                                         psf_binheader_readf (psf, "j2j", -4, &samplerate, 2) ;
373                                         psf_log_printf (psf, "    Val  : %u\n", samplerate) ;
374                                         psf->sf.samplerate = samplerate ;
375                                         }
376                                 break ;
377
378                 case MAT5_TYPE_COMP_UINT :
379                                 psf_log_printf (psf, "    Val  : %u\n", size) ;
380                                 psf->sf.samplerate = size ;
381                                 break ;
382
383                 default :
384                         psf_log_printf (psf, "    Type : %X    Size : %d  ***\n", type, size) ;
385                         return SFE_MAT5_SAMPLE_RATE ;
386                 } ;
387
388         /*-----------------------------------------*/
389
390
391         psf_binheader_readf (psf, "44", &type, &size) ;
392         psf_log_printf (psf, " Type : %X    Size : %d\n", type, size) ;
393
394         if (type != MAT5_TYPE_ARRAY)
395                 return SFE_MAT5_NO_BLOCK ;
396
397         psf_binheader_readf (psf, "44", &type, &size) ;
398         psf_log_printf (psf, "    Type : %X    Size : %d\n", type, size) ;
399
400         if (type != MAT5_TYPE_UINT32)
401                 return SFE_MAT5_NO_BLOCK ;
402
403         psf_binheader_readf (psf, "44", &flags1, &flags2) ;
404         psf_log_printf (psf, "    Flg1 : %X    Flg2 : %d\n", flags1, flags2) ;
405
406         psf_binheader_readf (psf, "44", &type, &size) ;
407         psf_log_printf (psf, "    Type : %X    Size : %d\n", type, size) ;
408
409         if (type != MAT5_TYPE_INT32)
410                 return SFE_MAT5_NO_BLOCK ;
411
412         psf_binheader_readf (psf, "44", &rows, &cols) ;
413         psf_log_printf (psf, "    Rows : %X    Cols : %d\n", rows, cols) ;
414
415         psf_binheader_readf (psf, "4", &type) ;
416
417         if (type == MAT5_TYPE_SCHAR)
418         {       psf_binheader_readf (psf, "4", &size) ;
419                 psf_log_printf (psf, "    Type : %X    Size : %d\n", type, size) ;
420                 if (size > SIGNED_SIZEOF (name) - 1)
421                 {       psf_log_printf (psf, "Error : Bad name length.\n") ;
422                         return SFE_MAT5_NO_BLOCK ;
423                         } ;
424
425                 psf_binheader_readf (psf, "bj", name, size, (8 - (size % 8)) % 8) ;
426                 name [size] = 0 ;
427                 }
428         else if ((type & 0xFFFF) == MAT5_TYPE_SCHAR)
429         {       size = type >> 16 ;
430                 if (size > 4)
431                 {       psf_log_printf (psf, "Error : Bad name length.\n") ;
432                         return SFE_MAT5_NO_BLOCK ;
433                         } ;
434
435                 psf_log_printf (psf, "    Type : %X\n", type) ;
436                 psf_binheader_readf (psf, "4", &name) ;
437                 name [size] = 0 ;
438                 }
439         else
440                 return SFE_MAT5_NO_BLOCK ;
441
442         psf_log_printf (psf, "    Name : %s\n", name) ;
443
444         psf_binheader_readf (psf, "44", &type, &size) ;
445         psf_log_printf (psf, "    Type : %X    Size : %d\n", type, size) ;
446
447         /*++++++++++++++++++++++++++++++++++++++++++++++++++*/
448
449         if (rows == 0 && cols == 0)
450         {       psf_log_printf (psf, "*** Error : zero channel count.\n") ;
451                 return SFE_MAT5_ZERO_CHANNELS ;
452                 } ;
453
454         psf->sf.channels        = rows ;
455         psf->sf.frames          = cols ;
456
457         psf->sf.format = psf->endian | SF_FORMAT_MAT5 ;
458
459         switch (type)
460         {       case MAT5_TYPE_DOUBLE :
461                                 psf_log_printf (psf, "Data type : double\n") ;
462                                 psf->sf.format |= SF_FORMAT_DOUBLE ;
463                                 psf->bytewidth = 8 ;
464                                 break ;
465
466                 case MAT5_TYPE_FLOAT :
467                                 psf_log_printf (psf, "Data type : float\n") ;
468                                 psf->sf.format |= SF_FORMAT_FLOAT ;
469                                 psf->bytewidth = 4 ;
470                                 break ;
471
472                 case MAT5_TYPE_INT32 :
473                                 psf_log_printf (psf, "Data type : 32 bit PCM\n") ;
474                                 psf->sf.format |= SF_FORMAT_PCM_32 ;
475                                 psf->bytewidth = 4 ;
476                                 break ;
477
478                 case MAT5_TYPE_INT16 :
479                                 psf_log_printf (psf, "Data type : 16 bit PCM\n") ;
480                                 psf->sf.format |= SF_FORMAT_PCM_16 ;
481                                 psf->bytewidth = 2 ;
482                                 break ;
483
484                 case MAT5_TYPE_UCHAR :
485                                 psf_log_printf (psf, "Data type : unsigned 8 bit PCM\n") ;
486                                 psf->sf.format |= SF_FORMAT_PCM_U8 ;
487                                 psf->bytewidth = 1 ;
488                                 break ;
489
490                 default :
491                                 psf_log_printf (psf, "*** Error : Bad marker %08X\n", type) ;
492                                 return SFE_UNIMPLEMENTED ;
493                 } ;
494
495         psf->dataoffset = psf_ftell (psf) ;
496         psf->datalength = psf->filelength - psf->dataoffset ;
497
498         return 0 ;
499 } /* mat5_read_header */
500
501 /*
502 ** Do not edit or modify anything in this comment block.
503 ** The arch-tag line is a file identity tag for the GNU Arch 
504 ** revision control system.
505 **
506 ** arch-tag: dfdb6742-b2be-4be8-b390-d0c674e8bc8e
507 */