From 68482bbed286981a5ef09a466620e459790c96e6 Mon Sep 17 00:00:00 2001 From: Linus Nielsen Feltzing Date: Mon, 4 Apr 2005 09:12:12 +0000 Subject: Patch #868645 by Philipp Pertermann, volume triggered recording for the Archos recording devices git-svn-id: svn://svn.rockbox.org/rockbox/trunk@6243 a1c6a512-1295-4272-9138-f99709370657 --- apps/lang/english.lang | 60 ++++++++ apps/recorder/peakmeter.c | 374 ++++++++++++++++++++++++++++++++++++++++----- apps/recorder/peakmeter.h | 34 ++++- apps/recorder/recording.c | 257 ++++++++++++++++++++++++------- apps/settings.c | 43 ++++++ apps/settings.h | 14 ++ apps/sound_menu.c | 375 +++++++++++++++++++++++++++++++++++++++++++++- apps/sound_menu.h | 1 + firmware/drivers/ata.c | 44 ++++-- firmware/export/ata.h | 1 + 10 files changed, 1097 insertions(+), 106 deletions(-) diff --git a/apps/lang/english.lang b/apps/lang/english.lang index a9f3dac6e4..f327595b11 100644 --- a/apps/lang/english.lang +++ b/apps/lang/english.lang @@ -3009,3 +3009,63 @@ desc: Announce that the RTC alarm has been turned off eng: "Alarm Disabled" voice: "Alarm Disabled" new: + +########################### +id: LANG_RECORD_TRIGGER +desc: in recording settings_menu +eng: "Trigger" +new: + +id: LANG_RECORD_START_THRESHOLD +desc: in recording settings_menu +eng: "Start above" +new: + +id: LANG_RECORD_MIN_DURATION +desc: in recording settings_menu +eng: "for at least" +new: + +id: LANG_RECORD_STOP_THRESHOLD +desc: in recording settings_menu +eng: "Stop below" +new: + +id: LANG_RECORD_STOP_POSTREC +desc: in recording settings_menu +eng: "for at least" +new: + +id: LANG_RECORD_STOP_GAP +desc: in recording settings_menu +eng: "presplit gap" +new: + +id: LANG_RECORD_TRIGGER_MODE +desc: in recording settings_menu +eng: "Trigger" +new: + +id: LANG_RECORD_TRIG_NOREARM +desc: in recording settings_menu +eng: "one" +new: + +id: LANG_RECORD_TRIG_REARM +desc: in recording settings_menu +eng: "repeat" +new: + +id: LANG_DB_INF +desc: -inf db for values below measurement +eng: "-inf" +new: + +id: LANG_RECORD_TRIG_IDLE +desc: waiting for threshold +eng: "Trigger idle" +new: + +id: LANG_RECORD_TRIGGER_ACTIVE +eng: "Trigger active" +new: diff --git a/apps/recorder/peakmeter.c b/apps/recorder/peakmeter.c index 41d375e076..0481e25acd 100644 --- a/apps/recorder/peakmeter.c +++ b/apps/recorder/peakmeter.c @@ -22,6 +22,7 @@ #include "kernel.h" #include "settings.h" #include "lcd.h" +#include "widgets.h" #include "wps-display.h" #include "sprintf.h" #include "button.h" @@ -67,6 +68,24 @@ static unsigned short db_min = 0; static unsigned short db_max = 9000; static unsigned short db_range = 9000; +static unsigned short trig_strt_threshold; +static long trig_strt_duration; +static long trig_strt_dropout; +static unsigned short trig_stp_threshold; +static long trig_stp_hold; +static long trig_rstrt_gap; + +/* point in time when the threshold was exceeded */ +static long trig_hightime; + +/* point in time when the volume fell below the threshold*/ +static long trig_lowtime; + +/* The output value of the trigger. See TRIG_XXX constants vor valid values */ +static int trig_status = TRIG_OFF; + +static void (*trigger_listener)(int) = NULL; + #if CONFIG_HWCODEC == MASNONE #define MAS_REG_DQPEAK_L 0 #define MAS_REG_DQPEAK_R 0 @@ -124,7 +143,7 @@ static const long clip_time_out[] = { /* precalculated peak values that represent magical dBfs values. Used to draw the scale */ -#define DB_SCALE_SRC_VALUES_SIZE 11 +#define DB_SCALE_SRC_VALUES_SIZE 12 #if 0 static const int db_scale_src_values[DB_SCALE_SRC_VALUES_SIZE] = { 32767, /* 0 db */ @@ -138,6 +157,7 @@ static const int db_scale_src_values[DB_SCALE_SRC_VALUES_SIZE] = { 328, /* -40 db */ 104, /* -50 db */ 33, /* -60 db */ + 1, /* -inf */ }; #else static const int db_scale_src_values[DB_SCALE_SRC_VALUES_SIZE] = { @@ -152,9 +172,25 @@ static const int db_scale_src_values[DB_SCALE_SRC_VALUES_SIZE] = { 373, /* -40 db */ 102, /* -50 db */ 33, /* -60 db */ + 0, /* -inf */ }; #endif +const char* peak_meter_dbnames[DB_SCALE_SRC_VALUES_SIZE] = { + "0 db", + "-3 db", + "-6 db", + "-9 db", + "-12 db", + "-18 db", + "-24 db", + "-30 db", + "-40 db", + "-50 db", + "-60 db", + "-inf", +}; + static int db_scale_count = DB_SCALE_SRC_VALUES_SIZE; /* if db_scale_valid is false the content of @@ -275,8 +311,8 @@ int calc_db (int isample) { /** - * A helper function for db_to_sample. Don't call it separately but - * use db_to_sample. If one or both of min and max are outside the + * A helper function for peak_meter_db2sample. Don't call it separately but + * use peak_meter_db2sample. If one or both of min and max are outside the * range 0 <= min (or max) < 8961 the behaviour of this function is * undefined. It may not return. * @param int min - The minimum of the value range that is searched. @@ -312,7 +348,7 @@ static int db_to_sample_bin_search(int min, int max, int db){ * @return int - The return value is in the range of * 0 <= return value < MAX_PEAK */ -static int db_to_sample(int db) { +int peak_meter_db2sample(int db) { int retval = 0; /* what is the maximum pseudo db value */ @@ -351,7 +387,7 @@ static int db_to_sample(int db) { */ void peak_meter_set_min(int newmin) { if (peak_meter_use_dbfs) { - peak_meter_range_min = db_to_sample(newmin); + peak_meter_range_min = peak_meter_db2sample(newmin); } else { if (newmin < peak_meter_range_max) { @@ -392,7 +428,7 @@ int peak_meter_get_min(void) { */ void peak_meter_set_max(int newmax) { if (peak_meter_use_dbfs) { - peak_meter_range_max = db_to_sample(newmax); + peak_meter_range_max = peak_meter_db2sample(newmax); } else { if (newmax > peak_meter_range_min) { peak_meter_range_max = newmax * MAX_PEAK / 100; @@ -504,6 +540,15 @@ void peak_meter_playback(bool playback) #endif } +static void set_trig_status(int new_state) { + if (trig_status != new_state) { + trig_status = new_state; + if (trigger_listener != NULL) { + trigger_listener(trig_status); + } + } +} + /** * Reads peak values from the MAS, and detects clips. The * values are stored in peak_meter_l peak_meter_r for later @@ -546,6 +591,121 @@ inline void peak_meter_peek(void) current_tick + clip_time_out[peak_meter_clip_hold]; } + switch (trig_status) { + case TRIG_READY: + /* no more changes, if trigger was activated as release trigger */ + /* threshold exceeded? */ + if ((left > trig_strt_threshold) || (right > trig_strt_threshold)) { + if (trig_strt_duration) { + /* reset trigger duration */ + trig_hightime = current_tick; + + /* reset dropout duration */ + trig_lowtime = current_tick; + + /* if trig_duration is set to 0 the user wants to start + recording immediately */ + set_trig_status(TRIG_STEADY); + } else { + set_trig_status(TRIG_GO); + } + } + break; + + case TRIG_STEADY: + case TRIG_RETRIG: + /* trigger duration exceeded */ + if (current_tick - trig_hightime > trig_strt_duration) { + set_trig_status(TRIG_GO); + } else { + /* threshold exceeded? */ + if ((left > trig_strt_threshold) || + (right > trig_strt_threshold)) { + /* reset lowtime */ + trig_lowtime = current_tick; + } + /* volume is below threshold */ + else { + /* dropout occurred? */ + if (current_tick - trig_lowtime > trig_strt_dropout){ + if (trig_status == TRIG_STEADY){ + set_trig_status(TRIG_READY); + } + /* trig_status == TRIG_RETRIG */ + else { + /* the gap has already expired */ + trig_lowtime = current_tick - trig_rstrt_gap - 1; + set_trig_status(TRIG_POSTREC); + } + } + } + } + break; + + case TRIG_GO: + case TRIG_CONTINUE: + /* threshold exceeded? */ + if ((left > trig_stp_threshold) || (right > trig_stp_threshold)) { + /* restart hold time countdown */ + trig_lowtime = current_tick; + } else { + set_trig_status(TRIG_POSTREC); + trig_hightime = current_tick; + } + break; + + case TRIG_POSTREC: + /* gap time expired? */ + if (current_tick - trig_lowtime > trig_rstrt_gap){ + /* start threshold exceeded? */ + if ((left > trig_strt_threshold) || + (right > trig_strt_threshold)) { + + set_trig_status(TRIG_RETRIG); + trig_hightime = current_tick; + } + else + + /* stop threshold exceeded */ + if ((left > trig_stp_threshold) || + (right > trig_stp_threshold)) { + if (current_tick - trig_hightime > trig_stp_hold){ + trig_lowtime = current_tick; + set_trig_status(TRIG_CONTINUE); + } else { + trig_lowtime = current_tick - trig_rstrt_gap - 1; + } + } + + /* below any threshold */ + else { + if (current_tick - trig_lowtime > trig_stp_hold){ + set_trig_status(TRIG_READY); + } else { + trig_hightime = current_tick; + } + } + } + + /* still within the gap time */ + else { + /* stop threshold exceeded */ + if ((left > trig_stp_threshold) || + (right > trig_stp_threshold)) { + set_trig_status(TRIG_CONTINUE); + trig_lowtime = current_tick; + } + + /* hold time expired */ + else if (current_tick - trig_lowtime > trig_stp_hold){ + trig_hightime = current_tick; + trig_lowtime = current_tick; + set_trig_status(TRIG_READY); + } + } + break; + } + /* peaks are searched -> we have to find the maximum. When many calls of peak_meter_peek the maximum value will be stored in peak_meter_x. This maximum is reset by the @@ -558,37 +718,6 @@ inline void peak_meter_peek(void) #endif } - -/** - * The thread function for the peak meter calls peak_meter_peek - * to reas out the mas and find maxima, clips, etc. No display - * is performed. - */ -/* -void peak_meter_thread(void) { - sleep(5000); - while (1) { - if (peak_meter_enabled && peak_meter_use_thread){ - peak_meter_peek(); - } - yield(); - } -} -*/ - -/** - * Creates the peak meter thread - */ -/* -void peak_meter_init(void) { - create_thread( - peak_meter_thread, - peak_meter_stack, - sizeof peak_meter_stack, - "peakmeter"); -} -*/ - /** * Reads out the peak volume of the left channel. * @return int - The maximum value that has been detected @@ -842,6 +971,22 @@ void peak_meter_draw(int x, int y, int width, int height) { lcd_invertpixel(db_scale_lcd_coord[i], y + height / 2 - 1); } + if (trig_status != TRIG_OFF) { + int start_trigx, stop_trigx, ycenter; + + ycenter = y + height / 2; + /* display threshold value */ + start_trigx = x+peak_meter_scale_value(trig_strt_threshold,meterwidth); + lcd_drawline(start_trigx, ycenter - 2, start_trigx, ycenter); + start_trigx ++; + if (start_trigx < LCD_WIDTH) lcd_drawpixel(start_trigx, ycenter - 1); + + stop_trigx = x + peak_meter_scale_value(trig_stp_threshold,meterwidth); + lcd_drawline(stop_trigx, ycenter - 2, stop_trigx, ycenter); + if (stop_trigx > 0) lcd_drawpixel(stop_trigx - 1, ycenter - 1); + + } + #ifdef PM_DEBUG /* display a bar to show how many calls to peak_meter_peek have ocurred since the last display */ @@ -866,6 +1011,161 @@ void peak_meter_draw(int x, int y, int width, int height) { last_right = right; } +/** + * Defines the parameters of the trigger. After these parameters are defined + * the trigger can be started either by peak_meter_attack_trigger or by + * peak_meter_release_trigger. Note that you can pass either linear (%) or + * logarithmic (db) values to the thresholds. Positive values are intepreted as + * percent (0 is 0% .. 100 is 100%). Negative values are interpreted as db. + * To avoid ambiguosity of the value 0 the negative values are shifted by -1. + * Thus -75 is -74db .. -1 is 0db. + * @param start_threshold - The threshold used for attack trigger. Negative + * values are interpreted as db -1, positive as %. + * @param start_duration - The minimum time span within which start_threshold + * must be exceeded to fire the attack trigger. + * @param start_dropout - The maximum time span the level may fall below + * start_threshold without releasing the attack trigger. + * @param stop_threshold - The threshold the volume must fall below to release + * the release trigger.Negative values are + * interpreted as db -1, positive as %. + * @param stop_hold - The minimum time the volume must fall below the + * stop_threshold to release the trigger. + * @param + */ +void peak_meter_define_trigger( + int start_threshold, + long start_duration, + long start_dropout, + int stop_threshold, + long stop_hold_time, + long restart_gap + ) +{ + if (start_threshold < 0) { + /* db */ + if (start_threshold < -89) { + trig_strt_threshold = 0; + } else { + trig_strt_threshold =peak_meter_db2sample((start_threshold+1)*100); + } + } else { + /* linear percent */ + trig_strt_threshold = start_threshold * MAX_PEAK / 100; + } + trig_strt_duration = start_duration; + trig_strt_dropout = start_dropout; + if (stop_threshold < 0) { + /* db */ + trig_stp_threshold = peak_meter_db2sample((stop_threshold + 1) * 100); + } else { + /* linear percent */ + trig_stp_threshold = stop_threshold * MAX_PEAK / 100; + } + trig_stp_hold = stop_hold_time; + trig_rstrt_gap = restart_gap; +} + +/** + * Enables or disables the trigger. + * @param on - If true the trigger is turned on. + */ +void peak_meter_trigger(bool on) { + /* don't use set_trigger here as that would fire an undesired event */ + trig_status = on ? TRIG_READY : TRIG_OFF; +} + +/** + * Registers the listener function that listenes on trig_status changes. + * @param listener - The function that is called with each change of + * trig_status. May be set to NULL if no callback is desired. + */ +void peak_meter_set_trigger_listener(void (*listener)(int status)) { + trigger_listener = listener; +} + +/** + * Fetches the status of the trigger. + * TRIG_OFF: the trigger is inactive + * TRIG_RELEASED: The volume level is below the threshold + * TRIG_ACTIVATED: The volume level has exceeded the threshold, but the trigger + * hasn't been fired yet. + * TRIG_FIRED: The volume exceeds the threshold + * + * To activate the trigger call either peak_meter_attack_trigger or + * peak_meter_release_trigger. To turn the trigger off call + * peak_meter_trigger_off. + */ +int peak_meter_trigger_status(void) { + return trig_status; /* & TRIG_PIT_MASK;*/ +} + +void peak_meter_draw_trig(int xpos, int ypos) { + int x = xpos + ICON_PLAY_STATE_WIDTH + 1; + switch (trig_status) { + long time_left; + + case TRIG_READY: + scrollbar(x, ypos + 1, TRIGBAR_WIDTH, TRIG_HEIGHT - 2, + TRIGBAR_WIDTH, 0, 0, HORIZONTAL); + lcd_bitmap(bitmap_icons_7x8[Icon_Stop], xpos, ypos, + ICON_PLAY_STATE_WIDTH, STATUSBAR_HEIGHT, false); + break; + + case TRIG_STEADY: + case TRIG_RETRIG: + time_left = trig_strt_duration - (current_tick - trig_hightime); + time_left = time_left * TRIGBAR_WIDTH / trig_strt_duration; + scrollbar(x, ypos + 1, TRIGBAR_WIDTH, TRIG_HEIGHT - 2, + TRIGBAR_WIDTH, 0, TRIGBAR_WIDTH - time_left, HORIZONTAL); + lcd_bitmap(bitmap_icons_7x8[Icon_Stop], xpos, ypos, + ICON_PLAY_STATE_WIDTH, STATUSBAR_HEIGHT, false); + break; + + case TRIG_GO: + case TRIG_CONTINUE: + scrollbar(x, ypos + 1, TRIGBAR_WIDTH, TRIG_HEIGHT - 2, + TRIGBAR_WIDTH, TRIGBAR_WIDTH, TRIGBAR_WIDTH, HORIZONTAL); + lcd_bitmap(bitmap_icons_7x8[Icon_Record], + TRIG_WIDTH - ICON_PLAY_STATE_WIDTH, ypos, + ICON_PLAY_STATE_WIDTH, STATUSBAR_HEIGHT, false); + break; + + case TRIG_POSTREC: + time_left = trig_stp_hold - (current_tick - trig_lowtime); + time_left = time_left * TRIGBAR_WIDTH / trig_stp_hold; + scrollbar(x, ypos + 1, TRIGBAR_WIDTH, TRIG_HEIGHT - 2, + TRIGBAR_WIDTH, time_left, TRIGBAR_WIDTH, HORIZONTAL); + lcd_bitmap(bitmap_icons_7x8[Icon_Record], + TRIG_WIDTH - ICON_PLAY_STATE_WIDTH, ypos, + ICON_PLAY_STATE_WIDTH, STATUSBAR_HEIGHT, false); + break; + } + +} + +int peak_meter_draw_get_btn(int x, int y, int width, int height) +{ + int button; + long next_refresh = current_tick; + long next_big_refresh = current_tick + HZ / 10; + button = BUTTON_NONE; + while (!TIME_AFTER(current_tick, next_big_refresh)) { + button = button_get(false); + if (button != BUTTON_NONE) { + break; + } + peak_meter_peek(); + yield(); + + if (TIME_AFTER(current_tick, next_refresh)) { + peak_meter_draw(x, y, width, height); + lcd_update_rect(x, y, width, height); + next_refresh = current_tick + HZ / peak_meter_fps; + } + } + return button; +} + #ifdef PM_DEBUG static void peak_meter_clear_histogram(void) { int i = 0; diff --git a/apps/recorder/peakmeter.h b/apps/recorder/peakmeter.h index db419a0afa..3c0a28bf3b 100644 --- a/apps/recorder/peakmeter.h +++ b/apps/recorder/peakmeter.h @@ -24,12 +24,12 @@ extern bool peak_meter_histogram(void); #endif - extern bool peak_meter_enabled; extern int peak_meter_fps; extern void peak_meter_playback(bool playback); extern void peak_meter_draw(int x, int y, int width, int height); +extern int peak_meter_draw_get_btn(int x, int y, int width, int height); extern void peak_meter_set_clip_hold(int time); extern void peak_meter_peek(void); extern void peak_meter_init_range( bool dbfs, int range_min, int range_max); @@ -42,8 +42,40 @@ extern int peak_meter_get_max(void); extern void peak_meter_set_use_dbfs(int use); extern int peak_meter_get_use_dbfs(void); extern int calc_db (int isample); +extern int peak_meter_db2sample(int db); extern unsigned short peak_meter_scale_value(unsigned short val, int meterwidth); +/* valid values for trigger_status */ +#define TRIG_OFF 0x00 +#define TRIG_READY 0x01 +#define TRIG_STEADY 0x02 +#define TRIG_GO 0x03 +#define TRIG_POSTREC 0x04 +#define TRIG_RETRIG 0x05 +#define TRIG_CONTINUE 0x06 + +extern void peak_meter_define_trigger( + int start_threshold, + long start_duration, + long start_dropout, + int stop_threshold, + long stop_hold_time, + long restart_gap + ); + +extern void peak_meter_trigger(bool on); +extern int peak_meter_trigger_status(void); +extern void peak_meter_set_trigger_listener(void (*listener)(int status)); + +//#define TRIG_WIDTH 12 +//#define TRIG_HEIGHT 14 + +#define TRIG_WIDTH 112 +#define TRIG_HEIGHT 8 +#define TRIGBAR_WIDTH (TRIG_WIDTH - (2 * (ICON_PLAY_STATE_WIDTH + 1))) + +extern void peak_meter_draw_trig(int x, int y); + extern unsigned short peak_meter_range_min; extern unsigned short peak_meter_range_max; diff --git a/apps/recorder/recording.c b/apps/recorder/recording.c index 7aa6aba98c..39e94739b5 100644 --- a/apps/recorder/recording.c +++ b/apps/recorder/recording.c @@ -50,6 +50,7 @@ #include "talk.h" #include "atoi.h" #include "sound.h" +#include "ata.h" #ifdef HAVE_RECORDING @@ -240,6 +241,56 @@ int rec_create_directory(void) return 0; } +static char path_buffer[MAX_PATH]; + +/* used in trigger_listerner and recording_screen */ +static unsigned int last_seconds = 0; + +/** + * Callback function so that the peak meter code can send an event + * to this application. This function can be passed to + * peak_meter_set_trigger_listener in order to activate the trigger. + */ +static void trigger_listener(int trigger_status) +{ + switch (trigger_status) + { + case TRIG_GO: + if((mpeg_status() & MPEG_STATUS_RECORD) != MPEG_STATUS_RECORD) + { + talk_buffer_steal(); /* we use the mp3 buffer */ + mpeg_record(rec_create_filename(path_buffer)); + + /* give control to mpeg thread so that it can start recording */ + yield(); yield(); yield(); + } + + /* if we're already recording this is a retrigger */ + else + { + mpeg_new_file(rec_create_filename(path_buffer)); + /* tell recording_screen to reset the time */ + last_seconds = 0; + } + break; + + /* A _change_ to TRIG_READY means the current recording has stopped */ + case TRIG_READY: + if(mpeg_status() & MPEG_STATUS_RECORD) + { + mpeg_stop(); + if (global_settings.rec_trigger_mode != TRIG_MODE_REARM) + { + peak_meter_set_trigger_listener(NULL); + peak_meter_trigger(false); + } + } + break; + } +} + +#define BLINK_MASK 0x10 + bool recording_screen(void) { long button; @@ -252,12 +303,11 @@ bool recording_screen(void) int update_countdown = 1; bool have_recorded = false; unsigned int seconds; - unsigned int last_seconds = 0; int hours, minutes; char path_buffer[MAX_PATH]; bool been_in_usb_mode = false; - bool led_state; - int led_delay; + int last_mpeg_stat = -1; + bool last_led_stat = false; const unsigned char *byte_units[] = { ID2P(LANG_BYTE), @@ -267,6 +317,9 @@ bool recording_screen(void) }; cursor = 0; +#ifndef SIMULATOR + ata_set_led_enabled(false); +#endif mpeg_init_recording(); sound_set(SOUND_VOLUME, global_settings.volume); @@ -288,6 +341,8 @@ bool recording_screen(void) set_gain(); + settings_apply_trigger(); + lcd_setfont(FONT_SYSFIXED); lcd_getstringsize("M", &w, &h); lcd_setmargins(global_settings.invert_cursor ? 0 : w, 8); @@ -295,45 +350,93 @@ bool recording_screen(void) if(rec_create_directory() > 0) have_recorded = true; - led_state = false; - led_delay = 0; - while(!done) { + int mpeg_stat = mpeg_status(); + /* * Flash the LED while waiting to record. Turn it on while * recording. */ - if(mpeg_status() != MPEG_STATUS_RECORD) + if(mpeg_stat & MPEG_STATUS_RECORD) { - if(led_delay++ >= 4) + if (mpeg_stat & MPEG_STATUS_PAUSE) + { + /* + This is supposed to be the same as + led(current_tick & BLINK_MASK) + But we do this hubub to prevent unnecessary hardware + communication when the led already has the desired state. + */ + if (last_led_stat != ((current_tick & BLINK_MASK) == BLINK_MASK)) + { + /* trigger is on in status TRIG_READY (no check needed) */ + last_led_stat = !last_led_stat; + led(last_led_stat); + } + } + else { - led_state = !led_state; - invert_led(led_state); - led_delay = 0; + /* trigger is on in status TRIG_READY (no check needed) */ + led(true); } } else { - if(!led_state) + int trigStat = peak_meter_trigger_status(); + + // other trigger stati than trig_off and trig_steady + // already imply that we are recording. + if (trigStat == TRIG_STEADY) { - led_state = true; - invert_led(true); + /* This is supposed to be the same as + led(current_tick & BLINK_MASK) + But we do this hubub to prevent unnecessary hardware + communication when the led already has the desired state. + */ + if (last_led_stat != ((current_tick & BLINK_MASK) == BLINK_MASK)) + { + /* trigger is on in status TRIG_READY (no check needed) */ + last_led_stat = !last_led_stat; + led(last_led_stat); + } + } + else + { + /* trigger is on in status TRIG_READY (no check needed) */ + led(false); } } - button = button_get_w_tmo(HZ / peak_meter_fps); + /* Wait for a button while drawing the peak meter */ + button = peak_meter_draw_get_btn(0, 8 + h*2, LCD_WIDTH, h); + + if (last_mpeg_stat != mpeg_stat) + { + if (mpeg_stat == MPEG_STATUS_RECORD) + { + have_recorded = true; + } + last_mpeg_stat = mpeg_stat; + } + switch(button) { case REC_STOPEXIT: - if(mpeg_status() & MPEG_STATUS_RECORD) + if(mpeg_stat & MPEG_STATUS_RECORD) { + /* turn off the trigger */ + peak_meter_trigger(false); + peak_meter_set_trigger_listener(NULL); mpeg_stop(); } else { peak_meter_playback(true); peak_meter_enabled = false; + /* turn off the trigger */ + peak_meter_set_trigger_listener(NULL); + peak_meter_trigger(false); done = true; } update_countdown = 1; /* Update immediately */ @@ -341,8 +444,13 @@ bool recording_screen(void) case REC_RECPAUSE: /* Only act if the mpeg is stopped */ - if(!(mpeg_status() & MPEG_STATUS_RECORD)) + if(!(mpeg_stat & MPEG_STATUS_RECORD)) + { + /* is this manual or triggered recording? */ + if ((global_settings.rec_trigger_mode == TRIG_MODE_OFF) || + (peak_meter_trigger_status() != TRIG_OFF)) { + /* manual recording */ have_recorded = true; talk_buffer_steal(); /* we use the mp3 buffer */ mpeg_record(rec_create_filename(path_buffer)); @@ -353,9 +461,22 @@ bool recording_screen(void) mpeg_beep(HZ/2); /* longer beep on start */ } } + + /* this is triggered recording */ + else + { + update_countdown = 1; /* Update immediately */ + + /* we don't start recording now, but enable the + trigger and let the callback function + trigger_listener control when the recording starts */ + peak_meter_trigger(true); + peak_meter_set_trigger_listener(&trigger_listener); + } + } else { - if(mpeg_status() & MPEG_STATUS_PAUSE) + if(mpeg_stat & MPEG_STATUS_PAUSE) { mpeg_resume_recording(); if (global_settings.talk_menu) @@ -473,11 +594,14 @@ bool recording_screen(void) #ifdef REC_SETTINGS case REC_SETTINGS: - if(mpeg_status() != MPEG_STATUS_RECORD) + if(mpeg_stat != MPEG_STATUS_RECORD) { - invert_led(false); + /* led is restored at begin of loop / end of function */ + led(false); if (recording_menu(false)) + { return SYS_USB_CONNECTED; + } settings_save(); if (global_settings.rec_prerecord_time) @@ -491,7 +615,6 @@ bool recording_screen(void) global_settings.rec_prerecord_time); set_gain(); - update_countdown = 1; /* Update immediately */ lcd_setfont(FONT_SYSFIXED); @@ -502,9 +625,10 @@ bool recording_screen(void) #ifdef REC_F2 case REC_F2: - if(mpeg_status() != MPEG_STATUS_RECORD) + if(mpeg_stat != MPEG_STATUS_RECORD) { - invert_led(false); + /* led is restored at begin of loop / end of function */ + led(false); if (f2_rec_screen()) { have_recorded = true; @@ -518,16 +642,17 @@ bool recording_screen(void) #ifdef REC_F3 case REC_F3: - if(mpeg_status() & MPEG_STATUS_RECORD) + if(mpeg_stat & MPEG_STATUS_RECORD) { mpeg_new_file(rec_create_filename(path_buffer)); last_seconds = 0; } else { - if(mpeg_status() != MPEG_STATUS_RECORD) + if(mpeg_stat != MPEG_STATUS_RECORD) { - invert_led(false); + /* led is restored at begin of loop / end of function */ + led(false); if (f3_rec_screen()) { have_recorded = true; @@ -542,7 +667,7 @@ bool recording_screen(void) case SYS_USB_CONNECTED: /* Only accept USB connection when not recording */ - if(mpeg_status() != MPEG_STATUS_RECORD) + if(mpeg_stat != MPEG_STATUS_RECORD) { default_event_handler(SYS_USB_CONNECTED); done = true; @@ -555,8 +680,6 @@ bool recording_screen(void) break; } - peak_meter_peek(); - if(TIME_AFTER(current_tick, timeout)) { lcd_setfont(FONT_SYSFIXED); @@ -585,7 +708,7 @@ bool recording_screen(void) dseconds = rec_timesplit_seconds(); - if(mpeg_status() & MPEG_STATUS_PRERECORD) + if(mpeg_stat & MPEG_STATUS_PRERECORD) { snprintf(buf, 32, "%s...", str(LANG_RECORD_PRERECORD)); } @@ -618,15 +741,13 @@ bool recording_screen(void) /* We will do file splitting regardless, since the OFF setting really means 24 hours. This is to make sure that the recorded files don't get too big. */ - if (mpeg_status() && (seconds >= dseconds)) + if (mpeg_stat && (seconds >= dseconds)) { mpeg_new_file(rec_create_filename(path_buffer)); update_countdown = 1; last_seconds = 0; } - peak_meter_draw(0, 8 + h*2, LCD_WIDTH, h); - /* Show mic gain if input source is Mic */ if(global_settings.rec_source == 0) { @@ -635,9 +756,9 @@ bool recording_screen(void) global_settings.rec_mic_gain, buf2, sizeof(buf2))); if (global_settings.invert_cursor && (pos++ == cursor)) - lcd_puts_style(0, 3, buf, STYLE_INVERT); + lcd_puts_style(0, 4, buf, STYLE_INVERT); else - lcd_puts(0, 3, buf); + lcd_puts(0, 4, buf); } else { @@ -650,53 +771,58 @@ bool recording_screen(void) fmt_gain(SOUND_LEFT_GAIN, gain, buf2, sizeof(buf2))); if (global_settings.invert_cursor && (pos++ == cursor)) - lcd_puts_style(0, 3, buf, STYLE_INVERT); + lcd_puts_style(0, 4, buf, STYLE_INVERT); else - lcd_puts(0, 3, buf); + lcd_puts(0, 4, buf); snprintf(buf, 32, "%s: %s", str(LANG_RECORDING_LEFT), fmt_gain(SOUND_LEFT_GAIN, global_settings.rec_left_gain, buf2, sizeof(buf2))); if (global_settings.invert_cursor && (pos++ == cursor)) - lcd_puts_style(0, 4, buf, STYLE_INVERT); + lcd_puts_style(0, 5, buf, STYLE_INVERT); else - lcd_puts(0, 4, buf); + lcd_puts(0, 5, buf); snprintf(buf, 32, "%s: %s", str(LANG_RECORDING_RIGHT), fmt_gain(SOUND_RIGHT_GAIN, global_settings.rec_right_gain, buf2, sizeof(buf2))); if (global_settings.invert_cursor && (pos++ == cursor)) - lcd_puts_style(0, 5, buf, STYLE_INVERT); + lcd_puts_style(0, 6, buf, STYLE_INVERT); else - lcd_puts(0, 5, buf); + lcd_puts(0, 6, buf); } } if(global_settings.rec_source != SOURCE_SPDIF) - put_cursorxy(0, 3 + cursor, true); - - snprintf(buf, 32, "%s %s [%d]", - freq_str[global_settings.rec_frequency], - global_settings.rec_channels? - str(LANG_CHANNEL_MONO):str(LANG_CHANNEL_STEREO), - global_settings.rec_quality); - lcd_puts(0, 6, buf); + put_cursorxy(0, 4 + cursor, true); + + if (global_settings.rec_source != SOURCE_LINE) { + snprintf(buf, 32, "%s %s [%d]", + freq_str[global_settings.rec_frequency], + global_settings.rec_channels? + str(LANG_CHANNEL_MONO):str(LANG_CHANNEL_STEREO), + global_settings.rec_quality); + lcd_puts(0, 6, buf); + } status_draw(true); + peak_meter_draw(0, 8 + h*2, LCD_WIDTH, h); lcd_update(); } - else + + /* draw the trigger status */ + if (peak_meter_trigger_status() != TRIG_OFF) { - lcd_clearrect(0, 8 + h*2, LCD_WIDTH, h); - peak_meter_draw(0, 8 + h*2, LCD_WIDTH, h); - lcd_update_rect(0, 8 + h*2, LCD_WIDTH, h); + peak_meter_draw_trig(LCD_WIDTH - TRIG_WIDTH, 4 * h); + lcd_update_rect(LCD_WIDTH - (TRIG_WIDTH + 2), 4 * h, + TRIG_WIDTH + 2, TRIG_HEIGHT); } } - if(mpeg_status() & MPEG_STATUS_ERROR) + if(mpeg_stat & MPEG_STATUS_ERROR) { done = true; } @@ -721,6 +847,10 @@ bool recording_screen(void) mpeg_init_playback(); + /* make sure the trigger is really turned off */ + peak_meter_trigger(false); + peak_meter_set_trigger_listener(NULL); + sound_settings_apply(); lcd_setfont(FONT_UI); @@ -728,6 +858,9 @@ bool recording_screen(void) if (have_recorded) reload_directory(); +#ifndef SIMULATOR + ata_set_led_enabled(true); +#endif return been_in_usb_mode; /* #endif @@ -883,10 +1016,26 @@ bool f3_rec_screen(void) lcd_bitmap(bitmap_icons_7x8[Icon_FastBackward], LCD_WIDTH/2 - 16, LCD_HEIGHT/2 - 4, 7, 8, true); + /* trigger setup */ + ptr = str(LANG_RECORD_TRIGGER); + lcd_getstringsize(ptr,&w,&h); + lcd_putsxy((LCD_WIDTH-w)/2, LCD_HEIGHT - h*2, ptr); + lcd_bitmap(bitmap_icons_7x8[Icon_DownArrow], + LCD_WIDTH/2 - 3, LCD_HEIGHT - h*3, 7, 8, true); + lcd_update(); button = button_get(true); switch (button) { + case BUTTON_DOWN: + case BUTTON_F3 | BUTTON_DOWN: +#ifndef SIMULATOR + rectrigger(); + settings_apply_trigger(); +#endif + exit = true; + break; + case BUTTON_LEFT: case BUTTON_F3 | BUTTON_LEFT: global_settings.rec_source++; diff --git a/apps/settings.c b/apps/settings.c index df03bdcfcf..bad8fa29ab 100644 --- a/apps/settings.c +++ b/apps/settings.c @@ -154,6 +154,11 @@ static const char off_on_ask[] = "off,on,ask"; static const char graphic_numeric[] = "graphic,numeric"; static const char off_number_spell_hover[] = "off,number,spell,hover"; +/* keep synchronous to trig_durations and + trigger_times in settings_apply_trigger */ +static const char trig_durations_conf [] = + "0s,1s,2s,5s,10s,15s,20s,25s,30s,1min,2min,5min,10min"; + /* the part of the settings which ends up in the RTC RAM, where available (those we either need early, save frequently, or without spinup) */ static const struct bit_entry rtc_bits[] = @@ -350,6 +355,14 @@ static const struct bit_entry hd_bits[] = #ifdef HAVE_RECORDING {1, S_O(rec_startup), false, "rec screen on startup", off_on }, + + /* values for the trigger */ + {8 | SIGNED, S_O(rec_start_thres), -35, "trigger start threshold", NULL}, + {8 | SIGNED, S_O(rec_stop_thres), -45, "trigger stop threshold", NULL}, + {4, S_O(rec_start_duration), 0, "trigger start duration", trig_durations_conf}, + {4, S_O(rec_stop_postrec), 2, "trigger stop postrec", trig_durations_conf}, + {4, S_O(rec_stop_gap), 1, "trigger min gap", trig_durations_conf}, + {4, S_O(rec_trigger_mode ), 1, "trigger mode", "off,no rearm,rearm"}, #endif /* new stuff to be added at the end */ @@ -1554,4 +1567,34 @@ unsigned int rec_timesplit_seconds(void) { return rec_timer_seconds[global_settings.rec_timesplit]; } + +/* + * Time strings used for the trigger durations. + * Keep synchronous to trigger_times in settings_apply_trigger + */ +char *trig_durations[TRIG_DURATION_COUNT] = +{ + "0s", "1s", "2s", "5s", + "10s", "15s", "20s", "25s", "30s", + "1min", "2min", "5min", "10min" +}; + +void settings_apply_trigger(void) +{ + /* Keep synchronous to trig_durations and trig_durations_conf*/ + static const long trigger_times[TRIG_DURATION_COUNT] = { + 0, HZ, 2*HZ, 5*HZ, + 10*HZ, 15*HZ, 20*HZ, 25*HZ, 30*HZ, + 60*HZ, 2*60*HZ, 5*60*HZ, 10*60*HZ + }; + + peak_meter_define_trigger( + global_settings.rec_start_thres, + trigger_times[global_settings.rec_start_duration], + MIN(trigger_times[global_settings.rec_start_duration] / 2, 2*HZ), + global_settings.rec_stop_thres, + trigger_times[global_settings.rec_stop_postrec], + trigger_times[global_settings.rec_stop_gap] + ); +} #endif diff --git a/apps/settings.h b/apps/settings.h index d738261c62..a76db9f143 100644 --- a/apps/settings.h +++ b/apps/settings.h @@ -102,6 +102,12 @@ #define FF_REWIND_45000 12 #define FF_REWIND_60000 13 +#define TRIG_MODE_OFF 0 +#define TRIG_MODE_NOREARM 1 +#define TRIG_MODE_REARM 2 + +#define TRIG_DURATION_COUNT 13 +extern char *trig_durations[TRIG_DURATION_COUNT]; /* These define "virtual pointers", which could either be a literal string, or a mean a string ID if the pointer is in a certain range. @@ -171,6 +177,13 @@ struct user_settings int rec_directory; /* 0=base dir, 1=current dir */ bool rec_startup; /* true means start Rockbox in recording screen */ + int rec_start_thres; /* negative: db, positive: % range -87 .. 100 */ + int rec_start_duration; /* index of trig_durations */ + int rec_stop_thres; /* negative: db, positive: % */ + int rec_stop_postrec; /* negative: db, positive: % range -87 .. 100 */ + int rec_stop_gap; /* index of trig_durations */ + int rec_trigger_mode; /* see TRIG_MODE_XXX constants */ + /* device settings */ int contrast; /* lcd contrast: 0-63 0=low 63=high */ @@ -325,6 +338,7 @@ int read_line(int fd, char* buffer, int buffer_size); void set_file(char* filename, char* setting, int maxlen); unsigned int rec_timesplit_seconds(void); +void settings_apply_trigger(void); /* global settings */ extern struct user_settings global_settings; diff --git a/apps/sound_menu.c b/apps/sound_menu.c index 560163c7c1..68c959a0ed 100644 --- a/apps/sound_menu.c +++ b/apps/sound_menu.c @@ -19,6 +19,7 @@ #include "config.h" #include #include +#include "system.h" #include "kernel.h" #include "lcd.h" #include "menu.h" @@ -29,12 +30,18 @@ #include "screens.h" #ifdef HAVE_LCD_BITMAP #include "icons.h" +#include "font.h" +#include "widgets.h" #endif #include "lang.h" #include "sprintf.h" #include "talk.h" #include "misc.h" #include "sound.h" +#if CONFIG_HWCODEC == MAS3587F || CONFIG_HWCODEC == MAS3539F +#include "peakmeter.h" +#include "mas.h" +#endif static const char* const fmt[] = { @@ -435,11 +442,373 @@ bool sound_menu(void) } #ifdef HAVE_RECORDING +enum trigger_menu_option +{ + TRIGGER_MODE, + PRERECORD_TIME, + START_THRESHOLD, + START_DURATION, + STOP_THRESHOLD, + STOP_POSTREC, + STOP_GAP, + TRIG_OPTION_COUNT, +}; + +#if !defined(SIMULATOR) && CONFIG_HWCODEC == MAS3587F +static char* create_thres_str(int threshold) +{ + static char retval[6]; + if (threshold < 0) { + if (threshold < -88) { + snprintf (retval, sizeof retval, "%s", str(LANG_DB_INF)); + } else { + snprintf (retval, sizeof retval, "%ddb", threshold + 1); + } + } else { + snprintf (retval, sizeof retval, "%d%%", threshold); + } + return retval; +} +#endif + +#if !defined(SIMULATOR) && (CONFIG_HWCODEC == MAS3587F || CONFIG_HWCODEC == MAS3539F) +#define INF_DB (-89) +static void change_threshold(int *threshold, int change) +{ + if (global_settings.peak_meter_dbfs) { + if (*threshold >= 0) { + int db = (calc_db(*threshold * MAX_PEAK / 100) - 9000) / 100; + *threshold = db; + } + *threshold += change; + if (*threshold > -1) { + *threshold = INF_DB; + } else if (*threshold < INF_DB) { + *threshold = -1; + } + } else { + if (*threshold < 0) { + *threshold = peak_meter_db2sample(*threshold * 100) * 100 / MAX_PEAK; + } + *threshold += change; + if (*threshold > 100) { + *threshold = 0; + } else if (*threshold < 0) { + *threshold = 100; + } + } +} + +/** + * Displays a menu for editing the trigger settings. + */ +bool rectrigger(void) +{ + int exit_request = false; + enum trigger_menu_option selected = TRIGGER_MODE; + bool retval = false; + int old_x_margin, old_y_margin; + +#define TRIGGER_MODE_COUNT 3 + char *trigger_modes[] = + { + str(LANG_OFF), + str(LANG_RECORD_TRIG_NOREARM), + str(LANG_RECORD_TRIG_REARM) + }; + +#define PRERECORD_TIMES_COUNT 31 + char *prerecord_times[] = { + str(LANG_OFF),"1s","2s", "3s", "4s", "5s", "6s", "7s", "8s", "9s", + "10s", "11s", "12s", "13s", "14s", "15s", "16s", "17s", "18s", "19s", + "20s", "21s", "22s", "23s", "24s", "25s", "26s", "27s", "28s", "29s", + "30s" + }; + + char *option_name[TRIG_OPTION_COUNT]; + + int old_start_thres = global_settings.rec_start_thres; + int old_start_duration = global_settings.rec_start_duration; + int old_prerecord_time = global_settings.rec_prerecord_time; + int old_stop_thres = global_settings.rec_stop_thres; + int old_stop_postrec = global_settings.rec_stop_postrec; + int old_stop_gap = global_settings.rec_stop_gap; + int old_trigger_mode = global_settings.rec_trigger_mode; + + int offset = 0; + int option_lines; + int w, h; + + option_name[TRIGGER_MODE] = str(LANG_RECORD_TRIGGER_MODE); + option_name[PRERECORD_TIME] = str(LANG_RECORD_PRERECORD_TIME); + option_name[START_THRESHOLD] = str(LANG_RECORD_START_THRESHOLD); + option_name[START_DURATION] = str(LANG_RECORD_MIN_DURATION); + option_name[STOP_THRESHOLD] = str(LANG_RECORD_STOP_THRESHOLD); + option_name[STOP_POSTREC] = str(LANG_RECORD_STOP_POSTREC); + option_name[STOP_GAP] = str(LANG_RECORD_STOP_GAP); + + + /* restart trigger with new values */ + settings_apply_trigger(); + peak_meter_trigger (global_settings.rec_trigger_mode != TRIG_MODE_OFF); + + lcd_clear_display(); + + old_x_margin = lcd_getxmargin(); + old_y_margin = lcd_getymargin(); + if(global_settings.statusbar) + lcd_setmargins(0, STATUSBAR_HEIGHT); + else + lcd_setmargins(0, 0); + + lcd_getstringsize("M", &w, &h); + + // two lines are reserved for peak meter and trigger status + option_lines = (LCD_HEIGHT/h) - (global_settings.statusbar ? 1:0) - 2; + + while (!exit_request) { + int stat_height = global_settings.statusbar ? STATUSBAR_HEIGHT : 0; + int button, i; + char *str; + char option_value[TRIG_OPTION_COUNT][7]; + + snprintf( + option_value[TRIGGER_MODE], + sizeof option_value[TRIGGER_MODE], + "%s", + trigger_modes[global_settings.rec_trigger_mode]); + + snprintf ( + option_value[PRERECORD_TIME], + sizeof option_value[PRERECORD_TIME], + "%s", + prerecord_times[global_settings.rec_prerecord_time]); + + /* due to value range shift (peak_meter_define_trigger) -1 is 0db */ + if (global_settings.rec_start_thres == -1) { + str = str(LANG_OFF); + } else { + str = create_thres_str(global_settings.rec_start_thres); + } + snprintf( + option_value[START_THRESHOLD], + sizeof option_value[START_THRESHOLD], + "%s", + str); + + snprintf( + option_value[START_DURATION], + sizeof option_value[START_DURATION], + "%s", + trig_durations[global_settings.rec_start_duration]); + + + if (global_settings.rec_stop_thres <= INF_DB) { + str = str(LANG_OFF); + } else { + str = create_thres_str(global_settings.rec_stop_thres); + } + snprintf( + option_value[STOP_THRESHOLD], + sizeof option_value[STOP_THRESHOLD], + "%s", + str); + + snprintf( + option_value[STOP_POSTREC], + sizeof option_value[STOP_POSTREC], + "%s", + trig_durations[global_settings.rec_stop_postrec]); + + snprintf( + option_value[STOP_GAP], + sizeof option_value[STOP_GAP], + "%s", + trig_durations[global_settings.rec_stop_gap]); + + lcd_clearrect(0, stat_height, LCD_WIDTH, LCD_HEIGHT - stat_height); + status_draw(true); + + /* reselect FONT_SYSFONT as status_draw has changed the font */ + /*lcd_setfont(FONT_SYSFIXED);*/ + + for (i = 0; i < option_lines; i++) { + int x, y; + + str = option_name[i + offset]; + lcd_putsxy(5, stat_height + i * h, str); + + str = option_value[i + offset]; + lcd_getstringsize(str, &w, &h); + y = stat_height + i * h; + x = LCD_WIDTH - w; + lcd_putsxy(x, y, str); + if ((int)selected == (i + offset)) + lcd_invertrect(x, y, w, h); + } + + scrollbar(0, stat_height, + 4, LCD_HEIGHT - 16 - stat_height, + TRIG_OPTION_COUNT, offset, offset + option_lines, + VERTICAL); + + peak_meter_draw_trig(0, LCD_HEIGHT - 8 - TRIG_HEIGHT); + + button = peak_meter_draw_get_btn(0, LCD_HEIGHT - 8, LCD_WIDTH, 8); + + lcd_update(); + + switch (button) { + case BUTTON_OFF: + splash(50, true, str(LANG_RESET_DONE_CANCEL)); + global_settings.rec_start_thres = old_start_thres; + global_settings.rec_start_duration = old_start_duration; + global_settings.rec_prerecord_time = old_prerecord_time; + global_settings.rec_stop_thres = old_stop_thres; + global_settings.rec_stop_postrec = old_stop_postrec; + global_settings.rec_stop_gap = old_stop_gap; + global_settings.rec_trigger_mode = old_trigger_mode; + exit_request = true; + break; + + case BUTTON_PLAY: + exit_request = true; + break; + + case BUTTON_UP: + selected += TRIG_OPTION_COUNT - 1; + selected %= TRIG_OPTION_COUNT; + offset = MIN(offset, (int)selected); + offset = MAX(offset, (int)selected - option_lines + 1); + break; + + case BUTTON_DOWN: + selected ++; + selected %= TRIG_OPTION_COUNT; + offset = MIN(offset, (int)selected); + offset = MAX(offset, (int)selected - option_lines + 1); + break; + + case BUTTON_RIGHT: + case BUTTON_RIGHT | BUTTON_REPEAT: + switch (selected) { + case TRIGGER_MODE: + global_settings.rec_trigger_mode ++; + global_settings.rec_trigger_mode %= TRIGGER_MODE_COUNT; + break; + + case PRERECORD_TIME: + global_settings.rec_prerecord_time ++; + global_settings.rec_prerecord_time %= PRERECORD_TIMES_COUNT; + break; + + case START_THRESHOLD: + change_threshold(&global_settings.rec_start_thres, 1); + break; + + case START_DURATION: + global_settings.rec_start_duration ++; + global_settings.rec_start_duration %= TRIG_DURATION_COUNT; + break; + + case STOP_THRESHOLD: + change_threshold(&global_settings.rec_stop_thres, 1); + break; + + case STOP_POSTREC: + global_settings.rec_stop_postrec ++; + global_settings.rec_stop_postrec %= TRIG_DURATION_COUNT; + break; + + case STOP_GAP: + global_settings.rec_stop_gap ++; + global_settings.rec_stop_gap %= TRIG_DURATION_COUNT; + break; + + case TRIG_OPTION_COUNT: + // avoid compiler warnings + break; + } + peak_meter_trigger(global_settings.rec_trigger_mode!=TRIG_OFF); + settings_apply_trigger(); + break; + + case BUTTON_LEFT: + case BUTTON_LEFT | BUTTON_REPEAT: + switch (selected) { + case TRIGGER_MODE: + global_settings.rec_trigger_mode+=TRIGGER_MODE_COUNT-1; + global_settings.rec_trigger_mode %= TRIGGER_MODE_COUNT; + break; + + case PRERECORD_TIME: + global_settings.rec_prerecord_time += PRERECORD_TIMES_COUNT - 1; + global_settings.rec_prerecord_time %= PRERECORD_TIMES_COUNT; + break; + + case START_THRESHOLD: + change_threshold(&global_settings.rec_start_thres, -1); + break; + + case START_DURATION: + global_settings.rec_start_duration += TRIG_DURATION_COUNT-1; + global_settings.rec_start_duration %= TRIG_DURATION_COUNT; + break; + + case STOP_THRESHOLD: + change_threshold(&global_settings.rec_stop_thres, -1); + break; + + case STOP_POSTREC: + global_settings.rec_stop_postrec += + TRIG_DURATION_COUNT - 1; + global_settings.rec_stop_postrec %= + TRIG_DURATION_COUNT; + break; + + case STOP_GAP: + global_settings.rec_stop_gap += + TRIG_DURATION_COUNT - 1; + global_settings.rec_stop_gap %= TRIG_DURATION_COUNT; + break; + + case TRIG_OPTION_COUNT: + // avoid compiler warnings + break; + } + + if (global_settings.rec_trigger_mode == TRIG_OFF) { + peak_meter_trigger(true); + } else { + /* restart trigger with new values */ + settings_apply_trigger(); + } + break; + + case BUTTON_F2: + peak_meter_trigger(true); + break; + + case SYS_USB_CONNECTED: + usb_screen(); + retval = true; + exit_request = true; + break; + } + } + + peak_meter_trigger(false); + lcd_setfont(FONT_UI); + lcd_setmargins(old_x_margin, old_y_margin); + return retval; +} +#endif + bool recording_menu(bool no_source) { int m; int i = 0; - struct menu_item items[9]; + struct menu_item items[10]; bool result; items[i].desc = ID2P(LANG_RECORDING_QUALITY); @@ -462,6 +831,10 @@ bool recording_menu(bool no_source) items[i++].function = recdirectory; items[i].desc = ID2P(LANG_RECORD_STARTUP); items[i++].function = reconstartup; +#if !defined(SIMULATOR) && CONFIG_HWCODEC == MAS3587F + items[i].desc = str(LANG_RECORD_TRIGGER); + items[i++].function = rectrigger; +#endif m=menu_init( items, i, NULL, NULL, NULL, NULL); result = menu_run(m); diff --git a/apps/sound_menu.h b/apps/sound_menu.h index 206d1e3715..4d295b0a70 100644 --- a/apps/sound_menu.h +++ b/apps/sound_menu.h @@ -23,5 +23,6 @@ bool sound_menu(void); bool recording_menu(bool no_source); +bool rectrigger(void); #endif diff --git a/firmware/drivers/ata.c b/firmware/drivers/ata.c index 10f5f20c7b..956f61dc89 100644 --- a/firmware/drivers/ata.c +++ b/firmware/drivers/ata.c @@ -241,6 +241,8 @@ static volatile unsigned char* ata_control; bool old_recorder = false; int ata_spinup_time = 0; +static bool ata_led_enabled = true; +static bool ata_led_on = false; static bool spinup = false; static bool sleeping = true; static long sleep_timeout = 5*HZ; @@ -473,6 +475,13 @@ static void copy_read_sectors(unsigned char* buf, int wordcount) #endif } +static void ata_led(bool on) { + ata_led_on = on; + if (ata_led_enabled) { + led(ata_led_on); + } +} + int ata_read_sectors(IF_MV2(int drive,) unsigned long start, int incount, @@ -492,21 +501,21 @@ int ata_read_sectors(IF_MV2(int drive,) last_disk_activity = current_tick; spinup_start = current_tick; - led(true); + ata_led(true); if ( sleeping ) { spinup = true; if (poweroff) { if (ata_power_on()) { mutex_unlock(&ata_mtx); - led(false); + ata_led(false); return -1; } } else { if (perform_soft_reset()) { mutex_unlock(&ata_mtx); - led(false); + ata_led(false); return -1; } } @@ -518,7 +527,7 @@ int ata_read_sectors(IF_MV2(int drive,) if (!wait_for_rdy()) { mutex_unlock(&ata_mtx); - led(false); + ata_led(false); return -2; } @@ -614,7 +623,7 @@ int ata_read_sectors(IF_MV2(int drive,) } break; } - led(false); + ata_led(false); mutex_unlock(&ata_mtx); @@ -775,21 +784,21 @@ int ata_write_sectors(IF_MV2(int drive,) last_disk_activity = current_tick; spinup_start = current_tick; - led(true); + ata_led(true); if ( sleeping ) { spinup = true; if (poweroff) { if (ata_power_on()) { mutex_unlock(&ata_mtx); - led(false); + ata_led(false); return -1; } } else { if (perform_soft_reset()) { mutex_unlock(&ata_mtx); - led(false); + ata_led(false); return -1; } } @@ -799,7 +808,7 @@ int ata_write_sectors(IF_MV2(int drive,) if (!wait_for_rdy()) { mutex_unlock(&ata_mtx); - led(false); + ata_led(false); return -2; } @@ -843,7 +852,7 @@ int ata_write_sectors(IF_MV2(int drive,) ret = -4; } - led(false); + ata_led(false); mutex_unlock(&ata_mtx); @@ -1042,9 +1051,9 @@ static void ata_thread(void) case SYS_USB_CONNECTED: if (poweroff) { mutex_lock(&ata_mtx); - led(true); + ata_led(true); ata_power_on(); - led(false); + ata_led(false); mutex_unlock(&ata_mtx); } @@ -1384,7 +1393,7 @@ int ata_init(void) mutex_init(&ata_mtx); - led(false); + ata_led(false); #if CONFIG_CPU == SH7034 /* Port A setup */ @@ -1458,3 +1467,12 @@ int ata_init(void) return 0; } + +void ata_set_led_enabled(bool enabled) { + ata_led_enabled = enabled; + if (ata_led_enabled) { + led(ata_led_on); + } else { + led(false); + } +} diff --git a/firmware/export/ata.h b/firmware/export/ata.h index fb604a120d..2043de915a 100644 --- a/firmware/export/ata.h +++ b/firmware/export/ata.h @@ -61,6 +61,7 @@ extern int ata_write_sectors(IF_MV2(int drive,) unsigned long start, int count, extern void ata_delayed_write(unsigned long sector, const void* buf); extern void ata_flush(void); extern void ata_spin(void); +extern void ata_set_led_enabled(bool enabled); extern unsigned short* ata_get_identify(void); extern long last_disk_activity; -- cgit v1.2.3