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

【C】预处理音量均衡

最近更新时间2023.03.17 16:45:47

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

使用流程

本功能是在单线程中使用,内部采用同步处理的策略即一帧输入处理后会返回一帧输出,不支持多线程调用

完整例子
#include "me_audio_encoder.h"
#include "sami_core.h"
#include "demo_helper.h"
#include <iostream>
#include <string>

int main(int argc, char* argv[]){
    if(argc < 3) {
        std::cerr << "Usage: " << argv[0] << " input.wav output.wav\n";
        return -1;
    }
    const std::string input = argv[1];
    const std::string output = argv[2];

    //解码输入文件
    std::shared_ptr<FileSource> input_src = FileSource::create(input);
    if(!input_src) {
        std::cerr << "cannot open " << input << std::endl;
        return -1;
    }
    int total_samples = input_src->getNumFrames();
    int num_channels = input_src->getNumChannel();
    int sample_rate = input_src->getSampleRate();
    int num_samples = sample_rate / 100;
    auto input_wav_data = loadWholeAudioFile(input_src);

    //create handle
    SAMICoreAudioCommonParameter parameter;
    parameter.sampleRate = sample_rate;
    parameter.channels = num_channels;
    SAMICoreHandle handle;
    ret = SAMICoreCreateHandleByIdentify(&handle, SAMICoreIdentify_Loudnorm, &parameter);
    assert(ret == SAMI_OK);

//  set property
    SAMICoreLoudnormProperty loudnormProperty;
    loudnormProperty.target_lufs = -16;
    loudnormProperty.source_lufs = 0;
    loudnormProperty.source_peak = 99;

    SAMICoreProperty coreProperty;
    coreProperty.id = SAMICorePropertyID_Processor_Loudnorm;
    coreProperty.data = &loudnormProperty;
    coreProperty.writable = 0;
    coreProperty.dataLen = 1;
    coreProperty.type = SAMICoreDataType_LoudNorm;
    ret = SAMICoreSetProperty(handle, SAMICorePropertyID_Processor_Loudnorm, &coreProperty);
    assert(ret == (SAMI_OK));

    SAMICoreAudioBuffer in_buffer;
    in_buffer.data = new float*[num_channels];
    in_buffer.isInterleave = 0;
    in_buffer.numberChannels = num_channels;
    for(int i = 0; i < num_channels; i++) { in_buffer.data[i] = new float[num_samples]; }

    SAMICoreAudioBuffer out_buffer;
    out_buffer.data = new float*[num_channels];
    out_buffer.isInterleave = 0;
    out_buffer.numberChannels = num_channels;
    for(int i = 0; i < num_channels; i++) { out_buffer.data[i] = new float[num_samples]; }


    SAMICoreBlock in_block;
    SAMICoreBlock out_block;
    in_block.numberAudioData = 1;
    in_block.dataType = SAMICoreDataType_AudioBuffer;
    in_block.audioData = &in_buffer;

    out_block.numberAudioData = 1;
    out_block.dataType = SAMICoreDataType_AudioBuffer;
    out_block.audioData = &out_buffer;

    int inx = 0;
//  process    
    while(inx < total_samples - num_samples) {
        int actual_block_size = getNextBlockSize(inx, total_samples, num_samples);
        in_buffer.numberSamples = actual_block_size;
        out_buffer.numberSamples = actual_block_size;
        for(int i = 0; i < num_channels; i++) {
            memcpy(in_buffer.data[i], input_wav_data[i].data() + inx, actual_block_size * sizeof(float));
        }
        ret = SAMICoreProcess(handle, &in_block, &out_block);

        auto* outBuffer = (SAMICoreAudioBuffer*)out_block.audioData;
        if(ret != SAMI_OK  || outBuffer == nullptr) {
            std::cerr << "process error: " << ret;
            exit(-1);
        }
        // write output block to file
        writePlanarData(outBuffer->data, outBuffer->numberChannels, actual_block_size);
        inx += actual_block_size;
    }
//  destory
    for(int i = 0; i < num_channels; i++) {
        delete[] in_buffer.data[i];
        delete[] out_buffer.data[i];
        in_buffer.data[i] = nullptr;
        out_buffer.data[i] = nullptr;
    }

    SAMICoreDestroyHandle(handle);
    delete[] in_buffer.data;
    delete[] out_buffer.data;
    handle = nullptr;
    return 0;

}
接入步骤

