听歌识曲算法技术[语音识别]

概念理解

  1. 语音识别:通过一种语音检索算法来识别一段音频内容的含义。
  2. 音频文件:指带有声音的文件,比如音乐。
  3. MFCC:音频文件转为数字的过程
  4. 时域:振幅Y和时间T的关系。
  5. 频域:振幅Y和频率F的关系。
  6. 短时傅里叶变换:通过对每个时间极短的音频片段做傅里叶变换,来得到这个时间段的频率分布,之所以成为短时,是因为这个算法只有再短时内语音音频的频率以及振幅是比较平稳的,如果时间过长就会导致傅里叶变换不准确。
  7. 频谱图:每个时间点出现的频率分布图,即每一帧或者每时刻上出现的频率数值和振幅数值,为了用二维数据表示这个三维数据,把振幅压缩到二维平面中,使用颜色的浅重来表示振幅的大小。[见图1A]
  8. 稀疏矩阵/星云图:经过处理的频谱图,把频谱图颜色重的变成星星【锚点】,把其它变成空白。[见图1B]
  9. 指纹哈希:指纹哈希是由星座图组成的,星座图中时间-频率点对组合在一起。选择锚点,每个锚点都有一个与之相关联的目标区域。每个锚点依次与目标区域内的点配对,每一对产生两个频率分量加上点之间的时间差.[见图1C]
  10. Hash:哈希值,32位的不可重复的数字,数据库用来存储数据的一种方式,优点查询速度快,缺点存储空间剧增,典型的拿空间换时间。
  11. time:时间值,32位不可重复的数字
  12. 哈希·时间偏移记录:Hash:time = [f1:f2:∆t]:t1 [见图1D]
  13. 扇出因子:在计算哈系数时,一个锚点的目标区域中一次处理相关锚点的个数,可理解为KNN中的K值,计算相邻点的个数
  14. 密度因子:单位区域内锚点的个数
  15. 每秒处理音频记录的哈希数 = 每秒星座点的密度因子 * 进入目标区域的扇出因子[F],也就是为什么扇出因子直接导致存储空间成本的变化
  16. 噪音:
  17. 降噪算法:【腐蚀算法】一种降噪的算法,类似于图形算法里面的原理https://jingyan.baidu.com/article/f96699bbf99d9e894f3c1b4c.html
  18. 张量:维度的意思
  19. 阈值:满足条件的最小值或者最大值

算法原理

由于商业原因,以下只是部分代码,只讲了大概原理,其原理是通过给歌曲建立指纹库来验证被检测样本是哪个歌曲,如果某段歌曲匹配了,那么它们在时间上会出现线性关系。

具体步骤如下:

A、建立歌曲指纹

1、读取歌曲

通过librosa包获取语音数据

# 语音预处理,生成频谱数据
	def _pre_music(self, music_path):
		# 音频时间序列,音频的采样率
		y, sr = librosa.load(
			path=music_path,
			sr=hp.fingerprint.core.sr,
		)
		
		arr2D = librosa.stft(
			y=y,
			n_fft=hp.fingerprint.core.n_fft,
			hop_length=hp.fingerprint.core.hop_length,
			win_length=hp.fingerprint.core.win_length
		)
		return np.abs(arr2D) ** 2

2、获取频谱图

通过Au软件可以看出来,音频的频谱图

  随着每个时间点的放大,可以看到每一个采样点的之间的频谱图

 把声谱图转为频谱图的过程是通过MFCC得到的,有兴趣的可以自己搜索相关技术文章,这属于信号处理专业的相关知识。

转为频谱图:shape = [频率,帧数]

# 生成频谱图
arr2D = mlab.specgram(
    # 数据
    channel_samples,
    # 窗口大小
    NFFT=wsize,
    # 采样率
    Fs=Fs,
    # 加窗
    window=mlab.window_hanning,
    # 重叠率
    noverlap=int(wsize * wratio))[0]
# (2049, 5027)
# print(np.shape(arr2D))

3、频谱图转为星图

把频谱图里面的频率0变为最小值,防止无法取对数

