import numpy as np
defalign_and_pad(
ref_f0, target_f0, max_shift: int = 5, rpa_threshold_cents: float = 50.0
):
"""Align and pad a target pitch sequence to match a reference in time. This function searches for the optimal frame shift that minimizes the logarithmic error between valid (voiced) segments of the two pitch sequences, compensating for algorithm-specific padding or windowing differences. The target is then shifted and padded with NaNs to match the exact length of the reference. Args: ref_f0 (np.ndarray): Reference pitch array in Hz. Unvoiced frames should be represented as np.nan or values <= 0. target_f0 (np.ndarray): Target pitch array to be aligned. max_shift (int, optional): Maximum number of frames to search for the optimal temporal discrepancy. Defaults to 5. rpa_threshold_cents (float, optional): Tolerance threshold in cents for calculating Raw Pitch Accuracy (RPA). Defaults to 50.0. Returns: tuple: A tuple containing: - aligned_f0 (np.ndarray): The corrected target pitch array, having the exact same length as `ref_f0`. - best_shift (int): The optimal shift amount in frames applied to the target. Positive means delayed. """
ref_len = len(ref_f0)
# 1. Calculate the cross-correlation in valid (non-NaN/0) intervals# to find the discrepancies.# (For simplicity, we find the shift that minimizes the absolute# error in frames where both values <200b><200b>exist.)
best_shift = 0
max_rpa = -1.0# RPA is maximizedfor shift inrange(-max_shift, max_shift + 1):
correct_frames = 0
valid_frames = 0for i, tgt_f0 inenumerate(target_f0):
ref_idx = i + shift
if0 <= ref_idx < ref_len:
v_ref = ref_f0[ref_idx]
v_tgt = tgt_f0
# Check if both are valid voiced framesif (
v_ref > 50and v_tgt > 50andnot np.isnan(v_ref)
andnot np.isnan(v_tgt)
):
valid_frames += 1# Eq (12): Calculate the deviation in the cent# region
diff_cents = np.abs(1200 * np.log2(v_ref / v_tgt))
# Eq (11): If it is within the threshold, it will be# counted as "correct answer (1)".if diff_cents < rpa_threshold_cents:
correct_frames += 1# RPA calculation (only if valid frames exist)if valid_frames > 0:
current_rpa = correct_frames / valid_frames
if current_rpa > max_rpa:
max_rpa = current_rpa
best_shift = shift
# Use the found `best_shift` to pad and even out the length.
aligned_f0 = np.full(ref_len, np.nan)
for i, tgt_f0 inenumerate(target_f0):
ref_idx = i + best_shift
if0 <= ref_idx < ref_len:
aligned_f0[ref_idx] = tgt_f0
# aligned_f0 will have exactly the same length as ref_f0 (the# missing part is padded with NaN)return aligned_f0, best_shift
第1項は観測誤差をL1ノルム(絶対誤差の和)で測っており,推定値を観測データの median に近づける効果がある.データの忠実性(fidelity)を評価しているとも見なせる.また第2項は正則化項であり,フレーム間におけるピッチの変化量を測りつつ,総和を取ることで,フレーム全体の総変動(total variation)を評価している.この項の導入により,系列の不要な振動を抑えて平坦にしつつ,急激な変化も許容する効果を持つ.