| /* | 
 |  *  Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. | 
 |  * | 
 |  *  Use of this source code is governed by a BSD-style license | 
 |  *  that can be found in the LICENSE file in the root of the source | 
 |  *  tree. An additional intellectual property rights grant can be found | 
 |  *  in the file PATENTS.  All contributing project authors may | 
 |  *  be found in the AUTHORS file in the root of the source tree. | 
 |  */ | 
 |  | 
 | #include "webrtc/voice_engine/voe_dtmf_impl.h" | 
 |  | 
 | #include "webrtc/system_wrappers/interface/critical_section_wrapper.h" | 
 | #include "webrtc/system_wrappers/interface/trace.h" | 
 | #include "webrtc/voice_engine/channel.h" | 
 | #include "webrtc/voice_engine/include/voe_errors.h" | 
 | #include "webrtc/voice_engine/output_mixer.h" | 
 | #include "webrtc/voice_engine/transmit_mixer.h" | 
 | #include "webrtc/voice_engine/voice_engine_impl.h" | 
 |  | 
 | namespace webrtc { | 
 |  | 
 | VoEDtmf* VoEDtmf::GetInterface(VoiceEngine* voiceEngine) | 
 | { | 
 | #ifndef WEBRTC_VOICE_ENGINE_DTMF_API | 
 |     return NULL; | 
 | #else | 
 |     if (NULL == voiceEngine) | 
 |     { | 
 |         return NULL; | 
 |     } | 
 |     VoiceEngineImpl* s = static_cast<VoiceEngineImpl*>(voiceEngine); | 
 |     s->AddRef(); | 
 |     return s; | 
 | #endif | 
 | } | 
 |  | 
 | #ifdef WEBRTC_VOICE_ENGINE_DTMF_API | 
 |  | 
 | VoEDtmfImpl::VoEDtmfImpl(voe::SharedData* shared) : | 
 |     _dtmfFeedback(true), | 
 |     _dtmfDirectFeedback(false), | 
 |     _shared(shared) | 
 | { | 
 |     WEBRTC_TRACE(kTraceMemory, kTraceVoice, VoEId(_shared->instance_id(), -1), | 
 |                  "VoEDtmfImpl::VoEDtmfImpl() - ctor"); | 
 | } | 
 |  | 
 | VoEDtmfImpl::~VoEDtmfImpl() | 
 | { | 
 |     WEBRTC_TRACE(kTraceMemory, kTraceVoice, VoEId(_shared->instance_id(), -1), | 
 |                  "VoEDtmfImpl::~VoEDtmfImpl() - dtor"); | 
 | } | 
 |  | 
 | int VoEDtmfImpl::SendTelephoneEvent(int channel, | 
 |                                     int eventCode, | 
 |                                     bool outOfBand, | 
 |                                     int lengthMs, | 
 |                                     int attenuationDb) | 
 | { | 
 |     WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_shared->instance_id(), -1), | 
 |                  "SendTelephoneEvent(channel=%d, eventCode=%d, outOfBand=%d," | 
 |                  "length=%d, attenuationDb=%d)", | 
 |                  channel, eventCode, (int)outOfBand, lengthMs, attenuationDb); | 
 |     if (!_shared->statistics().Initialized()) | 
 |     { | 
 |         _shared->SetLastError(VE_NOT_INITED, kTraceError); | 
 |         return -1; | 
 |     } | 
 |     voe::ChannelOwner ch = _shared->channel_manager().GetChannel(channel); | 
 |     voe::Channel* channelPtr = ch.channel(); | 
 |     if (channelPtr == NULL) | 
 |     { | 
 |         _shared->SetLastError(VE_CHANNEL_NOT_VALID, kTraceError, | 
 |             "SendTelephoneEvent() failed to locate channel"); | 
 |         return -1; | 
 |     } | 
 |     if (!channelPtr->Sending()) | 
 |     { | 
 |         _shared->SetLastError(VE_NOT_SENDING, kTraceError, | 
 |             "SendTelephoneEvent() sending is not active"); | 
 |         return -1; | 
 |     } | 
 |  | 
 |     // Sanity check | 
 |     const int maxEventCode = outOfBand ? | 
 |         static_cast<int>(kMaxTelephoneEventCode) : | 
 |         static_cast<int>(kMaxDtmfEventCode); | 
 |     const bool testFailed = ((eventCode < 0) || | 
 |         (eventCode > maxEventCode) || | 
 |         (lengthMs < kMinTelephoneEventDuration) || | 
 |         (lengthMs > kMaxTelephoneEventDuration) || | 
 |         (attenuationDb < kMinTelephoneEventAttenuation) || | 
 |         (attenuationDb > kMaxTelephoneEventAttenuation)); | 
 |     if (testFailed) | 
 |     { | 
 |         _shared->SetLastError(VE_INVALID_ARGUMENT, kTraceError, | 
 |             "SendTelephoneEvent() invalid parameter(s)"); | 
 |         return -1; | 
 |     } | 
 |  | 
 |     const bool isDtmf = | 
 |         (eventCode >= 0) && (eventCode <= kMaxDtmfEventCode); | 
 |     const bool playDtmfToneDirect = | 
 |         isDtmf && (_dtmfFeedback && _dtmfDirectFeedback); | 
 |  | 
 |     if (playDtmfToneDirect) | 
 |     { | 
 |         // Mute the microphone signal while playing back the tone directly. | 
 |         // This is to reduce the risk of introducing echo from the added output. | 
 |         _shared->transmit_mixer()->UpdateMuteMicrophoneTime(lengthMs); | 
 |  | 
 |         // Play out local feedback tone directly (same approach for both inband | 
 |         // and outband). | 
 |         // Reduce the length of the the tone with 80ms to reduce risk of echo. | 
 |         // For non-direct feedback, outband and inband cases are handled | 
 |         // differently. | 
 |         _shared->output_mixer()->PlayDtmfTone(eventCode, lengthMs - 80, | 
 |                                             attenuationDb); | 
 |     } | 
 |  | 
 |     if (outOfBand) | 
 |     { | 
 |         // The RTP/RTCP module will always deliver OnPlayTelephoneEvent when | 
 |         // an event is transmitted. It is up to the VoE to utilize it or not. | 
 |         // This flag ensures that feedback/playout is enabled; however, the | 
 |         // channel object must still parse out the Dtmf events (0-15) from | 
 |         // all possible events (0-255). | 
 |         const bool playDTFMEvent = (_dtmfFeedback && !_dtmfDirectFeedback); | 
 |  | 
 |         return channelPtr->SendTelephoneEventOutband(eventCode, | 
 |                                                      lengthMs, | 
 |                                                      attenuationDb, | 
 |                                                      playDTFMEvent); | 
 |     } | 
 |     else | 
 |     { | 
 |         // For Dtmf tones, we want to ensure that inband tones are played out | 
 |         // in sync with the transmitted audio. This flag is utilized by the | 
 |         // channel object to determine if the queued Dtmf e vent shall also | 
 |         // be fed to the output mixer in the same step as input audio is | 
 |         // replaced by inband Dtmf tones. | 
 |         const bool playDTFMEvent = | 
 |             (isDtmf && _dtmfFeedback && !_dtmfDirectFeedback); | 
 |  | 
 |         return channelPtr->SendTelephoneEventInband(eventCode, | 
 |                                                     lengthMs, | 
 |                                                     attenuationDb, | 
 |                                                     playDTFMEvent); | 
 |     } | 
 | } | 
 |  | 
 | int VoEDtmfImpl::SetSendTelephoneEventPayloadType(int channel, | 
 |                                                   unsigned char type) | 
 | { | 
 |     WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_shared->instance_id(), -1), | 
 |                  "SetSendTelephoneEventPayloadType(channel=%d, type=%u)", | 
 |                  channel, type); | 
 |     if (!_shared->statistics().Initialized()) | 
 |     { | 
 |         _shared->SetLastError(VE_NOT_INITED, kTraceError); | 
 |         return -1; | 
 |     } | 
 |     voe::ChannelOwner ch = _shared->channel_manager().GetChannel(channel); | 
 |     voe::Channel* channelPtr = ch.channel(); | 
 |     if (channelPtr == NULL) | 
 |     { | 
 |         _shared->SetLastError(VE_CHANNEL_NOT_VALID, kTraceError, | 
 |             "SetSendTelephoneEventPayloadType() failed to locate channel"); | 
 |         return -1; | 
 |     } | 
 |     return channelPtr->SetSendTelephoneEventPayloadType(type); | 
 | } | 
 |  | 
 | int VoEDtmfImpl::GetSendTelephoneEventPayloadType(int channel, | 
 |                                                   unsigned char& type) | 
 | { | 
 |     WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_shared->instance_id(), -1), | 
 |                  "GetSendTelephoneEventPayloadType(channel=%d)", channel); | 
 |     if (!_shared->statistics().Initialized()) | 
 |     { | 
 |         _shared->SetLastError(VE_NOT_INITED, kTraceError); | 
 |         return -1; | 
 |     } | 
 |     voe::ChannelOwner ch = _shared->channel_manager().GetChannel(channel); | 
 |     voe::Channel* channelPtr = ch.channel(); | 
 |     if (channelPtr == NULL) | 
 |     { | 
 |         _shared->SetLastError(VE_CHANNEL_NOT_VALID, kTraceError, | 
 |             "GetSendTelephoneEventPayloadType() failed to locate channel"); | 
 |         return -1; | 
 |     } | 
 |     return channelPtr->GetSendTelephoneEventPayloadType(type); | 
 | } | 
 |  | 
 | int VoEDtmfImpl::PlayDtmfTone(int eventCode, | 
 |                               int lengthMs, | 
 |                               int attenuationDb) | 
 | { | 
 |     WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_shared->instance_id(), -1), | 
 |                  "PlayDtmfTone(eventCode=%d, lengthMs=%d, attenuationDb=%d)", | 
 |                  eventCode, lengthMs, attenuationDb); | 
 |  | 
 |     if (!_shared->statistics().Initialized()) | 
 |     { | 
 |         _shared->SetLastError(VE_NOT_INITED, kTraceError); | 
 |         return -1; | 
 |     } | 
 |     if (!_shared->audio_device()->Playing()) | 
 |     { | 
 |         _shared->SetLastError(VE_NOT_PLAYING, kTraceError, | 
 |             "PlayDtmfTone() no channel is playing out"); | 
 |         return -1; | 
 |     } | 
 |     if ((eventCode < kMinDtmfEventCode) || | 
 |         (eventCode > kMaxDtmfEventCode) || | 
 |         (lengthMs < kMinTelephoneEventDuration) || | 
 |         (lengthMs > kMaxTelephoneEventDuration) || | 
 |         (attenuationDb <kMinTelephoneEventAttenuation) || | 
 |         (attenuationDb > kMaxTelephoneEventAttenuation)) | 
 |     { | 
 |         _shared->SetLastError(VE_INVALID_ARGUMENT, kTraceError, | 
 |         "PlayDtmfTone() invalid tone parameter(s)"); | 
 |         return -1; | 
 |     } | 
 |     return _shared->output_mixer()->PlayDtmfTone(eventCode, lengthMs, | 
 |                                                attenuationDb); | 
 | } | 
 |  | 
 | int VoEDtmfImpl::SetDtmfFeedbackStatus(bool enable, bool directFeedback) | 
 | { | 
 |     WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_shared->instance_id(), -1), | 
 |                  "SetDtmfFeedbackStatus(enable=%d, directFeeback=%d)", | 
 |                  (int)enable, (int)directFeedback); | 
 |  | 
 |     CriticalSectionScoped sc(_shared->crit_sec()); | 
 |  | 
 |     _dtmfFeedback = enable; | 
 |     _dtmfDirectFeedback = directFeedback; | 
 |  | 
 |     return 0; | 
 | } | 
 |  | 
 | int VoEDtmfImpl::GetDtmfFeedbackStatus(bool& enabled, bool& directFeedback) | 
 | { | 
 |     WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_shared->instance_id(), -1), | 
 |                  "GetDtmfFeedbackStatus()"); | 
 |  | 
 |     CriticalSectionScoped sc(_shared->crit_sec()); | 
 |  | 
 |     enabled = _dtmfFeedback; | 
 |     directFeedback = _dtmfDirectFeedback; | 
 |  | 
 |     WEBRTC_TRACE(kTraceStateInfo, kTraceVoice, | 
 |         VoEId(_shared->instance_id(), -1), | 
 |         "GetDtmfFeedbackStatus() => enabled=%d, directFeedback=%d", | 
 |         enabled, directFeedback); | 
 |     return 0; | 
 | } | 
 |  | 
 | int VoEDtmfImpl::SetDtmfPlayoutStatus(int channel, bool enable) | 
 | { | 
 |     WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_shared->instance_id(), -1), | 
 |                  "SetDtmfPlayoutStatus(channel=%d, enable=%d)", | 
 |                  channel, enable); | 
 |  | 
 |     if (!_shared->statistics().Initialized()) | 
 |     { | 
 |         _shared->SetLastError(VE_NOT_INITED, kTraceError); | 
 |         return -1; | 
 |     } | 
 |     voe::ChannelOwner ch = _shared->channel_manager().GetChannel(channel); | 
 |     voe::Channel* channelPtr = ch.channel(); | 
 |     if (channelPtr == NULL) | 
 |     { | 
 |         _shared->SetLastError(VE_CHANNEL_NOT_VALID, kTraceError, | 
 |             "SetDtmfPlayoutStatus() failed to locate channel"); | 
 |         return -1; | 
 |     } | 
 |     return channelPtr->SetDtmfPlayoutStatus(enable); | 
 | } | 
 |  | 
 | int VoEDtmfImpl::GetDtmfPlayoutStatus(int channel, bool& enabled) | 
 | { | 
 |     WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_shared->instance_id(), -1), | 
 |                  "GetDtmfPlayoutStatus(channel=%d, enabled=?)", channel); | 
 |     if (!_shared->statistics().Initialized()) | 
 |     { | 
 |         _shared->SetLastError(VE_NOT_INITED, kTraceError); | 
 |         return -1; | 
 |     } | 
 |     voe::ChannelOwner ch = _shared->channel_manager().GetChannel(channel); | 
 |     voe::Channel* channelPtr = ch.channel(); | 
 |     if (channelPtr == NULL) | 
 |     { | 
 |         _shared->SetLastError(VE_CHANNEL_NOT_VALID, kTraceError, | 
 |             "GetDtmfPlayoutStatus() failed to locate channel"); | 
 |         return -1; | 
 |     } | 
 |     enabled = channelPtr->DtmfPlayoutStatus(); | 
 |     WEBRTC_TRACE(kTraceStateInfo, kTraceVoice, | 
 |         VoEId(_shared->instance_id(), -1), | 
 |         "GetDtmfPlayoutStatus() => enabled=%d", enabled); | 
 |     return 0; | 
 | } | 
 |  | 
 | #endif  // #ifdef WEBRTC_VOICE_ENGINE_DTMF_API | 
 |  | 
 | }  // namespace webrtc |