summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlexander Levin <al.le@rockbox.org>2009-09-08 16:45:05 +0000
committerAlexander Levin <al.le@rockbox.org>2009-09-08 16:45:05 +0000
commitaf23b06b7cc6f6f3031ea727c7fd9a54473b8cd1 (patch)
tree36840e5e7b6fff9d2ea4e8d719a37206879dc7f0
parent1f10f99dc055815169e4cc3734da5d2ef26fd7be (diff)
downloadrockbox-af23b06b7cc6f6f3031ea727c7fd9a54473b8cd1.tar.gz
rockbox-af23b06b7cc6f6f3031ea727c7fd9a54473b8cd1.zip
New plugin: pitch detector (FS#8768) by Michael Lechner and David Johnston
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@22663 a1c6a512-1295-4272-9138-f99709370657
-rw-r--r--apps/plugins/CATEGORIES1
-rw-r--r--apps/plugins/SOURCES4
-rw-r--r--apps/plugins/pitch_detector.c1055
-rw-r--r--docs/CREDITS1
4 files changed, 1061 insertions, 0 deletions
diff --git a/apps/plugins/CATEGORIES b/apps/plugins/CATEGORIES
index 3dc11b636e..ae764ca125 100644
--- a/apps/plugins/CATEGORIES
+++ b/apps/plugins/CATEGORIES
@@ -58,6 +58,7 @@ pacbox,games
58pdbox,viewers 58pdbox,viewers
59pegbox,games 59pegbox,games
60pictureflow,demos 60pictureflow,demos
61pitch_detector,apps
61plasma,demos 62plasma,demos
62png,viewers 63png,viewers
63pong,games 64pong,games
diff --git a/apps/plugins/SOURCES b/apps/plugins/SOURCES
index 95ffb1c9de..b9061ee74b 100644
--- a/apps/plugins/SOURCES
+++ b/apps/plugins/SOURCES
@@ -29,6 +29,10 @@ firmware_flash.c
29rockbox_flash.c 29rockbox_flash.c
30#endif /* CONFIG_CPU */ 30#endif /* CONFIG_CPU */
31 31
32#if (CONFIG_CODEC == SWCODEC) && defined(HAVE_RECORDING) && \
33 (defined(HAVE_LINE_IN) || defined(HAVE_MIC_IN))
34pitch_detector.c
35#endif
32 36
33#if (CONFIG_CODEC == SWCODEC) || !defined(SIMULATOR) 37#if (CONFIG_CODEC == SWCODEC) || !defined(SIMULATOR)
34metronome.c 38metronome.c
diff --git a/apps/plugins/pitch_detector.c b/apps/plugins/pitch_detector.c
new file mode 100644
index 0000000000..8d0b3b7957
--- /dev/null
+++ b/apps/plugins/pitch_detector.c
@@ -0,0 +1,1055 @@
1/**************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id:$
9 *
10 * Copyright (C) 2008 Lechner Michael / smoking gnu
11 *
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.
14 *
15 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
16 * KIND, either express or implied.
17 *
18 * ----------------------------------------------------------------------------
19 *
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.
26 *
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 * - 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
35 *
36 * MAJOR CHANGES:
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
51 * latency)
52 * Improved the display
53 * Improved efficiency with judicious use of cpu_boost, the
54 * backlight, and volume detection to limit unneeded
55 * calculation
56 * Fixed a problem that caused an octave-off error
57 * -David
58 *
59 *
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
66 * frequencies.
67 */
68
69#include "plugin.h"
70#include "lib/pluginlib_actions.h"
71
72PLUGIN_HEADER
73
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
81#endif
82
83/* Some fixed point calculation stuff */
84typedef int32_t fixed_data;
85struct _fixed
86{
87 fixed_data a;
88};
89typedef 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})
121
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 */
129/* that variable. */
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)
135
136#define sqr(x) ((x)*(x))
137
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)
144
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
152 a power of 2 */
153#define BUFFER_SIZE 4096
154#define SAMPLE_SIZE 4096
155#define SAMPLE_SIZE_MIN 1024
156#define YIN_BUFFER_SIZE (BUFFER_SIZE / 4)
157
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)
161
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
167
168/* How many decimal places to display for the Hz value */
169#define DISPLAY_HZ_PRECISION 100
170
171typedef signed short audio_sample_type;
172/* It's stereo, so make the buffer twice as big */
173audio_sample_type audio_data[BUFFER_SIZE];
174fixed yin_buffer[YIN_BUFFER_SIZE];
175static int recording=0;
176
177/* Frequencies of all the notes of the scale */
178static const fixed freqs[12] =
179{
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),
192};
193/* logarithm of all the notes of the scale */
194static const fixed lfreqs[12] =
195{
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),
208};
209
210/* GUI */
211static unsigned back_color, front_color;
212static int font_w, font_h;
213static int bar_x_minus_50, bar_x_minus_20, bar_x_0, bar_x_20, bar_x_50;
214static int letter_y; /* y of the notes letters */
215static int note_bar_y; /* y of the yellow bars (under the notes) */
216static int bar_h; /* height of the yellow (note) and red (error) bars */
217static int freq_y; /* y of the line with the frequency */
218static int error_ticks_y; /* y of the error values (-50, -20, ...) */
219static int error_hline_y; /* y of the top line for error bar */
220static int error_hline_y2;/* y of the bottom line for error bar */
221static int error_bar_y; /* y of the error bar */
222static int error_bar_margin; /* distance between the error bar and the hline */
223
224static const char *english_notes[12] = {"A","A#","B","C","C#","D","D#","E",
225 "F","F#", "G", "G#"};
226static const char gui_letters[8] = {'A', 'B', 'C', 'D', 'E', 'F', 'G', '#'};
227static const char **notes = english_notes;
228
229/*Settings for the plugin */
230
231struct tuner_settings
232{
233 unsigned volume_threshold;
234 unsigned record_gain;
235 unsigned sample_size;
236 unsigned lowest_freq;
237 unsigned yin_threshold;
238} tuner_settings;
239
240/*=================================================================*/
241/* MENU */
242/*=================================================================*/
243
244/* Keymaps */
245const struct button_mapping* plugin_contexts[]={
246 generic_actions,
247 generic_increase_decrease,
248 generic_directions,
249#if NB_SCREENS == 2
250 remote_directions
251#endif
252};
253#define PLA_ARRAY_COUNT sizeof(plugin_contexts)/sizeof(plugin_contexts[0])
254
255fixed yin_threshold_table[] =
256{
257 float2fixed(0.01),
258 float2fixed(0.02),
259 float2fixed(0.03),
260 float2fixed(0.04),
261 float2fixed(0.05),
262 float2fixed(0.10),
263 float2fixed(0.15),
264 float2fixed(0.20),
265 float2fixed(0.25),
266 float2fixed(0.30),
267 float2fixed(0.35),
268 float2fixed(0.40),
269 float2fixed(0.45),
270 float2fixed(0.50),
271};
272
273#define DEFAULT_YIN_THRESHOLD 5 /* 0.10 */
274
275/* Option strings */
276static const struct opt_items yin_threshold_text[] =
277{
278 { "0.01", -1 },
279 { "0.02", -1 },
280 { "0.03", -1 },
281 { "0.04", -1 },
282 { "0.05", -1 },
283 { "0.10", -1 },
284 { "0.15", -1 },
285 { "0.20", -1 },
286 { "0.25", -1 },
287 { "0.30", -1 },
288 { "0.35", -1 },
289 { "0.40", -1 },
290 { "0.45", -1 },
291 { "0.50", -1 },
292};
293
294void set_min_freq(int new_freq)
295{
296 tuner_settings.sample_size = freq2period(new_freq) * 4;
297
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);
306}
307
308bool main_menu(void)
309{
310 int selection=0;
311 bool done = false;
312 bool exit_tuner=false;
313 int choice;
314
315 MENUITEM_STRINGLIST(menu,"Tuner Settings",NULL,
316 "Return to Tuner",
317 "Volume Threshold",
318 "Listening Volume",
319 "Lowest Frequency",
320 "Algorithm Pickiness",
321 "Quit");
322
323 while(!done)
324 {
325 choice = rb->do_menu(&menu, &selection, NULL, false);
326 switch(choice)
327 {
328 case 1:
329 rb->set_int("Volume Threshold", "%", UNIT_INT,
330 &tuner_settings.volume_threshold,
331 NULL, 5, 5, 95, NULL);
332 break;
333 case 2:
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);
338 break;
339 case 3:
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);
345 break;
346 case 4:
347 rb->set_option(
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]),
353 NULL);
354 break;
355 case 5:
356 exit_tuner = true;
357 done = true;
358 break;
359 case 0:
360 default: /* Return to the tuner */
361 done = true;
362 break;
363
364 }
365 }
366 return(exit_tuner);
367}
368
369/*=================================================================*/
370/* Settings loading and saving(adapted from the clock plugin) */
371/*=================================================================*/
372
373#define SETTINGS_FILENAME PLUGIN_APPS_DIR "/.pitch_settings"
374
375enum message
376{
377 MESSAGE_LOADING,
378 MESSAGE_LOADED,
379 MESSAGE_ERRLOAD,
380 MESSAGE_SAVING,
381 MESSAGE_SAVED,
382 MESSAGE_ERRSAVE
383};
384
385enum settings_file_status
386{
387 LOADED, ERRLOAD,
388 SAVED, ERRSAVE
389};
390
391/* The settings as they exist on the hard disk, so that
392 * we can know at saving time if changes have been made */
393struct tuner_settings hdd_tuner_settings;
394
395/*---------------------------------------------------------------------*/
396
397bool settings_needs_saving(struct tuner_settings* settings)
398{
399 return(rb->memcmp(settings, &hdd_tuner_settings, sizeof(*settings)));
400}
401
402/*---------------------------------------------------------------------*/
403
404void tuner_settings_reset(struct tuner_settings* settings)
405{
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;
411}
412
413/*---------------------------------------------------------------------*/
414
415enum settings_file_status tuner_settings_load(struct tuner_settings* settings,
416 char* filename)
417{
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));
423 rb->close(fd);
424 rb->memcpy(&hdd_tuner_settings, settings, sizeof(*settings));
425 return(LOADED);
426 }
427 }
428 /* Initializes the settings with default values at least */
429 tuner_settings_reset(settings);
430 return(ERRLOAD);
431}
432
433/*---------------------------------------------------------------------*/
434
435enum settings_file_status tuner_settings_save(struct tuner_settings* settings,
436 char* filename)
437{
438 int fd = rb->creat(filename);
439 if(fd >= 0){ /* does file exist? */
440 rb->write (fd, settings, sizeof(*settings));
441 rb->close(fd);
442 return(SAVED);
443 }
444 return(ERRSAVE);
445}
446
447/*---------------------------------------------------------------------*/
448
449void load_settings(void)
450{
451 tuner_settings_load(&tuner_settings, SETTINGS_FILENAME);
452
453 rb->storage_sleep();
454}
455
456/*---------------------------------------------------------------------*/
457
458void save_settings(void)
459{
460 if(!settings_needs_saving(&tuner_settings))
461 return;
462
463 tuner_settings_save(&tuner_settings, SETTINGS_FILENAME);
464}
465
466/*=================================================================*/
467/* Binary Log */
468/*=================================================================*/
469
470/* Fixed-point log base 2*/
471/* Adapted from python code at
472 http://en.wikipedia.org/wiki/Binary_logarithm#Algorithm
473*/
474fixed log(fixed inp)
475{
476 fixed x = inp;
477 fixed fp = int2fixed(1);
478 fixed res = int2fixed(0);
479
480 if(fp_lte(x, FP_ZERO))
481 {
482 return FP_MIN;
483 }
484
485 /* Integer part*/
486 /* while x<1 */
487 while(fp_lt(x, int2fixed(1)))
488 {
489 res = fp_sub(res, int2fixed(1));
490 x = fp_shl(x, 1);
491 }
492 /* while x>=2 */
493 while(fp_gte(x, int2fixed(2)))
494 {
495 res = fp_add(res, int2fixed(1));
496 x = fp_shr(x, 1);
497 }
498
499 /* Fractional part */
500 /* while fp > 0 */
501 while(fp_gt(fp, FP_ZERO))
502 {
503 fp = fp_shr(fp, 1);
504 x = fp_mul(x, x);
505 /* if x >= 2 */
506 if(fp_gte(x, int2fixed(2)))
507 {
508 x = fp_shr(x, 1);
509 res = fp_add(res, fp);
510 }
511 }
512
513 return res;
514}
515
516/*=================================================================*/
517/* GUI Stuff */
518/*=================================================================*/
519
520/* The function name is pretty self-explaining ;) */
521void print_int_xy(int x, int y, int v)
522{
523 char temp[20];
524
525 rb->lcd_set_foreground(front_color);
526 rb->snprintf(temp,20,"%d",v);
527 rb->lcd_putsxy(x,y,temp);
528 rb->lcd_update();
529}
530
531/* Print out the frequency etc - will be removed later on */
532void print_str(char* s)
533{
534 rb->lcd_set_foreground(front_color);
535 rb->lcd_putsxy(0, freq_y, s);
536 rb->lcd_update();
537}
538
539/* What can I say? Read the function name... */
540void print_char_xy(int x, int y, char c)
541{
542 char temp[2];
543
544 temp[0]=c;
545 temp[1]=0;
546 rb->lcd_set_foreground(front_color);
547
548 rb->lcd_putsxy(x, y, temp);
549}
550
551/* Draw the red bar and the white lines */
552void draw_bar(fixed wrong_by_cents)
553{
554#ifdef HAVE_LCD_COLOR
555 rb->lcd_set_foreground(LCD_RGBPACK(255,0,0)); /* Color screens */
556#elif LCD_DEPTH > 1
557 rb->lcd_set_foreground(LCD_DARKGRAY); /* Greyscale screens */
558#else
559 rb->lcd_set_foreground(LCD_BLACK); /* Black and white screens */
560#endif
561
562 if (fp_gt(wrong_by_cents, FP_ZERO))
563 {
564 rb->lcd_fillrect(bar_x_0, error_bar_y,
565 fixed2int(fp_mul(wrong_by_cents, LCD_FACTOR)), bar_h);
566 }
567 else
568 {
569 rb->lcd_fillrect(bar_x_0 + fixed2int(fp_mul(wrong_by_cents,LCD_FACTOR)),
570 error_bar_y,
571 fixed2int(fp_mul(wrong_by_cents, LCD_FACTOR)) * -1,
572 bar_h);
573 }
574#ifdef HAVE_LCD_COLOR
575 rb->lcd_set_foreground(LCD_RGBPACK(255,255,255)); /* Color screens */
576#elif LCD_DEPTH > 1
577 rb->lcd_set_foreground(LCD_BLACK); /* Greyscale screens */
578#else
579 rb->lcd_set_foreground(LCD_BLACK); /* Black and white screens */
580#endif
581
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);
585
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);
591}
592
593/* Print the letters A-G and the # on the screen */
594void draw_letters(void)
595{
596 int i;
597
598 rb->lcd_set_foreground(front_color);
599
600 for (i=0; i<8; i++)
601 {
602 print_char_xy(i*(LCD_WIDTH / 8 ) + font_w, letter_y, gui_letters[i]);
603 }
604}
605
606/* Draw the yellow point(s) below the letters and the '#' */
607void draw_points(const char *s)
608{
609 int i;
610
611 i = s[0]-'A';
612#ifdef HAVE_LCD_COLOR
613 rb->lcd_set_foreground(LCD_RGBPACK(255,255,0)); /* Color screens */
614#elif LCD_DEPTH > 1
615 rb->lcd_set_foreground(LCD_DARKGRAY); /* Grey screens */
616#else
617 rb->lcd_set_foreground(LCD_BLACK); /* Black and White screens */
618#endif
619
620 rb->lcd_fillrect(i*(LCD_WIDTH / 8 ) + font_w, note_bar_y, font_w, font_h);
621
622 if (s[1] == '#')
623 rb->lcd_fillrect(7*(LCD_WIDTH / 8 ) + font_w, note_bar_y, font_w, font_h);
624}
625
626/* Calculate how wrong the note is and draw the GUI */
627void display_frequency (fixed freq)
628{
629 fixed ldf, mldf;
630 fixed lfreq, nfreq;
631 int i, note = 0;
632 bool draw_freq;
633 char str_buf[30];
634
635 if (fp_lt(freq, FP_LOW))
636 freq = FP_LOW;
637 lfreq = log(freq);
638
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);
645 mldf = LOG_D_NOTE;
646 for (i=0; i<12; i++)
647 {
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))
651 {
652 mldf = ldf;
653 note = i;
654 }
655 }
656 nfreq = freqs[note];
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))
660 {
661 nfreq = fp_shl(nfreq, 1);
662 }
663
664 ldf=fp_mul(int2fixed(1200), log(fp_div(freq,nfreq)));
665
666 draw_freq = (fp_round(freq) != 0);
667 if (draw_freq)
668 {
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))));
673 }
674 else
675 {
676 ldf = FP_ZERO; /* prevents red bar at -32 cents when freq= 0*/
677 }
678
679 rb->lcd_clear_display();
680 draw_bar(ldf); /* The red bar */
681 draw_letters(); /* The A-G letters and the # */
682 if (draw_freq)
683 {
684 draw_points(notes[note]); /* The yellow point(s) */
685 print_str(str_buf);
686 }
687 rb->lcd_update();
688}
689
690/*-----------------------------------------------------------------------
691 * Functions for the Yin algorithm
692 *
693 * These were all adapted from the versions in Aubio v0.3.2
694 * Here's what the Aubio documentation has to say:
695 *
696 * This algorithm was developped by A. de Cheveigne and H. Kawahara and
697 * published in:
698 *
699 * de Cheveign?, A., Kawahara, H. (2002) "YIN, a fundamental frequency
700 * estimator for speech and music", J. Acoust. Soc. Am. 111, 1917-1930.
701 *
702 * see http://recherche.ircam.fr/equipes/pcm/pub/people/cheveign.html
703-------------------------------------------------------------------------*/
704
705/* Find the index of the minimum element of an array of floats */
706unsigned vec_min_elem(fixed *s, unsigned buflen)
707{
708 unsigned j, pos=0.0f;
709 fixed tmp = s[0];
710 for (j=0; j < buflen; j++)
711 {
712 if(fp_gt(tmp, s[j]))
713 {
714 pos = j;
715 tmp = s[j];
716 }
717 }
718 return pos;
719}
720
721
722fixed aubio_quadfrac(fixed s0, fixed s1, fixed s2, fixed pf)
723{
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)); */
730
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 */
734 fixed tmp = fp_add
735 (
736 s0,
737 fp_mul
738 (
739 fp_shr(pf, 1),
740 fp_sub
741 (
742 fp_add
743 (
744 fp_sub
745 (
746 fp_mul
747 (
748 pf,
749 fp_add
750 (
751 fp_sub
752 (
753 s0,
754 fp_shl(s1, 1)
755 ),
756 s2
757 )
758 ),
759 fp_mul
760 (
761 float2fixed(3.0f),
762 s0
763 )
764 ),
765 fp_shl(s1, 2)
766 ),
767 s2
768 )
769 )
770 );
771 return tmp;
772}
773
774#define QUADINT_STEP float2fixed(1.0f/200.0f)
775
776fixed vec_quadint_min(fixed *x, unsigned bufsize, unsigned pos, unsigned span)
777{
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;
782
783 if ((pos > span) && (pos < bufsize-span))
784 {
785 s0 = x[pos-span];
786 s1 = x[pos] ;
787 s2 = x[pos+span];
788 /* increase frac */
789 for (frac = float2fixed(0.0f);
790 fp_lt(frac, float2fixed(2.0f));
791 frac = fp_add(frac, QUADINT_STEP))
792 {
793 res = aubio_quadfrac(s0, s1, s2, frac);
794 if (fp_lt(res, resold))
795 {
796 resold = res;
797 }
798 else
799 {
800 /* exactpos += (frac-QUADINT_STEP)*span - span/2.0f; */
801 exactpos = fp_add(exactpos,
802 fp_sub(
803 fp_mul(
804 fp_sub(frac, QUADINT_STEP),
805 int2fixed(span)
806 ),
807 int2fixed(span)
808 )
809 );
810 break;
811 }
812 }
813 }
814 return exactpos;
815}
816
817
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. */
822
823fixed pitchyin(audio_sample_type *input, fixed *yin)
824{
825 fixed retval;
826 unsigned j,tau = 0;
827 int period;
828 unsigned yin_size = tuner_settings.sample_size / 4;
829
830 fixed tmp = FP_ZERO, tmp2 = FP_ZERO;
831 yin[0] = int2fixed(1);
832 for (tau = 1; tau < yin_size; tau++)
833 {
834 yin[tau] = FP_ZERO;
835 for (j = 0; j < yin_size; j++)
836 {
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));
840 }
841 tmp2 = fp_add(tmp2, yin[tau]);
842 if(!fp_equal(tmp2, FP_ZERO))
843 {
844 yin[tau] = fp_mul(yin[tau], fp_div(int2fixed(tau), tmp2));
845 }
846 period = tau - 3;
847 if(tau > 4 && fp_lt(yin[period],
848 yin_threshold_table[tuner_settings.yin_threshold])
849 && fp_lt(yin[period], yin[period+1]))
850 {
851 retval = vec_quadint_min(yin, yin_size, period, 1);
852 return retval;
853 }
854 }
855 retval = vec_quadint_min(yin, yin_size,
856 vec_min_elem(yin, yin_size), 1);
857 return retval;
858 /*return FP_ZERO;*/
859}
860
861/*-----------------------------------------------------------------*/
862
863uint32_t buffer_magnitude(audio_sample_type *input)
864{
865 unsigned n;
866 uint64_t tally = 0;
867
868 /* Operate on only one channel of the stereo signal */
869 for(n = 0; n < tuner_settings.sample_size; n+=2)
870 {
871 tally += (uint64_t)input[n] * (uint64_t)input[n];
872 }
873
874 tally /= tuner_settings.sample_size / 2;
875
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;
879}
880
881/* Stop the recording when the buffer is full */
882int recording_callback(int status)
883{
884 (void) status;
885
886 rb->pcm_stop_recording();
887 recording=0;
888 return -1;
889}
890
891/* The main program loop */
892void record_and_get_pitch(void)
893{
894 int quit=0, button;
895 bool redraw = true;
896 /* For tracking the latency */
897 /*
898 long timer;
899 char debug_string[20];
900 */
901 fixed period;
902 bool waiting = false;
903
904 while(!quit)
905 {
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));
910 recording=1;
911
912 while (recording && !quit) /* wait for the buffer to be filled */
913 {
914 rb->yield();
915 button=pluginlib_getaction(0, plugin_contexts, PLA_ARRAY_COUNT);
916 switch(button)
917 {
918 case PLA_QUIT:
919 quit=true;
920 rb->yield();
921 break;
922
923 case PLA_MENU:
924 if(main_menu())
925 quit=true;
926 rb->yield();
927 redraw = true;
928 break;
929
930 default:
931 rb->yield();
932
933 break;
934 }
935 }
936
937 /* Let's keep track of how long this takes */
938 /* timer = *(rb->current_tick); */
939
940 if(!quit)
941 {
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)))
946 {
947 if(waiting)
948 {
949 #ifdef HAVE_ADJUSTABLE_CPU_FREQ
950 rb->cpu_boost(true);
951 #endif
952 waiting = false;
953 }
954
955 rb->backlight_on();
956 redraw = false;
957
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))
962 {
963 display_frequency(fp_period2freq(period));
964 }
965 else
966 {
967 display_frequency(FP_ZERO);
968 }
969 }
970 else if(redraw || !waiting)
971 {
972 waiting = true;
973 redraw = false;
974 #ifdef HAVE_ADJUSTABLE_CPU_FREQ
975 rb->cpu_boost(false);
976 #endif
977 /*rb->backlight_off();*/
978 display_frequency(FP_ZERO);
979 }
980
981 /*rb->snprintf(debug_string, 20, "%x,%x",
982 audio_data[50], audio_data[52]);
983 rb->lcd_putsxy(0, 40, debug_string);
984 rb->lcd_update();
985 */
986
987 /* Print out how long it took to find the pitch */
988 /*
989 rb->snprintf(debug_string, 20, "latency: %ld",
990 *(rb->current_tick) - timer);
991 rb->lcd_putsxy(0, 40, debug_string);
992 rb->lcd_update();
993 */
994 }
995 }
996 rb->pcm_close_recording();
997#ifdef HAVE_ADJUSTABLE_CPU_FREQ
998 rb->cpu_boost(false);
999#endif
1000}
1001
1002/* Init recording, tuning, and GUI */
1003void init_everything(void)
1004{
1005 load_settings();
1006
1007 /* --------- Init the audio recording ----------------- */
1008 rb->audio_set_output_source(AUDIO_SRC_PLAYBACK);
1009 rb->audio_set_input_source(INPUT_TYPE, SRCF_RECORDING);
1010
1011 /* set to maximum gain */
1012 rb->audio_set_recording_gain(tuner_settings.record_gain,
1013 tuner_settings.record_gain,
1014 AUDIO_GAIN_MIC);
1015
1016 rb->pcm_set_frequency(SAMPLE_RATE);
1017 rb->pcm_apply_settings();
1018
1019 rb->pcm_init_recording();
1020
1021 /* GUI */
1022 back_color = rb->lcd_get_background();
1023 front_color = rb->lcd_get_foreground();
1024 rb->lcd_getstringsize("X", &font_w, &font_h);
1025
1026 bar_x_minus_50 = 0;
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;
1033
1034 letter_y = 10;
1035 note_bar_y = letter_y + font_h;
1036 bar_h = 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;
1043}
1044
1045
1046enum plugin_status plugin_start(const void* parameter) NO_PROF_ATTR
1047{
1048 (void)parameter;
1049
1050 init_everything();
1051 record_and_get_pitch();
1052 save_settings();
1053
1054 return 0;
1055}
diff --git a/docs/CREDITS b/docs/CREDITS
index 46a555b681..9066778249 100644
--- a/docs/CREDITS
+++ b/docs/CREDITS
@@ -492,6 +492,7 @@ Johannes Boy
492Jason Yu 492Jason Yu
493Aaron DeMille 493Aaron DeMille
494Tomasz Kowalczyk 494Tomasz Kowalczyk
495Michael Lechner
495 496
496The libmad team 497The libmad team
497The wavpack team 498The wavpack team