1

我正在尝试实现一个混合钢琴样本的软件。我想创建一个包含一种声音、另一种声音和两者混合的 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;
}
4

2 回答 2

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;
}
于 2012-11-18T05:58:44.637 回答
0

我根本不清楚您要做什么。您的描述说:“我想创建一个 wav 文件,其中包含一种声音、另一种声音以及两者的混合。” 如果你想把一种声音和另一种声音结合起来,为什么还要混合这两种声音呢?这就像把牛奶和奶油的混合物加进去一半一半

也许你试图让输出长三秒,第一秒包含第一个声音,第二秒包含第二个声音,第三秒包含两者的混合,但你的代码不是那样的,所以我将忽略这种可能性。

让我们做一些基础知识。

要复制第一个文件的第一秒,您的循环将如下所示:

buff3[i] = buff1[i] ;

要复制第二个文件的第一秒,您的循环将如下所示:

buff3[i] = buff2[i] ;

要混合两者,您只需添加它们。混合与组合相同。有时我们说我们正在对两个信号进行“叠加”:

buff3[i] = buff1[i] + buff2[i] ;

您通常希望除以 2 以防止信号“越界”:

buff3[i] = ( buff1[i] + buff2[i] ) / 2  ;

请注意,我们没有将信号的样本值彼此相乘。像您所做的逐个样本乘法是为非常不寻常的情况保留的,例如 AM 合成。

于 2012-11-18T05:50:10.887 回答