From 128c0a2597a7482fd6371d213acada6bafef3b0b Mon Sep 17 00:00:00 2001 From: juliatogr Date: Tue, 11 Jun 2024 10:04:35 +0200 Subject: [PATCH 01/14] configuration --- VERSION | 1 - .../tonal/harmonicproductspectrum.cpp | 79 ++++++++ .../tonal/harmonicproductspectrum.h | 106 +++++++++++ .../tonal/hps/vignesh_confidance.npy | Bin 0 -> 2224 bytes .../src/unittests/tonal/hps/vignesh_pitch.npy | Bin 0 -> 2224 bytes .../tonal/hps/vignesh_pitch_aubio.npy | Bin 0 -> 1120 bytes .../vignesh_pitch_aubio_with_tolerance.npy | Bin 0 -> 1120 bytes test/src/unittests/tonal/test_hps.py | 171 ++++++++++++++++++ 8 files changed, 356 insertions(+), 1 deletion(-) delete mode 100644 VERSION create mode 100644 src/algorithms/tonal/harmonicproductspectrum.cpp create mode 100644 src/algorithms/tonal/harmonicproductspectrum.h create mode 100644 test/src/unittests/tonal/hps/vignesh_confidance.npy create mode 100644 test/src/unittests/tonal/hps/vignesh_pitch.npy create mode 100644 test/src/unittests/tonal/hps/vignesh_pitch_aubio.npy create mode 100644 test/src/unittests/tonal/hps/vignesh_pitch_aubio_with_tolerance.npy create mode 100644 test/src/unittests/tonal/test_hps.py diff --git a/VERSION b/VERSION deleted file mode 100644 index a06e5954d..000000000 --- a/VERSION +++ /dev/null @@ -1 +0,0 @@ -2.1-beta6-dev diff --git a/src/algorithms/tonal/harmonicproductspectrum.cpp b/src/algorithms/tonal/harmonicproductspectrum.cpp new file mode 100644 index 000000000..d26e83d36 --- /dev/null +++ b/src/algorithms/tonal/harmonicproductspectrum.cpp @@ -0,0 +1,79 @@ +/* + * Copyright (C) 2006-2021 Music Technology Group - Universitat Pompeu Fabra + * + * This file is part of Essentia + * + * Essentia is free software: you can redistribute it and/or modify it under + * the terms of the GNU Affero General Public License as published by the Free + * Software Foundation (FSF), either version 3 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the Affero GNU General Public License + * version 3 along with this program. If not, see http://www.gnu.org/licenses/ + */ + +#include "harmonicproductspectrum.h" +#include "essentiamath.h" +#include + +using namespace std; +using namespace essentia; +using namespace standard; + +const char* HarmonicProductSpectrum::name = "HarmonicProductSpectrum"; +const char* HarmonicProductSpectrum::category = "Pitch"; +const char* HarmonicProductSpectrum::description = DOC("This algorithm estimates the fundamental frequency given the spectrum of a monophonic music signal. It is an implementation of YinFFT algorithm [1], which is an optimized version of Yin algorithm for computation in the frequency domain. It is recommended to window the input spectrum with a Hann window. The raw spectrum can be computed with the Spectrum algorithm.\n" +"\n" +"An exception is thrown if an empty spectrum is provided.\n" +"\n" +"Please note that if \"pitchConfidence\" is zero, \"pitch\" is undefined and should not be used for other algorithms.\n" +"Also note that a null \"pitch\" is never ouput by the algorithm and that \"pitchConfidence\" must always be checked out.\n" +"\n" +"References:\n" +" [1] P. M. Brossier, \"Automatic Annotation of Musical Audio for Interactive\n" +" Applications,” QMUL, London, UK, 2007.\n\n" +" [2] Pitch detection algorithm - Wikipedia, the free encyclopedia\n" +" http://en.wikipedia.org/wiki/Pitch_detection_algorithm"); + +void HarmonicProductSpectrum::configure() { + // compute buffer sizes + _frameSize = parameter("frameSize").toInt(); + _sampleRate = parameter("sampleRate").toReal(); + + + _tauMax = min(int(ceil(_sampleRate / parameter("minFrequency").toReal())), _frameSize/2); + _tauMin = min(int(floor(_sampleRate / parameter("maxFrequency").toReal())), _frameSize/2); + + if (_tauMax <= _tauMin) { + throw EssentiaException("HarmonicProductSpectrum: maxFrequency is lower than minFrequency, or they are too close, or they are out of the interval of detectable frequencies with respect to the specified frameSize. Minimum detectable frequency is ", _sampleRate / (_frameSize/2), " Hz"); + } + + // configure peak detection algorithm + _peakDetect->configure("range", _frameSize/2+1, + "maxPeaks", 1, + "minPosition", _tauMin, + "maxPosition", _tauMax, + "orderBy", "amplitude"); +} + +void HarmonicProductSpectrum::compute() { + const vector& spectrum = _spectrum.get(); + if (spectrum.empty()) { + throw EssentiaException("HarmonicProductSpectrum: Cannot compute pitch detection on empty spectrum."); + } + Real& pitch = _pitch.get(); + Real& pitchConfidence = _pitchConfidence.get(); + + if ((int)spectrum.size() != _frameSize/2+1) {//_sqrMag.size()/2+1) { + Algorithm::configure( "frameSize", int(2*(spectrum.size()-1)) ); + } + + pitch = 0.0; + pitchConfidence = 0.0; + +} diff --git a/src/algorithms/tonal/harmonicproductspectrum.h b/src/algorithms/tonal/harmonicproductspectrum.h new file mode 100644 index 000000000..1064666ff --- /dev/null +++ b/src/algorithms/tonal/harmonicproductspectrum.h @@ -0,0 +1,106 @@ +/* + * Copyright (C) 2006-2021 Music Technology Group - Universitat Pompeu Fabra + * + * This file is part of Essentia + * + * Essentia is free software: you can redistribute it and/or modify it under + * the terms of the GNU Affero General Public License as published by the Free + * Software Foundation (FSF), either version 3 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more + * details. + * + * You should have received a copy of the Affero GNU General Public License + * version 3 along with this program. If not, see http://www.gnu.org/licenses/ + */ + +/* + * This file is a port of the file harmonicproductspectrum.h from aubio, + * http://aubio.piem.org/, in its version 0.3.2. + * + * The port was written by the author of aubio, Paul Brossier + * . + */ + +#ifndef ESSENTIA_HARMONICPRODUCTSPECTRUM_H +#define ESSENTIA_HARMONICPRODUCTSPECTRUM_H + +#include "algorithmfactory.h" + +namespace essentia { +namespace standard { + +class HarmonicProductSpectrum : public Algorithm { + + private: + Input > _spectrum; + Output _pitch; + Output _pitchConfidence; + + Algorithm* _peakDetect; + + Real _sampleRate; /** sampling rate of the audio signal */ + int _frameSize; + int _tauMin; + int _tauMax; + + public: + HarmonicProductSpectrum() { + declareInput(_spectrum, "spectrum", "the input spectrum (preferably created with a hann window)"); + declareOutput(_pitch, "pitch", "detected pitch [Hz]"); + declareOutput(_pitchConfidence, "pitchConfidence", "confidence with which the pitch was detected [0,1]"); + + _peakDetect = AlgorithmFactory::create("PeakDetection"); + } + + ~HarmonicProductSpectrum() { + delete _peakDetect; + }; + + void declareParameters() { + declareParameter("frameSize", "number of samples in the input spectrum", "[2,inf)", 2048); + declareParameter("sampleRate", "sampling rate of the input spectrum [Hz]", "(0,inf)", 44100.); + declareParameter("minFrequency", "the minimum allowed frequency [Hz]", "(0,inf)", 20.0); + declareParameter("maxFrequency", "the maximum allowed frequency [Hz]", "(0,inf)", 22050.0); + } + + void configure(); + void compute(); + + static const char* name; + static const char* category; + static const char* description; + +}; // class HarmonicProductSpectrum + +} // namespace standard +} // namespace essentia + + +#include "streamingalgorithmwrapper.h" + +namespace essentia { +namespace streaming { + +class HarmonicProductSpectrum : public StreamingAlgorithmWrapper { + + protected: + Sink > _spectrum; + Source _pitch; + Source _pitchConfidence; + + public: + HarmonicProductSpectrum() { + declareAlgorithm("HarmonicProductSpectrum"); + declareInput(_spectrum, TOKEN, "spectrum"); + declareOutput(_pitch, TOKEN, "pitch"); + } +}; + +} // namespace streaming +} // namespace essentia + +#endif // ESSENTIA_HARMONICPRODUCTSPECTRUM_H diff --git a/test/src/unittests/tonal/hps/vignesh_confidance.npy b/test/src/unittests/tonal/hps/vignesh_confidance.npy new file mode 100644 index 0000000000000000000000000000000000000000..e823db71c02c7ee7d9132a128eedbd485d289da2 GIT binary patch literal 2224 zcmX|?dr(zX6vlUkibaJ(BlN(IQeI37l$1v(+rnE;Aq@C{fAk<+#?S#oO7f8#^8r(0 z%7l&<9Z?(1j4>@8)7&NoVS*9@7Rgae1Ve>NouG{SeN6YC-+XJWz4luBti5mXj9Ibq z$@dATSnf!3ElJIBL>i8$^l(Rz;YiQQ$<0a0oR^i8=5pJeDT|i4`t3^=q-4ALb>M_a z;Xz}AjFra!zXya6>h~%2(QYQbbsF4JTIHthnD864QF6sibMVcp(7qAv;FNDbEA%r^ zbQ}PUlC7ZbY9rqXNYx$D8xDJ#Br$Bp01!yM!ME~@9^6?Awm))d)VlDNQUqPAlGpNp1 zf_g2roO71;<%|;{tXppw*th8x;}!Fp1+34k{Tp_(=POX`y8+sJ_kq%S5bE;Xhpn8dV{P6ZJQ+UJSaQdD62% zyY&)iHAK?yT*i7PuY)#MUgP)Q)C=m#BaqKXe3Es{eVX~-O#tQWk7Hmv! zqPF;>F8N|LsK@PQy`EdC<6}T$g8;3DgIKR$=tey{ED2gwCz3S+G(+EDTw??%^Jjy` zpj1!=Ob1QVpZ?a7w7X(KksnDtA|14s<h5iz9(|q+VZFN3ll@e6 zGA@?!>P!)|I{yl2HG5HKG_$YuJb!5#&?>_V6dAl{cG5wdzgQT^@9~}&=EUAJ=Q6(k z&I;R2?Fa(xNrAMV`Jl~hmynmxzVP6{qiMuoPFGa7VE}?c6l7)R2}b$ z&WvS!fuk@dR%S8h<)dxnv6G3-#*fHTskZV=lVmM7%fr{SO)Ax6VW#^6O0OqnEx$j(m{& zE;`KftmQpbQ30sSa`1kbrK6$s_ZL~eUn(dY`5s!q5m;}Q{Y#FFMP4=0XlDCdjWmVtZzl%n+D#$y!FTL%Zu5D1yDwuTkj>no$l=&UlqNJPg#SV?c2# zn0j0^XiXb<2WyuiznB}xeInmRel_X`?suF27s@V!mI*EN7x0}GVQsXR4ZO4U-y)Cl z31!{`Cqd(gMw}RZ57ZXtPWN4bwukb6M33dWZ61CIb*kL!TxShu{TrVGZJ)=;LTWkW z4&vmGd^auUZR(Znpza!kc-i$Qw0fE6raXo*PYKUcFY|y_1>Ye~e8Kb9UA#Bexo2QE z{%J#Ba(*8B8?pfZ1A5Ig{vUik7*svIm@{+#BCGy)mfd|< GqW=RujLZE1 literal 0 HcmV?d00001 diff --git a/test/src/unittests/tonal/hps/vignesh_pitch.npy b/test/src/unittests/tonal/hps/vignesh_pitch.npy new file mode 100644 index 0000000000000000000000000000000000000000..9734e445eb5e9bab6890d93e8a49d153c8ad0a25 GIT binary patch literal 2224 zcmX}teN0zX7zgkJBrM1RN2XG8WZ4fDMRYdpMUTx`{ScucFTN}f!cR~L1dLEIE?75u79Wv_-Z zi>E4~6)d7>3sGthHpiz;`cNE*%6vt5A_uGp~JQywjMX2{I(n# zztuo{+Fo9t%6|2D))5h`W1jw=>vkQ6W=0cK*&R?{j>P*jGB!gsHag3BpUSJ7epmjE z6U)fpTt~ZO%2G$8;sUe}gycD~STL>BQT13ks@n&eIudn^yrVf-e<}QhH~kLHNynfLyUlo17qqT&y$&*PpU$(Ol_Xi;y#wl^73}}Ue)M5o zJIsDs>Y=gWE2w`df%XYEl$QoU)m(-9ts$MLXE#rx=||c3KkVQ5XgXrqIgyHQ`1&;@-ER`5+3avJ~c5tYLoV*8aE!>fmww4%{=LygwXu&4D+e z`eX;^>xgd|>bK3HE}mRWC!-$Fk|axl)H`C1y!Vb&KsmvcFs{bsO+Joy7O4mfynu%=!{&snO8x>UU@p_kVXI`ZYKDv(KLfpsq@Ar$F)M zEIKX@%9eP>y$MhyP#N(uuRrGvbwe%Qk8VtXMi0O9+P8lm?#DA9Fo)~1i=eFLch(Hx z_h0Wh!|P*?Lp8<+_3iGzxc>NQX!@Ok#!+gQH!~l25sEJ_@Vf6gs8YM29psC8)|Dab zd&^MPzwS>*J>-4G{p&kg5R1f1&|GpGsymOEZ{>X|DhT(>wEjAUyxO^gKZY$4DN0;`No?9lzi2e%y%QV~gbun69L8+_4mFvhZb>-Xs?bC8g%5A>ByfD{k^W_K76@=Om`}NhrSC|EWZ4mc%%%gi|lkR3ou^qRfW9GLb*a zJp4gMQ!R6NluS#f#BawW7Wm7otC4y6s?4sZGJ8fSq>WNAL@R_fDfrpUTzaD7x@hH| zSV3acDmpIYaQpE_s;XEhGOduA^3VQBQ?Q%^(=_m7hlJeFojIr95fM$ zv^E*=49a+WReBa!F|J%nzIG`?{T4Q6s&pjD+zm8Q)|ke^_*90q0b)|m8;F5%qV3yh z94?Dwc76m8QX)u>j%5FNf8ibW7x(6l7pgZun_K)DF^;cTzri2hEqg z7y^UDjY%Qmm#|C*7Fjtu&%&nu3|fXWc)ZktwberJF$;4}Sh(6?A^LU(Lwi-$JWxmq zkg;T?Q@z&21RonMJ~r-a()qG8ofTUoF8C=}B{KsKGe3r#IekWRRvMV(?9tK3}pWLU=eoCa%+1|O$}H#TWe)o2N7(BeqY;+)oSbyUNoLd$P+ zT2eku<-|^b<|2V^e}O?)z4ilv-17qKoC1<7PrNJ8l`Zg~LcpRDXloGI^_WOqk4VV( zA~(Jg@u?L#=^@fNAn?nOK%%F}o(7QzrbJqAi5y)ak?bLn5+)JYCgEn(@$F3+>$rid z>K(*}?qq5ICDhb5GIi0;!#HU5eVM2k2VTAViGH=2h^l%f-m1khXrsK!%CGCqv{OP} zW)Z!zi8p5o`SOQC29KIJ^K>!OA!QUVm>GMog0~y$QQv5y#%5>bh#mJA?Tnpk=1ON1 z?#4!(m-caU?Ot~8vGLla3T&fh0>m<6&Xi#JsF+);idnQ2Q8jJig3ZLHBPNpKi-<84 zGn-q2Dy@`idl}vP%#6=jSiEiJp4lqaL|1dgQcXv%jq&r99Ef%O-DD=LrIfQ46RYQQ z>F;|Ma-ZS#G6VN-2~>wh2B`m2<4!a>gPAxL%!aigd>#}^TKqZ=cC264ZE3Rml$YAR z_aSx5fh4?gwo`Z^h4)`cWx*lv-S4Sv8P?E{kWAXKBtj)m^*fm>|Hl*K9e{DzAHR!! zxcm6=@oirk?|lMo%o;M35%>hh{>y2f6>*Hoj3ye;WTjJ}NN0XR&r2CHeQ_dl-dZXS zCbKmb}F@{S{`wEqtoS$Yd)!b zIFQPY{Q_URNf-njGiDv%1nD??PGaDogz1pPwO)zLA&K5EBtBcEqoZAiZ=B556EbZh zGKXw3jczg}%{uI{IwrU4SnSc^;Un|-E}8qPW&Tc-neEcipD$78@|oPH#p0d9AIH^P zKE92t`2@0m-b`*mG!?;7uKOYx%a7!;XB5X8quBdbB(W(^QuU~c=RK9=lq Date: Tue, 11 Jun 2024 11:43:25 +0200 Subject: [PATCH 02/14] Version 1 --- .../tonal/harmonicproductspectrum.cpp | 35 +++++++++++++++++-- .../tonal/harmonicproductspectrum.h | 6 ++++ test/src/unittests/tonal/test_hps.py | 24 ------------- 3 files changed, 38 insertions(+), 27 deletions(-) diff --git a/src/algorithms/tonal/harmonicproductspectrum.cpp b/src/algorithms/tonal/harmonicproductspectrum.cpp index d26e83d36..16188546c 100644 --- a/src/algorithms/tonal/harmonicproductspectrum.cpp +++ b/src/algorithms/tonal/harmonicproductspectrum.cpp @@ -44,6 +44,8 @@ void HarmonicProductSpectrum::configure() { // compute buffer sizes _frameSize = parameter("frameSize").toInt(); _sampleRate = parameter("sampleRate").toReal(); + _numHarmonics = parameter("numHarmonics").toInt(); + _magnitudeThreshold = parameter("magnitudeThreshold").toReal(); _tauMax = min(int(ceil(_sampleRate / parameter("minFrequency").toReal())), _frameSize/2); @@ -55,7 +57,6 @@ void HarmonicProductSpectrum::configure() { // configure peak detection algorithm _peakDetect->configure("range", _frameSize/2+1, - "maxPeaks", 1, "minPosition", _tauMin, "maxPosition", _tauMax, "orderBy", "amplitude"); @@ -73,7 +74,35 @@ void HarmonicProductSpectrum::compute() { Algorithm::configure( "frameSize", int(2*(spectrum.size()-1)) ); } - pitch = 0.0; - pitchConfidence = 0.0; + vector hps(spectrum); + for (int h=2; h < _numHarmonics; h++) { + + vector downsampled(spectrum.size()/h, 1.0); + for (int bin=0; bin < downsampled.size(); bin++) { + downsampled[bin] = spectrum[bin*h]; + } + for (int bin=0; bin < downsampled.size(); bin++) { + hps[bin] *= downsampled[bin]; + } + } + + for (int bin=hps.size()/_numHarmonics; bin < hps.size(); bin++) { + hps[bin] = 0; + } + vector _positions; + vector _amplitudes; + + _peakDetect->input("array").set(hps); + _peakDetect->output("positions").set(_positions); + _peakDetect->output("amplitudes").set(_amplitudes); + _peakDetect->compute(); + + if (_positions.size() == 0) { + pitch = 0.0; + pitchConfidence = 0.0; + } else { + pitch = _positions[0] * _sampleRate / _frameSize; + pitchConfidence = 1.0; + } } diff --git a/src/algorithms/tonal/harmonicproductspectrum.h b/src/algorithms/tonal/harmonicproductspectrum.h index 1064666ff..058ce86f7 100644 --- a/src/algorithms/tonal/harmonicproductspectrum.h +++ b/src/algorithms/tonal/harmonicproductspectrum.h @@ -42,10 +42,14 @@ class HarmonicProductSpectrum : public Algorithm { Algorithm* _peakDetect; + std::vector _positions; /** peak positions */ + std::vector _amplitudes; /** peak amplitudes */ Real _sampleRate; /** sampling rate of the audio signal */ int _frameSize; int _tauMin; int _tauMax; + int _numHarmonics; + Real _magnitudeThreshold; public: HarmonicProductSpectrum() { @@ -65,6 +69,8 @@ class HarmonicProductSpectrum : public Algorithm { declareParameter("sampleRate", "sampling rate of the input spectrum [Hz]", "(0,inf)", 44100.); declareParameter("minFrequency", "the minimum allowed frequency [Hz]", "(0,inf)", 20.0); declareParameter("maxFrequency", "the maximum allowed frequency [Hz]", "(0,inf)", 22050.0); + declareParameter("numHarmonics", "number of harmonics to consider", "[1,inf)", 5); + declareParameter("magnitudeThreshold", "threshold ratio for the amplitude filtering [0,1]", "[0,1]", 0.2); } void configure(); diff --git a/test/src/unittests/tonal/test_hps.py b/test/src/unittests/tonal/test_hps.py index 60a445748..00f4c049e 100644 --- a/test/src/unittests/tonal/test_hps.py +++ b/test/src/unittests/tonal/test_hps.py @@ -140,30 +140,6 @@ def testARealCase(self): self.assertAlmostEqualVector(pitch, expected_pitch) self.assertAlmostEqualVector(confidence, expected_conf, 5e-5) - def testARealCaseAubioComparison(self): - # Compare with the results obtained with the aubio YinFFT - # implementation of the YinFFT algorithm - # https://aubio.org/ - - frameSize = 4096 - sr = 44100 - hopSize = 512 - filename = join(testdata.audio_dir, 'recorded', 'vignesh.wav') - audio = MonoLoader(filename=filename, sampleRate=44100)() - frames = FrameGenerator(audio, frameSize=frameSize, hopSize=hopSize, startFromZero=True) - win = Windowing(normalized=False, zeroPhase=False) - spec = Spectrum() - pitchDetect = HarmonicProductSpectrum(frameSize=frameSize, sampleRate=sr) - - pitch = array([pitchDetect(spec(win(frame)))[0] for frame in frames]) - - expected_pitch = numpy.load(join(filedir(), 'hps/vignesh_pitch_aubio.npy')) - - # Trim the first and last frames as the - # system behavior is unestable. - pitch = pitch[8:-5] - expected_pitch = expected_pitch[8:-5] - suite = allTests(TestHarmonicProductSpectrum) From d70d978705e3d738b245c6a0b13a33102f6a24b0 Mon Sep 17 00:00:00 2001 From: juliatogr Date: Tue, 11 Jun 2024 15:01:53 +0200 Subject: [PATCH 03/14] clean real cases tests --- .../tonal/hps/vignesh_confidance.npy | Bin 2224 -> 0 bytes .../src/unittests/tonal/hps/vignesh_pitch.npy | Bin 2224 -> 0 bytes .../tonal/hps/vignesh_pitch_aubio.npy | Bin 1120 -> 0 bytes .../vignesh_pitch_aubio_with_tolerance.npy | Bin 1120 -> 0 bytes test/src/unittests/tonal/test_hps.py | 30 ------------------ 5 files changed, 30 deletions(-) delete mode 100644 test/src/unittests/tonal/hps/vignesh_confidance.npy delete mode 100644 test/src/unittests/tonal/hps/vignesh_pitch.npy delete mode 100644 test/src/unittests/tonal/hps/vignesh_pitch_aubio.npy delete mode 100644 test/src/unittests/tonal/hps/vignesh_pitch_aubio_with_tolerance.npy diff --git a/test/src/unittests/tonal/hps/vignesh_confidance.npy b/test/src/unittests/tonal/hps/vignesh_confidance.npy deleted file mode 100644 index e823db71c02c7ee7d9132a128eedbd485d289da2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2224 zcmX|?dr(zX6vlUkibaJ(BlN(IQeI37l$1v(+rnE;Aq@C{fAk<+#?S#oO7f8#^8r(0 z%7l&<9Z?(1j4>@8)7&NoVS*9@7Rgae1Ve>NouG{SeN6YC-+XJWz4luBti5mXj9Ibq z$@dATSnf!3ElJIBL>i8$^l(Rz;YiQQ$<0a0oR^i8=5pJeDT|i4`t3^=q-4ALb>M_a z;Xz}AjFra!zXya6>h~%2(QYQbbsF4JTIHthnD864QF6sibMVcp(7qAv;FNDbEA%r^ zbQ}PUlC7ZbY9rqXNYx$D8xDJ#Br$Bp01!yM!ME~@9^6?Awm))d)VlDNQUqPAlGpNp1 zf_g2roO71;<%|;{tXppw*th8x;}!Fp1+34k{Tp_(=POX`y8+sJ_kq%S5bE;Xhpn8dV{P6ZJQ+UJSaQdD62% zyY&)iHAK?yT*i7PuY)#MUgP)Q)C=m#BaqKXe3Es{eVX~-O#tQWk7Hmv! zqPF;>F8N|LsK@PQy`EdC<6}T$g8;3DgIKR$=tey{ED2gwCz3S+G(+EDTw??%^Jjy` zpj1!=Ob1QVpZ?a7w7X(KksnDtA|14s<h5iz9(|q+VZFN3ll@e6 zGA@?!>P!)|I{yl2HG5HKG_$YuJb!5#&?>_V6dAl{cG5wdzgQT^@9~}&=EUAJ=Q6(k z&I;R2?Fa(xNrAMV`Jl~hmynmxzVP6{qiMuoPFGa7VE}?c6l7)R2}b$ z&WvS!fuk@dR%S8h<)dxnv6G3-#*fHTskZV=lVmM7%fr{SO)Ax6VW#^6O0OqnEx$j(m{& zE;`KftmQpbQ30sSa`1kbrK6$s_ZL~eUn(dY`5s!q5m;}Q{Y#FFMP4=0XlDCdjWmVtZzl%n+D#$y!FTL%Zu5D1yDwuTkj>no$l=&UlqNJPg#SV?c2# zn0j0^XiXb<2WyuiznB}xeInmRel_X`?suF27s@V!mI*EN7x0}GVQsXR4ZO4U-y)Cl z31!{`Cqd(gMw}RZ57ZXtPWN4bwukb6M33dWZ61CIb*kL!TxShu{TrVGZJ)=;LTWkW z4&vmGd^auUZR(Znpza!kc-i$Qw0fE6raXo*PYKUcFY|y_1>Ye~e8Kb9UA#Bexo2QE z{%J#Ba(*8B8?pfZ1A5Ig{vUik7*svIm@{+#BCGy)mfd|< GqW=RujLZE1 diff --git a/test/src/unittests/tonal/hps/vignesh_pitch.npy b/test/src/unittests/tonal/hps/vignesh_pitch.npy deleted file mode 100644 index 9734e445eb5e9bab6890d93e8a49d153c8ad0a25..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2224 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 Date: Tue, 11 Jun 2024 15:27:56 +0200 Subject: [PATCH 04/14] fix description --- src/algorithms/tonal/harmonicproductspectrum.cpp | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/src/algorithms/tonal/harmonicproductspectrum.cpp b/src/algorithms/tonal/harmonicproductspectrum.cpp index 16188546c..d5b52e593 100644 --- a/src/algorithms/tonal/harmonicproductspectrum.cpp +++ b/src/algorithms/tonal/harmonicproductspectrum.cpp @@ -27,18 +27,17 @@ using namespace standard; const char* HarmonicProductSpectrum::name = "HarmonicProductSpectrum"; const char* HarmonicProductSpectrum::category = "Pitch"; -const char* HarmonicProductSpectrum::description = DOC("This algorithm estimates the fundamental frequency given the spectrum of a monophonic music signal. It is an implementation of YinFFT algorithm [1], which is an optimized version of Yin algorithm for computation in the frequency domain. It is recommended to window the input spectrum with a Hann window. The raw spectrum can be computed with the Spectrum algorithm.\n" +const char* HarmonicProductSpectrum::description = DOC("This algorithm estimates the fundamental frequency given the spectrum of a monophonic music signal. It is an implementation of Harmonic Product Spectrum algorithm [1], computed in the frequency-domain. It is recommended to window the input spectrum with a Hann window. The raw spectrum can be computed with the Spectrum algorithm.\n" "\n" "An exception is thrown if an empty spectrum is provided.\n" "\n" -"Please note that if \"pitchConfidence\" is zero, \"pitch\" is undefined and should not be used for other algorithms.\n" -"Also note that a null \"pitch\" is never ouput by the algorithm and that \"pitchConfidence\" must always be checked out.\n" +"Note that a null “pitch” is never output by the algorithm.\n" "\n" "References:\n" -" [1] P. M. Brossier, \"Automatic Annotation of Musical Audio for Interactive\n" -" Applications,” QMUL, London, UK, 2007.\n\n" -" [2] Pitch detection algorithm - Wikipedia, the free encyclopedia\n" -" http://en.wikipedia.org/wiki/Pitch_detection_algorithm"); +" [1] Noll, A. M. (1970). Pitch Determination of Human Speech by the\n" +" Harmonic Product Spectrum, the Harmonic Sum Spectrum, and a Maximum\n" +" Likelihood Estimate. Symposium on Computer Processing in Communication,\n" +" Ed., 19, 779–797."); void HarmonicProductSpectrum::configure() { // compute buffer sizes From bf4e75f0fb662d60ba82e5ee79b6922bf3fc723e Mon Sep 17 00:00:00 2001 From: juliatogr Date: Wed, 12 Jun 2024 11:24:31 +0200 Subject: [PATCH 05/14] rename to pitchhps --- ...rmonicproductspectrum.cpp => pitchhps.cpp} | 16 +++++------ .../{harmonicproductspectrum.h => pitchhps.h} | 28 +++++++------------ .../tonal/{test_hps.py => test_pitchhps.py} | 14 +++++----- 3 files changed, 25 insertions(+), 33 deletions(-) rename src/algorithms/tonal/{harmonicproductspectrum.cpp => pitchhps.cpp} (75%) rename src/algorithms/tonal/{harmonicproductspectrum.h => pitchhps.h} (81%) rename test/src/unittests/tonal/{test_hps.py => test_pitchhps.py} (87%) diff --git a/src/algorithms/tonal/harmonicproductspectrum.cpp b/src/algorithms/tonal/pitchhps.cpp similarity index 75% rename from src/algorithms/tonal/harmonicproductspectrum.cpp rename to src/algorithms/tonal/pitchhps.cpp index d5b52e593..fb48d41f3 100644 --- a/src/algorithms/tonal/harmonicproductspectrum.cpp +++ b/src/algorithms/tonal/pitchhps.cpp @@ -17,7 +17,7 @@ * version 3 along with this program. If not, see http://www.gnu.org/licenses/ */ -#include "harmonicproductspectrum.h" +#include "pitchhps.h" #include "essentiamath.h" #include @@ -25,9 +25,9 @@ using namespace std; using namespace essentia; using namespace standard; -const char* HarmonicProductSpectrum::name = "HarmonicProductSpectrum"; -const char* HarmonicProductSpectrum::category = "Pitch"; -const char* HarmonicProductSpectrum::description = DOC("This algorithm estimates the fundamental frequency given the spectrum of a monophonic music signal. It is an implementation of Harmonic Product Spectrum algorithm [1], computed in the frequency-domain. It is recommended to window the input spectrum with a Hann window. The raw spectrum can be computed with the Spectrum algorithm.\n" +const char* PitchHPS::name = "PitchHPS"; +const char* PitchHPS::category = "Pitch"; +const char* PitchHPS::description = DOC("This algorithm estimates the fundamental frequency given the spectrum of a monophonic music signal. It is an implementation of Harmonic Product Spectrum algorithm [1], computed in the frequency-domain. It is recommended to window the input spectrum with a Hann window. The raw spectrum can be computed with the Spectrum algorithm.\n" "\n" "An exception is thrown if an empty spectrum is provided.\n" "\n" @@ -39,7 +39,7 @@ const char* HarmonicProductSpectrum::description = DOC("This algorithm estimates " Likelihood Estimate. Symposium on Computer Processing in Communication,\n" " Ed., 19, 779–797."); -void HarmonicProductSpectrum::configure() { +void PitchHPS::configure() { // compute buffer sizes _frameSize = parameter("frameSize").toInt(); _sampleRate = parameter("sampleRate").toReal(); @@ -51,7 +51,7 @@ void HarmonicProductSpectrum::configure() { _tauMin = min(int(floor(_sampleRate / parameter("maxFrequency").toReal())), _frameSize/2); if (_tauMax <= _tauMin) { - throw EssentiaException("HarmonicProductSpectrum: maxFrequency is lower than minFrequency, or they are too close, or they are out of the interval of detectable frequencies with respect to the specified frameSize. Minimum detectable frequency is ", _sampleRate / (_frameSize/2), " Hz"); + throw EssentiaException("PitchHPS: maxFrequency is lower than minFrequency, or they are too close, or they are out of the interval of detectable frequencies with respect to the specified frameSize. Minimum detectable frequency is ", _sampleRate / (_frameSize/2), " Hz"); } // configure peak detection algorithm @@ -61,10 +61,10 @@ void HarmonicProductSpectrum::configure() { "orderBy", "amplitude"); } -void HarmonicProductSpectrum::compute() { +void PitchHPS::compute() { const vector& spectrum = _spectrum.get(); if (spectrum.empty()) { - throw EssentiaException("HarmonicProductSpectrum: Cannot compute pitch detection on empty spectrum."); + throw EssentiaException("PitchHPS: Cannot compute pitch detection on empty spectrum."); } Real& pitch = _pitch.get(); Real& pitchConfidence = _pitchConfidence.get(); diff --git a/src/algorithms/tonal/harmonicproductspectrum.h b/src/algorithms/tonal/pitchhps.h similarity index 81% rename from src/algorithms/tonal/harmonicproductspectrum.h rename to src/algorithms/tonal/pitchhps.h index 058ce86f7..a732e0f16 100644 --- a/src/algorithms/tonal/harmonicproductspectrum.h +++ b/src/algorithms/tonal/pitchhps.h @@ -17,23 +17,15 @@ * version 3 along with this program. If not, see http://www.gnu.org/licenses/ */ -/* - * This file is a port of the file harmonicproductspectrum.h from aubio, - * http://aubio.piem.org/, in its version 0.3.2. - * - * The port was written by the author of aubio, Paul Brossier - * . - */ - -#ifndef ESSENTIA_HARMONICPRODUCTSPECTRUM_H -#define ESSENTIA_HARMONICPRODUCTSPECTRUM_H +#ifndef ESSENTIA_PITCHHPS_H +#define ESSENTIA_PITCHHPS_H #include "algorithmfactory.h" namespace essentia { namespace standard { -class HarmonicProductSpectrum : public Algorithm { +class PitchHPS : public Algorithm { private: Input > _spectrum; @@ -52,7 +44,7 @@ class HarmonicProductSpectrum : public Algorithm { Real _magnitudeThreshold; public: - HarmonicProductSpectrum() { + PitchHPS() { declareInput(_spectrum, "spectrum", "the input spectrum (preferably created with a hann window)"); declareOutput(_pitch, "pitch", "detected pitch [Hz]"); declareOutput(_pitchConfidence, "pitchConfidence", "confidence with which the pitch was detected [0,1]"); @@ -60,7 +52,7 @@ class HarmonicProductSpectrum : public Algorithm { _peakDetect = AlgorithmFactory::create("PeakDetection"); } - ~HarmonicProductSpectrum() { + ~PitchHPS() { delete _peakDetect; }; @@ -80,7 +72,7 @@ class HarmonicProductSpectrum : public Algorithm { static const char* category; static const char* description; -}; // class HarmonicProductSpectrum +}; // class PitchHPS } // namespace standard } // namespace essentia @@ -91,7 +83,7 @@ class HarmonicProductSpectrum : public Algorithm { namespace essentia { namespace streaming { -class HarmonicProductSpectrum : public StreamingAlgorithmWrapper { +class PitchHPS : public StreamingAlgorithmWrapper { protected: Sink > _spectrum; @@ -99,8 +91,8 @@ class HarmonicProductSpectrum : public StreamingAlgorithmWrapper { Source _pitchConfidence; public: - HarmonicProductSpectrum() { - declareAlgorithm("HarmonicProductSpectrum"); + PitchHPS() { + declareAlgorithm("PitchHPS"); declareInput(_spectrum, TOKEN, "spectrum"); declareOutput(_pitch, TOKEN, "pitch"); } @@ -109,4 +101,4 @@ class HarmonicProductSpectrum : public StreamingAlgorithmWrapper { } // namespace streaming } // namespace essentia -#endif // ESSENTIA_HARMONICPRODUCTSPECTRUM_H +#endif // ESSENTIA_PITCHHPS_H diff --git a/test/src/unittests/tonal/test_hps.py b/test/src/unittests/tonal/test_pitchhps.py similarity index 87% rename from test/src/unittests/tonal/test_hps.py rename to test/src/unittests/tonal/test_pitchhps.py index bef2ed96d..ed2934f80 100644 --- a/test/src/unittests/tonal/test_hps.py +++ b/test/src/unittests/tonal/test_pitchhps.py @@ -22,13 +22,13 @@ from essentia_test import * from numpy import sin, pi, mean, random -class TestHarmonicProductSpectrum(TestCase): +class TestPitchHPS(TestCase): def testEmpty(self): - self.assertComputeFails(HarmonicProductSpectrum(), []) + self.assertComputeFails(PitchHPS(), []) def testZero(self): - pitch, confidence = HarmonicProductSpectrum()(zeros(1024)) + pitch, confidence = PitchHPS()(zeros(1024)) self.assertEqual(pitch, 0) self.assertEqual(confidence, 0) @@ -95,7 +95,7 @@ def runTest(self, signal, sr, freq, pitch_precision = 1, conf_precision = 0.1): frames = FrameGenerator(signal, frameSize=frameSize, hopSize=hopsize) win = Windowing(type='hann') - pitchDetect = HarmonicProductSpectrum(frameSize=frameSize, sampleRate = sr) + pitchDetect = PitchHPS(frameSize=frameSize, sampleRate = sr) pitch = [] confidence = [] for frame in frames: @@ -107,11 +107,11 @@ def runTest(self, signal, sr, freq, pitch_precision = 1, conf_precision = 0.1): self.assertAlmostEqual(mean(confidence), 1, conf_precision) def testInvalidParam(self): - self.assertConfigureFails(HarmonicProductSpectrum(), {'frameSize' : 1}) - self.assertConfigureFails(HarmonicProductSpectrum(), {'sampleRate' : 0}) + self.assertConfigureFails(PitchHPS(), {'frameSize' : 1}) + self.assertConfigureFails(PitchHPS(), {'sampleRate' : 0}) -suite = allTests(TestHarmonicProductSpectrum) +suite = allTests(TestPitchHPS) if __name__ == '__main__': TextTestRunner(verbosity=2).run(suite) From 383d06456031b1c26dbb8aa13c343d661385aa43 Mon Sep 17 00:00:00 2001 From: juliatogr Date: Sun, 16 Jun 2024 18:55:01 +0200 Subject: [PATCH 06/14] version 2 --- src/algorithms/tonal/pitchhps.cpp | 27 +++++++++++++++++++++++---- 1 file changed, 23 insertions(+), 4 deletions(-) diff --git a/src/algorithms/tonal/pitchhps.cpp b/src/algorithms/tonal/pitchhps.cpp index fb48d41f3..78763beea 100644 --- a/src/algorithms/tonal/pitchhps.cpp +++ b/src/algorithms/tonal/pitchhps.cpp @@ -46,7 +46,6 @@ void PitchHPS::configure() { _numHarmonics = parameter("numHarmonics").toInt(); _magnitudeThreshold = parameter("magnitudeThreshold").toReal(); - _tauMax = min(int(ceil(_sampleRate / parameter("minFrequency").toReal())), _frameSize/2); _tauMin = min(int(floor(_sampleRate / parameter("maxFrequency").toReal())), _frameSize/2); @@ -73,13 +72,33 @@ void PitchHPS::compute() { Algorithm::configure( "frameSize", int(2*(spectrum.size()-1)) ); } - vector hps(spectrum); + vector filteredSpectrum(spectrum); + + if (_tauMin < _tauMax) { + for (int i = 0; i < _tauMin; i++) { + filteredSpectrum[i] = 0.0; + } + + for (int i = _tauMax * _numHarmonics; i < filteredSpectrum.size(); i++) { + filteredSpectrum[i] = 0.0; + } + } + + Real minAmplitude = filteredSpectrum[argmax(filteredSpectrum)] * _magnitudeThreshold; + + for (int i = _tauMax; i < _tauMin; i++) { + if (filteredSpectrum[i] < minAmplitude) { + filteredSpectrum[i] = 0.0; + } + } + + vector hps(filteredSpectrum); for (int h=2; h < _numHarmonics; h++) { - vector downsampled(spectrum.size()/h, 1.0); + vector downsampled(filteredSpectrum.size()/h, 1.0); for (int bin=0; bin < downsampled.size(); bin++) { - downsampled[bin] = spectrum[bin*h]; + downsampled[bin] = filteredSpectrum[bin*h]; } for (int bin=0; bin < downsampled.size(); bin++) { hps[bin] *= downsampled[bin]; From 2ab85dc84866c1a6102856a3a0aca9ff8f36ac69 Mon Sep 17 00:00:00 2001 From: juliatogr Date: Sun, 16 Jun 2024 22:32:58 +0200 Subject: [PATCH 07/14] add space --- setup.py | 2 +- src/algorithms/tonal/pitchhps.cpp | 1 + test/src/unittests/essentia_test.py | 12 ++++++------ wscript | 2 +- 4 files changed, 9 insertions(+), 8 deletions(-) diff --git a/setup.py b/setup.py index 1bd9c3e72..1e05932d6 100644 --- a/setup.py +++ b/setup.py @@ -72,7 +72,7 @@ def get_git_version(): def get_version(): - version = open('VERSION', 'r').read().strip('\n') + version = '2.1-beta6-dev' if version.count('-dev'): # Development version. Get the number of commits after the last release git_version = get_git_version() diff --git a/src/algorithms/tonal/pitchhps.cpp b/src/algorithms/tonal/pitchhps.cpp index 78763beea..fbf71dca1 100644 --- a/src/algorithms/tonal/pitchhps.cpp +++ b/src/algorithms/tonal/pitchhps.cpp @@ -46,6 +46,7 @@ void PitchHPS::configure() { _numHarmonics = parameter("numHarmonics").toInt(); _magnitudeThreshold = parameter("magnitudeThreshold").toReal(); + _tauMax = min(int(ceil(_sampleRate / parameter("minFrequency").toReal())), _frameSize/2); _tauMin = min(int(floor(_sampleRate / parameter("maxFrequency").toReal())), _frameSize/2); diff --git a/test/src/unittests/essentia_test.py b/test/src/unittests/essentia_test.py index 0f3814598..4090bb4fa 100644 --- a/test/src/unittests/essentia_test.py +++ b/test/src/unittests/essentia_test.py @@ -87,8 +87,8 @@ def allTests(testClass): class TestCase(BaseTestCase): def assertValidNumber(self, x): - self.assert_(not numpy.isnan(x)) - self.assert_(not numpy.isinf(x)) + self.assertTrue(not numpy.isnan(x)) + self.assertTrue(not numpy.isinf(x)) def assertValidPool(self, pool): for key in pool.descriptorNames(): @@ -96,8 +96,8 @@ def assertValidPool(self, pool): if type(x) is float: self.assertValidNumber(x) elif type(x) is numpy.ndarray: - self.assert_(not numpy.isnan(x).any()) - self.assert_(not numpy.isinf(x).any()) + self.assertTrue(not numpy.isnan(x).any()) + self.assertTrue(not numpy.isinf(x).any()) def assertEqualVector(self, found, expected): self.assertEqual(len(found), len(expected)) @@ -129,7 +129,7 @@ def assertAlmostEqual(self, found, expected, precision = 1e-7): diff = abs(expected) else: diff = abs((expected - found) / abs(expected)) - self.assert_(diff <= precision, + self.assertTrue(diff <= precision, """Difference is %e while allowed relative error is %e""" % (diff, precision)) def assertAlmostEqualVector(self, found, expected, precision = 1e-7): @@ -162,7 +162,7 @@ def assertAlmostEqualMatrix(self, found, expected, precision = 1e-7): def assertAlmostEqualAbs(self, found, expected, precision = 0.1): diff = abs(expected - found) - self.assert_(diff <= precision, 'Difference is %e while allowed absolute error is %e' % (diff, precision)) + self.assertTrue(diff <= precision, 'Difference is %e while allowed absolute error is %e' % (diff, precision)) def assertAlmostEqualVectorAbs(self, found, expected, precision = 0.1): self.assertEqual(len(found), len(expected)) diff --git a/wscript b/wscript index a48d304c8..1fd2bb1ec 100644 --- a/wscript +++ b/wscript @@ -20,7 +20,7 @@ def get_git_version(): APPNAME = 'essentia' -VERSION = open('VERSION', 'r').read().strip('\n') +VERSION = '2.1-beta6-dev' GIT_SHA = get_git_version() top = '.' From a008c790b4c6ff26a543d186d0966600fd679bde Mon Sep 17 00:00:00 2001 From: juliatogr Date: Sun, 16 Jun 2024 23:04:36 +0200 Subject: [PATCH 08/14] version 2 fixed --- src/algorithms/tonal/pitchhps.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/algorithms/tonal/pitchhps.cpp b/src/algorithms/tonal/pitchhps.cpp index fbf71dca1..04403bb81 100644 --- a/src/algorithms/tonal/pitchhps.cpp +++ b/src/algorithms/tonal/pitchhps.cpp @@ -86,8 +86,9 @@ void PitchHPS::compute() { } Real minAmplitude = filteredSpectrum[argmax(filteredSpectrum)] * _magnitudeThreshold; + int maxFreqPos = min(int(_numHarmonics * _tauMax), int(filteredSpectrum.size())); - for (int i = _tauMax; i < _tauMin; i++) { + for (int i = _tauMin; i < maxFreqPos; i++) { if (filteredSpectrum[i] < minAmplitude) { filteredSpectrum[i] = 0.0; } From 77104a5a81b08b80079f84ca158af98c8738669c Mon Sep 17 00:00:00 2001 From: juliatogr Date: Sun, 16 Jun 2024 23:05:13 +0200 Subject: [PATCH 09/14] version 2 finished --- src/algorithms/tonal/pitchhps.cpp | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/algorithms/tonal/pitchhps.cpp b/src/algorithms/tonal/pitchhps.cpp index 04403bb81..e7a7c7cba 100644 --- a/src/algorithms/tonal/pitchhps.cpp +++ b/src/algorithms/tonal/pitchhps.cpp @@ -85,14 +85,14 @@ void PitchHPS::compute() { } } - Real minAmplitude = filteredSpectrum[argmax(filteredSpectrum)] * _magnitudeThreshold; - int maxFreqPos = min(int(_numHarmonics * _tauMax), int(filteredSpectrum.size())); - - for (int i = _tauMin; i < maxFreqPos; i++) { - if (filteredSpectrum[i] < minAmplitude) { - filteredSpectrum[i] = 0.0; - } - } +// Real minAmplitude = filteredSpectrum[argmax(filteredSpectrum)] * _magnitudeThreshold; +// int maxFreqPos = min(int(_numHarmonics * _tauMax), int(filteredSpectrum.size())); +// +// for (int i = _tauMin; i < maxFreqPos; i++) { +// if (filteredSpectrum[i] < minAmplitude) { +// filteredSpectrum[i] = 0.0; +// } +// } vector hps(filteredSpectrum); From 5c6ba5d69e9dc0fe7acdff562cf69d70256e98b1 Mon Sep 17 00:00:00 2001 From: juliatogr Date: Mon, 17 Jun 2024 19:23:21 +0200 Subject: [PATCH 10/14] fix --- src/algorithms/tonal/pitchhps.cpp | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/src/algorithms/tonal/pitchhps.cpp b/src/algorithms/tonal/pitchhps.cpp index e7a7c7cba..4088a0602 100644 --- a/src/algorithms/tonal/pitchhps.cpp +++ b/src/algorithms/tonal/pitchhps.cpp @@ -27,7 +27,7 @@ using namespace standard; const char* PitchHPS::name = "PitchHPS"; const char* PitchHPS::category = "Pitch"; -const char* PitchHPS::description = DOC("This algorithm estimates the fundamental frequency given the spectrum of a monophonic music signal. It is an implementation of Harmonic Product Spectrum algorithm [1], computed in the frequency-domain. It is recommended to window the input spectrum with a Hann window. The raw spectrum can be computed with the Spectrum algorithm.\n" +const char* PitchHPS::description = DOC("This algorithm estimates the fundamental frequency given the spectrum of a monophonic music signal. It is an implementation of Harmonic Product Spectrum algorithm [1], computed in the frequency domain. It is recommended to window the input spectrum with a Hann window. The raw spectrum can be computed with the Spectrum algorithm.\n" "\n" "An exception is thrown if an empty spectrum is provided.\n" "\n" @@ -83,16 +83,17 @@ void PitchHPS::compute() { for (int i = _tauMax * _numHarmonics; i < filteredSpectrum.size(); i++) { filteredSpectrum[i] = 0.0; } - } -// Real minAmplitude = filteredSpectrum[argmax(filteredSpectrum)] * _magnitudeThreshold; -// int maxFreqPos = min(int(_numHarmonics * _tauMax), int(filteredSpectrum.size())); +// Real minAmplitude = filteredSpectrum[argmax(filteredSpectrum)] * _magnitudeThreshold; +// int maxFreqPos = min(int(_numHarmonics * _tauMax), int(filteredSpectrum.size())); // -// for (int i = _tauMin; i < maxFreqPos; i++) { -// if (filteredSpectrum[i] < minAmplitude) { -// filteredSpectrum[i] = 0.0; +// for (int i = _tauMin; i < maxFreqPos; i++) { +// if (filteredSpectrum[i] < minAmplitude) { +// filteredSpectrum[i] = 0.0; +// } // } -// } + } + vector hps(filteredSpectrum); From 040c3d10237364e4d8b609fe10580e240097745b Mon Sep 17 00:00:00 2001 From: juliatogr Date: Mon, 17 Jun 2024 19:24:02 +0200 Subject: [PATCH 11/14] description fix --- src/algorithms/tonal/pitchhps.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/algorithms/tonal/pitchhps.cpp b/src/algorithms/tonal/pitchhps.cpp index 4088a0602..096e93f35 100644 --- a/src/algorithms/tonal/pitchhps.cpp +++ b/src/algorithms/tonal/pitchhps.cpp @@ -27,7 +27,7 @@ using namespace standard; const char* PitchHPS::name = "PitchHPS"; const char* PitchHPS::category = "Pitch"; -const char* PitchHPS::description = DOC("This algorithm estimates the fundamental frequency given the spectrum of a monophonic music signal. It is an implementation of Harmonic Product Spectrum algorithm [1], computed in the frequency domain. It is recommended to window the input spectrum with a Hann window. The raw spectrum can be computed with the Spectrum algorithm.\n" +const char* PitchHPS::description = DOC("This algorithm estimates the fundamental frequency given the spectrum of a monophonic music signal. It is an implementation of the Harmonic Product Spectrum algorithm [1], computed in the frequency domain. It is recommended to window the input spectrum with a Hann window. The raw spectrum can be computed with the Spectrum algorithm.\n" "\n" "An exception is thrown if an empty spectrum is provided.\n" "\n" From d5ddae5461d70309de8f2edae5d5bd6e48c82bb3 Mon Sep 17 00:00:00 2001 From: juliatogr Date: Mon, 24 Jun 2024 20:00:08 +0200 Subject: [PATCH 12/14] fixes --- src/algorithms/tonal/pitchhps.cpp | 18 ++++++++++-------- src/algorithms/tonal/pitchhps.h | 3 +++ 2 files changed, 13 insertions(+), 8 deletions(-) diff --git a/src/algorithms/tonal/pitchhps.cpp b/src/algorithms/tonal/pitchhps.cpp index 096e93f35..996c35264 100644 --- a/src/algorithms/tonal/pitchhps.cpp +++ b/src/algorithms/tonal/pitchhps.cpp @@ -45,10 +45,12 @@ void PitchHPS::configure() { _sampleRate = parameter("sampleRate").toReal(); _numHarmonics = parameter("numHarmonics").toInt(); _magnitudeThreshold = parameter("magnitudeThreshold").toReal(); + _minFrequency = parameter("minFrequency").toReal(); + _maxFrequency = parameter("maxFrequency").toReal(); - _tauMax = min(int(ceil(_sampleRate / parameter("minFrequency").toReal())), _frameSize/2); - _tauMin = min(int(floor(_sampleRate / parameter("maxFrequency").toReal())), _frameSize/2); + _tauMax = min(int(ceil(_sampleRate / _minFrequency)), _frameSize/2); + _tauMin = min(int(floor(_sampleRate / _maxFrequency)), _frameSize/2); if (_tauMax <= _tauMin) { throw EssentiaException("PitchHPS: maxFrequency is lower than minFrequency, or they are too close, or they are out of the interval of detectable frequencies with respect to the specified frameSize. Minimum detectable frequency is ", _sampleRate / (_frameSize/2), " Hz"); @@ -76,13 +78,13 @@ void PitchHPS::compute() { vector filteredSpectrum(spectrum); if (_tauMin < _tauMax) { - for (int i = 0; i < _tauMin; i++) { - filteredSpectrum[i] = 0.0; - } + double frequencyResolution = _sampleRate / spectrum.size(); - for (int i = _tauMax * _numHarmonics; i < filteredSpectrum.size(); i++) { - filteredSpectrum[i] = 0.0; - } + size_t minBin = static_cast(std::floor(_minFrequency / frequencyResolution)); + size_t maxBin = static_cast(std::floor(_maxFrequency / frequencyResolution)); + + std::fill(filteredSpectrum.begin(), filteredSpectrum.begin() + minBin, 0); + std::fill(filteredSpectrum.begin() + maxBin + 1, filteredSpectrum.end(), 0); // Real minAmplitude = filteredSpectrum[argmax(filteredSpectrum)] * _magnitudeThreshold; // int maxFreqPos = min(int(_numHarmonics * _tauMax), int(filteredSpectrum.size())); diff --git a/src/algorithms/tonal/pitchhps.h b/src/algorithms/tonal/pitchhps.h index a732e0f16..233af7b4c 100644 --- a/src/algorithms/tonal/pitchhps.h +++ b/src/algorithms/tonal/pitchhps.h @@ -41,6 +41,8 @@ class PitchHPS : public Algorithm { int _tauMin; int _tauMax; int _numHarmonics; + int _minFrequency; + int _maxFrequency; Real _magnitudeThreshold; public: @@ -95,6 +97,7 @@ class PitchHPS : public StreamingAlgorithmWrapper { declareAlgorithm("PitchHPS"); declareInput(_spectrum, TOKEN, "spectrum"); declareOutput(_pitch, TOKEN, "pitch"); + declareOutput(_pitchConfidence, TOKEN, "pitchConfidence"); } }; From 79db76f0739b131619dbfaa1d674e97500bfbeac Mon Sep 17 00:00:00 2001 From: juliatogr Date: Tue, 2 Jul 2024 16:46:12 +0200 Subject: [PATCH 13/14] comment pitchConfidence --- src/algorithms/tonal/pitchhps.cpp | 8 ++++---- src/algorithms/tonal/pitchhps.h | 8 ++++---- test/src/unittests/tonal/test_pitchhps.py | 15 ++++++--------- 3 files changed, 14 insertions(+), 17 deletions(-) diff --git a/src/algorithms/tonal/pitchhps.cpp b/src/algorithms/tonal/pitchhps.cpp index 996c35264..42d2f6101 100644 --- a/src/algorithms/tonal/pitchhps.cpp +++ b/src/algorithms/tonal/pitchhps.cpp @@ -69,7 +69,7 @@ void PitchHPS::compute() { throw EssentiaException("PitchHPS: Cannot compute pitch detection on empty spectrum."); } Real& pitch = _pitch.get(); - Real& pitchConfidence = _pitchConfidence.get(); +// Real& pitchConfidence = _pitchConfidence.get(); if ((int)spectrum.size() != _frameSize/2+1) {//_sqrMag.size()/2+1) { Algorithm::configure( "frameSize", int(2*(spectrum.size()-1)) ); @@ -81,7 +81,7 @@ void PitchHPS::compute() { double frequencyResolution = _sampleRate / spectrum.size(); size_t minBin = static_cast(std::floor(_minFrequency / frequencyResolution)); - size_t maxBin = static_cast(std::floor(_maxFrequency / frequencyResolution)); + size_t maxBin = static_cast(std::floor(_maxFrequency * _numHarmonics / frequencyResolution)); std::fill(filteredSpectrum.begin(), filteredSpectrum.begin() + minBin, 0); std::fill(filteredSpectrum.begin() + maxBin + 1, filteredSpectrum.end(), 0); @@ -123,9 +123,9 @@ void PitchHPS::compute() { if (_positions.size() == 0) { pitch = 0.0; - pitchConfidence = 0.0; +// pitchConfidence = 0.0; } else { pitch = _positions[0] * _sampleRate / _frameSize; - pitchConfidence = 1.0; +// pitchConfidence = 1.0; } } diff --git a/src/algorithms/tonal/pitchhps.h b/src/algorithms/tonal/pitchhps.h index 233af7b4c..06281fa09 100644 --- a/src/algorithms/tonal/pitchhps.h +++ b/src/algorithms/tonal/pitchhps.h @@ -30,7 +30,7 @@ class PitchHPS : public Algorithm { private: Input > _spectrum; Output _pitch; - Output _pitchConfidence; +// Output _pitchConfidence; Algorithm* _peakDetect; @@ -49,7 +49,7 @@ class PitchHPS : public Algorithm { PitchHPS() { declareInput(_spectrum, "spectrum", "the input spectrum (preferably created with a hann window)"); declareOutput(_pitch, "pitch", "detected pitch [Hz]"); - declareOutput(_pitchConfidence, "pitchConfidence", "confidence with which the pitch was detected [0,1]"); +// declareOutput(_pitchConfidence, "pitchConfidence", "confidence with which the pitch was detected [0,1]"); _peakDetect = AlgorithmFactory::create("PeakDetection"); } @@ -90,14 +90,14 @@ class PitchHPS : public StreamingAlgorithmWrapper { protected: Sink > _spectrum; Source _pitch; - Source _pitchConfidence; +// Source _pitchConfidence; public: PitchHPS() { declareAlgorithm("PitchHPS"); declareInput(_spectrum, TOKEN, "spectrum"); declareOutput(_pitch, TOKEN, "pitch"); - declareOutput(_pitchConfidence, TOKEN, "pitchConfidence"); +// declareOutput(_pitchConfidence, TOKEN, "pitchConfidence"); } }; diff --git a/test/src/unittests/tonal/test_pitchhps.py b/test/src/unittests/tonal/test_pitchhps.py index ed2934f80..078d4ea9f 100644 --- a/test/src/unittests/tonal/test_pitchhps.py +++ b/test/src/unittests/tonal/test_pitchhps.py @@ -28,9 +28,8 @@ def testEmpty(self): self.assertComputeFails(PitchHPS(), []) def testZero(self): - pitch, confidence = PitchHPS()(zeros(1024)) + pitch = PitchHPS()(zeros(1024)) self.assertEqual(pitch, 0) - self.assertEqual(confidence, 0) def testSine(self): @@ -63,7 +62,7 @@ def testBandLimitedSaw(self): for i in range(1,size): for harm in range(1,nharms+1): signal[i] += 1./harm*sin(harm*i*w/sr) - self.runTest(signal, sr, freq, 1.1, 0.1) + self.runTest(signal, sr, freq, 1.1) def testBandLimitedSawMasked(self): sr = 44100 @@ -86,10 +85,10 @@ def testBandLimitedSawMasked(self): signal[i] += 0.5*sin(i*subw/sr) max_signal = max(signal) + 1 signal = signal/max_signal - self.runTest(signal, sr, freq, 1.5, 0.3) + self.runTest(signal, sr, freq, 1.5) - def runTest(self, signal, sr, freq, pitch_precision = 1, conf_precision = 0.1): + def runTest(self, signal, sr, freq, pitch_precision = 1): frameSize = 1024 hopsize = frameSize @@ -97,14 +96,12 @@ def runTest(self, signal, sr, freq, pitch_precision = 1, conf_precision = 0.1): win = Windowing(type='hann') pitchDetect = PitchHPS(frameSize=frameSize, sampleRate = sr) pitch = [] - confidence = [] for frame in frames: spec = Spectrum()(win(frame)) - f, conf = pitchDetect(spec) + f = pitchDetect(spec) pitch += [f] - confidence += [conf] + self.assertAlmostEqual(mean(f), freq, pitch_precision) - self.assertAlmostEqual(mean(confidence), 1, conf_precision) def testInvalidParam(self): self.assertConfigureFails(PitchHPS(), {'frameSize' : 1}) From f7e068303f6de711a285bc385e8dd46a4d796c99 Mon Sep 17 00:00:00 2001 From: juliatogr Date: Tue, 2 Jul 2024 17:04:58 +0200 Subject: [PATCH 14/14] rollback changes to allow local install --- VERSION | 1 + setup.py | 2 +- test/src/unittests/essentia_test.py | 12 ++++++------ wscript | 2 +- 4 files changed, 9 insertions(+), 8 deletions(-) create mode 100644 VERSION diff --git a/VERSION b/VERSION new file mode 100644 index 000000000..a06e5954d --- /dev/null +++ b/VERSION @@ -0,0 +1 @@ +2.1-beta6-dev diff --git a/setup.py b/setup.py index 1e05932d6..1bd9c3e72 100644 --- a/setup.py +++ b/setup.py @@ -72,7 +72,7 @@ def get_git_version(): def get_version(): - version = '2.1-beta6-dev' + version = open('VERSION', 'r').read().strip('\n') if version.count('-dev'): # Development version. Get the number of commits after the last release git_version = get_git_version() diff --git a/test/src/unittests/essentia_test.py b/test/src/unittests/essentia_test.py index 4090bb4fa..0f3814598 100644 --- a/test/src/unittests/essentia_test.py +++ b/test/src/unittests/essentia_test.py @@ -87,8 +87,8 @@ def allTests(testClass): class TestCase(BaseTestCase): def assertValidNumber(self, x): - self.assertTrue(not numpy.isnan(x)) - self.assertTrue(not numpy.isinf(x)) + self.assert_(not numpy.isnan(x)) + self.assert_(not numpy.isinf(x)) def assertValidPool(self, pool): for key in pool.descriptorNames(): @@ -96,8 +96,8 @@ def assertValidPool(self, pool): if type(x) is float: self.assertValidNumber(x) elif type(x) is numpy.ndarray: - self.assertTrue(not numpy.isnan(x).any()) - self.assertTrue(not numpy.isinf(x).any()) + self.assert_(not numpy.isnan(x).any()) + self.assert_(not numpy.isinf(x).any()) def assertEqualVector(self, found, expected): self.assertEqual(len(found), len(expected)) @@ -129,7 +129,7 @@ def assertAlmostEqual(self, found, expected, precision = 1e-7): diff = abs(expected) else: diff = abs((expected - found) / abs(expected)) - self.assertTrue(diff <= precision, + self.assert_(diff <= precision, """Difference is %e while allowed relative error is %e""" % (diff, precision)) def assertAlmostEqualVector(self, found, expected, precision = 1e-7): @@ -162,7 +162,7 @@ def assertAlmostEqualMatrix(self, found, expected, precision = 1e-7): def assertAlmostEqualAbs(self, found, expected, precision = 0.1): diff = abs(expected - found) - self.assertTrue(diff <= precision, 'Difference is %e while allowed absolute error is %e' % (diff, precision)) + self.assert_(diff <= precision, 'Difference is %e while allowed absolute error is %e' % (diff, precision)) def assertAlmostEqualVectorAbs(self, found, expected, precision = 0.1): self.assertEqual(len(found), len(expected)) diff --git a/wscript b/wscript index 1fd2bb1ec..a48d304c8 100644 --- a/wscript +++ b/wscript @@ -20,7 +20,7 @@ def get_git_version(): APPNAME = 'essentia' -VERSION = '2.1-beta6-dev' +VERSION = open('VERSION', 'r').read().strip('\n') GIT_SHA = get_git_version() top = '.'