#include "SpeexDsp.h"
#include <speex/speex_echo.h>
#include <speex/speex_preprocess.h>

SpeexDsp::~SpeexDsp() {
    close();
}



void SpeexDsp::start(int sampleRate, int channels, int period) {
    close();
    int frameSize = sampleRate * period / 1000; // 10-20 ms
    int filterLength = sampleRate * 500 / 1000; // 100-500 ms
    m_echoState = speex_echo_state_init_mc(frameSize, filterLength, channels, channels);
    speex_echo_ctl(m_echoState, SPEEX_ECHO_SET_SAMPLING_RATE, &sampleRate);

    m_preprocessState = speex_preprocess_state_init(frameSize, sampleRate);
    speex_preprocess_ctl(m_preprocessState, SPEEX_PREPROCESS_SET_ECHO_STATE, m_echoState);

    int32_t noiseSuppress = -25, i = 1;
    speex_preprocess_ctl(m_preprocessState, SPEEX_PREPROCESS_SET_DENOISE, &i);
    speex_preprocess_ctl(m_preprocessState, SPEEX_PREPROCESS_SET_NOISE_SUPPRESS, &noiseSuppress);

    i = 0;
    speex_preprocess_ctl(m_preprocessState, SPEEX_PREPROCESS_SET_AGC, &i);
    i = sampleRate;
    speex_preprocess_ctl(m_preprocessState, SPEEX_PREPROCESS_SET_AGC_LEVEL, &i);
    i = 0;
    speex_preprocess_ctl(m_preprocessState, SPEEX_PREPROCESS_SET_DEREVERB, &i);
    float f = .0;
    speex_preprocess_ctl(m_preprocessState, SPEEX_PREPROCESS_SET_DEREVERB_DECAY, &f);
    f = .0;
    speex_preprocess_ctl(m_preprocessState, SPEEX_PREPROCESS_SET_DEREVERB_LEVEL, &f);
}

void SpeexDsp::close() {
    if (m_preprocessState != nullptr) {
        speex_preprocess_state_destroy(m_preprocessState);
        m_preprocessState = nullptr;
    }

    if (m_echoState != nullptr) {
        speex_echo_state_destroy(m_echoState);
        m_echoState = nullptr;
    }
}

void SpeexDsp::reset() {
    if (m_echoState != nullptr) {
        speex_echo_state_reset(m_echoState);
    }
}

void SpeexDsp::preprocess(int16_t *pcm) {
    if (m_preprocessState != nullptr) {
        speex_preprocess_run(m_preprocessState, pcm);
    }
}

void SpeexDsp::echoPlayback(const int16_t *play) {
    if (m_echoState != nullptr) {
        speex_echo_playback(m_echoState, play);
    }
}

void SpeexDsp::echoCapture(const int16_t *record, int16_t *out) {
    if (m_echoState != nullptr) {
        speex_echo_capture(m_echoState, record, out);
    }
}

void SpeexDsp::echoCancellation(const int16_t *record, const int16_t *play, int16_t *out) {
    if (m_echoState != nullptr) {
        speex_echo_cancellation(m_echoState, record, play, out);
    }
}
