AAudio: Add sample rate conversion to MMAP

This CL adds sample rate conversion to the MMAP path in AAudio.
AAudio no longer needs to use AudioFlinger's sample rate converter for
MMAP streams. This effectively reduces the latency from 100ms to 30ms
on speaker, 3.5mm, and USB if MMAP is enabled and sample rate conversion
is needed.

A sample rate converter is added to the AAudio flowgraph. Since input
and output sample rates are different for the flowgraph, a variable
number of frames must be used. 8 frames are passed through the flowgraph
at any given point of times and the output of the flowgraph pulls as
much data as there exists in the flowgraph.

Burst sizes, capacity, latency, frames read, and frames written are all
changed reflect the sample rate of the app.

Flowgraph was also added to input MMAP streams.

Bug: 219533889
Test: OboeTester Test Input/Output with a variety of sample rates and formats
Test: atest AAudioTests
Change-Id: I9b6a2694921ea558fb91a231f956f638d3342837
diff --git a/media/libaaudio/src/client/AudioStreamInternal.cpp b/media/libaaudio/src/client/AudioStreamInternal.cpp
index 84c715f..ba4c63f 100644
--- a/media/libaaudio/src/client/AudioStreamInternal.cpp
+++ b/media/libaaudio/src/client/AudioStreamInternal.cpp
@@ -63,6 +63,8 @@
 
 #define LOG_TIMESTAMPS            0
 
+#define ENABLE_SAMPLE_RATE_CONVERTER 1
+
 AudioStreamInternal::AudioStreamInternal(AAudioServiceInterface  &serviceInterface, bool inService)
         : AudioStream()
         , mClockModel()
@@ -183,7 +185,6 @@
 
     mDeviceChannelCount = configurationOutput.getSamplesPerFrame();
 
-    setSampleRate(configurationOutput.getSampleRate());
     setDeviceId(configurationOutput.getDeviceId());
     setSessionId(configurationOutput.getSessionId());
     setSharingMode(configurationOutput.getSharingMode());
@@ -194,6 +195,18 @@
     setIsContentSpatialized(configurationOutput.isContentSpatialized());
     setInputPreset(configurationOutput.getInputPreset());
 
+    setDeviceSampleRate(configurationOutput.getSampleRate());
+
+    if (getSampleRate() == AAUDIO_UNSPECIFIED) {
+        setSampleRate(configurationOutput.getSampleRate());
+    }
+
+#if !ENABLE_SAMPLE_RATE_CONVERTER
+    if (getSampleRate() != getDeviceSampleRate()) {
+        goto error;
+    }
+#endif
+
     // Save device format so we can do format conversion and volume scaling together.
     setDeviceFormat(configurationOutput.getFormat());
 
@@ -233,39 +246,46 @@
 }
 
 aaudio_result_t AudioStreamInternal::configureDataInformation(int32_t callbackFrames) {
-    int32_t framesPerHardwareBurst = mEndpointDescriptor.dataQueueDescriptor.framesPerBurst;
+    int32_t deviceFramesPerBurst = mEndpointDescriptor.dataQueueDescriptor.framesPerBurst;
 
     // Scale up the burst size to meet the minimum equivalent in microseconds.
     // This is to avoid waking the CPU too often when the HW burst is very small
-    // or at high sample rates.
-    int32_t framesPerBurst = framesPerHardwareBurst;
+    // or at high sample rates. The actual number of frames that we call back to
+    // the app with will be 0 < N <= framesPerBurst so round up the division.
+    int32_t framesPerBurst = (static_cast<int64_t>(deviceFramesPerBurst) * getSampleRate() +
+             getDeviceSampleRate() - 1) / getDeviceSampleRate();
     int32_t burstMicros = 0;
     const int32_t burstMinMicros = android::AudioSystem::getAAudioHardwareBurstMinUsec();
     do {
         if (burstMicros > 0) {  // skip first loop
+            deviceFramesPerBurst *= 2;
             framesPerBurst *= 2;
         }
         burstMicros = framesPerBurst * static_cast<int64_t>(1000000) / getSampleRate();
     } while (burstMicros < burstMinMicros);
     ALOGD("%s() original HW burst = %d, minMicros = %d => SW burst = %d\n",
-          __func__, framesPerHardwareBurst, burstMinMicros, framesPerBurst);
+          __func__, deviceFramesPerBurst, burstMinMicros, framesPerBurst);
 
     // Validate final burst size.
     if (framesPerBurst < MIN_FRAMES_PER_BURST || framesPerBurst > MAX_FRAMES_PER_BURST) {
         ALOGE("%s - framesPerBurst out of range = %d", __func__, framesPerBurst);
         return AAUDIO_ERROR_OUT_OF_RANGE;
     }
