本功能是在单线程中使用,内部采用同步处理的策略即一帧输入处理后会返回一帧输出,不支持多线程调用
#include "me_audio_encoder.h" #include "sami_core.h" #include "demo_helper.h" #include <iostream> #include <string> #include <cfloat> #include <chrono> 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); double signal_duration = static_cast<double>(total_samples) / static_cast<double>(sample_rate) * 1000; AudioEncoderSettings setting; setting.format = mammon::AudioEncoderFormat::kWav_F32; std::unique_ptr<AudioEncoder> audio_encoder = nullptr; AudioEncoderStatus status; std::tie(audio_encoder, status) = AudioEncoder::create(setting); if(audio_encoder == nullptr || status != mammon::AudioEncoderStatus::kOK) { std::cerr << "open output file failed\n"; return -1; } int ret = audio_encoder->open(output, sample_rate, num_channels, 64000); if(ret != 0){ std::abort(); } // create handle SAMICoreAudioCommonParameter parameter; parameter.sampleRate = sample_rate; parameter.channels = num_channels; SAMICoreHandle handle; ret = SAMICoreCreateHandleByIdentify(&handle, SAMICoreIdentify_Loudnorm2, ¶meter); if(ret != 0){ std::abort(); } // set property SAMICoreLoudnorm2Property loudnormProperty; loudnormProperty.target_lufs = -16; SAMICoreProperty coreProperty; coreProperty.id = SAMICorePropertyID_Processor_Loudnorm2; coreProperty.data = &loudnormProperty; coreProperty.writable = 0; coreProperty.dataLen = 1; coreProperty.type = SAMICoreDataType_LoudNorm2; ret = SAMICoreSetProperty(handle, SAMICorePropertyID_Processor_Loudnorm2, &coreProperty); if(ret != 0){ std::abort(); } 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; auto start_time = std::chrono::steady_clock::now(); 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 audio_encoder->writePlanarData(outBuffer->data, outBuffer->numberChannels, actual_block_size); inx += actual_block_size; } auto end_time = std::chrono::steady_clock::now(); auto cost_time = std::chrono::duration<double, std::milli>(end_time - start_time).count(); if(signal_duration >= DBL_EPSILON) { double ratio = cost_time / signal_duration; std::cout << "Cast time " << cost_time << " ms " << "signal duration " << signal_duration << " ms " << " ratio " << ratio << std::endl; } 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; } audio_encoder->close(); 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_Loudnorm2, ¶meter); assert(ret == SAMI_OK);
SAMICoreAudioBuffer,用于存放音频数据,更多关于音频数据格式请参看【SAMI Core】名词解释 部分。SAMICoreBlock,用于存放需要处理的数据。
通过 SAMICoreSetProperty 设置target_lufs参数。
SAMICoreLoudnorm2Property loudnormProperty; loudnormProperty.target_lufs = -16; SAMICoreProperty coreProperty; coreProperty.id = SAMICorePropertyID_Processor_Loudnorm2; coreProperty.data = &loudnormProperty; coreProperty.writable = 0; coreProperty.dataLen = 1; coreProperty.type = SAMICoreDataType_LoudNorm; ret = SAMICoreSetProperty(handle, SAMICorePropertyID_Processor_Loudnorm2, &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 audio_encoder->writePlanarData(outBuffer->data, outBuffer->numberChannels, actual_block_size); inx += actual_block_size; }
更新音频数据的指针,指向正确的内存即可,这样可以避免内存数据的拷贝。
甚至某些场景下,你可以将 input 和 output 的内存指向同一处,这样处理后的结果将以 overwrite 的形式立即生效。
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;