summaryrefslogtreecommitdiffstats
path: root/apps
diff options
context:
space:
mode:
authorLinus Nielsen Feltzing <linus@haxx.se>2005-04-04 09:12:12 +0000
committerLinus Nielsen Feltzing <linus@haxx.se>2005-04-04 09:12:12 +0000
commit68482bbed286981a5ef09a466620e459790c96e6 (patch)
treede613ede8a83077b935debc1ec3edd965d5d6090 /apps
parent5fb6c64ffc8e6ab7512d805d2860831e492e5c52 (diff)
downloadrockbox-68482bbed286981a5ef09a466620e459790c96e6.tar.gz
rockbox-68482bbed286981a5ef09a466620e459790c96e6.tar.bz2
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')
-rw-r--r--apps/lang/english.lang60
-rw-r--r--apps/recorder/peakmeter.c374
-rw-r--r--apps/recorder/peakmeter.h34
-rw-r--r--apps/recorder/recording.c257
-rw-r--r--apps/settings.c43
-rw-r--r--apps/settings.h14
-rw-r--r--apps/sound_menu.c375
-rw-r--r--apps/sound_menu.h1
8 files changed, 1065 insertions, 93 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 <stdio.h>
#include <stdbool.h>
+#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