History

python/demos/demo_fastyin_compare.py: comparison of different yin versions

Paul Brossier committed 9 years ago

Commit 45f4f7a1 parent 21eb4c21 file · +175 −0Browse files

Showing the changes to python (1 of 1 file). Show all

Unified · Split

python/demos/demo_fastyin_compare.py+175−0
@@ -0,0 +1,175 @@
1+#! /usr/bin/env python
2+# -*- coding: utf8 -*-
3+
4+""" Pure python implementation of the sum of squared difference
5+
6+ sqd_yin: original sum of squared difference [0]
7+ d_t(tau) = x ⊗ kernel
8+ sqd_yinfast: sum of squared diff using complex domain [0]
9+ sqd_yinfftslow: tappered squared diff [1]
10+ sqd_yinfft: modified squared diff using complex domain [1]
11+
12+[0]:http://audition.ens.fr/adc/pdf/2002_JASA_YIN.pdf
13+[1]:https://aubio.org/phd/
14+"""
15+
16+import sys
17+import numpy as np
18+import matplotlib.pyplot as plt
19+
20+def sqd_yin(samples):
21+ """ compute original sum of squared difference
22+
23+ Brute-force computation (cost o(N**2), slow)."""
24+ B = len(samples)
25+ W = B//2
26+ yin = np.zeros(W)
27+ for j in range(W):
28+ for tau in range(1, W):
29+ yin[tau] += (samples[j] - samples[j+tau])**2
30+ return yin
31+
32+def sqd_yinfast(samples):
33+ """ compute approximate sum of squared difference
34+
35+ Using complex convolution (fast, cost o(n*log(n)) )"""
36+ # yin_t(tau) = (r_t(0) + r_(t+tau)(0)) - 2r_t(tau)
37+ B = len(samples)
38+ W = B//2
39+ yin = np.zeros(W)
40+ sqdiff = np.zeros(W)
41+ kernel = np.zeros(B)
42+ # compute r_(t+tau)(0)
43+ squares = samples**2
44+ for tau in range(W):
45+ sqdiff[tau] = squares[tau:tau+W].sum()
46+ # add r_t(0)
47+ sqdiff += sqdiff[0]
48+ # compute r_t(tau) using kernel convolution in complex domain
49+ samples_fft = np.fft.fft(samples)
50+ kernel[1:W+1] = samples[W-1::-1] # first half, reversed
51+ kernel_fft = np.fft.fft(kernel)
52+ r_t_tau = np.fft.ifft(samples_fft * kernel_fft).real[W:]
53+ # compute yin_t(tau)
54+ yin = sqdiff - 2 * r_t_tau
55+ return yin
56+
57+def sqd_yintapered(samples):
58+ """ compute tappered sum of squared difference
59+
60+ Brute-force computation (cost o(N**2), slow)."""
61+ B = len(samples)
62+ W = B//2
63+ yin = np.zeros(W)
64+ for tau in range(1, W):
65+ for j in range(W - tau):
66+ yin[tau] += (samples[j] - samples[j+tau])**2
67+ return yin
68+
69+def sqd_yinfft(samples):
70+ """ compute yinfft modified sum of squared differences
71+
72+ Very fast, improved performance in transients.
73+
74+ FIXME: biased."""
75+ B = len(samples)
76+ W = B//2
77+ yin = np.zeros(W)
78+ def hanningz(W):
79+ return .5 * (1. - np.cos(2. * np.pi * np.arange(W) / W))
80+ #win = np.ones(B)
81+ win = hanningz(B)
82+ sqrmag = np.zeros(B)
83+ fftout = np.fft.fft(win*samples)
84+ sqrmag[0] = fftout[0].real**2
85+ for l in range(1, W):
86+ sqrmag[l] = fftout[l].real**2 + fftout[l].imag**2
87+ sqrmag[B-l] = sqrmag[l]
88+ sqrmag[W] = fftout[W].real**2
89+ fftout = np.fft.fft(sqrmag)
90+ sqrsum = 2.*sqrmag[:W + 1].sum()
91+ yin[0] = 0
92+ yin[1:] = sqrsum - fftout.real[1:W]
93+ return yin / B
94+
95+def cumdiff(yin):
96+ """ compute the cumulative mean normalized difference """
97+ W = len(yin)
98+ yin[0] = 1.
99+ cumsum = 0.
100+ for tau in range(1, W):
101+ cumsum += yin[tau]
102+ if cumsum != 0:
103+ yin[tau] *= tau/cumsum
104+ else:
105+ yin[tau] = 1
106+ return yin
107+
108+def compute_all(x):
109+ import time
110+ now = time.time()
111+
112+ yin = sqd_yin(x)
113+ t1 = time.time()
114+ print ("yin took %.2fms" % ((t1-now) * 1000.))
115+
116+ yinfast = sqd_yinfast(x)
117+ t2 = time.time()
118+ print ("yinfast took: %.2fms" % ((t2-t1) * 1000.))
119+
120+ yintapered = sqd_yintapered(x)
121+ t3 = time.time()
122+ print ("yintapered took: %.2fms" % ((t3-t2) * 1000.))
123+
124+ yinfft = sqd_yinfft(x)
125+ t4 = time.time()
126+ print ("yinfft took: %.2fms" % ((t4-t3) * 1000.))
127+
128+ return yin, yinfast, yintapered, yinfft
129+
130+def plot_all(yin, yinfast, yintapered, yinfft):
131+ fig, axes = plt.subplots(nrows=2, ncols=2, sharex=True, sharey='col')
132+
133+ axes[0, 0].plot(yin, label='yin')
134+ axes[0, 0].plot(yintapered, label='yintapered')
135+ axes[0, 0].set_ylim(bottom=0)
136+ axes[0, 0].legend()
137+ axes[1, 0].plot(yinfast, '-', label='yinfast')
138+ axes[1, 0].plot(yinfft, label='yinfft')
139+ axes[1, 0].legend()
140+
141+ axes[0, 1].plot(cumdiff(yin), label='yin')
142+ axes[0, 1].plot(cumdiff(yintapered), label='yin tapered')
143+ axes[0, 1].set_ylim(bottom=0)
144+ axes[0, 1].legend()
145+ axes[1, 1].plot(cumdiff(yinfast), '-', label='yinfast')
146+ axes[1, 1].plot(cumdiff(yinfft), label='yinfft')
147+ axes[1, 1].legend()
148+
149+ fig.tight_layout()
150+
151+testfreqs = [441., 800., 10000., 40.]
152+
153+if len(sys.argv) > 1:
154+ testfreqs = map(float,sys.argv[1:])
155+
156+for f in testfreqs:
157+ print ("Comparing yin implementations for sine wave at %.fHz" % f)
158+ samplerate = 44100.
159+ win_s = 4096
160+
161+ x = np.cos(2.*np.pi * np.arange(win_s) * f / samplerate)
162+
163+ n_times = 1#00
164+ for n in range(n_times):
165+ yin, yinfast, yinfftslow, yinfft = compute_all(x)
166+ if 0: # plot difference
167+ plt.plot(yin-yinfast)
168+ plt.tight_layout()
169+ plt.show()
170+ if 1:
171+ plt.plot(yinfftslow-yinfft)
172+ plt.tight_layout()
173+ plt.show()
174+ plot_all(yin, yinfast, yinfftslow, yinfft)
175+plt.show()

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t