Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

a new algorithm #23

Merged
merged 1 commit into from
Oct 20, 2020
Merged

a new algorithm #23

merged 1 commit into from
Oct 20, 2020

Conversation

dalatant
Copy link
Contributor

@dalatant dalatant commented Nov 8, 2016

While doing my thesis on pitch detection, I came up with this utterly simple technique that boosts up autocorrelation performance. The algorithm you use right now has a 30% error rate while this one gives 3.7% tested over a 1500-sample database of 4 instruments that I collected from web.

It works quite well. It may seem unstable but it is only due to high resolution in cents. The low strings of an electric guitar have a natural ±0.3hz ≈ 5cents average fluctuation in their fundamental frequency during a pluck, according to spectrogram. So this is rather normal.

Thesis is in Greek but I am preparing a website with details in English. Thanks to the Web Audio API and this demo I also managed to translate this into a web app. Thanks for sharing.

@700software
Copy link

700software commented Nov 14, 2016

I do not think that @cwilso is actively accepting pull requests at this time, seeing that this previous pull request has not yet been accepted. Might I suggest you apply your pull requests here? That is, if your work is applicable, as I know that @markmarijnissen has made quite a few changes himself.

@mtree
Copy link

mtree commented Dec 4, 2016

Also, can you make it more descriptive?

@cwilso
Copy link
Owner

cwilso commented Dec 6, 2016 via email

@dalatant
Copy link
Contributor Author

dalatant commented Jan 4, 2017

Hello again! I've been really afk.

this is how I would tag the 4 steps
	// trim
	var r1=0, r2=SIZE-1, thres=0.2;
	for (var i=0; i<SIZE/2; i++)
		if (Math.abs(buf[i])<thres) { r1=i; break; }
	for (var i=1; i<SIZE/2; i++)
		if (Math.abs(buf[SIZE-i])<thres) { r2=SIZE-i; break; }

	buf = buf.slice(r1,r2);
	SIZE = buf.length;

	// autocorrelation
	var c = new Array(SIZE).fill(0);
	for (var i=0; i<SIZE; i++)
		for (var j=0; j<SIZE-i; j++)
			c[i] = c[i] + buf[j]*buf[j+i];

	// find first dip
	var d=0; while (c[d]>c[d+1]) d++;
	// find max
	var maxval=-1, maxpos=-1;
	for (var i=d; i<SIZE; i++) {
		if (c[i] > maxval) {
			maxval = c[i];
			maxpos = i;
		}
	}
	var T0 = maxpos;

	// interpolation
	var x1=c[T0-1], x2=c[T0], x3=c[T0+1];
	a = (x1 + x3 - 2*x2)/2;
	b = (x3 - x1)/2;
	if (a) T0 = T0 - b/(2*a);

trimming cuts the edges of the signal so that it starts and ends near zero. This is used to neutralize an inherent instability of the autocorrelation version I use.

autocorrelation is just autocorrelation. The original version.

find first dip / find max are self descriptive.

interpolation is parabolic interpolation. It helps with precision. We suppose that a parabola pass through the three points that comprise the peak. 'a' and 'b' are the unknowns from the linear equation system and b/(2a) is the "error" in the abscissa. Well x1,x2,x3 should be y1,y2,y3 because they are the ordinates.

I hope that helps.

@aworld1
Copy link

aworld1 commented Jan 5, 2017

I tried your version dalatant, but I had errors in two spots:

buf = buf.slice(r1,r2);

and

var c = new Array(SIZE).fill(0);

The errors were both "undefined is not a function" on my emulator, but when I ran it on chrome it worked perfectly fine. Why is it coming up with these errors? Thanks for reading!

@dalatant
Copy link
Contributor Author

dalatant commented Jan 5, 2017

@aworld1 fill() is a new javascript method that gained support the past 1-2 years. slice() is quite older, but as you figured out it is a matter of where you run it.

@aworld1
Copy link

aworld1 commented Jan 6, 2017

@dalatant The new algorithm is also very cluttered with for loops. It is impossible to run something in the background as six for loops are repeated over and over. Is there a way to condense the code, so everything runs faster and smoother, without interrupting the accuracy of the algorithm? I would greatly appreciate it!

@markmarijnissen
Copy link

markmarijnissen commented Jan 6, 2017 via email

@dalatant
Copy link
Contributor Author

dalatant commented Jan 7, 2017

Really where did you run it aworld1? I doubled the buffer because it was so fast in my pc that I couldn't read the indications, and I use an intel p4 that I bought in 2003. Condensing the code doesn't mean optimizing.

@aworld1
Copy link

aworld1 commented Jan 8, 2017

@dalatant I ran it on Intel XDK, with other code running at the same time, and it was very laggy when I spoke to the mic.

Edit: Oh my god. I edited buflen and everything worked. On every browser. Thanks @dalatant !!! I will use your algorithm.

@dalatant
Copy link
Contributor Author

dalatant commented Jan 8, 2017

Also keep in mind that it could work even with the big buffer. A big candidate for the lag is the way the algorithm is recurring inside the app.

@dalatant
Copy link
Contributor Author

I was so thrilled about this app but time passed. And I'm still thrilled! :p Check out my implementation

@fllprbt
Copy link

fllprbt commented Apr 30, 2018

@dalatant, thesis for? Just curious, my BSC's dissertation was in Greece and relevant to digital sound processing. Math or CS background?

@dalatant
Copy link
Contributor Author

dalatant commented May 2, 2018

@fllprbt I studied information and communication systems engineering in Samos, a 5yr school. Ye we had dsp and signal processing and all that painful stuff :).

@MysteryPancake
Copy link

This algorithm has given me far better results in all of my tests, especially for low notes. Thanks for this! I hope it gets committed at some point, because this is excellent!

@qnp
Copy link

qnp commented Oct 28, 2019

Just a naïve question:
Can't we just simply use a native ConvolverNode, as the autocorrelation can be expressed in terms of convolution. If we define ⦻ the correlation operator and * the convolution operator, we have:
Correlation vs Convolution:
Capture d’écran 2019-10-28 à 10 49 14
Autocorrelation:
Capture d’écran 2019-10-28 à 10 49 32

@dalatant
Copy link
Contributor Author

@qnp the only difference between correlation and convolution is that in convolution the 2nd signal is time-reversed. So if you convolve a signal with its reversed copy (as impulse response) that will be an autocorrelation.

I've never used ConvolverNode before, but if it can convolve two given signals then yes you can do it that way.

@bcardiff
Copy link

@dalatant thanks to this PR I am experiencing an improved detection on a side project of mine. Thanks x 💯** 💯!

@waterplea
Copy link

Any chance this PR gets merges, @cwilso? :) I have a web audio app and was looking into options to add Karaoke mode in it, googling pitch detection got me here.

@cwilso cwilso merged commit 4190bc7 into cwilso:master Oct 20, 2020
AlexanderEllis referenced this pull request in AlexanderEllis/blog May 7, 2022
@Meleeman01
Copy link

question, what sort of instruments did you use to test this?

@bcardiff
Copy link

I use it with an harmonica

@dalatant
Copy link
Contributor Author

@Meleeman01 Hmm nice question. Οnly an acoustic and an electric guitar. And also my voice. I actually don't have any other (musical) organ. Also good to know that it works on harmonica hehe.

@dalatant
Copy link
Contributor Author

Well I mean... when live testing the application...
If you talk about the recorded samples that I mentioned, that was: guitar, piano, clarinet and bassoon.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.