本篇文章将介绍一段用于分析音频文件特征的代码。在介绍过程中,我们将分析代码的功能、实现方式,并对关键代码进行解释。
这是 Bilibili 数据分析项目的一部分。
需求分析
在多媒体处理中,音频分析是一项重要任务。通过分析音频文件中的特征,如响度、基频、音调等,我们可以对音频内容进行更深入的理解和处理。本代码的需求是从音频文件中提取关键特征,并将结果保存为CSV文件。
功能介绍
本代码主要实现了以下功能:
- 读取音频文件
- 计算音频文件的关键特征,包括响度、基频、音调、MFCC、谱质心、谱衰减、零交叉率等。
- 将计算结果保存为CSV文件。
主要代码介绍
读取音频文件
为了读取音频文件,我们使用了 pydub
库的 AudioSegment
类。此外,我们还需要获取音频的采样率。以下是读取音频文件的关键代码:
def load_audio_file(file_path):
audio = AudioSegment.from_file(file_path)
audio_samples = audio.get_array_of_samples()
audio_samples = np.array(audio_samples, dtype=np.float32)
return audio_samples, audio.frame_rate
计算音频特征
我们使用 librosa
库来计算音频文件的关键特征。以下是计算各种特征的关键代码:
# 计算响度
loudness = librosa.feature.rms(y=audio_samples, frame_length=n_fft, hop_length=hop_length)
# 计算基频
pitches, _ = librosa.piptrack(y=audio_samples, sr=frame_rate, n_fft=n_fft,
hop_length=hop_length)
# 计算音调
chroma = librosa.feature.chroma_stft(y=audio_samples, sr=frame_rate, n_fft=n_fft, hop_length=hop_length)
# 计算MFCC
mfcc = librosa.feature.mfcc(y=audio_samples, sr=frame_rate, n_fft=n_fft, hop_length=hop_length)
# 计算谱质心
spectral_centroid = librosa.feature.spectral_centroid(y=audio_samples, sr=frame_rate, n_fft=n_fft, hop_length=hop_length)
# 计算谱衰减
spectral_rolloff = librosa.feature.spectral_rolloff(y=audio_samples, sr=frame_rate, n_fft=n_fft, hop_length=hop_length)
# 计算零交叉率
zero_crossing_rate = librosa.feature.zero_crossing_rate(y=audio_samples, frame_length=n_fft, hop_length=hop_length)
数据保存
将计算得到的各种特征数据整合为 pandas
数据框,然后将数据框合并,并将合并后的数据框保存为CSV文件。
data_df = pd.concat([loudness_df, pitch_df, chroma_df, mfcc_df, spectral_centroid_df, spectral_rolloff_df, zero_crossing_rate_df], axis=1)
data_df.to_csv('/Volumes/SSD/Data_demo/video/{}/{}_sound_1fps.csv'.format(bvid, bvid), index=True)
注意事项
在使用本代码时,请确保已经安装了所需的第三方库,如 numpy
、pandas
、pydub
和 librosa
。另外,请确保音频文件路径和CSV文件保存路径与实际情况相符。
完整代码
以下是本文介绍的完整代码:
import numpy as np
import pandas as pd
from pydub import AudioSegment
import librosa
from tqdm import tqdm
import os
def load_audio_file(file_path):
audio = AudioSegment.from_file(file_path)
audio_samples = audio.get_array_of_samples()
audio_samples = np.array(audio_samples, dtype=np.float32)
return audio_samples, audio.frame_rate
def analyze_audio(audio_samples, frame_rate, bvid):
# 设置参数
FRAMES = 1
hop_length = int(round(frame_rate / FRAMES)) # 计算每秒30帧所需的hop_length
n_fft = 2048
# 计算响度
loudness = librosa.feature.rms(y=audio_samples, frame_length=n_fft, hop_length=hop_length)
# 计算基频
pitches, _ = librosa.piptrack(y=audio_samples, sr=frame_rate, n_fft=n_fft,
hop_length=hop_length)
# 计算音调
chroma = librosa.feature.chroma_stft(y=audio_samples, sr=frame_rate, n_fft=n_fft,
hop_length=hop_length)
# 计算MFCC
mfcc = librosa.feature.mfcc(y=audio_samples, sr=frame_rate, n_fft=n_fft, hop_length=hop_length)
# 计算谱质心
spectral_centroid = librosa.feature.spectral_centroid(y=audio_samples, sr=frame_rate,
n_fft=n_fft, hop_length=hop_length)
# 计算谱衰减
spectral_rolloff = librosa.feature.spectral_rolloff(y=audio_samples, sr=frame_rate, n_fft=n_fft,
hop_length=hop_length)
# 计算零交叉率
zero_crossing_rate = librosa.feature.zero_crossing_rate(y=audio_samples, frame_length=n_fft,
hop_length=hop_length)
# 创建数据序列
data = {
'loudness': loudness[0],
'pitch': pitches,
'chroma': chroma,
'mfcc': mfcc,
'spectral_centroid': spectral_centroid[0],
'spectral_rolloff': spectral_rolloff[0],
'zero_crossing_rate': zero_crossing_rate[0]
}
# 将数据序列保存到CSV文件
loudness_df = pd.DataFrame(loudness.T, columns=['loudness'])
pitch_df = pd.DataFrame(pitches.T, columns=[f'pitch_{i}' for i in range(pitches.shape[0])])
chroma_df = pd.DataFrame(chroma.T, columns=[f'chroma_{i}' for i in range(chroma.shape[0])])
mfcc_df = pd.DataFrame(mfcc.T, columns=[f'mfcc_{i}' for i in range(mfcc.shape[0])])
spectral_centroid_df = pd.DataFrame(spectral_centroid.T, columns=['spectral_centroid'])
spectral_rolloff_df = pd.DataFrame(spectral_rolloff.T, columns=['spectral_rolloff'])
zero_crossing_rate_df = pd.DataFrame(zero_crossing_rate.T, columns=['zero_crossing_rate'])
data_df = pd.concat(
[loudness_df, pitch_df, chroma_df, mfcc_df, spectral_centroid_df, spectral_rolloff_df,
zero_crossing_rate_df], axis=1)
data_df.to_csv('/Volumes/SSD/Data_demo/video/{}/{}_sound_1fps.csv'.format(bvid, bvid), index=True)
if __name__ == "__main__":
# 读取csv中的bvid、cid
video_list = pd.read_csv('/Volumes/SSD/Data/getVideoinfo/getVideoinfo_byhot_1.csv')
bvid_list = video_list['bvid'].values.tolist()
cid_list = video_list['cid'].values.tolist()
for bvid in tqdm(bvid_list):
mp3_file_path = '/Volumes/SSD/Data_demo/video/{}/{}.mp3'.format(bvid, bvid)
if not os.path.exists(mp3_file_path):
continue
audio_samples, frame_rate = load_audio_file(mp3_file_path)
analyze_audio(audio_samples, frame_rate, bvid)