GNU Octave  9.1.0
A high-level interpreted language, primarily intended for numerical computations, mostly compatible with Matlab
audioread.cc
Go to the documentation of this file.
1 ////////////////////////////////////////////////////////////////////////
2 //
3 // Copyright (C) 2013-2024 The Octave Project Developers
4 //
5 // See the file COPYRIGHT.md in the top-level directory of this
6 // distribution or <https://octave.org/copyright/>.
7 //
8 // This file is part of Octave.
9 //
10 // Octave is free software: you can redistribute it and/or modify it
11 // under the terms of the GNU General Public License as published by
12 // the Free Software Foundation, either version 3 of the License, or
13 // (at your option) any later version.
14 //
15 // Octave is distributed in the hope that it will be useful, but
16 // WITHOUT ANY WARRANTY; without even the implied warranty of
17 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 // GNU General Public License for more details.
19 //
20 // You should have received a copy of the GNU General Public License
21 // along with Octave; see the file COPYING. If not, see
22 // <https://www.gnu.org/licenses/>.
23 //
24 ////////////////////////////////////////////////////////////////////////
25 
26 #if defined (HAVE_CONFIG_H)
27 # include "config.h"
28 #endif
29 
30 #include <algorithm>
31 #include <map>
32 #include <string>
33 
34 #include "dMatrix.h"
35 #include "dRowVector.h"
36 #include "file-ops.h"
37 #include "file-stat.h"
38 #include "oct-locbuf.h"
39 #include "unwind-prot.h"
40 
41 #include "defun-dld.h"
42 #include "error.h"
43 #include "errwarn.h"
44 #include "ov.h"
45 #include "ovl.h"
46 #include "pager.h"
47 
48 #if defined (HAVE_SNDFILE)
49 # include <sndfile.h>
50 #endif
51 
53 
54 DEFUN_DLD (audioread, args, ,
55  doc: /* -*- texinfo -*-
56 @deftypefn {} {[@var{y}, @var{fs}] =} audioread (@var{filename})
57 @deftypefnx {} {[@var{y}, @var{fs}] =} audioread (@var{filename}, @var{samples})
58 
59 @deftypefnx {} {[@var{y}, @var{fs}] =} audioread (@var{filename}, @var{datatype})
60 @deftypefnx {} {[@var{y}, @var{fs}] =} audioread (@var{filename}, @var{samples}, @var{datatype})
61 Read the audio file @var{filename} and return the audio data @var{y} and
62 sampling rate @var{fs}.
63 
64 The audio data is stored as matrix with rows corresponding to audio frames
65 and columns corresponding to channels.
66 
67 The optional two-element vector argument @var{samples} specifies starting
68 and ending frames.
69 
70 The optional argument @var{datatype} specifies the datatype to return.
71 If it is @qcode{"native"}, then the type of data depends on how the data
72 is stored in the audio file.
73 @seealso{audiowrite, audioformats, audioinfo}
74 @end deftypefn */)
75 {
76 #if defined (HAVE_SNDFILE)
77 
78  int nargin = args.length ();
79 
80  if (nargin < 1 || nargin > 3)
81  print_usage ();
82 
83  std::string filename = args(0).xstring_value ("audioread: FILENAME must be a string");
84 
85  SF_INFO info;
86  info.format = 0;
87  SNDFILE *file = sf_open (filename.c_str (), SFM_READ, &info);
88 
89  if (! file)
90  error ("audioread: failed to open input file '%s': %s",
91  filename.c_str (), sf_strerror (file));
92 
93  unwind_action close_open_file ([=] () { sf_close (file); });
94 
95  // FIXME: It would be nicer to use a C++ expandable data container and
96  // read a file of unknown length into memory in chunks and determine the
97  // number of samples after reading. See bug #60888.
98  if (info.frames == SF_COUNT_MAX)
99  error ("audioread: malformed header does not specify number of samples");
100 
101  OCTAVE_LOCAL_BUFFER (double, data, info.frames * info.channels);
102 
103  sf_read_double (file, data, info.frames * info.channels);
104 
105  sf_count_t start = 0;
106  sf_count_t end = info.frames;
107 
108  if ((nargin == 2 && ! args(1).is_string ()) || nargin == 3)
109  {
110  RowVector range = args(1).row_vector_value ();
111 
112  if (range.numel () != 2)
113  error ("audioread: invalid specification for range of frames");
114 
115  double dstart = (math::isinf (range(0)) ? info.frames : range(0));
116  double dend = (math::isinf (range(1)) ? info.frames : range(1));
117 
118  if (dstart < 1 || dstart > dend || dend > info.frames
119  || math::x_nint (dstart) != dstart
120  || math::x_nint (dend) != dend)
121  error ("audioread: invalid specification for range of frames");
122 
123  start = dstart - 1;
124  end = dend;
125  }
126 
127  sf_count_t items = end - start;
128 
129  Matrix audio (items, info.channels);
130 
131  double *paudio = audio.fortran_vec ();
132 
133  data += start * info.channels;
134 
135  for (int i = 0; i < items; i++)
136  {
137  for (int channel = 0; channel < info.channels; channel++)
138  paudio[items*channel+i] = *data++;
139  }
140 
141  octave_value ret_audio;
142 
143  if ((nargin == 2 && args(1).is_string ()) || nargin == 3)
144  {
145  std::string type;
146  if (nargin == 3)
147  type = args(2).string_value ();
148  else
149  type = args(1).string_value ();
150 
151  if (type == "native")
152  {
153  switch (info.format & SF_FORMAT_SUBMASK)
154  {
155  case SF_FORMAT_PCM_S8:
156  ret_audio = int8NDArray (audio * 128);
157  break;
158  case SF_FORMAT_PCM_U8:
159  ret_audio = uint8NDArray (audio * 128 + 128);
160  break;
161  case SF_FORMAT_PCM_16:
162  ret_audio = int16NDArray (audio * 32768);
163  break;
164  case SF_FORMAT_PCM_24:
165  ret_audio = int32NDArray (audio * 8388608);
166  break;
167  case SF_FORMAT_PCM_32:
168  ret_audio = int32NDArray (audio * 2147483648);
169  break;
170  case SF_FORMAT_FLOAT:
171  ret_audio = FloatNDArray (audio);
172  break;
173  default:
174  ret_audio = audio;
175  break;
176  }
177  }
178  else
179  ret_audio = audio;
180  }
181  else
182  ret_audio = audio;
183 
184  return ovl (ret_audio, info.samplerate);
185 
186 #else
187 
188  octave_unused_parameter (args);
189 
190  err_disabled_feature ("audioread",
191  "reading and writing sound files through libsndfile");
192 
193 #endif
194 }
195 
196 #if defined (HAVE_SNDFILE)
197 
198 static int
199 extension_to_format (const std::string& ext)
200 {
201  static bool initialized = false;
202 
203  static std::map<std::string, int> table;
204 
205  if (! initialized)
206  {
207  table["wav"] = SF_FORMAT_WAV;
208  table["aiff"] = SF_FORMAT_AIFF;
209  table["au"] = SF_FORMAT_AU;
210  table["raw"] = SF_FORMAT_RAW;
211  table["paf"] = SF_FORMAT_PAF;
212  table["svx"] = SF_FORMAT_SVX;
213  table["nist"] = SF_FORMAT_NIST;
214  table["voc"] = SF_FORMAT_VOC;
215  table["ircam"] = SF_FORMAT_IRCAM;
216  table["w64"] = SF_FORMAT_W64;
217  table["mat4"] = SF_FORMAT_MAT4;
218  table["mat5"] = SF_FORMAT_MAT5;
219  table["pvf"] = SF_FORMAT_PVF;
220  table["xi"] = SF_FORMAT_XI;
221  table["htk"] = SF_FORMAT_HTK;
222  table["sds"] = SF_FORMAT_SDS;
223  table["avr"] = SF_FORMAT_AVR;
224  table["wavex"] = SF_FORMAT_WAVEX;
225  table["sd2"] = SF_FORMAT_SD2;
226  table["flac"] = SF_FORMAT_FLAC;
227  table["caf"] = SF_FORMAT_CAF;
228  table["wve"] = SF_FORMAT_WVE;
229  table["ogg"] = SF_FORMAT_OGG;
230  table["mpc2k"] = SF_FORMAT_MPC2K;
231  table["rf64"] = SF_FORMAT_RF64;
232 #if defined (HAVE_LIB_SNDFILE_FORMAT_MP3)
233  table["m1a"] = SF_FORMAT_MPEG;
234 #endif
235 
236  initialized = true;
237  }
238 
239  std::map<std::string, int>::const_iterator it = table.find (ext);
240 
241  return (it != table.end ()) ? it->second : 0;
242 }
243 
244 #endif
245 
246 DEFUN_DLD (audiowrite, args, ,
247  doc: /* -*- texinfo -*-
248 @deftypefn {} {} audiowrite (@var{filename}, @var{y}, @var{fs})
249 @deftypefnx {} {} audiowrite (@var{filename}, @var{y}, @var{fs}, @var{name}, @var{value}, @dots{})
250 
251 Write audio data from the matrix @var{y} to @var{filename} at sampling rate
252 @var{fs} with the file format determined by the file extension.
253 
254 Additional name/value argument pairs may be used to specify the
255 following options:
256 
257 @table @samp
258 @item BitsPerSample
259 Number of bits per sample. Valid values are 8, 16, 24, and 32. Default is
260 16.
261 
262 @item BitRate
263 Valid argument name, but ignored. Left for compatibility with @sc{matlab}.
264 
265 @item Quality
266 Quality setting for the Ogg Vorbis compressor. Values can range between 0
267 and 100 with 100 being the highest quality setting. Default is 75.
268 
269 @item Title
270 Title for the audio file.
271 
272 @item Artist
273 Artist name.
274 
275 @item Comment
276 Comment.
277 @end table
278 @seealso{audioread, audioformats, audioinfo}
279 @end deftypefn */)
280 {
281 #if defined (HAVE_SNDFILE)
282 
283  int nargin = args.length ();
284 
285  if (nargin < 3)
286  print_usage ();
287 
288  std::string filename = args(0).xstring_value ("audiowrite: FILENAME must be a string");
289 
290  double bias = 0.0;
291  double scale = 1.0;
292 
293  if (args(1).is_uint8_type ())
294  bias = scale = 127.5;
295  else if (args(1).is_int16_type ())
296  scale = 32768; // 2^15
297  else if (args(1).is_int32_type ())
298  scale = 2147483648; // 2^31
299  else if (args(1).isinteger ())
300  err_wrong_type_arg ("audiowrite", args(1));
301 
302  Matrix audio = args(1).matrix_value ();
303 
304  if (! args(2).is_scalar_type () || ! args(2).isnumeric ())
305  error ("audiowrite: sample rate FS must be a positive scalar integer");
306  int samplerate = args(2).int_value ();
307  if (samplerate < 1)
308  error ("audiowrite: sample rate FS must be a positive scalar integer");
309 
310  std::string ext;
311  std::size_t dotpos = filename.find_last_of ('.');
312  if (dotpos != std::string::npos)
313  ext = filename.substr (dotpos + 1);
314  std::transform (ext.begin (), ext.end (), ext.begin (), ::tolower);
315 
316  sf_count_t items_to_write = audio.rows () * audio.columns ();
317 
318  if (audio.rows () == 1)
319  audio = audio.transpose ();
320 
321  OCTAVE_LOCAL_BUFFER (double, data, items_to_write);
322 
323  sf_count_t idx = 0;
324  for (int i = 0; i < audio.rows (); i++)
325  {
326  for (int j = 0; j < audio.columns (); j++)
327  {
328  double elem = (audio.xelem (i, j) - bias) / scale;
329  data[idx++] = std::min (std::max (elem, -1.0), 1.0);
330  }
331  }
332 
333  SF_INFO info;
334 
335  memset (&info, 0, sizeof (info));
336 
337  sf_count_t chunk_size = 0;
338 
339  if (ext == "ogg")
340  {
341  info.format = SF_FORMAT_VORBIS;
342 
343  // FIXME: There seems to be a bug writing ogg files in one shot that
344  // causes a segfault: https://bugs.debian.org/760898.
345  // Breaking it up into a series of smaller chunks appears to avoid the
346  // problem and produces valid files.
347  chunk_size = 0x100000;
348  }
349 #if defined (HAVE_LIB_SNDFILE_FORMAT_MP3)
350  else if (ext == "mp1")
351  info.format = SF_FORMAT_MPEG|SF_FORMAT_MPEG_LAYER_I;
352  else if (ext == "mp2")
353  info.format = SF_FORMAT_MPEG|SF_FORMAT_MPEG_LAYER_II;
354  else if (ext == "mp3")
355  info.format = SF_FORMAT_MPEG|SF_FORMAT_MPEG_LAYER_III;
356 #endif
357  else
358  info.format = SF_FORMAT_PCM_16;
359 
360  info.channels = audio.columns ();
361  info.samplerate = samplerate;
362  info.format |= extension_to_format (ext);
363 
364  std::string title = "";
365  std::string artist = "";
366  std::string comment = "";
367  double quality = 0.75;
368 
369  for (int i = 3; i < nargin; i += 2)
370  {
371  if (i >= nargin - 1)
372  error ("audiowrite: invalid number of arguments");
373 
374  std::string keyword_orig = args(i).string_value ();
375  std::string keyword = args(i).xtolower ().string_value ();
376  octave_value value_arg = args(i+1);
377 
378  if (keyword == "bitspersample")
379  {
380  info.format &= ~SF_FORMAT_SUBMASK;
381  int bits = value_arg.int_value ();
382  if (bits == 8)
383  {
384  if ((info.format & SF_FORMAT_TYPEMASK) == SF_FORMAT_WAV)
385  info.format |= SF_FORMAT_PCM_U8;
386  else
387  info.format |= SF_FORMAT_PCM_S8;
388  }
389  else if (bits == 16)
390  info.format |= SF_FORMAT_PCM_16;
391  else if (bits == 24)
392  {
393  if ((info.format & SF_FORMAT_TYPEMASK) == SF_FORMAT_WAV)
394  info.format |= SF_FORMAT_PCM_32;
395  else
396  info.format |= SF_FORMAT_PCM_24;
397  }
398  else if (bits == 32)
399  {
400  if ((info.format & SF_FORMAT_TYPEMASK) == SF_FORMAT_WAV
401  && args(1).isfloat ())
402  info.format |= SF_FORMAT_FLOAT;
403  else
404  info.format |= SF_FORMAT_PCM_32;
405  }
406  else if (bits == 64)
407  info.format |= SF_FORMAT_DOUBLE;
408  else
409  error ("audiowrite: wrong number of bits specified");
410  }
411  else if (keyword == "bitrate")
412  warning_with_id ("Octave:audiowrite:unused-parameter",
413  "audiowrite: 'BitRate' accepted for Matlab "
414  "compatibility, but is ignored");
415  else if (keyword == "quality")
416  {
417  if (! value_arg.is_scalar_type ())
418  error ("audiowrite: Quality value must be a scalar");
419 
420  double value =
421  value_arg.xdouble_value ("audiowrite: Quality value must be a numeric scalar between 0 and 100");
422 
423  if (math::isnan (value) || value < 0 || value > 100)
424  error ("audiowrite: Quality value must be a number between 0 and 100");
425 
426  quality = value / 100;
427  }
428  else if (keyword == "title")
429  title = value_arg.string_value ();
430  else if (keyword == "artist")
431  artist = value_arg.string_value ();
432  else if (keyword == "comment")
433  comment = value_arg.string_value ();
434  else
435  error ("audiowrite: unrecognized option: '%s'", keyword_orig.c_str ());
436  }
437 
438  SNDFILE *file = sf_open (filename.c_str (), SFM_WRITE, &info);
439 
440  if (! file)
441  error ("audiowrite: failed to open output file '%s': %s",
442  filename.c_str (), sf_strerror (file));
443 
444  unwind_action close_open_file ([=] () { sf_close (file); });
445 
446  sf_command (file, SFC_SET_NORM_DOUBLE, nullptr, SF_TRUE);
447  sf_command (file, SFC_SET_CLIPPING, nullptr, SF_TRUE) ;
448  sf_command (file, SFC_SET_VBR_ENCODING_QUALITY, &quality, sizeof (quality));
449 
450  if (title != "")
451  sf_set_string (file, SF_STR_TITLE, title.c_str ());
452 
453  if (artist != "")
454  sf_set_string (file, SF_STR_ARTIST, artist.c_str ());
455 
456  if (comment != "")
457  sf_set_string (file, SF_STR_COMMENT, comment.c_str ());
458 
459  sf_count_t total_items_written = 0;
460  sf_count_t offset = 0;
461 
462  if (chunk_size == 0)
463  chunk_size = items_to_write;
464 
465  while (total_items_written < items_to_write)
466  {
467  if (items_to_write - offset < chunk_size)
468  chunk_size = items_to_write - offset;
469 
470  sf_count_t items_written = sf_write_double (file, data+offset, chunk_size);
471 
472  if (items_written != chunk_size)
473  error ("audiowrite: write failed, wrote %" PRId64 " of %" PRId64
474  " items\n", items_written, chunk_size);
475 
476  total_items_written += items_written;
477  offset += chunk_size;
478  }
479 
480  // FIXME: Shouldn't we return something to indicate whether the file
481  // was written successfully? On the other hand, Matlab doesn't
482  // return anything.
483  return ovl ();
484 
485 #else
486 
487  octave_unused_parameter (args);
488 
489  err_disabled_feature ("audiowrite",
490  "reading and writing sound files through libsndfile");
491 
492 #endif
493 }
494 
495 /*
496 ## Joint audiowrite/audioread tests
497 ## 8-bit Unsigned PCM
498 %!testif HAVE_SNDFILE <*56889>
499 %! fname = [tempname() ".wav"];
500 %! unwind_protect
501 %! y1 = uint8 ([0, 1, 2, 253, 254, 255]);
502 %! audiowrite (fname, y1, 8000, "BitsPerSample", 8);
503 %! y2 = audioread (fname, "native");
504 %! unwind_protect_cleanup
505 %! unlink (fname);
506 %! end_unwind_protect
507 %! assert (y1(:), y2);
508 
509 ## 8-bit Signed PCM
510 %!testif HAVE_SNDFILE <*56889>
511 %! fname = [tempname() ".au"];
512 %! unwind_protect
513 %! y1 = uint8 ([0, 1, 2, 253, 254, 255]);
514 %! audiowrite (fname, y1, 8000, "BitsPerSample", 8);
515 %! y2 = audioread (fname, "native");
516 %! unwind_protect_cleanup
517 %! unlink (fname);
518 %! end_unwind_protect
519 %! assert (y2, int8 ([-128; -127; -126; 125; 126; 127]));
520 
521 ## 16-bit Signed PCM
522 %!testif HAVE_SNDFILE <*56889>
523 %! fname = [tempname() ".wav"];
524 %! unwind_protect
525 %! y1 = int16 ([-32768, -32767, -32766, 32765, 32766, 32767]);
526 %! audiowrite (fname, y1, 8000, "BitsPerSample", 16);
527 %! y2 = audioread (fname, "native");
528 %! unwind_protect_cleanup
529 %! unlink (fname);
530 %! end_unwind_protect
531 %! assert (y1(:), y2);
532 
533 ## 24-bit Signed PCM
534 %!testif HAVE_SNDFILE <*56889>
535 %! fname = [tempname() ".au"];
536 %! unwind_protect
537 %! y1 = [-8388608, -8388607, -8388606, 8388605, 8388606, 8388607] / 8388608;
538 %! audiowrite (fname, y1, 8000, "BitsPerSample", 24);
539 %! y2 = audioread (fname, "native");
540 %! unwind_protect_cleanup
541 %! unlink (fname);
542 %! end_unwind_protect
543 %! assert (int32 ([-8388608; -8388607; -8388606; 8388605; 8388606; 8388607]),
544 %! y2);
545 
546 ## 32-bit Signed PCM
547 %!testif HAVE_SNDFILE <*56889>
548 %! fname = [tempname() ".wav"];
549 %! unwind_protect
550 %! y1 = int32 ([-2147483648, -2147483647, -2147483646, 2147483645, 2147483646, 2147483647 ]);
551 %! audiowrite (fname, y1, 8000, "BitsPerSample", 32);
552 %! y2 = audioread (fname, "native");
553 %! unwind_protect_cleanup
554 %! unlink (fname);
555 %! end_unwind_protect
556 %! assert (y1(:), y2);
557 
558 ## Test input validation
559 %!testif HAVE_SNDFILE
560 %! fail ("audiowrite (1, 1, 8e3)", "FILENAME must be a string");
561 %! fail ("audiowrite ('foo', int64 (1), 8e3)",
562 %! "wrong type argument 'int64 scalar'");
563 %! fail ("audiowrite ('foo', [0 1], [8e3, 8e3])",
564 %! "FS must be a positive scalar");
565 %! fail ("audiowrite ('foo', 1, {8e3})", "FS must be a .* integer");
566 %! fail ("audiowrite ('foo', 1, -8e3)", "FS must be a positive");
567 %! fail ("audiowrite ('foo', 1, 8e3, 'bitspersample')",
568 %! "invalid number of arguments");
569 %! fail ("audiowrite ('foo', 1, 8e3, 'bitspersample', 48)",
570 %! "wrong number of bits specified");
571 %! fail ("audiowrite ('foo', 1, 8e3, 'quality', [2 3 4])",
572 %! "Quality value must be a scalar");
573 %! fail ("audiowrite ('foo', 1, 8e3, 'quality', NaN)",
574 %! "Quality value must be .* between 0 and 100");
575 %! fail ("audiowrite ('foo', 1, 8e3, 'quality', -1)",
576 %! "Quality value must be .* between 0 and 100");
577 %! fail ("audiowrite ('foo', 1, 8e3, 'quality', 101)",
578 %! "Quality value must be .* between 0 and 100");
579 %! fail ("audiowrite ('foo', 1, 8e3, 'foo', 'bar')",
580 %! "unrecognized option: 'foo'");
581 */
582 
583 DEFUN_DLD (audioinfo, args, ,
584  doc: /* -*- texinfo -*-
585 @deftypefn {} {@var{info} =} audioinfo (@var{filename})
586 Return information about an audio file specified by @var{filename}.
587 
588 The output @var{info} is a structure containing the following fields:
589 
590 @table @samp
591 @item Filename
592 Name of the audio file.
593 
594 @item CompressionMethod
595 Audio compression method. Unused, only present for compatibility with
596 @sc{matlab}.
597 
598 @item NumChannels
599 Number of audio channels.
600 
601 @item SampleRate
602 Sample rate of the audio, in Hertz.
603 
604 @item TotalSamples
605 Number of samples in the file.
606 
607 @item Duration
608 Duration of the audio, in seconds.
609 
610 @item BitsPerSample
611 Number of bits per sample.
612 
613 @item BitRate
614 Audio bit rate. Unused, only present for compatibility with @sc{matlab}.
615 
616 @item Title
617 @qcode{"Title"} audio metadata value as a string, or empty if not present.
618 
619 @item Artist
620 @qcode{"Artist"} audio metadata value as a string, or empty if not present.
621 
622 @item Comment
623 @qcode{"Comment"} audio metadata value as a string, or empty if not present.
624 @end table
625 @seealso{audioread, audiowrite}
626 @end deftypefn */)
627 {
628 #if defined (HAVE_SNDFILE)
629 
630  if (args.length () != 1)
631  print_usage ();
632 
633  std::string filename = args(0).xstring_value ("audioinfo: FILENAME must be a string");
634 
635  sys::file_stat fs (filename);
636  if (! fs.exists ())
637  error ("audioinfo: FILENAME '%s' not found", filename.c_str ());
638 
639  SF_INFO info;
640  info.format = 0;
641  SNDFILE *file = sf_open (filename.c_str (), SFM_READ, &info);
642 
643  if (! file)
644  error ("audioinfo: failed to open input file '%s': %s",
645  filename.c_str (), sf_strerror (file));
646 
647  unwind_action close_open_file ([=] () { sf_close (file); });
648 
649  octave_scalar_map result;
650 
651  std::string full_name = sys::canonicalize_file_name (filename);
652 
653  result.assign ("Filename", full_name);
654  result.assign ("CompressionMethod", "");
655  result.assign ("NumChannels", info.channels);
656  result.assign ("SampleRate", info.samplerate);
657  double dframes;
658  if (info.frames != SF_COUNT_MAX)
659  dframes = info.frames;
660  else
661  dframes = -1;
662  result.assign ("TotalSamples", dframes);
663 
664  if (dframes != -1)
665  {
666  double drate = info.samplerate;
667  result.assign ("Duration", dframes / drate);
668  }
669  else
670  result.assign ("Duration", -1);
671 
672  int bits;
673  switch (info.format & SF_FORMAT_SUBMASK)
674  {
675  case SF_FORMAT_PCM_S8:
676  bits = 8;
677  break;
678  case SF_FORMAT_PCM_U8:
679  bits = 8;
680  break;
681  case SF_FORMAT_PCM_16:
682  bits = 16;
683  break;
684  case SF_FORMAT_PCM_24:
685  bits = 24;
686  break;
687  case SF_FORMAT_PCM_32:
688  bits = 32;
689  break;
690  case SF_FORMAT_FLOAT:
691  bits = 32;
692  break;
693  case SF_FORMAT_DOUBLE:
694  bits = 64;
695  break;
696  default:
697  bits = -1;
698  break;
699  }
700 
701  result.assign ("BitsPerSample", bits);
702  result.assign ("BitRate", -1);
703  result.assign ("Title", sf_get_string (file, SF_STR_TITLE));
704  result.assign ("Artist", sf_get_string (file, SF_STR_ARTIST));
705  result.assign ("Comment", sf_get_string (file, SF_STR_COMMENT));
706 
707  return ovl (result);
708 
709 #else
710 
711  octave_unused_parameter (args);
712 
713  err_disabled_feature ("audioinfo",
714  "reading and writing sound files through libsndfile");
715 
716 #endif
717 }
718 
719 #if defined (HAVE_SNDFILE)
720 
721 static void
722 audio_sub_formats (int format)
723 {
724  int count;
725  sf_command (nullptr, SFC_GET_FORMAT_SUBTYPE_COUNT, &count, sizeof (int));
726 
727  for (int i = 0; i < count; i++)
728  {
729  SF_FORMAT_INFO info;
730  info.format = i;
731  sf_command (nullptr, SFC_GET_FORMAT_SUBTYPE, &info, sizeof (info));
732 
733  SF_INFO sfinfo;
734  memset (&sfinfo, 0, sizeof (sfinfo));
735  sfinfo.channels = 1;
736  sfinfo.format = (format & SF_FORMAT_TYPEMASK) | info.format;
737 
738  if (sf_format_check (&sfinfo))
739  octave_stdout << " " << info.name << std::endl;
740  }
741 }
742 
743 #endif
744 
745 DEFUN_DLD (audioformats, args, ,
746  doc: /* -*- texinfo -*-
747 @deftypefn {} {} audioformats ()
748 @deftypefnx {} {} audioformats (@var{format})
749 Display information about all supported audio formats.
750 
751 If the optional argument @var{format} is given, then display only formats
752 with names that start with @var{format}.
753 @seealso{audioread, audiowrite}
754 @end deftypefn */)
755 {
756 #if defined (HAVE_SNDFILE)
757 
758  if (args.length () > 1)
759  print_usage ();
760 
761  std::string search = "";
762  if (args.length () > 0)
763  {
764  search = args(0).string_value ();
765  std::transform (search.begin (), search.end (), search.begin (), tolower);
766  }
767 
768  int count;
769  sf_command (nullptr, SFC_GET_FORMAT_MAJOR_COUNT, &count, sizeof (int));
770 
771  for (int i = 0; i < count; i++)
772  {
773  SF_FORMAT_INFO info;
774  info.format = i;
775  sf_command (nullptr, SFC_GET_FORMAT_MAJOR, &info, sizeof (info));
776  bool match = true;
777 
778  if (! search.empty ())
779  {
780  std::string nm = info.name;
781  std::transform (nm.begin (), nm.end (), nm.begin (), tolower);
782  match = nm.compare (0, search.length (), search) == 0;
783  }
784 
785  if (match)
786  {
787  octave_stdout << "name: " << info.name << std::endl;
788  octave_stdout << "extension: " << info.extension << std::endl;
789  octave_stdout << "id: " << info.format << std::endl;
790  octave_stdout << "subformats:" << std::endl;
791 
792  audio_sub_formats (info.format);
793  }
794  }
795 
796  return octave_value_list ();
797 
798 #else
799 
800  octave_unused_parameter (args);
801 
802  err_disabled_feature ("audioformats",
803  "getting sound formats through libsndfile");
804 
805 #endif
806 }
807 
808 OCTAVE_END_NAMESPACE(octave)
charNDArray max(char d, const charNDArray &m)
Definition: chNDArray.cc:230
charNDArray min(char d, const charNDArray &m)
Definition: chNDArray.cc:207
T * fortran_vec()
Size of the specified dimension.
Definition: Array-base.cc:1764
octave_idx_type rows() const
Definition: Array.h:459
octave_idx_type columns() const
Definition: Array.h:471
T & xelem(octave_idx_type n)
Size of the specified dimension.
Definition: Array.h:524
Definition: dMatrix.h:42
Matrix transpose() const
Definition: dMatrix.h:140
int int_value(bool req_int=false, bool frc_str_conv=false) const
Definition: ov.h:812
bool is_scalar_type() const
Definition: ov.h:744
double xdouble_value(const char *fmt,...) const
std::string string_value(bool force=false) const
Definition: ov.h:974
OCTAVE_BEGIN_NAMESPACE(octave) static octave_value daspk_fcn
#define DEFUN_DLD(name, args_name, nargout_name, doc)
Macro to define an at run time dynamically loadable builtin function.
Definition: defun-dld.h:61
void print_usage(void)
Definition: defun-int.h:72
void warning_with_id(const char *id, const char *fmt,...)
Definition: error.cc:1078
void() error(const char *fmt,...)
Definition: error.cc:988
void err_disabled_feature(const std::string &fcn, const std::string &feature, const std::string &pkg)
Definition: errwarn.cc:53
void err_wrong_type_arg(const char *name, const char *s)
Definition: errwarn.cc:166
void scale(Matrix &m, double x, double y, double z)
Definition: graphics.cc:5500
ColumnVector transform(const Matrix &m, double x, double y, double z)
Definition: graphics.cc:5468
intNDArray< octave_int16 > int16NDArray
Definition: int16NDArray.h:36
intNDArray< octave_int32 > int32NDArray
Definition: int32NDArray.h:36
intNDArray< octave_int8 > int8NDArray
Definition: int8NDArray.h:36
bool isinteger(double x)
Definition: lo-mappers.h:225
bool isinf(double x)
Definition: lo-mappers.h:203
bool isnan(bool)
Definition: lo-mappers.h:178
T x_nint(T x)
Definition: lo-mappers.h:269
std::string canonicalize_file_name(const std::string &name)
Definition: file-ops.cc:798
#define OCTAVE_LOCAL_BUFFER(T, buf, size)
Definition: oct-locbuf.h:44
octave_value_list ovl(const OV_Args &... args)
Construct an octave_value_list with less typing.
Definition: ovl.h:219
#define octave_stdout
Definition: pager.h:309
intNDArray< octave_uint8 > uint8NDArray
Definition: uint8NDArray.h:36
std::size_t format(std::ostream &os, const char *fmt,...)