changeset 19556:fc03d6e0d842

maint: Merge default to temporary audio-gsoc branch.
author John W. Eaton <jwe@octave.org>
date Fri, 02 Jan 2015 16:54:34 -0500
parents 53ba58536f09 (diff) 78c0241306b5 (current diff)
children 5802ea7037d4
files
diffstat 37 files changed, 4651 insertions(+), 9 deletions(-) [+]
line wrap: on
line diff
--- a/NEWS	Fri Jan 02 09:38:06 2015 -0800
+++ b/NEWS	Fri Jan 02 16:54:34 2015 -0500
@@ -13,9 +13,16 @@
       methods       endmethods
       properties    endproperties
 
- ** New classes in Octave 4.2:
+ ** New audio functions and classes:
 
-      inputParser
+      audiodevinfo  audioread
+      audioinfo     audiorecorder
+      audioplayer   audiowrite
+
+ ** Other new classes in Octave 4.2:
+
+      audioplayer    inputParser
+      audiorecorder  
 
  ** Optional stricter Matlab compatibility for ranges, diagonal matrices,
     and permutation matrices.
--- a/configure.ac	Fri Jan 02 09:38:06 2015 -0800
+++ b/configure.ac	Fri Jan 02 16:54:34 2015 -0500
@@ -995,6 +995,76 @@
 LIBS="$save_LIBS"
 CPPFLAGS="$save_CPPFLAGS"
 