取对数,作用:某些值过大

取对数之后会出现无穷小的值,把这些值变为0

求局部最大值【即锚点-星星】,neighborhood控制着样本密度

# maximun_filter 找到局部最大值所对应的坐标
struct = generate_binary_structure(2, 2)  # 2维 离中心点一个距离
neighborhood = iterate_structure(struct, hp.fingerprint.core.neighborhood)  # 中心点扩大到离中心点40个距离
local_max = maximum_filter(spectrogram, footprint=neighborhood) == spectrogram
		
# 局部最大值
amps = spectrogram[local_max]

获取局部最大值的坐标【即锚点坐标】:[时间,频率], shape = [y,x] = [频率,时间]

# 获得peakes 是按[行,列]返回的:j是频率 i是时间
j, i = np.where(local_max)

 通过过滤能量最小值来减少噪音

4、存储指纹和歌曲ID

根据时间进行排序

peakes = sorted(peakes)

时间偏移坐标:t1

一个锚点的哈希个数取决于,当前锚点与其它锚点计算的范围:0<t1<200,样本密度和扇出因子是控制哈希·时间偏移记录的数量。

# 锚点
for i in range(len(peakes)):
	# 近邻点
	for j in range(1, hp.fingerprint.core.near_num):
		# 判断下标是否越界
		if (i + j) < len(peakes):
			t_1 = peakes[i][0]
			t_2 = peakes[i + j][0]
			f_1 = peakes[i][1]
			f_2 = peakes[i + j][1]
			delta_t = t_2 - t_1

 建立每个锚点的哈希值:[f1,f2,delta_t]

hash_str = "%s|%s|%s" % (str(freq1), str(freq2), str(t_delta))
h = hashlib.sha1(hash_str.encode('utf-8'))
yield (h.hexdigest()[0:20], t1)

数据库中的存储的数据是:哈希值,时间偏移记录,歌曲ID,

B、待测歌曲样本

获取待测样本数据,通过短时傅里叶变换转为频谱图

def get_audio():
	parser = argparse.ArgumentParser(formatter_class=RawTextHelpFormatter)
	parser.add_argument('-s', '--seconds', nargs='?')
	args = parser.parse_args()
	
	if not args.seconds:
		args.seconds = "7"
	seconds = int(args.seconds)
	chunksize = 2 ** 12
	channels = 1
	record_forever = False
	
	listener = Listener()
	
	listener.start_recording(
		seconds=seconds,
		chunksize=chunksize,
		channels=channels
	)
	
	while True:
		bufferSize = int(listener.rate / listener.chunksize * seconds)
		print("正在录音")
		for i in range(0, bufferSize):
			number = listener.process_recording()
		if not record_forever: break
	listener.stop_recording()
	
	print("录音停止")
	listener.save_recorded('xxx.mp3')
	
	return './xxx.mp3'

 样本频谱图

样本星座图 

  1. 用最小值替换全0,取log能量对数,把负无穷小取0
  2. 通过maximum_filter()获取最大值坐标,得到星座图
  3. 最后通过偏离每个锚点,计算得到每个锚点和目标区域的Hash:time
  4. 在数据库中通过查询hash来得到匹配的match_hashes,以及t_offset
  5. 统计match_hashes中每个t_offset所对应的歌曲id个数
  6. 个数最多的歌曲id就是该歌曲,t_offset是开始的地方
	if len(matches) > 0:
		match_collections = {}
		max_count = 0
		start_time = None
		final_music_id = None
		x, y, z = [], [], []
		for matche in matches:
			# 数据库t 样本t
			music_id, t, t_sample = matche
			x.append(int(t))
			y.append(t_sample)
			z.append(music_id)
			# 偏移值
			offset = int(t) - t_sample
			
			# 判断字典里面是否有这个偏移
			if offset not in match_collections:
				match_collections[offset] = {}
				pass
			# 判断字典里面是否有这个music_id
			if music_id not in match_collections[offset]:
				match_collections[offset][music_id] = 0
				pass
			
			match_collections[offset][music_id] += 1
			# 判断这个偏移下的music_id是否是最多
			if match_collections[offset][music_id] > max_count:
				max_count = match_collections[offset][music_id]
				start_time = offset
				final_music_id = music_id
		
		print(x, y, z)
		drow_plot(x, y, z, show=True)
		print(match_collections, max_count)
		# 开始匹配时间
		print((start_time * hp.fingerprint.hop_length / hp.fingerprint.sr))
		music_name = connector.find_music_by_id(final_music_id)
		print(music_name)

