summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--apps/lang/english.lang60
-rw-r--r--apps/recorder/peakmeter.c374
-rw-r--r--apps/recorder/peakmeter.h34
-rw-r--r--apps/recorder/recording.c257
-rw-r--r--apps/settings.c43
-rw-r--r--apps/settings.h14
-rw-r--r--apps/sound_menu.c375
-rw-r--r--apps/sound_menu.h1
-rw-r--r--firmware/drivers/ata.c44
-rw-r--r--firmware/export/ata.h1
10 files changed, 1097 insertions, 106 deletions
diff --git a/apps/lang/english.lang b/apps/lang/english.lang
index a9f3dac6e4..f327595b11 100644
--- a/apps/lang/english.lang
+++ b/apps/lang/english.lang
@@ -3009,3 +3009,63 @@ desc: Announce that the RTC alarm has been turned off
3009eng: "Alarm Disabled" 3009eng: "Alarm Disabled"
3010voice: "Alarm Disabled" 3010voice: "Alarm Disabled"
3011new: 3011new:
3012
3013###########################
3014id: LANG_RECORD_TRIGGER
3015desc: in recording settings_menu
3016eng: "Trigger"
3017new:
3018
3019id: LANG_RECORD_START_THRESHOLD
3020desc: in recording settings_menu
3021eng: "Start above"
3022new:
3023
3024id: LANG_RECORD_MIN_DURATION
3025desc: in recording settings_menu
3026eng: "for at least"
3027new:
3028
3029id: LANG_RECORD_STOP_THRESHOLD
3030desc: in recording settings_menu
3031eng: "Stop below"
3032new:
3033
3034id: LANG_RECORD_STOP_POSTREC
3035desc: in recording settings_menu
3036eng: "for at least"
3037new:
3038
3039id: LANG_RECORD_STOP_GAP
3040desc: in recording settings_menu
3041eng: "presplit gap"
3042new:
3043
3044id: LANG_RECORD_TRIGGER_MODE
3045desc: in recording settings_menu
3046eng: "Trigger"
3047new:
3048
3049id: LANG_RECORD_TRIG_NOREARM
3050desc: in recording settings_menu
3051eng: "one"
3052new:
3053
3054id: LANG_RECORD_TRIG_REARM
3055desc: in recording settings_menu
3056eng: "repeat"
3057new:
3058
3059id: LANG_DB_INF
3060desc: -inf db for values below measurement
3061eng: "-inf"
3062new:
3063
3064id: LANG_RECORD_TRIG_IDLE
3065desc: waiting for threshold
3066eng: "Trigger idle"
3067new:
3068
3069id: LANG_RECORD_TRIGGER_ACTIVE
3070eng: "Trigger active"
3071new:
diff --git a/apps/recorder/peakmeter.c b/apps/recorder/peakmeter.c
index 41d375e076..0481e25acd 100644
--- a/apps/recorder/peakmeter.c
+++ b/apps/recorder/peakmeter.c
@@ -22,6 +22,7 @@
22#include "kernel.h" 22#include "kernel.h"
23#include "settings.h" 23#include "settings.h"
24#include "lcd.h" 24#include "lcd.h"
25#include "widgets.h"
25#include "wps-display.h" 26#include "wps-display.h"
26#include "sprintf.h" 27#include "sprintf.h"
27#include "button.h" 28#include "button.h"
@@ -67,6 +68,24 @@ static unsigned short db_min = 0;
67static unsigned short db_max = 9000; 68static unsigned short db_max = 9000;
68static unsigned short db_range = 9000; 69static unsigned short db_range = 9000;
69 70
71static unsigned short trig_strt_threshold;
72static long trig_strt_duration;
73static long trig_strt_dropout;
74static unsigned short trig_stp_threshold;
75static long trig_stp_hold;
76static long trig_rstrt_gap;
77
78/* point in time when the threshold was exceeded */
79static long trig_hightime;
80
81/* point in time when the volume fell below the threshold*/
82static long trig_lowtime;
83
84/* The output value of the trigger. See TRIG_XXX constants vor valid values */
85static int trig_status = TRIG_OFF;
86
87static void (*trigger_listener)(int) = NULL;
88
70#if CONFIG_HWCODEC == MASNONE 89#if CONFIG_HWCODEC == MASNONE
71#define MAS_REG_DQPEAK_L 0 90#define MAS_REG_DQPEAK_L 0
72#define MAS_REG_DQPEAK_R 0 91#define MAS_REG_DQPEAK_R 0
@@ -124,7 +143,7 @@ static const long clip_time_out[] = {
124 143
125/* precalculated peak values that represent magical 144/* precalculated peak values that represent magical
126 dBfs values. Used to draw the scale */ 145 dBfs values. Used to draw the scale */
127#define DB_SCALE_SRC_VALUES_SIZE 11 146#define DB_SCALE_SRC_VALUES_SIZE 12
128#if 0 147#if 0
129static const int db_scale_src_values[DB_SCALE_SRC_VALUES_SIZE] = { 148static const int db_scale_src_values[DB_SCALE_SRC_VALUES_SIZE] = {
130 32767, /* 0 db */ 149 32767, /* 0 db */
@@ -138,6 +157,7 @@ static const int db_scale_src_values[DB_SCALE_SRC_VALUES_SIZE] = {
138 328, /* -40 db */ 157 328, /* -40 db */
139 104, /* -50 db */ 158 104, /* -50 db */
140 33, /* -60 db */ 159 33, /* -60 db */
160 1, /* -inf */
141}; 161};
142#else 162#else
143static const int db_scale_src_values[DB_SCALE_SRC_VALUES_SIZE] = { 163static 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] = {
152 373, /* -40 db */ 172 373, /* -40 db */
153 102, /* -50 db */ 173 102, /* -50 db */
154 33, /* -60 db */ 174 33, /* -60 db */
175 0, /* -inf */
155}; 176};
156#endif 177#endif
157 178
179const char* peak_meter_dbnames[DB_SCALE_SRC_VALUES_SIZE] = {
180 "0 db",
181 "-3 db",
182 "-6 db",
183 "-9 db",
184 "-12 db",
185 "-18 db",
186 "-24 db",
187 "-30 db",
188 "-40 db",
189 "-50 db",
190 "-60 db",
191 "-inf",
192};
193
158static int db_scale_count = DB_SCALE_SRC_VALUES_SIZE; 194static int db_scale_count = DB_SCALE_SRC_VALUES_SIZE;
159 195
160/* if db_scale_valid is false the content of 196/* if db_scale_valid is false the content of
@@ -275,8 +311,8 @@ int calc_db (int isample) {
275 311
276 312
277/** 313/**
278 * A helper function for db_to_sample. Don't call it separately but 314 * A helper function for peak_meter_db2sample. Don't call it separately but
279 * use db_to_sample. If one or both of min and max are outside the 315 * use peak_meter_db2sample. If one or both of min and max are outside the
280 * range 0 <= min (or max) < 8961 the behaviour of this function is 316 * range 0 <= min (or max) < 8961 the behaviour of this function is
281 * undefined. It may not return. 317 * undefined. It may not return.
282 * @param int min - The minimum of the value range that is searched. 318 * @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){
312 * @return int - The return value is in the range of 348 * @return int - The return value is in the range of
313 * 0 <= return value < MAX_PEAK 349 * 0 <= return value < MAX_PEAK
314 */ 350 */
315static int db_to_sample(int db) { 351int peak_meter_db2sample(int db) {
316 int retval = 0; 352 int retval = 0;
317 353
318 /* what is the maximum pseudo db value */ 354 /* what is the maximum pseudo db value */
@@ -351,7 +387,7 @@ static int db_to_sample(int db) {
351 */ 387 */
352void peak_meter_set_min(int newmin) { 388void peak_meter_set_min(int newmin) {
353 if (peak_meter_use_dbfs) { 389 if (peak_meter_use_dbfs) {
354 peak_meter_range_min = db_to_sample(newmin); 390 peak_meter_range_min = peak_meter_db2sample(newmin);
355 391
356 } else { 392 } else {
357 if (newmin < peak_meter_range_max) { 393 if (newmin < peak_meter_range_max) {
@@ -392,7 +428,7 @@ int peak_meter_get_min(void) {
392 */ 428 */
393void peak_meter_set_max(int newmax) { 429void peak_meter_set_max(int newmax) {
394 if (peak_meter_use_dbfs) { 430 if (peak_meter_use_dbfs) {
395 peak_meter_range_max = db_to_sample(newmax); 431 peak_meter_range_max = peak_meter_db2sample(newmax);
396 } else { 432 } else {
397 if (newmax > peak_meter_range_min) { 433 if (newmax > peak_meter_range_min) {
398 peak_meter_range_max = newmax * MAX_PEAK / 100; 434 peak_meter_range_max = newmax * MAX_PEAK / 100;
@@ -504,6 +540,15 @@ void peak_meter_playback(bool playback)
504#endif 540#endif
505} 541}
506 542
543static void set_trig_status(int new_state) {
544 if (trig_status != new_state) {
545 trig_status = new_state;
546 if (trigger_listener != NULL) {
547 trigger_listener(trig_status);
548 }
549 }
550}
551
507/** 552/**
508 * Reads peak values from the MAS, and detects clips. The 553 * Reads peak values from the MAS, and detects clips. The
509 * values are stored in peak_meter_l peak_meter_r for later 554 * values are stored in peak_meter_l peak_meter_r for later
@@ -546,6 +591,121 @@ inline void peak_meter_peek(void)
546 current_tick + clip_time_out[peak_meter_clip_hold]; 591 current_tick + clip_time_out[peak_meter_clip_hold];
547 } 592 }
548 593
594 switch (trig_status) {
595 case TRIG_READY:
596 /* no more changes, if trigger was activated as release trigger */
597 /* threshold exceeded? */
598 if ((left > trig_strt_threshold) || (right > trig_strt_threshold)) {
599 if (trig_strt_duration) {
600 /* reset trigger duration */
601 trig_hightime = current_tick;
602
603 /* reset dropout duration */
604 trig_lowtime = current_tick;
605
606 /* if trig_duration is set to 0 the user wants to start
607 recording immediately */
608 set_trig_status(TRIG_STEADY);
609 } else {
610 set_trig_status(TRIG_GO);
611 }
612 }
613 break;
614
615 case TRIG_STEADY:
616 case TRIG_RETRIG:
617 /* trigger duration exceeded */
618 if (current_tick - trig_hightime > trig_strt_duration) {
619 set_trig_status(TRIG_GO);
620 } else {
621 /* threshold exceeded? */
622 if ((left > trig_strt_threshold) ||
623 (right > trig_strt_threshold)) {
624 /* reset lowtime */
625 trig_lowtime = current_tick;
626 }
627 /* volume is below threshold */
628 else {
629 /* dropout occurred? */
630 if (current_tick - trig_lowtime > trig_strt_dropout){
631 if (trig_status == TRIG_STEADY){
632 set_trig_status(TRIG_READY);
633 }
634 /* trig_status == TRIG_RETRIG */
635 else {
636 /* the gap has already expired */
637 trig_lowtime = current_tick - trig_rstrt_gap - 1;
638 set_trig_status(TRIG_POSTREC);
639 }
640 }
641 }
642 }
643 break;
644
645 case TRIG_GO:
646 case TRIG_CONTINUE:
647 /* threshold exceeded? */
648 if ((left > trig_stp_threshold) || (right > trig_stp_threshold)) {
649 /* restart hold time countdown */
650 trig_lowtime = current_tick;
651 } else {
652 set_trig_status(TRIG_POSTREC);
653 trig_hightime = current_tick;
654 }
655 break;
656
657 case TRIG_POSTREC:
658 /* gap time expired? */
659 if (current_tick - trig_lowtime > trig_rstrt_gap){
660 /* start threshold exceeded? */
661 if ((left > trig_strt_threshold) ||
662 (right > trig_strt_threshold)) {
663
664 set_trig_status(TRIG_RETRIG);
665 trig_hightime = current_tick;
666 }
667 else
668
669 /* stop threshold exceeded */
670 if ((left > trig_stp_threshold) ||
671 (right > trig_stp_threshold)) {
672 if (current_tick - trig_hightime > trig_stp_hold){
673 trig_lowtime = current_tick;
674 set_trig_status(TRIG_CONTINUE);
675 } else {
676 trig_lowtime = current_tick - trig_rstrt_gap - 1;
677 }
678 }
679
680 /* below any threshold */
681 else {
682 if (current_tick - trig_lowtime > trig_stp_hold){
683 set_trig_status(TRIG_READY);
684 } else {
685 trig_hightime = current_tick;
686 }
687 }
688 }
689
690 /* still within the gap time */
691 else {
692 /* stop threshold exceeded */
693 if ((left > trig_stp_threshold) ||
694 (right > trig_stp_threshold)) {
695 set_trig_status(TRIG_CONTINUE);
696 trig_lowtime = current_tick;
697 }
698
699 /* hold time expired */
700 else if (current_tick - trig_lowtime > trig_stp_hold){
701 trig_hightime = current_tick;
702 trig_lowtime = current_tick;
703 set_trig_status(TRIG_READY);
704 }
705 }
706 break;
707 }
708
549 /* peaks are searched -> we have to find the maximum. When 709 /* peaks are searched -> we have to find the maximum. When
550 many calls of peak_meter_peek the maximum value will be 710 many calls of peak_meter_peek the maximum value will be
551 stored in peak_meter_x. This maximum is reset by the 711 stored in peak_meter_x. This maximum is reset by the
@@ -558,37 +718,6 @@ inline void peak_meter_peek(void)
558#endif 718#endif
559} 719}
560 720
561
562/**
563 * The thread function for the peak meter calls peak_meter_peek
564 * to reas out the mas and find maxima, clips, etc. No display
565 * is performed.
566 */
567/*
568void peak_meter_thread(void) {
569 sleep(5000);
570 while (1) {
571 if (peak_meter_enabled && peak_meter_use_thread){
572 peak_meter_peek();
573 }
574 yield();
575 }
576}
577*/
578
579/**
580 * Creates the peak meter thread
581 */
582/*
583void peak_meter_init(void) {
584 create_thread(
585 peak_meter_thread,
586 peak_meter_stack,
587 sizeof peak_meter_stack,
588 "peakmeter");
589}
590*/
591
592/** 721/**
593 * Reads out the peak volume of the left channel. 722 * Reads out the peak volume of the left channel.
594 * @return int - The maximum value that has been detected 723 * @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) {
842 lcd_invertpixel(db_scale_lcd_coord[i], y + height / 2 - 1); 971 lcd_invertpixel(db_scale_lcd_coord[i], y + height / 2 - 1);
843 } 972 }
844 973
974 if (trig_status != TRIG_OFF) {
975 int start_trigx, stop_trigx, ycenter;
976
977 ycenter = y + height / 2;
978 /* display threshold value */
979 start_trigx = x+peak_meter_scale_value(trig_strt_threshold,meterwidth);
980 lcd_drawline(start_trigx, ycenter - 2, start_trigx, ycenter);
981 start_trigx ++;
982 if (start_trigx < LCD_WIDTH) lcd_drawpixel(start_trigx, ycenter - 1);
983
984 stop_trigx = x + peak_meter_scale_value(trig_stp_threshold,meterwidth);
985 lcd_drawline(stop_trigx, ycenter - 2, stop_trigx, ycenter);
986 if (stop_trigx > 0) lcd_drawpixel(stop_trigx - 1, ycenter - 1);
987
988 }
989
845#ifdef PM_DEBUG 990#ifdef PM_DEBUG
846 /* display a bar to show how many calls to peak_meter_peek 991 /* display a bar to show how many calls to peak_meter_peek
847 have ocurred since the last display */ 992 have ocurred since the last display */
@@ -866,6 +1011,161 @@ void peak_meter_draw(int x, int y, int width, int height) {
866 last_right = right; 1011 last_right = right;
867} 1012}
868 1013
1014/**
1015 * Defines the parameters of the trigger. After these parameters are defined
1016 * the trigger can be started either by peak_meter_attack_trigger or by
1017 * peak_meter_release_trigger. Note that you can pass either linear (%) or
1018 * logarithmic (db) values to the thresholds. Positive values are intepreted as
1019 * percent (0 is 0% .. 100 is 100%). Negative values are interpreted as db.
1020 * To avoid ambiguosity of the value 0 the negative values are shifted by -1.
1021 * Thus -75 is -74db .. -1 is 0db.
1022 * @param start_threshold - The threshold used for attack trigger. Negative
1023 * values are interpreted as db -1, positive as %.
1024 * @param start_duration - The minimum time span within which start_threshold
1025 * must be exceeded to fire the attack trigger.
1026 * @param start_dropout - The maximum time span the level may fall below
1027 * start_threshold without releasing the attack trigger.
1028 * @param stop_threshold - The threshold the volume must fall below to release
1029 * the release trigger.Negative values are
1030 * interpreted as db -1, positive as %.
1031 * @param stop_hold - The minimum time the volume must fall below the
1032 * stop_threshold to release the trigger.
1033 * @param
1034 */
1035void peak_meter_define_trigger(
1036 int start_threshold,
1037 long start_duration,
1038 long start_dropout,
1039 int stop_threshold,
1040 long stop_hold_time,
1041 long restart_gap
1042 )
1043{
1044 if (start_threshold < 0) {
1045 /* db */
1046 if (start_threshold < -89) {
1047 trig_strt_threshold = 0;
1048 } else {
1049 trig_strt_threshold =peak_meter_db2sample((start_threshold+1)*100);
1050 }
1051 } else {
1052 /* linear percent */
1053 trig_strt_threshold = start_threshold * MAX_PEAK / 100;
1054 }
1055 trig_strt_duration = start_duration;
1056 trig_strt_dropout = start_dropout;
1057 if (stop_threshold < 0) {
1058 /* db */
1059 trig_stp_threshold = peak_meter_db2sample((stop_threshold + 1) * 100);
1060 } else {
1061 /* linear percent */
1062 trig_stp_threshold = stop_threshold * MAX_PEAK / 100;
1063 }
1064 trig_stp_hold = stop_hold_time;
1065 trig_rstrt_gap = restart_gap;
1066}
1067
1068/**
1069 * Enables or disables the trigger.
1070 * @param on - If true the trigger is turned on.
1071 */
1072void peak_meter_trigger(bool on) {
1073 /* don't use set_trigger here as that would fire an undesired event */
1074 trig_status = on ? TRIG_READY : TRIG_OFF;
1075}
1076
1077/**
1078 * Registers the listener function that listenes on trig_status changes.
1079 * @param listener - The function that is called with each change of
1080 * trig_status. May be set to NULL if no callback is desired.
1081 */
1082void peak_meter_set_trigger_listener(void (*listener)(int status)) {
1083 trigger_listener = listener;
1084}
1085
1086/**
1087 * Fetches the status of the trigger.
1088 * TRIG_OFF: the trigger is inactive
1089 * TRIG_RELEASED: The volume level is below the threshold
1090 * TRIG_ACTIVATED: The volume level has exceeded the threshold, but the trigger
1091 * hasn't been fired yet.
1092 * TRIG_FIRED: The volume exceeds the threshold
1093 *
1094 * To activate the trigger call either peak_meter_attack_trigger or
1095 * peak_meter_release_trigger. To turn the trigger off call
1096 * peak_meter_trigger_off.
1097 */
1098int peak_meter_trigger_status(void) {
1099 return trig_status; /* & TRIG_PIT_MASK;*/
1100}
1101
1102void peak_meter_draw_trig(int xpos, int ypos) {
1103 int x = xpos + ICON_PLAY_STATE_WIDTH + 1;
1104 switch (trig_status) {
1105 long time_left;
1106
1107 case TRIG_READY:
1108 scrollbar(x, ypos + 1, TRIGBAR_WIDTH, TRIG_HEIGHT - 2,
1109 TRIGBAR_WIDTH, 0, 0, HORIZONTAL);
1110 lcd_bitmap(bitmap_icons_7x8[Icon_Stop], xpos, ypos,
1111 ICON_PLAY_STATE_WIDTH, STATUSBAR_HEIGHT, false);
1112 break;
1113
1114 case TRIG_STEADY:
1115 case TRIG_RETRIG:
1116 time_left = trig_strt_duration - (current_tick - trig_hightime);
1117 time_left = time_left * TRIGBAR_WIDTH / trig_strt_duration;
1118 scrollbar(x, ypos + 1, TRIGBAR_WIDTH, TRIG_HEIGHT - 2,
1119 TRIGBAR_WIDTH, 0, TRIGBAR_WIDTH - time_left, HORIZONTAL);
1120 lcd_bitmap(bitmap_icons_7x8[Icon_Stop], xpos, ypos,
1121 ICON_PLAY_STATE_WIDTH, STATUSBAR_HEIGHT, false);
1122 break;
1123
1124 case TRIG_GO:
1125 case TRIG_CONTINUE:
1126 scrollbar(x, ypos + 1, TRIGBAR_WIDTH, TRIG_HEIGHT - 2,
1127 TRIGBAR_WIDTH, TRIGBAR_WIDTH, TRIGBAR_WIDTH, HORIZONTAL);
1128 lcd_bitmap(bitmap_icons_7x8[Icon_Record],
1129 TRIG_WIDTH - ICON_PLAY_STATE_WIDTH, ypos,
1130 ICON_PLAY_STATE_WIDTH, STATUSBAR_HEIGHT, false);
1131 break;
1132
1133 case TRIG_POSTREC:
1134 time_left = trig_stp_hold - (current_tick - trig_lowtime);
1135 time_left = time_left * TRIGBAR_WIDTH / trig_stp_hold;
1136 scrollbar(x, ypos + 1, TRIGBAR_WIDTH, TRIG_HEIGHT - 2,
1137 TRIGBAR_WIDTH, time_left, TRIGBAR_WIDTH, HORIZONTAL);
1138 lcd_bitmap(bitmap_icons_7x8[Icon_Record],
1139 TRIG_WIDTH - ICON_PLAY_STATE_WIDTH, ypos,
1140 ICON_PLAY_STATE_WIDTH, STATUSBAR_HEIGHT, false);
1141 break;
1142 }
1143
1144}
1145
1146int peak_meter_draw_get_btn(int x, int y, int width, int height)
1147{
1148 int button;
1149 long next_refresh = current_tick;
1150 long next_big_refresh = current_tick + HZ / 10;
1151 button = BUTTON_NONE;
1152 while (!TIME_AFTER(current_tick, next_big_refresh)) {
1153 button = button_get(false);
1154 if (button != BUTTON_NONE) {
1155 break;
1156 }
1157 peak_meter_peek();
1158 yield();
1159
1160 if (TIME_AFTER(current_tick, next_refresh)) {
1161 peak_meter_draw(x, y, width, height);
1162 lcd_update_rect(x, y, width, height);
1163 next_refresh = current_tick + HZ / peak_meter_fps;
1164 }
1165 }
1166 return button;
1167}
1168
869#ifdef PM_DEBUG 1169#ifdef PM_DEBUG
870static void peak_meter_clear_histogram(void) { 1170static void peak_meter_clear_histogram(void) {
871 int i = 0; 1171 int i = 0;
diff --git a/apps/recorder/peakmeter.h b/apps/recorder/peakmeter.h
index db419a0afa..3c0a28bf3b 100644
--- a/apps/recorder/peakmeter.h
+++ b/apps/recorder/peakmeter.h
@@ -24,12 +24,12 @@
24extern bool peak_meter_histogram(void); 24extern bool peak_meter_histogram(void);
25#endif 25#endif
26 26
27
28extern bool peak_meter_enabled; 27extern bool peak_meter_enabled;
29extern int peak_meter_fps; 28extern int peak_meter_fps;
30 29
31extern void peak_meter_playback(bool playback); 30extern void peak_meter_playback(bool playback);
32extern void peak_meter_draw(int x, int y, int width, int height); 31extern void peak_meter_draw(int x, int y, int width, int height);
32extern int peak_meter_draw_get_btn(int x, int y, int width, int height);
33extern void peak_meter_set_clip_hold(int time); 33extern void peak_meter_set_clip_hold(int time);
34extern void peak_meter_peek(void); 34extern void peak_meter_peek(void);
35extern void peak_meter_init_range( bool dbfs, int range_min, int range_max); 35extern void peak_meter_init_range( bool dbfs, int range_min, int range_max);
@@ -42,8 +42,40 @@ extern int peak_meter_get_max(void);
42extern void peak_meter_set_use_dbfs(int use); 42extern void peak_meter_set_use_dbfs(int use);
43extern int peak_meter_get_use_dbfs(void); 43extern int peak_meter_get_use_dbfs(void);
44extern int calc_db (int isample); 44extern int calc_db (int isample);
45extern int peak_meter_db2sample(int db);
45extern unsigned short peak_meter_scale_value(unsigned short val, int meterwidth); 46extern unsigned short peak_meter_scale_value(unsigned short val, int meterwidth);
46 47
48/* valid values for trigger_status */
49#define TRIG_OFF 0x00
50#define TRIG_READY 0x01
51#define TRIG_STEADY 0x02
52#define TRIG_GO 0x03
53#define TRIG_POSTREC 0x04
54#define TRIG_RETRIG 0x05
55#define TRIG_CONTINUE 0x06
56
57extern void peak_meter_define_trigger(
58 int start_threshold,
59 long start_duration,
60 long start_dropout,
61 int stop_threshold,
62 long stop_hold_time,
63 long restart_gap
64 );
65
66extern void peak_meter_trigger(bool on);
67extern int peak_meter_trigger_status(void);
68extern void peak_meter_set_trigger_listener(void (*listener)(int status));
69
70//#define TRIG_WIDTH 12
71//#define TRIG_HEIGHT 14
72
73#define TRIG_WIDTH 112
74#define TRIG_HEIGHT 8
75#define TRIGBAR_WIDTH (TRIG_WIDTH - (2 * (ICON_PLAY_STATE_WIDTH + 1)))
76
77extern void peak_meter_draw_trig(int x, int y);
78
47extern unsigned short peak_meter_range_min; 79extern unsigned short peak_meter_range_min;
48extern unsigned short peak_meter_range_max; 80extern unsigned short peak_meter_range_max;
49 81
diff --git a/apps/recorder/recording.c b/apps/recorder/recording.c
index 7aa6aba98c..39e94739b5 100644
--- a/apps/recorder/recording.c
+++ b/apps/recorder/recording.c
@@ -50,6 +50,7 @@
50#include "talk.h" 50#include "talk.h"
51#include "atoi.h" 51#include "atoi.h"
52#include "sound.h" 52#include "sound.h"
53#include "ata.h"
53 54
54#ifdef HAVE_RECORDING 55#ifdef HAVE_RECORDING
55 56
@@ -240,6 +241,56 @@ int rec_create_directory(void)
240 return 0; 241 return 0;
241} 242}
242 243
244static char path_buffer[MAX_PATH];
245
246/* used in trigger_listerner and recording_screen */
247static unsigned int last_seconds = 0;
248
249/**
250 * Callback function so that the peak meter code can send an event
251 * to this application. This function can be passed to
252 * peak_meter_set_trigger_listener in order to activate the trigger.
253 */
254static void trigger_listener(int trigger_status)
255{
256 switch (trigger_status)
257 {
258 case TRIG_GO:
259 if((mpeg_status() & MPEG_STATUS_RECORD) != MPEG_STATUS_RECORD)
260 {
261 talk_buffer_steal(); /* we use the mp3 buffer */
262 mpeg_record(rec_create_filename(path_buffer));
263
264 /* give control to mpeg thread so that it can start recording */
265 yield(); yield(); yield();
266 }
267
268 /* if we're already recording this is a retrigger */
269 else
270 {
271 mpeg_new_file(rec_create_filename(path_buffer));
272 /* tell recording_screen to reset the time */
273 last_seconds = 0;
274 }
275 break;
276
277 /* A _change_ to TRIG_READY means the current recording has stopped */
278 case TRIG_READY:
279 if(mpeg_status() & MPEG_STATUS_RECORD)
280 {
281 mpeg_stop();
282 if (global_settings.rec_trigger_mode != TRIG_MODE_REARM)
283 {
284 peak_meter_set_trigger_listener(NULL);
285 peak_meter_trigger(false);
286 }
287 }
288 break;
289 }
290}
291
292#define BLINK_MASK 0x10
293
243bool recording_screen(void) 294bool recording_screen(void)
244{ 295{
245 long button; 296 long button;
@@ -252,12 +303,11 @@ bool recording_screen(void)
252 int update_countdown = 1; 303 int update_countdown = 1;
253 bool have_recorded = false; 304 bool have_recorded = false;
254 unsigned int seconds; 305 unsigned int seconds;
255 unsigned int last_seconds = 0;
256 int hours, minutes; 306 int hours, minutes;
257 char path_buffer[MAX_PATH]; 307 char path_buffer[MAX_PATH];
258 bool been_in_usb_mode = false; 308 bool been_in_usb_mode = false;
259 bool led_state; 309 int last_mpeg_stat = -1;
260 int led_delay; 310 bool last_led_stat = false;
261 311
262 const unsigned char *byte_units[] = { 312 const unsigned char *byte_units[] = {
263 ID2P(LANG_BYTE), 313 ID2P(LANG_BYTE),
@@ -267,6 +317,9 @@ bool recording_screen(void)
267 }; 317 };
268 318
269 cursor = 0; 319 cursor = 0;
320#ifndef SIMULATOR
321 ata_set_led_enabled(false);
322#endif
270 mpeg_init_recording(); 323 mpeg_init_recording();
271 324
272 sound_set(SOUND_VOLUME, global_settings.volume); 325 sound_set(SOUND_VOLUME, global_settings.volume);
@@ -288,6 +341,8 @@ bool recording_screen(void)
288 341
289 set_gain(); 342 set_gain();
290 343
344 settings_apply_trigger();
345
291 lcd_setfont(FONT_SYSFIXED); 346 lcd_setfont(FONT_SYSFIXED);
292 lcd_getstringsize("M", &w, &h); 347 lcd_getstringsize("M", &w, &h);
293 lcd_setmargins(global_settings.invert_cursor ? 0 : w, 8); 348 lcd_setmargins(global_settings.invert_cursor ? 0 : w, 8);
@@ -295,45 +350,93 @@ bool recording_screen(void)
295 if(rec_create_directory() > 0) 350 if(rec_create_directory() > 0)
296 have_recorded = true; 351 have_recorded = true;
297 352
298 led_state = false;
299 led_delay = 0;
300
301 while(!done) 353 while(!done)
302 { 354 {
355 int mpeg_stat = mpeg_status();
356
303 /* 357 /*
304 * Flash the LED while waiting to record. Turn it on while 358 * Flash the LED while waiting to record. Turn it on while
305 * recording. 359 * recording.
306 */ 360 */
307 if(mpeg_status() != MPEG_STATUS_RECORD) 361 if(mpeg_stat & MPEG_STATUS_RECORD)
308 { 362 {
309 if(led_delay++ >= 4) 363 if (mpeg_stat & MPEG_STATUS_PAUSE)
364 {
365 /*
366 This is supposed to be the same as
367 led(current_tick & BLINK_MASK)
368 But we do this hubub to prevent unnecessary hardware
369 communication when the led already has the desired state.
370 */
371 if (last_led_stat != ((current_tick & BLINK_MASK) == BLINK_MASK))
372 {
373 /* trigger is on in status TRIG_READY (no check needed) */
374 last_led_stat = !last_led_stat;
375 led(last_led_stat);
376 }
377 }
378 else
310 { 379 {
311 led_state = !led_state; 380 /* trigger is on in status TRIG_READY (no check needed) */
312 invert_led(led_state); 381 led(true);
313 led_delay = 0;
314 } 382 }
315 } 383 }
316 else 384 else
317 { 385 {
318 if(!led_state) 386 int trigStat = peak_meter_trigger_status();
387
388 // other trigger stati than trig_off and trig_steady
389 // already imply that we are recording.
390 if (trigStat == TRIG_STEADY)
319 { 391 {
320 led_state = true; 392 /* This is supposed to be the same as
321 invert_led(true); 393 led(current_tick & BLINK_MASK)
394 But we do this hubub to prevent unnecessary hardware
395 communication when the led already has the desired state.
396 */
397 if (last_led_stat != ((current_tick & BLINK_MASK) == BLINK_MASK))
398 {
399 /* trigger is on in status TRIG_READY (no check needed) */
400 last_led_stat = !last_led_stat;
401 led(last_led_stat);
402 }
403 }
404 else
405 {
406 /* trigger is on in status TRIG_READY (no check needed) */
407 led(false);
322 } 408 }
323 } 409 }
324 410
325 button = button_get_w_tmo(HZ / peak_meter_fps); 411 /* Wait for a button while drawing the peak meter */
412 button = peak_meter_draw_get_btn(0, 8 + h*2, LCD_WIDTH, h);
413
414 if (last_mpeg_stat != mpeg_stat)
415 {
416 if (mpeg_stat == MPEG_STATUS_RECORD)
417 {
418 have_recorded = true;
419 }
420 last_mpeg_stat = mpeg_stat;
421 }
422
326 switch(button) 423 switch(button)
327 { 424 {
328 case REC_STOPEXIT: 425 case REC_STOPEXIT:
329 if(mpeg_status() & MPEG_STATUS_RECORD) 426 if(mpeg_stat & MPEG_STATUS_RECORD)
330 { 427 {
428 /* turn off the trigger */
429 peak_meter_trigger(false);
430 peak_meter_set_trigger_listener(NULL);
331 mpeg_stop(); 431 mpeg_stop();
332 } 432 }
333 else 433 else
334 { 434 {
335 peak_meter_playback(true); 435 peak_meter_playback(true);
336 peak_meter_enabled = false; 436 peak_meter_enabled = false;
437 /* turn off the trigger */
438 peak_meter_set_trigger_listener(NULL);
439 peak_meter_trigger(false);
337 done = true; 440 done = true;
338 } 441 }
339 update_countdown = 1; /* Update immediately */ 442 update_countdown = 1; /* Update immediately */
@@ -341,8 +444,13 @@ bool recording_screen(void)
341 444
342 case REC_RECPAUSE: 445 case REC_RECPAUSE:
343 /* Only act if the mpeg is stopped */ 446 /* Only act if the mpeg is stopped */
344 if(!(mpeg_status() & MPEG_STATUS_RECORD)) 447 if(!(mpeg_stat & MPEG_STATUS_RECORD))
448 {
449 /* is this manual or triggered recording? */
450 if ((global_settings.rec_trigger_mode == TRIG_MODE_OFF) ||
451 (peak_meter_trigger_status() != TRIG_OFF))
345 { 452 {
453 /* manual recording */
346 have_recorded = true; 454 have_recorded = true;
347 talk_buffer_steal(); /* we use the mp3 buffer */ 455 talk_buffer_steal(); /* we use the mp3 buffer */
348 mpeg_record(rec_create_filename(path_buffer)); 456 mpeg_record(rec_create_filename(path_buffer));
@@ -353,9 +461,22 @@ bool recording_screen(void)
353 mpeg_beep(HZ/2); /* longer beep on start */ 461 mpeg_beep(HZ/2); /* longer beep on start */
354 } 462 }
355 } 463 }
464
465 /* this is triggered recording */
466 else
467 {
468 update_countdown = 1; /* Update immediately */
469
470 /* we don't start recording now, but enable the
471 trigger and let the callback function
472 trigger_listener control when the recording starts */
473 peak_meter_trigger(true);
474 peak_meter_set_trigger_listener(&trigger_listener);
475 }
476 }
356 else 477 else
357 { 478 {
358 if(mpeg_status() & MPEG_STATUS_PAUSE) 479 if(mpeg_stat & MPEG_STATUS_PAUSE)
359 { 480 {
360 mpeg_resume_recording(); 481 mpeg_resume_recording();
361 if (global_settings.talk_menu) 482 if (global_settings.talk_menu)
@@ -473,11 +594,14 @@ bool recording_screen(void)
473 594
474#ifdef REC_SETTINGS 595#ifdef REC_SETTINGS
475 case REC_SETTINGS: 596 case REC_SETTINGS:
476 if(mpeg_status() != MPEG_STATUS_RECORD) 597 if(mpeg_stat != MPEG_STATUS_RECORD)
477 { 598 {
478 invert_led(false); 599 /* led is restored at begin of loop / end of function */
600 led(false);
479 if (recording_menu(false)) 601 if (recording_menu(false))
602 {
480 return SYS_USB_CONNECTED; 603 return SYS_USB_CONNECTED;
604 }
481 settings_save(); 605 settings_save();
482 606
483 if (global_settings.rec_prerecord_time) 607 if (global_settings.rec_prerecord_time)
@@ -491,7 +615,6 @@ bool recording_screen(void)
491 global_settings.rec_prerecord_time); 615 global_settings.rec_prerecord_time);
492 616
493 set_gain(); 617 set_gain();
494
495 update_countdown = 1; /* Update immediately */ 618 update_countdown = 1; /* Update immediately */
496 619
497 lcd_setfont(FONT_SYSFIXED); 620 lcd_setfont(FONT_SYSFIXED);
@@ -502,9 +625,10 @@ bool recording_screen(void)
502 625
503#ifdef REC_F2 626#ifdef REC_F2
504 case REC_F2: 627 case REC_F2:
505 if(mpeg_status() != MPEG_STATUS_RECORD) 628 if(mpeg_stat != MPEG_STATUS_RECORD)
506 { 629 {
507 invert_led(false); 630 /* led is restored at begin of loop / end of function */
631 led(false);
508 if (f2_rec_screen()) 632 if (f2_rec_screen())
509 { 633 {
510 have_recorded = true; 634 have_recorded = true;
@@ -518,16 +642,17 @@ bool recording_screen(void)
518 642
519#ifdef REC_F3 643#ifdef REC_F3
520 case REC_F3: 644 case REC_F3:
521 if(mpeg_status() & MPEG_STATUS_RECORD) 645 if(mpeg_stat & MPEG_STATUS_RECORD)
522 { 646 {
523 mpeg_new_file(rec_create_filename(path_buffer)); 647 mpeg_new_file(rec_create_filename(path_buffer));
524 last_seconds = 0; 648 last_seconds = 0;
525 } 649 }
526 else 650 else
527 { 651 {
528 if(mpeg_status() != MPEG_STATUS_RECORD) 652 if(mpeg_stat != MPEG_STATUS_RECORD)
529 { 653 {
530 invert_led(false); 654 /* led is restored at begin of loop / end of function */
655 led(false);
531 if (f3_rec_screen()) 656 if (f3_rec_screen())
532 { 657 {
533 have_recorded = true; 658 have_recorded = true;
@@ -542,7 +667,7 @@ bool recording_screen(void)
542 667
543 case SYS_USB_CONNECTED: 668 case SYS_USB_CONNECTED:
544 /* Only accept USB connection when not recording */ 669 /* Only accept USB connection when not recording */
545 if(mpeg_status() != MPEG_STATUS_RECORD) 670 if(mpeg_stat != MPEG_STATUS_RECORD)
546 { 671 {
547 default_event_handler(SYS_USB_CONNECTED); 672 default_event_handler(SYS_USB_CONNECTED);
548 done = true; 673 done = true;
@@ -555,8 +680,6 @@ bool recording_screen(void)
555 break; 680 break;
556 } 681 }
557 682
558 peak_meter_peek();
559
560 if(TIME_AFTER(current_tick, timeout)) 683 if(TIME_AFTER(current_tick, timeout))
561 { 684 {
562 lcd_setfont(FONT_SYSFIXED); 685 lcd_setfont(FONT_SYSFIXED);
@@ -585,7 +708,7 @@ bool recording_screen(void)
585 708
586 dseconds = rec_timesplit_seconds(); 709 dseconds = rec_timesplit_seconds();
587 710
588 if(mpeg_status() & MPEG_STATUS_PRERECORD) 711 if(mpeg_stat & MPEG_STATUS_PRERECORD)
589 { 712 {
590 snprintf(buf, 32, "%s...", str(LANG_RECORD_PRERECORD)); 713 snprintf(buf, 32, "%s...", str(LANG_RECORD_PRERECORD));
591 } 714 }
@@ -618,15 +741,13 @@ bool recording_screen(void)
618 /* We will do file splitting regardless, since the OFF 741 /* We will do file splitting regardless, since the OFF
619 setting really means 24 hours. This is to make sure 742 setting really means 24 hours. This is to make sure
620 that the recorded files don't get too big. */ 743 that the recorded files don't get too big. */
621 if (mpeg_status() && (seconds >= dseconds)) 744 if (mpeg_stat && (seconds >= dseconds))
622 { 745 {
623 mpeg_new_file(rec_create_filename(path_buffer)); 746 mpeg_new_file(rec_create_filename(path_buffer));
624 update_countdown = 1; 747 update_countdown = 1;
625 last_seconds = 0; 748 last_seconds = 0;
626 } 749 }
627 750
628 peak_meter_draw(0, 8 + h*2, LCD_WIDTH, h);
629
630 /* Show mic gain if input source is Mic */ 751 /* Show mic gain if input source is Mic */
631 if(global_settings.rec_source == 0) 752 if(global_settings.rec_source == 0)
632 { 753 {
@@ -635,9 +756,9 @@ bool recording_screen(void)
635 global_settings.rec_mic_gain, 756 global_settings.rec_mic_gain,
636 buf2, sizeof(buf2))); 757 buf2, sizeof(buf2)));
637 if (global_settings.invert_cursor && (pos++ == cursor)) 758 if (global_settings.invert_cursor && (pos++ == cursor))
638 lcd_puts_style(0, 3, buf, STYLE_INVERT); 759 lcd_puts_style(0, 4, buf, STYLE_INVERT);
639 else 760 else
640 lcd_puts(0, 3, buf); 761 lcd_puts(0, 4, buf);
641 } 762 }
642 else 763 else
643 { 764 {
@@ -650,53 +771,58 @@ bool recording_screen(void)
650 fmt_gain(SOUND_LEFT_GAIN, gain, 771 fmt_gain(SOUND_LEFT_GAIN, gain,
651 buf2, sizeof(buf2))); 772 buf2, sizeof(buf2)));
652 if (global_settings.invert_cursor && (pos++ == cursor)) 773 if (global_settings.invert_cursor && (pos++ == cursor))
653 lcd_puts_style(0, 3, buf, STYLE_INVERT); 774 lcd_puts_style(0, 4, buf, STYLE_INVERT);
654 else 775 else
655 lcd_puts(0, 3, buf); 776 lcd_puts(0, 4, buf);
656 777
657 snprintf(buf, 32, "%s: %s", str(LANG_RECORDING_LEFT), 778 snprintf(buf, 32, "%s: %s", str(LANG_RECORDING_LEFT),
658 fmt_gain(SOUND_LEFT_GAIN, 779 fmt_gain(SOUND_LEFT_GAIN,
659 global_settings.rec_left_gain, 780 global_settings.rec_left_gain,
660 buf2, sizeof(buf2))); 781 buf2, sizeof(buf2)));
661 if (global_settings.invert_cursor && (pos++ == cursor)) 782 if (global_settings.invert_cursor && (pos++ == cursor))
662 lcd_puts_style(0, 4, buf, STYLE_INVERT); 783 lcd_puts_style(0, 5, buf, STYLE_INVERT);
663 else 784 else
664 lcd_puts(0, 4, buf); 785 lcd_puts(0, 5, buf);
665 786
666 snprintf(buf, 32, "%s: %s", str(LANG_RECORDING_RIGHT), 787 snprintf(buf, 32, "%s: %s", str(LANG_RECORDING_RIGHT),
667 fmt_gain(SOUND_RIGHT_GAIN, 788 fmt_gain(SOUND_RIGHT_GAIN,
668 global_settings.rec_right_gain, 789 global_settings.rec_right_gain,
669 buf2, sizeof(buf2))); 790 buf2, sizeof(buf2)));
670 if (global_settings.invert_cursor && (pos++ == cursor)) 791 if (global_settings.invert_cursor && (pos++ == cursor))
671 lcd_puts_style(0, 5, buf, STYLE_INVERT); 792 lcd_puts_style(0, 6, buf, STYLE_INVERT);
672 else 793 else
673 lcd_puts(0, 5, buf); 794 lcd_puts(0, 6, buf);
674 } 795 }
675 } 796 }
676 797
677 if(global_settings.rec_source != SOURCE_SPDIF) 798 if(global_settings.rec_source != SOURCE_SPDIF)
678 put_cursorxy(0, 3 + cursor, true); 799 put_cursorxy(0, 4 + cursor, true);
679 800
680 snprintf(buf, 32, "%s %s [%d]", 801 if (global_settings.rec_source != SOURCE_LINE) {
681 freq_str[global_settings.rec_frequency], 802 snprintf(buf, 32, "%s %s [%d]",
682 global_settings.rec_channels? 803 freq_str[global_settings.rec_frequency],
683 str(LANG_CHANNEL_MONO):str(LANG_CHANNEL_STEREO), 804 global_settings.rec_channels?
684 global_settings.rec_quality); 805 str(LANG_CHANNEL_MONO):str(LANG_CHANNEL_STEREO),
685 lcd_puts(0, 6, buf); 806 global_settings.rec_quality);
807 lcd_puts(0, 6, buf);
808 }
686 809
687 status_draw(true); 810 status_draw(true);
811 peak_meter_draw(0, 8 + h*2, LCD_WIDTH, h);
688 812
689 lcd_update(); 813 lcd_update();
690 } 814 }
691 else 815
816 /* draw the trigger status */
817 if (peak_meter_trigger_status() != TRIG_OFF)
692 { 818 {
693 lcd_clearrect(0, 8 + h*2, LCD_WIDTH, h); 819 peak_meter_draw_trig(LCD_WIDTH - TRIG_WIDTH, 4 * h);
694 peak_meter_draw(0, 8 + h*2, LCD_WIDTH, h); 820 lcd_update_rect(LCD_WIDTH - (TRIG_WIDTH + 2), 4 * h,
695 lcd_update_rect(0, 8 + h*2, LCD_WIDTH, h); 821 TRIG_WIDTH + 2, TRIG_HEIGHT);
696 } 822 }
697 } 823 }
698 824
699 if(mpeg_status() & MPEG_STATUS_ERROR) 825 if(mpeg_stat & MPEG_STATUS_ERROR)
700 { 826 {
701 done = true; 827 done = true;
702 } 828 }
@@ -721,6 +847,10 @@ bool recording_screen(void)
721 847
722 mpeg_init_playback(); 848 mpeg_init_playback();
723 849
850 /* make sure the trigger is really turned off */
851 peak_meter_trigger(false);
852 peak_meter_set_trigger_listener(NULL);
853
724 sound_settings_apply(); 854 sound_settings_apply();
725 855
726 lcd_setfont(FONT_UI); 856 lcd_setfont(FONT_UI);
@@ -728,6 +858,9 @@ bool recording_screen(void)
728 if (have_recorded) 858 if (have_recorded)
729 reload_directory(); 859 reload_directory();
730 860
861#ifndef SIMULATOR
862 ata_set_led_enabled(true);
863#endif
731 return been_in_usb_mode; 864 return been_in_usb_mode;
732/* 865/*
733#endif 866#endif
@@ -883,10 +1016,26 @@ bool f3_rec_screen(void)
883 lcd_bitmap(bitmap_icons_7x8[Icon_FastBackward], 1016 lcd_bitmap(bitmap_icons_7x8[Icon_FastBackward],
884 LCD_WIDTH/2 - 16, LCD_HEIGHT/2 - 4, 7, 8, true); 1017 LCD_WIDTH/2 - 16, LCD_HEIGHT/2 - 4, 7, 8, true);
885 1018
1019 /* trigger setup */
1020 ptr = str(LANG_RECORD_TRIGGER);
1021 lcd_getstringsize(ptr,&w,&h);
1022 lcd_putsxy((LCD_WIDTH-w)/2, LCD_HEIGHT - h*2, ptr);
1023 lcd_bitmap(bitmap_icons_7x8[Icon_DownArrow],
1024 LCD_WIDTH/2 - 3, LCD_HEIGHT - h*3, 7, 8, true);
1025
886 lcd_update(); 1026 lcd_update();
887 1027
888 button = button_get(true); 1028 button = button_get(true);
889 switch (button) { 1029 switch (button) {
1030 case BUTTON_DOWN:
1031 case BUTTON_F3 | BUTTON_DOWN:
1032#ifndef SIMULATOR
1033 rectrigger();
1034 settings_apply_trigger();
1035#endif
1036 exit = true;
1037 break;
1038
890 case BUTTON_LEFT: 1039 case BUTTON_LEFT:
891 case BUTTON_F3 | BUTTON_LEFT: 1040 case BUTTON_F3 | BUTTON_LEFT:
892 global_settings.rec_source++; 1041 global_settings.rec_source++;
diff --git a/apps/settings.c b/apps/settings.c
index df03bdcfcf..bad8fa29ab 100644
--- a/apps/settings.c
+++ b/apps/settings.c
@@ -154,6 +154,11 @@ static const char off_on_ask[] = "off,on,ask";
154static const char graphic_numeric[] = "graphic,numeric"; 154static const char graphic_numeric[] = "graphic,numeric";
155static const char off_number_spell_hover[] = "off,number,spell,hover"; 155static const char off_number_spell_hover[] = "off,number,spell,hover";
156 156
157/* keep synchronous to trig_durations and
158 trigger_times in settings_apply_trigger */
159static const char trig_durations_conf [] =
160 "0s,1s,2s,5s,10s,15s,20s,25s,30s,1min,2min,5min,10min";
161
157/* the part of the settings which ends up in the RTC RAM, where available 162/* the part of the settings which ends up in the RTC RAM, where available
158 (those we either need early, save frequently, or without spinup) */ 163 (those we either need early, save frequently, or without spinup) */
159static const struct bit_entry rtc_bits[] = 164static const struct bit_entry rtc_bits[] =
@@ -350,6 +355,14 @@ static const struct bit_entry hd_bits[] =
350 355
351#ifdef HAVE_RECORDING 356#ifdef HAVE_RECORDING
352 {1, S_O(rec_startup), false, "rec screen on startup", off_on }, 357 {1, S_O(rec_startup), false, "rec screen on startup", off_on },
358
359 /* values for the trigger */
360 {8 | SIGNED, S_O(rec_start_thres), -35, "trigger start threshold", NULL},
361 {8 | SIGNED, S_O(rec_stop_thres), -45, "trigger stop threshold", NULL},
362 {4, S_O(rec_start_duration), 0, "trigger start duration", trig_durations_conf},
363 {4, S_O(rec_stop_postrec), 2, "trigger stop postrec", trig_durations_conf},
364 {4, S_O(rec_stop_gap), 1, "trigger min gap", trig_durations_conf},
365 {4, S_O(rec_trigger_mode ), 1, "trigger mode", "off,no rearm,rearm"},
353#endif 366#endif
354 367
355 /* new stuff to be added at the end */ 368 /* new stuff to be added at the end */
@@ -1554,4 +1567,34 @@ unsigned int rec_timesplit_seconds(void)
1554{ 1567{
1555 return rec_timer_seconds[global_settings.rec_timesplit]; 1568 return rec_timer_seconds[global_settings.rec_timesplit];
1556} 1569}
1570
1571/*
1572 * Time strings used for the trigger durations.
1573 * Keep synchronous to trigger_times in settings_apply_trigger
1574 */
1575char *trig_durations[TRIG_DURATION_COUNT] =
1576{
1577 "0s", "1s", "2s", "5s",
1578 "10s", "15s", "20s", "25s", "30s",
1579 "1min", "2min", "5min", "10min"
1580};
1581
1582void settings_apply_trigger(void)
1583{
1584 /* Keep synchronous to trig_durations and trig_durations_conf*/
1585 static const long trigger_times[TRIG_DURATION_COUNT] = {
1586 0, HZ, 2*HZ, 5*HZ,
1587 10*HZ, 15*HZ, 20*HZ, 25*HZ, 30*HZ,
1588 60*HZ, 2*60*HZ, 5*60*HZ, 10*60*HZ
1589 };
1590
1591 peak_meter_define_trigger(
1592 global_settings.rec_start_thres,
1593 trigger_times[global_settings.rec_start_duration],
1594 MIN(trigger_times[global_settings.rec_start_duration] / 2, 2*HZ),
1595 global_settings.rec_stop_thres,
1596 trigger_times[global_settings.rec_stop_postrec],
1597 trigger_times[global_settings.rec_stop_gap]
1598 );
1599}
1557#endif 1600#endif
diff --git a/apps/settings.h b/apps/settings.h
index d738261c62..a76db9f143 100644
--- a/apps/settings.h
+++ b/apps/settings.h
@@ -102,6 +102,12 @@
102#define FF_REWIND_45000 12 102#define FF_REWIND_45000 12
103#define FF_REWIND_60000 13 103#define FF_REWIND_60000 13
104 104
105#define TRIG_MODE_OFF 0
106#define TRIG_MODE_NOREARM 1
107#define TRIG_MODE_REARM 2
108
109#define TRIG_DURATION_COUNT 13
110extern char *trig_durations[TRIG_DURATION_COUNT];
105 111
106/* These define "virtual pointers", which could either be a literal string, 112/* These define "virtual pointers", which could either be a literal string,
107 or a mean a string ID if the pointer is in a certain range. 113 or a mean a string ID if the pointer is in a certain range.
@@ -171,6 +177,13 @@ struct user_settings
171 int rec_directory; /* 0=base dir, 1=current dir */ 177 int rec_directory; /* 0=base dir, 1=current dir */
172 bool rec_startup; /* true means start Rockbox in recording screen */ 178 bool rec_startup; /* true means start Rockbox in recording screen */
173 179
180 int rec_start_thres; /* negative: db, positive: % range -87 .. 100 */
181 int rec_start_duration; /* index of trig_durations */
182 int rec_stop_thres; /* negative: db, positive: % */
183 int rec_stop_postrec; /* negative: db, positive: % range -87 .. 100 */
184 int rec_stop_gap; /* index of trig_durations */
185 int rec_trigger_mode; /* see TRIG_MODE_XXX constants */
186
174 /* device settings */ 187 /* device settings */
175 188
176 int contrast; /* lcd contrast: 0-63 0=low 63=high */ 189 int contrast; /* lcd contrast: 0-63 0=low 63=high */
@@ -325,6 +338,7 @@ int read_line(int fd, char* buffer, int buffer_size);
325void set_file(char* filename, char* setting, int maxlen); 338void set_file(char* filename, char* setting, int maxlen);
326 339
327unsigned int rec_timesplit_seconds(void); 340unsigned int rec_timesplit_seconds(void);
341void settings_apply_trigger(void);
328 342
329/* global settings */ 343/* global settings */
330extern struct user_settings global_settings; 344extern struct user_settings global_settings;
diff --git a/apps/sound_menu.c b/apps/sound_menu.c
index 560163c7c1..68c959a0ed 100644
--- a/apps/sound_menu.c
+++ b/apps/sound_menu.c
@@ -19,6 +19,7 @@
19#include "config.h" 19#include "config.h"
20#include <stdio.h> 20#include <stdio.h>
21#include <stdbool.h> 21#include <stdbool.h>
22#include "system.h"
22#include "kernel.h" 23#include "kernel.h"
23#include "lcd.h" 24#include "lcd.h"
24#include "menu.h" 25#include "menu.h"
@@ -29,12 +30,18 @@
29#include "screens.h" 30#include "screens.h"
30#ifdef HAVE_LCD_BITMAP 31#ifdef HAVE_LCD_BITMAP
31#include "icons.h" 32#include "icons.h"
33#include "font.h"
34#include "widgets.h"
32#endif 35#endif
33#include "lang.h" 36#include "lang.h"
34#include "sprintf.h" 37#include "sprintf.h"
35#include "talk.h" 38#include "talk.h"
36#include "misc.h" 39#include "misc.h"
37#include "sound.h" 40#include "sound.h"
41#if CONFIG_HWCODEC == MAS3587F || CONFIG_HWCODEC == MAS3539F
42#include "peakmeter.h"
43#include "mas.h"
44#endif
38 45
39static const char* const fmt[] = 46static const char* const fmt[] =
40{ 47{
@@ -435,11 +442,373 @@ bool sound_menu(void)
435} 442}
436 443
437#ifdef HAVE_RECORDING 444#ifdef HAVE_RECORDING
445enum trigger_menu_option
446{
447 TRIGGER_MODE,
448 PRERECORD_TIME,
449 START_THRESHOLD,
450 START_DURATION,
451 STOP_THRESHOLD,
452 STOP_POSTREC,
453 STOP_GAP,
454 TRIG_OPTION_COUNT,
455};
456
457#if !defined(SIMULATOR) && CONFIG_HWCODEC == MAS3587F
458static char* create_thres_str(int threshold)
459{
460 static char retval[6];
461 if (threshold < 0) {
462 if (threshold < -88) {
463 snprintf (retval, sizeof retval, "%s", str(LANG_DB_INF));
464 } else {
465 snprintf (retval, sizeof retval, "%ddb", threshold + 1);
466 }
467 } else {
468 snprintf (retval, sizeof retval, "%d%%", threshold);
469 }
470 return retval;
471}
472#endif
473
474#if !defined(SIMULATOR) && (CONFIG_HWCODEC == MAS3587F || CONFIG_HWCODEC == MAS3539F)
475#define INF_DB (-89)
476static void change_threshold(int *threshold, int change)
477{
478 if (global_settings.peak_meter_dbfs) {
479 if (*threshold >= 0) {
480 int db = (calc_db(*threshold * MAX_PEAK / 100) - 9000) / 100;
481 *threshold = db;
482 }
483 *threshold += change;
484 if (*threshold > -1) {
485 *threshold = INF_DB;
486 } else if (*threshold < INF_DB) {
487 *threshold = -1;
488 }
489 } else {
490 if (*threshold < 0) {
491 *threshold = peak_meter_db2sample(*threshold * 100) * 100 / MAX_PEAK;
492 }
493 *threshold += change;
494 if (*threshold > 100) {
495 *threshold = 0;
496 } else if (*threshold < 0) {
497 *threshold = 100;
498 }
499 }
500}
501
502/**
503 * Displays a menu for editing the trigger settings.
504 */
505bool rectrigger(void)
506{
507 int exit_request = false;
508 enum trigger_menu_option selected = TRIGGER_MODE;
509 bool retval = false;
510 int old_x_margin, old_y_margin;
511
512#define TRIGGER_MODE_COUNT 3
513 char *trigger_modes[] =
514 {
515 str(LANG_OFF),
516 str(LANG_RECORD_TRIG_NOREARM),
517 str(LANG_RECORD_TRIG_REARM)
518 };
519
520#define PRERECORD_TIMES_COUNT 31
521 char *prerecord_times[] = {
522 str(LANG_OFF),"1s","2s", "3s", "4s", "5s", "6s", "7s", "8s", "9s",
523 "10s", "11s", "12s", "13s", "14s", "15s", "16s", "17s", "18s", "19s",
524 "20s", "21s", "22s", "23s", "24s", "25s", "26s", "27s", "28s", "29s",
525 "30s"
526 };
527
528 char *option_name[TRIG_OPTION_COUNT];
529
530 int old_start_thres = global_settings.rec_start_thres;
531 int old_start_duration = global_settings.rec_start_duration;
532 int old_prerecord_time = global_settings.rec_prerecord_time;
533 int old_stop_thres = global_settings.rec_stop_thres;
534 int old_stop_postrec = global_settings.rec_stop_postrec;
535 int old_stop_gap = global_settings.rec_stop_gap;
536 int old_trigger_mode = global_settings.rec_trigger_mode;
537
538 int offset = 0;
539 int option_lines;
540 int w, h;
541
542 option_name[TRIGGER_MODE] = str(LANG_RECORD_TRIGGER_MODE);
543 option_name[PRERECORD_TIME] = str(LANG_RECORD_PRERECORD_TIME);
544 option_name[START_THRESHOLD] = str(LANG_RECORD_START_THRESHOLD);
545 option_name[START_DURATION] = str(LANG_RECORD_MIN_DURATION);
546 option_name[STOP_THRESHOLD] = str(LANG_RECORD_STOP_THRESHOLD);
547 option_name[STOP_POSTREC] = str(LANG_RECORD_STOP_POSTREC);
548 option_name[STOP_GAP] = str(LANG_RECORD_STOP_GAP);
549
550
551 /* restart trigger with new values */
552 settings_apply_trigger();
553 peak_meter_trigger (global_settings.rec_trigger_mode != TRIG_MODE_OFF);
554
555 lcd_clear_display();
556
557 old_x_margin = lcd_getxmargin();
558 old_y_margin = lcd_getymargin();
559 if(global_settings.statusbar)
560 lcd_setmargins(0, STATUSBAR_HEIGHT);
561 else
562 lcd_setmargins(0, 0);
563
564 lcd_getstringsize("M", &w, &h);
565
566 // two lines are reserved for peak meter and trigger status
567 option_lines = (LCD_HEIGHT/h) - (global_settings.statusbar ? 1:0) - 2;
568
569 while (!exit_request) {
570 int stat_height = global_settings.statusbar ? STATUSBAR_HEIGHT : 0;
571 int button, i;
572 char *str;
573 char option_value[TRIG_OPTION_COUNT][7];
574
575 snprintf(
576 option_value[TRIGGER_MODE],
577 sizeof option_value[TRIGGER_MODE],
578 "%s",
579 trigger_modes[global_settings.rec_trigger_mode]);
580
581 snprintf (
582 option_value[PRERECORD_TIME],
583 sizeof option_value[PRERECORD_TIME],
584 "%s",
585 prerecord_times[global_settings.rec_prerecord_time]);
586
587 /* due to value range shift (peak_meter_define_trigger) -1 is 0db */
588 if (global_settings.rec_start_thres == -1) {
589 str = str(LANG_OFF);
590 } else {
591 str = create_thres_str(global_settings.rec_start_thres);
592 }
593 snprintf(
594 option_value[START_THRESHOLD],
595 sizeof option_value[START_THRESHOLD],
596 "%s",
597 str);
598
599 snprintf(
600 option_value[START_DURATION],
601 sizeof option_value[START_DURATION],
602 "%s",
603 trig_durations[global_settings.rec_start_duration]);
604
605
606 if (global_settings.rec_stop_thres <= INF_DB) {
607 str = str(LANG_OFF);
608 } else {
609 str = create_thres_str(global_settings.rec_stop_thres);
610 }
611 snprintf(
612 option_value[STOP_THRESHOLD],
613 sizeof option_value[STOP_THRESHOLD],
614 "%s",
615 str);
616
617 snprintf(
618 option_value[STOP_POSTREC],
619 sizeof option_value[STOP_POSTREC],
620 "%s",
621 trig_durations[global_settings.rec_stop_postrec]);
622
623 snprintf(
624 option_value[STOP_GAP],
625 sizeof option_value[STOP_GAP],
626 "%s",
627 trig_durations[global_settings.rec_stop_gap]);
628
629 lcd_clearrect(0, stat_height, LCD_WIDTH, LCD_HEIGHT - stat_height);
630 status_draw(true);
631
632 /* reselect FONT_SYSFONT as status_draw has changed the font */
633 /*lcd_setfont(FONT_SYSFIXED);*/
634
635 for (i = 0; i < option_lines; i++) {
636 int x, y;
637
638 str = option_name[i + offset];
639 lcd_putsxy(5, stat_height + i * h, str);
640
641 str = option_value[i + offset];
642 lcd_getstringsize(str, &w, &h);
643 y = stat_height + i * h;
644 x = LCD_WIDTH - w;
645 lcd_putsxy(x, y, str);
646 if ((int)selected == (i + offset))
647 lcd_invertrect(x, y, w, h);
648 }
649
650 scrollbar(0, stat_height,
651 4, LCD_HEIGHT - 16 - stat_height,
652 TRIG_OPTION_COUNT, offset, offset + option_lines,
653 VERTICAL);
654
655 peak_meter_draw_trig(0, LCD_HEIGHT - 8 - TRIG_HEIGHT);
656
657 button = peak_meter_draw_get_btn(0, LCD_HEIGHT - 8, LCD_WIDTH, 8);
658
659 lcd_update();
660
661 switch (button) {
662 case BUTTON_OFF:
663 splash(50, true, str(LANG_RESET_DONE_CANCEL));
664 global_settings.rec_start_thres = old_start_thres;
665 global_settings.rec_start_duration = old_start_duration;
666 global_settings.rec_prerecord_time = old_prerecord_time;
667 global_settings.rec_stop_thres = old_stop_thres;
668 global_settings.rec_stop_postrec = old_stop_postrec;
669 global_settings.rec_stop_gap = old_stop_gap;
670 global_settings.rec_trigger_mode = old_trigger_mode;
671 exit_request = true;
672 break;
673
674 case BUTTON_PLAY:
675 exit_request = true;
676 break;
677
678 case BUTTON_UP:
679 selected += TRIG_OPTION_COUNT - 1;
680 selected %= TRIG_OPTION_COUNT;
681 offset = MIN(offset, (int)selected);
682 offset = MAX(offset, (int)selected - option_lines + 1);
683 break;
684
685 case BUTTON_DOWN:
686 selected ++;
687 selected %= TRIG_OPTION_COUNT;
688 offset = MIN(offset, (int)selected);
689 offset = MAX(offset, (int)selected - option_lines + 1);
690 break;
691
692 case BUTTON_RIGHT:
693 case BUTTON_RIGHT | BUTTON_REPEAT:
694 switch (selected) {
695 case TRIGGER_MODE:
696 global_settings.rec_trigger_mode ++;
697 global_settings.rec_trigger_mode %= TRIGGER_MODE_COUNT;
698 break;
699
700 case PRERECORD_TIME:
701 global_settings.rec_prerecord_time ++;
702 global_settings.rec_prerecord_time %= PRERECORD_TIMES_COUNT;
703 break;
704
705 case START_THRESHOLD:
706 change_threshold(&global_settings.rec_start_thres, 1);
707 break;
708
709 case START_DURATION:
710 global_settings.rec_start_duration ++;
711 global_settings.rec_start_duration %= TRIG_DURATION_COUNT;
712 break;
713
714 case STOP_THRESHOLD:
715 change_threshold(&global_settings.rec_stop_thres, 1);
716 break;
717
718 case STOP_POSTREC:
719 global_settings.rec_stop_postrec ++;
720 global_settings.rec_stop_postrec %= TRIG_DURATION_COUNT;
721 break;
722
723 case STOP_GAP:
724 global_settings.rec_stop_gap ++;
725 global_settings.rec_stop_gap %= TRIG_DURATION_COUNT;
726 break;
727
728 case TRIG_OPTION_COUNT:
729 // avoid compiler warnings
730 break;
731 }
732 peak_meter_trigger(global_settings.rec_trigger_mode!=TRIG_OFF);
733 settings_apply_trigger();
734 break;
735
736 case BUTTON_LEFT:
737 case BUTTON_LEFT | BUTTON_REPEAT:
738 switch (selected) {
739 case TRIGGER_MODE:
740 global_settings.rec_trigger_mode+=TRIGGER_MODE_COUNT-1;
741 global_settings.rec_trigger_mode %= TRIGGER_MODE_COUNT;
742 break;
743
744 case PRERECORD_TIME:
745 global_settings.rec_prerecord_time += PRERECORD_TIMES_COUNT - 1;
746 global_settings.rec_prerecord_time %= PRERECORD_TIMES_COUNT;
747 break;
748
749 case START_THRESHOLD:
750 change_threshold(&global_settings.rec_start_thres, -1);
751 break;
752
753 case START_DURATION:
754 global_settings.rec_start_duration += TRIG_DURATION_COUNT-1;
755 global_settings.rec_start_duration %= TRIG_DURATION_COUNT;
756 break;
757
758 case STOP_THRESHOLD:
759 change_threshold(&global_settings.rec_stop_thres, -1);
760 break;
761
762 case STOP_POSTREC:
763 global_settings.rec_stop_postrec +=
764 TRIG_DURATION_COUNT - 1;
765 global_settings.rec_stop_postrec %=
766 TRIG_DURATION_COUNT;
767 break;
768
769 case STOP_GAP:
770 global_settings.rec_stop_gap +=
771 TRIG_DURATION_COUNT - 1;
772 global_settings.rec_stop_gap %= TRIG_DURATION_COUNT;
773 break;
774
775 case TRIG_OPTION_COUNT:
776 // avoid compiler warnings
777 break;
778 }
779
780 if (global_settings.rec_trigger_mode == TRIG_OFF) {
781 peak_meter_trigger(true);
782 } else {
783 /* restart trigger with new values */
784 settings_apply_trigger();
785 }
786 break;
787
788 case BUTTON_F2:
789 peak_meter_trigger(true);
790 break;
791
792 case SYS_USB_CONNECTED:
793 usb_screen();
794 retval = true;
795 exit_request = true;
796 break;
797 }
798 }
799
800 peak_meter_trigger(false);
801 lcd_setfont(FONT_UI);
802 lcd_setmargins(old_x_margin, old_y_margin);
803 return retval;
804}
805#endif
806
438bool recording_menu(bool no_source) 807bool recording_menu(bool no_source)
439{ 808{
440 int m; 809 int m;
441 int i = 0; 810 int i = 0;
442 struct menu_item items[9]; 811 struct menu_item items[10];
443 bool result; 812 bool result;
444 813
445 items[i].desc = ID2P(LANG_RECORDING_QUALITY); 814 items[i].desc = ID2P(LANG_RECORDING_QUALITY);
@@ -462,6 +831,10 @@ bool recording_menu(bool no_source)
462 items[i++].function = recdirectory; 831 items[i++].function = recdirectory;
463 items[i].desc = ID2P(LANG_RECORD_STARTUP); 832 items[i].desc = ID2P(LANG_RECORD_STARTUP);
464 items[i++].function = reconstartup; 833 items[i++].function = reconstartup;
834#if !defined(SIMULATOR) && CONFIG_HWCODEC == MAS3587F
835 items[i].desc = str(LANG_RECORD_TRIGGER);
836 items[i++].function = rectrigger;
837#endif
465 838
466 m=menu_init( items, i, NULL, NULL, NULL, NULL); 839 m=menu_init( items, i, NULL, NULL, NULL, NULL);
467 result = menu_run(m); 840 result = menu_run(m);
diff --git a/apps/sound_menu.h b/apps/sound_menu.h
index 206d1e3715..4d295b0a70 100644
--- a/apps/sound_menu.h
+++ b/apps/sound_menu.h
@@ -23,5 +23,6 @@
23 23
24bool sound_menu(void); 24bool sound_menu(void);
25bool recording_menu(bool no_source); 25bool recording_menu(bool no_source);
26bool rectrigger(void);
26 27
27#endif 28#endif
diff --git a/firmware/drivers/ata.c b/firmware/drivers/ata.c
index 10f5f20c7b..956f61dc89 100644
--- a/firmware/drivers/ata.c
+++ b/firmware/drivers/ata.c
@@ -241,6 +241,8 @@ static volatile unsigned char* ata_control;
241 241
242bool old_recorder = false; 242bool old_recorder = false;
243int ata_spinup_time = 0; 243int ata_spinup_time = 0;
244static bool ata_led_enabled = true;
245static bool ata_led_on = false;
244static bool spinup = false; 246static bool spinup = false;
245static bool sleeping = true; 247static bool sleeping = true;
246static long sleep_timeout = 5*HZ; 248static long sleep_timeout = 5*HZ;
@@ -473,6 +475,13 @@ static void copy_read_sectors(unsigned char* buf, int wordcount)
473#endif 475#endif
474} 476}
475 477
478static void ata_led(bool on) {
479 ata_led_on = on;
480 if (ata_led_enabled) {
481 led(ata_led_on);
482 }
483}
484
476int ata_read_sectors(IF_MV2(int drive,) 485int ata_read_sectors(IF_MV2(int drive,)
477 unsigned long start, 486 unsigned long start,
478 int incount, 487 int incount,
@@ -492,21 +501,21 @@ int ata_read_sectors(IF_MV2(int drive,)
492 last_disk_activity = current_tick; 501 last_disk_activity = current_tick;
493 spinup_start = current_tick; 502 spinup_start = current_tick;
494 503
495 led(true); 504 ata_led(true);
496 505
497 if ( sleeping ) { 506 if ( sleeping ) {
498 spinup = true; 507 spinup = true;
499 if (poweroff) { 508 if (poweroff) {
500 if (ata_power_on()) { 509 if (ata_power_on()) {
501 mutex_unlock(&ata_mtx); 510 mutex_unlock(&ata_mtx);
502 led(false); 511 ata_led(false);
503 return -1; 512 return -1;
504 } 513 }
505 } 514 }
506 else { 515 else {
507 if (perform_soft_reset()) { 516 if (perform_soft_reset()) {
508 mutex_unlock(&ata_mtx); 517 mutex_unlock(&ata_mtx);
509 led(false); 518 ata_led(false);
510 return -1; 519 return -1;
511 } 520 }
512 } 521 }
@@ -518,7 +527,7 @@ int ata_read_sectors(IF_MV2(int drive,)
518 if (!wait_for_rdy()) 527 if (!wait_for_rdy())
519 { 528 {
520 mutex_unlock(&ata_mtx); 529 mutex_unlock(&ata_mtx);
521 led(false); 530 ata_led(false);
522 return -2; 531 return -2;
523 } 532 }
524 533
@@ -614,7 +623,7 @@ int ata_read_sectors(IF_MV2(int drive,)
614 } 623 }
615 break; 624 break;
616 } 625 }
617 led(false); 626 ata_led(false);
618 627
619 mutex_unlock(&ata_mtx); 628 mutex_unlock(&ata_mtx);
620 629
@@ -775,21 +784,21 @@ int ata_write_sectors(IF_MV2(int drive,)
775 last_disk_activity = current_tick; 784 last_disk_activity = current_tick;
776 spinup_start = current_tick; 785 spinup_start = current_tick;
777 786
778 led(true); 787 ata_led(true);
779 788
780 if ( sleeping ) { 789 if ( sleeping ) {
781 spinup = true; 790 spinup = true;
782 if (poweroff) { 791 if (poweroff) {
783 if (ata_power_on()) { 792 if (ata_power_on()) {
784 mutex_unlock(&ata_mtx); 793 mutex_unlock(&ata_mtx);
785 led(false); 794 ata_led(false);
786 return -1; 795 return -1;
787 } 796 }
788 } 797 }
789 else { 798 else {
790 if (perform_soft_reset()) { 799 if (perform_soft_reset()) {
791 mutex_unlock(&ata_mtx); 800 mutex_unlock(&ata_mtx);
792 led(false); 801 ata_led(false);
793 return -1; 802 return -1;
794 } 803 }
795 } 804 }
@@ -799,7 +808,7 @@ int ata_write_sectors(IF_MV2(int drive,)
799 if (!wait_for_rdy()) 808 if (!wait_for_rdy())
800 { 809 {
801 mutex_unlock(&ata_mtx); 810 mutex_unlock(&ata_mtx);
802 led(false); 811 ata_led(false);
803 return -2; 812 return -2;
804 } 813 }
805 814
@@ -843,7 +852,7 @@ int ata_write_sectors(IF_MV2(int drive,)
843 ret = -4; 852 ret = -4;
844 } 853 }
845 854
846 led(false); 855 ata_led(false);
847 856
848 mutex_unlock(&ata_mtx); 857 mutex_unlock(&ata_mtx);
849 858
@@ -1042,9 +1051,9 @@ static void ata_thread(void)
1042 case SYS_USB_CONNECTED: 1051 case SYS_USB_CONNECTED:
1043 if (poweroff) { 1052 if (poweroff) {
1044 mutex_lock(&ata_mtx); 1053 mutex_lock(&ata_mtx);
1045 led(true); 1054 ata_led(true);
1046 ata_power_on(); 1055 ata_power_on();
1047 led(false); 1056 ata_led(false);
1048 mutex_unlock(&ata_mtx); 1057 mutex_unlock(&ata_mtx);
1049 } 1058 }
1050 1059
@@ -1384,7 +1393,7 @@ int ata_init(void)
1384 1393
1385 mutex_init(&ata_mtx); 1394 mutex_init(&ata_mtx);
1386 1395
1387 led(false); 1396 ata_led(false);
1388 1397
1389#if CONFIG_CPU == SH7034 1398#if CONFIG_CPU == SH7034
1390 /* Port A setup */ 1399 /* Port A setup */
@@ -1458,3 +1467,12 @@ int ata_init(void)
1458 1467
1459 return 0; 1468 return 0;
1460} 1469}
1470
1471void ata_set_led_enabled(bool enabled) {
1472 ata_led_enabled = enabled;
1473 if (ata_led_enabled) {
1474 led(ata_led_on);
1475 } else {
1476 led(false);
1477 }
1478}
diff --git a/firmware/export/ata.h b/firmware/export/ata.h
index fb604a120d..2043de915a 100644
--- a/firmware/export/ata.h
+++ b/firmware/export/ata.h
@@ -61,6 +61,7 @@ extern int ata_write_sectors(IF_MV2(int drive,) unsigned long start, int count,
61extern void ata_delayed_write(unsigned long sector, const void* buf); 61extern void ata_delayed_write(unsigned long sector, const void* buf);
62extern void ata_flush(void); 62extern void ata_flush(void);
63extern void ata_spin(void); 63extern void ata_spin(void);
64extern void ata_set_led_enabled(bool enabled);
64extern unsigned short* ata_get_identify(void); 65extern unsigned short* ata_get_identify(void);
65 66
66extern long last_disk_activity; 67extern long last_disk_activity;