+### Check for sndfile
+
+warn_sndfile="sndfile library fails tests.  The audioinfo, audioread and audiowrite functions for reading and writing audio files will not be fully functional."
+
+check_sndfile=no
+AC_ARG_WITH([sndfile],
+  [AS_HELP_STRING([--without-sndfile],
+    [don't use sndfile library, disable audio file I/O])],
+  [if test x"$withval" = x"no"; then
+     warn_sndfile="--without-sndfile specified.  The audioinfo, audioread and audiowrite functions for reading and writing audio files will not be fully functional."
+   else
+     check_sndfile=yes
+   fi],
+  [check_sndfile=yes])
+
+if test $check_sndfile = yes; then
+  PKG_CHECK_EXISTS([sndfile], [
+    SNDFILE_CPPFLAGS=`$PKG_CONFIG --cflags-only-I sndfile`
+    SNDFILE_LDFLAGS=`$PKG_CONFIG --libs-only-L sndfile`
+    SNDFILE_LIBS=`$PKG_CONFIG --libs-only-l sndfile`
+    warn_sndfile=
+  ])
+fi
+
+if test -z "$warn_sndfile"; then
+  AC_DEFINE(HAVE_SNDFILE, 1, [Define to 1 if sndfile is available.])
+else
+  SNDFILE_CPPFLAGS=
+  SNDFILE_LDFLAGS=
+  SNDFILE_LIBS=
+fi
+AC_SUBST(SNDFILE_CPPFLAGS)
+AC_SUBST(SNDFILE_LDFLAGS)
+AC_SUBST(SNDFILE_LIBS)
+
+### Check for PortAudio
+
+warn_portaudio="PortAudio library fails tests.  The audioplayer, audiorecorder classes and audiodevinfo function for audio playback and recording will not be fully functional."
+
+check_portaudio=no
+AC_ARG_WITH([portaudio],
+  [AS_HELP_STRING([--without-portaudio],
+    [don't use PortAudio library, disable audio playback and recording])],
+  [if test x"$withval" = x"no"; then
+     warn_portaudio="--without-portaudio specified.  The audioplayer, audiorecorder classes and audiodevinfo function for audio playback and recording will not be fully functional."
+   else
+     check_portaudio=yes
+   fi],
+  [check_portaudio=yes])
+
+if test $check_portaudio = yes; then
+  PKG_CHECK_EXISTS([portaudio-2.0 >= 19], [
+    PORTAUDIO_CPPFLAGS=`$PKG_CONFIG --cflags-only-I portaudio-2.0`
+    PORTAUDIO_LDFLAGS=`$PKG_CONFIG --libs-only-L portaudio-2.0`
+    PORTAUDIO_LIBS=`$PKG_CONFIG --libs-only-l portaudio-2.0`
+    warn_portaudio=
+  ])
+fi
+
+if test -z "$warn_portaudio"; then
+  AC_DEFINE(HAVE_PORTAUDIO, 1, [Define to 1 if PortAudio is available.])
+else
+  PORTAUDIO_CPPFLAGS=
+  PORTAUDIO_LDFLAGS=
+  PORTAUDIO_LIBS=
+fi
+AC_SUBST(PORTAUDIO_CPPFLAGS)
+AC_SUBST(PORTAUDIO_LDFLAGS)
+AC_SUBST(PORTAUDIO_LIBS)
+
 ### Check for either of Graphics/ImageMagick++ libraries
 
 AC_ARG_WITH([magick],
@@ -2963,6 +3033,9 @@
   OPENGL libraries:            $OPENGL_LIBS
   PCRE CPPFLAGS:               $PCRE_CPPFLAGS
   PCRE libraries:              $PCRE_LIBS
+  PortAudio CPPFLAGS:          $PORTAUDIO_CPPFLAGS
+  PortAudio LDFLAGS:           $PORTAUDIO_LDFLAGS
+  PortAudio libraries:         $PORTAUDIO_LIBS
   PTHREAD flags:               $PTHREAD_CFLAGS
   PTHREAD libraries:           $PTHREAD_LIBS
   QHULL CPPFLAGS:              $QHULL_CPPFLAGS
@@ -2975,6 +3048,9 @@
   Qt LDFLAGS:                  $QT_LDFLAGS
   Qt libraries:                $QT_LIBS
   READLINE libraries:          $READLINE_LIBS
+  Sndfile CPPFLAGS:            $SNDFILE_CPPFLAGS
+  Sndfile LDFLAGS:             $SNDFILE_LDFLAGS
+  Sndfile libraries:           $SNDFILE_LIBS
   TERM libraries:              $TERM_LIBS
   UMFPACK CPPFLAGS:            $UMFPACK_CPPFLAGS
   UMFPACK LDFLAGS:             $UMFPACK_LDFLAGS
--- a/doc/interpreter/audio.txi	Fri Jan 02 09:38:06 2015 -0800
+++ b/doc/interpreter/audio.txi	Fri Jan 02 16:54:34 2015 -0500
@@ -19,6 +19,75 @@
 @c Written by Kurt Hornik <Kurt.Hornik@wu-wien.ac.at> on 1996/05/14
 
 @node Audio Processing
+
+@chapter Audio File Utilities
+
+The following functions allow you to read, write and retrieve information about audio files. Various formats are supported including wav, flac and ogg vorbis.
+
+@DOCSTRING(audioinfo)
+@DOCSTRING(audioread)
+@DOCSTRING(audiowrite)
+
+@chapter Audio Device Information
+
+@DOCSTRING(audiodevinfo)
+
+@chapter Audio Player
+
+The following methods are used to create and use audioplayer objects. These objects can be used to play back audio data stored in Octave matrices and arrays. The audioplayer object supports playback from various devices available to the system, blocking and non-blocking playback, convenient pausing and resuming and much more.
+
+@DOCSTRING(@audioplayer/audioplayer)
+
+@section Playback
+
+The following methods are used to control player playback.
+
+@DOCSTRING(@audioplayer/play)
+@DOCSTRING(@audioplayer/playblocking)
+@DOCSTRING(@audioplayer/pause)
+@DOCSTRING(@audioplayer/resume)
+@DOCSTRING(@audioplayer/stop)
+@DOCSTRING(@audioplayer/isplaying)
+
+@section Properties
+
+The remaining couple of methods are used to get and set various properties of the audioplayer object.
+
+@DOCSTRING(@audioplayer/get)
+@DOCSTRING(@audioplayer/set)
+
+@chapter Audio Recorder
+
+The following methods are used to create and use audiorecorder objects. These objects can be used to record audio data from various devices available to the system. You can use convenient methods to retrieve that data or audioplayer objects created from that data. Methods for blocking and non-blocking recording, pausing and resuming recording and much more is available.
+
+@DOCSTRING(@audiorecorder/audiorecorder)
+
+@section Recording
+
+The following methods control the recording process.
+
+@DOCSTRING(@audiorecorder/record)
+@DOCSTRING(@audiorecorder/recordblocking)
+@DOCSTRING(@audiorecorder/pause)
+@DOCSTRING(@audiorecorder/resume)
+@DOCSTRING(@audiorecorder/stop)
+@DOCSTRING(@audiorecorder/isrecording)
+
+@section Data Retrieval
+
+The following methods allow you to retrieve recorded audio data in various ways.
+
+@DOCSTRING(@audiorecorder/getaudiodata)
+@DOCSTRING(@audiorecorder/getplayer)
+@DOCSTRING(@audiorecorder/play)
+
+@section Properties
+
+The remaining two methods allow you to read or alter the properties of audiorecorder objects.
+
+@DOCSTRING(@audiorecorder/get)
+@DOCSTRING(@audiorecorder/set)
+
 @chapter Audio Processing
 
 Octave provides a few functions for dealing with audio data.  An audio
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libinterp/dldfcn/audiodevinfo.cc	Fri Jan 02 16:54:34 2015 -0500
@@ -0,0 +1,2717 @@
+/*
+
+Copyright (C) 2013 Vytautas Jančauskas
+
+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/>.
+
+*/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+// <cstdint> requires c++11
+#include <stdint.h>
+
+#include <string>
+#include <vector>
+
+#include "defun-dld.h"
+#include "error.h"
+#include "gripes.h"
+#include "oct-obj.h"
+#include "ov.h"
+#include "ov-int32.h"
+#include "ov-struct.h"
+#include "parse.h"
+
+#if defined (HAVE_PORTAUDIO)
+#include <portaudio.h>
+#endif
+
+PaSampleFormat
+bits_to_format (int bits)
+{
+  if (bits == 8)
+    return paInt8;
+  else if (bits == 16)
+    return paInt16;
+  else if (bits == 24)
+    return paInt24;
+  else if (bits == 32)
+    return paInt32;
+  else if (bits == -1)
+    return paFloat32;
+  else
+    return 0;
+}
+
+DEFUN_DLD (audiodevinfo, args, ,
+  "-*- texinfo -*-\n\
+@deftypefn {Loadable Function} {@var{devinfo} =} audiodevinfo ()\n\
+\n\
+@deftypefnx {Loadable Function} {@var{devs} =} audiodevinfo (@var{io})\n\
+@deftypefnx {Loadable Function} {@var{name} =} audiodevinfo (@var{io}, @var{id})\n\
+@deftypefnx {Loadable Function} {@var{id} =} audiodevinfo (@var{io}, @var{name})\n\
+@deftypefnx {Loadable Function} {@var{id} =} audiodevinfo (@var{io}, @var{rate}, @var{bits}, @var{chans})\n\
+\n\
+@deftypefnx {Loadable Function} {@var{supports} =} audiodevinfo (@var{io}, @var{id}, @var{rate}, @var{bits}, @var{chans})\n\
+\n\
+Return a structure with fields \"input\" and \"output\".\n\
+The value of each field is a structure array with fields\n\
+\"Name\", \"DriverVersion\" and \"ID\" describing an audio device.\n\
+\n\
+\n\
+If the optional argument @var{io} is 1, return information about input\n\
+devices only.  If it is 0, return information about output devices only.\n\
+\n\
+If the optional argument @var{id} is provided, return information about\n\
+corresponding device.\n\
+\n\
+If the optional argument @var{name} is provided, return the id of the\n\
+named device.\n\
+\n\
+Given a sampling rate, bits per sample, and number of channels for\n\
+an input or output device, return the ID of the first device that\n\
+supports playback or recording using the specified parameters.\n\
+\n\
+If also given a device ID, return true if the device supports playback\n\
+or recording using those parameters.\n\
+@end deftypefn")
+{
+  octave_value retval;
+
+#ifdef HAVE_PORTAUDIO
+
+  int nargin = args.length ();
+
+  octave_scalar_map devinfo;
+  octave_value_list input;
+  octave_value_list output;
+
+  PaError err = Pa_Initialize ();
+
+  if (err != paNoError)
+    {
+      error ("audiodevinfo: PortAudio initialization failed");
+      return retval;
+    }
+
+  int num_devices = Pa_GetDeviceCount ();
+
+  if (num_devices < 0)
+    {
+      error ("audiodevinfo: no audio device found");
+      return retval;
+    }
+
+  octave_idx_type numinput = 0, numoutput = 0;
+  for (int i = 0; i < num_devices; i++)
+    {
+      const PaDeviceInfo *device_info = Pa_GetDeviceInfo (i);
+
+      if (device_info->maxInputChannels != 0)
+        numinput++;
+
+      if (device_info->maxOutputChannels != 0)
+        numoutput++;
+    }
+
+  Cell input_name (dim_vector (1, numinput));
+  Cell input_driver_version (dim_vector (1, numinput));
+  Cell input_id (dim_vector (1, numinput));
+  Cell output_name (dim_vector (1, numoutput));
+  Cell output_driver_version (dim_vector (1, numoutput));
+  Cell output_id (dim_vector (1, numoutput));
+
+  octave_idx_type idx_i = 0, idx_o = 0;
+  for (int i = 0; i < num_devices; i++)
+    {
+      const PaDeviceInfo *device_info = Pa_GetDeviceInfo (i);
+      const char *driver;
+      char name[128];
+      driver = Pa_GetHostApiInfo (device_info->hostApi)->name;
+      sprintf (name, "%s (%s)", device_info->name, driver);
+
+      if (device_info->maxInputChannels != 0)
+        {
+          input_name(idx_i) = name;
+          input_driver_version(idx_i) = driver;
+          input_id(idx_i) = i;
+          idx_i++;
+        }
+
+      if (device_info->maxOutputChannels != 0)
+        {
+          output_name(idx_o) = name;
+          output_driver_version(idx_o) = driver;
+          output_id(idx_o) = i;
+          idx_o++;
+        }
+    }
+
+  octave_map inputdev, outputdev;
+  inputdev.setfield ("Name", input_name);
+  inputdev.setfield ("DriverVersion", input_driver_version);
+  inputdev.setfield ("ID", input_id);
+  outputdev.setfield ("Name", output_name);
+  outputdev.setfield ("DriverVersion", output_driver_version);
+  outputdev.setfield ("ID", output_id);
+  devinfo.setfield ("input", inputdev);
+  devinfo.setfield ("output", outputdev);
+
+  // Return information about input and output audio devices and
+  // their properties.
+  if (nargin == 0)
+    retval = devinfo;
+  // Return the number of input or output devices
+  else if (nargin == 1)
+    {
+      if (args(0).int_value () == 0)
+        retval = numoutput;
+      else if (args(0).int_value () == 1)
+        retval = numinput;
+      else
+        {
+          error ("audiodevinfo: please specify 0 for output and 1 for input devices");
+          return retval;
+        }
+    }
+  // Return device name when given id or id when given device name.
+  else if (nargin == 2)
+    {
+      bool found = false;
+      int outin = args(0).int_value ();
+      if (args(1).is_string ())
+        {
+          if (outin == 0)
+            {
+              for (int i = 0; i < numoutput; i++)
+                {
+                  if (output_name(i).string_value () == args(1).string_value ())
+                    {
+                      retval = output_id(i);
+                      found = true;
+                      break;
+                    }
+                }
+            }
+          else if (outin == 1)
+            {
+              for (int i = 0; i < numinput; i++)
+                {
+                  if (input_name(i).string_value () == args(1).string_value ())
+                    {
+                      retval = input_id(i);
+                      found = true;
+                      break;
+                    }
+                }
+            }
+          else
+            {
+              error ("audiodevinfo: please specify 0 for output and 1 for input devices");
+              return retval;
+            }
+        }
+      else
+        {
+          if (outin == 0)
+            {
+              for (int i = 0; i < numoutput; i++)
+                {
+                  if (output_id(i).int_value () == args(1).int_value ())
+                    {
+                      retval = output_name(i);
+                      found = true;
+                      break;
+                    }
+                }
+            }
+          else if (outin == 1)
+            {
+              for (int i = 0; i < numinput; i++)
+                {
+                  if (input_id(i).int_value () == args(1).int_value ())
+                    {
+                      retval = input_name(i);
+                      found = true;
+                      break;
+                    }
+                }
+            }
+          else
+            {
+              error ("audiodevinfo: please specify 0 for output and 1 for input devices");
+              return retval;
+            }
+        }
+      if (not found)
+        error ("audiodevinfo: no device meeting the specified criteria found");
+    }
+  else if (nargin == 3)
+    {
+      // FIXME: what was supposed to happen here?
+    }
+  // Return the id of the first device meeting specified criteria.
+  else if (nargin == 4)
+    {
+      int io = args(0).int_value ();
+      int rate = args(1).int_value ();
+      int bits = args(2).int_value ();
+      int chans = args(3).int_value ();
+
+      for (int i = 0; i < num_devices; i++)
+        {
+          PaStreamParameters stream_parameters;
+          stream_parameters.device = i;
+          stream_parameters.channelCount = chans;
+          PaSampleFormat format = bits_to_format (bits);
+
+          if (format != 0)
+            stream_parameters.sampleFormat = format;
+          else
+            {
+              error ("audiodevinfo: no such bits per sample format");
+              return retval;
+            }
+
+          stream_parameters.suggestedLatency
+            = Pa_GetDeviceInfo (i)->defaultLowInputLatency;
+          stream_parameters.hostApiSpecificStreamInfo = 0;
+
+          if (io == 0)
+            {
+              if (Pa_GetDeviceInfo (i)->maxOutputChannels < chans)
+                continue;
+
+              err = Pa_IsFormatSupported (0, &stream_parameters, rate);
+
+              if (err == paFormatIsSupported)
+                {
+                  retval = i;
+                  return retval;
+                }
+            }
+          else if (io == 1)
+            {
+              if (Pa_GetDeviceInfo (i)->maxInputChannels < chans)
+                continue;
+
+              err = Pa_IsFormatSupported (&stream_parameters, 0, rate);
+              if (err == paFormatIsSupported)
+                {
+                  retval = i;
+                  return retval;
+                }
+            }
+        }
+      retval = -1;
+    }
+  // Check if given device supports specified playback or recording modes.
+  else if (nargin == 5)
+    {
+      int io = args(0).int_value ();
+      int id = args(1).int_value ();
+      int rate = args(2).int_value ();
+      int bits = args(3).int_value ();
+      int chans = args(4).int_value ();
+      PaStreamParameters stream_parameters;
+      stream_parameters.device = id;
+      stream_parameters.channelCount = chans;
+      PaSampleFormat format = bits_to_format (bits);
+      if (format != 0)
+        stream_parameters.sampleFormat = format;
+      else
+        {
+          error ("audiodevinfo: no such bits per sample format");
+          return retval;
+        }
+      stream_parameters.suggestedLatency =
+        Pa_GetDeviceInfo (id)->defaultLowInputLatency;
+      stream_parameters.hostApiSpecificStreamInfo = 0;
+      if (io == 0)
+        {
+          if (Pa_GetDeviceInfo (id)->maxOutputChannels < chans)
+            {
+              retval = 0;
+              return retval;
+            }
+          err = Pa_IsFormatSupported (0, &stream_parameters, rate);
+          if (err == paFormatIsSupported)
+            {
+              retval = 1;
+              return retval;
+            }
+        }
+      else if (io == 1)
+        {
+          if (Pa_GetDeviceInfo (id)->maxInputChannels < chans)
+            {
+              retval = 0;
+              return retval;
+            }
+          err = Pa_IsFormatSupported (&stream_parameters, 0, rate);
+          if (err == paFormatIsSupported)
+            {
+              retval = 1;
+              return retval;
+            }
+        }
+      else
+        {
+          error ("audiodevinfo: please specify 0 for output and 1 for input devices");
+          return retval;
+        }
+      retval = 0;
+    }
+  else
+    {
+      error ("audiodevinfo: wrong number of arguments");
+      return retval;
+    }
+#else
+
+  error ("portaudio not found on your system and thus audio functionality is not present");
+
+#endif
+
+  return retval;
+}
+
+/*
+%!test
+%! devinfo = audiodevinfo;
+%! assert (rows (devinfo.input), 1);
+%! assert (rows (devinfo.output), 1);
+
+%!test
+%! devinfo = audiodevinfo;
+%! nout = audiodevinfo (0);
+%! nin = audiodevinfo (1);
+%! assert (columns (devinfo.output), nout);
+%! assert (columns (devinfo.input), nin);
+
+%!test
+%! devinfo = audiodevinfo;
+%! nout = audiodevinfo (0);
+%! nin = audiodevinfo (1);
+%! for i = 1:nout,
+%!   assert (devinfo.output(i).Name, audiodevinfo (0, devinfo.output(i).ID))
+%! endfor
+%! for i=1:nin,
+%!   assert (devinfo.input(i).Name, audiodevinfo (1, devinfo.input(i).ID))
+%! endfor
+
+%!test
+%! devinfo = audiodevinfo;
+%! nout = audiodevinfo (0);
+%! nin = audiodevinfo (1);
+%! for i = 1:nout,
+%!   assert (devinfo.output(i).ID, audiodevinfo (0, devinfo.output(i).Name))
+%! endfor
+%! for i = 1:nin,
+%!   assert (devinfo.input(i).ID, audiodevinfo (1, devinfo.input(i).Name))
+%! endfor
+*/
+
+enum audio_type { INT8, UINT8, INT16, DOUBLE };
+
+class audioplayer : public octave_base_value
+{
+public:
+  audioplayer (void);
+  ~audioplayer (void) {};
+
+  // Overloaded base functions
+  double player_value (void) const { return 0; }
+  virtual double scalar_value (bool = false) const { return 0; }
+  void print (std::ostream& os, bool pr_as_read_syntax = false) const;
+  void print_raw (std::ostream& os, bool pr_as_read_syntax) const;
+
+  // Properties
+  bool is_constant (void) const { return true; }
+  bool is_defined (void) const { return true; }
+  bool print_as_scalar (void) const { return true; }
+
+  void init (void);
+  void init_fn (void);
+  void set_y (const octave_value& y);
+  void set_y (octave_function *fn);
+  void set_y (std::string fn);
+  Matrix& get_y (void);
+  RowVector *get_left (void);
+  RowVector *get_right (void);
+  void set_fs (int fs);
+  int get_fs (void);
+  void set_nbits (int nbits);
+  int get_nbits (void);
+  void set_id (int id);
+  int get_id (void);
+  int get_channels (void);
+  audio_type get_type (void);
+
+  void set_sample_number (unsigned int sample);
+  unsigned int get_sample_number (void);
+  unsigned int get_total_samples (void);
+  void set_end_sample (unsigned int sample);
+  unsigned int get_end_sample (void);
+  void reset_end_sample (void);
+  void set_tag (const charMatrix& tag);
+  charMatrix get_tag (void);
+  void set_userdata (const octave_value& userdata);
+  octave_value get_userdata (void);
+  PaStream *get_stream (void);
+
+  void playblocking (void);
+  void play (void);
+  void pause (void);
+  void resume (void);
+  void stop (void);
+  bool isplaying (void);
+
+  octave_function *octave_callback_function;
+
+private:
+  int id;
+  int fs;
+  int nbits;
+  int channels;
+  unsigned int sample_number;
+  unsigned int end_sample;
+  charMatrix tag;
+  Matrix y;
+  octave_value userdata;
+  RowVector left;
+  RowVector right;
+  PaStream *stream;
+  PaStreamParameters output_parameters;
+  audio_type type;
+
+  DECLARE_OCTAVE_ALLOCATOR
+  DECLARE_OV_TYPEID_FUNCTIONS_AND_DATA
+};
+
+#define BUFFER_SIZE 512
+
+DEFINE_OCTAVE_ALLOCATOR (audioplayer);
+DEFINE_OV_TYPEID_FUNCTIONS_AND_DATA (audioplayer, "audioplayer", "audioplayer");
+
+int
+is_big_endian (void)
+{
+  union
+    {
+      uint32_t i;
+      char c[4];
+    } bint = { 0x01020304 };
+  return bint.c[0] == 1;
+}
+
+static int
+octave_play_callback (const void *, void *output, unsigned long frames,
+                      const PaStreamCallbackTimeInfo *,
+                      PaStreamCallbackFlags, void *data)
+{
+  audioplayer *player = static_cast<audioplayer *> (data);
+  int big_endian = is_big_endian ();
+  octave_value_list args, retval;
+  args(0) = frames;
+  retval = feval (player->octave_callback_function, args, 1);
+  RowVector sound_l, sound_r;
+  Matrix sound = retval(0).matrix_value ();
+  int return_status = retval(1).int_value ();
+  sound_l.resize (frames);
+  sound_r.resize (frames);
+  if (sound.cols () == 1)
+    {
+      for (unsigned long i = 0; i < frames; i++)
+        {
+          sound_l(i) = sound(i, 0);
+          sound_r(i) = sound(i, 0);
+        }
+    }
+  else if (sound.cols () == 2)
+    {
+      for (unsigned long i = 0; i < frames; i++)
+        {
+          sound_l(i) = sound(i, 0);
+          sound_r(i) = sound(i, 1);
+        }
+    }
+  else
+    return paAbort;
+
+  for (unsigned long i = 0; i < frames; i++)
+    {
+      if (player->get_nbits () == 8)
+        {
+          int8_t *buffer = static_cast<int8_t *> (output);
+          buffer[2 * i] = sound_l.elem (i) * (pow (2.0, 7) - 1);
+          buffer[2 * i + 1] = sound_r.elem (i) * (pow (2.0, 7) - 1);
+        }
+      else if (player->get_nbits () == 16)
+        {
+          int16_t *buffer = static_cast<int16_t *> (output);
+          buffer[2 * i] = sound_l.elem (i) * (pow (2.0, 15) - 1);
+          buffer[2 * i + 1] = sound_r.elem (i) * (pow (2.0, 15) - 1);
+        }
+      else if (player->get_nbits () == 24)
+        {
+          uint8_t *buffer = static_cast<uint8_t *> (output);
+          int32_t sample_l = sound_l.elem (i) * (pow (2.0, 23) - 1);
+          int32_t sample_r = sound_r.elem (i) * (pow (2.0, 23) - 1);
+          sample_l &= 0x00ffffff;
+          sample_r &= 0x00ffffff;
+          // FIXME: Would a mask work better?
+          uint8_t *_sample_l = reinterpret_cast<uint8_t *> (&sample_l);
+          uint8_t *_sample_r = reinterpret_cast<uint8_t *> (&sample_r);
+          buffer[i * 6 + 0] = _sample_l[0 + big_endian];
+          buffer[i * 6 + 1] = _sample_l[1 + big_endian];
+          buffer[i * 6 + 2] = _sample_l[2 + big_endian];
+          buffer[i * 6 + 3] = _sample_r[0 + big_endian];
+          buffer[i * 6 + 4] = _sample_r[1 + big_endian];
+          buffer[i * 6 + 5] = _sample_r[2 + big_endian];
+        }
+    }
+  return return_status;
+}
+
+static int
+portaudio_play_callback (const void *, void *output, unsigned long frames,
+                         const PaStreamCallbackTimeInfo*,
+                         PaStreamCallbackFlags, void *data)
+{
+  audioplayer *player = static_cast<audioplayer *> (data);
+  int big_endian = is_big_endian ();
+  int channels = player->get_channels ();
+  RowVector *sound_l = player->get_left ();
+  RowVector *sound_r;
+
+  if (channels > 1)
+    sound_r = player->get_right ();
+  else
+    sound_r = sound_l;
+
+  for (unsigned long j = 0, k = 0; j < frames; j++, k += 2)
+    {
+      unsigned int sample_number = player->get_sample_number ();
+      if (sample_number > player->get_end_sample ())
+        return paAbort;
+
+      if (player->get_type () == DOUBLE)
+        {
+          if (player->get_nbits () == 8)
+            {
+              int8_t *buffer = static_cast<int8_t *> (output);
+              buffer[k] = sound_l->elem (sample_number) * (pow (2.0, 7) - 1);
+              buffer[k + 1] = sound_r->elem (sample_number) * (pow (2.0, 7) - 1);
+            }
+          else if (player->get_nbits () == 16)
+            {
+              int16_t *buffer = static_cast<int16_t *> (output);
+              buffer[k] = sound_l->elem (sample_number) * (pow (2.0, 15) - 1);
+              buffer[k + 1] = sound_r->elem (sample_number) * (pow (2.0, 15) - 1);
+            }
+          else if (player->get_nbits () == 24)
+            {
+              uint8_t *buffer = static_cast<uint8_t *> (output);
+              int32_t sample_l = sound_l->elem (sample_number) * (pow (2.0, 23) - 1);
+              int32_t sample_r = sound_r->elem (sample_number) * (pow (2.0, 23) - 1);
+              sample_l &= 0x00ffffff;
+              sample_r &= 0x00ffffff;
+              // FIXME: Would a mask work better?
+              uint8_t *_sample_l = reinterpret_cast<uint8_t *> (&sample_l);
+              uint8_t *_sample_r = reinterpret_cast<uint8_t *> (&sample_r);
+              buffer[j * 6 + 0] = _sample_l[0 + big_endian];
+              buffer[j * 6 + 1] = _sample_l[1 + big_endian];
+              buffer[j * 6 + 2] = _sample_l[2 + big_endian];
+              buffer[j * 6 + 3] = _sample_r[0 + big_endian];
+              buffer[j * 6 + 4] = _sample_r[1 + big_endian];
+              buffer[j * 6 + 5] = _sample_r[2 + big_endian];
+            }
+        }
+      else if (player->get_type () == INT8)
+        {
+          int8_t *buffer = static_cast<int8_t *> (output);
+          buffer[k] = sound_l->elem (sample_number);
+          buffer[k + 1] = sound_r->elem (sample_number);
+        }
+      else if (player->get_type () == UINT8)
+        {
+          uint8_t *buffer = static_cast<uint8_t *> (output);
+          buffer[k] = sound_l->elem (sample_number);
+          buffer[k + 1] = sound_r->elem (sample_number);
+        }
+      else if (player->get_type () == INT16)
+        {
+          int16_t *buffer = static_cast<int16_t *> (output);
+          buffer[k] = sound_l->elem (sample_number);
+          buffer[k + 1] = sound_r->elem (sample_number);
+        }
+      player->set_sample_number (sample_number + 1);
+    }
+  return paContinue;
+}
+
+audioplayer::audioplayer (void)
+  : octave_callback_function (0),
+    id (-1), fs (0), nbits (16), channels (0), sample_number (0),
+    end_sample (-1), tag (""), y (), userdata (Matrix ()),
+    left (), right (), stream (0), output_parameters (), type ()
+{ }
+
+void
+audioplayer::print (std::ostream& os, bool pr_as_read_syntax) const
+{
+  print_raw (os, pr_as_read_syntax);
+  newline (os);
+}
+
+void
+audioplayer::print_raw (std::ostream& os, bool) const
+{
+  os << 0;
+}
+
+void
+audioplayer::init_fn (void)
+{
+  if (Pa_Initialize () != paNoError)
+    {
+      error ("audioplayer: initialization error!");
+      return;
+    }
+
+  if (Pa_GetDeviceCount () < 0)
+    {
+      error ("audioplayer: no audio devices found!");
+      return;
+    }
+
+  int device = get_id ();
+
+  if (device == -1)
+    device = Pa_GetDefaultOutputDevice ();
+
+  output_parameters.device = device;
+  output_parameters.channelCount = 2;
+  output_parameters.sampleFormat = bits_to_format (get_nbits ());
+  output_parameters.suggestedLatency = Pa_GetDeviceInfo (device)->defaultHighOutputLatency;
+  output_parameters.hostApiSpecificStreamInfo = 0;
+}
+
+void
+audioplayer::init (void)
+{
+  // Both of these variables are unused.  Should they be
+  // eliminated or is something not yet implemented?
+  //
+  // int channels = y.rows ();
+  // RowVector *sound_l = get_left ();
+
+  if (Pa_Initialize () != paNoError)
+    {
+      error ("audioplayer: initialization error!");
+      return;
+    }
+
+  if (Pa_GetDeviceCount () < 0)
+    {
+      error ("audioplayer: no audio devices found!");
+      return;
+    }
+
+  int device = get_id ();
+
+  if (device == -1)
+    device = Pa_GetDefaultOutputDevice ();
+
+  output_parameters.device = device;
+  output_parameters.channelCount = 2;
+
+  if (type == DOUBLE)
+    output_parameters.sampleFormat = bits_to_format (get_nbits ());
+  else if (type == INT8)
+    output_parameters.sampleFormat = paInt8;
+  else if (type == UINT8)
+    output_parameters.sampleFormat = paUInt8;
+  else if (type == INT16)
+    output_parameters.sampleFormat = paInt16;
+
+  output_parameters.suggestedLatency = Pa_GetDeviceInfo (device)->defaultHighOutputLatency;
+  output_parameters.hostApiSpecificStreamInfo = 0;
+}
+
+void
+audioplayer::set_y (const octave_value& y_arg)
+{
+  if (y_arg.is_int8_type ())
+    type = INT8;
+  else if (y_arg.is_uint8_type ())
+    type = UINT8;
+  else if (y_arg.is_int16_type ())
+    type = INT16;
+  else
+    type = DOUBLE;
+
+  y = y_arg.matrix_value ();
+
+  if (y.rows () > 2)
+    y = y.transpose ();
+
+  channels = y.rows ();
+  left = y.row (0);
+
+  if (channels == 2)
+    right = y.row (1);
+
+  reset_end_sample ();
+}
+
+void
+audioplayer::set_y (octave_function *fn)
+{
+  octave_callback_function = fn;
+  channels = 2;
+  reset_end_sample ();
+}
+
+Matrix&
+audioplayer::get_y (void)
+{
+  return y;
+}
+
+RowVector *
+audioplayer::get_left (void)
+{
+  return &(left);
+}
+
+RowVector *
+audioplayer::get_right (void)
+{
+  return &(right);
+}
+
+void
+audioplayer::set_fs (int fs_arg)
+{
+  fs = fs_arg;
+}
+
+int
+audioplayer::get_fs (void)
+{
+  return fs;
+}
+
+void
+audioplayer::set_nbits (int nbits_arg)
+{
+  nbits = nbits_arg;
+}
+
+int
+audioplayer::get_nbits (void)
+{
+  return nbits;
+}
+
+void
+audioplayer::set_id (int id_arg)
+{
+  id = id_arg;
+}
+
+int
+audioplayer::get_id (void)
+{
+  return id;
+}
+
+int
+audioplayer::get_channels (void)
+{
+  return channels;
+}
+
+audio_type
+audioplayer::get_type (void)
+{
+  return type;
+}
+
+void
+audioplayer::set_sample_number (unsigned int sample_number_arg)
+{
+  sample_number = sample_number_arg;
+}
+
+unsigned int
+audioplayer::get_sample_number (void)
+{
+  return sample_number;
+}
+
+unsigned int
+audioplayer::get_total_samples (void)
+{
+  return left.length ();
+}
+
+void
+audioplayer::set_end_sample (unsigned int end_sample_arg)
+{
+  end_sample = end_sample_arg;
+}
+
+unsigned int
+audioplayer::get_end_sample (void)
+{
+  return end_sample;
+}
+
+void
+audioplayer::reset_end_sample (void)
+{
+  set_end_sample (left.length ());
+}
+
+void
+audioplayer::set_tag (const charMatrix& tag_arg)
+{
+  tag = tag_arg;
+}
+
+charMatrix
+audioplayer::get_tag (void)
+{
+  return tag;
+}
+
+void
+audioplayer::set_userdata (const octave_value& userdata_arg)
+{
+  userdata = userdata_arg;
+}
+
+octave_value
+audioplayer::get_userdata (void)
+{
+  return userdata;
+}
+
+void
+audioplayer::playblocking (void)
+{
+  if (get_stream ())
+    stop ();
+
+  PaError err;
+  uint32_t buffer[BUFFER_SIZE * 2];
+  err = Pa_OpenStream (&stream, 0, &(output_parameters), get_fs (),
+                               BUFFER_SIZE, paClipOff, 0, 0);
+  if (err != paNoError)
+    {
+      error ("audioplayer: unable to open audio playback stream");
+      return;
+    }
+
+  err = Pa_StartStream (stream);
+  if (err != paNoError)
+    {
+      error ("audioplayer: unable to start start audio playback stream");
+      return;
+    }
+
+  unsigned int start, end;
+  start = get_sample_number ();
+  end = get_end_sample ();
+  for (unsigned int i = start; i < end; i += BUFFER_SIZE)
+    {
+      if (octave_callback_function != 0)
+        octave_play_callback (0, buffer, BUFFER_SIZE, 0, 0, this);
+      else
+        portaudio_play_callback (0, buffer, BUFFER_SIZE, 0, 0, this);
+
+      err = Pa_WriteStream (stream, buffer, BUFFER_SIZE);
+    }
+
+  err = Pa_StopStream (stream);
+  if (err != paNoError)
+    {
+      error ("audioplayer: Error stoping audio playback stream");
+      return;
+    }
+
+  err = Pa_CloseStream (stream);
+  if (err != paNoError)
+    {
+      error ("audioplayer: Error closing audio playback stream");
+      return;
+    }
+
+  stream = 0;
+  set_sample_number (0);
+  reset_end_sample ();
+}
+
+void
+audioplayer::play (void)
+{
+  if (get_stream ())
+    stop ();
+
+  PaError err;
+  if (octave_callback_function != 0)
+    err = Pa_OpenStream (&stream, 0, &(output_parameters),
+                         get_fs (), BUFFER_SIZE, paClipOff,
+                         octave_play_callback, this);
+  else
+    err = Pa_OpenStream (&stream, 0, &(output_parameters),
+                         get_fs (), BUFFER_SIZE, paClipOff,
+                         portaudio_play_callback, this);
+
+  if (err != paNoError)
+    {
+      error ("audioplayer: Error opening audio playback stream");
+      return;
+    }
+
+  err = Pa_StartStream (stream);
+  if (err != paNoError)
+    {
+      error ("audioplayer: Error starting audio playback stream");
+      return;
+    }
+}
+
+void
+audioplayer::pause (void)
+{
+  if (get_stream () == 0)
+    return;
+
+  PaError err;
+  err = Pa_StopStream (stream);
+  if (err != paNoError)
+    {
+      error ("audiorecorder: Error stoping audio recording stream");
+      return;
+    }
+}
+
+void
+audioplayer::resume (void)
+{
+  if (get_stream () == 0)
+    return;
+
+  PaError err;
+  err = Pa_StartStream (stream);
+  if (err != paNoError)
+    {
+      error ("audiorecorder: Error starting audio recording stream");
+      return;
+    }
+}
+
+PaStream *
+audioplayer::get_stream (void)
+{
+  return stream;
+}
+
+void
+audioplayer::stop (void)
+{
+  if (get_stream () == 0)
+    return;
+
+  PaError err;
+  set_sample_number (0);
+  reset_end_sample ();
+  if (not Pa_IsStreamStopped (get_stream ()))
+    {
+      err = Pa_AbortStream (get_stream ());
+      if (err != paNoError)
+        {
+          error ("audioplayer: Error stopping audio playback stream");
+          return;
+        }
+    }
+
+  err = Pa_CloseStream (get_stream ());
+  if (err != paNoError)
+    {
+      error ("audioplayer: Error closing audio playback stream");
+      return;
+    }
+
+  stream = 0;
+}
+
+bool
+audioplayer::isplaying (void)
+{
+  if (get_stream () == 0)
+    return false;
+
+  PaError err;
+  err = Pa_IsStreamActive (stream);
+  if (err != 0 && err != 1)
+    {
+      error ("audiorecorder: Error checking stream activity status");
+      return false;
+    }
+
+  return (err == 1);
+}
+
+class audiorecorder : public octave_base_value
+{
+public:
+  audiorecorder (void);
+  ~audiorecorder (void) {};
+
+  // Overloaded base functions
+  double player_value (void) const { return 0; }
+  virtual double scalar_value (bool = false) const { return 0; }
+  void print (std::ostream& os, bool pr_as_read_syntax = false) const;
+  void print_raw (std::ostream& os, bool pr_as_read_syntax) const;
+
+  // Properties
+  bool is_constant (void) const { return true; }
+  bool is_defined (void) const { return true; }
+  bool print_as_scalar (void) const { return true; }
+
+  void init (void);
+  void set_fs (int fs);
+  int get_fs (void);
+  void set_nbits (int nbits);
+  int get_nbits (void);
+  void set_id (int id);
+  int get_id (void);
+  void set_channels (int channels);
+  int get_channels (void);
+  audio_type get_type (void);
+
+  void set_sample_number (unsigned int sample);
+  unsigned int get_sample_number (void);
+  unsigned int get_total_samples (void);
+  void set_end_sample (unsigned int sample);
+  unsigned int get_end_sample (void);
+  void reset_end_sample (void);
+  void set_tag (const charMatrix& tag);
+  charMatrix get_tag (void);
+  void set_userdata (const octave_value& userdata);
+  octave_value get_userdata (void);
+  PaStream *get_stream (void);
+
+  octave_value getaudiodata (void);
+  audioplayer *getplayer (void);
+  bool isrecording (void);
+  audioplayer play (void);
+  void record (void);
+  void recordblocking (float seconds);
+  void pause (void);
+  void resume (void);
+  void stop (void);
+  void append (float sample_l, float sample_r);
+
+  octave_function *octave_callback_function;
+
+private:
+  int id;
+  int fs;
+  int nbits;
+  int channels;
+  unsigned int sample_number;
+  unsigned int end_sample;
+  charMatrix tag;
+  Matrix y;
+  octave_value userdata;
+  std::vector<float> left;
+  std::vector<float> right;
+  PaStream *stream;
+  PaStreamParameters input_parameters;
+  audio_type type;
+
+  DECLARE_OCTAVE_ALLOCATOR
+  DECLARE_OV_TYPEID_FUNCTIONS_AND_DATA
+};
+
+DEFINE_OCTAVE_ALLOCATOR (audiorecorder);
+DEFINE_OV_TYPEID_FUNCTIONS_AND_DATA (audiorecorder, "audiorecorder", "audiorecorder");
+
+static int
+octave_record_callback (const void *input, void *, unsigned long frames,
+                        const PaStreamCallbackTimeInfo *,
+                        PaStreamCallbackFlags, void *data)
+{
+  audiorecorder *recorder = static_cast<audiorecorder *> (data);
+  int channels = recorder->get_channels ();
+  float sample_l, sample_r;
+  Matrix sound;
+  sound.resize (frames, 2);
+  if (recorder->get_nbits () == 8)
+    {
+      const int8_t *input8 = static_cast<const int8_t *> (input);
+      for (unsigned long i = 0; i < frames; i++)
+        {
+          sample_l = input8[i * channels] / (pow (2.0, 7) - 1.0);
+          sample_r = input8[i * channels + (channels - 1)] / (pow (2.0, 7) - 1.0);
+          sound(i, 0) = sample_l;
+          sound(i, 1) = sample_r;
+        }
+      }
+  else if (recorder->get_nbits () == 16)
+    {
+      const int16_t *input16 = static_cast<const int16_t *> (input);
+      for (unsigned long i = 0; i < frames; i++)
+        {
+          sample_l = input16[i * channels] / (pow (2.0, 15) - 1.0);
+          sample_r = input16[i * channels + (channels - 1)] / (pow (2.0, 15) - 1.0);
+          sound(i, 0) = sample_l;
+          sound(i, 1) = sample_r;
+        }
+    }
+  else if (recorder->get_nbits () == 24)
+    {
+      // FIXME: Is there a better way?
+      const uint8_t *input24 = static_cast<const uint8_t *> (input);
+
+      int32_t sample_l32 = 0, sample_r32 = 0;
+      uint8_t *_sample_l = reinterpret_cast<uint8_t *> (&sample_l);
+      uint8_t *_sample_r = reinterpret_cast<uint8_t *> (&sample_r);
+
+      for (unsigned long i = 0; i < frames; i++)
+        {
+          for (int j = 0; j < 3; j++)
+            {
+              _sample_l[j] = input24[i * channels * 3 + j];
+              _sample_r[j] = input24[i * channels * 3 + (channels - 1) * 3 + j];
+            }
+
+          if (sample_l32 & 0x00800000)
+            sample_l32 |= 0xff000000;
+
+          if (sample_r32 & 0x00800000)
+            sample_r32 |= 0xff000000;
+
+          sound(i, 0) = sample_l32 / pow (2.0, 23);
+          sound(i, 1) = sample_r32 / pow (2.0, 23);
+        }
+    }
+
+  octave_value_list args, retval;
+  args(0) = sound;
+  retval = feval (recorder->octave_callback_function, args, 1);
+
+  return retval(0).int_value ();
+}
+
+static int
+portaudio_record_callback (const void *input, void *, unsigned long frames,
+                           const PaStreamCallbackTimeInfo *,
+                           PaStreamCallbackFlags, void *data)
+{
+  audiorecorder *recorder = static_cast<audiorecorder *> (data);
+  int channels = recorder->get_channels ();
+  float sample_l, sample_r;
+  if (recorder->get_nbits () == 8)
+    {
+      const int8_t *input8 = static_cast<const int8_t *> (input);
+      for (unsigned long i = 0; i < frames; i++)
+        {
+          sample_l = input8[i * channels] / (pow (2.0, 7) - 1.0);
+          sample_r = input8[i * channels + (channels - 1)] / (pow (2.0, 7) - 1.0);
+          recorder->append (sample_l, sample_r);
+        }
+    }
+  else if (recorder->get_nbits () == 16)
+    {
+      const int16_t *input16 = static_cast<const int16_t *> (input);
+      for (unsigned long i = 0; i < frames; i++)
+        {
+          sample_l = input16[i * channels] / (pow (2.0, 15) - 1.0);
+          sample_r = input16[i * channels + (channels - 1)] / (pow (2.0, 15) - 1.0);
+          recorder->append (sample_l, sample_r);
+        }
+    }
+  else if (recorder->get_nbits () == 24)
+    {
+      // FIXME: Is there a better way?
+      const uint8_t *input24 = static_cast<const uint8_t *> (input);
+
+      int32_t sample_l32 = 0, sample_r32 = 0;
+      uint8_t *_sample_l = reinterpret_cast<uint8_t *> (&sample_l);
+      uint8_t *_sample_r = reinterpret_cast<uint8_t *> (&sample_r);
+
+      for (unsigned long i = 0; i < frames; i++)
+        {
+          for (int j = 0; j < 3; j++)
+            {
+              _sample_l[j] = input24[i * channels * 3 + j];
+              _sample_r[j] = input24[i * channels * 3 + (channels - 1) * 3 + j];
+            }
+
+          if (sample_l32 & 0x00800000)
+            sample_l32 |= 0xff000000;
+
+          if (sample_r32 & 0x00800000)
+            sample_r32 |= 0xff000000;
+
+          recorder->append (sample_l32 / pow (2.0, 23), sample_r32 / pow (2.0, 23));
+        }
+    }
+
+  if (recorder->get_sample_number () > recorder->get_end_sample ())
+    return paComplete;
+
+  return paContinue;
+}
+
+audiorecorder::audiorecorder (void)
+  : octave_callback_function (0),
+    id (-1), fs (44100), nbits (16), channels (2), sample_number (0),
+    end_sample (-1), tag (""), y (), userdata (Matrix ()),
+    left (), right (), stream (0), input_parameters (), type ()
+{ }
+
+void
+audiorecorder::print (std::ostream& os, bool pr_as_read_syntax) const
+{
+  print_raw (os, pr_as_read_syntax);
+  newline (os);
+}
+
+void
+audiorecorder::print_raw (std::ostream& os, bool) const
+{
+  os << 0;
+}
+
+void
+audiorecorder::init (void)
+{
+  if (Pa_Initialize () != paNoError)
+    {
+      error ("audiorecorder: initialization error!");
+      return;
+    }
+
+  if (Pa_GetDeviceCount () < 0)
+    {
+      error ("audiorecorder: no audio devices found!");
+      return;
+    }
+
+  int device = get_id ();
+
+  if (device == -1)
+    device = Pa_GetDefaultInputDevice ();
+
+  input_parameters.device = device;
+  input_parameters.channelCount = get_channels ();
+  input_parameters.sampleFormat = bits_to_format (get_nbits ());
+  input_parameters.suggestedLatency = Pa_GetDeviceInfo (device)->defaultHighInputLatency;
+  input_parameters.hostApiSpecificStreamInfo = 0;
+}
+
+void
+audiorecorder::set_fs (int fs_arg)
+{
+  fs = fs_arg;
+}
+
+int
+audiorecorder::get_fs (void)
+{
+  return fs;
+}
+
+void
+audiorecorder::set_nbits (int nbits_arg)
+{
+  nbits = nbits_arg;
+}
+
+int
+audiorecorder::get_nbits (void)
+{
+  return nbits;
+}
+
+void
+audiorecorder::set_id (int id_arg)
+{
+  id = id_arg;
+}
+
+int
+audiorecorder::get_id (void)
+{
+  return id;
+}
+
+void
+audiorecorder::set_channels (int channels_arg)
+{
+  assert (channels_arg == 1 || channels_arg == 2);
+  channels = channels_arg;
+}
+
+int
+audiorecorder::get_channels (void)
+{
+  return channels;
+}
+
+audio_type
+audiorecorder::get_type (void)
+{
+  return type;
+}
+
+void
+audiorecorder::set_sample_number (unsigned int sample_number_arg)
+{
+  sample_number = sample_number_arg;
+}
+
+unsigned int
+audiorecorder::get_sample_number (void)
+{
+  return sample_number;
+}
+
+unsigned int
+audiorecorder::get_total_samples (void)
+{
+  return left.size ();
+}
+
+void
+audiorecorder::set_end_sample (unsigned int end_sample_arg)
+{
+  end_sample = end_sample_arg;
+}
+
+unsigned int
+audiorecorder::get_end_sample (void)
+{
+  return end_sample;
+}
+
+void
+audiorecorder::reset_end_sample (void)
+{
+  set_end_sample (left.size ());
+}
+
+void
+audiorecorder::set_tag (const charMatrix& tag_arg)
+{
+  tag = tag_arg;
+}
+
+charMatrix
+audiorecorder::get_tag (void)
+{
+  return tag;
+}
+
+void
+audiorecorder::set_userdata (const octave_value& userdata_arg)
+{
+  userdata = userdata_arg;
+}
+
+octave_value
+audiorecorder::get_userdata (void)
+{
+  return userdata;
+}
+
+octave_value
+audiorecorder::getaudiodata (void)
+{
+  Matrix audio (2, left.size ());
+
+  for (unsigned int i = 0; i < left.size (); i++)
+    {
+      audio(0, i) = left[i];
+      audio(1, i) = right[i];
+    }
+
+  return audio;
+}
+
+audioplayer *
+audiorecorder::getplayer (void)
+{
+  audioplayer *player = new audioplayer ();
+
+  player->set_y (getaudiodata ());
+  player->set_fs (get_fs ());
+  player->set_nbits (get_nbits ());
+  player->init ();
+
+  return player;
+}
+
+bool
+audiorecorder::isrecording (void)
+{
+  if (get_stream () == 0)
+    return false;
+
+  PaError err;
+  err = Pa_IsStreamActive (stream);
+  if (err != 0 && err != 1)
+    {
+      error ("audiorecorder: unable to check stream activity status");
+      return false;
+    }
+
+  return (err == 1);
+}
+
+void
+audiorecorder::record (void)
+{
+  if (get_stream ())
+    stop ();
+
+  left.clear ();
+  right.clear ();
+  PaError err;
+  if (octave_callback_function != 0)
+    {
+      err = Pa_OpenStream (&stream, &(input_parameters), 0,
+                           get_fs (), BUFFER_SIZE, paClipOff,
+                           octave_record_callback, this);
+    }
+  else
+    {
+      err = Pa_OpenStream (&stream, &(input_parameters), 0,
+                           get_fs (), BUFFER_SIZE, paClipOff,
+                           portaudio_record_callback, this);
+    }
+  if (err != paNoError)
+    {
+      error ("audiorecorder: unable to open audio recording stream");
+      return;
+    }
+  err = Pa_StartStream (stream);
+  if (err != paNoError)
+    {
+      error ("audiorecorder: unable to start audio recording stream");
+      return;
+    }
+}
+
+void
+audiorecorder::recordblocking (float seconds)
+{
+  if (get_stream ())
+    stop ();
+
+  left.clear ();
+  right.clear ();
+
+  PaError err;
+  err = Pa_OpenStream (&stream, &(input_parameters), 0,
+                       get_fs (), BUFFER_SIZE, paClipOff, 0, this);
+  if (err != paNoError)
+    {
+      error ("audiorecorder: unable to open audio recording stream");
+      return;
+    }
+
+  err = Pa_StartStream (stream);
+  if (err != paNoError)
+    {
+      error ("audiorecorder: unable to start audio recording stream");
+      return;
+    }
+
+  unsigned int frames = seconds * get_fs ();
+  uint8_t buffer[BUFFER_SIZE * 2 * 3];
+  for (unsigned long i = 0; i < frames / BUFFER_SIZE; i++)
+    {
+      Pa_ReadStream (get_stream (), buffer, BUFFER_SIZE);
+      if (octave_callback_function != 0)
+        octave_record_callback (buffer, 0, BUFFER_SIZE, 0, 0, this);
+      else
+        portaudio_record_callback (buffer, 0, BUFFER_SIZE, 0, 0, this);
+    }
+}
+
+void
+audiorecorder::pause (void)
+{
+  if (get_stream () == 0)
+    return;
+
+  PaError err;
+  err = Pa_StopStream (stream);
+  if (err != paNoError)
+    {
+      error ("audiorecorder: unable to stop audio recording stream");
+      return;
+    }
+}
+
+void
+audiorecorder::resume (void)
+{
+  if (get_stream () == 0)
+    return;
+
+  PaError err;
+  err = Pa_StartStream (stream);
+  if (err != paNoError)
+    {
+      error ("audiorecorder: unable to start audio recording stream");
+      return;
+    }
+}
+
+void
+audiorecorder::stop (void)
+{
+  if (get_stream () == 0)
+    return;
+
+  PaError err;
+  if (not Pa_IsStreamStopped (get_stream ()))
+    {
+      err = Pa_AbortStream (get_stream ());
+      if (err != paNoError)
+        {
+          error ("audioplayer: unable to stop audio playback stream");
+          return;
+        }
+    }
+
+  err = Pa_CloseStream (stream);
+  if (err != paNoError)
+    {
+      error ("audiorecorder: unable to close audio recording stream");
+      return;
+    }
+
+  set_sample_number (0);
+  reset_end_sample ();
+  stream = 0;
+}
+
+void
+audiorecorder::append (float sample_l, float sample_r)
+{
+  left.push_back (sample_l);
+  right.push_back (sample_r);
+  set_sample_number (get_sample_number () + 1);
+}
+
+PaStream *
+audiorecorder::get_stream (void)
+{
+  return stream;
+}
+
+DEFUN_DLD (__recorder_audiorecorder__, args, ,
+  "-*- texinfo -*-\n\
+@deftypefn  {Loadable Function} {@var{recorder} =} __recorder_audiorecorder__ (@var{fs}, @var{nbits}, @var{channels})\n\
+@deftypefnx {Loadable Function} {@var{recorder} =} __recorder_audiorecorder__ (@var{fs}, @var{nbits}, @var{channels}, @var{id})\n\
+@deftypefnx {Loadable Function} {@var{recorder} =} __recorder_audiorecorder__ (@var{fcn}, @dots{})\n\
+Undocumented internal function.\n\
+@end deftypefn")
+{
+  octave_value retval;
+
+#ifdef HAVE_PORTAUDIO
+
+  int nargin = args.length ();
+
+  audiorecorder* recorder = new audiorecorder ();
+
+  int offset = 0;
+
+  if (nargin > 0)
+    {
+      bool is_function = args(0).is_string () || args(0).is_function_handle () || args(0).is_inline_function ();
+
+      if (is_function)
+        {
+          recorder->octave_callback_function = args(0).function_value ();
+          offset = 1;
+        }
+    }
+
+  switch (nargin - offset)
+    {
+    case 3:
+      recorder->set_fs (args(0 + offset).int_value ());
+      recorder->set_nbits (args(1 + offset).int_value ());
+      recorder->set_channels (args(2 + offset).int_value ());
+      break;
+
+    case 4:
+      recorder->set_fs (args(0 + offset).int_value ());
+      recorder->set_nbits (args(1 + offset).int_value ());
+      recorder->set_channels (args(2 + offset).int_value ());
+      recorder->set_id (args(3 + offset).int_value ());
+      break;
+    }
+
+  recorder->init ();
+
+  retval = recorder;
+
+#else
+
+  error ("portaudio not found on your system and thus audio functionality is not present");
+
+#endif
+
+  return retval;
+}
+
+static audiorecorder *
+get_recorder (const octave_value& ov)
+{
+  const octave_base_value& rep = ov.get_rep ();
+
+  octave_base_value *ncrep = const_cast<octave_base_value *> (&rep);
+
+  return dynamic_cast<audiorecorder *> (ncrep);
+}
+
+DEFUN_DLD (__recorder_getaudiodata__, args, ,
+  "-*- texinfo -*-\n\
+@deftypefn {Loadable Function} {@var{data}} __recorder_getaudiodata__ (@var{recorder})\n\
+Undocumented internal function.\n\
+@end deftypefn")
+{
+  octave_value retval;
+
+#ifdef HAVE_PORTAUDIO
+
+  audiorecorder *recorder = get_recorder (args(0));
+  retval = recorder->getaudiodata ();
+
+#else
+
+  error ("portaudio not found on your system and thus audio functionality is not present");
+
+#endif
+
+  return retval;
+}
+
+DEFUN_DLD (__recorder_get_channels__, args, ,
+  "-*- texinfo -*-\n\
+@deftypefn {Loadable Function} {@var{n} =} __recorder_get_channels__ (@var{recorder})\n\
+Undocumented internal function.\n\
+@end deftypefn")
+{
+  octave_value retval;
+
+#ifdef HAVE_PORTAUDIO
+
+  if (args.length () == 1)
+    {
+      audiorecorder *recorder = get_recorder (args(0));
+      retval = recorder->get_channels ();
+    }
+
+#else
+
+  error ("portaudio not found on your system and thus audio functionality is not present");
+
+#endif
+
+  return retval;
+}
+
+DEFUN_DLD (__recorder_get_fs__, args, ,
+  "-*- texinfo -*-\n\
+@deftypefn {Loadable Function} {@var{fs} =} __recorder_get_fs__ (@var{recorder})\n\
+Undocumented internal function.\n\
+@end deftypefn")
+{
+  octave_value retval;
+
+#ifdef HAVE_PORTAUDIO
+
+  if (args.length () == 1)
+    {
+      audiorecorder *recorder = get_recorder (args(0));
+      retval = recorder->get_fs ();
+    }
+
+#else
+
+  error ("portaudio not found on your system and thus audio functionality is not present");
+
+#endif
+
+  return retval;
+}
+
+DEFUN_DLD (__recorder_get_id__, args, ,
+  "-*- texinfo -*-\n\
+@deftypefn {Loadable Function} {@var{id} =} __recorder_get_id__ (@var{recorder})\n\
+Undocumented internal function.\n\
+@end deftypefn")
+{
+  octave_value retval;
+
+#ifdef HAVE_PORTAUDIO
+
+  if (args.length () == 1)
+    {
+      audiorecorder *recorder = get_recorder (args(0));
+      retval = recorder->get_id ();
+    }
+
+#else
+
+  error ("portaudio not found on your system and thus audio functionality is not present");
+
+#endif
+
+  return retval;
+}
+
+DEFUN_DLD (__recorder_get_nbits__, args, ,
+  "-*- texinfo -*-\n\
+@deftypefn {Loadable Function} {@var{nbits} =} __recorder_get_nbits__ (@var{recorder})\n\
+Undocumented internal function.\n\
+@end deftypefn")
+{
+  octave_value retval;
+
+#ifdef HAVE_PORTAUDIO
+
+  if (args.length () == 1)
+    {
+      audiorecorder *recorder = get_recorder (args(0));
+      retval = recorder->get_nbits ();
+    }
+
+#else
+
+  error ("portaudio not found on your system and thus audio functionality is not present");
+
+#endif
+
+  return retval;
+}
+
+DEFUN_DLD (__recorder_get_sample_number__, args, ,
+  "-*- texinfo -*-\n\
+@deftypefn {Loadable Function} {@var{n} =} __recorder_get_sample_number__ (@var{recorder})\n\
+Undocumented internal function.\n\
+@end deftypefn")
+{
+  octave_value retval;
+
+#ifdef HAVE_PORTAUDIO
+
+  if (args.length () == 1)
+    {
+      audiorecorder *recorder = get_recorder (args(0));
+      retval = recorder->get_sample_number ();
+    }
+
+#else
+
+  error ("portaudio not found on your system and thus audio functionality is not present");
+
+#endif
+
+  return retval;
+}
+
+DEFUN_DLD (__recorder_get_tag__, args, ,
+  "-*- texinfo -*-\n\
+@deftypefn {Loadable Function} {@var{tag} =} __recorder_get_tag__ (@var{recorder})\n\
+Undocumented internal function.\n\
+@end deftypefn")
+{
+  octave_value retval;
+
+#ifdef HAVE_PORTAUDIO
+
+  if (args.length () == 1)
+    {
+      audiorecorder *recorder = get_recorder (args(0));
+      retval = recorder->get_tag ();
+    }
+
+#else
+
+  error ("portaudio not found on your system and thus audio functionality is not present");
+
+#endif
+
+  return retval;
+}
+
+DEFUN_DLD (__recorder_get_total_samples__, args, ,
+  "-*- texinfo -*-\n\
+@deftypefn {Loadable Function} {@var{n} =} __recorder_get_total_samples__ (@var{recorder})\n\
+Undocumented internal function.\n\
+@end deftypefn")
+{
+  octave_value retval;
+
+#ifdef HAVE_PORTAUDIO
+
+  if (args.length () == 1)
+    {
+      audiorecorder *recorder = get_recorder (args(0));
+      retval = recorder->get_total_samples ();
+    }
+
+#else
+
+  error ("portaudio not found on your system and thus audio functionality is not present");
+
+#endif
+
+  return retval;
+}
+
+DEFUN_DLD (__recorder_get_userdata__, args, ,
+  "-*- texinfo -*-\n\
+@deftypefn {Loadable Function} {@var{data} =} __recorder_get_userdata__ (@var{recorder})\n\
+Undocumented internal function.\n\
+@end deftypefn")
+{
+  octave_value retval;
+
+#ifdef HAVE_PORTAUDIO
+
+  if (args.length () == 1)
+    {
+      audiorecorder *recorder = get_recorder (args(0));
+      retval = recorder->get_userdata ();
+    }
+
+#else
+
+  error ("portaudio not found on your system and thus audio functionality is not present");
+
+#endif
+
+  return retval;
+}
+
+DEFUN_DLD (__recorder_isrecording__, args, ,
+  "-*- texinfo -*-\n\
+@deftypefn {Loadable Function} {} __recorder_isrecording__ (@var{recorder})\n\
+Undocumented internal function.\n\
+@end deftypefn")
+{
+  octave_value retval;
+
+#ifdef HAVE_PORTAUDIO
+
+  if (args.length () == 1)
+    {
+      audiorecorder *recorder = get_recorder (args(0));
+      retval = recorder->isrecording () ? true : false;
+    }
+
+#else
+
+  error ("portaudio not found on your system and thus audio functionality is not present");
+
+#endif
+
+  return retval;
+}
+
+DEFUN_DLD (__recorder_pause__, args, ,
+  "-*- texinfo -*-\n\
+@deftypefn {Loadable Function} {} __recorder_pause__ (@var{recorder})\n\
+Undocumented internal function.\n\
+@end deftypefn")
+{
+  octave_value retval;
+
+#ifdef HAVE_PORTAUDIO
+
+  if (args.length () == 1)
+    {
+      audiorecorder *recorder = get_recorder (args(0));
+      recorder->pause ();
+    }
+
+#else
+
+  error ("portaudio not found on your system and thus audio functionality is not present");
+
+#endif
+
+  return retval;
+}
+
+DEFUN_DLD (__recorder_recordblocking__, args, ,
+  "-*- texinfo -*-\n\
+@deftypefn {Loadable Function} {} __recorder_recordblocking__ (@var{recorder}, @var{seconds})\n\
+Undocumented internal function.\n\
+@end deftypefn")
+{
+  octave_value retval;
+
+#ifdef HAVE_PORTAUDIO
+
+  audiorecorder *recorder = get_recorder (args(0));
+  recorder->recordblocking (args(1).float_value ());
+
+#else
+
+  error ("portaudio not found on your system and thus audio functionality is not present");
+
+#endif
+
+  return retval;
+}
+
+DEFUN_DLD (__recorder_record__, args, ,
+  "-*- texinfo -*-\n\
+@deftypefn  {Loadable Function} {} __recorder_record__ (@var{recorder})\n\
+@deftypefnx {Loadable Function} {} __recorder_record__ (@var{recorder}, @var{seconds})\n\
+Undocumented internal function.\n\
+@end deftypefn")
+{
+  octave_value retval;
+
+#ifdef HAVE_PORTAUDIO
+
+  audiorecorder *recorder = get_recorder (args(0));
+
+  if (args.length () == 1)
+    recorder->record ();
+  else if (args.length () == 2)
+    {
+      recorder->set_end_sample (args(1).int_value () * recorder->get_fs ());
+      recorder->record ();
+    }
+  else
+    error ("audiorecorder: wrong number of arguments passed to record");
+
+#else
+
+  error ("portaudio not found on your system and thus audio functionality is not present");
+
+#endif
+
+  return retval;
+}
+
+DEFUN_DLD (__recorder_resume__, args, ,
+  "-*- texinfo -*-\n\
+@deftypefn {Loadable Function} {} __recorder_resume__ (@var{recorder})\n\
+Undocumented internal function.\n\
+@end deftypefn")
+{
+  octave_value retval;
+
+#ifdef HAVE_PORTAUDIO
+
+  if (args.length () == 1)
+    {
+      audiorecorder *recorder = get_recorder (args(0));
+      recorder->resume ();
+    }
+
+#else
+
+  error ("portaudio not found on your system and thus audio functionality is not present");
+
+#endif
+
+  return retval;
+}
+
+DEFUN_DLD (__recorder_set_fs__, args, ,
+  "-*- texinfo -*-\n\
+@deftypefn {Loadable Function} {} __recorder_set_fs__ (@var{recorder}, @var{fs})\n\
+Undocumented internal function.\n\
+@end deftypefn")
+{
+  octave_value retval;
+
+#ifdef HAVE_PORTAUDIO
+
+  if (args.length () == 2)
+    {
+      audiorecorder *recorder = get_recorder (args(0));
+      recorder->set_fs (args(1).int_value ());
+    }
+
+#else
+
+  error ("portaudio not found on your system and thus audio functionality is not present");
+
+#endif
+
+  return retval;
+}
+
+DEFUN_DLD (__recorder_set_tag__, args, ,
+  "-*- texinfo -*-\n\
+@deftypefn {Loadable Function} {} __recorder_set_tag__ (@var{recorder}, @var{tag})\n\
+Undocumented internal function.\n\
+@end deftypefn")
+{
+  octave_value retval;
+
+#ifdef HAVE_PORTAUDIO
+
+  if (args.length () == 2)
+    {
+      audiorecorder *recorder = get_recorder (args(0));
+      recorder->set_tag (args(1).char_matrix_value ());
+    }
+
+#else
+
+  error ("portaudio not found on your system and thus audio functionality is not present");
+
+#endif
+
+  return retval;
+}
+
+DEFUN_DLD (__recorder_set_userdata__, args, ,
+  "-*- texinfo -*-\n\
+@deftypefn {Loadable Function} {} __recorder_set_userdata__ (@var{recorder}, @var{data})\n\
+Undocumented internal function.\n\
+@end deftypefn")
+{
+  octave_value retval;
+
+#ifdef HAVE_PORTAUDIO
+
+  if (args.length () == 2)
+    {
+      audiorecorder *recorder = get_recorder (args(0));
+      recorder->set_userdata (args(1));
+    }
+
+#else
+
+  error ("portaudio not found on your system and thus audio functionality is not present");
+
+#endif
+
+  return retval;
+}
+
+DEFUN_DLD (__recorder_stop__, args, ,
+  "-*- texinfo -*-\n\
+@deftypefn {Loadable Function} {} __recorder_stop__ (@var{recorder})\n\
+Undocumented internal function.\n\
+@end deftypefn")
+{
+  octave_value retval;
+
+#ifdef HAVE_PORTAUDIO
+
+  audiorecorder *recorder = get_recorder (args(0));
+  recorder->stop ();
+
+#else
+
+  error ("portaudio not found on your system and thus audio functionality is not present");
+
+#endif
+
+  return retval;
+}
+
+DEFUN_DLD (__player_audioplayer__, args, ,
+  "-*- texinfo -*-\n\
+@deftypefn  {Loadable Function} {@var{player} =} __player_audioplayer__ (@var{y}, @var{fs})\n\
+@deftypefnx {Loadable Function} {@var{player} =} __player_audioplayer__ (@var{y}, @var{fs}, @var{nbits})\n\
+@deftypefnx {Loadable Function} {@var{player} =} __player_audioplayer__ (@var{y}, @var{fs}, @var{nbits}, @var{id})\n\
+Undocumented internal function.\n\
+@end deftypefn")
+{
+  octave_value retval;
+
+#ifdef HAVE_PORTAUDIO
+
+  int nargin = args.length ();
+
+  audioplayer* recorder = new audioplayer ();
+
+  bool is_function = args(0).is_string () || args(0).is_function_handle () || args(0).is_inline_function ();
+
+  if (is_function)
+    recorder->set_y (args(0).function_value ());
+  else
+    recorder->set_y (args(0));
+
+  recorder->set_fs (args(1).int_value ());
+
+  switch (nargin)
+    {
+    case 3:
+      recorder->set_nbits (args(2).int_value ());
+      break;
+    case 4:
+      recorder->set_nbits (args(2).int_value ());
+      recorder->set_id (args(3).int_value ());
+      break;
+    }
+
+  if (is_function)
+    recorder->init_fn ();
+  else
+    recorder->init ();
+
+  retval = recorder;
+
+#else
+
+  error ("portaudio not found on your system and thus audio functionality is not present");
+
+#endif
+
+  return retval;
+}
+
+static audioplayer *
+get_player (const octave_value& ov)
+{
+  const octave_base_value& rep = ov.get_rep ();
+
+  octave_base_value *ncrep = const_cast<octave_base_value *> (&rep);
+
+  return dynamic_cast<audioplayer *> (ncrep);
+}
+
+DEFUN_DLD (__player_get_channels__, args, ,
+  "-*- texinfo -*-\n\
+@deftypefn {Loadable Function} {@var{n} =} __player_get_channels__ (@var{player})\n\
+Undocumented internal function.\n\
+@end deftypefn")
+{
+  octave_value retval;
+
+#ifdef HAVE_PORTAUDIO
+
+  if (args.length () == 1)
+    {
+      audioplayer *player = get_player (args(0));
+      retval = player->get_channels ();
+    }
+
+#else
+
+  error ("portaudio not found on your system and thus audio functionality is not present");
+
+#endif
+
+  return retval;
+}
+
+DEFUN_DLD (__player_get_fs__, args, ,
+  "-*- texinfo -*-\n\
+@deftypefn {Loadable Function} {@var{fs} =} __player_get_fs__ (@var{player})\n\
+Undocumented internal function.\n\
+@end deftypefn")
+{
+  octave_value retval;
+
+#ifdef HAVE_PORTAUDIO
+
+  if (args.length () == 1)
+    {
+      audioplayer *player = get_player (args(0));
+      retval = player->get_fs ();
+    }
+
+#else
+
+  error ("portaudio not found on your system and thus audio functionality is not present");
+
+#endif
+
+  return retval;
+}
+
+DEFUN_DLD (__player_get_id__, args, ,
+  "-*- texinfo -*-\n\
+@deftypefn {Loadable Function} {@var{id} =} __player_get_id__ (@var{player})\n\
+Undocumented internal function.\n\
+@end deftypefn")
+{
+  octave_value retval;
+
+#ifdef HAVE_PORTAUDIO
+
+  if (args.length () == 1)
+    {
+      audioplayer *player = get_player (args(0));
+      retval = player->get_id ();
+    }
+
+#else
+
+  error ("portaudio not found on your system and thus audio functionality is not present");
+
+#endif
+
+  return retval;
+}
+
+DEFUN_DLD (__player_get_nbits__, args, ,
+  "-*- texinfo -*-\n\
+@deftypefn {Loadable Function} {@var{nbits} =} __player_get_nbits__ (@var{player})\n\
+Undocumented internal function.\n\
+@end deftypefn")
+{
+  octave_value retval;
+
+#ifdef HAVE_PORTAUDIO
+
+  if (args.length () == 1)
+    {
+      audioplayer *player = get_player (args(0));
+      retval = player->get_nbits ();
+    }
+
+#else
+
+  error ("portaudio not found on your system and thus audio functionality is not present");
+
+#endif
+
+  return retval;
+}
+
+DEFUN_DLD (__player_get_sample_number__, args, ,
+  "-*- texinfo -*-\n\
+@deftypefn {Loadable Function} {@var{n} =} __player_get_sample_number__ (@var{player})\n\
+Undocumented internal function.\n\
+@end deftypefn")
+{
+  octave_value retval;
+
+#ifdef HAVE_PORTAUDIO
+
+  if (args.length () == 1)
+    {
+      audioplayer *player = get_player (args(0));
+      retval = player->get_sample_number ();
+    }
+
+#else
+
+  error ("portaudio not found on your system and thus audio functionality is not present");
+
+#endif
+
+  return retval;
+}
+
+DEFUN_DLD (__player_get_tag__, args, ,
+  "-*- texinfo -*-\n\
+@deftypefn {Loadable Function} {@var{tag} =} __player_get_tag__ (@var{player})\n\
+Undocumented internal function.\n\
+@end deftypefn")
+{
+  octave_value retval;
+
+#ifdef HAVE_PORTAUDIO
+
+  if (args.length () == 1)
+    {
+      audioplayer *player = get_player (args(0));
+      retval = player->get_tag ();
+    }
+
+#else
+
+  error ("portaudio not found on your system and thus audio functionality is not present");
+
+#endif
+
+  return retval;
+}
+
+DEFUN_DLD (__player_get_total_samples__, args, ,
+  "-*- texinfo -*-\n\
+@deftypefn {Loadable Function} {@var{n} =} __player_get_total_samples__ (@var{player})\n\
+Undocumented internal function.\n\
+@end deftypefn")
+{
+  octave_value retval;
+
+#ifdef HAVE_PORTAUDIO
+
+  if (args.length () == 1)
+    {
+      audioplayer *player = get_player (args(0));
+      retval = player->get_total_samples ();
+    }
+
+#else
+
+  error ("portaudio not found on your system and thus audio functionality is not present");
+
+#endif
+
+  return retval;
+}
+
+DEFUN_DLD (__player_get_userdata__, args, ,
+  "-*- texinfo -*-\n\
+@deftypefn {Loadable Function} {@var{data} =} __player_get_userdata__ (@var{player})\n\
+Undocumented internal function.\n\
+@end deftypefn")
+{
+  octave_value retval;
+
+#ifdef HAVE_PORTAUDIO
+
+  if (args.length () == 1)
+    {
+      audioplayer *player = get_player (args(0));
+      retval = player->get_userdata ();
+    }
+
+#else
+
+  error ("portaudio not found on your system and thus audio functionality is not present");
+
+#endif
+
+  return retval;
+}
+
+DEFUN_DLD (__player_isplaying__, args, ,
+  "-*- texinfo -*-\n\
+@deftypefn {Loadable Function} {} __player_isplaying__ (@var{player})\n\
+Undocumented internal function.\n\
+@end deftypefn")
+{
+  octave_value retval;
+
+#ifdef HAVE_PORTAUDIO
+
+  if (args.length () == 1)
+    {
+      audioplayer *player = get_player (args(0));
+      retval = player->isplaying () ? true : false;
+    }
+
+#else
+
+  error ("portaudio not found on your system and thus audio functionality is not present");
+
+#endif
+
+  return retval;
+}
+
+DEFUN_DLD (__player_pause__, args, ,
+  "-*- texinfo -*-\n\
+@deftypefn {Loadable Function} {} __player_pause__ (@var{player})\n\
+Undocumented internal function.\n\
+@end deftypefn")
+{
+  octave_value retval;
+
+#ifdef HAVE_PORTAUDIO
+
+  if (args.length () == 1)
+    {
+      audioplayer *player = get_player (args(0));
+      player->pause ();
+    }
+
+#else
+
+  error ("portaudio not found on your system and thus audio functionality is not present");
+
+#endif
+
+  return retval;
+}
+
+DEFUN_DLD (__player_playblocking__, args, ,
+  "-*- texinfo -*-\n\
+@deftypefn  {Loadable Function} {} __player_playblocking__ (@var{player})\n\
+@deftypefnx {Loadable Function} {} __player_playblocking__ (@var{player}, @var{start})\n\
+@deftypefnx {Loadable Function} {} __player_playblocking__ (@var{player}, [@var{start}, @var{end}])\n\
+Undocumented internal function.\n\
+@end deftypefn")
+{
+  octave_value retval;
+
+#ifdef HAVE_PORTAUDIO
+
+  if (args.length () == 1)
+    {
+      audioplayer *player = get_player (args(0));
+      player->playblocking ();
+    }
+  else
+    {
+      audioplayer *player = get_player (args(0));
+      if (args(1).is_matrix_type ())
+        {
+          RowVector range = args(1).row_vector_value ();
+
+          unsigned int start = range.elem (0) - 1;
+          unsigned int end = range.elem (1) - 1;
+
+          if (start > player->get_total_samples ()
+              || start > end || end > player->get_total_samples ())
+            {
+              error ("audioplayer: invalid range specified for playback");
+              return retval;
+            }
+
+          player->set_sample_number (start);
+          player->set_end_sample (end);
+        }
+      else
+        {
+          unsigned int start = args(1).int_value () - 1;
+
+          if (start > player->get_total_samples ())
+            {
+              error ("audioplayer: invalid range specified for playback");
+              return retval;
+            }
+
+          player->set_sample_number (start);
+        }
+
+      player->playblocking ();
+    }
+
+#else
+
+  error ("portaudio not found on your system and thus audio functionality is not present");
+
+#endif
+
+  return retval;
+}
+
+DEFUN_DLD (__player_play__, args, ,
+  "-*- texinfo -*-\n\
+@deftypefn  {Loadable Function} {} __player_play__ (@var{player})\n\
+@deftypefnx {Loadable Function} {} __player_play__ (@var{player}, @var{start})\n\
+@deftypefnx {Loadable Function} {} __player_play__ (@var{player}, [@var{start}, @var{end}])\n\
+Undocumented internal function.\n\
+@end deftypefn")
+{
+  octave_value retval;
+
+#ifdef HAVE_PORTAUDIO
+
+  if (args.length () == 1)
+    {
+      audioplayer *player = get_player (args(0));
+      player->play ();
+    }
+  else
+    {
+      audioplayer *player = get_player (args(0));
+
+      if (args(1).is_matrix_type ())
+        {
+          RowVector range = args(1).row_vector_value ();
+
+          unsigned int start = range.elem (0) - 1;
+          unsigned int end = range.elem (1) - 1;
+
+          if (start > player->get_total_samples ()
+              || start > end || end > player->get_total_samples ())
+            {
+              error ("audioplayer: invalid range specified for playback");
+              return retval;
+            }
+
+          player->set_sample_number (start);
+          player->set_end_sample (end);
+        }
+      else
+        {
+          unsigned int start = args(1).int_value () - 1;
+
+          if (start > player->get_total_samples ())
+            {
+              error ("audioplayer: invalid range specified for playback");
+              return retval;
+            }
+
+          player->set_sample_number (start);
+        }
+
+      player->play ();
+    }
+
+#else
+
+  error ("portaudio not found on your system and thus audio functionality is not present");
+
+#endif
+
+  return retval;
+}
+
+DEFUN_DLD (__player_resume__, args, ,
+  "-*- texinfo -*-\n\
+@deftypefn {Loadable Function} {} __player_resume__ (@var{player})\n\
+Undocumented internal function.\n\
+@end deftypefn")
+{
+  octave_value retval;
+
+#ifdef HAVE_PORTAUDIO
+
+  if (args.length () == 1)
+    {
+      audioplayer *player = get_player (args(0));
+      player->resume ();
+    }
+
+#else
+
+  error ("portaudio not found on your system and thus audio functionality is not present");
+
+#endif
+
+  return retval;
+}
+
+DEFUN_DLD (__player_set_fs__, args, ,
+  "-*- texinfo -*-\n\
+@deftypefn {Loadable Function} {} __player_set_fs__ (@var{player}, @var{fs})\n\
+Undocumented internal function.\n\
+@end deftypefn")
+{
+  octave_value retval;
+
+#ifdef HAVE_PORTAUDIO
+
+  if (args.length () == 2)
+    {
+      audioplayer *player = get_player (args(0));
+      player->set_fs (args(1).int_value ());
+    }
+
+#else
+
+  error ("portaudio not found on your system and thus audio functionality is not present");
+
+#endif
+
+  return retval;
+}
+
+DEFUN_DLD (__player_set_tag__, args, ,
+  "-*- texinfo -*-\n\
+@deftypefn {Loadable Function} {} __player_set_tag__ (@var{player}, @var{tag})\n\
+Undocumented internal function.\n\
+@end deftypefn")
+{
+  octave_value retval;
+
+#ifdef HAVE_PORTAUDIO
+
+  if (args.length () == 2)
+    {
+      audioplayer *player = get_player (args(0));
+      player->set_tag (args(1).char_matrix_value ());
+    }
+
+#else
+
+  error ("portaudio not found on your system and thus audio functionality is not present");
+
+#endif
+
+  return retval;
+}
+
+DEFUN_DLD (__player_set_userdata__, args, ,
+  "-*- texinfo -*-\n\
+@deftypefn {Loadable Function} {} __player_set_userdata__ (@var{player}, @var{data})\n\
+Undocumented internal function.\n\
+@end deftypefn")
+{
+  octave_value retval;
+
+#ifdef HAVE_PORTAUDIO
+
+  if (args.length () == 2)
+    {
+      audioplayer *player = get_player (args(0));
+      player->set_userdata (args(1));
+    }
+
+#else
+
+  error ("portaudio not found on your system and thus audio functionality is not present");
+
+#endif
+
+  return retval;
+}
+
+DEFUN_DLD (__player_stop__, args, ,
+  "-*- texinfo -*-\n\
+@deftypefn {Loadable Function} {} __player_stop__ (@var{player})\n\
+Undocumented internal function.\n\
+@end deftypefn")
+{
+  octave_value retval;
+
+#ifdef HAVE_PORTAUDIO
+
+  if (args.length () == 1)
+    {
+      audioplayer *player = get_player (args (0));
+      player->stop ();
+    }
+
+#else
+
+  error ("portaudio not found on your system and thus audio functionality is not present");
+
+#endif
+
+  return retval;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libinterp/dldfcn/audioread.cc	Fri Jan 02 16:54:34 2015 -0500
@@ -0,0 +1,393 @@
+/*
+
+Copyright (C) 2013 Vytautas Jančauskas
+
+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/>.
+
+*/
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <string>
+#include <map>
+
+#include "oct-locbuf.h"
+
+#include "defun-dld.h"
+#include "error.h"
+#include "gripes.h"
+#include "oct-obj.h"
+#include "ov.h"
+#include "ov-struct.h"
+
+#ifdef HAVE_SNDFILE
+#include <sndfile.h>
+#endif
+
+DEFUN_DLD (audioread, args, ,
+  "-*- texinfo -*-\n\
+@deftypefn {Loadable Function} {[@var{y}, @var{fs}] =} audioread (@var{filename})\n\
+@deftypefnx {Loadable Function} {[@var{y}, @var{fs}] =} audioread (@var{filename}, @var{samples})\n\
+\n\
+@deftypefnx {Loadable Function} {[@var{y}, @var{fs}] =} audioread (@var{filename}, @var{datatype})\n\
+@deftypefnx {Loadable Function} {[@var{y}, @var{fs}] =} audioread (@var{filename}, @var{samples}, @var{datatype})\n\
+Read the audio file @var{filename} and return the audio data and sampling\n\
+rate.  The audio data is stored as matrix with rows corresponding\n\
+to audio frames and columns corresponding to channels.\n\
+\n\
+The optional two-element vector argument @var{samples} specifies starting\n\
+and ending frames.\n\
+\n\
+The optional argument @var{datatype} specifies the datatype to return.\n\
+If it is @qcode{\"native\"}, then the type of data depends on how the\n\
+data is stored in the audio file.\n\
+\n\
+Read a file and return a specified range of frames in an array of specified type.\n\
+\n\
+@end deftypefn")
+{
+  octave_value_list retval;
+
+#ifdef HAVE_SNDFILE
+
+  int nargin = args.length ();
+
+  if (nargin < 1 || nargin > 3)
+    {
+      print_usage ();
+      return retval;
+    }
+
+  std::string filename = args(0).string_value ();
+
+  if (error_state)
+    return retval;
+  
+  SF_INFO info;
+  info.format = 0;
+  SNDFILE *file = sf_open (filename.c_str (), SFM_READ, &info);
+  int start = 0;
+  int end = info.frames;
+  OCTAVE_LOCAL_BUFFER (float, data, info.frames * info.channels);
+  sf_read_float (file, data, info.frames * info.channels);
+
+  if ((nargin == 2 && ! args(1).is_string ()) || nargin == 3)
+    {
+      RowVector range = args(1).row_vector_value ();
+
+      if (error_state)
+        return retval;
+
+      start = range(0);
+      end = range(1);
+    }
+
+  Matrix audio (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];
+    }
+
+  octave_value ret_audio;
+
+  if ((nargin == 2 && args(1).is_string ()) || nargin == 3)
+    {
+      std::string type;
+      if (nargin == 3)
+        type = args(2).string_value ();
+      else
+        type = args(1).string_value ();
+
+      if (error_state)
+        return retval;
+
+      if (type == "native")
+        {
+          if (info.format & SF_FORMAT_PCM_S8)
+            ret_audio = int8NDArray (audio * 127);
+          else if (info.format & SF_FORMAT_PCM_U8)
+            ret_audio = uint8NDArray (audio * 127 + 127);
+          else if (info.format & SF_FORMAT_PCM_16)
+            ret_audio = int16NDArray (audio * 32767);
+          else if (info.format & SF_FORMAT_PCM_24)
+            ret_audio = int32NDArray (audio * 8388608);
+          else if (info.format & SF_FORMAT_PCM_32)
+            ret_audio = int32NDArray (audio * 2147483648);
+          else
+            ret_audio = audio;
+        }
+      else
+        ret_audio = audio;
+    }
+  else
+    ret_audio = audio;
+
+  retval(1) = info.samplerate;
+  retval(0) = ret_audio;
+
+#else
+
+  error ("sndfile not found on your system and thus audioread is not functional");
+
+#endif
+
+  return retval;
+}
+
+#ifdef HAVE_SNDFILE
+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;
+}
+#endif
+
+DEFUN_DLD (audiowrite, args, ,
+  "-*- texinfo -*-\n\
+@deftypefn {Loadable Function} {} audiowrite (@var{filename}, @var{y}, @var{fs})\n\
+@deftypefnx {Loadable Function} {} audiowrite (@var{filename}, @var{y}, @var{fs}, @var{name}, @var{value}, @dots{})\n\
+\n\
+Write audio data from the matrix @var{y} to @var{filename} with the file\n\
+format determined by the file extension.\n\
+\n\
+Additional name and value argument pairs may be used to specify the\n\
+following options:\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 @sc{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")
+{
+  // FIXME: shouldn't we return something to indicate whether the file
+  // was written successfully?
+
+  octave_value retval;
+
+#ifdef HAVE_SNDFILE
+
+  int nargin = args.length ();
+
+  if (nargin < 3)
+    {
+      print_usage ();
+      return retval;
+    }
+
+  std::string filename = args(0).string_value ();
+
+  if (error_state)
+    return retval;
+
+  std::map<std::string, int> extension_to_format;
+  fill_extension_table (extension_to_format);
+
+  std::string extension = filename.substr (filename.find_last_of (".") + 1);
+  std::transform (extension.begin (), extension.end (), extension.begin (), ::tolower);
+
+  Matrix audio = args(1).matrix_value ();
+
+  if (error_state)
+    return retval;
+
+  SNDFILE *file;
+  SF_INFO info;
+
+  OCTAVE_LOCAL_BUFFER (float, data, audio.rows () * audio.cols ());
+
+  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 = "";
+  // Quality is currently unused?
+  //
+  // float quality = 0.75;
+  for (int i = 3; i < nargin; 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");
+              return retval;
+            }
+        }
+      else if (args(i).string_value () == "BitRate")
+        ;
+      // Quality is currently unused?
+      //
+      // 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");
+          return retval;
+        }
+    }
+
+  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);
+
+#else
+
+  error ("sndfile not found on your system and thus audiowrite is not functional");
+
+#endif
+
+  return retval;
+}
+
+DEFUN_DLD (audioinfo, args, ,
+  "-*- texinfo -*-\n\
+@deftypefn {Loadable Function} {@var{info} =} audioinfo (@var{filename})\n\
+Return information about an audio file specified by @var{filename}.\n\
+@end deftypefn")
+{
+  octave_value retval;
+
+  if (args.length () != 1)
+    {
+      print_usage ();
+      return retval;
+    }
+
+#ifdef HAVE_SNDFILE
+
+  SF_INFO info;
+  info.format = 0;
+  SNDFILE *file = sf_open (args(0).string_value ().c_str (), SFM_READ, &info);
+
+  octave_scalar_map result;
+
+  result.assign ("Filename", args(0).string_value ());
+  result.assign ("CompressionMethod", "");
+  result.assign ("NumChannels", info.channels);
+  result.assign ("SampleRate", info.samplerate);
+  result.assign ("TotalSamples", info.frames);
+
+  double dframes = info.frames;
+  double drate = info.samplerate;
+  result.assign ("Duration", dframes / drate);
+
+  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;
+
+  result.assign ("BitsPerSample", bits);
+  result.assign ("BitRate", -1);
+  result.assign ("Title", sf_get_string (file, SF_STR_TITLE));
+  result.assign ("Artist", sf_get_string (file, SF_STR_ARTIST));
+  result.assign ("Comment", sf_get_string (file, SF_STR_COMMENT));
+
+  retval = result;
+
+#else
+
+  error ("sndfile not found on your system and thus audioinfo is not functional");
+
+#endif
+
+  return retval;
+}
--- a/libinterp/dldfcn/module-files	Fri Jan 02 09:38:06 2015 -0800
+++ b/libinterp/dldfcn/module-files	Fri Jan 02 16:54:34 2015 -0500
@@ -21,3 +21,5 @@
 symbfact.cc|$(SPARSE_XCPPFLAGS)|$(SPARSE_XLDFLAGS)|$(SPARSE_XLIBS)
 symrcm.cc|$(SPARSE_XCPPFLAGS)|$(SPARSE_XLDFLAGS)|$(SPARSE_XLIBS)
 tsearch.cc
+audioread.cc|$(SNDFILE_CPPFLAGS)|$(SNDFILE_LDFLAGS)|$(SNDFILE_LIBS)
+audiodevinfo.cc|$(PORTAUDIO_CPPFLAGS)|$(PORTAUDIO_LDFLAGS)|$(PORTAUDIO_LIBS)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/scripts/audio/@audioplayer/__get_properties__.m	Fri Jan 02 16:54:34 2015 -0500
@@ -0,0 +1,62 @@
+## Copyright (C) 2013 Vytautas Jančauskas
+##
+## 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 {Function File} {@var{properties} =} __get_properties__ (@var{player})
+## Return a struct containing all named properties of the audioplayer
+## object @var{player}.
+## @end deftypefn
+
+function props = __get_properties__ (player)
+  if (__player_isplaying__ (struct (player).player))
+    running = "on";
+  else
+    running = "off";
+  endif
+
+  props = struct ("BitsPerSample",
+                  __player_get_nbits__ (struct (player).player),
+
+                  "CurrentSample",
+                  __player_get_sample_number__ (struct (player).player),
+
+                  "DeviceID",
+                  __player_get_id__ (struct (player).player),
+
+                  "NumberOfChannels",
+                  __player_get_channels__ (struct (player).player),
+
+                  "Running",
+                  running,
+
+                  "SampleRate",
+                  __player_get_fs__ (struct (player).player),
+
+                  "TotalSamples",
+                  __player_get_total_samples__ (struct (player).player),
+
+                  "Tag",
+                  __player_get_tag__ (struct (player).player),
+
+                  "Type",
+                  "audioplayer",
+
+                  "UserData",
+                  __player_get_userdata__ (struct (player).player));
+
+endfunction
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/scripts/audio/@audioplayer/audioplayer.m	Fri Jan 02 16:54:34 2015 -0500
@@ -0,0 +1,195 @@
+## Copyright (C) 2013 Vytautas Jančauskas
+##
+## 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 {Function File} {@var{player} =} audioplayer (@var{y}, @var{fs})
+## @deftypefnx {Function File} {@var{player} =} audioplayer (@var{y}, @var{fs}, @var{nbits})
+## @deftypefnx {Function File} {@var{player} =} audioplayer (@var{y}, @var{fs}, @var{nbits}, @var{id})
+## @deftypefnx {Function File} {@var{player} =} audioplayer (@var{function}, @dots{})
+## @deftypefnx {Function File} {@var{player} =} audioplayer (@var{recorder})
+## @deftypefnx {Function File} {@var{player} =} audioplayer (@var{recorder}, @var{id})
+## Create an audioplayer object that will play back data @var{y} at sample
+## rate @var{fs}.  The optional arguments @var{nbits}, and @var{id}
+## specify the bit depth and player device id, respectively.  Device IDs
+## may be found using the audiodevinfo function.
+## Given a function handle, use that function to process the audio.
+## Given an audioplayer object, use the data from the object to
+## initialize the player.
+## @end deftypefn
+##
+## The signal @var{y} can be a vector or a two dimensional array.
+##
+## The following example will create an audioplayer object that will play
+## back one second of white noise at 44100 sample rate using 8 bits per
+## sample.
+##
+## @example
+## @group
+## y = randn (2, 44100) - 0.5;
+## player = audioplayer (y, 44100, 8);
+## play (player);
+## @end group
+## @end example
+##
+## The following example will create and register a callback that generates
+## a sine wave on both channels.
+##
+## @example
+## @group
+## function [ sound, status ] = callback_sine (frames)
+##   global lphase = 0.0;
+##   global rphase = 0.0;
+##   incl = 440.0 / 44100.0;
+##   incr = 443.0 / 44100.0;
+##   nl = incl * frames;
+##   nr = incr * frames;
+##   left = sin (2.0 * pi * [lphase:incl:lphase+nl]);
+##   right = sin (2.0 * pi * [rphase:incr:rphase+nr]);
+##   sound = [left', right'];
+##   status = 0;
+##   lphase = lphase + nl;
+##   rphase = rphase + nr;
+## endfunction
+## player = audioplayer (@@callback_sine, 44100);
+## play (player);
+## # play for as long as you want
+## stop (player);
+## @end group
+## @end example
+
+function player = audioplayer (varargin)
+  if (nargin < 1 || nargin > 4)
+    print_usage ();
+  endif
+  if ((isa (varargin{1}, "function_handle")
+       || ischar (varargin{1})) && nargin < 2)
+    print_usage ();
+  endif
+  if isa (varargin{1}, "audiorecorder")
+    if nargin == 1
+      player = getplayer (varargin{1});
+    elseif nargin == 2
+      recorder = varargin{1};
+      data = getaudiodata (recorder);
+      player = audioplayer (data, get (recorder, "SampleRate"),
+                            get (recorder, "BitsPerSample"), varargin{2});
+    else
+      print_usage ();
+    endif
+  else
+    if (ischar (varargin{1}))
+      varargin{1} = str2func (varargin{1});
+    endif
+    player.player = __player_audioplayer__ (varargin{:});
+    player = class (player, "audioplayer");
+  endif
+endfunction
+
+%!test
+%! mono = randn (1, 44100) - 0.5;
+%! stereo = randn (2, 44100) - 0.5;
+%! fs = 44100;
+%! player1 = audioplayer (mono, fs);
+%! player2 = audioplayer (stereo, fs);
+%! assert (player1.NumberOfChannels, 1);
+%! assert (player2.NumberOfChannels, 2);
+%! assert (player1.SampleRate, 44100);
+%! assert (player2.SampleRate, 44100);
+%! assert (player1.TotalSamples, 44100);
+%! assert (player2.TotalSamples, 44100);
+%! playblocking (player1);
+%! playblocking (player2);
+
+%!test
+%! audio = randn (2, 88200) - 0.5;
+%! fs = 44100;
+%! player = audioplayer (audio, fs);
+%! assert (!isplaying (player));
+%! play (player);
+%! assert (isplaying (player));
+%! sleep (1);
+%! pause (player);
+%! assert (!isplaying (player));
+%! sleep (1);
+%! resume (player);
+%! assert (isplaying (player));
+%! sleep (1);
+
+%!test
+%! audio = randn (2, 88200) - 0.5;
+%! fs = 44100;
+%! player = audioplayer (audio, fs);
+%! assert (!isplaying (player));
+%! play (player);
+%! assert (isplaying (player));
+%! sleep (1);
+%! stop (player);
+%! sleep (1);
+%! assert (!isplaying (player));
+%! assert (player.CurrentSample, 0);
+
+%!test
+%! audio = randn (2, 44100) - 0.5;
+%! fs = 44100;
+%! player = audioplayer (audio, fs);
+%! set (player, {"SampleRate", "Tag", "UserData"}, {8000, "tag", [1, 2; 3, 4]});
+%! assert (player.SampleRate, 8000);
+%! assert (player.Tag, "tag");
+%! assert (player.UserData, [1, 2; 3, 4]);
+
+%!test
+%! audio = randn (2, 44100) - 0.5;
+%! fs = 44100;
+%! player = audioplayer (audio, fs);
+%! settable = set (player);
+%! settable.SampleRate = 8000;
+%! settable.Tag = "tag";
+%! settable.UserData = [1, 2; 3, 4];
+%! set (player, settable);
+%! assert (player.SampleRate, 8000);
+%! assert (player.Tag, "tag");
+%! assert (player.UserData, [1, 2; 3, 4]);
+
+%!test
+%! audio = randn (2, 44100) - 0.5;
+%! fs = 44100;
+%! player = audioplayer (audio, fs);
+%! player.SampleRate = 8000;
+%! player.Tag = "tag";
+%! player.UserData = [1, 2; 3, 4];
+%! properties = get (player, {"SampleRate", "Tag", "UserData"});
+%! assert (properties, {8000, "tag", [1, 2; 3, 4]});
+
+%!function [sound, status] = callback (samples)
+%!  sound = rand (samples, 2) - 0.5;
+%!  status = 0;
+%!endfunction
+
+%!test
+%! player = audioplayer (@callback, 44100);
+%! play (player);
+%! sleep (2);
+%! stop (player);
+%! assert (1);
+
+%!test
+%! player = audioplayer ("callback", 44100, 16);
+%! play (player);
+%! sleep (2);
+%! stop (player);
+%! assert (1);
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/scripts/audio/@audioplayer/display.m	Fri Jan 02 16:54:34 2015 -0500
@@ -0,0 +1,26 @@
+## Copyright (C) 2013 Vytautas Jančauskas
+##
+## 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 {Function File} {} display (@var{player})
+## Display the properties of the audioplayer object @var{player}.
+## @end deftypefn
+
+function display (player)
+  disp (__get_properties__ (player));
+endfunction
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/scripts/audio/@audioplayer/get.m	Fri Jan 02 16:54:34 2015 -0500
@@ -0,0 +1,49 @@
+## Copyright (C) 2013 Vytautas Jančauskas
+##
+## 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 {Function File} {@var{value} =} get (@var{player}, @var{name})
+## @deftypefnx {Function File} {@var{values} =} get (@var{player})
+## Return the @var{value} of the property identified by @var{name}.
+## If @var{name} is a cell array return the values of of the properties
+## identified by the elements of the cell array.  Given only the
+## player object, return a scalar structure with values of all
+## properties of @var{player}.  The field names of the structure
+## correspond to property names.
+## @end deftypefn
+
+function result = get (varargin)
+  player = varargin{1};
+  properties = __get_properties__ (player);
+  if (nargin == 1)
+    result = properties;
+  elseif (nargin == 2)
+    if (ischar (varargin{2}))
+      result = getfield (properties, varargin{2});
+    else
+      result = {};
+      index = 1;
+      for property = varargin{2}
+        result{index} = getfield (properties, char (property));
+        index = index + 1;
+      endfor
+    endif
+  else
+    error ("audioplayer: wrong number of arguments to the get method");
+  endif
+endfunction
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/scripts/audio/@audioplayer/isplaying.m	Fri Jan 02 16:54:34 2015 -0500
@@ -0,0 +1,27 @@
+## Copyright (C) 2013 Vytautas Jančauskas
+##
+## 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 {Function File} {} isplaying (@var{player})
+## Return 1 if the audioplayer object @var{player}is currently playing
+## back audio and 0 otherwise.
+## @end deftypefn
+
+function result = isplaying (player)
+  result = __player_isplaying__ (struct (player).player);
+endfunction
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/scripts/audio/@audioplayer/pause.m	Fri Jan 02 16:54:34 2015 -0500
@@ -0,0 +1,26 @@
+## Copyright (C) 2013 Vytautas Jančauskas
+##
+## 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 {Function File} {} pause (@var{player})
+## Pause the audioplayer @var{player}.
+## @end deftypefn
+
+function pause (player)
+  __player_pause__ (struct (player).player);
+endfunction
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/scripts/audio/@audioplayer/play.m	Fri Jan 02 16:54:34 2015 -0500
@@ -0,0 +1,35 @@
+## Copyright (C) 2013 Vytautas Jančauskas
+##
+## 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 {Function File} {} play (@var{player})
+## @deftypefnx {Function File} {} play  (@var{player}, @var{start})
+## @deftypefnx {Function File} {} play (@var{player}, @var{limits})
+## Play audio stored in the audioplayer object @var{player} without blocking.
+## Given optional argument start, begin playing at @var{start} seconds
+## in the recording.  Given a two-element vector @var{limits}, begin and
+## end playing at the number of seconds specified by the elements of the
+## vector.
+## @end deftypefn
+
+function play (varargin)
+  if (nargin < 1 || nargin > 2)
+    print_usage ();
+  endif
+  __player_play__ (struct (varargin{1}).player, varargin{2:end});
+endfunction
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/scripts/audio/@audioplayer/playblocking.m	Fri Jan 02 16:54:34 2015 -0500
@@ -0,0 +1,35 @@
+## Copyright (C) 2013 Vytautas Jančauskas
+##
+## 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 {Function File} {} playblocking (@var{player})
+## @deftypefnx {Function File} {} playblocking (@var{player}, @var{start})
+## @deftypefnx {Function File} {} playblocking (@var{player}, @var{limits})
+## Play audio stored in the audioplayer object @var{player} with blocking.
+## Given optional argument start, begin playing at @var{start} seconds
+## in the recording.  Given a two-element vector @var{limits}, begin and
+## end playing at the number of seconds specified by the elements of the
+## vector.
+## @end deftypefn
+
+function playblocking (varargin)
+  if (nargin < 1 || nargin > 2)
+    print_usage ();
+  endif
+  __player_playblocking__ (struct (varargin{1}).player, varargin{2:end});
+endfunction
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/scripts/audio/@audioplayer/resume.m	Fri Jan 02 16:54:34 2015 -0500
@@ -0,0 +1,26 @@
+## Copyright (C) 2013 Vytautas Jančauskas
+##
+## 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 {Function File} {} resume (@var{player})
+## Resume playback for the paused audioplayer object @var{player}.
+## @end deftypefn
+
+function resume (player)
+  __player_resume__ (struct (player).player);
+endfunction
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/scripts/audio/@audioplayer/set.m	Fri Jan 02 16:54:34 2015 -0500
@@ -0,0 +1,70 @@
+## Copyright (C) 2013 Vytautas Jančauskas
+##
+## 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 {Function File} {} set (@var{player}, @var{name}, @var{value})
+## @deftypefnx {Function File} {} set (@var{player}, @var{properties})
+## @deftypefnx {Function File} {@var{properties} =} set (@var{player})
+## Set the value of property specified by @var{name} to a given @var{value}.
+## If @var{name} and @var{value} are cell arrays, set each property to the
+## corresponding value.  Given a structure of @var{properties} with
+## fields corresponding to property names, set the value of those
+## properties to the field values.  Given only the audioplayer object,
+## return a structure of settable properties.
+## @end deftypefn
+
+function settable = set (varargin)
+  if (nargin < 1 || nargin > 3)
+    print_usage ();
+  endif
+  player = struct (varargin{1}).player;
+  if (nargin == 1)
+    settable.SampleRate = {};
+    settable.Tag = {};
+    settable.UserData = {};
+  elseif (nargin == 2)
+    for [value, property] = varargin{2}
+      setproperty (player, property, value);
+    endfor
+  elseif (nargin == 3)
+    if (iscell (varargin{2}))
+      index = 1;
+      for property = varargin{2}
+        setproperty (player, char (property), varargin{3}{index});
+        index = index + 1;
+      endfor
+    else
+      setproperty (player, varargin{2}, varargin{3});
+    endif
+  else
+    error ("audioplayer: wrong number of arguments to the set method");
+  endif
+endfunction
+
+function setproperty (player, property, value)
+  switch (property)
+    case "SampleRate"
+      __player_set_fs__ (player, value);
+    case "Tag"
+      __player_set_tag__ (player, value);
+    case "UserData"
+      __player_set_userdata__ (player, value);
+    otherwise
+      error ("audioplayer: no such property or the property specified is read-only");
+  endswitch
+endfunction
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/scripts/audio/@audioplayer/stop.m	Fri Jan 02 16:54:34 2015 -0500
@@ -0,0 +1,27 @@
+## Copyright (C) 2013 Vytautas Jančauskas
+##
+## 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 {Function File} {} stop (@var{player})
+## Stop the playback for the audioplayer @var{player} and reset the
+## relevant variables to their starting values.
+## @end deftypefn
+
+function stop (player)
+  __player_stop__ (struct (player).player);
+endfunction
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/scripts/audio/@audioplayer/subsasgn.m	Fri Jan 02 16:54:34 2015 -0500
@@ -0,0 +1,36 @@
+## Copyright (C) 2013 Vytautas Jančauskas
+##
+## 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 {Function File} {@var{value} =} subsasgn (@var{player}, @var{idx}, @var{rhs})
+## Perform subscripted assignment on the audio player object @var{player}.
+## Assign the value of @var{rhs} to the player property named by @var{idx}.
+## @end deftypefn
+
+function value = subsasgn (player, idx, rhs)
+  if (isempty (idx))
+    error ("audioplayer: missing index");
+  endif
+  if (strcmp (idx(1).type, "."))
+    field = idx.subs;
+    set (player, field, rhs);
+    value = player;
+  else
+    error ("audioplayer: invalid subscript type");
+  endif
+endfunction
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/scripts/audio/@audioplayer/subsref.m	Fri Jan 02 16:54:34 2015 -0500
@@ -0,0 +1,35 @@
+## Copyright (C) 2013 Vytautas Jančauskas
+##
+## 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 {Function File} {@var{value} =} subsref (@var{player}, @var{idx})
+## Perform subscripted selection on the audio player object @var{player}.
+## Return the player property value named by @var{idx}.
+## @end deftypefn
+
+function value = subsref (player, idx)
+  if (isempty (idx))
+    error ("audioplayer: missing index");
+  endif
+  if (strcmp (idx(1).type, "."))
+    field = idx.subs;
+    value = get (player, field);
+  else
+    error ("audioplayer: invalid subscript file")
+  endif
+endfunction
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/scripts/audio/@audiorecorder/__get_properties__.m	Fri Jan 02 16:54:34 2015 -0500
@@ -0,0 +1,62 @@
+## Copyright (C) 2013 Vytautas Jančauskas
+##
+## 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 {Function File} {@var{properties} =} __get_properties__ (@var{recorder})
+## Return a struct containing all named properties of the recorder object
+## @var{recorder}.
+## @end deftypefn
+
+function props = __get_properties__ (recorder)
+  if (__recorder_isrecording__ (struct (recorder).recorder))
+    running = "on";
+  else
+    running = "off";
+  endif
+
+  props = struct ("BitsPerSample",
+                  __recorder_get_nbits__ (struct (recorder).recorder),
+
+                  "CurrentSample",
+                  __recorder_get_sample_number__ (struct (recorder).recorder),
+
+                  "DeviceID",
+                  __recorder_get_id__ (struct (recorder).recorder),
+
+                  "NumberOfChannels",
+                  __recorder_get_channels__ (struct (recorder).recorder),
+
+                  "Running",
+                  running,
+
+                  "SampleRate",
+                  __recorder_get_fs__ (struct (recorder).recorder),
+
+                  "TotalSamples",
+                  __recorder_get_total_samples__ (struct (recorder).recorder),
+
+                  "Tag",
+                  __recorder_get_tag__ (struct (recorder).recorder),
+
+                  "Type",
+                  "audiorecorder",
+
+                  "UserData",
+                  __recorder_get_userdata__ (struct (recorder).recorder));
+
+endfunction
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/scripts/audio/@audiorecorder/audiorecorder.m	Fri Jan 02 16:54:34 2015 -0500
@@ -0,0 +1,129 @@
+## Copyright (C) 2013 Vytautas Jančauskas
+##
+## 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 {Function File} {@var{recorder} =} audiorecorder ()
+## @deftypefnx {Function File} {@var{recorder} =} audiorecorder (@var{fs}, @var{nbits}, @var{channels})
+## @deftypefnx {Function File} {@var{recorder} =} audiorecorder (@var{fs}, @var{nbits}, @var{channels}, @var{id})
+## @deftypefnx {Function File} {@var{recorder} =} audiorecorder (@var{function}, @dots{})
+## Create an audiorecorder object recording 8 bit mono audio at 8000 Hz
+## sample rate.  The optional arguments @var{fs}, @var{nbits},
+## @var{channels}, and @var{id} specify the sample rate, bit depth,
+## number of channels and recording device id, respectively.  Device IDs
+## may be found using the audiodevinfo function.
+## Given a function handle, use that function to process the audio.
+## @end deftypefn
+
+function recorder = audiorecorder (varargin)
+  if (nargin > 5)
+    print_usage ();
+  endif
+  if (nargin > 0 && ischar (varargin{1}))
+    varargin{1} = str2func (varargin{1});
+  endif
+  recorder.recorder = __recorder_audiorecorder__ (varargin{:});
+  recorder = class (recorder, "audiorecorder");
+endfunction
+
+%!test
+%! recorder = audiorecorder (44100, 16, 2);
+%! recordblocking (recorder, 1);
+%! data = getaudiodata (recorder, "int16");
+%! assert (strcmp (class (data), "int16"));
+%! data = getaudiodata (recorder, "int8");
+%! assert (strcmp (class (data), "int8"));
+%! data = getaudiodata (recorder, "uint8");
+%! assert (strcmp (class (data), "uint8"));
+%! assert (size (data)(1), recorder.TotalSamples);
+%! assert (size (data)(2), 2);
+%! assert (size (data)(1) != 0);
+
+%!test
+%! recorder = audiorecorder (44100, 16, 2);
+%! record (recorder, 1)
+%! sleep (2);
+%! record (recorder, 1);
+%! sleep (2);
+%! data = getaudiodata (recorder);
+%! assert (size (data)(1) < 44100 * 2);
+
+%!test
+%! recorder = audiorecorder (44100, 16, 2);
+%! record (recorder, 1);
+%! sleep (2);
+%! player1 = audioplayer (recorder);
+%! player2 = getplayer (recorder);
+%! play (player1);
+%! sleep (2);
+%! play (player2);
+%! sleep (2);
+%! assert (player1.TotalSamples, recorder.TotalSamples);
+%! assert (player2.TotalSamples, recorder.TotalSamples);
+
+%!test
+%! recorder = audiorecorder;
+%! set (recorder, {"SampleRate", "Tag", "UserData"}, {8000, "tag", [1, 2; 3, 4]});
+%! assert (recorder.SampleRate, 8000);
+%! assert (recorder.Tag, "tag");
+%! assert (recorder.UserData, [1, 2; 3, 4]);
+
+%!test
+%! recorder = audiorecorder;
+%! settable = set (recorder);
+%! settable.SampleRate = 8000;
+%! settable.Tag = "tag";
+%! settable.UserData = [1, 2; 3, 4];
+%! set (recorder, settable);
+%! assert (recorder.SampleRate, 8000);
+%! assert (recorder.Tag, "tag");
+%! assert (recorder.UserData, [1, 2; 3, 4]);
+
+%!test
+%! recorder = audiorecorder;
+%! recorder.SampleRate = 8000;
+%! recorder.Tag = "tag";
+%! recorder.UserData = [1, 2; 3, 4];
+%! properties = get (recorder, {"SampleRate", "Tag", "UserData"});
+%! assert (properties, {8000, "tag", [1, 2; 3, 4]});
+
+#%!function status = callback_record (sound)
+#%!  fid = fopen ("record.txt", "at");
+#%!  for index = 1:rows(sound)
+#%!    fprintf (fid, "%.4f, %.4f\n", sound(index, 1), sound(index, 2));
+#%!  endfor
+#%!  fclose (fid);
+#%!  status = 0;
+#%!endfunction
+
+#%!test
+#%! recorder = audiorecorder (@callback_record, 44100);
+#%! unlink ("record.txt")
+#%! record (recorder);
+#%! sleep (2);
+#%! stop (player);
+#%! s = stat ("record.txt");
+#%! assert (s.size > 0);
+
+#%!test
+#%! recorder = audiorecorder (@callback_record, 44100);
+#%! unlink ("record.txt")
+#%! record (recorder);
+#%! sleep (2);
+#%! stop (recorder);
+#%! s = stat ("record.txt");
+#%! assert (s.size > 0);
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/scripts/audio/@audiorecorder/display.m	Fri Jan 02 16:54:34 2015 -0500
@@ -0,0 +1,26 @@
+## Copyright (C) 2013 Vytautas Jančauskas
+##
+## 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 {Function File} {} display (@var{recorder})
+## Display the properties of the audiorecorder object @var{recorder}.
+## @end deftypefn
+
+function display (recorder)
+  disp (__get_properties__ (recorder));
+endfunction
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/scripts/audio/@audiorecorder/get.m	Fri Jan 02 16:54:34 2015 -0500
@@ -0,0 +1,49 @@
+## Copyright (C) 2013 Vytautas Jančauskas
+##
+## 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 {Function File} {@var{value} =} get (@var{recorder}, @var{name})
+## @deftypefnx {Function File} {@var{values} =} get (@var{recorder})
+## Return the @var{value} of the property identified by @var{name}.
+## If @var{name} is a cell array, return the values of the properties
+## corresponding to the elements of the cell array.  Given only the
+## recorder object, return a scalar structure with values of all
+## properties of @var{recorder}.  The field names of the structure
+## correspond to property names.
+## @end deftypefn
+
+function result = get (varargin)
+  recorder = varargin{1};
+  properties = __get_properties__ (recorder);
+  if (nargin == 1)
+    result = properties;
+  elseif (nargin == 2)
+    if (ischar (varargin{2}))
+      result = getfield (properties, varargin{2});
+    else
+      result = {};
+      index = 1;
+      for property = varargin{2}
+        result{index} = getfield (properties, char (property));
+        index = index + 1;
+      endfor
+    endif
+  else
+    error ("audiorecorder: wrong number of arguments to the get method");
+  endif
+endfunction
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/scripts/audio/@audiorecorder/getaudiodata.m	Fri Jan 02 16:54:34 2015 -0500
@@ -0,0 +1,53 @@
+## Copyright (C) 2013 Vytautas Jančauskas
+##
+## 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 {Function File} {@var{data} =} getaudiodata (@var{recorder})
+## @deftypefnx {Function File} {@var{data} =} getaudiodata (@var{recorder}, @var{datatype})
+## Return recorder audio data as a matrix with values between -1.0 and 1.0
+## and with as many columns as there are channels in the recorder.
+## Given the optional argument @var{datatype}, convert the recorded data
+## to the specified type, which may be one of @qcode{"double"},
+## @qcode{"single"}, @qcode{"int16"}, @qcode{"int8"} or @qcode{"uint8"}.
+## @end deftypefn
+
+function data = getaudiodata (varargin)
+  if (nargin < 1 || nargin > 2)
+    print_usage ();
+  endif
+  recorder = varargin{1};
+  if (nargin == 1)
+    data = __recorder_getaudiodata__ (struct (recorder).recorder);
+  else
+    data = __recorder_getaudiodata__ (struct (recorder).recorder);
+    type = varargin{2};
+    switch (type)
+      case "int16"
+        data = int16 (data * (2.0 ^ 15));
+      case "int8"
+        data = int8 (data * (2.0 ^ 7));
+      case "uint8"
+        data = uint8 ((data + 1.0) * 0.5 * (2.0 ^ 8 - 1));
+    endswitch
+  endif
+  if (get (recorder, "NumberOfChannels") == 2)
+    data = data';
+  else
+    data = data(1,:)';
+  endif
+endfunction
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/scripts/audio/@audiorecorder/getplayer.m	Fri Jan 02 16:54:34 2015 -0500
@@ -0,0 +1,33 @@
+## Copyright (C) 2013 Vytautas Jančauskas
+##
+## 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 {Function File} {@var{player} =} getplayer (@var{recorder})
+## Return an audioplayer object with data recorded by the audiorecorder
+## object @var{recorder}.
+## @end deftypefn
+
+function player = getplayer (varargin)
+  if (nargin < 1 || nargin > 2)
+    print_usage ();
+  endif
+  recorder = varargin{1};
+  data = getaudiodata (recorder);
+  player = audioplayer (data, get (recorder, "SampleRate"),
+                        get (recorder, "BitsPerSample"));
+endfunction
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/scripts/audio/@audiorecorder/isrecording.m	Fri Jan 02 16:54:34 2015 -0500
@@ -0,0 +1,27 @@
+## Copyright (C) 2013 Vytautas Jančauskas
+##
+## 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 {Function File} {} isrecording (@var{recorder})
+## Return 1 if the audiorecorder object @var{recorder} is currently
+## recording audio and 0 otherwise.
+## @end deftypefn
+
+function result = isrecording (recorder)
+  result = __recorder_isrecording__ (struct (recorder).recorder);
+endfunction
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/scripts/audio/@audiorecorder/pause.m	Fri Jan 02 16:54:34 2015 -0500
@@ -0,0 +1,26 @@
+## Copyright (C) 2013 Vytautas Jančauskas
+##
+## 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 {Function File} {} pause (@var{recorder})
+## Pause recording with audiorecorder object @var{recorder}.
+## @end deftypefn
+
+function pause (recorder)
+  __recorder_pause__ (struct (recorder).recorder);
+endfunction
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/scripts/audio/@audiorecorder/play.m	Fri Jan 02 16:54:34 2015 -0500
@@ -0,0 +1,43 @@
+## Copyright (C) 2013 Vytautas Jančauskas
+##
+## 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 {Function File} {@var{player} =} play (@var{recorder})
+## @deftypefnx {Function File} {@var{player} =} play (@var{recorder}, @var{start})
+## @deftypefnx {Function File} {@var{player} =} play (@var{recorder}, [@var{start}, @var{end}])
+## Play the audio recorded in @var{recorder} and return a corresponding
+## audioplayer object.  If the optional argument @var{start} is
+## provided, begin playing @var{start} seconds in to the recording.
+## If the optional argument @var{end} is provided, stop playing at
+## @var{end} seconds in the recording.
+## @end deftypefn
+
+function player = play (varargin)
+  if (nargin < 1 || nargin > 2)
+    print_usage ();
+  endif
+  recorder = varargin{1};
+  data = getaudiodata (recorder);
+  player = audioplayer (data, get (recorder, "SampleRate"),
+                        get (recorder, "BitsPerSample"));
+  if (nargin == 1)
+    play (player);
+  else
+    play (player, varargin{2});
+  endif
+endfunction
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/scripts/audio/@audiorecorder/record.m	Fri Jan 02 16:54:34 2015 -0500
@@ -0,0 +1,33 @@
+## Copyright (C) 2013 Vytautas Jančauskas
+##
+## 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 {Function File} {} record (@var{recorder})
+## @deftypefnx {Function File} {} record (@var{recorder}, @var{length})
+## Record audio without blocking using the audiorecorder object
+## @var{recorder} until stopped or paused by the @var{stop} or
+## @var{pause} method.  Given the optional argument @var{length}, record
+## for @var{length} seconds.
+## @end deftypefn
+
+function record (varargin)
+  if (nargin < 1 || nargin > 2)
+    print_usage ();
+  endif
+  __recorder_record__ (struct (varargin{1}).recorder, varargin{2:end});
+endfunction
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/scripts/audio/@audiorecorder/recordblocking.m	Fri Jan 02 16:54:34 2015 -0500
@@ -0,0 +1,30 @@
+## Copyright (C) 2013 Vytautas Jančauskas
+##
+## 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 {Function File} {} recordblocking (@var{recorder}, @var{length})
+## Record audio with blocking (synchronous I/O).  You must specify the
+## length of the recording in seconds.
+## @end deftypefn
+
+function recordblocking (varargin)
+  if (nargin != 2)
+    print_usage ();
+  endif
+  __recorder_recordblocking__ (struct (varargin{1}).recorder, varargin{2});
+endfunction
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/scripts/audio/@audiorecorder/resume.m	Fri Jan 02 16:54:34 2015 -0500
@@ -0,0 +1,26 @@
+## Copyright (C) 2013 Vytautas Jančauskas
+##
+## 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 {Function File} {} resume (@var{recorder})
+## Resume recording with the paused audiorecorder object @var{recorder}.
+## @end deftypefn
+
+function resume (recorder)
+  __recorder_resume__ (struct (recorder).recorder);
+endfunction
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/scripts/audio/@audiorecorder/set.m	Fri Jan 02 16:54:34 2015 -0500
@@ -0,0 +1,71 @@
+## Copyright (C) 2013 Vytautas Jančauskas
+##
+## 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 {Function File} {} set (@var{recorder}, @var{name}, @var{value})
+## @deftypefnx {Function File} {} set (@var{recorder}, @var{properties})
+## @deftypefnx {Function File} {@var{properties} =} set (@var{recorder})
+## Set the value of property specified by @var{name} to a given @var{value}.
+## If @var{name} and @var{value} are cell arrays of the same size,
+## set each property to a corresponding value.
+## Given a structure with fields corresponding to property names, set
+## the value of those properties to the corresponding field values.
+## Given a only the recorder object, return a structure of settable
+## properties.
+## @end deftypefn
+
+function settable = set (varargin)
+  if (nargin < 1 || nargin > 3)
+    print_usage ();
+  endif
+  recorder = struct (varargin{1}).recorder;
+  if (nargin == 1)
+    settable.SampleRate = {};
+    settable.Tag = {};
+    settable.UserData = {};
+  elseif (nargin == 2)
+    for [value, property] = varargin{2}
+      setproperty (recorder, property, value);
+    endfor
+  elseif (nargin == 3)
+    if (iscell (varargin{2}))
+      index = 1;
+      for property = varargin{2}
+        setproperty (recorder, char (property), varargin{3}{index});
+        index = index + 1;
+      endfor
+    else
+      setproperty (recorder, varargin{2}, varargin{3});
+    endif
+  else
+    error ("audiorecorder: wrong number of arguments to the set method");
+  endif
+endfunction
+
+function setproperty (recorder, property, value)
+  switch (property)
+    case "SampleRate"
+      __recorder_set_fs__ (recorder, value);
+    case "Tag"
+      __recorder_set_tag__ (recorder, value);
+    case "UserData"
+      __recorder_set_userdata__ (recorder, value);
+    otherwise
+      error ("audiorecorder: no such property or the property specified is read-only");
+  endswitch
+endfunction
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/scripts/audio/@audiorecorder/stop.m	Fri Jan 02 16:54:34 2015 -0500
@@ -0,0 +1,27 @@
+## Copyright (C) 2013 Vytautas Jančauskas
+##
+## 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 {Function File} {} stop (@var{recorder})
+## Stop the audiorecorder object @var{recorder} and clean up any audio
+## streams.
+## @end deftypefn
+
+function stop (recorder)
+  __recorder_stop__ (struct (recorder).recorder);
+endfunction
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/scripts/audio/@audiorecorder/subsasgn.m	Fri Jan 02 16:54:34 2015 -0500
@@ -0,0 +1,36 @@
+## Copyright (C) 2013 Vytautas Jančauskas
+##
+## 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 {Function File} {@var{value} =} subsasgn (@var{recorder}, @var{idx}, @var{rhs})
+## Perform subscripted assignment on the audio recorder object @var{recorder}.
+## Assign the value of @var{rhs} to the recorder property named by @var{idx}.
+## @end deftypefn
+
+function value = subsasgn (recorder, idx, rhs)
+  if (isempty (idx))
+    error ("audiorecorder: missing index");
+  endif
+  if (strcmp (idx(1).type, "."))
+    field = idx.subs;
+    set (recorder, field, rhs);
+    value = recorder;
+  else
+    error ("audiorecorder: invalid subscript type");
+  endif
+endfunction
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/scripts/audio/@audiorecorder/subsref.m	Fri Jan 02 16:54:34 2015 -0500
@@ -0,0 +1,35 @@
+## Copyright (C) 2013 Vytautas Jančauskas
+##
+## 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 {Function File} {@var{value} =} subsref (@var{recorder}, @var{idx})
+## Perform subscripted selection on the audio recorder object @var{recorder}.
+## Return the recorder property value named by @var{idx}.
+## @end deftypefn
+
+function value = subsref (recorder, idx)
+  if (isempty (idx))
+    error ("audiorecorder: missing index");
+  endif
+  if (strcmp (idx(1).type, "."))
+    field = idx.subs;
+    value = get (recorder, field);
+  else
+    error ("audiorecorder: invalid subscript file")
+  endif
+endfunction
--- a/scripts/audio/module.mk	Fri Jan 02 09:38:06 2015 -0800
+++ b/scripts/audio/module.mk	Fri Jan 02 16:54:34 2015 -0500
@@ -9,7 +9,36 @@
   audio/saveaudio.m \
   audio/setaudio.m \
   audio/wavread.m \
-  audio/wavwrite.m
+  audio/wavwrite.m \
+  audio/@audioplayer/__get_properties__.m \
+  audio/@audioplayer/audioplayer.m \
+  audio/@audioplayer/display.m \
+  audio/@audioplayer/get.m \
+  audio/@audioplayer/isplaying.m \
+  audio/@audioplayer/pause.m  \
+  audio/@audioplayer/play.m \
+  audio/@audioplayer/playblocking.m \
+  audio/@audioplayer/resume.m \
+  audio/@audioplayer/set.m \
+  audio/@audioplayer/stop.m \
+  audio/@audioplayer/subsasgn.m \
+  audio/@audioplayer/subsref.m \
+  audio/@audiorecorder/__get_properties__.m \
+  audio/@audiorecorder/audiorecorder.m \
+  audio/@audiorecorder/display.m \
+  audio/@audiorecorder/get.m \
+  audio/@audiorecorder/getaudiodata.m \
+  audio/@audiorecorder/getplayer.m \
+  audio/@audiorecorder/isrecording.m \
+  audio/@audiorecorder/pause.m \
+  audio/@audiorecorder/play.m \
+  audio/@audiorecorder/record.m \
+  audio/@audiorecorder/recordblocking.m \
+  audio/@audiorecorder/resume.m \
+  audio/@audiorecorder/set.m \
+  audio/@audiorecorder/stop.m \
+  audio/@audiorecorder/subsasgn.m \
+  audio/@audiorecorder/subsref.m
 
 FCN_FILES += $(audio_FCN_FILES)
 
--- a/scripts/help/__unimplemented__.m	Fri Jan 02 09:38:06 2015 -0800
+++ b/scripts/help/__unimplemented__.m	Fri Jan 02 16:54:34 2015 -0500
@@ -542,12 +542,6 @@
   "alphamap",
   "annotation",
   "array2table",
-  "audiodevinfo",
-  "audioinfo",
-  "audioplayer",
-  "audioread",
-  "audiorecorder",
-  "audiowrite",
   "bar3",
   "bar3h",
   "bench",