目录
一、常见三种混音算法
1.直接相加法
2.简单平均法
3.自适应权重法
二、代码实现混音算法
一、常见三种混音算法
-
直接相加法
void additiveMixing(const float* input1, const float* input2, float* output, int length)
{
for (int i = 0; i < length; i++)
{
output[i] = input1[i] + input2[i];
}
}
直接相加法混音是一种简单而常见的混音算法,其优缺点如下:
优点:
简单易懂:直接相加法混音算法非常简单,容易理解和实现,无需复杂的计算。
低计算开销:由于不涉及复杂的信号处理算法,直接相加法混音的计算开销相对较低,适用于实时处理等需要快速响应的应用场景。
缺点:
音量叠加问题:直接相加法混音仅是将多个音频信号简单地相加,可能导致音量过高,产生音频削波或失真的问题。在混音过程中可能需要对音频信号进行归一化处理,以避免超过合理范围的音量。
信号干扰:如果混音的音频信号存在频率冲突或相位差异,直接相加法混音可能会导致信号间的干扰或相互抵消。这可能会产生不良的音频效果或部分信号的消失。
2.简单平均法:
void averageMixing(float *input1, float *input2, float *output, int length)
{
float gain1 = 1;
float gain2 = 0.5;
for (int i = 0; i < length; i++)
{
output[i] = (input1[i] * gain1 + input2[i] * gain2) / 2.0f;
}
}
其优缺点如下:
优点:
简单易实现,计算速度快,能够自己按照比例混音
缺点:
无法对音频信号进行动态调整,可能导致混音后的声音过于平淡。
3.自适应权重法
void adaptiveWeightingMixing(float *input1, float *input2, float *output, int length)
{
float factor = 1;//衰减因子 初始值为1
float MAX = 3.4028235E38;
float MIN = -3.4028235E38;
float mixVal;
for (int i = 0; i < length; i++)
{
mixVal = (input1[i]+input2[i])*factor;
if (mixVal>MAX)
{
factor = MAX/mixVal;
mixVal = MAX;
}
if (mixVal<MIN){
factor = MIN/mixVal;
mixVal = MIN;
}
if (factor < 1)
{
//SETPSIZE为f的变化步长,通常的取值为(1-f)/VALUE,此处取SETPSIZE 为 32 VALUE值可以取 8, 16, 32,64,128.
factor += (1 - factor) / 32;
}
output[i] = mixVal;
}
}
自适应权重法相对于上面两种更为复杂。其优缺点如下:
优点:
动态调整权重:
能通过衰减因子factor动态调整音频信号的权重,以避免混音溢出边界(超出最大或最小值)。这样可以有效避免音频削波或失真的问题。
提供动态范围控制:通过根据混音结果调整衰减因子f的大小,自适应权重法可以对混音信号的动态范围进行控制,使其适应不同音频输入的音量差异。
缺点(相对而言):
算法复杂度较高:相较于简单相加法,自适应权重法需要进行较多的条件判断和计算,因此算法复杂度较高,可能对系统性能产生一定影响。
可能引入音频伪影:在自适应权重法中,权重的动态调整过程可能引入一定的音频伪影。这是由于频域的快速变化导致的,可能会对音频质量产生一定的影响。
二、代码实现案例
代码获取,公主号(gh_bffeac6359e7)回复:c语言实现混音算法代码
通过tinywav第三方库实现wav的读写,读取两个wav文件并选择混音模式,混音后写入新的wav文件,以下是代码一部分。
#include <math.h>
#include <assert.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "../inc/tinywav.h"
#define _USE_MATH_DEFINES
#define BLOCK_SIZE 240
#define sampleRate 48000
enum mix_style_
{
Additive_Mixing = 1,
Average_Mixing = 2,
Adaptive_Weighting_Mixing = 3
};
void additiveMixing(const float* input1, const float* input2, float* output, int length)
{
for (int i = 0; i < length; i++)
{
output[i] = input1[i] + input2[i];
}
}
void averageMixing(float *input1, float *input2, float *output, int length)
{
float gain1 = 1;
float gain2 = 0.5;
for (int i = 0; i < length; i++)
{
output[i] = (input1[i] * gain1 + input2[i] * gain2) / 2.0f;
}
}
void adaptiveWeightingMixing(float *input1, float *input2, float *output, int length)
{
float factor = 1;//衰减因子 初始值为1
float MAX = 3.4028235E38;
float MIN = -3.4028235E38;
float mixVal;
for (int i = 0; i < length; i++)
{
mixVal = (input1[i]+input2[i])*factor;
if (mixVal>MAX)
{
factor = MAX/mixVal;
mixVal = MAX;
}
if (mixVal<MIN){
factor = MIN/mixVal;
mixVal = MIN;
}
if (factor < 1)
{
//SETPSIZE为f的变化步长,通常的取值为(1-f)/VALUE,此处取SETPSIZE 为 32 VALUE值可以取 8, 16, 32,64,128.
factor += (1 - factor) / 32;
}
output[i] = mixVal;
}
}
int main(int argc, char *argv[])
{
char* outputPath = "./wav_file/output.wav";
if (argc < 4)
{
printf("open failed,please input such as: ./mix test1.wav test2.wav 1~3 \n");
printf("1.procress with Additive Mixing\n");
printf("2.procress with Average Mixing\n");
printf("3.procress with Adaptive Weighting Mixing\n");
return -1;
}
TinyWav twReaderWav1,twReaderWav2;
tinywav_open_read(&twReaderWav1, argv[1], TW_INLINE);
tinywav_open_read(&twReaderWav2, argv[2], TW_INLINE);
int16_t wav1_channels = twReaderWav1.numChannels;
int32_t wav1_samplerate = twReaderWav1.h.SampleRate;
TinyWavSampleFormat wav1_sampFmt = twReaderWav1.sampFmt;
TinyWavChannelFormat wav1_chanfmt = twReaderWav1.chanFmt;
int16_t wav2_channels = twReaderWav2.numChannels;
int32_t wav2_samplerate = twReaderWav2.h.SampleRate;
TinyWavSampleFormat wav2_sampFmt = twReaderWav2.sampFmt;
TinyWavChannelFormat wav2_chanfmt = twReaderWav2.chanFmt;
if(wav1_channels != 1 || wav1_samplerate!=48000 || wav2_channels != 1 || wav2_samplerate!=48000)
{
printf("wav channel must be 1 channel and samplerate 48000\n");
return -1;
}
TinyWav twWriter;
tinywav_open_write(&twWriter, 1, 48000, TW_INT16, TW_INLINE, outputPath);
int wav1_totalNumSamples = twReaderWav1.numFramesInHeader;
int wav1_samplesProcessed = 0;
int wav2__totalNumSamples = twReaderWav2.numFramesInHeader;
int wav2_samplesProcessed = 0;
int wav1_block_count = 0;
int wav2_block_count = 0;
float wav1_data[BLOCK_SIZE] = {0};
float wav2_data[BLOCK_SIZE] = {0};
float out_data [BLOCK_SIZE] = {0};
int mix_style = atoi(argv[3]);
while (wav1_samplesProcessed < wav1_totalNumSamples)
{
int wav1_samplesRead = tinywav_read_f(&twReaderWav1, wav1_data, BLOCK_SIZE);
wav1_samplesProcessed += wav1_samplesRead;
if(wav2_samplesProcessed < wav2__totalNumSamples)
{
int wav2_samplesRead = tinywav_read_f(&twReaderWav2, wav2_data, BLOCK_SIZE);
wav2_samplesProcessed += wav2_samplesRead;
switch (mix_style)
{
case Additive_Mixing:
additiveMixing(wav1_data,wav2_data,out_data,BLOCK_SIZE);
break;
case Average_Mixing:
averageMixing(wav1_data,wav2_data,out_data,BLOCK_SIZE);
break;
case Adaptive_Weighting_Mixing:
adaptiveWeightingMixing(wav1_data,wav2_data,out_data,BLOCK_SIZE);
break;
default:
break;
}
int samplesWritten = tinywav_write_f(&twWriter, out_data, BLOCK_SIZE);
}
}
tinywav_close_read(&twReaderWav1);
tinywav_close_read(&twReaderWav2);
tinywav_close_write(&twWriter);
return 0;
}