我正在尝试实现一个混合钢琴样本的软件。我想创建一个包含一个声音的wav文件,另一个包含两者的混合声。
我在一秒内截断样本,所以我有以下内容: [声音1的一秒] [声音2的一秒] [声音1 +声音2的一秒]
问题是每次转换都会产生奇怪的声音伪影。有人知道它来自哪里吗?
提前致谢。
以下是我正在使用的代码:
#include "stdafx.h"
#include <cstdlib>
#include <sndfile.h>
int _tmain(int argc, _TCHAR* argv[])
{
SF_INFO sInfo1;
SF_INFO sInfo2;
SF_INFO sInfo3;
SNDFILE *sFile1 = NULL;
SNDFILE *sFile2 = NULL;
SNDFILE *sFile3 = NULL;
double *buff1;
double *buff2;
double *buff3;
sf_count_t count1 = 0;
sf_count_t count2 = 0;
sf_count_t count3 = 0;
buff1 = (double*)malloc(88200*sizeof(double));
buff2 = (double*)malloc(88200*sizeof(double));
buff3 = (double*)malloc(88200*sizeof(double));
sInfo1.format = 0;
sInfo2.format = 0;
sFile1 = sf_open("C:/samples/mezzo forte/mcg_mf_022.wav", SFM_READ, &sInfo1);
sFile2 = sf_open("C:/samples/mezzo forte/mcg_mf_046.wav", SFM_READ, &sInfo2);
sInfo3 = sInfo2;
sFile3 = sf_open("C:/samples/test1.wav", SFM_WRITE, &sInfo3);
count1 = sf_read_double(sFile1, buff1, 88200);
count2 = sf_read_double(sFile2, buff2, 88200);
for(int i=0; i<88200; i++)
{
buff3[i] = buff1[i] + buff2[i] - ( buff1[i] * buff2[i] );
}
count1 = sf_write_double(sFile3, buff1, 88200);
count2 = sf_write_double(sFile3, buff2, 88200);
count3 = sf_write_double(sFile3, buff3, 88200);
sf_close(sFile1);
sf_close(sFile2);
sf_close(sFile3);
free(buff1);
free(buff2);
free(buff3);
//getchar();
return 0;
}
答案 0 :(得分:1)
这不是libsndfile问题。这是一般性音频合成问题。
无论何时将样本截断为任意值(例如,1秒),您都可以听到(或者看看,如果您要将结果文件加载到Audacity中并检出频谱图和波形,过渡边界)一件神器。这是因为样本波形的突然变化。我将跳过尝试讨论带限制的问题,并简单地敦促你快速淡出样本而不是截断它们。这会迫使您的音频波形在转换之前[快速]接近零 - 平滑。
您可能会发现,您还需要淡入(或交叉渐变,如果您重叠平滑过渡)下一个样本,通过将其前几个样本加权接近零的值,并将其加速[快速,或者你会错过攻击]到全面。首先,从每次转换前快速淡出开始,只有在需要时,才会担心淡入。实现是相同的(一个示例缩放值,斜坡上升或下降),但它是1秒样本的任意截断(结束),这可能导致最大的麻烦。
您需要使用一些不同的参数来查看哪些有效。例如,为简单起见,您可能希望以线性斜降而不是指数或抛物线衰减函数开始。在任何情况下,您都必须决定从转换点开始按比例缩小样本值的样本数量(或多少毫秒)。
编辑:
我最初假设您的混音很好,因为您只询问过渡工件。我的回答是这样的。然而,值得注意的是,根据你的既定目标,我完全不知道为什么你会像你一样完全融入buff3
。如果我正确理解你想简单地将两个声音组合成buff3
,只需将另外两个相应的样本加在一起,并确保它们不会剪辑(即超出范围[-1.0,+ 1.0])。 libsndfile自动“防范”剪切,但它只能设置例如1.0到1.0之间的样本值 - 不能确保两个音频波形均匀混合。
如果你的两个输入声音中的任何一个的音量足够高,那么简单的添加剂混音将会剪辑,这将是另一种类型的“神器”(除了可能破坏整个声音,所以在这种特殊情况下,你可能会注意到的)。但是,对于一般混音,你的循环将是:
for(int i=0; i<88200; i++)
{
/* multiply sum of signals by factor slightly
less than reciprocal of their count to guard
also against floating-point error. */
buff3[i] = (buff1[i] + buff2[i]) * 0.499;
}
答案 1 :(得分:0)
我一点也不清楚你尝试做什么。你的描述说:“我想创建一个包含一个声音的wav文件,另一个包含两者的混合。”如果你想将一种声音和另一种声音结合起来,为什么还要混合两种声音?这就像是加入牛奶和奶油的混合物并加入一些half and half。
也许你试图让输出为三秒钟,第一秒包含第一个声音,第二秒包含第二声音,第三秒包含两者的混合,但你的代码不是什么那,所以我将忽略这种可能性。
让我们做一些基础知识。
要复制第一个文件的第一秒,您的循环将如下所示:
buff3[i] = buff1[i] ;
要复制第二个文件的第一秒,您的循环将如下所示:
buff3[i] = buff2[i] ;
要混合两者,只需添加它们即可。混合与组合相同。有时我们说我们正在对这两个信号进行“叠加”:
buff3[i] = buff1[i] + buff2[i] ;
您通常希望除以2以防止信号“越界”:
buff3[i] = ( buff1[i] + buff2[i] ) / 2 ;
请注意,我们没有将信号的样本值相互乘以。像你正在进行的逐个样本乘法保留用于非常不寻常的情况,例如AM综合。