+    setDeviceFramesPerBurst(deviceFramesPerBurst);
     setFramesPerBurst(framesPerBurst); // only save good value
 
-    mBufferCapacityInFrames = mEndpointDescriptor.dataQueueDescriptor.capacityInFrames;
+    mDeviceBufferCapacityInFrames = mEndpointDescriptor.dataQueueDescriptor.capacityInFrames;
+
+    mBufferCapacityInFrames = static_cast<int64_t>(mDeviceBufferCapacityInFrames)
+            * getSampleRate() / getDeviceSampleRate();
     if (mBufferCapacityInFrames < getFramesPerBurst()
             || mBufferCapacityInFrames > MAX_BUFFER_CAPACITY_IN_FRAMES) {
         ALOGE("%s - bufferCapacity out of range = %d", __func__, mBufferCapacityInFrames);
         return AAUDIO_ERROR_OUT_OF_RANGE;
     }
 
-    mClockModel.setSampleRate(getSampleRate());
-    mClockModel.setFramesPerBurst(framesPerHardwareBurst);
+    mClockModel.setSampleRate(getDeviceSampleRate());
+    mClockModel.setFramesPerBurst(deviceFramesPerBurst);
 
     if (isDataCallbackSet()) {
         mCallbackFrames = callbackFrames;
@@ -315,7 +335,8 @@
         mTimeOffsetNanos = offsetMicros * AAUDIO_NANOS_PER_MICROSECOND;
     }
 
-    setBufferSize(mBufferCapacityInFrames / 2); // Default buffer size to match Q
+    // Default buffer size to match Q
+    setBufferSize(mBufferCapacityInFrames / 2);
     return AAUDIO_OK;
 }
 
@@ -374,9 +395,9 @@
     // Cache the buffer size which may be from client.
     const int32_t previousBufferSize = mBufferSizeInFrames;
     // Copy all available data from current data queue.
-    uint8_t buffer[getBufferCapacity() * getBytesPerFrame()];
-    android::fifo_frames_t fullFramesAvailable =
-            mAudioEndpoint->read(buffer, getBufferCapacity());
+    uint8_t buffer[getDeviceBufferCapacity() * getBytesPerFrame()];
+    android::fifo_frames_t fullFramesAvailable = mAudioEndpoint->read(buffer,
+            getDeviceBufferCapacity());
     mEndPointParcelable.closeDataFileDescriptor();
     aaudio_result_t result = mServiceInterface.exitStandby(
             mServiceStreamHandleInfo, endpointParcelable);
@@ -408,7 +429,7 @@
         goto exit;
     }
     // Write data from previous data buffer to new endpoint.
