纯音乐库特征提取
# 安装musly(Python绑定) pip install musly-python # 安装FAISS(CPU版,GPU版需单独编译) pip install faiss-cpu # 其他依赖 pip install librosa soundfile numpy pandas tqdm
import os import musly import faiss import numpy as np import pandas as pd from tqdm import tqdm import librosa # ===================== 配置参数 ===================== MUSIC_DIR = "./pure_music_library/" # 你的纯音乐目录 INDEX_PATH = "./music_index.faiss" # FAISS索引保存路径 METADATA_PATH = "./music_metadata.csv" # 音乐路径-特征映射表 SIMILAR_TOP_K = 10 # 检索返回最相似的K首 # ===================== 工具函数 ===================== def get_audio_files(dir_path): """获取目录下所有音频文件(WAV/MP3)""" audio_ext = (".wav", ".mp3", ".flac", ".ogg") audio_files = [] for root, _, files in os.walk(dir_path): for file in files: if file.lower().endswith(audio_ext): audio_files.append(os.path.join(root, file)) return audio_files def extract_musly_feature(audio_path): """使用Musly提取音频特征(timbre模式,纯音乐首选)""" try: # 初始化Musly(timbre模式:适合纯音乐音色/风格匹配) music_analyzer = musly.Analyzer() # 加载音频并提取特征(返回定长向量) feature = music_analyzer.analyze(audio_path, musly.MuslyTimbre) return feature.astype(np.float32) # FAISS要求float32 except Exception as e: print(f"提取特征失败 {audio_path}: {e}") return None # ===================== 构建索引 ===================== def build_music_index(): """提取特征并构建FAISS索引""" # 1. 获取所有音频文件 audio_files = get_audio_files(MUSIC_DIR) if not audio_files: print("未找到音频文件!") return # 2. 批量提取Musly特征 features = [] valid_files = [] for file in tqdm(audio_files, desc="提取Musly特征"): feat = extract_musly_feature(file) if feat is not None: features.append(feat) valid_files.append(file) # 3. 保存元数据(文件路径-索引映射) metadata = pd.DataFrame({"file_path": valid_files}) metadata.to_csv(METADATA_PATH, index=False, encoding="utf-8") # 4. 构建FAISS索引(L2距离:适配Musly特征分布) feature_matrix = np.vstack(features) dim = feature_matrix.shape[1] # Musly特征维度(默认~200维) # 选择索引类型:IVF_FLAT适合百万级数据,速度快 index = faiss.IndexIVFFlat( faiss.IndexFlatL2(dim), # 基础索引(L2距离) dim, min(100, len(valid_files)//10), # 聚类数(经验值) faiss.METRIC_L2 ) # 训练+添加数据 index.train(feature_matrix) index.add(feature_matrix) # 5. 保存索引 faiss.write_index(index, INDEX_PATH) print(f"索引构建完成!共处理 {len(valid_files)} 首音乐,索引保存至 {INDEX_PATH}") # ===================== 相似检索 ===================== def search_similar_music(query_audio_path): """检索与查询音频最相似的音乐""" # 1. 加载索引和元数据 if not os.path.exists(INDEX_PATH) or not os.path.exists(METADATA_PATH): print("索引/元数据不存在,先构建索引!") build_music_index() index = faiss.read_index(INDEX_PATH) metadata = pd.read_csv(METADATA_PATH, encoding="utf-8") # 2. 提取查询音频特征 query_feat = extract_musly_feature(query_audio_path) if query_feat is None: print("查询音频特征提取失败!") return [] # 3. FAISS检索(返回距离和索引) query_feat = query_feat.reshape(1, -1) # 转为2D数组 distances, indices = index.search(query_feat, SIMILAR_TOP_K) # 4. 解析结果(距离越小越相似) similar_results = [] for i, idx in enumerate(indices[0]): file_path = metadata.iloc[idx]["file_path"] similar_results.append({ "rank": i+1, "file_path": file_path, "distance": distances[0][i] # 距离越小,相似度越高 }) return similar_results # ===================== 主函数 ===================== if __name__ == "__main__": # 第一步:构建索引(首次运行执行) build_music_index() # 第二步:示例:检索相似音乐 query_audio = "./test_piano.mp3" # 你的查询纯音乐文件 if os.path.exists(query_audio): similar_music = search_similar_music(query_audio) print(f"\n与 {query_audio} 最相似的{SIMILAR_TOP_K}首音乐:") for res in similar_music: print(f"排名{res['rank']} | 距离{res['distance']:.4f} | 路径:{res['file_path']}") else: print(f"查询文件 {query_audio} 不存在!")