我正在开发一个系统来帮助音乐家进行转录。目的是在单个乐器单声道录音上执行自动音乐转录(它不一定是完美的,因为用户将在以后纠正错误/错误)。这里有没有人有自动音乐转录的经验?或一般的数字信号处理?无论您的背景如何,都非常感谢任何人的帮助。

到目前为止,我已经研究了使用快速傅里叶变换进行音调检测,并且MATLAB和我自己的Java测试程序中的大量测试表明它足够快速和准确地满足我的需求。需要解决的任务的另一个要素是以乐谱形式显示制作的MIDI数据,但这是我现在不关心的事情。

简而言之,我正在寻找的是一种用于音符开始检测的好方法,即信号中新音符开始的位置。由于慢速开启可能很难正确检测,我最初将使用带有钢琴录音的系统。这也部分归因于我弹钢琴的事实,应该处于更好的位置以获得合适的录音进行测试。如上所述,该系统的早期版本将用于简单的单声道录音,根据未来几周的进展,可能会在稍后进行更复杂的输入。

有帮助吗?

解决方案

这是一个图形,说明了记录开始检测的阈值方法:

此图显示了一个典型的WAV文件,其中连续播放了三个不连续的音符。红线表示选定的信号阈值,蓝线表示通过简单算法返回的音符开始位置,该算法在信号电平超过阈值时标记开始。

如图所示,选择合适的绝对阈值很困难。在这种情况下,第一个音符被正确拾取,第二个音符被完全错过,第三个音符(几乎没有)开始很晚。一般来说,低阈值会导致您获取幻像音符,而提高它会导致您错过音符。该问题的一个解决方案是使用相对阈值,如果信号在一定时间内增加一定百分比,则该相对阈值触发启动,但这具有其自身的问题。

更简单的解决方案是首先在波形文件上使用有点违反直觉命名的压缩(不是MP3压缩 - 这完全是)。压缩基本上会使音频数据中的峰值变平,然后放大所有内容,以使更多音频接近最大值。对上述样本的影响看起来像这样(这说明为什么名称<!>“compression <!>”似乎毫无意义 - 在音频设备上它通常被标记为<!>“;响度<!>”; ):

压缩后,绝对阈值方法将更好地工作(虽然它很容易过度压缩并开始拾取虚构的音符开始,与降低阈值的效果相同)。有很多wave编辑器可以很好地完成压缩,最好让他们处理这个任务 - 你可能需要做大量的工作<!>清理<!> ;无论如何,在检测音符之前你的波形文件。

在编码方面,加载到内存中的WAV文件基本上只是一个双字节整数数组,其中0表示无信号,32,767和-32,768表示峰。在其最简单的形式中,阈值检测算法将从第一个样本开始并读取数组,直到找到大于阈值的值。

short threshold = 10000;
for (int i = 0; i < samples.Length; i++)
{
    if ((short)Math.Abs(samples[i]) > threshold) 
    {
        // here is one note onset point
    }
}

在实践中,这非常有效,因为普通音频具有超过给定阈值的各种瞬态尖峰。一种解决方案是使用运行平均信号强度(即,在最后n个样本的平均值高于阈值之前不标记开始)。

short threshold = 10000;
int window_length = 100;
int running_total = 0;
// tally up the first window_length samples
for (int i = 0; i < window_length; i++)
{
    running_total += samples[i];
}
// calculate moving average
for (int i = window_length; i < samples.Length; i++)
{
    // remove oldest sample and add current
    running_total -= samples[i - window_length];
    running_total += samples[i];
    short moving_average = running_total / window_length;
    if (moving_average > threshold)
    {
        // here is one note onset point 
        int onset_point = i - (window_length / 2);
    }
}

所有这些都需要进行大量调整并使用设置来使其准确地找到WAV文件的起始位置,并且通常对一个文件起作用的内容在另一个文件中效果不佳。这是你选择的一个非常困难且不完美解决的问题领域,但我认为你正在解决这个问题很酷。

更新:此图显示了我遗漏的音符检测的详细信息,即检测音符何时结束:

黄线表示关闭阈值。一旦算法检测到音符开始,它就会假定音符继续,直到运行平均信号强度低于此值(此处用紫色线表示)。当然,这是另一个困难的来源,就像两个或多个音符重叠(复音)的情况一样。

一旦检测到每个音符的开始和停止点,您现在可以分析每个WAV文件数据片段以确定音高。

更新2:我刚刚阅读了您更新的问题。如果你是从零开始编写自己的,那么通过自相关进行音高检测比FFT更容易实现,但是如果你已经检出并使用了预先构建的FFT库,那么你最好还是肯定使用它。一旦你确定了每个音符的开始和停止位置(并且在开始和结束时包括一些填充以用于错过的攻击和释放部分),你现在可以拉出每个音频数据片段并将其传递给FFT函数确定音高。

这里的一个重点是不使用压缩音频数据的片段,而是使用原始未修改数据的片段。压缩过程会扭曲音频并可能产生不准确的音高读数。

