#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/denoise_model.model"; std::vector<uint_8> model_buf = loadModelAsBinary(model_path); assert(model_buf.size() > 0); // step 1, create denoise handle const int sample_rate = 44100; const int block_size = 512; const int num_channels = 1; SAMICoreHandle handle = nullptr; executorContextCreateParameter createParameter; createParameter.sampleRate = sample_rate; createParameter.maxBlockSize = block_size; createParameter.numChannel = 2; createParameter.modelBuffer = reinterpret_cast<char*>(modelBin.data()); createParameter.modelLen = modelBin.size(); int ret = SAMICoreCreateHandleByIdentify(&handle, SAMICoreIdentify::SAMICoreIdentify_TCNDENOISE44K, &createParameter); assert(ret == SAMI_OK); // step 2, create input and output audio block SAMICoreAudioBuffer in_audio_buffer; in_audio_buffer.numberChannels = num_channels; in_audio_buffer.numberSamples = pre_define_block_size; in_audio_buffer.data = new float*[num_channels]; in_audio_buffer.isInterleave = 0; SAMICoreAudioBuffer out_audio_buffer; out_audio_buffer.numberChannels = num_channels; out_audio_buffer.numberSamples = pre_define_block_size; out_audio_buffer.data = new float*[num_channels]; out_audio_buffer.isInterleave = 0; for(int c = 0; c < int(num_channels); ++c) { in_audio_buffer.data[c] = new float[pre_define_block_size]; out_audio_buffer.data[c] = new float[pre_define_block_size]; } SAMICoreBlock in_block; memset(&in_block, 0, sizeof(SAMICoreBlock)); in_block.numberAudioData = 1; in_block.dataType = SAMICoreDataType::SAMICoreDataType_AudioBuffer; in_block.audioData = &in_audio_buffer; SAMICoreBlock out_block; memset(&out_block, 0, sizeof(SAMICoreBlock)); out_block.numberAudioData = 1; out_block.dataType = SAMICoreDataType::SAMICoreDataType_AudioBuffer; out_block.audioData = &out_audio_buffer; // step 3, process block by block for(;hasAudioSamples();) { copySamplesToInputBuffer(in_audio_buffer); //拷贝数据或者修改数据指针in_audio_buffer的指向 int ret = SAMICoreProcess(handle, &in_block, &out_block); assert(ret == SAMI_OK); // do something after process doSomethingAfterProcess(out_block); //业务从out_block拷贝处理后的数据 } // step 4, remember release resource ret = SAMICoreDestroyHandle(handle); assert(ret == SAMI_OK); for(int c = 0; c < int(num_channels); ++c) { delete[] in_audio_buffer.data[c]; delete[] out_audio_buffer.data[c]; } delete[] in_audio_buffer.data; delete[] out_audio_buffer.data;
即读取整个模型文件到内存,实现方法自由发挥,例子中loadModelAsBinary
仅供参考。算法模型详见降噪/去混响/去啸叫介绍小节。
传入模型内存地址、模型大小、采样率和 maxBlockSize,通过 SAMICoreCreateHandleByIdentify
创建 handle。
注:算法内部集成了重采样,比如需要处理48k采样率的数据,可以创建SAMICoreIdentify_TCNDENOISE44K, 然后调用SAMICoreCreateHandleByIdentify
函数,传参数 createParameter.sampleRate
= 48000; 这样内部会进行重采样处理。
SAMICoreHandle handle = nullptr; executorContextCreateParameter createParameter; createParameter.sampleRate = sample_rate; createParameter.maxBlockSize = block_size; createParameter.numChannel = 2; createParameter.modelBuffer = reinterpret_cast<char*>(modelBin.data()); createParameter.modelLen = modelBin.size(); int ret = SAMICoreCreateHandleByIdentify(&handle, SAMICoreIdentify::SAMICoreIdentify_TCNDENOISEXXX, &createParameter); assert(ret == SAMI_OK);
有几种情况会导致创建失败:
SAMICoreAudioBuffer,用于存放音频数据,它支持 Planar-Float 和 Interleaved-Float 类型数据。更多关于音频数据格式请参看名词解释 部分。SAMICoreBlock,用于存放需要处理的数据。
目前接口内部有ringBuffer,所以支持任意block_size, 当数据量不足的时候,会返回静音数据。
SAMICoreAudioBuffer 申请 1 个即可,同时设置 out_block.numberAudioData =1
即可:
SAMICoreAudioBuffer in_audio_buffer; in_audio_buffer.numberChannels = num_channels; in_audio_buffer.numberSamples = block_size; in_audio_buffer.data = new float*[num_channels]; in_audio_buffer.isInterleave = 0; SAMICoreAudioBuffer out_audio_buffer; out_audio_buffer.numberChannels = num_channels; out_audio_buffer.numberSamples = block_size; out_audio_buffer.data = new float*[num_channels]; out_audio_buffer.isInterleave = 0; for(int c = 0; c < int(num_channels); ++c) { in_audio_buffer.data[c] = new float[block_size]; out_audio_buffer.data[c] = new float[block_size]; } SAMICoreBlock in_block; memset(&in_block, 0, sizeof(SAMICoreBlock)); in_block.numberAudioData = 1; in_block.dataType = SAMICoreDataType::SAMICoreDataType_AudioBuffer; in_block.audioData = &in_audio_buffer; SAMICoreBlock out_block; memset(&out_block, 0, sizeof(SAMICoreBlock)); out_block.numberAudioData = 1; out_block.dataType = SAMICoreDataType::SAMICoreDataType_AudioBuffer; out_block.audioData = &out_audio_buffer;
一些情况下可能需要实现一种比较自然的感觉,并不需要将噪音消除的很彻底,于是可以设置降噪比率(可选范围 0.0 ~ 1.0,默认是1.0,降噪比例最大)。
算法 | 参数id |
---|---|
TCN | SAMICorePropertyID_TCNDenoise_Rate |
V2 | SAMICorePropertyID_Denoise_V2_Rate |
float denoise_rate = 1.0f; SAMICoreProperty coreProperty; coreProperty.id = SAMICorePropertyId::SAMICorePropertyID_TCNDenoise_Rate; coreProperty.data = &denoise_rate; coreProperty.dataLen = sizeof(float); coreProperty.type = SAMICoreDataType_Float; ret = SAMICoreSetProperty(handle, SAMICorePropertyId::SAMICorePropertyID_TCNDenoise, &coreProperty); assert(ret == SAMI_OK);
提供了两种降噪模式
算法 | 参数id |
---|---|
TCN | SAMICorePropertyID_TCNDenoise_Mode |
V2 | SAMICorePropertyID_Denoise_V2_Mode |
float denoise_mode = 1.0f; SAMICoreProperty coreProperty; coreProperty.id = SAMICorePropertyId::SAMICorePropertyID_TCNDenoise_Mode; coreProperty.data = &denoise_mode; coreProperty.dataLen = sizeof(float); coreProperty.type = SAMICoreDataType_Float; ret = SAMICoreSetProperty(handle, SAMICorePropertyId::SAMICorePropertyID_TCNDenoise, &coreProperty); assert(ret == SAMI_OK);
将待处理的音频数据拷贝到 in_audio_buffer
中,经过 SAMICoreProcess
处理后,结果将拷贝至 output 中。示例中采用这种方法。
for(;hasAudioSamples();) { copySamplesToInputBuffer(in_audio_buffer); int ret = SAMICoreProcess(handle, &in_block, &out_block); assert(ret == SAMI_OK); // do something after process doSomethingAfterProcess(out_block); }
更新音频数据的指针,指向正确的内存即可,这样可以避免内存数据的拷贝。
for(;hasAudioSamples();) { updateInputBuffer(in_audio_buffer); updateOutputBuffer(out_audio_buffer); int ret = SAMICoreProcess(handle, &in_block, &out_block); assert(ret == SAMI_OK); doSomethingAfterProcess(out_block); }
甚至你可以将 input 和 output 的内存指向同一处,这样处理后的结果将以 overwrite 的形式立即生效。
for(;hasAudioSamples();) { updateBuffer(in_audio_buffer); int ret = SAMICoreProcess(handle, &in_block, &in_block); assert(ret == SAMI_OK); doSomethingAfterProcess(in_block); }
有几种情况导致处理失败:
需要注意的是:当 block size 较小的情况下,前几帧会由于数据量不足无法处理而返回静音数据,这种情况下 ret 仍然为 SAMI_OK
释放 handle
ret = SAMICoreDestroyHandle(handle);
此外,还要注意音频数据数据的内存释放(如果有)。例如:
for(int c = 0; c < int(num_channels); ++c) { delete[] in_audio_buffer.data[c]; delete[] out_audio_buffer.data[c]; } delete[] in_audio_buffer.data; delete[] out_audio_buffer.data; handle = nullptr;