Prepare new maemo release
[maemo-rb.git] / apps / plugins / pitch_detector.c
blob4ae43b323610e2565d9243bcc30d1d4e32d64319
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 * - Adapt the Yin FFT algorithm, which would reduce complexity from O(n^2)
29 * to O(nlogn), theoretically reducing latency by a factor of ~10. -David
31 * MAJOR CHANGES:
32 * 08.03.2008 Started coding
33 * 21.03.2008 Pitch detection works more or less
34 * Button definitions for most targets added
35 * 02.04.2008 Proper GUI added
36 * Todo, Major Changes and Current Limitations added
37 * 08.19.2009 Brought the code up to date with current plugin standards
38 * Made it work more nicely with color, BW and grayscale
39 * Changed pitch detection to use the Yin algorithm (better
40 * detection, but slower -- would be ~4x faster with
41 * fixed point math, I think). Code was poached from the
42 * Aubio sound processing library (aubio.org). -David
43 * 08.31.2009 Lots of changes:
44 * Added a menu to tweak settings
45 * Converted everything to fixed point (greatly improving
46 * latency)
47 * Improved the display
48 * Improved efficiency with judicious use of cpu_boost, the
49 * backlight, and volume detection to limit unneeded
50 * calculation
51 * Fixed a problem that caused an octave-off error
52 * -David
53 * 05.14.2010 Multibuffer continuous recording with two buffers
56 * CURRENT LIMITATIONS:
57 * - No gapless recording. Strictly speaking true gappless isn't possible,
58 * since the algorithm takes longer to calculate than the length of the
59 * sample, but latency could be improved a bit with proper use of the DMA
60 * recording functions.
61 * - Due to how the Yin algorithm works, latency is higher for lower
62 * frequencies.
65 #include "plugin.h"
66 #include "lib/pluginlib_actions.h"
67 #include "lib/picture.h"
68 #include "lib/helper.h"
69 #include "pluginbitmaps/pitch_notes.h"
72 /* Some fixed point calculation stuff */
73 typedef int32_t fixed;
74 #define FIXED_PRECISION 18
75 #define FP_MAX ((fixed) {0x7fffffff})
76 #define FP_MIN ((fixed) {-0x80000000})
77 #define int2fixed(x) ((fixed)((x) << FIXED_PRECISION))
78 #define int2mantissa(x) ((fixed)(x))
79 #define fixed2int(x) ((int)((x) >> FIXED_PRECISION))
80 #define fixed2float(x) (((float)(x)) / ((float)(1 << FIXED_PRECISION)))
81 #define float2fixed(x) ((fixed)(x * (float)(1 << FIXED_PRECISION)))
83 /* I adapted these ones from the Rockbox fixed point library */
84 #define fp_mul(x, y) \
85 ((fixed)((((int64_t)((x))) * ((int64_t)((y)))) >> (FIXED_PRECISION)))
86 #define fp_div(x, y) \
87 ((fixed)((((int64_t)((x))) << (FIXED_PRECISION)) / ((int64_t)((y)))))
88 /* Operators for fixed point */
89 #define fp_add(x, y) ((fixed)((x) + (y)))
90 #define fp_sub(x, y) ((fixed)((x) - (y)))
91 #define fp_shl(x, y) ((fixed)((x) << (y)))
92 #define fp_shr(x, y) ((fixed)((x) >> (y)))
93 #define fp_neg(x) ((fixed)(-(x)))
94 #define fp_gt(x, y) ((x) > (y))
95 #define fp_gte(x, y) ((x) >= (y))
96 #define fp_lt(x, y) ((x) < (y))
97 #define fp_lte(x, y) ((x) <= (y))
98 #define fp_sqr(x) fp_mul((x), (x))
99 #define fp_equal(x, y) ((x) == (y))
100 #define fp_round(x) (fixed2int(fp_add((x), float2fixed(0.5))))
101 #define fp_data(x) (x)
102 #define fp_frac(x) (fp_sub((x), int2fixed(fixed2int(x))))
103 #define FP_ZERO ((fixed)0)
104 #define FP_LOW ((fixed)2)
106 /* Some defines for converting between period and frequency */
108 /* I introduce some divisors in this because the fixed point */
109 /* variables aren't big enough to hold higher than a certain */
110 /* value. This loses a bit of precision but it means we */
111 /* don't have to use 32.32 variables (yikes). */
112 /* With an 18-bit decimal precision, the max value in the */
113 /* integer part is 8192. Divide 44100 by 7 and it'll fit in */
114 /* that variable. */
115 #define fp_period2freq(x) fp_div(int2fixed(sample_rate / 7), \
116 fp_div((x),int2fixed(7)))
117 #define fp_freq2period(x) fp_period2freq(x)
118 #define period2freq(x) (sample_rate / (x))
119 #define freq2period(x) period2freq(x)
121 #define sqr(x) ((x)*(x))
123 /* Some constants for tuning */
124 #define A_FREQ float2fixed(440.0f)
125 #define D_NOTE float2fixed(1.059463094359f)
126 #define LOG_D_NOTE float2fixed(1.0f/12.0f)
127 #define D_NOTE_SQRT float2fixed(1.029302236643f)
128 #define LOG_2 float2fixed(1.0f)
130 /* The recording buffer size */
131 /* This is how much is sampled at a time. */
132 /* It also determines latency -- if BUFFER_SIZE == sample_rate then */
133 /* there'll be one sample per second, or a latency of one second. */
134 /* Furthermore, the lowest detectable frequency will be about twice */
135 /* the number of reads per second */
136 /* If we ever switch to Yin FFT algorithm then this needs to be
137 a power of 2 */
138 #define BUFFER_SIZE 4096
139 #define SAMPLE_SIZE 4096
140 #define SAMPLE_SIZE_MIN 1024
141 #define YIN_BUFFER_SIZE (BUFFER_SIZE / 4)
143 #define LCD_FACTOR (fp_div(int2fixed(LCD_WIDTH), int2fixed(100)))
144 /* The threshold for the YIN algorithm */
145 #define DEFAULT_YIN_THRESHOLD 5 /* 0.10 */
146 static const fixed yin_threshold_table[] IDATA_ATTR =
148 float2fixed(0.01),
149 float2fixed(0.02),
150 float2fixed(0.03),
151 float2fixed(0.04),
152 float2fixed(0.05),
153 float2fixed(0.10),
154 float2fixed(0.15),
155 float2fixed(0.20),
156 float2fixed(0.25),
157 float2fixed(0.30),
158 float2fixed(0.35),
159 float2fixed(0.40),
160 float2fixed(0.45),
161 float2fixed(0.50),
164 /* Structure for the reference frequency (frequency of A)
165 * It's used for scaling the frequency before finding out
166 * the note. The frequency is scaled in a way that the main
167 * algorithm can assume the frequency of A to be 440 Hz.
169 static const struct
171 const int frequency; /* Frequency in Hz */
172 const fixed ratio; /* 440/frequency */
173 const fixed logratio; /* log2(factor) */
174 } freq_A[] =
176 {435, float2fixed(1.011363636), float2fixed( 0.016301812)},
177 {436, float2fixed(1.009090909), float2fixed( 0.013056153)},
178 {437, float2fixed(1.006818182), float2fixed( 0.009803175)},
179 {438, float2fixed(1.004545455), float2fixed( 0.006542846)},
180 {439, float2fixed(1.002272727), float2fixed( 0.003275132)},
181 {440, float2fixed(1.000000000), float2fixed( 0.000000000)},
182 {441, float2fixed(0.997727273), float2fixed(-0.003282584)},
183 {442, float2fixed(0.995454545), float2fixed(-0.006572654)},
184 {443, float2fixed(0.993181818), float2fixed(-0.009870244)},
185 {444, float2fixed(0.990909091), float2fixed(-0.013175389)},
186 {445, float2fixed(0.988636364), float2fixed(-0.016488123)},
189 /* Index of the entry for 440 Hz in the table (default frequency for A) */
190 #define DEFAULT_FREQ_A 5
191 #define NUM_FREQ_A (sizeof(freq_A)/sizeof(freq_A[0]))
193 /* How loud the audio has to be to start displaying pitch */
194 /* Must be between 0 and 100 */
195 #define VOLUME_THRESHOLD (50)
197 /* Change to AUDIO_SRC_LINEIN if you want to record from line-in */
198 #ifdef HAVE_MIC_IN
199 #define INPUT_TYPE AUDIO_SRC_MIC
200 #else
201 #define INPUT_TYPE AUDIO_SRC_LINEIN
202 #endif
204 /* How many decimal places to display for the Hz value */
205 #define DISPLAY_HZ_PRECISION 100
207 /* Where to put the various GUI elements */
208 static int note_y;
209 static int bar_grad_y;
210 #define LCD_RES_MIN (LCD_HEIGHT < LCD_WIDTH ? LCD_HEIGHT : LCD_WIDTH)
211 #define BAR_PADDING (LCD_RES_MIN / 32)
212 #define BAR_Y (LCD_HEIGHT * 3 / 4)
213 #define BAR_HEIGHT (LCD_RES_MIN / 4 - BAR_PADDING)
214 #define BAR_HLINE_Y (BAR_Y - BAR_PADDING)
215 #define BAR_HLINE_Y2 (BAR_Y + BAR_HEIGHT + BAR_PADDING - 1)
216 #define HZ_Y 0
217 #define GRADUATION 10 /* Subdivisions of the whole 100-cent scale */
219 /* Bitmaps for drawing the note names. These need to have height
220 <= (bar_grad_y - note_y), or 15/32 * LCD_HEIGHT
222 #define NUM_NOTE_IMAGES 9
223 #define NOTE_INDEX_A 0
224 #define NOTE_INDEX_B 1
225 #define NOTE_INDEX_C 2
226 #define NOTE_INDEX_D 3
227 #define NOTE_INDEX_E 4
228 #define NOTE_INDEX_F 5
229 #define NOTE_INDEX_G 6
230 #define NOTE_INDEX_SHARP 7
231 #define NOTE_INDEX_FLAT 8
233 static const struct picture note_bitmaps =
235 pitch_notes,
236 BMPWIDTH_pitch_notes,
237 BMPHEIGHT_pitch_notes,
238 BMPHEIGHT_pitch_notes/NUM_NOTE_IMAGES
242 static unsigned int sample_rate;
243 static int audio_head = 0; /* which of the two buffers to use? */
244 static volatile int audio_tail = 0; /* which of the two buffers to record? */
245 /* It's stereo, so make the buffer twice as big */
246 #ifndef SIMULATOR
247 static int16_t audio_data[2][BUFFER_SIZE] MEM_ALIGN_ATTR;
248 static fixed yin_buffer[YIN_BUFFER_SIZE] IBSS_ATTR;
249 #ifdef PLUGIN_USE_IRAM
250 static int16_t iram_audio_data[BUFFER_SIZE] IBSS_ATTR;
251 #else
252 #define iram_audio_data audio_data[audio_head]
253 #endif
254 #endif
256 /* Notes within one (reference) scale */
257 static const struct
259 const char *name; /* Name of the note, e.g. "A#" */
260 const fixed freq; /* Note frequency, Hz */
261 const fixed logfreq; /* log2(frequency) */
262 } notes[] =
264 {"A" , float2fixed(440.0000000f), float2fixed(8.781359714f)},
265 {"A#", float2fixed(466.1637615f), float2fixed(8.864693047f)},
266 {"B" , float2fixed(493.8833013f), float2fixed(8.948026380f)},
267 {"C" , float2fixed(523.2511306f), float2fixed(9.031359714f)},
268 {"C#", float2fixed(554.3652620f), float2fixed(9.114693047f)},
269 {"D" , float2fixed(587.3295358f), float2fixed(9.198026380f)},
270 {"D#", float2fixed(622.2539674f), float2fixed(9.281359714f)},
271 {"E" , float2fixed(659.2551138f), float2fixed(9.364693047f)},
272 {"F" , float2fixed(698.4564629f), float2fixed(9.448026380f)},
273 {"F#", float2fixed(739.9888454f), float2fixed(9.531359714f)},
274 {"G" , float2fixed(783.9908720f), float2fixed(9.614693047f)},
275 {"G#", float2fixed(830.6093952f), float2fixed(9.698026380f)},
278 /* GUI */
279 #if LCD_DEPTH > 1
280 static unsigned front_color;
281 #endif
282 static int font_w,font_h;
283 static int bar_x_0;
284 static int lbl_x_minus_50, lbl_x_minus_20, lbl_x_0, lbl_x_20, lbl_x_50;
286 /* Settings for the plugin */
287 static struct tuner_settings
289 unsigned volume_threshold;
290 unsigned record_gain;
291 unsigned sample_size;
292 unsigned lowest_freq;
293 unsigned yin_threshold;
294 int freq_A; /* Index of the frequency of A */
295 bool use_sharps;
296 bool display_hz;
297 int key_transposition; /* Which note to display as 'C'. */
298 /* 0=C, 1=D-flat, 2=D, ..., 11=B. This is useful if you */
299 /* use a transposing instrument. In that case, this */
300 /* setting tells which 'real' note is played by the */
301 /* instrument if you play a written 'C'. Thus, this */
302 /* setting is the number of semitones from the real 'C' */
303 /* up to the 'instrument key'. */
304 } settings;
306 /* By default, the real 'C' is displayed as 'C' */
307 #define DEFAULT_KEY_TRANSPOSITION 0
310 /*=================================================================*/
311 /* Settings loading and saving(adapted from the clock plugin) */
312 /*=================================================================*/
314 #define SETTINGS_FILENAME PLUGIN_APPS_DATA_DIR "/.pitch_detector_settings"
316 /* The settings as they exist on the hard disk, so that
317 * we can know at saving time if changes have been made */
318 static struct tuner_settings hdd_settings;
320 /*---------------------------------------------------------------------*/
322 static bool settings_needs_saving(void)
324 return(rb->memcmp(&settings, &hdd_settings, sizeof(settings)));
327 /*---------------------------------------------------------------------*/
329 static void tuner_settings_reset(void)
331 settings = (struct tuner_settings) {
332 .volume_threshold = VOLUME_THRESHOLD,
333 .record_gain = rb->global_settings->rec_mic_gain,
334 .sample_size = BUFFER_SIZE,
335 .lowest_freq = period2freq(BUFFER_SIZE / 4),
336 .yin_threshold = DEFAULT_YIN_THRESHOLD,
337 .freq_A = DEFAULT_FREQ_A,
338 .use_sharps = true,
339 .display_hz = false,
340 .key_transposition = DEFAULT_KEY_TRANSPOSITION,
344 /*---------------------------------------------------------------------*/
346 static void load_settings(void)
348 int fd = rb->open(SETTINGS_FILENAME, O_RDONLY);
349 if(fd < 0){ /* file doesn't exist */
350 /* Initializes the settings with default values at least */
351 tuner_settings_reset();
352 return;
355 /* basic consistency check */
356 if(rb->filesize(fd) == sizeof(settings)){
357 rb->read(fd, &settings, sizeof(settings));
358 rb->memcpy(&hdd_settings, &settings, sizeof(settings));
360 else{
361 tuner_settings_reset();
364 rb->close(fd);
367 /*---------------------------------------------------------------------*/
369 static void save_settings(void)
371 if(!settings_needs_saving())
372 return;
374 int fd = rb->creat(SETTINGS_FILENAME, 0666);
375 if(fd >= 0){ /* does file exist? */
376 rb->write (fd, &settings, sizeof(settings));
377 rb->close(fd);
381 /*=================================================================*/
382 /* MENU */
383 /*=================================================================*/
385 /* Keymaps */
386 const struct button_mapping* plugin_contexts[]={
387 pla_main_ctx,
388 #if NB_SCREENS == 2
389 pla_remote_ctx,
390 #endif
392 #define PLA_ARRAY_COUNT sizeof(plugin_contexts)/sizeof(plugin_contexts[0])
394 /* Option strings */
396 /* This has to match yin_threshold_table */
397 static const struct opt_items yin_threshold_text[] =
399 { "0.01", -1 },
400 { "0.02", -1 },
401 { "0.03", -1 },
402 { "0.04", -1 },
403 { "0.05", -1 },
404 { "0.10", -1 },
405 { "0.15", -1 },
406 { "0.20", -1 },
407 { "0.25", -1 },
408 { "0.30", -1 },
409 { "0.35", -1 },
410 { "0.40", -1 },
411 { "0.45", -1 },
412 { "0.50", -1 },
415 static const struct opt_items accidental_text[] =
417 { "Flat", -1 },
418 { "Sharp", -1 },
421 static const struct opt_items transpose_text[] =
423 { "C (Concert Pitch)", -1 },
424 { "D-flat", -1 },
425 { "D", -1 },
426 { "E-flat", -1 },
427 { "E", -1 },
428 { "F", -1 },
429 { "G-flat", -1 },
430 { "G", -1 },
431 { "A-flat", -1 },
432 { "A", -1 },
433 { "B-flat", -1 },
434 { "B", -1 },
437 static void set_min_freq(int new_freq)
439 settings.sample_size = freq2period(new_freq) * 4;
441 /* clamp the sample size between min and max */
442 if(settings.sample_size <= SAMPLE_SIZE_MIN)
443 settings.sample_size = SAMPLE_SIZE_MIN;
444 else if(settings.sample_size >= BUFFER_SIZE)
445 settings.sample_size = BUFFER_SIZE;
447 /* sample size must be divisible by 4 - round up */
448 settings.sample_size = (settings.sample_size + 3) & ~3;
451 /* Displays the menu. Returns true iff the user selects 'quit'. */
452 static bool main_menu(void)
454 int selection = 0;
455 bool done = false;
456 bool exit_tuner = false;
457 int choice;
458 int freq_val;
459 bool reset;
461 backlight_use_settings();
462 #ifdef HAVE_SCHEDULER_BOOSTCTRL
463 rb->cancel_cpu_boost();
464 #endif
466 MENUITEM_STRINGLIST(menu,"Tuner Settings",NULL,
467 "Return to Tuner",
468 "Volume Threshold",
469 "Listening Volume",
470 "Lowest Frequency",
471 "Algorithm Pickiness",
472 "Accidentals",
473 "Key Transposition",
474 "Display Frequency (Hz)",
475 "Frequency of A (Hz)",
476 "Reset Settings",
477 "Quit");
479 while(!done)
481 choice = rb->do_menu(&menu, &selection, NULL, false);
482 switch(choice)
484 case 1:
485 rb->set_int("Volume Threshold", "%", UNIT_INT,
486 &settings.volume_threshold,
487 NULL, 5, 5, 95, NULL);
488 break;
489 case 2:
490 rb->set_int("Listening Volume", "%", UNIT_INT,
491 &settings.record_gain,
492 NULL, 1, rb->sound_min(SOUND_MIC_GAIN),
493 rb->sound_max(SOUND_MIC_GAIN), NULL);
494 break;
495 case 3:
496 rb->set_int("Lowest Frequency", "Hz", UNIT_INT,
497 &settings.lowest_freq, set_min_freq, 1,
498 /* Range depends on the size of the buffer */
499 sample_rate / (BUFFER_SIZE / 4),
500 sample_rate / (SAMPLE_SIZE_MIN / 4), NULL);
501 break;
502 case 4:
503 rb->set_option(
504 "Algorithm Pickiness (Lower -> more discriminating)",
505 &settings.yin_threshold,
506 INT, yin_threshold_text,
507 sizeof(yin_threshold_text) / sizeof(yin_threshold_text[0]),
508 NULL);
509 break;
510 case 5:
511 rb->set_option("Display Accidentals As",
512 &settings.use_sharps,
513 BOOL, accidental_text, 2, NULL);
514 break;
515 case 6:
516 rb->set_option("Key Transposition",
517 &settings.key_transposition,
518 INT, transpose_text, 12, NULL);
519 break;
520 case 7:
521 rb->set_bool("Display Frequency (Hz)",
522 &settings.display_hz);
523 break;
524 case 8:
525 freq_val = freq_A[settings.freq_A].frequency;
526 rb->set_int("Frequency of A (Hz)",
527 "Hz", UNIT_INT, &freq_val, NULL,
528 1, freq_A[0].frequency, freq_A[NUM_FREQ_A-1].frequency,
529 NULL);
530 settings.freq_A = freq_val - freq_A[0].frequency;
531 break;
532 case 9:
533 reset = false;
534 rb->set_bool("Reset Tuner Settings?", &reset);
535 if (reset)
536 tuner_settings_reset();
537 break;
538 case 10:
539 exit_tuner = true;
540 done = true;
541 break;
542 case 0:
543 default:
544 /* Return to the tuner */
545 done = true;
546 break;
550 backlight_ignore_timeout();
551 return exit_tuner;
554 /*=================================================================*/
555 /* Binary Log */
556 /*=================================================================*/
558 /* Fixed-point log base 2*/
559 /* Adapted from python code at
560 http://en.wikipedia.org/wiki/Binary_logarithm#Algorithm
562 static fixed log(fixed inp)
564 fixed x = inp;
565 fixed fp = int2fixed(1);
566 fixed res = int2fixed(0);
568 if(fp_lte(x, FP_ZERO))
570 return FP_MIN;
573 /* Integer part*/
574 /* while x<1 */
575 while(fp_lt(x, int2fixed(1)))
577 res = fp_sub(res, int2fixed(1));
578 x = fp_shl(x, 1);
580 /* while x>=2 */
581 while(fp_gte(x, int2fixed(2)))
583 res = fp_add(res, int2fixed(1));
584 x = fp_shr(x, 1);
587 /* Fractional part */
588 /* while fp > 0 */
589 while(fp_gt(fp, FP_ZERO))
591 fp = fp_shr(fp, 1);
592 x = fp_mul(x, x);
593 /* if x >= 2 */
594 if(fp_gte(x, int2fixed(2)))
596 x = fp_shr(x, 1);
597 res = fp_add(res, fp);
601 return res;
604 /*=================================================================*/
605 /* GUI Stuff */
606 /*=================================================================*/
608 /* Draw the note bitmap */
609 static void draw_note(const char *note)
611 int i;
612 int note_x = (LCD_WIDTH - BMPWIDTH_pitch_notes) / 2;
613 int accidental_index = NOTE_INDEX_SHARP;
615 i = note[0]-'A';
617 if(note[1] == '#')
619 if(!(settings.use_sharps))
621 i = (i + 1) % 7;
622 accidental_index = NOTE_INDEX_FLAT;
625 vertical_picture_draw_sprite(rb->screens[0],
626 &note_bitmaps,
627 accidental_index,
628 LCD_WIDTH / 2,
629 note_y);
630 note_x = LCD_WIDTH / 2 - BMPWIDTH_pitch_notes;
633 vertical_picture_draw_sprite(rb->screens[0], &note_bitmaps, i,
634 note_x,
635 note_y);
638 /* Draw the red bar and the white lines */
639 static void draw_bar(fixed wrong_by_cents)
641 unsigned n;
642 int x;
644 #ifdef HAVE_LCD_COLOR
645 rb->lcd_set_foreground(LCD_RGBPACK(255,255,255)); /* Color screens */
646 #elif LCD_DEPTH > 1
647 rb->lcd_set_foreground(LCD_BLACK); /* Greyscale screens */
648 #endif
650 rb->lcd_hline(0,LCD_WIDTH-1, BAR_HLINE_Y);
651 rb->lcd_hline(0,LCD_WIDTH-1, BAR_HLINE_Y2);
653 /* Draw graduation lines on the off-by readout */
654 for(n = 0; n <= GRADUATION; n++)
656 x = (LCD_WIDTH * n + GRADUATION / 2) / GRADUATION;
657 if (x >= LCD_WIDTH)
658 x = LCD_WIDTH - 1;
659 rb->lcd_vline(x, BAR_HLINE_Y, BAR_HLINE_Y2);
662 #if LCD_DEPTH > 1
663 rb->lcd_set_foreground(front_color);
664 #endif
665 rb->lcd_putsxyf(lbl_x_minus_50 ,bar_grad_y, "%d", -50);
666 rb->lcd_putsxyf(lbl_x_minus_20 ,bar_grad_y, "%d", -20);
667 rb->lcd_putsxyf(lbl_x_0 ,bar_grad_y, "%d", 0);
668 rb->lcd_putsxyf(lbl_x_20 ,bar_grad_y, "%d", 20);
669 rb->lcd_putsxyf(lbl_x_50 ,bar_grad_y, "%d", 50);
671 #ifdef HAVE_LCD_COLOR
672 rb->lcd_set_foreground(LCD_RGBPACK(255,0,0)); /* Color screens */
673 #elif LCD_DEPTH > 1
674 rb->lcd_set_foreground(LCD_DARKGRAY); /* Greyscale screens */
675 #endif
677 if (fp_gt(wrong_by_cents, FP_ZERO))
679 rb->lcd_fillrect(bar_x_0, BAR_Y,
680 fixed2int(fp_mul(wrong_by_cents, LCD_FACTOR)), BAR_HEIGHT);
682 else
684 rb->lcd_fillrect(bar_x_0 + fixed2int(fp_mul(wrong_by_cents,LCD_FACTOR)),
685 BAR_Y,
686 fixed2int(fp_mul(wrong_by_cents, LCD_FACTOR)) * -1,
687 BAR_HEIGHT);
691 /* Calculate how wrong the note is and draw the GUI */
692 static void display_frequency (fixed freq)
694 fixed ldf, mldf;
695 fixed lfreq, nfreq;
696 fixed orig_freq;
697 int i, note = 0;
699 if (fp_lt(freq, FP_LOW))
700 freq = FP_LOW;
702 /* We calculate the frequency and its log as if */
703 /* the reference frequency of A were 440 Hz. */
704 orig_freq = freq;
705 lfreq = fp_add(log(freq), freq_A[settings.freq_A].logratio);
706 freq = fp_mul(freq, freq_A[settings.freq_A].ratio);
708 /* This calculates a log freq offset for note A */
709 /* Get the frequency to within the range of our reference table, */
710 /* i.e. into the right octave. */
711 while (fp_lt(lfreq, fp_sub(notes[0].logfreq, fp_shr(LOG_D_NOTE, 1))))
712 lfreq = fp_add(lfreq, LOG_2);
713 while (fp_gte(lfreq, fp_sub(fp_add(notes[0].logfreq, LOG_2),
714 fp_shr(LOG_D_NOTE, 1))))
715 lfreq = fp_sub(lfreq, LOG_2);
716 mldf = LOG_D_NOTE;
717 for (i=0; i<12; i++)
719 ldf = fp_gt(fp_sub(lfreq,notes[i].logfreq), FP_ZERO) ?
720 fp_sub(lfreq,notes[i].logfreq) : fp_neg(fp_sub(lfreq,notes[i].logfreq));
721 if (fp_lt(ldf, mldf))
723 mldf = ldf;
724 note = i;
727 nfreq = notes[note].freq;
728 while (fp_gt(fp_div(nfreq, freq), D_NOTE_SQRT))
729 nfreq = fp_shr(nfreq, 1);
731 while (fp_gt(fp_div(freq, nfreq), D_NOTE_SQRT))
732 nfreq = fp_shl(nfreq, 1);
734 ldf = fp_mul(int2fixed(1200), log(fp_div(freq,nfreq)));
736 rb->lcd_clear_display();
737 draw_bar(ldf); /* The red bar */
738 if(fp_round(freq) != 0)
740 /* Raise the displayed pitch an octave minus key_transposition */
741 /* semitones, effectively lowering it. Note that the pitch */
742 /* displayed alongside the frequency is unaffected. */
743 int transposition = 12 - settings.key_transposition;
745 draw_note(notes[(note + transposition) % 12].name);
747 if(settings.display_hz)
749 #if LCD_DEPTH > 1
750 rb->lcd_set_foreground(front_color);
751 #endif
752 rb->lcd_putsxyf(0, HZ_Y, "%s : %d cents (%d.%02dHz)",
753 notes[note].name, fp_round(ldf) ,fixed2int(orig_freq),
754 fp_round(fp_mul(fp_frac(orig_freq),
755 int2fixed(DISPLAY_HZ_PRECISION))));
758 rb->lcd_update();
761 #ifndef SIMULATOR
762 /*-----------------------------------------------------------------------
763 * Functions for the Yin algorithm
765 * These were all adapted from the versions in Aubio v0.3.2
766 * Here's what the Aubio documentation has to say:
768 * This algorithm was developped by A. de Cheveigne and H. Kawahara and
769 * published in:
771 * de Cheveign?, A., Kawahara, H. (2002) "YIN, a fundamental frequency
772 * estimator for speech and music", J. Acoust. Soc. Am. 111, 1917-1930.
774 * see http://recherche.ircam.fr/equipes/pcm/pub/people/cheveign.html
775 -------------------------------------------------------------------------*/
777 /* Find the index of the minimum element of an array of floats */
778 static unsigned vec_min_elem(fixed *s, unsigned buflen)
780 unsigned j, pos=0.0f;
781 fixed tmp = s[0];
782 for (j=0; j < buflen; j++)
784 if(fp_gt(tmp, s[j]))
786 pos = j;
787 tmp = s[j];
790 return pos;
794 static inline fixed aubio_quadfrac(fixed s0, fixed s1, fixed s2, fixed pf)
796 /* Original floating point version: */
797 /* tmp = s0 + (pf/2.0f) * (pf * ( s0 - 2.0f*s1 + s2 ) -
798 3.0f*s0 + 4.0f*s1 - s2);*/
799 /* Converted to explicit operator precedence: */
800 /* tmp = s0 + ((pf/2.0f) * ((((pf * ((s0 - (2*s1)) + s2)) -
801 (3*s0)) + (4*s1)) - s2)); */
803 /* I made it look like this so I could easily track the precedence and */
804 /* make sure it matched the original expression */
805 /* Oy, this is when I really wish I could do C++ operator overloading */
806 fixed tmp = fp_add
809 fp_mul
811 fp_shr(pf, 1),
812 fp_sub
814 fp_add
816 fp_sub
818 fp_mul
821 fp_add
823 fp_sub
826 fp_shl(s1, 1)
831 fp_mul
833 float2fixed(3.0f),
837 fp_shl(s1, 2)
843 return tmp;
846 #define QUADINT_STEP float2fixed(1.0f/200.0f)
848 static fixed ICODE_ATTR vec_quadint_min(fixed *x, unsigned bufsize, unsigned pos, unsigned span)
850 fixed res, frac, s0, s1, s2;
851 fixed exactpos = int2fixed(pos);
852 /* init resold to something big (in case x[pos+-span]<0)) */
853 fixed resold = FP_MAX;
855 if ((pos > span) && (pos < bufsize-span))
857 s0 = x[pos-span];
858 s1 = x[pos] ;
859 s2 = x[pos+span];
860 /* increase frac */
861 for (frac = float2fixed(0.0f);
862 fp_lt(frac, float2fixed(2.0f));
863 frac = fp_add(frac, QUADINT_STEP))
865 res = aubio_quadfrac(s0, s1, s2, frac);
866 if (fp_lt(res, resold))
868 resold = res;
870 else
872 /* exactpos += (frac-QUADINT_STEP)*span - span/2.0f; */
873 exactpos = fp_add(exactpos,
874 fp_sub(
875 fp_mul(
876 fp_sub(frac, QUADINT_STEP),
877 int2fixed(span)
879 int2fixed(span)
882 break;
886 return exactpos;
889 /* Calculate the period of the note in the
890 buffer using the YIN algorithm */
891 /* The yin pointer is just a buffer that the algorithm uses as a work
892 space. It needs to be half the length of the input buffer. */
894 static fixed ICODE_ATTR pitchyin(int16_t *input, fixed *yin)
896 fixed retval;
897 unsigned j,tau = 0;
898 int period;
899 unsigned yin_size = settings.sample_size / 4;
901 fixed tmp = FP_ZERO, tmp2 = FP_ZERO;
902 yin[0] = int2fixed(1);
903 for (tau = 1; tau < yin_size; tau++)
905 yin[tau] = FP_ZERO;
906 for (j = 0; j < yin_size; j++)
908 tmp = fp_sub(int2mantissa(input[2 * j]),
909 int2mantissa(input[2 * (j + tau)]));
910 yin[tau] = fp_add(yin[tau], fp_mul(tmp, tmp));
912 tmp2 = fp_add(tmp2, yin[tau]);
913 if(!fp_equal(tmp2, FP_ZERO))
915 yin[tau] = fp_mul(yin[tau], fp_div(int2fixed(tau), tmp2));
917 period = tau - 3;
918 if(tau > 4 && fp_lt(yin[period],
919 yin_threshold_table[settings.yin_threshold])
920 && fp_lt(yin[period], yin[period+1]))
922 retval = vec_quadint_min(yin, yin_size, period, 1);
923 return retval;
926 retval = vec_quadint_min(yin, yin_size,
927 vec_min_elem(yin, yin_size), 1);
928 return retval;
929 /*return FP_ZERO;*/
932 /*-----------------------------------------------------------------*/
934 static uint32_t ICODE_ATTR buffer_magnitude(int16_t *input)
936 unsigned n;
937 uint64_t tally = 0;
938 const unsigned size = settings.sample_size;
940 /* Operate on only one channel of the stereo signal */
941 for(n = 0; n < size; n+=2)
943 int s = input[n];
944 tally += s * s;
947 tally /= size / 2;
949 /* now tally holds the average of the squares of all the samples */
950 /* It must be between 0 and 0x7fff^2, so it fits in 32 bits */
951 return (uint32_t)tally;
954 /* Stop the recording when the buffer is full */
955 static void recording_callback(void **start, size_t *size)
957 int tail = audio_tail ^ 1;
959 /* Do not overrun the reader. Reuse current buffer if full. */
960 if (tail != audio_head)
961 audio_tail = tail;
963 /* Always record full buffer, even if not required */
964 *start = audio_data[tail];
965 *size = BUFFER_SIZE * sizeof (int16_t);
967 #endif /* SIMULATOR */
969 /* Start recording */
970 static void record_data(void)
972 #ifndef SIMULATOR
973 /* Always record full buffer, even if not required */
974 rb->pcm_record_data(recording_callback, NULL,
975 audio_data[audio_tail],
976 BUFFER_SIZE * sizeof (int16_t));
977 #endif
980 /* The main program loop */
981 static void record_and_get_pitch(void)
983 int quit=0, button;
984 #ifndef SIMULATOR
985 bool redraw = true;
986 #endif
987 /* For tracking the latency */
989 long timer;
990 char debug_string[20];
992 #ifndef SIMULATOR
993 fixed period;
994 bool waiting = false;
995 #else
996 audio_tail = 1;
997 #endif
999 backlight_ignore_timeout();
1001 record_data();
1003 while(!quit)
1005 while (audio_head == audio_tail && !quit) /* wait for the buffer to be filled */
1007 button=pluginlib_getaction(HZ/100, plugin_contexts, PLA_ARRAY_COUNT);
1009 switch(button)
1011 case PLA_EXIT:
1012 quit=true;
1013 break;
1015 case PLA_CANCEL:
1016 rb->pcm_stop_recording();
1017 quit = main_menu();
1018 if(!quit)
1020 #ifndef SIMULATOR
1021 redraw = true;
1022 #endif
1023 record_data();
1025 break;
1029 if(!quit)
1031 #ifndef SIMULATOR
1032 /* Only do the heavy lifting if the volume is high enough */
1033 if(buffer_magnitude(audio_data[audio_head]) >
1034 sqr(settings.volume_threshold *
1035 rb->sound_max(SOUND_MIC_GAIN)))
1037 waiting = false;
1038 redraw = false;
1040 #ifdef HAVE_SCHEDULER_BOOSTCTRL
1041 rb->trigger_cpu_boost();
1042 #endif
1043 #ifdef PLUGIN_USE_IRAM
1044 rb->memcpy(iram_audio_data, audio_data[audio_head],
1045 settings.sample_size * sizeof (int16_t));
1046 #endif
1047 /* This returns the period of the detected pitch in samples */
1048 period = pitchyin(iram_audio_data, yin_buffer);
1049 /* Hz = sample rate / period */
1050 if(fp_gt(period, FP_ZERO))
1052 display_frequency(fp_period2freq(period));
1054 else
1056 display_frequency(FP_ZERO);
1059 else if(redraw || !waiting)
1061 waiting = true;
1062 redraw = false;
1063 display_frequency(FP_ZERO);
1064 #ifdef HAVE_ADJUSTABLE_CPU_FREQ
1065 rb->cancel_cpu_boost();
1066 #endif
1069 /* Move to next buffer if not empty (but empty *shouldn't* happen
1070 * here). */
1071 if (audio_head != audio_tail)
1072 audio_head ^= 1;
1073 #else /* SIMULATOR */
1074 /* Display a preselected frequency */
1075 display_frequency(int2fixed(445));
1076 #endif
1079 rb->pcm_close_recording();
1080 rb->pcm_set_frequency(HW_SAMPR_RESET | SAMPR_TYPE_REC);
1081 #ifdef HAVE_SCHEDULER_BOOSTCTRL
1082 rb->cancel_cpu_boost();
1083 #endif
1085 backlight_use_settings();
1088 /* Init recording, tuning, and GUI */
1089 static void init_everything(void)
1091 /* Disable all talking before initializing IRAM */
1092 rb->talk_disable(true);
1094 load_settings();
1095 rb->storage_sleep();
1097 /* Stop all playback */
1098 rb->plugin_get_audio_buffer(NULL);
1100 /* --------- Init the audio recording ----------------- */
1101 rb->audio_set_output_source(AUDIO_SRC_PLAYBACK);
1102 rb->audio_set_input_source(INPUT_TYPE, SRCF_RECORDING);
1104 /* set to maximum gain */
1105 rb->audio_set_recording_gain(settings.record_gain,
1106 settings.record_gain,
1107 AUDIO_GAIN_MIC);
1109 /* Highest C on piano is approx 4.186 kHz, so we need just over
1110 * 8.372 kHz to pass it. */
1111 sample_rate = rb->round_value_to_list32(9000, rb->rec_freq_sampr,
1112 REC_NUM_FREQ, false);
1113 sample_rate = rb->rec_freq_sampr[sample_rate];
1114 rb->pcm_set_frequency(sample_rate | SAMPR_TYPE_REC);
1115 rb->pcm_init_recording();
1117 /* avoid divsion by zero */
1118 if(settings.lowest_freq == 0)
1119 settings.lowest_freq = period2freq(BUFFER_SIZE / 4);
1121 /* GUI */
1122 #if LCD_DEPTH > 1
1123 front_color = rb->lcd_get_foreground();
1124 #endif
1125 rb->lcd_getstringsize("X", &font_w, &font_h);
1127 bar_x_0 = LCD_WIDTH / 2;
1128 lbl_x_minus_50 = 0;
1129 lbl_x_minus_20 = (LCD_WIDTH / 2) -
1130 fixed2int(fp_mul(LCD_FACTOR, int2fixed(20))) - font_w;
1131 lbl_x_0 = (LCD_WIDTH - font_w) / 2;
1132 lbl_x_20 = (LCD_WIDTH / 2) +
1133 fixed2int(fp_mul(LCD_FACTOR, int2fixed(20))) - font_w;
1134 lbl_x_50 = LCD_WIDTH - 2 * font_w;
1136 bar_grad_y = BAR_Y - BAR_PADDING - font_h;
1137 /* Put the note right between the top and bottom text elements */
1138 note_y = ((font_h + bar_grad_y - note_bitmaps.slide_height) / 2);
1140 rb->talk_disable(false);
1143 enum plugin_status plugin_start(const void* parameter)
1145 (void)parameter;
1147 init_everything();
1148 record_and_get_pitch();
1149 save_settings();
1151 return PLUGIN_OK;