GNU Octave  4.0.0
A high-level interpreted language, primarily intended for numerical computations, mostly compatible with Matlab
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Pages
audioread.cc
Go to the documentation of this file.
1 /*
2 
3 Copyright (C) 2013-2015 Vytautas JanĨauskas
4 
5 This file is part of Octave.
6 
7 Octave is free software; you can redistribute it and/or modify it
8 under the terms of the GNU General Public License as published by the
9 Free Software Foundation; either version 3 of the License, or (at your
10 option) any later version.
11 
12 Octave is distributed in the hope that it will be useful, but WITHOUT
13 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
14 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
15 for more details.
16 
17 You should have received a copy of the GNU General Public License
18 along with Octave; see the file COPYING. If not, see
19 <http://www.gnu.org/licenses/>.
20 
21 */
22 
23 #ifdef HAVE_CONFIG_H
24 #include <config.h>
25 #endif
26 
27 #include <string>
28 #include <map>
29 
30 #include "oct-locbuf.h"
31 #include "unwind-prot.h"
32 
33 #include "defun-dld.h"
34 #include "error.h"
35 #include "gripes.h"
36 #include "oct-obj.h"
37 #include "ov.h"
38 #include "ov-struct.h"
39 
40 #ifdef HAVE_SNDFILE
41 #include <sndfile.h>
42 #endif
43 
44 #ifdef HAVE_SNDFILE
45 static void
46 safe_close (SNDFILE *file)
47 {
48  sf_close (file);
49 }
50 #endif
51 
52 DEFUN_DLD (audioread, args, ,
53  "-*- texinfo -*-\n\
54 @deftypefn {Loadable Function} {[@var{y}, @var{fs}] =} audioread (@var{filename})\n\
55 @deftypefnx {Loadable Function} {[@var{y}, @var{fs}] =} audioread (@var{filename}, @var{samples})\n\
56 \n\
57 @deftypefnx {Loadable Function} {[@var{y}, @var{fs}] =} audioread (@var{filename}, @var{datatype})\n\
58 @deftypefnx {Loadable Function} {[@var{y}, @var{fs}] =} audioread (@var{filename}, @var{samples}, @var{datatype})\n\
59 Read the audio file @var{filename} and return the audio data @var{y} and\n\
60 sampling rate @var{fs}.\n\
61 \n\
62 The audio data is stored as matrix with rows corresponding to audio frames\n\
63 and columns corresponding to channels.\n\
64 \n\
65 The optional two-element vector argument @var{samples} specifies starting\n\
66 and ending frames.\n\
67 \n\
68 The optional argument @var{datatype} specifies the datatype to return.\n\
69 If it is @qcode{\"native\"}, then the type of data depends on how the data\n\
70 is stored in the audio file.\n\
71 @end deftypefn")
72 {
73  octave_value_list retval;
74 
75 #ifdef HAVE_SNDFILE
76 
77  int nargin = args.length ();
78 
79  if (nargin < 1 || nargin > 3)
80  {
81  print_usage ();
82  return retval;
83  }
84 
85  std::string filename = args(0).string_value ();
86 
87  if (error_state)
88  return retval;
89 
90  SF_INFO info;
91  info.format = 0;
92  SNDFILE *file = sf_open (filename.c_str (), SFM_READ, &info);
93 
94  if (! file)
95  {
96  error ("audioread: failed to open input file %s", filename.c_str ());
97  return retval;
98  }
99 
100  unwind_protect frame;
101 
102  frame.add_fcn (safe_close, file);
103 
104  OCTAVE_LOCAL_BUFFER (float, data, info.frames * info.channels);
105 
106  sf_read_float (file, data, info.frames * info.channels);
107 
108  sf_count_t start = 0;
109  sf_count_t end = info.frames;
110 
111  if ((nargin == 2 && ! args(1).is_string ()) || nargin == 3)
112  {
113  RowVector range = args(1).row_vector_value ();
114 
115  if (error_state)
116  return retval;
117 
118  if (range.nelem () != 2)
119  {
120  error ("audioread: invalid specification for range of frames");
121  return retval;
122  }
123 
124  double dstart = xisinf (range(0)) ? info.frames : range(0);
125  double dend = xisinf (range(1)) ? info.frames : range(1);
126 
127  if (dstart < 1 || dstart > dend || dend > info.frames
128  || D_NINT (dstart) != dstart || D_NINT (dend) != dend)
129  {
130  error ("audioread: invalid specification for range of frames");
131  return retval;
132  }
133 
134  start = dstart - 1;
135  end = dend;
136  }
137 
138  sf_count_t items = end - start;
139 
140  Matrix audio (items, info.channels);
141 
142  double *paudio = audio.fortran_vec ();
143 
144  data += start * info.channels;
145 
146  for (int i = 0; i < items; i++)
147  {
148  for (int channel = 0; channel < info.channels; channel++)
149  paudio[items*channel+i] = *data++;
150  }
151 
152  octave_value ret_audio;
153 
154  if ((nargin == 2 && args(1).is_string ()) || nargin == 3)
155  {
156  std::string type;
157  if (nargin == 3)
158  type = args(2).string_value ();
159  else
160  type = args(1).string_value ();
161 
162  if (error_state)
163  return retval;
164 
165  if (type == "native")
166  {
167  switch (info.format & SF_FORMAT_SUBMASK)
168  {
169  case SF_FORMAT_PCM_S8:
170  ret_audio = int8NDArray (audio * 127);
171  break;
172  case SF_FORMAT_PCM_U8:
173  ret_audio = uint8NDArray (audio * 127 + 127);
174  break;
175  case SF_FORMAT_PCM_16:
176  ret_audio = int16NDArray (audio * 32767);
177  break;
178  case SF_FORMAT_PCM_24:
179  ret_audio = int32NDArray (audio * 8388608);
180  break;
181  case SF_FORMAT_PCM_32:
182  ret_audio = int32NDArray (audio * 2147483648);
183  break;
184  default:
185  ret_audio = audio;
186  break;
187  }
188  }
189  else
190  ret_audio = audio;
191  }
192  else
193  ret_audio = audio;
194 
195  retval(1) = info.samplerate;
196  retval(0) = ret_audio;
197 
198 #else
199 
200  error ("sndfile not found on your system and thus audioread is not functional");
201 
202 #endif
203 
204  return retval;
205 }
206 
207 #ifdef HAVE_SNDFILE
208 
209 static int
210 extension_to_format (const std::string& ext)
211 {
212  static bool initialized = false;
213 
214  static std::map<std::string, int> table;
215 
216  if (! initialized)
217  {
218  table["wav"] = SF_FORMAT_WAV;
219  table["aiff"] = SF_FORMAT_AIFF;
220  table["au"] = SF_FORMAT_AU;
221  table["raw"] = SF_FORMAT_RAW;
222  table["paf"] = SF_FORMAT_PAF;
223  table["svx"] = SF_FORMAT_SVX;
224  table["nist"] = SF_FORMAT_NIST;
225  table["voc"] = SF_FORMAT_VOC;
226  table["ircam"] = SF_FORMAT_IRCAM;
227  table["w64"] = SF_FORMAT_W64;
228  table["mat4"] = SF_FORMAT_MAT4;
229  table["mat5"] = SF_FORMAT_MAT5;
230  table["pvf"] = SF_FORMAT_PVF;
231  table["xi"] = SF_FORMAT_XI;
232  table["htk"] = SF_FORMAT_HTK;
233  table["sds"] = SF_FORMAT_SDS;
234  table["avr"] = SF_FORMAT_AVR;
235  table["wavex"] = SF_FORMAT_WAVEX;
236  table["sd2"] = SF_FORMAT_SD2;
237  table["flac"] = SF_FORMAT_FLAC;
238  table["caf"] = SF_FORMAT_CAF;
239  table["wve"] = SF_FORMAT_WVE;
240  table["ogg"] = SF_FORMAT_OGG;
241  table["mpc2k"] = SF_FORMAT_MPC2K;
242  table["rf64"] = SF_FORMAT_RF64;
243 
244  initialized = true;
245  }
246 
247  std::map<std::string, int>::const_iterator it = table.find (ext);
248 
249  return (it != table.end ()) ? it->second : 0;
250 }
251 
252 #endif
253 
254 DEFUN_DLD (audiowrite, args, ,
255  "-*- texinfo -*-\n\
256 @deftypefn {Loadable Function} {} audiowrite (@var{filename}, @var{y}, @var{fs})\n\
257 @deftypefnx {Loadable Function} {} audiowrite (@var{filename}, @var{y}, @var{fs}, @var{name}, @var{value}, @dots{})\n\
258 \n\
259 Write audio data from the matrix @var{y} to @var{filename} at sampling rate\n\
260 @var{fs} with the file format determined by the file extension.\n\
261 \n\
262 Additional name/value argument pairs may be used to specify the\n\
263 following options:\n\
264 \n\
265 @table @samp\n\
266 @item BitsPerSample\n\
267 Number of bits per sample, valid values are 8, 16, 24 and 32. Default is 16.\n\
268 \n\
269 @item BitRate\n\
270 Valid argument name, but ignored. Left for compatibility with @sc{matlab}.\n\
271 \n\
272 @item Quality\n\
273 Quality setting for the Ogg Vorbis compressor. Values can range between 0\n\
274 and 100 with 100 being the highest quality setting. Default is 75.\n\
275 \n\
276 @item Title\n\
277 Title for the audio file.\n\
278 \n\
279 @item Artist\n\
280 Artist name.\n\
281 \n\
282 @item Comment\n\
283 Comment.\n\
284 @end table\n\
285 @end deftypefn")
286 {
287  // FIXME: shouldn't we return something to indicate whether the file
288  // was written successfully?
289 
290  octave_value retval;
291 
292 #ifdef HAVE_SNDFILE
293 
294  int nargin = args.length ();
295 
296  if (nargin < 3)
297  {
298  print_usage ();
299  return retval;
300  }
301 
302  std::string filename = args(0).string_value ();
303 
304  if (error_state)
305  return retval;
306 
307  Matrix audio = args(1).matrix_value ();
308 
309  if (error_state)
310  return retval;
311 
312  double bias = 0.0;
313  double scale = 1.0;
314 
315  if (args(1).is_uint8_type ())
316  bias = scale = std::pow (2.0, 7);
317  else if (args(1).is_int16_type ())
318  scale = std::pow (2.0, 15);
319  else if (args(1).is_int32_type ())
320  scale = std::pow (2.0, 31);
321  else if (args(1).is_integer_type ())
322  {
323  gripe_wrong_type_arg ("audiowrite", args(1));
324  return retval;
325  }
326 
327  int samplerate = args(2).int_value ();
328 
329  if (error_state)
330  return retval;
331 
332  std::string ext;
333  size_t dotpos = filename.find_last_of (".");
334  if (dotpos != std::string::npos)
335  ext = filename.substr (dotpos + 1);
336  std::transform (ext.begin (), ext.end (), ext.begin (), ::tolower);
337 
338  sf_count_t items_to_write = audio.rows () * audio.columns ();
339 
340  if (audio.rows () == 1)
341  audio = audio.transpose ();
342 
343  OCTAVE_LOCAL_BUFFER (float, data, items_to_write);
344 
345  sf_count_t idx = 0;
346  for (int i = 0; i < audio.rows (); i++)
347  {
348  for (int j = 0; j < audio.columns (); j++)
349  {
350  double elem = (audio.xelem (i, j) - bias) / scale;
351  data[idx++] = std::min (std::max (elem, -1.0), 1.0);
352  }
353  }
354 
355  SF_INFO info;
356 
357  memset (&info, 0, sizeof (info)) ;
358 
359  sf_count_t chunk_size = 0;
360 
361  if (ext == "ogg")
362  {
363  info.format = SF_FORMAT_VORBIS;
364 
365  // FIXME: there seems to be a bug writing ogg files in one shot
366  // that causes a segfault. Breaking it up into a series of
367  // smaller chunks seems to avoid the problem and produce valid
368  // files.
369  chunk_size = 0x1FFFFE;
370  }
371  else
372  info.format = SF_FORMAT_PCM_16;
373 
374  info.channels = audio.columns ();
375  info.samplerate = samplerate;
376  info.channels = audio.cols ();
377  info.format |= extension_to_format (ext);
378 
379  std::string title = "";
380  std::string artist = "";
381  std::string comment = "";
382  // Quality is currently unused?
383  //
384  // float quality = 0.75;
385  for (int i = 3; i < nargin; i += 2)
386  {
387  if (args(i).string_value () == "BitsPerSample")
388  {
389  info.format &= ~SF_FORMAT_SUBMASK;
390  int bits = args(i + 1).int_value ();
391  if (bits == 8)
392  {
393  if ((info.format & SF_FORMAT_TYPEMASK) == SF_FORMAT_WAV)
394  info.format |= SF_FORMAT_PCM_U8;
395  else
396  info.format |= SF_FORMAT_PCM_S8;
397  }
398  else if (bits == 16)
399  info.format |= SF_FORMAT_PCM_16;
400  else if (bits == 24)
401  info.format |= SF_FORMAT_PCM_24;
402  else if (bits == 32)
403  info.format |= SF_FORMAT_PCM_32;
404  else
405  {
406  error ("audiowrite: wrong number of bits specified");
407  return retval;
408  }
409  }
410  else if (args(i).string_value () == "BitRate")
411  ;
412  // Quality is currently unused?
413  //
414  // else if (args(i).string_value () == "Quality")
415  // quality = args(i + 1).int_value () * 0.01;
416  else if (args(i).string_value () == "Title")
417  title = args(i + 1).string_value ();
418  else if (args(i).string_value () == "Artist")
419  artist = args(i + 1).string_value ();
420  else if (args(i).string_value () == "Comment")
421  comment = args(i + 1).string_value ();
422  else
423  {
424  error ("audiowrite: wrong argument name");
425  return retval;
426  }
427  }
428 
429  SNDFILE *file = sf_open (filename.c_str (), SFM_WRITE, &info);
430 
431  if (! file)
432  {
433  error ("audiowrite: failed to open output file %s", filename.c_str ());
434  return retval;
435  }
436 
437  unwind_protect frame;
438 
439  frame.add_fcn (safe_close, file);
440 
441  if (title != "")
442  sf_set_string (file, SF_STR_TITLE, title.c_str ());
443 
444  if (artist != "")
445  sf_set_string (file, SF_STR_ARTIST, artist.c_str ());
446 
447  if (comment != "")
448  sf_set_string (file, SF_STR_COMMENT, comment.c_str ());
449 
450  sf_count_t total_items_written = 0;
451  sf_count_t offset = 0;
452 
453  if (chunk_size == 0)
454  chunk_size = items_to_write;
455 
456  while (total_items_written < items_to_write)
457  {
458  if (items_to_write - offset < chunk_size)
459  chunk_size = items_to_write - offset;
460 
461  sf_count_t items_written = sf_write_float (file, data+offset, chunk_size);
462 
463  if (items_written != chunk_size)
464  {
465  error ("audiowrite: write failed, wrote %ld of %ld items\n",
466  items_written, chunk_size);
467  return retval;
468  }
469 
470  total_items_written += items_written;
471  offset += chunk_size;
472  }
473 
474 #else
475 
476  error ("sndfile not found on your system and thus audiowrite is not functional");
477 
478 #endif
479 
480  return retval;
481 }
482 
483 DEFUN_DLD (audioinfo, args, ,
484  "-*- texinfo -*-\n\
485 @deftypefn {Loadable Function} {@var{info} =} audioinfo (@var{filename})\n\
486 Return information about an audio file specified by @var{filename}.\n\
487 @end deftypefn")
488 {
489  octave_value retval;
490 
491 #ifdef HAVE_SNDFILE
492 
493  if (args.length () != 1)
494  {
495  print_usage ();
496  return retval;
497  }
498 
499  std::string filename = args(0).string_value ();
500 
501  if (error_state)
502  return retval;
503 
504  SF_INFO info;
505  info.format = 0;
506  SNDFILE *file = sf_open (filename.c_str (), SFM_READ, &info);
507 
508  if (! file)
509  {
510  error ("audioinfo: failed to open file %s", filename.c_str ());
511  return retval;
512  }
513 
514  unwind_protect frame;
515 
516  frame.add_fcn (safe_close, file);
517 
518  octave_scalar_map result;
519 
520  result.assign ("Filename", filename);
521  result.assign ("CompressionMethod", "");
522  result.assign ("NumChannels", info.channels);
523  result.assign ("SampleRate", info.samplerate);
524  result.assign ("TotalSamples", info.frames);
525 
526  double dframes = info.frames;
527  double drate = info.samplerate;
528  result.assign ("Duration", dframes / drate);
529 
530  int bits;
531  switch (info.format & SF_FORMAT_SUBMASK)
532  {
533  case SF_FORMAT_PCM_S8:
534  bits = 8;
535  break;
536  case SF_FORMAT_PCM_U8:
537  bits = 8;
538  break;
539  case SF_FORMAT_PCM_16:
540  bits = 16;
541  break;
542  case SF_FORMAT_PCM_24:
543  bits = 24;
544  break;
545  case SF_FORMAT_PCM_32:
546  bits = 32;
547  break;
548  default:
549  bits = -1;
550  break;
551  }
552 
553  result.assign ("BitsPerSample", bits);
554  result.assign ("BitRate", -1);
555  result.assign ("Title", sf_get_string (file, SF_STR_TITLE));
556  result.assign ("Artist", sf_get_string (file, SF_STR_ARTIST));
557  result.assign ("Comment", sf_get_string (file, SF_STR_COMMENT));
558 
559  retval = result;
560 
561 #else
562 
563  error ("sndfile not found on your system and thus audioinfo is not functional");
564 
565 #endif
566 
567  return retval;
568 }
void gripe_wrong_type_arg(const char *name, const char *s, bool is_error)
Definition: gripes.cc:135
OCTINTERP_API void print_usage(void)
Definition: defun.cc:51
octave_idx_type length(void) const
Definition: oct-obj.h:89
intNDArray< octave_uint8 > uint8NDArray
Definition: uint8NDArray.h:31
int int_value(bool req_int=false, bool frc_str_conv=false) const
Definition: ov.h:730
void error(const char *fmt,...)
Definition: error.cc:476
bool xisinf(double x)
Definition: lo-mappers.cc:160
intNDArray< octave_int16 > int16NDArray
Definition: int16NDArray.h:31
octave_idx_type rows(void) const
Definition: Array.h:313
octave_idx_type nelem(void) const
Number of elements in the array.
Definition: Array.h:271
void add_fcn(void(*fcn)(void))
intNDArray< octave_int8 > int8NDArray
Definition: int8NDArray.h:31
std::string string_value(bool force=false) const
Definition: ov.h:897
int error_state
Definition: error.cc:101
static int elem
Definition: __contourc__.cc:49
octave_int< T > pow(const octave_int< T > &a, const octave_int< T > &b)
Matrix transpose(void) const
Definition: dMatrix.h:114
octave_idx_type length(void) const
Definition: ov.cc:1525
Definition: dMatrix.h:35
OCTAVE_API double D_NINT(double x)
Definition: lo-mappers.h:240
T & xelem(octave_idx_type n)
Definition: Array.h:353
charNDArray max(char d, const charNDArray &m)
Definition: chNDArray.cc:233
intNDArray< octave_int32 > int32NDArray
Definition: int32NDArray.h:31
void assign(const std::string &k, const octave_value &val)
Definition: oct-map.h:225
void scale(Matrix &m, double x, double y, double z)
Definition: graphics.cc:5281
#define OCTAVE_LOCAL_BUFFER(T, buf, size)
Definition: oct-locbuf.h:197
ColumnVector transform(const Matrix &m, double x, double y, double z)
Definition: graphics.cc:5259
#define DEFUN_DLD(name, args_name, nargout_name, doc)
Definition: defun-dld.h:59
const T * fortran_vec(void) const
Definition: Array.h:481
octave_idx_type cols(void) const
Definition: Array.h:321
octave_idx_type columns(void) const
Definition: Array.h:322
charNDArray min(char d, const charNDArray &m)
Definition: chNDArray.cc:210