From f11702921fc137fb9eb08b3311bc4264ed6a2fe2 Mon Sep 17 00:00:00 2001 From: Linus Nielsen Feltzing Date: Fri, 27 Sep 2002 09:44:51 +0000 Subject: Philip Pertermanns peak meter git-svn-id: svn://svn.rockbox.org/rockbox/trunk@2437 a1c6a512-1295-4272-9138-f99709370657 --- apps/recorder/peakmeter.c | 292 ++++++++++++++++++++++++++++++++++++++++++++++ apps/recorder/peakmeter.h | 29 +++++ 2 files changed, 321 insertions(+) create mode 100644 apps/recorder/peakmeter.c create mode 100644 apps/recorder/peakmeter.h (limited to 'apps/recorder') diff --git a/apps/recorder/peakmeter.c b/apps/recorder/peakmeter.c new file mode 100644 index 0000000000..d184641c67 --- /dev/null +++ b/apps/recorder/peakmeter.c @@ -0,0 +1,292 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright (C) 2002 by Philipp Pertermann + * + * All files in this archive are subject to the GNU General Public License. + * See the file COPYING in the source tree root for full license agreement. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ****************************************************************************/ +#include "mas.h" +#include "thread.h" +#include "kernel.h" +#include "settings.h" +#include "lcd.h" +#include "wps-display.h" +#include "sprintf.h" +#include "button.h" +#include "system.h" +#include "font.h" +#include "icons.h" +#include "lang.h" +#include "peakmeter.h" + +/* buffer the read peak value */ +static int peak_meter_max_l; +static int peak_meter_max_r; + +/* point in time when peak_meter_max_x becomes invalid */ +static long peak_meter_timeout_l; +static long peak_meter_timeout_r; + +/* when true a clip has occurred */ +static bool peak_meter_l_clip = false; +static bool peak_meter_r_clip = false; + +/* point in time when peak_meter_x_oveflow becomes invalid */ +static long peak_meter_clip_timeout_l; +static long peak_meter_clip_timeout_r; + +/* if set to true clip timeout is disabled */ +static bool peak_meter_clip_eternal = false; + +#ifndef SIMULATOR +static int peak_meter_src_l = MAS_REG_DQPEAK_L; +static int peak_meter_src_r = MAS_REG_DQPEAK_R; +#endif + +/* temporarily en- / disables peak meter. This is + especially for external applications to detect + if the peak_meter is in use and needs drawing at all */ +bool peak_meter_enabled = true; + +static int peak_meter_l; +static int peak_meter_r; + +/* time out values for max */ +static long max_time_out[] = { + 0 * HZ, HZ / 5, 30, HZ / 2, HZ, 2 * HZ, + 3 * HZ, 4 * HZ, 5 * HZ, 6 * HZ, 7 * HZ, 8 * HZ, + 9 * HZ, 10 * HZ, 15 * HZ, 20 * HZ, 30 * HZ, 60 * HZ +}; + +/* time out values for clip */ +static long clip_time_out[] = { + 0 * HZ, 1 * HZ, 2 * HZ, 3 * HZ, 4 * HZ, 5 * HZ, + 6 * HZ, 7 * HZ, 8 * HZ, 9 * HZ, 10 * HZ, 15 * HZ, + 20 * HZ, 25 * HZ, 30 * HZ, 45 * HZ, 60 * HZ, 90 * HZ, + 120 * HZ, 180 * HZ, 300 * HZ, 600 * HZ, 1200 * HZ, + 2700 * HZ, 5400 * HZ +}; + +/** + * Set the source of the peak meter to playback or to + * record. + * @param: bool playback - If true playback peak meter is used. + * If false recording peak meter is used. + */ +void peak_meter_playback(bool playback) { +#ifdef SIMULATOR + (void)playback; +#else + if (playback) { + peak_meter_src_l = MAS_REG_DQPEAK_L; + peak_meter_src_r = MAS_REG_DQPEAK_R; + } else { + peak_meter_src_l = MAS_REG_QPEAK_L; + peak_meter_src_r = MAS_REG_QPEAK_R; + } +#endif +} + +/** + * Reads peak values from the MAS, and detects clips. The + * values are stored in peak_meter_l peak_meter_r for later + * evauluation. Consecutive calls to peak_meter_peek detect + * that ocurred. This function could be used by a thread for + * busy reading the MAS. + */ +static void peak_meter_peek(void) { +#ifdef SIMULATOR + int left = 8000; + int right = 9000; +#else + /* read the peak values */ + int left = mas_codec_readreg(peak_meter_src_l); + int right = mas_codec_readreg(peak_meter_src_r); +#endif + + /* check for clips + An clip is assumed when two consecutive readouts + of the volume are at full scale. This is proven + to be inaccurate in both ways: it may detect clips + when no clip occurred and it may fail to detect + a real clip. */ + if ((left == peak_meter_l) && + (left == MAX_PEAK - 1)) { + peak_meter_l_clip = true; + peak_meter_clip_timeout_l = + current_tick + clip_time_out[global_settings.peak_meter_clip_hold]; + } + + if ((right == peak_meter_r) && + (right == MAX_PEAK - 1)) { + peak_meter_r_clip = true; + peak_meter_clip_timeout_r = + current_tick + clip_time_out[global_settings.peak_meter_clip_hold]; + } + + /* peaks are searched -> we have to find the maximum */ + peak_meter_l = MAX(peak_meter_l, left); + peak_meter_r = MAX(peak_meter_r, right); +} + +/** + * Reads out the peak volume of the left channel. + * @return int - The maximum value that has been detected + * since the last call of peak_meter_read_l. The value + * is in the range 0 <= value < MAX_PEAK. + */ +static int peak_meter_read_l (void) { + int retval = peak_meter_l; +#ifdef SIMULATOR + peak_meter_l = 8000; +#else + peak_meter_l = mas_codec_readreg(peak_meter_src_l); +#endif + return retval; +} + +/** + * Reads out the peak volume of the right channel. + * @return int - The maximum value that has been detected + * since the last call of peak_meter_read_l. The value + * is in the range 0 <= value < MAX_PEAK. + */ +static int peak_meter_read_r (void) { + int retval = peak_meter_r; +#ifdef SIMULATOR + peak_meter_l = 8000; +#else + peak_meter_r = mas_codec_readreg(peak_meter_src_r); +#endif + return retval; +} + +/** + * Reset the detected clips. This method is for + * use by the user interface. + * @param int unused - This parameter was added to + * make the function compatible with set_int + */ +void peak_meter_set_clip_hold(int time) { + peak_meter_clip_eternal = false; + + if (time <= 0) { + peak_meter_l_clip = false; + peak_meter_r_clip = false; + peak_meter_clip_eternal = true; + } +} + + +/** + * Draws a peak meter in the specified size at the specified position. + * @param int x - The x coordinate. + * Make sure that 0 <= x and x + width < LCD_WIDTH + * @param int y - The y coordinate. + * Make sure that 0 <= y and y + height < LCD_HEIGHT + * @param int width - The width of the peak meter. Note that for display + * of clips a 3 pixel wide area is used -> + * width > 3 + * @param int height - The height of the peak meter. height > 3 + */ +void peak_meter_draw(int x, int y, int width, int height) { + int left = 0, right = 0; + static int last_left = 0, last_right = 0; + int meterwidth = width - 3; + int i; + + /* if disabled only draw the peak meter */ + if (peak_meter_enabled) { + /* read the volume info from MAS */ + left = peak_meter_read_l(); + right = peak_meter_read_r(); + peak_meter_peek(); + + + /* scale the samples */ + left /= (MAX_PEAK / meterwidth); + right /= (MAX_PEAK / meterwidth); + + /* apply release */ + left = MAX(left , last_left - global_settings.peak_meter_release); + right = MAX(right, last_right - global_settings.peak_meter_release); + + /* reset max values after timeout */ + if (TIME_AFTER(current_tick, peak_meter_timeout_l)){ + peak_meter_max_l = 0; + } + + if (TIME_AFTER(current_tick, peak_meter_timeout_r)){ + peak_meter_max_r = 0; + } + + if (!peak_meter_clip_eternal) { + if (peak_meter_l_clip && + TIME_AFTER(current_tick, peak_meter_clip_timeout_l)){ + peak_meter_l_clip = false; + } + + if (peak_meter_r_clip && + TIME_AFTER(current_tick, peak_meter_clip_timeout_r)){ + peak_meter_r_clip = false; + } + } + + /* check for new max values */ + if (left > peak_meter_max_l) { + peak_meter_max_l = left - 1; + peak_meter_timeout_l = + current_tick + max_time_out[global_settings.peak_meter_hold]; + } + + if (right > peak_meter_max_r) { + peak_meter_max_r = right - 1; + peak_meter_timeout_r = + current_tick + max_time_out[global_settings.peak_meter_hold]; + } + } + + /* draw the peak meter */ + lcd_clearrect(x, y, width, height); + + /* draw left */ + lcd_fillrect (x, y, left, height / 2 - 2 ); + if (peak_meter_max_l > 0) { + lcd_drawline(x + peak_meter_max_l, y, + x + peak_meter_max_l, y + height / 2 - 2 ); + } + if (peak_meter_l_clip) { + lcd_fillrect(x + meterwidth, y, 3, height / 2 - 1); + } + + /* draw right */ + lcd_fillrect(x, y + height / 2 + 1, right, height / 2 - 2); + if (peak_meter_max_r > 0) { + lcd_drawline( x + peak_meter_max_r, y + height / 2, + x + peak_meter_max_r, y + height - 2); + } + if (peak_meter_r_clip) { + lcd_fillrect(x + meterwidth, y + height / 2, 3, height / 2 - 1); + } + + /* draw scale */ + lcd_drawline(x + meterwidth, y, + x + meterwidth, y + height - 2); + for (i = 0; i < 10; i++) { + lcd_invertpixel(x + meterwidth * i / 10, y + height / 2 - 1); + } + + last_left = left; + last_right = right; +} diff --git a/apps/recorder/peakmeter.h b/apps/recorder/peakmeter.h new file mode 100644 index 0000000000..8d894cd920 --- /dev/null +++ b/apps/recorder/peakmeter.h @@ -0,0 +1,29 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright (C) 2002 by Philipp Pertermann + * + * All files in this archive are subject to the GNU General Public License. + * See the file COPYING in the source tree root for full license agreement. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ****************************************************************************/ +#ifndef __PEAKMETER_H__ +#define __PEAKMETER_H__ + +extern bool peak_meter_enabled; + +extern void peak_meter_init(void); +extern void peak_meter_playback(bool playback); +extern void peak_meter_draw(int x, int y, int width, int height); +extern void peak_meter_set_clip_hold(int time); + +#endif /* __PEAKMETER_H__ */ -- cgit v1.2.3