You need to enable JavaScript to run this app.
导航

【Java】回声消除-V3

最近更新时间2023.07.07 11:16:17

首次发布时间2023.03.16 11:40:12

使用步骤

1. 创建handle

SAMICoreExecutorContextCreateParameter参数介绍

参数类型说明
sampleRateint入参,指音频的采样率
maxBlockSizeint入参,每次输入音频的最大的大小,算法需要根据此字段提前分配内存等,接下来每次送入process的大小不能超过该值
numChannelint入参,音频的通道数
modelBufferString入参,模型的内容,模型详见回声消除介绍小节
modelLenint入参,模型的内容的长度
bussinessInfoString入参, 表示调用的业务方信息
numAudioBufferint入参, 回声消除v3为2
configInfoString入参, 见下文configInfo

configInfo

参数类型说明
utilitystring入参,固定设置为CommonAecUtility

enable_stereo

bool

入参,默认值:false;
当处理两个通道的数据时候,enable_stereo为ture,两个通道单独处理;enable_stereo为false,处理第一个通道后,拷贝结果覆盖第二个通道,节省一半计算量;

enable_pre_delay

bool

入参,默认值:false;
算法需要送入足够的数据才会输出结果,在实时场景需要等进等出,enable_pre_delay=true,会在一开始返回静音缓冲数据,减少接入难度,建议rtc场景默认开启

enable_pre_processbool入参,默认值:false; true时候打开防爆音前处理
enable_after_processbool入参,默认值:false; true时候打开单双讲检测后处理,在单讲场景提供更好的效果
after_process_paramjson-map入参,默认值:见下文 单双讲检测后处理的配置参数
  • after_process_param参数介绍
参数含义范围
dtd_threshold控制单双讲检测的阈值0<=x<=1,默认取0.2
st_ramp控制检测到单讲后gain的衰退速率0<=x<=1,默认取0.2
dt_ramp控制检测到双讲后gain的增益速率0<=x<=1,默认取0.2

举例:

SAMICoreExecutorContextCreateParameter parameter = new SAMICoreExecutorContextCreateParameter();
parameter.sampleRate = sampleRate;
parameter.numChannel = numChannel;
parameter.maxBlockSize = maxBlockSize;
parameter.modelBuffer = FunctionHelper.readBinaryFile(modelFileName);
parameter.modelLen = parameter.modelBuffer.length;
parameter.bussinessInfo= "java demo";
parameter.numberAudioData = 2;
parameter.configInfo="{\"utility\":\"CommonAecUtility\",
                       \"enable_stereo\":true,
                       \"enable_pre_delay\":true,
                       \"enable_pre_process\":true,
                       \"enable_after_process\":true,
                       \"after_process_param\": {\"dtd_threshold\":0.2, 
                                                 \"st_ramp\":0.2,
                                                 \"dt_ramp\":0.2}}";
int ret = samiCore.SAMICoreCreateHandleByIdentify(
        SAMICoreIdentify.SAMICoreIdentify_EngineExecutor_CE_AEC, parameter);
if (ret != SAMICoreCode.SAMI_OK) {
  System.out.println("Create AecV3 handle failed, ret " + ret);
  return ret;
}

2. 初始化buffer

SAMICoreBlock inBlock = new SAMICoreBlock();
SAMICoreBlock outBlock = new SAMICoreBlock();

inBlock.dataType = SAMICoreDataType.SAMICoreDataType_AudioBuffer;
SAMICoreAudioBuffer micAudioBuffer = new SAMICoreAudioBuffer();
micAudioBuffer.numberChannels = numChannel;
micAudioBuffer.numberSamples = maxBlockSize;
SAMICoreAudioBuffer refAudioBuffer = new SAMICoreAudioBuffer();
refAudioBuffer.numberChannels = numChannel;
refAudioBuffer.numberSamples = maxBlockSize;

inBlock.audioData = new SAMICoreAudioBuffer[2];
inBlock.audioData[0] = micAudioBuffer;
inBlock.audioData[1] = refAudioBuffer;

