changeset 19585:e4bce89532bc

improve error checking and efficiency of octave audio callback function * audiodevinfo.cc (octave_play_callback): Check for non-null player object. Move computations of constant values outside of loop. Invert nesting of switch statements and loops. Use const pointer to data instead of RowVector::elem method. (portaudio_play_callback): Check for non-null player object.
author John W. Eaton <jwe@octave.org>
date Thu, 08 Jan 2015 13:38:14 -0500
parents 0083cc91bfaa
children 8dce81f7448d
files libinterp/dldfcn/audiodevinfo.cc
diffstat 1 files changed, 116 insertions(+), 52 deletions(-) [+]
line wrap: on
line diff
--- a/libinterp/dldfcn/audiodevinfo.cc	Thu Jan 08 13:20:47 2015 -0500
+++ b/libinterp/dldfcn/audiodevinfo.cc	Thu Jan 08 13:38:14 2015 -0500
@@ -532,66 +532,124 @@
                       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;
+
+  if (! player)
+    {
+      error ("audio player callback function called without player");
+      return paAbort;
+    }
+
+  octave_value_list retval = feval (player->octave_callback_function,
+                                    ovl (static_cast<double> (frames)), 1);
+
+  if (error_state || retval.length () < 2)
+    {
+      error ("audio player callback function failed");
+      return paAbort;
+    }
+
   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)
+
+  if (error_state || frames != sound.rows ()
+      || sound.columns () < 1 || sound.columns () > 2)
     {
-      for (unsigned long i = 0; i < frames; i++)
-        {
-          sound_l(i) = sound(i, 0);
-          sound_r(i) = sound(i, 0);
-        }
+      error ("audio player callback function failed");
+      return paAbort;
     }
-  else if (sound.cols () == 2)
+
+  double scale_factor = 1.0;
+
+  switch (player->get_nbits ())
     {
-      for (unsigned long i = 0; i < frames; i++)
-        {
-          sound_l(i) = sound(i, 0);
-          sound_r(i) = sound(i, 1);
-        }
+    case 8:
+      scale_factor = pow (2.0, 7) - 1.0;
+      break;
+
+    case 16:
+      scale_factor = pow (2.0, 15) - 1.0;
+      break;
+
+    case 24:
+      scale_factor = pow (2.0, 23) - 1.0;
+      break;
+
+    default:
+      error ("invalid player bit depth in callback function");
+      break;
     }
-  else
-    return paAbort;
-
-  for (unsigned long i = 0; i < frames; i++)
+
+  sound = sound * scale_factor;
+
+  const RowVector sound_l = (sound.column (0)).transpose ();
+  const RowVector sound_r = (sound.columns () == 1)
+    ? sound_l : (sound.column (1)).transpose ();
+
+  const double *p_l = sound_l.data ();
+  const double *p_r = sound_r.data ();
+
+  switch (player->get_nbits ())
     {
-      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];
-        }
+    case 8:
+      {
+        int8_t *buffer = static_cast<int8_t *> (output);
+
+        for (unsigned long i = 0; i < frames; i++)
+          {
+            buffer[2*i] = p_l[i];
+            buffer[2*i+1] = p_r[i];
+          }
+      }
+      break;
+
+    case 16:
+      {
+        int16_t *buffer = static_cast<int16_t *> (output);
+
+        for (unsigned long i = 0; i < frames; i++)
+          {
+            buffer[2*i] = p_l[i];
+            buffer[2*i+1] = p_r[i];
+          }
+      }
+      break;
+
+    case 24:
+      {
+        int big_endian = is_big_endian ();
+
+        uint8_t *buffer = static_cast<uint8_t *> (output);
+
+        for (unsigned long i = 0; i < frames; i++)
+          {
+            int32_t sample_l = p_l[i];
+            int32_t sample_r = p_r[i];
+
+            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);
+
+            unsigned long offset = i * 6;
+
+            buffer[offset+0] = _sample_l[0+big_endian];
+            buffer[offset+1] = _sample_l[1+big_endian];
+            buffer[offset+2] = _sample_l[2+big_endian];
+
+            buffer[offset+3] = _sample_r[0+big_endian];
+            buffer[offset+4] = _sample_r[1+big_endian];
+            buffer[offset+5] = _sample_r[2+big_endian];
+          }
+      }
+      break;
+
+    default:
+      error ("invalid player bit depth in callback function");
+      break;
     }
+
   return return_status;
 }
 
@@ -602,6 +660,12 @@
 {
   audioplayer *player = static_cast<audioplayer *> (data);
 
+  if (! player)
+    {
+      error ("audio player callback function called without player");
+      return paAbort;
+    }
+
   double scale_factor = 1.0;
 
   switch (player->get_nbits ())