blob: 4a67d0ba75403b945c3684ce2d017fbd9f96d72d [file] [log] [blame]
Andy Hung546734b2014-04-01 18:31:42 -07001/*
2 * Copyright (C) 2014 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//#define LOG_NDEBUG 0
18#define LOG_TAG "audioflinger_resampler_tests"
19
20#include <unistd.h>
21#include <stdio.h>
22#include <stdlib.h>
23#include <fcntl.h>
24#include <string.h>
25#include <sys/mman.h>
26#include <sys/stat.h>
27#include <errno.h>
28#include <time.h>
29#include <math.h>
30#include <vector>
31#include <utility>
32#include <cutils/log.h>
33#include <gtest/gtest.h>
34#include <media/AudioBufferProvider.h>
35#include "AudioResampler.h"
Andy Hungc0e5ec82014-06-17 14:33:39 -070036#include "test_utils.h"
Andy Hung546734b2014-04-01 18:31:42 -070037
38void resample(void *output, size_t outputFrames, const std::vector<size_t> &outputIncr,
39 android::AudioBufferProvider *provider, android::AudioResampler *resampler)
40{
41 for (size_t i = 0, j = 0; i < outputFrames; ) {
42 size_t thisFrames = outputIncr[j++];
43 if (j >= outputIncr.size()) {
44 j = 0;
45 }
46 if (thisFrames == 0 || thisFrames > outputFrames - i) {
47 thisFrames = outputFrames - i;
48 }
49 resampler->resample((int32_t*) output + 2*i, thisFrames, provider);
50 i += thisFrames;
51 }
52}
53
54void buffercmp(const void *reference, const void *test,
55 size_t outputFrameSize, size_t outputFrames)
56{
57 for (size_t i = 0; i < outputFrames; ++i) {
58 int check = memcmp((const char*)reference + i * outputFrameSize,
59 (const char*)test + i * outputFrameSize, outputFrameSize);
60 if (check) {
61 ALOGE("Failure at frame %d", i);
62 ASSERT_EQ(check, 0); /* fails */
63 }
64 }
65}
66
67void testBufferIncrement(size_t channels, unsigned inputFreq, unsigned outputFreq,
68 enum android::AudioResampler::src_quality quality)
69{
70 // create the provider
Andy Hungc0e5ec82014-06-17 14:33:39 -070071 std::vector<int> inputIncr;
72 SignalProvider provider;
73 provider.setChirp<int16_t>(channels,
74 0., outputFreq/2., outputFreq, outputFreq/2000.);
75 provider.setIncr(inputIncr);
Andy Hung546734b2014-04-01 18:31:42 -070076
77 // calculate the output size
78 size_t outputFrames = ((int64_t) provider.getNumFrames() * outputFreq) / inputFreq;
79 size_t outputFrameSize = 2 * sizeof(int32_t);
80 size_t outputSize = outputFrameSize * outputFrames;
81 outputSize &= ~7;
82
83 // create the resampler
84 const int volumePrecision = 12; /* typical unity gain */
85 android::AudioResampler* resampler;
86
87 resampler = android::AudioResampler::create(16, channels, outputFreq, quality);
88 resampler->setSampleRate(inputFreq);
89 resampler->setVolume(1 << volumePrecision, 1 << volumePrecision);
90
91 // set up the reference run
92 std::vector<size_t> refIncr;
93 refIncr.push_back(outputFrames);
94 void* reference = malloc(outputSize);
95 resample(reference, outputFrames, refIncr, &provider, resampler);
96
97 provider.reset();
98
99#if 0
100 /* this test will fail - API interface issue: reset() does not clear internal buffers */
101 resampler->reset();
102#else
103 delete resampler;
104 resampler = android::AudioResampler::create(16, channels, outputFreq, quality);
105 resampler->setSampleRate(inputFreq);
106 resampler->setVolume(1 << volumePrecision, 1 << volumePrecision);
107#endif
108
109 // set up the test run
110 std::vector<size_t> outIncr;
111 outIncr.push_back(1);
112 outIncr.push_back(2);
113 outIncr.push_back(3);
114 void* test = malloc(outputSize);
115 resample(test, outputFrames, outIncr, &provider, resampler);
116
117 // check
118 buffercmp(reference, test, outputFrameSize, outputFrames);
119
120 free(reference);
121 free(test);
122 delete resampler;
123}
124
125template <typename T>
126inline double sqr(T v)
127{
128 double dv = static_cast<double>(v);
129 return dv * dv;
130}
131
132template <typename T>
133double signalEnergy(T *start, T *end, unsigned stride)
134{
135 double accum = 0;
136
137 for (T *p = start; p < end; p += stride) {
138 accum += sqr(*p);
139 }
140 unsigned count = (end - start + stride - 1) / stride;
141 return accum / count;
142}
143
144void testStopbandDownconversion(size_t channels,
145 unsigned inputFreq, unsigned outputFreq,
146 unsigned passband, unsigned stopband,
147 enum android::AudioResampler::src_quality quality)
148{
149 // create the provider
Andy Hungc0e5ec82014-06-17 14:33:39 -0700150 std::vector<int> inputIncr;
151 SignalProvider provider;
152 provider.setChirp<int16_t>(channels,
153 0., inputFreq/2., inputFreq, inputFreq/2000.);
154 provider.setIncr(inputIncr);
Andy Hung546734b2014-04-01 18:31:42 -0700155
156 // calculate the output size
157 size_t outputFrames = ((int64_t) provider.getNumFrames() * outputFreq) / inputFreq;
158 size_t outputFrameSize = 2 * sizeof(int32_t);
159 size_t outputSize = outputFrameSize * outputFrames;
160 outputSize &= ~7;
161
162 // create the resampler
163 const int volumePrecision = 12; /* typical unity gain */
164 android::AudioResampler* resampler;
165
166 resampler = android::AudioResampler::create(16, channels, outputFreq, quality);
167 resampler->setSampleRate(inputFreq);
168 resampler->setVolume(1 << volumePrecision, 1 << volumePrecision);
169
170 // set up the reference run
171 std::vector<size_t> refIncr;
172 refIncr.push_back(outputFrames);
173 void* reference = malloc(outputSize);
174 resample(reference, outputFrames, refIncr, &provider, resampler);
175
176 int32_t *out = reinterpret_cast<int32_t *>(reference);
177
178 // check signal energy in passband
179 const unsigned passbandFrame = passband * outputFreq / 1000.;
180 const unsigned stopbandFrame = stopband * outputFreq / 1000.;
181
182 // check each channel separately
183 for (size_t i = 0; i < channels; ++i) {
184 double passbandEnergy = signalEnergy(out, out + passbandFrame * channels, channels);
185 double stopbandEnergy = signalEnergy(out + stopbandFrame * channels,
186 out + outputFrames * channels, channels);
187 double dbAtten = -10. * log10(stopbandEnergy / passbandEnergy);
188 ASSERT_GT(dbAtten, 60.);
189
190#if 0
191 // internal verification
192 printf("if:%d of:%d pbf:%d sbf:%d sbe: %f pbe: %f db: %.2f\n",
193 provider.getNumFrames(), outputFrames,
194 passbandFrame, stopbandFrame, stopbandEnergy, passbandEnergy, dbAtten);
195 for (size_t i = 0; i < 10; ++i) {
196 printf("%d\n", out[i+passbandFrame*channels]);
197 }
198 for (size_t i = 0; i < 10; ++i) {
199 printf("%d\n", out[i+stopbandFrame*channels]);
200 }
201#endif
202 }
203
204 free(reference);
205 delete resampler;
206}
207
208/* Buffer increment test
209 *
210 * We compare a reference output, where we consume and process the entire
211 * buffer at a time, and a test output, where we provide small chunks of input
212 * data and process small chunks of output (which may not be equivalent in size).
213 *
214 * Two subtests - fixed phase (3:2 down) and interpolated phase (147:320 up)
215 */
216TEST(audioflinger_resampler, bufferincrement_fixedphase) {
217 // all of these work
218 static const enum android::AudioResampler::src_quality kQualityArray[] = {
219 android::AudioResampler::LOW_QUALITY,
220 android::AudioResampler::MED_QUALITY,
221 android::AudioResampler::HIGH_QUALITY,
222 android::AudioResampler::VERY_HIGH_QUALITY,
223 android::AudioResampler::DYN_LOW_QUALITY,
224 android::AudioResampler::DYN_MED_QUALITY,
225 android::AudioResampler::DYN_HIGH_QUALITY,
226 };
227
228 for (size_t i = 0; i < ARRAY_SIZE(kQualityArray); ++i) {
229 testBufferIncrement(2, 48000, 32000, kQualityArray[i]);
230 }
231}
232
233TEST(audioflinger_resampler, bufferincrement_interpolatedphase) {
234 // all of these work except low quality
235 static const enum android::AudioResampler::src_quality kQualityArray[] = {
236// android::AudioResampler::LOW_QUALITY,
237 android::AudioResampler::MED_QUALITY,
238 android::AudioResampler::HIGH_QUALITY,
239 android::AudioResampler::VERY_HIGH_QUALITY,
240 android::AudioResampler::DYN_LOW_QUALITY,
241 android::AudioResampler::DYN_MED_QUALITY,
242 android::AudioResampler::DYN_HIGH_QUALITY,
243 };
244
245 for (size_t i = 0; i < ARRAY_SIZE(kQualityArray); ++i) {
246 testBufferIncrement(2, 22050, 48000, kQualityArray[i]);
247 }
248}
249
250/* Simple aliasing test
251 *
252 * This checks stopband response of the chirp signal to make sure frequencies
253 * are properly suppressed. It uses downsampling because the stopband can be
254 * clearly isolated by input frequencies exceeding the output sample rate (nyquist).
255 */
256TEST(audioflinger_resampler, stopbandresponse) {
257 // not all of these may work (old resamplers fail on downsampling)
258 static const enum android::AudioResampler::src_quality kQualityArray[] = {
259 //android::AudioResampler::LOW_QUALITY,
260 //android::AudioResampler::MED_QUALITY,
261 //android::AudioResampler::HIGH_QUALITY,
262 //android::AudioResampler::VERY_HIGH_QUALITY,
263 android::AudioResampler::DYN_LOW_QUALITY,
264 android::AudioResampler::DYN_MED_QUALITY,
265 android::AudioResampler::DYN_HIGH_QUALITY,
266 };
267
268 // in this test we assume a maximum transition band between 12kHz and 20kHz.
269 // there must be at least 60dB relative attenuation between stopband and passband.
270 for (size_t i = 0; i < ARRAY_SIZE(kQualityArray); ++i) {
271 testStopbandDownconversion(2, 48000, 32000, 12000, 20000, kQualityArray[i]);
272 }
273
274 // in this test we assume a maximum transition band between 7kHz and 15kHz.
275 // there must be at least 60dB relative attenuation between stopband and passband.
276 // (the weird ratio triggers interpolative resampling)
277 for (size_t i = 0; i < ARRAY_SIZE(kQualityArray); ++i) {
278 testStopbandDownconversion(2, 48000, 22101, 7000, 15000, kQualityArray[i]);
279 }
280}