From be90f74e89978224a169c793d4e688867dc38b41 Mon Sep 17 00:00:00 2001 From: Peter D'Hoye Date: Wed, 3 Mar 2010 22:16:08 +0000 Subject: Histogram display on recording screen. Based on the work of Jvo Studer in FS #5021 but reduced and reworked since the recording screen code changed quite a bit since his patch. For now enabled on iriver h1x0 and h3x0 only. git-svn-id: svn://svn.rockbox.org/rockbox/trunk@25007 a1c6a512-1295-4272-9138-f99709370657 --- apps/features.txt | 4 + apps/lang/english.lang | 35 +++++++ apps/menus/recording_menu.c | 25 ++++- apps/recorder/peakmeter.c | 36 ++++--- apps/recorder/recording.c | 238 +++++++++++++++++++++++++++++++++++--------- apps/settings.h | 3 + apps/settings_list.c | 7 ++ 7 files changed, 282 insertions(+), 66 deletions(-) (limited to 'apps') diff --git a/apps/features.txt b/apps/features.txt index f94a74cd19..242d2d4386 100644 --- a/apps/features.txt +++ b/apps/features.txt @@ -148,6 +148,10 @@ recording_mic #endif #endif +#if defined(HAVE_RECORDING_HISTOGRAM) +recording_histogram +#endif + #if defined(HAVE_REMOTE_LCD) remote remote_lcd_invert diff --git a/apps/lang/english.lang b/apps/lang/english.lang index ecbd41505d..45c82fe378 100644 --- a/apps/lang/english.lang +++ b/apps/lang/english.lang @@ -13349,3 +13349,38 @@ remote: "Remote Screen" + + id: LANG_RECORDING_HISTOGRAM_MODE + desc: in record settings menu + user: core + + *: none + recording_histogram: "Histogram mode" + + + *: none + recording_histogram: "Histogram mode" + + + *: none + recording_histogram: "Histogram mode" + + + + id: LANG_RECORDING_HISTOGRAM_INTERVAL + desc: in record settings menu + user: core + + *: none + recording_histogram: "Histogram interval" + + + *: none + recording_histogram: "Histogram interval" + + + *: none + recording_histogram: "Histogram interval" + + + diff --git a/apps/menus/recording_menu.c b/apps/menus/recording_menu.c index 4bbc4b163c..b5bbecb9aa 100644 --- a/apps/menus/recording_menu.c +++ b/apps/menus/recording_menu.c @@ -350,7 +350,7 @@ static int clear_rec_directory(void) } MENUITEM_FUNCTION(clear_rec_directory_item, 0, ID2P(LANG_CLEAR_REC_DIR), clear_rec_directory, NULL, NULL, Icon_Folder); - + MENUITEM_SETTING(cliplight, &global_settings.cliplight, NULL); #ifdef HAVE_AGC @@ -393,6 +393,26 @@ MENUITEM_FUNCTION(agc_cliptime, 0, ID2P(LANG_RECORDING_AGC_CLIPTIME), agc_cliptime_func, NULL, NULL, Icon_Menu_setting); #endif /* HAVE_AGC */ +#if defined(HAVE_RECORDING_HISTOGRAM) +static bool history_interval(void) +{ + static const struct opt_items names[] = { + { "0s", TALK_ID(0, UNIT_SEC) }, + { "1s", TALK_ID(1, UNIT_SEC) }, + { "2s", TALK_ID(2, UNIT_SEC) }, + { "4s", TALK_ID(5, UNIT_SEC) } + }; + return set_option(str(LANG_RECORDING_HISTOGRAM_INTERVAL), + &global_settings.rec_histogram_interval, + INT, names, 4, NULL ); +} + +MENUITEM_FUNCTION(recording_histogram, 0, + ID2P(LANG_RECORDING_HISTOGRAM_INTERVAL), + history_interval, NULL, NULL, Icon_Menu_setting); + +#endif + /** Rec trigger **/ enum trigger_menu_option { @@ -647,6 +667,9 @@ MAKE_MENU(recording_settings_menu, ID2P(LANG_RECORDING_SETTINGS), #ifdef HAVE_AGC &agc_preset, &agc_cliptime, #endif +#if defined(HAVE_RECORDING_HISTOGRAM) + &recording_histogram, +#endif #ifdef HAVE_LCD_BITMAP &peak_meter_menu, #endif diff --git a/apps/recorder/peakmeter.c b/apps/recorder/peakmeter.c index fc68ce4267..8f32a837a7 100644 --- a/apps/recorder/peakmeter.c +++ b/apps/recorder/peakmeter.c @@ -66,7 +66,7 @@ static int pm_cur_left; /* current values (last peak_meter_peek) */ static int pm_cur_right; static int pm_max_left; /* maximum values between peak meter draws */ static int pm_max_right; -#ifdef HAVE_AGC +#if defined(HAVE_AGC) || defined(HAVE_RECORDING_HISTOGRAM) static int pm_peakhold_left; /* max. peak values between peakhold calls */ static int pm_peakhold_right; /* used for AGC and histogram display */ #endif @@ -799,9 +799,16 @@ static int peak_meter_read_l(void) { /* pm_max_left contains the maximum of all peak values that were read by peak_meter_peek since the last call of peak_meter_read_l */ - int retval = pm_max_left; + int retval; + +#if defined(SIMULATOR) && (CONFIG_CODEC != SWCODEC) + srand(current_tick); + pm_max_left = rand()%MAX_PEAK; +#endif -#ifdef HAVE_AGC + retval = pm_max_left; + +#if defined(HAVE_RECORDING_HISTOGRAM) || defined(HAVE_AGC) /* store max peak value for peak_meter_get_peakhold_x readout */ pm_peakhold_left = MAX(pm_max_left, pm_peakhold_left); #endif @@ -812,11 +819,6 @@ static int peak_meter_read_l(void) get fooled by an old maximum value */ pm_max_left = pm_cur_left; -#if defined(SIMULATOR) && (CONFIG_CODEC != SWCODEC) - srand(current_tick); - retval = rand()%MAX_PEAK; -#endif - return retval; } @@ -830,9 +832,16 @@ static int peak_meter_read_r(void) { /* peak_meter_r contains the maximum of all peak values that were read by peak_meter_peek since the last call of peak_meter_read_r */ - int retval = pm_max_right; + int retval; + +#if defined(SIMULATOR) && (CONFIG_CODEC != SWCODEC) + srand(current_tick); + pm_max_right = rand()%MAX_PEAK; +#endif -#ifdef HAVE_AGC + retval = pm_max_right; + +#if defined(HAVE_RECORDING_HISTOGRAM) || defined(HAVE_AGC) /* store max peak value for peak_meter_get_peakhold_x readout */ pm_peakhold_right = MAX(pm_max_right, pm_peakhold_right); #endif @@ -843,15 +852,10 @@ static int peak_meter_read_r(void) get fooled by an old maximum value */ pm_max_right = pm_cur_right; -#if defined(SIMULATOR) && (CONFIG_CODEC != SWCODEC) - srand(current_tick); - retval = rand()%MAX_PEAK; -#endif - return retval; } -#ifdef HAVE_AGC +#if defined(HAVE_AGC) || defined(HAVE_RECORDING_HISTOGRAM) /** * Reads out the current peak-hold values since the last call. * This is used by the histogram feature in the recording screen. diff --git a/apps/recorder/recording.c b/apps/recorder/recording.c index 8b52b7cac2..2c8e473c11 100644 --- a/apps/recorder/recording.c +++ b/apps/recorder/recording.c @@ -219,7 +219,6 @@ static char path_buffer[MAX_PATH]; * overflow every 13 years 8-) */ static long peak_time = 0; -static long hist_time = 0; static short peak_valid_mem[4]; #define BAL_MEM_SIZE 24 @@ -269,6 +268,38 @@ static short agc_baltime = 0; /* AGC maximum gain */ static short agc_maxgain; #endif /* HAVE_AGC */ +#if defined(HAVE_AGC) || defined(HAVE_RECORDING_HISTOGRAM) +static long hist_time = 0; +#endif /* HAVE_AGC or HAVE_RECORDING_HISTOGRAM */ +/* Histogram data */ +/* TO DO: move some of this stuff inside the recording function? */ +#ifdef HAVE_RECORDING_HISTOGRAM +static int hist_l = 0; +static int hist_r = 0; +#define HIST_Y (hist_pos_y+hist_size_h-1) +#define HIST_W (LCD_WIDTH / 2 - 4) +#if LCD_DEPTH > 1 +#ifdef HAVE_LCD_COLOR +#define LCD_BAL_L LCD_RGBPACK(0, 0, 255) +#define LCD_BAL_R LCD_RGBPACK(204, 0, 0) +#define LCD_HIST_OVER LCD_RGBPACK(204, 0, 0) +#define LCD_HIST_HI LCD_RGBPACK(255, 204, 0) +#define LCD_HIST_OK LCD_RGBPACK(51, 153, 0) +#else /* HAVE_LCD_COLOR */ +#define LCD_BATT_OK LCD_BLACK +#define LCD_BATT_LO LCD_DARKGRAY +#define LCD_DISK_OK LCD_BLACK +#define LCD_DISK_LO LCD_DARKGRAY +#define LCD_HIST_OVER LCD_BLACK +#define LCD_HIST_OK LCD_DARKGRAY +#define LCD_BAL LCD_DARKGRAY +#endif /* HAVE_LCD_COLOR */ +#else /* LCD_DEPTH > 1 */ +#define LCD_HIST_OVER LCD_DEFAULT_FG +#define LCD_HIST_OK LCD_DEFAULT_FG +#define LCD_BAL LCD_DEFAULT_FG +#endif /* LCD_DEPTH > 1 */ +#endif /* HAVE_RECORDING_HISTOGRAM */ static void set_gain(void) { @@ -317,6 +348,13 @@ static bool read_peak_levels(int *peak_l, int *peak_r, int *balance) *balance += balance_mem[i]; *balance = *balance / BAL_MEM_SIZE; +#ifdef HAVE_RECORDING_HISTOGRAM + if (*peak_l > hist_l) + hist_l = *peak_l; + if (*peak_r > hist_r) + hist_r = *peak_r; +#endif + return true; } @@ -1015,7 +1053,6 @@ bool recording_screen(bool no_source) #endif #ifdef HAVE_AGC bool peak_read = false; - bool peak_valid = false; int peak_l, peak_r; int balance = 0; #endif @@ -1025,9 +1062,21 @@ bool recording_screen(bool no_source) int pm_h[NB_SCREENS]; /* peakmeter height */ int trig_ypos[NB_SCREENS]; /* trigger bar y pos */ int trig_width[NB_SCREENS]; /* trigger bar width */ + int top_height_req[NB_SCREENS]; /* required height for top half */ bool compact_view[NB_SCREENS]; /* tweak layout tiny screens / big fonts */ - struct gui_synclist lists; /* the list in the bottom vp */ +#if defined(HAVE_AGC) || defined(HAVE_RECORDING_HISTOGRAM) + bool peak_valid = false; +#endif +#if defined(HAVE_RECORDING_HISTOGRAM) + unsigned short hist_pos_y = 0; + unsigned short hist_size_h = 0; + int history_pos = 0; + short hist_time_interval = 1; /* 1, 2, 4, 8 */ + unsigned char history_l[HIST_W]; + unsigned char history_r[HIST_W]; + const char hist_level_marks[6] = { 29, 26, 23, 17, 9, 2}; +#endif #ifdef HAVE_FMRADIO_REC int prev_rec_source = global_settings.rec_source; /* detect source change */ #endif @@ -1084,49 +1133,6 @@ bool recording_screen(bool no_source) rec_init_filename(); #endif - /* viewport init and calculations that only needs to be done once */ - FOR_NB_SCREENS(i) - { - struct viewport *v; - /* top vp, 4 lines, force sys font if total screen < 6 lines - NOTE: one could limit the list to 1 line and get away with 5 lines */ - v = &vp_top[i]; - viewport_set_defaults(v, i); - if (viewport_get_nb_lines(v) < 4) - { - /* compact needs 4 lines total */ - v->font = FONT_SYSFIXED; - compact_view[i] = false; - } - else - { - if (viewport_get_nb_lines(v) < (4+2)) /*top=4,list=2*/ - compact_view[i] = true; - else - compact_view[i] = false; - } - vp_list[i] = *v; /* get a copy now so it can be sized more easily */ - v->height = (font_get(v->font)->height)*(compact_view[i] ? 3 : 4); - - /* list section, rest of the screen */ - vp_list[i].y += vp_top[i].height; - vp_list[i].height -= vp_top[i].height; - screens[i].set_viewport(&vp_top[i]); /* req for next calls */ - - screens[i].getstringsize("W", &w, &h); - pm_y[i] = font_get(vp_top[i].font)->height * 2; - trig_ypos[i] = font_get(vp_top[i].font)->height * 3; - if(compact_view[i]) - trig_ypos[i] -= (font_get(vp_top[i].font)->height)/2; - } - - /* init the bottom list */ - gui_synclist_init(&lists, reclist_get_name, NULL, false, 1, vp_list); - gui_synclist_set_title(&lists, NULL, Icon_NOICON); - - - send_event(GUI_EVENT_ACTIONUPDATE, (void*)1); /* force a redraw */ - /* start of the loop: we stay in this loop until user quits recscreen */ while(done <= 0) { @@ -1143,6 +1149,65 @@ bool recording_screen(bool no_source) prev_rec_source = global_settings.rec_source; #endif + /* viewport init and calculations that only needs to be done once */ + FOR_NB_SCREENS(i) + { + struct viewport *v; + /* top vp, 4 lines, force sys font if total screen < 6 lines + NOTE: one could limit the list to 1 line and get away with 5 lines */ + top_height_req[i] = 4; +#if defined(HAVE_RECORDING_HISTOGRAM) + if((global_settings.rec_histogram_interval) && (!i)) + top_height_req[i] += 1; /* use one line for histogram */ + hist_time_interval = 1 << global_settings.rec_histogram_interval; +#endif + v = &vp_top[i]; + viewport_set_defaults(v, i); + if (viewport_get_nb_lines(v) < top_height_req[i]) + { + /* compact needs 4 lines total */ + v->font = FONT_SYSFIXED; + compact_view[i] = false; + } + else + { + /*top=4,list=2*/ + if (viewport_get_nb_lines(v) < (top_height_req[i]+2)) + compact_view[i] = true; + else + compact_view[i] = false; + } + vp_list[i] = *v; /* get a copy now so it can be sized more easily */ + v->height = (font_get(v->font)->height)*(compact_view[i] ? 3 : + top_height_req[i]); + + /* list section, rest of the screen */ + vp_list[i].y += vp_top[i].height; + vp_list[i].height -= vp_top[i].height; + screens[i].set_viewport(&vp_top[i]); /* req for next calls */ + + screens[i].getstringsize("W", &w, &h); + pm_y[i] = font_get(vp_top[i].font)->height * 2; + trig_ypos[i] = font_get(vp_top[i].font)->height * 3; + if(compact_view[i]) + trig_ypos[i] -= (font_get(vp_top[i].font)->height)/2; + } + + /* init the bottom list */ + gui_synclist_init(&lists, reclist_get_name, NULL, false, 1, vp_list); + gui_synclist_set_title(&lists, NULL, Icon_NOICON); + + send_event(GUI_EVENT_ACTIONUPDATE, (void*)1); /* force a redraw */ + +#if defined(HAVE_RECORDING_HISTOGRAM) + history_pos = 0; + hist_pos_y = (compact_view[0] ? 3 : 4) * (font_get(vp_top[0].font)->height) + + 1; + hist_size_h = font_get(vp_top[0].font)->height - 2; + memset(history_l, 0, sizeof(unsigned char)*HIST_W); + memset(history_r, 0, sizeof(unsigned char)*HIST_W); +#endif + FOR_NB_SCREENS(i) { pm_x[i] = 0; @@ -1673,12 +1738,11 @@ bool recording_screen(bool no_source) unsigned int dseconds, dhours, dminutes; unsigned long num_recorded_bytes, dsize, dmb; - FOR_NB_SCREENS(i) { screens[i].set_viewport(&vp_top[i]); screens[i].clear_viewport(); - } + } update_countdown = 5; last_seconds = seconds; @@ -1796,6 +1860,83 @@ bool recording_screen(bool no_source) } } +#ifdef HAVE_RECORDING_HISTOGRAM + if(global_settings.rec_histogram_interval) + { + if (peak_valid && !(hist_time % hist_time_interval) && hist_l) + { + history_l[history_pos] = hist_l * hist_size_h / 32767; + history_r[history_pos] = hist_r * hist_size_h / 32767; + history_pos = (history_pos + 1) % HIST_W; + history_l[history_pos] = history_r[history_pos] = 0; + history_l[(history_pos + 1) % HIST_W] = 0; + history_r[(history_pos + 1) % HIST_W] = 0; + hist_l = 0; + hist_r = 0; + } + lcd_set_drawmode(DRMODE_SOLID); + lcd_drawrect(0, hist_pos_y - 1, + HIST_W + 2, hist_size_h + 1); + lcd_drawrect(HIST_W + 6, hist_pos_y - 1, + HIST_W + 2, hist_size_h + 1); + lcd_set_drawmode(DRMODE_FG); +#ifdef HAVE_LCD_COLOR + for (i = 0; i < HIST_W; i++) + { + if (history_l[i]) + { + if (history_l[i] == hist_size_h) + lcd_set_foreground(LCD_HIST_OVER); + else if (history_l[i] > hist_level_marks[1]) + lcd_set_foreground(LCD_HIST_HI); + else + lcd_set_foreground(LCD_HIST_OK); + lcd_vline(1 + i, HIST_Y-1, HIST_Y - history_l[i]); + } + if (history_r[i]) + { + if (history_r[i] == hist_size_h) + lcd_set_foreground(LCD_HIST_OVER); + else if (history_r[i] > hist_level_marks[1]) + lcd_set_foreground(LCD_HIST_HI); + else + lcd_set_foreground(LCD_HIST_OK); + lcd_vline(HIST_W+7 + i, HIST_Y-1, HIST_Y - history_r[i]); + } + } +#else + for (i = 0; i < HIST_W; i++) + { + if (history_l[i]) + { + if (history_l[i] == hist_size_h) + lcd_set_foreground(LCD_HIST_OVER); + else + lcd_set_foreground(LCD_HIST_OK); + lcd_vline(1 + i, HIST_Y-1, HIST_Y - history_l[i]); + } + if (history_r[i]) + { + if (history_r[i] == hist_size_h) + lcd_set_foreground(LCD_HIST_OVER); + else + lcd_set_foreground(LCD_HIST_OK); + lcd_vline(HIST_W+7 + i, HIST_Y-1, HIST_Y - history_r[i]); + } + } +#endif /* HAVE_LCD_COLOR */ + lcd_set_foreground( +#ifdef HAVE_LCD_COLOR + global_settings.fg_color); +#else + LCD_DEFAULT_FG); +#endif + for (i = 0; i < 6; i++) + lcd_hline(HIST_W + 3, HIST_W + 4, + HIST_Y - hist_level_marks[i]); + } +#endif /* HAVE_RECORDING_HISTOGRAM */ + #ifdef HAVE_AGC hist_time++; #endif @@ -1922,7 +2063,6 @@ rec_abort: FOR_NB_SCREENS(i) screens[i].setfont(FONT_UI); - /* if the directory was created or recording happened, make sure the browser is updated */ diff --git a/apps/settings.h b/apps/settings.h index bba33366ae..1186f47fb0 100644 --- a/apps/settings.h +++ b/apps/settings.h @@ -469,6 +469,9 @@ struct user_settings int rec_stop_gap; /* index of trig_durations */ int rec_trigger_mode; /* see TRIG_MODE_XXX constants */ int rec_trigger_type; /* what to do when trigger released */ +#ifdef HAVE_RECORDING_HISTOGRAM + int rec_histogram_interval; /* recording peakmeter histogram */ +#endif #ifdef HAVE_AGC int rec_agc_preset_mic; /* AGC mic preset modes: diff --git a/apps/settings_list.c b/apps/settings_list.c index ade159b85c..0a7c3c5b99 100644 --- a/apps/settings_list.c +++ b/apps/settings_list.c @@ -1135,6 +1135,13 @@ const struct settings_list settings[] = { CHOICE_SETTING(F_RECSETTING, rec_trigger_type, LANG_RECORD_TRIGGER_TYPE, TRIG_TYPE_STOP, "trigger type","stop,pause,nf stp", NULL ,3, ID2P(LANG_RECORD_TRIGGER_STOP), ID2P(LANG_PAUSE), ID2P(LANG_RECORD_TRIGGER_NEWFILESTP)), +#ifdef HAVE_RECORDING_HISTOGRAM + /* TO DO: additional restictions of following REP items? */ + TABLE_SETTING(F_RECSETTING, rec_histogram_interval, LANG_RECORDING_HISTOGRAM_INTERVAL, 0, + "histogram interval","0s,1s,2s,4s", + UNIT_SEC, NULL, NULL, NULL, 4, 0,1,2,4), +#endif /* HAVE_RECORDING_HISTOGRAM */ + #endif /* HAVE_RECORDING */ #ifdef HAVE_SPDIF_POWER -- cgit v1.2.3