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 * - Adapt the Yin FFT algorithm, which would reduce complexity from O(n^2)
33 * to O(nlogn), theoretically reducing latency by a factor of ~10. -David
36 * 08.03.2008 Started coding
37 * 21.03.2008 Pitch detection works more or less
38 * Button definitions for most targets added
39 * 02.04.2008 Proper GUI added
40 * Todo, Major Changes and Current Limitations added
41 * 08.19.2009 Brought the code up to date with current plugin standards
42 * Made it work more nicely with color, BW and grayscale
43 * Changed pitch detection to use the Yin algorithm (better
44 * detection, but slower -- would be ~4x faster with
45 * fixed point math, I think). Code was poached from the
46 * Aubio sound processing library (aubio.org). -David
47 * 08.31.2009 Lots of changes:
48 * Added a menu to tweak settings
49 * Converted everything to fixed point (greatly improving
51 * Improved the display
52 * Improved efficiency with judicious use of cpu_boost, the
53 * backlight, and volume detection to limit unneeded
55 * Fixed a problem that caused an octave-off error
59 * CURRENT LIMITATIONS:
60 * - No gapless recording. Strictly speaking true gappless isn't possible,
61 * since the algorithm takes longer to calculate than the length of the
62 * sample, but latency could be improved a bit with proper use of the DMA
63 * recording functions.
64 * - Due to how the Yin algorithm works, latency is higher for lower
69 #include "lib/pluginlib_actions.h"
70 #include "lib/picture.h"
71 #include "pluginbitmaps/pitch_notes.h"
75 /* First figure out what sample rate we're going to use */
76 #if (REC_SAMPR_CAPS & SAMPR_CAP_44)
77 #define SAMPLE_RATE SAMPR_44
78 #elif (REC_SAMPR_CAPS & SAMPR_CAP_22)
79 #define SAMPLE_RATE SAMPR_22
80 #elif (REC_SAMPR_CAPS & SAMPR_CAP_11)
81 #define SAMPLE_RATE SAMPR_11
84 /* Some fixed point calculation stuff */
85 typedef int32_t fixed_data
;
90 typedef struct _fixed fixed
;
91 #define FIXED_PRECISION 18
92 #define FP_MAX ((fixed) {0x7fffffff})
93 #define FP_MIN ((fixed) {-0x80000000})
94 #define int2fixed(x) ((fixed){(x) << FIXED_PRECISION})
95 #define int2mantissa(x) ((fixed){x})
96 #define fixed2int(x) ((int)((x).a >> FIXED_PRECISION))
97 #define fixed2float(x) (((float)(x).a) / ((float)(1 << FIXED_PRECISION)))
98 #define float2fixed(x) \
99 ((fixed){(fixed_data)(x * (float)(1 << FIXED_PRECISION))})
100 /* I adapted these ones from the Rockbox fixed point library */
101 #define fp_mul(x, y) \
102 ((fixed){(((int64_t)((x).a)) * ((int64_t)((y).a))) >> (FIXED_PRECISION)})
103 #define fp_div(x, y) \
104 ((fixed){(((int64_t)((x).a)) << (FIXED_PRECISION)) / ((int64_t)((y).a))})
105 /* Operators for fixed point */
106 #define fp_add(x, y) ((fixed){(x).a + (y).a})
107 #define fp_sub(x, y) ((fixed){(x).a - (y).a})
108 #define fp_shl(x, y) ((fixed){(x).a << y})
109 #define fp_shr(x, y) ((fixed){(x).a >> y})
110 #define fp_neg(x) ((fixed){-(x).a})
111 #define fp_gt(x, y) ((x).a > (y).a)
112 #define fp_gte(x, y) ((x).a >= (y).a)
113 #define fp_lt(x, y) ((x).a < (y).a)
114 #define fp_lte(x, y) ((x).a <= (y).a)
115 #define fp_sqr(x) fp_mul((x), (x))
116 #define fp_equal(x, y) ((x).a == (y).a)
117 #define fp_round(x) (fixed2int(fp_add((x), float2fixed(0.5))))
118 #define fp_data(x) ((x).a)
119 #define fp_frac(x) (fp_sub((x), int2fixed(fixed2int(x))))
120 #define FP_ZERO ((fixed){0})
121 #define FP_LOW ((fixed){2})
123 /* Some defines for converting between period and frequency */
125 /* I introduce some divisors in this because the fixed point */
126 /* variables aren't big enough to hold higher than a certain */
127 /* value. This loses a bit of precision but it means we */
128 /* don't have to use 32.32 variables (yikes). */
129 /* With an 18-bit decimal precision, the max value in the */
130 /* integer part is 8192. Divide 44100 by 7 and it'll fit in */
132 #define fp_period2freq(x) fp_div(int2fixed(SAMPLE_RATE / 7), \
133 fp_div((x),int2fixed(7)))
134 #define fp_freq2period(x) fp_period2freq(x)
135 #define period2freq(x) (SAMPLE_RATE / (x))
136 #define freq2period(x) period2freq(x)
138 #define sqr(x) ((x)*(x))
140 /* Some constants for tuning */
141 #define A_FREQ float2fixed(440.0f)
142 #define D_NOTE float2fixed(1.059463094359f)
143 #define LOG_D_NOTE float2fixed(1.0f/12.0f)
144 #define D_NOTE_SQRT float2fixed(1.029302236643f)
145 #define LOG_2 float2fixed(1.0f)
147 /* The recording buffer size */
148 /* This is how much is sampled at a time. */
149 /* It also determines latency -- if BUFFER_SIZE == SAMPLE_RATE then */
150 /* there'll be one sample per second, or a latency of one second. */
151 /* Furthermore, the lowest detectable frequency will be about twice */
152 /* the number of reads per second */
153 /* If we ever switch to Yin FFT algorithm then this needs to be
155 #define BUFFER_SIZE 4096
156 #define SAMPLE_SIZE 4096
157 #define SAMPLE_SIZE_MIN 1024
158 #define YIN_BUFFER_SIZE (BUFFER_SIZE / 4)
160 #define LCD_FACTOR (fp_div(int2fixed(LCD_WIDTH), int2fixed(100)))
161 /* The threshold for the YIN algorithm */
162 #define DEFAULT_YIN_THRESHOLD 5 /* 0.10 */
163 const fixed yin_threshold_table
[] =
181 /* Structure for the reference frequency (frequency of A)
182 * It's used for scaling the frequency before finding out
183 * the note. The frequency is scaled in a way that the main
184 * algorithm can assume the frequency of A to be 440 Hz.
188 const int frequency
; /* Frequency in Hz */
189 const fixed ratio
; /* 440/frequency */
190 const fixed logratio
; /* log2(factor) */
193 const struct freq_A_entry freq_A
[] =
195 {435, float2fixed(1.011363636), float2fixed( 0.016301812)},
196 {436, float2fixed(1.009090909), float2fixed( 0.013056153)},
197 {437, float2fixed(1.006818182), float2fixed( 0.009803175)},
198 {438, float2fixed(1.004545455), float2fixed( 0.006542846)},
199 {439, float2fixed(1.002272727), float2fixed( 0.003275132)},
200 {440, float2fixed(1.000000000), float2fixed( 0.000000000)},
201 {441, float2fixed(0.997727273), float2fixed(-0.003282584)},
202 {442, float2fixed(0.995454545), float2fixed(-0.006572654)},
203 {443, float2fixed(0.993181818), float2fixed(-0.009870244)},
204 {444, float2fixed(0.990909091), float2fixed(-0.013175389)},
205 {445, float2fixed(0.988636364), float2fixed(-0.016488123)},
208 /* Index of the entry for 440 Hz in the table (default frequency for A) */
209 #define DEFAULT_FREQ_A 5
210 #define NUM_FREQ_A (sizeof(freq_A)/sizeof(freq_A[0]))
212 /* How loud the audio has to be to start displaying pitch */
213 /* Must be between 0 and 100 */
214 #define VOLUME_THRESHOLD (50)
216 /* Change to AUDIO_SRC_LINEIN if you want to record from line-in */
218 #define INPUT_TYPE AUDIO_SRC_MIC
220 #define INPUT_TYPE AUDIO_SRC_LINEIN
223 /* How many decimal places to display for the Hz value */
224 #define DISPLAY_HZ_PRECISION 100
226 /* Where to put the various GUI elements */
229 #define LCD_RES_MIN (LCD_HEIGHT < LCD_WIDTH ? LCD_HEIGHT : LCD_WIDTH)
230 #define BAR_PADDING (LCD_RES_MIN / 32)
231 #define BAR_Y (LCD_HEIGHT * 3 / 4)
232 #define BAR_HEIGHT (LCD_RES_MIN / 4 - BAR_PADDING)
233 #define BAR_HLINE_Y (BAR_Y - BAR_PADDING)
234 #define BAR_HLINE_Y2 (BAR_Y + BAR_HEIGHT + BAR_PADDING - 1)
236 #define GRADUATION 10 /* Subdivisions of the whole 100-cent scale */
238 /* Bitmaps for drawing the note names. These need to have height
239 <= (bar_grad_y - note_y), or 15/32 * LCD_HEIGHT
241 #define NUM_NOTE_IMAGES 9
242 #define NOTE_INDEX_A 0
243 #define NOTE_INDEX_B 1
244 #define NOTE_INDEX_C 2
245 #define NOTE_INDEX_D 3
246 #define NOTE_INDEX_E 4
247 #define NOTE_INDEX_F 5
248 #define NOTE_INDEX_G 6
249 #define NOTE_INDEX_SHARP 7
250 #define NOTE_INDEX_FLAT 8
251 const struct picture note_bitmaps
=
254 BMPWIDTH_pitch_notes
,
255 BMPHEIGHT_pitch_notes
,
256 BMPHEIGHT_pitch_notes
/NUM_NOTE_IMAGES
260 typedef signed short audio_sample_type
;
261 /* It's stereo, so make the buffer twice as big */
262 audio_sample_type audio_data
[BUFFER_SIZE
];
263 fixed yin_buffer
[YIN_BUFFER_SIZE
];
264 static int recording
=0;
266 /* Description of a note of scale */
269 const char *name
; /* Name of the note, e.g. "A#" */
270 const fixed freq
; /* Note frequency, Hz */
271 const fixed logfreq
; /* log2(frequency) */
274 /* Notes within one (reference) scale */
275 static const struct note_entry notes
[] =
277 {"A" , float2fixed(440.0000000f
), float2fixed(8.781359714f
)},
278 {"A#", float2fixed(466.1637615f
), float2fixed(8.864693047f
)},
279 {"B" , float2fixed(493.8833013f
), float2fixed(8.948026380f
)},
280 {"C" , float2fixed(523.2511306f
), float2fixed(9.031359714f
)},
281 {"C#", float2fixed(554.3652620f
), float2fixed(9.114693047f
)},
282 {"D" , float2fixed(587.3295358f
), float2fixed(9.198026380f
)},
283 {"D#", float2fixed(622.2539674f
), float2fixed(9.281359714f
)},
284 {"E" , float2fixed(659.2551138f
), float2fixed(9.364693047f
)},
285 {"F" , float2fixed(698.4564629f
), float2fixed(9.448026380f
)},
286 {"F#", float2fixed(739.9888454f
), float2fixed(9.531359714f
)},
287 {"G" , float2fixed(783.9908720f
), float2fixed(9.614693047f
)},
288 {"G#", float2fixed(830.6093952f
), float2fixed(9.698026380f
)},
292 static unsigned back_color
, front_color
;
293 static int font_w
,font_h
;
295 static int lbl_x_minus_50
, lbl_x_minus_20
, lbl_x_0
, lbl_x_20
, lbl_x_50
;
297 /* Settings for the plugin */
298 struct tuner_settings
300 unsigned volume_threshold
;
301 unsigned record_gain
;
302 unsigned sample_size
;
303 unsigned lowest_freq
;
304 unsigned yin_threshold
;
305 int freq_A
; /* Index of the frequency of A */
310 /*=================================================================*/
311 /* Settings loading and saving(adapted from the clock plugin) */
312 /*=================================================================*/
314 #define SETTINGS_FILENAME PLUGIN_APPS_DIR "/.pitch_settings"
326 enum settings_file_status
332 /* The settings as they exist on the hard disk, so that
333 * we can know at saving time if changes have been made */
334 struct tuner_settings hdd_tuner_settings
;
336 /*---------------------------------------------------------------------*/
338 bool settings_needs_saving(struct tuner_settings
* settings
)
340 return(rb
->memcmp(settings
, &hdd_tuner_settings
, sizeof(*settings
)));
343 /*---------------------------------------------------------------------*/
345 void tuner_settings_reset(struct tuner_settings
* settings
)
347 settings
->volume_threshold
= VOLUME_THRESHOLD
;
348 settings
->record_gain
= rb
->global_settings
->rec_mic_gain
;
349 settings
->sample_size
= BUFFER_SIZE
;
350 settings
->lowest_freq
= period2freq(BUFFER_SIZE
/ 4);
351 settings
->yin_threshold
= DEFAULT_YIN_THRESHOLD
;
352 settings
->freq_A
= DEFAULT_FREQ_A
;
353 settings
->use_sharps
= true;
354 settings
->display_hz
= false;
357 /*---------------------------------------------------------------------*/
359 enum settings_file_status
tuner_settings_load(struct tuner_settings
* settings
,
362 int fd
= rb
->open(filename
, O_RDONLY
);
363 if(fd
>= 0){ /* does file exist? */
364 /* basic consistency check */
365 if(rb
->filesize(fd
) == sizeof(*settings
)){
366 rb
->read(fd
, settings
, sizeof(*settings
));
368 rb
->memcpy(&hdd_tuner_settings
, settings
, sizeof(*settings
));
372 /* Initializes the settings with default values at least */
373 tuner_settings_reset(settings
);
377 /*---------------------------------------------------------------------*/
379 enum settings_file_status
tuner_settings_save(struct tuner_settings
* settings
,
382 int fd
= rb
->creat(filename
);
383 if(fd
>= 0){ /* does file exist? */
384 rb
->write (fd
, settings
, sizeof(*settings
));
391 /*---------------------------------------------------------------------*/
393 void load_settings(void)
395 tuner_settings_load(&tuner_settings
, SETTINGS_FILENAME
);
400 /*---------------------------------------------------------------------*/
402 void save_settings(void)
404 if(!settings_needs_saving(&tuner_settings
))
407 tuner_settings_save(&tuner_settings
, SETTINGS_FILENAME
);
410 /*=================================================================*/
412 /*=================================================================*/
415 const struct button_mapping
* plugin_contexts
[]={
417 generic_increase_decrease
,
423 #define PLA_ARRAY_COUNT sizeof(plugin_contexts)/sizeof(plugin_contexts[0])
427 /* This has to match yin_threshold_table */
428 static const struct opt_items yin_threshold_text
[] =
446 static const struct opt_items accidental_text
[] =
452 void set_min_freq(int new_freq
)
454 tuner_settings
.sample_size
= freq2period(new_freq
) * 4;
456 /* clamp the sample size between min and max */
457 if(tuner_settings
.sample_size
<= SAMPLE_SIZE_MIN
)
458 tuner_settings
.sample_size
= SAMPLE_SIZE_MIN
;
459 else if(tuner_settings
.sample_size
>= BUFFER_SIZE
)
460 tuner_settings
.sample_size
= BUFFER_SIZE
;
461 /* sample size must be divisible by 4 */
462 else if(tuner_settings
.sample_size
% 4 != 0)
463 tuner_settings
.sample_size
+= 4 - (tuner_settings
.sample_size
% 4);
470 bool exit_tuner
= false;
475 MENUITEM_STRINGLIST(menu
,"Tuner Settings",NULL
,
480 "Algorithm Pickiness",
482 "Display Frequency (Hz)",
483 "Frequency of A (Hz)",
489 choice
= rb
->do_menu(&menu
, &selection
, NULL
, false);
493 rb
->set_int("Volume Threshold", "%", UNIT_INT
,
494 &tuner_settings
.volume_threshold
,
495 NULL
, 5, 5, 95, NULL
);
498 rb
->set_int("Listening Volume", "%", UNIT_INT
,
499 &tuner_settings
.record_gain
,
500 NULL
, 1, rb
->sound_min(SOUND_MIC_GAIN
),
501 rb
->sound_max(SOUND_MIC_GAIN
), NULL
);
504 rb
->set_int("Lowest Frequency", "Hz", UNIT_INT
,
505 &tuner_settings
.lowest_freq
, set_min_freq
, 1,
506 /* Range depends on the size of the buffer */
507 SAMPLE_RATE
/ (BUFFER_SIZE
/ 4),
508 SAMPLE_RATE
/ (SAMPLE_SIZE_MIN
/ 4), NULL
);
512 "Algorithm Pickiness (Lower -> more discriminating)",
513 &tuner_settings
.yin_threshold
,
514 INT
, yin_threshold_text
,
515 sizeof(yin_threshold_text
) / sizeof(yin_threshold_text
[0]),
519 rb
->set_option("Display Accidentals As",
520 &tuner_settings
.use_sharps
,
521 BOOL
, accidental_text
, 2, NULL
);
524 rb
->set_bool("Display Frequency (Hz)",
525 &tuner_settings
.display_hz
);
528 freq_val
= freq_A
[tuner_settings
.freq_A
].frequency
;
529 rb
->set_int("Frequency of A (Hz)",
530 "Hz", UNIT_INT
, &freq_val
, NULL
,
531 1, freq_A
[0].frequency
, freq_A
[NUM_FREQ_A
-1].frequency
,
533 tuner_settings
.freq_A
= freq_val
- freq_A
[0].frequency
;
537 rb
->set_bool("Reset Tuner Settings?", &reset
);
539 tuner_settings_reset(&tuner_settings
);
547 /* Return to the tuner */
555 /*=================================================================*/
557 /*=================================================================*/
559 /* Fixed-point log base 2*/
560 /* Adapted from python code at
561 http://en.wikipedia.org/wiki/Binary_logarithm#Algorithm
566 fixed fp
= int2fixed(1);
567 fixed res
= int2fixed(0);
569 if(fp_lte(x
, FP_ZERO
))
576 while(fp_lt(x
, int2fixed(1)))
578 res
= fp_sub(res
, int2fixed(1));
582 while(fp_gte(x
, int2fixed(2)))
584 res
= fp_add(res
, int2fixed(1));
588 /* Fractional part */
590 while(fp_gt(fp
, FP_ZERO
))
595 if(fp_gte(x
, int2fixed(2)))
598 res
= fp_add(res
, fp
);
605 /*=================================================================*/
607 /*=================================================================*/
609 /* The function name is pretty self-explaining ;) */
610 void print_int_xy(int x
, int y
, int v
)
614 rb
->lcd_set_foreground(front_color
);
615 rb
->snprintf(temp
,20,"%d",v
);
616 rb
->lcd_putsxy(x
,y
,temp
);
619 /* Print out the frequency etc */
620 void print_str(char* s
)
622 rb
->lcd_set_foreground(front_color
);
623 rb
->lcd_putsxy(0, HZ_Y
, s
);
626 /* What can I say? Read the function name... */
627 void print_char_xy(int x
, int y
, char c
)
633 rb
->lcd_set_foreground(front_color
);
635 rb
->lcd_putsxy(x
, y
, temp
);
638 /* Draw the note bitmap */
639 void draw_note(const char *note
)
642 int note_x
= (LCD_WIDTH
- BMPWIDTH_pitch_notes
) / 2;
643 int accidental_index
= NOTE_INDEX_SHARP
;
649 if(!(tuner_settings
.use_sharps
))
652 accidental_index
= NOTE_INDEX_FLAT
;
655 vertical_picture_draw_sprite(rb
->screens
[0],
660 note_x
= LCD_WIDTH
/ 2 - BMPWIDTH_pitch_notes
;
663 vertical_picture_draw_sprite(rb
->screens
[0], ¬e_bitmaps
, i
,
667 /* Draw the red bar and the white lines */
668 void draw_bar(fixed wrong_by_cents
)
673 #ifdef HAVE_LCD_COLOR
674 rb
->lcd_set_foreground(LCD_RGBPACK(255,255,255)); /* Color screens */
676 rb
->lcd_set_foreground(LCD_BLACK
); /* Greyscale screens */
678 rb
->lcd_set_foreground(LCD_BLACK
); /* Black and white screens */
681 rb
->lcd_hline(0,LCD_WIDTH
-1, BAR_HLINE_Y
);
682 rb
->lcd_hline(0,LCD_WIDTH
-1, BAR_HLINE_Y2
);
684 /* Draw graduation lines on the off-by readout */
685 for(n
= 0; n
<= GRADUATION
; n
++)
687 x
= (LCD_WIDTH
* n
+ GRADUATION
/ 2) / GRADUATION
;
690 rb
->lcd_vline(x
, BAR_HLINE_Y
, BAR_HLINE_Y2
);
693 print_int_xy(lbl_x_minus_50
,bar_grad_y
, -50);
694 print_int_xy(lbl_x_minus_20
,bar_grad_y
, -20);
695 print_int_xy(lbl_x_0
,bar_grad_y
, 0);
696 print_int_xy(lbl_x_20
,bar_grad_y
, 20);
697 print_int_xy(lbl_x_50
,bar_grad_y
, 50);
699 #ifdef HAVE_LCD_COLOR
700 rb
->lcd_set_foreground(LCD_RGBPACK(255,0,0)); /* Color screens */
702 rb
->lcd_set_foreground(LCD_DARKGRAY
); /* Greyscale screens */
704 rb
->lcd_set_foreground(LCD_BLACK
); /* Black and white screens */
707 if (fp_gt(wrong_by_cents
, FP_ZERO
))
709 rb
->lcd_fillrect(bar_x_0
, BAR_Y
,
710 fixed2int(fp_mul(wrong_by_cents
, LCD_FACTOR
)), BAR_HEIGHT
);
714 rb
->lcd_fillrect(bar_x_0
+ fixed2int(fp_mul(wrong_by_cents
,LCD_FACTOR
)),
716 fixed2int(fp_mul(wrong_by_cents
, LCD_FACTOR
)) * -1,
721 /* Calculate how wrong the note is and draw the GUI */
722 void display_frequency (fixed freq
)
730 if (fp_lt(freq
, FP_LOW
))
733 /* We calculate the frequency and its log as if */
734 /* the reference frequency of A were 440 Hz. */
736 lfreq
= fp_add(log(freq
), freq_A
[tuner_settings
.freq_A
].logratio
);
737 freq
= fp_mul(freq
, freq_A
[tuner_settings
.freq_A
].ratio
);
739 /* This calculates a log freq offset for note A */
740 /* Get the frequency to within the range of our reference table, */
741 /* i.e. into the right octave. */
742 while (fp_lt(lfreq
, fp_sub(notes
[0].logfreq
, fp_shr(LOG_D_NOTE
, 1))))
743 lfreq
= fp_add(lfreq
, LOG_2
);
744 while (fp_gte(lfreq
, fp_sub(fp_add(notes
[0].logfreq
, LOG_2
),
745 fp_shr(LOG_D_NOTE
, 1))))
746 lfreq
= fp_sub(lfreq
, LOG_2
);
750 ldf
= fp_gt(fp_sub(lfreq
,notes
[i
].logfreq
), FP_ZERO
) ?
751 fp_sub(lfreq
,notes
[i
].logfreq
) : fp_neg(fp_sub(lfreq
,notes
[i
].logfreq
));
752 if (fp_lt(ldf
, mldf
))
758 nfreq
= notes
[note
].freq
;
759 while (fp_gt(fp_div(nfreq
, freq
), D_NOTE_SQRT
))
760 nfreq
= fp_shr(nfreq
, 1);
762 while (fp_gt(fp_div(freq
, nfreq
), D_NOTE_SQRT
))
763 nfreq
= fp_shl(nfreq
, 1);
765 ldf
= fp_mul(int2fixed(1200), log(fp_div(freq
,nfreq
)));
767 rb
->lcd_clear_display();
768 draw_bar(ldf
); /* The red bar */
769 if(fp_round(freq
) != 0)
771 draw_note(notes
[note
].name
);
772 if(tuner_settings
.display_hz
)
774 rb
->snprintf(str_buf
,30, "%s : %d cents (%d.%02dHz)",
775 notes
[note
].name
, fp_round(ldf
) ,fixed2int(orig_freq
),
776 fp_round(fp_mul(fp_frac(orig_freq
),
777 int2fixed(DISPLAY_HZ_PRECISION
))));
784 /*-----------------------------------------------------------------------
785 * Functions for the Yin algorithm
787 * These were all adapted from the versions in Aubio v0.3.2
788 * Here's what the Aubio documentation has to say:
790 * This algorithm was developped by A. de Cheveigne and H. Kawahara and
793 * de Cheveign?, A., Kawahara, H. (2002) "YIN, a fundamental frequency
794 * estimator for speech and music", J. Acoust. Soc. Am. 111, 1917-1930.
796 * see http://recherche.ircam.fr/equipes/pcm/pub/people/cheveign.html
797 -------------------------------------------------------------------------*/
799 /* Find the index of the minimum element of an array of floats */
800 unsigned vec_min_elem(fixed
*s
, unsigned buflen
)
802 unsigned j
, pos
=0.0f
;
804 for (j
=0; j
< buflen
; j
++)
816 fixed
aubio_quadfrac(fixed s0
, fixed s1
, fixed s2
, fixed pf
)
818 /* Original floating point version: */
819 /* tmp = s0 + (pf/2.0f) * (pf * ( s0 - 2.0f*s1 + s2 ) -
820 3.0f*s0 + 4.0f*s1 - s2);*/
821 /* Converted to explicit operator precedence: */
822 /* tmp = s0 + ((pf/2.0f) * ((((pf * ((s0 - (2*s1)) + s2)) -
823 (3*s0)) + (4*s1)) - s2)); */
825 /* I made it look like this so I could easily track the precedence and */
826 /* make sure it matched the original expression */
827 /* Oy, this is when I really wish I could do C++ operator overloading */
868 #define QUADINT_STEP float2fixed(1.0f/200.0f)
870 fixed
vec_quadint_min(fixed
*x
, unsigned bufsize
, unsigned pos
, unsigned span
)
872 fixed res
, frac
, s0
, s1
, s2
;
873 fixed exactpos
= int2fixed(pos
);
874 /* init resold to something big (in case x[pos+-span]<0)) */
875 fixed resold
= FP_MAX
;
877 if ((pos
> span
) && (pos
< bufsize
-span
))
883 for (frac
= float2fixed(0.0f
);
884 fp_lt(frac
, float2fixed(2.0f
));
885 frac
= fp_add(frac
, QUADINT_STEP
))
887 res
= aubio_quadfrac(s0
, s1
, s2
, frac
);
888 if (fp_lt(res
, resold
))
894 /* exactpos += (frac-QUADINT_STEP)*span - span/2.0f; */
895 exactpos
= fp_add(exactpos
,
898 fp_sub(frac
, QUADINT_STEP
),
912 /* Calculate the period of the note in the
913 buffer using the YIN algorithm */
914 /* The yin pointer is just a buffer that the algorithm uses as a work
915 space. It needs to be half the length of the input buffer. */
917 fixed
pitchyin(audio_sample_type
*input
, fixed
*yin
)
922 unsigned yin_size
= tuner_settings
.sample_size
/ 4;
924 fixed tmp
= FP_ZERO
, tmp2
= FP_ZERO
;
925 yin
[0] = int2fixed(1);
926 for (tau
= 1; tau
< yin_size
; tau
++)
929 for (j
= 0; j
< yin_size
; j
++)
931 tmp
= fp_sub(int2mantissa(input
[2 * j
]),
932 int2mantissa(input
[2 * (j
+ tau
)]));
933 yin
[tau
] = fp_add(yin
[tau
], fp_mul(tmp
, tmp
));
935 tmp2
= fp_add(tmp2
, yin
[tau
]);
936 if(!fp_equal(tmp2
, FP_ZERO
))
938 yin
[tau
] = fp_mul(yin
[tau
], fp_div(int2fixed(tau
), tmp2
));
941 if(tau
> 4 && fp_lt(yin
[period
],
942 yin_threshold_table
[tuner_settings
.yin_threshold
])
943 && fp_lt(yin
[period
], yin
[period
+1]))
945 retval
= vec_quadint_min(yin
, yin_size
, period
, 1);
949 retval
= vec_quadint_min(yin
, yin_size
,
950 vec_min_elem(yin
, yin_size
), 1);
955 /*-----------------------------------------------------------------*/
957 uint32_t buffer_magnitude(audio_sample_type
*input
)
962 /* Operate on only one channel of the stereo signal */
963 for(n
= 0; n
< tuner_settings
.sample_size
; n
+=2)
965 tally
+= (uint64_t)input
[n
] * (uint64_t)input
[n
];
968 tally
/= tuner_settings
.sample_size
/ 2;
970 /* now tally holds the average of the squares of all the samples */
971 /* It must be between 0 and 0x7fff^2, so it fits in 32 bits */
972 return (uint32_t)tally
;
975 /* Stop the recording when the buffer is full */
976 int recording_callback(int status
)
980 rb
->pcm_stop_recording();
985 /* The main program loop */
986 void record_and_get_pitch(void)
990 /* For tracking the latency */
993 char debug_string[20];
997 bool waiting
= false;
1003 /* Start recording */
1004 rb
->pcm_record_data(recording_callback
, (void *) audio_data
,
1005 (size_t) tuner_settings
.sample_size
*
1006 sizeof(audio_sample_type
));
1010 while (recording
&& !quit
) /* wait for the buffer to be filled */
1014 /* Only do this loop once if this is the simulator */
1017 button
=pluginlib_getaction(0, plugin_contexts
, PLA_ARRAY_COUNT
);
1042 /* Only do the heavy lifting if the volume is high enough */
1043 if(buffer_magnitude(audio_data
) >
1044 sqr(tuner_settings
.volume_threshold
*
1045 rb
->sound_max(SOUND_MIC_GAIN
)))
1049 #ifdef HAVE_ADJUSTABLE_CPU_FREQ
1050 rb
->cpu_boost(true);
1058 /* This returns the period of the detected pitch in samples */
1059 period
= pitchyin(audio_data
, yin_buffer
);
1060 /* Hz = sample rate / period */
1061 if(fp_gt(period
, FP_ZERO
))
1063 display_frequency(fp_period2freq(period
));
1067 display_frequency(FP_ZERO
);
1070 else if(redraw
|| !waiting
)
1074 #ifdef HAVE_ADJUSTABLE_CPU_FREQ
1075 rb
->cpu_boost(false);
1077 /*rb->backlight_off();*/
1078 display_frequency(FP_ZERO
);
1080 #else /* SIMULATOR */
1081 /* Display a preselected frequency */
1082 display_frequency(int2fixed(445));
1086 rb
->pcm_close_recording();
1087 #ifdef HAVE_ADJUSTABLE_CPU_FREQ
1088 rb
->cpu_boost(false);
1092 /* Init recording, tuning, and GUI */
1093 void init_everything(void)
1097 /* --------- Init the audio recording ----------------- */
1098 rb
->audio_set_output_source(AUDIO_SRC_PLAYBACK
);
1099 rb
->audio_set_input_source(INPUT_TYPE
, SRCF_RECORDING
);
1101 /* set to maximum gain */
1102 rb
->audio_set_recording_gain(tuner_settings
.record_gain
,
1103 tuner_settings
.record_gain
,
1106 rb
->pcm_set_frequency(SAMPLE_RATE
);
1107 rb
->pcm_apply_settings();
1109 rb
->pcm_init_recording();
1112 back_color
= rb
->lcd_get_background();
1113 front_color
= rb
->lcd_get_foreground();
1114 rb
->lcd_getstringsize("X", &font_w
, &font_h
);
1116 bar_x_0
= LCD_WIDTH
/ 2;
1118 lbl_x_minus_20
= (LCD_WIDTH
/ 2) -
1119 fixed2int(fp_mul(LCD_FACTOR
, int2fixed(20))) - font_w
;
1120 lbl_x_0
= (LCD_WIDTH
- font_w
) / 2;
1121 lbl_x_20
= (LCD_WIDTH
/ 2) +
1122 fixed2int(fp_mul(LCD_FACTOR
, int2fixed(20))) - font_w
;
1123 lbl_x_50
= LCD_WIDTH
- 2 * font_w
;
1125 bar_grad_y
= BAR_Y
- BAR_PADDING
- font_h
;
1126 /* Put the note right between the top and bottom text elements */
1127 note_y
= ((font_h
+ bar_grad_y
- note_bitmaps
.slide_height
) / 2);
1131 enum plugin_status
plugin_start(const void* parameter
) NO_PROF_ATTR
1136 record_and_get_pitch();