结果

线性关系图:

Audio-Fingerprinting 是一个音乐指纹识别系统,使用的语言为 JAVA,同时需要用到 MySQL 数据库(虽然不是必须的,但这个系统采用他保存指纹和音乐信息)。他包含了指纹生成,数据库存储,和简易的服务器和客户端。他通过生成和记录音乐指纹,能够识别来自麦克风、文件等各个来源的音乐,并且有很高的抗噪性,同时他对文件属性和音乐质量不敏感。你可以使用服务器给手机或者其他程序提供音乐识别服务。你可以根据需求调节里面的参数,当前参数是为了在较短时间识别来自极大噪声和失真的音源,1500个左右的文件将产生接近24000000个指纹数 据。如果你只用于识别文件并且没有严重的噪声与失真,你可以修改参数,1个文件只需要少量指纹就可以识别,对于噪声较低的音源10s 200个指纹已经满足大多需求。简易使用方法需要安装MySQL,并执行Fingerprint. sql, 同时你可能需要修改max_allowed_packet参数,因为添加歌曲需要发送较大的包,我采用的参数是32M。修改MysqlDB中的数据库信息为你的数据库信息,如:private final String url = "jdbc:mysql://127.0.0.1:3306/musiclibary?user=yecheng"; private final String user = "yecheng"; private final String password = "yecheng";添加文件的方法:Ps:你可以重写添加的方法或者制作脚本或者直接使用其他软件实现转码功能,目前他能够从%title%}}%album%}}%artist%的文件名中获得信息。将文件转码为WAV,采样率为8000。调用Insert,参数为文件名或者文件夹。搜索音乐你可以调用Search 文件名搜索。在数据库较大的情况推荐采用运行Server,使用Client 文件名搜索。主要参数介绍Fingerprint:NPeaks:一个周期中每个子带的峰值点的个数 fftSize:FFT的窗口大小 overlap:FFT的窗口重叠大小 C:一个周期包含多少个窗口 peakRange:取峰值点时与多大范围的邻居比较 range_time:取点对的时候的时间范围,单位为秒 range_freq:取点对的时候的频率范围,单位为频率 Band:分成的子带,值对应FFT产生的数组索引 minFreq:最小频率 maxFreq:最大频率 minPower:最小能量修改的建议:提高识别率:减小minPower, 增加Band、NPeaks、range_time降低数据量:增大minPower,减小Band、NPeaks、rang_time其中建议先修改Band和minPower。Server:port:服务器的端口Client:ip:服务器的ip port:服务器的端口性能与效果数据量:音乐库为1500首歌,指纹数量为24000000个左右,服务器稳定后占用内存约340M。速度:处理器i7-3632QM,添加1500首歌用时约1919秒,一首歌约用时1.3秒。使用服务器查找10s的歌曲用时约0.2秒(不考虑客户端读取文件的时间)。准确度:对噪声较低的音频有很高的识别率,对噪声较高的也有接近商用的准确率,但是相对来说如果对于未出现在曲库的歌曲,也有一定的误报率。抗噪性:能够抵抗较强的失真和噪声,可以参考我给的测试音频。工作原理参考文档:ShazamMel scale本算法实现类似Shazam,首先我计算出音频的频谱图,将频谱根据频率分成若干子带,对每个子带查找若干个峰值点,本算法子带划分基于Mel频率。将获得的峰值点根据频率、时间范围组成点对。本算法的取点对频率范围为在子带内,其目的在于减少点对的数目并且提高分布式能力。取点对的时间范围为1s-4s。你可以根据需要修改这些参数。 标签:Audio
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

语音不识别

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值