You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
240 lines
11 KiB
240 lines
11 KiB
4 months ago
|
/*
|
||
|
* Copyright 2021 The Android Open Source Project
|
||
|
*
|
||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||
|
* you may not use this file except in compliance with the License.
|
||
|
* You may obtain a copy of the License at
|
||
|
*
|
||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||
|
*
|
||
|
* Unless required by applicable law or agreed to in writing, software
|
||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||
|
* See the License for the specific language governing permissions and
|
||
|
* limitations under the License.
|
||
|
*/
|
||
|
|
||
|
#include <audio_effects/effect_presetreverb.h>
|
||
|
#include <VectorArithmetic.h>
|
||
|
|
||
|
#include "EffectTestHelper.h"
|
||
|
using namespace android;
|
||
|
|
||
|
constexpr effect_uuid_t kEffectUuids[] = {
|
||
|
// NXP SW insert environmental reverb
|
||
|
{0xc7a511a0, 0xa3bb, 0x11df, 0x860e, {0x00, 0x02, 0xa5, 0xd5, 0xc5, 0x1b}},
|
||
|
// NXP SW insert preset reverb
|
||
|
{0x172cdf00, 0xa3bc, 0x11df, 0xa72f, {0x00, 0x02, 0xa5, 0xd5, 0xc5, 0x1b}},
|
||
|
// NXP SW auxiliary environmental reverb
|
||
|
{0x4a387fc0, 0x8ab3, 0x11df, 0x8bad, {0x00, 0x02, 0xa5, 0xd5, 0xc5, 0x1b}},
|
||
|
// NXP SW auxiliary preset reverb
|
||
|
{0xf29a1400, 0xa3bb, 0x11df, 0x8ddc, {0x00, 0x02, 0xa5, 0xd5, 0xc5, 0x1b}},
|
||
|
};
|
||
|
|
||
|
constexpr size_t kNumEffectUuids = std::size(kEffectUuids);
|
||
|
|
||
|
static bool isAuxMode(const effect_uuid_t* uuid) {
|
||
|
// Update this, if the order of effects in kEffectUuids is updated
|
||
|
return (uuid == &kEffectUuids[2] || uuid == &kEffectUuids[3]);
|
||
|
}
|
||
|
|
||
|
constexpr int kPresets[] = {
|
||
|
REVERB_PRESET_NONE, REVERB_PRESET_SMALLROOM, REVERB_PRESET_MEDIUMROOM,
|
||
|
REVERB_PRESET_LARGEROOM, REVERB_PRESET_MEDIUMHALL, REVERB_PRESET_LARGEHALL,
|
||
|
REVERB_PRESET_PLATE,
|
||
|
};
|
||
|
|
||
|
constexpr size_t kNumPresets = std::size(kPresets);
|
||
|
|
||
|
typedef std::tuple<int, int, int, int, int, int> SingleEffectTestParam;
|
||
|
class SingleEffectTest : public ::testing::TestWithParam<SingleEffectTestParam> {
|
||
|
public:
|
||
|
SingleEffectTest()
|
||
|
: mSampleRate(EffectTestHelper::kSampleRates[std::get<1>(GetParam())]),
|
||
|
mFrameCount(EffectTestHelper::kFrameCounts[std::get<2>(GetParam())]),
|
||
|
mLoopCount(EffectTestHelper::kLoopCounts[std::get<3>(GetParam())]),
|
||
|
mTotalFrameCount(mFrameCount * mLoopCount),
|
||
|
mUuid(&kEffectUuids[std::get<4>(GetParam())]),
|
||
|
mInChMask(isAuxMode(mUuid) ? AUDIO_CHANNEL_OUT_MONO
|
||
|
: EffectTestHelper::kChMasks[std::get<0>(GetParam())]),
|
||
|
mInChannelCount(audio_channel_count_from_out_mask(mInChMask)),
|
||
|
mOutChMask(EffectTestHelper::kChMasks[std::get<0>(GetParam())]),
|
||
|
mOutChannelCount(audio_channel_count_from_out_mask(mOutChMask)),
|
||
|
mPreset(kPresets[std::get<5>(GetParam())]) {}
|
||
|
|
||
|
const size_t mSampleRate;
|
||
|
const size_t mFrameCount;
|
||
|
const size_t mLoopCount;
|
||
|
const size_t mTotalFrameCount;
|
||
|
const effect_uuid_t* mUuid;
|
||
|
const size_t mInChMask;
|
||
|
const size_t mInChannelCount;
|
||
|
const size_t mOutChMask;
|
||
|
const size_t mOutChannelCount;
|
||
|
const size_t mPreset;
|
||
|
};
|
||
|
|
||
|
// Tests applying a single effect
|
||
|
TEST_P(SingleEffectTest, SimpleProcess) {
|
||
|
SCOPED_TRACE(testing::Message() << "outChMask: " << mOutChMask << " sampleRate: " << mSampleRate
|
||
|
<< " frameCount: " << mFrameCount
|
||
|
<< " loopCount: " << mLoopCount << " preset: " << mPreset);
|
||
|
|
||
|
EffectTestHelper effect(mUuid, mInChMask, mOutChMask, mSampleRate, mFrameCount, mLoopCount);
|
||
|
|
||
|
ASSERT_NO_FATAL_FAILURE(effect.createEffect());
|
||
|
ASSERT_NO_FATAL_FAILURE(effect.setConfig());
|
||
|
ASSERT_NO_FATAL_FAILURE(effect.setParam(REVERB_PARAM_PRESET, mPreset));
|
||
|
|
||
|
// Initialize input buffer with deterministic pseudo-random values
|
||
|
std::vector<float> input(mTotalFrameCount * mInChannelCount);
|
||
|
std::vector<float> output(mTotalFrameCount * mOutChannelCount);
|
||
|
std::minstd_rand gen(mOutChMask);
|
||
|
std::uniform_real_distribution<> dis(-1.0f, 1.0f);
|
||
|
for (auto& in : input) {
|
||
|
in = dis(gen);
|
||
|
}
|
||
|
ASSERT_NO_FATAL_FAILURE(effect.process(input.data(), output.data()));
|
||
|
ASSERT_NO_FATAL_FAILURE(effect.releaseEffect());
|
||
|
}
|
||
|
|
||
|
INSTANTIATE_TEST_SUITE_P(
|
||
|
EffectReverbTestAll, SingleEffectTest,
|
||
|
::testing::Combine(::testing::Range(0, (int)EffectTestHelper::kNumChMasks),
|
||
|
::testing::Range(0, (int)EffectTestHelper::kNumSampleRates),
|
||
|
::testing::Range(0, (int)EffectTestHelper::kNumFrameCounts),
|
||
|
::testing::Range(0, (int)EffectTestHelper::kNumLoopCounts),
|
||
|
::testing::Range(0, (int)kNumEffectUuids),
|
||
|
::testing::Range(0, (int)kNumPresets)));
|
||
|
|
||
|
typedef std::tuple<int, int, int, int, int> SingleEffectComparisonTestParam;
|
||
|
class SingleEffectComparisonTest
|
||
|
: public ::testing::TestWithParam<SingleEffectComparisonTestParam> {
|
||
|
public:
|
||
|
SingleEffectComparisonTest()
|
||
|
: mSampleRate(EffectTestHelper::kSampleRates[std::get<0>(GetParam())]),
|
||
|
mFrameCount(EffectTestHelper::kFrameCounts[std::get<1>(GetParam())]),
|
||
|
mLoopCount(EffectTestHelper::kLoopCounts[std::get<2>(GetParam())]),
|
||
|
mTotalFrameCount(mFrameCount * mLoopCount),
|
||
|
mUuid(&kEffectUuids[std::get<3>(GetParam())]),
|
||
|
mPreset(kPresets[std::get<4>(GetParam())]) {}
|
||
|
|
||
|
const size_t mSampleRate;
|
||
|
const size_t mFrameCount;
|
||
|
const size_t mLoopCount;
|
||
|
const size_t mTotalFrameCount;
|
||
|
const effect_uuid_t* mUuid;
|
||
|
const size_t mPreset;
|
||
|
};
|
||
|
|
||
|
// Compares first two channels in multi-channel output to stereo output when same effect is applied
|
||
|
TEST_P(SingleEffectComparisonTest, SimpleProcess) {
|
||
|
SCOPED_TRACE(testing::Message()
|
||
|
<< " sampleRate: " << mSampleRate << " frameCount: " << mFrameCount
|
||
|
<< " loopCount: " << mLoopCount << " preset: " << mPreset);
|
||
|
|
||
|
// Initialize mono input buffer with deterministic pseudo-random values
|
||
|
std::vector<float> monoInput(mTotalFrameCount);
|
||
|
|
||
|
std::minstd_rand gen(mSampleRate);
|
||
|
std::uniform_real_distribution<> dis(-1.0f, 1.0f);
|
||
|
for (auto& in : monoInput) {
|
||
|
in = dis(gen);
|
||
|
}
|
||
|
|
||
|
// Generate stereo by repeating mono channel data
|
||
|
std::vector<float> stereoInput(mTotalFrameCount * FCC_2);
|
||
|
adjust_channels(monoInput.data(), FCC_1, stereoInput.data(), FCC_2, sizeof(float),
|
||
|
mTotalFrameCount * sizeof(float) * FCC_1);
|
||
|
|
||
|
// Apply effect on stereo channels
|
||
|
EffectTestHelper stereoEffect(
|
||
|
mUuid, isAuxMode(mUuid) ? AUDIO_CHANNEL_OUT_MONO : AUDIO_CHANNEL_OUT_STEREO,
|
||
|
AUDIO_CHANNEL_OUT_STEREO, mSampleRate, mFrameCount, mLoopCount);
|
||
|
|
||
|
ASSERT_NO_FATAL_FAILURE(stereoEffect.createEffect());
|
||
|
ASSERT_NO_FATAL_FAILURE(stereoEffect.setConfig());
|
||
|
ASSERT_NO_FATAL_FAILURE(stereoEffect.setParam(REVERB_PARAM_PRESET, mPreset));
|
||
|
|
||
|
std::vector<float> stereoOutput(mTotalFrameCount * FCC_2);
|
||
|
ASSERT_NO_FATAL_FAILURE(stereoEffect.process(
|
||
|
(isAuxMode(mUuid) ? monoInput.data() : stereoInput.data()), stereoOutput.data()));
|
||
|
ASSERT_NO_FATAL_FAILURE(stereoEffect.releaseEffect());
|
||
|
|
||
|
// Average of both channels data is stored for mono comparison
|
||
|
std::vector<float> monoOutput(mTotalFrameCount);
|
||
|
From2iToMono_Float((const float*)stereoOutput.data(), monoOutput.data(), mTotalFrameCount);
|
||
|
|
||
|
// Convert stereo float data to stereo int16_t to be used as reference
|
||
|
std::vector<int16_t> stereoRefI16(mTotalFrameCount * FCC_2);
|
||
|
memcpy_to_i16_from_float(stereoRefI16.data(), stereoOutput.data(), mTotalFrameCount * FCC_2);
|
||
|
|
||
|
// mono int16_t to be used as refernece for mono comparison
|
||
|
std::vector<int16_t> monoRefI16(mTotalFrameCount);
|
||
|
memcpy_to_i16_from_float(monoRefI16.data(), monoOutput.data(), mTotalFrameCount);
|
||
|
|
||
|
for (size_t outChMask : EffectTestHelper::kChMasks) {
|
||
|
size_t outChannelCount = audio_channel_count_from_out_mask(outChMask);
|
||
|
size_t inChMask = isAuxMode(mUuid) ? AUDIO_CHANNEL_OUT_MONO : outChMask;
|
||
|
|
||
|
EffectTestHelper testEffect(mUuid, inChMask, outChMask, mSampleRate, mFrameCount,
|
||
|
mLoopCount);
|
||
|
|
||
|
ASSERT_NO_FATAL_FAILURE(testEffect.createEffect());
|
||
|
ASSERT_NO_FATAL_FAILURE(testEffect.setConfig());
|
||
|
ASSERT_NO_FATAL_FAILURE(testEffect.setParam(REVERB_PARAM_PRESET, mPreset));
|
||
|
|
||
|
std::vector<float> testInput(mTotalFrameCount * outChannelCount);
|
||
|
|
||
|
// Repeat mono channel data to all the channels
|
||
|
// adjust_channels() zero fills channels > 2, hence can't be used here
|
||
|
for (size_t i = 0; i < mTotalFrameCount; ++i) {
|
||
|
auto* fp = &testInput[i * outChannelCount];
|
||
|
std::fill(fp, fp + outChannelCount, monoInput[i]);
|
||
|
}
|
||
|
|
||
|
std::vector<float> testOutput(mTotalFrameCount * outChannelCount);
|
||
|
ASSERT_NO_FATAL_FAILURE(testEffect.process(
|
||
|
(isAuxMode(mUuid) ? monoInput.data() : testInput.data()), testOutput.data()));
|
||
|
ASSERT_NO_FATAL_FAILURE(testEffect.releaseEffect());
|
||
|
|
||
|
if (outChannelCount == FCC_1) {
|
||
|
// Convert the test data to int16_t
|
||
|
std::vector<int16_t> monoTestI16(mTotalFrameCount);
|
||
|
memcpy_to_i16_from_float(monoTestI16.data(), testOutput.data(), mTotalFrameCount);
|
||
|
|
||
|
ASSERT_EQ(0, memcmp(monoRefI16.data(), monoTestI16.data(), mTotalFrameCount * FCC_2))
|
||
|
<< "Mono channel do not match with reference output \n";
|
||
|
} else {
|
||
|
// Extract first two channels
|
||
|
std::vector<float> stereoTestOutput(mTotalFrameCount * FCC_2);
|
||
|
adjust_channels(testOutput.data(), outChannelCount, stereoTestOutput.data(), FCC_2,
|
||
|
sizeof(float), mTotalFrameCount * sizeof(float) * outChannelCount);
|
||
|
|
||
|
// Convert the test data to int16_t
|
||
|
std::vector<int16_t> stereoTestI16(mTotalFrameCount * FCC_2);
|
||
|
memcpy_to_i16_from_float(stereoTestI16.data(), stereoTestOutput.data(),
|
||
|
mTotalFrameCount * FCC_2);
|
||
|
|
||
|
ASSERT_EQ(0,
|
||
|
memcmp(stereoRefI16.data(), stereoTestI16.data(), mTotalFrameCount * FCC_2))
|
||
|
<< "First two channels do not match with stereo output \n";
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
INSTANTIATE_TEST_SUITE_P(
|
||
|
EffectReverbTestAll, SingleEffectComparisonTest,
|
||
|
::testing::Combine(::testing::Range(0, (int)EffectTestHelper::kNumSampleRates),
|
||
|
::testing::Range(0, (int)EffectTestHelper::kNumFrameCounts),
|
||
|
::testing::Range(0, (int)EffectTestHelper::kNumLoopCounts),
|
||
|
::testing::Range(0, (int)kNumEffectUuids),
|
||
|
::testing::Range(0, (int)kNumPresets)));
|
||
|
|
||
|
int main(int argc, char** argv) {
|
||
|
::testing::InitGoogleTest(&argc, argv);
|
||
|
int status = RUN_ALL_TESTS();
|
||
|
ALOGV("Test result = %d\n", status);
|
||
|
return status;
|
||
|
}
|