1// Copyright (c) 2012 The Chromium Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5// MSVC++ requires this to be set before any other includes to get M_SQRT1_2. 6#define _USE_MATH_DEFINES 7 8#include <cmath> 9 10#include "base/strings/stringprintf.h" 11#include "media/audio/audio_parameters.h" 12#include "media/base/audio_bus.h" 13#include "media/base/channel_mixer.h" 14#include "testing/gtest/include/gtest/gtest.h" 15 16namespace media { 17 18// Number of frames to test with. 19enum { kFrames = 16 }; 20 21// Test all possible layout conversions can be constructed and mixed. 22TEST(ChannelMixerTest, ConstructAllPossibleLayouts) { 23 for (ChannelLayout input_layout = CHANNEL_LAYOUT_MONO; 24 input_layout < CHANNEL_LAYOUT_MAX; 25 input_layout = static_cast<ChannelLayout>(input_layout + 1)) { 26 for (ChannelLayout output_layout = CHANNEL_LAYOUT_MONO; 27 output_layout < CHANNEL_LAYOUT_STEREO_DOWNMIX; 28 output_layout = static_cast<ChannelLayout>(output_layout + 1)) { 29 // DISCRETE can't be tested here based on the current approach. 30 if (input_layout == CHANNEL_LAYOUT_DISCRETE || 31 output_layout == CHANNEL_LAYOUT_DISCRETE) 32 continue; 33 34 SCOPED_TRACE(base::StringPrintf( 35 "Input Layout: %d, Output Layout: %d", input_layout, output_layout)); 36 ChannelMixer mixer(input_layout, output_layout); 37 scoped_ptr<AudioBus> input_bus = AudioBus::Create( 38 ChannelLayoutToChannelCount(input_layout), kFrames); 39 scoped_ptr<AudioBus> output_bus = AudioBus::Create( 40 ChannelLayoutToChannelCount(output_layout), kFrames); 41 for (int ch = 0; ch < input_bus->channels(); ++ch) 42 std::fill(input_bus->channel(ch), input_bus->channel(ch) + kFrames, 1); 43 44 mixer.Transform(input_bus.get(), output_bus.get()); 45 } 46 } 47} 48 49struct ChannelMixerTestData { 50 ChannelMixerTestData(ChannelLayout input_layout, ChannelLayout output_layout, 51 float* channel_values, int num_channel_values, 52 float scale) 53 : input_layout(input_layout), 54 output_layout(output_layout), 55 channel_values(channel_values), 56 num_channel_values(num_channel_values), 57 scale(scale) { 58 input_channels = ChannelLayoutToChannelCount(input_layout); 59 output_channels = ChannelLayoutToChannelCount(output_layout); 60 } 61 62 ChannelMixerTestData(ChannelLayout input_layout, int input_channels, 63 ChannelLayout output_layout, int output_channels, 64 float* channel_values, int num_channel_values) 65 : input_layout(input_layout), 66 input_channels(input_channels), 67 output_layout(output_layout), 68 output_channels(output_channels), 69 channel_values(channel_values), 70 num_channel_values(num_channel_values), 71 scale(1.0f) { 72 } 73 74 std::string DebugString() const { 75 return base::StringPrintf( 76 "Input Layout: %d, Output Layout %d, Scale: %f", input_layout, 77 output_layout, scale); 78 } 79 80 ChannelLayout input_layout; 81 int input_channels; 82 ChannelLayout output_layout; 83 int output_channels; 84 float* channel_values; 85 int num_channel_values; 86 float scale; 87}; 88 89std::ostream& operator<<(std::ostream& os, const ChannelMixerTestData& data) { 90 return os << data.DebugString(); 91} 92 93class ChannelMixerTest : public testing::TestWithParam<ChannelMixerTestData> {}; 94 95// Verify channels are mixed and scaled correctly. The test only works if all 96// output channels have the same value. 97TEST_P(ChannelMixerTest, Mixing) { 98 ChannelLayout input_layout = GetParam().input_layout; 99 int input_channels = GetParam().input_channels; 100 scoped_ptr<AudioBus> input_bus = AudioBus::Create(input_channels, kFrames); 101 AudioParameters input_audio(AudioParameters::AUDIO_PCM_LINEAR, 102 input_layout, 103 input_layout == CHANNEL_LAYOUT_DISCRETE ? 104 input_channels : 105 ChannelLayoutToChannelCount(input_layout), 106 0, 107 AudioParameters::kAudioCDSampleRate, 16, 108 kFrames, 109 AudioParameters::NO_EFFECTS); 110 111 ChannelLayout output_layout = GetParam().output_layout; 112 int output_channels = GetParam().output_channels; 113 scoped_ptr<AudioBus> output_bus = AudioBus::Create(output_channels, kFrames); 114 AudioParameters output_audio(AudioParameters::AUDIO_PCM_LINEAR, 115 output_layout, 116 output_layout == CHANNEL_LAYOUT_DISCRETE ? 117 output_channels : 118 ChannelLayoutToChannelCount(output_layout), 119 0, 120 AudioParameters::kAudioCDSampleRate, 16, 121 kFrames, 122 AudioParameters::NO_EFFECTS); 123 124 const float* channel_values = GetParam().channel_values; 125 ASSERT_EQ(input_bus->channels(), GetParam().num_channel_values); 126 127 float expected_value = 0; 128 float scale = GetParam().scale; 129 for (int ch = 0; ch < input_bus->channels(); ++ch) { 130 std::fill(input_bus->channel(ch), input_bus->channel(ch) + kFrames, 131 channel_values[ch]); 132 expected_value += channel_values[ch] * scale; 133 } 134 135 ChannelMixer mixer(input_audio, output_audio); 136 mixer.Transform(input_bus.get(), output_bus.get()); 137 138 // Validate the output channel 139 if (input_layout != CHANNEL_LAYOUT_DISCRETE) { 140 for (int ch = 0; ch < output_bus->channels(); ++ch) { 141 for (int frame = 0; frame < output_bus->frames(); ++frame) { 142 ASSERT_FLOAT_EQ(output_bus->channel(ch)[frame], expected_value); 143 } 144 } 145 } else { 146 // Processing discrete mixing. If there is a matching input channel, 147 // then the output channel should be set. If no input channel, 148 // output channel should be 0 149 for (int ch = 0; ch < output_bus->channels(); ++ch) { 150 expected_value = (ch < input_channels) ? channel_values[ch] : 0; 151 for (int frame = 0; frame < output_bus->frames(); ++frame) { 152 ASSERT_FLOAT_EQ(output_bus->channel(ch)[frame], expected_value); 153 } 154 } 155 } 156} 157 158static float kStereoToMonoValues[] = { 0.5f, 0.75f }; 159static float kMonoToStereoValues[] = { 0.5f }; 160// Zero the center channel since it will be mixed at scale 1 vs M_SQRT1_2. 161static float kFiveOneToMonoValues[] = { 0.1f, 0.2f, 0.0f, 0.4f, 0.5f, 0.6f }; 162static float kFiveDiscreteValues[] = { 0.1f, 0.2f, 0.3f, 0.4f, 0.5f }; 163 164// Run through basic sanity tests for some common conversions. 165INSTANTIATE_TEST_CASE_P(ChannelMixerTest, ChannelMixerTest, testing::Values( 166 ChannelMixerTestData(CHANNEL_LAYOUT_STEREO, CHANNEL_LAYOUT_MONO, 167 kStereoToMonoValues, arraysize(kStereoToMonoValues), 168 0.5f), 169 ChannelMixerTestData(CHANNEL_LAYOUT_MONO, CHANNEL_LAYOUT_STEREO, 170 kMonoToStereoValues, arraysize(kMonoToStereoValues), 171 1.0f), 172 ChannelMixerTestData(CHANNEL_LAYOUT_5_1, CHANNEL_LAYOUT_MONO, 173 kFiveOneToMonoValues, arraysize(kFiveOneToMonoValues), 174 static_cast<float>(M_SQRT1_2)), 175 ChannelMixerTestData(CHANNEL_LAYOUT_DISCRETE, 2, 176 CHANNEL_LAYOUT_DISCRETE, 2, 177 kStereoToMonoValues, arraysize(kStereoToMonoValues)), 178 ChannelMixerTestData(CHANNEL_LAYOUT_DISCRETE, 2, 179 CHANNEL_LAYOUT_DISCRETE, 5, 180 kStereoToMonoValues, arraysize(kStereoToMonoValues)), 181 ChannelMixerTestData(CHANNEL_LAYOUT_DISCRETE, 5, 182 CHANNEL_LAYOUT_DISCRETE, 2, 183 kFiveDiscreteValues, arraysize(kFiveDiscreteValues)) 184)); 185 186} // namespace media 187