Add floating point volume handling to AudioMixer
Use floating point volume in AudioMixer mixing when floating
point input is used with the new mixer engine.
AudioResampler is updated to take floating point volume to match.
Both legacy integer and floating point mixer engines work.
For now, integer volume is used when the new mixer engine
runs in integer input mode, for backward compatibility with
the legacy mixer. The new mixer engine will generally run in
floating point input mode. When the legacy path is removed,
the integer volumes will be removed.
Change-Id: I79e80c292ae7c8b8bdd0aa371a1b2c3a1b618290
diff --git a/services/audioflinger/Android.mk b/services/audioflinger/Android.mk
index 0bdf5a3..697fb37 100644
--- a/services/audioflinger/Android.mk
+++ b/services/audioflinger/Android.mk
@@ -115,6 +115,9 @@
AudioResamplerSinc.cpp.arm \
AudioResamplerDyn.cpp.arm
+LOCAL_C_INCLUDES := \
+ $(call include-path-for, audio-utils)
+
LOCAL_SHARED_LIBRARIES := \
libcutils \
libdl \
diff --git a/services/audioflinger/AudioMixer.cpp b/services/audioflinger/AudioMixer.cpp
index 4dbbc43..e57cb8a 100644
--- a/services/audioflinger/AudioMixer.cpp
+++ b/services/audioflinger/AudioMixer.cpp
@@ -22,6 +22,7 @@
#include <stdint.h>
#include <string.h>
#include <stdlib.h>
+#include <math.h>
#include <sys/types.h>
#include <utils/Errors.h>
@@ -293,17 +294,32 @@
// assume default parameters for the track, except where noted below
track_t* t = &mState.tracks[n];
t->needs = 0;
+
+ // Integer volume.
+ // Currently integer volume is kept for the legacy integer mixer.
+ // Will be removed when the legacy mixer path is removed.
t->volume[0] = UNITY_GAIN_INT;
t->volume[1] = UNITY_GAIN_INT;
- // no initialization needed
- // t->prevVolume[0]
- // t->prevVolume[1]
+ t->prevVolume[0] = UNITY_GAIN_INT << 16;
+ t->prevVolume[1] = UNITY_GAIN_INT << 16;
t->volumeInc[0] = 0;
t->volumeInc[1] = 0;
t->auxLevel = 0;
t->auxInc = 0;
+ t->prevAuxLevel = 0;
+
+ // Floating point volume.
+ t->mVolume[0] = UNITY_GAIN_FLOAT;
+ t->mVolume[1] = UNITY_GAIN_FLOAT;
+ t->mPrevVolume[0] = UNITY_GAIN_FLOAT;
+ t->mPrevVolume[1] = UNITY_GAIN_FLOAT;
+ t->mVolumeInc[0] = 0.;
+ t->mVolumeInc[1] = 0.;
+ t->mAuxLevel = 0.;
+ t->mAuxInc = 0.;
+ t->mPrevAuxLevel = 0.;
+
// no initialization needed
- // t->prevAuxLevel
// t->frameCount
t->channelCount = audio_channel_count_from_out_mask(channelMask);
t->enabled = false;
@@ -574,39 +590,68 @@
/* Sets the volume ramp variables for the AudioMixer.
*
- * The volume ramp variables are used to transition between the previous
- * volume to the target volume. The duration of the transition is
- * set by ramp, which is either 0 for immediate, or typically one state
- * framecount period.
+ * The volume ramp variables are used to transition from the previous
+ * volume to the set volume. ramp controls the duration of the transition.
+ * Its value is typically one state framecount period, but may also be 0,
+ * meaning "immediate."
*
- * @param newFloatValue new volume target in float [0.0, 1.0].
- * @param ramp number of frames to increment over. ramp is 0 if the volume
- * should be set immediately.
- * @param volume reference to the U4.12 target volume, set on return.
- * @param prevVolume reference to the U4.27 previous volume, set on return.
- * @param volumeInc reference to the increment per output audio frame, set on return.
+ * FIXME: 1) Volume ramp is enabled only if there is a nonzero integer increment
+ * even if there is a nonzero floating point increment (in that case, the volume
+ * change is immediate). This restriction should be changed when the legacy mixer
+ * is removed (see #2).
+ * FIXME: 2) Integer volume variables are used for Legacy mixing and should be removed
+ * when no longer needed.
+ *
+ * @param newVolume set volume target in floating point [0.0, 1.0].
+ * @param ramp number of frames to increment over. if ramp is 0, the volume
+ * should be set immediately. Currently ramp should not exceed 65535 (frames).
+ * @param pIntSetVolume pointer to the U4.12 integer target volume, set on return.
+ * @param pIntPrevVolume pointer to the U4.28 integer previous volume, set on return.
+ * @param pIntVolumeInc pointer to the U4.28 increment per output audio frame, set on return.
+ * @param pSetVolume pointer to the float target volume, set on return.
+ * @param pPrevVolume pointer to the float previous volume, set on return.
+ * @param pVolumeInc pointer to the float increment per output audio frame, set on return.
* @return true if the volume has changed, false if volume is same.
*/
-static inline bool setVolumeRampVariables(float newFloatValue, int32_t ramp,
- int16_t &volume, int32_t &prevVolume, int32_t &volumeInc) {
- int32_t newValue = newFloatValue * AudioMixer::UNITY_GAIN_INT;
- if (newValue > AudioMixer::UNITY_GAIN_INT) {
- newValue = AudioMixer::UNITY_GAIN_INT;
- } else if (newValue < 0) {
- ALOGE("negative volume %.7g", newFloatValue);
- newValue = 0; // should never happen, but for safety check.
- }
- if (newValue == volume) {
+static inline bool setVolumeRampVariables(float newVolume, int32_t ramp,
+ int16_t *pIntSetVolume, int32_t *pIntPrevVolume, int32_t *pIntVolumeInc,
+ float *pSetVolume, float *pPrevVolume, float *pVolumeInc) {
+ if (newVolume == *pSetVolume) {
return false;
}
+ /* set the floating point volume variables */
if (ramp != 0) {
- volumeInc = ((newValue - volume) << 16) / ramp;
- prevVolume = (volumeInc == 0 ? newValue : volume) << 16;
+ *pVolumeInc = (newVolume - *pSetVolume) / ramp;
+ *pPrevVolume = *pSetVolume;
} else {
- volumeInc = 0;
- prevVolume = newValue << 16;
+ *pVolumeInc = 0;
+ *pPrevVolume = newVolume;
}
- volume = newValue;
+ *pSetVolume = newVolume;
+
+ /* set the legacy integer volume variables */
+ int32_t intVolume = newVolume * AudioMixer::UNITY_GAIN_INT;
+ if (intVolume > AudioMixer::UNITY_GAIN_INT) {
+ intVolume = AudioMixer::UNITY_GAIN_INT;
+ } else if (intVolume < 0) {
+ ALOGE("negative volume %.7g", newVolume);
+ intVolume = 0; // should never happen, but for safety check.
+ }
+ if (intVolume == *pIntSetVolume) {
+ *pIntVolumeInc = 0;
+ /* TODO: integer/float workaround: ignore floating volume ramp */
+ *pVolumeInc = 0;
+ *pPrevVolume = newVolume;
+ return true;
+ }
+ if (ramp != 0) {
+ *pIntVolumeInc = ((intVolume - *pIntSetVolume) << 16) / ramp;
+ *pIntPrevVolume = (*pIntVolumeInc == 0 ? intVolume : *pIntSetVolume) << 16;
+ } else {
+ *pIntVolumeInc = 0;
+ *pIntPrevVolume = intVolume << 16;
+ }
+ *pIntSetVolume = intVolume;
return true;
}
@@ -716,8 +761,10 @@
case VOLUME1:
if (setVolumeRampVariables(*reinterpret_cast<float*>(value),
target == RAMP_VOLUME ? mState.frameCount : 0,
- track.volume[param - VOLUME0], track.prevVolume[param - VOLUME0],
- track.volumeInc[param - VOLUME0])) {
+ &track.volume[param - VOLUME0], &track.prevVolume[param - VOLUME0],
+ &track.volumeInc[param - VOLUME0],
+ &track.mVolume[param - VOLUME0], &track.mPrevVolume[param - VOLUME0],
+ &track.mVolumeInc[param - VOLUME0])) {
ALOGV("setParameter(%s, VOLUME%d: %04x)",
target == VOLUME ? "VOLUME" : "RAMP_VOLUME", param - VOLUME0,
track.volume[param - VOLUME0]);
@@ -725,10 +772,10 @@
}
break;
case AUXLEVEL:
- //ALOG_ASSERT(0 <= valueInt && valueInt <= MAX_GAIN_INT, "bad aux level %d", valueInt);
if (setVolumeRampVariables(*reinterpret_cast<float*>(value),
target == RAMP_VOLUME ? mState.frameCount : 0,
- track.auxLevel, track.prevAuxLevel, track.auxInc)) {
+ &track.auxLevel, &track.prevAuxLevel, &track.auxInc,
+ &track.mAuxLevel, &track.mPrevAuxLevel, &track.mAuxInc)) {
ALOGV("setParameter(%s, AUXLEVEL: %04x)",
target == VOLUME ? "VOLUME" : "RAMP_VOLUME", track.auxLevel);
invalidateState(1 << name);
@@ -777,21 +824,58 @@
return false;
}
-inline
-void AudioMixer::track_t::adjustVolumeRamp(bool aux)
+/* Checks to see if the volume ramp has completed and clears the increment
+ * variables appropriately.
+ *
+ * FIXME: There is code to handle int/float ramp variable switchover should it not
+ * complete within a mixer buffer processing call, but it is preferred to avoid switchover
+ * due to precision issues. The switchover code is included for legacy code purposes
+ * and can be removed once the integer volume is removed.
+ *
+ * It is not sufficient to clear only the volumeInc integer variable because
+ * if one channel requires ramping, all channels are ramped.
+ *
+ * There is a bit of duplicated code here, but it keeps backward compatibility.
+ */
+inline void AudioMixer::track_t::adjustVolumeRamp(bool aux, bool useFloat)
{
- for (uint32_t i=0 ; i<MAX_NUM_CHANNELS ; i++) {
- if (((volumeInc[i]>0) && (((prevVolume[i]+volumeInc[i])>>16) >= volume[i])) ||
- ((volumeInc[i]<0) && (((prevVolume[i]+volumeInc[i])>>16) <= volume[i]))) {
- volumeInc[i] = 0;
- prevVolume[i] = volume[i]<<16;
+ if (useFloat) {
+ for (uint32_t i=0 ; i<MAX_NUM_CHANNELS ; i++) {
+ if (mVolumeInc[i] != 0 && fabs(mVolume[i] - mPrevVolume[i]) <= fabs(mVolumeInc[i])) {
+ volumeInc[i] = 0;
+ prevVolume[i] = volume[i] << 16;
+ mVolumeInc[i] = 0.;
+ mPrevVolume[i] = mVolume[i];
+
+ } else {
+ //ALOGV("ramp: %f %f %f", mVolume[i], mPrevVolume[i], mVolumeInc[i]);
+ prevVolume[i] = u4_28_from_float(mPrevVolume[i]);
+ }
+ }
+ } else {
+ for (uint32_t i=0 ; i<MAX_NUM_CHANNELS ; i++) {
+ if (((volumeInc[i]>0) && (((prevVolume[i]+volumeInc[i])>>16) >= volume[i])) ||
+ ((volumeInc[i]<0) && (((prevVolume[i]+volumeInc[i])>>16) <= volume[i]))) {
+ volumeInc[i] = 0;
+ prevVolume[i] = volume[i] << 16;
+ mVolumeInc[i] = 0.;
+ mPrevVolume[i] = mVolume[i];
+ } else {
+ //ALOGV("ramp: %d %d %d", volume[i] << 16, prevVolume[i], volumeInc[i]);
+ mPrevVolume[i] = float_from_u4_28(prevVolume[i]);
+ }
}
}
+ /* TODO: aux is always integer regardless of output buffer type */
if (aux) {
if (((auxInc>0) && (((prevAuxLevel+auxInc)>>16) >= auxLevel)) ||
- ((auxInc<0) && (((prevAuxLevel+auxInc)>>16) <= auxLevel))) {
+ ((auxInc<0) && (((prevAuxLevel+auxInc)>>16) <= auxLevel))) {
auxInc = 0;
- prevAuxLevel = auxLevel<<16;
+ prevAuxLevel = auxLevel << 16;
+ mAuxInc = 0.;
+ mPrevAuxLevel = mAuxLevel;
+ } else {
+ //ALOGV("aux ramp: %d %d %d", auxLevel << 16, prevAuxLevel, auxInc);
}
}
}
@@ -985,7 +1069,7 @@
// always resample with unity gain when sending to auxiliary buffer to be able
// to apply send level after resampling
// TODO: modify each resampler to support aux channel?
- t->resampler->setVolume(UNITY_GAIN_INT, UNITY_GAIN_INT);
+ t->resampler->setVolume(UNITY_GAIN_FLOAT, UNITY_GAIN_FLOAT);
memset(temp, 0, outFrameCount * MAX_NUM_CHANNELS * sizeof(int32_t));
t->resampler->resample(temp, outFrameCount, t->bufferProvider);
if (CC_UNLIKELY(t->volumeInc[0]|t->volumeInc[1]|t->auxInc)) {
@@ -995,7 +1079,7 @@
}
} else {
if (CC_UNLIKELY(t->volumeInc[0]|t->volumeInc[1])) {
- t->resampler->setVolume(UNITY_GAIN_INT, UNITY_GAIN_INT);
+ t->resampler->setVolume(UNITY_GAIN_FLOAT, UNITY_GAIN_FLOAT);
memset(temp, 0, outFrameCount * MAX_NUM_CHANNELS * sizeof(int32_t));
t->resampler->resample(temp, outFrameCount, t->bufferProvider);
volumeRampStereo(t, out, outFrameCount, temp, aux);
@@ -1003,7 +1087,7 @@
// constant gain
else {
- t->resampler->setVolume(t->volume[0], t->volume[1]);
+ t->resampler->setVolume(t->mVolume[0], t->mVolume[1]);
t->resampler->resample(out, outFrameCount, t->bufferProvider);
}
}
@@ -1721,6 +1805,36 @@
ALOGW_IF(!sIsMultichannelCapable, "unable to find downmix effect");
}
+template <int MIXTYPE, int NCHAN, bool USEFLOATVOL, bool ADJUSTVOL,
+ typename TO, typename TI, typename TA>
+void AudioMixer::volumeMix(TO *out, size_t outFrames,
+ const TI *in, TA *aux, bool ramp, AudioMixer::track_t *t)
+{
+ if (USEFLOATVOL) {
+ if (ramp) {
+ volumeRampMulti<MIXTYPE, NCHAN>(out, outFrames, in, aux,
+ t->mPrevVolume, t->mVolumeInc, &t->prevAuxLevel, t->auxInc);
+ if (ADJUSTVOL) {
+ t->adjustVolumeRamp(aux != NULL, true);
+ }
+ } else {
+ volumeMulti<MIXTYPE, NCHAN>(out, outFrames, in, aux,
+ t->mVolume, t->auxLevel);
+ }
+ } else {
+ if (ramp) {
+ volumeRampMulti<MIXTYPE, NCHAN>(out, outFrames, in, aux,
+ t->prevVolume, t->volumeInc, &t->prevAuxLevel, t->auxInc);
+ if (ADJUSTVOL) {
+ t->adjustVolumeRamp(aux != NULL);
+ }
+ } else {
+ volumeMulti<MIXTYPE, NCHAN>(out, outFrames, in, aux,
+ t->volume, t->auxLevel);
+ }
+ }
+}
+
/* This process hook is called when there is a single track without
* aux buffer, volume ramp, or resampling.
* TODO: Update the hook selection: this can properly handle aux and ramp.
@@ -1757,13 +1871,9 @@
}
const size_t outFrames = b.frameCount;
- if (ramp) {
- volumeRampMulti<MIXTYPE_MULTI_SAVEONLY, NCHAN>(out, outFrames, in, aux,
- t->prevVolume, t->volumeInc, &t->prevAuxLevel, t->auxInc);
- } else {
- volumeMulti<MIXTYPE_MULTI_SAVEONLY, NCHAN>(out, outFrames, in, aux,
- t->volume, t->auxLevel);
- }
+ volumeMix<MIXTYPE, NCHAN, is_same<TI, float>::value, false> (out,
+ outFrames, in, aux, ramp, t);
+
out += outFrames * NCHAN;
if (aux != NULL) {
aux += NCHAN;
@@ -1774,7 +1884,7 @@
t->bufferProvider->releaseBuffer(&b);
}
if (ramp) {
- t->adjustVolumeRamp(aux != NULL);
+ t->adjustVolumeRamp(aux != NULL, is_same<TI, float>::value);
}
}
@@ -1792,19 +1902,15 @@
// if ramp: resample with unity gain to temp buffer and scale/mix in 2nd step.
// if aux != NULL: resample with unity gain to temp buffer then apply send level.
- t->resampler->setVolume(UNITY_GAIN_INT, UNITY_GAIN_INT);
+ t->resampler->setVolume(UNITY_GAIN_FLOAT, UNITY_GAIN_FLOAT);
memset(temp, 0, outFrameCount * NCHAN * sizeof(TO));
t->resampler->resample((int32_t*)temp, outFrameCount, t->bufferProvider);
- if (ramp) {
- volumeRampMulti<MIXTYPE_MULTI, NCHAN>(out, outFrameCount, temp, aux,
- t->prevVolume, t->volumeInc, &t->prevAuxLevel, t->auxInc);
- t->adjustVolumeRamp(aux != NULL);
- } else {
- volumeMulti<MIXTYPE_MULTI, NCHAN>(out, outFrameCount, temp, aux,
- t->volume, t->auxLevel);
- }
+
+ volumeMix<MIXTYPE, NCHAN, is_same<TI, float>::value, true>(out, outFrameCount,
+ temp, aux, ramp, t);
+
} else { // constant volume gain
- t->resampler->setVolume(t->volume[0], t->volume[1]);
+ t->resampler->setVolume(t->mVolume[0], t->mVolume[1]);
t->resampler->resample((int32_t*)out, outFrameCount, t->bufferProvider);
}
}
@@ -1819,13 +1925,9 @@
ALOGVV("track__NoResample\n");
const TI *in = static_cast<const TI *>(t->in);
- if (t->needsRamp()) {
- volumeRampMulti<MIXTYPE, NCHAN>(out, frameCount, in, aux,
- t->prevVolume, t->volumeInc, &t->prevAuxLevel, t->auxInc);
- t->adjustVolumeRamp(aux != NULL);
- } else {
- volumeMulti<MIXTYPE, NCHAN>(out, frameCount, in, aux, t->volume, t->auxLevel);
- }
+ volumeMix<MIXTYPE, NCHAN, is_same<TI, float>::value, true>(out, frameCount,
+ in, aux, t->needsRamp(), t);
+
// MIXTYPE_MONOEXPAND reads a single input channel and expands to NCHAN output channels.
// MIXTYPE_MULTI reads NCHAN input channels and places to NCHAN output channels.
in += (MIXTYPE == MIXTYPE_MONOEXPAND) ? frameCount : frameCount * NCHAN;
diff --git a/services/audioflinger/AudioMixer.h b/services/audioflinger/AudioMixer.h
index e6de00c..a9f4761 100644
--- a/services/audioflinger/AudioMixer.h
+++ b/services/audioflinger/AudioMixer.h
@@ -163,8 +163,9 @@
struct track_t {
uint32_t needs;
+ // TODO: Eventually remove legacy integer volume settings
union {
- int16_t volume[MAX_NUM_CHANNELS]; // [0]3.12 fixed point
+ int16_t volume[MAX_NUM_CHANNELS]; // U4.12 fixed point (top bit should be zero)
int32_t volumeRL;
};
@@ -217,7 +218,13 @@
audio_format_t mMixerInFormat; // mix internal format AUDIO_FORMAT_PCM_(FLOAT|16_BIT)
// each track must be converted to this format.
- int32_t mUnused[1]; // alignment padding
+ float mVolume[MAX_NUM_CHANNELS]; // floating point set volume
+ float mPrevVolume[MAX_NUM_CHANNELS]; // floating point previous volume
+ float mVolumeInc[MAX_NUM_CHANNELS]; // floating point volume increment
+
+ float mAuxLevel; // floating point set aux level
+ float mPrevAuxLevel; // floating point prev aux level
+ float mAuxInc; // floating point aux increment
// 16-byte boundary
@@ -225,7 +232,7 @@
bool setResampler(uint32_t sampleRate, uint32_t devSampleRate);
bool doesResample() const { return resampler != NULL; }
void resetResampler() { if (resampler != NULL) resampler->reset(); }
- void adjustVolumeRamp(bool aux);
+ void adjustVolumeRamp(bool aux, bool useFloat = false);
size_t getUnreleasedFrames() const { return resampler != NULL ?
resampler->getUnreleasedFrames() : 0; };
};
@@ -349,6 +356,22 @@
static pthread_once_t sOnceControl;
static void sInitRoutine();
+ /* multi-format volume mixing function (calls template functions
+ * in AudioMixerOps.h). The template parameters are as follows:
+ *
+ * MIXTYPE (see AudioMixerOps.h MIXTYPE_* enumeration)
+ * NCHAN (number of channels, 2 for now)
+ * USEFLOATVOL (set to true if float volume is used)
+ * ADJUSTVOL (set to true if volume ramp parameters needs adjustment afterwards)
+ * TO: int32_t (Q4.27) or float
+ * TI: int32_t (Q4.27) or int16_t (Q0.15) or float
+ * TA: int32_t (Q4.27)
+ */
+ template <int MIXTYPE, int NCHAN, bool USEFLOATVOL, bool ADJUSTVOL,
+ typename TO, typename TI, typename TA>
+ static void volumeMix(TO *out, size_t outFrames,
+ const TI *in, TA *aux, bool ramp, AudioMixer::track_t *t);
+
// multi-format process hooks
template <int MIXTYPE, int NCHAN, typename TO, typename TI, typename TA>
static void process_NoResampleOneTrack(state_t* state, int64_t pts);
diff --git a/services/audioflinger/AudioMixerOps.h b/services/audioflinger/AudioMixerOps.h
index de92946..ad739ff 100644
--- a/services/audioflinger/AudioMixerOps.h
+++ b/services/audioflinger/AudioMixerOps.h
@@ -136,6 +136,46 @@
return clamp16(MixMul<int32_t, int32_t, int32_t>(value, volume) >> 12);
}
+/* Required for floating point volume. Some are needed for compilation but
+ * are not needed in execution and should be removed from the final build by
+ * an optimizing compiler.
+ */
+template <>
+inline float MixMul<float, float, float>(float value, float volume) {
+ return value * volume;
+}
+
+template <>
+inline float MixMul<float, int16_t, float>(int16_t value, float volume) {
+ static const float float_from_q_15 = 1. / (1 << 15);
+ return value * volume * float_from_q_15;
+}
+
+template <>
+inline int32_t MixMul<int32_t, int32_t, float>(int32_t value, float volume) {
+ LOG_ALWAYS_FATAL("MixMul<int32_t, int32_t, float> Runtime Should not be here");
+ return value * volume;
+}
+
+template <>
+inline int32_t MixMul<int32_t, int16_t, float>(int16_t value, float volume) {
+ LOG_ALWAYS_FATAL("MixMul<int32_t, int16_t, float> Runtime Should not be here");
+ static const float u4_12_from_float = (1 << 12);
+ return value * volume * u4_12_from_float;
+}
+
+template <>
+inline int16_t MixMul<int16_t, int16_t, float>(int16_t value, float volume) {
+ LOG_ALWAYS_FATAL("MixMul<int16_t, int16_t, float> Runtime Should not be here");
+ return value * volume;
+}
+
+template <>
+inline int16_t MixMul<int16_t, float, float>(float value, float volume) {
+ static const float q_15_from_float = (1 << 15);
+ return value * volume * q_15_from_float;
+}
+
/*
* MixAccum is used to add into an accumulator register of a possibly different
* type. The TO and TI types are the same as MixMul.
diff --git a/services/audioflinger/AudioResampler.cpp b/services/audioflinger/AudioResampler.cpp
index 38c9061..1f7a613 100644
--- a/services/audioflinger/AudioResampler.cpp
+++ b/services/audioflinger/AudioResampler.cpp
@@ -22,6 +22,7 @@
#include <sys/types.h>
#include <cutils/log.h>
#include <cutils/properties.h>
+#include <audio_utils/primitives.h>
#include "AudioResampler.h"
#include "AudioResamplerSinc.h"
#include "AudioResamplerCubic.h"
@@ -266,8 +267,9 @@
mPhaseFraction(0), mLocalTimeFreq(0),
mPTS(AudioBufferProvider::kInvalidPTS), mQuality(quality) {
+ const int maxChannels = quality < DYN_LOW_QUALITY ? 2 : 8;
if (inChannelCount < 1
- || inChannelCount > (quality < DYN_LOW_QUALITY ? 2 : 8)) {
+ || inChannelCount > maxChannels) {
LOG_ALWAYS_FATAL("Unsupported sample format %d quality %d channels",
quality, inChannelCount);
}
@@ -297,10 +299,12 @@
mPhaseIncrement = (uint32_t)((kPhaseMultiplier * inSampleRate) / mSampleRate);
}
-void AudioResampler::setVolume(int16_t left, int16_t right) {
+void AudioResampler::setVolume(float left, float right) {
// TODO: Implement anti-zipper filter
- mVolume[0] = left;
- mVolume[1] = right;
+ // convert to U4.12 for internal integer use (round down)
+ // integer volume values are clamped to 0 to UNITY_GAIN.
+ mVolume[0] = u4_12_from_float(clampFloatVol(left));
+ mVolume[1] = u4_12_from_float(clampFloatVol(right));
}
void AudioResampler::setLocalTimeFreq(uint64_t freq) {
diff --git a/services/audioflinger/AudioResampler.h b/services/audioflinger/AudioResampler.h
index be747f6..cdc6d92 100644
--- a/services/audioflinger/AudioResampler.h
+++ b/services/audioflinger/AudioResampler.h
@@ -47,6 +47,8 @@
DYN_HIGH_QUALITY=7,
};
+ static const float UNITY_GAIN_FLOAT = 1.0f;
+
static AudioResampler* create(audio_format_t format, int inChannelCount,
int32_t sampleRate, src_quality quality=DEFAULT_QUALITY);
@@ -54,7 +56,7 @@
virtual void init() = 0;
virtual void setSampleRate(int32_t inSampleRate);
- virtual void setVolume(int16_t left, int16_t right);
+ virtual void setVolume(float left, float right);
virtual void setLocalTimeFreq(uint64_t freq);
// set the PTS of the next buffer output by the resampler
@@ -142,6 +144,15 @@
+ (mSampleRate - 1))/mSampleRate;
}
+ inline float clampFloatVol(float volume) {
+ if (volume > UNITY_GAIN_FLOAT) {
+ return UNITY_GAIN_FLOAT;
+ } else if (volume >= 0.) {
+ return volume;
+ }
+ return 0.; // NaN or negative volume maps to 0.
+ }
+
private:
const src_quality mQuality;
diff --git a/services/audioflinger/AudioResamplerDyn.cpp b/services/audioflinger/AudioResamplerDyn.cpp
index 043c803..e201ff8 100644
--- a/services/audioflinger/AudioResamplerDyn.cpp
+++ b/services/audioflinger/AudioResamplerDyn.cpp
@@ -27,6 +27,7 @@
#include <cutils/properties.h>
#include <utils/Debug.h>
#include <utils/Log.h>
+#include <audio_utils/primitives.h>
#include "AudioResamplerFirOps.h" // USE_NEON and USE_INLINE_ASSEMBLY defined here
#include "AudioResamplerFirProcess.h"
@@ -190,17 +191,17 @@
}
template<typename TC, typename TI, typename TO>
-void AudioResamplerDyn<TC, TI, TO>::setVolume(int16_t left, int16_t right)
+void AudioResamplerDyn<TC, TI, TO>::setVolume(float left, float right)
{
AudioResampler::setVolume(left, right);
- // volume is applied on the output type.
if (is_same<TO, float>::value || is_same<TO, double>::value) {
- const TO scale = 1. / (1UL << 12);
- mVolumeSimd[0] = static_cast<TO>(left) * scale;
- mVolumeSimd[1] = static_cast<TO>(right) * scale;
- } else {
- mVolumeSimd[0] = static_cast<int32_t>(left) << 16;
- mVolumeSimd[1] = static_cast<int32_t>(right) << 16;
+ mVolumeSimd[0] = static_cast<TO>(left);
+ mVolumeSimd[1] = static_cast<TO>(right);
+ } else { // integer requires scaling to U4_28 (rounding down)
+ // integer volumes are clamped to 0 to UNITY_GAIN so there
+ // are no issues with signed overflow.
+ mVolumeSimd[0] = u4_28_from_float(clampFloatVol(left));
+ mVolumeSimd[1] = u4_28_from_float(clampFloatVol(right));
}
}
@@ -410,7 +411,7 @@
// Note: A stride of 2 is achieved with non-SIMD processing.
int stride = ((c.mHalfNumCoefs & 7) == 0) ? 16 : 2;
LOG_ALWAYS_FATAL_IF(stride < 16, "Resampler stride must be 16 or more");
- LOG_ALWAYS_FATAL_IF(mChannelCount > 8 || mChannelCount < 1,
+ LOG_ALWAYS_FATAL_IF(mChannelCount < 1 || mChannelCount > 8,
"Resampler channels(%d) must be between 1 to 8", mChannelCount);
// stride 16 (falls back to stride 2 for machines that do not support NEON)
if (locked) {
diff --git a/services/audioflinger/AudioResamplerDyn.h b/services/audioflinger/AudioResamplerDyn.h
index 3044bfb..e886a68 100644
--- a/services/audioflinger/AudioResamplerDyn.h
+++ b/services/audioflinger/AudioResamplerDyn.h
@@ -50,7 +50,7 @@
virtual void setSampleRate(int32_t inSampleRate);
- virtual void setVolume(int16_t left, int16_t right);
+ virtual void setVolume(float left, float right);
virtual void resample(int32_t* out, size_t outFrameCount,
AudioBufferProvider* provider);
diff --git a/services/audioflinger/AudioResamplerSinc.cpp b/services/audioflinger/AudioResamplerSinc.cpp
index 60ff88e..d03e578 100644
--- a/services/audioflinger/AudioResamplerSinc.cpp
+++ b/services/audioflinger/AudioResamplerSinc.cpp
@@ -27,6 +27,7 @@
#include <cutils/properties.h>
#include <utils/Log.h>
+#include <audio_utils/primitives.h>
#include "AudioResamplerSinc.h"
@@ -500,10 +501,12 @@
mRingFull = mImpulse + (numCoefs+1)*mChannelCount;
}
-void AudioResamplerSinc::setVolume(int16_t left, int16_t right) {
+void AudioResamplerSinc::setVolume(float left, float right) {
AudioResampler::setVolume(left, right);
- mVolumeSIMD[0] = int32_t(left)<<16;
- mVolumeSIMD[1] = int32_t(right)<<16;
+ // convert to U4_28 (rounding down).
+ // integer volume values are clamped to 0 to UNITY_GAIN.
+ mVolumeSIMD[0] = u4_28_from_float(clampFloatVol(left));
+ mVolumeSIMD[1] = u4_28_from_float(clampFloatVol(right));
}
void AudioResamplerSinc::resample(int32_t* out, size_t outFrameCount,
diff --git a/services/audioflinger/AudioResamplerSinc.h b/services/audioflinger/AudioResamplerSinc.h
index 97ae3d0..4691d0a 100644
--- a/services/audioflinger/AudioResamplerSinc.h
+++ b/services/audioflinger/AudioResamplerSinc.h
@@ -44,7 +44,7 @@
private:
void init();
- virtual void setVolume(int16_t left, int16_t right);
+ virtual void setVolume(float left, float right);
template<int CHANNELS>
void resample(int32_t* out, size_t outFrameCount,
diff --git a/services/audioflinger/Tracks.cpp b/services/audioflinger/Tracks.cpp
index 0ef9fe5..cacb066 100644
--- a/services/audioflinger/Tracks.cpp
+++ b/services/audioflinger/Tracks.cpp
@@ -1899,7 +1899,7 @@
thread->mChannelCount, sampleRate);
// source SR
mResampler->setSampleRate(thread->mSampleRate);
- mResampler->setVolume(AudioMixer::UNITY_GAIN_INT, AudioMixer::UNITY_GAIN_INT);
+ mResampler->setVolume(AudioMixer::UNITY_GAIN_FLOAT, AudioMixer::UNITY_GAIN_FLOAT);
mResamplerBufferProvider = new ResamplerBufferProvider(this);
}
diff --git a/services/audioflinger/test-resample.cpp b/services/audioflinger/test-resample.cpp
index 78c9927..84a655a 100644
--- a/services/audioflinger/test-resample.cpp
+++ b/services/audioflinger/test-resample.cpp
@@ -383,20 +383,8 @@
AudioResampler* resampler = AudioResampler::create(format, channels,
output_freq, quality);
-
- /* set volume precision to 12 bits, so the volume scale is 1<<12.
- * The output int32_t is represented as Q4.27, with 4 bits of guard
- * followed by the int16_t Q.15 portion, and then 12 trailing bits of
- * additional precision.
- *
- * Generally 0 < volumePrecision <= 14 (due to the limits of
- * int16_t values for Volume). volumePrecision cannot be 0 due
- * to rounding and shifts.
- */
- const int volumePrecision = 12; // in bits
-
resampler->setSampleRate(input_freq);
- resampler->setVolume(1 << volumePrecision, 1 << volumePrecision);
+ resampler->setVolume(AudioResampler::UNITY_GAIN_FLOAT, AudioResampler::UNITY_GAIN_FLOAT);
if (profileResample) {
/*
@@ -481,19 +469,20 @@
int32_t* out = (int32_t*) output_vaddr;
int16_t* convert = (int16_t*) malloc(output_frames * channels * sizeof(int16_t));
+ const int volumeShift = 12; // shift requirement for Q4.27 to Q.15
// round to half towards zero and saturate at int16 (non-dithered)
- const int roundVal = (1<<(volumePrecision-1)) - 1; // volumePrecision > 0
+ const int roundVal = (1<<(volumeShift-1)) - 1; // volumePrecision > 0
for (size_t i = 0; i < output_frames; i++) {
for (int j = 0; j < channels; j++) {
int32_t s = out[i * output_channels + j] + roundVal; // add offset here
if (s < 0) {
- s = (s + 1) >> volumePrecision; // round to 0
+ s = (s + 1) >> volumeShift; // round to 0
if (s < -32768) {
s = -32768;
}
} else {
- s = s >> volumePrecision;
+ s = s >> volumeShift;
if (s > 32767) {
s = 32767;
}
diff --git a/services/audioflinger/tests/resampler_tests.cpp b/services/audioflinger/tests/resampler_tests.cpp
index 987162c..8624b62 100644
--- a/services/audioflinger/tests/resampler_tests.cpp
+++ b/services/audioflinger/tests/resampler_tests.cpp
@@ -89,12 +89,12 @@
outputSize &= ~7;
// create the resampler
- const int volumePrecision = 12; /* typical unity gain */
android::AudioResampler* resampler;
resampler = android::AudioResampler::create(format, channels, outputFreq, quality);
resampler->setSampleRate(inputFreq);
- resampler->setVolume(1 << volumePrecision, 1 << volumePrecision);
+ resampler->setVolume(android::AudioResampler::UNITY_GAIN_FLOAT,
+ android::AudioResampler::UNITY_GAIN_FLOAT);
// set up the reference run
std::vector<size_t> refIncr;
@@ -111,7 +111,8 @@
delete resampler;
resampler = android::AudioResampler::create(format, channels, outputFreq, quality);
resampler->setSampleRate(inputFreq);
- resampler->setVolume(1 << volumePrecision, 1 << volumePrecision);
+ resampler->setVolume(android::AudioResampler::UNITY_GAIN_FLOAT,
+ android::AudioResampler::UNITY_GAIN_FLOAT);
#endif
// set up the test run
@@ -171,13 +172,13 @@
outputSize &= ~7;
// create the resampler
- const int volumePrecision = 12; /* typical unity gain */
android::AudioResampler* resampler;
resampler = android::AudioResampler::create(AUDIO_FORMAT_PCM_16_BIT,
channels, outputFreq, quality);
resampler->setSampleRate(inputFreq);
- resampler->setVolume(1 << volumePrecision, 1 << volumePrecision);
+ resampler->setVolume(android::AudioResampler::UNITY_GAIN_FLOAT,
+ android::AudioResampler::UNITY_GAIN_FLOAT);
// set up the reference run
std::vector<size_t> refIncr;