changeset 27882:65a54ab6ceaa

Workaround for a bug in PortAudio 8-bit recording (bug #44305). Detect 8-bit format and call PortAudio in 16-bit format instead, and then scale to 8-bit. * audiodevinfo.cc (audiorecorder): Add new member function get_sampleFormat. * audiodevinfo.cc (audiorecorder::get_sampleFormat): New member function. * audiodevinfo.cc (octave_record_callback, portaudio_record_callback): Correct text in error() meessage. Use new get_sampleFormat to find number of bits in requested. Detect 16-bit format where number of bits is 8 and use special case code to discard lower 8 bits of recorded data and then scale to 8-bits.
author Lars Kindermann <lars.kindermann@reglos.de>
date Sun, 24 Nov 2019 12:54:59 +0100
parents f5e1072f1635
children 8675d88a0e26
files libinterp/dldfcn/audiodevinfo.cc
diffstat 1 files changed, 60 insertions(+), 8 deletions(-) [+]
line wrap: on
line diff
--- a/libinterp/dldfcn/audiodevinfo.cc	Sun Dec 29 12:40:07 2019 +0100
+++ b/libinterp/dldfcn/audiodevinfo.cc	Sun Nov 24 12:54:59 2019 +0100
@@ -1243,6 +1243,7 @@
   int get_fs (void);
   void set_nbits (int nbits);
   int get_nbits (void);
+  PaSampleFormat get_sampleFormat (void);
   void set_id (int id);
   int get_id (void);
   void set_channels (int channels);
@@ -1303,14 +1304,14 @@
   audiorecorder *recorder = static_cast<audiorecorder *> (data);
 
   if (! recorder)
-    error ("audio recorder callback function called without player");
+    error ("audio recorder callback function called without recorder");
 
   int channels = recorder->get_channels ();
 
   Matrix sound (frames, 2);
   sound.resize (frames, 2);
 
-  if (recorder->get_nbits () == 8)
+  if (recorder->get_sampleFormat () == bits_to_format (8))
     {
       static double scale_factor = std::pow (2.0, 7) - 1.0;
 
@@ -1325,7 +1326,27 @@
           sound(i,1) = sample_r;
         }
     }
-  else if (recorder->get_nbits () == 16)
+  // FIXME: This is a workaround for a bug in PortAudio affecting 8-Bit
+  //        recording (see Octave bug #44305).
+  //        Remove this clause once the bug in PortAudio has been fixed.
+  else if (recorder->get_sampleFormat () == bits_to_format (16)
+           && recorder->get_nbits () == 8)
+    {
+      static double scale_factor = std::pow (2.0, 7) - 1.0;
+
+      const int16_t *input16 = static_cast<const int16_t *> (input);
+
+      for (unsigned long i = 0; i < frames; i++)
+        {
+          float sample_l = (input16[i*channels] >> 8) / scale_factor;
+          float sample_r = (input16[i*channels + (channels - 1)] >> 8)
+                           / scale_factor;
+
+          sound(i,0) = sample_l;
+          sound(i,1) = sample_r;
+        }
+    }
+  else if (recorder->get_sampleFormat () == bits_to_format (16))
     {
       static double scale_factor = std::pow (2.0, 15) - 1.0;
 
@@ -1340,7 +1361,7 @@
           sound(i,1) = sample_r;
         }
     }
-  else if (recorder->get_nbits () == 24)
+  else if (recorder->get_sampleFormat () == bits_to_format (24))
     {
       static double scale_factor = std::pow (2.0, 23);
 
@@ -1386,11 +1407,11 @@
   audiorecorder *recorder = static_cast<audiorecorder *> (data);
 
   if (! recorder)
-    error ("audio recorder callback function called without player");
+    error ("audio recorder callback function called without recorder");
 
   int channels = recorder->get_channels ();
 
-  if (recorder->get_nbits () == 8)
+  if (recorder->get_sampleFormat () == bits_to_format (8))
     {
       static float scale_factor = std::pow (2.0f, 7) - 1.0f;
 
@@ -1404,7 +1425,26 @@
           recorder->append (sample_l, sample_r);
         }
     }
-  else if (recorder->get_nbits () == 16)
+  // FIXME: This is a workaround for a bug in PortAudio affecting 8-Bit
+  //        recording (see Octave bug #44305).
+  //        Remove this clause once the bug in PortAudio has been fixed.
+  else if (recorder->get_sampleFormat () == bits_to_format (16)
+           && recorder->get_nbits () == 8)
+    {
+      static double scale_factor = std::pow (2.0, 7) - 1.0;
+
+      const int16_t *input16 = static_cast<const int16_t *> (input);
+
+      for (unsigned long i = 0; i < frames; i++)
+        {
+          float sample_l = (input16[i*channels] >> 8) / scale_factor;
+          float sample_r = (input16[i*channels + (channels - 1)] >> 8) 
+                           / scale_factor;
+
+          recorder->append (sample_l, sample_r);
+        }
+    }
+  else if (recorder->get_sampleFormat () == bits_to_format (16))
     {
       static float scale_factor = std::pow (2.0f, 15) - 1.0f;
 
@@ -1418,7 +1458,7 @@
           recorder->append (sample_l, sample_r);
         }
     }
-  else if (recorder->get_nbits () == 24)
+  else if (recorder->get_sampleFormat () == bits_to_format (24))
     {
       static float scale_factor = std::pow (2.0f, 23);
 
@@ -1510,6 +1550,12 @@
   input_parameters.channelCount = get_channels ();
   input_parameters.sampleFormat = bits_to_format (get_nbits ());
 
+  // FIXME: This is a workaround for a bug in PortAudio affecting 8-Bit
+  //        recording (see Octave bug #44305).
+  //        Remove this clause once the bug in PortAudio has been fixed.
+  if (get_nbits () == 8)
+    input_parameters.sampleFormat = bits_to_format (16);
+
   const PaDeviceInfo *device_info = Pa_GetDeviceInfo (device);
 
   if (! device_info)
@@ -1546,6 +1592,12 @@
   return nbits;
 }
 
+PaSampleFormat
+audiorecorder::get_sampleFormat (void)
+{
+  return input_parameters.sampleFormat;
+}
+
 void
 audiorecorder::set_id (int id_arg)
 {