changeset 269:446f6d3dc809

Add sawtooth tests
author Jamie Bullock <jamie@jamiebullock.com>
date Tue, 11 Nov 2014 17:30:17 +0000
parents b371ffcecb74
children 3d0fa7347500
files README.md VERSION tests/.xttest_scalar.cpp.swp tests/xttest_scalar.cpp tests/xttest_util.cpp tests/xttest_util.hpp
diffstat 6 files changed, 569 insertions(+), 3 deletions(-) [+]
line wrap: on
line diff
--- a/README.md	Tue Nov 11 17:01:47 2014 +0000
+++ b/README.md	Tue Nov 11 17:30:17 2014 +0000
@@ -36,7 +36,6 @@
 **Pre-generated documentation can be found on the [LibXtract website](http://jamiebullock.github.io/LibXtract/documentation)**
 
 
-
 ## LicenseĀ 
 
 Copyright (C) 2012 Jamie Bullock
--- a/VERSION	Tue Nov 11 17:01:47 2014 +0000
+++ b/VERSION	Tue Nov 11 17:30:17 2014 +0000
@@ -1,1 +1,1 @@
-0.7.2
+0.8.0
Binary file tests/.xttest_scalar.cpp.swp has changed
--- a/tests/xttest_scalar.cpp	Tue Nov 11 17:01:47 2014 +0000
+++ b/tests/xttest_scalar.cpp	Tue Nov 11 17:30:17 2014 +0000
@@ -532,3 +532,557 @@
         }
     }
 }
