diff options
author | Linus Nielsen Feltzing <linus@haxx.se> | 2005-04-04 09:12:12 +0000 |
---|---|---|
committer | Linus Nielsen Feltzing <linus@haxx.se> | 2005-04-04 09:12:12 +0000 |
commit | 68482bbed286981a5ef09a466620e459790c96e6 (patch) | |
tree | de613ede8a83077b935debc1ec3edd965d5d6090 /apps/recorder | |
parent | 5fb6c64ffc8e6ab7512d805d2860831e492e5c52 (diff) | |
download | rockbox-68482bbed286981a5ef09a466620e459790c96e6.tar.gz rockbox-68482bbed286981a5ef09a466620e459790c96e6.zip |
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
Diffstat (limited to 'apps/recorder')
-rw-r--r-- | apps/recorder/peakmeter.c | 374 | ||||
-rw-r--r-- | apps/recorder/peakmeter.h | 34 | ||||
-rw-r--r-- | apps/recorder/recording.c | 257 |
3 files changed, 573 insertions, 92 deletions
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++; |