// Copyright (c) 2012 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. // MSVC++ requires this to be set before any other includes to get M_SQRT1_2. #define _USE_MATH_DEFINES #include <cmath> #include "base/strings/stringprintf.h" #include "media/audio/audio_parameters.h" #include "media/base/audio_bus.h" #include "media/base/channel_mixer.h" #include "testing/gtest/include/gtest/gtest.h" namespace media { // Number of frames to test with. enum { kFrames = 16 }; // Test all possible layout conversions can be constructed and mixed. TEST(ChannelMixerTest, ConstructAllPossibleLayouts) { for (ChannelLayout input_layout = CHANNEL_LAYOUT_MONO; input_layout <= CHANNEL_LAYOUT_MAX; input_layout = static_cast<ChannelLayout>(input_layout + 1)) { for (ChannelLayout output_layout = CHANNEL_LAYOUT_MONO; output_layout < CHANNEL_LAYOUT_STEREO_DOWNMIX; output_layout = static_cast<ChannelLayout>(output_layout + 1)) { // DISCRETE can't be tested here based on the current approach. // CHANNEL_LAYOUT_STEREO_AND_KEYBOARD_MIC is not mixable. if (input_layout == CHANNEL_LAYOUT_DISCRETE || input_layout == CHANNEL_LAYOUT_STEREO_AND_KEYBOARD_MIC || output_layout == CHANNEL_LAYOUT_DISCRETE || output_layout == CHANNEL_LAYOUT_STEREO_AND_KEYBOARD_MIC) { continue; } SCOPED_TRACE(base::StringPrintf( "Input Layout: %d, Output Layout: %d", input_layout, output_layout)); ChannelMixer mixer(input_layout, output_layout); scoped_ptr<AudioBus> input_bus = AudioBus::Create( ChannelLayoutToChannelCount(input_layout), kFrames); scoped_ptr<AudioBus> output_bus = AudioBus::Create( ChannelLayoutToChannelCount(output_layout), kFrames); for (int ch = 0; ch < input_bus->channels(); ++ch) std::fill(input_bus->channel(ch), input_bus->channel(ch) + kFrames, 1); mixer.Transform(input_bus.get(), output_bus.get()); } } } struct ChannelMixerTestData { ChannelMixerTestData(ChannelLayout input_layout, ChannelLayout output_layout, float* channel_values, int num_channel_values, float scale) : input_layout(input_layout), output_layout(output_layout), channel_values(channel_values), num_channel_values(num_channel_values), scale(scale) { input_channels = ChannelLayoutToChannelCount(input_layout); output_channels = ChannelLayoutToChannelCount(output_layout); } ChannelMixerTestData(ChannelLayout input_layout, int input_channels, ChannelLayout output_layout, int output_channels, float* channel_values, int num_channel_values) : input_layout(input_layout), input_channels(input_channels), output_layout(output_layout), output_channels(output_channels), channel_values(channel_values), num_channel_values(num_channel_values), scale(1.0f) { } std::string DebugString() const { return base::StringPrintf( "Input Layout: %d, Output Layout %d, Scale: %f", input_layout, output_layout, scale); } ChannelLayout input_layout; int input_channels; ChannelLayout output_layout; int output_channels; float* channel_values; int num_channel_values; float scale; }; std::ostream& operator<<(std::ostream& os, const ChannelMixerTestData& data) { return os << data.DebugString(); } class ChannelMixerTest : public testing::TestWithParam<ChannelMixerTestData> {}; // Verify channels are mixed and scaled correctly. The test only works if all // output channels have the same value. TEST_P(ChannelMixerTest, Mixing) { ChannelLayout input_layout = GetParam().input_layout; int input_channels = GetParam().input_channels; scoped_ptr<AudioBus> input_bus = AudioBus::Create(input_channels, kFrames); AudioParameters input_audio(AudioParameters::AUDIO_PCM_LINEAR, input_layout, input_layout == CHANNEL_LAYOUT_DISCRETE ? input_channels : ChannelLayoutToChannelCount(input_layout), 0, AudioParameters::kAudioCDSampleRate, 16, kFrames, AudioParameters::NO_EFFECTS); ChannelLayout output_layout = GetParam().output_layout; int output_channels = GetParam().output_channels; scoped_ptr<AudioBus> output_bus = AudioBus::Create(output_channels, kFrames); AudioParameters output_audio(AudioParameters::AUDIO_PCM_LINEAR, output_layout, output_layout == CHANNEL_LAYOUT_DISCRETE ? output_channels : ChannelLayoutToChannelCount(output_layout), 0, AudioParameters::kAudioCDSampleRate, 16, kFrames, AudioParameters::NO_EFFECTS); const float* channel_values = GetParam().channel_values; ASSERT_EQ(input_bus->channels(), GetParam().num_channel_values); float expected_value = 0; float scale = GetParam().scale; for (int ch = 0; ch < input_bus->channels(); ++ch) { std::fill(input_bus->channel(ch), input_bus->channel(ch) + kFrames, channel_values[ch]); expected_value += channel_values[ch] * scale; } ChannelMixer mixer(input_audio, output_audio); mixer.Transform(input_bus.get(), output_bus.get()); // Validate the output channel if (input_layout != CHANNEL_LAYOUT_DISCRETE) { for (int ch = 0; ch < output_bus->channels(); ++ch) { for (int frame = 0; frame < output_bus->frames(); ++frame) { ASSERT_FLOAT_EQ(output_bus->channel(ch)[frame], expected_value); } } } else { // Processing discrete mixing. If there is a matching input channel, // then the output channel should be set. If no input channel, // output channel should be 0 for (int ch = 0; ch < output_bus->channels(); ++ch) { expected_value = (ch < input_channels) ? channel_values[ch] : 0; for (int frame = 0; frame < output_bus->frames(); ++frame) { ASSERT_FLOAT_EQ(output_bus->channel(ch)[frame], expected_value); } } } } static float kStereoToMonoValues[] = { 0.5f, 0.75f }; static float kMonoToStereoValues[] = { 0.5f }; // Zero the center channel since it will be mixed at scale 1 vs M_SQRT1_2. static float kFiveOneToMonoValues[] = { 0.1f, 0.2f, 0.0f, 0.4f, 0.5f, 0.6f }; static float kFiveDiscreteValues[] = { 0.1f, 0.2f, 0.3f, 0.4f, 0.5f }; // Run through basic sanity tests for some common conversions. INSTANTIATE_TEST_CASE_P(ChannelMixerTest, ChannelMixerTest, testing::Values( ChannelMixerTestData(CHANNEL_LAYOUT_STEREO, CHANNEL_LAYOUT_MONO, kStereoToMonoValues, arraysize(kStereoToMonoValues), 0.5f), ChannelMixerTestData(CHANNEL_LAYOUT_MONO, CHANNEL_LAYOUT_STEREO, kMonoToStereoValues, arraysize(kMonoToStereoValues), 1.0f), ChannelMixerTestData(CHANNEL_LAYOUT_5_1, CHANNEL_LAYOUT_MONO, kFiveOneToMonoValues, arraysize(kFiveOneToMonoValues), static_cast<float>(M_SQRT1_2)), ChannelMixerTestData(CHANNEL_LAYOUT_DISCRETE, 2, CHANNEL_LAYOUT_DISCRETE, 2, kStereoToMonoValues, arraysize(kStereoToMonoValues)), ChannelMixerTestData(CHANNEL_LAYOUT_DISCRETE, 2, CHANNEL_LAYOUT_DISCRETE, 5, kStereoToMonoValues, arraysize(kStereoToMonoValues)), ChannelMixerTestData(CHANNEL_LAYOUT_DISCRETE, 5, CHANNEL_LAYOUT_DISCRETE, 2, kFiveDiscreteValues, arraysize(kFiveDiscreteValues)) )); } // namespace media