summaryrefslogtreecommitdiff
path: root/apps/recorder/peakmeter.c
diff options
context:
space:
mode:
authorLinus Nielsen Feltzing <linus@haxx.se>2005-04-04 09:12:12 +0000
committerLinus Nielsen Feltzing <linus@haxx.se>2005-04-04 09:12:12 +0000
commit68482bbed286981a5ef09a466620e459790c96e6 (patch)
treede613ede8a83077b935debc1ec3edd965d5d6090 /apps/recorder/peakmeter.c
parent5fb6c64ffc8e6ab7512d805d2860831e492e5c52 (diff)
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
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 @@
#include "kernel.h"
#include "settings.h"
#include "lcd.h"
+#include "widgets.h"
#include "wps-display.h"
#include "sprintf.h"
#include "button.h"
@@ -67,6 +68,24 @@ static unsigned short db_min = 0;
static unsigned short db_max = 9000;
static unsigned short db_range = 9000;
+static unsigned short trig_strt_threshold;
+static long trig_strt_duration;
+static long trig_strt_dropout;
+static unsigned short trig_stp_threshold;
+static long trig_stp_hold;
+static long trig_rstrt_gap;
+
+/* point in time when the threshold was exceeded */
+static long trig_hightime;
+
+/* point in time when the volume fell below the threshold*/
+static long trig_lowtime;
+
+/* The output value of the trigger. See TRIG_XXX constants vor valid values */
+static int trig_status = TRIG_OFF;
+
+static void (*trigger_listener)(int) = NULL;
+
#if CONFIG_HWCODEC == MASNONE
#define MAS_REG_DQPEAK_L 0
#define MAS_REG_DQPEAK_R 0
@@ -124,7 +143,7 @@ static const long clip_time_out[] = {
/* precalculated peak values that represent magical
dBfs values. Used to draw the scale */
-#define DB_SCALE_SRC_VALUES_SIZE 11
+#define DB_SCALE_SRC_VALUES_SIZE 12
#if 0
static const int db_scale_src_values[DB_SCALE_SRC_VALUES_SIZE] = {
32767, /* 0 db */
@@ -138,6 +157,7 @@ static const int db_scale_src_values[DB_SCALE_SRC_VALUES_SIZE] = {
328, /* -40 db */
104, /* -50 db */
33, /* -60 db */
+ 1, /* -inf */
};
#else
static const int db_scale_src_values[DB_SCALE_SRC_VALUES_SIZE] = {
@@ -152,9 +172,25 @@ static const int db_scale_src_values[DB_SCALE_SRC_VALUES_SIZE] = {
373, /* -40 db */
102, /* -50 db */
33, /* -60 db */
+ 0, /* -inf */
};
#endif
+const char* peak_meter_dbnames[DB_SCALE_SRC_VALUES_SIZE] = {
+ "0 db",
+ "-3 db",
+ "-6 db",
+ "-9 db",
+ "-12 db",
+ "-18 db",
+ "-24 db",
+ "-30 db",
+ "-40 db",
+ "-50 db",
+ "-60 db",
+ "-inf",
+};
+
static int db_scale_count = DB_SCALE_SRC_VALUES_SIZE;
/* if db_scale_valid is false the content of
@@ -275,8 +311,8 @@ int calc_db (int isample) {
/**
- * A helper function for db_to_sample. Don't call it separately but
- * use db_to_sample. If one or both of min and max are outside the
+ * A helper function for peak_meter_db2sample. Don't call it separately but
+ * use peak_meter_db2sample. If one or both of min and max are outside the
* range 0 <= min (or max) < 8961 the behaviour of this function is
* undefined. It may not return.
* @param int min - The minimum of the value range that is searched.
@@ -312,7 +348,7 @@ static int db_to_sample_bin_search(int min, int max, int db){
* @return int - The return value is in the range of
* 0 <= return value < MAX_PEAK
*/
-static int db_to_sample(int db) {
+int peak_meter_db2sample(int db) {
int retval = 0;
/* what is the maximum pseudo db value */
@@ -351,7 +387,7 @@ static int db_to_sample(int db) {
*/
void peak_meter_set_min(int newmin) {
if (peak_meter_use_dbfs) {
- peak_meter_range_min = db_to_sample(newmin);
+ peak_meter_range_min = peak_meter_db2sample(newmin);
} else {
if (newmin < peak_meter_range_max) {
@@ -392,7 +428,7 @@ int peak_meter_get_min(void) {
*/
void peak_meter_set_max(int newmax) {
if (peak_meter_use_dbfs) {
- peak_meter_range_max = db_to_sample(newmax);
+ peak_meter_range_max = peak_meter_db2sample(newmax);
} else {
if (newmax > peak_meter_range_min) {
peak_meter_range_max = newmax * MAX_PEAK / 100;
@@ -504,6 +540,15 @@ void peak_meter_playback(bool playback)
#endif
}
+static void set_trig_status(int new_state) {
+ if (trig_status != new_state) {
+ trig_status = new_state;
+ if (trigger_listener != NULL) {
+ trigger_listener(trig_status);
+ }
+ }
+}
+
/**
* Reads peak values from the MAS, and detects clips. The
* values are stored in peak_meter_l peak_meter_r for later
@@ -546,6 +591,121 @@ inline void peak_meter_peek(void)
current_tick + clip_time_out[peak_meter_clip_hold];
}
+ switch (trig_status) {
+ case TRIG_READY:
+ /* no more changes, if trigger was activated as release trigger */
+ /* threshold exceeded? */
+ if ((left > trig_strt_threshold) || (right > trig_strt_threshold)) {
+ if (trig_strt_duration) {
+ /* reset trigger duration */
+ trig_hightime = current_tick;
+
+ /* reset dropout duration */
+ trig_lowtime = current_tick;
+
+ /* if trig_duration is set to 0 the user wants to start
+ recording immediately */
+ set_trig_status(TRIG_STEADY);
+ } else {
+ set_trig_status(TRIG_GO);
+ }
+ }
+ break;
+
+ case TRIG_STEADY:
+ case TRIG_RETRIG:
+ /* trigger duration exceeded */
+ if (current_tick - trig_hightime > trig_strt_duration) {
+ set_trig_status(TRIG_GO);
+ } else {
+ /* threshold exceeded? */
+ if ((left > trig_strt_threshold) ||
+ (right > trig_strt_threshold)) {
+ /* reset lowtime */
+ trig_lowtime = current_tick;
+ }
+ /* volume is below threshold */
+ else {
+ /* dropout occurred? */
+ if (current_tick - trig_lowtime > trig_strt_dropout){
+ if (trig_status == TRIG_STEADY){
+ set_trig_status(TRIG_READY);
+ }
+ /* trig_status == TRIG_RETRIG */
+ else {
+ /* the gap has already expired */
+ trig_lowtime = current_tick - trig_rstrt_gap - 1;
+ set_trig_status(TRIG_POSTREC);
+ }
+ }
+ }
+ }
+ break;
+
+ case TRIG_GO:
+ case TRIG_CONTINUE:
+ /* threshold exceeded? */
+ if ((left > trig_stp_threshold) || (right > trig_stp_threshold)) {
+ /* restart hold time countdown */
+ trig_lowtime = current_tick;
+ } else {
+ set_trig_status(TRIG_POSTREC);
+ trig_hightime = current_tick;
+ }
+ break;
+
+ case TRIG_POSTREC:
+ /* gap time expired? */
+ if (current_tick - trig_lowtime > trig_rstrt_gap){
+ /* start threshold exceeded? */
+ if ((left > trig_strt_threshold) ||
+ (right > trig_strt_threshold)) {
+
+ set_trig_status(TRIG_RETRIG);
+ trig_hightime = current_tick;
+ }
+ else
+
+ /* stop threshold exceeded */
+ if ((left > trig_stp_threshold) ||
+ (right > trig_stp_threshold)) {
+ if (current_tick - trig_hightime > trig_stp_hold){
+ trig_lowtime = current_tick;
+ set_trig_status(TRIG_CONTINUE);
+ } else {
+ trig_lowtime = current_tick - trig_rstrt_gap - 1;
+ }
+ }
+
+ /* below any threshold */
+ else {
+ if (current_tick - trig_lowtime > trig_stp_hold){
+ set_trig_status(TRIG_READY);
+ } else {
+ trig_hightime = current_tick;
+ }
+ }
+ }
+
+ /* still within the gap time */
+ else {
+ /* stop threshold exceeded */
+ if ((left > trig_stp_threshold) ||
+ (right > trig_stp_threshold)) {
+ set_trig_status(TRIG_CONTINUE);
+ trig_lowtime = current_tick;
+ }
+
+ /* hold time expired */
+ else if (current_tick - trig_lowtime > trig_stp_hold){
+ trig_hightime = current_tick;
+ trig_lowtime = current_tick;
+ set_trig_status(TRIG_READY);
+ }
+ }
+ break;
+ }
+
/* peaks are searched -> we have to find the maximum. When
many calls of peak_meter_peek the maximum value will be
stored in peak_meter_x. This maximum is reset by the
@@ -558,37 +718,6 @@ inline void peak_meter_peek(void)
#endif
}
-
-/**
- * The thread function for the peak meter calls peak_meter_peek
- * to reas out the mas and find maxima, clips, etc. No display
- * is performed.
- */
-/*
-void peak_meter_thread(void) {
- sleep(5000);
- while (1) {
- if (peak_meter_enabled && peak_meter_use_thread){
- peak_meter_peek();
- }
- yield();
- }
-}
-*/
-
-/**
- * Creates the peak meter thread
- */
-/*
-void peak_meter_init(void) {
- create_thread(
- peak_meter_thread,
- peak_meter_stack,
- sizeof peak_meter_stack,
- "peakmeter");
-}
-*/
-
/**
* Reads out the peak volume of the left channel.
* @return int - The maximum value that has been detected
@@ -842,6 +971,22 @@ void peak_meter_draw(int x, int y, int width, int height) {
lcd_invertpixel(db_scale_lcd_coord[i], y + height / 2 - 1);
}
+ if (trig_status != TRIG_OFF) {
+ int start_trigx, stop_trigx, ycenter;
+
+ ycenter = y + height / 2;
+ /* display threshold value */
+ start_trigx = x+peak_meter_scale_value(trig_strt_threshold,meterwidth);
+ lcd_drawline(start_trigx, ycenter - 2, start_trigx, ycenter);
+ start_trigx ++;
+ if (start_trigx < LCD_WIDTH) lcd_drawpixel(start_trigx, ycenter - 1);
+
+ stop_trigx = x + peak_meter_scale_value(trig_stp_threshold,meterwidth);
+ lcd_drawline(stop_trigx, ycenter - 2, stop_trigx, ycenter);
+ if (stop_trigx > 0) lcd_drawpixel(stop_trigx - 1, ycenter - 1);
+
+ }
+
#ifdef PM_DEBUG
/* display a bar to show how many calls to peak_meter_peek
have ocurred since the last display */
@@ -866,6 +1011,161 @@ void peak_meter_draw(int x, int y, int width, int height) {
last_right = right;
}
+/**
+ * Defines the parameters of the trigger. After these parameters are defined
+ * the trigger can be started either by peak_meter_attack_trigger or by
+ * peak_meter_release_trigger. Note that you can pass either linear (%) or
+ * logarithmic (db) values to the thresholds. Positive values are intepreted as
+ * percent (0 is 0% .. 100 is 100%). Negative values are interpreted as db.
+ * To avoid ambiguosity of the value 0 the negative values are shifted by -1.
+ * Thus -75 is -74db .. -1 is 0db.
+ * @param start_threshold - The threshold used for attack trigger. Negative
+ * values are interpreted as db -1, positive as %.
+ * @param start_duration - The minimum time span within which start_threshold
+ * must be exceeded to fire the attack trigger.
+ * @param start_dropout - The maximum time span the level may fall below
+ * start_threshold without releasing the attack trigger.
+ * @param stop_threshold - The threshold the volume must fall below to release
+ * the release trigger.Negative values are
+ * interpreted as db -1, positive as %.
+ * @param stop_hold - The minimum time the volume must fall below the
+ * stop_threshold to release the trigger.
+ * @param
+ */
+void peak_meter_define_trigger(
+ int start_threshold,
+ long start_duration,
+ long start_dropout,
+ int stop_threshold,
+ long stop_hold_time,
+ long restart_gap
+ )
+{
+ if (start_threshold < 0) {
+ /* db */
+ if (start_threshold < -89) {
+ trig_strt_threshold = 0;
+ } else {
+ trig_strt_threshold =peak_meter_db2sample((start_threshold+1)*100);
+ }
+ } else {
+ /* linear percent */
+ trig_strt_threshold = start_threshold * MAX_PEAK / 100;
+ }
+ trig_strt_duration = start_duration;
+ trig_strt_dropout = start_dropout;
+ if (stop_threshold < 0) {
+ /* db */
+ trig_stp_threshold = peak_meter_db2sample((stop_threshold + 1) * 100);
+ } else {
+ /* linear percent */
+ trig_stp_threshold = stop_threshold * MAX_PEAK / 100;
+ }
+ trig_stp_hold = stop_hold_time;
+ trig_rstrt_gap = restart_gap;
+}
+
+/**
+ * Enables or disables the trigger.
+ * @param on - If true the trigger is turned on.
+ */
+void peak_meter_trigger(bool on) {
+ /* don't use set_trigger here as that would fire an undesired event */
+ trig_status = on ? TRIG_READY : TRIG_OFF;
+}
+
+/**
+ * Registers the listener function that listenes on trig_status changes.
+ * @param listener - The function that is called with each change of
+ * trig_status. May be set to NULL if no callback is desired.
+ */
+void peak_meter_set_trigger_listener(void (*listener)(int status)) {
+ trigger_listener = listener;
+}
+
+/**
+ * Fetches the status of the trigger.
+ * TRIG_OFF: the trigger is inactive
+ * TRIG_RELEASED: The volume level is below the threshold
+ * TRIG_ACTIVATED: The volume level has exceeded the threshold, but the trigger
+ * hasn't been fired yet.
+ * TRIG_FIRED: The volume exceeds the threshold
+ *
+ * To activate the trigger call either peak_meter_attack_trigger or
+ * peak_meter_release_trigger. To turn the trigger off call
+ * peak_meter_trigger_off.
+ */
+int peak_meter_trigger_status(void) {
+ return trig_status; /* & TRIG_PIT_MASK;*/
+}
+
+void peak_meter_draw_trig(int xpos, int ypos) {
+ int x = xpos + ICON_PLAY_STATE_WIDTH + 1;
+ switch (trig_status) {
+ long time_left;
+
+ case TRIG_READY:
+ scrollbar(x, ypos + 1, TRIGBAR_WIDTH, TRIG_HEIGHT - 2,
+ TRIGBAR_WIDTH, 0, 0, HORIZONTAL);
+ lcd_bitmap(bitmap_icons_7x8[Icon_Stop], xpos, ypos,
+ ICON_PLAY_STATE_WIDTH, STATUSBAR_HEIGHT, false);
+ break;
+
+ case TRIG_STEADY:
+ case TRIG_RETRIG:
+ time_left = trig_strt_duration - (current_tick - trig_hightime);
+ time_left = time_left * TRIGBAR_WIDTH / trig_strt_duration;
+ scrollbar(x, ypos + 1, TRIGBAR_WIDTH, TRIG_HEIGHT - 2,
+ TRIGBAR_WIDTH, 0, TRIGBAR_WIDTH - time_left, HORIZONTAL);
+ lcd_bitmap(bitmap_icons_7x8[Icon_Stop], xpos, ypos,
+ ICON_PLAY_STATE_WIDTH, STATUSBAR_HEIGHT, false);
+ break;
+
+ case TRIG_GO:
+ case TRIG_CONTINUE:
+ scrollbar(x, ypos + 1, TRIGBAR_WIDTH, TRIG_HEIGHT - 2,
+ TRIGBAR_WIDTH, TRIGBAR_WIDTH, TRIGBAR_WIDTH, HORIZONTAL);
+ lcd_bitmap(bitmap_icons_7x8[Icon_Record],
+ TRIG_WIDTH - ICON_PLAY_STATE_WIDTH, ypos,
+ ICON_PLAY_STATE_WIDTH, STATUSBAR_HEIGHT, false);
+ break;
+
+ case TRIG_POSTREC:
+ time_left = trig_stp_hold - (current_tick - trig_lowtime);
+ time_left = time_left * TRIGBAR_WIDTH / trig_stp_hold;
+ scrollbar(x, ypos + 1, TRIGBAR_WIDTH, TRIG_HEIGHT - 2,
+ TRIGBAR_WIDTH, time_left, TRIGBAR_WIDTH, HORIZONTAL);
+ lcd_bitmap(bitmap_icons_7x8[Icon_Record],
+ TRIG_WIDTH - ICON_PLAY_STATE_WIDTH, ypos,
+ ICON_PLAY_STATE_WIDTH, STATUSBAR_HEIGHT, false);
+ break;
+ }
+
+}
+
+int peak_meter_draw_get_btn(int x, int y, int width, int height)
+{
+ int button;
+ long next_refresh = current_tick;
+ long next_big_refresh = current_tick + HZ / 10;
+ button = BUTTON_NONE;
+ while (!TIME_AFTER(current_tick, next_big_refresh)) {
+ button = button_get(false);
+ if (button != BUTTON_NONE) {
+ break;
+ }
+ peak_meter_peek();
+ yield();
+
+ if (TIME_AFTER(current_tick, next_refresh)) {
+ peak_meter_draw(x, y, width, height);
+ lcd_update_rect(x, y, width, height);
+ next_refresh = current_tick + HZ / peak_meter_fps;
+ }
+ }
+ return button;
+}
+
#ifdef PM_DEBUG
static void peak_meter_clear_histogram(void) {
int i = 0;