diff options
author | Linus Nielsen Feltzing <linus@haxx.se> | 2005-04-04 09:12:12 +0000 |
---|---|---|
committer | Linus Nielsen Feltzing <linus@haxx.se> | 2005-04-04 09:12:12 +0000 |
commit | 68482bbed286981a5ef09a466620e459790c96e6 (patch) | |
tree | de613ede8a83077b935debc1ec3edd965d5d6090 | |
parent | 5fb6c64ffc8e6ab7512d805d2860831e492e5c52 (diff) | |
download | rockbox-68482bbed286981a5ef09a466620e459790c96e6.tar.gz rockbox-68482bbed286981a5ef09a466620e459790c96e6.zip |
Patch #868645 by Philipp Pertermann, volume triggered recording for the Archos recording devices
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@6243 a1c6a512-1295-4272-9138-f99709370657
-rw-r--r-- | apps/lang/english.lang | 60 | ||||
-rw-r--r-- | apps/recorder/peakmeter.c | 374 | ||||
-rw-r--r-- | apps/recorder/peakmeter.h | 34 | ||||
-rw-r--r-- | apps/recorder/recording.c | 257 | ||||
-rw-r--r-- | apps/settings.c | 43 | ||||
-rw-r--r-- | apps/settings.h | 14 | ||||
-rw-r--r-- | apps/sound_menu.c | 375 | ||||
-rw-r--r-- | apps/sound_menu.h | 1 | ||||
-rw-r--r-- | firmware/drivers/ata.c | 44 | ||||
-rw-r--r-- | firmware/export/ata.h | 1 |
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 | |||
3009 | eng: "Alarm Disabled" | 3009 | eng: "Alarm Disabled" |
3010 | voice: "Alarm Disabled" | 3010 | voice: "Alarm Disabled" |
3011 | new: | 3011 | new: |
3012 | |||
3013 | ########################### | ||
3014 | id: LANG_RECORD_TRIGGER | ||
3015 | desc: in recording settings_menu | ||
3016 | eng: "Trigger" | ||
3017 | new: | ||
3018 | |||
3019 | id: LANG_RECORD_START_THRESHOLD | ||
3020 | desc: in recording settings_menu | ||
3021 | eng: "Start above" | ||
3022 | new: | ||
3023 | |||
3024 | id: LANG_RECORD_MIN_DURATION | ||
3025 | desc: in recording settings_menu | ||
3026 | eng: "for at least" | ||
3027 | new: | ||
3028 | |||
3029 | id: LANG_RECORD_STOP_THRESHOLD | ||
3030 | desc: in recording settings_menu | ||
3031 | eng: "Stop below" | ||
3032 | new: | ||
3033 | |||
3034 | id: LANG_RECORD_STOP_POSTREC | ||
3035 | desc: in recording settings_menu | ||
3036 | eng: "for at least" | ||
3037 | new: | ||
3038 | |||
3039 | id: LANG_RECORD_STOP_GAP | ||
3040 | desc: in recording settings_menu | ||
3041 | eng: "presplit gap" | ||
3042 | new: | ||
3043 | |||
3044 | id: LANG_RECORD_TRIGGER_MODE | ||
3045 | desc: in recording settings_menu | ||
3046 | eng: "Trigger" | ||
3047 | new: | ||
3048 | |||
3049 | id: LANG_RECORD_TRIG_NOREARM | ||
3050 | desc: in recording settings_menu | ||
3051 | eng: "one" | ||
3052 | new: | ||
3053 | |||
3054 | id: LANG_RECORD_TRIG_REARM | ||
3055 | desc: in recording settings_menu | ||
3056 | eng: "repeat" | ||
3057 | new: | ||
3058 | |||
3059 | id: LANG_DB_INF | ||
3060 | desc: -inf db for values below measurement | ||
3061 | eng: "-inf" | ||
3062 | new: | ||
3063 | |||
3064 | id: LANG_RECORD_TRIG_IDLE | ||
3065 | desc: waiting for threshold | ||
3066 | eng: "Trigger idle" | ||
3067 | new: | ||
3068 | |||
3069 | id: LANG_RECORD_TRIGGER_ACTIVE | ||
3070 | eng: "Trigger active" | ||
3071 | new: | ||
diff --git a/apps/recorder/peakmeter.c b/apps/recorder/peakmeter.c index 41d375e076..0481e25acd 100644 --- a/apps/recorder/peakmeter.c +++ b/apps/recorder/peakmeter.c | |||
@@ -22,6 +22,7 @@ | |||
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; | |||
67 | static unsigned short db_max = 9000; | 68 | static unsigned short db_max = 9000; |
68 | static unsigned short db_range = 9000; | 69 | static unsigned short db_range = 9000; |
69 | 70 | ||
71 | static unsigned short trig_strt_threshold; | ||
72 | static long trig_strt_duration; | ||
73 | static long trig_strt_dropout; | ||
74 | static unsigned short trig_stp_threshold; | ||
75 | static long trig_stp_hold; | ||
76 | static long trig_rstrt_gap; | ||
77 | |||
78 | /* point in time when the threshold was exceeded */ | ||
79 | static long trig_hightime; | ||
80 | |||
81 | /* point in time when the volume fell below the threshold*/ | ||
82 | static long trig_lowtime; | ||
83 | |||
84 | /* The output value of the trigger. See TRIG_XXX constants vor valid values */ | ||
85 | static int trig_status = TRIG_OFF; | ||
86 | |||
87 | static 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 |
129 | static const int db_scale_src_values[DB_SCALE_SRC_VALUES_SIZE] = { | 148 | static 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 |
143 | static const int db_scale_src_values[DB_SCALE_SRC_VALUES_SIZE] = { | 163 | 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] = { | |||
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 | ||
179 | const 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 | |||
158 | static int db_scale_count = DB_SCALE_SRC_VALUES_SIZE; | 194 | static 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 | */ |
315 | static int db_to_sample(int db) { | 351 | int 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 | */ |
352 | void peak_meter_set_min(int newmin) { | 388 | void 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 | */ |
393 | void peak_meter_set_max(int newmax) { | 429 | void 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 | ||
543 | static 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 | /* | ||
568 | void 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 | /* | ||
583 | void 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 | */ | ||
1035 | void 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 | */ | ||
1072 | void 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 | */ | ||
1082 | void 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 | */ | ||
1098 | int peak_meter_trigger_status(void) { | ||
1099 | return trig_status; /* & TRIG_PIT_MASK;*/ | ||
1100 | } | ||
1101 | |||
1102 | void 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 | |||
1146 | int 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 |
870 | static void peak_meter_clear_histogram(void) { | 1170 | static 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 @@ | |||
24 | extern bool peak_meter_histogram(void); | 24 | extern bool peak_meter_histogram(void); |
25 | #endif | 25 | #endif |
26 | 26 | ||
27 | |||
28 | extern bool peak_meter_enabled; | 27 | extern bool peak_meter_enabled; |
29 | extern int peak_meter_fps; | 28 | extern int peak_meter_fps; |
30 | 29 | ||
31 | extern void peak_meter_playback(bool playback); | 30 | extern void peak_meter_playback(bool playback); |
32 | extern void peak_meter_draw(int x, int y, int width, int height); | 31 | extern void peak_meter_draw(int x, int y, int width, int height); |
32 | extern int peak_meter_draw_get_btn(int x, int y, int width, int height); | ||
33 | extern void peak_meter_set_clip_hold(int time); | 33 | extern void peak_meter_set_clip_hold(int time); |
34 | extern void peak_meter_peek(void); | 34 | extern void peak_meter_peek(void); |
35 | extern void peak_meter_init_range( bool dbfs, int range_min, int range_max); | 35 | 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); | |||
42 | extern void peak_meter_set_use_dbfs(int use); | 42 | extern void peak_meter_set_use_dbfs(int use); |
43 | extern int peak_meter_get_use_dbfs(void); | 43 | extern int peak_meter_get_use_dbfs(void); |
44 | extern int calc_db (int isample); | 44 | extern int calc_db (int isample); |
45 | extern int peak_meter_db2sample(int db); | ||
45 | extern unsigned short peak_meter_scale_value(unsigned short val, int meterwidth); | 46 | extern 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 | |||
57 | extern 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 | |||
66 | extern void peak_meter_trigger(bool on); | ||
67 | extern int peak_meter_trigger_status(void); | ||
68 | extern 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 | |||
77 | extern void peak_meter_draw_trig(int x, int y); | ||
78 | |||
47 | extern unsigned short peak_meter_range_min; | 79 | extern unsigned short peak_meter_range_min; |
48 | extern unsigned short peak_meter_range_max; | 80 | extern 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 | ||
244 | static char path_buffer[MAX_PATH]; | ||
245 | |||
246 | /* used in trigger_listerner and recording_screen */ | ||
247 | static 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 | */ | ||
254 | static 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 | |||
243 | bool recording_screen(void) | 294 | bool 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"; | |||
154 | static const char graphic_numeric[] = "graphic,numeric"; | 154 | static const char graphic_numeric[] = "graphic,numeric"; |
155 | static const char off_number_spell_hover[] = "off,number,spell,hover"; | 155 | static 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 */ | ||
159 | static 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) */ |
159 | static const struct bit_entry rtc_bits[] = | 164 | static 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 | */ | ||
1575 | char *trig_durations[TRIG_DURATION_COUNT] = | ||
1576 | { | ||
1577 | "0s", "1s", "2s", "5s", | ||
1578 | "10s", "15s", "20s", "25s", "30s", | ||
1579 | "1min", "2min", "5min", "10min" | ||
1580 | }; | ||
1581 | |||
1582 | void 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 | ||
110 | extern 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); | |||
325 | void set_file(char* filename, char* setting, int maxlen); | 338 | void set_file(char* filename, char* setting, int maxlen); |
326 | 339 | ||
327 | unsigned int rec_timesplit_seconds(void); | 340 | unsigned int rec_timesplit_seconds(void); |
341 | void settings_apply_trigger(void); | ||
328 | 342 | ||
329 | /* global settings */ | 343 | /* global settings */ |
330 | extern struct user_settings global_settings; | 344 | extern struct user_settings global_settings; |
diff --git a/apps/sound_menu.c b/apps/sound_menu.c index 560163c7c1..68c959a0ed 100644 --- a/apps/sound_menu.c +++ b/apps/sound_menu.c | |||
@@ -19,6 +19,7 @@ | |||
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 | ||
39 | static const char* const fmt[] = | 46 | static 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 |
445 | enum 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 | ||
458 | static 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) | ||
476 | static 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 | */ | ||
505 | bool 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 | |||
438 | bool recording_menu(bool no_source) | 807 | bool 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 | ||
24 | bool sound_menu(void); | 24 | bool sound_menu(void); |
25 | bool recording_menu(bool no_source); | 25 | bool recording_menu(bool no_source); |
26 | bool 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 | ||
242 | bool old_recorder = false; | 242 | bool old_recorder = false; |
243 | int ata_spinup_time = 0; | 243 | int ata_spinup_time = 0; |
244 | static bool ata_led_enabled = true; | ||
245 | static bool ata_led_on = false; | ||
244 | static bool spinup = false; | 246 | static bool spinup = false; |
245 | static bool sleeping = true; | 247 | static bool sleeping = true; |
246 | static long sleep_timeout = 5*HZ; | 248 | static 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 | ||
478 | static void ata_led(bool on) { | ||
479 | ata_led_on = on; | ||
480 | if (ata_led_enabled) { | ||
481 | led(ata_led_on); | ||
482 | } | ||
483 | } | ||
484 | |||
476 | int ata_read_sectors(IF_MV2(int drive,) | 485 | int 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 | |||
1471 | void 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, | |||
61 | extern void ata_delayed_write(unsigned long sector, const void* buf); | 61 | extern void ata_delayed_write(unsigned long sector, const void* buf); |
62 | extern void ata_flush(void); | 62 | extern void ata_flush(void); |
63 | extern void ata_spin(void); | 63 | extern void ata_spin(void); |
64 | extern void ata_set_led_enabled(bool enabled); | ||
64 | extern unsigned short* ata_get_identify(void); | 65 | extern unsigned short* ata_get_identify(void); |
65 | 66 | ||
66 | extern long last_disk_activity; | 67 | extern long last_disk_activity; |