基于音频指纹的音乐识别
说到音乐识别,相信很多小伙伴都很熟悉,比如酷狗音乐,QQ音乐,网易音乐,他们都有很多的功能,其中就有一个功能就有音乐识别。本届我们就来了解一下基于“音频指纹”的音乐识别系统。
系统架构的核心模块包括信号采集,音频指纹(亦可称之为声纹)生成以及数据存储系统。其中,音频指纹生成苏算法的核心部分会采用傅里叶变换。
音频信号采集与播放
“听歌识曲”首先是“听”,然后才能“识”。“听”的过程,实际上就是一个音频采集(录音与采样)的过程。:识“的过程,是将采集到的音频片段与曲库中的歌曲数据进行比对来获取歌曲信息的过程。曲库中保存着大量的歌曲数据,包括歌曲名称,歌曲存储位置,歌曲声纹等信息。
在写代码之前,我们需要先进行安装模块pyaudio模块。
#音频信号采集与播放 import wave import pyaudio class SoundProcessing(): def Record(self,CHUNK=44100,FORMAT=pyaudio.paInt16,CHANNELS=2,RATE=441100, RECORD_SECONDS=200,WAVE_OUTPUT_FILENAME="demoWave.wav"): p=pyaudio.PyAudio() stream=p.open(format=FORMAT, channels=CHANNELS, rate=RATE, input=True, frames_per_buffer=CHUNK) frames=[] for i in range(0,int(RATE/CHUNK*RECORD_SECONDS)): data=stream.read(CHUNK) frames.append(data) stream.stop_stream() stream.close() p.terminate() wf=wave.open(WAVE_OUTPUT_FILENAME,'wb') wf.setnchannels(CHANNELS) wf.setsampwidth(p.get_sample_size(FORMAT)) wf.setframerate(RATE) wf.writeframes(b''.join(frames)) wf.close() def Play(self,FILEPATH): chunk=1024 wf=wave.open(FILEPATH,'rb') p=pyaudio.PyAudio() #打开声音输出流 stream=p.open(format=p.get_format_from_width(wf.getsampwidth()), channels=wf.getnchannels(), raate=wf.getframerate(), output=True) while True: data=wf.readframes(chunk) if data=="": break stream.write(data) stream.close() p.terminate() if __name__=='__main__': snd=SoundProcessing() #类的实例化 #录音并将其存储为demoWave.wav文件 snd.Record(RECORD_SECONDS=30,WAVE_OUTPUT_FILENAME='demoWave.wav') snd.Play('demoWave.wav')
音频指纹生成
音频文件有很多格式,注入MP3,WAV,PCM等。其中,WAV格式是Microsoft公司研发的一种音频格式文件,它符合资源交换文件格式(Resource Interchange File Format,RIFF)规范,用于保存Windows操作系统的音频信息资源,beiWindows操作系统及其应用程序广泛支持。标准化的WAV文件采用44100采样率,16bit深度编码。WAV文件分为两个部分,第一个部分是WAV头文件,第二个部分是利用脉冲编码调制(Pulse Code Modulation,PCM)方式进行编码的音频数据。我们将以常见的WAV格式为例,来阐述如何生成音乐的音频指纹(声纹)。
从本质上来看,声音就是一种波。
我们用一个音频文件来描述其波形:
import wave import matplotlib.pyplot as plt import numpy as np import os waveFile=wave.open('./test.wav','rb') params=waveFile.getparams() nchannels,sampwidth,framerate,nframes=params[:4] strData=waveFile.readframes(nframes)#读取音频文件并转为字符串格式 waveData=np.fromstring(strData,dtype=np.int16) #将字符串格式转化为int类型 waveData=waveData*1.0/(max(abs(waveData))) #wave幅值归一化 #音频波形可视化 time=np.arange(0,nframes)*(1.0/framerate) waveData=np.arange(453632) plt.plot(time,waveData) plt.xlabel("Time(X)") plt.ylabel("Amplitude(Y)") plt.title("Single Channel Wave") plt.grid("on") plt.show()
音频波形文件是连续的模拟信号,经过采样可形成数字信号。对于一个单声道音频文件而言,经采样后,实际上就产生一个二维数组,每个元素是对应采样点的数值。对于我们普通人来说,我们该如何去实现听歌识曲?那就是背歌词,只要听到某一句或者多句歌词,就会记起来,但是对于像博主我记忆力不好的人而言,记住很多歌曲是不现实的,所以,我们就要采用其他的方式。那就是利用计算机来记住音频信号序列。声音属于时域信号,我们需要将其转化为频域信号以便后续的信号处理。以下示例代码,通过傅里叶变换将音频时域信号转换为频域信号。
import wave import matplotlib.pyplot as plt import numpy as np import os waveFile=wave.open('./test.wav','rb') params=waveFile.getparams() nchannels,sampwidth,framerate,nframes=params[:4] strData=waveFile.readframes(nframes)#读取音频文件并转为字符串格式 waveData=np.fromstring(strData,dtype=np.int16) #将字符串格式转化为int类型 waveData=waveData*1.0/(max(abs(waveData))) #wave幅值归一化 #音频波形可视化 time=np.arange(0,nframes)*(1.0/framerate) #waveData=np.arange(453632) df=1 freq=[df*n for n in range(0,len(waveData))] c=np.fft.fft(waveData) plt.plot(freq,abs(c),color='blue') plt.xlabel("Frequency(Hz)") plt.ylabel("Amplitude(Y)") plt.title("Frequency Domain Signal") plt.grid(True) plt.show()
音频信号经傅里叶变换之后,原本x,y轴分别为时间和波幅,现在则是频率以及频率幅值,与此同时,也改变了我们对音频构成的理解。
对每一帧音频信号进行傅里叶变换后,就可以开始构造声纹了,这就是整个系统中最核心的部分。构造声纹的最大挑战就是在于如何从众多的频率中选出区分都最大的频率分量。直观上看,选择具有最大幅值的频率(峰值)较为可靠。由于一首音乐的频率分布跨度太大,所以分析的时候会存在难以选择,为了避免分析整个频谱,我们通常将频谱分成多个子带,从每个子带中选择一个频率峰值。低子音带为30Hz~40Hz,40~80Hz和80~120Hz,中音和高音子带分别为120Hz~180Hz和180Hz~300Hz(人声和大部分乐器的基频出现在这俩个子带)。每个子带的最大频率就构成了这一帧信号的签名,这也就是我们最核心的“音频指纹”。
音频文件载入,音频指纹生成功能的实现示例代码:
import numpy as np import os import wave class SoundProcessing(): SongName="" document=[] #存放歌曲指纹列表 def DataLoad(self,FILEPATH): filename_list=os.listdir(FILEPATH) for filename in filename_list: if filename.endswith('wav'): #检查文件是否是wav格式 SoundProcessing.SongName=filename f=wave.open(FILEPATH+"\\"+filename,'rb') params=f.getparams() self.nchannels,self.sampwidth,self.framerate,self.nframes=params[:4] str_data=f.readframes(self.nframes) self.wave_data=np.fromstring(str_data,dtype=np.short) self.wave_data.shape=-1,self.sampwidth self.wave_data=self.wave_data.T self.GenerateFingerPrint() self.name=os.path.basename('./test1.wav') return True f.close() def GenerateFingerPrint(self,frames=40): block=[] fft_blocks=[] self.high_point=[] #用于保存生成的音频指纹 blocks_size=self.framerate//frames blocks_num=self.nframes/blocks_size for i in range(0,len(self.wave_data[0])-blocks_size,blocks_size): block.append(self.wave_data[0][i:i+blocks_size]) fft_blocks.append(np.abs(np.fft.fft(self.wave_data[0][i:i+blocks_size]))) self.high_point.append((np.argmax(fft_blocks[-1][0:40]),np.argmax(fft_blocks[-1][40:80])+40, np.argmax(fft_blocks[-1][80:120])+40, np.argmax(fft_blocks[-1][120:180])+120,)) song_fp="{'songname'"+":'"+SoundProcessing.SongName+"','fingerprint'"+":"+str(self.high_point[20:25])+"}" song_fp=eval(song_fp) SoundProcessing.document.append(song_fp) print (SoundProcessing.document) if __name__=="__main__": a=SoundProcessing() a.DataLoad('F:\人工智能算法') a.GenerateFingerPrint(frames=40)
结果展示:
[{'songname': 'test.wav', 'fingerprint': []}] [{'songname': 'test.wav', 'fingerprint': []}, {'songname': 'test.wav', 'fingerprint': []}]
在构造指纹的时候,我们需要注意的是,用户所除的环境的复杂,所以录制的音乐可能会有所杂音,于是,我建议在使用音频指纹的时候引入模糊化操作等手段来提高算法的抗噪能力。否则会影响最终检索质量。
语音克隆技术简介
语音合成,是指文本到音频的人工转换,或给定一段文字让计算机自动生成对应的人类读音。通过AI语音技术,在极短的时间内“克隆”出目标人物的声音。相信喜欢看碟战片的小伙伴比较了解,里面各个国家的特工,利用语音合成,模仿别人的声音,获取情报。在计算机技术发展迅速的今天,这些都已经能够实现。
合成自然语音,需要对大量高质量的语音转录进行训练。若要支持多人语音通常需要对每个人的语音数据集进行数十分钟的训练,而为多人记录大量高质量的数据不太现实。该项目所采用的方法是,通过独立训练一个捕捉说话人特征空间(Space of Speaker Characterstics)语音识别嵌入网络(Speaker-Descriminative Embedding Network),并在一个较小的数据集上训练一个高质量的TTS模型,从而将说话人建模与语音合成分离。解耦合网络使他们能够在独立的数据上进行训练,从而减少获得高质量多峰训练数据的需要。
该语音克隆系统由3个独立训练的神经网络组成。如图:
1.说话人语音编码器(Speaker Encoder)
它先从说话人那里获取一段参考语音信号(Speaker Reference Waveform),亦即说话人的音频小样本,然后从中计算出一个固定维矢量(Fixed Dimensional Vector)。
2.序列合成器(Synthesizer)
它基于说话人嵌入矢量,根据一系列字素(Grapheme)或音素输入,预测出一个对数梅尔频谱(Log-Mel Spectrum)。也就是给定一段文本,将其编码为向量表示,然后,将语音与文本这两个向量结合起来解码成声谱图(Spectrogram)。
3.声码器(Vocoder)
它将对数梅尔频谱转换成时域波形(Waveform)。也就是将声谱图转换成我们可以听到的音频波形。
由于博主对机器学习知识还不掌握,这里只能稍做介绍。语音篇的学习就到此结束了,后面就是自然语言处理技术的分享。期待大家的关注,点赞,收藏。