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/examples/write_sine/jni/Android.mk b/media/libaaudio/examples/write_sine/jni/Android.mk
index 5a884e1..0bda008 100644
--- a/media/libaaudio/examples/write_sine/jni/Android.mk
+++ b/media/libaaudio/examples/write_sine/jni/Android.mk
@@ -4,7 +4,8 @@
LOCAL_MODULE_TAGS := tests
LOCAL_C_INCLUDES := \
$(call include-path-for, audio-utils) \
- frameworks/av/media/libaaudio/include
+ frameworks/av/media/libaaudio/include \
+ frameworks/av/media/libaaudio/src
# NDK recommends using this kind of relative path instead of an absolute path.
LOCAL_SRC_FILES:= ../src/write_sine.cpp
diff --git a/media/libaaudio/examples/write_sine/src/SineGenerator.h b/media/libaaudio/examples/write_sine/src/SineGenerator.h
index f2eb984..64b772d 100644
--- a/media/libaaudio/examples/write_sine/src/SineGenerator.h
+++ b/media/libaaudio/examples/write_sine/src/SineGenerator.h
@@ -79,7 +79,7 @@
}
}
- double mAmplitude = 0.005; // unitless scaler
+ double mAmplitude = 0.05; // unitless scaler
double mPhase = 0.0;
double mPhaseIncrement = 440 * M_PI * 2 / 48000;
double mFrameRate = 48000;
diff --git a/media/libaaudio/examples/write_sine/src/write_sine.cpp b/media/libaaudio/examples/write_sine/src/write_sine.cpp
index df55c3f..9107a7c 100644
--- a/media/libaaudio/examples/write_sine/src/write_sine.cpp
+++ b/media/libaaudio/examples/write_sine/src/write_sine.cpp
@@ -22,8 +22,8 @@
#include <aaudio/AAudio.h>
#include "SineGenerator.h"
-#define SAMPLE_RATE 48000
-#define NUM_SECONDS 5
+#define SAMPLE_RATE 48000
+#define NUM_SECONDS 15
#define NANOS_PER_MICROSECOND ((int64_t)1000)
#define NANOS_PER_MILLISECOND (NANOS_PER_MICROSECOND * 1000)
#define NANOS_PER_SECOND (NANOS_PER_MILLISECOND * 1000)
diff --git a/media/libaaudio/examples/write_sine/src/write_sine_callback.cpp b/media/libaaudio/examples/write_sine/src/write_sine_callback.cpp
index a7e32bd..cc0c3a4 100644
--- a/media/libaaudio/examples/write_sine/src/write_sine_callback.cpp
+++ b/media/libaaudio/examples/write_sine/src/write_sine_callback.cpp
@@ -26,7 +26,7 @@
#include <aaudio/AAudio.h>
#include "SineGenerator.h"
-#define NUM_SECONDS 5
+#define NUM_SECONDS 15
//#define SHARING_MODE AAUDIO_SHARING_MODE_EXCLUSIVE
#define SHARING_MODE AAUDIO_SHARING_MODE_SHARED
diff --git a/media/libaaudio/examples/write_sine/static/Android.mk b/media/libaaudio/examples/write_sine/static/Android.mk
index e4da6a8..3fee08a 100644
--- a/media/libaaudio/examples/write_sine/static/Android.mk
+++ b/media/libaaudio/examples/write_sine/static/Android.mk
@@ -4,6 +4,7 @@
LOCAL_MODULE_TAGS := examples
LOCAL_C_INCLUDES := \
$(call include-path-for, audio-utils) \
+ frameworks/av/media/libaaudio/src \
frameworks/av/media/libaaudio/include
# NDK recommends using this kind of relative path instead of an absolute path.
diff --git a/media/libaaudio/src/Android.mk b/media/libaaudio/src/Android.mk
index b5bb75f..f43c0ad 100644
--- a/media/libaaudio/src/Android.mk
+++ b/media/libaaudio/src/Android.mk
@@ -39,6 +39,7 @@
utility/FixedBlockAdapter.cpp \
utility/FixedBlockReader.cpp \
utility/FixedBlockWriter.cpp \
+ utility/LinearRamp.cpp \
fifo/FifoBuffer.cpp \
fifo/FifoControllerBase.cpp \
client/AudioEndpoint.cpp \
@@ -93,6 +94,7 @@
utility/FixedBlockAdapter.cpp \
utility/FixedBlockReader.cpp \
utility/FixedBlockWriter.cpp \
+ utility/LinearRamp.cpp \
fifo/FifoBuffer.cpp \
fifo/FifoControllerBase.cpp \
client/AudioEndpoint.cpp \
diff --git a/media/libaaudio/src/client/AudioStreamInternal.cpp b/media/libaaudio/src/client/AudioStreamInternal.cpp
index af4b93a..810751a 100644
--- a/media/libaaudio/src/client/AudioStreamInternal.cpp
+++ b/media/libaaudio/src/client/AudioStreamInternal.cpp
@@ -32,9 +32,10 @@
#include "binding/AAudioStreamConfiguration.h"
#include "binding/IAAudioService.h"
#include "binding/AAudioServiceMessage.h"
-#include "fifo/FifoBuffer.h"
-
#include "core/AudioStreamBuilder.h"
+#include "fifo/FifoBuffer.h"
+#include "utility/LinearRamp.h"
+
#include "AudioStreamInternal.h"
#define LOG_TIMESTAMPS 0
@@ -478,8 +479,9 @@
ALOGW("WARNING - processCommands() AAUDIO_SERVICE_EVENT_DISCONNECTED");
break;
case AAUDIO_SERVICE_EVENT_VOLUME:
- mVolume = message->event.dataDouble;
- ALOGD_IF(MYLOG_CONDITION, "processCommands() AAUDIO_SERVICE_EVENT_VOLUME %f", mVolume);
+ mVolumeRamp.setTarget((float) message->event.dataDouble);
+ ALOGD_IF(MYLOG_CONDITION, "processCommands() AAUDIO_SERVICE_EVENT_VOLUME %f",
+ message->event.dataDouble);
break;
default:
ALOGW("WARNING - processCommands() Unrecognized event = %d",
@@ -639,10 +641,10 @@
}
-// TODO this function needs a major cleanup.
aaudio_result_t AudioStreamInternal::writeNowWithConversion(const void *buffer,
int32_t numFrames) {
- // ALOGD_IF(MYLOG_CONDITION, "AudioStreamInternal::writeNowWithConversion(%p, %d)", buffer, numFrames);
+ // ALOGD_IF(MYLOG_CONDITION, "AudioStreamInternal::writeNowWithConversion(%p, %d)",
+ // buffer, numFrames);
WrappingBuffer wrappingBuffer;
uint8_t *source = (uint8_t *) buffer;
int32_t framesLeft = numFrames;
@@ -659,31 +661,67 @@
framesToWrite = framesAvailable;
}
int32_t numBytes = getBytesPerFrame() * framesToWrite;
- // TODO handle volume scaling
- if (getFormat() == mDeviceFormat) {
- // Copy straight through.
- memcpy(wrappingBuffer.data[partIndex], source, numBytes);
- } else if (getFormat() == AAUDIO_FORMAT_PCM_FLOAT
- && mDeviceFormat == AAUDIO_FORMAT_PCM_I16) {
- // Data conversion.
- AAudioConvert_floatToPcm16(
- (const float *) source,
- framesToWrite * getSamplesPerFrame(),
- (int16_t *) wrappingBuffer.data[partIndex]);
- } else if (getFormat() == AAUDIO_FORMAT_PCM_I16
- && mDeviceFormat == AAUDIO_FORMAT_PCM_FLOAT) {
- // Data conversion.
- AAudioConvert_pcm16ToFloat(
- (const int16_t *) source,
- framesToWrite * getSamplesPerFrame(),
- (float *) wrappingBuffer.data[partIndex]);
- } else {
- // TODO handle more conversions
- ALOGE("AudioStreamInternal::writeNowWithConversion() unsupported formats: %d, %d",
- getFormat(), mDeviceFormat);
- return AAUDIO_ERROR_UNEXPECTED_VALUE;
+ int32_t numSamples = framesToWrite * getSamplesPerFrame();
+ // Data conversion.
+ float levelFrom;
+ float levelTo;
+ bool ramping = mVolumeRamp.nextSegment(framesToWrite * getSamplesPerFrame(),
+ &levelFrom, &levelTo);
+ // The formats are validated when the stream is opened so we do not have to
+ // check for illegal combinations here.
+ if (getFormat() == AAUDIO_FORMAT_PCM_FLOAT) {
+ if (mDeviceFormat == AAUDIO_FORMAT_PCM_FLOAT) {
+ AAudio_linearRamp(
+ (const float *) source,
+ (float *) wrappingBuffer.data[partIndex],
+ framesToWrite,
+ getSamplesPerFrame(),
+ levelFrom,
+ levelTo);
+ } else if (mDeviceFormat == AAUDIO_FORMAT_PCM_I16) {
+ if (ramping) {
+ AAudioConvert_floatToPcm16(
+ (const float *) source,
+ (int16_t *) wrappingBuffer.data[partIndex],
+ framesToWrite,
+ getSamplesPerFrame(),
+ levelFrom,
+ levelTo);
+ } else {
+ AAudioConvert_floatToPcm16(
+ (const float *) source,
+ (int16_t *) wrappingBuffer.data[partIndex],
+ numSamples,
+ levelTo);
+ }
+ }
+ } else if (getFormat() == AAUDIO_FORMAT_PCM_I16) {
+ if (mDeviceFormat == AAUDIO_FORMAT_PCM_FLOAT) {
+ if (ramping) {
+ AAudioConvert_pcm16ToFloat(
+ (const int16_t *) source,
+ (float *) wrappingBuffer.data[partIndex],
+ framesToWrite,
+ getSamplesPerFrame(),
+ levelFrom,
+ levelTo);
+ } else {
+ AAudioConvert_pcm16ToFloat(
+ (const int16_t *) source,
+ (float *) wrappingBuffer.data[partIndex],
+ numSamples,
+ levelTo);
+ }
+ } else if (mDeviceFormat == AAUDIO_FORMAT_PCM_I16) {
+ AAudio_linearRamp(
+ (const int16_t *) source,
+ (int16_t *) wrappingBuffer.data[partIndex],
+ framesToWrite,
+ getSamplesPerFrame(),
+ levelFrom,
+ levelTo);
+ }
}
-
source += numBytes;
framesLeft -= framesToWrite;
} else {
diff --git a/media/libaaudio/src/client/AudioStreamInternal.h b/media/libaaudio/src/client/AudioStreamInternal.h
index 8244311..e550ba3 100644
--- a/media/libaaudio/src/client/AudioStreamInternal.h
+++ b/media/libaaudio/src/client/AudioStreamInternal.h
@@ -22,11 +22,11 @@
#include "binding/IAAudioService.h"
#include "binding/AudioEndpointParcelable.h"
+#include "binding/AAudioServiceInterface.h"
#include "client/IsochronousClockModel.h"
#include "client/AudioEndpoint.h"
#include "core/AudioStream.h"
-
-#include "binding/AAudioServiceInterface.h"
+#include "utility/LinearRamp.h"
using android::sp;
using android::IAAudioService;
@@ -154,7 +154,7 @@
int64_t mLastFramesRead = 0; // used to prevent retrograde motion
int32_t mFramesPerBurst; // frames per HAL transfer
int32_t mXRunCount = 0; // how many underrun events?
- float mVolume = 1.0; // volume that the server told us to use
+ LinearRamp mVolumeRamp;
AAudioServiceInterface &mServiceInterface; // abstract interface to the service
diff --git a/media/libaaudio/src/legacy/AudioStreamTrack.cpp b/media/libaaudio/src/legacy/AudioStreamTrack.cpp
index 1bb9e53..96fd427 100644
--- a/media/libaaudio/src/legacy/AudioStreamTrack.cpp
+++ b/media/libaaudio/src/legacy/AudioStreamTrack.cpp
@@ -32,6 +32,7 @@
// Arbitrary and somewhat generous number of bursts.
#define DEFAULT_BURSTS_PER_BUFFER_CAPACITY 8
+static const bool FAST_TRACKS_ENABLED = true;
/*
* Create a stream that uses the AudioTrack.
@@ -69,7 +70,9 @@
samplesPerFrame, channelMask);
// TODO add more performance options
- audio_output_flags_t flags = (audio_output_flags_t) AUDIO_OUTPUT_FLAG_FAST;
+ audio_output_flags_t flags = FAST_TRACKS_ENABLED
+ ? AUDIO_OUTPUT_FLAG_FAST
+ : AUDIO_OUTPUT_FLAG_NONE;
int32_t frameCount = builder.getBufferCapacity();
ALOGD("AudioStreamTrack::open(), requested buffer capacity %d", frameCount);
diff --git a/media/libaaudio/src/utility/AAudioUtilities.cpp b/media/libaaudio/src/utility/AAudioUtilities.cpp
index efbbfc5..5fa228a 100644
--- a/media/libaaudio/src/utility/AAudioUtilities.cpp
+++ b/media/libaaudio/src/utility/AAudioUtilities.cpp
@@ -27,6 +27,11 @@
using namespace android;
+// This is 3 dB, (10^(3/20)), to match the maximum headroom in AudioTrack for float data.
+// It is designed to allow occasional transient peaks.
+#define MAX_HEADROOM (1.41253754f)
+#define MIN_HEADROOM (0 - MAX_HEADROOM)
+
int32_t AAudioConvert_formatToSizeInBytes(aaudio_audio_format_t format) {
int32_t size = AAUDIO_ERROR_ILLEGAL_ARGUMENT;
switch (format) {
@@ -42,24 +47,153 @@
return size;
}
-// TODO This similar to a function in audio_utils. Consider using that instead.
-void AAudioConvert_floatToPcm16(const float *source, int32_t numSamples, int16_t *destination) {
+
+// TODO call clamp16_from_float function in primitives.h
+static inline int16_t clamp16_from_float(float f) {
+ /* Offset is used to expand the valid range of [-1.0, 1.0) into the 16 lsbs of the
+ * floating point significand. The normal shift is 3<<22, but the -15 offset
+ * is used to multiply by 32768.
+ */
+ static const float offset = (float)(3 << (22 - 15));
+ /* zero = (0x10f << 22) = 0x43c00000 (not directly used) */
+ static const int32_t limneg = (0x10f << 22) /*zero*/ - 32768; /* 0x43bf8000 */
+ static const int32_t limpos = (0x10f << 22) /*zero*/ + 32767; /* 0x43c07fff */
+
+ union {
+ float f;
+ int32_t i;
+ } u;
+
+ u.f = f + offset; /* recenter valid range */
+ /* Now the valid range is represented as integers between [limneg, limpos].
+ * Clamp using the fact that float representation (as an integer) is an ordered set.
+ */
+ if (u.i < limneg)
+ u.i = -32768;
+ else if (u.i > limpos)
+ u.i = 32767;
+ return u.i; /* Return lower 16 bits, the part of interest in the significand. */
+}
+
+// Same but without clipping.
+// Convert -1.0f to +1.0f to -32768 to +32767
+static inline int16_t floatToInt16(float f) {
+ static const float offset = (float)(3 << (22 - 15));
+ union {
+ float f;
+ int32_t i;
+ } u;
+ u.f = f + offset; /* recenter valid range */
+ return u.i; /* Return lower 16 bits, the part of interest in the significand. */
+}
+
+static float clipAndClampFloatToPcm16(float sample, float scaler) {
+ // Clip to valid range of a float sample to prevent excessive volume.
+ if (sample > MAX_HEADROOM) sample = MAX_HEADROOM;
+ else if (sample < MIN_HEADROOM) sample = MIN_HEADROOM;
+
+ // Scale and convert to a short.
+ float fval = sample * scaler;
+ return clamp16_from_float(fval);
+}
+
+void AAudioConvert_floatToPcm16(const float *source,
+ int16_t *destination,
+ int32_t numSamples,
+ float amplitude) {
+ float scaler = amplitude;
for (int i = 0; i < numSamples; i++) {
- float fval = source[i];
- fval += 1.0; // to avoid discontinuity at 0.0 caused by truncation
- fval *= 32768.0f;
- int32_t sample = (int32_t) fval;
- // clip to 16-bit range
- if (sample < 0) sample = 0;
- else if (sample > 0x0FFFF) sample = 0x0FFFF;
- sample -= 32768; // center at zero
- destination[i] = (int16_t) sample;
+ float sample = *source++;
+ *destination++ = clipAndClampFloatToPcm16(sample, scaler);
}
}
-void AAudioConvert_pcm16ToFloat(const int16_t *source, int32_t numSamples, float *destination) {
+void AAudioConvert_floatToPcm16(const float *source,
+ int16_t *destination,
+ int32_t numFrames,
+ int32_t samplesPerFrame,
+ float amplitude1,
+ float amplitude2) {
+ float scaler = amplitude1;
+ // divide by numFrames so that we almost reach amplitude2
+ float delta = (amplitude2 - amplitude1) / numFrames;
+ for (int frameIndex = 0; frameIndex < numFrames; frameIndex++) {
+ for (int sampleIndex = 0; sampleIndex < samplesPerFrame; sampleIndex++) {
+ float sample = *source++;
+ *destination++ = clipAndClampFloatToPcm16(sample, scaler);
+ }
+ scaler += delta;
+ }
+}
+
+#define SHORT_SCALE 32768
+
+void AAudioConvert_pcm16ToFloat(const int16_t *source,
+ float *destination,
+ int32_t numSamples,
+ float amplitude) {
+ float scaler = amplitude / SHORT_SCALE;
for (int i = 0; i < numSamples; i++) {
- destination[i] = source[i] * (1.0f / 32768.0f);
+ destination[i] = source[i] * scaler;
+ }
+}
+
+// This code assumes amplitude1 and amplitude2 are between 0.0 and 1.0
+void AAudioConvert_pcm16ToFloat(const int16_t *source,
+ float *destination,
+ int32_t numFrames,
+ int32_t samplesPerFrame,
+ float amplitude1,
+ float amplitude2) {
+ float scaler = amplitude1 / SHORT_SCALE;
+ float delta = (amplitude2 - amplitude1) / (SHORT_SCALE * (float) numFrames);
+ for (int frameIndex = 0; frameIndex < numFrames; frameIndex++) {
+ for (int sampleIndex = 0; sampleIndex < samplesPerFrame; sampleIndex++) {
+ *destination++ = *source++ * scaler;
+ }
+ scaler += delta;
+ }
+}
+
+// This code assumes amplitude1 and amplitude2 are between 0.0 and 1.0
+void AAudio_linearRamp(const float *source,
+ float *destination,
+ int32_t numFrames,
+ int32_t samplesPerFrame,
+ float amplitude1,
+ float amplitude2) {
+ float scaler = amplitude1;
+ float delta = (amplitude2 - amplitude1) / numFrames;
+ for (int frameIndex = 0; frameIndex < numFrames; frameIndex++) {
+ for (int sampleIndex = 0; sampleIndex < samplesPerFrame; sampleIndex++) {
+ float sample = *source++;
+
+ // Clip to valid range of a float sample to prevent excessive volume.
+ if (sample > MAX_HEADROOM) sample = MAX_HEADROOM;
+ else if (sample < MIN_HEADROOM) sample = MIN_HEADROOM;
+
+ *destination++ = sample * scaler;
+ }
+ scaler += delta;
+ }
+}
+
+// This code assumes amplitude1 and amplitude2 are between 0.0 and 1.0
+void AAudio_linearRamp(const int16_t *source,
+ int16_t *destination,
+ int32_t numFrames,
+ int32_t samplesPerFrame,
+ float amplitude1,
+ float amplitude2) {
+ float scaler = amplitude1 / SHORT_SCALE;
+ float delta = (amplitude2 - amplitude1) / (SHORT_SCALE * (float) numFrames);
+ for (int frameIndex = 0; frameIndex < numFrames; frameIndex++) {
+ for (int sampleIndex = 0; sampleIndex < samplesPerFrame; sampleIndex++) {
+ // No need to clip because int16_t range is inherently limited.
+ float sample = *source++ * scaler;
+ *destination++ = floatToInt16(sample);
+ }
+ scaler += delta;
}
}
diff --git a/media/libaaudio/src/utility/AAudioUtilities.h b/media/libaaudio/src/utility/AAudioUtilities.h
index 3dc501e..0078cbb 100644
--- a/media/libaaudio/src/utility/AAudioUtilities.h
+++ b/media/libaaudio/src/utility/AAudioUtilities.h
@@ -35,9 +35,120 @@
*/
aaudio_result_t AAudioConvert_androidToAAudioResult(android::status_t status);
-void AAudioConvert_floatToPcm16(const float *source, int32_t numSamples, int16_t *destination);
+/**
+ * Convert an array of floats to an array of int16_t.
+ *
+ * @param source
+ * @param destination
+ * @param numSamples number of values in the array
+ * @param amplitude level between 0.0 and 1.0
+ */
+void AAudioConvert_floatToPcm16(const float *source,
+ int16_t *destination,
+ int32_t numSamples,
+ float amplitude);
-void AAudioConvert_pcm16ToFloat(const int16_t *source, int32_t numSamples, float *destination);
+/**
+ * Convert floats to int16_t and scale by a linear ramp.
+ *
+ * The ramp stops just short of reaching amplitude2 so that the next
+ * ramp can start at amplitude2 without causing a discontinuity.
+ *
+ * @param source
+ * @param destination
+ * @param numFrames
+ * @param samplesPerFrame AKA number of channels
+ * @param amplitude1 level at start of ramp, between 0.0 and 1.0
+ * @param amplitude2 level past end of ramp, between 0.0 and 1.0
+ */
+void AAudioConvert_floatToPcm16(const float *source,
+ int16_t *destination,
+ int32_t numFrames,
+ int32_t samplesPerFrame,
+ float amplitude1,
+ float amplitude2);
+
+/**
+ * Convert int16_t array to float array ranging from -1.0 to +1.0.
+ * @param source
+ * @param destination
+ * @param numSamples
+ */
+//void AAudioConvert_pcm16ToFloat(const int16_t *source, int32_t numSamples,
+// float *destination);
+
+/**
+ *
+ * Convert int16_t array to float array ranging from +/- amplitude.
+ * @param source
+ * @param destination
+ * @param numSamples
+ * @param amplitude
+ */
+void AAudioConvert_pcm16ToFloat(const int16_t *source,
+ float *destination,
+ int32_t numSamples,
+ float amplitude);
+
+/**
+ * Convert floats to int16_t and scale by a linear ramp.
+ *
+ * The ramp stops just short of reaching amplitude2 so that the next
+ * ramp can start at amplitude2 without causing a discontinuity.
+ *
+ * @param source
+ * @param destination
+ * @param numFrames
+ * @param samplesPerFrame AKA number of channels
+ * @param amplitude1 level at start of ramp, between 0.0 and 1.0
+ * @param amplitude2 level at end of ramp, between 0.0 and 1.0
+ */
+void AAudioConvert_pcm16ToFloat(const int16_t *source,
+ float *destination,
+ int32_t numFrames,
+ int32_t samplesPerFrame,
+ float amplitude1,
+ float amplitude2);
+
+/**
+ * Scale floats by a linear ramp.
+ *
+ * The ramp stops just short of reaching amplitude2 so that the next
+ * ramp can start at amplitude2 without causing a discontinuity.
+ *
+ * @param source
+ * @param destination
+ * @param numFrames
+ * @param samplesPerFrame
+ * @param amplitude1
+ * @param amplitude2
+ */
+void AAudio_linearRamp(const float *source,
+ float *destination,
+ int32_t numFrames,
+ int32_t samplesPerFrame,
+ float amplitude1,
+ float amplitude2);
+
+/**
+ * Scale int16_t's by a linear ramp.
+ *
+ * The ramp stops just short of reaching amplitude2 so that the next
+ * ramp can start at amplitude2 without causing a discontinuity.
+ *
+ * @param source
+ * @param destination
+ * @param numFrames
+ * @param samplesPerFrame
+ * @param amplitude1
+ * @param amplitude2
+ */
+void AAudio_linearRamp(const int16_t *source,
+ int16_t *destination,
+ int32_t numFrames,
+ int32_t samplesPerFrame,
+ float amplitude1,
+ float amplitude2);
/**
* Calculate the number of bytes and prevent numeric overflow.
diff --git a/media/libaaudio/src/utility/LinearRamp.cpp b/media/libaaudio/src/utility/LinearRamp.cpp
new file mode 100644
index 0000000..1714bbf
--- /dev/null
+++ b/media/libaaudio/src/utility/LinearRamp.cpp
@@ -0,0 +1,50 @@
+/*
+ * 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.
+ */
+
+#include "LinearRamp.h"
+
+bool LinearRamp::isRamping() {
+ float target = mTarget.load();
+ if (target != mLevelTo) {
+ // Update target. Continue from previous level.
+ mLevelTo = target;
+ mRemaining = mLengthInFrames;
+ return true;
+ } else {
+ return mRemaining > 0;
+ }
+}
+
+bool LinearRamp::nextSegment(int32_t frames, float *levelFrom, float *levelTo) {
+ bool ramping = isRamping();
+ *levelFrom = mLevelFrom;
+ if (ramping) {
+ float level;
+ if (frames >= mRemaining) {
+ level = mLevelTo;
+ mRemaining = 0;
+ } else {
+ // Interpolate to a point along the full ramp.
+ level = mLevelFrom + (frames * (mLevelTo - mLevelFrom) / mRemaining);
+ mRemaining -= frames;
+ }
+ mLevelFrom = level; // for next ramp
+ *levelTo = level;
+ } else {
+ *levelTo = mLevelTo;
+ }
+ return ramping;
+}
\ No newline at end of file
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
diff --git a/media/libaaudio/tests/Android.mk b/media/libaaudio/tests/Android.mk
index 06c9364..01360b1 100644
--- a/media/libaaudio/tests/Android.mk
+++ b/media/libaaudio/tests/Android.mk
@@ -35,3 +35,15 @@
LOCAL_STATIC_LIBRARIES := libaaudio
LOCAL_MODULE := test_block_adapter
include $(BUILD_NATIVE_TEST)
+
+include $(CLEAR_VARS)
+LOCAL_C_INCLUDES := \
+ $(call include-path-for, audio-utils) \
+ frameworks/av/media/libaaudio/include \
+ frameworks/av/media/libaaudio/src
+LOCAL_SRC_FILES:= test_linear_ramp.cpp
+LOCAL_SHARED_LIBRARIES := libaudioclient libaudioutils libbinder \
+ libcutils liblog libmedia libutils
+LOCAL_STATIC_LIBRARIES := libaaudio
+LOCAL_MODULE := test_linear_ramp
+include $(BUILD_NATIVE_TEST)
diff --git a/media/libaaudio/tests/test_linear_ramp.cpp b/media/libaaudio/tests/test_linear_ramp.cpp
new file mode 100644
index 0000000..5c53982
--- /dev/null
+++ b/media/libaaudio/tests/test_linear_ramp.cpp
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 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.
+ */
+
+#include <iostream>
+
+#include <gtest/gtest.h>
+
+#include "utility/AAudioUtilities.h"
+#include "utility/LinearRamp.h"
+
+
+TEST(test_linear_ramp, linear_ramp_segments) {
+ LinearRamp ramp;
+ const float source[4] = {1.0f, 1.0f, 1.0f, 1.0f };
+ float destination[4] = {1.0f, 1.0f, 1.0f, 1.0f };
+
+ float levelFrom = -1.0f;
+ float levelTo = -1.0f;
+ ramp.setLengthInFrames(8);
+ ramp.setTarget(8.0f);
+
+ ASSERT_EQ(8, ramp.getLengthInFrames());
+
+ bool ramping = ramp.nextSegment(4, &levelFrom, &levelTo);
+ ASSERT_EQ(1, ramping);
+ ASSERT_EQ(0.0f, levelFrom);
+ ASSERT_EQ(4.0f, levelTo);
+
+ AAudio_linearRamp(source, destination, 4, 1, levelFrom, levelTo);
+ ASSERT_EQ(0.0f, destination[0]);
+ ASSERT_EQ(1.0f, destination[1]);
+ ASSERT_EQ(2.0f, destination[2]);
+ ASSERT_EQ(3.0f, destination[3]);
+
+ ramping = ramp.nextSegment(4, &levelFrom, &levelTo);
+ ASSERT_EQ(1, ramping);
+ ASSERT_EQ(4.0f, levelFrom);
+ ASSERT_EQ(8.0f, levelTo);
+
+ AAudio_linearRamp(source, destination, 4, 1, levelFrom, levelTo);
+ ASSERT_EQ(4.0f, destination[0]);
+ ASSERT_EQ(5.0f, destination[1]);
+ ASSERT_EQ(6.0f, destination[2]);
+ ASSERT_EQ(7.0f, destination[3]);
+
+ ramping = ramp.nextSegment(4, &levelFrom, &levelTo);
+ ASSERT_EQ(0, ramping);
+ ASSERT_EQ(8.0f, levelFrom);
+ ASSERT_EQ(8.0f, levelTo);
+
+ AAudio_linearRamp(source, destination, 4, 1, levelFrom, levelTo);
+ ASSERT_EQ(8.0f, destination[0]);
+ ASSERT_EQ(8.0f, destination[1]);
+ ASSERT_EQ(8.0f, destination[2]);
+ ASSERT_EQ(8.0f, destination[3]);
+
+};
+
+
+TEST(test_linear_ramp, linear_ramp_forced) {
+ LinearRamp ramp;
+ const float source[4] = {1.0f, 1.0f, 1.0f, 1.0f };
+ float destination[4] = {1.0f, 1.0f, 1.0f, 1.0f };
+
+ float levelFrom = -1.0f;
+ float levelTo = -1.0f;
+ ramp.setLengthInFrames(4);
+ ramp.setTarget(8.0f);
+ ramp.forceCurrent(4.0f);
+ ASSERT_EQ(4.0f, ramp.getCurrent());
+
+ bool ramping = ramp.nextSegment(4, &levelFrom, &levelTo);
+ ASSERT_EQ(1, ramping);
+ ASSERT_EQ(4.0f, levelFrom);
+ ASSERT_EQ(8.0f, levelTo);
+
+ AAudio_linearRamp(source, destination, 4, 1, levelFrom, levelTo);
+ ASSERT_EQ(4.0f, destination[0]);
+ ASSERT_EQ(5.0f, destination[1]);
+ ASSERT_EQ(6.0f, destination[2]);
+ ASSERT_EQ(7.0f, destination[3]);
+
+ ramping = ramp.nextSegment(4, &levelFrom, &levelTo);
+ ASSERT_EQ(0, ramping);
+ ASSERT_EQ(8.0f, levelFrom);
+ ASSERT_EQ(8.0f, levelTo);
+
+ AAudio_linearRamp(source, destination, 4, 1, levelFrom, levelTo);
+ ASSERT_EQ(8.0f, destination[0]);
+ ASSERT_EQ(8.0f, destination[1]);
+ ASSERT_EQ(8.0f, destination[2]);
+ ASSERT_EQ(8.0f, destination[3]);
+
+};
+
diff --git a/services/oboeservice/AAudioServiceEndpoint.cpp b/services/oboeservice/AAudioServiceEndpoint.cpp
index b197798..d3e182a 100644
--- a/services/oboeservice/AAudioServiceEndpoint.cpp
+++ b/services/oboeservice/AAudioServiceEndpoint.cpp
@@ -46,6 +46,7 @@
// Use 2 for "double buffered"
#define BUFFER_SIZE_IN_BURSTS 2
+#define BURSTS_PER_MIX_LOOP 1
// The mStreamInternal will use a service interface that does not go through Binder.
AAudioServiceEndpoint::AAudioServiceEndpoint(AAudioService &audioService)