1a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)// Copyright 2014 The Chromium Authors. All rights reserved.
2a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be
3a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)// found in the LICENSE file.
4a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
5a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)#include "ash/system/win/audio/tray_audio_delegate_win.h"
6a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
7a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)#include <audiopolicy.h>
8a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)#include <cmath>
9a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
10a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)#include "media/audio/win/core_audio_util_win.h"
11a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
12a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)using base::win::ScopedComPtr;
13a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
14a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)namespace {
15a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
16a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)// Volume value which should be considered as muted in range [0, 100].
17a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)const int kMuteThresholdPercent = 1;
18a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
19a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)// Lowest volume which is considered to be audible in the range [0, 100].
20a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)const int kDefaultUnmuteVolumePercent = 4;
21a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
22a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)}  // namespace
23a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
24a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)namespace ash  {
25a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)namespace system {
26a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
27a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)void TrayAudioDelegateWin::AdjustOutputVolumeToAudibleLevel() {
28a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  if (GetOutputVolumeLevel() <= kMuteThresholdPercent)
29a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    SetOutputVolumeLevel(kDefaultUnmuteVolumePercent);
30a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)}
31a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
32a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)int TrayAudioDelegateWin::GetOutputDefaultVolumeMuteLevel() {
33a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  return kMuteThresholdPercent;
34a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)}
35a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
36a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)int TrayAudioDelegateWin::GetOutputVolumeLevel() {
37a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  ScopedComPtr<ISimpleAudioVolume> volume_control =
38a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      CreateDefaultVolumeControl();
39a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  if (!volume_control)
40a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    return 0;
41a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
42a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  float level = 0.0f;
43a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  if (FAILED(volume_control->GetMasterVolume(&level)))
44a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    return 0;
45a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
46a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  // MSVC prior to 2013 doesn't have a round function.  The below code is not
47a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  // conformant to C99 round(), but since we know that 0 <= level <= 100, it
48a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  // should be ok.
49a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  return static_cast<int>(level + 0.5);
50a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)}
51a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
52a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)int TrayAudioDelegateWin::GetActiveOutputDeviceIconId() {
53a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  return kNoAudioDeviceIcon;
54a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)}
55a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
56a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)bool TrayAudioDelegateWin::HasAlternativeSources() {
57a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  return false;
58a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)}
59a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
60a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)bool TrayAudioDelegateWin::IsOutputAudioMuted() {
61a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  ScopedComPtr<ISimpleAudioVolume> volume_control =
62a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      CreateDefaultVolumeControl();
63a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
64a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  if (!volume_control)
65a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    return false;
66a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
67a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  BOOL mute = FALSE;
68a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  if (FAILED(volume_control->GetMute(&mute)))
69a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    return false;
70a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
71a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  return !!mute;
72a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)}
73a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
74a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)void TrayAudioDelegateWin::SetOutputAudioIsMuted(bool is_muted) {
75a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  ScopedComPtr<ISimpleAudioVolume> volume_control =
76a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      CreateDefaultVolumeControl();
77a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
78a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  if (!volume_control)
79a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    return;
80a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
81a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  volume_control->SetMute(is_muted, NULL);
82a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)}
83a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
84a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)void TrayAudioDelegateWin::SetOutputVolumeLevel(int level) {
85a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  ScopedComPtr<ISimpleAudioVolume> volume_control =
86a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      CreateDefaultVolumeControl();
87a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
88a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  if (!volume_control)
89a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    return;
90a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
91a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  float volume_level = static_cast<float>(level) / 100.0f;
92a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  volume_control->SetMasterVolume(volume_level, NULL);
93a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)}
94a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
95a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)ScopedComPtr<ISimpleAudioVolume>
96a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)TrayAudioDelegateWin::CreateDefaultVolumeControl() {
97a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  ScopedComPtr<ISimpleAudioVolume> volume_control;
98a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  ScopedComPtr<IAudioSessionManager> session_manager;
99a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
100a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  ScopedComPtr<IMMDevice> device =
101a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      media::CoreAudioUtil::CreateDefaultDevice(eRender, eConsole);
102a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  if (!device ||
103a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      FAILED(device->Activate(__uuidof(IAudioSessionManager), CLSCTX_ALL, NULL,
104a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)             session_manager.ReceiveVoid()))) {
105a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    return volume_control;
106a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  }
107a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
108a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  session_manager->GetSimpleAudioVolume(NULL, FALSE,
109a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)      volume_control.Receive());
110a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
111a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  return volume_control;
112a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)}
113a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
114a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)}  // namespace system
115a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)}  // namespace ash
116