1*b9df5ad1SAndroid Build Coastguard Worker /*
2*b9df5ad1SAndroid Build Coastguard Worker * Copyright (C) 2023 The Android Open Source Project
3*b9df5ad1SAndroid Build Coastguard Worker *
4*b9df5ad1SAndroid Build Coastguard Worker * Licensed under the Apache License, Version 2.0 (the "License");
5*b9df5ad1SAndroid Build Coastguard Worker * you may not use this file except in compliance with the License.
6*b9df5ad1SAndroid Build Coastguard Worker * You may obtain a copy of the License at
7*b9df5ad1SAndroid Build Coastguard Worker *
8*b9df5ad1SAndroid Build Coastguard Worker * http://www.apache.org/licenses/LICENSE-2.0
9*b9df5ad1SAndroid Build Coastguard Worker *
10*b9df5ad1SAndroid Build Coastguard Worker * Unless required by applicable law or agreed to in writing, software
11*b9df5ad1SAndroid Build Coastguard Worker * distributed under the License is distributed on an "AS IS" BASIS,
12*b9df5ad1SAndroid Build Coastguard Worker * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13*b9df5ad1SAndroid Build Coastguard Worker * See the License for the specific language governing permissions and
14*b9df5ad1SAndroid Build Coastguard Worker * limitations under the License.
15*b9df5ad1SAndroid Build Coastguard Worker */
16*b9df5ad1SAndroid Build Coastguard Worker
17*b9df5ad1SAndroid Build Coastguard Worker #include <audio_utils/ChannelMix.h>
18*b9df5ad1SAndroid Build Coastguard Worker
19*b9df5ad1SAndroid Build Coastguard Worker namespace android::audio_utils::channels {
20*b9df5ad1SAndroid Build Coastguard Worker
21*b9df5ad1SAndroid Build Coastguard Worker /**
22*b9df5ad1SAndroid Build Coastguard Worker * The ChannelMix code relies on sparse matrix optimization for speed.
23*b9df5ad1SAndroid Build Coastguard Worker *
24*b9df5ad1SAndroid Build Coastguard Worker * This requires -ffast-math to be specified.
25*b9df5ad1SAndroid Build Coastguard Worker */
26*b9df5ad1SAndroid Build Coastguard Worker
27*b9df5ad1SAndroid Build Coastguard Worker
28*b9df5ad1SAndroid Build Coastguard Worker /*
29*b9df5ad1SAndroid Build Coastguard Worker Implementation detail:
30*b9df5ad1SAndroid Build Coastguard Worker
31*b9df5ad1SAndroid Build Coastguard Worker We "compute" the channel mix matrix by constexpr computation,
32*b9df5ad1SAndroid Build Coastguard Worker but alternatively we could use a straight 2D array initialization.
33*b9df5ad1SAndroid Build Coastguard Worker
34*b9df5ad1SAndroid Build Coastguard Worker The thought is that the channel mix matrix by computation is easier
35*b9df5ad1SAndroid Build Coastguard Worker to keep consistent with modifications.
36*b9df5ad1SAndroid Build Coastguard Worker */
37*b9df5ad1SAndroid Build Coastguard Worker
38*b9df5ad1SAndroid Build Coastguard Worker namespace {
39*b9df5ad1SAndroid Build Coastguard Worker // A container for the channel matrix
40*b9df5ad1SAndroid Build Coastguard Worker template <audio_channel_mask_t INPUT_CHANNEL_MASK, audio_channel_mask_t OUTPUT_CHANNEL_MASK>
41*b9df5ad1SAndroid Build Coastguard Worker struct ChannelMatrixContainer {
42*b9df5ad1SAndroid Build Coastguard Worker static inline constexpr size_t INPUT_CHANNEL_COUNT =
43*b9df5ad1SAndroid Build Coastguard Worker audio_channel_count_from_out_mask(INPUT_CHANNEL_MASK);
44*b9df5ad1SAndroid Build Coastguard Worker static inline constexpr size_t OUTPUT_CHANNEL_COUNT =
45*b9df5ad1SAndroid Build Coastguard Worker audio_channel_count_from_out_mask(OUTPUT_CHANNEL_MASK);
46*b9df5ad1SAndroid Build Coastguard Worker float f[INPUT_CHANNEL_COUNT][OUTPUT_CHANNEL_COUNT];
47*b9df5ad1SAndroid Build Coastguard Worker };
48*b9df5ad1SAndroid Build Coastguard Worker
49*b9df5ad1SAndroid Build Coastguard Worker template <audio_channel_mask_t INPUT_CHANNEL_MASK, audio_channel_mask_t OUTPUT_CHANNEL_MASK>
computeMatrix()50*b9df5ad1SAndroid Build Coastguard Worker constexpr ChannelMatrixContainer<INPUT_CHANNEL_MASK, OUTPUT_CHANNEL_MASK> computeMatrix() {
51*b9df5ad1SAndroid Build Coastguard Worker ChannelMatrixContainer<INPUT_CHANNEL_MASK, OUTPUT_CHANNEL_MASK> channelMatrix{};
52*b9df5ad1SAndroid Build Coastguard Worker // Compiler bug: cannot check result of this through static_assert.
53*b9df5ad1SAndroid Build Coastguard Worker (void)fillChannelMatrix<OUTPUT_CHANNEL_MASK>(INPUT_CHANNEL_MASK, channelMatrix.f);
54*b9df5ad1SAndroid Build Coastguard Worker return channelMatrix;
55*b9df5ad1SAndroid Build Coastguard Worker }
56*b9df5ad1SAndroid Build Coastguard Worker
57*b9df5ad1SAndroid Build Coastguard Worker } // namespace
58*b9df5ad1SAndroid Build Coastguard Worker
59*b9df5ad1SAndroid Build Coastguard Worker /**
60*b9df5ad1SAndroid Build Coastguard Worker * Remixes a multichannel signal of specified number of channels
61*b9df5ad1SAndroid Build Coastguard Worker *
62*b9df5ad1SAndroid Build Coastguard Worker * INPUT_CHANNEL_MASK the src input.
63*b9df5ad1SAndroid Build Coastguard Worker * OUTPUT_CHANNEL_MASK the dst output.
64*b9df5ad1SAndroid Build Coastguard Worker * ACCUMULATE is true if the remix is added to the destination or
65*b9df5ad1SAndroid Build Coastguard Worker * false if the remix replaces the destination.
66*b9df5ad1SAndroid Build Coastguard Worker *
67*b9df5ad1SAndroid Build Coastguard Worker * \param src multichannel audio buffer to remix
68*b9df5ad1SAndroid Build Coastguard Worker * \param dst remixed stereo audio samples
69*b9df5ad1SAndroid Build Coastguard Worker * \param frameCount number of multichannel frames to remix
70*b9df5ad1SAndroid Build Coastguard Worker *
71*b9df5ad1SAndroid Build Coastguard Worker * \return false if the CHANNEL_COUNT is not supported.
72*b9df5ad1SAndroid Build Coastguard Worker */
73*b9df5ad1SAndroid Build Coastguard Worker template <audio_channel_mask_t INPUT_CHANNEL_MASK,
74*b9df5ad1SAndroid Build Coastguard Worker audio_channel_mask_t OUTPUT_CHANNEL_MASK, bool ACCUMULATE>
sparseChannelMatrixMultiply(const float * src,float * dst,size_t frameCount)75*b9df5ad1SAndroid Build Coastguard Worker bool sparseChannelMatrixMultiply(const float *src, float *dst, size_t frameCount) {
76*b9df5ad1SAndroid Build Coastguard Worker static constexpr auto s = computeMatrix<INPUT_CHANNEL_MASK, OUTPUT_CHANNEL_MASK>();
77*b9df5ad1SAndroid Build Coastguard Worker
78*b9df5ad1SAndroid Build Coastguard Worker // matrix multiply
79*b9df5ad1SAndroid Build Coastguard Worker if (INPUT_CHANNEL_MASK == AUDIO_CHANNEL_NONE) return false;
80*b9df5ad1SAndroid Build Coastguard Worker for (;frameCount > 0; --frameCount) {
81*b9df5ad1SAndroid Build Coastguard Worker float ch[s.OUTPUT_CHANNEL_COUNT]{};
82*b9df5ad1SAndroid Build Coastguard Worker #pragma unroll
83*b9df5ad1SAndroid Build Coastguard Worker for (size_t i = 0; i < s.INPUT_CHANNEL_COUNT; ++i) {
84*b9df5ad1SAndroid Build Coastguard Worker const float (&array)[s.OUTPUT_CHANNEL_COUNT] = s.f[i];
85*b9df5ad1SAndroid Build Coastguard Worker #pragma unroll
86*b9df5ad1SAndroid Build Coastguard Worker for (size_t j = 0; j < s.OUTPUT_CHANNEL_COUNT; ++j) {
87*b9df5ad1SAndroid Build Coastguard Worker ch[j] += array[j] * src[i];
88*b9df5ad1SAndroid Build Coastguard Worker }
89*b9df5ad1SAndroid Build Coastguard Worker }
90*b9df5ad1SAndroid Build Coastguard Worker if constexpr (ACCUMULATE) {
91*b9df5ad1SAndroid Build Coastguard Worker #pragma unroll
92*b9df5ad1SAndroid Build Coastguard Worker for (size_t j = 0; j < s.OUTPUT_CHANNEL_COUNT; ++j) {
93*b9df5ad1SAndroid Build Coastguard Worker ch[j] += dst[j];
94*b9df5ad1SAndroid Build Coastguard Worker }
95*b9df5ad1SAndroid Build Coastguard Worker }
96*b9df5ad1SAndroid Build Coastguard Worker #pragma unroll
97*b9df5ad1SAndroid Build Coastguard Worker for (size_t j = 0; j < s.OUTPUT_CHANNEL_COUNT; ++j) {
98*b9df5ad1SAndroid Build Coastguard Worker dst[j] = clamp(ch[j]);
99*b9df5ad1SAndroid Build Coastguard Worker }
100*b9df5ad1SAndroid Build Coastguard Worker src += s.INPUT_CHANNEL_COUNT;
101*b9df5ad1SAndroid Build Coastguard Worker dst += s.OUTPUT_CHANNEL_COUNT;
102*b9df5ad1SAndroid Build Coastguard Worker }
103*b9df5ad1SAndroid Build Coastguard Worker return true;
104*b9df5ad1SAndroid Build Coastguard Worker }
105*b9df5ad1SAndroid Build Coastguard Worker
106*b9df5ad1SAndroid Build Coastguard Worker // Create accelerated instances
107*b9df5ad1SAndroid Build Coastguard Worker
108*b9df5ad1SAndroid Build Coastguard Worker #define INSTANTIATE(INPUT_MASK, OUTPUT_MASK) \
109*b9df5ad1SAndroid Build Coastguard Worker template bool \
110*b9df5ad1SAndroid Build Coastguard Worker sparseChannelMatrixMultiply<INPUT_MASK, OUTPUT_MASK, true>( \
111*b9df5ad1SAndroid Build Coastguard Worker const float *src, float *dst, size_t frameCount); \
112*b9df5ad1SAndroid Build Coastguard Worker template bool \
113*b9df5ad1SAndroid Build Coastguard Worker sparseChannelMatrixMultiply<INPUT_MASK, OUTPUT_MASK, false>( \
114*b9df5ad1SAndroid Build Coastguard Worker const float *src, float *dst, size_t frameCount); \
115*b9df5ad1SAndroid Build Coastguard Worker
116*b9df5ad1SAndroid Build Coastguard Worker #define INSTANTIATE_MASKS(CHANNEL) \
117*b9df5ad1SAndroid Build Coastguard Worker INSTANTIATE(AUDIO_CHANNEL_OUT_STEREO, (CHANNEL)) \
118*b9df5ad1SAndroid Build Coastguard Worker INSTANTIATE(AUDIO_CHANNEL_OUT_QUAD_BACK, (CHANNEL)) \
119*b9df5ad1SAndroid Build Coastguard Worker INSTANTIATE(AUDIO_CHANNEL_OUT_5POINT1_BACK, (CHANNEL)) \
120*b9df5ad1SAndroid Build Coastguard Worker INSTANTIATE(AUDIO_CHANNEL_OUT_7POINT1, (CHANNEL)) \
121*b9df5ad1SAndroid Build Coastguard Worker INSTANTIATE(AUDIO_CHANNEL_OUT_5POINT1POINT2, (CHANNEL)) \
122*b9df5ad1SAndroid Build Coastguard Worker INSTANTIATE(AUDIO_CHANNEL_OUT_5POINT1POINT4, (CHANNEL)) \
123*b9df5ad1SAndroid Build Coastguard Worker INSTANTIATE(AUDIO_CHANNEL_OUT_7POINT1POINT2, (CHANNEL)) \
124*b9df5ad1SAndroid Build Coastguard Worker INSTANTIATE(AUDIO_CHANNEL_OUT_7POINT1POINT4, (CHANNEL)) \
125*b9df5ad1SAndroid Build Coastguard Worker INSTANTIATE(AUDIO_CHANNEL_OUT_9POINT1POINT6, (CHANNEL)) \
126*b9df5ad1SAndroid Build Coastguard Worker INSTANTIATE(AUDIO_CHANNEL_OUT_22POINT2, (CHANNEL))
127*b9df5ad1SAndroid Build Coastguard Worker
128*b9df5ad1SAndroid Build Coastguard Worker INSTANTIATE_MASKS(AUDIO_CHANNEL_OUT_STEREO)
INSTANTIATE_MASKS(AUDIO_CHANNEL_OUT_5POINT1)129*b9df5ad1SAndroid Build Coastguard Worker INSTANTIATE_MASKS(AUDIO_CHANNEL_OUT_5POINT1)
130*b9df5ad1SAndroid Build Coastguard Worker INSTANTIATE_MASKS(AUDIO_CHANNEL_OUT_7POINT1)
131*b9df5ad1SAndroid Build Coastguard Worker INSTANTIATE_MASKS(AUDIO_CHANNEL_OUT_7POINT1POINT4)
132*b9df5ad1SAndroid Build Coastguard Worker INSTANTIATE_MASKS(AUDIO_CHANNEL_OUT_9POINT1POINT6)
133*b9df5ad1SAndroid Build Coastguard Worker
134*b9df5ad1SAndroid Build Coastguard Worker /* static */
135*b9df5ad1SAndroid Build Coastguard Worker std::shared_ptr<IChannelMix> IChannelMix::create(audio_channel_mask_t outputChannelMask) {
136*b9df5ad1SAndroid Build Coastguard Worker switch (outputChannelMask) {
137*b9df5ad1SAndroid Build Coastguard Worker case AUDIO_CHANNEL_OUT_STEREO:
138*b9df5ad1SAndroid Build Coastguard Worker return std::make_shared<ChannelMix<AUDIO_CHANNEL_OUT_STEREO>>();
139*b9df5ad1SAndroid Build Coastguard Worker case AUDIO_CHANNEL_OUT_5POINT1:
140*b9df5ad1SAndroid Build Coastguard Worker return std::make_shared<ChannelMix<AUDIO_CHANNEL_OUT_5POINT1>>();
141*b9df5ad1SAndroid Build Coastguard Worker case AUDIO_CHANNEL_OUT_7POINT1:
142*b9df5ad1SAndroid Build Coastguard Worker return std::make_shared<ChannelMix<AUDIO_CHANNEL_OUT_7POINT1>>();
143*b9df5ad1SAndroid Build Coastguard Worker case AUDIO_CHANNEL_OUT_7POINT1POINT4:
144*b9df5ad1SAndroid Build Coastguard Worker return std::make_shared<ChannelMix<AUDIO_CHANNEL_OUT_7POINT1POINT4>>();
145*b9df5ad1SAndroid Build Coastguard Worker case AUDIO_CHANNEL_OUT_9POINT1POINT6:
146*b9df5ad1SAndroid Build Coastguard Worker return std::make_shared<ChannelMix<AUDIO_CHANNEL_OUT_9POINT1POINT6>>();
147*b9df5ad1SAndroid Build Coastguard Worker default:
148*b9df5ad1SAndroid Build Coastguard Worker return {};
149*b9df5ad1SAndroid Build Coastguard Worker }
150*b9df5ad1SAndroid Build Coastguard Worker }
151*b9df5ad1SAndroid Build Coastguard Worker
152*b9df5ad1SAndroid Build Coastguard Worker /* static */
isOutputChannelMaskSupported(audio_channel_mask_t outputChannelMask)153*b9df5ad1SAndroid Build Coastguard Worker bool IChannelMix::isOutputChannelMaskSupported(audio_channel_mask_t outputChannelMask) {
154*b9df5ad1SAndroid Build Coastguard Worker switch (outputChannelMask) {
155*b9df5ad1SAndroid Build Coastguard Worker case AUDIO_CHANNEL_OUT_STEREO:
156*b9df5ad1SAndroid Build Coastguard Worker case AUDIO_CHANNEL_OUT_5POINT1:
157*b9df5ad1SAndroid Build Coastguard Worker case AUDIO_CHANNEL_OUT_7POINT1:
158*b9df5ad1SAndroid Build Coastguard Worker case AUDIO_CHANNEL_OUT_7POINT1POINT4:
159*b9df5ad1SAndroid Build Coastguard Worker case AUDIO_CHANNEL_OUT_9POINT1POINT6:
160*b9df5ad1SAndroid Build Coastguard Worker return true;
161*b9df5ad1SAndroid Build Coastguard Worker default:
162*b9df5ad1SAndroid Build Coastguard Worker return false;
163*b9df5ad1SAndroid Build Coastguard Worker }
164*b9df5ad1SAndroid Build Coastguard Worker }
165*b9df5ad1SAndroid Build Coastguard Worker
166*b9df5ad1SAndroid Build Coastguard Worker } // android::audio_utils::channels
167