一、创建算法句柄

传入采样率、声道数和 maxBlockSize,通过 SAMICoreCreateHandleByIdentify 创建 handle。

//  create handle
    SAMICoreAudioCommonParameter parameter;
    parameter.sampleRate = sample_rate;
    parameter.channels = num_channels;
    SAMICoreHandle handle;
    ret = SAMICoreCreateHandleByIdentify(&handle, SAMICoreIdentify_Loudnorm, &parameter);
    assert(ret == SAMI_OK);

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

SAMICoreAudioBuffer,用于存放音频数据,更多关于音频数据格式请参看名词解释一节。SAMICoreBlock,用于存放需要处理的数据。

三、设置参数

将 source_lufs(源文件响度) 、source_peak(源文件峰值) (参考响度检测【C】响度检测)和 target_lufs(目标响度) 参数组成对象,通过 SAMICoreSetProperty 设置参数。

  • target_lufs(默认值-16dB),(-24到-12,建议实验值为-12,-16,-20)

  • source_lufs(默认值0dB),(-inf:输入为零数据时,-70 - 0:正常范围)

  • source_peak(默认值99),(0到1,部分歌曲可能大于1,截取到1)

SAMICoreLoudnormProperty loudnormProperty;
loudnormProperty.target_lufs = -16;
loudnormProperty.source_lufs = 0;
loudnormProperty.source_peak = 99;

SAMICoreProperty coreProperty;
coreProperty.id = SAMICorePropertyID_Processor_Loudnorm;
coreProperty.data = &loudnormProperty;
coreProperty.writable = 0;
coreProperty.dataLen = 1;
coreProperty.type = SAMICoreDataType_LoudNorm;
ret = SAMICoreSetProperty(handle, SAMICorePropertyID_Processor_Loudnorm, &coreProperty);
assert(ret == (SAMI_OK));

四、处理音频

准备内存空间

由于此算法输入和输出的大小是对等的,所以为了提高效率输入和输出的内存空间都由用户提前分配好

SAMICoreAudioBuffer in_buffer;
in_buffer.data = new float*[num_channels];
in_buffer.isInterleave = 0;
in_buffer.numberChannels = num_channels;
for(int i = 0; i < num_channels; i++) { in_buffer.data[i] = new float[num_samples]; }

SAMICoreAudioBuffer out_buffer;
out_buffer.data = new float*[num_channels];
out_buffer.isInterleave = 0;
out_buffer.numberChannels = num_channels;
for(int i = 0; i < num_channels; i++) { out_buffer.data[i] = new float[num_samples]; }

拷贝数据进行处理

将待处理的音频数据拷贝到 in_buffer 中,经过 SAMICoreProcess 处理后,结果将拷贝至 out_buffer 中。

int inx = 0;
while(inx < total_samples - num_samples) {
    int actual_block_size = getNextBlockSize(inx, total_samples, num_samples);
    in_buffer.numberSamples = actual_block_size;
    out_buffer.numberSamples = actual_block_size;
    for(int i = 0; i < num_channels; i++) {
        memcpy(in_buffer.data[i], input_wav_data[i].data() + inx, actual_block_size * sizeof(float));
    }
    ret = SAMICoreProcess(handle, &in_block, &out_block);

    auto* outBuffer = (SAMICoreAudioBuffer*)out_block.audioData;
    if(ret != SAMI_OK  || outBuffer == nullptr) {
        std::cerr << "process error: " << ret;
        exit(-1);
    }
    // write output block to file
    writePlanarData(outBuffer->data, outBuffer->numberChannels, actual_block_size);
    inx += actual_block_size;
}

五、释放资源

ret = SAMICoreDestroyHandle(handle);

此外,还要注意音频数据数据的内存释放(如果有)。例如:

for(int i = 0; i < num_channels; i++) {
    delete[] in_buffer.data[i];
    delete[] out_buffer.data[i];
    in_buffer.data[i] = nullptr;
    out_buffer.data[i] = nullptr;
}

delete[] in_buffer.data;
delete[] out_buffer.data;