summaryrefslogtreecommitdiff
path: root/apps/recorder/peakmeter.c
diff options
context:
space:
mode:
Diffstat (limited to 'apps/recorder/peakmeter.c')
-rw-r--r--apps/recorder/peakmeter.c374
1 files changed, 337 insertions, 37 deletions
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;