1 /**************************************************************************
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
10 * Copyright (C) 2008 Lechner Michael / smoking gnu
12 * All files in this archive are subject to the GNU General Public License.
13 * See the file COPYING in the source tree root for full license agreement.
15 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
16 * KIND, either express or implied.
18 * ----------------------------------------------------------------------------
21 * OK, this is an attempt to write an instrument tuner for rockbox.
22 * It uses a Schmitt trigger algorithm, which I copied from
23 * tuneit [ (c) 2004 Mario Lang <mlang@delysid.org> ], for detecting the
24 * fundamental freqency of a sound. A FFT algorithm would be more accurate
25 * but also much slower.
28 * - 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
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
47 * Improved the display
48 * Improved efficiency with judicious use of cpu_boost, the
49 * backlight, and volume detection to limit unneeded
51 * Fixed a problem that caused an octave-off error
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
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 */
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
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
=
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.
171 const int frequency
; /* Frequency in Hz */
172 const fixed ratio
; /* 440/frequency */
173 const fixed logratio
; /* log2(factor) */
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 */
199 #define INPUT_TYPE AUDIO_SRC_MIC
201 #define INPUT_TYPE AUDIO_SRC_LINEIN
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 */
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)
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
=
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 */
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
;
252 #define iram_audio_data audio_data[audio_head]
256 /* Notes within one (reference) scale */
259 const char *name
; /* Name of the note, e.g. "A#" */
260 const fixed freq
; /* Note frequency, Hz */
261 const fixed logfreq
; /* log2(frequency) */
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
)},
280 static unsigned front_color
;
282 static int font_w
,font_h
;
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 */
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'. */
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
,
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();
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
));
361 tuner_settings_reset();
367 /*---------------------------------------------------------------------*/
369 static void save_settings(void)
371 if(!settings_needs_saving())
374 int fd
= rb
->creat(SETTINGS_FILENAME
, 0666);
375 if(fd
>= 0){ /* does file exist? */
376 rb
->write (fd
, &settings
, sizeof(settings
));
381 /*=================================================================*/
383 /*=================================================================*/
386 const struct button_mapping
* plugin_contexts
[]={
392 #define PLA_ARRAY_COUNT sizeof(plugin_contexts)/sizeof(plugin_contexts[0])
396 /* This has to match yin_threshold_table */
397 static const struct opt_items yin_threshold_text
[] =
415 static const struct opt_items accidental_text
[] =
421 static const struct opt_items transpose_text
[] =
423 { "C (Concert Pitch)", -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)
456 bool exit_tuner
= false;
461 backlight_use_settings();
462 #ifdef HAVE_SCHEDULER_BOOSTCTRL
463 rb
->cancel_cpu_boost();
466 MENUITEM_STRINGLIST(menu
,"Tuner Settings",NULL
,
471 "Algorithm Pickiness",
474 "Display Frequency (Hz)",
475 "Frequency of A (Hz)",
481 choice
= rb
->do_menu(&menu
, &selection
, NULL
, false);
485 rb
->set_int("Volume Threshold", "%", UNIT_INT
,
486 &settings
.volume_threshold
,
487 NULL
, 5, 5, 95, NULL
);
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
);
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
);
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]),
511 rb
->set_option("Display Accidentals As",
512 &settings
.use_sharps
,
513 BOOL
, accidental_text
, 2, NULL
);
516 rb
->set_option("Key Transposition",
517 &settings
.key_transposition
,
518 INT
, transpose_text
, 12, NULL
);
521 rb
->set_bool("Display Frequency (Hz)",
522 &settings
.display_hz
);
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
,
530 settings
.freq_A
= freq_val
- freq_A
[0].frequency
;
534 rb
->set_bool("Reset Tuner Settings?", &reset
);
536 tuner_settings_reset();
544 /* Return to the tuner */
550 backlight_ignore_timeout();
554 /*=================================================================*/
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
)
565 fixed fp
= int2fixed(1);
566 fixed res
= int2fixed(0);
568 if(fp_lte(x
, FP_ZERO
))
575 while(fp_lt(x
, int2fixed(1)))
577 res
= fp_sub(res
, int2fixed(1));
581 while(fp_gte(x
, int2fixed(2)))
583 res
= fp_add(res
, int2fixed(1));
587 /* Fractional part */
589 while(fp_gt(fp
, FP_ZERO
))
594 if(fp_gte(x
, int2fixed(2)))
597 res
= fp_add(res
, fp
);
604 /*=================================================================*/
606 /*=================================================================*/
608 /* Draw the note bitmap */
609 static void draw_note(const char *note
)
612 int note_x
= (LCD_WIDTH
- BMPWIDTH_pitch_notes
) / 2;
613 int accidental_index
= NOTE_INDEX_SHARP
;
619 if(!(settings
.use_sharps
))
622 accidental_index
= NOTE_INDEX_FLAT
;
625 vertical_picture_draw_sprite(rb
->screens
[0],
630 note_x
= LCD_WIDTH
/ 2 - BMPWIDTH_pitch_notes
;
633 vertical_picture_draw_sprite(rb
->screens
[0], ¬e_bitmaps
, i
,
638 /* Draw the red bar and the white lines */
639 static void draw_bar(fixed wrong_by_cents
)
644 #ifdef HAVE_LCD_COLOR
645 rb
->lcd_set_foreground(LCD_RGBPACK(255,255,255)); /* Color screens */
647 rb
->lcd_set_foreground(LCD_BLACK
); /* Greyscale screens */
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
;
659 rb
->lcd_vline(x
, BAR_HLINE_Y
, BAR_HLINE_Y2
);
663 rb
->lcd_set_foreground(front_color
);
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 */
674 rb
->lcd_set_foreground(LCD_DARKGRAY
); /* Greyscale screens */
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
);
684 rb
->lcd_fillrect(bar_x_0
+ fixed2int(fp_mul(wrong_by_cents
,LCD_FACTOR
)),
686 fixed2int(fp_mul(wrong_by_cents
, LCD_FACTOR
)) * -1,
691 /* Calculate how wrong the note is and draw the GUI */
692 static void display_frequency (fixed freq
)
699 if (fp_lt(freq
, FP_LOW
))
702 /* We calculate the frequency and its log as if */
703 /* the reference frequency of A were 440 Hz. */
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
);
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
))
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
)
750 rb
->lcd_set_foreground(front_color
);
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
))));
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
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
;
782 for (j
=0; j
< buflen
; j
++)
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 */
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
))
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
))
872 /* exactpos += (frac-QUADINT_STEP)*span - span/2.0f; */
873 exactpos
= fp_add(exactpos
,
876 fp_sub(frac
, QUADINT_STEP
),
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
)
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
++)
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
));
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);
926 retval
= vec_quadint_min(yin
, yin_size
,
927 vec_min_elem(yin
, yin_size
), 1);
932 /*-----------------------------------------------------------------*/
934 static uint32_t ICODE_ATTR
buffer_magnitude(int16_t *input
)
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)
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(int status
, 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
)
963 /* Always record full buffer, even if not required */
964 *start
= audio_data
[tail
];
965 *size
= BUFFER_SIZE
* sizeof (int16_t);
969 #endif /* SIMULATOR */
971 /* Start recording */
972 static void record_data(void)
975 /* Always record full buffer, even if not required */
976 rb
->pcm_record_data(recording_callback
, audio_data
[audio_tail
],
977 BUFFER_SIZE
* sizeof (int16_t));
981 /* The main program loop */
982 static void record_and_get_pitch(void)
988 /* For tracking the latency */
991 char debug_string[20];
995 bool waiting
= false;
1000 backlight_ignore_timeout();
1006 while (audio_head
== audio_tail
&& !quit
) /* wait for the buffer to be filled */
1008 button
=pluginlib_getaction(HZ
/100, plugin_contexts
, PLA_ARRAY_COUNT
);
1017 rb
->pcm_stop_recording();
1033 /* Only do the heavy lifting if the volume is high enough */
1034 if(buffer_magnitude(audio_data
[audio_head
]) >
1035 sqr(settings
.volume_threshold
*
1036 rb
->sound_max(SOUND_MIC_GAIN
)))
1041 #ifdef HAVE_SCHEDULER_BOOSTCTRL
1042 rb
->trigger_cpu_boost();
1044 #ifdef PLUGIN_USE_IRAM
1045 rb
->memcpy(iram_audio_data
, audio_data
[audio_head
],
1046 settings
.sample_size
* sizeof (int16_t));
1048 /* This returns the period of the detected pitch in samples */
1049 period
= pitchyin(iram_audio_data
, yin_buffer
);
1050 /* Hz = sample rate / period */
1051 if(fp_gt(period
, FP_ZERO
))
1053 display_frequency(fp_period2freq(period
));
1057 display_frequency(FP_ZERO
);
1060 else if(redraw
|| !waiting
)
1064 display_frequency(FP_ZERO
);
1065 #ifdef HAVE_ADJUSTABLE_CPU_FREQ
1066 rb
->cancel_cpu_boost();
1070 /* Move to next buffer if not empty (but empty *shouldn't* happen
1072 if (audio_head
!= audio_tail
)
1074 #else /* SIMULATOR */
1075 /* Display a preselected frequency */
1076 display_frequency(int2fixed(445));
1080 rb
->pcm_close_recording();
1081 rb
->pcm_set_frequency(REC_SAMPR_DEFAULT
| SAMPR_TYPE_REC
);
1082 #ifdef HAVE_SCHEDULER_BOOSTCTRL
1083 rb
->cancel_cpu_boost();
1086 backlight_use_settings();
1089 /* Init recording, tuning, and GUI */
1090 static void init_everything(void)
1092 /* Disable all talking before initializing IRAM */
1093 rb
->talk_disable(true);
1096 rb
->storage_sleep();
1098 /* Stop all playback */
1099 rb
->plugin_get_audio_buffer(NULL
);
1101 /* --------- Init the audio recording ----------------- */
1102 rb
->audio_set_output_source(AUDIO_SRC_PLAYBACK
);
1103 rb
->audio_set_input_source(INPUT_TYPE
, SRCF_RECORDING
);
1105 /* set to maximum gain */
1106 rb
->audio_set_recording_gain(settings
.record_gain
,
1107 settings
.record_gain
,
1110 /* Highest C on piano is approx 4.186 kHz, so we need just over
1111 * 8.372 kHz to pass it. */
1112 sample_rate
= rb
->round_value_to_list32(9000, rb
->rec_freq_sampr
,
1113 REC_NUM_FREQ
, false);
1114 sample_rate
= rb
->rec_freq_sampr
[sample_rate
];
1115 rb
->pcm_set_frequency(sample_rate
| SAMPR_TYPE_REC
);
1116 rb
->pcm_init_recording();
1118 /* avoid divsion by zero */
1119 if(settings
.lowest_freq
== 0)
1120 settings
.lowest_freq
= period2freq(BUFFER_SIZE
/ 4);
1124 front_color
= rb
->lcd_get_foreground();
1126 rb
->lcd_getstringsize("X", &font_w
, &font_h
);
1128 bar_x_0
= LCD_WIDTH
/ 2;
1130 lbl_x_minus_20
= (LCD_WIDTH
/ 2) -
1131 fixed2int(fp_mul(LCD_FACTOR
, int2fixed(20))) - font_w
;
1132 lbl_x_0
= (LCD_WIDTH
- font_w
) / 2;
1133 lbl_x_20
= (LCD_WIDTH
/ 2) +
1134 fixed2int(fp_mul(LCD_FACTOR
, int2fixed(20))) - font_w
;
1135 lbl_x_50
= LCD_WIDTH
- 2 * font_w
;
1137 bar_grad_y
= BAR_Y
- BAR_PADDING
- font_h
;
1138 /* Put the note right between the top and bottom text elements */
1139 note_y
= ((font_h
+ bar_grad_y
- note_bitmaps
.slide_height
) / 2);
1141 rb
->talk_disable(false);
1144 enum plugin_status
plugin_start(const void* parameter
)
1149 record_and_get_pitch();