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 |
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 |
--------------------------------------------------------------------------------