view libinterp/dldfcn/audiodevinfo.cc @ 19548:ef6875adb053

* audiodevinfo.cc: Avoid warnings about comparision operations.
author John W. Eaton <jwe@octave.org>
date Fri, 02 Jan 2015 02:16:18 -0500
parents 4cb4210bd392
children 0f7788e2d677
line wrap: on
line source

/*

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\
Returns a structure with two fields called \"input\" and \"output\".\n\
Each structure contains an array of structures with three fields called\n\
\"Name\", \"DriverVersion\" and \"ID\". Each structure contains information\n\
about a PortAudio device.\n\
\n\
@end deftypefn\n\
\n\
@deftypefn {Loadable Function} {@var{devs} =} audiodevinfo (@var{io})\n\
\n\
Returns the number of input or output devices available. Set @var{io} to 1\n\
for input devices and to 0 for output devices.\n\
@end deftypefn\n\
\n\
@deftypefn {Loadable Function} {@var{name} =} audiodevinfo (@var{io}, @var{id})\n\
\n\
Returns the name of a device specified by numerical @var{id}. Set @var{io}\n\
to 1 for input devices and to 0 for output devices.\n\
@end deftypefn\n\
\n\
@deftypefn {Loadable Function} {@var{id} =} audiodevinfo (@var{io}, @var{name})\n\
\n\
Returns the id of a device specified by name. Set @var{io}\n\
to 1 for input devices and to 0 for output devices.\n\
@end deftypefn\n\
\n\
@deftypefn {Loadable Function} {@var{id} =} audiodevinfo (@var{io}, @var{rate}, @var{bits}, @var{chans})\n\
\n\
Returns the id of the first device that supports playback or recording\n\
using the specified sampling rate (@var{rate}), bits per sample (@var{bits})\n\
and number of channels (@var{chans}). Set @var{io} to 1 for input devices\n\
and to 0 for output devices.\n\
@end deftypefn\n\
\n\
@deftypefn {Loadable Function} {@var{supports} =} audiodevinfo (@var{io}, @var{id}, @var{rate}, @var{bits}, @var{chans})\n\
\n\
Returns 1 if the device bearing @var{id} supports specified sampling rate\n\
(@var{rate}), bits per sample (@var{bits}) and number of channels (@var{chans}).\n\
Returns 0 otherwise. Set @var{io} to 1 for input devices and to 0 for output\n\
devices.\n\
@end deftypefn")
{
  octave_value retval;
#ifdef HAVE_PORTAUDIO
  int nargin = args.length ();
  PaError err;
  octave_scalar_map devinfo;
  octave_value_list input;
  octave_value_list output;

  err = Pa_Initialize ();
  if (err != paNoError)
    {
      error ("audiodevinfo: cannot initialize PortAudio");
      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 = octave_value (numoutput);
      else if (args(0).int_value () == 1)
        retval = octave_value (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)
    {
      //
    }
  // 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 = NULL;
          if (io == 0)
            {
              if (Pa_GetDeviceInfo (i)->maxOutputChannels < chans)
                continue;

              err = Pa_IsFormatSupported (NULL, &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, NULL, 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 = NULL;
      if (io == 0)
        {
          if (Pa_GetDeviceInfo (id)->maxOutputChannels < chans)
            {
              retval = 0;
              return retval;
            }
          err = Pa_IsFormatSupported (NULL, &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, NULL, 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 (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 (charMatrix tag);
  charMatrix get_tag (void);
  void set_userdata (octave_value userdata);
  octave_value get_userdata (void);
  PaStream *get_stream (void);
  octave_function *octave_callback_function;

  void playblocking (void);
  void play (void);
  void pause (void);
  void resume (void);
  void stop (void);
  bool isplaying (void);

private:
  Matrix y;
  RowVector left;
  RowVector right;
  charMatrix tag;
  octave_value userdata;
  int channels;
  int fs;
  int nbits;
  int id;
  unsigned int sample_number;
  unsigned int end_sample;
  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)
{
  this->nbits = 16;
  this->id = -1;
  this->sample_number = 0;
  this->tag = charMatrix ("");
  Matrix userdata;
  this->userdata = octave_value (userdata);
  this->stream = 0;
  this->octave_callback_function = 0;
}

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)
{
  PaError err;
  int device;

  err = Pa_Initialize ();
  if (err != paNoError)
    {
      error ("audioplayer: Initialization error!");
      return;
    }

  int numDevices = Pa_GetDeviceCount ();
  if (numDevices < 0)
    {
      error ("audioplayer: No audio devices found!");
      return;
    }

  if (this->get_id () == -1)
    device = Pa_GetDefaultOutputDevice ();
  else
    device = this->get_id ();

  output_parameters.device = device;
  output_parameters.channelCount = 2;
  output_parameters.sampleFormat = bits_to_format (this->get_nbits ());
  output_parameters.suggestedLatency = Pa_GetDeviceInfo (device)->defaultHighOutputLatency;
  output_parameters.hostApiSpecificStreamInfo = NULL;
}

void
audioplayer::init (void)
{
  PaError err;

  // Both of these variables are unused.  Should they be
  // eliminated or is something not yet implemented?
  //
  // int channels = this->y.rows ();
  // RowVector *sound_l = this->get_left ();

  int device;

  err = Pa_Initialize ();
  if (err != paNoError)
    {
      error ("audioplayer: Initialization error!");
      return;
    }

  int numDevices = Pa_GetDeviceCount ();
  if (numDevices < 0)
    {
      error ("audioplayer: No audio devices found!");
      return;
    }

  if (this->get_id () == -1)
    device = Pa_GetDefaultOutputDevice ();
  else
    device = this->get_id ();

  output_parameters.device = device;
  output_parameters.channelCount = 2;

  if (this->type == DOUBLE)
    output_parameters.sampleFormat = bits_to_format (this->get_nbits ());
  else if (this->type == INT8)
    output_parameters.sampleFormat = paInt8;
  else if (this->type == UINT8)
    output_parameters.sampleFormat = paUInt8;
  else if (this->type == INT16)
    output_parameters.sampleFormat = paInt16;

  output_parameters.suggestedLatency = Pa_GetDeviceInfo (device)->defaultHighOutputLatency;
  output_parameters.hostApiSpecificStreamInfo = NULL;
}

void
audioplayer::set_y (octave_value y)
{
  if (y.is_int8_type ())
    this->type = INT8;
  else if (y.is_uint8_type ())
    this->type = UINT8;
  else if (y.is_int16_type ())
    this->type = INT16;
  else
    this->type = DOUBLE;

  this->y = y.matrix_value ();
  if (this->y.rows () > 2)
    this->y = this->y.transpose ();

  this->channels = this->y.rows ();
  this->left = this->y.row (0);
  if (this->channels == 2)
    this->right = this->y.row (1);

  this->reset_end_sample ();
}

void
audioplayer::set_y (octave_function *fn)
{
  this->octave_callback_function = fn;
  this->channels = 2;
  this->reset_end_sample ();
}

Matrix&
audioplayer::get_y (void)
{
  return this->y;
}

RowVector *
audioplayer::get_left (void)
{
  return &(this->left);
}

RowVector *
audioplayer::get_right (void)
{
  return &(this->right);
}

void
audioplayer::set_fs (int fs)
{
  this->fs = fs;
}

int
audioplayer::get_fs (void)
{
  return this->fs;
}

void
audioplayer::set_nbits (int nbits)
{
  this->nbits = nbits;
}

int
audioplayer::get_nbits (void)
{
  return this->nbits;
}

void
audioplayer::set_id (int id)
{
  this->id = id;
}

int
audioplayer::get_id (void)
{
  return this->id;
}

int
audioplayer::get_channels (void)
{
  return this->channels;
}

audio_type
audioplayer::get_type (void)
{
  return this->type;
}

void
audioplayer::set_sample_number (unsigned int sample_number)
{
  this->sample_number = sample_number;
}

unsigned int
audioplayer::get_sample_number (void)
{
  return this->sample_number;
}

unsigned int
audioplayer::get_total_samples (void)
{
  return this->left.length ();
}

void
audioplayer::set_end_sample (unsigned int end_sample)
{
  this->end_sample = end_sample;
}

unsigned int
audioplayer::get_end_sample (void)
{
  return this->end_sample;
}

void
audioplayer::reset_end_sample (void)
{
  this->set_end_sample (this->left.length ());
}

void
audioplayer::set_tag (charMatrix tag)
{
  this->tag = tag;
}

charMatrix
audioplayer::get_tag (void)
{
  return this->tag;
}

void
audioplayer::set_userdata (octave_value userdata)
{
  this->userdata = userdata;
}

octave_value
audioplayer::get_userdata (void)
{
  return this->userdata;
}

void
audioplayer::playblocking (void)
{
  if (this->get_stream ())
    this->stop ();

  PaError err;
  uint32_t buffer[BUFFER_SIZE * 2];
  err = Pa_OpenStream (&stream, NULL, &(this->output_parameters), this->get_fs (), BUFFER_SIZE, paClipOff, NULL, NULL);
  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;
    }

  unsigned int start, end;
  start = this->get_sample_number ();
  end = this->get_end_sample ();
  for (unsigned int i = start; i < end; i += BUFFER_SIZE)
    {
      if (this->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;
  this->set_sample_number (0);
  this->reset_end_sample ();
}

void
audioplayer::play (void)
{
  if (this->get_stream ())
    this->stop ();

  PaError err;
  if (this->octave_callback_function != 0)
    err = Pa_OpenStream (&stream, NULL, &(this->output_parameters),
                         this->get_fs (), BUFFER_SIZE, paClipOff,
                         octave_play_callback, this);
  else
    err = Pa_OpenStream (&stream, NULL, &(this->output_parameters),
                         this->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 (this->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 (this->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 this->stream;
}

void
audioplayer::stop (void)
{
  if (this->get_stream () == 0)
    return;

  PaError err;
  this->set_sample_number (0);
  this->reset_end_sample ();
  if (not Pa_IsStreamStopped (this->get_stream ()))
    {
      err = Pa_AbortStream (this->get_stream ());
      if (err != paNoError)
        {
          error ("audioplayer: Error stopping audio playback stream");
          return;
        }
    }

  err = Pa_CloseStream (this->get_stream ());
  if (err != paNoError)
    {
      error ("audioplayer: Error closing audio playback stream");
      return;
    }

  stream = 0;
}

bool
audioplayer::isplaying (void)
{
  if (this->get_stream () == 0)
    return false;

  PaError err;
  err = Pa_IsStreamActive (stream);
  if (err != 0 and 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 (charMatrix tag);
  charMatrix get_tag (void);
  void set_userdata (octave_value userdata);
  octave_value get_userdata (void);
  PaStream *get_stream (void);
  octave_function *octave_callback_function;

  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);

private:
  Matrix y;
  std::vector<float> left;
  std::vector<float> right;
  charMatrix tag;
  octave_value userdata;
  int channels;
  int fs;
  int nbits;
  int id;
  unsigned int sample_number;
  unsigned int end_sample;
  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, sample_r32;
      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, sample_r32;
      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)
{
  this->id = -1;
  this->sample_number = 0;
  this->channels = 1;
  this->tag = charMatrix ("");
  Matrix userdata;
  this->userdata = octave_value (userdata);
  this->stream = 0;
  this->end_sample = -1;
  this->set_fs (44100);
  this->set_nbits (16);
  this->set_channels (2);
  this->octave_callback_function = 0;
}

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)
{
  PaError err;
  int device;
  err = Pa_Initialize ();
  if (err != paNoError)
    {
      error ("audiorecorder: Initialization error!");
      return;
    }

  int numDevices = Pa_GetDeviceCount ();
  if (numDevices < 0)
    {
      error ("audiorecorder: No audio devices found!");
      return;
    }

  if (this->get_id () == -1)
    device = Pa_GetDefaultInputDevice ();
  else
    device = this->get_id ();

  this->input_parameters.device = device;
  this->input_parameters.channelCount = this->get_channels ();
  this->input_parameters.sampleFormat = bits_to_format (this->get_nbits ());
  this->input_parameters.suggestedLatency = Pa_GetDeviceInfo (device)->defaultHighInputLatency;
  this->input_parameters.hostApiSpecificStreamInfo = NULL;
}

void
audiorecorder::set_fs (int fs)
{
  this->fs = fs;
}

int
audiorecorder::get_fs (void)
{
  return this->fs;
}

void
audiorecorder::set_nbits (int nbits)
{
  this->nbits = nbits;
}

int
audiorecorder::get_nbits (void)
{
  return this->nbits;
}

void
audiorecorder::set_id (int id)
{
  this->id = id;
}

int
audiorecorder::get_id (void)
{
  return this->id;
}

void
audiorecorder::set_channels (int channels)
{
  assert (channels == 1 or channels == 2);
  this->channels = channels;
}

int
audiorecorder::get_channels (void)
{
  return this->channels;
}

audio_type
audiorecorder::get_type (void)
{
  return this->type;
}

void
audiorecorder::set_sample_number (unsigned int sample_number)
{
  this->sample_number = sample_number;
}

unsigned int
audiorecorder::get_sample_number (void)
{
  return this->sample_number;
}

unsigned int
audiorecorder::get_total_samples (void)
{
  return this->left.size ();
}

void
audiorecorder::set_end_sample (unsigned int end_sample)
{
  this->end_sample = end_sample;
}

unsigned int
audiorecorder::get_end_sample (void)
{
  return this->end_sample;
}

void
audiorecorder::reset_end_sample (void)
{
  this->set_end_sample (this->left.size ());
}

void
audiorecorder::set_tag (charMatrix tag)
{
  this->tag = tag;
}

charMatrix
audiorecorder::get_tag (void)
{
  return this->tag;
}

void
audiorecorder::set_userdata (octave_value userdata)
{
  this->userdata = userdata;
}

octave_value
audiorecorder::get_userdata (void)
{
  return this->userdata;
}

octave_value
audiorecorder::getaudiodata (void)
{
  Matrix audio (2, this->left.size ());
  for (unsigned int i = 0; i < this->left.size (); i++)
    {
      audio(0, i) = this->left[i];
      audio(1, i) = this->right[i];
    }
  return octave_value (audio);
}

audioplayer *
audiorecorder::getplayer (void)
{
  audioplayer *player = new audioplayer ();
  player->set_y (this->getaudiodata ());
  player->set_fs (this->get_fs ());
  player->set_nbits (this->get_nbits ());
  player->init ();
  return player;
}

bool
audiorecorder::isrecording (void)
{
  if (this->get_stream () == 0)
    return false;

  PaError err;
  err = Pa_IsStreamActive (stream);
  if (err != 0 and err != 1)
    {
      error ("audiorecorder: Error checking stream activity status");
      return false;
    }

  return (err == 1);
}

void
audiorecorder::record (void)
{
  if (this->get_stream ())
    this->stop ();

  this->left.clear ();
  this->right.clear ();
  PaError err;
  if (this->octave_callback_function != 0)
    {
      err = Pa_OpenStream (&stream, &(this->input_parameters), NULL,
                           this->get_fs (), BUFFER_SIZE, paClipOff,
                           octave_record_callback, this);
    }
  else
    {
      err = Pa_OpenStream (&stream, &(this->input_parameters), NULL,
                           this->get_fs (), BUFFER_SIZE, paClipOff,
                           portaudio_record_callback, this);
    }
  if (err != paNoError)
    {
      error ("audiorecorder: Error opening audio recording stream");
      return;
    }
  err = Pa_StartStream (stream);
  if (err != paNoError)
    {
      error ("audiorecorder: Error starting audio recording stream");
      return;
    }
}

void
audiorecorder::recordblocking (float seconds)
{
  if (this->get_stream ())
    this->stop ();

  this->left.clear ();
  this->right.clear ();

  PaError err;
  err = Pa_OpenStream (&stream, &(this->input_parameters), NULL,
                       this->get_fs (), BUFFER_SIZE, paClipOff, NULL, this);
  if (err != paNoError)
    {
      error ("audiorecorder: Error opening audio recording stream");
      return;
    }

  err = Pa_StartStream (stream);
  if (err != paNoError)
    {
      error ("audiorecorder: Error starting audio recording stream");
      return;
    }

  unsigned int frames = seconds * this->get_fs ();
  uint8_t buffer[BUFFER_SIZE * 2 * 3];
  for (unsigned long i = 0; i < frames / BUFFER_SIZE; i++)
    {
      Pa_ReadStream (this->get_stream (), buffer, BUFFER_SIZE);
      if (this->octave_callback_function != 0)
        octave_record_callback (buffer, NULL, BUFFER_SIZE, 0, 0, this);
      else
        portaudio_record_callback (buffer, NULL, BUFFER_SIZE, 0, 0, this);
    }
}

void
audiorecorder::pause (void)
{
  if (this->get_stream () == 0)
    return;

  PaError err;
  err = Pa_StopStream (stream);
  if (err != paNoError)
    {
      error ("audiorecorder: Error stoping audio recording stream");
      return;
    }
}

void
audiorecorder::resume (void)
{
  if (this->get_stream () == 0)
    return;

  PaError err;
  err = Pa_StartStream (stream);
  if (err != paNoError)
    {
      error ("audiorecorder: Error starting audio recording stream");
      return;
    }
}

void
audiorecorder::stop (void)
{
  if (this->get_stream () == 0)
    return;

  PaError err;
  if (not Pa_IsStreamStopped (this->get_stream ()))
    {
      err = Pa_AbortStream (this->get_stream ());
      if (err != paNoError)
        {
          error ("audioplayer: Error stopping audio playback stream");
          return;
        }
    }

  err = Pa_CloseStream (stream);
  if (err != paNoError)
    {
      error ("audiorecorder: Error closing audio recording stream");
      return;
    }

  this->set_sample_number (0);
  this->reset_end_sample ();
  stream = 0;
}

void
audiorecorder::append (float sample_l, float sample_r)
{
  this->left.push_back (sample_l);
  this->right.push_back (sample_r);
  this->set_sample_number (this->get_sample_number () + 1);
}

PaStream *
audiorecorder::get_stream (void)
{
  return this->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")
{
#ifdef HAVE_PORTAUDIO
  int nargin = args.length ();
  audiorecorder* retval = 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)
        {
          retval->octave_callback_function = args(0).function_value ();
          offset = 1;
        }
    }
  switch (nargin - offset)
     {
      case 3:
        retval->set_fs (args(0 + offset).int_value ());
        retval->set_nbits (args(1 + offset).int_value ());
        retval->set_channels (args(2 + offset).int_value ());
        break;
      case 4:
        retval->set_fs (args(0 + offset).int_value ());
        retval->set_nbits (args(1 + offset).int_value ());
        retval->set_channels (args(2 + offset).int_value ());
        retval->set_id (args(3 + offset).int_value ());
        break;
    }
  retval->init ();
  return octave_value (retval);
#else
  octave_value retval;
  error ("portaudio not found on your system and thus audio functionality is not present");
  return retval;
#endif
}

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 = octave_value (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
  int nargin = args.length ();
  if (nargin == 1)
    {
      audiorecorder *recorder = get_recorder (args(0));
      retval = octave_value (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
  int nargin = args.length ();
  if (nargin == 1)
    {
      audiorecorder *recorder = get_recorder (args(0));
      retval = octave_value (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
  int nargin = args.length ();
  if (nargin == 1)
    {
      audiorecorder *recorder = get_recorder (args(0));
      retval = octave_value (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
  int nargin = args.length ();
  if (nargin == 1)
    {
      audiorecorder *recorder = get_recorder (args(0));
      retval = octave_value (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
  int nargin = args.length ();
  if (nargin == 1)
    {
      audiorecorder *recorder = get_recorder (args(0));
      retval = octave_value (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
  int nargin = args.length ();
  if (nargin == 1)
    {
      audiorecorder *recorder = get_recorder (args(0));
      retval = octave_value (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
  int nargin = args.length ();
  if (nargin == 1)
    {
      audiorecorder *recorder = get_recorder (args(0));
      retval = octave_value (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
  int nargin = args.length ();
  if (nargin == 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
  int nargin = args.length ();
  if (nargin == 1)
    {
      audiorecorder *recorder = get_recorder (args(0));
      if (recorder->isrecording ())
        return octave_value (1);
      else
        return octave_value (0);
    }
#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
  int nargin = args.length ();
  if (nargin == 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
  int nargin = args.length ();
  if (nargin == 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
  int nargin = args.length ();
  if (nargin == 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
  int nargin = args.length ();
  if (nargin == 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
  int nargin = args.length ();
  if (nargin == 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")
{
#ifdef HAVE_PORTAUDIO
  int nargin = args.length ();
  audioplayer* retval = new audioplayer ();
  bool is_function = args(0).is_string () || args(0).is_function_handle () || args(0).is_inline_function ();
  if (is_function)
    retval->set_y (args(0).function_value ());
  else
    retval->set_y (args(0));
  retval->set_fs (args(1).int_value ());
  switch (nargin)
    {
      case 3:
        retval->set_nbits (args(2).int_value ());
        break;
      case 4:
        retval->set_nbits (args(2).int_value ());
        retval->set_id (args(3).int_value ());
        break;
    }
  if (is_function)
    retval->init_fn ();
  else
    retval->init ();
  return octave_value (retval);
#else
  octave_value retval;
  error ("portaudio not found on your system and thus audio functionality is not present");
  return retval;
#endif
}

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
  int nargin = args.length ();
  if (nargin == 1)
    {
      audioplayer *player = get_player (args(0));
      retval = octave_value (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
  int nargin = args.length ();
  if (nargin == 1)
    {
      audioplayer *player = get_player (args(0));
      retval = octave_value (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
  int nargin = args.length ();
  if (nargin == 1)
    {
      audioplayer *player = get_player (args(0));
      retval = octave_value (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
  int nargin = args.length ();
  if (nargin == 1)
    {
      audioplayer *player = get_player (args(0));
      retval = octave_value (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
  int nargin = args.length ();
  if (nargin == 1)
    {
      audioplayer *player = get_player (args(0));
      retval = octave_value (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
  int nargin = args.length ();
  if (nargin == 1)
    {
      audioplayer *player = get_player (args(0));
      retval = octave_value (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
  int nargin = args.length ();
  if (nargin == 1)
    {
      audioplayer *player = get_player (args(0));
      retval = octave_value (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
  int nargin = args.length ();
  if (nargin == 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
  int nargin = args.length ();
  if (nargin == 1)
    {
      audioplayer *player = get_player (args(0));
      if (player->isplaying ())
        return octave_value (1);
      else
        return octave_value (0);
    }
#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
  int nargin = args.length ();
  if (nargin == 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
  int nargin = args.length ();
  if (nargin == 1)
    {
      audioplayer *player = get_player (args(0));
      player->playblocking ();
    }
  else
    {
      audioplayer *player = get_player (args(0));
      if (args(1).is_matrix_type ())
        {
          unsigned int start, end;
          RowVector range = args(1).row_vector_value ();
          start = range.elem (0) - 1;
          end = range.elem (1) - 1;
          if (start > player->get_total_samples () or
              start > end or end > player->get_total_samples ())
            error ("audioplayer: invalid range specified for playback");
          player->set_sample_number (start);
          player->set_end_sample (end);
        }
      else
        {
          unsigned int start;
          start = args(1).int_value () - 1;
          if (start > player->get_total_samples ())
            error ("audioplayer: invalid range specified for playback");
          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
  int nargin = args.length ();
  if (nargin == 1)
    {
      audioplayer *player = get_player (args(0));
      player->play ();
    }
  else
    {
      audioplayer *player = get_player (args(0));
      if (args(1).is_matrix_type ())
        {
          unsigned int start, end;
          RowVector range = args(1).row_vector_value ();
          start = range.elem (0) - 1;
          end = range.elem (1) - 1;
          if (start > player->get_total_samples () or
              start > end or end > player->get_total_samples ())
            error ("audioplayer: invalid range specified for playback");
          player->set_sample_number (start);
          player->set_end_sample (end);
        }
      else
        {
          unsigned int start;
          start = args(1).int_value () - 1;
          if (start > player->get_total_samples ())
            error ("audioplayer: invalid range specified for playback");
          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
  int nargin = args.length ();
  if (nargin == 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
  int nargin = args.length ();
  if (nargin == 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
  int nargin = args.length ();
  if (nargin == 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
  int nargin = args.length ();
  if (nargin == 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
  int nargin = args.length ();
  if (nargin == 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;
}