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
This commit is contained in:
Linus Nielsen Feltzing 2005-04-04 09:12:12 +00:00
parent 5fb6c64ffc
commit 68482bbed2
10 changed files with 1096 additions and 105 deletions

View file

@ -3009,3 +3009,63 @@ desc: Announce that the RTC alarm has been turned off
eng: "Alarm Disabled" eng: "Alarm Disabled"
voice: "Alarm Disabled" voice: "Alarm Disabled"
new: 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:

View file

@ -22,6 +22,7 @@
#include "kernel.h" #include "kernel.h"
#include "settings.h" #include "settings.h"
#include "lcd.h" #include "lcd.h"
#include "widgets.h"
#include "wps-display.h" #include "wps-display.h"
#include "sprintf.h" #include "sprintf.h"
#include "button.h" #include "button.h"
@ -67,6 +68,24 @@ static unsigned short db_min = 0;
static unsigned short db_max = 9000; static unsigned short db_max = 9000;
static unsigned short db_range = 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 #if CONFIG_HWCODEC == MASNONE
#define MAS_REG_DQPEAK_L 0 #define MAS_REG_DQPEAK_L 0
#define MAS_REG_DQPEAK_R 0 #define MAS_REG_DQPEAK_R 0
@ -124,7 +143,7 @@ static const long clip_time_out[] = {
/* precalculated peak values that represent magical /* precalculated peak values that represent magical
dBfs values. Used to draw the scale */ dBfs values. Used to draw the scale */
#define DB_SCALE_SRC_VALUES_SIZE 11 #define DB_SCALE_SRC_VALUES_SIZE 12
#if 0 #if 0
static const int db_scale_src_values[DB_SCALE_SRC_VALUES_SIZE] = { static const int db_scale_src_values[DB_SCALE_SRC_VALUES_SIZE] = {
32767, /* 0 db */ 32767, /* 0 db */
@ -138,6 +157,7 @@ static const int db_scale_src_values[DB_SCALE_SRC_VALUES_SIZE] = {
328, /* -40 db */ 328, /* -40 db */
104, /* -50 db */ 104, /* -50 db */
33, /* -60 db */ 33, /* -60 db */
1, /* -inf */
}; };
#else #else
static const int db_scale_src_values[DB_SCALE_SRC_VALUES_SIZE] = { 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 */ 373, /* -40 db */
102, /* -50 db */ 102, /* -50 db */
33, /* -60 db */ 33, /* -60 db */
0, /* -inf */
}; };
#endif #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; static int db_scale_count = DB_SCALE_SRC_VALUES_SIZE;
/* if db_scale_valid is false the content of /* 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 * A helper function for peak_meter_db2sample. Don't call it separately but
* use db_to_sample. If one or both of min and max are outside the * 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 * range 0 <= min (or max) < 8961 the behaviour of this function is
* undefined. It may not return. * undefined. It may not return.
* @param int min - The minimum of the value range that is searched. * @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 * @return int - The return value is in the range of
* 0 <= return value < MAX_PEAK * 0 <= return value < MAX_PEAK
*/ */
static int db_to_sample(int db) { int peak_meter_db2sample(int db) {
int retval = 0; int retval = 0;
/* what is the maximum pseudo db value */ /* 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) { void peak_meter_set_min(int newmin) {
if (peak_meter_use_dbfs) { if (peak_meter_use_dbfs) {
peak_meter_range_min = db_to_sample(newmin); peak_meter_range_min = peak_meter_db2sample(newmin);
} else { } else {
if (newmin < peak_meter_range_max) { if (newmin < peak_meter_range_max) {
@ -392,7 +428,7 @@ int peak_meter_get_min(void) {
*/ */
void peak_meter_set_max(int newmax) { void peak_meter_set_max(int newmax) {
if (peak_meter_use_dbfs) { if (peak_meter_use_dbfs) {
peak_meter_range_max = db_to_sample(newmax); peak_meter_range_max = peak_meter_db2sample(newmax);
} else { } else {
if (newmax > peak_meter_range_min) { if (newmax > peak_meter_range_min) {
peak_meter_range_max = newmax * MAX_PEAK / 100; peak_meter_range_max = newmax * MAX_PEAK / 100;
@ -504,6 +540,15 @@ void peak_meter_playback(bool playback)
#endif #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 * Reads peak values from the MAS, and detects clips. The
* values are stored in peak_meter_l peak_meter_r for later * 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]; 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 /* peaks are searched -> we have to find the maximum. When
many calls of peak_meter_peek the maximum value will be many calls of peak_meter_peek the maximum value will be
stored in peak_meter_x. This maximum is reset by the stored in peak_meter_x. This maximum is reset by the
@ -558,37 +718,6 @@ inline void peak_meter_peek(void)
#endif #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. * Reads out the peak volume of the left channel.
* @return int - The maximum value that has been detected * @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); 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 #ifdef PM_DEBUG
/* display a bar to show how many calls to peak_meter_peek /* display a bar to show how many calls to peak_meter_peek
have ocurred since the last display */ 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; 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 #ifdef PM_DEBUG
static void peak_meter_clear_histogram(void) { static void peak_meter_clear_histogram(void) {
int i = 0; int i = 0;

View file

@ -24,12 +24,12 @@
extern bool peak_meter_histogram(void); extern bool peak_meter_histogram(void);
#endif #endif
extern bool peak_meter_enabled; extern bool peak_meter_enabled;
extern int peak_meter_fps; extern int peak_meter_fps;
extern void peak_meter_playback(bool playback); extern void peak_meter_playback(bool playback);
extern void peak_meter_draw(int x, int y, int width, int height); 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_set_clip_hold(int time);
extern void peak_meter_peek(void); extern void peak_meter_peek(void);
extern void peak_meter_init_range( bool dbfs, int range_min, int range_max); extern void peak_meter_init_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 void peak_meter_set_use_dbfs(int use);
extern int peak_meter_get_use_dbfs(void); extern int peak_meter_get_use_dbfs(void);
extern int calc_db (int isample); 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); 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_min;
extern unsigned short peak_meter_range_max; extern unsigned short peak_meter_range_max;

View file

@ -50,6 +50,7 @@
#include "talk.h" #include "talk.h"
#include "atoi.h" #include "atoi.h"
#include "sound.h" #include "sound.h"
#include "ata.h"
#ifdef HAVE_RECORDING #ifdef HAVE_RECORDING
@ -240,6 +241,56 @@ int rec_create_directory(void)
return 0; 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) bool recording_screen(void)
{ {
long button; long button;
@ -252,12 +303,11 @@ bool recording_screen(void)
int update_countdown = 1; int update_countdown = 1;
bool have_recorded = false; bool have_recorded = false;
unsigned int seconds; unsigned int seconds;
unsigned int last_seconds = 0;
int hours, minutes; int hours, minutes;
char path_buffer[MAX_PATH]; char path_buffer[MAX_PATH];
bool been_in_usb_mode = false; bool been_in_usb_mode = false;
bool led_state; int last_mpeg_stat = -1;
int led_delay; bool last_led_stat = false;
const unsigned char *byte_units[] = { const unsigned char *byte_units[] = {
ID2P(LANG_BYTE), ID2P(LANG_BYTE),
@ -267,6 +317,9 @@ bool recording_screen(void)
}; };
cursor = 0; cursor = 0;
#ifndef SIMULATOR
ata_set_led_enabled(false);
#endif
mpeg_init_recording(); mpeg_init_recording();
sound_set(SOUND_VOLUME, global_settings.volume); sound_set(SOUND_VOLUME, global_settings.volume);
@ -288,6 +341,8 @@ bool recording_screen(void)
set_gain(); set_gain();
settings_apply_trigger();
lcd_setfont(FONT_SYSFIXED); lcd_setfont(FONT_SYSFIXED);
lcd_getstringsize("M", &w, &h); lcd_getstringsize("M", &w, &h);
lcd_setmargins(global_settings.invert_cursor ? 0 : w, 8); lcd_setmargins(global_settings.invert_cursor ? 0 : w, 8);
@ -295,45 +350,93 @@ bool recording_screen(void)
if(rec_create_directory() > 0) if(rec_create_directory() > 0)
have_recorded = true; have_recorded = true;
led_state = false;
led_delay = 0;
while(!done) while(!done)
{ {
int mpeg_stat = mpeg_status();
/* /*
* Flash the LED while waiting to record. Turn it on while * Flash the LED while waiting to record. Turn it on while
* recording. * recording.
*/ */
if(mpeg_status() != MPEG_STATUS_RECORD) if(mpeg_stat & MPEG_STATUS_RECORD)
{ {
if(led_delay++ >= 4) if (mpeg_stat & MPEG_STATUS_PAUSE)
{ {
led_state = !led_state; /*
invert_led(led_state); This is supposed to be the same as
led_delay = 0; 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 else
{ {
if(!led_state) /* trigger is on in status TRIG_READY (no check needed) */
led(true);
}
}
else
{ {
led_state = true; int trigStat = peak_meter_trigger_status();
invert_led(true);
// other trigger stati than trig_off and trig_steady
// already imply that we are recording.
if (trigStat == TRIG_STEADY)
{
/* 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) switch(button)
{ {
case REC_STOPEXIT: 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(); mpeg_stop();
} }
else else
{ {
peak_meter_playback(true); peak_meter_playback(true);
peak_meter_enabled = false; peak_meter_enabled = false;
/* turn off the trigger */
peak_meter_set_trigger_listener(NULL);
peak_meter_trigger(false);
done = true; done = true;
} }
update_countdown = 1; /* Update immediately */ update_countdown = 1; /* Update immediately */
@ -341,8 +444,13 @@ bool recording_screen(void)
case REC_RECPAUSE: case REC_RECPAUSE:
/* Only act if the mpeg is stopped */ /* 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; have_recorded = true;
talk_buffer_steal(); /* we use the mp3 buffer */ talk_buffer_steal(); /* we use the mp3 buffer */
mpeg_record(rec_create_filename(path_buffer)); mpeg_record(rec_create_filename(path_buffer));
@ -353,9 +461,22 @@ bool recording_screen(void)
mpeg_beep(HZ/2); /* longer beep on start */ mpeg_beep(HZ/2); /* longer beep on start */
} }
} }
/* this is triggered recording */
else else
{ {
if(mpeg_status() & MPEG_STATUS_PAUSE) 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_stat & MPEG_STATUS_PAUSE)
{ {
mpeg_resume_recording(); mpeg_resume_recording();
if (global_settings.talk_menu) if (global_settings.talk_menu)
@ -473,11 +594,14 @@ bool recording_screen(void)
#ifdef REC_SETTINGS #ifdef REC_SETTINGS
case 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)) if (recording_menu(false))
{
return SYS_USB_CONNECTED; return SYS_USB_CONNECTED;
}
settings_save(); settings_save();
if (global_settings.rec_prerecord_time) if (global_settings.rec_prerecord_time)
@ -491,7 +615,6 @@ bool recording_screen(void)
global_settings.rec_prerecord_time); global_settings.rec_prerecord_time);
set_gain(); set_gain();
update_countdown = 1; /* Update immediately */ update_countdown = 1; /* Update immediately */
lcd_setfont(FONT_SYSFIXED); lcd_setfont(FONT_SYSFIXED);
@ -502,9 +625,10 @@ bool recording_screen(void)
#ifdef REC_F2 #ifdef REC_F2
case 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()) if (f2_rec_screen())
{ {
have_recorded = true; have_recorded = true;
@ -518,16 +642,17 @@ bool recording_screen(void)
#ifdef REC_F3 #ifdef REC_F3
case 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)); mpeg_new_file(rec_create_filename(path_buffer));
last_seconds = 0; last_seconds = 0;
} }
else 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()) if (f3_rec_screen())
{ {
have_recorded = true; have_recorded = true;
@ -542,7 +667,7 @@ bool recording_screen(void)
case SYS_USB_CONNECTED: case SYS_USB_CONNECTED:
/* Only accept USB connection when not recording */ /* 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); default_event_handler(SYS_USB_CONNECTED);
done = true; done = true;
@ -555,8 +680,6 @@ bool recording_screen(void)
break; break;
} }
peak_meter_peek();
if(TIME_AFTER(current_tick, timeout)) if(TIME_AFTER(current_tick, timeout))
{ {
lcd_setfont(FONT_SYSFIXED); lcd_setfont(FONT_SYSFIXED);
@ -585,7 +708,7 @@ bool recording_screen(void)
dseconds = rec_timesplit_seconds(); dseconds = rec_timesplit_seconds();
if(mpeg_status() & MPEG_STATUS_PRERECORD) if(mpeg_stat & MPEG_STATUS_PRERECORD)
{ {
snprintf(buf, 32, "%s...", str(LANG_RECORD_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 /* We will do file splitting regardless, since the OFF
setting really means 24 hours. This is to make sure setting really means 24 hours. This is to make sure
that the recorded files don't get too big. */ 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)); mpeg_new_file(rec_create_filename(path_buffer));
update_countdown = 1; update_countdown = 1;
last_seconds = 0; last_seconds = 0;
} }
peak_meter_draw(0, 8 + h*2, LCD_WIDTH, h);
/* Show mic gain if input source is Mic */ /* Show mic gain if input source is Mic */
if(global_settings.rec_source == 0) if(global_settings.rec_source == 0)
{ {
@ -635,9 +756,9 @@ bool recording_screen(void)
global_settings.rec_mic_gain, global_settings.rec_mic_gain,
buf2, sizeof(buf2))); buf2, sizeof(buf2)));
if (global_settings.invert_cursor && (pos++ == cursor)) if (global_settings.invert_cursor && (pos++ == cursor))
lcd_puts_style(0, 3, buf, STYLE_INVERT); lcd_puts_style(0, 4, buf, STYLE_INVERT);
else else
lcd_puts(0, 3, buf); lcd_puts(0, 4, buf);
} }
else else
{ {
@ -650,53 +771,58 @@ bool recording_screen(void)
fmt_gain(SOUND_LEFT_GAIN, gain, fmt_gain(SOUND_LEFT_GAIN, gain,
buf2, sizeof(buf2))); buf2, sizeof(buf2)));
if (global_settings.invert_cursor && (pos++ == cursor)) if (global_settings.invert_cursor && (pos++ == cursor))
lcd_puts_style(0, 3, buf, STYLE_INVERT); lcd_puts_style(0, 4, buf, STYLE_INVERT);
else else
lcd_puts(0, 3, buf); lcd_puts(0, 4, buf);
snprintf(buf, 32, "%s: %s", str(LANG_RECORDING_LEFT), snprintf(buf, 32, "%s: %s", str(LANG_RECORDING_LEFT),
fmt_gain(SOUND_LEFT_GAIN, fmt_gain(SOUND_LEFT_GAIN,
global_settings.rec_left_gain, global_settings.rec_left_gain,
buf2, sizeof(buf2))); buf2, sizeof(buf2)));
if (global_settings.invert_cursor && (pos++ == cursor)) if (global_settings.invert_cursor && (pos++ == cursor))
lcd_puts_style(0, 4, buf, STYLE_INVERT); lcd_puts_style(0, 5, buf, STYLE_INVERT);
else else
lcd_puts(0, 4, buf); lcd_puts(0, 5, buf);
snprintf(buf, 32, "%s: %s", str(LANG_RECORDING_RIGHT), snprintf(buf, 32, "%s: %s", str(LANG_RECORDING_RIGHT),
fmt_gain(SOUND_RIGHT_GAIN, fmt_gain(SOUND_RIGHT_GAIN,
global_settings.rec_right_gain, global_settings.rec_right_gain,
buf2, sizeof(buf2))); buf2, sizeof(buf2)));
if (global_settings.invert_cursor && (pos++ == cursor)) if (global_settings.invert_cursor && (pos++ == cursor))
lcd_puts_style(0, 5, buf, STYLE_INVERT); lcd_puts_style(0, 6, buf, STYLE_INVERT);
else else
lcd_puts(0, 5, buf); lcd_puts(0, 6, buf);
} }
} }
if(global_settings.rec_source != SOURCE_SPDIF) if(global_settings.rec_source != SOURCE_SPDIF)
put_cursorxy(0, 3 + cursor, true); put_cursorxy(0, 4 + cursor, true);
if (global_settings.rec_source != SOURCE_LINE) {
snprintf(buf, 32, "%s %s [%d]", snprintf(buf, 32, "%s %s [%d]",
freq_str[global_settings.rec_frequency], freq_str[global_settings.rec_frequency],
global_settings.rec_channels? global_settings.rec_channels?
str(LANG_CHANNEL_MONO):str(LANG_CHANNEL_STEREO), str(LANG_CHANNEL_MONO):str(LANG_CHANNEL_STEREO),
global_settings.rec_quality); global_settings.rec_quality);
lcd_puts(0, 6, buf); lcd_puts(0, 6, buf);
}
status_draw(true); status_draw(true);
peak_meter_draw(0, 8 + h*2, LCD_WIDTH, h);
lcd_update(); 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_trig(LCD_WIDTH - TRIG_WIDTH, 4 * h);
peak_meter_draw(0, 8 + h*2, LCD_WIDTH, h); lcd_update_rect(LCD_WIDTH - (TRIG_WIDTH + 2), 4 * h,
lcd_update_rect(0, 8 + h*2, LCD_WIDTH, h); TRIG_WIDTH + 2, TRIG_HEIGHT);
} }
} }
if(mpeg_status() & MPEG_STATUS_ERROR) if(mpeg_stat & MPEG_STATUS_ERROR)
{ {
done = true; done = true;
} }
@ -721,6 +847,10 @@ bool recording_screen(void)
mpeg_init_playback(); mpeg_init_playback();
/* make sure the trigger is really turned off */
peak_meter_trigger(false);
peak_meter_set_trigger_listener(NULL);
sound_settings_apply(); sound_settings_apply();
lcd_setfont(FONT_UI); lcd_setfont(FONT_UI);
@ -728,6 +858,9 @@ bool recording_screen(void)
if (have_recorded) if (have_recorded)
reload_directory(); reload_directory();
#ifndef SIMULATOR
ata_set_led_enabled(true);
#endif
return been_in_usb_mode; return been_in_usb_mode;
/* /*
#endif #endif
@ -883,10 +1016,26 @@ bool f3_rec_screen(void)
lcd_bitmap(bitmap_icons_7x8[Icon_FastBackward], lcd_bitmap(bitmap_icons_7x8[Icon_FastBackward],
LCD_WIDTH/2 - 16, LCD_HEIGHT/2 - 4, 7, 8, true); 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(); lcd_update();
button = button_get(true); button = button_get(true);
switch (button) { 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_LEFT:
case BUTTON_F3 | BUTTON_LEFT: case BUTTON_F3 | BUTTON_LEFT:
global_settings.rec_source++; global_settings.rec_source++;

View file

@ -154,6 +154,11 @@ static const char off_on_ask[] = "off,on,ask";
static const char graphic_numeric[] = "graphic,numeric"; static const char graphic_numeric[] = "graphic,numeric";
static const char off_number_spell_hover[] = "off,number,spell,hover"; 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 /* the part of the settings which ends up in the RTC RAM, where available
(those we either need early, save frequently, or without spinup) */ (those we either need early, save frequently, or without spinup) */
static const struct bit_entry rtc_bits[] = static const struct bit_entry rtc_bits[] =
@ -350,6 +355,14 @@ static const struct bit_entry hd_bits[] =
#ifdef HAVE_RECORDING #ifdef HAVE_RECORDING
{1, S_O(rec_startup), false, "rec screen on startup", off_on }, {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 #endif
/* new stuff to be added at the end */ /* 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]; 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 #endif

View file

@ -102,6 +102,12 @@
#define FF_REWIND_45000 12 #define FF_REWIND_45000 12
#define FF_REWIND_60000 13 #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, /* 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. 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 */ int rec_directory; /* 0=base dir, 1=current dir */
bool rec_startup; /* true means start Rockbox in recording screen */ 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 */ /* device settings */
int contrast; /* lcd contrast: 0-63 0=low 63=high */ 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); void set_file(char* filename, char* setting, int maxlen);
unsigned int rec_timesplit_seconds(void); unsigned int rec_timesplit_seconds(void);
void settings_apply_trigger(void);
/* global settings */ /* global settings */
extern struct user_settings global_settings; extern struct user_settings global_settings;

