2 ** Copyright (C) 2002-2004 Erik de Castro Lopo <erikd@mega-nerd.com>
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.
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.
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.
29 #include "float_cast.h"
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.
38 /*------------------------------------------------------------------------------
39 ** Macros to handle big/little endian issues.
42 #define MATL_MARKER (MAKE_MARKER ('M', 'A', 'T', 'L'))
44 #define IM_MARKER (('I' << 8) + 'M')
45 #define MI_MARKER (('M' << 8) + 'I')
47 /*------------------------------------------------------------------------------
48 ** Enums and typedefs.
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,
62 MAT5_TYPE_COMP_USHORT = 0x00020004,
63 MAT5_TYPE_COMP_UINT = 0x00040006
72 /*------------------------------------------------------------------------------
73 ** Private static functions.
76 static int mat5_close (SF_PRIVATE *psf) ;
78 static int mat5_write_header (SF_PRIVATE *psf, int calc_length) ;
79 static int mat5_read_header (SF_PRIVATE *psf) ;
81 /*------------------------------------------------------------------------------
86 mat5_open (SF_PRIVATE *psf)
87 { int subformat, error = 0 ;
89 if (psf->mode == SFM_READ || (psf->mode == SFM_RDWR && psf->filelength > 0))
90 { if ((error = mat5_read_header (psf)))
94 if ((psf->sf.format & SF_FORMAT_TYPEMASK) != SF_FORMAT_MAT5)
95 return SFE_BAD_OPEN_FORMAT ;
97 subformat = psf->sf.format & SF_FORMAT_SUBMASK ;
99 if (psf->mode == SFM_WRITE || psf->mode == SFM_RDWR)
101 return SFE_NO_PIPE_WRITE ;
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 ;
109 if ((error = mat5_write_header (psf, SF_FALSE)))
112 psf->write_header = mat5_write_header ;
115 psf->container_close = mat5_close ;
117 psf->blockwidth = psf->bytewidth * psf->sf.channels ;
120 { case SF_FORMAT_PCM_U8 :
121 case SF_FORMAT_PCM_16 :
122 case SF_FORMAT_PCM_32 :
123 error = pcm_init (psf) ;
126 case SF_FORMAT_FLOAT :
127 error = float32_init (psf) ;
130 case SF_FORMAT_DOUBLE :
131 error = double64_init (psf) ;
140 /*------------------------------------------------------------------------------
144 mat5_close (SF_PRIVATE *psf)
146 if (psf->mode == SFM_WRITE || psf->mode == SFM_RDWR)
147 mat5_write_header (psf, SF_TRUE) ;
152 /*------------------------------------------------------------------------------
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 ;
163 current = psf_ftell (psf) ;
166 { psf_fseek (psf, 0, SEEK_END) ;
167 psf->filelength = psf_ftell (psf) ;
168 psf_fseek (psf, 0, SEEK_SET) ;
170 psf->datalength = psf->filelength - psf->dataoffset ;
172 psf->datalength -= psf->filelength - psf->dataend ;
174 psf->sf.frames = psf->datalength / (psf->bytewidth * psf->sf.channels) ;
177 switch (psf->sf.format & SF_FORMAT_SUBMASK)
178 { case SF_FORMAT_PCM_U8 :
179 encoding = MAT5_TYPE_UCHAR ;
182 case SF_FORMAT_PCM_16 :
183 encoding = MAT5_TYPE_INT16 ;
186 case SF_FORMAT_PCM_32 :
187 encoding = MAT5_TYPE_INT32 ;
190 case SF_FORMAT_FLOAT :
191 encoding = MAT5_TYPE_FLOAT ;
194 case SF_FORMAT_DOUBLE :
195 encoding = MAT5_TYPE_DOUBLE ;
199 return SFE_BAD_OPEN_FORMAT ;
202 /* Reset the current header length to zero. */
203 psf->header [0] = 0 ;
205 psf_fseek (psf, 0, SEEK_SET) ;
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) ;
210 memset (psf->u.scbuf, ' ', 124 - psf->headindex) ;
211 psf_binheader_writef (psf, "b", psf->u.scbuf, 124 - psf->headindex) ;
213 psf->rwf_endian = psf->endian ;
215 if (psf->rwf_endian == SF_ENDIAN_BIG)
216 psf_binheader_writef (psf, "2b", 0x0100, "MI", 2) ;
218 psf_binheader_writef (psf, "2b", 0x0100, "IM", 2) ;
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) ;
224 if (psf->sf.samplerate > 0xFFFF)
225 psf_binheader_writef (psf, "44", MAT5_TYPE_COMP_UINT, psf->sf.samplerate) ;
227 { unsigned short samplerate = psf->sf.samplerate ;
229 psf_binheader_writef (psf, "422", MAT5_TYPE_COMP_USHORT, samplerate, 0) ;
232 datasize = psf->sf.frames * psf->sf.channels * psf->bytewidth ;
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)) ;
238 datasize = psf->sf.frames * psf->sf.channels * psf->bytewidth ;
239 if (datasize > 0x7FFFFFFF)
240 datasize = 0x7FFFFFFF ;
242 psf_binheader_writef (psf, "t48", encoding, datasize) ;
244 /* Header construction complete so write it out. */
245 psf_fwrite (psf->header, psf->headindex, 1, psf) ;
250 psf->dataoffset = psf->headindex ;
253 psf_fseek (psf, current, SEEK_SET) ;
256 } /* mat5_write_header */
259 mat5_read_header (SF_PRIVATE *psf)
261 short version, endian ;
262 int type, size, flags1, flags2, rows, cols ;
264 psf_binheader_readf (psf, "pb", 0, psf->u.cbuf, 124) ;
266 psf->u.scbuf [125] = 0 ;
268 if (strlen (psf->u.cbuf) >= 124)
269 return SFE_UNIMPLEMENTED ;
271 if (strstr (psf->u.cbuf, "MATLAB 5.0 MAT-file") == psf->u.cbuf)
272 psf_log_printf (psf, "%s\n", psf->u.scbuf) ;
275 psf_binheader_readf (psf, "E22", &version, &endian) ;
277 if (endian == MI_MARKER)
278 { psf->endian = psf->rwf_endian = SF_ENDIAN_BIG ;
279 if (CPU_IS_LITTLE_ENDIAN) version = ENDSWAP_SHORT (version) ;
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) ;
286 return SFE_MAT5_BAD_ENDIAN ;
288 if ((CPU_IS_LITTLE_ENDIAN && endian == IM_MARKER) ||
289 (CPU_IS_BIG_ENDIAN && endian == MI_MARKER))
290 version = ENDSWAP_SHORT (version) ;
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") ;
296 /*========================================================*/
297 psf_binheader_readf (psf, "44", &type, &size) ;
298 psf_log_printf (psf, "Block\n Type : %X Size : %d\n", type, size) ;
300 if (type != MAT5_TYPE_ARRAY)
301 return SFE_MAT5_NO_BLOCK ;
303 psf_binheader_readf (psf, "44", &type, &size) ;
304 psf_log_printf (psf, " Type : %X Size : %d\n", type, size) ;
306 if (type != MAT5_TYPE_UINT32)
307 return SFE_MAT5_NO_BLOCK ;
309 psf_binheader_readf (psf, "44", &flags1, &flags2) ;
310 psf_log_printf (psf, " Flg1 : %X Flg2 : %d\n", flags1, flags2) ;
312 psf_binheader_readf (psf, "44", &type, &size) ;
313 psf_log_printf (psf, " Type : %X Size : %d\n", type, size) ;
315 if (type != MAT5_TYPE_INT32)
316 return SFE_MAT5_NO_BLOCK ;
318 psf_binheader_readf (psf, "44", &rows, &cols) ;
319 psf_log_printf (psf, " Rows : %X Cols : %d\n", rows, cols) ;
321 if (rows != 1 || cols != 1)
322 return SFE_MAT5_SAMPLE_RATE ;
324 psf_binheader_readf (psf, "4", &type) ;
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 ;
334 psf_binheader_readf (psf, "bj", name, size, (8 - (size % 8)) % 8) ;
337 else if ((type & 0xFFFF) == MAT5_TYPE_SCHAR)
338 { size = type >> 16 ;
340 { psf_log_printf (psf, "Error : Bad name length.\n") ;
341 return SFE_MAT5_NO_BLOCK ;
344 psf_log_printf (psf, " Type : %X\n", type) ;
345 psf_binheader_readf (psf, "4", &name) ;
349 return SFE_MAT5_NO_BLOCK ;
351 psf_log_printf (psf, " Name : %s\n", name) ;
353 /*-----------------------------------------*/
355 psf_binheader_readf (psf, "44", &type, &size) ;
358 { case MAT5_TYPE_DOUBLE :
359 { double samplerate ;
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) ;
365 psf->sf.samplerate = lrint (samplerate) ;
369 case MAT5_TYPE_COMP_USHORT :
370 { unsigned short samplerate ;
372 psf_binheader_readf (psf, "j2j", -4, &samplerate, 2) ;
373 psf_log_printf (psf, " Val : %u\n", samplerate) ;
374 psf->sf.samplerate = samplerate ;
378 case MAT5_TYPE_COMP_UINT :
379 psf_log_printf (psf, " Val : %u\n", size) ;
380 psf->sf.samplerate = size ;
384 psf_log_printf (psf, " Type : %X Size : %d ***\n", type, size) ;
385 return SFE_MAT5_SAMPLE_RATE ;
388 /*-----------------------------------------*/
391 psf_binheader_readf (psf, "44", &type, &size) ;
392 psf_log_printf (psf, " Type : %X Size : %d\n", type, size) ;
394 if (type != MAT5_TYPE_ARRAY)
395 return SFE_MAT5_NO_BLOCK ;
397 psf_binheader_readf (psf, "44", &type, &size) ;
398 psf_log_printf (psf, " Type : %X Size : %d\n", type, size) ;
400 if (type != MAT5_TYPE_UINT32)
401 return SFE_MAT5_NO_BLOCK ;
403 psf_binheader_readf (psf, "44", &flags1, &flags2) ;
404 psf_log_printf (psf, " Flg1 : %X Flg2 : %d\n", flags1, flags2) ;
406 psf_binheader_readf (psf, "44", &type, &size) ;
407 psf_log_printf (psf, " Type : %X Size : %d\n", type, size) ;
409 if (type != MAT5_TYPE_INT32)
410 return SFE_MAT5_NO_BLOCK ;
412 psf_binheader_readf (psf, "44", &rows, &cols) ;
413 psf_log_printf (psf, " Rows : %X Cols : %d\n", rows, cols) ;
415 psf_binheader_readf (psf, "4", &type) ;
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 ;
425 psf_binheader_readf (psf, "bj", name, size, (8 - (size % 8)) % 8) ;
428 else if ((type & 0xFFFF) == MAT5_TYPE_SCHAR)
429 { size = type >> 16 ;
431 { psf_log_printf (psf, "Error : Bad name length.\n") ;
432 return SFE_MAT5_NO_BLOCK ;
435 psf_log_printf (psf, " Type : %X\n", type) ;
436 psf_binheader_readf (psf, "4", &name) ;
440 return SFE_MAT5_NO_BLOCK ;
442 psf_log_printf (psf, " Name : %s\n", name) ;
444 psf_binheader_readf (psf, "44", &type, &size) ;
445 psf_log_printf (psf, " Type : %X Size : %d\n", type, size) ;
447 /*++++++++++++++++++++++++++++++++++++++++++++++++++*/
449 if (rows == 0 && cols == 0)
450 { psf_log_printf (psf, "*** Error : zero channel count.\n") ;
451 return SFE_MAT5_ZERO_CHANNELS ;
454 psf->sf.channels = rows ;
455 psf->sf.frames = cols ;
457 psf->sf.format = psf->endian | SF_FORMAT_MAT5 ;
460 { case MAT5_TYPE_DOUBLE :
461 psf_log_printf (psf, "Data type : double\n") ;
462 psf->sf.format |= SF_FORMAT_DOUBLE ;
466 case MAT5_TYPE_FLOAT :
467 psf_log_printf (psf, "Data type : float\n") ;
468 psf->sf.format |= SF_FORMAT_FLOAT ;
472 case MAT5_TYPE_INT32 :
473 psf_log_printf (psf, "Data type : 32 bit PCM\n") ;
474 psf->sf.format |= SF_FORMAT_PCM_32 ;
478 case MAT5_TYPE_INT16 :
479 psf_log_printf (psf, "Data type : 16 bit PCM\n") ;
480 psf->sf.format |= SF_FORMAT_PCM_16 ;
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 ;
491 psf_log_printf (psf, "*** Error : Bad marker %08X\n", type) ;
492 return SFE_UNIMPLEMENTED ;
495 psf->dataoffset = psf_ftell (psf) ;
496 psf->datalength = psf->filelength - psf->dataoffset ;
499 } /* mat5_read_header */
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.
506 ** arch-tag: dfdb6742-b2be-4be8-b390-d0c674e8bc8e