+
+SCENARIO( "F0 is correctly detected for a sawtooth wave", "[xtract_f0]" )
+{
+    uint16_t expected = 0;
+    uint16_t actual = 0;
+
+    GIVEN( "a 512 sample block with a sample rate of 44100" )
+    {
+        uint32_t blocksize = 512;
+        double samplerate = 44100;
+        double result = -1.0;
+        double amplitude = 1.0;
+        double table[blocksize];
+
+        WHEN( "the frequency is 86.1328125 Hz" ) // period of exactly 512 samples: 1 cycles in the block
+        {
+            double frequency = 86.1328125;
+
+            xttest_gen_sawtooth(table, blocksize, samplerate, frequency, amplitude);
+            int rv = xtract_f0(table, blocksize, &samplerate, &result);
+
+            THEN( "frequency detection fails correctly (XTRACT_NO_RESULT is returned, result set to 0.0)" )
+            {
+                REQUIRE(rv == XTRACT_NO_RESULT); 
+                REQUIRE(result == 0.0);
+            }
+        }
+
+        WHEN( "the frequency is 172.265625 Hz" ) // period of exactly 256 samples: 2 cycles in the block
+        {
+            double frequency = 172.265625;
+
+            xttest_gen_sawtooth(table, blocksize, samplerate, frequency, amplitude);
+            int rv = xtract_f0(table, blocksize, &samplerate, &result);
+
+           THEN( "the detected F0 is accurate to the nearest quarter-tone" )
+            {
+                actual = xttest_ftom(result);
+                expected = xttest_ftom(frequency);
+                uint16_t min = expected - 50;
+                uint16_t max = expected + 50;
+                CAPTURE( actual );
+                CAPTURE( expected );
+                REQUIRE( actual > min ); 
+                REQUIRE( actual < max ); 
+            }     
+        }
+
+
+        WHEN( "the frequency is 344.53125 Hz" ) // period of exactly 128 samples: 4 cycles in the block
+        {
+            double frequency = 344.53125;
+
+            xttest_gen_sawtooth(table, blocksize, samplerate, frequency, amplitude);
+            xtract_f0(table, blocksize, &samplerate, &result);
+
+            THEN( "the detected F0 is accurate to the nearest quarter-tone" )
+            {
+                actual = xttest_ftom(result);
+                expected = xttest_ftom(frequency);
+                uint16_t min = expected - 50;
+                uint16_t max = expected + 50;
+                CAPTURE( actual );
+                CAPTURE( expected );
+                REQUIRE( actual > min ); 
+                REQUIRE( actual < max ); 
+            }     
+
+
+            WHEN( "the amplitude is 0.01" ) // Only test a different amplitude for one case
+            {
+                double amplitude = 0.01;
+
+                xttest_gen_sawtooth(table, blocksize, samplerate, frequency, amplitude);
+                xtract_f0(table, blocksize, &samplerate, &result);
+
+                THEN( "the detected F0 is accurate to the nearest quarter-tone" )
+                {
+                    actual = xttest_ftom(result);
+                    expected = xttest_ftom(frequency);
+                    uint16_t min = expected - 50;
+                    uint16_t max = expected + 50;
+                    CAPTURE( actual );
+                    CAPTURE( expected );
+                    REQUIRE( actual > min ); 
+                    REQUIRE( actual < max ); 
+                }     
+            }
+        }
+    }
+
+    GIVEN( "a 1024 sample block with a sample rate of 44100" )
+    {
+        uint32_t blocksize      = 1024;
+        double samplerate       = 44100;
+        double result           = -1.0;
+        double amplitude        = 1.0;
+        double table[blocksize];
+
+        WHEN( "the frequency is 86.1328125 Hz" ) // period of exactly 512 samples: 2 cycles in the block
+        {
+            double frequency = 86.1328125;
+
+            xttest_gen_sawtooth(table, blocksize, samplerate, frequency, amplitude);
+            int rv = xtract_f0(table, blocksize, &samplerate, &result);
+
+            THEN( "the detected F0 is accurate to the nearest quarter-tone" )
+            {
+                actual = xttest_ftom(result);
+                expected = xttest_ftom(frequency);
+                uint16_t min = expected - 50;
+                uint16_t max = expected + 50;
+                CAPTURE( actual );
+                CAPTURE( expected );
+                REQUIRE( actual > min ); 
+                REQUIRE( actual < max ); 
+            }     
+        }
+
+        WHEN( "the frequency is 140 Hz" ) // period of 315 samples: 3.25 cycles in the block
+        {
+            double frequency = 140;
+
+            xttest_gen_sawtooth(table, blocksize, samplerate, frequency, amplitude);
+            xtract_f0(table, blocksize, &samplerate, &result);
+
+            THEN( "the detected F0 is accurate to the nearest MIDI cent" )
+            {
+                actual = xttest_ftom(result);
+                expected = xttest_ftom(frequency);
+                CAPTURE( actual );
+                CAPTURE( expected );
+                REQUIRE(actual == expected); 
+            }
+        }
+
+        WHEN( "the frequency is 155 Hz" ) // period of 284.52 samples: 3.6 cycles in the block
+        {
+            double frequency = 155;
+
+            xttest_gen_sawtooth(table, blocksize, samplerate, frequency, amplitude);
+            xtract_f0(table, blocksize, &samplerate, &result);
+
+            THEN( "the detected F0 is quantized to the nearest whole number of samples" )
+            {
+                actual = xttest_ftom(result);
+                expected = xttest_ftom(155.28169014); // period of 284 samples
+                CAPTURE( result );
+                CAPTURE( expected );
+                REQUIRE(actual == expected); 
+            }
+        }
+
+
+        WHEN( "the frequency is 172.265625 Hz" ) // period of exactly 256 samples: 4 cycles in the block
+        {
+            double frequency = 172.265625;
+
+            xttest_gen_sawtooth(table, blocksize, samplerate, frequency, amplitude);
+            xtract_f0(table, blocksize, &samplerate, &result);
+
+            THEN( "the detected F0 is accurate to the nearest quarter-tone" )
+            {
+                actual = xttest_ftom(result);
+                expected = xttest_ftom(frequency);
+                uint16_t min = expected - 50;
+                uint16_t max = expected + 50;
+                CAPTURE( actual );
+                CAPTURE( expected );
+                REQUIRE( actual > min ); 
+                REQUIRE( actual < max ); 
+            }     
+        }
+
+        WHEN( "the frequency is 344.53125 Hz" ) // period of exactly 128 samples: 8 cycles in the block
+        {
+            double frequency = 344.53125;
+            double noise[blocksize];
+            expected = xttest_ftom(frequency);
+            CAPTURE( expected );
+
+            WHEN( "the amplitude is 1.0" )
+            {
+                xttest_gen_sawtooth(table, blocksize, samplerate, frequency, amplitude);
+                xtract_f0(table, blocksize, &samplerate, &result);
+
+                THEN( "the detected F0 is accurate to the nearest quarter-tone" )
+                {
+                    actual = xttest_ftom(result);
+                    expected = xttest_ftom(frequency);
+                    uint16_t min = expected - 50;
+                    uint16_t max = expected + 50;
+                    CAPTURE( actual );
+                    CAPTURE( expected );
+                    REQUIRE( actual > min ); 
+                    REQUIRE( actual < max ); 
+                }     
+            }
+
+            WHEN( "the amplitude is 0.01" ) // Only test a different amplitude for one case
+            {
+                amplitude = 0.01;
+
+                xttest_gen_sawtooth(table, blocksize, samplerate, frequency, amplitude);
+                xtract_f0(table, blocksize, &samplerate, &result);
+
+                THEN( "the detected F0 is accurate to the nearest quarter-tone" )
+                {
+                    actual = xttest_ftom(result);
+                    expected = xttest_ftom(frequency);
+                    uint16_t min = expected - 50;
+                    uint16_t max = expected + 50;
+                    CAPTURE( actual );
+                    CAPTURE( expected );
+                    REQUIRE( actual > min ); 
+                    REQUIRE( actual < max ); 
+                }     
+            }
+
+            WHEN( "white noise is added at 10%" ) // Only test noise for one case
+            {
+                amplitude = 0.1;
+
+                xttest_gen_sawtooth(table, blocksize, samplerate, frequency, 1.0 - amplitude);
+                xttest_gen_noise(noise, blocksize, amplitude);
+                xttest_add(table, noise, blocksize);
+                xtract_f0(table, blocksize, &samplerate, &result);
+
+               THEN( "the detected F0 is accurate to the nearest quarter-tone" )
+                {
+                    actual = xttest_ftom(result);
+                    expected = xttest_ftom(frequency);
+                    uint16_t min = expected - 50;
+                    uint16_t max = expected + 50;
+                    CAPTURE( actual );
+                    CAPTURE( expected );
+                    REQUIRE( actual > min ); 
+                    REQUIRE( actual < max ); 
+                }     
+            }
+
+            WHEN( "white noise is added at 20%" )
+            {
+                amplitude = 0.2;
+
+                xttest_gen_sawtooth(table, blocksize, samplerate, frequency, 1.0 - amplitude);
+                xttest_gen_noise(noise, blocksize, amplitude);
+                xttest_add(table, noise, blocksize);
+                xtract_f0(table, blocksize, &samplerate, &result);
+
+               THEN( "the detected F0 is accurate to the nearest quarter-tone" )
+                {
+                    actual = xttest_ftom(result);
+                    expected = xttest_ftom(frequency);
+                    uint16_t min = expected - 50;
+                    uint16_t max = expected + 50;
+                    CAPTURE( actual );
+                    CAPTURE( expected );
+                    REQUIRE( actual > min ); 
+                    REQUIRE( actual < max ); 
+                }     
+            }
+
+            WHEN( "white noise is added at 25%" )
+            {
+                amplitude = 0.25;
+
+                xttest_gen_sawtooth(table, blocksize, samplerate, frequency, 1.0 - amplitude);
+                xttest_gen_noise(noise, blocksize, amplitude);
+                xttest_add(table, noise, blocksize);
+                xtract_f0(table, blocksize, &samplerate, &result);
+
+               THEN( "the detected F0 is accurate to the nearest quarter-tone" )
+                {
+                    actual = xttest_ftom(result);
+                    expected = xttest_ftom(frequency);
+                    uint16_t min = expected - 50;
+                    uint16_t max = expected + 50;
+                    CAPTURE( actual );
+                    CAPTURE( expected );
+                    REQUIRE( actual > min ); 
+                    REQUIRE( actual < max ); 
+                }     
+            }
+
+            WHEN( "white noise is added at 30%" )
+            {
+                amplitude = 0.25;
+
+                xttest_gen_sawtooth(table, blocksize, samplerate, frequency, 1.0 - amplitude);
+                xttest_gen_noise(noise, blocksize, amplitude);
+                xttest_add(table, noise, blocksize);
+                xtract_f0(table, blocksize, &samplerate, &result);
+
+                THEN( "the detected F0 is accurate to the nearest quarter-tone" )
+                {
+                    actual = xttest_ftom(result);
+                    uint16_t min = expected - 50;
+                    uint16_t max = expected + 50;
+                    CAPTURE( actual );
+                    REQUIRE( actual > min ); 
+                    REQUIRE( actual < max ); 
+                }
+            }
+
+            WHEN( "white noise is added at 35%" )
+            {
+                amplitude = 0.35;
+
+                xttest_gen_sawtooth(table, blocksize, samplerate, frequency, 1.0 - amplitude);
+                xttest_gen_noise(noise, blocksize, amplitude);
+                xttest_add(table, noise, blocksize);
+                xtract_f0(table, blocksize, &samplerate, &result);
+
+                THEN( "the detected F0 is accurate to the nearest quarter-tone" )
+                {
+                    actual = xttest_ftom(result);
+                    expected = xttest_ftom(frequency);
+                    uint16_t min = expected - 50;
+                    uint16_t max = expected + 50;
+                    CAPTURE( actual );
+                    CAPTURE( expected );
+                    REQUIRE( actual > min ); 
+                    REQUIRE( actual < max ); 
+                }     
+            }
+        }
+    }
+
+    GIVEN( "a 1024 sample block with a sample rate of 11025" )
+    {
+        uint32_t blocksize      = 1024;
+        double samplerate       = 11025;
+        double result           = -1.0;
+        double table[blocksize];
+
+        WHEN( "the frequency is 86.1328125 Hz" ) // period of exactly 512 samples: 2 cycles in the block
+        {
+            double frequency = 86.1328125;
+            double amplitude = 1.0;
+
+            xttest_gen_sawtooth(table, blocksize, samplerate, frequency, amplitude);
+            int rv = xtract_f0(table, blocksize, &samplerate, &result);
+
+            THEN( "the detected F0 is accurate to the nearest quarter-tone" )
+            {
+                actual = xttest_ftom(result);
+                expected = xttest_ftom(frequency);
+                uint16_t min = expected - 50;
+                uint16_t max = expected + 50;
+                CAPTURE( actual );
+                CAPTURE( expected );
+                REQUIRE( actual > min ); 
+                REQUIRE( actual < max ); 
+            }     
+        }
+
+        WHEN( "the frequency is 172.265625 Hz" ) // period of exactly 256 samples: 4 cycles in the block
+        {
+            double frequency = 172.265625;
+            double amplitude = 1.0;
+
+            xttest_gen_sawtooth(table, blocksize, samplerate, frequency, amplitude);
+            xtract_f0(table, blocksize, &samplerate, &result);
+            
+            THEN( "the detected F0 is accurate to the nearest quarter-tone" )
+            {
+                actual = xttest_ftom(result);
+                expected = xttest_ftom(frequency);
+                uint16_t min = expected - 50;
+                uint16_t max = expected + 50;
+                CAPTURE( actual );
+                CAPTURE( expected );
+                REQUIRE( actual > min ); 
+                REQUIRE( actual < max ); 
+            }    
+        }
+
+        WHEN( "the frequency is 344.53125 Hz" ) // period of exactly 128 samples: 8 cycles in the block
+        {
+            double frequency = 344.53125;
+            expected = xttest_ftom(frequency);
+            CAPTURE( expected );
+
+            WHEN( "the amplitude is 1.0" )
+            {
+                double amplitude = 1.0;
+
+                xttest_gen_sawtooth(table, blocksize, samplerate, frequency, amplitude);
+                xtract_f0(table, blocksize, &samplerate, &result);
+
+                THEN( "the detected F0 is accurate to the nearest quarter-tone" )
+                {
+                    actual = xttest_ftom(result);
+                    expected = xttest_ftom(frequency);
+                    uint16_t min = expected - 50;
+                    uint16_t max = expected + 50;
+                    CAPTURE( actual );
+                    CAPTURE( expected );
+                    REQUIRE( actual > min ); 
+                    REQUIRE( actual < max ); 
+                }    
+            }
+
+            WHEN( "the amplitude is 0.01" ) // Only test a different amplitude for one case
+            {
+                double amplitude = 0.01;
+
+                xttest_gen_sawtooth(table, blocksize, samplerate, frequency, amplitude);
+                xtract_f0(table, blocksize, &samplerate, &result);
+
+               THEN( "the detected F0 is accurate to the nearest quarter-tone" )
+                {
+                    actual = xttest_ftom(result);
+                    expected = xttest_ftom(frequency);
+                    uint16_t min = expected - 50;
+                    uint16_t max = expected + 50;
+                    CAPTURE( actual );
+                    CAPTURE( expected );
+                    REQUIRE( actual > min ); 
+                    REQUIRE( actual < max ); 
+                }    
+            }
+
+            WHEN( "white noise is added at 20%" )
+            {
+                double amplitude = 0.2;
+                double noise[blocksize];
+
+                xttest_gen_sawtooth(table, blocksize, samplerate, frequency, 1.0 - amplitude);
+                xttest_gen_noise(noise, blocksize, amplitude);
+                xttest_add(table, noise, blocksize);
+                xtract_f0(table, blocksize, &samplerate, &result);
+
+                THEN( "the detected F0 is accurate to the nearest quarter-tone" )
+                {
+                    actual = xttest_ftom(result);
+                    uint16_t min = expected - 50;
+                    uint16_t max = expected + 50;
+                    CAPTURE( actual );
+                    REQUIRE( actual > min ); 
+                    REQUIRE( actual < max ); 
+                }        
+            }
+
+            WHEN( "white noise is added at 40%" )
+            {
+                double amplitude = 0.4;
+                double noise[blocksize];
+
+                xttest_gen_sawtooth(table, blocksize, samplerate, frequency, 1.0 - amplitude);
+                xttest_gen_noise(noise, blocksize, amplitude);
+                xttest_add(table, noise, blocksize);
+                xtract_f0(table, blocksize, &samplerate, &result);
+
+                THEN( "the detected F0 is inaccurate by more than one semitone" )
+                {
+                    actual = xttest_ftom(result);
+                    uint16_t difference = abs(expected - actual);
+                    CAPTURE( actual );
+                    REQUIRE( difference > 100 ); 
+                }
+            }
+        }
+    }
+
+    GIVEN( "a 2048 sample block with a sample rate of 44100" )
+    {
+        uint32_t blocksize      = 2048;
+        double samplerate       = 44100;
+        double result           = -1.0;
+        double table[blocksize];
+
+        WHEN( "the frequency is 43.06640625 Hz" ) // period of exactly 256 samples: 2 cycles in the block
+        {
+            double frequency = 43.06640625;
+            double amplitude = 1.0;
+
+            xttest_gen_sawtooth(table, blocksize, samplerate, frequency, amplitude);
+            int rv = xtract_f0(table, blocksize, &samplerate, &result);
+
+            THEN( "the detected F0 is accurate to the nearest quarter-tone" )
+            {
+                actual = xttest_ftom(result);
+                expected = xttest_ftom(frequency);
+                uint16_t min = expected - 50;
+                uint16_t max = expected + 50;
+                CAPTURE( actual );
+                CAPTURE( expected );
+                REQUIRE( actual > min ); 
+                REQUIRE( actual < max ); 
+            }       
+        }
+
+        WHEN( "the frequency is 86.1328125 Hz" ) // period of exactly 512 samples: 4 cycles in the block
+        {
+            double frequency = 86.1328125;
+            double amplitude = 1.0;
+
+            xttest_gen_sawtooth(table, blocksize, samplerate, frequency, amplitude);
+            int rv = xtract_f0(table, blocksize, &samplerate, &result);
+
+            THEN( "the detected F0 is accurate to the nearest MIDI cent" )
+            {
+                actual = xttest_ftom(result);
+                expected = xttest_ftom(frequency);
+                CAPTURE( actual );
+                CAPTURE( expected );
+                REQUIRE(actual == expected); 
+            }
+        }
+
+        WHEN( "the frequency is 172.265625 Hz" ) // period of exactly 256 samples: 8 cycles in the block
+        {
+            double frequency = 172.265625;
+            double amplitude = 1.0;
+
+            xttest_gen_sawtooth(table, blocksize, samplerate, frequency, amplitude);
+            xtract_f0(table, blocksize, &samplerate, &result);
+
+            THEN( "the detected F0 is accurate to the nearest MIDI cent" )
+            {
+                actual = xttest_ftom(result);
+                expected = xttest_ftom(frequency);
+                CAPTURE( actual );
+                CAPTURE( expected );
+                REQUIRE(actual == expected); 
+            }
+        }
+
+        WHEN( "the frequency is 344.53125 Hz" ) // period of exactly 128 samples: 16 cycles in the block
+        {
+            double frequency = 344.53125;
+
+            WHEN( "the amplitude is 1.0" )
+            {
+                double amplitude = 1.0;
+
+                xttest_gen_sawtooth(table, blocksize, samplerate, frequency, amplitude);
+                xtract_f0(table, blocksize, &samplerate, &result);
+
+                THEN( "the detected F0 is accurate to the nearest MIDI cent" )
+                {
+                    actual = xttest_ftom(result);
+                    expected = xttest_ftom(frequency);
+                    CAPTURE( actual );
+                    CAPTURE( expected );
+                    REQUIRE(actual == expected); 
+                }
+            }        
+        }
+    }
+}
+
--- a/tests/xttest_util.cpp	Tue Nov 11 17:01:47 2014 +0000
+++ b/tests/xttest_util.cpp	Tue Nov 11 17:30:17 2014 +0000
@@ -22,6 +22,17 @@
     }
 }
 
