Move the pcm period settings into properties

QEMU has somewhat long period (in pcm_open), so
hardcoding this value into the shared audio HAL
will not be optimal (this increases latency and
could even be too big for the audio card kernel
buffer by default).

Bug: 238957114
Test: boot, check if sound works
Signed-off-by: Roman Kiryanov <rkir@google.com>
Change-Id: I9a849595f3052c6e69e505ef6c574bafd8fe09fa
diff --git a/audio/device_port_sink.cpp b/audio/device_port_sink.cpp
index a42f692..668f2f0 100644
--- a/audio/device_port_sink.cpp
+++ b/audio/device_port_sink.cpp
@@ -46,13 +46,6 @@
 constexpr int kMaxJitterUs = 3000;  // Enforced by CTS, should be <= 6ms
 
 struct TinyalsaSink : public DevicePortSink {
-    // Mostly magic numbers.
-    // In pcm, the hardware works with `period_size` granularity.
-    // The `period_count` is the number of `period_size` units in the pcm
-    // buffer.
-    static constexpr size_t kPcmPeriodCount = 8;
-    static constexpr size_t kPcmPeriodSizeMultiplier = 2;
-
     TinyalsaSink(unsigned pcmCard, unsigned pcmDevice,
                  const AudioConfig &cfg,
                  uint64_t &frames)
@@ -67,8 +60,7 @@
             , mPcm(talsa::pcmOpen(pcmCard, pcmDevice,
                                   util::countChannels(cfg.base.channelMask),
                                   cfg.base.sampleRateHz,
-                                  kPcmPeriodCount,
-                                  kPcmPeriodSizeMultiplier * cfg.frameCount / kPcmPeriodCount,
+                                  cfg.frameCount,
                                   true /* isOut */)) {
         if (mPcm) {
             LOG_ALWAYS_FATAL_IF(!talsa::pcmPrepare(mPcm.get()));
@@ -85,8 +77,10 @@
 
     static int getLatencyMs(const AudioConfig &cfg) {
         constexpr size_t inMs = 1000;
-        const size_t numerator = kPcmPeriodSizeMultiplier * cfg.frameCount;
-        const size_t denominator = kPcmPeriodCount * cfg.base.sampleRateHz / inMs;
+        const talsa::PcmPeriodSettings periodSettings =
+            talsa::pcmGetPcmPeriodSettings();
+        const size_t numerator = periodSettings.periodSizeMultiplier * cfg.frameCount;
+        const size_t denominator = periodSettings.periodCount * cfg.base.sampleRateHz / inMs;
 
         // integer division with rounding
         return (numerator + (denominator >> 1)) / denominator;
diff --git a/audio/device_port_source.cpp b/audio/device_port_source.cpp
index c3d9b84..e4c4528 100644
--- a/audio/device_port_source.cpp
+++ b/audio/device_port_source.cpp
@@ -50,13 +50,6 @@
 constexpr int kMaxJitterUs = 3000;  // Enforced by CTS, should be <= 6ms
 
 struct TinyalsaSource : public DevicePortSource {
-    // Mostly magic numbers.
-    // In pcm, the hardware works with `period_size` granularity.
-    // The `period_count` is the number of `period_size` units in the pcm
-    // buffer.
-    static constexpr size_t kPcmPeriodCount = 8;
-    static constexpr size_t kPcmPeriodSizeMultiplier = 2;
-
     TinyalsaSource(unsigned pcmCard, unsigned pcmDevice,
                    const AudioConfig &cfg, uint64_t &frames)
             : mStartNs(systemTime(SYSTEM_TIME_MONOTONIC))
@@ -69,8 +62,7 @@
             , mPcm(talsa::pcmOpen(pcmCard, pcmDevice,
                                   util::countChannels(cfg.base.channelMask),
                                   cfg.base.sampleRateHz,
-                                  kPcmPeriodCount,
-                                  kPcmPeriodSizeMultiplier * cfg.frameCount / kPcmPeriodCount,
+                                  cfg.frameCount,
                                   false /* isOut */)) {
         if (mPcm) {
             LOG_ALWAYS_FATAL_IF(!talsa::pcmPrepare(mPcm.get()));
diff --git a/audio/talsa.cpp b/audio/talsa.cpp
index a0fc73c..e538c4e 100644
--- a/audio/talsa.cpp
+++ b/audio/talsa.cpp
@@ -15,6 +15,7 @@
  */
 
 #include <mutex>
+#include <cutils/properties.h>
 #include <log/log.h>
 #include "talsa.h"
 #include "debug.h"
@@ -31,6 +32,9 @@
 struct mixer *gMixer0 = nullptr;
 int gMixerRefcounter0 = 0;
 std::mutex gMixerMutex;
+const PcmPeriodSettings kDefaultPcmPeriodSettings = { 4, 1 };
+PcmPeriodSettings gPcmPeriodSettings;
+std::once_flag gPcmPeriodSettingsFlag;
 
 void mixerSetValueAll(struct mixer_ctl *ctl, int value) {
     const unsigned int n = mixer_ctl_get_num_values(ctl);
@@ -95,8 +99,38 @@
     return mixerUnrefImpl(mixer, gMixer0, gMixerRefcounter0);
 }
 
+bool initPcmPeriodSettings(PcmPeriodSettings *dst) {
+    char prop_value[PROPERTY_VALUE_MAX];
+
+    if (property_get("ro.hardware.audio.tinyalsa.period_count", prop_value, nullptr) < 0) {
+        return false;
+    }
+    if (sscanf(prop_value, "%u", &dst->periodCount) != 1) {
+        return false;
+    }
+
+    if (property_get("ro.hardware.audio.tinyalsa.period_size_multiplier", prop_value, nullptr) < 0) {
+        return false;
+    }
+    if (sscanf(prop_value, "%u", &dst->periodSizeMultiplier) != 1) {
+        return false;
+    }
+
+    return true;
+}
+
 }  // namespace
 
+PcmPeriodSettings pcmGetPcmPeriodSettings() {
+    std::call_once(gPcmPeriodSettingsFlag, [](){
+        if (!initPcmPeriodSettings(&gPcmPeriodSettings)) {
+            gPcmPeriodSettings = kDefaultPcmPeriodSettings;
+        }
+    });
+
+    return gPcmPeriodSettings;
+}
+
 void PcmDeleter::operator()(pcm_t *x) const {
     LOG_ALWAYS_FATAL_IF(::pcm_close(x) != 0);
 };
@@ -105,16 +139,20 @@
                                            const unsigned int card,
                                            const unsigned int nChannels,
                                            const size_t sampleRateHz,
-                                           const size_t periodCount,
-                                           const size_t periodSize,
+                                           const size_t frameCount,
                                            const bool isOut) {
+    const PcmPeriodSettings periodSettings = pcmGetPcmPeriodSettings();
+
     struct pcm_config pcm_config;
     memset(&pcm_config, 0, sizeof(pcm_config));
 
     pcm_config.channels = nChannels;
     pcm_config.rate = sampleRateHz;
-    pcm_config.period_count = periodCount; // Approx interrupts per buffer
-    pcm_config.period_size = periodSize; // Approx frames between interrupts
+    // Approx interrupts per buffer
+    pcm_config.period_count = periodSettings.periodCount;
+    // Approx frames between interrupts
+    pcm_config.period_size =
+        periodSettings.periodSizeMultiplier * frameCount / periodSettings.periodCount;
     pcm_config.format = PCM_FORMAT_S16_LE;
 
     PcmPtr pcm =
@@ -125,8 +163,8 @@
         return pcm;
     } else {
         ALOGE("%s:%d pcm_open failed for nChannels=%u sampleRateHz=%zu "
-              "period_count=%zu period_size=%zu isOut=%d with %s", __func__, __LINE__,
-              nChannels, sampleRateHz, periodCount, periodSize, isOut,
+              "period_count=%d period_size=%d isOut=%d with %s", __func__, __LINE__,
+              nChannels, sampleRateHz, pcm_config.period_count, pcm_config.period_size, isOut,
               pcm_get_error(pcm.get()));
         return FAILURE(nullptr);
     }
diff --git a/audio/talsa.h b/audio/talsa.h
index 6024158..1854f1e 100644
--- a/audio/talsa.h
+++ b/audio/talsa.h
@@ -28,12 +28,18 @@
 constexpr unsigned int kPcmDevice = 0;
 constexpr unsigned int kPcmCard = 0;
 
+struct PcmPeriodSettings {
+    unsigned periodCount;
+    unsigned periodSizeMultiplier;
+};
+
+PcmPeriodSettings pcmGetPcmPeriodSettings();
+
 typedef struct pcm pcm_t;
 struct PcmDeleter { void operator()(pcm_t *x) const; };
 typedef std::unique_ptr<pcm_t, PcmDeleter> PcmPtr;
 PcmPtr pcmOpen(unsigned int dev, unsigned int card, unsigned int nChannels,
-               size_t sampleRateHz, size_t periodCount, size_t periodSize,
-               bool isOut);
+               size_t sampleRateHz, size_t frameCount, bool isOut);
 bool pcmPrepare(pcm_t *pcm);
 bool pcmStart(pcm_t *pcm);
 bool pcmStop(pcm_t *pcm);
diff --git a/init.ranchu.rc b/init.ranchu.rc
index c4313c1..5c6bc9f 100644
--- a/init.ranchu.rc
+++ b/init.ranchu.rc
@@ -94,6 +94,8 @@
     chown root system /sys/power/wake_lock
     chown root system /sys/power/wake_unlock
     setprop ro.hardware.audio.primary goldfish
+    setprop ro.hardware.audio.tinyalsa.period_count 4
+    setprop ro.hardware.audio.tinyalsa.period_size_multiplier 4
 
     setprop wifi.interface wlan0
     setprop wifi.direct.interface p2p-dev-wlan0