Coverage Report

Created: 2025-10-13 06:57

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libsndfile/src/mat4.c
Line
Count
Source
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
668
#define MAT4_BE_DOUBLE  (MAKE_MARKER (0, 0, 0x03, 0xE8))
43
994
#define MAT4_LE_DOUBLE  (MAKE_MARKER (0, 0, 0, 0))
44
45
2
#define MAT4_BE_FLOAT (MAKE_MARKER (0, 0, 0x03, 0xF2))
46
100
#define MAT4_LE_FLOAT (MAKE_MARKER (0x0A, 0, 0, 0))
47
48
3
#define MAT4_BE_PCM_32  (MAKE_MARKER (0, 0, 0x03, 0xFC))
49
104
#define MAT4_LE_PCM_32  (MAKE_MARKER (0x14, 0, 0, 0))
50
51
15
#define MAT4_BE_PCM_16  (MAKE_MARKER (0, 0, 0x04, 0x06))
52
76
#define MAT4_LE_PCM_16  (MAKE_MARKER (0x1E, 0, 0, 0))
53
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))
57
58
/*------------------------------------------------------------------------------
59
** Private static functions.
60
*/
61
62
static  int   mat4_close    (SF_PRIVATE *psf) ;
63
64
static  int   mat4_format_to_encoding (int format, int endian) ;
65
66
static int    mat4_write_header (SF_PRIVATE *psf, int calc_length) ;
67
static int    mat4_read_header (SF_PRIVATE *psf) ;
68
69
static const char * mat4_marker_to_str (int marker) ;
70
71
/*------------------------------------------------------------------------------
72
** Public function.
73
*/
74
75
int
76
mat4_open (SF_PRIVATE *psf)
77
665
{ int   subformat, error = 0 ;
78
79
665
  if (psf->file.mode == SFM_READ || (psf->file.mode == SFM_RDWR && psf->filelength > 0))
80
665
  { if ((error = mat4_read_header (psf)))
81
374
      return error ;
82
665
    } ;
83
84
291
  if ((SF_CONTAINER (psf->sf.format)) != SF_FORMAT_MAT4)
85
0
    return  SFE_BAD_OPEN_FORMAT ;
86
87
291
  subformat = SF_CODEC (psf->sf.format) ;
88
89
291
  if (psf->file.mode == SFM_WRITE || psf->file.mode == SFM_RDWR)
90
0
  { if (psf->is_pipe)
91
0
      return SFE_NO_PIPE_WRITE ;
92
93
0
    psf->endian = SF_ENDIAN (psf->sf.format) ;
94
0
    if (CPU_IS_LITTLE_ENDIAN && (psf->endian == SF_ENDIAN_CPU || psf->endian == 0))
95
0
      psf->endian = SF_ENDIAN_LITTLE ;
96
0
    else if (CPU_IS_BIG_ENDIAN && (psf->endian == SF_ENDIAN_CPU || psf->endian == 0))
97
0
      psf->endian = SF_ENDIAN_BIG ;
98
99
0
    if ((error = mat4_write_header (psf, SF_FALSE)))
100
0
      return error ;
101
102
0
    psf->write_header = mat4_write_header ;
103
291
    } ;
104
105
291
  psf->container_close = mat4_close ;
106
107
291
  psf->blockwidth = (sf_count_t) psf->bytewidth * psf->sf.channels ;
108
109
291
  switch (subformat)
110
291
  { case SF_FORMAT_PCM_16 :
111
93
    case SF_FORMAT_PCM_32 :
112
93
        error = pcm_init (psf) ;
113
93
        break ;
114
115
50
    case SF_FORMAT_FLOAT :
116
50
        error = float32_init (psf) ;
117
50
        break ;
118
119
148
    case SF_FORMAT_DOUBLE :
120
148
        error = double64_init (psf) ;
121
148
        break ;
122
123
0
    default : break ;
124
291
    } ;
125
126
291
  return error ;
127
291
} /* mat4_open */
128
129
/*------------------------------------------------------------------------------
130
*/
131
132
static int
133
mat4_close  (SF_PRIVATE *psf)
134
291
{
135
291
  if (psf->file.mode == SFM_WRITE || psf->file.mode == SFM_RDWR)
136
0
    mat4_write_header (psf, SF_TRUE) ;
137
138
291
  return 0 ;
139
291
} /* mat4_close */
140
141
/*------------------------------------------------------------------------------
142
*/
143
144
static int
145
mat4_write_header (SF_PRIVATE *psf, int calc_length)
146
0
{ sf_count_t  current ;
147
0
  int     encoding ;
148
0
  double    samplerate ;
149
150
0
  current = psf_ftell (psf) ;
151
152
0
  if (calc_length)
153
0
  { psf->filelength = psf_get_filelen (psf) ;
154
155
0
    psf->datalength = psf->filelength - psf->dataoffset ;
156
0
    if (psf->dataend)
157
0
      psf->datalength -= psf->filelength - psf->dataend ;
158
159
0
    psf->sf.frames = psf->datalength / (psf->bytewidth * psf->sf.channels) ;
160
0
    } ;
161
162
0
  encoding = mat4_format_to_encoding (SF_CODEC (psf->sf.format), psf->endian) ;
163
164
0
  if (encoding == -1)
165
0
    return SFE_BAD_OPEN_FORMAT ;
166
167
  /* Reset the current header length to zero. */
168
0
  psf->header.ptr [0] = 0 ;
169
0
  psf->header.indx = 0 ;
170
0
  psf_fseek (psf, 0, SEEK_SET) ;
171
172
  /* Need sample rate as a double for writing to the header. */
173
0
  samplerate = psf->sf.samplerate ;
174
175
0
  if (psf->endian == SF_ENDIAN_BIG)
176
0
  { psf_binheader_writef (psf, "Em444", BHWm (MAT4_BE_DOUBLE), BHW4 (1), BHW4 (1), BHW4 (0)) ;
177
0
    psf_binheader_writef (psf, "E4bd", BHW4 (11), BHWv ("samplerate"), BHWz (11), BHWd (samplerate)) ;
178
0
    psf_binheader_writef (psf, "tEm484", BHWm (encoding), BHW4 (psf->sf.channels), BHW8 (psf->sf.frames), BHW4 (0)) ;
179
0
    psf_binheader_writef (psf, "E4b", BHW4 (9), BHWv ("wavedata"), BHWz (9)) ;
180
0
    }
181
0
  else if (psf->endian == SF_ENDIAN_LITTLE)
182
0
  { psf_binheader_writef (psf, "em444", BHWm (MAT4_LE_DOUBLE), BHW4 (1), BHW4 (1), BHW4 (0)) ;
183
0
    psf_binheader_writef (psf, "e4bd", BHW4 (11), BHWv ("samplerate"), BHWz (11), BHWd (samplerate)) ;
184
0
    psf_binheader_writef (psf, "tem484", BHWm (encoding), BHW4 (psf->sf.channels), BHW8 (psf->sf.frames), BHW4 (0)) ;
185
0
    psf_binheader_writef (psf, "e4b", BHW4 (9), BHWv ("wavedata"), BHWz (9)) ;
186
0
    }
187
0
  else
188
0
    return SFE_BAD_OPEN_FORMAT ;
189
190
  /* Header construction complete so write it out. */
191
0
  psf_fwrite (psf->header.ptr, psf->header.indx, 1, psf) ;
192
193
0
  if (psf->error)
194
0
    return psf->error ;
195
196
0
  psf->dataoffset = psf->header.indx ;
197
198
0
  if (current > 0)
199
0
    psf_fseek (psf, current, SEEK_SET) ;
200
201
0
  return psf->error ;
202
0
} /* mat4_write_header */
203
204
static int
205
mat4_read_header (SF_PRIVATE *psf)
206
665
{ char  buffer [256] ;
207
665
  uint32_t marker, namesize ;
208
665
  int   rows, cols, imag ;
209
665
  double  value ;
210
665
  const char *marker_str ;
211
665
  char  name [64] ;
212
213
665
  psf_binheader_readf (psf, "pm", 0, &marker) ;
214
215
  /* MAT4 file must start with a double for the samplerate. */
216
665
  if (marker == MAT4_BE_DOUBLE)
217
14
  { psf->endian = psf->rwf_endian = SF_ENDIAN_BIG ;
218
14
    marker_str = "big endian double" ;
219
14
    }
220
651
  else if (marker == MAT4_LE_DOUBLE)
221
651
  { psf->endian = psf->rwf_endian = SF_ENDIAN_LITTLE ;
222
651
    marker_str = "little endian double" ;
223
651
    }
224
0
  else
225
0
    return SFE_UNIMPLEMENTED ;
226
227
665
  psf_log_printf (psf, "GNU Octave 2.0 / MATLAB v4.2 format\nMarker : %s\n", marker_str) ;
228
229
665
  psf_binheader_readf (psf, "444", &rows, &cols, &imag) ;
230
231
665
  psf_log_printf (psf, " Rows  : %d\n Cols  : %d\n Imag  : %s\n", rows, cols, imag ? "True" : "False") ;
232
233
665
  psf_binheader_readf (psf, "4", &namesize) ;
234
235
665
  if (namesize >= SIGNED_SIZEOF (name))
236
37
    return SFE_MAT4_BAD_NAME ;
237
238
628
  psf_binheader_readf (psf, "b", name, namesize) ;
239
628
  name [namesize] = 0 ;
240
241
628
  psf_log_printf (psf, " Name  : %s\n", name) ;
242
243
628
  psf_binheader_readf (psf, "d", &value) ;
244
245
628
  snprintf (buffer, sizeof (buffer), " Value : %f\n", value) ;
246
628
  psf_log_printf (psf, buffer) ;
247
248
628
  if ((rows != 1) || (cols != 1))
249
0
    return SFE_MAT4_NO_SAMPLERATE ;
250
251
628
  psf->sf.samplerate = psf_lrint (value) ;
252
253
  /* Now write out the audio data. */
254
255
628
  psf_binheader_readf (psf, "m", &marker) ;
256
257
628
  psf_log_printf (psf, "Marker : %s\n", mat4_marker_to_str (marker)) ;
258
259
628
  psf_binheader_readf (psf, "444", &rows, &cols, &imag) ;
260
261
628
  psf_log_printf (psf, " Rows  : %d\n Cols  : %d\n Imag  : %s\n", rows, cols, imag ? "True" : "False") ;
262
263
628
  psf_binheader_readf (psf, "4", &namesize) ;
264
265
628
  if (namesize >= SIGNED_SIZEOF (name))
266
15
    return SFE_MAT4_BAD_NAME ;
267
268
613
  psf_binheader_readf (psf, "b", name, namesize) ;
269
613
  name [namesize] = 0 ;
270
271
613
  psf_log_printf (psf, " Name  : %s\n", name) ;
272
273
613
  psf->dataoffset = psf_ftell (psf) ;
274
275
613
  if (rows == 0)
276
95
  { psf_log_printf (psf, "*** Error : zero channel count.\n") ;
277
95
    return SFE_CHANNEL_COUNT_ZERO ;
278
95
    }
279
518
  else if (rows > SF_MAX_CHANNELS)
280
17
  { psf_log_printf (psf, "*** Error : channel count %d > SF_MAX_CHANNELS.\n", rows) ;
281
17
    return SFE_CHANNEL_COUNT ;
282
501
    } ;
283
284
501
  psf->sf.channels  = rows ;
285
501
  psf->sf.frames    = cols ;
286
287
501
  psf->sf.format = psf->endian | SF_FORMAT_MAT4 ;
288
501
  switch (marker)
289
501
  { case MAT4_BE_DOUBLE :
290
148
    case MAT4_LE_DOUBLE :
291
148
        psf->sf.format |= SF_FORMAT_DOUBLE ;
292
148
        psf->bytewidth = 8 ;
293
148
        break ;
294
295
1
    case MAT4_BE_FLOAT :
296
50
    case MAT4_LE_FLOAT :
297
50
        psf->sf.format |= SF_FORMAT_FLOAT ;
298
50
        psf->bytewidth = 4 ;
299
50
        break ;
300
301
1
    case MAT4_BE_PCM_32  :
302
52
    case MAT4_LE_PCM_32  :
303
52
        psf->sf.format |= SF_FORMAT_PCM_32 ;
304
52
        psf->bytewidth = 4 ;
305
52
        break ;
306
307
7
    case MAT4_BE_PCM_16  :
308
41
    case MAT4_LE_PCM_16  :
309
41
        psf->sf.format |= SF_FORMAT_PCM_16 ;
310
41
        psf->bytewidth = 2 ;
311
41
        break ;
312
313
210
    default :
314
210
        psf_log_printf (psf, "*** Error : Bad marker %08X\n", marker) ;
315
210
        return SFE_UNIMPLEMENTED ;
316
501
    } ;
317
318
291
  if ((psf->filelength - psf->dataoffset) < psf->sf.channels * psf->sf.frames * psf->bytewidth)
319
4
  { psf_log_printf (psf, "*** File seems to be truncated. %D <--> %D\n",
320
4
        psf->filelength - psf->dataoffset, psf->sf.channels * psf->sf.frames * psf->bytewidth) ;
321
4
    }
322
287
  else if ((psf->filelength - psf->dataoffset) > psf->sf.channels * psf->sf.frames * psf->bytewidth)
323
245
    psf->dataend = psf->dataoffset + (sf_count_t) rows * (sf_count_t) cols * psf->bytewidth ;
324
325
291
  psf->datalength = psf->filelength - psf->dataoffset - psf->dataend ;
326
327
291
  psf->sf.sections = 1 ;
328
329
291
  return 0 ;
330
501
} /* mat4_read_header */
331
332
static int
333
mat4_format_to_encoding (int format, int endian)
334
0
{
335
0
  switch (format | endian)
336
0
  { case (SF_FORMAT_PCM_16 | SF_ENDIAN_BIG) :
337
0
        return MAT4_BE_PCM_16 ;
338
339
0
    case (SF_FORMAT_PCM_16 | SF_ENDIAN_LITTLE) :
340
0
        return MAT4_LE_PCM_16 ;
341
342
0
    case (SF_FORMAT_PCM_32 | SF_ENDIAN_BIG) :
343
0
        return MAT4_BE_PCM_32 ;
344
345
0
    case (SF_FORMAT_PCM_32 | SF_ENDIAN_LITTLE) :
346
0
        return MAT4_LE_PCM_32 ;
347
348
0
    case (SF_FORMAT_FLOAT | SF_ENDIAN_BIG) :
349
0
        return MAT4_BE_FLOAT ;
350
351
0
    case (SF_FORMAT_FLOAT | SF_ENDIAN_LITTLE) :
352
0
        return MAT4_LE_FLOAT ;
353
354
0
    case (SF_FORMAT_DOUBLE | SF_ENDIAN_BIG) :
355
0
        return MAT4_BE_DOUBLE ;
356
357
0
    case (SF_FORMAT_DOUBLE | SF_ENDIAN_LITTLE) :
358
0
        return MAT4_LE_DOUBLE ;
359
360
0
    default : break ;
361
0
    } ;
362
363
0
  return -1 ;
364
0
} /* mat4_format_to_encoding */
365
366
static const char *
367
mat4_marker_to_str (int marker)
368
628
{ static char str [32] ;
369
370
628
  switch (marker)
371
628
  {
372
8
    case MAT4_BE_PCM_16  : return "big endian 16 bit PCM" ;
373
35
    case MAT4_LE_PCM_16  : return "little endian 16 bit PCM" ;
374
375
2
    case MAT4_BE_PCM_32  : return "big endian 32 bit PCM" ;
376
52
    case MAT4_LE_PCM_32  : return "little endian 32 bit PCM" ;
377
378
379
1
    case MAT4_BE_FLOAT :  return "big endian float" ;
380
50
    case MAT4_LE_FLOAT :  return "big endian float" ;
381
382
2
    case MAT4_BE_DOUBLE  : return "big endian double" ;
383
195
    case MAT4_LE_DOUBLE  : return "little endian double" ;
384
628
    } ;
385
386
  /* This is a little unsafe but is really only for debugging. */
387
283
  str [sizeof (str) - 1] = 0 ;
388
283
  snprintf (str, sizeof (str) - 1, "%08X", marker) ;
389
283
  return str ;
390
628
} /* mat4_marker_to_str */
391