关于笔记攻击时间的最后一点是它可能不像你想象的那么严重。通常在音乐中,具有慢速攻击的乐器(如软合成器)将比尖锐的攻击乐器(如钢琴)更早地开始音符,并且两个音符听起来好像它们同时开始。如果您正在以这种方式演奏乐器,那么算法可以为两种乐器选择相同的开始时间,从WAV到MIDI的角度来看这是很好的。

最后更新(我希望):忘掉我所说的关于在每个音符的早期攻击部分包含一些填充样本的内容 - 我忘了这对于音高检测实际上是一个坏主意。许多乐器(特别是钢琴和其他打击乐器乐器)的攻击部分包含的瞬态不是基本音高的倍数,并且会使音调检测变得棘手。实际上,你想要在攻击后稍微开始每个切片。

哦,有点重要:术语<!> quot; compression <!> quot;这里没有提到MP3式压缩

再次更新:这是一个执行非动态压缩的简单函数:

public void StaticCompress(short[] samples, float param)
{
    for (int i = 0; i < samples.Length; i++)
    {
        int sign = (samples[i] < 0) ? -1 : 1;
        float norm = ABS(samples[i] / 32768); // NOT short.MaxValue
        norm = 1.0 - POW(1.0 - norm, param);
        samples[i] = 32768 * norm * sign;
    }
}

当param = 1.0时,此功能对音频没有影响。较大的参数值(2.0是好的,它将使每个样本和最大峰值之间的归一化差异平方)将产生更多压缩和更大声(但是蹩脚)的声音。低于1.0的值将产生扩展效果。

另一个可能显而易见的一点:你应该将音乐录制在一个小的非回声房间,因为这个算法通常会将回声作为幻象音符拾取。

更新:这是一个将在C#中编译的StaticCompress版本,并且明确表达了所有内容。这将返回预期结果:

public void StaticCompress(short[] samples, double param)
{
    for (int i = 0; i < samples.Length; i++)
    {
        Compress(ref samples[i], param);
    }
}

public void Compress(ref short orig, double param)
{
    double sign = 1;
    if (orig < 0)
    {
        sign = -1;
    }
    // 32768 is max abs value of a short. best practice is to pre-
    // normalize data or use peak value in place of 32768
    double norm = Math.Abs((double)orig / 32768.0);
    norm = 1.0 - Math.Pow(1.0 - norm, param);
    orig = (short)(32768.0 * norm * sign); // should round before cast,
        // but won't affect note onset detection
}

抱歉,我在Matlab上的知识得分是0.如果你发布了另一个问题,为什么你的Matlab功能没有按预期工作,它会得到答案(只是不是我)。

其他提示

您想要做的通常被称为 WAV-to-MIDI (google <!>“wav-to-midi <!>”;)。在这个过程中有许多尝试,结果各不相同(注意开始是困难之一;复调很难处理)。我建议从彻底搜索现成的解决方案开始,只有在没有任何可接受的情况下才能自己开始工作。

你需要的过程的另一部分是将MIDI输出呈现为传统乐谱,但是有超过十亿的产品可以做到这一点。

另一个答案是:是的,我已经做了很多数字信号处理(参见我网站上的软件 - 这是一个用VB和C编写的无限语音软件合成器),我很感兴趣帮助你这个问题。 WAV-to-MIDI部分在概念上并不是那么困难,它只是让它在实践中可靠地运行起来很难。注意开始只是设置一个阈值 - 可以很容易地向前或向后调整错误,以补偿音符攻击差异。对于录音而言,音高检测比实时更容易进行,并且仅涉及实现自动关联例程。

你应该看一下 MIRToolbox - 它是为Matlab编写的,内置了一个起始探测器 - 它工作得非常好。源代码是GPL,因此您可以使用适合您的任何语言实现该算法。您的生产代码将使用哪种语言?

这个库以音频标签为中心:

aubio

  

aubio是一个音频标签库。它的功能包括在每次攻击之前分割声音文件,执行音调检测,敲击节拍以及从现场音频产生midi流。 aubio这个名字来自'音频',有一个拼写错误:结果中也可能发现几个转录错误。

我对它的开始检测和音高检测有好运。它在c中,但有swig / python包装器。

另外,图书馆的作者在页面上有他的论文的pdf,其中有关于标签的很好的信息和背景。

通过使用平均能量测量,可以在时域中轻松检测到硬启动。

SUM从0到N(X ^ 2)

使用整个信号块进行此操作。你应该在发生开始时看到峰值(窗口大小取决于你,我的建议是50ms或更长)。

关于发起检测的大量论文:

对于铁杆工程师:

http://www.nyu.edu/classes/bello/MIR_files /2005_BelloEtAl_IEEE_TSALP.pdf

普通人更容易理解:

http://bingweb.binghamton.edu/~ahess2/Onset_Detection_Nov302011.pdf

您可以尝试将wav信号转换为幅度与时间的关系图。然后确定一致起始的方法是计算信号上升沿的拐点中的切线与x轴的交点。

scroll top