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