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/interface/critical_section_wrapper.h"
14#include "webrtc/system_wrappers/interface/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{
25#ifndef WEBRTC_VOICE_ENGINE_VOLUME_CONTROL_API
26    return NULL;
27#else
28    if (NULL == voiceEngine)
29    {
30        return NULL;
31    }
32    VoiceEngineImpl* s = static_cast<VoiceEngineImpl*>(voiceEngine);
33    s->AddRef();
34    return s;
35#endif
36}
37
38#ifdef WEBRTC_VOICE_ENGINE_VOLUME_CONTROL_API
39
40VoEVolumeControlImpl::VoEVolumeControlImpl(voe::SharedData* shared)
41    : _shared(shared)
42{
43    WEBRTC_TRACE(kTraceMemory, kTraceVoice, VoEId(_shared->instance_id(), -1),
44               "VoEVolumeControlImpl::VoEVolumeControlImpl() - ctor");
45}
46
47VoEVolumeControlImpl::~VoEVolumeControlImpl()
48{
49    WEBRTC_TRACE(kTraceMemory, kTraceVoice, VoEId(_shared->instance_id(), -1),
50               "VoEVolumeControlImpl::~VoEVolumeControlImpl() - dtor");
51}
52
53int VoEVolumeControlImpl::SetSpeakerVolume(unsigned int volume)
54{
55    WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_shared->instance_id(), -1),
56               "SetSpeakerVolume(volume=%u)", volume);
57
58    if (!_shared->statistics().Initialized())
59    {
60        _shared->SetLastError(VE_NOT_INITED, kTraceError);
61        return -1;
62    }
63    if (volume > kMaxVolumeLevel)
64    {
65        _shared->SetLastError(VE_INVALID_ARGUMENT, kTraceError,
66            "SetSpeakerVolume() invalid argument");
67        return -1;
68    }
69
70    uint32_t maxVol(0);
71    uint32_t spkrVol(0);
72
73    // scale: [0,kMaxVolumeLevel] -> [0,MaxSpeakerVolume]
74    if (_shared->audio_device()->MaxSpeakerVolume(&maxVol) != 0)
75    {
76        _shared->SetLastError(VE_MIC_VOL_ERROR, kTraceError,
77            "SetSpeakerVolume() failed to get max volume");
78        return -1;
79    }
80    // Round the value and avoid floating computation.
81    spkrVol = (uint32_t)((volume * maxVol +
82        (int)(kMaxVolumeLevel / 2)) / (kMaxVolumeLevel));
83
84    // set the actual volume using the audio mixer
85    if (_shared->audio_device()->SetSpeakerVolume(spkrVol) != 0)
86    {
87        _shared->SetLastError(VE_MIC_VOL_ERROR, kTraceError,
88            "SetSpeakerVolume() failed to set speaker volume");
89        return -1;
90    }
91    return 0;
92}
93
94int VoEVolumeControlImpl::GetSpeakerVolume(unsigned int& volume)
95{
96    WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_shared->instance_id(), -1),
97               "GetSpeakerVolume()");
98
99    if (!_shared->statistics().Initialized())
100    {
101        _shared->SetLastError(VE_NOT_INITED, kTraceError);
102        return -1;
103    }
104
105    uint32_t spkrVol(0);
106    uint32_t maxVol(0);
107
108    if (_shared->audio_device()->SpeakerVolume(&spkrVol) != 0)
109    {
110        _shared->SetLastError(VE_GET_MIC_VOL_ERROR, kTraceError,
111            "GetSpeakerVolume() unable to get speaker volume");
112        return -1;
113    }
114
115    // scale: [0, MaxSpeakerVolume] -> [0, kMaxVolumeLevel]
116    if (_shared->audio_device()->MaxSpeakerVolume(&maxVol) != 0)
117    {
118        _shared->SetLastError(VE_GET_MIC_VOL_ERROR, kTraceError,
119            "GetSpeakerVolume() unable to get max speaker volume");
120        return -1;
121    }
122    // Round the value and avoid floating computation.
123    volume = (uint32_t) ((spkrVol * kMaxVolumeLevel +
124        (int)(maxVol / 2)) / (maxVol));
125
126    WEBRTC_TRACE(kTraceStateInfo, kTraceVoice,
127        VoEId(_shared->instance_id(), -1),
128        "GetSpeakerVolume() => volume=%d", volume);
129    return 0;
130}
131
132int VoEVolumeControlImpl::SetMicVolume(unsigned int volume)
133{
134    WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_shared->instance_id(), -1),
135               "SetMicVolume(volume=%u)", volume);
136
137    if (!_shared->statistics().Initialized())
138    {
139        _shared->SetLastError(VE_NOT_INITED, kTraceError);
140        return -1;
141    }
142    if (volume > kMaxVolumeLevel)
143    {
144        _shared->SetLastError(VE_INVALID_ARGUMENT, kTraceError,
145            "SetMicVolume() invalid argument");
146        return -1;
147    }
148
149    uint32_t maxVol(0);
150    uint32_t micVol(0);
151
152    // scale: [0, kMaxVolumeLevel] -> [0,MaxMicrophoneVolume]
153    if (_shared->audio_device()->MaxMicrophoneVolume(&maxVol) != 0)
154    {
155        _shared->SetLastError(VE_MIC_VOL_ERROR, kTraceError,
156            "SetMicVolume() failed to get max volume");
157        return -1;
158    }
159
160    if (volume == kMaxVolumeLevel) {
161      // On Linux running pulse, users are able to set the volume above 100%
162      // through the volume control panel, where the +100% range is digital
163      // scaling. WebRTC does not support setting the volume above 100%, and
164      // simply ignores changing the volume if the user tries to set it to
165      // |kMaxVolumeLevel| while the current volume is higher than |maxVol|.
166      if (_shared->audio_device()->MicrophoneVolume(&micVol) != 0) {
167        _shared->SetLastError(VE_GET_MIC_VOL_ERROR, kTraceError,
168            "SetMicVolume() unable to get microphone volume");
169        return -1;
170      }
171      if (micVol >= maxVol)
172        return 0;
173    }
174
175    // Round the value and avoid floating point computation.
176    micVol = (uint32_t) ((volume * maxVol +
177        (int)(kMaxVolumeLevel / 2)) / (kMaxVolumeLevel));
178
179    // set the actual volume using the audio mixer
180    if (_shared->audio_device()->SetMicrophoneVolume(micVol) != 0)
181    {
182        _shared->SetLastError(VE_MIC_VOL_ERROR, kTraceError,
183            "SetMicVolume() failed to set mic volume");
184        return -1;
185    }
186    return 0;
187}
188
189int VoEVolumeControlImpl::GetMicVolume(unsigned int& volume)
190{
191    WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_shared->instance_id(), -1),
192               "GetMicVolume()");
193
194    if (!_shared->statistics().Initialized())
195    {
196        _shared->SetLastError(VE_NOT_INITED, kTraceError);
197        return -1;
198    }
199
200    uint32_t micVol(0);
201    uint32_t maxVol(0);
202
203    if (_shared->audio_device()->MicrophoneVolume(&micVol) != 0)
204    {
205        _shared->SetLastError(VE_GET_MIC_VOL_ERROR, kTraceError,
206            "GetMicVolume() unable to get microphone volume");
207        return -1;
208    }
209
210    // scale: [0, MaxMicrophoneVolume] -> [0, kMaxVolumeLevel]
211    if (_shared->audio_device()->MaxMicrophoneVolume(&maxVol) != 0)
212    {
213        _shared->SetLastError(VE_GET_MIC_VOL_ERROR, kTraceError,
214            "GetMicVolume() unable to get max microphone volume");
215        return -1;
216    }
217    if (micVol < maxVol) {
218      // Round the value and avoid floating point calculation.
219      volume = (uint32_t) ((micVol * kMaxVolumeLevel +
220          (int)(maxVol / 2)) / (maxVol));
221    } else {
222      // Truncate the value to the kMaxVolumeLevel.
223      volume = kMaxVolumeLevel;
224    }
225
226    WEBRTC_TRACE(kTraceStateInfo, kTraceVoice,
227        VoEId(_shared->instance_id(), -1),
228        "GetMicVolume() => volume=%d", volume);
229    return 0;
230}
231
232int VoEVolumeControlImpl::SetInputMute(int channel, bool enable)
233{
234    WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_shared->instance_id(), -1),
235               "SetInputMute(channel=%d, enable=%d)", channel, enable);
236
237    if (!_shared->statistics().Initialized())
238    {
239        _shared->SetLastError(VE_NOT_INITED, kTraceError);
240        return -1;
241    }
242    if (channel == -1)
243    {
244        // Mute before demultiplexing <=> affects all channels
245        return _shared->transmit_mixer()->SetMute(enable);
246    }
247    // Mute after demultiplexing <=> affects one channel only
248    voe::ChannelOwner ch = _shared->channel_manager().GetChannel(channel);
249    voe::Channel* channelPtr = ch.channel();
250    if (channelPtr == NULL)
251    {
252        _shared->SetLastError(VE_CHANNEL_NOT_VALID, kTraceError,
253            "SetInputMute() failed to locate channel");
254        return -1;
255    }
256    return channelPtr->SetMute(enable);
257}
258
259int VoEVolumeControlImpl::GetInputMute(int channel, bool& enabled)
260{
261    WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_shared->instance_id(), -1),
262               "GetInputMute(channel=%d)", channel);
263
264    if (!_shared->statistics().Initialized())
265    {
266        _shared->SetLastError(VE_NOT_INITED, kTraceError);
267        return -1;
268    }
269    if (channel == -1)
270    {
271        enabled = _shared->transmit_mixer()->Mute();
272    }
273    else
274    {
275        voe::ChannelOwner ch = _shared->channel_manager().GetChannel(channel);
276        voe::Channel* channelPtr = ch.channel();
277        if (channelPtr == NULL)
278        {
279            _shared->SetLastError(VE_CHANNEL_NOT_VALID, kTraceError,
280                "SetInputMute() failed to locate channel");
281            return -1;
282        }
283        enabled = channelPtr->Mute();
284    }
285    WEBRTC_TRACE(kTraceStateInfo, kTraceVoice,
286        VoEId(_shared->instance_id(), -1),
287        "GetInputMute() => enabled = %d", (int)enabled);
288    return 0;
289}
290
291int VoEVolumeControlImpl::GetSpeechInputLevel(unsigned int& level)
292{
293    WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_shared->instance_id(), -1),
294               "GetSpeechInputLevel()");
295
296    if (!_shared->statistics().Initialized())
297    {
298        _shared->SetLastError(VE_NOT_INITED, kTraceError);
299        return -1;
300    }
301    int8_t currentLevel = _shared->transmit_mixer()->AudioLevel();
302    level = static_cast<unsigned int> (currentLevel);
303    WEBRTC_TRACE(kTraceStateInfo, kTraceVoice,
304        VoEId(_shared->instance_id(), -1),
305        "GetSpeechInputLevel() => %d", level);
306    return 0;
307}
308
309int VoEVolumeControlImpl::GetSpeechOutputLevel(int channel,
310                                               unsigned int& level)
311{
312    WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_shared->instance_id(), -1),
313               "GetSpeechOutputLevel(channel=%d, level=?)", channel);
314
315    if (!_shared->statistics().Initialized())
316    {
317        _shared->SetLastError(VE_NOT_INITED, kTraceError);
318        return -1;
319    }
320    if (channel == -1)
321    {
322        return _shared->output_mixer()->GetSpeechOutputLevel(
323            (uint32_t&)level);
324    }
325    else
326    {
327        voe::ChannelOwner ch = _shared->channel_manager().GetChannel(channel);
328        voe::Channel* channelPtr = ch.channel();
329        if (channelPtr == NULL)
330        {
331            _shared->SetLastError(VE_CHANNEL_NOT_VALID, kTraceError,
332                "GetSpeechOutputLevel() failed to locate channel");
333            return -1;
334        }
335        channelPtr->GetSpeechOutputLevel((uint32_t&)level);
336    }
337    return 0;
338}
339
340int VoEVolumeControlImpl::GetSpeechInputLevelFullRange(unsigned int& level)
341{
342    WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_shared->instance_id(), -1),
343               "GetSpeechInputLevelFullRange(level=?)");
344
345    if (!_shared->statistics().Initialized())
346    {
347        _shared->SetLastError(VE_NOT_INITED, kTraceError);
348        return -1;
349    }
350    int16_t currentLevel = _shared->transmit_mixer()->
351        AudioLevelFullRange();
352    level = static_cast<unsigned int> (currentLevel);
353    WEBRTC_TRACE(kTraceStateInfo, kTraceVoice,
354        VoEId(_shared->instance_id(), -1),
355        "GetSpeechInputLevelFullRange() => %d", level);
356    return 0;
357}
358
359int VoEVolumeControlImpl::GetSpeechOutputLevelFullRange(int channel,
360                                                        unsigned int& level)
361{
362    WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_shared->instance_id(), -1),
363               "GetSpeechOutputLevelFullRange(channel=%d, level=?)", channel);
364
365    if (!_shared->statistics().Initialized())
366    {
367        _shared->SetLastError(VE_NOT_INITED, kTraceError);
368        return -1;
369    }
370    if (channel == -1)
371    {
372        return _shared->output_mixer()->GetSpeechOutputLevelFullRange(
373            (uint32_t&)level);
374    }
375    else
376    {
377        voe::ChannelOwner ch = _shared->channel_manager().GetChannel(channel);
378        voe::Channel* channelPtr = ch.channel();
379        if (channelPtr == NULL)
380        {
381            _shared->SetLastError(VE_CHANNEL_NOT_VALID, kTraceError,
382                "GetSpeechOutputLevelFullRange() failed to locate channel");
383            return -1;
384        }
385        channelPtr->GetSpeechOutputLevelFullRange((uint32_t&)level);
386    }
387    return 0;
388}
389
390int VoEVolumeControlImpl::SetChannelOutputVolumeScaling(int channel,
391                                                        float scaling)
392{
393    WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_shared->instance_id(), -1),
394               "SetChannelOutputVolumeScaling(channel=%d, scaling=%3.2f)",
395               channel, scaling);
396    if (!_shared->statistics().Initialized())
397    {
398        _shared->SetLastError(VE_NOT_INITED, kTraceError);
399        return -1;
400    }
401    if (scaling < kMinOutputVolumeScaling ||
402        scaling > kMaxOutputVolumeScaling)
403    {
404        _shared->SetLastError(VE_INVALID_ARGUMENT, kTraceError,
405            "SetChannelOutputVolumeScaling() invalid parameter");
406        return -1;
407    }
408    voe::ChannelOwner ch = _shared->channel_manager().GetChannel(channel);
409    voe::Channel* channelPtr = ch.channel();
410    if (channelPtr == NULL)
411    {
412        _shared->SetLastError(VE_CHANNEL_NOT_VALID, kTraceError,
413            "SetChannelOutputVolumeScaling() failed to locate channel");
414        return -1;
415    }
416    return channelPtr->SetChannelOutputVolumeScaling(scaling);
417}
418
419int VoEVolumeControlImpl::GetChannelOutputVolumeScaling(int channel,
420                                                        float& scaling)
421{
422    WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_shared->instance_id(), -1),
423               "GetChannelOutputVolumeScaling(channel=%d, scaling=?)", channel);
424    if (!_shared->statistics().Initialized())
425    {
426        _shared->SetLastError(VE_NOT_INITED, kTraceError);
427        return -1;
428    }
429    voe::ChannelOwner ch = _shared->channel_manager().GetChannel(channel);
430    voe::Channel* channelPtr = ch.channel();
431    if (channelPtr == NULL)
432    {
433        _shared->SetLastError(VE_CHANNEL_NOT_VALID, kTraceError,
434            "GetChannelOutputVolumeScaling() failed to locate channel");
435        return -1;
436    }
437    return channelPtr->GetChannelOutputVolumeScaling(scaling);
438}
439
440int VoEVolumeControlImpl::SetOutputVolumePan(int channel,
441                                             float left,
442                                             float right)
443{
444    WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_shared->instance_id(), -1),
445               "SetOutputVolumePan(channel=%d, left=%2.1f, right=%2.1f)",
446               channel, left, right);
447
448    if (!_shared->statistics().Initialized())
449    {
450        _shared->SetLastError(VE_NOT_INITED, kTraceError);
451        return -1;
452    }
453
454    bool available(false);
455    _shared->audio_device()->StereoPlayoutIsAvailable(&available);
456    if (!available)
457    {
458        _shared->SetLastError(VE_FUNC_NO_STEREO, kTraceError,
459            "SetOutputVolumePan() stereo playout not supported");
460        return -1;
461    }
462    if ((left < kMinOutputVolumePanning)  ||
463        (left > kMaxOutputVolumePanning)  ||
464        (right < kMinOutputVolumePanning) ||
465        (right > kMaxOutputVolumePanning))
466    {
467        _shared->SetLastError(VE_INVALID_ARGUMENT, kTraceError,
468            "SetOutputVolumePan() invalid parameter");
469        return -1;
470    }
471
472    if (channel == -1)
473    {
474        // Master balance (affectes the signal after output mixing)
475        return _shared->output_mixer()->SetOutputVolumePan(left, right);
476    }
477    // Per-channel balance (affects the signal before output mixing)
478    voe::ChannelOwner ch = _shared->channel_manager().GetChannel(channel);
479    voe::Channel* channelPtr = ch.channel();
480    if (channelPtr == NULL)
481    {
482        _shared->SetLastError(VE_CHANNEL_NOT_VALID, kTraceError,
483            "SetOutputVolumePan() failed to locate channel");
484        return -1;
485    }
486    return channelPtr->SetOutputVolumePan(left, right);
487}
488
489int VoEVolumeControlImpl::GetOutputVolumePan(int channel,
490                                             float& left,
491                                             float& right)
492{
493    WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_shared->instance_id(), -1),
494               "GetOutputVolumePan(channel=%d, left=?, right=?)", channel);
495
496    if (!_shared->statistics().Initialized())
497    {
498        _shared->SetLastError(VE_NOT_INITED, kTraceError);
499        return -1;
500    }
501
502    bool available(false);
503    _shared->audio_device()->StereoPlayoutIsAvailable(&available);
504    if (!available)
505    {
506        _shared->SetLastError(VE_FUNC_NO_STEREO, kTraceError,
507            "GetOutputVolumePan() stereo playout not supported");
508        return -1;
509    }
510
511    if (channel == -1)
512    {
513        return _shared->output_mixer()->GetOutputVolumePan(left, right);
514    }
515    voe::ChannelOwner ch = _shared->channel_manager().GetChannel(channel);
516    voe::Channel* channelPtr = ch.channel();
517    if (channelPtr == NULL)
518    {
519        _shared->SetLastError(VE_CHANNEL_NOT_VALID, kTraceError,
520            "GetOutputVolumePan() failed to locate channel");
521        return -1;
522    }
523    return channelPtr->GetOutputVolumePan(left, right);
524}
525
526#endif  // #ifdef WEBRTC_VOICE_ENGINE_VOLUME_CONTROL_API
527
528}  // namespace webrtc
529