使用python和librosa的音乐可视化工具



我正在尝试制作一个小脚本,以在python中生成一些音频文件的可视化。我的最终目标是生成一个30fps的视频,该视频由python中汇编的一些图像资产整理而成。但我有点拘泥于声音分析部分,因为我对声音及其背后的物理和数学几乎一无所知。无论如何。为了在python中处理导入声音,我使用了librosa,它看起来非常强大。

import librosa
import numpy as np
#load an example audio
filename = librosa.ex('trumpet')
y, sr = librosa.load(filename, sr=44100)
#apply short-time Fourier transform
stft = np.abs(librosa.stft(y, n_fft=1024, hop_length=None, win_length=None, window='hann', center=True, dtype=None, pad_mode='reflect'))
# converting the matrix to decibel matrix
D = librosa.amplitude_to_db(stft, ref=np.max)

通过这种方式,我获得形状为(513,480)的矩阵D,意思是513〃;步骤";在频率范围和480个数据点或帧中。假设采样的持续时间约为5.3秒,这使得其约为每秒172.3帧。因为我想要30fps,所以我决定在采样率上做一些尝试和错误,直到我达到23038。加载文件时应用此项:

y, sr = librosa.load(filename, sr=23038)

我获得了89.998帧/秒的帧速率,这似乎更适合我的用途。所以我继续降低了我的数据的分辨率,将我在9英寸中的读数平均;桶";频率:

#Initialize new empty array of target size
nD = np.empty(shape=(9,D.shape[1]))
#populate new array
for i in range(D.shape[1]):
nD[:,i] = D[:,i].reshape(-1, 57).mean(axis=1)

这里的9来自于这样一个事实,那就是我可以得到的一个数字除以57,这是一个因子第513页。结果,我把3帧聚合在一起,达到了30fps:

count = 0
for i in range(0, nD.shape[1],3):
try:
nnD[:,count] = nD[:,i:i+3].reshape(-1, 3).mean(axis=1)
count+=1
except Exception:
pass

这似乎完全是疯了,除了尝试的部分是存在的,因为有时我会有索引错误,并且计数递增计数器似乎已经足够愚蠢了。但令人难以置信的是,这似乎在某种程度上奏效了。出于测试目的,我制作了一个可视化程序

from time import sleep
from os import system
nnD = ((nnD+80)/10).astype(int)
for i in range(nnD.shape[1]):
for j in range(nnD.shape[0]):
print ("#"*nnD[j,i])
sleep(1/30)
system('clear')

这显示了终端中由#组成的行。

现在,我的问题是:

我该怎么做才合适?

更具体地说:

1_有没有一种方法可以在不破坏采样率的情况下匹配傅立叶数据的帧速率?

2_有没有一种更合适的方法来聚合我的数据,可能是一个任意的数字,而不是必须在513的因子之间进行选择?

如果您希望以秒为单位的时间分辨率为大约N FPS,您可以执行以下代码。但请注意,由于跃点需要是一个整数,这将导致漂移,请参阅打印输出。因此,定期重置同步是必要的,例如每1分钟重置一次。或者每首歌一次就可以逃脱惩罚。

import math
def next_power_of_2(x):
return 2**(math.ceil(math.log(x, 2)))
def params_for_fps(fps=30, sr=16000):
frame_seconds=1.0/fps
frame_hop = round(frame_seconds*sr) # in samples
frame_fft = next_power_of_2(2*frame_hop)
rel_error = (frame_hop-(frame_seconds*sr))/frame_hop

return frame_hop, frame_fft, rel_error

seconds = 10*60
fps = 15
sr = 16000
frame_hop, frame_fft, frame_err = params_for_fps(fps=fps, sr=sr)
print(f"Frame timestep error {frame_err*100:.2f} %")
drift = frame_err * seconds
print(f"Drift over {seconds} seconds: {drift:.2f} seconds. {drift*fps:.2f} frames")
# Then variables can be used with
# librosa.stft(...hop_length=frame_hop, n_fft=frame_fft)

如果这种方法不够好,则需要基于(视频(帧计数器对音频特征进行插值。线性插值会很好。这允许补偿相关的漂移。这可以针对每个帧动态地完成,或者可以对音频时间序列重新采样以与FPS帧对齐。

这已经很好地完成了这个技巧。正如我所怀疑的,您需要调整hop_length

import time
import librosa
import numpy as np
import scipy.signal
# Tunable parameters
hop_length_secs = 1 / 30
bands = 10  # How many frequency bands?
characters = " ..::##@"  # Characters to print things with
filename = librosa.ex("trumpet")
y, sr = librosa.load(filename, sr=22050)
sound_length = y.shape[0] / sr
print(f"{sound_length = }")
hop_length_samples = int(hop_length_secs * sr)
print(f"{hop_length_secs = }")
print(f"{hop_length_samples = }")
stft = np.abs(librosa.stft(y, n_fft=1024, hop_length=hop_length_samples))
num_bins, num_samples = stft.shape
# This should be approximately `sound_length` now
print(f"{num_samples * hop_length_secs = }")
# Resample to the desired number of frequency bins
stft2 = np.abs(scipy.signal.resample(stft, bands, axis=0))
stft2 = stft2 / np.max(stft2)  # Normalize to 0..1

# Remap the 0..1 signal to integer indices
# -- the square root boosts the otherwise lower signals for better visibility.
char_indices = (np.sqrt(stft2) * (len(characters) - 1)).astype(np.uint8)
# Print out the signal "in time".
for y in range(num_samples):
print("".join(characters[i] for i in char_indices[:, y]))
time.sleep(hop_length_secs)

输出为

sound_length = 5.333378684807256
hop_length_secs = 0.03333333333333333
hop_length_samples = 735
num_samples * hop_length_secs = 5.366666666666666
.:..
.##:.. .
.#:: . .
.#:. .
.#:.
.#:. . .
....
.@#:.. ...
.##:.. .
.#:. .
.#:.
.::.
.#:.
.::.
.::.
.::.
....
.::.
.##:.. .
.##:.. .
.#:. .
.#:.
.::.
.:..
.:..
.:..
.:..
.##:..
.##:.. .
.##:.. .
.##:..
.:..
(etc...)

如果你斜视的话,它看起来确实像一个视觉。。。

最新更新