OSDN Git Service

1fb2031397c62fd5b9c9335ba4d81d375f1c4660
[android-x86/system-media.git] / audio_utils / Balance.cpp
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 // Implementation detail: parametric volume curve transfer function
22
23 constexpr float CURVE_PARAMETER = 2.f;
24
25 static inline float curve(float parameter, float inVolume) {
26     return exp(parameter * inVolume) - 1.f;
27 }
28
29 Balance::Balance()
30     : mCurveNorm(1.f / curve(CURVE_PARAMETER, 1.f /* inVolume */))
31 {
32     setChannelMask(AUDIO_CHANNEL_OUT_STEREO);
33 }
34
35 void Balance::setChannelMask(audio_channel_mask_t channelMask)
36 {
37     channelMask &= ~ AUDIO_CHANNEL_HAPTIC_ALL;
38     if (!audio_is_output_channel(channelMask) // invalid mask
39             || mChannelMask == channelMask) { // no need to do anything
40         return;
41     }
42
43     mChannelMask = channelMask;
44     mChannelCount = audio_channel_count_from_out_mask(channelMask);
45
46     // reset mVolumes (the next process() will recalculate if needed).
47     mVolumes.resize(mChannelCount);
48     std::fill(mVolumes.begin(), mVolumes.end(), 1.f);
49     mBalance = 0.f;
50
51     // Implementation detail (may change):
52     // For implementation speed, we precompute the side (left, right, center),
53     // which is a fixed geometrical constant for a given channel mask.
54     // This assumes that the channel mask does not change frequently.
55     //
56     // For the channel mask spec, see system/media/audio/include/system/audio-base.h.
57     //
58     // The side is: 0 = left, 1 = right, 2 = center.
59     static constexpr int sideFromChannel[] = {
60         0, // AUDIO_CHANNEL_OUT_FRONT_LEFT            = 0x1u,
61         1, // AUDIO_CHANNEL_OUT_FRONT_RIGHT           = 0x2u,
62         2, // AUDIO_CHANNEL_OUT_FRONT_CENTER          = 0x4u,
63         2, // AUDIO_CHANNEL_OUT_LOW_FREQUENCY         = 0x8u,
64         0, // AUDIO_CHANNEL_OUT_BACK_LEFT             = 0x10u,
65         1, // AUDIO_CHANNEL_OUT_BACK_RIGHT            = 0x20u,
66         0, // AUDIO_CHANNEL_OUT_FRONT_LEFT_OF_CENTER  = 0x40u,
67         1, // AUDIO_CHANNEL_OUT_FRONT_RIGHT_OF_CENTER = 0x80u,
68         2, // AUDIO_CHANNEL_OUT_BACK_CENTER           = 0x100u,
69         0, // AUDIO_CHANNEL_OUT_SIDE_LEFT             = 0x200u,
70         1, // AUDIO_CHANNEL_OUT_SIDE_RIGHT            = 0x400u,
71         2, // AUDIO_CHANNEL_OUT_TOP_CENTER            = 0x800u,
72         0, // AUDIO_CHANNEL_OUT_TOP_FRONT_LEFT        = 0x1000u,
73         2, // AUDIO_CHANNEL_OUT_TOP_FRONT_CENTER      = 0x2000u,
74         1, // AUDIO_CHANNEL_OUT_TOP_FRONT_RIGHT       = 0x4000u,
75         0, // AUDIO_CHANNEL_OUT_TOP_BACK_LEFT         = 0x8000u,
76         2, // AUDIO_CHANNEL_OUT_TOP_BACK_CENTER       = 0x10000u,
77         1, // AUDIO_CHANNEL_OUT_TOP_BACK_RIGHT        = 0x20000u,
78         0, // AUDIO_CHANNEL_OUT_TOP_SIDE_LEFT         = 0x40000u,
79         1, // AUDIO_CHANNEL_OUT_TOP_SIDE_RIGHT        = 0x80000u,
80      };
81
82     mSides.resize(mChannelCount);
83     for (unsigned i = 0, channel = channelMask; channel != 0; ++i) {
84         const int index = __builtin_ctz(channel);
85         if (index < sizeof(sideFromChannel) / sizeof(sideFromChannel[0])) {
86             mSides[i] = 2; // consider center
87         } else {
88             mSides[i] = sideFromChannel[index];
89         }
90         channel &= ~(1 << index);
91     }
92 }
93
94 void Balance::process(float *buffer, float balance, size_t frames)
95 {
96     setBalance(balance);
97     for (size_t i = 0; i < frames; ++i) {
98         for (size_t j = 0; j < mChannelCount; ++j) {
99             *buffer++ *= mVolumes[j];
100         }
101     }
102 }
103
104 // Implementation detail (may change):
105 // This is not an energy preserving balance (e.g. using sin/cos cross fade or some such).
106 // Rather it preserves full gain on left and right when balance is 0.f,
107 // and decreases the right or left as one changes the balance.
108 void Balance::computeStereoBalance(float balance, float *left, float *right) const
109 {
110     if (balance > 0.f) {
111         *left = curve(CURVE_PARAMETER, 1.f - balance) * mCurveNorm;
112         *right = 1.f;
113     } else if (balance < 0.f) {
114         *left = 1.f;
115         *right = curve(CURVE_PARAMETER, 1.f + balance) * mCurveNorm;
116     } else {
117         *left = 1.f;
118         *right = 1.f;
119     }
120
121     // Functionally:
122     // *left = balance > 0.f ? curve(CURVE_PARAMETER, 1.f - balance) * mCurveNorm : 1.f;
123     // *right = balance < 0.f ? curve(CURVE_PARAMETER, 1.f + balance) * mCurveNorm : 1.f;
124 }
125
126 std::string Balance::toString() const
127 {
128     std::stringstream ss;
129     ss << "balance " << mBalance << " channelCount " << mChannelCount << " volumes:";
130     for (float volume : mVolumes) {
131         ss << " " << volume;
132     }
133     return ss.str();
134 }
135
136 void Balance::setBalance(float balance)
137 {
138     if (isnan(balance) || fabs(balance) > 1.f              // balance out of range
139             || mBalance == balance || mChannelCount < 2) { // change not applicable
140         return;
141     }
142
143     mBalance = balance;
144
145     // handle the common cases
146     if (mChannelMask == AUDIO_CHANNEL_OUT_STEREO
147             || audio_channel_mask_get_representation(mChannelMask)
148                     == AUDIO_CHANNEL_REPRESENTATION_INDEX) {
149         computeStereoBalance(balance, &mVolumes[0], &mVolumes[1]);
150         return;
151     }
152
153     float balanceVolumes[3]; // left, right, center
154     computeStereoBalance(balance, &balanceVolumes[0], &balanceVolumes[1]);
155     balanceVolumes[2] = 1.f; // center  TODO: consider center scaling.
156
157     for (size_t i = 0; i < mVolumes.size(); ++i) {
158         mVolumes[i] = balanceVolumes[mSides[i]];
159     }
160 }
161
162 } // namespace android::audio_utils
163