blob: 913feb03337bca0a36be845aa77f2985eefd4e61 [file] [log] [blame]
Phil Burkbb78a732018-03-28 15:37:19 -07001/*
2 * Copyright 2018 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/*
18 * Test FlowGraph
19 */
20
21#include <iostream>
22
23#include <gtest/gtest.h>
24
25#include "flowgraph/ClipToRange.h"
Robert Wud7400832021-12-04 01:11:19 +000026#include "flowgraph/MonoBlend.h"
Phil Burkbb78a732018-03-28 15:37:19 -070027#include "flowgraph/MonoToMultiConverter.h"
28#include "flowgraph/SourceFloat.h"
29#include "flowgraph/RampLinear.h"
30#include "flowgraph/SinkFloat.h"
31#include "flowgraph/SinkI16.h"
32#include "flowgraph/SinkI24.h"
33#include "flowgraph/SourceI16.h"
34#include "flowgraph/SourceI24.h"
35
Robert Wuedc850a2022-04-05 16:47:03 +000036using namespace FLOWGRAPH_OUTER_NAMESPACE::flowgraph;
Phil Burkbb78a732018-03-28 15:37:19 -070037
38constexpr int kBytesPerI24Packed = 3;
39
Phil Burk30e58932022-03-29 00:12:44 +000040// Simple test that tries to reproduce a Clang compiler bug.
41__attribute__((noinline))
42void local_convert_float_to_int16(const float *input,
43 int16_t *output,
44 int count) {
45 for (int i = 0; i < count; i++) {
46 int32_t n = (int32_t) (*input++ * 32768.0f);
47 *output++ = std::min(INT16_MAX, std::max(INT16_MIN, n)); // clip
48 }
49}
50
51TEST(test_flowgraph, local_convert_float_int16) {
52 static constexpr int kNumSamples = 8;
53 static constexpr std::array<float, kNumSamples> input = {
54 1.0f, 0.5f, -0.25f, -1.0f,
55 0.0f, 53.9f, -87.2f, -1.02f};
56 static constexpr std::array<int16_t, kNumSamples> expected = {
57 32767, 16384, -8192, -32768,
58 0, 32767, -32768, -32768};
59 std::array<int16_t, kNumSamples> output;
60
61 // Do it inline, which will probably work even with the buggy compiler.
62 // This validates the expected data.
63 const float *in = input.data();
64 int16_t *out = output.data();
65 output.fill(777);
66 for (int i = 0; i < kNumSamples; i++) {
67 int32_t n = (int32_t) (*in++ * 32768.0f);
68 *out++ = std::min(INT16_MAX, std::max(INT16_MIN, n)); // clip
69 }
70 for (int i = 0; i < kNumSamples; i++) {
71 EXPECT_EQ(expected.at(i), output.at(i)) << ", i = " << i;
72 }
73
74 // Convert audio signal using the function.
75 output.fill(777);
76 local_convert_float_to_int16(input.data(), output.data(), kNumSamples);
77 for (int i = 0; i < kNumSamples; i++) {
78 EXPECT_EQ(expected.at(i), output.at(i)) << ", i = " << i;
79 }
80}
81
Phil Burkbb78a732018-03-28 15:37:19 -070082TEST(test_flowgraph, module_sinki16) {
Phil Burk30e58932022-03-29 00:12:44 +000083 static constexpr int kNumSamples = 8;
84 static constexpr std::array<float, kNumSamples> input = {
85 1.0f, 0.5f, -0.25f, -1.0f,
86 0.0f, 53.9f, -87.2f, -1.02f};
87 static constexpr std::array<int16_t, kNumSamples> expected = {
88 32767, 16384, -8192, -32768,
89 0, 32767, -32768, -32768};
90 std::array<int16_t, kNumSamples + 10> output; // larger than input
91
Phil Burkbb78a732018-03-28 15:37:19 -070092 SourceFloat sourceFloat{1};
93 SinkI16 sinkI16{1};
94
Phil Burk30e58932022-03-29 00:12:44 +000095 sourceFloat.setData(input.data(), kNumSamples);
Phil Burkbb78a732018-03-28 15:37:19 -070096 sourceFloat.output.connect(&sinkI16.input);
97
Phil Burk30e58932022-03-29 00:12:44 +000098 output.fill(777);
99 int32_t numRead = sinkI16.read(output.data(), output.size());
100 ASSERT_EQ(kNumSamples, numRead);
Phil Burkbb78a732018-03-28 15:37:19 -0700101 for (int i = 0; i < numRead; i++) {
Phil Burk30e58932022-03-29 00:12:44 +0000102 EXPECT_EQ(expected.at(i), output.at(i)) << ", i = " << i;
Phil Burkbb78a732018-03-28 15:37:19 -0700103 }
104}
105
106TEST(test_flowgraph, module_mono_to_stereo) {
107 static const float input[] = {1.0f, 2.0f, 3.0f};
108 float output[100] = {};
109 SourceFloat sourceFloat{1};
110 MonoToMultiConverter monoToStereo{2};
111 SinkFloat sinkFloat{2};
112
113 sourceFloat.setData(input, 3);
114
115 sourceFloat.output.connect(&monoToStereo.input);
116 monoToStereo.output.connect(&sinkFloat.input);
117
118 int32_t numRead = sinkFloat.read(output, 8);
119 ASSERT_EQ(3, numRead);
120 EXPECT_EQ(input[0], output[0]);
121 EXPECT_EQ(input[0], output[1]);
122 EXPECT_EQ(input[1], output[2]);
123 EXPECT_EQ(input[1], output[3]);
124}
125
126TEST(test_flowgraph, module_ramp_linear) {
Robert Wuc3278fe2021-06-23 16:51:40 +0000127 constexpr int singleNumOutput = 1;
Phil Burkbb78a732018-03-28 15:37:19 -0700128 constexpr int rampSize = 5;
129 constexpr int numOutput = 100;
130 constexpr float value = 1.0f;
Robert Wuc3278fe2021-06-23 16:51:40 +0000131 constexpr float initialTarget = 10.0f;
132 constexpr float finalTarget = 100.0f;
133 constexpr float tolerance = 0.0001f; // arbitrary
Phil Burkbb78a732018-03-28 15:37:19 -0700134 float output[numOutput] = {};
135 RampLinear rampLinear{1};
136 SinkFloat sinkFloat{1};
137
138 rampLinear.input.setValue(value);
139 rampLinear.setLengthInFrames(rampSize);
Phil Burkbb78a732018-03-28 15:37:19 -0700140 rampLinear.output.connect(&sinkFloat.input);
141
Robert Wuc3278fe2021-06-23 16:51:40 +0000142 // Check that the values go to the initial target instantly.
143 rampLinear.setTarget(initialTarget);
144 int32_t singleNumRead = sinkFloat.read(output, singleNumOutput);
145 ASSERT_EQ(singleNumRead, singleNumOutput);
146 EXPECT_NEAR(value * initialTarget, output[0], tolerance);
147
148 // Now set target and check that the linear ramp works as expected.
149 rampLinear.setTarget(finalTarget);
Phil Burkbb78a732018-03-28 15:37:19 -0700150 int32_t numRead = sinkFloat.read(output, numOutput);
Robert Wuc3278fe2021-06-23 16:51:40 +0000151 const float incrementSize = (finalTarget - initialTarget) / rampSize;
Phil Burkbb78a732018-03-28 15:37:19 -0700152 ASSERT_EQ(numOutput, numRead);
Robert Wuc3278fe2021-06-23 16:51:40 +0000153
Phil Burkbb78a732018-03-28 15:37:19 -0700154 int i = 0;
155 for (; i < rampSize; i++) {
Robert Wuc3278fe2021-06-23 16:51:40 +0000156 float expected = value * (initialTarget + i * incrementSize);
Phil Burkbb78a732018-03-28 15:37:19 -0700157 EXPECT_NEAR(expected, output[i], tolerance);
158 }
159 for (; i < numOutput; i++) {
Robert Wuc3278fe2021-06-23 16:51:40 +0000160 float expected = value * finalTarget;
Phil Burkbb78a732018-03-28 15:37:19 -0700161 EXPECT_NEAR(expected, output[i], tolerance);
162 }
163}
164
165// It is easiest to represent packed 24-bit data as a byte array.
166// This test will read from input, convert to float, then write
167// back to output as bytes.
168TEST(test_flowgraph, module_packed_24) {
169 static const uint8_t input[] = {0x01, 0x23, 0x45,
170 0x67, 0x89, 0xAB,
171 0xCD, 0xEF, 0x5A};
172 uint8_t output[99] = {};
173 SourceI24 sourceI24{1};
174 SinkI24 sinkI24{1};
175
176 int numInputFrames = sizeof(input) / kBytesPerI24Packed;
177 sourceI24.setData(input, numInputFrames);
178 sourceI24.output.connect(&sinkI24.input);
179
180 int32_t numRead = sinkI24.read(output, sizeof(output) / kBytesPerI24Packed);
181 ASSERT_EQ(numInputFrames, numRead);
182 for (size_t i = 0; i < sizeof(input); i++) {
183 EXPECT_EQ(input[i], output[i]);
184 }
185}
186
187TEST(test_flowgraph, module_clip_to_range) {
188 constexpr float myMin = -2.0f;
189 constexpr float myMax = 1.5f;
190
191 static const float input[] = {-9.7, 0.5f, -0.25, 1.0f, 12.3};
192 static const float expected[] = {myMin, 0.5f, -0.25, 1.0f, myMax};
193 float output[100];
194 SourceFloat sourceFloat{1};
195 ClipToRange clipper{1};
196 SinkFloat sinkFloat{1};
197
198 int numInputFrames = sizeof(input) / sizeof(input[0]);
199 sourceFloat.setData(input, numInputFrames);
200
201 clipper.setMinimum(myMin);
202 clipper.setMaximum(myMax);
203
204 sourceFloat.output.connect(&clipper.input);
205 clipper.output.connect(&sinkFloat.input);
206
207 int numOutputFrames = sizeof(output) / sizeof(output[0]);
208 int32_t numRead = sinkFloat.read(output, numOutputFrames);
209 ASSERT_EQ(numInputFrames, numRead);
210 constexpr float tolerance = 0.000001f; // arbitrary
211 for (int i = 0; i < numRead; i++) {
212 EXPECT_NEAR(expected[i], output[i], tolerance);
213 }
214}
Robert Wud7400832021-12-04 01:11:19 +0000215
216TEST(test_flowgraph, module_mono_blend) {
217 // Two channel to two channel with 3 inputs and outputs.
218 constexpr int numChannels = 2;
219 constexpr int numFrames = 3;
220
221 static const float input[] = {-0.7, 0.5, -0.25, 1.25, 1000, 2000};
222 static const float expected[] = {-0.1, -0.1, 0.5, 0.5, 1500, 1500};
223 float output[100];
224 SourceFloat sourceFloat{numChannels};
225 MonoBlend monoBlend{numChannels};
226 SinkFloat sinkFloat{numChannels};
227
228 sourceFloat.setData(input, numFrames);
229
230 sourceFloat.output.connect(&monoBlend.input);
231 monoBlend.output.connect(&sinkFloat.input);
232
233 int32_t numRead = sinkFloat.read(output, numFrames);
234 ASSERT_EQ(numRead, numFrames);
235 constexpr float tolerance = 0.000001f; // arbitrary
236 for (int i = 0; i < numRead; i++) {
237 EXPECT_NEAR(expected[i], output[i], tolerance);
238 }
239}
240