diff options
-rw-r--r-- | services/audioflinger/Android.mk | 3 | ||||
-rw-r--r-- | services/audioflinger/AudioMixer.cpp | 242 | ||||
-rw-r--r-- | services/audioflinger/AudioMixer.h | 29 | ||||
-rw-r--r-- | services/audioflinger/AudioMixerOps.h | 40 | ||||
-rw-r--r-- | services/audioflinger/AudioResampler.cpp | 12 | ||||
-rw-r--r-- | services/audioflinger/AudioResampler.h | 13 | ||||
-rw-r--r-- | services/audioflinger/AudioResamplerDyn.cpp | 19 | ||||
-rw-r--r-- | services/audioflinger/AudioResamplerDyn.h | 2 | ||||
-rw-r--r-- | services/audioflinger/AudioResamplerSinc.cpp | 9 | ||||
-rw-r--r-- | services/audioflinger/AudioResamplerSinc.h | 2 | ||||
-rw-r--r-- | services/audioflinger/Tracks.cpp | 2 | ||||
-rw-r--r-- | services/audioflinger/test-resample.cpp | 21 | ||||
-rw-r--r-- | services/audioflinger/tests/resampler_tests.cpp | 11 |
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; |