1/* 2** Copyright (C) 2002-2017 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#include <math.h> 26 27#include "sndfile.h" 28#include "sfendian.h" 29#include "common.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 51enum 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 66typedef 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 76static int mat5_close (SF_PRIVATE *psf) ; 77 78static int mat5_write_header (SF_PRIVATE *psf, int calc_length) ; 79static int mat5_read_header (SF_PRIVATE *psf) ; 80 81/*------------------------------------------------------------------------------ 82** Public function. 83*/ 84 85int 86mat5_open (SF_PRIVATE *psf) 87{ int subformat, error = 0 ; 88 89 if (psf->file.mode == SFM_READ || (psf->file.mode == SFM_RDWR && psf->filelength > 0)) 90 { if ((error = mat5_read_header (psf))) 91 return error ; 92 } ; 93 94 if ((SF_CONTAINER (psf->sf.format)) != SF_FORMAT_MAT5) 95 return SFE_BAD_OPEN_FORMAT ; 96 97 subformat = SF_CODEC (psf->sf.format) ; 98 99 if (psf->file.mode == SFM_WRITE || psf->file.mode == SFM_RDWR) 100 { if (psf->is_pipe) 101 return SFE_NO_PIPE_WRITE ; 102 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 ; 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 143static int 144mat5_close (SF_PRIVATE *psf) 145{ 146 if (psf->file.mode == SFM_WRITE || psf->file.mode == SFM_RDWR) 147 mat5_write_header (psf, SF_TRUE) ; 148 149 return 0 ; 150} /* mat5_close */ 151 152/*------------------------------------------------------------------------------ 153*/ 154 155static int 156mat5_write_header (SF_PRIVATE *psf, int calc_length) 157{ static const char *filename = "MATLAB 5.0 MAT-file, written by " PACKAGE_NAME "-" 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 char buffer [256] ; 161 sf_count_t current, datasize ; 162 int encoding ; 163 164 current = psf_ftell (psf) ; 165 166 if (calc_length) 167 { psf_fseek (psf, 0, SEEK_END) ; 168 psf->filelength = psf_ftell (psf) ; 169 psf_fseek (psf, 0, SEEK_SET) ; 170 171 psf->datalength = psf->filelength - psf->dataoffset ; 172 if (psf->dataend) 173 psf->datalength -= psf->filelength - psf->dataend ; 174 175 psf->sf.frames = psf->datalength / (psf->bytewidth * psf->sf.channels) ; 176 } ; 177 178 switch (SF_CODEC (psf->sf.format)) 179 { case SF_FORMAT_PCM_U8 : 180 encoding = MAT5_TYPE_UCHAR ; 181 break ; 182 183 case SF_FORMAT_PCM_16 : 184 encoding = MAT5_TYPE_INT16 ; 185 break ; 186 187 case SF_FORMAT_PCM_32 : 188 encoding = MAT5_TYPE_INT32 ; 189 break ; 190 191 case SF_FORMAT_FLOAT : 192 encoding = MAT5_TYPE_FLOAT ; 193 break ; 194 195 case SF_FORMAT_DOUBLE : 196 encoding = MAT5_TYPE_DOUBLE ; 197 break ; 198 199 default : 200 return SFE_BAD_OPEN_FORMAT ; 201 } ; 202 203 /* Reset the current header length to zero. */ 204 psf->header.ptr [0] = 0 ; 205 psf->header.indx = 0 ; 206 psf_fseek (psf, 0, SEEK_SET) ; 207 208 psf_get_date_str (buffer, sizeof (buffer)) ; 209 psf_binheader_writef (psf, "bb", BHWv (filename), BHWz (strlen (filename)), BHWv (buffer), BHWz (strlen (buffer) + 1)) ; 210 211 memset (buffer, ' ', 124 - psf->header.indx) ; 212 psf_binheader_writef (psf, "b", BHWv (buffer), BHWz (124 - psf->header.indx)) ; 213 214 psf->rwf_endian = psf->endian ; 215 216 if (psf->rwf_endian == SF_ENDIAN_BIG) 217 psf_binheader_writef (psf, "2b", BHW2 (0x0100), BHWv ("MI"), BHWz (2)) ; 218 else 219 psf_binheader_writef (psf, "2b", BHW2 (0x0100), BHWv ("IM"), BHWz (2)) ; 220 221 psf_binheader_writef (psf, "444444", BHW4 (MAT5_TYPE_ARRAY), BHW4 (64), BHW4 (MAT5_TYPE_UINT32), BHW4 (8), BHW4 (6), BHW4 (0)) ; 222 psf_binheader_writef (psf, "4444", BHW4 (MAT5_TYPE_INT32), BHW4 (8), BHW4 (1), BHW4 (1)) ; 223 psf_binheader_writef (psf, "44b", BHW4 (MAT5_TYPE_SCHAR), BHW4 (strlen (sr_name)), BHWv (sr_name), BHWz (16)) ; 224 225 if (psf->sf.samplerate > 0xFFFF) 226 psf_binheader_writef (psf, "44", BHW4 (MAT5_TYPE_COMP_UINT), BHW4 (psf->sf.samplerate)) ; 227 else 228 { unsigned short samplerate = psf->sf.samplerate ; 229 230 psf_binheader_writef (psf, "422", BHW4 (MAT5_TYPE_COMP_USHORT), BHW2 (samplerate), BHW2 (0)) ; 231 } ; 232 233 datasize = psf->sf.frames * psf->sf.channels * psf->bytewidth ; 234 235 psf_binheader_writef (psf, "t484444", BHW4 (MAT5_TYPE_ARRAY), BHW8 (datasize + 64), BHW4 (MAT5_TYPE_UINT32), BHW4 (8), BHW4 (6), BHW4 (0)) ; 236 psf_binheader_writef (psf, "t4448", BHW4 (MAT5_TYPE_INT32), BHW4 (8), BHW4 (psf->sf.channels), BHW8 (psf->sf.frames)) ; 237 psf_binheader_writef (psf, "44b", BHW4 (MAT5_TYPE_SCHAR), BHW4 (strlen (wd_name)), BHWv (wd_name), BHWz (strlen (wd_name))) ; 238 239 datasize = psf->sf.frames * psf->sf.channels * psf->bytewidth ; 240 if (datasize > 0x7FFFFFFF) 241 datasize = 0x7FFFFFFF ; 242 243 psf_binheader_writef (psf, "t48", BHW4 (encoding), BHW8 (datasize)) ; 244 245 /* Header construction complete so write it out. */ 246 psf_fwrite (psf->header.ptr, psf->header.indx, 1, psf) ; 247 248 if (psf->error) 249 return psf->error ; 250 251 psf->dataoffset = psf->header.indx ; 252 253 if (current > 0) 254 psf_fseek (psf, current, SEEK_SET) ; 255 256 return psf->error ; 257} /* mat5_write_header */ 258 259static int 260mat5_read_header (SF_PRIVATE *psf) 261{ char buffer [256], name [32] ; 262 short version, endian ; 263 int type, flags1, flags2, rows, cols ; 264 unsigned size ; 265 int have_samplerate = 1 ; 266 267 psf_binheader_readf (psf, "pb", 0, buffer, 124) ; 268 269 buffer [125] = 0 ; 270 271 if (strlen (buffer) >= 124) 272 return SFE_UNIMPLEMENTED ; 273 274 if (strstr (buffer, "MATLAB 5.0 MAT-file") == buffer) 275 psf_log_printf (psf, "%s\n", buffer) ; 276 277 278 psf_binheader_readf (psf, "E22", &version, &endian) ; 279 280 if (endian == MI_MARKER) 281 { psf->endian = psf->rwf_endian = SF_ENDIAN_BIG ; 282 if (CPU_IS_LITTLE_ENDIAN) version = ENDSWAP_16 (version) ; 283 } 284 else if (endian == IM_MARKER) 285 { psf->endian = psf->rwf_endian = SF_ENDIAN_LITTLE ; 286 if (CPU_IS_BIG_ENDIAN) version = ENDSWAP_16 (version) ; 287 } 288 else 289 return SFE_MAT5_BAD_ENDIAN ; 290 291 if ((CPU_IS_LITTLE_ENDIAN && endian == IM_MARKER) || 292 (CPU_IS_BIG_ENDIAN && endian == MI_MARKER)) 293 version = ENDSWAP_16 (version) ; 294 295 psf_log_printf (psf, "Version : 0x%04X\n", version) ; 296 psf_log_printf (psf, "Endian : 0x%04X => %s\n", endian, 297 (psf->endian == SF_ENDIAN_LITTLE) ? "Little" : "Big") ; 298 299 /*========================================================*/ 300 psf_binheader_readf (psf, "44", &type, &size) ; 301 psf_log_printf (psf, "Block\n Type : %X Size : %d\n", type, size) ; 302 303 if (type != MAT5_TYPE_ARRAY) 304 return SFE_MAT5_NO_BLOCK ; 305 306 psf_binheader_readf (psf, "44", &type, &size) ; 307 psf_log_printf (psf, " Type : %X Size : %d\n", type, size) ; 308 309 if (type != MAT5_TYPE_UINT32) 310 return SFE_MAT5_NO_BLOCK ; 311 312 psf_binheader_readf (psf, "44", &flags1, &flags2) ; 313 psf_log_printf (psf, " Flg1 : %X Flg2 : %d\n", flags1, flags2) ; 314 315 psf_binheader_readf (psf, "44", &type, &size) ; 316 psf_log_printf (psf, " Type : %X Size : %d\n", type, size) ; 317 318 if (type != MAT5_TYPE_INT32) 319 return SFE_MAT5_NO_BLOCK ; 320 321 psf_binheader_readf (psf, "44", &rows, &cols) ; 322 psf_log_printf (psf, " Rows : %d Cols : %d\n", rows, cols) ; 323 324 if (rows != 1 || cols != 1) 325 { if (psf->sf.samplerate == 0) 326 psf->sf.samplerate = 44100 ; 327 have_samplerate = 0 ; 328 } 329 psf_binheader_readf (psf, "4", &type) ; 330 331 if (type == MAT5_TYPE_SCHAR) 332 { psf_binheader_readf (psf, "4", &size) ; 333 psf_log_printf (psf, " Type : %X Size : %d\n", type, size) ; 334 if (size > SIGNED_SIZEOF (name) - 1) 335 { psf_log_printf (psf, "Error : Bad name length.\n") ; 336 return SFE_MAT5_NO_BLOCK ; 337 } ; 338 339 psf_binheader_readf (psf, "bj", name, size, (8 - (size % 8)) % 8) ; 340 name [size] = 0 ; 341 } 342 else if ((type & 0xFFFF) == MAT5_TYPE_SCHAR) 343 { size = type >> 16 ; 344 if (size > 4) 345 { psf_log_printf (psf, "Error : Bad name length.\n") ; 346 return SFE_MAT5_NO_BLOCK ; 347 } ; 348 349 psf_log_printf (psf, " Type : %X\n", type) ; 350 psf_binheader_readf (psf, "4", &name) ; 351 name [size] = 0 ; 352 } 353 else 354 return SFE_MAT5_NO_BLOCK ; 355 356 psf_log_printf (psf, " Name : %s\n", name) ; 357 358 /*-----------------------------------------*/ 359 360 psf_binheader_readf (psf, "44", &type, &size) ; 361 362 if (!have_samplerate) 363 goto skip_samplerate ; 364 365 switch (type) 366 { case MAT5_TYPE_DOUBLE : 367 { double samplerate ; 368 369 psf_binheader_readf (psf, "d", &samplerate) ; 370 snprintf (name, sizeof (name), "%f\n", samplerate) ; 371 psf_log_printf (psf, " Val : %s\n", name) ; 372 373 psf->sf.samplerate = psf_lrint (samplerate) ; 374 } ; 375 break ; 376 377 case MAT5_TYPE_COMP_USHORT : 378 { unsigned short samplerate ; 379 380 psf_binheader_readf (psf, "j2j", -4, &samplerate, 2) ; 381 psf_log_printf (psf, " Val : %u\n", samplerate) ; 382 psf->sf.samplerate = samplerate ; 383 } 384 break ; 385 386 case MAT5_TYPE_COMP_UINT : 387 psf_log_printf (psf, " Val : %u\n", size) ; 388 psf->sf.samplerate = size ; 389 break ; 390 391 default : 392 psf_log_printf (psf, " Type : %X Size : %d ***\n", type, size) ; 393 return SFE_MAT5_SAMPLE_RATE ; 394 } ; 395 396 /*-----------------------------------------*/ 397 398 399 psf_binheader_readf (psf, "44", &type, &size) ; 400 psf_log_printf (psf, " Type : %X Size : %d\n", type, size) ; 401 402 if (type != MAT5_TYPE_ARRAY) 403 return SFE_MAT5_NO_BLOCK ; 404 405 psf_binheader_readf (psf, "44", &type, &size) ; 406 psf_log_printf (psf, " Type : %X Size : %d\n", type, size) ; 407 408 if (type != MAT5_TYPE_UINT32) 409 return SFE_MAT5_NO_BLOCK ; 410 411 psf_binheader_readf (psf, "44", &flags1, &flags2) ; 412 psf_log_printf (psf, " Flg1 : %X Flg2 : %d\n", flags1, flags2) ; 413 414 psf_binheader_readf (psf, "44", &type, &size) ; 415 psf_log_printf (psf, " Type : %X Size : %d\n", type, size) ; 416 417 if (type != MAT5_TYPE_INT32) 418 return SFE_MAT5_NO_BLOCK ; 419 420 psf_binheader_readf (psf, "44", &rows, &cols) ; 421 psf_log_printf (psf, " Rows : %X Cols : %d\n", rows, cols) ; 422 423 psf_binheader_readf (psf, "4", &type) ; 424 425 if (type == MAT5_TYPE_SCHAR) 426 { psf_binheader_readf (psf, "4", &size) ; 427 psf_log_printf (psf, " Type : %X Size : %d\n", type, size) ; 428 if (size > SIGNED_SIZEOF (name) - 1) 429 { psf_log_printf (psf, "Error : Bad name length.\n") ; 430 return SFE_MAT5_NO_BLOCK ; 431 } ; 432 433 psf_binheader_readf (psf, "bj", name, size, (8 - (size % 8)) % 8) ; 434 name [size] = 0 ; 435 } 436 else if ((type & 0xFFFF) == MAT5_TYPE_SCHAR) 437 { size = type >> 16 ; 438 if (size > 4) 439 { psf_log_printf (psf, "Error : Bad name length.\n") ; 440 return SFE_MAT5_NO_BLOCK ; 441 } ; 442 443 psf_log_printf (psf, " Type : %X\n", type) ; 444 psf_binheader_readf (psf, "4", &name) ; 445 name [size] = 0 ; 446 } 447 else 448 return SFE_MAT5_NO_BLOCK ; 449 450 psf_log_printf (psf, " Name : %s\n", name) ; 451 452 psf_binheader_readf (psf, "44", &type, &size) ; 453 psf_log_printf (psf, " Type : %X Size : %d\n", type, size) ; 454 455skip_samplerate : 456 /*++++++++++++++++++++++++++++++++++++++++++++++++++*/ 457 458 if (rows == 0 && cols == 0) 459 { psf_log_printf (psf, "*** Error : zero channel count.\n") ; 460 return SFE_CHANNEL_COUNT_ZERO ; 461 } ; 462 463 psf->sf.channels = rows ; 464 psf->sf.frames = cols ; 465 466 psf->sf.format = psf->endian | SF_FORMAT_MAT5 ; 467 468 switch (type) 469 { case MAT5_TYPE_DOUBLE : 470 psf_log_printf (psf, "Data type : double\n") ; 471 psf->sf.format |= SF_FORMAT_DOUBLE ; 472 psf->bytewidth = 8 ; 473 break ; 474 475 case MAT5_TYPE_FLOAT : 476 psf_log_printf (psf, "Data type : float\n") ; 477 psf->sf.format |= SF_FORMAT_FLOAT ; 478 psf->bytewidth = 4 ; 479 break ; 480 481 case MAT5_TYPE_INT32 : 482 psf_log_printf (psf, "Data type : 32 bit PCM\n") ; 483 psf->sf.format |= SF_FORMAT_PCM_32 ; 484 psf->bytewidth = 4 ; 485 break ; 486 487 case MAT5_TYPE_INT16 : 488 psf_log_printf (psf, "Data type : 16 bit PCM\n") ; 489 psf->sf.format |= SF_FORMAT_PCM_16 ; 490 psf->bytewidth = 2 ; 491 break ; 492 493 case MAT5_TYPE_UCHAR : 494 psf_log_printf (psf, "Data type : unsigned 8 bit PCM\n") ; 495 psf->sf.format |= SF_FORMAT_PCM_U8 ; 496 psf->bytewidth = 1 ; 497 break ; 498 499 default : 500 psf_log_printf (psf, "*** Error : Bad marker %08X\n", type) ; 501 return SFE_UNIMPLEMENTED ; 502 } ; 503 504 psf->dataoffset = psf_ftell (psf) ; 505 psf->datalength = psf->filelength - psf->dataoffset ; 506 507 return 0 ; 508} /* mat5_read_header */ 509 510