Index: apps/settings.c =================================================================== RCS file: /cvsroot/rockbox/apps/settings.c,v retrieving revision 1.384 diff -u -r1.384 settings.c --- apps/settings.c 14 May 2006 23:34:24 -0000 1.384 +++ apps/settings.c 15 May 2006 22:00:54 -0000 @@ -556,6 +556,13 @@ {1, S_O(warnon_erase_dynplaylist), false, "warn when erasing dynamic playlist", off_on }, +#ifdef HAVE_UDA1380 + {4, S_O(rec_agc_preset_mic), 1, "agc mic preset", NULL}, /* 0...5 */ + {4, S_O(rec_agc_preset_line), 1, "agc line preset", NULL}, /* 0...5 */ + {8|SIGNED, S_O(rec_agc_maxgain_mic), 104, "agc maximum mic gain", NULL}, + {8|SIGNED, S_O(rec_agc_maxgain_line), 96, "agc maximum line gain", NULL}, + {3, S_O(rec_agc_cliptime), 1, "agc cliptime", "0.2s,0.4s,0.6s,0.8,1s"}, +#endif /* If values are just added to the end, no need to bump the version. */ /* new stuff to be added at the end */ Index: apps/settings.h =================================================================== RCS file: /cvsroot/rockbox/apps/settings.h,v retrieving revision 1.217 diff -u -r1.217 settings.h --- apps/settings.h 14 May 2006 23:34:24 -0000 1.217 +++ apps/settings.h 15 May 2006 22:00:55 -0000 @@ -253,6 +253,31 @@ int rec_stop_gap; /* index of trig_durations */ int rec_trigger_mode; /* see TRIG_MODE_XXX constants */ +#ifdef HAVE_UDA1380 + int rec_agc_preset_mic; /* AGC mic preset modes: + 0 = Off + 1 = Safety (clip) + 2 = Live (slow) + 3 = DJ-Set (slow) + 4 = Medium + 5 = Voice (fast) */ + int rec_agc_preset_line; /* AGC line-in preset modes: + 0 = Off + 1 = Safety (clip) + 2 = Live (slow) + 3 = DJ-Set (slow) + 4 = Medium + 5 = Voice (fast) */ + int rec_agc_maxgain_mic; /* AGC maximum mic gain */ + int rec_agc_maxgain_line; /* AGC maximum line-in gain */ + int rec_agc_cliptime; /* 0.2, 0.4, 0.6, 0.8, 1s */ +#endif + +#if defined(HAVE_LCD_BITMAP) && (LCD_WIDTH > 111) + int rec_histogram_mode; /* small lin/log /w balance, big lin/log */ + int rec_histogram_interval; /* interval: 0.5s, 1s, 2s, 4s */ +#endif + /* device settings */ int contrast; /* lcd contrast: 0-63 0=low 63=high */ Index: apps/sound_menu.c =================================================================== RCS file: /cvsroot/rockbox/apps/sound_menu.c,v retrieving revision 1.103 diff -u -r1.103 sound_menu.c --- apps/sound_menu.c 11 May 2006 22:55:24 -0000 1.103 +++ apps/sound_menu.c 15 May 2006 22:00:56 -0000 @@ -450,6 +450,42 @@ } #endif /*CONFIG_BACKLIGHT */ +#ifdef HAVE_UDA1380 +static bool agc_preset(void) +{ + static const struct opt_items names[] = { + { STR(LANG_OFF) }, + { STR(LANG_AGC_SAFETY) }, + { STR(LANG_AGC_LIVE) }, + { STR(LANG_AGC_DJSET) }, + { STR(LANG_AGC_MEDIUM) }, + { STR(LANG_AGC_VOICE) }, + }; + if (global_settings.rec_source) + return set_option(str(LANG_RECORD_AGC_PRESET), + &global_settings.rec_agc_preset_line, + INT, names, 6, NULL ); + else + return set_option(str(LANG_RECORD_AGC_PRESET), + &global_settings.rec_agc_preset_mic, + INT, names, 6, NULL ); +} + +static bool agc_cliptime(void) +{ + static const struct opt_items names[] = { + { "200ms", TALK_ID(200, UNIT_MS) }, + { "400ms", TALK_ID(400, UNIT_MS) }, + { "600ms", TALK_ID(600, UNIT_MS) }, + { "800ms", TALK_ID(800, UNIT_MS) }, + { "1s", TALK_ID(1, UNIT_SEC) } + }; + return set_option(str(LANG_RECORD_AGC_CLIPTIME), + &global_settings.rec_agc_cliptime, + INT, names, 5, NULL ); +} +#endif /* HAVE_UDA1380 */ + #endif /* HAVE_RECORDING */ static bool chanconf(void) @@ -941,6 +977,12 @@ items[i].desc = ID2P(LANG_RECORD_TRIGGER); items[i++].function = rectrigger; #endif +#ifdef HAVE_UDA1380 + items[i].desc = ID2P(LANG_RECORD_AGC_PRESET); + items[i++].function = agc_preset; + items[i].desc = ID2P(LANG_RECORD_AGC_CLIPTIME); + items[i++].function = agc_cliptime; +#endif m=menu_init( items, i, NULL, NULL, NULL, NULL); result = menu_run(m); Index: apps/lang/english.lang =================================================================== RCS file: /cvsroot/rockbox/apps/lang/english.lang,v retrieving revision 1.250 diff -u -r1.250 english.lang --- apps/lang/english.lang 27 Apr 2006 22:48:15 -0000 1.250 +++ apps/lang/english.lang 15 May 2006 22:01:01 -0000 @@ -8502,6 +8502,123 @@ + id: LANG_RECORD_AGC_PRESET + desc: automatic gain control in record settings + + *: "Automatic Gain Control" + + + *: "Automatic Gain Control" + + + *: "pixels" + + + + id: LANG_AGC_SAFETY + desc: AGC preset + + *: "Safety (clip)" + + + *: "Safety (clip)" + + + *: "pixels" + + + + id: LANG_AGC_LIVE + desc: AGC preset + + *: "Live (slow)" + + + *: "Live (slow)" + + + *: "pixels" + + + + id: LANG_AGC_DJSET + desc: AGC preset + + *: "DJ-Set (slow)" + + + *: "DJ-Set (slow)" + + + *: "pixels" + + + + id: LANG_AGC_MEDIUM + desc: AGC preset + + *: "Medium" + + + *: "Medium" + + + *: "pixels" + + + + id: LANG_AGC_VOICE + desc: AGC preset + + *: "Voice (fast)" + + + *: "Voice (fast)" + + + *: "pixels" + + + + id: LANG_RECORD_AGC_CLIPTIME + desc: in record settings + + *: "AGC clip time" + + + *: "AGC clip time" + + + *: "pixels" + + + + id: LANG_RECORDING_AGC_PRESET + desc: automatic gain control in recording screen + + *: "AGC" + + + *: "AGC" + + + *: "pixels" + + + + id: LANG_RECORDING_AGC_MAXGAIN + desc: AGC maximum gain in recording screen + + *: "AGC max. gain" + + + *: "AGC max. gain" + + + *: "pixels" + + + id: LANG_INVALID_FILENAME desc: "invalid filename entered" error message user: @@ -8514,4 +8631,4 @@ *: "Invalid Filename" - \ No newline at end of file + Index: apps/recorder/peakmeter.c =================================================================== RCS file: /cvsroot/rockbox/apps/recorder/peakmeter.c,v retrieving revision 1.34 diff -u -r1.34 peakmeter.c --- apps/recorder/peakmeter.c 3 Apr 2006 08:20:20 -0000 1.34 +++ apps/recorder/peakmeter.c 15 May 2006 22:01:04 -0000 @@ -62,6 +62,8 @@ static int pm_cur_right; static int pm_max_left; /* maximum values between peak meter draws */ static int pm_max_right; +static int pm_peakhold_left; /* max. peak values between peakhold calls */ +static int pm_peakhold_right; /* used for AGC and histogram display */ /* Clip hold */ static bool pm_clip_left = false; /* when true a clip has occurred */ @@ -139,17 +141,17 @@ /* precalculated peak values that represent magical dBfs values. Used to draw the scale */ static const int db_scale_src_values[DB_SCALE_SRC_VALUES_SIZE] = { - 32752, /* 0 db */ - 22784, /* - 3 db */ - 14256, /* - 6 db */ - 11752, /* - 9 db */ - 9256, /* -12 db */ - 4256, /* -18 db */ - 2186, /* -24 db */ - 1186, /* -30 db */ - 373, /* -40 db */ - 102, /* -50 db */ - 33, /* -60 db */ + 32736, /* 0 db */ + 22752, /* - 3 db */ + 16640, /* - 6 db */ + 11648, /* - 9 db */ + 8320, /* -12 db */ + 4364, /* -18 db */ + 2064, /* -24 db */ + 1194, /* -30 db */ + 363, /* -40 db */ + 101, /* -50 db */ + 34, /* -60 db */ 0, /* -inf */ }; @@ -160,17 +162,19 @@ * @param int sample - The input value * Make sure that 0 <= value < SAMPLE_RANGE * - * @return int - The 2 digit fixed comma result of the euation + * @return int - The 2 digit fixed point result of the euation * 20 * log (sample / SAMPLE_RANGE) + 90 - * Output range is 0-8961 (that is 0,0 - 89,6 dB). + * Output range is 0-9000 (that is 0.0 - 90.0 dB). * Normally 0dB is full scale, here it is shifted +90dB. * The calculation is based on the results of a linear * approximation tool written specifically for this problem - * by Andreas Zwirtes (radhard@gmx.de). The result hat an + * by Andreas Zwirtes (radhard@gmx.de). The result has an * accurracy of better than 2%. It is highly runtime optimized, * the cascading if-clauses do an successive approximation on * the input value. This avoids big lookup-tables and * for-loops. + * Improved by Jvo Studer for errors < 0.2dB for critical + * range of -12dB to 0dB (78.0 to 90.0dB). */ int calc_db (int isample) @@ -180,81 +184,69 @@ long m; int istart; - /* Range 1-4 */ - if (isample < 119) { + if (isample < 2308) { /* Range 1-5 */ - /* Range 1-2 */ - if (isample < 5) { + if (isample < 115) { /* Range 1-3 */ - /* Range 1 */ - if (isample < 1) { - istart = 0; - n = 0; - m = 5900; - } + if (isample < 24) { - /* Range 2 */ - else { - istart = 1; - n = 59; + if (isample < 5) { + istart = 1; /* Range 1 */ + n = 98; m = 34950; } - } - - /* Range 3-4 */ else { - - /* Range 3 */ - if (isample < 24) { - istart = 5; - n = 1457; + istart = 5; /* Range 2 */ + n = 1496; m = 7168; } + } + else { + istart = 24; /* Range 3 */ + n = 2858; + m = 1498; + } + } + else { /* Range 4-5 */ - /* Range 4 */ + if (isample < 534) { + istart = 114; /* Range 4 */ + n = 4207; + m = 319; + } else { - istart = 24; - n = 2819; - m = 1464; + istart = 588; /* Range 5 */ + n = 5583; + m = 69; } } } - /* Range 5-8 */ - else { + else { /* Range 6-9 */ - /* Range 5-6 */ - if (isample < 2918) { + if (isample < 12932) { - /* Range 5 */ - if (isample < 592) { - istart = 119; - n = 4210; - m = 295; + if (isample < 6394) { + istart = 2608; /* Range 6 */ + n = 6832; + m = 21; } - - /* Range 6 */ else { - istart = 592; - n = 5605; - m = 60; + istart = 7000; /* Range 7 */ + n = 7682; + m = 9; } } - - /* Range 7-8 */ else { - /* Range 7 */ - if (isample < 15352) { - istart = 2918; - n = 7001; - m = 12; + if (isample < 22450) { + istart = 13000; /* Range 8 */ + n = 8219; + m = 5; } - - /* Range 8 */ else { - istart = 15352; - n = 8439; + istart = 22636; /* Range 9 */ + n = 8697; m = 3; } } @@ -726,6 +718,9 @@ by peak_meter_peek since the last call of peak_meter_read_l */ int retval = pm_max_left; + /* store max peak value for peak_meter_get_peakhold_x readout */ + pm_peakhold_left = MAX(pm_max_left, pm_peakhold_left); + #ifdef PM_DEBUG peek_calls = 0; #endif @@ -747,6 +742,9 @@ by peak_meter_peek since the last call of peak_meter_read_r */ int retval = pm_max_right; + /* store max peak value for peak_meter_get_peakhold_x readout */ + pm_peakhold_right = MAX(pm_max_right, pm_peakhold_right); + #ifdef PM_DEBUG peek_calls = 0; #endif @@ -757,6 +755,21 @@ } /** + * Reads out the current peak-hold values since the last call. + * This is used by the histogram feature in the recording screen. + * Values are in the range 0 <= peak_x < MAX_PEAK. MAX_PEAK is typ 32767. + */ +extern void peak_meter_get_peakhold(int *peak_left, int *peak_right) +{ + if (peak_left) + *peak_left = pm_peakhold_left; + if (peak_right) + *peak_right = pm_peakhold_right; + pm_peakhold_left = 0; + pm_peakhold_right = 0; +} + +/** * Reset the detected clips. This method is for * use by the user interface. * @param int unused - This parameter was added to Index: apps/recorder/peakmeter.h =================================================================== RCS file: /cvsroot/rockbox/apps/recorder/peakmeter.h,v retrieving revision 1.7 diff -u -r1.7 peakmeter.h --- apps/recorder/peakmeter.h 25 Mar 2006 13:35:31 -0000 1.7 +++ apps/recorder/peakmeter.h 15 May 2006 22:01:04 -0000 @@ -34,6 +34,7 @@ extern void peak_meter_peek(void); extern void peak_meter_init_range( bool dbfs, int range_min, int range_max); extern void peak_meter_init_times(int release, int hold, int clip_hold); +extern void peak_meter_get_peakhold(int *peak_left, int *peak_right); extern void peak_meter_set_min(int newmin); extern int peak_meter_get_min(void); Index: apps/recorder/recording.c =================================================================== RCS file: /cvsroot/rockbox/apps/recorder/recording.c,v retrieving revision 1.111 diff -u -r1.111 recording.c --- apps/recorder/recording.c 14 May 2006 23:34:24 -0000 1.111 +++ apps/recorder/recording.c 15 May 2006 22:01:06 -0000 @@ -139,6 +139,10 @@ #define MAX_FILE_SIZE 0x7FF00000 /* 2 GB - 1 MB */ +#ifdef HAVE_UDA1380 +#define HAVE_AGC /* only for codecs with 0.5dB gain resolution */ +#endif + const char* const freq_str[6] = { "44.1kHz", @@ -149,6 +153,64 @@ "16kHz" }; +/* Timing counters: + * peak_time is incremented every 0.2s, every 2nd run of record screen loop. + * hist_time is incremented every 0.5s, display update. + * peak_time is the counter of the peak hold read and agc process, + * 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 +static short balance_mem[BAL_MEM_SIZE]; + +/* Automatic Gain Control */ +#ifdef HAVE_AGC +#define AGC_MODE_SIZE 5 +static char* agc_preset_str[] = +{ "Off", "S", "L", "D", "M", "V" }; +/* "Off", + "Safety (clip)", + "Live (slow)", + "DJ-Set (slow)", + "Medium", + "Voice (fast)" */ +#define AGC_CLIP 32766 +#define AGC_PEAK 29883 /* fast gain reduction threshold -0.8dB */ +#define AGC_HIGH 27254 /* accelerated gain reduction threshold -1.6dB */ +#define AGC_IMG 823 /* threshold for balance control -32dB */ +/* autogain high level thresholds (-3dB, -7dB, -4dB, -5dB, -5dB) */ +const short agc_th_hi[AGC_MODE_SIZE] = +{ 23197, 14637, 21156, 18428, 18426 }; +/* autogain low level thresholds (-14dB, -11dB, -6dB, -7dB, -8dB) */ +const short agc_th_lo[AGC_MODE_SIZE] = +{ 6538, 9235, 16422, 14636, 13045 }; +/* autogain threshold times [1/5s] or [200ms] */ +const short agc_tdrop[AGC_MODE_SIZE] = +{ 900, 225, 150, 60, 8 }; +const short agc_trise[AGC_MODE_SIZE] = +{ 9000, 750, 400, 150, 20 }; +const short agc_tbal[AGC_MODE_SIZE] = +{ 4500, 500, 300, 100, 15 }; +/* AGC operation */ +static bool agc_enable = true; +static short agc_preset; +/* AGC levels */ +static int agc_left = 0; +static int agc_right = 0; +/* AGC time since high target volume was exceeded */ +static short agc_droptime = 0; +/* AGC time since volume fallen below low target */ +static short agc_risetime = 0; +/* AGC balance time exceeding +/- 0.7dB */ +static short agc_baltime = 0; +/* AGC maximum gain */ +static short agc_maxgain; +#endif /* HAVE_AGC */ + + static void set_gain(void) { if(global_settings.rec_source == SOURCE_MIC) @@ -164,6 +226,225 @@ } } +/* Read peak meter values & calculate balance. + * Returns validity of peak values. + * Used for automatic gain control and history diagram. + */ +bool read_peak_levels(int *peak_l, int *peak_r, int *balance) +{ + peak_meter_get_peakhold(peak_l, peak_r); + peak_valid_mem[peak_time % 3] = *peak_l; + if (((peak_valid_mem[0] == peak_valid_mem[1]) && + (peak_valid_mem[1] == peak_valid_mem[2])) && + ((*peak_l < 32767) || ata_disk_is_active())) + return false; + + if (*peak_r > *peak_l) + balance_mem[peak_time % BAL_MEM_SIZE] = + MIN((10000 * *peak_r) / *peak_l - 10000, 15118); + else + balance_mem[peak_time % BAL_MEM_SIZE] = + MAX(10000 - (10000 * *peak_l) / *peak_r, -15118); + *balance = 0; + int i; + for (i = 0; i < BAL_MEM_SIZE; i++) + *balance += balance_mem[i]; + *balance = *balance / BAL_MEM_SIZE; + + return true; +} + +#ifdef HAVE_AGC +/* AGC helper function to check if maximum gain is reached */ +bool agc_gain_is_max(bool left, bool right) +{ + /* range -128...+108 [0.5dB] */ + short gain_current_l; + short gain_current_r; + + if (agc_preset == 0) + return false; + + if (global_settings.rec_source == SOURCE_LINE) + { + gain_current_l = global_settings.rec_left_gain; + gain_current_r = global_settings.rec_right_gain; + } else + { + gain_current_l = global_settings.rec_mic_gain; + gain_current_r = global_settings.rec_mic_gain; + } + + return ((left && (gain_current_l >= agc_maxgain)) || + (right && (gain_current_r >= agc_maxgain))); +} + +void change_recording_gain(bool increment, bool left, bool right) +{ + int factor = (increment ? 1 : -1); + + if (global_settings.rec_source == SOURCE_LINE) + { + if(left) global_settings.rec_left_gain += factor; + if (right) global_settings.rec_right_gain += factor; + } + else + { + global_settings.rec_mic_gain += factor; + } +} + +/* + * Handle automatic gain control (AGC). + * Change recording gain if peak_x levels are above or below + * target volume for specified timeouts. + */ +void auto_gain_control(int *peak_l, int *peak_r, int *balance) +{ + int agc_mono; + short agc_mode; + bool increment; + + if (*peak_l > agc_left) + agc_left = *peak_l; + else + agc_left -= (agc_left - *peak_l + 3) >> 2; + if (*peak_r > agc_right) + agc_right = *peak_r; + else + agc_right -= (agc_right - *peak_r + 3) >> 2; + agc_mono = (agc_left + agc_right) / 2; + + agc_mode = abs(agc_preset) - 1; + if (agc_mode < 0) { + agc_enable = false; + return; + } + + /* Automatic balance control */ + if ((agc_left > AGC_IMG) && (agc_right > AGC_IMG)) + { + if (*balance < -556) + { + if (*balance > -900) + agc_baltime -= !(peak_time % 4); /* 0.47 - 0.75dB */ + else if (*balance > -4125) + agc_baltime--; /* 0.75 - 3.00dB */ + else if (*balance > -7579) + agc_baltime -= 2; /* 3.00 - 4.90dB */ + else + agc_baltime -= !(peak_time % 8); /* 4.90 - inf dB */ + if (agc_baltime > 0) + agc_baltime -= (peak_time % 2); + } + else if (*balance > 556) + { + if (*balance < 900) + agc_baltime += !(peak_time % 4); + else if (*balance < 4125) + agc_baltime++; + else if (*balance < 7579) + agc_baltime += 2; + else + agc_baltime += !(peak_time % 8); + if (agc_baltime < 0) + agc_baltime += (peak_time % 2); + } + + if ((*balance * agc_baltime) < 0) + { + if (*balance < 0) + agc_baltime -= peak_time % 2; + else + agc_baltime += peak_time % 2; + } + + increment = ((agc_risetime / 2) > agc_droptime); + + if (agc_baltime < -agc_tbal[agc_mode]) + { + if (!increment || !agc_gain_is_max(!increment, increment)) { + change_recording_gain(increment, !increment, increment); + set_gain(); + } + agc_baltime = 0; + } + else if (agc_baltime > +agc_tbal[agc_mode]) + { + if (!increment || !agc_gain_is_max(increment, !increment)) { + change_recording_gain(increment, increment, !increment); + set_gain(); + } + agc_baltime = 0; + } + } + else if (!(hist_time % 4)) + { + if (agc_baltime < 0) + agc_baltime++; + else + agc_baltime--; + } + + /* Automatic gain control */ + if ((agc_left > agc_th_hi[agc_mode]) || (agc_right > agc_th_hi[agc_mode])) + { + if ((agc_left > AGC_CLIP) || (agc_right > AGC_CLIP)) + agc_droptime += agc_tdrop[agc_mode] / + (global_settings.rec_agc_cliptime + 1); + if (agc_left > AGC_HIGH) { + agc_droptime++; + agc_risetime=0; + if (agc_left > AGC_PEAK) + agc_droptime += 2; + } + if (agc_right > AGC_HIGH) { + agc_droptime++; + agc_risetime=0; + if (agc_right > AGC_PEAK) + agc_droptime += 2; + } + if (agc_mono > agc_th_hi[agc_mode]) + agc_droptime++; + else + agc_droptime += !(peak_time % 2); + + if (agc_droptime >= agc_tdrop[agc_mode]) + { + change_recording_gain(false, true, true); + agc_droptime = 0; + agc_risetime = 0; + set_gain(); + } + agc_risetime = MAX(agc_risetime - 1, 0); + } + else if (agc_mono < agc_th_lo[agc_mode]) + { + if (agc_mono < (agc_th_lo[agc_mode] / 8)) + agc_risetime += !(peak_time % 5); + else if (agc_mono < (agc_th_lo[agc_mode] / 2)) + agc_risetime += 2; + else + agc_risetime++; + + if (agc_risetime >= agc_trise[agc_mode]) { + if (!agc_gain_is_max(true, true)) { + change_recording_gain(true, true, true); + set_gain(); + } + agc_risetime = 0; + agc_droptime = 0; + } + agc_droptime = MAX(agc_droptime - 1, 0); + } + else if (!(peak_time % 6)) /* on target level every 1.2 sec */ + { + agc_risetime = MAX(agc_risetime - 1, 0); + agc_droptime = MAX(agc_droptime - 1, 0); + } +} +#endif /* HAVE_AGC */ + static const char* const fmtstr[] = { "%c%d %s", /* no decimals */ @@ -207,6 +488,16 @@ if(cursor < 0) cursor = 0; +#ifdef HAVE_AGC + if (global_settings.rec_source == SOURCE_MIC) + { + if(cursor == 2) + cursor = 4; + else if(cursor == 3) + cursor = 1; + } + max_cursor = 5; +#else switch(global_settings.rec_source) { case SOURCE_MIC: @@ -219,6 +510,7 @@ max_cursor = 0; break; } +#endif /* HAVE_AGC */ if(cursor > max_cursor) cursor = max_cursor; @@ -333,6 +625,11 @@ bool led_state = false; int led_countdown = 2; #endif + bool peak_read = false; + bool peak_valid = false; + int peak_l, peak_r; + int balance = 0; + int line; int i; const unsigned char *byte_units[] = { @@ -365,6 +662,7 @@ peak_meter_playback(true); #endif peak_meter_enabled = true; + peak_meter_get_peakhold(&peak_l, &peak_r); #if CONFIG_CODEC != SWCODEC if (global_settings.rec_prerecord_time) @@ -387,6 +685,25 @@ settings_apply_trigger(); +#ifdef HAVE_AGC + agc_preset_str[0] = str(LANG_OFF); + agc_preset_str[1] = str(LANG_AGC_SAFETY); + agc_preset_str[2] = str(LANG_AGC_LIVE); + agc_preset_str[3] = str(LANG_AGC_DJSET); + agc_preset_str[4] = str(LANG_AGC_MEDIUM); + agc_preset_str[5] = str(LANG_AGC_VOICE); + if (global_settings.rec_source == SOURCE_MIC) { + agc_preset = global_settings.rec_agc_preset_mic; + agc_maxgain = global_settings.rec_agc_maxgain_mic; + } + else { + agc_preset = global_settings.rec_agc_preset_line; + agc_maxgain = global_settings.rec_agc_maxgain_line; + } +#endif + + gui_syncsplash(1, true, "%s...", str(LANG_RECORDING)); + FOR_NB_SCREENS(i) { screens[i].setfont(FONT_SYSFIXED); @@ -397,6 +714,7 @@ if(rec_create_directory() > 0) have_recorded = true; + while(!done) { #if CONFIG_CODEC == SWCODEC @@ -610,6 +928,26 @@ sound_max(SOUND_RIGHT_GAIN)) global_settings.rec_right_gain++; break; +#ifdef HAVE_AGC + case 4: + agc_preset = MIN(agc_preset + 1, AGC_MODE_SIZE); + agc_enable = (agc_preset != 0); + if (global_settings.rec_source == SOURCE_MIC) { + global_settings.rec_agc_preset_mic = agc_preset; + agc_maxgain = global_settings.rec_agc_maxgain_mic; + } else { + global_settings.rec_agc_preset_line = agc_preset; + agc_maxgain = global_settings.rec_agc_maxgain_line; + } + break; + case 5: + agc_maxgain = MIN(agc_maxgain + 1, 96); + if (global_settings.rec_source == SOURCE_MIC) + global_settings.rec_agc_maxgain_mic = agc_maxgain; + else + global_settings.rec_agc_maxgain_line = agc_maxgain; + break; +#endif } set_gain(); update_countdown = 1; /* Update immediately */ @@ -656,6 +994,26 @@ sound_min(SOUND_RIGHT_GAIN)) global_settings.rec_right_gain--; break; +#ifdef HAVE_AGC + case 4: + agc_preset = MAX(agc_preset - 1, 0); + agc_enable = (agc_preset != 0); + if (global_settings.rec_source == SOURCE_MIC) { + global_settings.rec_agc_preset_mic = agc_preset; + agc_maxgain = global_settings.rec_agc_maxgain_mic; + } else { + global_settings.rec_agc_preset_line = agc_preset; + agc_maxgain = global_settings.rec_agc_maxgain_line; + } + break; + case 5: + agc_maxgain = MAX(agc_maxgain - 1, -24); + if (global_settings.rec_source == SOURCE_MIC) + global_settings.rec_agc_maxgain_mic = agc_maxgain; + else + global_settings.rec_agc_maxgain_line = agc_maxgain; + break; +#endif } set_gain(); update_countdown = 1; /* Update immediately */ @@ -689,6 +1047,16 @@ global_settings.rec_channels, global_settings.rec_editable, global_settings.rec_prerecord_time); +#ifdef HAVE_AGC + if (global_settings.rec_source == SOURCE_MIC) { + agc_preset = global_settings.rec_agc_preset_mic; + agc_maxgain = global_settings.rec_agc_maxgain_mic; + } + else { + agc_preset = global_settings.rec_agc_preset_line; + agc_maxgain = global_settings.rec_agc_maxgain_line; + } +#endif adjust_cursor(); set_gain(); @@ -766,6 +1134,18 @@ if (button != BUTTON_NONE) lastbutton = button; + peak_read = !peak_read; + if (peak_read) { /* every 2nd run of loop */ + peak_time++; + peak_valid = read_peak_levels(&peak_l, &peak_r, &balance); + } + +#ifdef HAVE_AGC + /* Handle AGC every 200ms when enabled and peak data is valid */ + if (peak_read && agc_enable && peak_valid) + auto_gain_control(&peak_l, &peak_r, &balance); +#endif + FOR_NB_SCREENS(i) screens[i].setfont(FONT_SYSFIXED); @@ -911,7 +1291,71 @@ } } - if(!global_settings.invert_cursor){ +#ifdef HAVE_AGC + /************** AGC test info ****************** + snprintf(buf, 32, "D:%d U:%d", + (agc_droptime+2)/5, (agc_risetime+2)/5); + lcd_putsxy(1, LCD_HEIGHT - 8, buf); + snprintf(buf, 32, "B:%d", + (agc_baltime+2)/5); + lcd_putsxy(LCD_WIDTH/2 + 3, LCD_HEIGHT - 8, buf); + ***********************************************/ + + if (cursor == 5) + snprintf(buf, 32, "%s: %s", + str(LANG_RECORDING_AGC_MAXGAIN), + fmt_gain(SOUND_LEFT_GAIN, + agc_maxgain, buf2, sizeof(buf2))); + else if (agc_preset == 0) + snprintf(buf, 32, "%s: %s", + str(LANG_RECORDING_AGC_PRESET), + agc_preset_str[agc_preset]); + else if (global_settings.rec_source == SOURCE_MIC) + snprintf(buf, 32, "%s: %s%s", + str(LANG_RECORDING_AGC_PRESET), + agc_preset_str[agc_preset], + fmt_gain(SOUND_LEFT_GAIN, + agc_maxgain - + global_settings.rec_mic_gain, + buf2, sizeof(buf2))); + else + snprintf(buf, 32, "%s: %s%s", + str(LANG_RECORDING_AGC_PRESET), + agc_preset_str[agc_preset], + fmt_gain(SOUND_LEFT_GAIN, + agc_maxgain - + (global_settings.rec_left_gain + + global_settings.rec_right_gain)/2, + buf2, sizeof(buf2))); + line = (global_settings.rec_source == SOURCE_MIC)? 6 : 7; + if(global_settings.invert_cursor && ((cursor==4) || (cursor==5))) + { + screens[0].puts_style_offset(0, line, buf, STYLE_INVERT,0); +#ifdef HAVE_REMOTE_LCD + screens[1].puts_style_offset(0, 6, buf, STYLE_INVERT,0); +#endif + } + else + { + FOR_NB_SCREENS(i) + screens[i].puts(0, line, buf); + } + + if (global_settings.rec_source == SOURCE_MIC) + { + if(agc_maxgain < (global_settings.rec_mic_gain)) + change_recording_gain(false, true, true); + } + else + { + if(agc_maxgain < (global_settings.rec_left_gain)) + change_recording_gain(false, true, false); + if(agc_maxgain < (global_settings.rec_right_gain)) + change_recording_gain(false, false, true); + } +#endif /* HAVE_AGC */ + + if(!global_settings.invert_cursor) { switch(cursor) { case 1: @@ -936,6 +1380,15 @@ screen_put_cursorxy(&screens[i], 0, 4+PM_HEIGHT, true); break; +#ifdef HAVE_AGC + case 4: + case 5: + screen_put_cursorxy(&screens[0], 0, line, true); +#ifdef HAVE_REMOTE_LCD + screen_put_cursorxy(&screens[1], 0, 6, true); +#endif + break; +#endif /* HAVE_AGC */ default: FOR_NB_SCREENS(i) screen_put_cursorxy(&screens[i], 0, @@ -957,11 +1410,15 @@ freq_str[global_settings.rec_frequency], global_settings.rec_channels ? str(LANG_CHANNEL_MONO) : str(LANG_CHANNEL_STEREO)); + + line = (global_settings.rec_source == SOURCE_MIC)? 5 : 6; FOR_NB_SCREENS(i) - screens[i].puts(0, 5+PM_HEIGHT, buf); + screens[i].puts(0, line+PM_HEIGHT, buf); gui_syncstatusbar_draw(&statusbars, true); + hist_time++; + FOR_NB_SCREENS(i) { peak_meter_screen(&screens[i], 0, 8 + h*2, h*PM_HEIGHT);