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"
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:

View file

@ -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;

View file

@ -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;

View file

@ -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)
{
led_state = !led_state;
invert_led(led_state);
led_delay = 0;
/*
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
{
if(!led_state)
/* trigger is on in status TRIG_READY (no check needed) */
led(true);
}
}
else
{
led_state = true;
invert_led(true);
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)
{
/* 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
{
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();
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);
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++;

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 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

View file

@ -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;

View file

@ -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);

View file

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

View file

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

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_flush(void);
extern void ata_spin(void);
extern void ata_set_led_enabled(bool enabled);
extern unsigned short* ata_get_identify(void);
extern long last_disk_activity;