338477e07a147d3cdfe69faadfc3bec5413b836e
2 ** Copyright (C) 2002-2011 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.
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
->file
.mode
== SFM_READ
|| (psf
->file
.mode
== SFM_RDWR
&& psf
->filelength
> 0))
90 { if ((error
= mat5_read_header (psf
)))
94 if ((SF_CONTAINER (psf
->sf
.format
)) != SF_FORMAT_MAT5
)
95 return SFE_BAD_OPEN_FORMAT
;
97 subformat
= SF_CODEC (psf
->sf
.format
) ;
99 if (psf
->file
.mode
== SFM_WRITE
|| psf
->file
.mode
== SFM_RDWR
)
101 return SFE_NO_PIPE_WRITE
;
103 psf
->endian
= SF_ENDIAN (psf
->sf
.format
) ;
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
->file
.mode
== SFM_WRITE
|| psf
->file
.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 (SF_CODEC (psf
->sf
.format
))
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
, make_size_t (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", make_size_t (2)) ;
218 psf_binheader_writef (psf
, "2b", 0x0100, "IM", make_size_t (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
, make_size_t (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
, flags1
, flags2
, rows
, cols
;
265 psf_binheader_readf (psf
, "pb", 0, psf
->u
.cbuf
, 124) ;
267 psf
->u
.scbuf
[125] = 0 ;
269 if (strlen (psf
->u
.cbuf
) >= 124)
270 return SFE_UNIMPLEMENTED
;
272 if (strstr (psf
->u
.cbuf
, "MATLAB 5.0 MAT-file") == psf
->u
.cbuf
)
273 psf_log_printf (psf
, "%s\n", psf
->u
.scbuf
) ;
276 psf_binheader_readf (psf
, "E22", &version
, &endian
) ;
278 if (endian
== MI_MARKER
)
279 { psf
->endian
= psf
->rwf_endian
= SF_ENDIAN_BIG
;
280 if (CPU_IS_LITTLE_ENDIAN
) version
= ENDSWAP_SHORT (version
) ;
282 else if (endian
== IM_MARKER
)
283 { psf
->endian
= psf
->rwf_endian
= SF_ENDIAN_LITTLE
;
284 if (CPU_IS_BIG_ENDIAN
) version
= ENDSWAP_SHORT (version
) ;
287 return SFE_MAT5_BAD_ENDIAN
;
289 if ((CPU_IS_LITTLE_ENDIAN
&& endian
== IM_MARKER
) ||
290 (CPU_IS_BIG_ENDIAN
&& endian
== MI_MARKER
))
291 version
= ENDSWAP_SHORT (version
) ;
293 psf_log_printf (psf
, "Version : 0x%04X\n", version
) ;
294 psf_log_printf (psf
, "Endian : 0x%04X => %s\n", endian
,
295 (psf
->endian
== SF_ENDIAN_LITTLE
) ? "Little" : "Big") ;
297 /*========================================================*/
298 psf_binheader_readf (psf
, "44", &type
, &size
) ;
299 psf_log_printf (psf
, "Block\n Type : %X Size : %d\n", type
, size
) ;
301 if (type
!= MAT5_TYPE_ARRAY
)
302 return SFE_MAT5_NO_BLOCK
;
304 psf_binheader_readf (psf
, "44", &type
, &size
) ;
305 psf_log_printf (psf
, " Type : %X Size : %d\n", type
, size
) ;
307 if (type
!= MAT5_TYPE_UINT32
)
308 return SFE_MAT5_NO_BLOCK
;
310 psf_binheader_readf (psf
, "44", &flags1
, &flags2
) ;
311 psf_log_printf (psf
, " Flg1 : %X Flg2 : %d\n", flags1
, flags2
) ;
313 psf_binheader_readf (psf
, "44", &type
, &size
) ;
314 psf_log_printf (psf
, " Type : %X Size : %d\n", type
, size
) ;
316 if (type
!= MAT5_TYPE_INT32
)
317 return SFE_MAT5_NO_BLOCK
;
319 psf_binheader_readf (psf
, "44", &rows
, &cols
) ;
320 psf_log_printf (psf
, " Rows : %X Cols : %d\n", rows
, cols
) ;
322 if (rows
!= 1 || cols
!= 1)
323 return SFE_MAT5_SAMPLE_RATE
;
325 psf_binheader_readf (psf
, "4", &type
) ;
327 if (type
== MAT5_TYPE_SCHAR
)
328 { psf_binheader_readf (psf
, "4", &size
) ;
329 psf_log_printf (psf
, " Type : %X Size : %d\n", type
, size
) ;
330 if (size
> SIGNED_SIZEOF (name
) - 1)
331 { psf_log_printf (psf
, "Error : Bad name length.\n") ;
332 return SFE_MAT5_NO_BLOCK
;
335 psf_binheader_readf (psf
, "bj", name
, size
, (8 - (size
% 8)) % 8) ;
338 else if ((type
& 0xFFFF) == MAT5_TYPE_SCHAR
)
339 { size
= type
>> 16 ;
341 { psf_log_printf (psf
, "Error : Bad name length.\n") ;
342 return SFE_MAT5_NO_BLOCK
;
345 psf_log_printf (psf
, " Type : %X\n", type
) ;
346 psf_binheader_readf (psf
, "4", &name
) ;
350 return SFE_MAT5_NO_BLOCK
;
352 psf_log_printf (psf
, " Name : %s\n", name
) ;
354 /*-----------------------------------------*/
356 psf_binheader_readf (psf
, "44", &type
, &size
) ;
359 { case MAT5_TYPE_DOUBLE
:
360 { double samplerate
;
362 psf_binheader_readf (psf
, "d", &samplerate
) ;
363 snprintf (name
, sizeof (name
), "%f\n", samplerate
) ;
364 psf_log_printf (psf
, " Val : %s\n", name
) ;
366 psf
->sf
.samplerate
= lrint (samplerate
) ;
370 case MAT5_TYPE_COMP_USHORT
:
371 { unsigned short samplerate
;
373 psf_binheader_readf (psf
, "j2j", -4, &samplerate
, 2) ;
374 psf_log_printf (psf
, " Val : %u\n", samplerate
) ;
375 psf
->sf
.samplerate
= samplerate
;
379 case MAT5_TYPE_COMP_UINT
:
380 psf_log_printf (psf
, " Val : %u\n", size
) ;
381 psf
->sf
.samplerate
= size
;
385 psf_log_printf (psf
, " Type : %X Size : %d ***\n", type
, size
) ;
386 return SFE_MAT5_SAMPLE_RATE
;
389 /*-----------------------------------------*/
392 psf_binheader_readf (psf
, "44", &type
, &size
) ;
393 psf_log_printf (psf
, " Type : %X Size : %d\n", type
, size
) ;
395 if (type
!= MAT5_TYPE_ARRAY
)
396 return SFE_MAT5_NO_BLOCK
;
398 psf_binheader_readf (psf
, "44", &type
, &size
) ;
399 psf_log_printf (psf
, " Type : %X Size : %d\n", type
, size
) ;
401 if (type
!= MAT5_TYPE_UINT32
)
402 return SFE_MAT5_NO_BLOCK
;
404 psf_binheader_readf (psf
, "44", &flags1
, &flags2
) ;
405 psf_log_printf (psf
, " Flg1 : %X Flg2 : %d\n", flags1
, flags2
) ;
407 psf_binheader_readf (psf
, "44", &type
, &size
) ;
408 psf_log_printf (psf
, " Type : %X Size : %d\n", type
, size
) ;
410 if (type
!= MAT5_TYPE_INT32
)
411 return SFE_MAT5_NO_BLOCK
;
413 psf_binheader_readf (psf
, "44", &rows
, &cols
) ;
414 psf_log_printf (psf
, " Rows : %X Cols : %d\n", rows
, cols
) ;
416 psf_binheader_readf (psf
, "4", &type
) ;
418 if (type
== MAT5_TYPE_SCHAR
)
419 { psf_binheader_readf (psf
, "4", &size
) ;
420 psf_log_printf (psf
, " Type : %X Size : %d\n", type
, size
) ;
421 if (size
> SIGNED_SIZEOF (name
) - 1)
422 { psf_log_printf (psf
, "Error : Bad name length.\n") ;
423 return SFE_MAT5_NO_BLOCK
;
426 psf_binheader_readf (psf
, "bj", name
, size
, (8 - (size
% 8)) % 8) ;
429 else if ((type
& 0xFFFF) == MAT5_TYPE_SCHAR
)
430 { size
= type
>> 16 ;
432 { psf_log_printf (psf
, "Error : Bad name length.\n") ;
433 return SFE_MAT5_NO_BLOCK
;
436 psf_log_printf (psf
, " Type : %X\n", type
) ;
437 psf_binheader_readf (psf
, "4", &name
) ;
441 return SFE_MAT5_NO_BLOCK
;
443 psf_log_printf (psf
, " Name : %s\n", name
) ;
445 psf_binheader_readf (psf
, "44", &type
, &size
) ;
446 psf_log_printf (psf
, " Type : %X Size : %d\n", type
, size
) ;
448 /*++++++++++++++++++++++++++++++++++++++++++++++++++*/
450 if (rows
== 0 && cols
== 0)
451 { psf_log_printf (psf
, "*** Error : zero channel count.\n") ;
452 return SFE_CHANNEL_COUNT_ZERO
;
455 psf
->sf
.channels
= rows
;
456 psf
->sf
.frames
= cols
;
458 psf
->sf
.format
= psf
->endian
| SF_FORMAT_MAT5
;
461 { case MAT5_TYPE_DOUBLE
:
462 psf_log_printf (psf
, "Data type : double\n") ;
463 psf
->sf
.format
|= SF_FORMAT_DOUBLE
;
467 case MAT5_TYPE_FLOAT
:
468 psf_log_printf (psf
, "Data type : float\n") ;
469 psf
->sf
.format
|= SF_FORMAT_FLOAT
;
473 case MAT5_TYPE_INT32
:
474 psf_log_printf (psf
, "Data type : 32 bit PCM\n") ;
475 psf
->sf
.format
|= SF_FORMAT_PCM_32
;
479 case MAT5_TYPE_INT16
:
480 psf_log_printf (psf
, "Data type : 16 bit PCM\n") ;
481 psf
->sf
.format
|= SF_FORMAT_PCM_16
;
485 case MAT5_TYPE_UCHAR
:
486 psf_log_printf (psf
, "Data type : unsigned 8 bit PCM\n") ;
487 psf
->sf
.format
|= SF_FORMAT_PCM_U8
;
492 psf_log_printf (psf
, "*** Error : Bad marker %08X\n", type
) ;
493 return SFE_UNIMPLEMENTED
;
496 psf
->dataoffset
= psf_ftell (psf
) ;
497 psf
->datalength
= psf
->filelength
- psf
->dataoffset
;
500 } /* mat5_read_header */