Question regarding notefreq code

Status
Not open for further replies.

polar

New member
Hi

I have a few questions regading the code of analzye_notefreq. Since it was written by duff most probably can answer it, but some questions are rather general, so maybe sombody else can also provide some insight. I have a background in programming in different languages, but I am by no means a c/c++ expert. Also I normally programm on "high level", means not close to hardware and rarely have to really optimize code. Therefore some questions might be basic.
I had read the YIN paper a while ago and also wrote matlab code that works. I was glad that there already exists a optimized version for teensy which worked well for my project. I had a look at the code to see whether I could adapt it a little for my specific needs to improve detection. Then some questions surfaced :)

first question is around line 106 in the calculation of the difference function:
Code:
            lag = *( ( int16_t * )p + ( x+tau ) );
            current = *( ( int16_t * )p+x );
            delta = ( current-lag );
            sum += delta * delta;
            x += 4;

x is incremented by 4 and therefore also the pointer to the audiobuffer. it looks like the audio buffer is subsampled by a factor of 4? is that correct?

the the block above is copied 4 times in the while loop. is this to reduce the "while checks" on the end? does this really make much difference? also, the number of "iterations" to generate the sum is depends on AUDIO_GUITARTUNER_BLOCKS, so one could also use a for loop. I assume there is a reason to use a while?

next question is line 191, in the parabolic interpolation routine. the indices to the yin "ringbuffer" are calculated. if the head index is 4. idx1 is reset to 0 (which is correct, as the rinbuffer has 5 elements), however idx2 is also set to 0, but I think it should be 1.

Code:
        idx0 = _head;
        idx1 = _head + 1;
        idx1 = ( idx1 >= 5 ) ? 0 : idx1;
        idx2 = head + 2;
        idx2 = ( idx2 >= 5 ) ? 0 : idx2;

general question (e.g. line 260) regarding passing values. what is the reason for disabling and later enabling interrupts?

Code:
float AudioAnalyzeNoteFrequency::probability( void ) {
    __disable_irq( );
    float p = periodicity;    
    __enable_irq( );
    return p;



thanks

Polar
 
Code:
            lag = *( ( int16_t * )p + ( x+tau ) );
            current = *( ( int16_t * )p+x );
            delta = ( current-lag );
            sum += delta * delta;
            x += 4;

x is incremented by 4 and therefore also the pointer to the audiobuffer. it looks like the audio buffer is subsampled by a factor of 4? is that correct?
Yes, it was done for performance issues. You also should lowpass filter the data stream before feeding it to notefreq. If you look at my GitHub version I do that for you when decimating the audio buffer using the CMSIS arm_fir_decimate_fast_q15.

the the block above is copied 4 times in the while loop. is this to reduce the "while checks" on the end? does this really make much difference? also, the number of "iterations" to generate the sum is depends on AUDIO_GUITARTUNER_BLOCKS, so one could also use a for loop. I assume there is a reason to use a while?
In my tests it did help to manually unroll but yes you could just do that 1 time. You could probably rework it with a for loop if you want.

next question is line 191, in the parabolic interpolation routine. the indices to the yin "ringbuffer" are calculated. if the head index is 4. idx1 is reset to 0 (which is correct, as the rinbuffer has 5 elements), however idx2 is also set to 0, but I think it should be 1.

Code:
        idx0 = _head;
        idx1 = _head + 1;
        idx1 = ( idx1 >= 5 ) ? 0 : idx1;
        idx2 = head + 2;
        idx2 = ( idx2 >= 5 ) ? 0 : idx2;

yes this is problem but its idx2 that needs to be fixed look at my GitHub code for the fix.

general question (e.g. line 260) regarding passing values. what is the reason for disabling and later enabling interrupts?

Code:
float AudioAnalyzeNoteFrequency::probability( void ) {
    __disable_irq( );
    float p = periodicity;    
    __enable_irq( );
    return p;
periodicity is not declared volatile, this is also true with the variable data. There is small speed up doing this way.
 
Also in the bigger picture this library uses double buffering so when one buffer is filling up the other is being worked on, this helps with latency. This algorithm has a high cpu load so I had to split up the processing between Audio Blocks to make it work on a Teensy 3.2. It does not start processing the next buffer until that buffer is full which is determined by the AUDIO_GUITARTUNER_BLOCKS.
 
Thanks a lot for the answers. Good to know that there is an improved version, I will have a look at it :) Your original version worked fine on my teensy 3.2 in terms of cpu load as far as I can tell. Also I was lucky regarding the subsampling I was not aware of: As was only interested in frequencies below 1kHz I put a RC filter on the mic to get rid of higher, unwanted disturbances which I thought should improve detection. With the 3dB point at approx 500Hz, this was kind of overkill in terms of antialiasing if one considers a the samplerate is 44.1kHz, but it doesn't hurt anyways. Now I know Fs is more like 10kHz, but that still turns our fine, since even it is only a simple RC it has approx -20dB at 10kHz, still good enough as antialiasing filter. I will try with your new release, if i don't decimate below 10kHz I dont think I need the FIR :)
 
Status
Not open for further replies.
Back
Top