summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--services/audioflinger/Android.mk3
-rw-r--r--services/audioflinger/AudioMixer.cpp242
-rw-r--r--services/audioflinger/AudioMixer.h29
-rw-r--r--services/audioflinger/AudioMixerOps.h40
-rw-r--r--services/audioflinger/AudioResampler.cpp12
-rw-r--r--services/audioflinger/AudioResampler.h13
-rw-r--r--services/audioflinger/AudioResamplerDyn.cpp19
-rw-r--r--services/audioflinger/AudioResamplerDyn.h2
-rw-r--r--services/audioflinger/AudioResamplerSinc.cpp9
-rw-r--r--services/audioflinger/AudioResamplerSinc.h2
-rw-r--r--services/audioflinger/Tracks.cpp2
-rw-r--r--services/audioflinger/test-resample.cpp21
-rw-r--r--services/audioflinger/tests/resampler_tests.cpp11
13 files changed, 291 insertions, 114 deletions
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 @@ LOCAL_SRC_FILES:= \
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 @@ int AudioMixer::getTrackName(audio_channel_mask_t channelMask,
// 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 @@ void AudioMixer::disable(int name)
/* 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."
+ *
+ * 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 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.
+ * @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) {
+ *pVolumeInc = (newVolume - *pSetVolume) / ramp;
+ *pPrevVolume = *pSetVolume;
+ } else {
+ *pVolumeInc = 0;
+ *pPrevVolume = newVolume;
+ }
+ *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) {
- volumeInc = ((newValue - volume) << 16) / ramp;
- prevVolume = (volumeInc == 0 ? newValue : volume) << 16;
+ *pIntVolumeInc = ((intVolume - *pIntSetVolume) << 16) / ramp;
+ *pIntPrevVolume = (*pIntVolumeInc == 0 ? intVolume : *pIntSetVolume) << 16;
} else {
- volumeInc = 0;
- prevVolume = newValue << 16;
+ *pIntVolumeInc = 0;
+ *pIntPrevVolume = intVolume << 16;
}
- volume = newValue;
+ *pIntSetVolume = intVolume;
return true;
}
@@ -716,8 +761,10 @@ void AudioMixer::setParameter(int name, int target, int param, void *value)
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 @@ void AudioMixer::setParameter(int name, int target, int param, void *value)
}
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 @@ bool AudioMixer::track_t::setResampler(uint32_t value, uint32_t devSampleRate)
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 @@ void AudioMixer::track__genericResample(track_t* t, int32_t* out, size_t outFram
// 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 @@ void AudioMixer::track__genericResample(track_t* t, int32_t* out, size_t outFram
}
} 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 @@ void AudioMixer::track__genericResample(track_t* t, int32_t* out, size_t outFram
// 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 @@ int64_t AudioMixer::calculateOutputPTS(const track_t& t, int64_t basePTS,
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 @@ void AudioMixer::process_NoResampleOneTrack(state_t* state, int64_t pts)
}
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 @@ void AudioMixer::process_NoResampleOneTrack(state_t* state, int64_t pts)
t->bufferProvider->releaseBuffer(&b);
}
if (ramp) {
- t->adjustVolumeRamp(aux != NULL);
+ t->adjustVolumeRamp(aux != NULL, is_same<TI, float>::value);
}
}
@@ -1792,19 +1902,15 @@ void AudioMixer::track__Resample(track_t* t, TO* out, size_t outFrameCount, TO*
// 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 @@ void AudioMixer::track__NoResample(track_t* t, TO* out, size_t frameCount,
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 @@ private:
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 @@ private:
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 @@ private:
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 @@ private:
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 @@ inline int16_t MixMul<int16_t, int32_t, int32_t>(int32_t value, int32_t volume)
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 @@ AudioResampler::AudioResampler(int inChannelCount,
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 @@ void AudioResampler::setSampleRate(int32_t inSampleRate) {
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 @@ public:
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 @@ public:
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 @@ protected:
+ (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 @@ void AudioResamplerDyn<TC, TI, TO>::init()
}
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 @@ void AudioResamplerDyn<TC, TI, TO>::setSampleRate(int32_t inSampleRate)
// 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 @@ public:
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 @@ void AudioResamplerSinc::init() {
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 @@ public:
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 @@ AudioFlinger::RecordThread::RecordTrack::RecordTrack(
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 @@ int main(int argc, char* argv[]) {
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 @@ int main(int argc, char* argv[]) {
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 @@ void testBufferIncrement(size_t channels, bool useFloat,
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 @@ void testBufferIncrement(size_t channels, bool useFloat,
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 @@ void testStopbandDownconversion(size_t channels,
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;