#include "sami_core.h" // help function std::vector<uint8_t> loadModelAsBinary(const std::string& path) { std::ifstream file(path, std::ios::binary | std::ios::ate); std::streamsize size = file.tellg(); file.seekg(0, std::ios::beg); std::vector<uint8_t> buffer(size); if(file.read((char*)buffer.data(), size)) { return buffer; } return {}; } // step 0, load model const std::string model_path = "/path/to/aec.model"; std::vector<uint_8> model_buffer = loadModelAsBinary(model_path); assert(model_buffer.size() > 0); // step 1, create aec handle const int sample_rate = 44100; // 44100 needs aec44k.model, 16000 needs aec.model const int num_channels = 2; const int block_size = 512; SAMICoreHandle handle = nullptr; SAMICoreExecutorContextCreateParameter create_param; memset(&create_param,0,sizeof(create_param)); create_param.sampleRate = sample_rate; create_param.numChannel = num_channels; create_param.maxBlockSize = block_size; create_param.modelBuffer = (const char*)(model_buffer.data()); create_param.modelLen = model_buffer.size(); int ret = SAMICoreCreateHandleByIdentify(&handle, SAMICoreIdentify::SAMICoreIdentify_RNNAEC, & create_param); assert(ret == SAMI_OK); // step 2, create input and output audio block SAMICoreAudioBuffer in_mic_buffer; SAMICoreAudioBuffer in_ref_buffer; SAMICoreAudioBuffer out_buffer; in_mic_buffer.numberChannels = num_channels; in_mic_buffer.numberSamples = block_size; in_mic_buffer.data = new float*[num_channels]; in_mic_buffer.isInterleave = 0; in_ref_buffer.numberChannels = num_channels; in_ref_buffer.numberSamples = block_size; in_ref_buffer.data = new float*[num_channels]; in_ref_buffer.isInterleave = 0; out_buffer.numberChannels = num_channels; out_buffer.numberSamples = block_size; out_buffer.data = new float*[num_channels]; out_buffer.isInterleave = 0; for(int c = 0; c < int(num_channels); ++c){ in_mic_buffer.data[c] = new float[block_size]; in_ref_buffer.data[c] = new float[block_size]; out_buffer.data[c] = new float[block_size]; } SAMICoreAecInput aec_input; aec_input.mic = &in_mic_buffer; aec_input.ref = &in_ref_buffer; SAMICoreBlock in_block; SAMICoreBlock out_block; in_block.numberAudioData = 1; in_block.dataType = SAMICoreDataType_AecInput; in_block.audioData = &aec_input; out_block.numberAudioData = 1; out_block.dataType = SAMICoreDataType_AudioBuffer; out_block.audioData = &out_buffer; // step 3, process block by block for(;hasAudioSamples();) { copyMicSamplesToInputBuffer(in_mic_buffer); copyRefSamplesToInputBuffer(in_ref_buffer); int ret = SAMICoreProcess(handle, &in_block, &out_block); assert(ret == SAMI_OK); // do something after process doSomethingAfterProcess(out_buffer); } // step 4, remember release resource ret = SAMICoreDestroyHandle(handle); assert(ret == SAMI_OK); for(int c = 0; c < int(num_channels); ++c) { delete[] in_mic_buffer.data[c]; delete[] in_ref_buffer.data[c]; delete[] out_buffer.data[c]; } delete[] in_mic_buffer.data; delete[] in_ref_buffer.data; delete[] out_buffer.data;
即读取整个模型文件到内存,实现方法自由发挥,例子中loadModelAsBinary
仅供参考。算法模型详见回声消除介绍小节。
传入模型内存地址、模型大小、采样率和 maxBlockSize,通过 SAMICoreCreateHandleByIdentify
创建 handle。
const int sample_rate = 44100; // 44100 needs aec44k.model, 16000 needs aec.model const int num_channels = 2; const int block_size = 512; SAMICoreHandle handle = nullptr; SAMICoreExecutorContextCreateParameter create_param; create_param.sampleRate = sample_rate; create_param.numChannel = num_channels; create_param.maxBlockSize = block_size; create_param.modelBuffer = (const char*)(model_buffer.data()); create_param.modelLen = model_buffer.size(); int ret = SAMICoreCreateHandleByIdentify(&handle, SAMICoreIdentify::SAMICoreIdentify_RNNAEC, & create_param); assert(ret == SAMI_OK);
SAMICore3ACreateParameter createParameter; createParameter.channels = num_channels; createParameter.sampleRate = sample_rate; int ret = SAMICoreCreateHandleByIdentify(&handle, SAMICoreIdentify::SAMICoreIdentify_AEC, &createParameter); assert(ret == SAMI_OK);
有几种情况会导致创建失败:
模型数据不正确,例如模型数据损坏或者大小不对。
采样率不支持。目前 AEC 只支持 16000 和 44100 采样率,需要注意一下,采样率必须要与模型对应。16k 对应模型 aec.model,44.1k 对应 aec44k.model
SAMICoreAudioBuffer,用于存放音频数据,AEC 目前仅 Planar-Float 。更多关于音频数据格式请参看名词****解释部分。SAMICoreBlock,用于存放需要处理的数据。
SAMICoreAudioBuffer in_mic_buffer; SAMICoreAudioBuffer in_ref_buffer; SAMICoreAudioBuffer out_buffer; in_mic_buffer.numberChannels = num_channels; in_mic_buffer.numberSamples = block_size; in_mic_buffer.data = new float*[num_channels]; in_mic_buffer.isInterleave = 0; in_ref_buffer.numberChannels = num_channels; in_ref_buffer.numberSamples = block_size; in_ref_buffer.data = new float*[num_channels]; in_ref_buffer.isInterleave = 0; out_buffer.numberChannels = num_channels; out_buffer.numberSamples = block_size; out_buffer.data = new float*[num_channels]; out_buffer.isInterleave = 0; for(int c = 0; c < int(num_channels); ++c){ in_mic_buffer.data[c] = new float[block_size]; in_ref_buffer.data[c] = new float[block_size]; out_buffer.data[c] = new float[block_size]; } SAMICoreAecInput aec_input; aec_input.mic = &in_mic_buffer; aec_input.ref = &in_ref_buffer; SAMICoreBlock in_block; SAMICoreBlock out_block; in_block.numberAudioData = 1; in_block.dataType = SAMICoreDataType_AecInput; in_block.audioData = &aec_input; out_block.numberAudioData = 1; out_block.dataType = SAMICoreDataType_AudioBuffer; out_block.audioData = &out_buffer;
将待处理的音频数据拷贝到 in_mic_buffer
和 in_ref_buffer
,经过 SAMICoreProcess
处理后,结果将拷贝至 output 中。示例中采用这种方法。
for(;hasAudioSamples();) { copyMicSamplesToInputBuffer(in_mic_buffer); copyRefSamplesToInputBuffer(in_ref_buffer); int ret = SAMICoreProcess(handle, &in_block, &out_block); assert(ret == SAMI_OK); // do something after process doSomethingAfterProcess(out_buffer); }
更新音频数据的指针,指向正确的内存即可,这样可以避免内存数据的拷贝。
for(;hasAudioSamples();) { updateMicInputBuffer(in_mic_buffer); updateRefInputBuffer(in_ref_buffer); updateOutputBuffer(out_buffer); int ret = SAMICoreProcess(handle, &in_block, &out_block); assert(ret == SAMI_OK); doSomethingAfterProcess(out_buffer); }
释放 handle
ret = SAMICoreDestroyHandle(handle);
此外,还要注意音频数据数据的内存释放(如果有)。例如:
for(int c = 0; c < int(num_channels); ++c) { delete[] in_mic_buffer.data[c]; delete[] in_ref_buffer.data[c]; delete[] out_buffer.data[c]; } delete[] in_mic_buffer.data; delete[] in_ref_buffer.data; delete[] out_buffer.data;