#include "SpeexProcessor.h" #include #include #include #include #include #include #include #include #include #include "gui/settings/rsharesettings.h" #define iroundf(x) ( static_cast(x) ) using namespace QtSpeex; SpeexInputProcessor::SpeexInputProcessor(QObject *parent) : QIODevice(parent), preprocessor(0), enc_state(0), enc_bits(), send_timestamp(0), echo_state(0), inputBuffer(), iMaxBitRate(16800), bResetProcessor(true), lastEchoFrame(NULL) { enc_bits = new SpeexBits; speex_bits_init(enc_bits); speex_bits_reset(enc_bits); enc_state = speex_encoder_init(&speex_wb_mode); int iArg = 0; speex_encoder_ctl(enc_state,SPEEX_SET_VAD, &iArg); speex_encoder_ctl(enc_state,SPEEX_SET_DTX, &iArg); float fArg=9.0; speex_encoder_ctl(enc_state,SPEEX_SET_VBR_QUALITY, &fArg); iArg = iMaxBitRate; speex_encoder_ctl(enc_state, SPEEX_SET_VBR_MAX_BITRATE, &iArg); iArg = 10; speex_encoder_ctl(enc_state,SPEEX_SET_COMPLEXITY, &iArg); iArg = 9; speex_encoder_ctl(enc_state,SPEEX_SET_QUALITY, &iArg); echo_state = NULL; //iEchoFreq = iMicFreq = iSampleRate; iSilentFrames = 0; iHoldFrames = 0; bResetProcessor = true; //bEchoMulti = false; preprocessor = NULL; echo_state = NULL; //srsMic = srsEcho = NULL; //iJitterSeq = 0; //iMinBuffered = 1000; //psMic = new short[iFrameSize]; psClean = new short[SAMPLING_RATE]; //psSpeaker = NULL; //iEchoChannels = iMicChannels = 0; //iEchoFilled = iMicFilled = 0; //eMicFormat = eEchoFormat = SampleFloat; //iMicSampleSize = iEchoSampleSize = 0; bPreviousVoice = false; //pfMicInput = pfEchoInput = pfOutput = NULL; iRealTimeBitrate = 0; dPeakSignal = dPeakSpeaker = dPeakMic = dPeakCleanMic = dVoiceAcivityLevel = 0.0; //if (g.uiSession) { //TODO : get the maxbitrate from a rs service or a dynamic code //iMaxBitRate = 10000; //} //bRunning = true; } SpeexInputProcessor::~SpeexInputProcessor() { speex_preprocess_state_destroy(preprocessor); if (echo_state) { speex_echo_state_destroy(echo_state); } speex_encoder_destroy(enc_state); speex_bits_destroy(enc_bits); delete enc_bits; free(psClean); } QByteArray SpeexInputProcessor::getNetworkPacket() { return outputNetworkBuffer.takeFirst(); } bool SpeexInputProcessor::hasPendingPackets() { return !outputNetworkBuffer.empty(); } qint64 SpeexInputProcessor::writeData(const char *data, qint64 maxSize) { int iArg; int i; float sum; short max; inputBuffer += QByteArray(data, maxSize); while(inputBuffer.size() > FRAME_SIZE * sizeof(qint16)) { QByteArray source_frame = inputBuffer.left(FRAME_SIZE * sizeof(qint16)); short* psMic = (short *)source_frame.data(); //let's do volume detection sum=1.0f; for (i=0;i(psMic[i] * psMic[i]); } dPeakMic = qMax(20.0f*log10f(sqrtf(sum / static_cast(FRAME_SIZE)) / 32768.0f), -96.0f); max = 1; for (i=0;i(std::abs(psMic[i]) > max ? std::abs(psMic[i]) : max); dMaxMic = max; dPeakSpeaker = 0.0; QMutexLocker l(&qmSpeex); if (bResetProcessor) { if (preprocessor) speex_preprocess_state_destroy(preprocessor); preprocessor = speex_preprocess_state_init(FRAME_SIZE, SAMPLING_RATE); iArg = 1; speex_preprocess_ctl(preprocessor, SPEEX_PREPROCESS_SET_VAD, &iArg); speex_preprocess_ctl(preprocessor, SPEEX_PREPROCESS_SET_AGC, &iArg); speex_preprocess_ctl(preprocessor, SPEEX_PREPROCESS_SET_DENOISE, &iArg); speex_preprocess_ctl(preprocessor, SPEEX_PREPROCESS_SET_DEREVERB, &iArg); iArg = 30000; speex_preprocess_ctl(preprocessor, SPEEX_PREPROCESS_SET_AGC_TARGET, &iArg); iArg = -60; speex_preprocess_ctl(preprocessor, SPEEX_PREPROCESS_SET_AGC_DECREMENT, &iArg); iArg = Settings->getVoipiNoiseSuppress(); speex_preprocess_ctl(preprocessor, SPEEX_PREPROCESS_SET_NOISE_SUPPRESS, &iArg); if (echo_state) { iArg = SAMPLING_RATE; speex_echo_ctl(echo_state, SPEEX_ECHO_SET_SAMPLING_RATE, &iArg); speex_preprocess_ctl(preprocessor, SPEEX_PREPROCESS_SET_ECHO_STATE, echo_state); } bResetProcessor = false; } float v = 30000.0f / static_cast(Settings->getVoipiMinLoudness()); iArg = iroundf(floorf(20.0f * log10f(v))); speex_preprocess_ctl(preprocessor, SPEEX_PREPROCESS_SET_AGC_MAX_GAIN, &iArg); speex_preprocess_ctl(preprocessor, SPEEX_PREPROCESS_GET_AGC_GAIN, &iArg); float gainValue = static_cast(iArg); iArg = Settings->getVoipiNoiseSuppress() - iArg; speex_preprocess_ctl(preprocessor, SPEEX_PREPROCESS_SET_NOISE_SUPPRESS, &iArg); short * psSource = psMic; if (echo_state && Settings->getVoipEchoCancel()) { speex_echo_playback(echo_state, (short*)lastEchoFrame->data()); speex_echo_capture(echo_state,psMic,psClean); psSource = psClean; } speex_preprocess_run(preprocessor, psSource); //we will now analize the processed signal sum=1.0f; for (i=0;i(psSource[i] * psSource[i]); float micLevel = sqrtf(sum / static_cast(FRAME_SIZE)); dPeakSignal = qMax(20.0f*log10f(micLevel / 32768.0f), -96.0f); spx_int32_t prob = 0; speex_preprocess_ctl(preprocessor, SPEEX_PREPROCESS_GET_PROB, &prob);//speech probability fSpeechProb = static_cast(prob) / 100.0f; // clean microphone level: peak of filtered signal attenuated by AGC gain dPeakCleanMic = qMax(dPeakSignal - gainValue, -96.0f); dVoiceAcivityLevel = 0.4f * fSpeechProb + 0.6f * (1.0f + dPeakCleanMic / 96.0f);//ponderation for speech detection and audio amplitude bool bIsSpeech = false; if (dVoiceAcivityLevel > (static_cast(Settings->getVoipfVADmax()) / 32767)) bIsSpeech = true; else if (dVoiceAcivityLevel > (static_cast(Settings->getVoipfVADmin()) / 32767) && bPreviousVoice) bIsSpeech = true; if (! bIsSpeech) { iHoldFrames++; if (iHoldFrames < Settings->getVoipVoiceHold()) bIsSpeech = true; } else { iHoldFrames = 0; } if (Settings->getVoipATransmit() == RshareSettings::AudioTransmitContinous) { bIsSpeech = true; } else if (Settings->getVoipATransmit() == RshareSettings::AudioTransmitPushToTalk) bIsSpeech = false;//g.s.uiDoublePush && ((g.uiDoublePush < g.s.uiDoublePush) || (g.tDoublePush.elapsed() < g.s.uiDoublePush)); //bIsSpeech = bIsSpeech || (g.iPushToTalk > 0); /*if (g.s.bMute || ((g.s.lmLoopMode != Settings::Local) && p && (p->bMute || p->bSuppress)) || g.bPushToMute || (g.iTarget < 0)) { bIsSpeech = false; }*/ if (bIsSpeech) { iSilentFrames = 0; } else { iSilentFrames++; } /*if (p) { if (! bIsSpeech) p->setTalking(Settings::Passive); else if (g.iTarget == 0) p->setTalking(Settings::Talking); else p->setTalking(Settings::Shouting); }*/ if (! bIsSpeech && ! bPreviousVoice) { iRealTimeBitrate = 0; /*if (g.s.iIdleTime && ! g.s.bDeaf && ((tIdle.elapsed() / 1000000ULL) > g.s.iIdleTime)) { emit doDeaf(); tIdle.restart(); }*/ spx_int32_t increment = 0; speex_preprocess_ctl(preprocessor, SPEEX_PREPROCESS_SET_AGC_INCREMENT, &increment); } else { spx_int32_t increment = 12; speex_preprocess_ctl(preprocessor, SPEEX_PREPROCESS_SET_AGC_INCREMENT, &increment); } int vbr_on=0; //just use fixed bitrate for now //encryption of VBR-encoded speech may not ensure complete privacy, as phrases can still be identified, at least in a controlled setting with a small dictionary of phrases, by analysing the pattern of variation of the bit rate. if (Settings->getVoipATransmit() == RshareSettings::AudioTransmitVAD) {//maybe we can do fixer bitrate when voice detection is active vbr_on = 1;//test it on for all modes } else {//maybe we can do vbr for ppt and continuous vbr_on = 1; } speex_encoder_ctl(enc_state,SPEEX_SET_VBR, &vbr_on); int br = 0; speex_encoder_ctl(enc_state, SPEEX_GET_VBR_MAX_BITRATE, &br); if (br != iMaxBitRate) { br = iMaxBitRate; speex_encoder_ctl(enc_state, SPEEX_SET_VBR_MAX_BITRATE, &br); } speex_encoder_ctl(enc_state, SPEEX_GET_BITRATE, &br); if (br != iMaxBitRate) { br = iMaxBitRate; speex_encoder_ctl(enc_state, SPEEX_SET_BITRATE, &br); } if (! bPreviousVoice) speex_encoder_ctl(enc_state, SPEEX_RESET_STATE, NULL); if (bIsSpeech) { speex_bits_reset(enc_bits); speex_encode_int(enc_state, psSource, enc_bits); QByteArray networkFrame; networkFrame.resize(speex_bits_nbytes(enc_bits)+4);//add 4 for the frame timestamp for the jitter buffer int packetSize = speex_bits_write(enc_bits, networkFrame.data()+4, networkFrame.size()-4); ((int*)networkFrame.data())[0] = send_timestamp; outputNetworkBuffer.append(networkFrame); emit networkPacketReady(); iRealTimeBitrate = packetSize * SAMPLING_RATE / FRAME_SIZE * 8; } else { iRealTimeBitrate = 0; } bPreviousVoice = bIsSpeech; //std::cerr << "iRealTimeBitrate : " << iRealTimeBitrate << std::endl; send_timestamp += FRAME_SIZE; if (send_timestamp >= INT_MAX) send_timestamp = 0; inputBuffer = inputBuffer.right(inputBuffer.size() - FRAME_SIZE * sizeof(qint16)); } return maxSize; } SpeexOutputProcessor::SpeexOutputProcessor(QObject *parent) : QIODevice(parent), outputBuffer() { } SpeexOutputProcessor::~SpeexOutputProcessor() { QHashIterator i(userJitterHash); while (i.hasNext()) { i.next(); speex_jitter_destroy(*(i.value())); free (i.value()); } } void SpeexOutputProcessor::putNetworkPacket(QString name, QByteArray packet) { //buffer: // timestamp | encodedBuf // —————–———–——————–———–——————–———–——————– // 4 | totalSize – 4 //the size part (first 4 byets) is not actually used in the logic if (packet.size() > 4) { SpeexJitter* userJitter; if (userJitterHash.contains(name)) { userJitter = userJitterHash.value(name); } else { userJitter = (SpeexJitter*)malloc(sizeof(SpeexJitter)); speex_jitter_init(userJitter, speex_decoder_init(&speex_wb_mode), SAMPLING_RATE); int on = 1; speex_decoder_ctl(userJitter->dec, SPEEX_SET_ENH, &on); userJitterHash.insert(name, userJitter); } int recv_timestamp = ((int*)packet.data())[0]; userJitter->mostUpdatedTSatPut = recv_timestamp; if (userJitter->firsttimecalling_get) return; speex_jitter_put(*userJitter, (char *)packet.data()+4, packet.size()-4, recv_timestamp); } } bool SpeexInputProcessor::isSequential() const { return true; } void SpeexInputProcessor::addEchoFrame(QByteArray* echo_frame) { if (Settings->getVoipEchoCancel() && echo_frame) { QMutexLocker l(&qmSpeex); lastEchoFrame = echo_frame; if (!echo_state) {//init echo_state echo_state = speex_echo_state_init(FRAME_SIZE, ECHOTAILSIZE*FRAME_SIZE); int tmp = SAMPLING_RATE; speex_echo_ctl(echo_state, SPEEX_ECHO_SET_SAMPLING_RATE, &tmp); bResetProcessor = true; } lastEchoFrame = echo_frame; } } qint64 SpeexOutputProcessor::readData(char *data, qint64 maxSize) { int ts = 0; //time stamp for the jitter call while(outputBuffer.size() < maxSize) { QByteArray* result_frame = new QByteArray(); result_frame->resize(FRAME_SIZE * sizeof(qint16)); result_frame->fill(0,FRAME_SIZE * sizeof(qint16)); QHashIterator i(userJitterHash); while (i.hasNext()) { i.next(); SpeexJitter* jitter = i.value(); QByteArray intermediate_frame; intermediate_frame.resize(FRAME_SIZE * sizeof(qint16)); if (jitter->firsttimecalling_get) { int ts = jitter->mostUpdatedTSatPut; jitter->firsttimecalling_get = false; } speex_jitter_get(*jitter, (spx_int16_t*)intermediate_frame.data(), &ts); for (int j = 0; j< FRAME_SIZE; j++) { short sample1 = ((short*)result_frame->data())[j]; short sample2 = ((short*)intermediate_frame.data())[j]; float samplef1 = sample1 / 32768.0f; float samplef2 = sample2 / 32768.0f; float mixed = samplef1 + 0.8f * samplef2; // hard clipping if (mixed > 1.0f) mixed = 1.0f; if (mixed < -1.0f) mixed = -1.0f; ((spx_int16_t*)result_frame->data())[j] = (short)(mixed * 32768.0f); } } outputBuffer += *result_frame; emit playingFrame(result_frame); } QByteArray resultBuffer = outputBuffer.left(maxSize); memcpy(data, resultBuffer.data(), resultBuffer.size()); outputBuffer = outputBuffer.right(outputBuffer.size() - resultBuffer.size()); return resultBuffer.size(); } bool SpeexOutputProcessor::isSequential() const { return true; } void SpeexOutputProcessor::speex_jitter_init(SpeexJitter *jit, void *decoder, int sampling_rate) { jit->dec = decoder; speex_decoder_ctl(decoder, SPEEX_GET_FRAME_SIZE, &jit->frame_size); jit->packets = jitter_buffer_init(jit->frame_size); jit->current_packet = new SpeexBits; speex_bits_init(jit->current_packet); jit->valid_bits = 0; jit->firsttimecalling_get = true; jit->mostUpdatedTSatPut = 0; } void SpeexOutputProcessor::speex_jitter_destroy(SpeexJitter jitter) { if (jitter.dec) { speex_decoder_destroy(jitter.dec); } jitter_buffer_destroy(jitter.packets); speex_bits_destroy(jitter.current_packet); } void SpeexOutputProcessor::speex_jitter_put(SpeexJitter jitter, char *packet, int len, int timestamp) { JitterBufferPacket p; p.data = packet; p.len = len; p.timestamp = timestamp; p.span = jitter.frame_size; jitter_buffer_put(jitter.packets, &p); } void SpeexOutputProcessor::speex_jitter_get(SpeexJitter jitter, spx_int16_t *out, int *current_timestamp) { int i; int ret; spx_int32_t activity; int bufferCount = 0; JitterBufferPacket packet; char data[FRAME_SIZE * ECHOTAILSIZE * 10]; packet.data = data; packet.len = FRAME_SIZE * ECHOTAILSIZE * 10; if (jitter.valid_bits) { /* Try decoding last received packet */ ret = speex_decode_int(jitter.dec, jitter.current_packet, out); if (ret == 0) { jitter_buffer_tick(jitter.packets); return; } else { jitter.valid_bits = 0; } } if (current_timestamp) ret = jitter_buffer_get(jitter.packets, &packet, jitter.frame_size, current_timestamp); else ret = jitter_buffer_get(jitter.packets, &packet, jitter.frame_size, NULL); if (ret != JITTER_BUFFER_OK) { /* No packet found */ speex_decode_int(jitter.dec, NULL, out); } else { speex_bits_read_from(jitter.current_packet, packet.data, packet.len); /* Decode packet */ ret = speex_decode_int(jitter.dec, jitter.current_packet, out); if (ret == 0) { jitter.valid_bits = 1; } else { /* Error while decoding */ for (i=0;i