summaryrefslogtreecommitdiffstats
path: root/services/audioflinger/AudioMixer.cpp
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/AudioMixer.cpp
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/AudioMixer.cpp')
-rw-r--r--services/audioflinger/AudioMixer.cpp242
1 files changed, 172 insertions, 70 deletions
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;