1 /*
2  * Copyright 2021 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 <random>
18 #include <vector>
19 
20 #include <audio_effects/effect_downmix.h>
21 #include <audio_utils/channels.h>
22 #include <audio_utils/primitives.h>
23 #include <audio_utils/Statistics.h>
24 #include <benchmark/benchmark.h>
25 #include <log/log.h>
26 #include <system/audio.h>
27 
28 #include "EffectDownmix.h"
29 
30 extern audio_effect_library_t AUDIO_EFFECT_LIBRARY_INFO_SYM;
31 
32 static constexpr audio_channel_mask_t kChannelPositionMasks[] = {
33     AUDIO_CHANNEL_OUT_FRONT_LEFT,
34     AUDIO_CHANNEL_OUT_FRONT_CENTER,
35     AUDIO_CHANNEL_OUT_STEREO,
36     AUDIO_CHANNEL_OUT_2POINT1,
37     AUDIO_CHANNEL_OUT_2POINT0POINT2,
38     AUDIO_CHANNEL_OUT_QUAD, // AUDIO_CHANNEL_OUT_QUAD_BACK
39     AUDIO_CHANNEL_OUT_QUAD_SIDE,
40     AUDIO_CHANNEL_OUT_SURROUND,
41     AUDIO_CHANNEL_OUT_2POINT1POINT2,
42     AUDIO_CHANNEL_OUT_3POINT0POINT2,
43     AUDIO_CHANNEL_OUT_PENTA,
44     AUDIO_CHANNEL_OUT_3POINT1POINT2,
45     AUDIO_CHANNEL_OUT_5POINT1, // AUDIO_CHANNEL_OUT_5POINT1_BACK
46     AUDIO_CHANNEL_OUT_5POINT1_SIDE,
47     AUDIO_CHANNEL_OUT_6POINT1,
48     AUDIO_CHANNEL_OUT_5POINT1POINT2,
49     AUDIO_CHANNEL_OUT_7POINT1,
50     AUDIO_CHANNEL_OUT_5POINT1POINT4,
51     AUDIO_CHANNEL_OUT_7POINT1POINT2,
52     AUDIO_CHANNEL_OUT_7POINT1POINT4,
53     AUDIO_CHANNEL_OUT_13POINT_360RA,
54     AUDIO_CHANNEL_OUT_22POINT2,
55 };
56 
57 static constexpr effect_uuid_t downmix_uuid = {
58     0x93f04452, 0xe4fe, 0x41cc, 0x91f9, {0xe4, 0x75, 0xb6, 0xd1, 0xd6, 0x9f}};
59 
60 static constexpr size_t kFrameCount = 1000;
61 
62 /*
63 Pixel 4XL
64 $ adb shell /data/benchmarktest/downmix_benchmark/vendor/downmix_benchmark
65 
66 --------------------------------------------------------
67 Benchmark              Time             CPU   Iterations
68 --------------------------------------------------------
69 BM_Downmix/0        3638 ns         3624 ns       197517 AUDIO_CHANNEL_OUT_MONO
70 BM_Downmix/1        4040 ns         4024 ns       178766
71 BM_Downmix/2        4759 ns         4740 ns       134741 AUDIO_CHANNEL_OUT_STEREO
72 BM_Downmix/3        6042 ns         6017 ns       129546 AUDIO_CHANNEL_OUT_2POINT1
73 BM_Downmix/4        6897 ns         6868 ns        96316 AUDIO_CHANNEL_OUT_2POINT0POINT2
74 BM_Downmix/5        2117 ns         2109 ns       331705 AUDIO_CHANNEL_OUT_QUAD
75 BM_Downmix/6        2097 ns         2088 ns       335421 AUDIO_CHANNEL_OUT_QUAD_SIDE
76 BM_Downmix/7        7291 ns         7263 ns        96256 AUDIO_CHANNEL_OUT_SURROUND
77 BM_Downmix/8        8246 ns         8206 ns        84318 AUDIO_CHANNEL_OUT_2POINT1POINT2
78 BM_Downmix/9        8341 ns         8303 ns        84298 AUDIO_CHANNEL_OUT_3POINT0POINT2
79 BM_Downmix/10       7549 ns         7517 ns        84293 AUDIO_CHANNEL_OUT_PENTA
80 BM_Downmix/11       9395 ns         9354 ns        75209 AUDIO_CHANNEL_OUT_3POINT1POINT2
81 BM_Downmix/12       3267 ns         3253 ns       215596 AUDIO_CHANNEL_OUT_5POINT1
82 BM_Downmix/13       3178 ns         3163 ns       220132 AUDIO_CHANNEL_OUT_5POINT1_SIDE
83 BM_Downmix/14      10245 ns        10199 ns        67486 AUDIO_CHANNEL_OUT_6POINT1
84 BM_Downmix/15      10975 ns        10929 ns        61359 AUDIO_CHANNEL_OUT_5POINT1POINT2
85 BM_Downmix/16       3796 ns         3780 ns       184728 AUDIO_CHANNEL_OUT_7POINT1
86 BM_Downmix/17      13562 ns        13503 ns        51823 AUDIO_CHANNEL_OUT_5POINT1POINT4
87 BM_Downmix/18      13573 ns        13516 ns        51800 AUDIO_CHANNEL_OUT_7POINT1POINT2
88 BM_Downmix/19      15502 ns        15435 ns        47147 AUDIO_CHANNEL_OUT_7POINT1POINT4
89 BM_Downmix/20      16693 ns        16624 ns        42109 AUDIO_CHANNEL_OUT_13POINT_360RA
90 BM_Downmix/21      28267 ns        28116 ns        24982 AUDIO_CHANNEL_OUT_22POINT2
91 */
92 
BM_Downmix(benchmark::State & state)93 static void BM_Downmix(benchmark::State& state) {
94     const audio_channel_mask_t channelMask = kChannelPositionMasks[state.range(0)];
95     const size_t channelCount = audio_channel_count_from_out_mask(channelMask);
96     const int sampleRate = 48000;
97 
98     // Initialize input buffer with deterministic pseudo-random values
99     std::minstd_rand gen(channelMask);
100     std::uniform_real_distribution<> dis(-1.0f, 1.0f);
101     std::vector<float> input(kFrameCount * channelCount);
102     std::vector<float> output(kFrameCount * FCC_2);
103     for (auto& in : input) {
104         in = dis(gen);
105     }
106     effect_handle_t effectHandle = nullptr;
107     if (int status = AUDIO_EFFECT_LIBRARY_INFO_SYM.create_effect(
108             &downmix_uuid, 1, 1, &effectHandle);
109         status != 0) {
110         ALOGE("create_effect returned an error = %d\n", status);
111         return;
112     }
113 
114     effect_config_t config{};
115     config.inputCfg.accessMode = EFFECT_BUFFER_ACCESS_READ;
116     config.inputCfg.format = AUDIO_FORMAT_PCM_FLOAT;
117     config.inputCfg.bufferProvider.getBuffer = nullptr;
118     config.inputCfg.bufferProvider.releaseBuffer = nullptr;
119     config.inputCfg.bufferProvider.cookie = nullptr;
120     config.inputCfg.mask = EFFECT_CONFIG_ALL;
121 
122     config.outputCfg.accessMode = EFFECT_BUFFER_ACCESS_WRITE;
123     config.outputCfg.format = AUDIO_FORMAT_PCM_FLOAT;
124     config.outputCfg.bufferProvider.getBuffer = nullptr;
125     config.outputCfg.bufferProvider.releaseBuffer = nullptr;
126     config.outputCfg.bufferProvider.cookie = nullptr;
127     config.outputCfg.mask = EFFECT_CONFIG_ALL;
128 
129     config.inputCfg.samplingRate = sampleRate;
130     config.inputCfg.channels = channelMask;
131 
132     config.outputCfg.samplingRate = sampleRate;
133     config.outputCfg.channels = AUDIO_CHANNEL_OUT_STEREO; // output always stereo
134 
135     int reply = 0;
136     uint32_t replySize = sizeof(reply);
137     if (int status = (*effectHandle)
138             ->command(effectHandle, EFFECT_CMD_SET_CONFIG, sizeof(effect_config_t),
139                     &config, &replySize, &reply);
140         status != 0) {
141         ALOGE("command returned an error = %d\n", status);
142         return;
143     }
144 
145     if (int status = (*effectHandle)
146             ->command(effectHandle, EFFECT_CMD_ENABLE, 0, nullptr, &replySize, &reply);
147         status != 0) {
148         ALOGE("Command enable call returned error %d\n", reply);
149         return;
150     }
151 
152     // Run the test
153     for (auto _ : state) {
154         benchmark::DoNotOptimize(input.data());
155         benchmark::DoNotOptimize(output.data());
156 
157         audio_buffer_t inBuffer = {.frameCount = kFrameCount, .f32 = input.data()};
158         audio_buffer_t outBuffer = {.frameCount = kFrameCount, .f32 = output.data()};
159         (*effectHandle)->process(effectHandle, &inBuffer, &outBuffer);
160 
161         benchmark::ClobberMemory();
162     }
163 
164     state.SetComplexityN(channelCount);
165     state.SetLabel(audio_channel_out_mask_to_string(channelMask));
166 
167     if (int status = AUDIO_EFFECT_LIBRARY_INFO_SYM.release_effect(effectHandle); status != 0) {
168         ALOGE("release_effect returned an error = %d\n", status);
169         return;
170     }
171 }
172 
DownmixArgs(benchmark::internal::Benchmark * b)173 static void DownmixArgs(benchmark::internal::Benchmark* b) {
174     for (int i = 0; i < (int)std::size(kChannelPositionMasks); i++) {
175         b->Args({i});
176     }
177 }
178 
179 BENCHMARK(BM_Downmix)->Apply(DownmixArgs);
180 
181 BENCHMARK_MAIN();
182