+void xttest_gen_sawtooth(double *table, uint32_t tablesize, double samplerate, double frequency, double amplitude)
+{
+    int samples_per_period = samplerate / frequency;
+
+    for (uint32_t i = 0; i < tablesize; ++i)
+    {
+        int phase = i % samples_per_period;
+        table[i] = ((phase / (double)samples_per_period) * 2.0 - 1.0) * amplitude;
+    }
+}
+
 void xttest_gen_noise(double *table, uint32_t tablesize, double amplitude)
 {
     for (uint32_t i = 0; i < tablesize; ++i)
@@ -30,7 +41,6 @@
     }
 }
 
-
 uint16_t xttest_ftom(double frequency)
 {
     return (int)roundf(6900.0 + 1200.0 * log2(frequency / 440.0));
--- a/tests/xttest_util.hpp	Tue Nov 11 17:01:47 2014 +0000
+++ b/tests/xttest_util.hpp	Tue Nov 11 17:30:17 2014 +0000
@@ -5,6 +5,9 @@
 // Fill table with sine wave at given frequency and amplitude
 void xttest_gen_sine(double *table, uint32_t tablesize, double samplerate, double frequency, double amplitude);
 
+// Fill table with sawtooth wave at given frequency and amplitude
+void xttest_gen_sawtooth(double *table, uint32_t tablesize, double samplerate, double frequency, double amplitude);
+
 // Fill table with noise at given frequency and amplitude
 // N.B. The implementation actually provides "fake" noise from a table for reproducible testing
 void xttest_gen_noise(double *table, uint32_t tablesize, double amplitude);