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

【OC】回声消除-V3

最近更新时间2023.08.30 15:12:18

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

使用步骤

1. 创建handle

SAMICore_CreateParameter参数介绍

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

举例:

SAMICore_CreateParameter *createParameter = [[SAMICore_CreateParameter alloc] init];
createParameter.sampleRate = sample_rate_;
createParameter.maxBlockSize = block_size_;
createParameter.modelBuffer = (char*)modelData.bytes;
createParameter.modelLen = modelData.length;
createParameter.numChannel = num_channel_;
createParameter.numAudioBuffer = 2;
createParameter.bussinessInfo = "oc_demo";
createParameter.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 = 0;
handle_ = [[SAMICore alloc] initWithIdentify:SAMICore_Identify_EngineExecutor_CE_AEC param:createParameter result:&ret];
if(ret != SAMI_OK) return NO;

2. 初始化buffer

NSArray<SAMICore_AudioBuffer *>* audio_buffers_ = [[NSArray alloc] initWithObjects:
         [[SAMICore_AudioBuffer alloc] initWithNumberSamples:block_size_
                                             numberChannels:num_channel_
                                               isInterleave:NO],
         [[SAMICore_AudioBuffer alloc] initWithNumberSamples:block_size_
                                             numberChannels:num_channel_
                                               isInterleave:NO], nil];

input_block_ = [[SAMICore_AudioBlock alloc] init];
input_block_.numberAudioData = 2;
input_block_.dataType = SAMICore_DataType_AudioBuffer;
input_block_.audioData = audio_buffers_;
    
SAMICore_AudioBuffer *out_audio_buffer_ = [[SAMICore_AudioBuffer alloc] initWithNumberSamples:block_size_
                                                                               numberChannels:num_channel_
                                                                                 isInterleave:NO];
output_block_ = [[SAMICore_AudioBlock alloc] init];
output_block_.numberAudioData = 1;
output_block_.dataType = SAMICore_DataType_AudioBuffer;
output_block_.audioData = out_audio_buffer_;

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

3. 处理数据

for(int inx = 0; inx < (int)num_samples_; inx += block_size_) {
    int real_size = inx + block_size_ > num_samples_ ? num_samples_ - inx : block_size_;
    for (int ch = 0; ch < num_channel_; ++ch) {
        std::copy_n(mic_data_[ch] + inx, real_size, ((float**)audio_buffers_[0].data)[ch]);
        std::copy_n(ref_data_[ch] + inx, real_size, ((float**)audio_buffers_[1].data)[ch]);
    }

    audio_buffers_[0].numberSamples = real_size;
    audio_buffers_[1].numberSamples = real_size;
    out_audio_buffer_.numberSamples = real_size;

    // process
    int ret = [handle_ processWithInBlock:input_block_ outBlock:output_block_];  
    if (ret != SAMI_OK && ret != SAMI_ENGINE_INPUT_NEED_MORE_DATA) {
        return NO;
    }

    if([out_audio_buffer_ isKindOfClass:[SAMICore_AudioBuffer class]] && out_audio_buffer_.numberSamples > 0) {
        // copy data to out_data
        for (int ch = 0; ch < num_channel_; ++ch) {
             std::copy_n(((float**)out_audio_buffer_.data)[ch], out_audio_buffer_.numberSamples, out_data[ch] + out_inx);
        }
        out_inx += out_audio_buffer_.numberSamples;
    }
}

4. 获取最后结果

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

SAMICore_Property *flushProperty = [[SAMICore_Property alloc] init];
flushProperty.id = SAMICore_PropertyID_Common_Flush;
flushProperty.type = SAMICore_DataType_AudioBuffer;
int ret = [handle_ getProperty:flushProperty withId:SAMICore_PropertyID_Common_Flush];
if(ret != SAMI_OK) {
   return NO;
}
NSArray* array = (NSArray*)flushProperty.data;
SAMICore_AudioBuffer* buffer = (SAMICore_AudioBuffer*)array[0];
if([buffer isKindOfClass:[SAMICore_AudioBuffer class]] && buffer.numberSamples > 0) {
    for(int ch = 0; ch < num_channel_; ++ch) {
        std::copy_n(((float**)buffer.data)[ch], buffer.numberSamples, out_data[ch] + out_inx);
    }
    out_inx += buffer.numberSamples;
}

5. 重置handle

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

SAMICore_Property *flushProperty = [[SAMICore_Property alloc] init];
flushProperty.id = SAMICore_PropertyID_Common_Reset;
flushProperty.type = SAMICore_DataType_Null;
int ret = [handle_ setProperty:core_property withId:SAMICore_PropertyID_Common_Reset];
if(ret != SAMI_OK) {
   return NO;
}

6. 释放handle

handle_ = nil;