# HG changeset patch
# User Damjan Angelovski <damjangelovski@gmail.com>
# Date 1459277034 -7200
#      Tue Mar 29 20:43:54 2016 +0200
# Node ID ebbfa3658f43645d383bc9cf0cfec48f4481a7db
# Parent  65d1daa7d7a7d5f102a76bb2315d6ea738e699e8
audformats with sndfile

diff -r 65d1daa7d7a7 -r ebbfa3658f43 libinterp/dldfcn/audioread.cc
--- a/libinterp/dldfcn/audioread.cc	Sat Mar 19 13:48:34 2016 -0400
+++ b/libinterp/dldfcn/audioread.cc	Tue Mar 29 20:43:54 2016 +0200
@@ -1,6 +1,7 @@
 /*
 
 Copyright (C) 2013-2015 Vytautas Jančauskas
+Copyright (C) 2016 Damjan Angelovski
 
 This file is part of Octave.
 
@@ -493,3 +494,74 @@
                         "reading and writing sound files through libsndfile");
 #endif
 }
+
+
+DEFUN_DLD (__NumAudFormats__, args, ,
+"-*- texinfo -*-\n\
+@deftypefn {} {} __NumAudFormats__ (@var{formats})\n\
+Get Major Formats from sndfile.\n\
+\n\
+@end deftypefn")
+{
+  int count=0;
+  sf_command (NULL, SFC_GET_FORMAT_MAJOR_COUNT, &count, sizeof (int));
+  return ovl(count);
+}
+DEFUN_DLD (__AudMajorFormats__, args, ,
+"-*- texinfo -*-\n\
+@deftypefn {} {} __AudMajorFormats__ (@var{formats})\n\
+Get Major Formats from sndfile.\n\
+\n\
+@end deftypefn")
+{
+  octave_map formats = args(0).map_value ();
+  SF_FORMAT_INFO	info ;
+  SF_INFO 		sfinfo ;
+  int count, m;
+  memset (&sfinfo, 0, sizeof (sfinfo)) ;
+  sf_command (NULL, SFC_GET_FORMAT_MAJOR_COUNT, &count, sizeof (int));
+  sfinfo.channels = 1 ;
+  for (m = 0 ; m < count ; m++)
+  {
+    info.format = m ;
+    sf_command (NULL, SFC_GET_FORMAT_MAJOR, &info, sizeof (info)) ;
+    octave_scalar_map fmt=formats.checkelem (m);
+    fmt.assign ("name",  info.name);
+    fmt.setfield ("extension",  info.extension);
+    fmt.setfield ("id",  info.format);
+    fmt.setfield ("subFormats",  "none");
+    formats.fast_elem_insert (m, fmt);
+  } ;
+  return ovl(formats);
+}
+DEFUN_DLD (__AudSubFormats__, args, ,
+"-*- texinfo -*-\n\
+@deftypefn {} {} __AudSubFormats__ (@var{integer})\n\
+get subFormats from sndfile.\n\
+\n\
+@end deftypefn")
+{
+  octave_value_list output = (octave_value_list)new octave_value_list();
+  int subtype_count,s,format=(int)args(0).int_value();
+  SF_FORMAT_INFO	info ;
+  SF_INFO 		sfinfo ;
+  std::string outStr="";
+  memset (&sfinfo, 0, sizeof (sfinfo)) ;
+  sf_command (NULL, SFC_GET_FORMAT_SUBTYPE_COUNT, &subtype_count, sizeof (int)) ;
+
+  sfinfo.channels = 1 ;
+  for (s = 0 ; s < subtype_count ; s++)
+  {
+    info.format = s ;
+    sf_command (NULL, SFC_GET_FORMAT_SUBTYPE, &info, sizeof (info)) ;
+    format = (format & SF_FORMAT_TYPEMASK) | info.format ;
+    sfinfo.format = format ;
+    if (sf_format_check (&sfinfo)){
+      outStr+="  ";
+      outStr+=info.name;
+      outStr+="\n";
+    };
+  };
+  output(0)=outStr;
+  return output;
+}
diff -r 65d1daa7d7a7 -r ebbfa3658f43 scripts/audio/audformats.m
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/scripts/audio/audformats.m	Tue Mar 29 20:43:54 2016 +0200
@@ -0,0 +1,326 @@
+## Copyright (C) 2016 Carnë Draug && Damjan Angelovski
+##
+## This file is part of Octave.
+##
+## Octave is free software; you can redistribute it and/or modify it
+## under the terms of the GNU General Public License as published by
+## the Free Software Foundation; either version 3 of the License, or (at
+## your option) any later version.
+##
+## Octave is distributed in the hope that it will be useful, but
+## WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+## General Public License for more details.
+##
+## You should have received a copy of the GNU General Public License
+## along with Octave; see the file COPYING.  If not, see
+## <http://www.gnu.org/licenses/>.
+
+## -*- texinfo -*-
+## @deftypefn  {} {} audformats ()
+## @deftypefnx {} {@var{formats} =} audformats (@var{ext})
+## @deftypefnx {} {@var{formats} =} audformats (@var{format})
+## @deftypefnx {} {@var{formats} =} audformats ("add", @var{format})
+## @deftypefnx {} {@var{formats} =} audformats ("remove", @var{ext})
+## @deftypefnx {} {@var{formats} =} audformats ("update", @var{ext}, @var{format})
+## @deftypefnx {} {@var{formats} =} audformats ("factory")
+## Manage supported image formats.
+##
+## @var{formats} is a structure with information about each supported file
+## format, or from a specific format @var{ext}, the value displayed on the
+## field @code{ext}.  It contains the following fields:
+##
+## @table @asis
+## @item ext
+## The name of the file format.  This may match the file extension but Octave
+## will automatically detect the file format.
+##
+## @item description
+## A long description of the file format.
+##
+## @item @nospell{isa}
+## A function handle to confirm if a file is of the specified format.
+##
+## @item write
+## A function handle to write if a file is of the specified format.
+##
+## @item read
+## A function handle to open files the specified format.
+##
+## @item info
+## A function handle to obtain image information of the specified format.
+##
+## @end table
+##
+## It is possible to change the way Octave manages file formats with the
+## options @qcode{"add"}, @qcode{"remove"}, and @qcode{"update"}, and supplying
+## a structure @var{format} with the required fields.  The option
+## @qcode{"factory"} resets the configuration to the default.
+##
+## This can be used by Octave packages to extend the audio reading capabilities
+## Octave, through use of the PKG_ADD and PKG_DEL commands.
+##
+## @end deftypefn
+##
+## Inital implementation copied from imformats.m (by Carnë Draug)
+## Author: Damjan Angelovski <damjangelovski@gmail.com>
+
+%{
+function varargout = audformats (arg1, arg2, arg3)
+  if (nargin > 3)
+    print_usage ();
+  endif
+
+
+  mlock (); # prevent formats to be removed by "clear all"
+  persistent formats = default_formats ();
+
+  if (nargin == 0 && nargout == 0)
+    pretty_print_formats (formats);
+  elseif (nargin >= 1)
+    if (isstruct (arg1))
+      arrayfun (@is_valid_format, arg1);
+      ## FIXME: what is the return value in this situation?
+      formats = arg1;
+
+    elseif (ischar (arg1))
+      switch (tolower (arg1))
+        case "add",
+          if (! isstruct (arg2))
+            error ("audformats: FORMAT to %s must be a structure.", arg1);
+          endif
+          arrayfun (@is_valid_format, arg2);
+          formats(end + 1: end + numel (arg2)) = arg2;
+          varargout{1} = formats;
+
+        case {"remove", "update"},
+          if (! ischar (arg2))
+            error ("audformats: EXT to %s must be a string.", arg1);
+          endif
+          ## FIXME: suppose a format with multiple extensions. If one of
+          ##        them is requested to be removed, should we remove the
+          ##        whole format, or just that extension from the format?
+          match = find_ext_idx (formats, arg2);
+          if (! any (match))
+            error ("audformats: no EXT `%s' found.", arg2);
+          endif
+          if (strcmpi (arg1, "remove"))
+            formats(match) = [];
+          else
+            ## then it's update
+            if (! isstruct (arg3))
+              error ("audformats: FORMAT to update must be a structure.");
+            endif
+            is_valid_format (arg3);
+            formats(match) = arg3;
+          endif
+          varargout{1} = formats;
+
+        case "factory",
+          formats = default_formats ();
+        otherwise
+          ## then we look for a format with that extension.
+          match = find_ext_idx (formats, arg1);
+          ## For matlab compatibility, if we don't find any format we must
+          ## return an empty struct with NO fields. We can't use match as mask
+          if (any (match))
+            varargout{1} = formats(match);
+          else
+            varargout{1} = struct ();
+          endif
+      endswitch
+    else
+      error ("audformats: first argument must be either a structure or string.");
+    endif
+  else
+    varargout{1} = formats;
+  endif
+endfunction
+%}
+
+#function default_formats ()
+function audformats ()
+  
+persistent formats = struct ( "name", {},
+                                "extension", {},
+                                "id", {},
+                                "subFormats", {});
+  
+
+  ##      Building the formats info
+
+  count=__NumAudFormats__();
+  outStr="Total: ";
+  outStr=[outStr,num2str(count)];
+  for fidx = 1: count
+  formats(fidx).name = "mockup Name";
+  endfor
+  formats=__AudMajorFormats__(formats);
+  for fidx = 1: count
+    formats(fidx).subFormats=__AudSubFormats__(formats(fidx).id);
+    outStr=[outStr,"\nName: ",formats(fidx).name,"\nExtension: ",formats(fidx).extension,"\nID number: ",num2str(formats(fidx).id),"\nSub Formats: \n",formats(fidx).subFormats];
+    #disp("Name:");
+    #disp(formats(fidx).name);
+    #disp("fidx:");
+    #disp(fidx);
+    #disp("Extension:")
+    #disp(formats(fidx).extension);
+    #disp("ID number:");
+    #disp(formats(fidx).id);
+    #disp("Sub formats:");
+    #disp(formats(fidx).subFormats);
+  endfor
+ disp(outStr);
+endfunction
+
+#{
+function is_valid_format (format)
+
+  min_fields  = {"ext", "read", "isa", "write", "description"};
+  fields_mask = isfield (format, min_fields);
+  if (! all (fields_mask))
+    error ("audformats: structure has missing field `%s'.", min_fields(! fields_mask){1});
+  endif
+endfunction
+
+function match = find_ext_idx (formats, ext)
+  ## XXX: what should we do if there's more than one hit?
+  ##      Should this function prevent the addition of
+  ##      duplicated extensions?
+  match = cellfun (@(x) any (strcmpi (x, ext)), {formats.ext});
+endfunction
+
+
+
+function pretty_print_formats (formats)
+  ## define header names (none should be shorter than 3 characters)
+  #headers = {"name", "extension", "id", "subFormats"};
+  #cols_length = cellfun (@numel, headers);
+
+  ## Adjust the maximal length of the extensions column
+  #extensions = cellfun (@strjoin, {formats.name}, {", "},
+  #                      "UniformOutput", false);
+  #cols_length(1) = max (max (cellfun (@numel, extensions)), cols_length(1));
+  #headers{1} = postpad (headers{1}, cols_length(1), " ");
+
+  ## Print the headers
+  #disp (strjoin (headers, " | "));
+  #under_headers = cellfun (@(x) repmat ("-", 1, numel (x)), headers,
+  #                         "UniformOutput", false);
+  #disp (strjoin (under_headers, "-+-"));
+
+  #template = strjoin (arrayfun (@(x) sprintf ("%%-%is", x), cols_length,
+  #                              "UniformOutput", false), " | ");
+
+  ## Print the function handle for this things won't be a pretty table.  So
+  ## instead we replace them with "yes" or "no", based on the support it has.
+  #yes_no_cols = cat (2, {formats.isa}(:), {formats.read}(:),
+  #                   {formats.write}(:));
+  #empty = cellfun (@isempty, yes_no_cols);
+  #yes_no_cols(empty) = "no";
+  #yes_no_cols(! empty) = "yes";
+
+  #descriptions = {formats.description};
+  #table = cat ({formats.name}(:), {formats.extension}(:), {formats.id}(:),{formats.subFormats}(:));
+  #printf ([template "\n"], table'{:});
+  printf ( "sdg");
+
+endfunction
+#}
+## Tests are only copied from imformats
+#they are not needed yet because the formats are hardcoded
+%!test
+%! formats = audformats ()
+%! assert (isstruct (formats))
+%!
+%! min_fields = {"ext", "read", "isa", "write", "info", "alpha", "description"};
+%! assert (all (ismember (min_fields, fieldnames (formats))))
+%!
+%! if (__have_feature__ ("MAGICK"))
+%!   assert (numel (formats) > 0)
+%! else
+%!   assert (numel (formats), 0)
+%! endif
+
+## When imread or imfinfo are called, the file must exist or the
+## function defined by imformats will never be called.  Because
+## of this, we must create a file for the tests to work.
+
+## changing the function that does the reading
+%!testif HAVE_MAGICK
+%! fname = [tempname() ".jpg"];
+%! def_fmt = imformats ();
+%! fid = fopen (fname, "w");
+%! unwind_protect
+%!   fmt = imformats ("jpg");
+%!   fmt.read = @numel;
+%!   imformats ("update", "jpg", fmt);
+%!   assert (imread (fname), numel (fname));
+%! unwind_protect_cleanup
+%!   fclose (fid);
+%!   unlink (fname);
+%!   imformats (def_fmt);
+%! end_unwind_protect
+
+## adding a new format
+%! fname = [tempname() ".new_fmt"];
+%! def_fmt = imformats ();
+%! fid = fopen (fname, "w");
+%! unwind_protect
+%!   fmt = imformats ("jpg"); # take jpg as template
+%!   fmt.ext = "new_fmt";
+%!   fmt.read = @() true ();
+%!   imformats ("add", fmt);
+%!   assert (imread (fname), true);
+%! unwind_protect_cleanup
+%!   fclose (fid);
+%!   unlink (fname);
+%!   imformats (def_fmt);
+%! end_unwind_protect
+
+## adding multiple formats at the same time
+%! fname1 = [tempname() ".new_fmt1"];
+%! fid1 = fopen (fname1, "w");
+%! fname2 = [tempname() ".new_fmt2"];
+%! fid2 = fopen (fname2, "w");
+%! def_fmt = imformats ();
+%! unwind_protect
+%!   fmt = imformats ("jpg"); # take jpg as template
+%!   fmt.ext = "new_fmt1";
+%!   fmt.read = @() true();
+%!   fmt(2) = fmt(1);
+%!   fmt(2).ext = "new_fmt2";
+%!   imformats ("add", fmt);
+%!   assert (imread (fname1), true);
+%!   assert (imread (fname2), true);
+%! unwind_protect_cleanup
+%!   fclose (fid1);
+%!   fclose (fid2);
+%!   unlink (fname1);
+%!   unlink (fname2);
+%!   imformats (def_fmt);
+%! end_unwind_protect
+
+## changing format and resetting back to default
+%!testif HAVE_MAGICK
+%! ori_fmt = mod_fmt = imformats ("jpg");
+%! mod_fmt.description = "Another description";
+%! imformats ("update", "jpg", mod_fmt);
+%! new_fmt = imformats ("jpg");
+%! assert (new_fmt.description, mod_fmt.description);
+%! imformats ("factory");
+%! new_fmt = imformats ("jpg");
+%! assert (new_fmt.description, ori_fmt.description);
+
+## updating to an invalid format should cause an error
+%!testif HAVE_MAGICK
+%! fmt = imformats ("jpg");
+%! fmt = rmfield (fmt, "read");
+%! error_thrown = false;
+%! try
+%!   imformats ("update", "jpg", fmt);
+%! catch
+%!   error_thrown = true;
+%! end_try_catch
+%! assert (error_thrown, true);
+
diff -r 65d1daa7d7a7 -r ebbfa3658f43 scripts/audio/module.mk
--- a/scripts/audio/module.mk	Sat Mar 19 13:48:34 2016 -0400
+++ b/scripts/audio/module.mk	Tue Mar 29 20:43:54 2016 +0200
@@ -8,7 +8,8 @@
   scripts/audio/mu2lin.m \
   scripts/audio/record.m \
   scripts/audio/sound.m \
-  scripts/audio/soundsc.m
+  scripts/audio/soundsc.m \
+  scripts/audio/audformats.m 
 
 scripts_audio_@audioplayer_FCN_FILES = \
   scripts/audio/@audioplayer/__get_properties__.m \
