Use a range of values for ASIO buffer sizes if provided by driver
authorTim Mayberry <mojofunk@gmail.com>
Mon, 2 Nov 2015 05:22:43 +0000 (15:22 +1000)
committerTim Mayberry <mojofunk@gmail.com>
Thu, 19 Nov 2015 00:23:26 +0000 (10:23 +1000)
This has been tested on four devices:

- A RME HDSP Multiface
- A Yamaha AG06
- A Focusrite 2i2
- A built-in soundcard running ASIO4ALL

The HDSP and the AG06 only return one buffer size when queried so the preferred
size is used as before.

The Focusrite returns a min corresponding to the position of the slider in the
control dialog and the max is 1024. The granularity is 1 so this means that the
number of values needs to be reduced for the current UI design with a combo
box so the granularity is increased until there are around 8-9 buffer sizes to
choose from evenly spaced between min and max(but we could easily change this
if the UI changes etc).

The ASIO4ALL driver returns a min of 64 and a max of 2048 and a granularity of
8. So where the minimum buffer size and granularity is a power of 2 use only
buffer sizes that are power of 2.

If the driver returns different values for min and max it is not currently
possible to indicate which is the driver preferred value. A checkbox or other
UI element could be added to the AudioSetup dialog to only use the preferred
value but that is more work and perhaps not necessary.

libs/backends/portaudio/portaudio_io.cc
libs/backends/portaudio/portaudio_io.h

index f3276cc7eaa4e37d27b4ed5457bd5ce59382f2c4..22314f84cd4ef92fc213c1ce9ddcde59e22ac894 100644 (file)
@@ -204,8 +204,17 @@ PortAudioIO::get_asio_buffer_properties (int device_id,
        return true;
 }
 
+static
 bool
-PortAudioIO::get_asio_buffer_sizes (int device_id, std::vector<uint32_t>& buffer_sizes)
+is_power_of_two (uint32_t v)
+{
+       return ((v != 0) && !(v & (v - 1)));
+}
+
+bool
+PortAudioIO::get_asio_buffer_sizes(int device_id,
+                                   std::vector<uint32_t>& buffer_sizes,
+                                   bool preferred_only)
 {
        long min_size_frames = 0;
        long max_size_frames = 0;
@@ -231,26 +240,51 @@ PortAudioIO::get_asio_buffer_sizes (int device_id, std::vector<uint32_t>& buffer
                                     preferred_size_frames,
                                     granularity));
 
-#ifdef USE_ASIO_MIN_MAX_BUFFER_SIZES
-       if (min_size_frames >= max_size_frames) {
-               buffer_sizes.push_back (preferred_size_frames);
+       bool driver_returns_one_size = (min_size_frames == max_size_frames) &&
+                                      (min_size_frames == preferred_size_frames);
+
+       if (preferred_only || driver_returns_one_size) {
+               buffer_sizes.push_back(preferred_size_frames);
                return true;
        }
 
        long buffer_size = min_size_frames;
-       while (buffer_size <= max_size_frames) {
-               buffer_sizes.push_back (buffer_size);
 
-               if (granularity <= 0) {
-                       // buffer sizes are power of 2
-                       buffer_size = buffer_size * 2;
-               } else {
+       // If min size and granularity are power of two then just use values that
+       // are power of 2 even if the granularity allows for more values
+       bool use_power_of_two =
+           is_power_of_two(min_size_frames) && is_power_of_two(granularity);
+
+       if (granularity <= 0 || use_power_of_two) {
+               // driver uses buffer sizes that are power of 2
+               while (buffer_size <= max_size_frames) {
+                       buffer_sizes.push_back(buffer_size);
+                       buffer_size *= 2;
+               }
+       } else {
+               if (min_size_frames == max_size_frames) {
+                       // The devices I have tested either return the same values for
+                       // min/max/preferred and changing buffer size is intended to only be
+                       // done via the control dialog or they return a range where min != max
+                       // but I guess min == max could happen if a driver only supports a single
+                       // buffer size
+                       buffer_sizes.push_back(min_size_frames);
+                       return true;
+               }
+
+               // If min_size_frames is not power of 2 use at most 8 of the possible
+               // buffer sizes spread evenly between min and max
+               long max_values = 8;
+               while (((max_size_frames - min_size_frames) / granularity) > max_values) {
+                       granularity *= 2;
+               }
+
+               while (buffer_size < max_size_frames) {
+                       buffer_sizes.push_back(buffer_size);
                        buffer_size += granularity;
                }
+               buffer_sizes.push_back(max_size_frames);
        }
-#else
-       buffer_sizes.push_back (preferred_size_frames);
-#endif
        return true;
 }
 #endif
@@ -272,7 +306,7 @@ PortAudioIO::available_buffer_sizes(int device_id, std::vector<uint32_t>& buffer
 {
 #ifdef WITH_ASIO
        if (get_current_host_api_type() == paASIO) {
-               if (get_asio_buffer_sizes (device_id, buffer_sizes)) {
+               if (get_asio_buffer_sizes (device_id, buffer_sizes, false)) {
                        return 0;
                }
        }
index e08490d0b574bc3ce7a34394128b1b4d204e8b82..c67fdc1b19c5efbfa653c88b46d51f9076a80a26 100644 (file)
@@ -63,7 +63,9 @@ public:
                                         long& preferred_size_frames,
                                         long& granularity);
 
-       bool get_asio_buffer_sizes (int device_id, std::vector<uint32_t>& buffer_size);
+       bool get_asio_buffer_sizes(int device_id,
+                                  std::vector<uint32_t>& buffer_size,
+                                  bool preferred_only);
 #endif
 
        std::string control_app_name (int device_id) const;