音符起始点检测(音频节奏检测)(1)

您所在的位置:网站首页 钢琴节奏音频 音符起始点检测(音频节奏检测)(1)

音符起始点检测(音频节奏检测)(1)

2024-07-17 22:46:40| 来源: 网络整理| 查看: 265

原文链接:Onset Detection Part 1: The Basics

在这篇文章中,我想开始一个小的循序渐进的系列,它将允许您为您的音乐游戏需求构建自己的起始点检测器(onset detector)。我们将从基础开始。一些事情类似阅读不同格式的音乐文件,我会留给你,因为已经有很多材料了。我将展示的是我用来启动起始点检测器的一些步骤。今天我用frap拍了几个视频,它们展示了90%的不同类型的节拍检测器:

视频1

视频2

请务必观看高清视频,否则您将无法看到细节。而且,Fraps搞砸了很多次,所以视频里面有很多挂起(hangs)。本视频展示的是3个频率子带(frequency subbands)上的3个检测函数(detection functions)。我们将在本系列的后面看到这意味着什么。现在你所需要知道的是,在绿线上的每一个红峰本质上是一个音符起始点,无论是鼓的开始(onset),吉他的开始,还是小提琴的开始。该系统自动适应音乐类型,并将选择(pick)它能找到的最具代表性的节奏起始点。我用这个检测方案检查了大约20首不同的歌曲,效果非常好。此外,它的实现非常简单。让我们从最基本的开始。

声音可以看作是空气、水等介质中的波。当我们记录声音时,我们记录麦克风上的介质的压力,更准确的说是波的振幅(more precisely the waves amplitude)。数字记录则更进一步,因为它必须离散这个记录的过程。麦克风以非常高的频率进行采样(checked),频率越高,录音质量越好。这个频率也称为采样率。标准CD音频采样率为44100Hz (Herz)。这意味着我们每秒获得44100次麦克风振幅的测量值。在这种情况下测量通常被称为采样,每个采样步骤返回给我们一个所谓的样本。样本表示麦克风在给定时间测量的振幅。样本的值必须以某种方式存储。这就是所谓的位深度(bit-depth)和编码(encoding)的作用。位深度告诉我们用多少位来表示一个样本的振幅值。这可以是一个字节,一个短字节(2字节)或一个字(4字节)。上面的任何东西通常都不用(Anything above is usually not used)。我们当然还需要定义振幅的取值范围。它可以是有符号的,也可以是无符号的,通常使用有符号的格式。样本也可以存储为整数或规范化(normalized)浮点值。整数样本的范围取决于样本使用的字节数。16位有符号的样本因此有−32768到32767的取值范围。浮点数样本几乎总是标准化到-1到1之间,这会比使用它们的整数计数器部分(integer counter parts)要容易一些。通过除法和乘法(by division and multiplication),我们可以很容易地在16位带符号整数样本和32位浮点样本之间进行转换(我把细节留给您:)。

为了了解纯PCM数据需要多少存储空间(==以某种位深度采样,使用某种编码,例如整数、浮点数等),让我们假设一首单声道歌曲(只有一个PCM通道)的采样率为44100Hz。样本存储为16位带符号整数。这首歌的总时长为180秒。由于采样率的关系,每秒钟我们有44100个样本。每个样本占用16位或2字节。也就是每秒88200字节,大约88Kb。这首歌的长度是180秒,所以我们有88100*180 = 15876000字节,大约15Mb。哇,这么一首双关语(punny)歌曲居然有这么多数据。如果我们将示例存储为32位浮点数,则所需内存将增加一倍,即30Mb。

因此,最流行的格式都对PCM数据进行某种压缩(compression),以将该大小缩小到可管理和可转移的大小。MP3或Ogg Vorbis等格式在这方面做得非常好,但代价是损失了一些音源质量(original quality)。这些格式的音频压缩算法都是有损的,即解压后无法100%恢复原始数据。不过这些压缩算法会被重造(constructed),使得这种损失对大多数听众来说是听不到的。

当我们想分析音乐时,我们并不能直接使用压缩格式。我们必须以某种方式获得直接的PCM数据。这就是解码器(decoder)的作用。Java, C, c#中有无数的库可以用来解码。我不会在这里详细介绍如何使用一个特定的解码器,因为有很多关于这个问题的材料。对于mp3有libmad和libmpg123,它们都是用C写的,Java中有JLayer。对于Ogg Vorbis有用C写的的libvorbis和用Java写的Jorbis。我强烈建议您选择一款适合您的,并阅读所有这些库附带的文档和示例代码。它们中的大多数都非常容易使用。

让我们假设内存中有从解码器获取的PCM数据。大多数歌曲都是立体声的(stereo),所以我们要做的第一件事就是合并两个通道(channel),对每个左声道样本和它对应的右声道样本取平均值。PCM数据也可能是16位带符号整数格式,因此我们还将它转换为浮点数组。我们的函数的返回值将是一个浮点数组,其中包含n个连续的PCM样本。这里有一些代码

public float[] convertPCM( short[] leftChannel, short[] rightChannel ) { float[] result = new float[leftChannel.length]; for( int i = 0; i < leftChannel.length; i++ ) result[i] = (leftChannel[i] + rightChannel[i]) / 2 / 32768.0f; return result; }

我们传入两个short数组,一个存着左声道的振幅样本,另一个存着右声道的振幅样本,我们对两个声道的振幅样本取平均值并除以32768.0 f,就可以得到取值范围[-1,1]的振幅(amplitude)的浮点数组,(技术上由于带符号short的取值范围,我们应该对负样本除以32768,对正样本除以32767,但我们懒。。。)。