outBlock.dataType = SAMICoreDataType.SAMICoreDataType_AudioBuffer;
SAMICoreAudioBuffer outAudioBuffer = new SAMICoreAudioBuffer();
outAudioBuffer.numberChannels = numChannel;
outAudioBuffer.numberSamples = maxBlockSize;
outBlock.audioData = new SAMICoreAudioBuffer[1];
outBlock.audioData[0] = outAudioBuffer;

numberChannels : 需要跟创建handle设置的一致
numberSamples:每一个处理的实际帧数,要求 numberSamples<= parameter.maxBlockSize

3. 处理数据

for (int numSamples = 0; numSamples < totalNumSamples;) {
    //更新实际输入输出的帧数
    if((numSamples + inAudioBuffer.numberSamples) > totalNumSamples) {
        micAudioBuffer.numberSamples = totalNumSamples - numSamples;
        refAudioBuffer.numberSamples = totalNumSamples - numSamples;
        outAudioBuffer.numberSamples = totalNumSamples - numSamples;
    }
    
    //拷贝处理数据到要处理的buffer
    for (int i = 0; i < inAudioBuffer.numberChannels; i++) {
        for (int j = 0; j < inAudioBuffer.numberSamples; j++) {
            micAudioBuffer.data[i][j] = in_mic_planar_float_data[i][numSamples + j];
            refAudioBuffer.data[i][j] = in_ref_planar_float_data[i][numSamples + j];
        }
    }
    
    numSamples = numSamples + micAudioBuffer.numberSamples;

    //调用处理函数
    int ret = samiCore.SAMICoreProcess(inBlock, outBlock);
    if (ret != SAMICoreCode.SAMI_OK  && ret != SAMICoreCode.SAMI_ENGINE_INPUT_NEED_MORE_DAT) {
      System.out.println("AecV3 process audio data failed, ret " + ret);
      return ret;
    }
    
    if(outAudioBuffer.numberSamples>0){
        //从outAudioBuffer获取处理后的数据
        for (int i = 0; i < inAudioBuffer.numberChannels; i++) {
            for (int j = 0; j < inAudioBuffer.numberSamples; j++) {
                out_planar_float_data[i][j+numSamples] = outAudioBuffer.data[i][j];
            }
        }
    }
}

4. 获取最后结果

由于算法存在缓存部分数据,因此当音频已经全部送入算法后,需要调用这个接口,将所有缓存的数据取出

SAMICoreProperty flushProperty = new SAMICoreProperty();
flushProperty.id=SAMICorePropertyId.SAMICorePropertyID_Common_Flush;
samiCore.SAMICoreGetPropertyById(SAMICorePropertyId.SAMICorePropertyID_Common_Flush,flushProperty);
SAMICoreAudioBuffer[] outAudioBufferArray = (SAMICoreAudioBuffer[])flushProperty.dataObjectArray;
int bufcnt = flushProperty.dataArrayLen;
for(int i=0;i<outAudioBufferArray[0].numberChannels;i++){
    for(int j=0;j<outAudioBufferArray[0].numberSamples;j++){
        out_planar_float_data[i][j+outPutSampleCnt] = outAudioBufferArray[0].data[i][j];
    }
}

5. 重置handle

当处理完成后,后续有新数据需要处理,而又不想重复创建handle时,可以调用reset接口,之后就可以当做一个新实例环境来用

SAMICoreProperty resetProperty = new SAMICoreProperty();
resetProperty.id = SAMICorePropertyId.SAMICorePropertyID_Common_Reset;
resetProperty.type = SAMICoreDataType.SAMICoreDataType_Null;
int ret = samiCore.SAMICoreSetProperty(SAMICorePropertyId.SAMICorePropertyID_Common_Reset,samiCoreProperty);
if (ret != SAMICoreCode.SAMI_OK) {
  System.out.println("SAMICoreSetProperty failed, ret " + ret);
  return ret;
}

6. 释放handle

samiCore.SAMICoreDestroyHandle();