├── examples ├── Sample_Guitar_Tunning_Notes │ ├── a2_note.h │ ├── b3_note.h │ ├── d3_note.h │ ├── e2_note.h │ ├── e4_note.h │ ├── g3_note.h │ ├── coeff.h │ └── Sample_Guitar_Tunning_Notes.ino └── Simple_Sine │ ├── coeff.h │ ├── Handle_Commands.ino │ └── Simple_Sine.ino ├── library.properties ├── keywords.txt ├── license.md ├── revision.md ├── AudioTuner.h ├── README.md └── AudioTuner.cpp /examples/Sample_Guitar_Tunning_Notes/a2_note.h: -------------------------------------------------------------------------------- 1 | #include "Arduino.h" 2 | extern const unsigned int a2_note[53971]; 3 | -------------------------------------------------------------------------------- /examples/Sample_Guitar_Tunning_Notes/b3_note.h: -------------------------------------------------------------------------------- 1 | #include "Arduino.h" 2 | extern const unsigned int b3_note[53990]; 3 | -------------------------------------------------------------------------------- /examples/Sample_Guitar_Tunning_Notes/d3_note.h: -------------------------------------------------------------------------------- 1 | #include "Arduino.h" 2 | extern const unsigned int d3_note[53974]; 3 | -------------------------------------------------------------------------------- /examples/Sample_Guitar_Tunning_Notes/e2_note.h: -------------------------------------------------------------------------------- 1 | #include "Arduino.h" 2 | extern const unsigned int e2_note[53990]; 3 | -------------------------------------------------------------------------------- /examples/Sample_Guitar_Tunning_Notes/e4_note.h: -------------------------------------------------------------------------------- 1 | #include "Arduino.h" 2 | extern const unsigned int e4_note[53990]; 3 | -------------------------------------------------------------------------------- /examples/Sample_Guitar_Tunning_Notes/g3_note.h: -------------------------------------------------------------------------------- 1 | #include "Arduino.h" 2 | extern const unsigned int g3_note[53965]; 3 | -------------------------------------------------------------------------------- /library.properties: -------------------------------------------------------------------------------- 1 | name=AudioTuner 2 | version=3.3 3 | author=Colin Duffy 4 | maintainer=Colin Duffy 5 | sentence=Yin algorithm 6 | paragraph=Yin algorithm to detect the fundmental frequency. 7 | category=Signal Input/Output 8 | url=http://github.com/duff2013/AudioTuner 9 | architectures=* 10 | -------------------------------------------------------------------------------- /examples/Simple_Sine/coeff.h: -------------------------------------------------------------------------------- 1 | int16_t fir_44117_HZ_coefficients[22] = 2 | { 3 | 0, 3, 6, -11, -71, 21, 4 | 352, -15, -1202, -6, 5011, 8209, 5 | 5011, -6, -1202, -15, 352, 21, 6 | -71, -11, 6, 3 7 | }; 8 | 9 | int16_t fir_22059_HZ_coefficients[20] = 10 | { 11 | 0, 1, -6, -54, 18, 326, 12 | -14, -1178, -6, 5001, 8209, 5001, 13 | -6, -1178, -14, 326, 18, -54, 14 | -6, 1 15 | }; 16 | 17 | int16_t fir_11029_HZ_coefficients[22] = 18 | { 19 | 0, 3, 6, -11, -71, 21, 20 | 352, -15, -1202, -6, 5011, 8209, 21 | 5011, -6, -1202, -15, 352, 21, 22 | -71, -11, 6, 3 23 | }; 24 | 25 | int16_t fir_5515_HZ_coefficients[20] = 26 | { 27 | 0, 6, 4, -94, -2, 409, 28 | -11, -1267, 23, 5040, 8164, 5040, 29 | 23, -1267, -11, 409, -2, -94, 30 | 4, 6 31 | }; 32 | -------------------------------------------------------------------------------- /examples/Sample_Guitar_Tunning_Notes/coeff.h: -------------------------------------------------------------------------------- 1 | int16_t fir_44117_HZ_coefficients[22] = 2 | { 3 | 0, 3, 6, -11, -71, 21, 4 | 352, -15, -1202, -6, 5011, 8209, 5 | 5011, -6, -1202, -15, 352, 21, 6 | -71, -11, 6, 3 7 | }; 8 | 9 | int16_t fir_22059_HZ_coefficients[20] = 10 | { 11 | 0, 1, -6, -54, 18, 326, 12 | -14, -1178, -6, 5001, 8209, 5001, 13 | -6, -1178, -14, 326, 18, -54, 14 | -6, 1 15 | }; 16 | 17 | int16_t fir_11029_HZ_coefficients[22] = 18 | { 19 | 0, 3, 6, -11, -71, 21, 20 | 352, -15, -1202, -6, 5011, 8209, 21 | 5011, -6, -1202, -15, 352, 21, 22 | -71, -11, 6, 3 23 | }; 24 | 25 | int16_t fir_5515_HZ_coefficients[20] = 26 | { 27 | 0, 6, 4, -94, -2, 409, 28 | -11, -1267, 23, 5040, 8164, 5040, 29 | 23, -1267, -11, 409, -2, -94, 30 | 4, 6 31 | }; 32 | -------------------------------------------------------------------------------- /keywords.txt: -------------------------------------------------------------------------------- 1 | ####################################### 2 | # Syntax Coloring Map AudioTuner.h 3 | ####################################### 4 | 5 | ####################################### 6 | # Datatypes (KEYWORD1) 7 | ####################################### 8 | AudioTuner KEYWORD1 9 | ####################################### 10 | # Methods and Functions (KEYWORD2) 11 | ####################################### 12 | AudioTuner KEYWORD2 13 | available KEYWORD2 14 | probability KEYWORD2 15 | initialize KEYWORD2 16 | read KEYWORD2 17 | ####################################### 18 | # Instances (KEYWORD2) 19 | ####################################### 20 | 21 | ####################################### 22 | # Constants (LITERAL1) 23 | ####################################### 24 | threshold LITERAL1 25 | periodicity LITERAL1 26 | tuner LITERAL1 -------------------------------------------------------------------------------- /examples/Simple_Sine/Handle_Commands.ino: -------------------------------------------------------------------------------- 1 | void handleCmds( String cmd ) { 2 | String p = cmd; 3 | 4 | if (p.startsWith("f ")) { 5 | p.trim(); 6 | p = p.substring(2); 7 | float t = p.toFloat(); 8 | Serial.print("new frequency: "); 9 | Serial.println(t); 10 | AudioNoInterrupts(); // disable audio library momentarily 11 | sine.frequency(p.toFloat()); 12 | AudioInterrupts(); // enable, both tones will start together 13 | } 14 | else if (p.startsWith("a ")) { 15 | p.trim(); 16 | p = p.substring(2); 17 | float t = p.toFloat(); 18 | Serial.print("new amplitude: "); 19 | Serial.println(t); 20 | AudioNoInterrupts(); // disable audio library momentarily 21 | sine.amplitude(p.toFloat()); 22 | AudioInterrupts(); // enable, both tones will start together 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /license.md: -------------------------------------------------------------------------------- 1 | Copyright 2015 Colin Duffy 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 8 | -------------------------------------------------------------------------------- /revision.md: -------------------------------------------------------------------------------- 1 | >Updated (1/17/18 v3.2)
2 | * Fixed disable function.
3 | 4 | >Updated (1/12/18 v3.2)
5 | * Added disable function.
6 | 7 | >Updated (12/7/17 v3.1)
8 | * Fix wrong include.
9 | 10 | >Updated (02/17/17 v3.0)
11 | * Now we decimate the signal before analysis, significant speed up.
12 | * More robust algorithm to determine the fundamental frequency.
13 | 14 | >Updated (11/23/15 v2.3)
15 | * Totally new method to gather and process data, data is available after 24 Blocks of data have been collected (~69.6ms) for all frequencies.
16 | * Double buffer to collect Audio data, while one collects the other buffer is processed.
17 | 18 | >Updated (10/12/15 v2.2)
19 | * Fixed yin cpu usage throttling code in update function.
20 | * Function initialize second param takes a float (0.0 - 1.0).
21 | * Fix many spelling and grammar errors. :(
22 | 23 | >Updated (10/11/15 v2.1)
24 | * Made yin implementation faster and more reliable.
25 | * Improved user interface.
26 | 27 | >Updated (7/10/15 v2.0)
28 | * First commit 29 | -------------------------------------------------------------------------------- /examples/Simple_Sine/Simple_Sine.ino: -------------------------------------------------------------------------------- 1 | /* 2 | C C# D Eb E F F# G G# A Bb B 3 | 0 16.35 17.32 18.35 19.45 20.60 21.83 23.12 24.50 25.96 27.50 29.14 30.87 4 | 1 32.70 34.65 36.71 38.89 41.20 43.65 46.25 49.00 51.91 55.00 58.27 61.74 5 | 2 65.41 69.30 73.42 77.78 82.41 87.31 92.50 98.00 103.8 110.0 116.5 123.5 6 | 3 130.8 138.6 146.8 155.6 164.8 174.6 185.0 196.0 207.7 220.0 233.1 246.9 7 | 4 261.6 277.2 293.7 311.1 329.6 349.2 370.0 392.0 415.3 440.0 466.2 493.9 8 | 5 523.3 554.4 587.3 622.3 659.3 698.5 740.0 784.0 830.6 880.0 932.3 987.8 9 | 6 1047 1109 1175 1245 1319 1397 1480 1568 1661 1760 1865 1976 10 | 7 2093 2217 2349 2489 2637 2794 2960 3136 3322 3520 3729 3951 11 | 8 4186 4435 4699 4978 5274 5588 5920 6272 6645 7040 7459 7902 12 | 13 | Guitar strings are E2=82.41Hz, A2=110Hz, D3=146.8Hz, G3=196Hz, B3=246.9Hz, E4=329.6Hz 14 | 15 | Bass strings are (5th string) B0=30.87Hz, (4th string) E1=41.20Hz, A1=55Hz, D2=73.42Hz, G2=98Hz 16 | 17 | You can change the frequency by typing "f " + frequency in the serial monitor. 18 | EX. "f 30.87" 19 | 20 | You can change the amplitude by typing "a " + amplitude in the serial monitor. (0,1) 21 | EX. "a .5" 22 | */ 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | 30 | #include "coeff.h" 31 | 32 | AudioTuner tuner; 33 | AudioSynthWaveformSine sine; 34 | AudioOutputAnalog dac; 35 | AudioMixer4 mixer; 36 | 37 | AudioConnection patchCord1(sine, 0, mixer, 0); 38 | AudioConnection patchCord2(mixer, 0, tuner, 0); 39 | AudioConnection patchCord3(mixer, 0, dac, 0); 40 | 41 | // hold serial commands 42 | char buffer[10]; 43 | 44 | void setup() { 45 | AudioMemory(30); 46 | /* 47 | * Initialize the yin algorithm's absolute 48 | * threshold, this is good number. 49 | */ 50 | tuner.begin(.15, fir_22059_HZ_coefficients, sizeof(fir_22059_HZ_coefficients), 2); 51 | 52 | sine.frequency(30.87); 53 | sine.amplitude(1); 54 | } 55 | 56 | void loop() { 57 | // read back fundamental frequency 58 | if (tuner.available()) { 59 | float note = tuner.read(); 60 | float prob = tuner.probability(); 61 | Serial.printf("Note: %3.2f | Probability: %.2f\n", note, prob); 62 | } 63 | 64 | if (Serial.available()) { 65 | Serial.readBytesUntil('\n', buffer, 10); 66 | handleCmds( buffer ); 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /examples/Sample_Guitar_Tunning_Notes/Sample_Guitar_Tunning_Notes.ino: -------------------------------------------------------------------------------- 1 | /* 2 | C C# D Eb E F F# G G# A Bb B 3 | 0 16.35 17.32 18.35 19.45 20.60 21.83 23.12 24.50 25.96 27.50 29.14 30.87 4 | 1 32.70 34.65 36.71 38.89 41.20 43.65 46.25 49.00 51.91 55.00 58.27 61.74 5 | 2 65.41 69.30 73.42 77.78 82.41 87.31 92.50 98.00 103.8 110.0 116.5 123.5 6 | 3 130.8 138.6 146.8 155.6 164.8 174.6 185.0 196.0 207.7 220.0 233.1 246.9 7 | 4 261.6 277.2 293.7 311.1 329.6 349.2 370.0 392.0 415.3 440.0 466.2 493.9 8 | 5 523.3 554.4 587.3 622.3 659.3 698.5 740.0 784.0 830.6 880.0 932.3 987.8 9 | 6 1047 1109 1175 1245 1319 1397 1480 1568 1661 1760 1865 1976 10 | 7 2093 2217 2349 2489 2637 2794 2960 3136 3322 3520 3729 3951 11 | 8 4186 4435 4699 4978 5274 5588 5920 6272 6645 7040 7459 7902 12 | 13 | Guitar strings are E2=82.41Hz, A2=110Hz, D3=146.8Hz, G3=196Hz, B3=246.9Hz, E4=329.6Hz 14 | 15 | Bass strings are (5th string) B0=30.87Hz, (4th string) E1=41.20Hz, A1=55Hz, D2=73.42Hz, G2=98Hz 16 | 17 | This example tests the yin algorithm with actual notes from nylon string guitar recorded 18 | as wav format at 16B @ 44100 samples/sec. Since the decay of the notes will be longer than what 19 | the teensy can store in flash these notes are truncated to ~120,000B or about 1/2 of the whole 20 | signal. 21 | */ 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | 29 | #include "coeff.h" 30 | //--------------------------------------------------------------------------------------- 31 | #include "e2_note.h" 32 | #include "a2_note.h" 33 | #include "d3_note.h" 34 | #include "g3_note.h" 35 | #include "b3_note.h" 36 | #include "e4_note.h" 37 | //--------------------------------------------------------------------------------------- 38 | AudioTuner tuner; 39 | AudioOutputAnalog dac; 40 | AudioPlayMemory wav_note; 41 | AudioMixer4 mixer; 42 | //--------------------------------------------------------------------------------------- 43 | AudioConnection patchCord0(wav_note, 0, mixer, 0); 44 | AudioConnection patchCord1(mixer, 0, tuner, 0); 45 | AudioConnection patchCord2(mixer, 0, dac, 0); 46 | //--------------------------------------------------------------------------------------- 47 | IntervalTimer playNoteTimer; 48 | 49 | void playNote(void) { 50 | if (!wav_note.isPlaying()) { 51 | wav_note.play(e2_note); 52 | //wav_note.play(a2_note); 53 | //wav_note.play(d3_note); 54 | //wav_note.play(g3_note); 55 | //wav_note.play(b3_note); 56 | //wav_note.play(e4_note); 57 | digitalWriteFast(LED_BUILTIN, !digitalReadFast(LED_BUILTIN)); 58 | } 59 | } 60 | //--------------------------------------------------------------------------------------- 61 | void setup() { 62 | AudioMemory(30); 63 | /* 64 | * Initialize the yin algorithm's absolute 65 | * threshold, this is good number. 66 | */ 67 | tuner.begin(.15, fir_22059_HZ_coefficients, sizeof(fir_22059_HZ_coefficients), 2); 68 | pinMode(LED_BUILTIN, OUTPUT); 69 | playNoteTimer.begin(playNote, 1000); 70 | } 71 | 72 | void loop() { 73 | // read back fundamental frequency 74 | if (tuner.available()) { 75 | float note = tuner.read(); 76 | float prob = tuner.probability(); 77 | Serial.printf("Note: %3.2f | Probability: %.2f\n", note, prob); 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /AudioTuner.h: -------------------------------------------------------------------------------- 1 | /* Audio Library Note Frequency Detection & Guitar/Bass Tuner 2 | * Copyright (c) 2015, Colin Duffy 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice, development funding notice, and this permission 12 | * notice shall be included in all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | * THE SOFTWARE. 21 | */ 22 | 23 | #ifndef AudioTuner_h_ 24 | #define AudioTuner_h_ 25 | 26 | #include "AudioStream.h" 27 | #include "arm_math.h" 28 | /*********************************************************************** 29 | * Safe to adjust these values below * 30 | * * 31 | * This parameter defines the size of the buffer. * 32 | * * 33 | * 1. AUDIO_GUITARTUNER_BLOCKS - Buffer size is 128 * AUDIO_BLOCKS. * 34 | * The more AUDIO_GUITARTUNER_BLOCKS the lower * 35 | * the frequency you can detect. The default * 36 | * (24) is set to measure down to 29.14 Hz * 37 | * or B(flat)0. * 38 | * * 39 | * 2. MAX_COEFF - Maxium number of coefficeints for the FIR filter. * 40 | * * 41 | ***********************************************************************/ 42 | #define AUDIO_GUITARTUNER_BLOCKS 24 43 | #define MAX_COEFF 200 44 | /***********************************************************************/ 45 | 46 | class AudioTuner : public AudioStream { 47 | public: 48 | /** 49 | * constructor to setup Audio Library and initialize 50 | * 51 | * @return none 52 | */ 53 | AudioTuner( void ) : AudioStream( 1, inputQueueArray ), 54 | data( 0.0 ), 55 | coeff_p( NULL ), 56 | enabled( false ), 57 | new_output( false ), 58 | coeff_size( 0 ) 59 | 60 | { 61 | 62 | } 63 | 64 | /** 65 | * initialize variables and start 66 | * 67 | * @param threshold Allowed uncertainty 68 | * @param coeff coefficients for fir filter 69 | * @param taps number of coefficients, even 70 | * @param factor must be power of 2 71 | */ 72 | void begin( float threshold, int16_t *coeff, uint8_t taps, uint8_t factor ); 73 | 74 | /** 75 | * sets threshold value 76 | * 77 | * @param thresh 78 | * @return none 79 | */ 80 | void threshold( float p ); 81 | 82 | /** 83 | * triggers true when valid frequency is found 84 | * 85 | * @return flag to indicate valid frequency is found 86 | */ 87 | bool available( void ); 88 | /** 89 | * get frequency 90 | * 91 | * @return frequency in hertz 92 | */ 93 | float read( void ); 94 | 95 | /** 96 | * get predicitity 97 | * 98 | * @return probability of frequency found 99 | */ 100 | float probability( void ); 101 | 102 | /** 103 | * fir decimation coefficents 104 | * 105 | * @return none 106 | */ 107 | void coeff( int16_t *p, int n ); 108 | 109 | /** 110 | * disable yin 111 | * 112 | * @return none 113 | */ 114 | void disable( void ); 115 | 116 | /** 117 | * Audio Library calls this update function ~2.9ms 118 | * 119 | * @return none 120 | */ 121 | virtual void update( void ); 122 | 123 | private: 124 | /** 125 | * check the sampled data for fundamental frequency 126 | * 127 | * @param yin buffer to hold sum*tau value 128 | * @param rs buffer to hold running sum for sampled window 129 | * @param head buffer index 130 | * @param tau lag we are currently working on this gets incremented 131 | * 132 | * @return tau 133 | */ 134 | uint16_t estimate( uint64_t *yin, uint64_t *rs, uint16_t head, uint16_t tau ); 135 | 136 | /** 137 | * process audio data 138 | * 139 | * @return none 140 | */ 141 | void process( int16_t *p ); 142 | 143 | /** 144 | * Variables 145 | */ 146 | float periodicity, yin_threshold, data; 147 | uint64_t running_sum, yin_buffer[5], rs_buffer[5]; 148 | uint16_t tau_global; 149 | int16_t AudioBuffer[AUDIO_GUITARTUNER_BLOCKS*AUDIO_BLOCK_SAMPLES] __attribute__ ( ( aligned ( 4 ) ) ); 150 | int16_t coeff_state[AUDIO_BLOCK_SAMPLES + MAX_COEFF]; 151 | int16_t *coeff_p; 152 | uint8_t yin_idx, state, coeff_size, decimation_factor, decimation_shift; 153 | volatile bool new_output, process_buffer, enabled; 154 | audio_block_t *inputQueueArray[1]; 155 | arm_fir_decimate_instance_q15 firDecimateInst; 156 | }; 157 | #endif 158 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | Guitar and Bass Tuner Library v3.3
3 | Teensy 3.1/2
4 |

5 | 6 | >Software algorithm ([YIN]) for guitar and bass tuning using a Teensy Audio Library. This audio object's algorithm can be some what memory and processor hungry but will allow you to detect with fairly good accuracy the fundamental frequencies fo from electric guitars and basses. 7 | >>You can install this as a normal Arduino Library and will work with the Audio Library, no need to edit the Audio libraries source now. 8 | 9 | 10 | Hookup Guide - ~.6v DC Bias and High Pass Filter - No Amplification 11 | 12 | *--------------------------------------------------* 13 | | | 14 | | Pull Down Resistor | ' | | 15 | *------------/\/\/\-------------* |' '| | 16 | | 47K | _|_'_|_ | 17 | | | |` ` ` `| | 18 | *---)|+---* | | ` ` ` | | 19 | | 10uF | | |` ` ` `| | 20 | TEENSY 3.1 | | | | ` ` ` | | 21 | _______________ *-/\/\/\--* | |` ` ` `| | 22 | |GND |_____| Vin| | 2.2K | | | ` ` ` | | 23 | |0 ----- AGND|<-* | | |` ` ` `| | 24 | |1 |`````| 3.3V|>---/\/\/\--*--/\/\/\--* | | ` ` ` | | 25 | |2 | | 23| 10K 47K | | |` ` ` `| | 26 | |3 ----- 22| | | | ` ` ` | | 27 | |4 |'| 21| | | \=====/ | 28 | |5 ------ 20| | | | '`| | 29 | |6 |::::::::| 19| | REMOVE | | S`| | 30 | |7 |::::::::| 18| | DC | | H`| | 31 | |8 |::::::::| 17| | BIAS | | I`| | 32 | |9 ------A2/16|<---SIGNAL-.6v-BIAS----*---+|(----* | E`|>--ANGD--* 33 | |10 --- 15| .6VDC 10uF | | L`| 34 | |11 |(`)| 14| | | D`| 35 | |12 --- 13| | | `| 36 | --------------- | |===| 37 | | \_/ 38 | | /T\ 39 | | .-I-. 40 | *---<\ P / 41 | \_/ 42 | 43 | >Many optimizations have been done to the [YIN] algorithm for frequencies between 29-400Hz. 44 | >>While its still using a brute force method ( n2 ) for finding the fundamental frequency fo, it is tuned to skip certain tau () values and focus mostly on frequencies found in the bass and guitar. 45 | >>>The input is double buffered so while you are processing one buffer it is filling the other to double throughput. 46 | >>>>The parameter AUDIO_BLOCKS below can be adjusted but its default of 24 I found to be best to work with the guitar and bass frequency range (29- 400)Hz. 47 | >>>>>Looking into finding the Auto Correlation using FFT and IFFT to speed up processing of data! Not that simple because the YIN algorithm uses a squared difference tweak to the Auto Correlation. 48 | 49 |

AudioTuner.h

50 | 51 | ``` 52 | /*********************************************************************** 53 | * Safe to adjust these values below * 54 | * * 55 | * This parameter defines the size of the buffer. * 56 | * * 57 | * 1. AUDIO_GUITARTUNER_BLOCKS - Buffer size is 128 * AUDIO_BLOCKS. * 58 | * The more AUDIO_GUITARTUNER_BLOCKS the lower * 59 | * the frequency you can detect. The default * 60 | * (24) is set to measure down to 29.14 Hz * 61 | * or B(flat)0. * 62 | * * 63 | * 2. MAX_COEFF - Maxium number of coefficeints for the FIR filter. * 64 | * * 65 | ***********************************************************************/ 66 | #define AUDIO_GUITARTUNER_BLOCKS 24 67 | #define MAX_COEFF 200 68 | /***********************************************************************/ 69 | ``` 70 | 71 |
72 | YIN Algorithm 73 |
    74 |
  1. Difference Function - Squared difference of the (signal - signal lag())
    75 |
  2. 76 | 77 |
  3. Cumulative Mean Normalized Difference Function - Calculate the cumulative mean on the normalized difference signal
  4. 78 |
  5. Absolute Threshold - Fix for subharmonic error with Autocorrelation, choose () that gives a minimum
  6. 79 |
80 |
81 | 82 | [YIN]:http://recherche.ircam.fr/equipes/pcm/cheveign/pss/2002_JASA_YIN.pdf 83 | [Teensy Audio Library]:http://www.pjrc.com/teensy/td_libs_Audio.html 84 | -------------------------------------------------------------------------------- /AudioTuner.cpp: -------------------------------------------------------------------------------- 1 | /* Audio Library Note Frequency Detection & Guitar/Bass Tuner 2 | * Copyright (c) 2015, Colin Duffy 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice, development funding notice, and this permission 12 | * notice shall be included in all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | * THE SOFTWARE. 21 | */ 22 | 23 | #include "utility/dspinst.h" 24 | #include "arm_math.h" 25 | #include "Arduino.h" 26 | #include "AudioTuner.h" 27 | 28 | #define NUM_SAMPLES ( AUDIO_GUITARTUNER_BLOCKS << 7 ) 29 | 30 | void AudioTuner::update( void ) { 31 | 32 | audio_block_t *block; 33 | 34 | block = receiveReadOnly(); 35 | if (!block) return; 36 | 37 | if ( !enabled ) { 38 | release( block ); 39 | return; 40 | } 41 | 42 | /** 43 | * "factor" is the new block size calculated by 44 | * the decimated shift to incremnt the buffer 45 | * address. 46 | */ 47 | const uint8_t factor = AUDIO_BLOCK_SAMPLES >> decimation_shift; 48 | 49 | // filter and decimate block by block the incoming signal and store in a buffer. 50 | arm_fir_decimate_fast_q15( &firDecimateInst, block->data, AudioBuffer + ( state * factor ), AUDIO_BLOCK_SAMPLES ); 51 | 52 | /** 53 | * when half the blocks + 1 of the total 54 | * blocks have been stored in the buffer 55 | * start processing the data. 56 | */ 57 | if ( state++ >= AUDIO_GUITARTUNER_BLOCKS >> 1 ) { 58 | 59 | if ( process_buffer ) process( AudioBuffer ); 60 | 61 | if ( state == 0 ) process_buffer = true; 62 | } 63 | 64 | release( block ); 65 | } 66 | 67 | /** 68 | * Start the Yin algorithm 69 | * 70 | * TODO: Significant speed up would be to use spectral domain to find fundamental frequency. 71 | * This paper explains: https://aubio.org/phd/thesis/brossier06thesis.pdf -> Section 3.2.4 72 | * page 79. Might have to downsample for low fundmental frequencies because of fft buffer 73 | * size limit. 74 | */ 75 | void AudioTuner::process( int16_t *p ) { 76 | 77 | const uint16_t inner_cycles = ( NUM_SAMPLES >> decimation_shift ) >> 1; 78 | uint16_t outer_cycles = inner_cycles / AUDIO_GUITARTUNER_BLOCKS; 79 | uint16_t tau = tau_global; 80 | do { 81 | uint64_t sum = 0; 82 | int32_t a1, a2, b1, b2, c1, c2, d1, d2; 83 | int32_t out1, out2, out3, out4; 84 | uint16_t blkCnt; 85 | int16_t * cur = p; 86 | int16_t * lag = p + tau; 87 | // unrolling the inner loop by 8 88 | blkCnt = inner_cycles >> 3; 89 | do { 90 | // a(n), b(n), c(n), d(n) each hold two samples 91 | a1 = *__SIMD32( cur )++; 92 | a2 = *__SIMD32( cur )++; 93 | b1 = *__SIMD32( lag )++; 94 | b2 = *__SIMD32( lag )++; 95 | c1 = *__SIMD32( cur )++; 96 | c2 = *__SIMD32( cur )++; 97 | d1 = *__SIMD32( lag )++; 98 | d2 = *__SIMD32( lag )++; 99 | // subract two samples at a time 100 | out1 = __QSUB16( a1, b1 ); 101 | out2 = __QSUB16( a2, b2 ); 102 | out3 = __QSUB16( c1, d1 ); 103 | out4 = __QSUB16( c2, d2 ); 104 | // square the difference 105 | sum = multiply_accumulate_16tx16t_add_16bx16b( sum, out1, out1 ); 106 | sum = multiply_accumulate_16tx16t_add_16bx16b( sum, out2, out2 ); 107 | sum = multiply_accumulate_16tx16t_add_16bx16b( sum, out3, out3 ); 108 | sum = multiply_accumulate_16tx16t_add_16bx16b( sum, out4, out4 ); 109 | 110 | } while( --blkCnt ); 111 | 112 | uint64_t rs = running_sum; 113 | rs += sum; 114 | yin_buffer[yin_idx] = sum*tau; 115 | rs_buffer[yin_idx] = rs; 116 | running_sum = rs; 117 | yin_idx = ( ++yin_idx >= 5 ) ? 0 : yin_idx; 118 | tau = estimate( yin_buffer, rs_buffer, yin_idx, tau ); 119 | 120 | if ( tau == 0 ) { 121 | process_buffer = false; 122 | new_output = true; 123 | yin_idx = 1; 124 | running_sum = 0; 125 | tau_global = 1; 126 | state = 0; 127 | return; 128 | } 129 | 130 | } while ( --outer_cycles ); 131 | 132 | if ( tau >= inner_cycles ) { 133 | process_buffer = true; 134 | new_output = false; 135 | yin_idx = 1; 136 | running_sum = 0; 137 | tau_global = 1; 138 | state = 0; 139 | return; 140 | } 141 | tau_global = tau; 142 | } 143 | 144 | /** 145 | * check the sampled data for fundamental frequency 146 | * 147 | * @param yin buffer to hold sum*tau value 148 | * @param rs buffer to hold running sum for sampled window 149 | * @param head buffer index 150 | * @param tau lag we are curly working on gets incremented 151 | * 152 | * @return tau 153 | */ 154 | uint16_t AudioTuner::estimate( uint64_t *yin, uint64_t *rs, uint16_t head, uint16_t tau ) { 155 | const uint64_t *y = ( uint64_t * )yin; 156 | const uint64_t *r = ( uint64_t * )rs; 157 | uint16_t _tau, _head; 158 | const float thresh = yin_threshold; 159 | _tau = tau; 160 | _head = head; 161 | 162 | if ( _tau > 4 ) { 163 | 164 | uint16_t idx0, idx1, idx2; 165 | idx0 = _head; 166 | idx1 = _head + 1; 167 | idx1 = ( idx1 >= 5 ) ? 0 : idx1; 168 | idx2 = _head + 2; 169 | idx2 = ( idx2 >= 5 ) ? idx2 - 5 : idx2; 170 | 171 | // maybe fixed point would be better here? But how? 172 | float s0, s1, s2; 173 | s0 = ( ( float )*( y+idx0 ) / ( float )*( r+idx0 ) ); 174 | s1 = ( ( float )*( y+idx1 ) / ( float )*( r+idx1 ) ); 175 | s2 = ( ( float )*( y+idx2 ) / ( float )*( r+idx2 ) ); 176 | 177 | if ( s1 < thresh && s1 < s2 ) { 178 | uint16_t period = _tau - 3; 179 | periodicity = 1 - s1; 180 | data = period + 0.5f * ( s0 - s2 ) / ( s0 - 2.0f * s1 + s2 ); 181 | return 0; 182 | } 183 | } 184 | return _tau + 1; 185 | } 186 | 187 | /** 188 | * Initialise 189 | * 190 | * @param threshold Allowed uncertainty 191 | */ 192 | void AudioTuner::begin( float threshold, int16_t *coeff, uint8_t taps, uint8_t factor ) { 193 | __disable_irq( ); 194 | process_buffer = true; 195 | yin_threshold = threshold; 196 | periodicity = 0.0f; 197 | running_sum = 0; 198 | tau_global = 1; 199 | yin_idx = 1; 200 | enabled = true; 201 | state = 0; 202 | data = 0.0f; 203 | decimation_factor = factor; 204 | decimation_shift = log( factor ) / log( 2 ); 205 | coeff_size = taps; 206 | coeff_p = coeff; 207 | arm_fir_decimate_init_q15( &firDecimateInst, coeff_size, decimation_factor, coeff_p, &coeff_state[0], AUDIO_BLOCK_SAMPLES ); 208 | __enable_irq( ); 209 | } 210 | 211 | /** 212 | * available 213 | * 214 | * @return true if data is ready else false 215 | */ 216 | bool AudioTuner::available( void ) { 217 | __disable_irq( ); 218 | bool flag = new_output; 219 | if ( flag ) new_output = false; 220 | __enable_irq( ); 221 | return flag; 222 | } 223 | 224 | /** 225 | * read processes the data samples for the Yin algorithm. 226 | * 227 | * @return frequency in hertz 228 | */ 229 | float AudioTuner::read( void ) { 230 | __disable_irq( ); 231 | float d = data; 232 | __enable_irq( ); 233 | return ( AUDIO_SAMPLE_RATE_EXACT / decimation_factor ) / d; 234 | } 235 | 236 | /** 237 | * Periodicity of the sampled signal. 238 | * 239 | * @return periodicity 240 | */ 241 | float AudioTuner::probability( void ) { 242 | __disable_irq( ); 243 | float p = periodicity; 244 | __enable_irq( ); 245 | return p; 246 | } 247 | 248 | /** 249 | * New LP coeffients for decimation. 250 | * 251 | * @param p array pointer of coeffients. 252 | * @param n array size. 253 | */ 254 | void AudioTuner::coeff( int16_t *p, int n ) { 255 | //coeff_size = n; 256 | //coeff_p = p; 257 | //arm_fir_decimate_init_q15(&firDecimateInst, coeff_size, 4, coeff_p, coeff_state, 128); 258 | } 259 | 260 | /** 261 | * Initialise parameters. 262 | * 263 | * @param thresh Allowed uncertainty 264 | */ 265 | void AudioTuner::threshold( float p ) { 266 | __disable_irq( ); 267 | yin_threshold = p; 268 | __enable_irq( ); 269 | } 270 | 271 | /** 272 | * disable yin from processing data, use begin to start back up 273 | * 274 | * @return none 275 | */ 276 | void AudioTuner::disable( void ) { 277 | __disable_irq( ); 278 | enabled = false; 279 | __enable_irq( ); 280 | } 281 | --------------------------------------------------------------------------------