aaudio: convert I16 input to float

This allows an app that requests a LOW_LATENCY FLOAT INPUT stream
to get a FAST track when using Legacy mode.
This is needed because Legacy AudioRecord does not allow FAST for FLOAT
streams.

Bug: 38268031
Test: adb shell input_monitor -m1 -pl -f2
Test: adb shell input_monitor_callback -m1 -pl -f2
Test: On Pixel phones you should get actual performanceMode=12
Change-Id: Ia5879ff4904f50bbb5009a3fc058800e53770710
diff --git a/media/libaaudio/src/Android.bp b/media/libaaudio/src/Android.bp
index 788833b..b9e28a0 100644
--- a/media/libaaudio/src/Android.bp
+++ b/media/libaaudio/src/Android.bp
@@ -57,6 +57,7 @@
 
     shared_libs: [
         "libaudioclient",
+        "libaudioutils",
         "liblog",
         "libcutils",
         "libutils",
diff --git a/media/libaaudio/src/client/AudioStreamInternal.cpp b/media/libaaudio/src/client/AudioStreamInternal.cpp
index 2a3e668..ff138aa 100644
--- a/media/libaaudio/src/client/AudioStreamInternal.cpp
+++ b/media/libaaudio/src/client/AudioStreamInternal.cpp
@@ -156,7 +156,7 @@
     setInputPreset(configurationOutput.getInputPreset());
 
     // Save device format so we can do format conversion and volume scaling together.
-    mDeviceFormat = configurationOutput.getFormat();
+    setDeviceFormat(configurationOutput.getFormat());
 
     result = mServiceInterface.getStreamDescription(mServiceStreamHandle, mEndPointParcelable);
     if (result != AAUDIO_OK) {
diff --git a/media/libaaudio/src/client/AudioStreamInternal.h b/media/libaaudio/src/client/AudioStreamInternal.h
index 0e0724b..0425cd5 100644
--- a/media/libaaudio/src/client/AudioStreamInternal.h
+++ b/media/libaaudio/src/client/AudioStreamInternal.h
@@ -138,8 +138,6 @@
     // Calculate timeout for an operation involving framesPerOperation.
     int64_t calculateReasonableTimeout(int32_t framesPerOperation);
 
-    aaudio_format_t getDeviceFormat() const { return mDeviceFormat; }
-
     int32_t getDeviceChannelCount() const { return mDeviceChannelCount; }
 
     /**
@@ -195,9 +193,6 @@
 
     int64_t                  mServiceLatencyNanos = 0;
 
-    // Sometimes the hardware is operating with a different format or channel count from the app.
-    // Then we require conversion in AAudio.
-    aaudio_format_t          mDeviceFormat = AAUDIO_FORMAT_UNSPECIFIED;
     int32_t                  mDeviceChannelCount = 0;
 };
 
diff --git a/media/libaaudio/src/core/AudioStream.cpp b/media/libaaudio/src/core/AudioStream.cpp
index 61e03db..acb82c8 100644
--- a/media/libaaudio/src/core/AudioStream.cpp
+++ b/media/libaaudio/src/core/AudioStream.cpp
@@ -367,7 +367,6 @@
     return err ? AAudioConvert_androidToAAudioResult(-errno) : mThreadRegistrationResult;
 }
 
-
 aaudio_data_callback_result_t AudioStream::maybeCallDataCallback(void *audioData,
                                                                  int32_t numFrames) {
     aaudio_data_callback_result_t result = AAUDIO_CALLBACK_RESULT_STOP;
diff --git a/media/libaaudio/src/core/AudioStream.h b/media/libaaudio/src/core/AudioStream.h
index 5273e36..c46bdc5 100644
--- a/media/libaaudio/src/core/AudioStream.h
+++ b/media/libaaudio/src/core/AudioStream.h
@@ -252,6 +252,20 @@
         return AAudioConvert_formatToSizeInBytes(mFormat);
     }
 
+    /**
+     * This is only valid after setSamplesPerFrame() and setDeviceFormat() have been called.
+     */
+    int32_t getBytesPerDeviceFrame() const {
+        return mSamplesPerFrame * getBytesPerDeviceSample();
+    }
+
+    /**
+     * This is only valid after setDeviceFormat() has been called.
+     */
+    int32_t getBytesPerDeviceSample() const {
+        return AAudioConvert_formatToSizeInBytes(getDeviceFormat());
+    }
+
     virtual int64_t getFramesWritten() = 0;
 
     virtual int64_t getFramesRead() = 0;
@@ -471,6 +485,17 @@
         mFormat = format;
     }
 
+    /**
+     * This should not be called after the open() call.
+     */
+    void setDeviceFormat(aaudio_format_t format) {
+        mDeviceFormat = format;
+    }
+
+    aaudio_format_t getDeviceFormat() const {
+        return mDeviceFormat;
+    }
+
     void setState(aaudio_stream_state_t state);
 
     void setDeviceId(int32_t deviceId) {
@@ -485,9 +510,23 @@
 
     float                mDuckAndMuteVolume = 1.0f;
 
-
 protected:
 
+    /**
+     * Either convert the data from device format to app format and return a pointer
+     * to the conversion buffer,
+     * OR just pass back the original pointer.
+     *
+     * Note that this is only used for the INPUT path.
+     *
+     * @param audioData
+     * @param numFrames
+     * @return original pointer or the conversion buffer
+     */
+    virtual const void * maybeConvertDeviceData(const void *audioData, int32_t numFrames) {
+        return audioData;
+    }
+
     void setPeriodNanoseconds(int64_t periodNanoseconds) {
         mPeriodNanoseconds.store(periodNanoseconds, std::memory_order_release);
     }
@@ -539,6 +578,10 @@
 
     int32_t                     mSessionId = AAUDIO_UNSPECIFIED;
 
+    // Sometimes the hardware is operating with a different format from the app.
+    // Then we require conversion in AAudio.
+    aaudio_format_t             mDeviceFormat = AAUDIO_FORMAT_UNSPECIFIED;
+
     // callback ----------------------------------
 
     AAudioStream_dataCallback   mDataCallbackProc = nullptr;  // external callback functions
diff --git a/media/libaaudio/src/legacy/AudioStreamLegacy.cpp b/media/libaaudio/src/legacy/AudioStreamLegacy.cpp
index 8bbb9d9..a6b9f5d 100644
--- a/media/libaaudio/src/legacy/AudioStreamLegacy.cpp
+++ b/media/libaaudio/src/legacy/AudioStreamLegacy.cpp
@@ -19,10 +19,12 @@
 #include <utils/Log.h>
 
 #include <stdint.h>
-#include <utils/String16.h>
+
+#include <aaudio/AAudio.h>
+#include <audio_utils/primitives.h>
 #include <media/AudioTrack.h>
 #include <media/AudioTimestamp.h>
-#include <aaudio/AAudio.h>
+#include <utils/String16.h>
 
 #include "core/AudioStream.h"
 #include "legacy/AudioStreamLegacy.h"
@@ -48,14 +50,17 @@
     return AudioStreamLegacy_callback;
 }
 
-aaudio_data_callback_result_t AudioStreamLegacy::callDataCallbackFrames(uint8_t *buffer, int32_t numFrames) {
+aaudio_data_callback_result_t AudioStreamLegacy::callDataCallbackFrames(uint8_t *buffer,
+                                                                        int32_t numFrames) {
+    void *finalAudioData = buffer;
     if (getDirection() == AAUDIO_DIRECTION_INPUT) {
         // Increment before because we already got the data from the device.
         incrementFramesRead(numFrames);
+        finalAudioData = (void *) maybeConvertDeviceData(buffer, numFrames);
     }
 
     // Call using the AAudio callback interface.
-    aaudio_data_callback_result_t callbackResult = maybeCallDataCallback(buffer, numFrames);
+    aaudio_data_callback_result_t callbackResult = maybeCallDataCallback(finalAudioData, numFrames);
 
     if (callbackResult == AAUDIO_CALLBACK_RESULT_CONTINUE
             && getDirection() == AAUDIO_DIRECTION_OUTPUT) {
@@ -67,15 +72,15 @@
 
 // Implement FixedBlockProcessor
 int32_t AudioStreamLegacy::onProcessFixedBlock(uint8_t *buffer, int32_t numBytes) {
-    int32_t numFrames = numBytes / getBytesPerFrame();
+    int32_t numFrames = numBytes / getBytesPerDeviceFrame();
     return (int32_t) callDataCallbackFrames(buffer, numFrames);
 }
 
 void AudioStreamLegacy::processCallbackCommon(aaudio_callback_operation_t opcode, void *info) {
     aaudio_data_callback_result_t callbackResult;
-    // This illegal size can be used to AudioFlinger to stop calling us.
+    // This illegal size can be used to tell AudioFlinger to stop calling us.
     // This takes advantage of AudioFlinger killing the stream.
-    // TODO need API change in AudioRecord and AudioTrack
+    // TODO add to API in AudioRecord and AudioTrack
     const size_t SIZE_STOP_CALLBACKS = SIZE_MAX;
 
     switch (opcode) {
@@ -100,7 +105,7 @@
 
                 // If the caller specified an exact size then use a block size adapter.
                 if (mBlockAdapter != nullptr) {
-                    int32_t byteCount = audioBuffer->frameCount * getBytesPerFrame();
+                    int32_t byteCount = audioBuffer->frameCount * getBytesPerDeviceFrame();
                     callbackResult = mBlockAdapter->processVariableBlock(
                             (uint8_t *) audioBuffer->raw, byteCount);
                 } else {
@@ -109,7 +114,7 @@
                                                             audioBuffer->frameCount);
                 }
                 if (callbackResult == AAUDIO_CALLBACK_RESULT_CONTINUE) {
-                    audioBuffer->size = audioBuffer->frameCount * getBytesPerFrame();
+                    audioBuffer->size = audioBuffer->frameCount * getBytesPerDeviceFrame();
                 } else { // STOP or invalid result
                     ALOGW("%s() callback requested stop, fake an error", __func__);
                     audioBuffer->size = SIZE_STOP_CALLBACKS;
diff --git a/media/libaaudio/src/legacy/AudioStreamRecord.cpp b/media/libaaudio/src/legacy/AudioStreamRecord.cpp
index 28158e2..1981ba3 100644
--- a/media/libaaudio/src/legacy/AudioStreamRecord.cpp
+++ b/media/libaaudio/src/legacy/AudioStreamRecord.cpp
@@ -19,13 +19,15 @@
 #include <utils/Log.h>
 
 #include <stdint.h>
-#include <utils/String16.h>
-#include <media/AudioRecord.h>
-#include <aaudio/AAudio.h>
 
-#include "AudioClock.h"
+#include <aaudio/AAudio.h>
+#include <audio_utils/primitives.h>
+#include <media/AudioRecord.h>
+#include <utils/String16.h>
+
 #include "legacy/AudioStreamLegacy.h"
 #include "legacy/AudioStreamRecord.h"
+#include "utility/AudioClock.h"
 #include "utility/FixedBlockWriter.h"
 
 using namespace android;
@@ -63,10 +65,6 @@
     size_t frameCount = (builder.getBufferCapacity() == AAUDIO_UNSPECIFIED) ? 0
                         : builder.getBufferCapacity();
 
-    // TODO implement an unspecified Android format then use that.
-    audio_format_t format = (getFormat() == AAUDIO_FORMAT_UNSPECIFIED)
-            ? AUDIO_FORMAT_PCM_FLOAT
-            : AAudioConvert_aaudioToAndroidDataFormat(getFormat());
 
     audio_input_flags_t flags = AUDIO_INPUT_FLAG_NONE;
     aaudio_performance_mode_t perfMode = getPerformanceMode();
@@ -82,6 +80,35 @@
             break;
     }
 
+    // Preserve behavior of API 26
+    if (getFormat() == AAUDIO_FORMAT_UNSPECIFIED) {
+        setFormat(AAUDIO_FORMAT_PCM_FLOAT);
+    }
+
+    // Maybe change device format to get a FAST path.
+    // AudioRecord does not support FAST mode for FLOAT data.
+    // TODO AudioRecord should allow FLOAT data paths for FAST tracks.
+    // So IF the user asks for low latency FLOAT
+    // AND the sampleRate is likely to be compatible with FAST
+    // THEN request I16 and convert to FLOAT when passing to user.
+    // Note that hard coding 48000 Hz is not ideal because the sampleRate
+    // for a FAST path might not be 48000 Hz.
+    // It normally is but there is a chance that it is not.
+    // And there is no reliable way to know that in advance.
+    // Luckily the consequences of a wrong guess are minor.
+    // We just may not get a FAST track.
+    // But we wouldn't have anyway without this hack.
+    constexpr int32_t kMostLikelySampleRateForFast = 48000;
+    if (getFormat() == AAUDIO_FORMAT_PCM_FLOAT
+            && perfMode == AAUDIO_PERFORMANCE_MODE_LOW_LATENCY
+            && (samplesPerFrame <= 2) // FAST only for mono and stereo
+            && (getSampleRate() == kMostLikelySampleRateForFast
+                || getSampleRate() == AAUDIO_UNSPECIFIED)) {
+        setDeviceFormat(AAUDIO_FORMAT_PCM_I16);
+    } else {
+        setDeviceFormat(getFormat());
+    }
+
     uint32_t notificationFrames = 0;
 
     // Setup the callback if there is one.
@@ -96,9 +123,6 @@
     }
     mCallbackBufferSize = builder.getFramesPerDataCallback();
 
-    ALOGD("open(), request notificationFrames = %u, frameCount = %u",
-          notificationFrames, (uint)frameCount);
-
     // Don't call mAudioRecord->setInputDevice() because it will be overwritten by set()!
     audio_port_handle_t selectedDeviceId = (getDeviceId() == AAUDIO_UNSPECIFIED)
                                            ? AUDIO_PORT_HANDLE_NONE
@@ -120,39 +144,59 @@
     aaudio_session_id_t requestedSessionId = builder.getSessionId();
     audio_session_t sessionId = AAudioConvert_aaudioToAndroidSessionId(requestedSessionId);
 
-    mAudioRecord = new AudioRecord(
-            mOpPackageName // const String16& opPackageName TODO does not compile
-            );
-    mAudioRecord->set(
-            AUDIO_SOURCE_DEFAULT, // ignored because we pass attributes below
-            getSampleRate(),
-            format,
-            channelMask,
-            frameCount,
-            callback,
-            callbackData,
-            notificationFrames,
-            false /*threadCanCallJava*/,
-            sessionId,
-            streamTransferType,
-            flags,
-            AUDIO_UID_INVALID, // DEFAULT uid
-            -1,                // DEFAULT pid
-            &attributes,
-            selectedDeviceId
-            );
+    // ----------- open the AudioRecord ---------------------
+    // Might retry, but never more than once.
+    for (int i = 0; i < 2; i ++) {
+        audio_format_t requestedInternalFormat =
+                AAudioConvert_aaudioToAndroidDataFormat(getDeviceFormat());
 
-    // Did we get a valid track?
-    status_t status = mAudioRecord->initCheck();
-    if (status != OK) {
-        close();
-        ALOGE("open(), initCheck() returned %d", status);
-        return AAudioConvert_androidToAAudioResult(status);
+        mAudioRecord = new AudioRecord(
+                mOpPackageName // const String16& opPackageName TODO does not compile
+        );
+        mAudioRecord->set(
+                AUDIO_SOURCE_DEFAULT, // ignored because we pass attributes below
+                getSampleRate(),
+                requestedInternalFormat,
+                channelMask,
+                frameCount,
+                callback,
+                callbackData,
+                notificationFrames,
+                false /*threadCanCallJava*/,
+                sessionId,
+                streamTransferType,
+                flags,
+                AUDIO_UID_INVALID, // DEFAULT uid
+                -1,                // DEFAULT pid
+                &attributes,
+                selectedDeviceId
+        );
+
+        // Did we get a valid track?
+        status_t status = mAudioRecord->initCheck();
+        if (status != OK) {
+            close();
+            ALOGE("open(), initCheck() returned %d", status);
+            return AAudioConvert_androidToAAudioResult(status);
+        }
+
+        // Check to see if it was worth hacking the deviceFormat.
+        bool gotFastPath = (mAudioRecord->getFlags() & AUDIO_INPUT_FLAG_FAST)
+                           == AUDIO_INPUT_FLAG_FAST;
+        if (getFormat() != getDeviceFormat() && !gotFastPath) {
+            // We tried to get a FAST path by switching the device format.
+            // But it didn't work. So we might as well reopen using the same
+            // format for device and for app.
+            ALOGD("%s() used a different device format but no FAST path, reopen", __func__);
+            mAudioRecord.clear();
+            setDeviceFormat(getFormat());
+        } else {
+            break; // Keep the one we just opened.
+        }
     }
 
     // Get the actual values from the AudioRecord.
     setSamplesPerFrame(mAudioRecord->channelCount());
-    setFormat(AAudioConvert_androidToAAudioDataFormat(mAudioRecord->format()));
 
     int32_t actualSampleRate = mAudioRecord->getSampleRate();
     ALOGW_IF(actualSampleRate != getSampleRate(),
@@ -169,6 +213,29 @@
         mBlockAdapter = nullptr;
     }
 
+    // Allocate format conversion buffer if needed.
+    if (getDeviceFormat() == AAUDIO_FORMAT_PCM_I16
+        && getFormat() == AAUDIO_FORMAT_PCM_FLOAT) {
+
+        if (builder.getDataCallbackProc() != nullptr) {
+            // If we have a callback then we need to convert the data into an internal float
+            // array and then pass that entire array to the app.
+            mFormatConversionBufferSizeInFrames =
+                    (mCallbackBufferSize != AAUDIO_UNSPECIFIED)
+                    ? mCallbackBufferSize : getFramesPerBurst();
+            int32_t numSamples = mFormatConversionBufferSizeInFrames * getSamplesPerFrame();
+            mFormatConversionBufferFloat = std::make_unique<float[]>(numSamples);
+        } else {
+            // If we don't have a callback then we will read into an internal short array
+            // and then convert into the app float array in read().
+            mFormatConversionBufferSizeInFrames = getFramesPerBurst();
+            int32_t numSamples = mFormatConversionBufferSizeInFrames * getSamplesPerFrame();
+            mFormatConversionBufferI16 = std::make_unique<int16_t[]>(numSamples);
+        }
+        ALOGD("%s() setup I16>FLOAT conversion buffer with %d frames",
+              __func__, mFormatConversionBufferSizeInFrames);
+    }
+
     // Update performance mode based on the actual stream.
     // For example, if the sample rate does not match native then you won't get a FAST track.
     audio_input_flags_t actualFlags = mAudioRecord->getFlags();
@@ -216,6 +283,24 @@
     return AudioStream::close();
 }
 
+const void * AudioStreamRecord::maybeConvertDeviceData(const void *audioData, int32_t numFrames) {
+    if (mFormatConversionBufferFloat.get() != nullptr) {
+        LOG_ALWAYS_FATAL_IF(numFrames > mFormatConversionBufferSizeInFrames,
+                            "%s() conversion size %d too large for buffer %d",
+                            __func__, numFrames, mFormatConversionBufferSizeInFrames);
+
+        int32_t numSamples = numFrames * getSamplesPerFrame();
+        // Only conversion supported is I16 to FLOAT
+        memcpy_to_float_from_i16(
+                    mFormatConversionBufferFloat.get(),
+                    (const int16_t *) audioData,
+                    numSamples);
+        return mFormatConversionBufferFloat.get();
+    } else {
+        return audioData;
+    }
+}
+
 void AudioStreamRecord::processCallback(int event, void *info) {
     switch (event) {
         case AudioRecord::EVENT_MORE_DATA:
@@ -302,9 +387,10 @@
                                       int32_t numFrames,
                                       int64_t timeoutNanoseconds)
 {
-    int32_t bytesPerFrame = getBytesPerFrame();
+    int32_t bytesPerDeviceFrame = getBytesPerDeviceFrame();
     int32_t numBytes;
-    aaudio_result_t result = AAudioConvert_framesToBytes(numFrames, bytesPerFrame, &numBytes);
+    // This will detect out of range values for numFrames.
+    aaudio_result_t result = AAudioConvert_framesToBytes(numFrames, bytesPerDeviceFrame, &numBytes);
     if (result != AAUDIO_OK) {
         return result;
     }
@@ -315,19 +401,49 @@
 
     // TODO add timeout to AudioRecord
     bool blocking = (timeoutNanoseconds > 0);
-    ssize_t bytesRead = mAudioRecord->read(buffer, numBytes, blocking);
-    if (bytesRead == WOULD_BLOCK) {
+
+    ssize_t bytesActuallyRead = 0;
+    ssize_t totalBytesRead = 0;
+    if (mFormatConversionBufferI16.get() != nullptr) {
+        // Convert I16 data to float using an intermediate buffer.
+        float *floatBuffer = (float *) buffer;
+        int32_t framesLeft = numFrames;
+        // Perform conversion using multiple read()s if necessary.
+        while (framesLeft > 0) {
+            // Read into short internal buffer.
+            int32_t framesToRead = std::min(framesLeft, mFormatConversionBufferSizeInFrames);
+            size_t bytesToRead = framesToRead * bytesPerDeviceFrame;
+            bytesActuallyRead = mAudioRecord->read(mFormatConversionBufferI16.get(), bytesToRead, blocking);
+            if (bytesActuallyRead <= 0) {
+                break;
+            }
+            totalBytesRead += bytesActuallyRead;
+            int32_t framesToConvert = bytesActuallyRead / bytesPerDeviceFrame;
+            // Convert into app float buffer.
+            size_t numSamples = framesToConvert * getSamplesPerFrame();
+            memcpy_to_float_from_i16(
+                    floatBuffer,
+                    mFormatConversionBufferI16.get(),
+                    numSamples);
+            floatBuffer += numSamples;
+            framesLeft -= framesToConvert;
+        }
+    } else {
+        bytesActuallyRead = mAudioRecord->read(buffer, numBytes, blocking);
+        totalBytesRead = bytesActuallyRead;
+    }
+    if (bytesActuallyRead == WOULD_BLOCK) {
         return 0;
-    } else if (bytesRead < 0) {
-        // in this context, a DEAD_OBJECT is more likely to be a disconnect notification due to
-        // AudioRecord invalidation
-        if (bytesRead == DEAD_OBJECT) {
+    } else if (bytesActuallyRead < 0) {
+        // In this context, a DEAD_OBJECT is more likely to be a disconnect notification due to
+        // AudioRecord invalidation.
+        if (bytesActuallyRead == DEAD_OBJECT) {
             setState(AAUDIO_STREAM_STATE_DISCONNECTED);
             return AAUDIO_ERROR_DISCONNECTED;
         }
-        return AAudioConvert_androidToAAudioResult(bytesRead);
+        return AAudioConvert_androidToAAudioResult(bytesActuallyRead);
     }
-    int32_t framesRead = (int32_t)(bytesRead / bytesPerFrame);
+    int32_t framesRead = (int32_t)(totalBytesRead / bytesPerDeviceFrame);
     incrementFramesRead(framesRead);
 
     result = updateStateMachine();
diff --git a/media/libaaudio/src/legacy/AudioStreamRecord.h b/media/libaaudio/src/legacy/AudioStreamRecord.h
index c1723ba..2f41d34 100644
--- a/media/libaaudio/src/legacy/AudioStreamRecord.h
+++ b/media/libaaudio/src/legacy/AudioStreamRecord.h
@@ -76,6 +76,8 @@
         return incrementFramesRead(frames);
     }
 
+    const void * maybeConvertDeviceData(const void *audioData, int32_t numFrames) override;
+
 private:
     android::sp<android::AudioRecord> mAudioRecord;
     // adapts between variable sized blocks and fixed size blocks
@@ -83,6 +85,11 @@
 
     // TODO add 64-bit position reporting to AudioRecord and use it.
     android::String16                mOpPackageName;
+
+    // Only one type of conversion buffer is used.
+    std::unique_ptr<float[]>         mFormatConversionBufferFloat;
+    std::unique_ptr<int16_t[]>       mFormatConversionBufferI16;
+    int32_t                          mFormatConversionBufferSizeInFrames = 0;
 };
 
 } /* namespace aaudio */
diff --git a/media/libaaudio/src/legacy/AudioStreamTrack.cpp b/media/libaaudio/src/legacy/AudioStreamTrack.cpp
index 023e8af..9653601 100644
--- a/media/libaaudio/src/legacy/AudioStreamTrack.cpp
+++ b/media/libaaudio/src/legacy/AudioStreamTrack.cpp
@@ -181,6 +181,7 @@
     aaudio_format_t aaudioFormat =
             AAudioConvert_androidToAAudioDataFormat(mAudioTrack->format());
     setFormat(aaudioFormat);
+    setDeviceFormat(aaudioFormat);
 
     int32_t actualSampleRate = mAudioTrack->getSampleRate();
     ALOGW_IF(actualSampleRate != getSampleRate(),