/*************************************************************************** * __________ __ ___. * Open \______ \ ____ ____ | | _\_ |__ _______ ___ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ * \/ \/ \/ \/ \/ * $Id: $ * * Copyright (C) 2002 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 #include "menu.h" #include "lcd.h" #include "button.h" #include "mas.h" #include "system.h" #include "oscillosgraph.h" #define SCALE 0x200 /* The different drawing modes */ #define DRAW_MODE_FILLED 0 #define DRAW_MODE_OUTLINE 1 #define DRAW_MODE_PIXEL 2 #define DRAW_MODE_COUNT 3 /* number of ticks between two volume samples */ static int speed = 1; /* roll == true -> lcd rolls */ static bool roll = true; /* see DRAW_MODE_XXX constants for valid values */ static int drawMode = DRAW_MODE_FILLED; /** * Displays a vertically scrolling oscillosgraph using * hardware scrolling of the display. The user can change * speed */ Menu oscillosgraph(void){ /* stores current volume value left */ int left; /* stores current volume value right */ int right; /* specifies the current position on the lcd */ int y = LCD_WIDTH - 1; /* buffer the last user input */ int button = 0; /* only needed when drawing lines */ int lastLeft = 0; int lastRight = 0; int lasty = 0; /* the main loop */ while (button != BUTTON_OFF){ /* read the volume info from MAS */ left = mas_codec_readreg(0xC) / SCALE; left = MIN(left, LCD_WIDTH / 2 - 1); right = mas_codec_readreg(0xD) / SCALE; right = MIN(right, LCD_WIDTH / 2 - 1); /* delete current line. I believe that lcd_clearrect may be faster than lcd_clearline because it might plot servaral pixel at once. At least it would be easier to optimize a drawing routine that can be reduced to vertical / horizontal directions while lcd_drawline must always be capable to draw lines in any angle. */ lcd_clearrect(0, y, LCD_WIDTH, 1); switch (drawMode) { case DRAW_MODE_FILLED: /* Here again I hope to increase performance by using orthogonal drawing functions. */ lcd_fillrect(LCD_WIDTH / 2 + 1, y, right, 1); lcd_fillrect(LCD_WIDTH / 2 - left, y, left, 1); break; case DRAW_MODE_OUTLINE: /* last position needed for lines */ lasty = MAX(y-1, 0); /* Here real lines were neccessary because anything else was ugly. */ lcd_drawline(LCD_WIDTH / 2 + right , y, LCD_WIDTH / 2 + lastRight , lasty); lcd_drawline(LCD_WIDTH / 2 - left , y, LCD_WIDTH / 2 - lastLeft, lasty); /* have to store the old values for drawing lines the next time */ lastRight = right; lastLeft = left; break; case DRAW_MODE_PIXEL: /* straight and simple */ lcd_drawpixel(LCD_WIDTH / 2 + right, y); lcd_drawpixel(LCD_WIDTH / 2 - left, y); break; } /* increment and adjust the drawing position */ y++; if (y >= LCD_HEIGHT) { y = 0; } /* I roll before update because otherwise the new line would appear at the wrong end of the display */ if (roll) { lcd_v_roll(y); } /* now finally make the new sample visible */ lcd_update_rect(0, MAX(y-1, 0), LCD_WIDTH, 2); /* There are two mechanisms to alter speed: 1.) slowing down is achieved by increasing the time waiting for user input. This mechanism uses positive values. 2.) speeding up is achieved by leaving out the user input check for (-speed) volume samples. For this mechanism negative values are used. */ if (speed >= 0 || (speed < 0 && !(y % (-speed)))) { char buf[12]; /* speed values > 0 slow the oszi down. By user input speed might become < 1. If a value < 1 was passed user input would be disabled. Thus it must be ensured that at least 1 is passed. */ button = button_get_w_tmo(MAX(speed, 1)); /* react to user input */ switch (button) { case BUTTON_UP: speed ++; snprintf(buf, sizeof buf, "Speed: %d", -speed); lcd_putsxy(0, (y + LCD_HEIGHT - 8) % LCD_HEIGHT, buf, 0); lcd_update_rect(0, (y + LCD_HEIGHT - 8) % LCD_HEIGHT, LCD_WIDTH, 8); break; case BUTTON_DOWN: speed --; snprintf(buf, sizeof buf, "Speed: %d", -speed); lcd_putsxy(0, (y + LCD_HEIGHT - 8) % LCD_HEIGHT, buf, 0); lcd_update_rect(0, (y + LCD_HEIGHT - 8) % LCD_HEIGHT, LCD_WIDTH, 8); break; case BUTTON_PLAY: /* pause the demo */ button_get(true); break; case BUTTON_F1: /* toggle rolling */ roll = !roll; break; case BUTTON_F2: /* step through the display modes */ drawMode ++; drawMode = drawMode % DRAW_MODE_COUNT; /* lcd buffer might be rolled so that the transition from LCD_HEIGHT to 0 takes place in the middle of the screen. That produces ugly results in DRAW_MODE_OUTLINE mode. If rolling is enabled this change will be reverted before the next update anyway.*/ lcd_v_roll(0); break; case BUTTON_F3: speed = 1; snprintf(buf, sizeof buf, "Speed: %d", -speed); lcd_putsxy(0, (y + LCD_HEIGHT - 8) % LCD_HEIGHT, buf, 0); lcd_update_rect(0, (y + LCD_HEIGHT - 8) % LCD_HEIGHT, LCD_WIDTH, 8); break; } } } /* restore to default roll position. Looks funny if you forget to do this... */ lcd_v_roll(0); lcd_update(); /* standard return */ return MENU_OK; }