69befb58ef
git-svn-id: svn://svn.rockbox.org/rockbox/trunk@2441 a1c6a512-1295-4272-9138-f99709370657
292 lines
8.8 KiB
C
292 lines
8.8 KiB
C
/***************************************************************************
|
|
* __________ __ ___.
|
|
* 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.
|
|
*/
|
|
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;
|
|
}
|