diff options
Diffstat (limited to 'apps/plugins/pitch_detector.c')
-rw-r--r-- | apps/plugins/pitch_detector.c | 567 |
1 files changed, 312 insertions, 255 deletions
diff --git a/apps/plugins/pitch_detector.c b/apps/plugins/pitch_detector.c index 8d0b3b7957..857d74afc4 100644 --- a/apps/plugins/pitch_detector.c +++ b/apps/plugins/pitch_detector.c | |||
@@ -5,7 +5,7 @@ | |||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | 5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < |
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | 6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ |
7 | * \/ \/ \/ \/ \/ | 7 | * \/ \/ \/ \/ \/ |
8 | * $Id:$ | 8 | * $Id$ |
9 | * | 9 | * |
10 | * Copyright (C) 2008 Lechner Michael / smoking gnu | 10 | * Copyright (C) 2008 Lechner Michael / smoking gnu |
11 | * | 11 | * |
@@ -29,7 +29,6 @@ | |||
29 | * recording code to use proper, gapless recording with a callback function | 29 | * recording code to use proper, gapless recording with a callback function |
30 | * that provides new buffer, instead of stopping and restarting recording | 30 | * that provides new buffer, instead of stopping and restarting recording |
31 | * everytime the buffer is full | 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) | 32 | * - 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 | 33 | * to O(nlogn), theoretically reducing latency by a factor of ~10. -David |
35 | * | 34 | * |
@@ -68,6 +67,8 @@ | |||
68 | 67 | ||
69 | #include "plugin.h" | 68 | #include "plugin.h" |
70 | #include "lib/pluginlib_actions.h" | 69 | #include "lib/pluginlib_actions.h" |
70 | #include "lib/picture.h" | ||
71 | #include "pluginbitmaps/pitch_notes.h" | ||
71 | 72 | ||
72 | PLUGIN_HEADER | 73 | PLUGIN_HEADER |
73 | 74 | ||
@@ -120,9 +121,10 @@ typedef struct _fixed fixed; | |||
120 | #define FP_LOW ((fixed){1}) | 121 | #define FP_LOW ((fixed){1}) |
121 | 122 | ||
122 | /* Some defines for converting between period and frequency */ | 123 | /* Some defines for converting between period and frequency */ |
124 | |||
123 | /* I introduce some divisors in this because the fixed point */ | 125 | /* I introduce some divisors in this because the fixed point */ |
124 | /* variables aren't big enough to hold higher than a certain */ | 126 | /* variables aren't big enough to hold higher than a certain */ |
125 | /* vallue. This loses a bit of precision but it means we */ | 127 | /* value. This loses a bit of precision but it means we */ |
126 | /* don't have to use 32.32 variables (yikes). */ | 128 | /* don't have to use 32.32 variables (yikes). */ |
127 | /* With an 18-bit decimal precision, the max value in the */ | 129 | /* 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 */ | 130 | /* integer part is 8192. Divide 44100 by 7 and it'll fit in */ |
@@ -157,7 +159,24 @@ typedef struct _fixed fixed; | |||
157 | 159 | ||
158 | #define LCD_FACTOR (fp_div(int2fixed(LCD_WIDTH), int2fixed(100))) | 160 | #define LCD_FACTOR (fp_div(int2fixed(LCD_WIDTH), int2fixed(100))) |
159 | /* The threshold for the YIN algorithm */ | 161 | /* The threshold for the YIN algorithm */ |
160 | #define YIN_THRESHOLD float2fixed(0.05f) | 162 | #define DEFAULT_YIN_THRESHOLD 5 /* 0.10 */ |
163 | const fixed yin_threshold_table[] = | ||
164 | { | ||
165 | float2fixed(0.01), | ||
166 | float2fixed(0.02), | ||
167 | float2fixed(0.03), | ||
168 | float2fixed(0.04), | ||
169 | float2fixed(0.05), | ||
170 | float2fixed(0.10), | ||
171 | float2fixed(0.15), | ||
172 | float2fixed(0.20), | ||
173 | float2fixed(0.25), | ||
174 | float2fixed(0.30), | ||
175 | float2fixed(0.35), | ||
176 | float2fixed(0.40), | ||
177 | float2fixed(0.45), | ||
178 | float2fixed(0.50), | ||
179 | }; | ||
161 | 180 | ||
162 | /* How loud the audio has to be to start displaying pitch */ | 181 | /* How loud the audio has to be to start displaying pitch */ |
163 | /* Must be between 0 and 100 */ | 182 | /* Must be between 0 and 100 */ |
@@ -168,6 +187,40 @@ typedef struct _fixed fixed; | |||
168 | /* How many decimal places to display for the Hz value */ | 187 | /* How many decimal places to display for the Hz value */ |
169 | #define DISPLAY_HZ_PRECISION 100 | 188 | #define DISPLAY_HZ_PRECISION 100 |
170 | 189 | ||
190 | /* Where to put the various GUI elements */ | ||
191 | int note_y; | ||
192 | int bar_grad_y; | ||
193 | #define LCD_RES_MIN (LCD_HEIGHT < LCD_WIDTH ? LCD_HEIGHT : LCD_WIDTH) | ||
194 | #define BAR_PADDING (LCD_RES_MIN / 32) | ||
195 | #define BAR_Y (LCD_HEIGHT * 3 / 4) | ||
196 | #define BAR_HEIGHT (LCD_RES_MIN / 4 - BAR_PADDING) | ||
197 | #define BAR_HLINE_Y (BAR_Y - BAR_PADDING) | ||
198 | #define BAR_HLINE_Y2 (BAR_Y + BAR_HEIGHT + BAR_PADDING - 1) | ||
199 | #define HZ_Y 0 | ||
200 | #define GRADUATION 10 /* Subdivisions of the whole 100-cent scale */ | ||
201 | |||
202 | /* Bitmaps for drawing the note names. These need to have height | ||
203 | <= (bar_grad_y - note_y), or 15/32 * LCD_HEIGHT | ||
204 | */ | ||
205 | #define NUM_NOTE_IMAGES 9 | ||
206 | #define NOTE_INDEX_A 0 | ||
207 | #define NOTE_INDEX_B 1 | ||
208 | #define NOTE_INDEX_C 2 | ||
209 | #define NOTE_INDEX_D 3 | ||
210 | #define NOTE_INDEX_E 4 | ||
211 | #define NOTE_INDEX_F 5 | ||
212 | #define NOTE_INDEX_G 6 | ||
213 | #define NOTE_INDEX_SHARP 7 | ||
214 | #define NOTE_INDEX_FLAT 8 | ||
215 | const struct picture note_bitmaps = | ||
216 | { | ||
217 | pitch_notes, | ||
218 | BMPWIDTH_pitch_notes, | ||
219 | BMPHEIGHT_pitch_notes, | ||
220 | BMPHEIGHT_pitch_notes/NUM_NOTE_IMAGES | ||
221 | }; | ||
222 | |||
223 | |||
171 | typedef signed short audio_sample_type; | 224 | typedef signed short audio_sample_type; |
172 | /* It's stereo, so make the buffer twice as big */ | 225 | /* It's stereo, so make the buffer twice as big */ |
173 | audio_sample_type audio_data[BUFFER_SIZE]; | 226 | audio_sample_type audio_data[BUFFER_SIZE]; |
@@ -209,21 +262,12 @@ static const fixed lfreqs[12] = | |||
209 | 262 | ||
210 | /* GUI */ | 263 | /* GUI */ |
211 | static unsigned back_color, front_color; | 264 | static unsigned back_color, front_color; |
212 | static int font_w, font_h; | 265 | static int font_w,font_h; |
213 | static int bar_x_minus_50, bar_x_minus_20, bar_x_0, bar_x_20, bar_x_50; | 266 | static int bar_x_0; |
214 | static int letter_y; /* y of the notes letters */ | 267 | static int lbl_x_minus_50, lbl_x_minus_20, lbl_x_0, lbl_x_20, lbl_x_50; |
215 | static int note_bar_y; /* y of the yellow bars (under the notes) */ | 268 | |
216 | static int bar_h; /* height of the yellow (note) and red (error) bars */ | 269 | static const char *english_notes[] = {"A","A#","B","C","C#","D","D#","E", |
217 | static int freq_y; /* y of the line with the frequency */ | 270 | "F","F#","G","G#"}; |
218 | static int error_ticks_y; /* y of the error values (-50, -20, ...) */ | ||
219 | static int error_hline_y; /* y of the top line for error bar */ | ||
220 | static int error_hline_y2;/* y of the bottom line for error bar */ | ||
221 | static int error_bar_y; /* y of the error bar */ | ||
222 | static int error_bar_margin; /* distance between the error bar and the hline */ | ||
223 | |||
224 | static const char *english_notes[12] = {"A","A#","B","C","C#","D","D#","E", | ||
225 | "F","F#", "G", "G#"}; | ||
226 | static const char gui_letters[8] = {'A', 'B', 'C', 'D', 'E', 'F', 'G', '#'}; | ||
227 | static const char **notes = english_notes; | 271 | static const char **notes = english_notes; |
228 | 272 | ||
229 | /*Settings for the plugin */ | 273 | /*Settings for the plugin */ |
@@ -235,9 +279,118 @@ struct tuner_settings | |||
235 | unsigned sample_size; | 279 | unsigned sample_size; |
236 | unsigned lowest_freq; | 280 | unsigned lowest_freq; |
237 | unsigned yin_threshold; | 281 | unsigned yin_threshold; |
282 | bool use_sharps; | ||
283 | bool display_hz; | ||
238 | } tuner_settings; | 284 | } tuner_settings; |
239 | 285 | ||
240 | /*=================================================================*/ | 286 | /*=================================================================*/ |
287 | /* Settings loading and saving(adapted from the clock plugin) */ | ||
288 | /*=================================================================*/ | ||
289 | |||
290 | #define SETTINGS_FILENAME PLUGIN_APPS_DIR "/.pitch_settings" | ||
291 | |||
292 | enum message | ||
293 | { | ||
294 | MESSAGE_LOADING, | ||
295 | MESSAGE_LOADED, | ||
296 | MESSAGE_ERRLOAD, | ||
297 | MESSAGE_SAVING, | ||
298 | MESSAGE_SAVED, | ||
299 | MESSAGE_ERRSAVE | ||
300 | }; | ||
301 | |||
302 | enum settings_file_status | ||
303 | { | ||
304 | LOADED, ERRLOAD, | ||
305 | SAVED, ERRSAVE | ||
306 | }; | ||
307 | |||
308 | /* The settings as they exist on the hard disk, so that | ||
309 | * we can know at saving time if changes have been made */ | ||
310 | struct tuner_settings hdd_tuner_settings; | ||
311 | |||
312 | /*---------------------------------------------------------------------*/ | ||
313 | |||
314 | bool settings_needs_saving(struct tuner_settings* settings) | ||
315 | { | ||
316 | return(rb->memcmp(settings, &hdd_tuner_settings, sizeof(*settings))); | ||
317 | } | ||
318 | |||
319 | /*---------------------------------------------------------------------*/ | ||
320 | |||
321 | void tuner_settings_reset(struct tuner_settings* settings) | ||
322 | { | ||
323 | settings->volume_threshold = VOLUME_THRESHOLD; | ||
324 | settings->record_gain = rb->global_settings->rec_mic_gain; | ||
325 | settings->sample_size = BUFFER_SIZE; | ||
326 | settings->lowest_freq = period2freq(BUFFER_SIZE / 4); | ||
327 | settings->yin_threshold = DEFAULT_YIN_THRESHOLD; | ||
328 | settings->use_sharps = true; | ||
329 | settings->display_hz = false; | ||
330 | } | ||
331 | |||
332 | /*---------------------------------------------------------------------*/ | ||
333 | |||
334 | void tuner_settings_reset_query(int yes) | ||
335 | { | ||
336 | if(yes) | ||
337 | tuner_settings_reset(&tuner_settings); | ||
338 | } | ||
339 | |||
340 | /*---------------------------------------------------------------------*/ | ||
341 | |||
342 | enum settings_file_status tuner_settings_load(struct tuner_settings* settings, | ||
343 | char* filename) | ||
344 | { | ||
345 | int fd = rb->open(filename, O_RDONLY); | ||
346 | if(fd >= 0){ /* does file exist? */ | ||
347 | /* basic consistency check */ | ||
348 | if(rb->filesize(fd) == sizeof(*settings)){ | ||
349 | rb->read(fd, settings, sizeof(*settings)); | ||
350 | rb->close(fd); | ||
351 | rb->memcpy(&hdd_tuner_settings, settings, sizeof(*settings)); | ||
352 | return(LOADED); | ||
353 | } | ||
354 | } | ||
355 | /* Initializes the settings with default values at least */ | ||
356 | tuner_settings_reset(settings); | ||
357 | return(ERRLOAD); | ||
358 | } | ||
359 | |||
360 | /*---------------------------------------------------------------------*/ | ||
361 | |||
362 | enum settings_file_status tuner_settings_save(struct tuner_settings* settings, | ||
363 | char* filename) | ||
364 | { | ||
365 | int fd = rb->creat(filename); | ||
366 | if(fd >= 0){ /* does file exist? */ | ||
367 | rb->write (fd, settings, sizeof(*settings)); | ||
368 | rb->close(fd); | ||
369 | return(SAVED); | ||
370 | } | ||
371 | return(ERRSAVE); | ||
372 | } | ||
373 | |||
374 | /*---------------------------------------------------------------------*/ | ||
375 | |||
376 | void load_settings(void) | ||
377 | { | ||
378 | tuner_settings_load(&tuner_settings, SETTINGS_FILENAME); | ||
379 | |||
380 | rb->storage_sleep(); | ||
381 | } | ||
382 | |||
383 | /*---------------------------------------------------------------------*/ | ||
384 | |||
385 | void save_settings(void) | ||
386 | { | ||
387 | if(!settings_needs_saving(&tuner_settings)) | ||
388 | return; | ||
389 | |||
390 | tuner_settings_save(&tuner_settings, SETTINGS_FILENAME); | ||
391 | } | ||
392 | |||
393 | /*=================================================================*/ | ||
241 | /* MENU */ | 394 | /* MENU */ |
242 | /*=================================================================*/ | 395 | /*=================================================================*/ |
243 | 396 | ||
@@ -252,27 +405,9 @@ const struct button_mapping* plugin_contexts[]={ | |||
252 | }; | 405 | }; |
253 | #define PLA_ARRAY_COUNT sizeof(plugin_contexts)/sizeof(plugin_contexts[0]) | 406 | #define PLA_ARRAY_COUNT sizeof(plugin_contexts)/sizeof(plugin_contexts[0]) |
254 | 407 | ||
255 | fixed 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 */ | 408 | /* Option strings */ |
409 | |||
410 | /* This has to match yin_threshold_table */ | ||
276 | static const struct opt_items yin_threshold_text[] = | 411 | static const struct opt_items yin_threshold_text[] = |
277 | { | 412 | { |
278 | { "0.01", -1 }, | 413 | { "0.01", -1 }, |
@@ -291,6 +426,18 @@ static const struct opt_items yin_threshold_text[] = | |||
291 | { "0.50", -1 }, | 426 | { "0.50", -1 }, |
292 | }; | 427 | }; |
293 | 428 | ||
429 | static const struct opt_items accidental_text[] = | ||
430 | { | ||
431 | { "Flat", -1 }, | ||
432 | { "Sharp", -1 }, | ||
433 | }; | ||
434 | |||
435 | static const struct opt_items noyes_text[] = | ||
436 | { | ||
437 | { "No", -1 }, | ||
438 | { "Yes", -1 } | ||
439 | }; | ||
440 | |||
294 | void set_min_freq(int new_freq) | 441 | void set_min_freq(int new_freq) |
295 | { | 442 | { |
296 | tuner_settings.sample_size = freq2period(new_freq) * 4; | 443 | tuner_settings.sample_size = freq2period(new_freq) * 4; |
@@ -311,6 +458,7 @@ bool main_menu(void) | |||
311 | bool done = false; | 458 | bool done = false; |
312 | bool exit_tuner=false; | 459 | bool exit_tuner=false; |
313 | int choice; | 460 | int choice; |
461 | bool reset = false; | ||
314 | 462 | ||
315 | MENUITEM_STRINGLIST(menu,"Tuner Settings",NULL, | 463 | MENUITEM_STRINGLIST(menu,"Tuner Settings",NULL, |
316 | "Return to Tuner", | 464 | "Return to Tuner", |
@@ -318,6 +466,9 @@ bool main_menu(void) | |||
318 | "Listening Volume", | 466 | "Listening Volume", |
319 | "Lowest Frequency", | 467 | "Lowest Frequency", |
320 | "Algorithm Pickiness", | 468 | "Algorithm Pickiness", |
469 | "Accidentals", | ||
470 | "Display Frequency (Hz)", | ||
471 | "Reset Settings", | ||
321 | "Quit"); | 472 | "Quit"); |
322 | 473 | ||
323 | while(!done) | 474 | while(!done) |
@@ -353,11 +504,25 @@ bool main_menu(void) | |||
353 | NULL); | 504 | NULL); |
354 | break; | 505 | break; |
355 | case 5: | 506 | case 5: |
356 | exit_tuner = true; | 507 | rb->set_option("Display Accidentals As", |
357 | done = true; | 508 | &tuner_settings.use_sharps, |
509 | BOOL, accidental_text, 2, NULL); | ||
358 | break; | 510 | break; |
359 | case 0: | 511 | case 6: |
360 | default: /* Return to the tuner */ | 512 | rb->set_option("Display Frequency (Hz)", |
513 | &tuner_settings.display_hz, | ||
514 | BOOL, noyes_text, 2, NULL); | ||
515 | break; | ||
516 | case 7: | ||
517 | rb->set_option("Reset Tuner Settings?", | ||
518 | &reset, | ||
519 | BOOL, noyes_text, 2, tuner_settings_reset_query); | ||
520 | break; | ||
521 | case 8: | ||
522 | exit_tuner = true; | ||
523 | case 0: | ||
524 | default: | ||
525 | /* Return to the tuner */ | ||
361 | done = true; | 526 | done = true; |
362 | break; | 527 | break; |
363 | 528 | ||
@@ -367,103 +532,6 @@ bool main_menu(void) | |||
367 | } | 532 | } |
368 | 533 | ||
369 | /*=================================================================*/ | 534 | /*=================================================================*/ |
370 | /* Settings loading and saving(adapted from the clock plugin) */ | ||
371 | /*=================================================================*/ | ||
372 | |||
373 | #define SETTINGS_FILENAME PLUGIN_APPS_DIR "/.pitch_settings" | ||
374 | |||
375 | enum message | ||
376 | { | ||
377 | MESSAGE_LOADING, | ||
378 | MESSAGE_LOADED, | ||
379 | MESSAGE_ERRLOAD, | ||
380 | MESSAGE_SAVING, | ||
381 | MESSAGE_SAVED, | ||
382 | MESSAGE_ERRSAVE | ||
383 | }; | ||
384 | |||
385 | enum 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 */ | ||
393 | struct tuner_settings hdd_tuner_settings; | ||
394 | |||
395 | /*---------------------------------------------------------------------*/ | ||
396 | |||
397 | bool settings_needs_saving(struct tuner_settings* settings) | ||
398 | { | ||
399 | return(rb->memcmp(settings, &hdd_tuner_settings, sizeof(*settings))); | ||
400 | } | ||
401 | |||
402 | /*---------------------------------------------------------------------*/ | ||
403 | |||
404 | void 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 | |||
415 | enum 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 | |||
435 | enum 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 | |||
449 | void load_settings(void) | ||
450 | { | ||
451 | tuner_settings_load(&tuner_settings, SETTINGS_FILENAME); | ||
452 | |||
453 | rb->storage_sleep(); | ||
454 | } | ||
455 | |||
456 | /*---------------------------------------------------------------------*/ | ||
457 | |||
458 | void 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 */ | 535 | /* Binary Log */ |
468 | /*=================================================================*/ | 536 | /*=================================================================*/ |
469 | 537 | ||
@@ -525,15 +593,13 @@ void print_int_xy(int x, int y, int v) | |||
525 | rb->lcd_set_foreground(front_color); | 593 | rb->lcd_set_foreground(front_color); |
526 | rb->snprintf(temp,20,"%d",v); | 594 | rb->snprintf(temp,20,"%d",v); |
527 | rb->lcd_putsxy(x,y,temp); | 595 | rb->lcd_putsxy(x,y,temp); |
528 | rb->lcd_update(); | ||
529 | } | 596 | } |
530 | 597 | ||
531 | /* Print out the frequency etc - will be removed later on */ | 598 | /* Print out the frequency etc */ |
532 | void print_str(char* s) | 599 | void print_str(char* s) |
533 | { | 600 | { |
534 | rb->lcd_set_foreground(front_color); | 601 | rb->lcd_set_foreground(front_color); |
535 | rb->lcd_putsxy(0, freq_y, s); | 602 | rb->lcd_putsxy(0, HZ_Y, s); |
536 | rb->lcd_update(); | ||
537 | } | 603 | } |
538 | 604 | ||
539 | /* What can I say? Read the function name... */ | 605 | /* What can I say? Read the function name... */ |
@@ -548,9 +614,67 @@ void print_char_xy(int x, int y, char c) | |||
548 | rb->lcd_putsxy(x, y, temp); | 614 | rb->lcd_putsxy(x, y, temp); |
549 | } | 615 | } |
550 | 616 | ||
617 | /* Draw the note bitmap */ | ||
618 | void draw_note(const char *note) | ||
619 | { | ||
620 | int i; | ||
621 | int note_x = (LCD_WIDTH - BMPWIDTH_pitch_notes) / 2; | ||
622 | int accidental_index = NOTE_INDEX_SHARP; | ||
623 | |||
624 | i = note[0]-'A'; | ||
625 | |||
626 | if(note[1] == '#') | ||
627 | { | ||
628 | if(!(tuner_settings.use_sharps)) | ||
629 | { | ||
630 | i = (i + 1) % 7; | ||
631 | accidental_index = NOTE_INDEX_FLAT; | ||
632 | } | ||
633 | |||
634 | vertical_picture_draw_sprite(rb->screens[0], | ||
635 | ¬e_bitmaps, | ||
636 | accidental_index, | ||
637 | LCD_WIDTH / 2, | ||
638 | note_y); | ||
639 | note_x = LCD_WIDTH / 2 - BMPWIDTH_pitch_notes; | ||
640 | } | ||
641 | |||
642 | vertical_picture_draw_sprite(rb->screens[0], ¬e_bitmaps, i, | ||
643 | note_x, | ||
644 | note_y); | ||
645 | } | ||
551 | /* Draw the red bar and the white lines */ | 646 | /* Draw the red bar and the white lines */ |
552 | void draw_bar(fixed wrong_by_cents) | 647 | void draw_bar(fixed wrong_by_cents) |
553 | { | 648 | { |
649 | unsigned n; | ||
650 | int x; | ||
651 | |||
652 | #ifdef HAVE_LCD_COLOR | ||
653 | rb->lcd_set_foreground(LCD_RGBPACK(255,255,255)); /* Color screens */ | ||
654 | #elif LCD_DEPTH > 1 | ||
655 | rb->lcd_set_foreground(LCD_BLACK); /* Greyscale screens */ | ||
656 | #else | ||
657 | rb->lcd_set_foreground(LCD_BLACK); /* Black and white screens */ | ||
658 | #endif | ||
659 | |||
660 | rb->lcd_hline(0,LCD_WIDTH-1, BAR_HLINE_Y); | ||
661 | rb->lcd_hline(0,LCD_WIDTH-1, BAR_HLINE_Y2); | ||
662 | |||
663 | /* Draw graduation lines on the off-by readout */ | ||
664 | for(n = 0; n <= GRADUATION; n++) | ||
665 | { | ||
666 | x = (LCD_WIDTH * n + GRADUATION / 2) / GRADUATION; | ||
667 | if (x >= LCD_WIDTH) | ||
668 | x = LCD_WIDTH - 1; | ||
669 | rb->lcd_vline(x, BAR_HLINE_Y, BAR_HLINE_Y2); | ||
670 | } | ||
671 | |||
672 | print_int_xy(lbl_x_minus_50 ,bar_grad_y, -50); | ||
673 | print_int_xy(lbl_x_minus_20 ,bar_grad_y, -20); | ||
674 | print_int_xy(lbl_x_0 ,bar_grad_y, 0); | ||
675 | print_int_xy(lbl_x_20 ,bar_grad_y, 20); | ||
676 | print_int_xy(lbl_x_50 ,bar_grad_y, 50); | ||
677 | |||
554 | #ifdef HAVE_LCD_COLOR | 678 | #ifdef HAVE_LCD_COLOR |
555 | rb->lcd_set_foreground(LCD_RGBPACK(255,0,0)); /* Color screens */ | 679 | rb->lcd_set_foreground(LCD_RGBPACK(255,0,0)); /* Color screens */ |
556 | #elif LCD_DEPTH > 1 | 680 | #elif LCD_DEPTH > 1 |
@@ -561,66 +685,16 @@ void draw_bar(fixed wrong_by_cents) | |||
561 | 685 | ||
562 | if (fp_gt(wrong_by_cents, FP_ZERO)) | 686 | if (fp_gt(wrong_by_cents, FP_ZERO)) |
563 | { | 687 | { |
564 | rb->lcd_fillrect(bar_x_0, error_bar_y, | 688 | rb->lcd_fillrect(bar_x_0, BAR_Y, |
565 | fixed2int(fp_mul(wrong_by_cents, LCD_FACTOR)), bar_h); | 689 | fixed2int(fp_mul(wrong_by_cents, LCD_FACTOR)), BAR_HEIGHT); |
566 | } | 690 | } |
567 | else | 691 | else |
568 | { | 692 | { |
569 | rb->lcd_fillrect(bar_x_0 + fixed2int(fp_mul(wrong_by_cents,LCD_FACTOR)), | 693 | rb->lcd_fillrect(bar_x_0 + fixed2int(fp_mul(wrong_by_cents,LCD_FACTOR)), |
570 | error_bar_y, | 694 | BAR_Y, |
571 | fixed2int(fp_mul(wrong_by_cents, LCD_FACTOR)) * -1, | 695 | fixed2int(fp_mul(wrong_by_cents, LCD_FACTOR)) * -1, |
572 | bar_h); | 696 | BAR_HEIGHT); |
573 | } | 697 | } |
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 */ | ||
594 | void 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 '#' */ | ||
607 | void 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 | } | 698 | } |
625 | 699 | ||
626 | /* Calculate how wrong the note is and draw the GUI */ | 700 | /* Calculate how wrong the note is and draw the GUI */ |
@@ -629,7 +703,6 @@ void display_frequency (fixed freq) | |||
629 | fixed ldf, mldf; | 703 | fixed ldf, mldf; |
630 | fixed lfreq, nfreq; | 704 | fixed lfreq, nfreq; |
631 | int i, note = 0; | 705 | int i, note = 0; |
632 | bool draw_freq; | ||
633 | char str_buf[30]; | 706 | char str_buf[30]; |
634 | 707 | ||
635 | if (fp_lt(freq, FP_LOW)) | 708 | if (fp_lt(freq, FP_LOW)) |
@@ -663,26 +736,19 @@ void display_frequency (fixed freq) | |||
663 | 736 | ||
664 | ldf=fp_mul(int2fixed(1200), log(fp_div(freq,nfreq))); | 737 | ldf=fp_mul(int2fixed(1200), log(fp_div(freq,nfreq))); |
665 | 738 | ||
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(); | 739 | rb->lcd_clear_display(); |
680 | draw_bar(ldf); /* The red bar */ | 740 | draw_bar(ldf); /* The red bar */ |
681 | draw_letters(); /* The A-G letters and the # */ | 741 | if(fp_round(freq) != 0) |
682 | if (draw_freq) | ||
683 | { | 742 | { |
684 | draw_points(notes[note]); /* The yellow point(s) */ | 743 | draw_note(notes[note]); |
685 | print_str(str_buf); | 744 | if(tuner_settings.display_hz) |
745 | { | ||
746 | rb->snprintf(str_buf,30, "%s : %d cents (%d.%02dHz)", | ||
747 | notes[note], fp_round(ldf) ,fixed2int(freq), | ||
748 | fp_round(fp_mul(fp_frac(freq), | ||
749 | int2fixed(DISPLAY_HZ_PRECISION)))); | ||
750 | print_str(str_buf); | ||
751 | } | ||
686 | } | 752 | } |
687 | rb->lcd_update(); | 753 | rb->lcd_update(); |
688 | } | 754 | } |
@@ -796,7 +862,7 @@ fixed vec_quadint_min(fixed *x, unsigned bufsize, unsigned pos, unsigned span) | |||
796 | resold = res; | 862 | resold = res; |
797 | } | 863 | } |
798 | else | 864 | else |
799 | { | 865 | { |
800 | /* exactpos += (frac-QUADINT_STEP)*span - span/2.0f; */ | 866 | /* exactpos += (frac-QUADINT_STEP)*span - span/2.0f; */ |
801 | exactpos = fp_add(exactpos, | 867 | exactpos = fp_add(exactpos, |
802 | fp_sub( | 868 | fp_sub( |
@@ -898,20 +964,28 @@ void record_and_get_pitch(void) | |||
898 | long timer; | 964 | long timer; |
899 | char debug_string[20]; | 965 | char debug_string[20]; |
900 | */ | 966 | */ |
967 | #ifndef SIMULATOR | ||
901 | fixed period; | 968 | fixed period; |
902 | bool waiting = false; | 969 | bool waiting = false; |
970 | #endif | ||
903 | 971 | ||
904 | while(!quit) | 972 | while(!quit) |
905 | { | 973 | { |
974 | #ifndef SIMULATOR | ||
906 | /* Start recording */ | 975 | /* Start recording */ |
907 | rb->pcm_record_data(recording_callback, (void *) audio_data, | 976 | rb->pcm_record_data(recording_callback, (void *) audio_data, |
908 | (size_t) tuner_settings.sample_size * | 977 | (size_t) tuner_settings.sample_size * |
909 | sizeof(audio_sample_type)); | 978 | sizeof(audio_sample_type)); |
979 | #endif | ||
910 | recording=1; | 980 | recording=1; |
911 | 981 | ||
912 | while (recording && !quit) /* wait for the buffer to be filled */ | 982 | while (recording && !quit) /* wait for the buffer to be filled */ |
913 | { | 983 | { |
914 | rb->yield(); | 984 | rb->yield(); |
985 | #ifdef SIMULATOR | ||
986 | /* Only do this loop once if this is the simulator */ | ||
987 | recording = 0; | ||
988 | #endif | ||
915 | button=pluginlib_getaction(0, plugin_contexts, PLA_ARRAY_COUNT); | 989 | button=pluginlib_getaction(0, plugin_contexts, PLA_ARRAY_COUNT); |
916 | switch(button) | 990 | switch(button) |
917 | { | 991 | { |
@@ -923,8 +997,8 @@ void record_and_get_pitch(void) | |||
923 | case PLA_MENU: | 997 | case PLA_MENU: |
924 | if(main_menu()) | 998 | if(main_menu()) |
925 | quit=true; | 999 | quit=true; |
1000 | else redraw = true; | ||
926 | rb->yield(); | 1001 | rb->yield(); |
927 | redraw = true; | ||
928 | break; | 1002 | break; |
929 | 1003 | ||
930 | default: | 1004 | default: |
@@ -934,11 +1008,9 @@ void record_and_get_pitch(void) | |||
934 | } | 1008 | } |
935 | } | 1009 | } |
936 | 1010 | ||
937 | /* Let's keep track of how long this takes */ | ||
938 | /* timer = *(rb->current_tick); */ | ||
939 | |||
940 | if(!quit) | 1011 | if(!quit) |
941 | { | 1012 | { |
1013 | #ifndef SIMULATOR | ||
942 | /* Only do the heavy lifting if the volume is high enough */ | 1014 | /* Only do the heavy lifting if the volume is high enough */ |
943 | if(buffer_magnitude(audio_data) > | 1015 | if(buffer_magnitude(audio_data) > |
944 | sqr(tuner_settings.volume_threshold * | 1016 | sqr(tuner_settings.volume_threshold * |
@@ -946,9 +1018,9 @@ void record_and_get_pitch(void) | |||
946 | { | 1018 | { |
947 | if(waiting) | 1019 | if(waiting) |
948 | { | 1020 | { |
949 | #ifdef HAVE_ADJUSTABLE_CPU_FREQ | 1021 | #ifdef HAVE_ADJUSTABLE_CPU_FREQ |
950 | rb->cpu_boost(true); | 1022 | rb->cpu_boost(true); |
951 | #endif | 1023 | #endif |
952 | waiting = false; | 1024 | waiting = false; |
953 | } | 1025 | } |
954 | 1026 | ||
@@ -971,26 +1043,16 @@ void record_and_get_pitch(void) | |||
971 | { | 1043 | { |
972 | waiting = true; | 1044 | waiting = true; |
973 | redraw = false; | 1045 | redraw = false; |
974 | #ifdef HAVE_ADJUSTABLE_CPU_FREQ | 1046 | #ifdef HAVE_ADJUSTABLE_CPU_FREQ |
975 | rb->cpu_boost(false); | 1047 | rb->cpu_boost(false); |
976 | #endif | 1048 | #endif |
977 | /*rb->backlight_off();*/ | 1049 | /*rb->backlight_off();*/ |
978 | display_frequency(FP_ZERO); | 1050 | display_frequency(FP_ZERO); |
979 | } | 1051 | } |
980 | 1052 | #else /* SIMULATOR */ | |
981 | /*rb->snprintf(debug_string, 20, "%x,%x", | 1053 | /* Display a preselected frequency */ |
982 | audio_data[50], audio_data[52]); | 1054 | display_frequency(int2fixed(445)); |
983 | rb->lcd_putsxy(0, 40, debug_string); | 1055 | #endif |
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 | } | 1056 | } |
995 | } | 1057 | } |
996 | rb->pcm_close_recording(); | 1058 | rb->pcm_close_recording(); |
@@ -1023,23 +1085,18 @@ void init_everything(void) | |||
1023 | front_color = rb->lcd_get_foreground(); | 1085 | front_color = rb->lcd_get_foreground(); |
1024 | rb->lcd_getstringsize("X", &font_w, &font_h); | 1086 | rb->lcd_getstringsize("X", &font_w, &font_h); |
1025 | 1087 | ||
1026 | bar_x_minus_50 = 0; | 1088 | bar_x_0 = LCD_WIDTH / 2; |
1027 | bar_x_minus_20 = (LCD_WIDTH / 2) - | 1089 | lbl_x_minus_50 = 0; |
1090 | lbl_x_minus_20 = (LCD_WIDTH / 2) - | ||
1028 | fixed2int(fp_mul(LCD_FACTOR, int2fixed(20))) - font_w; | 1091 | fixed2int(fp_mul(LCD_FACTOR, int2fixed(20))) - font_w; |
1029 | bar_x_0 = LCD_WIDTH / 2; | 1092 | lbl_x_0 = (LCD_WIDTH - font_w) / 2; |
1030 | bar_x_20 = (LCD_WIDTH / 2) + | 1093 | lbl_x_20 = (LCD_WIDTH / 2) + |
1031 | fixed2int(fp_mul(LCD_FACTOR, int2fixed(20))) - font_w; | 1094 | fixed2int(fp_mul(LCD_FACTOR, int2fixed(20))) - font_w; |
1032 | bar_x_50 = LCD_WIDTH - 2 * font_w; | 1095 | lbl_x_50 = LCD_WIDTH - 2 * font_w; |
1033 | 1096 | ||
1034 | letter_y = 10; | 1097 | bar_grad_y = BAR_Y - BAR_PADDING - font_h; |
1035 | note_bar_y = letter_y + font_h; | 1098 | /* Put the note right between the top and bottom text elements */ |
1036 | bar_h = font_h; | 1099 | note_y = ((font_h + bar_grad_y - note_bitmaps.slide_height) / 2); |
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 | } | 1100 | } |
1044 | 1101 | ||
1045 | 1102 | ||