那么我们现在能拿这个振幅数组做些什么呢?让我们先来看看如何得到一个特定时间点的样本,比如歌曲开始的第10秒。为了获取这个特定时间的样本,我们需要知道采样率。解码器库会很乐意为我们提供这些信息,通常我们会从几乎所有的mp3和Oggs获取到44100Hz的采样率。从现在开始,我们假设固定采样率为44100Hz。我们记得采样率表示了每秒可取到多少个样本。在44100Hz下,每个样本编码(encodes)1 / 44100秒或~0.00002秒。如果我们有我们想要的以秒为单位的样本位置,那么计算样本在浮点数组中的索引是小菜一碟:

float time = 10; // 10 seconds into the song float samplingRate = 44100.0f; int index = (int)(time / samplingRate);

我们只要用给定的时间除以采样率就能得到编码(encodes)该时间点振幅的样本的索引。如果您只关心将PCM数据输出到声卡,那么我亲爱的朋友们(像Java Sound或Android上的mediaframework这样的库)几乎就是您需要知道的一切。在Android上,您可以通过AudioTrack类将PCM数据直接发送到声卡,Java声音也存在类似的机制。

为了好玩,让我们为一个440Hz的单声道声音创建PCM数据。这个频率相当于音乐中的A音。声音只不过是波而已,我们通常把它描述为正弦曲线(sinusoid)。在现实生活中,声音信号是许多许多这样的正弦信号的总和,这些正弦信号加在一起构成声音信号的整体音频。当我们想为音符A创建一个a的声音时,我们只需要生成一个周期(period)为1/440秒的正弦曲线。

图片

这个图中的x轴是时间,y轴是正弦波的振幅。周期是0.002秒。我们所说的周期是指从一个窥视点(peek)(在正y轴或负y轴上)到下一个窥视点所需的时间。

让我们编写一个函数,该函数可以为任何指定频率、采样率和持续时间(以秒为单位)的单声道音符生成PCM数据:

public float[] generateSound( float frequency, float samplingRate, float duration ) { float[] pcm = new float[(int)(samplingRate*duration)]; float increment = 2 * (float)Math.PI * frequency / samlpingRate; float angle = 0; for( int i = 0; i < pcm.length; i++ ) { pcm[i] = Math.sin( angle ); angle += increment; } return pcm; }

代码不是很多,但是其中的一些表达式似乎有些神秘。让我们分解一下。我们首先创建一个浮点数组,它将保存我们的PCM样本。数组的长度是由采样持续时间乘以采样率得出的,也就是每秒采样的数量。如果持续时间为5秒,则需要44100个样本乘以5。不难。下一行计算增量。什么的增量?稍后我们将传给Math.sin()的角度(angle)参数的增量。正如我之前说的,我们研究的是正弦波。为了得到正弦波,我们使用正弦函数。Java中的正弦函数输入一个以弧度表示的角,并输出这个角的正弦值。一个周期, 可以用2 *PI 或者,360°来表示。如果我们想要一个1Hz的声音,我们需要在44100个采样结束时跑完一个周期。对于440Hz,我们需要跑完440个周期等等。所以我们先用2 *PI乘以频率。然后再除以采样率就会得到每一次循环需要的弧度增量(increment)。在循环中,我们将当前样本(pcm[i])赋值为当前弧度的正弦值(Math.sin(angle)),然后将当前弧度(angle)增加一个我们之前计算得出的弧度增量(increment)。现在我们已经知道了写电子合成器(synthesizer)所需要的一切。电子合成器除了使用与上述方法非常相似的方法生成PCM样本外,再无其他。通过将一些函数组合在一起,合成器可以产生十分有趣和复杂的声音。它的基础全是正弦函数(或锯齿函数,或矩形函数)。我们的这个函数缺少的是一种操纵产生的正弦波的响度或音量的方法。sin()函数给出了单位圆的正弦值,因此我们得到了在[-1,1]范围内可能的最大振幅。如果我们想让产生的声音减半我们只要把sin乘以0.5。为了得到最大响度的四分之一,我们乘以0.25,以此类推。很简单不是吗?

本系列的第一部分到此结束。下次我们会看到傅里叶变换是关于什么的。不要担心,我们不会使用任何高等数学,甚至正弦:)。现在,研究一下Java声音库是如何工作的,让您的耳朵被自己生成的正弦波所震耳欲聋吧!



【本文地址】

公司简介

联系我们

今日新闻


点击排行

实验室常用的仪器、试剂和
说到实验室常用到的东西,主要就分为仪器、试剂和耗
不用再找了,全球10大实验
01、赛默飞世尔科技(热电)Thermo Fisher Scientif
三代水柜的量产巅峰T-72坦
作者:寞寒最近,西边闹腾挺大,本来小寞以为忙完这
通风柜跟实验室通风系统有
说到通风柜跟实验室通风,不少人都纠结二者到底是不
集消毒杀菌、烘干收纳为一
厨房是家里细菌较多的地方,潮湿的环境、没有完全密
实验室设备之全钢实验台如
全钢实验台是实验室家具中较为重要的家具之一,很多

推荐新闻


图片新闻

实验室药品柜的特性有哪些
实验室药品柜是实验室家具的重要组成部分之一,主要
小学科学实验中有哪些教学
计算机 计算器 一般 打孔器 打气筒 仪器车 显微镜
实验室各种仪器原理动图讲
1.紫外分光光谱UV分析原理:吸收紫外光能量,引起分
高中化学常见仪器及实验装
1、可加热仪器:2、计量仪器:(1)仪器A的名称:量
微生物操作主要设备和器具
今天盘点一下微生物操作主要设备和器具,别嫌我啰嗦
浅谈通风柜使用基本常识
 众所周知,通风柜功能中最主要的就是排气功能。在

专题文章

    CopyRight 2018-2019 实验室设备网 版权所有 win10的实时保护怎么永久关闭