summaryrefslogtreecommitdiffstats
path: root/services/audioflinger
diff options
context:
space:
mode:
authorAndy Hung <hunga@google.com>2014-06-23 19:07:29 -0700
committerAndy Hung <hunga@google.com>2014-07-08 21:09:13 -0700
commit5e58b0abe5b6c8f5bd96a8f78bbeeeb4d3892020 (patch)
tree975ed02efce4eaefcf5b0ceb5f33d212542a33a6 /services/audioflinger
parent72d039f007722c92ee5ea7ffd03ece19d2781103 (diff)
downloadframeworks_av-5e58b0abe5b6c8f5bd96a8f78bbeeeb4d3892020.zip
frameworks_av-5e58b0abe5b6c8f5bd96a8f78bbeeeb4d3892020.tar.gz
frameworks_av-5e58b0abe5b6c8f5bd96a8f78bbeeeb4d3892020.tar.bz2
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
Diffstat (limited to 'services/audioflinger')
-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;