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