aaudio: apply volume in the MMAP data path

The volume scaling is in AudioStreamInternal and not the mixer
because we will need volume scaling in EXCLUSIVE mode too.

Bug: 37518243
Test: play a tone using NativeOboe app then press volume keys
Change-Id: Ibbac9770ea4493f8ade64681be86f109a92803cd
Signed-off-by: Phil Burk <philburk@google.com>
diff --git a/media/libaaudio/src/utility/LinearRamp.h b/media/libaaudio/src/utility/LinearRamp.h
new file mode 100644
index 0000000..ff09dce
--- /dev/null
+++ b/media/libaaudio/src/utility/LinearRamp.h
@@ -0,0 +1,97 @@
+/*
+ * Copyright 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef AAUDIO_LINEAR_RAMP_H
+#define AAUDIO_LINEAR_RAMP_H
+
+#include <atomic>
+#include <stdint.h>
+
+/**
+ * Generate segments along a linear ramp.
+ * The ramp target can be updated from another thread.
+ * When the target is updated, a new ramp is started from the current position.
+ *
+ * The first ramp starts at 0.0.
+ *
+ */
+class LinearRamp {
+public:
+    LinearRamp() {
+        mTarget.store(1.0f);
+    }
+
+    void setLengthInFrames(int32_t frames) {
+        mLengthInFrames = frames;
+    }
+
+    int32_t getLengthInFrames() {
+        return mLengthInFrames;
+    }
+
+    /**
+     * This may be called by another thread.
+     * @param target
+     */
+    void setTarget(float target) {
+        mTarget.store(target);
+    }
+
+    float getTarget() {
+        return mTarget.load();
+    }
+
+    /**
+     * Force the nextSegment to start from this level.
+     *
+     * WARNING: this can cause a discontinuity if called while the ramp is being used.
+     * Only call this when setting the initial ramp.
+     *
+     * @param level
+     */
+    void forceCurrent(float level) {
+        mLevelFrom = level;
+        mLevelTo = level; // forces a ramp if it does not match target
+    }
+
+    float getCurrent() {
+        return mLevelFrom;
+    }
+
+    /**
+     * Get levels for next ramp segment.
+     *
+     * @param frames number of frames in the segment
+     * @param levelFrom pointer to starting amplitude
+     * @param levelTo pointer to ending amplitude
+     * @return true if ramp is still moving towards the target
+     */
+    bool nextSegment(int32_t frames, float *levelFrom, float *levelTo);
+
+private:
+
+    bool isRamping();
+
+    std::atomic<float>   mTarget;
+
+    int32_t mLengthInFrames  = 48000 / 50; // 20 msec at 48000 Hz
+    int32_t mRemaining       = 0;
+    float   mLevelFrom       = 0.0f;
+    float   mLevelTo         = 0.0f;
+};
+
+
+#endif //AAUDIO_LINEAR_RAMP_H