1 /* 2 * Copyright (C) 2019 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 #include <audio_utils/Balance.h> 18 19 namespace android::audio_utils { 20 21 void Balance::setChannelMask(audio_channel_mask_t channelMask) 22 { 23 using namespace ::android::audio_utils::channels; 24 channelMask = static_cast<audio_channel_mask_t>(channelMask & ~AUDIO_CHANNEL_HAPTIC_ALL); 25 if (!audio_is_output_channel(channelMask) // invalid mask 26 || mChannelMask == channelMask) { // no need to do anything 27 return; 28 } 29 30 mChannelMask = channelMask; 31 mChannelCount = audio_channel_count_from_out_mask(channelMask); 32 33 // save mBalance into balance for later restoring, then reset 34 const float balance = mBalance; 35 mBalance = 0.f; 36 37 // reset mVolumes 38 mVolumes.resize(mChannelCount); 39 std::fill(mVolumes.begin(), mVolumes.end(), 1.f); 40 41 // reset ramping variables 42 mRampBalance = 0.f; 43 mRampVolumes.clear(); 44 45 if (audio_channel_mask_get_representation(mChannelMask) 46 == AUDIO_CHANNEL_REPRESENTATION_INDEX) { 47 mSides.clear(); // mSides unused for channel index masks. 48 setBalance(balance); // recompute balance 49 return; 50 } 51 52 mSides.resize(mChannelCount); 53 // If LFE and LFE2 both exist, it should be L and R in 22.2 54 int lfe = -1; 55 int lfe2 = -1; 56 constexpr unsigned LFE_CHANNEL_INDEX = 3; 57 constexpr unsigned LFE2_CHANNEL_INDEX = 23; 58 for (unsigned i = 0, channel = channelMask; channel != 0; ++i) { 59 const int index = __builtin_ctz(channel); 60 mSides[i] = sideFromChannelIdx(index); 61 // Keep track of LFE indices 62 if (index == LFE_CHANNEL_INDEX) { 63 lfe = i; 64 } else if (index == LFE2_CHANNEL_INDEX) { 65 lfe2 = i; 66 } 67 channel &= ~(1 << index); 68 } 69 if (lfe >= 0 && lfe2 >= 0) { // if both LFEs exist assign to L and R. 70 mSides[lfe] = AUDIO_GEOMETRY_SIDE_LEFT; 71 mSides[lfe2] = AUDIO_GEOMETRY_SIDE_RIGHT; 72 } 73 setBalance(balance); // recompute balance 74 } 75 76 void Balance::process(float *buffer, size_t frames) 77 { 78 if (mBalance == 0.f || mChannelCount < 2) { 79 return; 80 } 81 82 if (mRamp) { 83 if (mRampVolumes.size() != mVolumes.size()) { 84 // If mRampVolumes is empty, we do not ramp in this process() but directly 85 // apply the existing mVolumes. We save the balance and volume state here 86 // and fall through to non-ramping code below. The next process() will ramp if needed. 87 mRampBalance = mBalance; 88 mRampVolumes = mVolumes; 89 } else if (mRampBalance != mBalance) { 90 if (frames > 0) { 91 std::vector<float> mDeltas(mVolumes.size()); 92 const float r = 1.f / frames; 93 for (size_t j = 0; j < mChannelCount; ++j) { 94 mDeltas[j] = (mVolumes[j] - mRampVolumes[j]) * r; 95 } 96 97 // ramped balance 98 for (size_t i = 0; i < frames; ++i) { 99 const float findex = i; 100 for (size_t j = 0; j < mChannelCount; ++j) { // better precision: delta * i 101 *buffer++ *= mRampVolumes[j] + mDeltas[j] * findex; 102 } 103 } 104 } 105 mRampBalance = mBalance; 106 mRampVolumes = mVolumes; 107 return; 108 } 109 // fall through 110 } 111 112 // non-ramped balance 113 for (size_t i = 0; i < frames; ++i) { 114 for (size_t j = 0; j < mChannelCount; ++j) { 115 *buffer++ *= mVolumes[j]; 116 } 117 } 118 } 119 120 void Balance::computeStereoBalance(float balance, float *left, float *right) const 121 { 122 if (balance > 0.f) { 123 *left = mCurve(1.f - balance); 124 *right = 1.f; 125 } else if (balance < 0.f) { 126 *left = 1.f; 127 *right = mCurve(1.f + balance); 128 } else { 129 *left = 1.f; 130 *right = 1.f; 131 } 132 133 // Functionally: 134 // *left = balance > 0.f ? mCurve(1.f - balance) : 1.f; 135 // *right = balance < 0.f ? mCurve(1.f + balance) : 1.f; 136 } 137 138 std::string Balance::toString() const 139 { 140 std::stringstream ss; 141 ss << "balance " << mBalance << " channelCount " << mChannelCount << " volumes:"; 142 for (float volume : mVolumes) { 143 ss << " " << volume; 144 } 145 // we do not show mSides, which is only valid for channel position masks. 146 return ss.str(); 147 } 148 149 void Balance::setBalance(float balance) 150 { 151 using namespace ::android::audio_utils::channels; 152 if (mBalance == balance // no change 153 || isnan(balance) || fabs(balance) > 1.f) { // balance out of range 154 return; 155 } 156 157 mBalance = balance; 158 159 if (mChannelCount < 2) { // if channel count is 1, mVolumes[0] is already set to 1.f 160 return; // and if channel count < 2, we don't do anything in process(). 161 } 162 163 // Handle the common cases: 164 // stereo and channel index masks only affect the first two channels as left and right. 165 if (mChannelMask == AUDIO_CHANNEL_OUT_STEREO 166 || audio_channel_mask_get_representation(mChannelMask) 167 == AUDIO_CHANNEL_REPRESENTATION_INDEX) { 168 computeStereoBalance(balance, &mVolumes[0], &mVolumes[1]); 169 return; 170 } 171 172 // For position masks with more than 2 channels, we consider which side the 173 // speaker position is on to figure the volume used. 174 float balanceVolumes[3]; // left, right, center (we don't care the order) 175 static_assert(AUDIO_GEOMETRY_SIDE_LEFT >= 0 176 && AUDIO_GEOMETRY_SIDE_LEFT <= std::size(balanceVolumes)); 177 static_assert(AUDIO_GEOMETRY_SIDE_RIGHT >= 0 178 && AUDIO_GEOMETRY_SIDE_RIGHT <= std::size(balanceVolumes)); 179 static_assert(AUDIO_GEOMETRY_SIDE_CENTER >= 0 180 && AUDIO_GEOMETRY_SIDE_CENTER <= std::size(balanceVolumes)); 181 computeStereoBalance(balance, &balanceVolumes[AUDIO_GEOMETRY_SIDE_LEFT], 182 &balanceVolumes[AUDIO_GEOMETRY_SIDE_RIGHT]); 183 balanceVolumes[AUDIO_GEOMETRY_SIDE_CENTER] = 1.f; // center TODO: consider center scaling. 184 185 for (size_t i = 0; i < mVolumes.size(); ++i) { 186 mVolumes[i] = balanceVolumes[mSides[i]]; 187 } 188 } 189 190 } // namespace android::audio_utils 191