-    if (android::fifo_frames_t framesWritten =
+    if (const android::fifo_frames_t framesWritten =
                 mAudioEndpoint->write(buffer, fullFramesAvailable);
             framesWritten != fullFramesAvailable) {
         ALOGW("Some data lost after exiting standby, frames written: %d, "
@@ -448,7 +469,7 @@
         ALOGD("requestStart() but DISCONNECTED");
         return AAUDIO_ERROR_DISCONNECTED;
     }
-    aaudio_stream_state_t originalState = getState();
+    const aaudio_stream_state_t originalState = getState();
     setState(AAUDIO_STREAM_STATE_STARTING);
 
     // Clear any stale timestamps from the previous run.
@@ -605,7 +626,11 @@
     // Generated in server and passed to client. Return latest.
     if (mAtomicInternalTimestamp.isValid()) {
         Timestamp timestamp = mAtomicInternalTimestamp.read();
-        int64_t position = timestamp.getPosition() + mFramesOffsetFromService;
+        // This should not overflow as timestamp.getPosition() should be a position in a buffer and
+        // not the actual timestamp. timestamp.getNanoseconds() below uses the actual timestamp.
+        // At 48000 Hz we can run for over 100 years before overflowing the int64_t.
+        int64_t position = (timestamp.getPosition() + mFramesOffsetFromService) * getSampleRate() /
+                getDeviceSampleRate();
         if (position >= 0) {
             *framePosition = position;
             *timeNanoseconds = timestamp.getNanoseconds();
@@ -889,7 +914,8 @@
         adjustedFrames = maximumSize;
     } else {
         // Round to the next highest burst size.
-        int32_t numBursts = (adjustedFrames + getFramesPerBurst() - 1) / getFramesPerBurst();
+        int32_t numBursts = (static_cast<int64_t>(adjustedFrames) + getFramesPerBurst() - 1) /
+                getFramesPerBurst();
         adjustedFrames = numBursts * getFramesPerBurst();
         // Clip just in case maximumSize is not a multiple of getFramesPerBurst().
         adjustedFrames = std::min(maximumSize, adjustedFrames);
@@ -897,23 +923,32 @@
 
     if (mAudioEndpoint) {
         // Clip against the actual size from the endpoint.
-        int32_t actualFrames = 0;
+        int32_t actualFramesDevice = 0;
+        int32_t maximumFramesDevice = (static_cast<int64_t>(maximumSize) * getDeviceSampleRate()
+                + getSampleRate() - 1) / getSampleRate();
         // Set to maximum size so we can write extra data when ready in order to reduce glitches.
         // The amount we keep in the buffer is controlled by mBufferSizeInFrames.
-        mAudioEndpoint->setBufferSizeInFrames(maximumSize, &actualFrames);
+        mAudioEndpoint->setBufferSizeInFrames(maximumFramesDevice, &actualFramesDevice);
+        int32_t actualFrames = (static_cast<int64_t>(actualFramesDevice) * getSampleRate() +
+                 getDeviceSampleRate() - 1) / getDeviceSampleRate();
         // actualFrames should be <= actual maximum size of endpoint
         adjustedFrames = std::min(actualFrames, adjustedFrames);
     }
 
-    if (adjustedFrames != mBufferSizeInFrames) {
+    const int32_t bufferSizeInFrames = adjustedFrames;
+    const int32_t deviceBufferSizeInFrames = static_cast<int64_t>(bufferSizeInFrames) *
+            getDeviceSampleRate() / getSampleRate();
+
+    if (deviceBufferSizeInFrames != mDeviceBufferSizeInFrames) {
         android::mediametrics::LogItem(mMetricsId)
                 .set(AMEDIAMETRICS_PROP_EVENT, AMEDIAMETRICS_PROP_EVENT_VALUE_SETBUFFERSIZE)
-                .set(AMEDIAMETRICS_PROP_BUFFERSIZEFRAMES, adjustedFrames)
+                .set(AMEDIAMETRICS_PROP_BUFFERSIZEFRAMES, deviceBufferSizeInFrames)
                 .set(AMEDIAMETRICS_PROP_UNDERRUN, (int32_t) getXRunCount())
                 .record();
     }
 
-    mBufferSizeInFrames = adjustedFrames;
+    mBufferSizeInFrames = bufferSizeInFrames;
+    mDeviceBufferSizeInFrames = deviceBufferSizeInFrames;
     ALOGV("%s(%d) returns %d", __func__, requestedFrames, adjustedFrames);
     return (aaudio_result_t) adjustedFrames;
 }
@@ -922,10 +957,18 @@
     return mBufferSizeInFrames;
 }
 
+int32_t AudioStreamInternal::getDeviceBufferSize() const {
+    return mDeviceBufferSizeInFrames;
+}
+
 int32_t AudioStreamInternal::getBufferCapacity() const {
     return mBufferCapacityInFrames;
 }
 
+int32_t AudioStreamInternal::getDeviceBufferCapacity() const {
+    return mDeviceBufferCapacityInFrames;
+}
+
 bool AudioStreamInternal::isClockModelInControl() const {
     return isActive() && mAudioEndpoint->isFreeRunning() && mClockModel.isRunning();
 }