解释
我想要能够根据声音的音色(音调)对列表中的声音集合进行排序。这里有一个玩具示例,其中我手动对我创建并上传到此存储库的12个声音文件的频谱图进行了排序。我知道这些已经正确排序,因为每个文件产生的声音与其前面的文件完全相同,只是添加了一种效果或滤镜。
例如,声音x
、y
和z
的正确排序如下:
- 声音x和y相同,但y有失真效果
- 声音y和z相同,但z过滤掉高频
- 声音x和z相同,但z有失真效果,且z过滤掉高频
正确的排序应该是x, y, z
仅从频谱图中观察,我可以看到一些视觉指标,提示如何对声音进行分类,但我希望通过让计算机识别这些指标来自动化排序过程。
上图中声音文件:
- 长度相同
- 音高相同
- 同时开始
- 幅度相同(响度级别)
- MFCC_8 的起始位置与第一张图片中的 MFCC_8 不同
- MFCC_9 与第一张图片中的 MFCC_9 相同,但被复制了一次(因此长度加倍)
我打算在我的真实程序中通过声音变化类似这样来分割mp3文件。
我的程序目前为止
这是生成帖子中第一张图片的程序。我需要将函数sort_sound_files
中的代码替换为实际基于音色对声音文件进行排序的代码。需要完成的部分在底部,声音文件在此存储库上。我还在jupyter笔记本中有这段代码,其中还包括一个更类似于我实际想要的程序的第二个示例。
import librosa
import librosa.display
import matplotlib.pyplot as plt
import numpy as np
import math
from os import path
from typing import List
class Spec:
name: str = ''
sr: int = 44100
class MFCC(Spec):
mfcc: np.ndarray # Mel-frequency cepstral coefficient
delta_mfcc: np.ndarray # delta Mel-frequency cepstral coefficient
delta2_mfcc: np.ndarray # delta2 Mel-frequency cepstral coefficient
n_mfcc: int = 13
def __init__(self, soundFile: str):
self.name = path.basename(soundFile)
y, sr = librosa.load(soundFile, sr=self.sr)
self.mfcc = librosa.feature.mfcc(y, n_mfcc=self.n_mfcc, sr=sr)
self.delta_mfcc = librosa.feature.delta(self.mfcc, mode="nearest")
self.delta2_mfcc = librosa.feature.delta(self.mfcc, mode="nearest", order=2)
def get_mfccs(sound_files: List[str]) -> List[MFCC]:
'''
:param sound_files: Each item is a path to a sound file (wav, mp3, ...)
'''
mfccs = [MFCC(sound_file) for sound_file in sound_files]
return mfccs
def draw_specs(specList: List[Spec], attribute: str, title: str):
'''
Takes a list of same type audio features, and draws a spectrogram for each one
'''
def draw_spec(spec: Spec, attribute: str, fig: plt.Figure, ax: plt.Axes):
img = librosa.display.specshow(
librosa.amplitude_to_db(getattr(spec, attribute), ref=np.max),
y_axis='log',
x_axis='time',
ax=ax
)
ax.set_title(title + str(spec.name))
fig.colorbar(img, ax=ax, format="%+2.0f dB")
specLen = len(specList)
fig, axs = plt.subplots(math.ceil(specLen/3), 3, figsize=(30, specLen * 2))
for spec in range(0, len(specList), 3):
draw_spec(specList[spec], attribute, fig, axs.flat[spec])
if (spec+1 < len(specList)):
draw_spec(specList[spec+1], attribute, fig, axs.flat[spec+1])
if (spec+2 < len(specList)):
draw_spec(specList[spec+2], attribute, fig, axs.flat[spec+2])
sound_files_1 = [
'../assets/transients_1/4.wav',
'../assets/transients_1/6.wav',
'../assets/transients_1/1.wav',
'../assets/transients_1/11.wav',
'../assets/transients_1/13.wav',
'../assets/transients_1/9.wav',
'../assets/transients_1/3.wav',
'../assets/transients_1/7.wav',
'../assets/transients_1/12.wav',
'../assets/transients_1/2.wav',
'../assets/transients_1/5.wav',
'../assets/transients_1/10.wav',
'../assets/transients_1/8.wav'
]
mfccs_1 = get_mfccs(sound_files_1)
##################################################################
def sort_sound_files(sound_files: List[str]):
# TODO: Complete this function. The soundfiles must be sorted based on the content in the file, do not use the name of the file
# This is the correct order that the sounds should be sorted in
return [f"../assets/transients_1/{num}.wav" for num in range(1, 14)] # TODO: remove(or comment) once method is completed
##################################################################
sorted_sound_files_1 = sort_sound_files(sound_files_1)
mfccs_1 = get_mfccs(sorted_sound_files_1)
draw_specs(mfccs_1, 'mfcc', "Transients_1 Sorted MFCC-")
plt.savefig('sorted_sound_spectrograms.png')
编辑
我后来才意识到,另一个非常重要的问题是会有许多性质在振荡。例如,第一组中声音5和声音6之间的区别在于声音6是声音5但加了音量振荡(LFO),这种类型的振荡可以放置在频率滤波器、效果器(如失真)或甚至音高上。我意识到这使得问题变得更加棘手,它超出了我所问的范围。你有什么建议吗?我甚至可以使用几种不同的类型,并且只同时查看一种属性。