changeset 19496:0243b86b3cee

Added libsndfile based audio file utilities to the source tree * audioinfo.cc, audioread.cc, audiowrite.cc: new files * module-files: appended with lines to build new files
author Vytautas Jančauskas <unaudio@gmail.com>
date Tue, 10 Sep 2013 11:50:55 +0300
parents 5eae41c8c07f
children 157eeaa53277
files libinterp/dldfcn/audioinfo.cc libinterp/dldfcn/audioread.cc libinterp/dldfcn/audiowrite.cc libinterp/dldfcn/module-files
diffstat 4 files changed, 334 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libinterp/dldfcn/audioinfo.cc	Tue Sep 10 11:50:55 2013 +0300
@@ -0,0 +1,58 @@
+#include <octave/oct.h>
+#include <octave/ov-struct.h>
+#include <sndfile.h>
+  
+DEFUN_DLD(audioinfo, args, ,
+"-*- texinfo -*-\n\
+@deftypefn{Loadable Function} info = audioinfo(@var{filename})\n\
+\n\
+Return information about an audio file specified by @var{filename}.\
+\n\
+@end deftypefn"
+)
+{
+  octave_scalar_map retval;
+  Matrix audio;
+  SNDFILE *file;
+  SF_INFO info;
+  info.format = 0;
+  int start, end;
+  file = sf_open(args(0).string_value ().c_str (), SFM_READ, &info);
+  retval.assign ("Filename", args(0).string_value ());
+  retval.assign ("CompressionMethod", "");
+  retval.assign ("NumChannels", info.channels);
+  retval.assign ("SampleRate", info.samplerate);
+  retval.assign ("TotalSamples", info.frames);
+  retval.assign ("Duration", (float)info.frames / (float)info.samplerate);
+  int bits;
+  if (info.format & SF_FORMAT_PCM_S8)
+    {
+      bits = 8;
+    }
+  else if (info.format & SF_FORMAT_PCM_U8)
+    {
+      bits = 8;
+    }
+  else if (info.format & SF_FORMAT_PCM_16)
+    {
+      bits = 16;
+    }
+  else if (info.format & SF_FORMAT_PCM_24)
+    {
+      bits = 24;
+    }
+  else if (info.format & SF_FORMAT_PCM_32)
+    {
+      bits = 32;
+    }
+  else
+    {
+      bits = -1;
+    }
+  retval.assign ("BitsPerSample", bits);
+  retval.assign ("BitRate", -1);
+  retval.assign ("Title", sf_get_string (file, SF_STR_TITLE));
+  retval.assign ("Artist", sf_get_string (file, SF_STR_ARTIST));
+  retval.assign ("Comment", sf_get_string (file, SF_STR_COMMENT));
+  return octave_value(retval);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libinterp/dldfcn/audioread.cc	Tue Sep 10 11:50:55 2013 +0300
@@ -0,0 +1,109 @@
+#include <octave/oct.h>
+#include <octave/ov-struct.h>
+#include <sndfile.h>
+  
+DEFUN_DLD(audioread, args, ,
+"-*- texinfo -*-\n\
+@deftypefn{Loadable Function} [@var{y}, @var{Fs}] = audioread(@var{filename})\n\
+\n\
+Load an audio file that is specified by @var{filename}. It will be loaded in to \
+a column matrix with as many rows as there are audio frames and as many columns \
+as there are channels in the file. Sampling rate will be stored in @var{Fs}. \
+\n\
+@deftypefnx{Loadable Function} [@var{y}, @var{Fs}] = audioread(@var{filename}, @var{samples})\n\
+\n\
+Read a specified range of samples from a file specified by @var{filename}. \
+Argument @var{samples} is a vector with two values specifying starting frame \
+and ending frame. \
+\n\
+@deftypefnx{Loadable Function} [@var{y}, @var{Fs}] = audioread(@var{filename}, @var{dataType})\n\
+\n\
+Read a file and return an array of specified type. If @var{dataType} is \"native\" then \
+an array of fixed width integer type will be returned depending on how data is stored \
+in the audio file. If @var{dataType} is \"double\" a double matrix will be returned. \
+\n\
+@deftypefnx{Loadable Function} [@var{y}, @var{Fs}] = audioread(@var{filename}, @var{samples}, @var{dataType})\n\
+\n\
+Read a file and return a specified range of frames in an array of specified type. \
+\n\
+@end deftypefn"
+)
+{
+  octave_value_list retval;
+  Matrix audio;
+  octave_value ret_audio;
+  SNDFILE *file;
+  SF_INFO info;
+  info.format = 0;
+  int start, end;
+  file = sf_open(args(0).string_value ().c_str (), SFM_READ, &info);
+  start = 0;
+  end = info.frames;
+  float *data = (float *)malloc (sizeof(float) * info.frames * info.channels);
+  sf_read_float(file, data, info.frames * info.channels);
+  if (args.length () == 2 && !args(1).is_string () || args.length () == 3)
+    {
+      RowVector range = args(1).row_vector_value ();
+      start = range(0);
+      end = range(1);
+    }
+  audio.resize (end - start, info.channels);
+  for (int i = start; i < end; i++)
+    {
+      for (int channel = 0; channel < info.channels; channel++)
+        {
+          audio(i - start, channel) = data[i * info.channels + channel];
+        }
+    } 
+  free (data);
+  if (args.length () == 2 && args(1).is_string () || args.length () == 3)
+    {
+      std::string type;
+      if (args.length () == 3)
+        {
+          type = args(2).string_value ();
+        }
+      else
+        {
+          type = args(1).string_value ();
+        }
+      if (type == "native")
+        {
+          if (info.format & SF_FORMAT_PCM_S8)
+            {
+              ret_audio = octave_value ((audio * 127)).int8_array_value ();
+            }
+          else if (info.format & SF_FORMAT_PCM_U8)
+            {
+              ret_audio = octave_value ((audio * 127 + 127)).uint8_array_value ();
+            }
+          else if (info.format & SF_FORMAT_PCM_16)
+            {
+              ret_audio = octave_value ((audio * 32767)).int16_array_value ();
+            }
+          else if (info.format & SF_FORMAT_PCM_24)
+            {
+              ret_audio = octave_value ((audio * 8388608)).int32_array_value ();
+            }
+          else if (info.format & SF_FORMAT_PCM_32)
+            {
+              ret_audio = octave_value ((audio * 2147483648)).int32_array_value ();
+            }
+          else
+            {
+              ret_audio = octave_value (audio);
+            }
+        }
+      else
+        {
+          ret_audio = octave_value (audio);
+        }
+    }
+  else
+    {
+      ret_audio = octave_value (audio);
+    }
+  retval(0) = ret_audio;
+  retval(1) = info.samplerate;
+  return octave_value(retval);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libinterp/dldfcn/audiowrite.cc	Tue Sep 10 11:50:55 2013 +0300
@@ -0,0 +1,164 @@
+#include <octave/oct.h>
+#include <octave/ov-struct.h>
+#include <sndfile.h>
+#include <string>
+#include <map>
+
+static void fill_extension_table(std::map<std::string, int> &table)
+{
+  table["wav"] = SF_FORMAT_WAV;
+  table["aiff"] = SF_FORMAT_AIFF;
+  table["au"] = SF_FORMAT_AU;
+  table["raw"] = SF_FORMAT_RAW;
+  table["paf"] = SF_FORMAT_PAF;
+  table["svx"] = SF_FORMAT_SVX;
+  table["nist"] = SF_FORMAT_NIST;
+  table["voc"] = SF_FORMAT_VOC;
+  table["ircam"] = SF_FORMAT_IRCAM;
+  table["w64"] = SF_FORMAT_W64;
+  table["mat4"] = SF_FORMAT_MAT4;
+  table["mat5"] = SF_FORMAT_MAT5;
+  table["pvf"] = SF_FORMAT_PVF;
+  table["xi"] = SF_FORMAT_XI;
+  table["htk"] = SF_FORMAT_HTK;
+  table["sds"] = SF_FORMAT_SDS;
+  table["avr"] = SF_FORMAT_AVR;
+  table["wavex"] = SF_FORMAT_WAVEX;
+  table["sd2"] = SF_FORMAT_SD2;
+  table["flac"] = SF_FORMAT_FLAC;
+  table["caf"] = SF_FORMAT_CAF;
+  table["wve"] = SF_FORMAT_WVE;
+  table["ogg"] = SF_FORMAT_OGG;
+  table["mpc2k"] = SF_FORMAT_MPC2K;
+  table["rf64"] = SF_FORMAT_RF64;
+}
+  
+DEFUN_DLD(audiowrite, args, ,
+"-*- texinfo -*-\n\
+@deftypefn{Loadable Function} audiowrite(@var{filename}, @var{y}, @var{Fs})\n\
+\n\
+Write audio data from the matrix @var{y} to a file specified by @var{filename}, \
+file format will be determined by the file extension.\
+\n\
+@deftypefnx{Loadable Function} audiowrite(@var{filename}, @var{y}, @var{Fs}, @var{Name}, @var{Value})\n\
+\n\
+Lets you specify additional parameters when writing the file. Those parameters are given in the table below:\n\
+\n\
+@table @samp\n\
+@item BitsPerSample\n\
+Number of bits per sample, valid values are 8, 16, 24 and 32. Default is 16.\n\
+@item BitRate\n\
+Valid argument name, but ignored. Left for compatibility with MATLAB.\n\
+@item Quality\n\
+Quality setting for the Ogg Vorbis compressor. Values can range between 0 and 100 with 100 being the highest quality setting. Default is 75.\n\
+@item Title\n\
+Title for the audio file.\n\
+@item Artist\n\
+Artist name.\n\
+@item Comment\n\
+Comment.\n\
+@end table\n\
+@end deftypefn"
+)
+{
+  std::map<std::string, int> extension_to_format;
+  fill_extension_table(extension_to_format);
+  std::string filename = args(0).string_value ();
+  std::string extension = filename.substr(filename.find_last_of(".") + 1);
+  std::transform(extension.begin(), extension.end(), extension.begin(), ::tolower);
+  octave_scalar_map retval;
+  Matrix audio = args(1).matrix_value ();
+  SNDFILE *file;
+  SF_INFO info;
+  float *data = (float *)malloc(audio.rows () * audio.cols () * sizeof(float));
+  for (int i = 0; i < audio.cols (); i++)
+    {
+      for (int j = 0; j < audio.rows (); j++)
+        {
+          data[j * audio.cols () + i] = audio(j, i);
+        }
+    }
+  if (extension == "ogg")
+    {
+      info.format = SF_FORMAT_VORBIS;
+    }
+  else
+    {
+      info.format = SF_FORMAT_PCM_16;
+    }
+  std::string title = "";
+  std::string artist = "";
+  std::string comment = "";
+  float quality = 0.75;
+  for (int i = 3; i < args.length (); i += 2)
+    {
+      if (args(i).string_value () == "BitsPerSample")
+        {
+          int bits = args(i + 1).int_value ();
+          if (bits == 8)
+            {
+              info.format |= SF_FORMAT_PCM_S8;
+            }
+          else if (bits == 16)
+            {
+              info.format |= SF_FORMAT_PCM_16;
+            }
+          else if (bits == 24)
+            {
+              info.format |= SF_FORMAT_PCM_24;
+            }
+          else if (bits == 32)
+            {
+              info.format |= SF_FORMAT_PCM_32;
+            }
+          else
+            {
+              error("audiowrite: wrong number of bits specified");
+            }
+        }
+      else if (args(i).string_value () == "BitRate")
+        {
+
+        }
+      else if (args(i).string_value () == "Quality")
+        {
+          quality = args(i + 1).int_value () * 0.01;
+        }
+      else if (args(i).string_value () == "Title")
+        {
+          title = args(i + 1).string_value ();
+        }
+      else if (args(i).string_value () == "Artist")
+        {
+          artist = args(i + 1).string_value ();
+        }
+      else if (args(i).string_value () == "Comment")
+        {
+          comment = args(i + 1).string_value ();
+        }
+      else
+        {
+          error("audiowrite: wrong argument name");
+        }
+    }
+  info.samplerate = args(2).int_value ();
+  info.channels = audio.cols ();
+  info.format |= extension_to_format[extension];
+  file = sf_open (filename.c_str (), SFM_WRITE, &info);
+  if (title != "")
+    {
+      sf_set_string (file, SF_STR_TITLE, title.c_str ());
+    }
+  if (artist != "")
+    {
+      sf_set_string (file, SF_STR_ARTIST, artist.c_str ());
+    }
+  if (comment != "")
+    {
+      sf_set_string (file, SF_STR_COMMENT, comment.c_str ());
+    }
+  sf_write_float (file, data, audio.rows () * audio.cols ());
+  sf_close (file);
+  free (data);
+  return octave_value(retval);
+}
--- a/libinterp/dldfcn/module-files	Tue Oct 01 18:02:23 2013 -0700
+++ b/libinterp/dldfcn/module-files	Tue Sep 10 11:50:55 2013 +0300
@@ -20,3 +20,6 @@
 symrcm.cc|$(SPARSE_XCPPFLAGS)|$(SPARSE_XLDFLAGS)|$(SPARSE_XLIBS)
 tsearch.cc
 urlwrite.cc|$(CURL_CPPFLAGS)|$(CURL_LDFLAGS)|$(CURL_LIBS)
+audioread.cc|-lsndfile
+audiowrite.cc|-lsndfile
+audioinfo.cc|-lsndfile
\ No newline at end of file