# HG changeset patch
# User Lars Kindermann <lars.kindermann@reglos.de>
# Date 1574596499 -3600
#      Sun Nov 24 12:54:59 2019 +0100
# Node ID 57e20a5fdfb5cf5ab28f4e9b75d74bcfd6a43c4a
# Parent  d39c56824d0187744eeeb8478ad77bb0b393a308
audiodevinfo.cc: workaround for a bug in portaudio 8-bit
bug #44305: audiorecorder (8000, 8, 1) returns corrupted audio stream
When 8-bit format is requested, portaudio is called with 16 bit instead
and the result is rescaled to 8 bit resolution

diff -r d39c56824d01 -r 57e20a5fdfb5 libinterp/dldfcn/audiodevinfo.cc
--- a/libinterp/dldfcn/audiodevinfo.cc	Sat Nov 23 10:12:49 2019 -0800
+++ 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,26 @@
           sound(i,1) = sample_r;
         }
     }
-  else if (recorder->get_nbits () == 16)
+  // This is a workaround for a bug in portaudio affecting 8-Bit recording
+  // bug #44305: audiorecorder (8000, 8, 1) returns corrupted audio stream
+  // FIXME: simply 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  int integer_scale_factor = 256;
+
+      const int16_t *input16 = static_cast<const int16_t *> (input);
+
+      for (unsigned long i = 0; i < frames; i++)
+        {
+          float sample_l = input16[i*channels] / integer_scale_factor / scale_factor;
+          float sample_r = input16[i*channels + (channels - 1)] / integer_scale_factor / 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 +1360,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 +1406,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 +1424,25 @@
           recorder->append (sample_l, sample_r);
         }
     }
-  else if (recorder->get_nbits () == 16)
+  // This is a workaround for a bug in portaudio affecting 8-Bit recording
+  // bug #44305: audiorecorder (8000, 8, 1) returns corrupted audio stream
+  // FIXME: simply 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 int integer_scale_factor = 256;
+
+      const int16_t *input16 = static_cast<const int16_t *> (input);
+
+      for (unsigned long i = 0; i < frames; i++)
+        {
+          float sample_l = input16[i*channels] / integer_scale_factor / scale_factor;
+          float sample_r = input16[i*channels + (channels - 1)] / integer_scale_factor / 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 +1456,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 +1548,12 @@
   input_parameters.channelCount = get_channels ();
   input_parameters.sampleFormat = bits_to_format (get_nbits ());
 
+  // This is a workaround for a bug in portaudio affecting 8-Bit recording
+  // bug #44305: audiorecorder (8000, 8, 1) returns corrupted audio stream
+  // FIXME: simply 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 +1590,12 @@
   return nbits;
 }
 
+PaSampleFormat
+audiorecorder::get_sampleFormat (void)
+{
+  return input_parameters.sampleFormat;
+}
+
 void
 audiorecorder::set_id (int id_arg)
 {
