/* * 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 "acm_speex.h" #include "acm_codec_database.h" #include "acm_common_defs.h" #include "acm_neteq.h" #include "trace.h" #include "webrtc_neteq.h" #include "webrtc_neteq_help_macros.h" #ifdef WEBRTC_CODEC_SPEEX // NOTE! Speex is not included in the open-source package. Modify this file or // your codec API to match the function calls and names of used Speex API file. #include "speex_interface.h" #endif namespace webrtc { #ifndef WEBRTC_CODEC_SPEEX ACMSPEEX::ACMSPEEX(WebRtc_Word16 /* codecID */) : _encoderInstPtr(NULL), _decoderInstPtr(NULL), _complMode(0), _vbrEnabled(false), _encodingRate(-1), _samplingFrequency(-1), _samplesIn20MsAudio(-1) { return; } ACMSPEEX::~ACMSPEEX() { return; } WebRtc_Word16 ACMSPEEX::InternalEncode(WebRtc_UWord8* /* bitStream */, WebRtc_Word16* /* bitStreamLenByte */) { return -1; } WebRtc_Word16 ACMSPEEX::DecodeSafe(WebRtc_UWord8* /* bitStream */, WebRtc_Word16 /* bitStreamLenByte */, WebRtc_Word16* /* audio */, WebRtc_Word16* /* audioSamples */, WebRtc_Word8* /* speechType */) { return -1; } WebRtc_Word16 ACMSPEEX::EnableDTX() { return -1; } WebRtc_Word16 ACMSPEEX::DisableDTX() { return -1; } WebRtc_Word16 ACMSPEEX::InternalInitEncoder( WebRtcACMCodecParams* /* codecParams */) { return -1; } WebRtc_Word16 ACMSPEEX::InternalInitDecoder( WebRtcACMCodecParams* /* codecParams */) { return -1; } WebRtc_Word32 ACMSPEEX::CodecDef(WebRtcNetEQ_CodecDef& /* codecDef */, const CodecInst& /* codecInst */) { return -1; } ACMGenericCodec* ACMSPEEX::CreateInstance(void) { return NULL; } WebRtc_Word16 ACMSPEEX::InternalCreateEncoder() { return -1; } void ACMSPEEX::DestructEncoderSafe() { return; } WebRtc_Word16 ACMSPEEX::InternalCreateDecoder() { return -1; } void ACMSPEEX::DestructDecoderSafe() { return; } WebRtc_Word16 ACMSPEEX::SetBitRateSafe(const WebRtc_Word32 /* rate */) { return -1; } void ACMSPEEX::InternalDestructEncoderInst(void* /* ptrInst */) { return; } #ifdef UNUSEDSPEEX WebRtc_Word16 ACMSPEEX::EnableVBR() { return -1; } WebRtc_Word16 ACMSPEEX::DisableVBR() { return -1; } WebRtc_Word16 ACMSPEEX::SetComplMode(WebRtc_Word16 mode) { return -1; } #endif #else //===================== Actual Implementation ======================= ACMSPEEX::ACMSPEEX(WebRtc_Word16 codecID) : _encoderInstPtr(NULL), _decoderInstPtr(NULL) { _codecID = codecID; // Set sampling frequency, frame size and rate Speex if (_codecID == ACMCodecDB::kSPEEX8) { _samplingFrequency = 8000; _samplesIn20MsAudio = 160; _encodingRate = 11000; } else if (_codecID == ACMCodecDB::kSPEEX16) { _samplingFrequency = 16000; _samplesIn20MsAudio = 320; _encodingRate = 22000; } else { WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _uniqueID, "Wrong codec id for Speex."); _samplingFrequency = -1; _samplesIn20MsAudio = -1; _encodingRate = -1; } _hasInternalDTX = true; _dtxEnabled = false; _vbrEnabled = false; _complMode = 3; // default complexity value return; } ACMSPEEX::~ACMSPEEX() { if (_encoderInstPtr != NULL) { WebRtcSpeex_FreeEnc(_encoderInstPtr); _encoderInstPtr = NULL; } if (_decoderInstPtr != NULL) { WebRtcSpeex_FreeDec(_decoderInstPtr); _decoderInstPtr = NULL; } return; } WebRtc_Word16 ACMSPEEX::InternalEncode(WebRtc_UWord8* bitStream, WebRtc_Word16* bitStreamLenByte) { WebRtc_Word16 status; WebRtc_Word16 numEncodedSamples = 0; WebRtc_Word16 n = 0; while (numEncodedSamples < _frameLenSmpl) { status = WebRtcSpeex_Encode(_encoderInstPtr, &_inAudio[_inAudioIxRead], _encodingRate); // increment the read index this tell the caller that how far // we have gone forward in reading the audio buffer _inAudioIxRead += _samplesIn20MsAudio; numEncodedSamples += _samplesIn20MsAudio; if (status < 0) { WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _uniqueID, "Error in Speex encoder"); return status; } // Update VAD, if internal DTX is used if (_hasInternalDTX && _dtxEnabled) { _vadLabel[n++] = status; _vadLabel[n++] = status; } if (status == 0) { // This frame is detected as inactive. We need send whatever // encoded so far. *bitStreamLenByte = WebRtcSpeex_GetBitstream(_encoderInstPtr, (WebRtc_Word16*) bitStream); return *bitStreamLenByte; } } *bitStreamLenByte = WebRtcSpeex_GetBitstream(_encoderInstPtr, (WebRtc_Word16*) bitStream); return *bitStreamLenByte; } WebRtc_Word16 ACMSPEEX::DecodeSafe(WebRtc_UWord8* /* bitStream */, WebRtc_Word16 /* bitStreamLenByte */, WebRtc_Word16* /* audio */, WebRtc_Word16* /* audioSamples */, WebRtc_Word8* /* speechType */) { return 0; } WebRtc_Word16 ACMSPEEX::EnableDTX() { if (_dtxEnabled) { return 0; } else if (_encoderExist) { // check if encoder exist // enable DTX if (WebRtcSpeex_EncoderInit(_encoderInstPtr, (_vbrEnabled ? 1 : 0), _complMode, 1) < 0) { WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _uniqueID, "Cannot enable DTX for Speex"); return -1; } _dtxEnabled = true; return 0; } else { return -1; } return 0; } WebRtc_Word16 ACMSPEEX::DisableDTX() { if (!_dtxEnabled) { return 0; } else if (_encoderExist) { // check if encoder exist // disable DTX if (WebRtcSpeex_EncoderInit(_encoderInstPtr, (_vbrEnabled ? 1 : 0), _complMode, 0) < 0) { WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _uniqueID, "Cannot disable DTX for Speex"); return -1; } _dtxEnabled = false; return 0; } else { // encoder doesn't exists, therefore disabling is harmless return 0; } return 0; } WebRtc_Word16 ACMSPEEX::InternalInitEncoder(WebRtcACMCodecParams* codecParams) { // sanity check if (_encoderInstPtr == NULL) { WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _uniqueID, "Cannot initialize Speex encoder, instance does not exist"); return -1; } WebRtc_Word16 status = SetBitRateSafe((codecParams->codecInstant).rate); status += (WebRtcSpeex_EncoderInit(_encoderInstPtr, _vbrEnabled, _complMode, ((codecParams->enableDTX) ? 1 : 0)) < 0) ? -1 : 0; if (status >= 0) { return 0; } else { WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _uniqueID, "Error in initialization of Speex encoder"); return -1; } } WebRtc_Word16 ACMSPEEX::InternalInitDecoder( WebRtcACMCodecParams* /* codecParams */) { WebRtc_Word16 status; // sanity check if (_decoderInstPtr == NULL) { WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _uniqueID, "Cannot initialize Speex decoder, instance does not exist"); return -1; } status = ((WebRtcSpeex_DecoderInit(_decoderInstPtr) < 0) ? -1 : 0); if (status >= 0) { return 0; } else { WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _uniqueID, "Error in initialization of Speex decoder"); return -1; } } WebRtc_Word32 ACMSPEEX::CodecDef(WebRtcNetEQ_CodecDef& codecDef, const CodecInst& codecInst) { if (!_decoderInitialized) { WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _uniqueID, "Error, Speex decoder is not initialized"); return -1; } // Fill up the structure by calling // "SET_CODEC_PAR" & "SET_SPEEX_FUNCTION." // Then call NetEQ to add the codec to its // database. switch (_samplingFrequency) { case 8000: { SET_CODEC_PAR((codecDef), kDecoderSPEEX_8, codecInst.pltype, _decoderInstPtr, 8000); break; } case 16000: { SET_CODEC_PAR((codecDef), kDecoderSPEEX_16, codecInst.pltype, _decoderInstPtr, 16000); break; } default: { WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _uniqueID, "Unsupported sampling frequency for Speex"); return -1; } } SET_SPEEX_FUNCTIONS((codecDef)); return 0; } ACMGenericCodec* ACMSPEEX::CreateInstance(void) { return NULL; } WebRtc_Word16 ACMSPEEX::InternalCreateEncoder() { return WebRtcSpeex_CreateEnc(&_encoderInstPtr, _samplingFrequency); } void ACMSPEEX::DestructEncoderSafe() { if (_encoderInstPtr != NULL) { WebRtcSpeex_FreeEnc(_encoderInstPtr); _encoderInstPtr = NULL; } // there is no encoder set the following _encoderExist = false; _encoderInitialized = false; _encodingRate = 0; } WebRtc_Word16 ACMSPEEX::InternalCreateDecoder() { return WebRtcSpeex_CreateDec(&_decoderInstPtr, _samplingFrequency, 1); } void ACMSPEEX::DestructDecoderSafe() { if (_decoderInstPtr != NULL) { WebRtcSpeex_FreeDec(_decoderInstPtr); _decoderInstPtr = NULL; } // there is no encoder instance set the followings _decoderExist = false; _decoderInitialized = false; } WebRtc_Word16 ACMSPEEX::SetBitRateSafe(const WebRtc_Word32 rate) { // Check if changed rate if (rate == _encodingRate) { return 0; } else if (rate > 2000) { _encodingRate = rate; _encoderParams.codecInstant.rate = rate; } else { WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _uniqueID, "Unsupported encoding rate for Speex"); return -1; } return 0; } void ACMSPEEX::InternalDestructEncoderInst(void* ptrInst) { if (ptrInst != NULL) { WebRtcSpeex_FreeEnc((SPEEX_encinst_t_*) ptrInst); } return; } #ifdef UNUSEDSPEEX // This API is currently not in use. If requested to be able to enable/disable VBR // an ACM API need to be added. WebRtc_Word16 ACMSPEEX::EnableVBR() { if (_vbrEnabled) { return 0; } else if (_encoderExist) // check if encoder exist { // enable Variable Bit Rate (VBR) if (WebRtcSpeex_EncoderInit(_encoderInstPtr, 1, _complMode, (_dtxEnabled ? 1 : 0)) < 0) { WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _uniqueID, "Cannot enable VBR mode for Speex"); return -1; } _vbrEnabled = true; return 0; } else { return -1; } } // This API is currently not in use. If requested to be able to enable/disable // VBR an ACM API need to be added. WebRtc_Word16 ACMSPEEX::DisableVBR() { if (!_vbrEnabled) { return 0; } else if (_encoderExist) { // check if encoder exist // disable DTX if (WebRtcSpeex_EncoderInit(_encoderInstPtr, 0, _complMode, (_dtxEnabled ? 1 : 0)) < 0) { WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _uniqueID, "Cannot disable DTX for Speex"); return -1; } _vbrEnabled = false; return 0; } else { // encoder doesn't exists, therefore disabling is harmless return 0; } } // This API is currently not in use. If requested to be able to set complexity // an ACM API need to be added. WebRtc_Word16 ACMSPEEX::SetComplMode(WebRtc_Word16 mode) { // Check if new mode if (mode == _complMode) { return 0; } else if (_encoderExist) { // check if encoder exist // Set new mode if (WebRtcSpeex_EncoderInit(_encoderInstPtr, 0, mode, (_dtxEnabled ? 1 : 0)) < 0) { WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceAudioCoding, _uniqueID, "Error in complexity mode for Speex"); return -1; } _complMode = mode; return 0; } else { // encoder doesn't exists, therefore disabling is harmless return 0; } } #endif #endif } // namespace webrtc