1/*
2 *  Copyright (c) 2012 The WebRTC project authors. All Rights Reserved.
3 *
4 *  Use of this source code is governed by a BSD-style license
5 *  that can be found in the LICENSE file in the root of the source
6 *  tree. An additional intellectual property rights grant can be found
7 *  in the file PATENTS.  All contributing project authors may
8 *  be found in the AUTHORS file in the root of the source tree.
9 */
10
11#include "webrtc/voice_engine/voe_volume_control_impl.h"
12
13#include "webrtc/system_wrappers/include/critical_section_wrapper.h"
14#include "webrtc/system_wrappers/include/trace.h"
15#include "webrtc/voice_engine/channel.h"
16#include "webrtc/voice_engine/include/voe_errors.h"
17#include "webrtc/voice_engine/output_mixer.h"
18#include "webrtc/voice_engine/transmit_mixer.h"
19#include "webrtc/voice_engine/voice_engine_impl.h"
20
21namespace webrtc {
22
23VoEVolumeControl* VoEVolumeControl::GetInterface(VoiceEngine* voiceEngine) {
24#ifndef WEBRTC_VOICE_ENGINE_VOLUME_CONTROL_API
25  return NULL;
26#else
27  if (NULL == voiceEngine) {
28    return NULL;
29  }
30  VoiceEngineImpl* s = static_cast<VoiceEngineImpl*>(voiceEngine);
31  s->AddRef();
32  return s;
33#endif
34}
35
36#ifdef WEBRTC_VOICE_ENGINE_VOLUME_CONTROL_API
37
38VoEVolumeControlImpl::VoEVolumeControlImpl(voe::SharedData* shared)
39    : _shared(shared) {
40  WEBRTC_TRACE(kTraceMemory, kTraceVoice, VoEId(_shared->instance_id(), -1),
41               "VoEVolumeControlImpl::VoEVolumeControlImpl() - ctor");
42}
43
44VoEVolumeControlImpl::~VoEVolumeControlImpl() {
45  WEBRTC_TRACE(kTraceMemory, kTraceVoice, VoEId(_shared->instance_id(), -1),
46               "VoEVolumeControlImpl::~VoEVolumeControlImpl() - dtor");
47}
48
49int VoEVolumeControlImpl::SetSpeakerVolume(unsigned int volume) {
50  WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_shared->instance_id(), -1),
51               "SetSpeakerVolume(volume=%u)", volume);
52
53  if (!_shared->statistics().Initialized()) {
54    _shared->SetLastError(VE_NOT_INITED, kTraceError);
55    return -1;
56  }
57  if (volume > kMaxVolumeLevel) {
58    _shared->SetLastError(VE_INVALID_ARGUMENT, kTraceError,
59                          "SetSpeakerVolume() invalid argument");
60    return -1;
61  }
62
63  uint32_t maxVol(0);
64  uint32_t spkrVol(0);
65
66  // scale: [0,kMaxVolumeLevel] -> [0,MaxSpeakerVolume]
67  if (_shared->audio_device()->MaxSpeakerVolume(&maxVol) != 0) {
68    _shared->SetLastError(VE_MIC_VOL_ERROR, kTraceError,
69                          "SetSpeakerVolume() failed to get max volume");
70    return -1;
71  }
72  // Round the value and avoid floating computation.
73  spkrVol = (uint32_t)((volume * maxVol + (int)(kMaxVolumeLevel / 2)) /
74                       (kMaxVolumeLevel));
75
76  // set the actual volume using the audio mixer
77  if (_shared->audio_device()->SetSpeakerVolume(spkrVol) != 0) {
78    _shared->SetLastError(VE_MIC_VOL_ERROR, kTraceError,
79                          "SetSpeakerVolume() failed to set speaker volume");
80    return -1;
81  }
82  return 0;
83}
84
85int VoEVolumeControlImpl::GetSpeakerVolume(unsigned int& volume) {
86
87  if (!_shared->statistics().Initialized()) {
88    _shared->SetLastError(VE_NOT_INITED, kTraceError);
89    return -1;
90  }
91
92  uint32_t spkrVol(0);
93  uint32_t maxVol(0);
94
95  if (_shared->audio_device()->SpeakerVolume(&spkrVol) != 0) {
96    _shared->SetLastError(VE_GET_MIC_VOL_ERROR, kTraceError,
97                          "GetSpeakerVolume() unable to get speaker volume");
98    return -1;
99  }
100
101  // scale: [0, MaxSpeakerVolume] -> [0, kMaxVolumeLevel]
102  if (_shared->audio_device()->MaxSpeakerVolume(&maxVol) != 0) {
103    _shared->SetLastError(
104        VE_GET_MIC_VOL_ERROR, kTraceError,
105        "GetSpeakerVolume() unable to get max speaker volume");
106    return -1;
107  }
108  // Round the value and avoid floating computation.
109  volume =
110      (uint32_t)((spkrVol * kMaxVolumeLevel + (int)(maxVol / 2)) / (maxVol));
111
112  return 0;
113}
114
115int VoEVolumeControlImpl::SetMicVolume(unsigned int volume) {
116  WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_shared->instance_id(), -1),
117               "SetMicVolume(volume=%u)", volume);
118
119  if (!_shared->statistics().Initialized()) {
120    _shared->SetLastError(VE_NOT_INITED, kTraceError);
121    return -1;
122  }
123  if (volume > kMaxVolumeLevel) {
124    _shared->SetLastError(VE_INVALID_ARGUMENT, kTraceError,
125                          "SetMicVolume() invalid argument");
126    return -1;
127  }
128
129  uint32_t maxVol(0);
130  uint32_t micVol(0);
131
132  // scale: [0, kMaxVolumeLevel] -> [0,MaxMicrophoneVolume]
133  if (_shared->audio_device()->MaxMicrophoneVolume(&maxVol) != 0) {
134    _shared->SetLastError(VE_MIC_VOL_ERROR, kTraceError,
135                          "SetMicVolume() failed to get max volume");
136    return -1;
137  }
138
139  if (volume == kMaxVolumeLevel) {
140    // On Linux running pulse, users are able to set the volume above 100%
141    // through the volume control panel, where the +100% range is digital
142    // scaling. WebRTC does not support setting the volume above 100%, and
143    // simply ignores changing the volume if the user tries to set it to
144    // |kMaxVolumeLevel| while the current volume is higher than |maxVol|.
145    if (_shared->audio_device()->MicrophoneVolume(&micVol) != 0) {
146      _shared->SetLastError(VE_GET_MIC_VOL_ERROR, kTraceError,
147                            "SetMicVolume() unable to get microphone volume");
148      return -1;
149    }
150    if (micVol >= maxVol)
151      return 0;
152  }
153
154  // Round the value and avoid floating point computation.
155  micVol = (uint32_t)((volume * maxVol + (int)(kMaxVolumeLevel / 2)) /
156                      (kMaxVolumeLevel));
157
158  // set the actual volume using the audio mixer
159  if (_shared->audio_device()->SetMicrophoneVolume(micVol) != 0) {
160    _shared->SetLastError(VE_MIC_VOL_ERROR, kTraceError,
161                          "SetMicVolume() failed to set mic volume");
162    return -1;
163  }
164  return 0;
165}
166
167int VoEVolumeControlImpl::GetMicVolume(unsigned int& volume) {
168  if (!_shared->statistics().Initialized()) {
169    _shared->SetLastError(VE_NOT_INITED, kTraceError);
170    return -1;
171  }
172
173  uint32_t micVol(0);
174  uint32_t maxVol(0);
175
176  if (_shared->audio_device()->MicrophoneVolume(&micVol) != 0) {
177    _shared->SetLastError(VE_GET_MIC_VOL_ERROR, kTraceError,
178                          "GetMicVolume() unable to get microphone volume");
179    return -1;
180  }
181
182  // scale: [0, MaxMicrophoneVolume] -> [0, kMaxVolumeLevel]
183  if (_shared->audio_device()->MaxMicrophoneVolume(&maxVol) != 0) {
184    _shared->SetLastError(VE_GET_MIC_VOL_ERROR, kTraceError,
185                          "GetMicVolume() unable to get max microphone volume");
186    return -1;
187  }
188  if (micVol < maxVol) {
189    // Round the value and avoid floating point calculation.
190    volume =
191        (uint32_t)((micVol * kMaxVolumeLevel + (int)(maxVol / 2)) / (maxVol));
192  } else {
193    // Truncate the value to the kMaxVolumeLevel.
194    volume = kMaxVolumeLevel;
195  }
196  return 0;
197}
198
199int VoEVolumeControlImpl::SetInputMute(int channel, bool enable) {
200  WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_shared->instance_id(), -1),
201               "SetInputMute(channel=%d, enable=%d)", channel, enable);
202
203  if (!_shared->statistics().Initialized()) {
204    _shared->SetLastError(VE_NOT_INITED, kTraceError);
205    return -1;
206  }
207  if (channel == -1) {
208    // Mute before demultiplexing <=> affects all channels
209    return _shared->transmit_mixer()->SetMute(enable);
210  }
211  // Mute after demultiplexing <=> affects one channel only
212  voe::ChannelOwner ch = _shared->channel_manager().GetChannel(channel);
213  voe::Channel* channelPtr = ch.channel();
214  if (channelPtr == NULL) {
215    _shared->SetLastError(VE_CHANNEL_NOT_VALID, kTraceError,
216                          "SetInputMute() failed to locate channel");
217    return -1;
218  }
219  return channelPtr->SetMute(enable);
220}
221
222int VoEVolumeControlImpl::GetInputMute(int channel, bool& enabled) {
223  if (!_shared->statistics().Initialized()) {
224    _shared->SetLastError(VE_NOT_INITED, kTraceError);
225    return -1;
226  }
227  if (channel == -1) {
228    enabled = _shared->transmit_mixer()->Mute();
229  } else {
230    voe::ChannelOwner ch = _shared->channel_manager().GetChannel(channel);
231    voe::Channel* channelPtr = ch.channel();
232    if (channelPtr == NULL) {
233      _shared->SetLastError(VE_CHANNEL_NOT_VALID, kTraceError,
234                            "SetInputMute() failed to locate channel");
235      return -1;
236    }
237    enabled = channelPtr->Mute();
238  }
239  return 0;
240}
241
242int VoEVolumeControlImpl::GetSpeechInputLevel(unsigned int& level) {
243  if (!_shared->statistics().Initialized()) {
244    _shared->SetLastError(VE_NOT_INITED, kTraceError);
245    return -1;
246  }
247  int8_t currentLevel = _shared->transmit_mixer()->AudioLevel();
248  level = static_cast<unsigned int>(currentLevel);
249  return 0;
250}
251
252int VoEVolumeControlImpl::GetSpeechOutputLevel(int channel,
253                                               unsigned int& level) {
254  if (!_shared->statistics().Initialized()) {
255    _shared->SetLastError(VE_NOT_INITED, kTraceError);
256    return -1;
257  }
258  if (channel == -1) {
259    return _shared->output_mixer()->GetSpeechOutputLevel((uint32_t&)level);
260  } else {
261    voe::ChannelOwner ch = _shared->channel_manager().GetChannel(channel);
262    voe::Channel* channelPtr = ch.channel();
263    if (channelPtr == NULL) {
264      _shared->SetLastError(VE_CHANNEL_NOT_VALID, kTraceError,
265                            "GetSpeechOutputLevel() failed to locate channel");
266      return -1;
267    }
268    channelPtr->GetSpeechOutputLevel((uint32_t&)level);
269  }
270  return 0;
271}
272
273int VoEVolumeControlImpl::GetSpeechInputLevelFullRange(unsigned int& level) {
274  if (!_shared->statistics().Initialized()) {
275    _shared->SetLastError(VE_NOT_INITED, kTraceError);
276    return -1;
277  }
278  int16_t currentLevel = _shared->transmit_mixer()->AudioLevelFullRange();
279  level = static_cast<unsigned int>(currentLevel);
280  return 0;
281}
282
283int VoEVolumeControlImpl::GetSpeechOutputLevelFullRange(int channel,
284                                                        unsigned int& level) {
285  if (!_shared->statistics().Initialized()) {
286    _shared->SetLastError(VE_NOT_INITED, kTraceError);
287    return -1;
288  }
289  if (channel == -1) {
290    return _shared->output_mixer()->GetSpeechOutputLevelFullRange(
291        (uint32_t&)level);
292  } else {
293    voe::ChannelOwner ch = _shared->channel_manager().GetChannel(channel);
294    voe::Channel* channelPtr = ch.channel();
295    if (channelPtr == NULL) {
296      _shared->SetLastError(
297          VE_CHANNEL_NOT_VALID, kTraceError,
298          "GetSpeechOutputLevelFullRange() failed to locate channel");
299      return -1;
300    }
301    channelPtr->GetSpeechOutputLevelFullRange((uint32_t&)level);
302  }
303  return 0;
304}
305
306int VoEVolumeControlImpl::SetChannelOutputVolumeScaling(int channel,
307                                                        float scaling) {
308  WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_shared->instance_id(), -1),
309               "SetChannelOutputVolumeScaling(channel=%d, scaling=%3.2f)",
310               channel, scaling);
311  if (!_shared->statistics().Initialized()) {
312    _shared->SetLastError(VE_NOT_INITED, kTraceError);
313    return -1;
314  }
315  if (scaling < kMinOutputVolumeScaling || scaling > kMaxOutputVolumeScaling) {
316    _shared->SetLastError(VE_INVALID_ARGUMENT, kTraceError,
317                          "SetChannelOutputVolumeScaling() invalid parameter");
318    return -1;
319  }
320  voe::ChannelOwner ch = _shared->channel_manager().GetChannel(channel);
321  voe::Channel* channelPtr = ch.channel();
322  if (channelPtr == NULL) {
323    _shared->SetLastError(
324        VE_CHANNEL_NOT_VALID, kTraceError,
325        "SetChannelOutputVolumeScaling() failed to locate channel");
326    return -1;
327  }
328  return channelPtr->SetChannelOutputVolumeScaling(scaling);
329}
330
331int VoEVolumeControlImpl::GetChannelOutputVolumeScaling(int channel,
332                                                        float& scaling) {
333  if (!_shared->statistics().Initialized()) {
334    _shared->SetLastError(VE_NOT_INITED, kTraceError);
335    return -1;
336  }
337  voe::ChannelOwner ch = _shared->channel_manager().GetChannel(channel);
338  voe::Channel* channelPtr = ch.channel();
339  if (channelPtr == NULL) {
340    _shared->SetLastError(
341        VE_CHANNEL_NOT_VALID, kTraceError,
342        "GetChannelOutputVolumeScaling() failed to locate channel");
343    return -1;
344  }
345  return channelPtr->GetChannelOutputVolumeScaling(scaling);
346}
347
348int VoEVolumeControlImpl::SetOutputVolumePan(int channel,
349                                             float left,
350                                             float right) {
351  WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_shared->instance_id(), -1),
352               "SetOutputVolumePan(channel=%d, left=%2.1f, right=%2.1f)",
353               channel, left, right);
354
355  if (!_shared->statistics().Initialized()) {
356    _shared->SetLastError(VE_NOT_INITED, kTraceError);
357    return -1;
358  }
359
360  bool available(false);
361  _shared->audio_device()->StereoPlayoutIsAvailable(&available);
362  if (!available) {
363    _shared->SetLastError(VE_FUNC_NO_STEREO, kTraceError,
364                          "SetOutputVolumePan() stereo playout not supported");
365    return -1;
366  }
367  if ((left < kMinOutputVolumePanning) || (left > kMaxOutputVolumePanning) ||
368      (right < kMinOutputVolumePanning) || (right > kMaxOutputVolumePanning)) {
369    _shared->SetLastError(VE_INVALID_ARGUMENT, kTraceError,
370                          "SetOutputVolumePan() invalid parameter");
371    return -1;
372  }
373
374  if (channel == -1) {
375    // Master balance (affectes the signal after output mixing)
376    return _shared->output_mixer()->SetOutputVolumePan(left, right);
377  }
378  // Per-channel balance (affects the signal before output mixing)
379  voe::ChannelOwner ch = _shared->channel_manager().GetChannel(channel);
380  voe::Channel* channelPtr = ch.channel();
381  if (channelPtr == NULL) {
382    _shared->SetLastError(VE_CHANNEL_NOT_VALID, kTraceError,
383                          "SetOutputVolumePan() failed to locate channel");
384    return -1;
385  }
386  return channelPtr->SetOutputVolumePan(left, right);
387}
388
389int VoEVolumeControlImpl::GetOutputVolumePan(int channel,
390                                             float& left,
391                                             float& right) {
392  if (!_shared->statistics().Initialized()) {
393    _shared->SetLastError(VE_NOT_INITED, kTraceError);
394    return -1;
395  }
396
397  bool available(false);
398  _shared->audio_device()->StereoPlayoutIsAvailable(&available);
399  if (!available) {
400    _shared->SetLastError(VE_FUNC_NO_STEREO, kTraceError,
401                          "GetOutputVolumePan() stereo playout not supported");
402    return -1;
403  }
404
405  if (channel == -1) {
406    return _shared->output_mixer()->GetOutputVolumePan(left, right);
407  }
408  voe::ChannelOwner ch = _shared->channel_manager().GetChannel(channel);
409  voe::Channel* channelPtr = ch.channel();
410  if (channelPtr == NULL) {
411    _shared->SetLastError(VE_CHANNEL_NOT_VALID, kTraceError,
412                          "GetOutputVolumePan() failed to locate channel");
413    return -1;
414  }
415  return channelPtr->GetOutputVolumePan(left, right);
416}
417
418#endif  // #ifdef WEBRTC_VOICE_ENGINE_VOLUME_CONTROL_API
419
420}  // namespace webrtc
421