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#include <cmath>
6
7#include "base/logging.h"
8#include "base/memory/scoped_ptr.h"
9#include "media/audio/audio_io.h"
10#include "media/audio/audio_manager_base.h"
11#include "testing/gtest/include/gtest/gtest.h"
12
13#if defined(OS_WIN)
14#include "base/win/scoped_com_initializer.h"
15#include "media/audio/win/core_audio_util_win.h"
16#endif
17
18namespace media {
19
20double GetVolumeAfterSetVolumeOnLinux(AudioInputStream* ais,
21                                      double target_volume) {
22  // SetVolume() is asynchronous on Linux, we need to keep trying until
23  // the SetVolume() operation is done.
24  static const int kTimesToRun = 10;
25  double volume = 0.0;
26  for (int i = 0; i < kTimesToRun; ++i) {
27    volume = ais->GetVolume();
28    if (volume == target_volume)
29      break;
30
31    // Sleep 100ms to wait for the operation.
32    base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(100));
33  }
34
35  return volume;
36}
37
38class AudioInputVolumeTest : public ::testing::Test {
39 protected:
40  AudioInputVolumeTest()
41      : audio_manager_(AudioManager::CreateForTesting())
42#if defined(OS_WIN)
43       , com_init_(base::win::ScopedCOMInitializer::kMTA)
44#endif
45  {
46  }
47
48  bool CanRunAudioTests() {
49#if defined(OS_WIN)
50    // TODO(henrika): add support for volume control on Windows XP as well.
51    // For now, we might as well signal false already here to avoid running
52    // these tests on Windows XP.
53    if (!CoreAudioUtil::IsSupported())
54      return false;
55#endif
56    if (!audio_manager_)
57      return false;
58
59    return audio_manager_->HasAudioInputDevices();
60  }
61
62  // Helper method which checks if the stream has volume support.
63  bool HasDeviceVolumeControl(AudioInputStream* stream) {
64    if (!stream)
65      return false;
66
67    return (stream->GetMaxVolume() != 0.0);
68  }
69
70  AudioInputStream* CreateAndOpenStream(const std::string& device_id) {
71    const AudioParameters& params =
72        audio_manager_->GetInputStreamParameters(device_id);
73    AudioInputStream* ais = audio_manager_->MakeAudioInputStream(
74        params, device_id);
75    EXPECT_TRUE(NULL != ais);
76
77#if defined(OS_LINUX) || defined(OS_OPENBSD)
78    // Some linux devices do not support our settings, we may fail to open
79    // those devices.
80    if (!ais->Open()) {
81      // Default device should always be able to be opened.
82      EXPECT_TRUE(AudioManagerBase::kDefaultDeviceId != device_id);
83      ais->Close();
84      ais = NULL;
85    }
86#elif defined(OS_WIN) || defined(OS_MACOSX)
87    EXPECT_TRUE(ais->Open());
88#endif
89
90    return ais;
91  }
92
93  scoped_ptr<AudioManager> audio_manager_;
94
95#if defined(OS_WIN)
96  base::win::ScopedCOMInitializer com_init_;
97#endif
98};
99
100#if defined(OS_LINUX) && !defined(OS_CHROMEOS)
101// Currently failing on linux ARM bot: http://crbug/238490
102// Also flaky on x86_64: http://crbug/236936
103#define MAYBE_InputVolumeTest DISABLED_InputVolumeTest
104#else
105#define MAYBE_InputVolumeTest InputVolumeTest
106#endif
107
108TEST_F(AudioInputVolumeTest, MAYBE_InputVolumeTest) {
109  if (!CanRunAudioTests())
110    return;
111
112  // Retrieve a list of all available input devices.
113  AudioDeviceNames device_names;
114  audio_manager_->GetAudioInputDeviceNames(&device_names);
115  if (device_names.empty()) {
116    LOG(WARNING) << "Could not find any available input device";
117    return;
118  }
119
120  // Scan all available input devices and repeat the same test for all of them.
121  for (AudioDeviceNames::const_iterator it = device_names.begin();
122       it != device_names.end();
123       ++it) {
124    AudioInputStream* ais = CreateAndOpenStream(it->unique_id);
125    if (!ais) {
126      DLOG(WARNING) << "Failed to open stream for device " << it->unique_id;
127      continue;
128    }
129
130    if (!HasDeviceVolumeControl(ais)) {
131      DLOG(WARNING) << "Device: " << it->unique_id
132                    << ", does not have volume control.";
133      ais->Close();
134      continue;
135    }
136
137    double max_volume = ais->GetMaxVolume();
138    EXPECT_GT(max_volume, 0.0);
139
140    // Store the current input-device volume level.
141    double original_volume = ais->GetVolume();
142    EXPECT_GE(original_volume, 0.0);
143#if defined(OS_WIN) || defined(OS_MACOSX)
144    // Note that |original_volume| can be higher than |max_volume| on Linux.
145    EXPECT_LE(original_volume, max_volume);
146#endif
147
148    // Set the volume to the maxiumum level..
149    ais->SetVolume(max_volume);
150    double current_volume = ais->GetVolume();
151    EXPECT_EQ(max_volume, current_volume);
152
153    // Set the volume to the minimum level (=0).
154    double new_volume = 0.0;
155    ais->SetVolume(new_volume);
156#if defined(OS_LINUX)
157    current_volume = GetVolumeAfterSetVolumeOnLinux(ais, new_volume);
158#else
159    current_volume = ais->GetVolume();
160#endif
161    EXPECT_EQ(new_volume, current_volume);
162
163    // Set the volume to the mid level (50% of max).
164    // Verify that the absolute error is small enough.
165    new_volume = max_volume / 2;
166    ais->SetVolume(new_volume);
167#if defined(OS_LINUX)
168    current_volume = GetVolumeAfterSetVolumeOnLinux(ais, new_volume);
169#else
170    current_volume = ais->GetVolume();
171#endif
172    EXPECT_LT(current_volume, max_volume);
173    EXPECT_GT(current_volume, 0);
174    EXPECT_NEAR(current_volume, new_volume, 0.25 * max_volume);
175
176    // Restores the volume to the original value.
177    ais->SetVolume(original_volume);
178    current_volume = ais->GetVolume();
179    EXPECT_EQ(original_volume, current_volume);
180
181    ais->Close();
182  }
183}
184
185}  // namespace media
186