File size: 3,276 Bytes
e34aada
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
import numba as nb
import numpy as np


@nb.njit()
def find_nearest_stft_bin(f0_, freqs):
    freqs = np.expand_dims(freqs, 0)
    f0_ = np.expand_dims(f0_, 1)
    return np.abs(freqs - f0_).argmin()


@nb.njit()
def get_med_curve(f0, step_size=20):
    v_begin = -1
    v_end = -1
    x_med_curve = []
    y_med_curve = []
    T = len(f0)

    for i in range(T):
        if f0[i] >= 50 and i < T - 1:
            if v_begin == -1:
                v_begin = i
            v_end = i
        else:
            if v_end != -1:
                if v_end - v_begin > 3:
                    for j in range(v_begin, v_end + 1 - step_size, step_size):
                        frag_med = np.median(f0[j:j + step_size])
                        x_med_curve.append(j)
                        y_med_curve.append(frag_med)
                    x_med_curve.append(v_end)
                    y_med_curve.append(np.median(f0[v_end - step_size:v_end + 1]))
            v_end = v_begin = -1
    x_med_curve = [0] + x_med_curve + [T]
    x_med_curve = np.array(x_med_curve)
    y_med_curve = [y_med_curve[0]] + y_med_curve + [y_med_curve[-1]]
    y_med_curve = np.array(y_med_curve)
    return x_med_curve, y_med_curve


@nb.njit()
def clean_short_v_frag(f0):
    v_begin = -1
    T = len(f0)

    uv = np.zeros_like(f0).astype(np.bool_)
    for i in range(T):
        if f0[i] >= 1e-4 and i < T - 1:
            if v_begin == -1:
                v_begin = i
        else:
            if v_begin != -1:
                v_end = i if f0[i] >= 1e-4 else i - 1
                if v_end - v_begin + 1 < 3:
                    uv[v_begin:v_end + 1] = 1
            v_begin = -1
    return uv


@nb.njit()
def find_best_f0_using_har_energy(spec, pitches, freqs, hars, hars_mhalf, f0_min, f0_max):
    re = np.zeros_like(spec)
    T = len(spec)
    for i in range(T):
        spec_i = spec[i]
        for j, f0_j in enumerate(pitches[i]):
            if f0_j == 0 or f0_j < f0_min[i] or f0_j > f0_max[i]:
                continue
            mask = np.zeros((10000,))
            mask_mhalf = np.zeros((10000,))
            for mul in hars:
                b = find_nearest_stft_bin(np.array((f0_j * mul,)), freqs)
                for delta in range(-1, 2):
                    mask[b + delta] = 1
            for mul in hars_mhalf:
                b_mhalf = find_nearest_stft_bin(np.array((f0_j * (mul - 0.5),)), freqs)
                for delta in range(-1, 2):
                    mask_mhalf[b_mhalf + delta] = 1
            mask = mask[:len(spec_i)]
            mask_mhalf = mask_mhalf[:len(spec_i)]
            energy = (np.exp(spec_i) * mask).sum() / mask.sum()
            energy_mhalf = (np.exp(spec_i) * mask_mhalf).sum() / mask_mhalf.sum()
            re[i, j] = energy / energy_mhalf
    f0_2d_mask = 10000 * (re > 2) + 20000 * (re > 3) + np.expand_dims(np.arange(re.shape[1])[::-1], 0)
    f0_idx = np.zeros((T,), dtype=np.int_)
    for i in range(T):
        f0_idx[i] = f0_2d_mask[i].argmax()
    uv = re.sum(-1) == 0

    f0 = np.zeros((T,))
    for i in range(T):
        f0[i] = pitches[i, f0_idx[i]]
    f0 = f0 * (1 - uv)
    uv = clean_short_v_frag(f0)
    f0[uv] = 0
    x_med_curve, y_med_curve = get_med_curve(f0)
    re = re * (re > 1.5)
    return re, f0, x_med_curve, y_med_curve