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 MAT4_BE_DOUBLE (MAKE_MARKER (0, 0, 0x03, 0xE8))
43 #define MAT4_LE_DOUBLE (MAKE_MARKER (0, 0, 0, 0))
45 #define MAT4_BE_FLOAT (MAKE_MARKER (0, 0, 0x03, 0xF2))
46 #define MAT4_LE_FLOAT (MAKE_MARKER (0x0A, 0, 0, 0))
48 #define MAT4_BE_PCM_32 (MAKE_MARKER (0, 0, 0x03, 0xFC))
49 #define MAT4_LE_PCM_32 (MAKE_MARKER (0x14, 0, 0, 0))
51 #define MAT4_BE_PCM_16 (MAKE_MARKER (0, 0, 0x04, 0x06))
52 #define MAT4_LE_PCM_16 (MAKE_MARKER (0x1E, 0, 0, 0))
54 /* Can't see any reason to ever implement this. */
55 #define MAT4_BE_PCM_U8 (MAKE_MARKER (0, 0, 0x04, 0x1A))
56 #define MAT4_LE_PCM_U8 (MAKE_MARKER (0x32, 0, 0, 0))
58 /*------------------------------------------------------------------------------
59 ** Private static functions.
62 static int mat4_close (SF_PRIVATE
*psf
) ;
64 static int mat4_format_to_encoding (int format
, int endian
) ;
66 static int mat4_write_header (SF_PRIVATE
*psf
, int calc_length
) ;
67 static int mat4_read_header (SF_PRIVATE
*psf
) ;
69 static const char * mat4_marker_to_str (int marker
) ;
71 /*------------------------------------------------------------------------------
76 mat4_open (SF_PRIVATE
*psf
)
77 { int subformat
, error
= 0 ;
79 if (psf
->file
.mode
== SFM_READ
|| (psf
->file
.mode
== SFM_RDWR
&& psf
->filelength
> 0))
80 { if ((error
= mat4_read_header (psf
)))
84 if ((SF_CONTAINER (psf
->sf
.format
)) != SF_FORMAT_MAT4
)
85 return SFE_BAD_OPEN_FORMAT
;
87 subformat
= SF_CODEC (psf
->sf
.format
) ;
89 if (psf
->file
.mode
== SFM_WRITE
|| psf
->file
.mode
== SFM_RDWR
)
91 return SFE_NO_PIPE_WRITE
;
93 psf
->endian
= SF_ENDIAN (psf
->sf
.format
) ;
94 if (CPU_IS_LITTLE_ENDIAN
&& (psf
->endian
== SF_ENDIAN_CPU
|| psf
->endian
== 0))
95 psf
->endian
= SF_ENDIAN_LITTLE
;
96 else if (CPU_IS_BIG_ENDIAN
&& (psf
->endian
== SF_ENDIAN_CPU
|| psf
->endian
== 0))
97 psf
->endian
= SF_ENDIAN_BIG
;
99 if ((error
= mat4_write_header (psf
, SF_FALSE
)))
102 psf
->write_header
= mat4_write_header
;
105 psf
->container_close
= mat4_close
;
107 psf
->blockwidth
= psf
->bytewidth
* psf
->sf
.channels
;
110 { case SF_FORMAT_PCM_16
:
111 case SF_FORMAT_PCM_32
:
112 error
= pcm_init (psf
) ;
115 case SF_FORMAT_FLOAT
:
116 error
= float32_init (psf
) ;
119 case SF_FORMAT_DOUBLE
:
120 error
= double64_init (psf
) ;
132 /*------------------------------------------------------------------------------
136 mat4_close (SF_PRIVATE
*psf
)
138 if (psf
->file
.mode
== SFM_WRITE
|| psf
->file
.mode
== SFM_RDWR
)
139 mat4_write_header (psf
, SF_TRUE
) ;
144 /*------------------------------------------------------------------------------
148 mat4_write_header (SF_PRIVATE
*psf
, int calc_length
)
149 { sf_count_t current
;
153 current
= psf_ftell (psf
) ;
156 { psf
->filelength
= psf_get_filelen (psf
) ;
158 psf
->datalength
= psf
->filelength
- psf
->dataoffset
;
160 psf
->datalength
-= psf
->filelength
- psf
->dataend
;
162 psf
->sf
.frames
= psf
->datalength
/ (psf
->bytewidth
* psf
->sf
.channels
) ;
165 encoding
= mat4_format_to_encoding (SF_CODEC (psf
->sf
.format
), psf
->endian
) ;
168 return SFE_BAD_OPEN_FORMAT
;
170 /* Reset the current header length to zero. */
171 psf
->header
[0] = 0 ;
173 psf_fseek (psf
, 0, SEEK_SET
) ;
175 /* Need sample rate as a double for writing to the header. */
176 samplerate
= psf
->sf
.samplerate
;
178 if (psf
->endian
== SF_ENDIAN_BIG
)
179 { psf_binheader_writef (psf
, "Em444", MAT4_BE_DOUBLE
, 1, 1, 0) ;
180 psf_binheader_writef (psf
, "E4bd", 11, "samplerate", make_size_t (11), samplerate
) ;
181 psf_binheader_writef (psf
, "tEm484", encoding
, psf
->sf
.channels
, psf
->sf
.frames
, 0) ;
182 psf_binheader_writef (psf
, "E4b", 9, "wavedata", make_size_t (9)) ;
184 else if (psf
->endian
== SF_ENDIAN_LITTLE
)
185 { psf_binheader_writef (psf
, "em444", MAT4_LE_DOUBLE
, 1, 1, 0) ;
186 psf_binheader_writef (psf
, "e4bd", 11, "samplerate", make_size_t (11), samplerate
) ;
187 psf_binheader_writef (psf
, "tem484", encoding
, psf
->sf
.channels
, psf
->sf
.frames
, 0) ;
188 psf_binheader_writef (psf
, "e4b", 9, "wavedata", make_size_t (9)) ;
191 return SFE_BAD_OPEN_FORMAT
;
193 /* Header construction complete so write it out. */
194 psf_fwrite (psf
->header
, psf
->headindex
, 1, psf
) ;
199 psf
->dataoffset
= psf
->headindex
;
202 psf_fseek (psf
, current
, SEEK_SET
) ;
205 } /* mat4_write_header */
208 mat4_read_header (SF_PRIVATE
*psf
)
209 { int marker
, rows
, cols
, imag
;
212 const char *marker_str
;
215 psf_binheader_readf (psf
, "pm", 0, &marker
) ;
217 /* MAT4 file must start with a double for the samplerate. */
218 if (marker
== MAT4_BE_DOUBLE
)
219 { psf
->endian
= psf
->rwf_endian
= SF_ENDIAN_BIG
;
220 marker_str
= "big endian double" ;
222 else if (marker
== MAT4_LE_DOUBLE
)
223 { psf
->endian
= psf
->rwf_endian
= SF_ENDIAN_LITTLE
;
224 marker_str
= "little endian double" ;
227 return SFE_UNIMPLEMENTED
;
229 psf_log_printf (psf
, "GNU Octave 2.0 / MATLAB v4.2 format\nMarker : %s\n", marker_str
) ;
231 psf_binheader_readf (psf
, "444", &rows
, &cols
, &imag
) ;
233 psf_log_printf (psf
, " Rows : %d\n Cols : %d\n Imag : %s\n", rows
, cols
, imag
? "True" : "False") ;
235 psf_binheader_readf (psf
, "4", &namesize
) ;
237 if (namesize
>= SIGNED_SIZEOF (name
))
238 return SFE_MAT4_BAD_NAME
;
240 psf_binheader_readf (psf
, "b", name
, namesize
) ;
241 name
[namesize
] = 0 ;
243 psf_log_printf (psf
, " Name : %s\n", name
) ;
245 psf_binheader_readf (psf
, "d", &value
) ;
247 snprintf (psf
->u
.cbuf
, sizeof (psf
->u
.cbuf
), " Value : %f\n", value
) ;
248 psf_log_printf (psf
, psf
->u
.cbuf
) ;
250 if ((rows
!= 1) || (cols
!= 1))
251 return SFE_MAT4_NO_SAMPLERATE
;
253 psf
->sf
.samplerate
= lrint (value
) ;
255 /* Now write out the audio data. */
257 psf_binheader_readf (psf
, "m", &marker
) ;
259 psf_log_printf (psf
, "Marker : %s\n", mat4_marker_to_str (marker
)) ;
261 psf_binheader_readf (psf
, "444", &rows
, &cols
, &imag
) ;
263 psf_log_printf (psf
, " Rows : %d\n Cols : %d\n Imag : %s\n", rows
, cols
, imag
? "True" : "False") ;
265 psf_binheader_readf (psf
, "4", &namesize
) ;
267 if (namesize
>= SIGNED_SIZEOF (name
))
268 return SFE_MAT4_BAD_NAME
;
270 psf_binheader_readf (psf
, "b", name
, namesize
) ;
271 name
[namesize
] = 0 ;
273 psf_log_printf (psf
, " Name : %s\n", name
) ;
275 psf
->dataoffset
= psf_ftell (psf
) ;
277 if (rows
== 0 && cols
== 0)
278 { psf_log_printf (psf
, "*** Error : zero channel count.\n") ;
279 return SFE_CHANNEL_COUNT_ZERO
;
282 psf
->sf
.channels
= rows
;
283 psf
->sf
.frames
= cols
;
285 psf
->sf
.format
= psf
->endian
| SF_FORMAT_MAT4
;
287 { case MAT4_BE_DOUBLE
:
288 case MAT4_LE_DOUBLE
:
289 psf
->sf
.format
|= SF_FORMAT_DOUBLE
;
295 psf
->sf
.format
|= SF_FORMAT_FLOAT
;
299 case MAT4_BE_PCM_32
:
300 case MAT4_LE_PCM_32
:
301 psf
->sf
.format
|= SF_FORMAT_PCM_32
;
305 case MAT4_BE_PCM_16
:
306 case MAT4_LE_PCM_16
:
307 psf
->sf
.format
|= SF_FORMAT_PCM_16
;
312 psf_log_printf (psf
, "*** Error : Bad marker %08X\n", marker
) ;
313 return SFE_UNIMPLEMENTED
;
316 if ((psf
->filelength
- psf
->dataoffset
) < psf
->sf
.channels
* psf
->sf
.frames
* psf
->bytewidth
)
317 { psf_log_printf (psf
, "*** File seems to be truncated. %D <--> %D\n",
318 psf
->filelength
- psf
->dataoffset
, psf
->sf
.channels
* psf
->sf
.frames
* psf
->bytewidth
) ;
320 else if ((psf
->filelength
- psf
->dataoffset
) > psf
->sf
.channels
* psf
->sf
.frames
* psf
->bytewidth
)
321 psf
->dataend
= psf
->dataoffset
+ rows
* cols
* psf
->bytewidth
;
323 psf
->datalength
= psf
->filelength
- psf
->dataoffset
- psf
->dataend
;
325 psf
->sf
.sections
= 1 ;
328 } /* mat4_read_header */
331 mat4_format_to_encoding (int format
, int endian
)
333 switch (format
| endian
)
334 { case (SF_FORMAT_PCM_16
| SF_ENDIAN_BIG
) :
335 return MAT4_BE_PCM_16
;
337 case (SF_FORMAT_PCM_16
| SF_ENDIAN_LITTLE
) :
338 return MAT4_LE_PCM_16
;
340 case (SF_FORMAT_PCM_32
| SF_ENDIAN_BIG
) :
341 return MAT4_BE_PCM_32
;
343 case (SF_FORMAT_PCM_32
| SF_ENDIAN_LITTLE
) :
344 return MAT4_LE_PCM_32
;
346 case (SF_FORMAT_FLOAT
| SF_ENDIAN_BIG
) :
347 return MAT4_BE_FLOAT
;
349 case (SF_FORMAT_FLOAT
| SF_ENDIAN_LITTLE
) :
350 return MAT4_LE_FLOAT
;
352 case (SF_FORMAT_DOUBLE
| SF_ENDIAN_BIG
) :
353 return MAT4_BE_DOUBLE
;
355 case (SF_FORMAT_DOUBLE
| SF_ENDIAN_LITTLE
) :
356 return MAT4_LE_DOUBLE
;
362 } /* mat4_format_to_encoding */
365 mat4_marker_to_str (int marker
)
366 { static char str
[32] ;
370 case MAT4_BE_PCM_16
: return "big endian 16 bit PCM" ;
371 case MAT4_LE_PCM_16
: return "little endian 16 bit PCM" ;
373 case MAT4_BE_PCM_32
: return "big endian 32 bit PCM" ;
374 case MAT4_LE_PCM_32
: return "little endian 32 bit PCM" ;
377 case MAT4_BE_FLOAT
: return "big endian float" ;
378 case MAT4_LE_FLOAT
: return "big endian float" ;
380 case MAT4_BE_DOUBLE
: return "big endian double" ;
381 case MAT4_LE_DOUBLE
: return "little endian double" ;
384 /* This is a little unsafe but is really only for debugging. */
385 str
[sizeof (str
) - 1] = 0 ;
386 snprintf (str
, sizeof (str
) - 1, "%08X", marker
) ;
388 } /* mat4_marker_to_str */