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

【C】降噪/去混响-V2

最近更新时间2023.03.16 11:40:12

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

完整例子
#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);

有几种情况会导致创建失败:

  1. 模型数据不正确,例如模型数据损坏或者大小不对。
  2. Block size 数据不正确。

二、创建 SAMICoreBlock 用于存放输入和输出

SAMICoreAudioBuffer,用于存放音频数据,它支持 Planar-FloatInterleaved-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
TCNSAMICorePropertyID_TCNDenoise_Rate
V2SAMICorePropertyID_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);

降噪模式

提供了两种降噪模式

  1. 多声道独立降噪。该模式会对每个声道单独进行降噪处理。(denoise_mode 为 1)
  2. 多声道混合后再降噪。该模式会先mix所有声道音频,进行降噪处理,再将降噪结果拷贝到所有声道。(denoise_mode 为 0)
算法参数id
TCNSAMICorePropertyID_TCNDenoise_Mode
V2SAMICorePropertyID_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);
}

In-Place 进行处理

更新音频数据的指针,指向正确的内存即可,这样可以避免内存数据的拷贝。

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);
}

有几种情况导致处理失败:

  1. 无效的 handle。handle 创建失败了,但仍然拿错误的 handle 进行 process
  2. SAMICoreAudioBuffer 和 SAMICoreBlock 设置错误。

需要注意的是:当 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;