You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于Librosa的卡拉OK评分系统异常低分问题排查求助

问题排查与修正方案

1. 音高MSE计算的核心错误

你的MSE计算逻辑完全颠倒,且未正确对齐音高轮廓:

  • 当两个音高数组长度不一致时,你对短数组补零后,却用补零后的数组和原短数组做差,结果全为0,完全没意义——应该用补零/插值后的数组和另一个完整数组做比较。
  • MSE的正确公式是差值平方的均值,你写成了均值的平方,逻辑顺序错误。
  • 直接用Hz值计算偏差不符合人耳对数感知特性,应转成半音刻度再计算偏差,避免低频小偏差被放大、高频大偏差被低估。

修正后的音高计算代码:

import librosa
import numpy as np

# 统一采样率加载,确保时间轴对齐
sr_target = 22050
y, sr = librosa.load('karaoke_performance.wav', sr=sr_target)
ref_y, ref_sr = librosa.load('song_reference.wav', sr=sr_target)

# 提取半音刻度的音高轮廓
def get_pitch_contour(audio, sr):
    # YIN算法提取基频(Hz)
    pitch_hz = librosa.core.pitch.yin(audio, fmin=librosa.note_to_hz('C2'), fmax=librosa.note_to_hz('C7'), sr=sr)
    # 转半音,将静音段的0值替换为NaN
    pitch_midi = librosa.hz_to_midi(pitch_hz)
    pitch_midi[pitch_midi == -np.inf] = np.nan
    return pitch_midi

arr = get_pitch_contour(y, sr)
arr2 = get_pitch_contour(ref_y, ref_sr)

# 用插值对齐两个音高轮廓的长度(比补零更贴合时间轴)
max_len = max(len(arr), len(arr2))
arr_aligned = np.interp(np.linspace(0, len(arr)-1, max_len), np.arange(len(arr)), arr)
arr2_aligned = np.interp(np.linspace(0, len(arr2)-1, max_len), np.arange(len(arr2)), arr2)

# 忽略静音段,计算有效音高的MSE
valid_mask = ~np.isnan(arr_aligned) & ~np.isnan(arr2_aligned)
mse = np.mean((arr_aligned[valid_mask] - arr2_aligned[valid_mask]) ** 2) if np.any(valid_mask) else 0

2. 节奏偏差计算的逻辑错误

beat_frames是音频帧的索引,两个音频长度不同时直接相减完全无意义——需先将节拍帧转成时间戳,再用动态时间规整(DTW)对齐两个节拍序列后计算偏差:

修正后的节奏计算代码:

# 提取节拍时间戳(秒)
def get_beat_timestamps(audio, sr):
    tempo, beat_frames = librosa.beat.beat_track(audio, sr=sr)
    return librosa.frames_to_time(beat_frames, sr=sr), tempo

beat_times, tempo = get_beat_timestamps(y, sr)
ref_beat_times, ref_tempo = get_beat_timestamps(ref_y, ref_sr)

# 用DTW对齐节拍序列,匹配对应节拍点
from librosa.sequence import dtw
D, wp = dtw(beat_times.reshape(-1,1), ref_beat_times.reshape(-1,1))
# 计算对齐后的节拍时间偏差
aligned_beat = beat_times[wp[:,0]]
aligned_ref = ref_beat_times[wp[:,1]]
rhythm_deviation = np.mean(np.abs(aligned_beat - aligned_ref))

3. 得分映射的合理性问题

直接用100 - (mse * 100)会因MSE数值过大导致负分,需将偏差归一化到0-100的得分区间:

  • 半音MSE的合理上限为25(对应5半音偏差),用100 - min(mse * 4, 100)限制得分在0-100之间。
  • 节奏偏差以秒为单位,假设最大允许0.5秒偏差,用100 - min(rhythm_deviation * 200, 100)限制得分。

修正后的得分计算:

pitch_score = max(0, 100 - min(mse * 4, 100))
rhythm_score = max(0, 100 - min(rhythm_deviation * 200, 100))
normalized_final_score = (pitch_score + rhythm_score) / 2

print("Pitch Score:", pitch_score)
print("Rhythm Score:", rhythm_score)
print("Final Score:", normalized_final_score)

额外注意事项

  • 确保演唱音频和参考音频时间段落匹配,裁剪掉开头/结尾的空白片段,避免对齐误差。
  • YIN算法在静音段输出的0值需标记为NaN并忽略,否则会干扰偏差计算的准确性。

内容的提问来源于stack exchange,提问作者Martin Benjámin Muzsik

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.27 22:12:52