rockbox/apps/gui/pitchscreen.c
Jonathan Gordon e385ee18ce Decouple the statusbar drawing from the rest of the screen drawing. it is not drawn roughly 4x per second automatically.
viewport_Set_defaults() will setup the given viewport with the correct "full screen" dimensions (so start at 0,0 if statusbars are disabled or 0,8 if they are enabled.)
All screens should keep the statusbar enabled, but if you really want to ignore the user setting you can disbaled it with viewportmanager_set_statusbar(false).

This commit also includes some menu/list viewport cleanups from kugel in FS#9603


git-svn-id: svn://svn.rockbox.org/rockbox/trunk@19622 a1c6a512-1295-4272-9138-f99709370657
2008-12-31 05:59:26 +00:00

366 lines
11 KiB
C

/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
* $Id$
*
* Copyright (C) 2002 Björn Stenberg
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
****************************************************************************/
#include <stdbool.h>
#include <string.h>
#include <stdio.h>
#include "config.h"
#include "sprintf.h"
#include "settings.h"
#include "action.h"
#include "system.h"
#include "font.h"
#include "misc.h"
#include "dsp.h"
#include "sound.h"
#include "pcmbuf.h"
#include "lang.h"
#include "icons.h"
#include "screen_access.h"
#include "screens.h"
#include "statusbar.h"
#include "viewport.h"
#include "pitchscreen.h"
#define PITCH_MODE_ABSOLUTE 1
#define PITCH_MODE_SEMITONE -PITCH_MODE_ABSOLUTE
#define ICON_BORDER 12 /* icons are currently 7x8, so add ~2 pixels */
/* on both sides when drawing */
#define PITCH_MAX 2000
#define PITCH_MIN 500
#define PITCH_SMALL_DELTA 1
#define PITCH_BIG_DELTA 10
#define PITCH_NUDGE_DELTA 20
static int pitch_mode = PITCH_MODE_ABSOLUTE; /* 1 - absolute, -1 - semitone */
enum
{
PITCH_TOP = 0,
PITCH_MID,
PITCH_BOTTOM,
PITCH_ITEM_COUNT,
};
static void pitchscreen_fix_viewports(struct viewport *parent,
struct viewport pitch_viewports[PITCH_ITEM_COUNT])
{
short n, height;
height = font_get(parent->font)->height;
for (n = 0; n < PITCH_ITEM_COUNT; n++)
{
pitch_viewports[n] = *parent;
pitch_viewports[n].height = height;
}
pitch_viewports[PITCH_TOP].y += ICON_BORDER;
pitch_viewports[PITCH_MID].x += ICON_BORDER;
pitch_viewports[PITCH_MID].width = parent->width - ICON_BORDER*2;
pitch_viewports[PITCH_MID].height = height * 2;
pitch_viewports[PITCH_MID].y += parent->height / 2 -
pitch_viewports[PITCH_MID].height / 2;
pitch_viewports[PITCH_BOTTOM].y += parent->height - height - ICON_BORDER;
}
/* must be called before pitchscreen_draw, or within
* since it neither clears nor updates the display */
static void pitchscreen_draw_icons (struct screen *display,
struct viewport *parent)
{
display->set_viewport(parent);
display->mono_bitmap(bitmap_icons_7x8[Icon_UpArrow],
parent->width/2 - 3,
2, 7, 8);
display->mono_bitmap(bitmap_icons_7x8[Icon_DownArrow],
parent->width /2 - 3,
parent->height - 10, 7, 8);
display->mono_bitmap(bitmap_icons_7x8[Icon_FastForward],
parent->width - 10,
parent->height /2 - 4, 7, 8);
display->mono_bitmap(bitmap_icons_7x8[Icon_FastBackward],
2,
parent->height /2 - 4, 7, 8);
}
static void pitchscreen_draw (struct screen *display, int max_lines,
struct viewport pitch_viewports[PITCH_ITEM_COUNT], int pitch)
{
unsigned char* ptr;
unsigned char buf[32];
int w, h;
bool show_lang_pitch;
/* Hide "Pitch up/Pitch down" for a small screen */
if (max_lines >= 5)
{
/* UP: Pitch Up */
display->set_viewport(&pitch_viewports[PITCH_TOP]);
if (pitch_mode == PITCH_MODE_ABSOLUTE) {
ptr = str(LANG_PITCH_UP);
} else {
ptr = str(LANG_PITCH_UP_SEMITONE);
}
display->getstringsize(ptr,&w,&h);
display->clear_viewport();
/* draw text */
display->putsxy((pitch_viewports[PITCH_TOP].width / 2) -
(w / 2), 0, ptr);
/* DOWN: Pitch Down */
display->set_viewport(&pitch_viewports[PITCH_BOTTOM]);
if (pitch_mode == PITCH_MODE_ABSOLUTE) {
ptr = str(LANG_PITCH_DOWN);
} else {
ptr = str(LANG_PITCH_DOWN_SEMITONE);
}
display->getstringsize(ptr,&w,&h);
display->clear_viewport();
/* draw text */
display->putsxy((pitch_viewports[PITCH_BOTTOM].width / 2) -
(w / 2), 0, ptr);
}
display->set_viewport(&pitch_viewports[PITCH_MID]);
snprintf((char *)buf, sizeof(buf), "%s", str(LANG_PITCH));
display->getstringsize(buf,&w,&h);
/* lets hide LANG_PITCH for smaller screens */
display->clear_viewport();
if ((show_lang_pitch = (max_lines >= 3)))
display->putsxy((pitch_viewports[PITCH_MID].width / 2) - (w / 2),
0, buf);
/* we don't need max_lines any more, reuse it*/
max_lines = w;
/* "XXX.X%" */
snprintf((char *)buf, sizeof(buf), "%d.%d%%",
pitch / 10, pitch % 10 );
display->getstringsize(buf,&w,&h);
display->putsxy((pitch_viewports[PITCH_MID].width / 2) - (w / 2),
(show_lang_pitch? h : h/2), buf);
/* What's wider? LANG_PITCH or the value?
* Only interesting if LANG_PITCH is actually drawn */
max_lines = (show_lang_pitch ? ((max_lines > w) ? max_lines : w) : w);
/* Let's treat '+' and '-' as equally wide
* This saves a getstringsize call
* Also, it wouldn't look nice if -2% shows up, but +2% not */
display->getstringsize("+2%",&w,&h);
max_lines += 2*w;
/* hide +2%/-2% for a narrow screens */
if (max_lines < pitch_viewports[PITCH_MID].width)
{
/* RIGHT: +2% */
display->putsxy(pitch_viewports[PITCH_MID].width - w, h /2, "+2%");
/* LEFT: -2% */
display->putsxy(0, h / 2, "-2%");
}
/* Lastly, a fullscreen update */
display->set_viewport(NULL);
display->update();
}
static int pitch_increase(int pitch, int delta, bool allow_cutoff)
{
int new_pitch;
if (delta < 0) {
if (pitch + delta >= PITCH_MIN) {
new_pitch = pitch + delta;
} else {
if (!allow_cutoff) {
return pitch;
}
new_pitch = PITCH_MIN;
}
} else if (delta > 0) {
if (pitch + delta <= PITCH_MAX) {
new_pitch = pitch + delta;
} else {
if (!allow_cutoff) {
return pitch;
}
new_pitch = PITCH_MAX;
}
} else {
/* delta == 0 -> no real change */
return pitch;
}
sound_set_pitch(new_pitch);
return new_pitch;
}
/* Factor for changing the pitch one half tone up.
The exact value is 2^(1/12) = 1.05946309436
But we use only integer arithmetics, so take
rounded factor multiplied by 10^5=100,000. This is
enough to get the same promille values as if we
had used floating point (checked with a spread
sheet).
*/
#define PITCH_SEMITONE_FACTOR 105946L
/* Some helpful constants. K is the scaling factor for SEMITONE.
N is for more accurate rounding
KN is K * N
*/
#define PITCH_K_FCT 100000UL
#define PITCH_N_FCT 10
#define PITCH_KN_FCT 1000000UL
static int pitch_increase_semitone(int pitch, bool up)
{
uint32_t tmp;
uint32_t round_fct; /* How much to scale down at the end */
tmp = pitch;
if (up) {
tmp = tmp * PITCH_SEMITONE_FACTOR;
round_fct = PITCH_K_FCT;
} else {
tmp = (tmp * PITCH_KN_FCT) / PITCH_SEMITONE_FACTOR;
round_fct = PITCH_N_FCT;
}
/* Scaling down with rounding */
tmp = (tmp + round_fct / 2) / round_fct;
return pitch_increase(pitch, tmp - pitch, false);
}
/*
returns:
0 on exit
1 if USB was connected
*/
int gui_syncpitchscreen_run(void)
{
int button;
int pitch = sound_get_pitch();
int new_pitch, delta = 0;
bool nudged = false;
bool exit = false;
short i;
struct viewport parent[NB_SCREENS]; /* should maybe
be a parameter of this function */
short max_lines[NB_SCREENS];
struct viewport pitch_viewports[NB_SCREENS][PITCH_ITEM_COUNT];
/* initialize pitchscreen vps */
FOR_NB_SCREENS(i)
{
screens[i].clear_display();
viewport_set_defaults(&parent[i], i);
max_lines[i] = viewport_get_nb_lines(&parent[i]);
pitchscreen_fix_viewports(&parent[i], pitch_viewports[i]);
/* also, draw the icons now, it's only needed once */
pitchscreen_draw_icons(&screens[i], &parent[i]);
}
#if CONFIG_CODEC == SWCODEC
pcmbuf_set_low_latency(true);
#endif
i = 0;
while (!exit)
{
FOR_NB_SCREENS(i)
pitchscreen_draw(&screens[i], max_lines[i],
pitch_viewports[i], pitch);
button = get_action(CONTEXT_PITCHSCREEN,HZ);
switch (button) {
case ACTION_PS_INC_SMALL:
delta = PITCH_SMALL_DELTA;
break;
case ACTION_PS_INC_BIG:
delta = PITCH_BIG_DELTA;
break;
case ACTION_PS_DEC_SMALL:
delta = -PITCH_SMALL_DELTA;
break;
case ACTION_PS_DEC_BIG:
delta = -PITCH_BIG_DELTA;
break;
case ACTION_PS_NUDGE_RIGHT:
new_pitch = pitch_increase(pitch, PITCH_NUDGE_DELTA, false);
nudged = (new_pitch != pitch);
pitch = new_pitch;
break;
case ACTION_PS_NUDGE_RIGHTOFF:
if (nudged) {
pitch = pitch_increase(pitch, -PITCH_NUDGE_DELTA, false);
}
nudged = false;
break;
case ACTION_PS_NUDGE_LEFT:
new_pitch = pitch_increase(pitch, -PITCH_NUDGE_DELTA, false);
nudged = (new_pitch != pitch);
pitch = new_pitch;
break;
case ACTION_PS_NUDGE_LEFTOFF:
if (nudged) {
pitch = pitch_increase(pitch, PITCH_NUDGE_DELTA, false);
}
nudged = false;
break;
case ACTION_PS_RESET:
pitch = 1000;
sound_set_pitch( pitch );
break;
case ACTION_PS_TOGGLE_MODE:
pitch_mode = -pitch_mode;
break;
case ACTION_PS_EXIT:
exit = true;
break;
default:
if(default_event_handler(button) == SYS_USB_CONNECTED)
return 1;
break;
}
if(delta)
{
if (pitch_mode == PITCH_MODE_ABSOLUTE) {
pitch = pitch_increase(pitch, delta, true);
} else {
pitch = pitch_increase_semitone(pitch, delta > 0 ? true:false);
}
delta = 0;
}
}
#if CONFIG_CODEC == SWCODEC
pcmbuf_set_low_latency(false);
#endif
return 0;
}