diff options
Diffstat (limited to 'services/audioflinger')
| -rw-r--r-- | services/audioflinger/AudioMixer.cpp | 111 | 
1 files changed, 88 insertions, 23 deletions
diff --git a/services/audioflinger/AudioMixer.cpp b/services/audioflinger/AudioMixer.cpp index 586c737..01efc53 100644 --- a/services/audioflinger/AudioMixer.cpp +++ b/services/audioflinger/AudioMixer.cpp @@ -66,6 +66,13 @@  #define ARRAY_SIZE(x) (sizeof(x)/sizeof((x)[0]))  #endif +// TODO: Move these macro/inlines to a header file. +template <typename T> +static inline +T max(const T& x, const T& y) { +    return x > y ? x : y; +} +  // Set kUseNewMixer to true to use the new mixer engine always. Otherwise the  // original code will be used for stereo sinks, the new mixer for multichannel.  static const bool kUseNewMixer = true; @@ -499,41 +506,99 @@ void AudioMixer::disable(int name)  static inline bool setVolumeRampVariables(float newVolume, int32_t ramp,          int16_t *pIntSetVolume, int32_t *pIntPrevVolume, int32_t *pIntVolumeInc,          float *pSetVolume, float *pPrevVolume, float *pVolumeInc) { +    // check floating point volume to see if it is identical to the previously +    // set volume. +    // We do not use a tolerance here (and reject changes too small) +    // as it may be confusing to use a different value than the one set. +    // If the resulting volume is too small to ramp, it is a direct set of the volume.      if (newVolume == *pSetVolume) {          return false;      } -    /* set the floating point volume variables */ -    if (ramp != 0) { -        *pVolumeInc = (newVolume - *pSetVolume) / ramp; -        *pPrevVolume = *pSetVolume; +    if (newVolume < 0) { +        newVolume = 0; // should not have negative volumes      } else { -        *pVolumeInc = 0; -        *pPrevVolume = newVolume; +        switch (fpclassify(newVolume)) { +        case FP_SUBNORMAL: +        case FP_NAN: +            newVolume = 0; +            break; +        case FP_ZERO: +            break; // zero volume is fine +        case FP_INFINITE: +            // Infinite volume could be handled consistently since +            // floating point math saturates at infinities, +            // but we limit volume to unity gain float. +            // ramp = 0; break; +            // +            newVolume = AudioMixer::UNITY_GAIN_FLOAT; +            break; +        case FP_NORMAL: +        default: +            // Floating point does not have problems with overflow wrap +            // that integer has.  However, we limit the volume to +            // unity gain here. +            // TODO: Revisit the volume limitation and perhaps parameterize. +            if (newVolume > AudioMixer::UNITY_GAIN_FLOAT) { +                newVolume = AudioMixer::UNITY_GAIN_FLOAT; +            } +            break; +        } +    } + +    // set floating point volume ramp +    if (ramp != 0) { +        // when the ramp completes, *pPrevVolume is set to *pSetVolume, so there +        // is no computational mismatch; hence equality is checked here. +        ALOGD_IF(*pPrevVolume != *pSetVolume, "previous float ramp hasn't finished," +                " prev:%f  set_to:%f", *pPrevVolume, *pSetVolume); +        const float inc = (newVolume - *pPrevVolume) / ramp; // could be inf, nan, subnormal +        const float maxv = max(newVolume, *pPrevVolume); // could be inf, cannot be nan, subnormal + +        if (isnormal(inc) // inc must be a normal number (no subnormals, infinite, nan) +                && maxv + inc != maxv) { // inc must make forward progress +            *pVolumeInc = inc; +            // ramp is set now. +            // Note: if newVolume is 0, then near the end of the ramp, +            // it may be possible that the ramped volume may be subnormal or +            // temporarily negative by a small amount or subnormal due to floating +            // point inaccuracies. +        } else { +            ramp = 0; // ramp not allowed +        }      } -    *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. +    // compute and check integer volume, no need to check negative values +    // The integer volume is limited to "unity_gain" to avoid wrapping and other +    // audio artifacts, so it never reaches the range limit of U4.28. +    // We safely use signed 16 and 32 bit integers here. +    const float scaledVolume = newVolume * AudioMixer::UNITY_GAIN_INT; // not neg, subnormal, nan +    const int32_t intVolume = (scaledVolume >= (float)AudioMixer::UNITY_GAIN_INT) ? +            AudioMixer::UNITY_GAIN_INT : (int32_t)scaledVolume; + +    // set integer volume ramp +    if (ramp != 0) { +        // integer volume is U4.12 (to use 16 bit multiplies), but ramping uses U4.28. +        // when the ramp completes, *pIntPrevVolume is set to *pIntSetVolume << 16, so there +        // is no computational mismatch; hence equality is checked here. +        ALOGD_IF(*pIntPrevVolume != *pIntSetVolume << 16, "previous int ramp hasn't finished," +                " prev:%d  set_to:%d", *pIntPrevVolume, *pIntSetVolume << 16); +        const int32_t inc = ((intVolume << 16) - *pIntPrevVolume) / ramp; + +        if (inc != 0) { // inc must make forward progress +            *pIntVolumeInc = inc; +        } else { +            ramp = 0; // ramp not allowed +        }      } -    if (intVolume == *pIntSetVolume) { -        *pIntVolumeInc = 0; -        /* TODO: integer/float workaround: ignore floating volume ramp */ + +    // if no ramp, or ramp not allowed, then clear float and integer increments +    if (ramp == 0) {          *pVolumeInc = 0;          *pPrevVolume = newVolume; -        return true; -    } -    if (ramp != 0) { -        *pIntVolumeInc = ((intVolume - *pIntSetVolume) << 16) / ramp; -        *pIntPrevVolume = (*pIntVolumeInc == 0 ? intVolume : *pIntSetVolume) << 16; -    } else {          *pIntVolumeInc = 0;          *pIntPrevVolume = intVolume << 16;      } +    *pSetVolume = newVolume;      *pIntSetVolume = intVolume;      return true;  }  | 