View file

@ -19,6 +19,7 @@
#include "config.h" #include "config.h"
#include <stdio.h> #include <stdio.h>
#include <stdbool.h> #include <stdbool.h>
#include "system.h"
#include "kernel.h" #include "kernel.h"
#include "lcd.h" #include "lcd.h"
#include "menu.h" #include "menu.h"
@ -29,12 +30,18 @@
#include "screens.h" #include "screens.h"
#ifdef HAVE_LCD_BITMAP #ifdef HAVE_LCD_BITMAP
#include "icons.h" #include "icons.h"
#include "font.h"
#include "widgets.h"
#endif #endif
#include "lang.h" #include "lang.h"
#include "sprintf.h" #include "sprintf.h"
#include "talk.h" #include "talk.h"
#include "misc.h" #include "misc.h"
#include "sound.h" #include "sound.h"
#if CONFIG_HWCODEC == MAS3587F || CONFIG_HWCODEC == MAS3539F
#include "peakmeter.h"
#include "mas.h"
#endif
static const char* const fmt[] = static const char* const fmt[] =
{ {
@ -435,11 +442,373 @@ bool sound_menu(void)
} }
#ifdef HAVE_RECORDING #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) bool recording_menu(bool no_source)
{ {
int m; int m;
int i = 0; int i = 0;
struct menu_item items[9]; struct menu_item items[10];
bool result; bool result;
items[i].desc = ID2P(LANG_RECORDING_QUALITY); items[i].desc = ID2P(LANG_RECORDING_QUALITY);
@ -462,6 +831,10 @@ bool recording_menu(bool no_source)
items[i++].function = recdirectory; items[i++].function = recdirectory;
items[i].desc = ID2P(LANG_RECORD_STARTUP); items[i].desc = ID2P(LANG_RECORD_STARTUP);
items[i++].function = reconstartup; 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); m=menu_init( items, i, NULL, NULL, NULL, NULL);
result = menu_run(m); result = menu_run(m);

View file

@ -23,5 +23,6 @@
bool sound_menu(void); bool sound_menu(void);
bool recording_menu(bool no_source); bool recording_menu(bool no_source);
bool rectrigger(void);
#endif #endif

View file

@ -241,6 +241,8 @@ static volatile unsigned char* ata_control;
bool old_recorder = false; bool old_recorder = false;
int ata_spinup_time = 0; int ata_spinup_time = 0;
static bool ata_led_enabled = true;
static bool ata_led_on = false;
static bool spinup = false; static bool spinup = false;
static bool sleeping = true; static bool sleeping = true;
static long sleep_timeout = 5*HZ; static long sleep_timeout = 5*HZ;
@ -473,6 +475,13 @@ static void copy_read_sectors(unsigned char* buf, int wordcount)
#endif #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,) int ata_read_sectors(IF_MV2(int drive,)
unsigned long start, unsigned long start,
int incount, int incount,
@ -492,21 +501,21 @@ int ata_read_sectors(IF_MV2(int drive,)
last_disk_activity = current_tick; last_disk_activity = current_tick;
spinup_start = current_tick; spinup_start = current_tick;
led(true); ata_led(true);
if ( sleeping ) { if ( sleeping ) {
spinup = true; spinup = true;
if (poweroff) { if (poweroff) {
if (ata_power_on()) { if (ata_power_on()) {
mutex_unlock(&ata_mtx); mutex_unlock(&ata_mtx);
led(false); ata_led(false);
return -1; return -1;
} }
} }
else { else {
if (perform_soft_reset()) { if (perform_soft_reset()) {
mutex_unlock(&ata_mtx); mutex_unlock(&ata_mtx);
led(false); ata_led(false);
return -1; return -1;
} }
} }
@ -518,7 +527,7 @@ int ata_read_sectors(IF_MV2(int drive,)
if (!wait_for_rdy()) if (!wait_for_rdy())
{ {
mutex_unlock(&ata_mtx); mutex_unlock(&ata_mtx);
led(false); ata_led(false);
return -2; return -2;
} }
@ -614,7 +623,7 @@ int ata_read_sectors(IF_MV2(int drive,)
} }
break; break;
} }
led(false); ata_led(false);
mutex_unlock(&ata_mtx); mutex_unlock(&ata_mtx);
@ -775,21 +784,21 @@ int ata_write_sectors(IF_MV2(int drive,)
last_disk_activity = current_tick; last_disk_activity = current_tick;
spinup_start = current_tick; spinup_start = current_tick;
led(true); ata_led(true);
if ( sleeping ) { if ( sleeping ) {
spinup = true; spinup = true;
if (poweroff) { if (poweroff) {
if (ata_power_on()) { if (ata_power_on()) {
mutex_unlock(&ata_mtx); mutex_unlock(&ata_mtx);
led(false); ata_led(false);
return -1; return -1;
} }
} }
else { else {
if (perform_soft_reset()) { if (perform_soft_reset()) {
mutex_unlock(&ata_mtx); mutex_unlock(&ata_mtx);
led(false); ata_led(false);
return -1; return -1;
} }
} }
@ -799,7 +808,7 @@ int ata_write_sectors(IF_MV2(int drive,)
if (!wait_for_rdy()) if (!wait_for_rdy())
{ {
mutex_unlock(&ata_mtx); mutex_unlock(&ata_mtx);
led(false); ata_led(false);
return -2; return -2;
} }
@ -843,7 +852,7 @@ int ata_write_sectors(IF_MV2(int drive,)
ret = -4; ret = -4;
} }
led(false); ata_led(false);
mutex_unlock(&ata_mtx); mutex_unlock(&ata_mtx);
@ -1042,9 +1051,9 @@ static void ata_thread(void)
case SYS_USB_CONNECTED: case SYS_USB_CONNECTED:
if (poweroff) { if (poweroff) {
mutex_lock(&ata_mtx); mutex_lock(&ata_mtx);
led(true); ata_led(true);
ata_power_on(); ata_power_on();
led(false); ata_led(false);
mutex_unlock(&ata_mtx); mutex_unlock(&ata_mtx);
} }
@ -1384,7 +1393,7 @@ int ata_init(void)
mutex_init(&ata_mtx); mutex_init(&ata_mtx);
led(false); ata_led(false);
#if CONFIG_CPU == SH7034 #if CONFIG_CPU == SH7034
/* Port A setup */ /* Port A setup */
@ -1458,3 +1467,12 @@ int ata_init(void)
return 0; return 0;
} }
void ata_set_led_enabled(bool enabled) {
ata_led_enabled = enabled;
if (ata_led_enabled) {
led(ata_led_on);
} else {
led(false);
}
}

View file

@ -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_delayed_write(unsigned long sector, const void* buf);
extern void ata_flush(void); extern void ata_flush(void);
extern void ata_spin(void); extern void ata_spin(void);
extern void ata_set_led_enabled(bool enabled);
extern unsigned short* ata_get_identify(void); extern unsigned short* ata_get_identify(void);
extern long last_disk_activity; extern long last_disk_activity;