summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlexander Levin <al.le@rockbox.org>2009-09-12 21:19:17 +0000
committerAlexander Levin <al.le@rockbox.org>2009-09-12 21:19:17 +0000
commitc38f6072cc43f7abcba5b3476f1496dcf822be99 (patch)
treec91d5b6f4cb2205b0c895383c5303e9436a55a34
parent589e1f0072d2d9c3155287b2fe5484799ef28d11 (diff)
downloadrockbox-c38f6072cc43f7abcba5b3476f1496dcf822be99.tar.gz
rockbox-c38f6072cc43f7abcba5b3476f1496dcf822be99.zip
UI improvements for the pitch detector (ongoing work by David Johnston from FS#8768)
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@22685 a1c6a512-1295-4272-9138-f99709370657
-rw-r--r--apps/plugins/bitmaps/native/SOURCES21
-rw-r--r--apps/plugins/bitmaps/native/pitch_notes.128x128x16.bmpbin0 -> 64856 bytes
-rw-r--r--apps/plugins/bitmaps/native/pitch_notes.128x96x2.bmpbin0 -> 14472 bytes
-rw-r--r--apps/plugins/bitmaps/native/pitch_notes.132x80x16.bmpbin0 -> 26552 bytes
-rw-r--r--apps/plugins/bitmaps/native/pitch_notes.160x128x16.bmpbin0 -> 64856 bytes
-rw-r--r--apps/plugins/bitmaps/native/pitch_notes.160x128x2.bmpbin0 -> 21672 bytes
-rw-r--r--apps/plugins/bitmaps/native/pitch_notes.176x132x16.bmpbin0 -> 64856 bytes
-rw-r--r--apps/plugins/bitmaps/native/pitch_notes.176x220x16.bmpbin0 -> 204176 bytes
-rw-r--r--apps/plugins/bitmaps/native/pitch_notes.220x176x16.bmpbin0 -> 121016 bytes
-rw-r--r--apps/plugins/bitmaps/native/pitch_notes.320x240x16.bmpbin0 -> 248456 bytes
-rw-r--r--apps/plugins/pitch_detector.c567
11 files changed, 333 insertions, 255 deletions
diff --git a/apps/plugins/bitmaps/native/SOURCES b/apps/plugins/bitmaps/native/SOURCES
index 4d4cee074a..9f0897d48a 100644
--- a/apps/plugins/bitmaps/native/SOURCES
+++ b/apps/plugins/bitmaps/native/SOURCES
@@ -826,5 +826,26 @@ rockboxlogo.91x32x1.bmp
826#endif 826#endif
827#endif 827#endif
828 828
829/* Pitch detector */
830#if (LCD_WIDTH >= 320) && (LCD_HEIGHT >= 240) && (LCD_DEPTH >= 16)
831pitch_notes.320x240x16.bmp
832#elif (LCD_WIDTH >= 220) && (LCD_HEIGHT >= 176) && (LCD_DEPTH >= 16)
833pitch_notes.220x176x16.bmp
834#elif (LCD_WIDTH >= 176) && (LCD_HEIGHT >= 132) && (LCD_DEPTH >= 16)
835pitch_notes.176x132x16.bmp
836#elif (LCD_WIDTH >= 160) && (LCD_HEIGHT >= 128) && (LCD_DEPTH >= 16)
837pitch_notes.160x128x16.bmp
838#elif (LCD_WIDTH >= 128) && (LCD_HEIGHT >= 128) && (LCD_DEPTH >= 16)
839pitch_notes.128x128x16.bmp
840#elif (LCD_WIDTH >= 160) && (LCD_HEIGHT >= 128) && (LCD_DEPTH >= 2)
841pitch_notes.160x128x2.bmp
842#elif (LCD_WIDTH >= 138) && (LCD_HEIGHT >= 110) && (LCD_DEPTH >= 2)
843pitch_notes.160x128x2.bmp
844#elif (LCD_WIDTH >= 132) && (LCD_HEIGHT >= 80) && (LCD_DEPTH >= 16)
845pitch_notes.132x80x16.bmp
846#elif (LCD_WIDTH >= 112) && (LCD_HEIGHT >= 64) && (LCD_DEPTH >= 1)
847/* There isn't a target currently with this screen and recording ability */
848#endif
849
829 850
830#endif /* HAVE_LCD_BITMAP */ 851#endif /* HAVE_LCD_BITMAP */
diff --git a/apps/plugins/bitmaps/native/pitch_notes.128x128x16.bmp b/apps/plugins/bitmaps/native/pitch_notes.128x128x16.bmp
new file mode 100644
index 0000000000..af59728cb7
--- /dev/null
+++ b/apps/plugins/bitmaps/native/pitch_notes.128x128x16.bmp
Binary files differ
diff --git a/apps/plugins/bitmaps/native/pitch_notes.128x96x2.bmp b/apps/plugins/bitmaps/native/pitch_notes.128x96x2.bmp
new file mode 100644
index 0000000000..da5845610c
--- /dev/null
+++ b/apps/plugins/bitmaps/native/pitch_notes.128x96x2.bmp
Binary files differ
diff --git a/apps/plugins/bitmaps/native/pitch_notes.132x80x16.bmp b/apps/plugins/bitmaps/native/pitch_notes.132x80x16.bmp
new file mode 100644
index 0000000000..d77d25efe2
--- /dev/null
+++ b/apps/plugins/bitmaps/native/pitch_notes.132x80x16.bmp
Binary files differ
diff --git a/apps/plugins/bitmaps/native/pitch_notes.160x128x16.bmp b/apps/plugins/bitmaps/native/pitch_notes.160x128x16.bmp
new file mode 100644
index 0000000000..af59728cb7
--- /dev/null
+++ b/apps/plugins/bitmaps/native/pitch_notes.160x128x16.bmp
Binary files differ
diff --git a/apps/plugins/bitmaps/native/pitch_notes.160x128x2.bmp b/apps/plugins/bitmaps/native/pitch_notes.160x128x2.bmp
new file mode 100644
index 0000000000..ee400902fa
--- /dev/null
+++ b/apps/plugins/bitmaps/native/pitch_notes.160x128x2.bmp
Binary files differ
diff --git a/apps/plugins/bitmaps/native/pitch_notes.176x132x16.bmp b/apps/plugins/bitmaps/native/pitch_notes.176x132x16.bmp
new file mode 100644
index 0000000000..af59728cb7
--- /dev/null
+++ b/apps/plugins/bitmaps/native/pitch_notes.176x132x16.bmp
Binary files differ
diff --git a/apps/plugins/bitmaps/native/pitch_notes.176x220x16.bmp b/apps/plugins/bitmaps/native/pitch_notes.176x220x16.bmp
new file mode 100644
index 0000000000..a95ef0d6cb
--- /dev/null
+++ b/apps/plugins/bitmaps/native/pitch_notes.176x220x16.bmp
Binary files differ
diff --git a/apps/plugins/bitmaps/native/pitch_notes.220x176x16.bmp b/apps/plugins/bitmaps/native/pitch_notes.220x176x16.bmp
new file mode 100644
index 0000000000..087235380b
--- /dev/null
+++ b/apps/plugins/bitmaps/native/pitch_notes.220x176x16.bmp
Binary files differ
diff --git a/apps/plugins/bitmaps/native/pitch_notes.320x240x16.bmp b/apps/plugins/bitmaps/native/pitch_notes.320x240x16.bmp
new file mode 100644
index 0000000000..5454a67490
--- /dev/null
+++ b/apps/plugins/bitmaps/native/pitch_notes.320x240x16.bmp
Binary files differ
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
72PLUGIN_HEADER 73PLUGIN_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 */
163const 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 */
191int note_y;
192int 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
215const 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
171typedef signed short audio_sample_type; 224typedef 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 */
173audio_sample_type audio_data[BUFFER_SIZE]; 226audio_sample_type audio_data[BUFFER_SIZE];
@@ -209,21 +262,12 @@ static const fixed lfreqs[12] =
209 262
210/* GUI */ 263/* GUI */
211static unsigned back_color, front_color; 264static unsigned back_color, front_color;
212static int font_w, font_h; 265static int font_w,font_h;
213static int bar_x_minus_50, bar_x_minus_20, bar_x_0, bar_x_20, bar_x_50; 266static int bar_x_0;
214static int letter_y; /* y of the notes letters */ 267static int lbl_x_minus_50, lbl_x_minus_20, lbl_x_0, lbl_x_20, lbl_x_50;
215static int note_bar_y; /* y of the yellow bars (under the notes) */ 268
216static int bar_h; /* height of the yellow (note) and red (error) bars */ 269static const char *english_notes[] = {"A","A#","B","C","C#","D","D#","E",
217static int freq_y; /* y of the line with the frequency */ 270 "F","F#","G","G#"};
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; 271static 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
292enum message
293{
294 MESSAGE_LOADING,
295 MESSAGE_LOADED,
296 MESSAGE_ERRLOAD,
297 MESSAGE_SAVING,
298 MESSAGE_SAVED,
299 MESSAGE_ERRSAVE
300};
301
302enum 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 */
310struct tuner_settings hdd_tuner_settings;
311
312/*---------------------------------------------------------------------*/
313
314bool settings_needs_saving(struct tuner_settings* settings)
315{
316 return(rb->memcmp(settings, &hdd_tuner_settings, sizeof(*settings)));
317}
318
319/*---------------------------------------------------------------------*/
320
321void 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
334void tuner_settings_reset_query(int yes)
335{
336 if(yes)
337 tuner_settings_reset(&tuner_settings);
338}
339
340/*---------------------------------------------------------------------*/
341
342enum 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
362enum 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
376void load_settings(void)
377{
378 tuner_settings_load(&tuner_settings, SETTINGS_FILENAME);
379
380 rb->storage_sleep();
381}
382
383/*---------------------------------------------------------------------*/
384
385void 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
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 */ 408/* Option strings */
409
410/* This has to match yin_threshold_table */
276static const struct opt_items yin_threshold_text[] = 411static 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
429static const struct opt_items accidental_text[] =
430{
431 { "Flat", -1 },
432 { "Sharp", -1 },
433};
434
435static const struct opt_items noyes_text[] =
436{
437 { "No", -1 },
438 { "Yes", -1 }
439};
440
294void set_min_freq(int new_freq) 441void 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
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 */ 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 */
532void print_str(char* s) 599void 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 */
618void 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 &note_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], &note_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 */
552void draw_bar(fixed wrong_by_cents) 647void 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 */
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} 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