• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2023 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/ChannelMix.h>
18 
19 namespace android::audio_utils::channels {
20 
21 /**
22  * The ChannelMix code relies on sparse matrix optimization for speed.
23  *
24  * This requires -ffast-math to be specified.
25  */
26 
27 
28 /*
29  Implementation detail:
30 
31  We "compute" the channel mix matrix by constexpr computation,
32  but alternatively we could use a straight 2D array initialization.
33 
34  The thought is that the channel mix matrix by computation is easier
35  to keep consistent with modifications.
36  */
37 
38 namespace {
39 // A container for the channel matrix
40 template <audio_channel_mask_t INPUT_CHANNEL_MASK, audio_channel_mask_t OUTPUT_CHANNEL_MASK>
41 struct ChannelMatrixContainer {
42     static inline constexpr size_t INPUT_CHANNEL_COUNT =
43             audio_channel_count_from_out_mask(INPUT_CHANNEL_MASK);
44     static inline constexpr size_t OUTPUT_CHANNEL_COUNT =
45             audio_channel_count_from_out_mask(OUTPUT_CHANNEL_MASK);
46     float f[INPUT_CHANNEL_COUNT][OUTPUT_CHANNEL_COUNT];
47 };
48 
49 template <audio_channel_mask_t INPUT_CHANNEL_MASK, audio_channel_mask_t OUTPUT_CHANNEL_MASK>
computeMatrix()50 constexpr ChannelMatrixContainer<INPUT_CHANNEL_MASK, OUTPUT_CHANNEL_MASK> computeMatrix() {
51     ChannelMatrixContainer<INPUT_CHANNEL_MASK, OUTPUT_CHANNEL_MASK> channelMatrix{};
52     // Compiler bug: cannot check result of this through static_assert.
53     (void)fillChannelMatrix<OUTPUT_CHANNEL_MASK>(INPUT_CHANNEL_MASK, channelMatrix.f);
54     return channelMatrix;
55 }
56 
57 } // namespace
58 
59 /**
60  * Remixes a multichannel signal of specified number of channels
61  *
62  * INPUT_CHANNEL_MASK the src input.
63  * OUTPUT_CHANNEL_MASK the dst output.
64  * ACCUMULATE is true if the remix is added to the destination or
65  *               false if the remix replaces the destination.
66  *
67  * \param src          multichannel audio buffer to remix
68  * \param dst          remixed stereo audio samples
69  * \param frameCount   number of multichannel frames to remix
70  *
71  * \return false if the CHANNEL_COUNT is not supported.
72  */
73 template <audio_channel_mask_t INPUT_CHANNEL_MASK,
74         audio_channel_mask_t OUTPUT_CHANNEL_MASK, bool ACCUMULATE>
sparseChannelMatrixMultiply(const float * src,float * dst,size_t frameCount)75 bool sparseChannelMatrixMultiply(const float *src, float *dst, size_t frameCount) {
76     static constexpr auto s = computeMatrix<INPUT_CHANNEL_MASK, OUTPUT_CHANNEL_MASK>();
77 
78     // matrix multiply
79     if (INPUT_CHANNEL_MASK == AUDIO_CHANNEL_NONE) return false;
80     for (;frameCount > 0; --frameCount) {
81         float ch[s.OUTPUT_CHANNEL_COUNT]{};
82         #pragma unroll
83         for (size_t i = 0; i < s.INPUT_CHANNEL_COUNT; ++i) {
84             const float (&array)[s.OUTPUT_CHANNEL_COUNT] = s.f[i];
85             #pragma unroll
86             for (size_t j = 0; j < s.OUTPUT_CHANNEL_COUNT; ++j) {
87                 ch[j] += array[j] * src[i];
88             }
89         }
90         if constexpr (ACCUMULATE) {
91             #pragma unroll
92             for (size_t j = 0; j < s.OUTPUT_CHANNEL_COUNT; ++j) {
93                 ch[j] += dst[j];
94             }
95         }
96         #pragma unroll
97         for (size_t j = 0; j < s.OUTPUT_CHANNEL_COUNT; ++j) {
98             dst[j] = clamp(ch[j]);
99         }
100         src += s.INPUT_CHANNEL_COUNT;
101         dst += s.OUTPUT_CHANNEL_COUNT;
102     }
103     return true;
104 }
105 
106 // Create accelerated instances
107 
108 #define INSTANTIATE(INPUT_MASK, OUTPUT_MASK) \
109 template bool \
110 sparseChannelMatrixMultiply<INPUT_MASK, OUTPUT_MASK, true>( \
111         const float *src, float *dst, size_t frameCount); \
112 template bool \
113 sparseChannelMatrixMultiply<INPUT_MASK, OUTPUT_MASK, false>( \
114         const float *src, float *dst, size_t frameCount); \
115 
116 #define INSTANTIATE_MASKS(CHANNEL) \
117 INSTANTIATE(AUDIO_CHANNEL_OUT_STEREO, (CHANNEL)) \
118 INSTANTIATE(AUDIO_CHANNEL_OUT_QUAD_BACK, (CHANNEL)) \
119 INSTANTIATE(AUDIO_CHANNEL_OUT_5POINT1_BACK, (CHANNEL)) \
120 INSTANTIATE(AUDIO_CHANNEL_OUT_7POINT1, (CHANNEL)) \
121 INSTANTIATE(AUDIO_CHANNEL_OUT_5POINT1POINT2, (CHANNEL)) \
122 INSTANTIATE(AUDIO_CHANNEL_OUT_5POINT1POINT4, (CHANNEL)) \
123 INSTANTIATE(AUDIO_CHANNEL_OUT_7POINT1POINT2, (CHANNEL)) \
124 INSTANTIATE(AUDIO_CHANNEL_OUT_7POINT1POINT4, (CHANNEL)) \
125 INSTANTIATE(AUDIO_CHANNEL_OUT_9POINT1POINT6, (CHANNEL)) \
126 INSTANTIATE(AUDIO_CHANNEL_OUT_22POINT2, (CHANNEL))
127 
128 INSTANTIATE_MASKS(AUDIO_CHANNEL_OUT_STEREO)
INSTANTIATE_MASKS(AUDIO_CHANNEL_OUT_5POINT1)129 INSTANTIATE_MASKS(AUDIO_CHANNEL_OUT_5POINT1)
130 INSTANTIATE_MASKS(AUDIO_CHANNEL_OUT_7POINT1)
131 INSTANTIATE_MASKS(AUDIO_CHANNEL_OUT_7POINT1POINT4)
132 INSTANTIATE_MASKS(AUDIO_CHANNEL_OUT_9POINT1POINT6)
133 
134 /* static */
135 std::shared_ptr<IChannelMix> IChannelMix::create(audio_channel_mask_t outputChannelMask) {
136      switch (outputChannelMask) {
137      case AUDIO_CHANNEL_OUT_STEREO:
138          return std::make_shared<ChannelMix<AUDIO_CHANNEL_OUT_STEREO>>();
139      case AUDIO_CHANNEL_OUT_5POINT1:
140          return std::make_shared<ChannelMix<AUDIO_CHANNEL_OUT_5POINT1>>();
141      case AUDIO_CHANNEL_OUT_7POINT1:
142          return std::make_shared<ChannelMix<AUDIO_CHANNEL_OUT_7POINT1>>();
143      case AUDIO_CHANNEL_OUT_7POINT1POINT4:
144          return std::make_shared<ChannelMix<AUDIO_CHANNEL_OUT_7POINT1POINT4>>();
145      case AUDIO_CHANNEL_OUT_9POINT1POINT6:
146          return std::make_shared<ChannelMix<AUDIO_CHANNEL_OUT_9POINT1POINT6>>();
147      default:
148          return {};
149      }
150 }
151 
152 /* static */
isOutputChannelMaskSupported(audio_channel_mask_t outputChannelMask)153 bool IChannelMix::isOutputChannelMaskSupported(audio_channel_mask_t outputChannelMask) {
154     switch (outputChannelMask) {
155     case AUDIO_CHANNEL_OUT_STEREO:
156     case AUDIO_CHANNEL_OUT_5POINT1:
157     case AUDIO_CHANNEL_OUT_7POINT1:
158     case AUDIO_CHANNEL_OUT_7POINT1POINT4:
159     case AUDIO_CHANNEL_OUT_9POINT1POINT6:
160         return true;
161     default:
162         return false;
163     }
164 }
165 
166 } // android::audio_utils::channels
167