由于喜欢玩儿音乐游戏,因此打算研究一下如何用深度学习的模型生成音游的谱面。这篇文章主要目的是介绍或者总结一些音频的知识和代码。python
恩。若是没玩儿过的话,音乐游戏大概是下面这个样子。git
下面进入正题。github
我Google了一下,找到了这篇文章:Music Feature Extraction in Python。而后这篇文章介绍完了还有一个歌曲分类的实践:Classification of Music into different Genres using Keras。shell
下面的内容会主要参考一下这两篇文章,并加入一些个人理解。内容以下:后端
主要涉及的背景知识有:网络
首先先百度一下音频信号,app
音频信号是(Audio)带有语音、音乐和音效的有规律的声波的频率、幅度变化信息载体。 根据声波的特征,可把音频信息分类为规则音频和不规则声音。其中规则音频又能够分为语音、音乐和音效。规则音频是一种连续变化的模拟信号,可用一条连续的曲线来表示,称为声波。声音的三个要素是音调、音强和音色。声波或正弦波有三个重要参数:频率
、幅度
和相位
,这也就决定了音频信号的特征。机器学习
整体来讲:音频信号就是不一样频率和相位的正弦波的一个叠加。函数
通常的声音大概就是这个样子。学习
横轴是时间,纵轴是声音的幅度。由于本质上就是正弦波的一个叠加,因此看到实际上是有正有负的。
生活中存在各类正弦波,但并非全部的波都能被人耳听到。好比说咱们手机通讯的信号,wifi信号,以及阳光都是一种波,但并不能被人听见。
正常人耳听见声音的频率范围是 20Hz 到 2 万 Hz 。相同强度的声音如频率不一样的话,听起来的响度是不同的。至敏感的频率是 3000 和 4000Hz 。
因此声波的信号基本上只要关注2wHz之内就行了。
声音本质上是一种模拟信号,但在计算机或者在其余数字设备上传输时,咱们要把模拟信号转换为数字信号,须要进行采样。
奈奎斯特采样定理以下:
在进行模拟/数字信号的转换过程当中,当采样频率fs.max大于信号中最高频率fmax的2倍时(fs.max>2fmax),采样以后的数字信号完整地保留了原始信号中的信息。
这个定理描述的很简单,证实其实也不难,对于声音信号,只要采样的频率大于2*2wHz=4WHz的话,咱们就能够听到无损的音质了。
上面说人耳听力敏感的范围主要是在4000Hz,因此咱们通常听到的音乐实际上是使用8000Hz频率进行采样的。这里能够看下最近比较火的芒种这首歌。
这首歌的时间是3分36秒也就是216秒,它的标准品质的大小是3.3M。这里能够计算下使用8000Hz频率,16bit进行采样的话,那么这个文件的大小是:
大概也就是3.3兆了。
背景知识大概就上面那些,下面开始实践环节。
这里使用到了librosa,numpy, sklearn与keras。固然为了方便开发,我这里还用了IPython NoteBook。
pip install librosa numpy sklearn tensorflow keras
复制代码
接下来开始在python中加载音乐,这里我选的是一首我超级喜欢的一首音游的歌曲。连接在这里:visions.mp3
import librosa
x , sr = librosa.load("visions.mp3", sr=8000)
print(x.shape, sr)
复制代码
这里x
是音频信号的数字信息,能够看到是一维的,sr
是采样频率,用8000就行了。
而后能够看下这首歌在时域上的波形。
%matplotlib inline
import matplotlib.pyplot as plt
import librosa.display
plt.figure(figsize=(14, 5))
librosa.display.waveplot(x, sr=sr)
复制代码
运行后,效果以下:
上面是音乐时域上的信息。接下来须要对音乐的频率信息进行分析。
Spectogram这里没找到好的翻译, 大概表示的意思就是:时变的频谱图。
咱们知道对于周期信号,可使用傅里叶变换能够将时间域的信息变换到频率域上。
好比说看上面这张图的这个波实际上是由三个频率的正弦波构成的,能够看到从频率域的原点往上延伸,频率愈来愈高,能够看到对应的波形愈来愈密集。
在文章中提到了一个短时傅里叶变换的概念,做为一名本科学通讯的专业学生,我也不记得老师有讲过这个变换,只记得有过离散傅里叶变换。原做者给了个视频:The Short Time Fourier Transform | Digital Signal Processing
不过在看视频以前,凭借个人想象,感受这个变换既然叫短时傅里叶变换,那应该是把音频信号拆分开,而后进行傅里叶变换。由于若是对整个音乐进行傅里叶变换的话,咱们只能看到全部的频率信息,但不能肯定每一个频率的发生的大概时间。只能看到有某些频率的声音而已。
后来在网上找了一个我感受比较好的解释:
短时傅立叶变换基本思想是将信号加滑动时间窗,并对窗内信号作傅立叶变换,获得信号的时变频谱。
另外,还有一个东西能够感觉STFT,这里能够打开网易云音乐,找到一首歌,打开动效。
能够看到不断变化的各波形,拆分开来看,其实表明着就当前这一短期内包含的声音的频率信息。上面的这张图其实也就是对当前时刻的音频信号,进行DFT获得的。
接下来咱们调用librosa的stft方法能够直接获得短时傅里叶变换的结果。
X = librosa.stft(x)
Xdb = librosa.amplitude_to_db(abs(X)) # 把幅度转成分贝格式
plt.figure(figsize=(14, 5))
librosa.display.specshow(Xdb, sr=sr, x_axis='time', y_axis='hz')
plt.colorbar()
复制代码
效果以下:
这里横轴是时间,纵轴是频率,颜色则表明分贝(声音的响度),能够看到越红的地方信号音量越大。
接下来,介绍了一些音乐中的主要特征,这里主要翻译原做者的内容。
过零率(Zero Crossing Rate,ZCR)是指在每帧中,语音信号经过零点(从正变为负或从负变为正)的次数。这个特征已在语音识别和音乐信息检索领域获得普遍使用,是金属声音和摇滚乐的关键特征。
回到上面的图,咱们把一个时间上放大。
n0 = 9000
n1 = 9100
plt.figure(figsize=(14, 5))
plt.plot(x[n0:n1])
plt.grid()
复制代码
效果以下:
这里有7个过零点,能够经过下面的方法进行计算。
zero_crossings = librosa.zero_crossings(x[n0:n1], pad=False)
print(sum(zero_crossings))
复制代码
频谱中心表明声音的“质心”,又称为频谱一阶距。频谱中心的值越小,代表越多的频谱能量集中在低频范围内。
#spectral centroid -- centre of mass -- weighted mean of the frequencies present in the sound
import sklearn
spectral_centroids = librosa.feature.spectral_centroid(x[:80000], sr=sr)[0]
# Computing the time variable for visualization
frames = range(len(spectral_centroids))
t = librosa.frames_to_time(frames, sr=8000)
# Normalising the spectral centroid for visualisation
def normalize(x, axis=0):
return sklearn.preprocessing.minmax_scale(x, axis=axis)
#Plotting the Spectral Centroid along the waveform
librosa.display.waveplot(x[:80000], sr=sr, alpha=0.4)
plt.plot(t, normalize(spectral_centroids), color='r')
复制代码
这里用x[:80000]
表示音乐的前10秒。
.spectral_centroid
来计算每一帧的频谱中心。
.frames_to_time
将帧转换为时间,time[i] == frame[i]。【由于stft是一个窗口(帧)一个窗口取的,和采样频率并不对应,因此有个转换】
为了更好的可视化,对频谱中心进行了归一化。
频谱滚降点的意思,我翻译过来大概是:比该频率低的频率的全部能量大于必定比例的整个频谱的能量,一般这个比例为0.85。
感受仍是有点儿别扭,用公式比较好理解一些。
下面看下代码,和上面频谱中心的代码其实相似,也是一帧一帧的进行特征抽取。
spectral_rolloff = librosa.feature.spectral_rolloff(x, sr=sr)[0]
librosa.display.waveplot(x, sr=sr, alpha=0.4)
plt.plot(t, normalize(spectral_rolloff), color='r')
复制代码
.spectral_rolloff
来计算每一帧的频谱滚降点。
MFCC是音频信号特征中最重要的一个,基本上处理音频信号就会用到。(做为一名通讯学士,我也是才知道的)。
信号的MFCC参数是一个小集合的特征(通常10-20个),它可以简洁的表示频谱的包络。
mfccs = librosa.feature.mfcc(x, sr=sr)
print(mfccs.shape)
#Displaying the MFCCs:
librosa.display.specshow(mfccs, sr=sr, x_axis='time')
复制代码
.mfcc
用来计算信号的MFCC参数。
经过打印mfccs.shape
,能够看看每一帧里面有多少维的MFCC特征。第一个参数是mfcc参数的维度,第二个参数是帧数。
这里一共3177帧,每一帧有20维特征。
接下来,完成了以上的对音频信号的特征提取,就能够进行一波机器学习了。(这里不会涉及到机器学习的背景知识,默认读者不是机器学习的小白。)
这里将要使用GTZAN genre collection。这个数据集包含了10个题材,每一个题材包含了100个30s长的音乐。
这里将使用Tensorflow后端的Keras进行编码。
这里首先加载这些数据。
import librosa
import numpy as np
import os
genres = 'blues classical country disco hiphop jazz metal pop reggae rock'.split()
data_set = []
label_set = []
label2id = {genre:i for i,genre in enumerate(genres)}
id2label = {i:genre for i,genre in enumerate(genres)}
print(label2id)
for g in genres:
print(g)
for filename in os.listdir(f'./genres/{g}/'):
songname = f'./genres/{g}/{filename}'
y, sr = librosa.load(songname, mono=True, duration=30)
chroma_stft = librosa.feature.chroma_stft(y=y, sr=sr)
rmse = librosa.feature.rms(y=y)
spec_cent = librosa.feature.spectral_centroid(y=y, sr=sr)
spec_bw = librosa.feature.spectral_bandwidth(y=y, sr=sr)
rolloff = librosa.feature.spectral_rolloff(y=y, sr=sr)
zcr = librosa.feature.zero_crossing_rate(y)
mfcc = librosa.feature.mfcc(y=y, sr=sr)
to_append = f'{np.mean(chroma_stft)} {np.mean(rmse)} {np.mean(spec_cent)} {np.mean(spec_bw)} {np.mean(rolloff)} {np.mean(zcr)}'
for e in mfcc:
to_append += f' {np.mean(e)}'
data_set.append([float(i) for i in to_append.split(" ")])
label_set.append(label2id[g])
复制代码
能够看到除了上面讲到的一些特征以外,原做者这里还说了一些其余的特征,好比说rmse,chroma_stft这种。这里就再也不一一介绍了。
整体来讲,这里使用chroma_stft,rmse,spec_cent,spec_bw,rolloff,zcr的六个特征的均值,再加上mfcc的20个特征的均值,总共26个特征,来表示一个音乐的。
接下来咱们对数据进行一下归一化,对标签进行one-hot编码。
from sklearn.preprocessing import StandardScaler
from keras.utils import np_utils
scaler = StandardScaler()
X = scaler.fit_transform(np.array(data_set, dtype = float))
y = np_utils.to_categorical(np.array(label_set))
复制代码
效果以下:
而后把测试集和训练集进行分割。
from sklearn.model_selection import train_test_split
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2)
复制代码
这里的咱们的特征不多,因此用几层神经网络就能够了。
from keras import models
from keras.layers import Dense, Dropout
def create_model():
model = models.Sequential()
model.add(Dense(256, activation='relu', input_shape=(X_train.shape[1],)))
model.add(Dense(128, activation='relu'))
model.add(Dense(64, activation='relu'))
model.add(Dropout(0.5))
model.add(Dense(10, activation='softmax'))
return model
model = create_model()
复制代码
这里建立了一个包含三个隐藏层的神经网络,最后一层输出的是分类层,由于是10类,因此最后一层是10个单元。(这里相比原做者的代码多了一层Dropout减小数据过拟合)
而后是编译模型,咱们这里是一个分类问题,因此使用类别交叉熵函数categorical_crossentropy
,而后优化器选择Adam,评价指标选择正确率。
model.compile(optimizer='adam',
loss='categorical_crossentropy',
metrics=['accuracy'])
复制代码
接下来使用fit
方法进行训练,训练50轮。
model.fit(X_train, y_train, epochs=50, batch_size=128)
复制代码
训练很快就完了,而后使用evaluate
方法进行评估。
test_loss, test_acc = model.evaluate(X_test,y_test)
print('test_acc: ',test_acc)
复制代码
这里的测试准确率也就百分之70不到,原做者后面还有使用模型预测的代码,感受都没有必要了。
这篇博客主要是介绍了一些声音方面的特征提取的知识和代码,关于最后的案例看着玩玩儿就行,基本不具备实用性。