Pitch Detector: group note related data together
[kugel-rb.git] / apps / plugins / pitch_detector.c
blob461b8562d8f3dd78bad62f87016de53072d4fc02
1 /**************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
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 * ----------------------------------------------------------------------------
20 * INTRODUCTION:
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.
27 * TODO:
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
35 * MAJOR CHANGES:
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
50 * latency)
51 * Improved the display
52 * Improved efficiency with judicious use of cpu_boost, the
53 * backlight, and volume detection to limit unneeded
54 * calculation
55 * Fixed a problem that caused an octave-off error
56 * -David
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
65 * frequencies.
68 #include "plugin.h"
69 #include "lib/pluginlib_actions.h"
70 #include "lib/picture.h"
71 #include "pluginbitmaps/pitch_notes.h"
73 PLUGIN_HEADER
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
82 #endif
84 /* Some fixed point calculation stuff */
85 typedef int32_t fixed_data;
86 struct _fixed
88 fixed_data a;
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){1})
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 */
131 /* that variable. */
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
154 a power of 2 */
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[] =
165 float2fixed(0.01),
166 float2fixed(0.02),
167 float2fixed(0.03),
168 float2fixed(0.04),
169 float2fixed(0.05),
170 float2fixed(0.10),
171 float2fixed(0.15),
172 float2fixed(0.20),
173 float2fixed(0.25),
174 float2fixed(0.30),
175 float2fixed(0.35),
176 float2fixed(0.40),
177 float2fixed(0.45),
178 float2fixed(0.50),
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.
186 struct freq_A_entry
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)
215 /* Change to AUDIO_SRC_LINEIN if you want to record from line-in */
216 #define INPUT_TYPE AUDIO_SRC_MIC
218 /* How many decimal places to display for the Hz value */
219 #define DISPLAY_HZ_PRECISION 100
221 /* Where to put the various GUI elements */
222 int note_y;
223 int bar_grad_y;
224 #define LCD_RES_MIN (LCD_HEIGHT < LCD_WIDTH ? LCD_HEIGHT : LCD_WIDTH)
225 #define BAR_PADDING (LCD_RES_MIN / 32)
226 #define BAR_Y (LCD_HEIGHT * 3 / 4)
227 #define BAR_HEIGHT (LCD_RES_MIN / 4 - BAR_PADDING)
228 #define BAR_HLINE_Y (BAR_Y - BAR_PADDING)
229 #define BAR_HLINE_Y2 (BAR_Y + BAR_HEIGHT + BAR_PADDING - 1)
230 #define HZ_Y 0
231 #define GRADUATION 10 /* Subdivisions of the whole 100-cent scale */
233 /* Bitmaps for drawing the note names. These need to have height
234 <= (bar_grad_y - note_y), or 15/32 * LCD_HEIGHT
236 #define NUM_NOTE_IMAGES 9
237 #define NOTE_INDEX_A 0
238 #define NOTE_INDEX_B 1
239 #define NOTE_INDEX_C 2
240 #define NOTE_INDEX_D 3
241 #define NOTE_INDEX_E 4
242 #define NOTE_INDEX_F 5
243 #define NOTE_INDEX_G 6
244 #define NOTE_INDEX_SHARP 7
245 #define NOTE_INDEX_FLAT 8
246 const struct picture note_bitmaps =
248 pitch_notes,
249 BMPWIDTH_pitch_notes,
250 BMPHEIGHT_pitch_notes,
251 BMPHEIGHT_pitch_notes/NUM_NOTE_IMAGES
255 typedef signed short audio_sample_type;
256 /* It's stereo, so make the buffer twice as big */
257 audio_sample_type audio_data[BUFFER_SIZE];
258 fixed yin_buffer[YIN_BUFFER_SIZE];
259 static int recording=0;
261 /* Description of a note of scale */
262 struct note_entry
264 const char *name; /* Name of the note, e.g. "A#" */
265 const fixed freq; /* Note frequency */
266 const fixed logfreq; /* log2(frequency) */
269 /* Notes within one (reference) scale */
270 static const struct note_entry notes[] =
272 {"A" , float2fixed(440.0000000f), float2fixed(8.781359714f)},
273 {"A#", float2fixed(466.1637615f), float2fixed(8.864693047f)},
274 {"B" , float2fixed(493.8833013f), float2fixed(8.948026380f)},
275 {"C" , float2fixed(523.2511306f), float2fixed(9.031359714f)},
276 {"C#", float2fixed(554.3652620f), float2fixed(9.114693047f)},
277 {"D" , float2fixed(587.3295358f), float2fixed(9.198026380f)},
278 {"D#", float2fixed(622.2539674f), float2fixed(9.281359714f)},
279 {"E" , float2fixed(659.2551138f), float2fixed(9.364693047f)},
280 {"F" , float2fixed(698.4564629f), float2fixed(9.448026380f)},
281 {"F#", float2fixed(739.9888454f), float2fixed(9.531359714f)},
282 {"G" , float2fixed(783.9908720f), float2fixed(9.614693047f)},
283 {"G#", float2fixed(830.6093952f), float2fixed(9.698026380f)},
286 /* GUI */
287 static unsigned back_color, front_color;
288 static int font_w,font_h;
289 static int bar_x_0;
290 static int lbl_x_minus_50, lbl_x_minus_20, lbl_x_0, lbl_x_20, lbl_x_50;
292 /* Settings for the plugin */
293 struct tuner_settings
295 unsigned volume_threshold;
296 unsigned record_gain;
297 unsigned sample_size;
298 unsigned lowest_freq;
299 unsigned yin_threshold;
300 int freq_A; /* Index of the frequency of A */
301 bool use_sharps;
302 bool display_hz;
303 } tuner_settings;
305 /*=================================================================*/
306 /* Settings loading and saving(adapted from the clock plugin) */
307 /*=================================================================*/
309 #define SETTINGS_FILENAME PLUGIN_APPS_DIR "/.pitch_settings"
311 enum message
313 MESSAGE_LOADING,
314 MESSAGE_LOADED,
315 MESSAGE_ERRLOAD,
316 MESSAGE_SAVING,
317 MESSAGE_SAVED,
318 MESSAGE_ERRSAVE
321 enum settings_file_status
323 LOADED, ERRLOAD,
324 SAVED, ERRSAVE
327 /* The settings as they exist on the hard disk, so that
328 * we can know at saving time if changes have been made */
329 struct tuner_settings hdd_tuner_settings;
331 /*---------------------------------------------------------------------*/
333 bool settings_needs_saving(struct tuner_settings* settings)
335 return(rb->memcmp(settings, &hdd_tuner_settings, sizeof(*settings)));
338 /*---------------------------------------------------------------------*/
340 void tuner_settings_reset(struct tuner_settings* settings)
342 settings->volume_threshold = VOLUME_THRESHOLD;
343 settings->record_gain = rb->global_settings->rec_mic_gain;
344 settings->sample_size = BUFFER_SIZE;
345 settings->lowest_freq = period2freq(BUFFER_SIZE / 4);
346 settings->yin_threshold = DEFAULT_YIN_THRESHOLD;
347 settings->freq_A = DEFAULT_FREQ_A;
348 settings->use_sharps = true;
349 settings->display_hz = false;
352 /*---------------------------------------------------------------------*/
354 enum settings_file_status tuner_settings_load(struct tuner_settings* settings,
355 char* filename)
357 int fd = rb->open(filename, O_RDONLY);
358 if(fd >= 0){ /* does file exist? */
359 /* basic consistency check */
360 if(rb->filesize(fd) == sizeof(*settings)){
361 rb->read(fd, settings, sizeof(*settings));
362 rb->close(fd);
363 rb->memcpy(&hdd_tuner_settings, settings, sizeof(*settings));
364 return(LOADED);
367 /* Initializes the settings with default values at least */
368 tuner_settings_reset(settings);
369 return(ERRLOAD);
372 /*---------------------------------------------------------------------*/
374 enum settings_file_status tuner_settings_save(struct tuner_settings* settings,
375 char* filename)
377 int fd = rb->creat(filename);
378 if(fd >= 0){ /* does file exist? */
379 rb->write (fd, settings, sizeof(*settings));
380 rb->close(fd);
381 return(SAVED);
383 return(ERRSAVE);
386 /*---------------------------------------------------------------------*/
388 void load_settings(void)
390 tuner_settings_load(&tuner_settings, SETTINGS_FILENAME);
392 rb->storage_sleep();
395 /*---------------------------------------------------------------------*/
397 void save_settings(void)
399 if(!settings_needs_saving(&tuner_settings))
400 return;
402 tuner_settings_save(&tuner_settings, SETTINGS_FILENAME);
405 /*=================================================================*/
406 /* MENU */
407 /*=================================================================*/
409 /* Keymaps */
410 const struct button_mapping* plugin_contexts[]={
411 generic_actions,
412 generic_increase_decrease,
413 generic_directions,
414 #if NB_SCREENS == 2
415 remote_directions
416 #endif
418 #define PLA_ARRAY_COUNT sizeof(plugin_contexts)/sizeof(plugin_contexts[0])
420 /* Option strings */
422 /* This has to match yin_threshold_table */
423 static const struct opt_items yin_threshold_text[] =
425 { "0.01", -1 },
426 { "0.02", -1 },
427 { "0.03", -1 },
428 { "0.04", -1 },
429 { "0.05", -1 },
430 { "0.10", -1 },
431 { "0.15", -1 },
432 { "0.20", -1 },
433 { "0.25", -1 },
434 { "0.30", -1 },
435 { "0.35", -1 },
436 { "0.40", -1 },
437 { "0.45", -1 },
438 { "0.50", -1 },
441 static const struct opt_items accidental_text[] =
443 { "Flat", -1 },
444 { "Sharp", -1 },
447 void set_min_freq(int new_freq)
449 tuner_settings.sample_size = freq2period(new_freq) * 4;
451 /* clamp the sample size between min and max */
452 if(tuner_settings.sample_size <= SAMPLE_SIZE_MIN)
453 tuner_settings.sample_size = SAMPLE_SIZE_MIN;
454 else if(tuner_settings.sample_size >= BUFFER_SIZE)
455 tuner_settings.sample_size = BUFFER_SIZE;
456 /* sample size must be divisible by 4 */
457 else if(tuner_settings.sample_size % 4 != 0)
458 tuner_settings.sample_size += 4 - (tuner_settings.sample_size % 4);
461 bool main_menu(void)
463 int selection = 0;
464 bool done = false;
465 bool exit_tuner = false;
466 int choice;
467 int freq_val;
468 bool reset;
470 MENUITEM_STRINGLIST(menu,"Tuner Settings",NULL,
471 "Return to Tuner",
472 "Volume Threshold",
473 "Listening Volume",
474 "Lowest Frequency",
475 "Algorithm Pickiness",
476 "Accidentals",
477 "Display Frequency (Hz)",
478 "Frequency of A (Hz)",
479 "Reset Settings",
480 "Quit");
482 while(!done)
484 choice = rb->do_menu(&menu, &selection, NULL, false);
485 switch(choice)
487 case 1:
488 rb->set_int("Volume Threshold", "%", UNIT_INT,
489 &tuner_settings.volume_threshold,
490 NULL, 5, 5, 95, NULL);
491 break;
492 case 2:
493 rb->set_int("Listening Volume", "%", UNIT_INT,
494 &tuner_settings.record_gain,
495 NULL, 1, rb->sound_min(SOUND_MIC_GAIN),
496 rb->sound_max(SOUND_MIC_GAIN), NULL);
497 break;
498 case 3:
499 rb->set_int("Lowest Frequency", "Hz", UNIT_INT,
500 &tuner_settings.lowest_freq, set_min_freq, 1,
501 /* Range depends on the size of the buffer */
502 SAMPLE_RATE / (BUFFER_SIZE / 4),
503 SAMPLE_RATE / (SAMPLE_SIZE_MIN / 4), NULL);
504 break;
505 case 4:
506 rb->set_option(
507 "Algorithm Pickiness (Lower -> more discriminating)",
508 &tuner_settings.yin_threshold,
509 INT, yin_threshold_text,
510 sizeof(yin_threshold_text) / sizeof(yin_threshold_text[0]),
511 NULL);
512 break;
513 case 5:
514 rb->set_option("Display Accidentals As",
515 &tuner_settings.use_sharps,
516 BOOL, accidental_text, 2, NULL);
517 break;
518 case 6:
519 rb->set_bool("Display Frequency (Hz)",
520 &tuner_settings.display_hz);
521 break;
522 case 7:
523 freq_val = freq_A[tuner_settings.freq_A].frequency;
524 rb->set_int("Frequency of A (Hz)",
525 "Hz", UNIT_INT, &freq_val, NULL,
526 1, freq_A[0].frequency, freq_A[NUM_FREQ_A-1].frequency,
527 NULL);
528 tuner_settings.freq_A = freq_val - freq_A[0].frequency;
529 break;
530 case 8:
531 reset = false;
532 rb->set_bool("Reset Tuner Settings?", &reset);
533 if (reset)
534 tuner_settings_reset(&tuner_settings);
535 break;
536 case 9:
537 exit_tuner = true;
538 done = true;
539 break;
540 case 0:
541 default:
542 /* Return to the tuner */
543 done = true;
544 break;
547 return exit_tuner;
550 /*=================================================================*/
551 /* Binary Log */
552 /*=================================================================*/
554 /* Fixed-point log base 2*/
555 /* Adapted from python code at
556 http://en.wikipedia.org/wiki/Binary_logarithm#Algorithm
558 fixed log(fixed inp)
560 fixed x = inp;
561 fixed fp = int2fixed(1);
562 fixed res = int2fixed(0);
564 if(fp_lte(x, FP_ZERO))
566 return FP_MIN;
569 /* Integer part*/
570 /* while x<1 */
571 while(fp_lt(x, int2fixed(1)))
573 res = fp_sub(res, int2fixed(1));
574 x = fp_shl(x, 1);
576 /* while x>=2 */
577 while(fp_gte(x, int2fixed(2)))
579 res = fp_add(res, int2fixed(1));
580 x = fp_shr(x, 1);
583 /* Fractional part */
584 /* while fp > 0 */
585 while(fp_gt(fp, FP_ZERO))
587 fp = fp_shr(fp, 1);
588 x = fp_mul(x, x);
589 /* if x >= 2 */
590 if(fp_gte(x, int2fixed(2)))
592 x = fp_shr(x, 1);
593 res = fp_add(res, fp);
597 return res;
600 /*=================================================================*/
601 /* GUI Stuff */
602 /*=================================================================*/
604 /* The function name is pretty self-explaining ;) */
605 void print_int_xy(int x, int y, int v)
607 char temp[20];
609 rb->lcd_set_foreground(front_color);
610 rb->snprintf(temp,20,"%d",v);
611 rb->lcd_putsxy(x,y,temp);
614 /* Print out the frequency etc */
615 void print_str(char* s)
617 rb->lcd_set_foreground(front_color);
618 rb->lcd_putsxy(0, HZ_Y, s);
621 /* What can I say? Read the function name... */
622 void print_char_xy(int x, int y, char c)
624 char temp[2];
626 temp[0]=c;
627 temp[1]=0;
628 rb->lcd_set_foreground(front_color);
630 rb->lcd_putsxy(x, y, temp);
633 /* Draw the note bitmap */
634 void draw_note(const char *note)
636 int i;
637 int note_x = (LCD_WIDTH - BMPWIDTH_pitch_notes) / 2;
638 int accidental_index = NOTE_INDEX_SHARP;
640 i = note[0]-'A';
642 if(note[1] == '#')
644 if(!(tuner_settings.use_sharps))
646 i = (i + 1) % 7;
647 accidental_index = NOTE_INDEX_FLAT;
650 vertical_picture_draw_sprite(rb->screens[0],
651 &note_bitmaps,
652 accidental_index,
653 LCD_WIDTH / 2,
654 note_y);
655 note_x = LCD_WIDTH / 2 - BMPWIDTH_pitch_notes;
658 vertical_picture_draw_sprite(rb->screens[0], &note_bitmaps, i,
659 note_x,
660 note_y);
662 /* Draw the red bar and the white lines */
663 void draw_bar(fixed wrong_by_cents)
665 unsigned n;
666 int x;
668 #ifdef HAVE_LCD_COLOR
669 rb->lcd_set_foreground(LCD_RGBPACK(255,255,255)); /* Color screens */
670 #elif LCD_DEPTH > 1
671 rb->lcd_set_foreground(LCD_BLACK); /* Greyscale screens */
672 #else
673 rb->lcd_set_foreground(LCD_BLACK); /* Black and white screens */
674 #endif
676 rb->lcd_hline(0,LCD_WIDTH-1, BAR_HLINE_Y);
677 rb->lcd_hline(0,LCD_WIDTH-1, BAR_HLINE_Y2);
679 /* Draw graduation lines on the off-by readout */
680 for(n = 0; n <= GRADUATION; n++)
682 x = (LCD_WIDTH * n + GRADUATION / 2) / GRADUATION;
683 if (x >= LCD_WIDTH)
684 x = LCD_WIDTH - 1;
685 rb->lcd_vline(x, BAR_HLINE_Y, BAR_HLINE_Y2);
688 print_int_xy(lbl_x_minus_50 ,bar_grad_y, -50);
689 print_int_xy(lbl_x_minus_20 ,bar_grad_y, -20);
690 print_int_xy(lbl_x_0 ,bar_grad_y, 0);
691 print_int_xy(lbl_x_20 ,bar_grad_y, 20);
692 print_int_xy(lbl_x_50 ,bar_grad_y, 50);
694 #ifdef HAVE_LCD_COLOR
695 rb->lcd_set_foreground(LCD_RGBPACK(255,0,0)); /* Color screens */
696 #elif LCD_DEPTH > 1
697 rb->lcd_set_foreground(LCD_DARKGRAY); /* Greyscale screens */
698 #else
699 rb->lcd_set_foreground(LCD_BLACK); /* Black and white screens */
700 #endif
702 if (fp_gt(wrong_by_cents, FP_ZERO))
704 rb->lcd_fillrect(bar_x_0, BAR_Y,
705 fixed2int(fp_mul(wrong_by_cents, LCD_FACTOR)), BAR_HEIGHT);
707 else
709 rb->lcd_fillrect(bar_x_0 + fixed2int(fp_mul(wrong_by_cents,LCD_FACTOR)),
710 BAR_Y,
711 fixed2int(fp_mul(wrong_by_cents, LCD_FACTOR)) * -1,
712 BAR_HEIGHT);
716 /* Calculate how wrong the note is and draw the GUI */
717 void display_frequency (fixed freq)
719 fixed ldf, mldf;
720 fixed lfreq, nfreq;
721 fixed orig_freq;
722 int i, note = 0;
723 char str_buf[30];
725 if (fp_lt(freq, FP_LOW))
726 freq = FP_LOW;
728 /* We calculate the frequency and its log as if */
729 /* the reference frequency of A were 440 Hz. */
730 orig_freq = freq;
731 lfreq = fp_add(log(freq), freq_A[tuner_settings.freq_A].logratio);
732 freq = fp_mul(freq, freq_A[tuner_settings.freq_A].ratio);
734 /* This calculates a log freq offset for note A */
735 /* Get the frequency to within the range of our reference table, */
736 /* i.e. into the right octave. */
737 while (fp_lt(lfreq, fp_sub(notes[0].logfreq, fp_shr(LOG_D_NOTE, 1))))
738 lfreq = fp_add(lfreq, LOG_2);
739 while (fp_gte(lfreq, fp_sub(fp_add(notes[0].logfreq, LOG_2),
740 fp_shr(LOG_D_NOTE, 1))))
741 lfreq = fp_sub(lfreq, LOG_2);
742 mldf = LOG_D_NOTE;
743 for (i=0; i<12; i++)
745 ldf = fp_gt(fp_sub(lfreq,notes[i].logfreq), FP_ZERO) ?
746 fp_sub(lfreq,notes[i].logfreq) : fp_neg(fp_sub(lfreq,notes[i].logfreq));
747 if (fp_lt(ldf, mldf))
749 mldf = ldf;
750 note = i;
753 nfreq = notes[note].freq;
754 while (fp_gt(fp_div(nfreq, freq), D_NOTE_SQRT))
755 nfreq = fp_shr(nfreq, 1);
756 while (fp_gt(fp_div(freq, nfreq), D_NOTE_SQRT))
758 nfreq = fp_shl(nfreq, 1);
761 ldf = fp_mul(int2fixed(1200), log(fp_div(freq,nfreq)));
763 rb->lcd_clear_display();
764 draw_bar(ldf); /* The red bar */
765 if(fp_round(freq) != 0)
767 draw_note(notes[note].name);
768 if(tuner_settings.display_hz)
770 rb->snprintf(str_buf,30, "%s : %d cents (%d.%02dHz)",
771 notes[note].name, fp_round(ldf) ,fixed2int(orig_freq),
772 fp_round(fp_mul(fp_frac(orig_freq),
773 int2fixed(DISPLAY_HZ_PRECISION))));
774 print_str(str_buf);
777 rb->lcd_update();
780 /*-----------------------------------------------------------------------
781 * Functions for the Yin algorithm
783 * These were all adapted from the versions in Aubio v0.3.2
784 * Here's what the Aubio documentation has to say:
786 * This algorithm was developped by A. de Cheveigne and H. Kawahara and
787 * published in:
789 * de Cheveign?, A., Kawahara, H. (2002) "YIN, a fundamental frequency
790 * estimator for speech and music", J. Acoust. Soc. Am. 111, 1917-1930.
792 * see http://recherche.ircam.fr/equipes/pcm/pub/people/cheveign.html
793 -------------------------------------------------------------------------*/
795 /* Find the index of the minimum element of an array of floats */
796 unsigned vec_min_elem(fixed *s, unsigned buflen)
798 unsigned j, pos=0.0f;
799 fixed tmp = s[0];
800 for (j=0; j < buflen; j++)
802 if(fp_gt(tmp, s[j]))
804 pos = j;
805 tmp = s[j];
808 return pos;
812 fixed aubio_quadfrac(fixed s0, fixed s1, fixed s2, fixed pf)
814 /* Original floating point version: */
815 /* tmp = s0 + (pf/2.0f) * (pf * ( s0 - 2.0f*s1 + s2 ) -
816 3.0f*s0 + 4.0f*s1 - s2);*/
817 /* Converted to explicit operator precedence: */
818 /* tmp = s0 + ((pf/2.0f) * ((((pf * ((s0 - (2*s1)) + s2)) -
819 (3*s0)) + (4*s1)) - s2)); */
821 /* I made it look like this so I could easily track the precedence and */
822 /* make sure it matched the original expression */
823 /* Oy, this is when I really wish I could do C++ operator overloading */
824 fixed tmp = fp_add
827 fp_mul
829 fp_shr(pf, 1),
830 fp_sub
832 fp_add
834 fp_sub
836 fp_mul
839 fp_add
841 fp_sub
844 fp_shl(s1, 1)
849 fp_mul
851 float2fixed(3.0f),
855 fp_shl(s1, 2)
861 return tmp;
864 #define QUADINT_STEP float2fixed(1.0f/200.0f)
866 fixed vec_quadint_min(fixed *x, unsigned bufsize, unsigned pos, unsigned span)
868 fixed res, frac, s0, s1, s2;
869 fixed exactpos = int2fixed(pos);
870 /* init resold to something big (in case x[pos+-span]<0)) */
871 fixed resold = FP_MAX;
873 if ((pos > span) && (pos < bufsize-span))
875 s0 = x[pos-span];
876 s1 = x[pos] ;
877 s2 = x[pos+span];
878 /* increase frac */
879 for (frac = float2fixed(0.0f);
880 fp_lt(frac, float2fixed(2.0f));
881 frac = fp_add(frac, QUADINT_STEP))
883 res = aubio_quadfrac(s0, s1, s2, frac);
884 if (fp_lt(res, resold))
886 resold = res;
888 else
890 /* exactpos += (frac-QUADINT_STEP)*span - span/2.0f; */
891 exactpos = fp_add(exactpos,
892 fp_sub(
893 fp_mul(
894 fp_sub(frac, QUADINT_STEP),
895 int2fixed(span)
897 int2fixed(span)
900 break;
904 return exactpos;
908 /* Calculate the period of the note in the
909 buffer using the YIN algorithm */
910 /* The yin pointer is just a buffer that the algorithm uses as a work
911 space. It needs to be half the length of the input buffer. */
913 fixed pitchyin(audio_sample_type *input, fixed *yin)
915 fixed retval;
916 unsigned j,tau = 0;
917 int period;
918 unsigned yin_size = tuner_settings.sample_size / 4;
920 fixed tmp = FP_ZERO, tmp2 = FP_ZERO;
921 yin[0] = int2fixed(1);
922 for (tau = 1; tau < yin_size; tau++)
924 yin[tau] = FP_ZERO;
925 for (j = 0; j < yin_size; j++)
927 tmp = fp_sub(int2mantissa(input[2 * j]),
928 int2mantissa(input[2 * (j + tau)]));
929 yin[tau] = fp_add(yin[tau], fp_mul(tmp, tmp));
931 tmp2 = fp_add(tmp2, yin[tau]);
932 if(!fp_equal(tmp2, FP_ZERO))
934 yin[tau] = fp_mul(yin[tau], fp_div(int2fixed(tau), tmp2));
936 period = tau - 3;
937 if(tau > 4 && fp_lt(yin[period],
938 yin_threshold_table[tuner_settings.yin_threshold])
939 && fp_lt(yin[period], yin[period+1]))
941 retval = vec_quadint_min(yin, yin_size, period, 1);
942 return retval;
945 retval = vec_quadint_min(yin, yin_size,
946 vec_min_elem(yin, yin_size), 1);
947 return retval;
948 /*return FP_ZERO;*/
951 /*-----------------------------------------------------------------*/
953 uint32_t buffer_magnitude(audio_sample_type *input)
955 unsigned n;
956 uint64_t tally = 0;
958 /* Operate on only one channel of the stereo signal */
959 for(n = 0; n < tuner_settings.sample_size; n+=2)
961 tally += (uint64_t)input[n] * (uint64_t)input[n];
964 tally /= tuner_settings.sample_size / 2;
966 /* now tally holds the average of the squares of all the samples */
967 /* It must be between 0 and 0x7fff^2, so it fits in 32 bits */
968 return (uint32_t)tally;
971 /* Stop the recording when the buffer is full */
972 int recording_callback(int status)
974 (void) status;
976 rb->pcm_stop_recording();
977 recording=0;
978 return -1;
981 /* The main program loop */
982 void record_and_get_pitch(void)
984 int quit=0, button;
985 bool redraw = true;
986 /* For tracking the latency */
988 long timer;
989 char debug_string[20];
991 #ifndef SIMULATOR
992 fixed period;
993 bool waiting = false;
994 #endif
996 while(!quit)
998 #ifndef SIMULATOR
999 /* Start recording */
1000 rb->pcm_record_data(recording_callback, (void *) audio_data,
1001 (size_t) tuner_settings.sample_size *
1002 sizeof(audio_sample_type));
1003 #endif
1004 recording=1;
1006 while (recording && !quit) /* wait for the buffer to be filled */
1008 rb->yield();
1009 #ifdef SIMULATOR
1010 /* Only do this loop once if this is the simulator */
1011 recording = 0;
1012 #endif
1013 button=pluginlib_getaction(0, plugin_contexts, PLA_ARRAY_COUNT);
1014 switch(button)
1016 case PLA_QUIT:
1017 quit=true;
1018 rb->yield();
1019 break;
1021 case PLA_MENU:
1022 if(main_menu())
1023 quit=true;
1024 else redraw = true;
1025 rb->yield();
1026 break;
1028 default:
1029 rb->yield();
1031 break;
1035 if(!quit)
1037 #ifndef SIMULATOR
1038 /* Only do the heavy lifting if the volume is high enough */
1039 if(buffer_magnitude(audio_data) >
1040 sqr(tuner_settings.volume_threshold *
1041 rb->sound_max(SOUND_MIC_GAIN)))
1043 if(waiting)
1045 #ifdef HAVE_ADJUSTABLE_CPU_FREQ
1046 rb->cpu_boost(true);
1047 #endif
1048 waiting = false;
1051 rb->backlight_on();
1052 redraw = false;
1054 /* This returns the period of the detected pitch in samples */
1055 period = pitchyin(audio_data, yin_buffer);
1056 /* Hz = sample rate / period */
1057 if(fp_gt(period, FP_ZERO))
1059 display_frequency(fp_period2freq(period));
1061 else
1063 display_frequency(FP_ZERO);
1066 else if(redraw || !waiting)
1068 waiting = true;
1069 redraw = false;
1070 #ifdef HAVE_ADJUSTABLE_CPU_FREQ
1071 rb->cpu_boost(false);
1072 #endif
1073 /*rb->backlight_off();*/
1074 display_frequency(FP_ZERO);
1076 #else /* SIMULATOR */
1077 /* Display a preselected frequency */
1078 display_frequency(int2fixed(445));
1079 #endif
1082 rb->pcm_close_recording();
1083 #ifdef HAVE_ADJUSTABLE_CPU_FREQ
1084 rb->cpu_boost(false);
1085 #endif
1088 /* Init recording, tuning, and GUI */
1089 void init_everything(void)
1091 load_settings();
1093 /* --------- Init the audio recording ----------------- */
1094 rb->audio_set_output_source(AUDIO_SRC_PLAYBACK);
1095 rb->audio_set_input_source(INPUT_TYPE, SRCF_RECORDING);
1097 /* set to maximum gain */
1098 rb->audio_set_recording_gain(tuner_settings.record_gain,
1099 tuner_settings.record_gain,
1100 AUDIO_GAIN_MIC);
1102 rb->pcm_set_frequency(SAMPLE_RATE);
1103 rb->pcm_apply_settings();
1105 rb->pcm_init_recording();
1107 /* GUI */
1108 back_color = rb->lcd_get_background();
1109 front_color = rb->lcd_get_foreground();
1110 rb->lcd_getstringsize("X", &font_w, &font_h);
1112 bar_x_0 = LCD_WIDTH / 2;
1113 lbl_x_minus_50 = 0;
1114 lbl_x_minus_20 = (LCD_WIDTH / 2) -
1115 fixed2int(fp_mul(LCD_FACTOR, int2fixed(20))) - font_w;
1116 lbl_x_0 = (LCD_WIDTH - font_w) / 2;
1117 lbl_x_20 = (LCD_WIDTH / 2) +
1118 fixed2int(fp_mul(LCD_FACTOR, int2fixed(20))) - font_w;
1119 lbl_x_50 = LCD_WIDTH - 2 * font_w;
1121 bar_grad_y = BAR_Y - BAR_PADDING - font_h;
1122 /* Put the note right between the top and bottom text elements */
1123 note_y = ((font_h + bar_grad_y - note_bitmaps.slide_height) / 2);
1127 enum plugin_status plugin_start(const void* parameter) NO_PROF_ATTR
1129 (void)parameter;
1131 init_everything();
1132 record_and_get_pitch();
1133 save_settings();
1135 return 0;