From e5f720fc9b496e9995408dc8a9bea3a746e982e6 Mon Sep 17 00:00:00 2001 From: cvf-bcn-gituser Date: Sun, 25 Feb 2024 17:56:22 +0100 Subject: [PATCH 1/5] Adding Unit Test for Tonal Extractor --- .../extractor/test_tonalextractor.py | 115 ++++++++++++++++++ 1 file changed, 115 insertions(+) create mode 100644 test/src/unittests/extractor/test_tonalextractor.py diff --git a/test/src/unittests/extractor/test_tonalextractor.py b/test/src/unittests/extractor/test_tonalextractor.py new file mode 100644 index 000000000..0bc59f6e3 --- /dev/null +++ b/test/src/unittests/extractor/test_tonalextractor.py @@ -0,0 +1,115 @@ +#!/usr/bin/env python + +# 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/ + +from essentia_test import * +import numpy as np + +class TestTonalExtractor(TestCase): + + def testEmpty(self): + # Test if the algorithm handles an empty input signal correctly + with self.assertRaises(RuntimeError): + chords_changes_rate, _, _, chords_number_rate, _, _, _, _, _, _, _, key_strength = TonalExtractor()(np.array([], dtype=np.float32)) + + def testSilence(self): + # In this test we jiuts check three of the output parameters of type real + silence_vec = np.zeros(44100, dtype=np.single) + chords_changes_rate, _, _, chords_number_rate, _, _, _, _, _, _, _, key_strength = TonalExtractor()(silence_vec) + self.assertEqual(chords_changes_rate, 0.0) + self.assertGreaterEqual(chords_number_rate, 0.0) + self.assertEqual(key_strength, 0.0) + + def testInvalidParameters(self): + # Test if the algorithm handles invalid parameters correctly + extractor = TonalExtractor() + + # Test case 1: Negative frameSize + with self.subTest(msg="Negative frameSize"): + with self.assertRaises(RuntimeError): + extractor.configure(frameSize=-1, hopSize=2048, tuningFrequency=440.0) + + # Test case 2: Negative hopSize + with self.subTest(msg="Negative hopSize"): + with self.assertRaises(RuntimeError): + extractor.configure(frameSize=4096, hopSize=-1, tuningFrequency=440.0) + + # Test case 3: Negative tuningFrequency + with self.subTest(msg="Negative tuningFrequency"): + with self.assertRaises(RuntimeError): + extractor.configure(frameSize=4096, hopSize=2048, tuningFrequency=-440.0) + + # Test case 4: Zero frameSize and hopSize + with self.subTest(msg="Zero frameSize and hopSize"): + with self.assertRaises(RuntimeError): + extractor.configure(frameSize=0, hopSize=0, tuningFrequency=440.0) + + # Test case 5: Zero frameSize + with self.subTest(msg="Zero frameSize"): + with self.assertRaises(RuntimeError): + extractor.configure(frameSize=0, hopSize=2048, tuningFrequency=440.0) + + # Test case 6: Zero hopSize + with self.subTest(msg="Zero hopSize"): + with self.assertRaises(RuntimeError): + extractor.configure(frameSize=4096, hopSize=0, tuningFrequency=440.0) + + # Test case 7: Non-negative parameters + with self.subTest(msg="Valid parameters"): + # This should not raise an exception + extractor.configure(frameSize=4096, hopSize=2048, tuningFrequency=440.0) + + def testRandomInput(self): + n = 10 + for _ in range(n): + rand_input = np.random.random(88200).astype(np.single) * 2 - 1 + result = TonalExtractor()(rand_input) + expected_result = np.sum(rand_input * rand_input) ** 0.67 + self.assertAlmostEqual(result[0], expected_result, 9.999e+02) + + def testRegression(self): + frameSizes = [128, 256, 512, 1024, 2048, 4096] + hopSizes = [256, 512, 1024, 2048, 4096, 8192] + + input_filename = join(testdata.audio_dir, "recorded", "dubstep.wav") # Replace 'testdata' with actual path + real_audio_input = MonoLoader(filename=input_filename)() + + + # Iterate through pairs of frameSize and corresponding hopSize + for fs, hs in zip(frameSizes, hopSizes): + with self.subTest(frameSize=fs, hopSize=hs): + # Process the algorithm on real audio with the current frameSize and hopSize + te = TonalExtractor() + te.configure(frameSize=fs, hopSize=hs) + chords_changes_rate, _, _, chords_number_rate, _, _, _, _, _, _, _, key_strength= te(real_audio_input) + + # Perform assertions on one or more outputs + # Example: Assert that chords_changes_rate is a non-negative scalar + self.assertIsInstance(chords_changes_rate, (int, float)) + self.assertGreaterEqual(chords_changes_rate, 0) + self.assertIsInstance(chords_number_rate, (int, float)) + self.assertGreaterEqual(chords_number_rate, 0) + self.assertIsInstance(key_strength, (int, float)) + self.assertGreaterEqual(key_strength, 0) + # You can add more assertions on other outputs as needed + + +suite = allTests(TestTonalExtractor) + +if __name__ == '__main__': + unittest.main() From 2d56ff239696c0cc1e8669535f24ca03240740ea Mon Sep 17 00:00:00 2001 From: cvf-bcn-gituser Date: Tue, 27 Feb 2024 22:22:51 +0100 Subject: [PATCH 2/5] QA Unit tests: Add test on real Audio for tonal extractor algorithm --- .../extractor/test_tonalextractor.py | 49 +++++++++++++++++-- 1 file changed, 44 insertions(+), 5 deletions(-) diff --git a/test/src/unittests/extractor/test_tonalextractor.py b/test/src/unittests/extractor/test_tonalextractor.py index 0bc59f6e3..e31c274eb 100644 --- a/test/src/unittests/extractor/test_tonalextractor.py +++ b/test/src/unittests/extractor/test_tonalextractor.py @@ -16,10 +16,12 @@ # # 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/ +import os.path from essentia_test import * import numpy as np + class TestTonalExtractor(TestCase): def testEmpty(self): @@ -83,20 +85,20 @@ def testRandomInput(self): self.assertAlmostEqual(result[0], expected_result, 9.999e+02) def testRegression(self): - frameSizes = [128, 256, 512, 1024, 2048, 4096] - hopSizes = [256, 512, 1024, 2048, 4096, 8192] + frameSizes = [256, 512, 1024, 2048, 4096, 8192] + hopSizes = [128, 256, 512, 1024, 2048, 4096] input_filename = join(testdata.audio_dir, "recorded", "dubstep.wav") # Replace 'testdata' with actual path - real_audio_input = MonoLoader(filename=input_filename)() - + realAudio = MonoLoader(filename=input_filename)() # Iterate through pairs of frameSize and corresponding hopSize + # TODO: Extend loop to try different tuningFrequency values for fs, hs in zip(frameSizes, hopSizes): with self.subTest(frameSize=fs, hopSize=hs): # Process the algorithm on real audio with the current frameSize and hopSize te = TonalExtractor() te.configure(frameSize=fs, hopSize=hs) - chords_changes_rate, _, _, chords_number_rate, _, _, _, _, _, _, _, key_strength= te(real_audio_input) + chords_changes_rate, _, _, chords_number_rate, _, _, _, _, _, _, _, key_strength= te(realAudio) # Perform assertions on one or more outputs # Example: Assert that chords_changes_rate is a non-negative scalar @@ -108,6 +110,43 @@ def testRegression(self): self.assertGreaterEqual(key_strength, 0) # You can add more assertions on other outputs as needed + def testRealAudio(self): + + # These reference values could also be compared with th results of tonal extractors of alternative a + # audio libraries (e.g. MadMom, libs fromn Alexander Lerch etc.) + # ccr = chord changes rate ; cnr = chord number rate; ks = key strength + mozart_ccr = 0.03400309011340141 + mozart_cnr = 0.010819165036082268 + mozart_ks = 0.8412253260612488 + + vivaldi_ccr = 0.052405908703804016 + vivaldi_cnr = 0.004764173645526171 + vivaldi_ks = 0.7122617959976196 + + thresh = 0.5 + + def test_on_real_audio(path, ccr, cnr, ks): + realAudio = MonoLoader(filename=path)() + + # Use default configuration of algorothm + # This function could be extended to test for more outputs + # TODO: Extend to test non-scalar and string outputs: + # i.e. chords_histogram, chords_progression, chords_scale, chords_strength + # hpcp, hpcp_highres, key_key and key_scale + te = TonalExtractor() + chords_changes_rate, _, _, chords_number_rate, _, _, _, _, _, _, _, key_strength= te(realAudio) + self.assertIsInstance(chords_changes_rate, (int, float)) + self.assertGreaterEqual(chords_changes_rate, 0) + self.assertAlmostEqual(chords_changes_rate, ccr, thresh) + self.assertIsInstance(chords_number_rate, (int, float)) + self.assertGreaterEqual(chords_number_rate, 0) + self.assertAlmostEqual(chords_number_rate, cnr, thresh) + self.assertIsInstance(key_strength, (int, float)) + self.assertGreaterEqual(key_strength, 0) + self.assertAlmostEqual(key_strength, ks, thresh) + + test_on_real_audio(join(testdata.audio_dir, "recorded", "mozart_c_major_30sec.wav"), mozart_ccr, mozart_cnr, mozart_ks) + test_on_real_audio(join(testdata.audio_dir, "recorded", "Vivaldi_Sonata_5_II_Allegro.wav"), vivaldi_ccr, vivaldi_cnr, vivaldi_ks) suite = allTests(TestTonalExtractor) From aabc2f8d697ed9be340ad1de8f3bf6bbc676dc18 Mon Sep 17 00:00:00 2001 From: cvf-bcn-gituser Date: Tue, 27 Feb 2024 22:27:06 +0100 Subject: [PATCH 3/5] QA Unit tests: spelling errors in comments --- test/src/unittests/extractor/test_tonalextractor.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/src/unittests/extractor/test_tonalextractor.py b/test/src/unittests/extractor/test_tonalextractor.py index e31c274eb..1d8272c93 100644 --- a/test/src/unittests/extractor/test_tonalextractor.py +++ b/test/src/unittests/extractor/test_tonalextractor.py @@ -30,7 +30,7 @@ def testEmpty(self): chords_changes_rate, _, _, chords_number_rate, _, _, _, _, _, _, _, key_strength = TonalExtractor()(np.array([], dtype=np.float32)) def testSilence(self): - # In this test we jiuts check three of the output parameters of type real + # In this test we check three of the output parameters of type real silence_vec = np.zeros(44100, dtype=np.single) chords_changes_rate, _, _, chords_number_rate, _, _, _, _, _, _, _, key_strength = TonalExtractor()(silence_vec) self.assertEqual(chords_changes_rate, 0.0) @@ -112,8 +112,8 @@ def testRegression(self): def testRealAudio(self): - # These reference values could also be compared with th results of tonal extractors of alternative a - # audio libraries (e.g. MadMom, libs fromn Alexander Lerch etc.) + # These reference values could also be compared with the results of tonal extractors of alternative + # audio libraries (e.g. MadMom, libs from Alexander Lerch etc.) # ccr = chord changes rate ; cnr = chord number rate; ks = key strength mozart_ccr = 0.03400309011340141 mozart_cnr = 0.010819165036082268 From b295038d3d0993bfe58cb9c3e14976e7292a8533 Mon Sep 17 00:00:00 2001 From: cvf-bcn-gituser Date: Wed, 28 Feb 2024 21:13:52 +0100 Subject: [PATCH 4/5] adding pitch meolody --- test/src/unittests/tonal/test_pitchmelodia.py | 178 +++++++++++++++++- 1 file changed, 172 insertions(+), 6 deletions(-) diff --git a/test/src/unittests/tonal/test_pitchmelodia.py b/test/src/unittests/tonal/test_pitchmelodia.py index e84af7e9f..b523add54 100644 --- a/test/src/unittests/tonal/test_pitchmelodia.py +++ b/test/src/unittests/tonal/test_pitchmelodia.py @@ -18,18 +18,184 @@ # version 3 along with this program. If not, see http://www.gnu.org/licenses/ - from essentia_test import * - class TestPitchMelodia(TestCase): - def testZero(self): - signal = zeros(256) + def testInvalidParam(self): + # Test for all the values above the boundary limits. + self.assertConfigureFails(PitchMelodia(), {'binResolution': -1}) + self.assertConfigureFails(PitchMelodia(), {'binResolution': 0}) + self.assertConfigureFails(PitchMelodia(), {'filterIterations': 0}) + self.assertConfigureFails(PitchMelodia(), {'filterIterations': -1}) + self.assertConfigureFails(PitchMelodia(), {'frameSize': -1}) + self.assertConfigureFails(PitchMelodia(), {'frameSize': 0}) + self.assertConfigureFails(PitchMelodia(), {'harmonicWeight': -1}) + self.assertConfigureFails(PitchMelodia(), {'hopSize': 0}) + self.assertConfigureFails(PitchMelodia(), {'hopSize': -1}) + self.assertConfigureFails(PitchMelodia(), {'magnitudeCompression': -1}) + self.assertConfigureFails(PitchMelodia(), {'magnitudeCompression': 0}) + self.assertConfigureFails(PitchMelodia(), {'magnitudeCompression': 2}) + self.assertConfigureFails(PitchMelodia(), {'magnitudeThreshold': -1}) + self.assertConfigureFails(PitchMelodia(), {'maxFrequency': -1}) + self.assertConfigureFails(PitchMelodia(), {'minDuration': 0}) + self.assertConfigureFails(PitchMelodia(), {'minDuration': -1}) + self.assertConfigureFails(PitchMelodia(), {'minFrequency': -1}) + self.assertConfigureFails(PitchMelodia(), {'numberHarmonics': -1}) + self.assertConfigureFails(PitchMelodia(), {'peakDistributionThreshold': -1}) + self.assertConfigureFails(PitchMelodia(), {'peakDistributionThreshold': 2.1}) + self.assertConfigureFails(PitchMelodia(), {'peakFrameThreshold': -1}) + self.assertConfigureFails(PitchMelodia(), {'peakFrameThreshold': 2}) + self.assertConfigureFails(PitchMelodia(), {'pitchContinuity': -1}) + self.assertConfigureFails(PitchMelodia(), {'referenceFrequency': 0}) + self.assertConfigureFails(PitchMelodia(), {'referenceFrequency': -1}) + self.assertConfigureFails(PitchMelodia(), {'sampleRate': 0}) + self.assertConfigureFails(PitchMelodia(), {'sampleRate': -1}) + self.assertConfigureFails(PitchMelodia(), {'timeContinuity': 0}) + self.assertConfigureFails(PitchMelodia(), {'timeContinuity': -1}) + + def testDefaultParameters(self): + signal = randn(1024) pitch, confidence = PitchMelodia()(signal) - self.assertAlmostEqualVector(pitch, [0., 0., 0.]) - self.assertAlmostEqualVector(confidence, [0., 0., 0.]) + # Assert that default parameters produce valid outputs + self.assertIsNotNone(pitch) + self.assertIsNotNone(confidence) + + def testEmptyInput(self): + pitch, confidence = PitchMelodia()([]) + self.assertEqualVector(pitch, []) + self.assertEqualVector(confidence, []) + + def testZerosInput(self): + signal = zeros(1024) + pitch, confidence = PitchMelodia()(signal) + self.assertAlmostEqualVector(pitch, [0.] * 9) # Use [0.] * 9 for flexibility + self.assertAlmostEqualVector(confidence, [0.] * 9) + + def testOnesInput(self): + signal = ones(1024) + pitch, confidence = PitchMelodia()(signal) + self.assertAlmostEqualVector(pitch, [0.] * 9) # Use [0.] * 9 for flexibility + self.assertAlmostEqualVector(confidence, [0.] * 9) + + def testCustomParameters(self): + signal = randn(2048) + # Use custom parameters + params = { + 'binResolution': 5, + 'filterIterations': 5, + 'frameSize': 1024, + 'guessUnvoiced': True, + 'harmonicWeight': 0.9, + 'hopSize': 256, + 'magnitudeCompression': 0.5, + 'magnitudeThreshold': 30, + 'maxFrequency': 15000, + 'minDuration': 50, + 'minFrequency': 60, + 'numberHarmonics': 15, + 'peakDistributionThreshold': 1.0, + 'peakFrameThreshold': 0.8, + 'pitchContinuity': 30.0, + 'referenceFrequency': 60, + 'sampleRate': 22050, + 'timeContinuity': 150 + } + pitch, confidence = PitchMelodia(**params)(signal) + # Assert that custom parameters produce valid outputs + self.assertIsNotNone(pitch) + self.assertIsNotNone(confidence) + + def testInputWithSilence(self): + signal = concatenate([zeros(512), randn(1024), zeros(512)]) + pitch, confidence = PitchMelodia()(signal) + # Assert that silent portions don't have pitch information + self.assertTrue(all(p == 0.0 for p in pitch[:512])) + self.assertTrue(all(c == 0.0 for c in confidence[:512])) + + def testHighPitchResolution(self): + signal = randn(1024) + pitch, confidence = PitchMelodia(binResolution=1)(signal) + # Assert that using high bin resolution produces valid outputs + self.assertIsNotNone(pitch) + self.assertIsNotNone(confidence) + self.assertEqual(len(pitch), 3) + self.assertEqual(len(confidence), 3) + + def testRealCase(self): + filename = join(testdata.audio_dir, 'recorded', 'vignesh.wav') + audio = MonoLoader(filename=filename, sampleRate=44100)() + pm = PitchMelodia() + pitch, pitchConfidence = pm(audio) + + # Save reference values for later loading + save('pitchmelodiapitch.npy', pitch) + save('pitchmelodiaconfidence.npy', pitchConfidence) + + loadedPitchMelodiaPitch = load(join(filedir(), 'pitchmelodia/pitchmelodiapitch.npy')) + self.assertAlmostEqualVectorFixedPrecision(pitch, loadedPitchMelodiaPitch.tolist(), 8) + + loadedPitchConfidence = load(join(filedir(), 'pitchmelodia/pitchmelodiaconfidence.npy')) + self.assertAlmostEqualVectorFixedPrecision(pitchConfidence, loadedPitchConfidence.tolist(), 8) + + def testRealCaseEqualLoudness(self): + filename = join(testdata.audio_dir, 'recorded', 'vignesh.wav') + audio = MonoLoader(filename=filename, sampleRate=44100)() + pm = PitchMelodia() + eq = EqualLoudness() + eqAudio = eq(audio) + pitch, pitchConfidence = pm(eqAudio) + + # Save reference values for later loading + save('pitchmelodiapitch_eqloud.npy', pitch) + save('pitchmelodiaconfidence_eqloud.npy', pitchConfidence) + + loadedPitchMelodiaPitch = load(join(filedir(), 'pitchmelodia/pitchmelodiapitch_eqloud.npy')) + self.assertAlmostEqualVectorFixedPrecision(pitch, loadedPitchMelodiaPitch.tolist(), 8) + + loadedPitchConfidence = load(join(filedir(), 'pitchmelodia/pitchmelodiaconfidence_eqloud.npy')) + self.assertAlmostEqualVectorFixedPrecision(pitchConfidence, loadedPitchConfidence.tolist(), 8) + + def test110Hz(self): + signal = 0.5 * numpy.sin((array(range(10 * 4096))/44100.) * 110 * 2*math.pi) + pm = PitchMelodia() + pitch, confidence = pm(signal) + self.assertAlmostEqual(pitch[50], 110.0, 10) + + def test110HzPeakThresholds(self): + signal = 0.5 * numpy.sin((array(range(10 * 4096))/44100.) * 110 * 2*math.pi) + pm_default = PitchMelodia() + pm_hw0 = PitchMelodia(peakFrameThreshold=0) + pm_hw1 = PitchMelodia(peakFrameThreshold=1) + + pitch_default, confidence_default = pm_default(signal) + pitch_hw0, confidence_hw0 = pm_hw0(signal) + pitch_hw1, confidence_hw1 = pm_hw1(signal) + + self.assertAlmostEqual(pitch_default[50], 110.0, 10) + self.assertAlmostEqual(pitch_hw0[50], 110.0, 10) + self.assertAlmostEqual(pitch_hw1[50], 110.0, 10) + + def testDifferentPeaks(self): + signal_55Hz = 0.5 * numpy.sin((array(range(10 * 4096))/44100.) * 55 * 2*math.pi) + signal_85Hz = 0.5 * numpy.sin((array(range(10 * 4096))/44100.) * 85 * 2*math.pi) + signal = signal_55Hz + signal_85Hz + pm = PitchMelodia() + pitch, confidence = pm(signal) + + for p in pitch[83:129]: # Adjusted the range to be more clear + self.assertGreater(p, 55) + self.assertLess(p, 85) + + def testBelowReferenceFrequency1(self): + signal_50Hz = 1.5 * numpy.sin((array(range(10 * 4096))/44100.) * 50 * 2*math.pi) + pitch, confidence = PitchMelodia()(signal_50Hz) + self.assertAlmostEqual(pitch[10], 100.0, 2) + def testBelowReferenceFrequency2(self): + signal_30Hz = 1.5 * numpy.sin((array(range(10 * 4096))/44100.) * 30 * 2*math.pi) + pitch, confidence = PitchMelodia(referenceFrequency=40)(signal_30Hz) + self.assertAlmostEqual(pitch[10], 60.0, 2) suite = allTests(TestPitchMelodia) From e7ac53af432ced5e6c7c14aabcf2678526e7ee4d Mon Sep 17 00:00:00 2001 From: cvf-bcn-gituser Date: Sat, 2 Mar 2024 14:59:23 +0100 Subject: [PATCH 5/5] [Unit Tests] Adding PitchMelodia UT and data files for tests --- .../pitchmelodia/pitchmelodiaconfidence.npy | Bin 0 -> 4400 bytes .../pitchmelodiaconfidence_eqloud.npy | Bin 0 -> 4400 bytes .../tonal/pitchmelodia/pitchmelodiapitch.npy | Bin 0 -> 4400 bytes .../pitchmelodia/pitchmelodiapitch_eqloud.npy | Bin 0 -> 4400 bytes test/src/unittests/tonal/test_pitchmelodia.py | 55 +++++++++--------- 5 files changed, 29 insertions(+), 26 deletions(-) create mode 100644 test/src/unittests/tonal/pitchmelodia/pitchmelodiaconfidence.npy create mode 100644 test/src/unittests/tonal/pitchmelodia/pitchmelodiaconfidence_eqloud.npy create mode 100644 test/src/unittests/tonal/pitchmelodia/pitchmelodiapitch.npy create mode 100644 test/src/unittests/tonal/pitchmelodia/pitchmelodiapitch_eqloud.npy diff --git a/test/src/unittests/tonal/pitchmelodia/pitchmelodiaconfidence.npy b/test/src/unittests/tonal/pitchmelodia/pitchmelodiaconfidence.npy new file mode 100644 index 0000000000000000000000000000000000000000..9ec2a122b3f0ad71c9b1f3d35e6e676496c3ba94 GIT binary patch literal 4400 zcmbR27wQ`j$;eQ~P_3SlTAW;@Zl$1ZlV+l>qoAIaUsO_*m=~X4l#&V(cT3DEP6dh= zXCxM+0{I$-24)sInmP)#3giMVhCu`ZQylC@@n{%~rh(BkFq#HN)4*sN7)=9%H4O}+ qW)q52SikAojpBg|1C+=Zq@0eFP`gn)8U~|jVC1ENL0Tjc?JNL!UAZIx literal 0 HcmV?d00001 diff --git a/test/src/unittests/tonal/pitchmelodia/pitchmelodiaconfidence_eqloud.npy b/test/src/unittests/tonal/pitchmelodia/pitchmelodiaconfidence_eqloud.npy new file mode 100644 index 0000000000000000000000000000000000000000..a476e4421a340a74878b9234aef1b73a763ce1fa GIT binary patch literal 4400 zcmbR27wQ`j$;eQ~P_3SlTAW;@Zl$1ZlV+l>qoAIaUsO_*m=~X4l#&V(cT3DEP6dh= zXCxM+0{I$-24)sInmP)#3giMVhCv9fZk%m9ibum>G!2ZVfzdQDng&MGz-Ss6Y-wN+ rHk*iclK+GWwxgIfVL)^w44}fO=PPYT@n{%~rh$={1_rR$p^DW2XBO{T literal 0 HcmV?d00001 diff --git a/test/src/unittests/tonal/pitchmelodia/pitchmelodiapitch.npy b/test/src/unittests/tonal/pitchmelodia/pitchmelodiapitch.npy new file mode 100644 index 0000000000000000000000000000000000000000..0359daa36422ced0a9bbac44def70e273ba575d5 GIT binary patch literal 4400 zcmd6pUuf1<7{}iTy6D2PEHYy)oxL-cZfa|&Y5A?U6-~ujq*U`&w=ActcXK+{pQ*iR zH&G@8;%Jewkgn_^EG-OnQ7MfGimW7vNUB9$#kw#*2flLr^_!cc!Cv_C{ND3B=Q+>w zeV+3@zt@-6*DYUpNv1v1nO)b|y0(y=Uz%OmG%s6Gnr&(+v=#E38d?hL8X3>!n_C;t zj<>GQKizote|c49bxp;LiqhvwNBg@xGmd^&esrG}pT7S;n0f630%`9UW8F~LyQfwl z?S0ap_ZhAI>h3y?HO4x&MVpt(xR_jqi|b;1dH<1E`yI(#<}~*%JBNmo%LS%PToLeg z?SuMz+e0cjTCeK{AJ#85kLcBom6~*9m9}Ia)#2P@`tg;=^=V;^u3z7zX)RBwa@Qsu zu6ssZQ`*$?XS-(icj%oZotiM~d0q8dm%6UluKeyD>Z*THWv7aI_rOb9wQ;Aas&{El z=H(HAxvT-K$Ir3J#o!a)`Tp%;pCik*DbxEx*gNiGm6gP)h_qur+JyU<}pP~MP<@)Y}n{`>=bnWdg(;MGUJBPtvrz(l~y`NbZ178NdLCK+P0Q<2o z*x7SSh~w&g6(J_#B0l0I-u#Dib?KM4g&e*exLt!2=c|3eLUpgWBjiY~9UtEn@+NmU z0PR!n5uBKx_!}U;Q>^p0aO-E!^S6L~oov20_+}4tP7Hm;#UUo*BTnLldvYL`V)v|& zFZsg_+#UPpCbivIsyAy(GM;h?{!3R@7+qp*f+q$d~-#1K!Oy9GXwx zlk~9fM0$HTFxT*%>a&0^?p}Pmc0L=#K+Tf%qEm`5Fr+x2x%=+N}vGw}cT3?2;J~y_GE4Gd~X_@1(c>Y*Fd(Wgb$D^@HUqk(O zZbQ9K8~;Dz@xEBs3LDqeb$3ss=@Qy$ZSq}$K5?H|qqxrj`hZRW?soJF?Eu_0@m-Iu iB;otjbG_EqCSFE4IuUAp|dRClU3y(Ztax{#h%onFvBH(ghqZtpB?EaWz1I}2;_ywBu1y7H&q zcdgAmmOu5owxPbUscuGH^;6Yn`gdXK9Q<2&;w~jI^NPFw@h)C-$$NKdEbQLZQgSZd z&oeg4;q%*C|8<@<|0T-#S8lFSpPDx*uNx2hoMro!jJ93LTCeFCTp}ZTY^kP9 zTo&+p%RTyY%e|^P+@{H+_vy!``}Jbaa!oq4LQkX~(80`u`tF5?^g~Tft0v{O`SM59 zTf1HdTOU{7l#Lqry<4+~d-TSlUQL+wv@U(APluLlQEulp^|d{#n&U;ix$ilxSifBj zjXN|a^?b!(J^R?tIUihE3}#c$SzW;WjQ}sJGgjk5@=*R0ct@F_UVl<8%eppFdwYFVJz)k;$s{`Q5_^7Zdyknj{ zoWi-JClvVl@BeAkKkv!RTC+8lk?7tZN&DI&#}LLj&tYO9IJEa zJh~3arvX>aE%{Bt^YQ4tj6O5AW&8HWb#9&=E3v~%vW7>#%!a#<2A~CK0@{E^pcQBa z+JT0kC+G@#gC^2j=uO8u?$D>1jJl`ZuA|>B(vjD0(aLqr`epij4V-M!;PFQNIy6@+ z56scX-g@;6%+`cG*Xy!9b=p2~ord?B`i61`T3vPe9=o-D+Ql;kX)j9`1n<{pRnu7~?hbMpgz-Qbe-5aUvNe;m~|}9wJbs>rlBd)*}1m(f*biS>yL!|9_0OnHAwIv$&5b$ literal 0 HcmV?d00001 diff --git a/test/src/unittests/tonal/test_pitchmelodia.py b/test/src/unittests/tonal/test_pitchmelodia.py index b523add54..f2d9e2a10 100644 --- a/test/src/unittests/tonal/test_pitchmelodia.py +++ b/test/src/unittests/tonal/test_pitchmelodia.py @@ -19,6 +19,8 @@ from essentia_test import * +import math as math +import numpy as np class TestPitchMelodia(TestCase): @@ -55,7 +57,7 @@ def testInvalidParam(self): self.assertConfigureFails(PitchMelodia(), {'timeContinuity': -1}) def testDefaultParameters(self): - signal = randn(1024) + signal = np.random.random(1024) pitch, confidence = PitchMelodia()(signal) # Assert that default parameters produce valid outputs self.assertIsNotNone(pitch) @@ -69,17 +71,17 @@ def testEmptyInput(self): def testZerosInput(self): signal = zeros(1024) pitch, confidence = PitchMelodia()(signal) - self.assertAlmostEqualVector(pitch, [0.] * 9) # Use [0.] * 9 for flexibility + self.assertAlmostEqualVector(pitch, [0.] * 9) self.assertAlmostEqualVector(confidence, [0.] * 9) def testOnesInput(self): signal = ones(1024) pitch, confidence = PitchMelodia()(signal) - self.assertAlmostEqualVector(pitch, [0.] * 9) # Use [0.] * 9 for flexibility + self.assertAlmostEqualVector(pitch, [0.] * 9) self.assertAlmostEqualVector(confidence, [0.] * 9) def testCustomParameters(self): - signal = randn(2048) + signal = np.random.random(2048) # Use custom parameters params = { 'binResolution': 5, @@ -107,54 +109,55 @@ def testCustomParameters(self): self.assertIsNotNone(confidence) def testInputWithSilence(self): - signal = concatenate([zeros(512), randn(1024), zeros(512)]) + rand_signal = np.random.random(512) + signal = np.concatenate([zeros(512), rand_signal, zeros(512)]) pitch, confidence = PitchMelodia()(signal) # Assert that silent portions don't have pitch information self.assertTrue(all(p == 0.0 for p in pitch[:512])) self.assertTrue(all(c == 0.0 for c in confidence[:512])) def testHighPitchResolution(self): - signal = randn(1024) - pitch, confidence = PitchMelodia(binResolution=1)(signal) - # Assert that using high bin resolution produces valid outputs + rand_signal = np.random.random(1024) + pitch, confidence = PitchMelodia(binResolution=1)(rand_signal) + # Assert that using high bin resolution produces valid outputs self.assertIsNotNone(pitch) self.assertIsNotNone(confidence) - self.assertEqual(len(pitch), 3) - self.assertEqual(len(confidence), 3) + self.assertEqual(len(pitch), 9) + self.assertEqual(len(confidence), 9) def testRealCase(self): filename = join(testdata.audio_dir, 'recorded', 'vignesh.wav') - audio = MonoLoader(filename=filename, sampleRate=44100)() + audio = MonoLoader(filename=filename)() pm = PitchMelodia() pitch, pitchConfidence = pm(audio) - # Save reference values for later loading - save('pitchmelodiapitch.npy', pitch) - save('pitchmelodiaconfidence.npy', pitchConfidence) + # np.save reference values for later np.loading + #np.save('pitchmelodiapitch.npy', pitch) + #np.save('pitchmelodiaconfidence.npy', pitchConfidence) - loadedPitchMelodiaPitch = load(join(filedir(), 'pitchmelodia/pitchmelodiapitch.npy')) - self.assertAlmostEqualVectorFixedPrecision(pitch, loadedPitchMelodiaPitch.tolist(), 8) + np.loadedPitchMelodiaPitch = np.load(join(filedir(), 'pitchmelodia/pitchmelodiapitch.npy')) + self.assertAlmostEqualVectorFixedPrecision(pitch, np.loadedPitchMelodiaPitch.tolist(), 8) - loadedPitchConfidence = load(join(filedir(), 'pitchmelodia/pitchmelodiaconfidence.npy')) - self.assertAlmostEqualVectorFixedPrecision(pitchConfidence, loadedPitchConfidence.tolist(), 8) + np.loadedPitchConfidence = np.load(join(filedir(), 'pitchmelodia/pitchmelodiaconfidence.npy')) + self.assertAlmostEqualVectorFixedPrecision(pitchConfidence, np.loadedPitchConfidence.tolist(), 8) def testRealCaseEqualLoudness(self): filename = join(testdata.audio_dir, 'recorded', 'vignesh.wav') - audio = MonoLoader(filename=filename, sampleRate=44100)() + audio = MonoLoader(filename=filename)() pm = PitchMelodia() eq = EqualLoudness() eqAudio = eq(audio) pitch, pitchConfidence = pm(eqAudio) - # Save reference values for later loading - save('pitchmelodiapitch_eqloud.npy', pitch) - save('pitchmelodiaconfidence_eqloud.npy', pitchConfidence) + # np.save reference values for later np.loading + #np.save('pitchmelodiapitch_eqloud.npy', pitch) + #np.save('pitchmelodiaconfidence_eqloud.npy', pitchConfidence) - loadedPitchMelodiaPitch = load(join(filedir(), 'pitchmelodia/pitchmelodiapitch_eqloud.npy')) - self.assertAlmostEqualVectorFixedPrecision(pitch, loadedPitchMelodiaPitch.tolist(), 8) + np.loadedPitchMelodiaPitch = np.load(join(filedir(), 'pitchmelodia/pitchmelodiapitch_eqloud.npy')) + self.assertAlmostEqualVectorFixedPrecision(pitch, np.loadedPitchMelodiaPitch.tolist(), 8) - loadedPitchConfidence = load(join(filedir(), 'pitchmelodia/pitchmelodiaconfidence_eqloud.npy')) - self.assertAlmostEqualVectorFixedPrecision(pitchConfidence, loadedPitchConfidence.tolist(), 8) + np.loadedPitchConfidence = np.load(join(filedir(), 'pitchmelodia/pitchmelodiaconfidence_eqloud.npy')) + self.assertAlmostEqualVectorFixedPrecision(pitchConfidence, np.loadedPitchConfidence.tolist(), 8) def test110Hz(self): signal = 0.5 * numpy.sin((array(range(10 * 4096))/44100.) * 110 * 2*math.pi)