1 /**************************************************************************
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
10 * Copyright (C) 2008 Lechner Michael / smoking gnu
12 * All files in this archive are subject to the GNU General Public License.
13 * See the file COPYING in the source tree root for full license agreement.
15 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
16 * KIND, either express or implied.
18 * ----------------------------------------------------------------------------
21 * OK, this is an attempt to write an instrument tuner for rockbox.
22 * It uses a Schmitt trigger algorithm, which I copied from
23 * tuneit [ (c) 2004 Mario Lang <mlang@delysid.org> ], for detecting the
24 * fundamental freqency of a sound. A FFT algorithm would be more accurate
25 * but also much slower.
28 * - Find someone who knows how recording actually works, and rewrite the
29 * recording code to use proper, gapless recording with a callback function
30 * that provides new buffer, instead of stopping and restarting recording
31 * everytime the buffer is full
32 * - Convert all floating point operations to fixed-point
33 * - Adapt the Yin FFT algorithm, which would reduce complexity from O(n^2)
34 * to O(nlogn), theoretically reducing latency by a factor of ~10. -David
37 * 08.03.2008 Started coding
38 * 21.03.2008 Pitch detection works more or less
39 * Button definitions for most targets added
40 * 02.04.2008 Proper GUI added
41 * Todo, Major Changes and Current Limitations added
42 * 08.19.2009 Brought the code up to date with current plugin standards
43 * Made it work more nicely with color, BW and grayscale
44 * Changed pitch detection to use the Yin algorithm (better
45 * detection, but slower -- would be ~4x faster with
46 * fixed point math, I think). Code was poached from the
47 * Aubio sound processing library (aubio.org). -David
48 * 08.31.2009 Lots of changes:
49 * Added a menu to tweak settings
50 * Converted everything to fixed point (greatly improving
52 * Improved the display
53 * Improved efficiency with judicious use of cpu_boost, the
54 * backlight, and volume detection to limit unneeded
56 * Fixed a problem that caused an octave-off error
60 * CURRENT LIMITATIONS:
61 * - No gapless recording. Strictly speaking true gappless isn't possible,
62 * since the algorithm takes longer to calculate than the length of the
63 * sample, but latency could be improved a bit with proper use of the DMA
64 * recording functions.
65 * - Due to how the Yin algorithm works, latency is higher for lower
70 #include "lib/pluginlib_actions.h"
74 /* First figure out what sample rate we're going to use */
75 #if (REC_SAMPR_CAPS & SAMPR_CAP_44)
76 #define SAMPLE_RATE SAMPR_44
77 #elif (REC_SAMPR_CAPS & SAMPR_CAP_22)
78 #define SAMPLE_RATE SAMPR_22
79 #elif (REC_SAMPR_CAPS & SAMPR_CAP_11)
80 #define SAMPLE_RATE SAMPR_11
83 /* Some fixed point calculation stuff */
84 typedef int32_t fixed_data
;
89 typedef struct _fixed fixed
;
90 #define FIXED_PRECISION 18
91 #define FP_MAX ((fixed) {0x7fffffff})
92 #define FP_MIN ((fixed) {-0x80000000})
93 #define int2fixed(x) ((fixed){(x) << FIXED_PRECISION})
94 #define int2mantissa(x) ((fixed){x})
95 #define fixed2int(x) ((int)((x).a >> FIXED_PRECISION))
96 #define fixed2float(x) (((float)(x).a) / ((float)(1 << FIXED_PRECISION)))
97 #define float2fixed(x) \
98 ((fixed){(fixed_data)(x * (float)(1 << FIXED_PRECISION))})
99 /* I adapted these ones from the Rockbox fixed point library */
100 #define fp_mul(x, y) \
101 ((fixed){(((int64_t)((x).a)) * ((int64_t)((y).a))) >> (FIXED_PRECISION)})
102 #define fp_div(x, y) \
103 ((fixed){(((int64_t)((x).a)) << (FIXED_PRECISION)) / ((int64_t)((y).a))})
104 /* Operators for fixed point */
105 #define fp_add(x, y) ((fixed){(x).a + (y).a})
106 #define fp_sub(x, y) ((fixed){(x).a - (y).a})
107 #define fp_shl(x, y) ((fixed){(x).a << y})
108 #define fp_shr(x, y) ((fixed){(x).a >> y})
109 #define fp_neg(x) ((fixed){-(x).a})
110 #define fp_gt(x, y) ((x).a > (y).a)
111 #define fp_gte(x, y) ((x).a >= (y).a)
112 #define fp_lt(x, y) ((x).a < (y).a)
113 #define fp_lte(x, y) ((x).a <= (y).a)
114 #define fp_sqr(x) fp_mul((x), (x))
115 #define fp_equal(x, y) ((x).a == (y).a)
116 #define fp_round(x) (fixed2int(fp_add((x), float2fixed(0.5))))
117 #define fp_data(x) ((x).a)
118 #define fp_frac(x) (fp_sub((x), int2fixed(fixed2int(x))))
119 #define FP_ZERO ((fixed){0})
120 #define FP_LOW ((fixed){1})
122 /* Some defines for converting between period and frequency */
123 /* I introduce some divisors in this because the fixed point */
124 /* variables aren't big enough to hold higher than a certain */
125 /* vallue. This loses a bit of precision but it means we */
126 /* don't have to use 32.32 variables (yikes). */
127 /* With an 18-bit decimal precision, the max value in the */
128 /* integer part is 8192. Divide 44100 by 7 and it'll fit in */
130 #define fp_period2freq(x) fp_div(int2fixed(SAMPLE_RATE / 7), \
131 fp_div((x),int2fixed(7)))
132 #define fp_freq2period(x) fp_period2freq(x)
133 #define period2freq(x) (SAMPLE_RATE / (x))
134 #define freq2period(x) period2freq(x)
136 #define sqr(x) ((x)*(x))
138 /* Some constants for tuning */
139 #define A_FREQ float2fixed(440.0f)
140 #define D_NOTE float2fixed(1.059463094359f)
141 #define LOG_D_NOTE float2fixed(1.0f/12.0f)
142 #define D_NOTE_SQRT float2fixed(1.029302236643f)
143 #define LOG_2 float2fixed(1.0f)
145 /* The recording buffer size */
146 /* This is how much is sampled at a time. */
147 /* It also determines latency -- if BUFFER_SIZE == SAMPLE_RATE then */
148 /* there'll be one sample per second, or a latency of one second. */
149 /* Furthermore, the lowest detectable frequency will be about twice */
150 /* the number of reads per second */
151 /* If we ever switch to Yin FFT algorithm then this needs to be
153 #define BUFFER_SIZE 4096
154 #define SAMPLE_SIZE 4096
155 #define SAMPLE_SIZE_MIN 1024
156 #define YIN_BUFFER_SIZE (BUFFER_SIZE / 4)
158 #define LCD_FACTOR (fp_div(int2fixed(LCD_WIDTH), int2fixed(100)))
159 /* The threshold for the YIN algorithm */
160 #define YIN_THRESHOLD float2fixed(0.05f)
162 /* How loud the audio has to be to start displaying pitch */
163 /* Must be between 0 and 100 */
164 #define VOLUME_THRESHOLD (50)
165 /* Change to AUDIO_SRC_LINEIN if you want to record from line-in */
166 #define INPUT_TYPE AUDIO_SRC_MIC
168 /* How many decimal places to display for the Hz value */
169 #define DISPLAY_HZ_PRECISION 100
171 typedef signed short audio_sample_type
;
172 /* It's stereo, so make the buffer twice as big */
173 audio_sample_type audio_data
[BUFFER_SIZE
];
174 fixed yin_buffer
[YIN_BUFFER_SIZE
];
175 static int recording
=0;
177 /* Frequencies of all the notes of the scale */
178 static const fixed freqs
[12] =
180 float2fixed(440.0f
), /* A */
181 float2fixed(466.1637615f
), /* A# */
182 float2fixed(493.8833013f
), /* etc... */
183 float2fixed(523.2511306f
),
184 float2fixed(554.3652620f
),
185 float2fixed(587.3295358f
),
186 float2fixed(622.2539674f
),
187 float2fixed(659.2551138f
),
188 float2fixed(698.4564629f
),
189 float2fixed(739.9888454f
),
190 float2fixed(783.9908720f
),
191 float2fixed(830.6093952f
),
193 /* logarithm of all the notes of the scale */
194 static const fixed lfreqs
[12] =
196 float2fixed(8.781359714f
),
197 float2fixed(8.864693047f
),
198 float2fixed(8.948026380f
),
199 float2fixed(9.031359714f
),
200 float2fixed(9.114693047f
),
201 float2fixed(9.198026380f
),
202 float2fixed(9.281359714f
),
203 float2fixed(9.364693047f
),
204 float2fixed(9.448026380f
),
205 float2fixed(9.531359714f
),
206 float2fixed(9.614693047f
),
207 float2fixed(9.698026380f
),
211 static unsigned back_color
, front_color
;
212 static int font_w
, font_h
;
213 static int bar_x_minus_50
, bar_x_minus_20
, bar_x_0
, bar_x_20
, bar_x_50
;
214 static int letter_y
; /* y of the notes letters */
215 static int note_bar_y
; /* y of the yellow bars (under the notes) */
216 static int bar_h
; /* height of the yellow (note) and red (error) bars */
217 static int freq_y
; /* y of the line with the frequency */
218 static int error_ticks_y
; /* y of the error values (-50, -20, ...) */
219 static int error_hline_y
; /* y of the top line for error bar */
220 static int error_hline_y2
;/* y of the bottom line for error bar */
221 static int error_bar_y
; /* y of the error bar */
222 static int error_bar_margin
; /* distance between the error bar and the hline */
224 static const char *english_notes
[12] = {"A","A#","B","C","C#","D","D#","E",
225 "F","F#", "G", "G#"};
226 static const char gui_letters
[8] = {'A', 'B', 'C', 'D', 'E', 'F', 'G', '#'};
227 static const char **notes
= english_notes
;
229 /*Settings for the plugin */
231 struct tuner_settings
233 unsigned volume_threshold
;
234 unsigned record_gain
;
235 unsigned sample_size
;
236 unsigned lowest_freq
;
237 unsigned yin_threshold
;
240 /*=================================================================*/
242 /*=================================================================*/
245 const struct button_mapping
* plugin_contexts
[]={
247 generic_increase_decrease
,
253 #define PLA_ARRAY_COUNT sizeof(plugin_contexts)/sizeof(plugin_contexts[0])
255 fixed yin_threshold_table
[] =
273 #define DEFAULT_YIN_THRESHOLD 5 /* 0.10 */
276 static const struct opt_items yin_threshold_text
[] =
294 void set_min_freq(int new_freq
)
296 tuner_settings
.sample_size
= freq2period(new_freq
) * 4;
298 /* clamp the sample size between min and max */
299 if(tuner_settings
.sample_size
<= SAMPLE_SIZE_MIN
)
300 tuner_settings
.sample_size
= SAMPLE_SIZE_MIN
;
301 else if(tuner_settings
.sample_size
>= BUFFER_SIZE
)
302 tuner_settings
.sample_size
= BUFFER_SIZE
;
303 /* sample size must be divisible by 4 */
304 else if(tuner_settings
.sample_size
% 4 != 0)
305 tuner_settings
.sample_size
+= 4 - (tuner_settings
.sample_size
% 4);
312 bool exit_tuner
=false;
315 MENUITEM_STRINGLIST(menu
,"Tuner Settings",NULL
,
320 "Algorithm Pickiness",
325 choice
= rb
->do_menu(&menu
, &selection
, NULL
, false);
329 rb
->set_int("Volume Threshold", "%", UNIT_INT
,
330 &tuner_settings
.volume_threshold
,
331 NULL
, 5, 5, 95, NULL
);
334 rb
->set_int("Listening Volume", "%", UNIT_INT
,
335 &tuner_settings
.record_gain
,
336 NULL
, 1, rb
->sound_min(SOUND_MIC_GAIN
),
337 rb
->sound_max(SOUND_MIC_GAIN
), NULL
);
340 rb
->set_int("Lowest Frequency", "Hz", UNIT_INT
,
341 &tuner_settings
.lowest_freq
, set_min_freq
, 1,
342 /* Range depends on the size of the buffer */
343 SAMPLE_RATE
/ (BUFFER_SIZE
/ 4),
344 SAMPLE_RATE
/ (SAMPLE_SIZE_MIN
/ 4), NULL
);
348 "Algorithm Pickiness (Lower -> more discriminating)",
349 &tuner_settings
.yin_threshold
,
350 INT
, yin_threshold_text
,
351 sizeof(yin_threshold_text
) /
352 sizeof(yin_threshold_text
[0]),
360 default: /* Return to the tuner */
369 /*=================================================================*/
370 /* Settings loading and saving(adapted from the clock plugin) */
371 /*=================================================================*/
373 #define SETTINGS_FILENAME PLUGIN_APPS_DIR "/.pitch_settings"
385 enum settings_file_status
391 /* The settings as they exist on the hard disk, so that
392 * we can know at saving time if changes have been made */
393 struct tuner_settings hdd_tuner_settings
;
395 /*---------------------------------------------------------------------*/
397 bool settings_needs_saving(struct tuner_settings
* settings
)
399 return(rb
->memcmp(settings
, &hdd_tuner_settings
, sizeof(*settings
)));
402 /*---------------------------------------------------------------------*/
404 void tuner_settings_reset(struct tuner_settings
* settings
)
406 settings
->volume_threshold
= VOLUME_THRESHOLD
;
407 settings
->record_gain
= rb
->global_settings
->rec_mic_gain
;
408 settings
->sample_size
= BUFFER_SIZE
;
409 settings
->lowest_freq
= period2freq(BUFFER_SIZE
/ 4);
410 settings
->yin_threshold
= DEFAULT_YIN_THRESHOLD
;
413 /*---------------------------------------------------------------------*/
415 enum settings_file_status
tuner_settings_load(struct tuner_settings
* settings
,
418 int fd
= rb
->open(filename
, O_RDONLY
);
419 if(fd
>= 0){ /* does file exist? */
420 /* basic consistency check */
421 if(rb
->filesize(fd
) == sizeof(*settings
)){
422 rb
->read(fd
, settings
, sizeof(*settings
));
424 rb
->memcpy(&hdd_tuner_settings
, settings
, sizeof(*settings
));
428 /* Initializes the settings with default values at least */
429 tuner_settings_reset(settings
);
433 /*---------------------------------------------------------------------*/
435 enum settings_file_status
tuner_settings_save(struct tuner_settings
* settings
,
438 int fd
= rb
->creat(filename
);
439 if(fd
>= 0){ /* does file exist? */
440 rb
->write (fd
, settings
, sizeof(*settings
));
447 /*---------------------------------------------------------------------*/
449 void load_settings(void)
451 tuner_settings_load(&tuner_settings
, SETTINGS_FILENAME
);
456 /*---------------------------------------------------------------------*/
458 void save_settings(void)
460 if(!settings_needs_saving(&tuner_settings
))
463 tuner_settings_save(&tuner_settings
, SETTINGS_FILENAME
);
466 /*=================================================================*/
468 /*=================================================================*/
470 /* Fixed-point log base 2*/
471 /* Adapted from python code at
472 http://en.wikipedia.org/wiki/Binary_logarithm#Algorithm
477 fixed fp
= int2fixed(1);
478 fixed res
= int2fixed(0);
480 if(fp_lte(x
, FP_ZERO
))
487 while(fp_lt(x
, int2fixed(1)))
489 res
= fp_sub(res
, int2fixed(1));
493 while(fp_gte(x
, int2fixed(2)))
495 res
= fp_add(res
, int2fixed(1));
499 /* Fractional part */
501 while(fp_gt(fp
, FP_ZERO
))
506 if(fp_gte(x
, int2fixed(2)))
509 res
= fp_add(res
, fp
);
516 /*=================================================================*/
518 /*=================================================================*/
520 /* The function name is pretty self-explaining ;) */
521 void print_int_xy(int x
, int y
, int v
)
525 rb
->lcd_set_foreground(front_color
);
526 rb
->snprintf(temp
,20,"%d",v
);
527 rb
->lcd_putsxy(x
,y
,temp
);
531 /* Print out the frequency etc - will be removed later on */
532 void print_str(char* s
)
534 rb
->lcd_set_foreground(front_color
);
535 rb
->lcd_putsxy(0, freq_y
, s
);
539 /* What can I say? Read the function name... */
540 void print_char_xy(int x
, int y
, char c
)
546 rb
->lcd_set_foreground(front_color
);
548 rb
->lcd_putsxy(x
, y
, temp
);
551 /* Draw the red bar and the white lines */
552 void draw_bar(fixed wrong_by_cents
)
554 #ifdef HAVE_LCD_COLOR
555 rb
->lcd_set_foreground(LCD_RGBPACK(255,0,0)); /* Color screens */
557 rb
->lcd_set_foreground(LCD_DARKGRAY
); /* Greyscale screens */
559 rb
->lcd_set_foreground(LCD_BLACK
); /* Black and white screens */
562 if (fp_gt(wrong_by_cents
, FP_ZERO
))
564 rb
->lcd_fillrect(bar_x_0
, error_bar_y
,
565 fixed2int(fp_mul(wrong_by_cents
, LCD_FACTOR
)), bar_h
);
569 rb
->lcd_fillrect(bar_x_0
+ fixed2int(fp_mul(wrong_by_cents
,LCD_FACTOR
)),
571 fixed2int(fp_mul(wrong_by_cents
, LCD_FACTOR
)) * -1,
574 #ifdef HAVE_LCD_COLOR
575 rb
->lcd_set_foreground(LCD_RGBPACK(255,255,255)); /* Color screens */
577 rb
->lcd_set_foreground(LCD_BLACK
); /* Greyscale screens */
579 rb
->lcd_set_foreground(LCD_BLACK
); /* Black and white screens */
582 rb
->lcd_hline(0,LCD_WIDTH
-1, error_hline_y
);
583 rb
->lcd_hline(0,LCD_WIDTH
-1, error_hline_y2
);
584 rb
->lcd_vline(LCD_WIDTH
/ 2, error_hline_y
, error_hline_y2
);
586 print_int_xy(bar_x_minus_50
, error_ticks_y
, -50);
587 print_int_xy(bar_x_minus_20
, error_ticks_y
, -20);
588 print_int_xy(bar_x_0
, error_ticks_y
, 0);
589 print_int_xy(bar_x_20
, error_ticks_y
, 20);
590 print_int_xy(bar_x_50
, error_ticks_y
, 50);
593 /* Print the letters A-G and the # on the screen */
594 void draw_letters(void)
598 rb
->lcd_set_foreground(front_color
);
602 print_char_xy(i
*(LCD_WIDTH
/ 8 ) + font_w
, letter_y
, gui_letters
[i
]);
606 /* Draw the yellow point(s) below the letters and the '#' */
607 void draw_points(const char *s
)
612 #ifdef HAVE_LCD_COLOR
613 rb
->lcd_set_foreground(LCD_RGBPACK(255,255,0)); /* Color screens */
615 rb
->lcd_set_foreground(LCD_DARKGRAY
); /* Grey screens */
617 rb
->lcd_set_foreground(LCD_BLACK
); /* Black and White screens */
620 rb
->lcd_fillrect(i
*(LCD_WIDTH
/ 8 ) + font_w
, note_bar_y
, font_w
, font_h
);
623 rb
->lcd_fillrect(7*(LCD_WIDTH
/ 8 ) + font_w
, note_bar_y
, font_w
, font_h
);
626 /* Calculate how wrong the note is and draw the GUI */
627 void display_frequency (fixed freq
)
635 if (fp_lt(freq
, FP_LOW
))
639 /* Get the frequency to within the range of our reference table */
640 while (fp_lt(lfreq
, fp_sub(lfreqs
[0], fp_shr(LOG_D_NOTE
, 1))))
641 lfreq
= fp_add(lfreq
, LOG_2
);
642 while (fp_gte(lfreq
, fp_sub(fp_add(lfreqs
[0], LOG_2
),
643 fp_shr(LOG_D_NOTE
, 1))))
644 lfreq
= fp_sub(lfreq
, LOG_2
);
648 ldf
= fp_gt(fp_sub(lfreq
,lfreqs
[i
]), FP_ZERO
) ?
649 fp_sub(lfreq
,lfreqs
[i
]) : fp_neg(fp_sub(lfreq
,lfreqs
[i
]));
650 if (fp_lt(ldf
, mldf
))
657 while (fp_gt(fp_div(nfreq
, freq
), D_NOTE_SQRT
))
658 nfreq
= fp_shr(nfreq
, 1);
659 while (fp_gt(fp_div(freq
, nfreq
), D_NOTE_SQRT
))
661 nfreq
= fp_shl(nfreq
, 1);
664 ldf
=fp_mul(int2fixed(1200), log(fp_div(freq
,nfreq
)));
666 draw_freq
= (fp_round(freq
) != 0);
669 rb
->snprintf(str_buf
,30, "%s : %d cents (%d.%dHz)",
670 notes
[note
], fp_round(ldf
) ,fixed2int(freq
),
671 fp_round(fp_mul(fp_frac(freq
),
672 int2fixed(DISPLAY_HZ_PRECISION
))));
676 ldf
= FP_ZERO
; /* prevents red bar at -32 cents when freq= 0*/
679 rb
->lcd_clear_display();
680 draw_bar(ldf
); /* The red bar */
681 draw_letters(); /* The A-G letters and the # */
684 draw_points(notes
[note
]); /* The yellow point(s) */
690 /*-----------------------------------------------------------------------
691 * Functions for the Yin algorithm
693 * These were all adapted from the versions in Aubio v0.3.2
694 * Here's what the Aubio documentation has to say:
696 * This algorithm was developped by A. de Cheveigne and H. Kawahara and
699 * de Cheveign?, A., Kawahara, H. (2002) "YIN, a fundamental frequency
700 * estimator for speech and music", J. Acoust. Soc. Am. 111, 1917-1930.
702 * see http://recherche.ircam.fr/equipes/pcm/pub/people/cheveign.html
703 -------------------------------------------------------------------------*/
705 /* Find the index of the minimum element of an array of floats */
706 unsigned vec_min_elem(fixed
*s
, unsigned buflen
)
708 unsigned j
, pos
=0.0f
;
710 for (j
=0; j
< buflen
; j
++)
722 fixed
aubio_quadfrac(fixed s0
, fixed s1
, fixed s2
, fixed pf
)
724 /* Original floating point version: */
725 /* tmp = s0 + (pf/2.0f) * (pf * ( s0 - 2.0f*s1 + s2 ) -
726 3.0f*s0 + 4.0f*s1 - s2);*/
727 /* Converted to explicit operator precedence: */
728 /* tmp = s0 + ((pf/2.0f) * ((((pf * ((s0 - (2*s1)) + s2)) -
729 (3*s0)) + (4*s1)) - s2)); */
731 /* I made it look like this so I could easily track the precedence and */
732 /* make sure it matched the original expression */
733 /* Oy, this is when I really wish I could do C++ operator overloading */
774 #define QUADINT_STEP float2fixed(1.0f/200.0f)
776 fixed
vec_quadint_min(fixed
*x
, unsigned bufsize
, unsigned pos
, unsigned span
)
778 fixed res
, frac
, s0
, s1
, s2
;
779 fixed exactpos
= int2fixed(pos
);
780 /* init resold to something big (in case x[pos+-span]<0)) */
781 fixed resold
= FP_MAX
;
783 if ((pos
> span
) && (pos
< bufsize
-span
))
789 for (frac
= float2fixed(0.0f
);
790 fp_lt(frac
, float2fixed(2.0f
));
791 frac
= fp_add(frac
, QUADINT_STEP
))
793 res
= aubio_quadfrac(s0
, s1
, s2
, frac
);
794 if (fp_lt(res
, resold
))
800 /* exactpos += (frac-QUADINT_STEP)*span - span/2.0f; */
801 exactpos
= fp_add(exactpos
,
804 fp_sub(frac
, QUADINT_STEP
),
818 /* Calculate the period of the note in the
819 buffer using the YIN algorithm */
820 /* The yin pointer is just a buffer that the algorithm uses as a work
821 space. It needs to be half the length of the input buffer. */
823 fixed
pitchyin(audio_sample_type
*input
, fixed
*yin
)
828 unsigned yin_size
= tuner_settings
.sample_size
/ 4;
830 fixed tmp
= FP_ZERO
, tmp2
= FP_ZERO
;
831 yin
[0] = int2fixed(1);
832 for (tau
= 1; tau
< yin_size
; tau
++)
835 for (j
= 0; j
< yin_size
; j
++)
837 tmp
= fp_sub(int2mantissa(input
[2 * j
]),
838 int2mantissa(input
[2 * (j
+ tau
)]));
839 yin
[tau
] = fp_add(yin
[tau
], fp_mul(tmp
, tmp
));
841 tmp2
= fp_add(tmp2
, yin
[tau
]);
842 if(!fp_equal(tmp2
, FP_ZERO
))
844 yin
[tau
] = fp_mul(yin
[tau
], fp_div(int2fixed(tau
), tmp2
));
847 if(tau
> 4 && fp_lt(yin
[period
],
848 yin_threshold_table
[tuner_settings
.yin_threshold
])
849 && fp_lt(yin
[period
], yin
[period
+1]))
851 retval
= vec_quadint_min(yin
, yin_size
, period
, 1);
855 retval
= vec_quadint_min(yin
, yin_size
,
856 vec_min_elem(yin
, yin_size
), 1);
861 /*-----------------------------------------------------------------*/
863 uint32_t buffer_magnitude(audio_sample_type
*input
)
868 /* Operate on only one channel of the stereo signal */
869 for(n
= 0; n
< tuner_settings
.sample_size
; n
+=2)
871 tally
+= (uint64_t)input
[n
] * (uint64_t)input
[n
];
874 tally
/= tuner_settings
.sample_size
/ 2;
876 /* now tally holds the average of the squares of all the samples */
877 /* It must be between 0 and 0x7fff^2, so it fits in 32 bits */
878 return (uint32_t)tally
;
881 /* Stop the recording when the buffer is full */
882 int recording_callback(int status
)
886 rb
->pcm_stop_recording();
891 /* The main program loop */
892 void record_and_get_pitch(void)
896 /* For tracking the latency */
899 char debug_string[20];
902 bool waiting
= false;
906 /* Start recording */
907 rb
->pcm_record_data(recording_callback
, (void *) audio_data
,
908 (size_t) tuner_settings
.sample_size
*
909 sizeof(audio_sample_type
));
912 while (recording
&& !quit
) /* wait for the buffer to be filled */
915 button
=pluginlib_getaction(0, plugin_contexts
, PLA_ARRAY_COUNT
);
937 /* Let's keep track of how long this takes */
938 /* timer = *(rb->current_tick); */
942 /* Only do the heavy lifting if the volume is high enough */
943 if(buffer_magnitude(audio_data
) >
944 sqr(tuner_settings
.volume_threshold
*
945 rb
->sound_max(SOUND_MIC_GAIN
)))
949 #ifdef HAVE_ADJUSTABLE_CPU_FREQ
958 /* This returns the period of the detected pitch in samples */
959 period
= pitchyin(audio_data
, yin_buffer
);
960 /* Hz = sample rate / period */
961 if(fp_gt(period
, FP_ZERO
))
963 display_frequency(fp_period2freq(period
));
967 display_frequency(FP_ZERO
);
970 else if(redraw
|| !waiting
)
974 #ifdef HAVE_ADJUSTABLE_CPU_FREQ
975 rb
->cpu_boost(false);
977 /*rb->backlight_off();*/
978 display_frequency(FP_ZERO
);
981 /*rb->snprintf(debug_string, 20, "%x,%x",
982 audio_data[50], audio_data[52]);
983 rb->lcd_putsxy(0, 40, debug_string);
987 /* Print out how long it took to find the pitch */
989 rb->snprintf(debug_string, 20, "latency: %ld",
990 *(rb->current_tick) - timer);
991 rb->lcd_putsxy(0, 40, debug_string);
996 rb
->pcm_close_recording();
997 #ifdef HAVE_ADJUSTABLE_CPU_FREQ
998 rb
->cpu_boost(false);
1002 /* Init recording, tuning, and GUI */
1003 void init_everything(void)
1007 /* --------- Init the audio recording ----------------- */
1008 rb
->audio_set_output_source(AUDIO_SRC_PLAYBACK
);
1009 rb
->audio_set_input_source(INPUT_TYPE
, SRCF_RECORDING
);
1011 /* set to maximum gain */
1012 rb
->audio_set_recording_gain(tuner_settings
.record_gain
,
1013 tuner_settings
.record_gain
,
1016 rb
->pcm_set_frequency(SAMPLE_RATE
);
1017 rb
->pcm_apply_settings();
1019 rb
->pcm_init_recording();
1022 back_color
= rb
->lcd_get_background();
1023 front_color
= rb
->lcd_get_foreground();
1024 rb
->lcd_getstringsize("X", &font_w
, &font_h
);
1027 bar_x_minus_20
= (LCD_WIDTH
/ 2) -
1028 fixed2int(fp_mul(LCD_FACTOR
, int2fixed(20))) - font_w
;
1029 bar_x_0
= LCD_WIDTH
/ 2;
1030 bar_x_20
= (LCD_WIDTH
/ 2) +
1031 fixed2int(fp_mul(LCD_FACTOR
, int2fixed(20))) - font_w
;
1032 bar_x_50
= LCD_WIDTH
- 2 * font_w
;
1035 note_bar_y
= letter_y
+ font_h
;
1037 freq_y
= note_bar_y
+ bar_h
+ 3;
1038 error_ticks_y
= freq_y
+ font_h
+ 8;
1039 error_hline_y
= error_ticks_y
+ font_h
+ 2;
1040 error_bar_margin
= 2;
1041 error_bar_y
= error_hline_y
+ error_bar_margin
;
1042 error_hline_y2
= error_bar_y
+ bar_h
+ error_bar_margin
;
1046 enum plugin_status
plugin_start(const void* parameter
) NO_PROF_ATTR
1051 record_and_get_pitch();