summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--apps/plugin.c10
-rw-r--r--apps/plugin.h15
-rw-r--r--apps/plugins/lib/SOURCES1
-rw-r--r--apps/plugins/lib/osd.c470
-rw-r--r--apps/plugins/lib/osd.h94
-rw-r--r--apps/plugins/oscilloscope.c1910
-rw-r--r--firmware/export/pcm_mixer.h10
-rw-r--r--firmware/pcm_mixer.c22
8 files changed, 2096 insertions, 436 deletions
diff --git a/apps/plugin.c b/apps/plugin.c
index e0e565e504..baf6a2a256 100644
--- a/apps/plugin.c
+++ b/apps/plugin.c
@@ -791,6 +791,16 @@ static const struct plugin_api rockbox_api = {
/* new stuff at the end, sort into place next time
the API gets incompatible */
+
+#ifdef HAVE_LCD_BITMAP
+#if CONFIG_CODEC == SWCODEC
+ mixer_channel_set_buffer_hook,
+#endif
+ lcd_set_viewport,
+ viewport_set_fullscreen,
+ lcd_set_framebuffer,
+ lcd_bmp_part,
+#endif
};
int plugin_load(const char* plugin, const void* parameter)
diff --git a/apps/plugin.h b/apps/plugin.h
index bb2778164b..b8ed7a9084 100644
--- a/apps/plugin.h
+++ b/apps/plugin.h
@@ -155,7 +155,7 @@ void* plugin_get_buffer(size_t *buffer_size);
#define PLUGIN_MAGIC 0x526F634B /* RocK */
/* increase this every time the api struct changes */
-#define PLUGIN_API_VERSION 220
+#define PLUGIN_API_VERSION 221
/* update this to latest version if a change to the api struct breaks
backwards compatibility (and please take the opportunity to sort in any
@@ -960,6 +960,19 @@ struct plugin_api {
/* new stuff at the end, sort into place next time
the API gets incompatible */
+
+#ifdef HAVE_LCD_BITMAP
+#if CONFIG_CODEC == SWCODEC
+ void (*mixer_channel_set_buffer_hook)(enum pcm_mixer_channel channel,
+ chan_buffer_hook_fn_type fn);
+#endif
+ void (*lcd_set_viewport)(struct viewport* vp);
+ void (*viewport_set_fullscreen)(struct viewport *vp,
+ const enum screen_type screen);
+ void (*lcd_set_framebuffer)(fb_data *fb);
+ void (*lcd_bmp_part)(const struct bitmap *bm, int src_x, int src_y,
+ int x, int y, int width, int height);
+#endif
};
/* plugin header */
diff --git a/apps/plugins/lib/SOURCES b/apps/plugins/lib/SOURCES
index 4b33901088..d49605f1ad 100644
--- a/apps/plugins/lib/SOURCES
+++ b/apps/plugins/lib/SOURCES
@@ -52,6 +52,7 @@ pluginlib_jpeg_load.c
#endif
checkbox.c
+osd.c
picture.c
xlcd_core.c
xlcd_draw.c
diff --git a/apps/plugins/lib/osd.c b/apps/plugins/lib/osd.c
new file mode 100644
index 0000000000..ff0533a898
--- /dev/null
+++ b/apps/plugins/lib/osd.c
@@ -0,0 +1,470 @@
+/***************************************************************************
+* __________ __ ___.
+* Open \______ \ ____ ____ | | _\_ |__ _______ ___
+* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+* \/ \/ \/ \/ \/
+* $Id$
+*
+* Floating on-screen display
+*
+* Copyright (C) 2012 Michael Sevakis
+*
+* 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 "plugin.h"
+#include "osd.h"
+
+#if 1
+#undef DEBUGF
+#define DEBUGF(...)
+#endif
+
+/* At this time: assumes use of the default viewport for normal drawing */
+
+/* If multiple OSD's are wanted, could convert to caller-allocated */
+static struct osd
+{
+ enum osd_status
+ {
+ OSD_DISABLED = 0, /* Disabled entirely */
+ OSD_HIDDEN, /* Hidden from view */
+ OSD_VISIBLE, /* Visible on screen */
+ OSD_ERASED, /* Erased in preparation for regular drawing */
+ } status; /* View status */
+ struct viewport vp; /* Clipping viewport */
+ struct bitmap lcd_bitmap; /* The main LCD fb bitmap */
+ struct bitmap back_bitmap; /* The OSD backbuffer fb bitmap */
+ int maxwidth; /* How wide may it be at most? */
+ int maxheight; /* How high may it be at most? */
+ long timeout; /* Current popup stay duration */
+ long hide_tick; /* Tick when it should be hidden */
+ osd_draw_cb_fn_t draw_cb; /* Draw update callback */
+} osd;
+
+/* Framebuffer allocation macros */
+#if LCD_DEPTH == 1
+# if LCD_PIXELFORMAT == HORIZONTAL_PACKING
+# define LCD_WIDTH2BYTES(w) (((w)+7)/8)
+# define LCD_BYTES2WIDTH(b) ((b)*8)
+# elif LCD_PIXELFORMAT == VERTICAL_PACKING
+# define LCD_HEIGHT2BYTES(h) (((h)+7)/8)
+# define LCD_BYTES2HEIGHT(b) ((b)*8)
+# else
+# error Unknown 1-bit format; please define macros
+# endif /* LCD_PIXELFORMAT */
+#elif LCD_DEPTH == 2
+# if LCD_PIXELFORMAT == HORIZONTAL_PACKING
+# define LCD_WIDTH2BYTES(w) (((w)+3)/4)
+# define LCD_BYTES2WIDTH(b) ((b)*4)
+# elif LCD_PIXELFORMAT == VERTICAL_PACKING
+# define LCD_HEIGHT2BYTES(h) (((h)+3)/4)
+# define LCD_BYTES2HEIGHT(b) ((b)*4)
+# elif LCD_PIXELFORMAT == VERTICAL_INTERLEAVED
+# define LCD_WIDTH2BYTES(w) ((w)*2)
+# define LCD_BYTES2WIDTH(b) ((b)/2)
+# define LCD_HEIGHT2BYTES(h) (((h)+7)/8*2)
+# define LCD_BYTES2HEIGHT(b) ((b)/2*8)
+# else
+# error Unknown 2-bit format; please define macros
+# endif /* LCD_PIXELFORMAT */
+#elif LCD_DEPTH == 16
+# define LCD_WIDTH2BYTES(w) ((w)*2)
+# define LCD_BYTES2WIDTH(b) ((b)/2)
+#else
+# error Unknown LCD depth; please define macros
+#endif /* LCD_DEPTH */
+/* Set defaults if not defined different yet. */
+#ifndef LCD_WIDTH2BYTES
+# define LCD_WIDTH2BYTES(w) (w)
+#endif
+#ifndef LCD_BYTES2WIDTH
+# define LCD_BYTES2WIDTH(b) (b)
+#endif
+#ifndef LCD_HEIGHT2BYTES
+# define LCD_HEIGHT2BYTES(h) (h)
+#endif
+#ifndef LCD_BYTES2HEIGHT
+# define LCD_BYTES2HEIGHT(b) (b)
+#endif
+
+/* Create a bitmap framebuffer from a buffer */
+static fb_data * buf_to_fb_bitmap(void *buf, size_t bufsize,
+ int *width, int *height)
+{
+ /* Used as dest, the LCD functions cannot deal with alternate
+ strides as of now - the stride guides the calulations. If
+ that is no longer the case, then width or height can be
+ used instead (and less memory needed for a small surface!).
+ */
+ DEBUGF("buf: %p bufsize: %lu\n", buf, (unsigned long)bufsize);
+
+#if defined(LCD_STRIDEFORMAT) && LCD_STRIDEFORMAT == VERTICAL_STRIDE
+ int h = LCD_BYTES2HEIGHT(LCD_HEIGHT2BYTES(LCD_HEIGHT));
+ int w = bufsize / LCD_HEIGHT2BYTES(h);
+
+ if (w == 0)
+ {
+ DEBUGF("OSD: not enough buffer\n");
+ return NULL; /* not enough buffer */
+ }
+#else
+ int w = LCD_BYTES2WIDTH(LCD_WIDTH2BYTES(LCD_WIDTH));
+ int h = bufsize / LCD_WIDTH2BYTES(w);
+
+ if (h == 0)
+ {
+ DEBUGF("OSD: not enough buffer\n");
+ return NULL; /* not enough buffer */
+ }
+#endif
+
+ DEBUGF("fbw:%d fbh:%d\n", w, h);
+
+ *width = w;
+ *height = h;
+
+ return (fb_data *)buf;
+}
+
+static inline void osd_set_vp_pos(int x, int y, int width, int height)
+{
+ osd.vp.x = x;
+ osd.vp.y = y;
+ osd.vp.width = width;
+ osd.vp.height = height;
+}
+
+/* Sync the backbuffer to the on-screen image */
+static void osd_lcd_update_back_buffer(void)
+{
+ rb->lcd_set_framebuffer((fb_data *)osd.back_bitmap.data);
+ rb->lcd_bmp_part(&osd.lcd_bitmap, osd.vp.x, osd.vp.y,
+ 0, 0, osd.vp.width, osd.vp.height);
+ /* Assume it was on default framebuffer for now */
+ rb->lcd_set_framebuffer(NULL);
+}
+
+/* Erase the OSD to restore the framebuffer */
+static void osd_lcd_erase(void)
+{
+ rb->lcd_bmp_part(&osd.back_bitmap, 0, 0, osd.vp.x, osd.vp.y,
+ osd.vp.width, osd.vp.height);
+}
+
+/* Draw the OSD image portion using the callback */
+static void osd_lcd_draw_rect(int x, int y, int width, int height)
+{
+ rb->lcd_set_viewport(&osd.vp);
+ osd.draw_cb(x, y, width, height);
+ rb->lcd_set_viewport(NULL);
+}
+
+/* Draw the OSD image using the callback */
+static void osd_lcd_draw(void)
+{
+ osd_lcd_draw_rect(0, 0, osd.vp.width, osd.vp.height);
+}
+
+
+/** Public APIs **/
+
+/* Initialized the OSD and set its backbuffer */
+bool osd_init(void *backbuf, size_t backbuf_size,
+ osd_draw_cb_fn_t draw_cb)
+{
+ osd_show(OSD_HIDE);
+
+ osd.status = OSD_DISABLED; /* Disabled unless all is okay */
+ osd_set_vp_pos(0, 0, 0, 0);
+ osd.maxwidth = osd.maxheight = 0;
+ osd.timeout = 0;
+
+ if (!draw_cb)
+ return false;
+
+ if (!backbuf)
+ return false;
+
+ ALIGN_BUFFER(backbuf, backbuf_size, FB_DATA_SZ);
+
+ if (!backbuf_size)
+ return false;
+
+ rb->viewport_set_fullscreen(&osd.vp, SCREEN_MAIN);
+
+ fb_data *backfb = buf_to_fb_bitmap(backbuf, backbuf_size,
+ &osd.maxwidth, &osd.maxheight);
+
+ if (!backfb)
+ return false;
+
+ osd.draw_cb = draw_cb;
+
+ /* LCD framebuffer bitmap */
+ osd.lcd_bitmap.width = LCD_BYTES2WIDTH(LCD_WIDTH2BYTES(LCD_WIDTH));
+ osd.lcd_bitmap.height = LCD_BYTES2HEIGHT(LCD_HEIGHT2BYTES(LCD_HEIGHT));
+#if LCD_DEPTH > 1
+ osd.lcd_bitmap.format = FORMAT_NATIVE;
+ osd.lcd_bitmap.maskdata = NULL;
+#endif
+#ifdef HAVE_LCD_COLOR
+ osd.lcd_bitmap.alpha_offset = 0;
+#endif
+ osd.lcd_bitmap.data = (void *)rb->lcd_framebuffer;
+
+ /* Backbuffer bitmap */
+ osd.back_bitmap.width = osd.maxwidth;
+ osd.back_bitmap.height = osd.maxheight;
+#if LCD_DEPTH > 1
+ osd.back_bitmap.format = FORMAT_NATIVE;
+ osd.back_bitmap.maskdata = NULL;
+#endif
+#ifdef HAVE_LCD_COLOR
+ osd.back_bitmap.alpha_offset = 0;
+#endif
+ osd.back_bitmap.data = (void *)backfb;
+
+ DEBUGF("FB:%p BB:%p\n", osd.lcd_bitmap.data, osd.back_bitmap.data);
+
+ /* Set the default position to the whole thing */
+ osd_set_vp_pos(0, 0, osd.maxwidth, osd.maxheight);
+
+ osd.status = OSD_HIDDEN; /* Ready when you are */
+ return true;
+}
+
+/* Show/Hide the OSD on screen */
+bool osd_show(unsigned flags)
+{
+ if (flags & OSD_SHOW)
+ {
+ switch (osd.status)
+ {
+ case OSD_DISABLED:
+ break; /* No change */
+
+ case OSD_HIDDEN:
+ osd_lcd_update_back_buffer();
+ osd.status = OSD_VISIBLE;
+ osd_update();
+ osd.hide_tick = *rb->current_tick + osd.timeout;
+ break;
+
+ case OSD_VISIBLE:
+ if (flags & OSD_UPDATENOW)
+ osd_update();
+ /* Fall-through */
+ case OSD_ERASED:
+ osd.hide_tick = *rb->current_tick + osd.timeout;
+ return true;
+ }
+ }
+ else
+ {
+ switch (osd.status)
+ {
+ case OSD_DISABLED:
+ case OSD_HIDDEN:
+ break;
+
+ case OSD_VISIBLE:
+ osd_lcd_erase();
+ rb->lcd_update_rect(osd.vp.x, osd.vp.y, osd.vp.width,
+ osd.vp.height);
+ /* Fall-through */
+ case OSD_ERASED:
+ osd.status = OSD_HIDDEN;
+ return true;
+ }
+ }
+
+ return false;
+}
+
+/* Redraw the entire OSD */
+bool osd_update(void)
+{
+ if (osd.status != OSD_VISIBLE)
+ return false;
+
+ osd_lcd_draw();
+
+ rb->lcd_update_rect(osd.vp.x, osd.vp.y, osd.vp.width,
+ osd.vp.height);
+
+ return true;
+}
+
+/* Redraw part of the OSD (viewport-relative coordinates) */
+bool osd_update_rect(int x, int y, int width, int height)
+{
+ if (osd.status != OSD_VISIBLE)
+ return false;
+
+ osd_lcd_draw_rect(x, y, width, height);
+
+ if (x + width > osd.vp.width)
+ width = osd.vp.width - x;
+
+ if (x < 0)
+ {
+ width += x;
+ x = 0;
+ }
+
+ if (width <= 0)
+ return false;
+
+ if (y + height > osd.vp.height)
+ height = osd.vp.height - y;
+
+ if (y < 0)
+ {
+ height += y;
+ y = 0;
+ }
+
+ if (height <= 0)
+ return false;
+
+ rb->lcd_update_rect(osd.vp.x + x, osd.vp.y + y, width, height);
+
+ return true;
+}
+
+/* Set a new screen location and size (screen coordinates) */
+bool osd_update_pos(int x, int y, int width, int height)
+{
+ if (osd.status == OSD_DISABLED)
+ return false;
+
+ if (width < 0)
+ width = 0;
+ else if (width > osd.maxwidth)
+ width = osd.maxwidth;
+
+ if (height < 0)
+ height = 0;
+ else if (height > osd.maxheight)
+ height = osd.maxheight;
+
+ if (x == osd.vp.x && y == osd.vp.y &&
+ width == osd.vp.width && height == osd.vp.height)
+ return false; /* No change */
+
+ if (osd.status != OSD_VISIBLE)
+ {
+ /* Not visible - just update pos */
+ osd_set_vp_pos(x, y, width, height);
+ return false;
+ }
+
+ /* Visible area has changed */
+ osd_lcd_erase();
+
+ /* Update the smallest rectangle that encloses both the old and new
+ regions to make the change free of flicker (they may overlap) */
+ int xu = MIN(osd.vp.x, x);
+ int yu = MIN(osd.vp.y, y);
+ int wu = MAX(osd.vp.x + osd.vp.width, x + width) - xu;
+ int hu = MAX(osd.vp.y + osd.vp.height, y + height) - yu;
+
+ osd_set_vp_pos(x, y, width, height);
+ osd_lcd_update_back_buffer();
+ osd_lcd_draw();
+
+ rb->lcd_update_rect(xu, yu, wu, hu);
+ return true;
+}
+
+/* Call periodically to have the OSD timeout and hide itself */
+void osd_monitor_timeout(void)
+{
+ if (osd.status <= OSD_HIDDEN)
+ return; /* Already hidden/disabled */
+
+ if (osd.timeout > 0 && TIME_AFTER(*rb->current_tick, osd.hide_tick))
+ osd_show(OSD_HIDE);
+}
+
+/* Set the OSD timeout value. <= 0 = never timeout */
+void osd_set_timeout(long timeout)
+{
+ if (osd.status == OSD_DISABLED)
+ return;
+
+ osd.timeout = timeout;
+ osd_monitor_timeout();
+}
+
+/* Use the OSD viewport context */
+struct viewport * osd_get_viewport(void)
+{
+ return &osd.vp;
+}
+
+/* Get the maximum dimensions calculated by osd_init() */
+void osd_get_max_dims(int *maxwidth, int *maxheight)
+{
+ if (maxwidth)
+ *maxwidth = osd.maxwidth;
+
+ if (maxheight)
+ *maxheight = osd.maxheight;
+}
+
+/* Is the OSD enabled? */
+bool osd_enabled(void)
+{
+ return osd.status != OSD_DISABLED;
+}
+
+
+/** LCD update substitutes **/
+
+/* Prepare LCD framebuffer for regular drawing */
+void osd_lcd_update_prepare(void)
+{
+ if (osd.status == OSD_VISIBLE)
+ {
+ osd.status = OSD_ERASED;
+ osd_lcd_erase();
+ }
+}
+
+/* Update the whole screen */
+void osd_lcd_update(void)
+{
+ if (osd.status == OSD_ERASED)
+ {
+ /* Save the screen image underneath and restore the OSD image */
+ osd.status = OSD_VISIBLE;
+ osd_lcd_update_back_buffer();
+ osd_lcd_draw();
+ }
+
+ rb->lcd_update();
+}
+
+/* Update a part of the screen */
+void osd_lcd_update_rect(int x, int y, int width, int height)
+{
+ if (osd.status == OSD_ERASED)
+ {
+ /* Save the screen image underneath and restore the OSD image */
+ osd.status = OSD_VISIBLE;
+ osd_lcd_update_back_buffer();
+ osd_lcd_draw();
+ }
+
+ rb->lcd_update_rect(x, y, width, height);
+}
diff --git a/apps/plugins/lib/osd.h b/apps/plugins/lib/osd.h
new file mode 100644
index 0000000000..89441ae273
--- /dev/null
+++ b/apps/plugins/lib/osd.h
@@ -0,0 +1,94 @@
+/***************************************************************************
+* __________ __ ___.
+* Open \______ \ ____ ____ | | _\_ |__ _______ ___
+* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+* \/ \/ \/ \/ \/
+* $Id$
+*
+* Floating on-screen display
+*
+* Copyright (C) 2012 Michael Sevakis
+*
+* 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.
+*
+****************************************************************************/
+#ifndef OSD_H
+#define OSD_H
+
+/* At this time: assumes use of the default viewport for normal drawing */
+
+/* Callback implemented by user. Paramters are OSD vp-relative coordinates */
+typedef void (* osd_draw_cb_fn_t)(int x, int y, int width, int height);
+
+/* Initialize the OSD, set its backbuffer, update callback and enable it if
+ * the call succeeded. */
+bool osd_init(void *backbuf, size_t backbuf_size,
+ osd_draw_cb_fn_t draw_cb);
+
+enum
+{
+ OSD_HIDE = 0x0, /* Hide from view */
+ OSD_SHOW = 0x1, /* Bring into view, updating if needed */
+ OSD_UPDATENOW = 0x2, /* Force update even if nothing changed */
+};
+
+/* Show/Hide the OSD on screen returning previous status */
+bool osd_show(unsigned flags);
+
+/* Redraw the entire OSD returning true if screen update occurred */
+bool osd_update(void);
+
+/* Redraw part of the OSD (OSD viewport-relative coordinates) returning true
+ if any screen update occurred */
+bool osd_update_rect(int x, int y, int width, int height);
+
+/* Set a new screen location and size (screen coordinates) */
+bool osd_update_pos(int x, int y, int width, int height);
+
+/* Call periodically to have the OSD timeout and hide itself */
+void osd_monitor_timeout(void);
+
+/* Set the OSD timeout value. 'timeout' <= 0 == never timeout */
+void osd_set_timeout(long timeout);
+
+/* Use the OSD viewport context */
+struct viewport * osd_get_viewport(void);
+
+/* Get the maximum buffer dimensions calculated by osd_init() */
+void osd_get_max_dims(int *maxwidth, int *maxheight);
+
+/* Is the OSD enabled? */
+bool osd_enabled(void);
+
+/** Functions that must be used in lieu of regular LCD functions for this
+ ** to work.
+ **
+ ** To be efficient, as much drawing as possible should be combined between
+ ** *prepare and *update.
+ **
+ ** osd_lcd_update_prepare();
+ ** <draw stuff using lcd_* routines>
+ ** osd_lcd_update[_rect]();
+ **
+ ** TODO: Make it work seamlessly with greylib and regular LCD functions.
+ **/
+
+/* Prepare LCD frambuffer for regular drawing - call before any other LCD
+ function */
+void osd_lcd_update_prepare(void);
+
+/* Update the whole screen and restore OSD if it is visible */
+void osd_lcd_update(void);
+
+/* Update a part of the screen and restore OSD if it is visible */
+void osd_lcd_update_rect(int x, int y, int width, int height);
+
+#endif /* OSD_H */
diff --git a/apps/plugins/oscilloscope.c b/apps/plugins/oscilloscope.c
index af2c821037..66ebb43774 100644
--- a/apps/plugins/oscilloscope.c
+++ b/apps/plugins/oscilloscope.c
@@ -27,353 +27,435 @@
#include "lib/xlcd.h"
#include "lib/configfile.h"
-
-
+#include "fixedpoint.h"
+#include "lib/fixedpoint.h"
+#include "lib/osd.h"
/* variable button definitions */
#if CONFIG_KEYPAD == RECORDER_PAD
-#define OSCILLOSCOPE_QUIT BUTTON_OFF
-#define OSCILLOSCOPE_DRAWMODE BUTTON_F1
-#define OSCILLOSCOPE_ADVMODE BUTTON_F2
-#define OSCILLOSCOPE_ORIENTATION BUTTON_F3
-#define OSCILLOSCOPE_PAUSE BUTTON_PLAY
-#define OSCILLOSCOPE_SPEED_UP BUTTON_RIGHT
-#define OSCILLOSCOPE_SPEED_DOWN BUTTON_LEFT
-#define OSCILLOSCOPE_VOL_UP BUTTON_UP
-#define OSCILLOSCOPE_VOL_DOWN BUTTON_DOWN
+#define OSCILLOSCOPE_QUIT BUTTON_OFF
+#define OSCILLOSCOPE_DRAWMODE BUTTON_F1
+#define OSCILLOSCOPE_ADVMODE BUTTON_F2
+#define OSCILLOSCOPE_ORIENTATION BUTTON_F3
+#define OSCILLOSCOPE_PAUSE BUTTON_PLAY
+#define OSCILLOSCOPE_SPEED_UP BUTTON_RIGHT
+#define OSCILLOSCOPE_SPEED_DOWN BUTTON_LEFT
+#define OSCILLOSCOPE_VOL_UP BUTTON_UP
+#define OSCILLOSCOPE_VOL_DOWN BUTTON_DOWN
#elif CONFIG_KEYPAD == ARCHOS_AV300_PAD
-#define OSCILLOSCOPE_QUIT BUTTON_OFF
-#define OSCILLOSCOPE_DRAWMODE BUTTON_F1
-#define OSCILLOSCOPE_ADVMODE BUTTON_F2
-#define OSCILLOSCOPE_ORIENTATION BUTTON_F3
-#define OSCILLOSCOPE_PAUSE BUTTON_SELECT
-#define OSCILLOSCOPE_SPEED_UP BUTTON_RIGHT
-#define OSCILLOSCOPE_SPEED_DOWN BUTTON_LEFT
-#define OSCILLOSCOPE_VOL_UP BUTTON_UP
-#define OSCILLOSCOPE_VOL_DOWN BUTTON_DOWN
+#define OSCILLOSCOPE_QUIT BUTTON_OFF
+#define OSCILLOSCOPE_DRAWMODE BUTTON_F1
+#define OSCILLOSCOPE_ADVMODE BUTTON_F2
+#define OSCILLOSCOPE_ORIENTATION BUTTON_F3
+#define OSCILLOSCOPE_PAUSE BUTTON_SELECT
+#define OSCILLOSCOPE_SPEED_UP BUTTON_RIGHT
+#define OSCILLOSCOPE_SPEED_DOWN BUTTON_LEFT
+#define OSCILLOSCOPE_VOL_UP BUTTON_UP
+#define OSCILLOSCOPE_VOL_DOWN BUTTON_DOWN
#elif CONFIG_KEYPAD == ONDIO_PAD
-#define OSCILLOSCOPE_QUIT BUTTON_OFF
-#define OSCILLOSCOPE_DRAWMODE_PRE BUTTON_MENU
-#define OSCILLOSCOPE_DRAWMODE (BUTTON_MENU | BUTTON_REL)
-#define OSCILLOSCOPE_ADVMODE (BUTTON_MENU | BUTTON_RIGHT)
-#define OSCILLOSCOPE_ORIENTATION (BUTTON_MENU | BUTTON_LEFT)
-#define OSCILLOSCOPE_PAUSE (BUTTON_MENU | BUTTON_OFF)
-#define OSCILLOSCOPE_SPEED_UP BUTTON_RIGHT
-#define OSCILLOSCOPE_SPEED_DOWN BUTTON_LEFT
-#define OSCILLOSCOPE_VOL_UP BUTTON_UP
-#define OSCILLOSCOPE_VOL_DOWN BUTTON_DOWN
+#define OSCILLOSCOPE_QUIT BUTTON_OFF
+#define OSCILLOSCOPE_DRAWMODE_PRE BUTTON_MENU
+#define OSCILLOSCOPE_DRAWMODE (BUTTON_MENU | BUTTON_REL)
+#define OSCILLOSCOPE_ADVMODE (BUTTON_MENU | BUTTON_RIGHT)
+#define OSCILLOSCOPE_ORIENTATION (BUTTON_MENU | BUTTON_LEFT)
+#define OSCILLOSCOPE_PAUSE (BUTTON_MENU | BUTTON_OFF)
+#define OSCILLOSCOPE_SPEED_UP BUTTON_RIGHT
+#define OSCILLOSCOPE_SPEED_DOWN BUTTON_LEFT
+#define OSCILLOSCOPE_VOL_UP BUTTON_UP
+#define OSCILLOSCOPE_VOL_DOWN BUTTON_DOWN
+#define NEED_LASTBUTTON
#elif (CONFIG_KEYPAD == IRIVER_H100_PAD) || (CONFIG_KEYPAD == IRIVER_H300_PAD)
-#define OSCILLOSCOPE_QUIT BUTTON_OFF
-#define OSCILLOSCOPE_DRAWMODE BUTTON_SELECT
-#define OSCILLOSCOPE_ADVMODE BUTTON_MODE
-#define OSCILLOSCOPE_ORIENTATION BUTTON_REC
-#define OSCILLOSCOPE_PAUSE BUTTON_ON
-#define OSCILLOSCOPE_SPEED_UP BUTTON_RIGHT
-#define OSCILLOSCOPE_SPEED_DOWN BUTTON_LEFT
-#define OSCILLOSCOPE_VOL_UP BUTTON_UP
-#define OSCILLOSCOPE_VOL_DOWN BUTTON_DOWN
-#define OSCILLOSCOPE_RC_QUIT BUTTON_RC_STOP
+#define OSCILLOSCOPE_QUIT BUTTON_OFF
+#define OSCILLOSCOPE_DRAWMODE BUTTON_SELECT
+#define OSCILLOSCOPE_ADVMODE BUTTON_MODE
+#define OSCILLOSCOPE_ORIENTATION_PRE BUTTON_REC
+#define OSCILLOSCOPE_ORIENTATION (BUTTON_REC | BUTTON_REL)
+#define OSCILLOSCOPE_GRAPHMODE_PRE BUTTON_REC
+#define OSCILLOSCOPE_GRAPHMODE (BUTTON_REC | BUTTON_REPEAT)
+#define OSCILLOSCOPE_PAUSE BUTTON_ON
+#define OSCILLOSCOPE_SPEED_UP BUTTON_RIGHT
+#define OSCILLOSCOPE_SPEED_DOWN BUTTON_LEFT
+#define OSCILLOSCOPE_VOL_UP BUTTON_UP
+#define OSCILLOSCOPE_VOL_DOWN BUTTON_DOWN
+#define OSCILLOSCOPE_RC_QUIT BUTTON_RC_STOP
+#define NEED_LASTBUTTON
#elif (CONFIG_KEYPAD == IPOD_4G_PAD) || (CONFIG_KEYPAD == IPOD_3G_PAD) || \
(CONFIG_KEYPAD == IPOD_1G2G_PAD)
-#define OSCILLOSCOPE_QUIT (BUTTON_SELECT | BUTTON_MENU)
-#define OSCILLOSCOPE_DRAWMODE (BUTTON_SELECT | BUTTON_PLAY)
-#define OSCILLOSCOPE_ADVMODE (BUTTON_SELECT | BUTTON_RIGHT)
-#define OSCILLOSCOPE_ORIENTATION (BUTTON_SELECT | BUTTON_LEFT)
-#define OSCILLOSCOPE_PAUSE BUTTON_PLAY
-#define OSCILLOSCOPE_SPEED_UP BUTTON_RIGHT
-#define OSCILLOSCOPE_SPEED_DOWN BUTTON_LEFT
-#define OSCILLOSCOPE_VOL_UP BUTTON_SCROLL_FWD
-#define OSCILLOSCOPE_VOL_DOWN BUTTON_SCROLL_BACK
+#define OSCILLOSCOPE_QUIT (BUTTON_SELECT | BUTTON_MENU)
+#define OSCILLOSCOPE_DRAWMODE (BUTTON_SELECT | BUTTON_PLAY)
+#define OSCILLOSCOPE_ADVMODE (BUTTON_SELECT | BUTTON_RIGHT)
+#define OSCILLOSCOPE_ORIENTATION (BUTTON_SELECT | BUTTON_LEFT)
+#define OSCILLOSCOPE_GRAPHMODE BUTTON_MENU
+#define OSCILLOSCOPE_PAUSE BUTTON_PLAY
+#define OSCILLOSCOPE_SPEED_UP BUTTON_RIGHT
+#define OSCILLOSCOPE_SPEED_DOWN BUTTON_LEFT
+#define OSCILLOSCOPE_VOL_UP BUTTON_SCROLL_FWD
+#define OSCILLOSCOPE_VOL_DOWN BUTTON_SCROLL_BACK
+/* Need GRAPHMODE */
#elif (CONFIG_KEYPAD == GIGABEAT_PAD)
-#define OSCILLOSCOPE_QUIT BUTTON_POWER
-#define OSCILLOSCOPE_DRAWMODE BUTTON_SELECT
-#define OSCILLOSCOPE_ADVMODE BUTTON_DOWN
-#define OSCILLOSCOPE_ORIENTATION BUTTON_UP
-#define OSCILLOSCOPE_PAUSE BUTTON_A
-#define OSCILLOSCOPE_SPEED_UP BUTTON_RIGHT
-#define OSCILLOSCOPE_SPEED_DOWN BUTTON_LEFT
-#define OSCILLOSCOPE_VOL_UP BUTTON_VOL_UP
-#define OSCILLOSCOPE_VOL_DOWN BUTTON_VOL_DOWN
+#define OSCILLOSCOPE_QUIT BUTTON_POWER
+#define OSCILLOSCOPE_DRAWMODE BUTTON_SELECT
+#define OSCILLOSCOPE_ADVMODE BUTTON_DOWN
+#define OSCILLOSCOPE_ORIENTATION BUTTON_UP
+#define OSCILLOSCOPE_GRAPHMODE BUTTON_MENU
+#define OSCILLOSCOPE_PAUSE BUTTON_A
+#define OSCILLOSCOPE_SPEED_UP BUTTON_RIGHT
+#define OSCILLOSCOPE_SPEED_DOWN BUTTON_LEFT
+#define OSCILLOSCOPE_VOL_UP BUTTON_VOL_UP
+#define OSCILLOSCOPE_VOL_DOWN BUTTON_VOL_DOWN
#elif (CONFIG_KEYPAD == SANSA_E200_PAD)
-#define OSCILLOSCOPE_QUIT BUTTON_POWER
-#define OSCILLOSCOPE_DRAWMODE BUTTON_SELECT
-#define OSCILLOSCOPE_ADVMODE BUTTON_DOWN
-#define OSCILLOSCOPE_ORIENTATION BUTTON_UP
-#define OSCILLOSCOPE_PAUSE BUTTON_REC
-#define OSCILLOSCOPE_SPEED_UP BUTTON_RIGHT
-#define OSCILLOSCOPE_SPEED_DOWN BUTTON_LEFT
-#define OSCILLOSCOPE_VOL_UP BUTTON_SCROLL_FWD
-#define OSCILLOSCOPE_VOL_DOWN BUTTON_SCROLL_BACK
+#define OSCILLOSCOPE_QUIT BUTTON_POWER
+#define OSCILLOSCOPE_DRAWMODE_PRE BUTTON_SELECT
+#define OSCILLOSCOPE_DRAWMODE (BUTTON_SELECT | BUTTON_REL)
+#define OSCILLOSCOPE_ORIENTATION_PRE BUTTON_SELECT
+#define OSCILLOSCOPE_ORIENTATION (BUTTON_SELECT | BUTTON_REPEAT)
+#define OSCILLOSCOPE_ADVMODE BUTTON_DOWN
+#define OSCILLOSCOPE_PAUSE_PRE BUTTON_UP
+#define OSCILLOSCOPE_PAUSE (BUTTON_UP | BUTTON_REL)
+#define OSCILLOSCOPE_GRAPHMODE_PRE BUTTON_UP
+#define OSCILLOSCOPE_GRAPHMODE (BUTTON_UP | BUTTON_REPEAT)
+#define OSCILLOSCOPE_SPEED_UP BUTTON_RIGHT
+#define OSCILLOSCOPE_SPEED_DOWN BUTTON_LEFT
+#define OSCILLOSCOPE_VOL_UP BUTTON_SCROLL_FWD
+#define OSCILLOSCOPE_VOL_DOWN BUTTON_SCROLL_BACK
+#define NEED_LASTBUTTON
#elif (CONFIG_KEYPAD == SANSA_FUZE_PAD)
-#define OSCILLOSCOPE_QUIT (BUTTON_HOME|BUTTON_REPEAT)
-#define OSCILLOSCOPE_DRAWMODE (BUTTON_SELECT | BUTTON_REL)
-#define OSCILLOSCOPE_ADVMODE (BUTTON_SELECT | BUTTON_RIGHT)
-#define OSCILLOSCOPE_ORIENTATION (BUTTON_SELECT | BUTTON_LEFT)
-#define OSCILLOSCOPE_PAUSE BUTTON_UP
-#define OSCILLOSCOPE_SPEED_UP BUTTON_RIGHT
-#define OSCILLOSCOPE_SPEED_DOWN BUTTON_LEFT
-#define OSCILLOSCOPE_VOL_UP BUTTON_SCROLL_FWD
-#define OSCILLOSCOPE_VOL_DOWN BUTTON_SCROLL_BACK
+#define OSCILLOSCOPE_QUIT (BUTTON_HOME|BUTTON_REPEAT)
+#define OSCILLOSCOPE_DRAWMODE_PRE BUTTON_SELECT
+#define OSCILLOSCOPE_DRAWMODE (BUTTON_SELECT | BUTTON_REL)
+#define OSCILLOSCOPE_ORIENTATION_PRE BUTTON_SELECT
+#define OSCILLOSCOPE_ORIENTATION (BUTTON_SELECT | BUTTON_REPEAT)
+#define OSCILLOSCOPE_ADVMODE BUTTON_DOWN
+#define OSCILLOSCOPE_PAUSE_PRE BUTTON_UP
+#define OSCILLOSCOPE_PAUSE (BUTTON_UP | BUTTON_REL)
+#define OSCILLOSCOPE_GRAPHMODE_PRE BUTTON_UP
+#define OSCILLOSCOPE_GRAPHMODE (BUTTON_UP | BUTTON_REPEAT)
+#define OSCILLOSCOPE_SPEED_UP BUTTON_RIGHT
+#define OSCILLOSCOPE_SPEED_DOWN BUTTON_LEFT
+#define OSCILLOSCOPE_VOL_UP BUTTON_SCROLL_FWD
+#define OSCILLOSCOPE_VOL_DOWN BUTTON_SCROLL_BACK
+#define NEED_LASTBUTTON
#elif (CONFIG_KEYPAD == SANSA_C200_PAD)
-#define OSCILLOSCOPE_QUIT BUTTON_POWER
-#define OSCILLOSCOPE_DRAWMODE BUTTON_SELECT
-#define OSCILLOSCOPE_ADVMODE BUTTON_DOWN
-#define OSCILLOSCOPE_ORIENTATION BUTTON_UP
-#define OSCILLOSCOPE_PAUSE BUTTON_REC
-#define OSCILLOSCOPE_SPEED_UP BUTTON_RIGHT
-#define OSCILLOSCOPE_SPEED_DOWN BUTTON_LEFT
-#define OSCILLOSCOPE_VOL_UP BUTTON_VOL_UP
-#define OSCILLOSCOPE_VOL_DOWN BUTTON_VOL_DOWN
+#define OSCILLOSCOPE_QUIT BUTTON_POWER
+#define OSCILLOSCOPE_DRAWMODE BUTTON_SELECT
+#define OSCILLOSCOPE_ADVMODE BUTTON_DOWN
+#define OSCILLOSCOPE_ORIENTATION BUTTON_UP
+#define OSCILLOSCOPE_PAUSE BUTTON_REC
+#define OSCILLOSCOPE_SPEED_UP BUTTON_RIGHT
+#define OSCILLOSCOPE_SPEED_DOWN BUTTON_LEFT
+#define OSCILLOSCOPE_VOL_UP BUTTON_VOL_UP
+#define OSCILLOSCOPE_VOL_DOWN BUTTON_VOL_DOWN
+/* v1 - Need GRAPHMODE */
+/* v2 - Not enough plugin RAM for waveform view */
#elif (CONFIG_KEYPAD == SANSA_CLIP_PAD)
-#define OSCILLOSCOPE_QUIT BUTTON_POWER
-#define OSCILLOSCOPE_DRAWMODE BUTTON_SELECT
-#define OSCILLOSCOPE_ADVMODE BUTTON_DOWN
-#define OSCILLOSCOPE_ORIENTATION BUTTON_UP
-#define OSCILLOSCOPE_PAUSE BUTTON_HOME
-#define OSCILLOSCOPE_SPEED_UP BUTTON_RIGHT
-#define OSCILLOSCOPE_SPEED_DOWN BUTTON_LEFT
-#define OSCILLOSCOPE_VOL_UP BUTTON_VOL_UP
-#define OSCILLOSCOPE_VOL_DOWN BUTTON_VOL_DOWN
+#define OSCILLOSCOPE_QUIT BUTTON_POWER
+#define OSCILLOSCOPE_DRAWMODE_PRE BUTTON_SELECT
+#define OSCILLOSCOPE_DRAWMODE (BUTTON_SELECT | BUTTON_REL)
+#define OSCILLOSCOPE_ORIENTATION_PRE BUTTON_SELECT
+#define OSCILLOSCOPE_ORIENTATION (BUTTON_SELECT | BUTTON_REPEAT)
+#define OSCILLOSCOPE_ADVMODE BUTTON_DOWN
+#define OSCILLOSCOPE_PAUSE BUTTON_UP
+#define OSCILLOSCOPE_SPEED_UP BUTTON_RIGHT
+#define OSCILLOSCOPE_SPEED_DOWN BUTTON_LEFT
+#define OSCILLOSCOPE_VOL_UP BUTTON_VOL_UP
+#define OSCILLOSCOPE_VOL_DOWN BUTTON_VOL_DOWN
+#define NEED_LASTBUTTON
+/* Not enough plugin RAM for waveform view */
#elif (CONFIG_KEYPAD == SANSA_M200_PAD)
-#define OSCILLOSCOPE_QUIT BUTTON_POWER
-#define OSCILLOSCOPE_DRAWMODE (BUTTON_SELECT | BUTTON_REL)
-#define OSCILLOSCOPE_ADVMODE BUTTON_DOWN
-#define OSCILLOSCOPE_ORIENTATION BUTTON_UP
-#define OSCILLOSCOPE_PAUSE (BUTTON_SELECT | BUTTON_UP)
-#define OSCILLOSCOPE_SPEED_UP BUTTON_RIGHT
-#define OSCILLOSCOPE_SPEED_DOWN BUTTON_LEFT
-#define OSCILLOSCOPE_VOL_UP BUTTON_VOL_UP
-#define OSCILLOSCOPE_VOL_DOWN BUTTON_VOL_DOWN
+#define OSCILLOSCOPE_QUIT BUTTON_POWER
+#define OSCILLOSCOPE_DRAWMODE (BUTTON_SELECT | BUTTON_REL)
+#define OSCILLOSCOPE_ADVMODE BUTTON_DOWN
+#define OSCILLOSCOPE_ORIENTATION_PRE BUTTON_UP
+#define OSCILLOSCOPE_ORIENTATION (BUTTON_UP | BUTTON_REL)
+#define OSCILLOSCOPE_GRAPHMODE_PRE BUTTON_UP
+#define OSCILLOSCOPE_GRAPHMODE (BUTTON_UP | BUTTON_REPEAT)
+#define OSCILLOSCOPE_PAUSE (BUTTON_SELECT | BUTTON_UP)
+#define OSCILLOSCOPE_SPEED_UP BUTTON_RIGHT
+#define OSCILLOSCOPE_SPEED_DOWN BUTTON_LEFT
+#define OSCILLOSCOPE_VOL_UP BUTTON_VOL_UP
+#define OSCILLOSCOPE_VOL_DOWN BUTTON_VOL_DOWN
+#define NEED_LASTBUTTON
#elif CONFIG_KEYPAD == IAUDIO_X5M5_PAD
-#define OSCILLOSCOPE_QUIT BUTTON_POWER
-#define OSCILLOSCOPE_DRAWMODE_PRE BUTTON_SELECT
-#define OSCILLOSCOPE_DRAWMODE (BUTTON_SELECT | BUTTON_REL)
-#define OSCILLOSCOPE_ADVMODE BUTTON_REC
+#define OSCILLOSCOPE_QUIT BUTTON_POWER
+#define OSCILLOSCOPE_DRAWMODE_PRE BUTTON_SELECT
+#define OSCILLOSCOPE_DRAWMODE (BUTTON_SELECT | BUTTON_REL)
+#define OSCILLOSCOPE_ADVMODE BUTTON_REC
#define OSCILLOSCOPE_ORIENTATION_PRE BUTTON_SELECT
-#define OSCILLOSCOPE_ORIENTATION (BUTTON_SELECT | BUTTON_REPEAT)
-#define OSCILLOSCOPE_PAUSE BUTTON_PLAY
-#define OSCILLOSCOPE_SPEED_UP BUTTON_RIGHT
-#define OSCILLOSCOPE_SPEED_DOWN BUTTON_LEFT
-#define OSCILLOSCOPE_VOL_UP BUTTON_UP
-#define OSCILLOSCOPE_VOL_DOWN BUTTON_DOWN
+#define OSCILLOSCOPE_ORIENTATION (BUTTON_SELECT | BUTTON_REPEAT)
+#define OSCILLOSCOPE_GRAPHMODE_PRE BUTTON_PLAY
+#define OSCILLOSCOPE_GRAPHMODE (BUTTON_PLAY | BUTTON_REPEAT)
+#define OSCILLOSCOPE_PAUSE_PRE BUTTON_PLAY
+#define OSCILLOSCOPE_PAUSE (BUTTON_PLAY | BUTTON_REL)
+#define OSCILLOSCOPE_SPEED_UP BUTTON_RIGHT
+#define OSCILLOSCOPE_SPEED_DOWN BUTTON_LEFT
+#define OSCILLOSCOPE_VOL_UP BUTTON_UP
+#define OSCILLOSCOPE_VOL_DOWN BUTTON_DOWN
+#define NEED_LASTBUTTON
#elif CONFIG_KEYPAD == IRIVER_H10_PAD
-#define OSCILLOSCOPE_QUIT BUTTON_POWER
-#define OSCILLOSCOPE_DRAWMODE_PRE BUTTON_REW
-#define OSCILLOSCOPE_DRAWMODE (BUTTON_REW | BUTTON_REL)
-#define OSCILLOSCOPE_ADVMODE BUTTON_FF
+#define OSCILLOSCOPE_QUIT BUTTON_POWER
+#define OSCILLOSCOPE_DRAWMODE_PRE BUTTON_REW
+#define OSCILLOSCOPE_DRAWMODE (BUTTON_REW | BUTTON_REL)
+#define OSCILLOSCOPE_ADVMODE BUTTON_FF
#define OSCILLOSCOPE_ORIENTATION_PRE BUTTON_REW
-#define OSCILLOSCOPE_ORIENTATION (BUTTON_REW | BUTTON_REPEAT)
-#define OSCILLOSCOPE_PAUSE BUTTON_PLAY
-#define OSCILLOSCOPE_SPEED_UP BUTTON_RIGHT
-#define OSCILLOSCOPE_SPEED_DOWN BUTTON_LEFT
-#define OSCILLOSCOPE_VOL_UP BUTTON_SCROLL_UP
-#define OSCILLOSCOPE_VOL_DOWN BUTTON_SCROLL_DOWN
+#define OSCILLOSCOPE_ORIENTATION (BUTTON_REW | BUTTON_REPEAT)
+#define OSCILLOSCOPE_GRAPHMODE_PRE BUTTON_PLAY
+#define OSCILLOSCOPE_GRAPHMODE (BUTTON_PLAY | BUTTON_REPEAT)
+#define OSCILLOSCOPE_PAUSE_PRE BUTTON_PLAY
+#define OSCILLOSCOPE_PAUSE (BUTTON_PLAY | BUTTON_REL)
+#define OSCILLOSCOPE_SPEED_UP BUTTON_RIGHT
+#define OSCILLOSCOPE_SPEED_DOWN BUTTON_LEFT
+#define OSCILLOSCOPE_VOL_UP BUTTON_SCROLL_UP
+#define OSCILLOSCOPE_VOL_DOWN BUTTON_SCROLL_DOWN
+#define NEED_LASTBUTTON
#elif CONFIG_KEYPAD == GIGABEAT_S_PAD
-#define OSCILLOSCOPE_QUIT BUTTON_BACK
-#define OSCILLOSCOPE_DRAWMODE BUTTON_PREV
-#define OSCILLOSCOPE_ADVMODE BUTTON_NEXT
-#define OSCILLOSCOPE_ORIENTATION BUTTON_MENU
-#define OSCILLOSCOPE_PAUSE BUTTON_PLAY
-#define OSCILLOSCOPE_SPEED_UP BUTTON_RIGHT
-#define OSCILLOSCOPE_SPEED_DOWN BUTTON_LEFT
-#define OSCILLOSCOPE_VOL_UP BUTTON_VOL_UP
-#define OSCILLOSCOPE_VOL_DOWN BUTTON_VOL_DOWN
+#define OSCILLOSCOPE_QUIT BUTTON_BACK
+#define OSCILLOSCOPE_DRAWMODE BUTTON_SELECT
+#define OSCILLOSCOPE_ADVMODE BUTTON_DOWN
+#define OSCILLOSCOPE_ORIENTATION BUTTON_UP
+#define OSCILLOSCOPE_GRAPHMODE BUTTON_MENU
+#define OSCILLOSCOPE_PAUSE BUTTON_PLAY
+#define OSCILLOSCOPE_SPEED_UP BUTTON_RIGHT
+#define OSCILLOSCOPE_SPEED_DOWN BUTTON_LEFT
+#define OSCILLOSCOPE_VOL_UP BUTTON_VOL_UP
+#define OSCILLOSCOPE_VOL_DOWN BUTTON_VOL_DOWN
#elif (CONFIG_KEYPAD == MROBE100_PAD)
-#define OSCILLOSCOPE_QUIT BUTTON_POWER
-#define OSCILLOSCOPE_DRAWMODE BUTTON_SELECT
-#define OSCILLOSCOPE_ADVMODE BUTTON_MENU
-#define OSCILLOSCOPE_ORIENTATION BUTTON_PLAY
-#define OSCILLOSCOPE_PAUSE BUTTON_DISPLAY
-#define OSCILLOSCOPE_SPEED_UP BUTTON_RIGHT
-#define OSCILLOSCOPE_SPEED_DOWN BUTTON_LEFT
-#define OSCILLOSCOPE_VOL_UP BUTTON_UP
-#define OSCILLOSCOPE_VOL_DOWN BUTTON_DOWN
+#define OSCILLOSCOPE_QUIT BUTTON_POWER
+#define OSCILLOSCOPE_DRAWMODE BUTTON_SELECT
+#define OSCILLOSCOPE_ADVMODE BUTTON_MENU
+#define OSCILLOSCOPE_ORIENTATION_PRE BUTTON_PLAY
+#define OSCILLOSCOPE_ORIENTATION (BUTTON_PLAY | BUTTON_REL)
+#define OSCILLOSCOPE_GRAPHMODE_PRE BUTTON_PLAY
+#define OSCILLOSCOPE_GRAPHMODE (BUTTON_PLAY | BUTTON_REPEAT)
+#define OSCILLOSCOPE_PAUSE BUTTON_DISPLAY
+#define OSCILLOSCOPE_SPEED_UP BUTTON_RIGHT
+#define OSCILLOSCOPE_SPEED_DOWN BUTTON_LEFT
+#define OSCILLOSCOPE_VOL_UP BUTTON_UP
+#define OSCILLOSCOPE_VOL_DOWN BUTTON_DOWN
+#define NEED_LASTBUTTON
#elif CONFIG_KEYPAD == IAUDIO_M3_PAD
-#define OSCILLOSCOPE_QUIT BUTTON_RC_REC
-#define OSCILLOSCOPE_DRAWMODE_PRE BUTTON_RC_MODE
-#define OSCILLOSCOPE_DRAWMODE (BUTTON_RC_MODE|BUTTON_REL)
-#define OSCILLOSCOPE_ADVMODE BUTTON_RC_MENU
+#define OSCILLOSCOPE_QUIT BUTTON_RC_REC
+#define OSCILLOSCOPE_DRAWMODE_PRE BUTTON_RC_MODE
+#define OSCILLOSCOPE_DRAWMODE (BUTTON_RC_MODE|BUTTON_REL)
+#define OSCILLOSCOPE_ADVMODE BUTTON_RC_MENU
#define OSCILLOSCOPE_ORIENTATION_PRE BUTTON_RC_MODE
-#define OSCILLOSCOPE_ORIENTATION (BUTTON_RC_MODE|BUTTON_REPEAT)
-#define OSCILLOSCOPE_PAUSE BUTTON_RC_PLAY
-#define OSCILLOSCOPE_SPEED_UP BUTTON_RC_FF
-#define OSCILLOSCOPE_SPEED_DOWN BUTTON_RC_REW
-#define OSCILLOSCOPE_VOL_UP BUTTON_RC_VOL_UP
-#define OSCILLOSCOPE_VOL_DOWN BUTTON_RC_VOL_DOWN
+#define OSCILLOSCOPE_ORIENTATION (BUTTON_RC_MODE|BUTTON_REPEAT)
+#define OSCILLOSCOPE_PAUSE_PRE BUTTON_RC_PLAY
+#define OSCILLOSCOPE_PAUSE (BUTTON_RC_PLAY|BUTTON_REL)
+#define OSCILLOSCOPE_GRAPHMODE_PRE BUTTON_RC_PLAY
+#define SCILLLOSCOPE_GRAPHMODE (BUTTON_RC_PLAY|BUTTON_REPEAT)
+#define OSCILLOSCOPE_SPEED_UP BUTTON_RC_FF
+#define OSCILLOSCOPE_SPEED_DOWN BUTTON_RC_REW
+#define OSCILLOSCOPE_VOL_UP BUTTON_RC_VOL_UP
+#define OSCILLOSCOPE_VOL_DOWN BUTTON_RC_VOL_DOWN
+#define NEED_LASTBUTTON
#elif CONFIG_KEYPAD == COWON_D2_PAD
-#define OSCILLOSCOPE_QUIT BUTTON_POWER
-#define OSCILLOSCOPE_VOL_UP BUTTON_PLUS
-#define OSCILLOSCOPE_VOL_DOWN BUTTON_MINUS
+#define OSCILLOSCOPE_QUIT BUTTON_POWER
+#define OSCILLOSCOPE_VOL_UP BUTTON_PLUS
+#define OSCILLOSCOPE_VOL_DOWN BUTTON_MINUS
#elif CONFIG_KEYPAD == CREATIVEZVM_PAD
-#define OSCILLOSCOPE_QUIT BUTTON_BACK
-#define OSCILLOSCOPE_DRAWMODE BUTTON_SELECT
-#define OSCILLOSCOPE_ADVMODE BUTTON_CUSTOM
-#define OSCILLOSCOPE_ORIENTATION BUTTON_MENU
-#define OSCILLOSCOPE_PAUSE BUTTON_PLAY
-#define OSCILLOSCOPE_SPEED_UP BUTTON_RIGHT
-#define OSCILLOSCOPE_SPEED_DOWN BUTTON_LEFT
-#define OSCILLOSCOPE_VOL_UP BUTTON_UP
-#define OSCILLOSCOPE_VOL_DOWN BUTTON_DOWN
+#define OSCILLOSCOPE_QUIT BUTTON_BACK
+#define OSCILLOSCOPE_DRAWMODE BUTTON_SELECT
+#define OSCILLOSCOPE_ADVMODE BUTTON_CUSTOM
+#define OSCILLOSCOPE_ORIENTATION_PRE BUTTON_MENU
+#define OSCILLOSCOPE_ORIENTATION (BUTTON_MENU | BUTTON_REL)
+#define OSCILLOSCOPE_GRAPHMODE_PRE BUTTON_MENU
+#define OSCILLOSCOPE_GRAPHMODE (BUTTON_MENU | BUTTON_REPEAT)
+#define OSCILLOSCOPE_PAUSE BUTTON_PLAY
+#define OSCILLOSCOPE_SPEED_UP BUTTON_RIGHT
+#define OSCILLOSCOPE_SPEED_DOWN BUTTON_LEFT
+#define OSCILLOSCOPE_VOL_UP BUTTON_UP
+#define OSCILLOSCOPE_VOL_DOWN BUTTON_DOWN
+#define NEED_LASTBUTTON
#elif CONFIG_KEYPAD == PHILIPS_HDD1630_PAD
-#define OSCILLOSCOPE_QUIT BUTTON_POWER
-#define OSCILLOSCOPE_DRAWMODE BUTTON_MENU
-#define OSCILLOSCOPE_ADVMODE BUTTON_VIEW
-#define OSCILLOSCOPE_ORIENTATION BUTTON_UP
-#define OSCILLOSCOPE_PAUSE BUTTON_SELECT
-#define OSCILLOSCOPE_SPEED_UP BUTTON_RIGHT
-#define OSCILLOSCOPE_SPEED_DOWN BUTTON_LEFT
-#define OSCILLOSCOPE_VOL_UP BUTTON_VOL_UP
-#define OSCILLOSCOPE_VOL_DOWN BUTTON_VOL_DOWN
+#define OSCILLOSCOPE_QUIT BUTTON_POWER
+#define OSCILLOSCOPE_DRAWMODE BUTTON_MENU
+#define OSCILLOSCOPE_ADVMODE BUTTON_VIEW
+#define OSCILLOSCOPE_ORIENTATION_PRE BUTTON_UP
+#define OSCILLOSCOPE_ORIENTATION (BUTTON_UP | BUTTON_REL)
+#define OSCILLOSCOPE_GRAPHMODE_PRE BUTTON_UP
+#define OSCILLOSCOPE_GRAPHMODE (BUTTON_UP | BUTTON_REPEAT)
+#define OSCILLOSCOPE_PAUSE BUTTON_SELECT
+#define OSCILLOSCOPE_SPEED_UP BUTTON_RIGHT
+#define OSCILLOSCOPE_SPEED_DOWN BUTTON_LEFT
+#define OSCILLOSCOPE_VOL_UP BUTTON_VOL_UP
+#define OSCILLOSCOPE_VOL_DOWN BUTTON_VOL_DOWN
+#define NEED_LASTBUTTON
#elif CONFIG_KEYPAD == PHILIPS_HDD6330_PAD
-#define OSCILLOSCOPE_QUIT BUTTON_POWER
-#define OSCILLOSCOPE_DRAWMODE BUTTON_MENU
-#define OSCILLOSCOPE_ADVMODE BUTTON_RIGHT
-#define OSCILLOSCOPE_ORIENTATION BUTTON_UP
-#define OSCILLOSCOPE_PAUSE BUTTON_PLAY
-#define OSCILLOSCOPE_SPEED_UP BUTTON_NEXT
-#define OSCILLOSCOPE_SPEED_DOWN BUTTON_PREV
-#define OSCILLOSCOPE_VOL_UP BUTTON_VOL_UP
-#define OSCILLOSCOPE_VOL_DOWN BUTTON_VOL_DOWN
+#define OSCILLOSCOPE_QUIT BUTTON_POWER
+#define OSCILLOSCOPE_DRAWMODE BUTTON_MENU
+#define OSCILLOSCOPE_ADVMODE BUTTON_RIGHT
+#define OSCILLOSCOPE_ORIENTATION_PRE BUTTON_UP
+#define OSCILLOSCOPE_ORIENTATION (BUTTON_UP | BUTTON_REL)
+#define OSCILLOSCOPE_GRAPHMODE_PRE BUTTON_UP
+#define OSCILLOSCOPE_GRAPHMODE (BUTTON_UP | BUTTON_REPEAT)
+#define OSCILLOSCOPE_PAUSE BUTTON_PLAY
+#define OSCILLOSCOPE_SPEED_UP BUTTON_NEXT
+#define OSCILLOSCOPE_SPEED_DOWN BUTTON_PREV
+#define OSCILLOSCOPE_VOL_UP BUTTON_VOL_UP
+#define OSCILLOSCOPE_VOL_DOWN BUTTON_VOL_DOWN
+#define NEED_LASTBUTTON
#elif CONFIG_KEYPAD == PHILIPS_SA9200_PAD
-#define OSCILLOSCOPE_QUIT BUTTON_POWER
-#define OSCILLOSCOPE_DRAWMODE BUTTON_MENU
-#define OSCILLOSCOPE_ADVMODE BUTTON_RIGHT
-#define OSCILLOSCOPE_ORIENTATION BUTTON_UP
-#define OSCILLOSCOPE_PAUSE BUTTON_PLAY
-#define OSCILLOSCOPE_SPEED_UP BUTTON_NEXT
-#define OSCILLOSCOPE_SPEED_DOWN BUTTON_PREV
-#define OSCILLOSCOPE_VOL_UP BUTTON_VOL_UP
-#define OSCILLOSCOPE_VOL_DOWN BUTTON_VOL_DOWN
+#define OSCILLOSCOPE_QUIT BUTTON_POWER
+#define OSCILLOSCOPE_DRAWMODE BUTTON_MENU
+#define OSCILLOSCOPE_ADVMODE BUTTON_RIGHT
+#define OSCILLOSCOPE_ORIENTATION_PRE BUTTON_UP
+#define OSCILLOSCOPE_ORIENTATION (BUTTON_UP | BUTTON_REL)
+#define OSCILLOSCOPE_GRAPHMODE_PRE BUTTON_UP
+#define OSCILLOSCOPE_GRAPHMODE (BUTTON_UP | BUTTON_REPEAT)
+#define OSCILLOSCOPE_PAUSE BUTTON_PLAY
+#define OSCILLOSCOPE_SPEED_UP BUTTON_NEXT
+#define OSCILLOSCOPE_SPEED_DOWN BUTTON_PREV
+#define OSCILLOSCOPE_VOL_UP BUTTON_VOL_UP
+#define OSCILLOSCOPE_VOL_DOWN BUTTON_VOL_DOWN
+#define NEED_LASTBUTTON
#elif CONFIG_KEYPAD == ONDAVX747_PAD
-#define OSCILLOSCOPE_QUIT BUTTON_POWER
-#define OSCILLOSCOPE_VOL_UP BUTTON_VOL_UP
-#define OSCILLOSCOPE_VOL_DOWN BUTTON_VOL_DOWN
+#define OSCILLOSCOPE_QUIT BUTTON_POWER
+#define OSCILLOSCOPE_VOL_UP BUTTON_VOL_UP
+#define OSCILLOSCOPE_VOL_DOWN BUTTON_VOL_DOWN
#elif CONFIG_KEYPAD == ONDAVX777_PAD
-#define OSCILLOSCOPE_QUIT BUTTON_POWER
+#define OSCILLOSCOPE_QUIT BUTTON_POWER
#elif CONFIG_KEYPAD == MROBE500_PAD
-#define OSCILLOSCOPE_QUIT BUTTON_POWER
+#define OSCILLOSCOPE_QUIT BUTTON_POWER
#elif CONFIG_KEYPAD == SAMSUNG_YH_PAD
-#define OSCILLOSCOPE_QUIT BUTTON_REC
-#define OSCILLOSCOPE_DRAWMODE (BUTTON_PLAY|BUTTON_LEFT)
-#define OSCILLOSCOPE_ADVMODE (BUTTON_PLAY|BUTTON_RIGHT)
-#define OSCILLOSCOPE_ORIENTATION (BUTTON_PLAY|BUTTON_UP)
-#define OSCILLOSCOPE_PAUSE (BUTTON_PLAY|BUTTON_DOWN)
-#define OSCILLOSCOPE_SPEED_UP BUTTON_RIGHT
-#define OSCILLOSCOPE_SPEED_DOWN BUTTON_LEFT
-#define OSCILLOSCOPE_VOL_UP BUTTON_UP
-#define OSCILLOSCOPE_VOL_DOWN BUTTON_DOWN
+#define OSCILLOSCOPE_QUIT BUTTON_REC
+#define OSCILLOSCOPE_DRAWMODE (BUTTON_PLAY|BUTTON_LEFT)
+#define OSCILLOSCOPE_ADVMODE (BUTTON_PLAY|BUTTON_RIGHT)
+#define OSCILLOSCOPE_ORIENTATION (BUTTON_PLAY|BUTTON_UP)
+#define OSCILLOSCOPE_PAUSE (BUTTON_PLAY|BUTTON_DOWN)
+#define OSCILLOSCOPE_SPEED_UP BUTTON_RIGHT
+#define OSCILLOSCOPE_SPEED_DOWN BUTTON_LEFT
+#define OSCILLOSCOPE_VOL_UP BUTTON_UP
+#define OSCILLOSCOPE_VOL_DOWN BUTTON_DOWN
+/* Need GRAPHMODE */
#elif CONFIG_KEYPAD == PBELL_VIBE500_PAD
-#define OSCILLOSCOPE_QUIT BUTTON_REC
-#define OSCILLOSCOPE_DRAWMODE BUTTON_MENU
-#define OSCILLOSCOPE_ADVMODE BUTTON_CANCEL
-#define OSCILLOSCOPE_ORIENTATION BUTTON_OK
-#define OSCILLOSCOPE_PAUSE BUTTON_PLAY
-#define OSCILLOSCOPE_SPEED_UP BUTTON_PREV
-#define OSCILLOSCOPE_SPEED_DOWN BUTTON_NEXT
-#define OSCILLOSCOPE_VOL_UP BUTTON_UP
-#define OSCILLOSCOPE_VOL_DOWN BUTTON_DOWN
+#define OSCILLOSCOPE_QUIT BUTTON_REC
+#define OSCILLOSCOPE_DRAWMODE BUTTON_MENU
+#define OSCILLOSCOPE_ADVMODE BUTTON_CANCEL
+#define OSCILLOSCOPE_ORIENTATION_PRE BUTTON_OK
+#define OSCILLOSCOPE_ORIENTATION (BUTTON_OK | BUTTON_REL)
+#define OSCILLOSCOPE_GRAPHMODE_PRE BUTTON_OK
+#define OSCILLOSCOPE_GRAPHMODE (BUTTON_OK | BUTTON_REPEAT)
+#define OSCILLOSCOPE_PAUSE BUTTON_PLAY
+#define OSCILLOSCOPE_SPEED_UP BUTTON_PREV
+#define OSCILLOSCOPE_SPEED_DOWN BUTTON_NEXT
+#define OSCILLOSCOPE_VOL_UP BUTTON_UP
+#define OSCILLOSCOPE_VOL_DOWN BUTTON_DOWN
+#define NEED_LASTBUTTON
#elif CONFIG_KEYPAD == MPIO_HD200_PAD
-#define OSCILLOSCOPE_QUIT (BUTTON_REC | BUTTON_PLAY)
-#define OSCILLOSCOPE_DRAWMODE BUTTON_FUNC
-#define OSCILLOSCOPE_ADVMODE BUTTON_REC
-#define OSCILLOSCOPE_ORIENTATION (BUTTON_FUNC|BUTTON_REPEAT)
-#define OSCILLOSCOPE_PAUSE BUTTON_PLAY
-#define OSCILLOSCOPE_SPEED_UP BUTTON_FF
-#define OSCILLOSCOPE_SPEED_DOWN BUTTON_REW
-#define OSCILLOSCOPE_VOL_UP BUTTON_VOL_UP
-#define OSCILLOSCOPE_VOL_DOWN BUTTON_VOL_DOWN
+#define OSCILLOSCOPE_QUIT (BUTTON_REC | BUTTON_PLAY)
+#define OSCILLOSCOPE_DRAWMODE BUTTON_FUNC
+#define OSCILLOSCOPE_ADVMODE BUTTON_REC
+#define OSCILLOSCOPE_ORIENTATION (BUTTON_FUNC | BUTTON_REPEAT)
+#define OSCILLOSCOPE_PAUSE BUTTON_PLAY
+#define OSCILLOSCOPE_SPEED_UP BUTTON_FF
+#define OSCILLOSCOPE_SPEED_DOWN BUTTON_REW
+#define OSCILLOSCOPE_VOL_UP BUTTON_VOL_UP
+#define OSCILLOSCOPE_VOL_DOWN BUTTON_VOL_DOWN
+/* Need GRAPHMODE */
#elif CONFIG_KEYPAD == MPIO_HD300_PAD
-#define OSCILLOSCOPE_QUIT (BUTTON_MENU | BUTTON_REPEAT)
-#define OSCILLOSCOPE_DRAWMODE BUTTON_ENTER
-#define OSCILLOSCOPE_ADVMODE BUTTON_REC
-#define OSCILLOSCOPE_ORIENTATION BUTTON_MENU
-#define OSCILLOSCOPE_PAUSE BUTTON_PLAY
-#define OSCILLOSCOPE_SPEED_UP BUTTON_FF
-#define OSCILLOSCOPE_SPEED_DOWN BUTTON_REW
-#define OSCILLOSCOPE_VOL_UP BUTTON_UP
-#define OSCILLOSCOPE_VOL_DOWN BUTTON_DOWN
+#define OSCILLOSCOPE_QUIT (BUTTON_MENU | BUTTON_REPEAT)
+#define OSCILLOSCOPE_DRAWMODE BUTTON_ENTER
+#define OSCILLOSCOPE_ADVMODE BUTTON_REC
+#define OSCILLOSCOPE_ORIENTATION_PRE BUTTON_MENU
+#define OSCILLOSCOPE_ORIENTATION (BUTTON_MENU | BUTTON_REL)
+#define OSCILLOSCOPE_GRAPHMODE_PRE BUTTON_MENU
+#define OSCILLOSCOPE_GRAPHMODE (BUTTON_MENU | BUTTON_REPEAT)
+#define OSCILLOSCOPE_PAUSE BUTTON_PLAY
+#define OSCILLOSCOPE_SPEED_UP BUTTON_FF
+#define OSCILLOSCOPE_SPEED_DOWN BUTTON_REW
+#define OSCILLOSCOPE_VOL_UP BUTTON_UP
+#define OSCILLOSCOPE_VOL_DOWN BUTTON_DOWN
+#define NEED_LASTBUTTON
#elif CONFIG_KEYPAD == SANSA_FUZEPLUS_PAD
#define OSCILLOSCOPE_QUIT BUTTON_POWER
#define OSCILLOSCOPE_DRAWMODE BUTTON_SELECT
#define OSCILLOSCOPE_ADVMODE BUTTON_BACK
-#define OSCILLOSCOPE_ORIENTATION BUTTON_UP
+#define OSCILLOSCOPE_ORIENTATION (BUTTON_BACK|BUTTON_REPEAT)
#define OSCILLOSCOPE_PAUSE BUTTON_PLAYPAUSE
#define OSCILLOSCOPE_SPEED_UP BUTTON_LEFT
#define OSCILLOSCOPE_SPEED_DOWN BUTTON_RIGHT
#define OSCILLOSCOPE_VOL_UP BUTTON_VOL_UP
#define OSCILLOSCOPE_VOL_DOWN BUTTON_VOL_DOWN
+/* Need GRAPHMODE */
#elif (CONFIG_KEYPAD == SANSA_CONNECT_PAD)
-#define OSCILLOSCOPE_QUIT BUTTON_POWER
-#define OSCILLOSCOPE_DRAWMODE BUTTON_SELECT
-#define OSCILLOSCOPE_ADVMODE BUTTON_DOWN
-#define OSCILLOSCOPE_ORIENTATION BUTTON_UP
-#define OSCILLOSCOPE_PAUSE BUTTON_NEXT
-#define OSCILLOSCOPE_SPEED_UP BUTTON_RIGHT
-#define OSCILLOSCOPE_SPEED_DOWN BUTTON_LEFT
-#define OSCILLOSCOPE_VOL_UP BUTTON_VOL_UP
-#define OSCILLOSCOPE_VOL_DOWN BUTTON_VOL_DOWN
+#define OSCILLOSCOPE_QUIT BUTTON_POWER
+#define OSCILLOSCOPE_DRAWMODE BUTTON_SELECT
+#define OSCILLOSCOPE_ADVMODE BUTTON_DOWN
+#define OSCILLOSCOPE_ORIENTATION_PRE BUTTON_UP
+#define OSCILLOSCOPE_ORIENTATION (BUTTON_UP | BUTTON_REL)
+#define OSCILLOSCOPE_GRAPHMODE_PRE BUTTON_UP
+#define OSCILLOSCOPE_GRAPHMODE (BUTTON_UP | BUTTON_REPEAT)
+#define OSCILLOSCOPE_PAUSE BUTTON_NEXT
+#define OSCILLOSCOPE_SPEED_UP BUTTON_RIGHT
+#define OSCILLOSCOPE_SPEED_DOWN BUTTON_LEFT
+#define OSCILLOSCOPE_VOL_UP BUTTON_VOL_UP
+#define OSCILLOSCOPE_VOL_DOWN BUTTON_VOL_DOWN
+#define NEED_LASTBUTTON
#elif (CONFIG_KEYPAD == SAMSUNG_YPR0_PAD)
-#define OSCILLOSCOPE_QUIT BUTTON_BACK
-#define OSCILLOSCOPE_DRAWMODE BUTTON_USER
-#define OSCILLOSCOPE_ADVMODE BUTTON_MENU
-#define OSCILLOSCOPE_ORIENTATION BUTTON_POWER
-#define OSCILLOSCOPE_PAUSE BUTTON_SELECT
-#define OSCILLOSCOPE_SPEED_UP BUTTON_RIGHT
-#define OSCILLOSCOPE_SPEED_DOWN BUTTON_LEFT
-#define OSCILLOSCOPE_VOL_UP BUTTON_UP
-#define OSCILLOSCOPE_VOL_DOWN BUTTON_DOWN
+#define OSCILLOSCOPE_QUIT BUTTON_BACK
+#define OSCILLOSCOPE_DRAWMODE BUTTON_USER
+#define OSCILLOSCOPE_ADVMODE BUTTON_MENU
+#define OSCILLOSCOPE_ORIENTATION BUTTON_POWER
+#define OSCILLOSCOPE_PAUSE BUTTON_SELECT
+#define OSCILLOSCOPE_SPEED_UP BUTTON_RIGHT
+#define OSCILLOSCOPE_SPEED_DOWN BUTTON_LEFT
+#define OSCILLOSCOPE_VOL_UP BUTTON_UP
+#define OSCILLOSCOPE_VOL_DOWN BUTTON_DOWN
+/* Need GRAPHMODE */
#elif (CONFIG_KEYPAD == HM60X_PAD)
-#define OSCILLOSCOPE_QUIT BUTTON_POWER
-#define OSCILLOSCOPE_DRAWMODE (BUTTON_POWER | BUTTON_SELECT)
-#define OSCILLOSCOPE_ADVMODE (BUTTON_POWER | BUTTON_RIGHT)
-#define OSCILLOSCOPE_ORIENTATION (BUTTON_POWER | BUTTON_LEFT)
-#define OSCILLOSCOPE_PAUSE BUTTON_SELECT
-#define OSCILLOSCOPE_SPEED_UP BUTTON_UP
-#define OSCILLOSCOPE_SPEED_DOWN BUTTON_DOWN
-#define OSCILLOSCOPE_VOL_UP BUTTON_RIGHT
-#define OSCILLOSCOPE_VOL_DOWN BUTTON_LEFT
+#define OSCILLOSCOPE_QUIT BUTTON_POWER
+#define OSCILLOSCOPE_DRAWMODE (BUTTON_POWER | BUTTON_SELECT)
+#define OSCILLOSCOPE_ADVMODE (BUTTON_POWER | BUTTON_RIGHT)
+#define OSCILLOSCOPE_ORIENTATION (BUTTON_POWER | BUTTON_LEFT)
+#define OSCILLOSCOPE_PAUSE BUTTON_SELECT
+#define OSCILLOSCOPE_SPEED_UP BUTTON_UP
+#define OSCILLOSCOPE_SPEED_DOWN BUTTON_DOWN
+#define OSCILLOSCOPE_VOL_UP BUTTON_RIGHT
+#define OSCILLOSCOPE_VOL_DOWN BUTTON_LEFT
+/* Need GRAPHMODE */
#elif (CONFIG_KEYPAD == HM801_PAD)
-#define OSCILLOSCOPE_QUIT BUTTON_POWER
-#define OSCILLOSCOPE_DRAWMODE BUTTON_PREV
-#define OSCILLOSCOPE_ADVMODE BUTTON_NEXT
-#define OSCILLOSCOPE_ORIENTATION BUTTON_PLAY
-#define OSCILLOSCOPE_PAUSE BUTTON_SELECT
-#define OSCILLOSCOPE_SPEED_UP BUTTON_UP
-#define OSCILLOSCOPE_SPEED_DOWN BUTTON_DOWN
-#define OSCILLOSCOPE_VOL_UP BUTTON_RIGHT
-#define OSCILLOSCOPE_VOL_DOWN BUTTON_LEFT
+#define OSCILLOSCOPE_QUIT BUTTON_POWER
+#define OSCILLOSCOPE_DRAWMODE BUTTON_PREV
+#define OSCILLOSCOPE_ADVMODE BUTTON_NEXT
+#define OSCILLOSCOPE_ORIENTATION BUTTON_PLAY
+#define OSCILLOSCOPE_PAUSE BUTTON_SELECT
+#define OSCILLOSCOPE_SPEED_UP BUTTON_UP
+#define OSCILLOSCOPE_SPEED_DOWN BUTTON_DOWN
+#define OSCILLOSCOPE_VOL_UP BUTTON_RIGHT
+#define OSCILLOSCOPE_VOL_DOWN BUTTON_LEFT
+/* Need GRAPHMODE */
#else
#error No keymap defined!
@@ -381,119 +463,419 @@
#ifdef HAVE_TOUCHSCREEN
#ifndef OSCILLOSCOPE_QUIT
-#define OSCILLOSCOPE_QUIT BUTTON_TOPLEFT
+#define OSCILLOSCOPE_QUIT BUTTON_TOPLEFT
#endif
#ifndef OSCILLOSCOPE_DRAWMODE
-#define OSCILLOSCOPE_DRAWMODE BUTTON_TOPMIDDLE
+#define OSCILLOSCOPE_DRAWMODE BUTTON_TOPMIDDLE
#endif
#ifndef OSCILLOSCOPE_ADVMODE
-#define OSCILLOSCOPE_ADVMODE BUTTON_BOTTOMMIDDLE
+#define OSCILLOSCOPE_ADVMODE BUTTON_BOTTOMMIDDLE
#endif
#ifndef OSCILLOSCOPE_ORIENTATION
-#define OSCILLOSCOPE_ORIENTATION BUTTON_BOTTOMLEFT
+#define OSCILLOSCOPE_ORIENTATION_PRE BUTTON_BOTTOMLEFT
+#define OSCILLOSCOPE_ORIENTATION (BUTTON_BOTTOMLEFT|BUTTON_REL)
+#endif
+#ifndef OSCILLOSCOPE_GRAPHMODE
+#define OSCILLOSCOPE_GRAPHMODE_PRE BUTTON_BOTTOMLEFT
+#define OSCILLOSCOPE_GRAPHMODE (BUTTON_BOTTOMLEFT|BUTTON_REPEAT)
#endif
#ifndef OSCILLOSCOPE_PAUSE
-#define OSCILLOSCOPE_PAUSE BUTTON_CENTER
+#define OSCILLOSCOPE_PAUSE BUTTON_CENTER
#endif
#ifndef OSCILLOSCOPE_SPEED_UP
-#define OSCILLOSCOPE_SPEED_UP BUTTON_MIDRIGHT
+#define OSCILLOSCOPE_SPEED_UP BUTTON_MIDRIGHT
#endif
#ifndef OSCILLOSCOPE_SPEED_DOWN
-#define OSCILLOSCOPE_SPEED_DOWN BUTTON_MIDLEFT
+#define OSCILLOSCOPE_SPEED_DOWN BUTTON_MIDLEFT
#endif
#ifndef OSCILLOSCOPE_VOL_UP
-#define OSCILLOSCOPE_VOL_UP BUTTON_TOPRIGHT
+#define OSCILLOSCOPE_VOL_UP BUTTON_TOPRIGHT
#endif
#ifndef OSCILLOSCOPE_VOL_DOWN
-#define OSCILLOSCOPE_VOL_DOWN BUTTON_BOTTOMRIGHT
-#endif
+#define OSCILLOSCOPE_VOL_DOWN BUTTON_BOTTOMRIGHT
#endif
+#define NEED_LASTBUTTON
+#endif /* HAVE_TOUCHSCREEN */
/* colours */
#if LCD_DEPTH > 1
#ifdef HAVE_LCD_COLOR
-#define BACKG_COLOR LCD_BLACK
-#define GRAPH_COLOR LCD_RGBPACK(128, 255, 0)
-#define CURSOR_COLOR LCD_RGBPACK(255, 0, 0)
+#define BACKG_COLOR LCD_BLACK
+#define GRAPH_COLOR LCD_RGBPACK(128, 255, 0)
+#define CURSOR_COLOR LCD_RGBPACK(255, 0, 0)
+#define GRID_COLOR LCD_RGBPACK(192, 255, 64)
+#define OSD_TEXT_COLOR LCD_WHITE
+#define OSD_BACKG_COLOR LCD_BLACK
+#define OSD_BORDER_COLOR LCD_RGBPACK(255, 0, 0)
+#define OSC_OSD_MARGIN_SIZE 0 /* Border and text are different */
#else
-#define BACKG_COLOR LCD_WHITE
-#define GRAPH_COLOR LCD_BLACK
-#define CURSOR_COLOR LCD_DARKGRAY
+#define BACKG_COLOR LCD_WHITE
+#define GRAPH_COLOR LCD_BLACK
+#define CURSOR_COLOR LCD_DARKGRAY
+#define GRID_COLOR LCD_DARKGRAY
+#define OSD_TEXT_COLOR LCD_BLACK
+#define OSD_BACKG_COLOR LCD_WHITE
+#define OSD_BORDER_COLOR LCD_BLACK
+#endif
#endif
+
+#ifndef OSC_OSD_MARGIN_SIZE
+#define OSC_OSD_MARGIN_SIZE 1
#endif
+#define MIN_SPEED 1
+#define MAX_SPEED 100
+
enum { DRAW_FILLED, DRAW_LINE, DRAW_PIXEL, MAX_DRAW };
enum { ADV_SCROLL, ADV_WRAP, MAX_ADV };
enum { OSC_HORIZ, OSC_VERT, MAX_OSC };
+enum
+{
+ GRAPH_PEAKS,
+#ifdef OSCILLOSCOPE_GRAPHMODE
+ GRAPH_WAVEFORM,
+#endif
+ MAX_GRAPH
+};
-#define CFGFILE_VERSION 0 /* Current config file version */
+#define CFGFILE_VERSION 1 /* Current config file version */
#define CFGFILE_MINVERSION 0 /* Minimum config file version to accept */
+/* global variables */
-#define MAX_PEAK 0x8000
-
-#if defined(SIMULATOR) && (CONFIG_CODEC != SWCODEC)
-#define mas_codec_readreg(x) rand()%MAX_PEAK
-#endif
+/*
+ * / 99*[e ^ ((100 - x) / z) - 1] \
+ * osc_delay = HZ * | ---------------------------- + 1 |
+ * \ [e ^ (99 / z) - 1] /
+ *
+ * x: 1 .. 100, z: 15, delay: 100.00 .. 1.00
+ */
+static const unsigned short osc_delay_tbl[100] =
+{
+ 10000, 9361, 8763, 8205, 7682, 7192, 6735, 6307, 5906, 5532,
+ 5181, 4853, 4546, 4259, 3991, 3739, 3504, 3285, 3079, 2886,
+ 2706, 2538, 2380, 2233, 2095, 1966, 1845, 1732, 1627, 1528,
+ 1435, 1349, 1268, 1192, 1121, 1055, 993, 935, 880, 830,
+ 782, 737, 696, 657, 620, 586, 554, 524, 497, 470,
+ 446, 423, 402, 381, 363, 345, 329, 313, 299, 285,
+ 273, 261, 250, 239, 230, 221, 212, 204, 197, 190,
+ 183, 177, 171, 166, 161, 156, 152, 147, 144, 140,
+ 136, 133, 130, 127, 125, 122, 120, 118, 116, 114,
+ 112, 110, 108, 107, 106, 104, 103, 102, 101, 100
+};
-/* global variables */
+#define GRAPH_PEAKS_DEFAULT 68
+#define GRAPH_WAVEFORM_DEFAULT 50
/* settings */
-struct osc_config {
- int delay; /* in ticks */
+static struct osc_config
+{
+ int speed[MAX_GRAPH];
int draw;
int advance;
int orientation;
+ int graphmode;
+} osc_disk =
+{
+ .speed =
+ {
+ [GRAPH_PEAKS] = GRAPH_PEAKS_DEFAULT,
+#ifdef OSCILLOSCOPE_GRAPHMODE
+ [GRAPH_WAVEFORM] = GRAPH_WAVEFORM_DEFAULT,
+#endif
+ },
+ .draw = DRAW_FILLED,
+ .advance = ADV_SCROLL,
+ .orientation = OSC_HORIZ,
+#ifdef OSCILLOSCOPE_GRAPHMODE
+ .graphmode = GRAPH_PEAKS,
+#endif
};
-struct osc_config osc_disk = { 2, DRAW_FILLED, ADV_SCROLL, OSC_HORIZ };
-struct osc_config osc; /* running config */
+static struct osc_config osc; /* running config */
static const char cfg_filename[] = "oscilloscope.cfg";
static char *draw_str[3] = { "filled", "line", "pixel" };
static char *advance_str[2] = { "scroll", "wrap" };
static char *orientation_str[2] = { "horizontal", "vertical" };
+#ifdef OSCILLOSCOPE_GRAPHMODE
+static char *graphmode_str[2] = { "peaks", "waveform" };
+#endif
-struct configdata disk_config[] = {
- { TYPE_INT, 1, 99, { .int_p = &osc_disk.delay }, "delay", NULL },
+struct configdata disk_config[] =
+{
+ { TYPE_INT, MIN_SPEED, MAX_SPEED,
+ { .int_p = &osc_disk.speed[GRAPH_PEAKS] }, "speed", NULL },
{ TYPE_ENUM, 0, MAX_DRAW, { .int_p = &osc_disk.draw }, "draw", draw_str },
{ TYPE_ENUM, 0, MAX_ADV, { .int_p = &osc_disk.advance }, "advance",
advance_str },
{ TYPE_ENUM, 0, MAX_OSC, { .int_p = &osc_disk.orientation }, "orientation",
- orientation_str }
+ orientation_str },
+#ifdef OSCILLOSCOPE_GRAPHMODE
+ { TYPE_INT, MAX_SPEED, MAX_SPEED,
+ { .int_p = &osc_disk.speed[GRAPH_WAVEFORM] }, "wavespeed", NULL },
+ { TYPE_ENUM, 0, MAX_GRAPH, { .int_p = &osc_disk.graphmode }, "graphmode",
+ graphmode_str },
+#endif
};
+enum osc_message_ids
+{
+ OSC_MSG_ADVMODE,
+ OSC_MSG_DRAWMODE,
+#ifdef OSCILLOSCOPE_GRAPHMODE
+ OSC_MSG_GRAPHMODE,
+#endif
+ OSC_MSG_ORIENTATION,
+ OSC_MSG_PAUSED,
+ OSC_MSG_SPEED,
+ OSC_MSG_VOLUME,
+};
-long last_tick = 0; /* time of last drawing */
-int last_pos = 0; /* last x or y drawing position. Reset for aspect switch. */
-int last_left; /* last channel values */
-int last_right;
-
-unsigned char message[16]; /* message to display */
-bool displaymsg = false;
-int font_height = 8;
+static long (*anim_draw)(void);
+static int message = -1, msgval = 0; /* popup message */
+static bool paused = false;
+static bool one_frame_paused = false; /* Allow one frame to be drawn when paused if
+ view is erased */
+static long osc_delay; /* delay in 100ths of a tick */
+static long osc_delay_error; /* delay fraction error accumulator */
/* implementation */
-static void anim_horizontal(int cur_left, int cur_right)
+
+/** Messages **/
+
+static unsigned char osc_osd_message[16]; /* message to display (formatted) */
+
+static inline void osc_popupmsg(int msg, int val)
+{
+ message = msg; msgval = val;
+}
+
+/* Format a message to display */
+static void osc_osd_format_message(enum osc_message_ids id, int val)
{
+ const char *msg = "";
+
+ switch (id)
+ {
+ case OSC_MSG_ADVMODE:
+ msg = val == ADV_WRAP ? "Wrap" : "Scroll";
+ break;
+
+ case OSC_MSG_DRAWMODE:
+ switch (val)
+ {
+ case DRAW_PIXEL: msg = "Pixel"; break;
+ case DRAW_LINE: msg = "Line"; break;
+ default: msg = "Filled";
+ }
+ break;
+
+#ifdef OSCILLOSCOPE_GRAPHMODE
+ case OSC_MSG_GRAPHMODE:
+ msg = val == GRAPH_WAVEFORM ? "Waveform" : "Peaks";
+ break;
+#endif
+
+ case OSC_MSG_ORIENTATION:
+ msg = val == OSC_VERT ? "Vertical" : "Horizontal";
+ break;
+
+ case OSC_MSG_PAUSED:
+ msg = val ? "Paused" : "Running";
+ break;
+
+ case OSC_MSG_SPEED:
+ msg = "Speed: %d";
+ break;
+
+ case OSC_MSG_VOLUME:
+ rb->snprintf(osc_osd_message, sizeof (osc_osd_message),
+ "Volume: %d%s",
+ rb->sound_val2phys(SOUND_VOLUME, val),
+ rb->sound_unit(SOUND_VOLUME));
+ return;
+ }
+
+ /* Default action: format integer */
+ rb->snprintf(osc_osd_message, sizeof (osc_osd_message), msg, val);
+}
+
+
+/** OSD **/
+
+/* Actually draw the OSD within the OSD viewport */
+extern void osd_lcd_draw_cb(int x, int y, int width, int height)
+{
+#if LCD_DEPTH > 1
+ rb->lcd_set_foreground(OSD_TEXT_COLOR);
+ rb->lcd_set_background(OSD_BACKG_COLOR);
+#endif
+#if OSC_OSD_MARGIN_SIZE != 0
+ rb->lcd_set_drawmode(DRMODE_SOLID|DRMODE_INVERSEVID);
+ rb->lcd_fillrect(1, 1, width - 2, height - 2);
+ rb->lcd_set_drawmode(DRMODE_SOLID);
+#endif
+ rb->lcd_putsxy(1+OSC_OSD_MARGIN_SIZE, 1+OSC_OSD_MARGIN_SIZE,
+ osc_osd_message);
+#if LCD_DEPTH > 1
+ rb->lcd_set_foreground(OSD_BORDER_COLOR);
+#endif
+ rb->lcd_drawrect(0, 0, width, height);
+
+ (void)x; (void)y;
+}
+
+/* Perform all necessary basic setup tasks for the OSD */
+static void osc_osd_init(void)
+{
+ /* Grab the plugin buffer for the OSD back buffer */
+ size_t bufsize;
+ void *buf = rb->plugin_get_buffer(&bufsize);
+ osd_init(buf, bufsize, osd_lcd_draw_cb);
+}
+
+/* Format a message by ID and show the OSD */
+static void osc_osd_show_message(int id, int val)
+{
+ osc_osd_format_message(id, val);
+
+ if (!osd_enabled())
+ return;
+
+ int width, height;
+ int maxwidth, maxheight;
+
+ rb->lcd_set_viewport(osd_get_viewport());
+ osd_get_max_dims(&maxwidth, &maxheight);
+ rb->lcd_getstringsize(osc_osd_message, &width, &height);
+ rb->lcd_set_viewport(NULL); /* to regular viewport */
+
+ width += 2 + 2*OSC_OSD_MARGIN_SIZE;
+ if (width > maxwidth)
+ width = maxwidth;
+
+ height += 2 + 2*OSC_OSD_MARGIN_SIZE;
+ if (height > maxheight)
+ height = maxheight;
+
+ /* Center it on the screen */
+ bool drawn = osd_update_pos((LCD_WIDTH - width) / 2,
+ (LCD_HEIGHT - height) / 2,
+ width, height);
+
+ osd_show(OSD_SHOW | (drawn ? 0 : OSD_UPDATENOW));
+}
+
+/* Draw cursor bar for horizontal views */
+static void anim_draw_cursor_h(int x)
+{
+#if LCD_DEPTH > 1
+ rb->lcd_set_foreground(CURSOR_COLOR);
+ rb->lcd_vline(x, 0, LCD_HEIGHT-1);
+ rb->lcd_set_foreground(GRAPH_COLOR);
+#else
+ rb->lcd_set_drawmode(DRMODE_COMPLEMENT);
+ rb->lcd_vline(x, 0, LCD_HEIGHT-1);
+ rb->lcd_set_drawmode(DRMODE_SOLID);
+#endif
+}
+
+/* Draw cursor bar for vertical views */
+static void anim_draw_cursor_v(int y)
+{
+#if LCD_DEPTH > 1 /* cursor bar */
+ rb->lcd_set_foreground(CURSOR_COLOR);
+ rb->lcd_hline(0, LCD_WIDTH-1, y);
+ rb->lcd_set_foreground(GRAPH_COLOR);
+#else
+ rb->lcd_set_drawmode(DRMODE_COMPLEMENT);
+ rb->lcd_hline(0, LCD_WIDTH-1, y);
+ rb->lcd_set_drawmode(DRMODE_SOLID);
+#endif
+}
+
+
+/** Peaks View **/
+
+static long last_tick = 0; /* time of last drawing */
+static long last_delay = 0; /* last delay value used */
+static int last_pos = 0; /* last x or y drawing position. Reset for aspect switch. */
+static int last_left; /* last channel values */
+static int last_right;
+
+static void get_peaks(int *left, int *right)
+{
+#if CONFIG_CODEC == SWCODEC
+ static struct pcm_peaks peaks;
+ rb->mixer_channel_calculate_peaks(PCM_MIXER_CHAN_PLAYBACK,
+ &peaks);
+ *left = peaks.left;
+ *right = peaks.right;
+#elif defined (SIMULATOR)
+ *left = rand() % 0x8000;
+ *right = rand() % 0x8000;
+#elif (CONFIG_CODEC == MAS3587F) || (CONFIG_CODEC == MAS3539F)
+ *left = rb->mas_codec_readreg(0xC);
+ *right = rb->mas_codec_readreg(0xD);
+#else
+ *left = 0;
+ *right = 0;
+#endif
+}
+
+static long get_next_delay(void)
+{
+ long delay = osc_delay / 100;
+ osc_delay_error += osc_delay - delay * 100;
+
+ if (osc_delay_error >= 100)
+ {
+ delay++;
+ osc_delay_error -= 100;
+ }
+
+ return delay;
+}
+
+static long anim_peaks_horizontal(void)
+{
+ long cur_tick = *rb->current_tick;
int cur_x, x;
int left, right, dl, dr;
- long cur_tick = *rb->current_tick;
- long d = (cur_tick - last_tick) / osc.delay;
+ long d = (cur_tick - last_tick) / last_delay;
bool full_update = false;
- if (d == 0) /* too early, bail out */
- return;
+ if (paused)
+ {
+ one_frame_paused = false;
+ osd_lcd_update_prepare();
+ anim_draw_cursor_h(0);
+ osd_lcd_update_rect(0, 0, 1, LCD_HEIGHT);
+ last_pos = -1;
+ return cur_tick + HZ/5;
+ }
+
+ if (d <= 0) /* too early, bail out */
+ return last_tick + last_delay;
last_tick = cur_tick;
+ int cur_left, cur_right;
+ get_peaks(&cur_left, &cur_right);
+
if (d > HZ) /* first call or too much delay, (re)start */
{
last_left = cur_left;
last_right = cur_right;
- return;
+ return cur_tick + last_delay;
}
+
+ one_frame_paused = false;
+
+ osd_lcd_update_prepare();
+
cur_x = last_pos + d;
if (cur_x >= LCD_WIDTH)
@@ -608,70 +990,71 @@ static void anim_horizontal(int cur_left, int cur_right)
break;
}
+
last_left = cur_left;
last_right = cur_right;
- if (displaymsg)
- {
- int width;
-
- rb->lcd_getstringsize(message, &width, NULL);
- last_pos -= width - 1;
- rb->lcd_putsxy(last_pos, 0, message);
- displaymsg = false;
-
- if (last_pos < 0)
- last_pos = 0;
- }
-
if (full_update)
{
- rb->lcd_update();
+ osd_lcd_update();
}
else
{
-#if LCD_DEPTH > 1 /* cursor bar */
- rb->lcd_set_foreground(CURSOR_COLOR);
- rb->lcd_vline(cur_x + 1, 0, LCD_HEIGHT-1);
- rb->lcd_set_foreground(GRAPH_COLOR);
-#else
- rb->lcd_set_drawmode(DRMODE_COMPLEMENT);
- rb->lcd_vline(cur_x + 1, 0, LCD_HEIGHT-1);
- rb->lcd_set_drawmode(DRMODE_SOLID);
-#endif
+ anim_draw_cursor_h(cur_x + 1); /* cursor bar */
if (cur_x > last_pos)
{
- rb->lcd_update_rect(last_pos, 0, cur_x - last_pos + 2, LCD_HEIGHT);
+ osd_lcd_update_rect(last_pos, 0, cur_x - last_pos + 2, LCD_HEIGHT);
}
else
{
- rb->lcd_update_rect(last_pos, 0, LCD_WIDTH - last_pos, LCD_HEIGHT);
- rb->lcd_update_rect(0, 0, cur_x + 2, LCD_HEIGHT);
+ osd_lcd_update_rect(last_pos, 0, LCD_WIDTH - last_pos, LCD_HEIGHT);
+ osd_lcd_update_rect(0, 0, cur_x + 2, LCD_HEIGHT);
}
}
last_pos = cur_x;
+
+ last_delay = get_next_delay();
+ return cur_tick + last_delay;
}
-static void anim_vertical(int cur_left, int cur_right)
+static long anim_peaks_vertical(void)
{
+ long cur_tick = *rb->current_tick;
int cur_y, y;
int left, right, dl, dr;
- long cur_tick = *rb->current_tick;
- long d = (cur_tick - last_tick) / osc.delay;
bool full_update = false;
+ long d = (cur_tick - last_tick) / last_delay;
+
+ if (paused)
+ {
+ one_frame_paused = false;
+ osd_lcd_update_prepare();
+ anim_draw_cursor_v(0);
+ osd_lcd_update_rect(0, 0, LCD_WIDTH, 1);
+ last_pos = -1;
+ return cur_tick + HZ/5;
+ }
if (d == 0) /* too early, bail out */
- return;
+ return last_tick + last_delay;
last_tick = cur_tick;
+ int cur_left, cur_right;
+ get_peaks(&cur_left, &cur_right);
+
if (d > HZ) /* first call or too much delay, (re)start */
{
last_left = cur_left;
last_right = cur_right;
- return;
+ return cur_tick + last_delay;
}
+
+ one_frame_paused = false;
+
+ osd_lcd_update_prepare();
+
cur_y = last_pos + d;
if (cur_y >= LCD_HEIGHT)
@@ -786,114 +1169,736 @@ static void anim_vertical(int cur_left, int cur_right)
break;
}
+
last_left = cur_left;
last_right = cur_right;
- if (displaymsg)
+ if (full_update)
+ {
+ osd_lcd_update();
+ }
+ else
{
- last_pos -= font_height - 1;
- rb->lcd_putsxy(0, last_pos, message);
- displaymsg = false;
+ anim_draw_cursor_v(cur_y + 1); /* cursor bar */
- if (last_pos < 0)
- last_pos = 0;
+ if (cur_y > last_pos)
+ {
+ osd_lcd_update_rect(0, last_pos, LCD_WIDTH, cur_y - last_pos + 2);
+ }
+ else
+ {
+ osd_lcd_update_rect(0, last_pos, LCD_WIDTH, LCD_HEIGHT - last_pos);
+ osd_lcd_update_rect(0, 0, LCD_WIDTH, cur_y + 2);
+ }
}
+ last_pos = cur_y;
- if (full_update)
+ last_delay = get_next_delay();
+ return cur_tick + last_delay;
+}
+
+
+/** Waveform View **/
+
+#ifdef OSCILLOSCOPE_GRAPHMODE
+static int16_t waveform_buffer[2*ALIGN_UP(NATIVE_FREQUENCY, 2048)+2*2048]
+ MEM_ALIGN_ATTR;
+static size_t waveform_buffer_threshold = 0;
+static size_t volatile waveform_buffer_have = 0;
+static size_t waveform_buffer_break = 0;
+#define PCM_SAMPLESIZE (2*sizeof(int16_t))
+#define PCM_BYTERATE (NATIVE_FREQUENCY*PCM_SAMPLESIZE)
+
+#define WAVEFORM_SCALE_PCM(full_scale, sample) \
+ ((((full_scale) * (sample)) + (1 << 14)) >> 15)
+
+static void waveform_buffer_set_threshold(size_t threshold)
+{
+ if (threshold > sizeof (waveform_buffer))
+ threshold = sizeof (waveform_buffer);
+
+ if (threshold == waveform_buffer_threshold)
+ return;
+
+ /* Avoid changing it in the middle of buffer callback */
+ rb->pcm_play_lock();
+
+ waveform_buffer_threshold = threshold;
+
+ rb->pcm_play_unlock();
+}
+
+static inline bool waveform_buffer_have_enough(void)
+{
+ return waveform_buffer_have >= waveform_buffer_threshold;
+}
+
+static void waveform_buffer_done(void)
+{
+ /* This is safe because buffer callback won't add more data unless the
+ treshold is changed or data is removed below the threshold. This isn't
+ called until after the threshold is already met. */
+ size_t threshold = waveform_buffer_threshold;
+ size_t have = 0;
+
+ if (threshold != 0)
+ {
+ have = waveform_buffer_have;
+ size_t slide = (have - 1) / threshold * threshold;
+
+ if (slide == 0)
+ slide = threshold;
+
+ have -= slide;
+
+ if (have > 0)
+ {
+ /* memcpy: the slide >= threshold and have <= threshold */
+ memcpy(waveform_buffer, (void *)waveform_buffer + slide, have);
+ }
+ }
+
+ waveform_buffer_have = have;
+}
+
+/* where the samples are obtained and buffered */
+static void waveform_buffer_callback(const void *start, size_t size)
+{
+ size_t threshold = waveform_buffer_threshold;
+ size_t have = waveform_buffer_have;
+
+ if (have >= threshold)
{
- rb->lcd_update();
+ waveform_buffer_break += size;
+ return;
+ }
+
+ if (waveform_buffer_break > 0)
+ {
+ /* Previosly missed a part or all of a frame and the break would
+ happen within the data threshold area. Start where frame would
+ end up if all had been processed fully. This might mean a period
+ of resynchronization will have to happen first before the buffer
+ is filled to the threshold or even begins filling. Maintaining
+ scan phase relationship is important to proper appearance or else
+ the waveform display looks sloppy. */
+ size_t brk = have + waveform_buffer_break;
+ waveform_buffer_have = have = 0;
+
+ brk %= threshold;
+
+ if (brk != 0)
+ {
+ brk += size;
+
+ if (brk <= threshold)
+ {
+ waveform_buffer_break = brk;
+ return;
+ }
+
+ brk -= threshold;
+ start += size - brk;
+ size = brk;
+ }
+
+ waveform_buffer_break = 0;
+ }
+
+ size_t remaining = sizeof (waveform_buffer) - have;
+ size_t copy = size;
+
+ if (copy > remaining)
+ {
+ waveform_buffer_break = copy - remaining;
+ copy = remaining;
+ }
+
+ memcpy((void *)waveform_buffer + have, start, copy);
+
+ waveform_buffer_have = have + copy;
+}
+
+static void waveform_buffer_reset(void)
+{
+ /* only called when callback is off */
+ waveform_buffer_have = 0;
+ waveform_buffer_threshold = 0;
+ waveform_buffer_break = 0;
+}
+
+static void anim_waveform_plot_filled_h(int x, int x_prev,
+ int vmin, int vmax,
+ int vmin_prev, int vmax_prev)
+{
+ if (vmin != vmax || vmin_prev != vmax_prev)
+ {
+ /* Graph compression */
+ if (vmax > vmin_prev)
+ vmax = vmin_prev;
+ else if (vmin < vmax_prev)
+ vmin = vmax_prev;
+
+ rb->lcd_vline(x, vmax, vmin);
}
else
{
-#if LCD_DEPTH > 1 /* cursor bar */
- rb->lcd_set_foreground(CURSOR_COLOR);
- rb->lcd_hline(0, LCD_WIDTH-1, cur_y + 1);
- rb->lcd_set_foreground(GRAPH_COLOR);
-#else
- rb->lcd_set_drawmode(DRMODE_COMPLEMENT);
- rb->lcd_hline(0, LCD_WIDTH-1, cur_y + 1);
- rb->lcd_set_drawmode(DRMODE_SOLID);
+ /* 1:1 or stretch */
+ rb->lcd_drawline(x_prev, vmin_prev, x, vmin);
+ }
+}
+
+static void anim_waveform_plot_lines_h(int x, int x_prev,
+ int vmin, int vmax,
+ int vmin_prev, int vmax_prev)
+{
+ rb->lcd_drawline(x_prev, vmin_prev, x, vmin);
+
+ if (vmax_prev != vmin_prev || vmax != vmin)
+ rb->lcd_drawline(x_prev, vmax_prev, x, vmax);
+}
+
+static void anim_waveform_plot_pixel_h(int x, int x_prev,
+ int vmin, int vmax,
+ int vmin_prev, int vmax_prev)
+{
+ rb->lcd_drawpixel(x, vmin);
+
+ if (vmax != vmin)
+ rb->lcd_drawpixel(x, vmax);
+
+ (void)x_prev; (void)vmin_prev; (void)vmax_prev;
+}
+
+static long anim_waveform_horizontal(void)
+{
+ static void (* const plot[MAX_DRAW])(int, int, int, int, int, int) =
+ {
+ [DRAW_FILLED] = anim_waveform_plot_filled_h,
+ [DRAW_LINE] = anim_waveform_plot_lines_h,
+ [DRAW_PIXEL] = anim_waveform_plot_pixel_h,
+ };
+
+ long cur_tick = *rb->current_tick;
+
+ if (rb->mixer_channel_status(PCM_MIXER_CHAN_PLAYBACK) != CHANNEL_PLAYING)
+ {
+ osd_lcd_update_prepare();
+ rb->lcd_hline(0, LCD_WIDTH-1, 1*LCD_HEIGHT/4);
+ rb->lcd_hline(0, LCD_WIDTH-1, 3*LCD_HEIGHT/4);
+ osd_lcd_update();
+ one_frame_paused = false;
+ return cur_tick + HZ/5;
+ }
+
+ int count = (NATIVE_FREQUENCY*osc_delay + 100*HZ - 1) / (100*HZ);
+
+ waveform_buffer_set_threshold(count*PCM_SAMPLESIZE);
+
+ if (!waveform_buffer_have_enough())
+ return cur_tick + HZ/100;
+
+ one_frame_paused = false;
+
+ osd_lcd_update_prepare();
+
+ rb->lcd_set_drawmode(DRMODE_SOLID|DRMODE_INVERSEVID);
+ rb->lcd_fillrect(0, 0, LCD_WIDTH, LCD_HEIGHT);
+ rb->lcd_set_drawmode(DRMODE_SOLID);
+#if LCD_DEPTH > 1
+ rb->lcd_set_foreground(GRAPH_COLOR);
#endif
- if (cur_y > last_pos)
+ int x = 0, x_prev = count - 1;
+ int x_step = (LCD_WIDTH-1) / x_prev;
+ int x_a_step = (LCD_WIDTH-1) - x_step * x_prev;
+ int x_a = 0;
+
+ int idx_step = (count / LCD_WIDTH) * 2;
+ int idx_a_step = count - idx_step * (LCD_WIDTH/2);
+ int idx = idx_step, idx_prev = 0;
+ int idx_a = idx_a_step;
+
+ int a_lim = MIN(x_prev, LCD_WIDTH-1);
+
+ int lmin, lmin_prev = 0;
+ int rmin, rmin_prev = 0;
+ int lmax, lmax_prev = 0;
+ int rmax, rmax_prev = 0;
+
+ if (osc.draw == DRAW_PIXEL)
+ goto plot_start_noprev; /* Doesn't need previous points */
+
+ lmax = lmin = waveform_buffer[0];
+ rmax = rmin = waveform_buffer[1];
+
+ /* Find min-max envelope for interval */
+ for (int i = 2; i < idx; i += 2)
+ {
+ int sl = waveform_buffer[i + 0];
+ int sr = waveform_buffer[i + 1];
+
+ if (sl < lmin)
+ lmin = sl;
+ if (sl > lmax)
+ lmax = sl;
+
+ if (sr < rmin)
+ rmin = sr;
+ if (sr > rmax)
+ rmax = sr;
+ }
+
+ lmin = (1*LCD_HEIGHT/4) - WAVEFORM_SCALE_PCM(LCD_HEIGHT/4-1, lmin);
+ lmax = (1*LCD_HEIGHT/4) - WAVEFORM_SCALE_PCM(LCD_HEIGHT/4-1, lmax);
+ rmin = (3*LCD_HEIGHT/4) - WAVEFORM_SCALE_PCM(LCD_HEIGHT/4-1, rmin);
+ rmax = (3*LCD_HEIGHT/4) - WAVEFORM_SCALE_PCM(LCD_HEIGHT/4-1, rmax);
+
+ while (1)
+ {
+ x_prev = x;
+ x += x_step;
+ x_a += x_a_step;
+
+ if (x_a >= a_lim)
{
- rb->lcd_update_rect(0, last_pos, LCD_WIDTH, cur_y - last_pos + 2);
+ x_a -= a_lim;
+ x++;
}
- else
+
+ if (x >= LCD_WIDTH)
+ break;
+
+ idx_prev = idx;
+ idx += idx_step;
+ idx_a += idx_a_step;
+
+ if (idx_a > a_lim)
{
- rb->lcd_update_rect(0, last_pos, LCD_WIDTH, LCD_HEIGHT - last_pos);
- rb->lcd_update_rect(0, 0, LCD_WIDTH, cur_y + 2);
+ idx_a -= a_lim + 1;
+ idx += 2;
}
+
+ lmin_prev = lmin, lmax_prev = lmax;
+ rmin_prev = rmin, rmax_prev = rmax;
+
+ plot_start_noprev:
+ lmax = lmin = waveform_buffer[idx_prev + 0];
+ rmax = rmin = waveform_buffer[idx_prev + 1];
+
+ /* Find min-max envelope for interval */
+ for (int i = idx_prev + 2; i < idx; i += 2)
+ {
+ int sl = waveform_buffer[i + 0];
+ int sr = waveform_buffer[i + 1];
+
+ if (sl < lmin)
+ lmin = sl;
+ if (sl > lmax)
+ lmax = sl;
+
+ if (sr < rmin)
+ rmin = sr;
+ if (sr > rmax)
+ rmax = sr;
+ }
+
+ lmin = (1*LCD_HEIGHT/4) - WAVEFORM_SCALE_PCM(LCD_HEIGHT/4-1, lmin);
+ lmax = (1*LCD_HEIGHT/4) - WAVEFORM_SCALE_PCM(LCD_HEIGHT/4-1, lmax);
+ rmin = (3*LCD_HEIGHT/4) - WAVEFORM_SCALE_PCM(LCD_HEIGHT/4-1, rmin);
+ rmax = (3*LCD_HEIGHT/4) - WAVEFORM_SCALE_PCM(LCD_HEIGHT/4-1, rmax);
+
+ plot[osc.draw](x, x_prev, lmin, lmax, lmin_prev, lmax_prev);
+ plot[osc.draw](x, x_prev, rmin, rmax, rmin_prev, rmax_prev);
}
- last_pos = cur_y;
+
+ waveform_buffer_done();
+
+ osd_lcd_update();
+
+ long delay = get_next_delay();
+ return cur_tick + delay - waveform_buffer_have * HZ / PCM_BYTERATE;
+}
+
+static void anim_waveform_plot_filled_v(int y, int y_prev,
+ int vmin, int vmax,
+ int vmin_prev, int vmax_prev)
+{
+ if (vmin != vmax || vmin_prev != vmax_prev)
+ {
+ /* Graph compression */
+ if (vmax < vmin_prev)
+ vmax = vmin_prev;
+ else if (vmin > vmax_prev)
+ vmin = vmax_prev;
+
+ rb->lcd_hline(vmin, vmax, y);
+ }
+ else
+ {
+ /* 1:1 or stretch */
+ rb->lcd_drawline(vmin_prev, y_prev, vmin, y);
+ }
+}
+
+static void anim_waveform_plot_lines_v(int y, int y_prev,
+ int vmin, int vmax,
+ int vmin_prev, int vmax_prev)
+{
+ rb->lcd_drawline(vmin_prev, y_prev, vmin, y);
+
+ if (vmax_prev != vmin_prev || vmax != vmin)
+ rb->lcd_drawline(vmax_prev, y_prev, vmax, y);
+}
+
+static void anim_waveform_plot_pixel_v(int y, int y_prev,
+ int vmin, int vmax,
+ int vmin_prev, int vmax_prev)
+{
+ rb->lcd_drawpixel(vmin, y);
+
+ if (vmax != vmin)
+ rb->lcd_drawpixel(vmax, y);
+
+ (void)y_prev; (void)vmin_prev; (void)vmax_prev;
+}
+
+static long anim_waveform_vertical(void)
+{
+ static void (* const plot[MAX_DRAW])(int, int, int, int, int, int) =
+ {
+ [DRAW_FILLED] = anim_waveform_plot_filled_v,
+ [DRAW_LINE] = anim_waveform_plot_lines_v,
+ [DRAW_PIXEL] = anim_waveform_plot_pixel_v,
+ };
+
+ long cur_tick = *rb->current_tick;
+
+ if (rb->mixer_channel_status(PCM_MIXER_CHAN_PLAYBACK) != CHANNEL_PLAYING)
+ {
+ osd_lcd_update_prepare();
+ rb->lcd_vline(1*LCD_WIDTH/4, 0, LCD_HEIGHT-1);
+ rb->lcd_vline(3*LCD_WIDTH/4, 0, LCD_HEIGHT-1);
+ osd_lcd_update();
+ one_frame_paused = false;
+ return cur_tick + HZ/5;
+ }
+
+ int count = (NATIVE_FREQUENCY*osc_delay + 100*HZ - 1) / (100*HZ);
+
+ waveform_buffer_set_threshold(count*PCM_SAMPLESIZE);
+
+ if (!waveform_buffer_have_enough())
+ return cur_tick + HZ/100;
+
+ one_frame_paused = false;
+
+ osd_lcd_update_prepare();
+
+ rb->lcd_set_drawmode(DRMODE_SOLID|DRMODE_INVERSEVID);
+ rb->lcd_fillrect(0, 0, LCD_WIDTH, LCD_HEIGHT);
+ rb->lcd_set_drawmode(DRMODE_SOLID);
+#if LCD_DEPTH > 1
+ rb->lcd_set_foreground(GRAPH_COLOR);
+#endif
+
+ int y = 0, y_prev = count - 1;
+ int y_step = (LCD_HEIGHT-1) / y_prev;
+ int y_a_step = (LCD_HEIGHT-1) - y_step * y_prev;
+ int y_a = 0;
+
+ int idx_step = (count / LCD_HEIGHT) * 2;
+ int idx_a_step = count - idx_step * (LCD_HEIGHT/2);
+ int idx = idx_step, idx_prev = 0;
+ int idx_a = idx_a_step;
+
+ int a_lim = MIN(y_prev, LCD_HEIGHT-1);
+
+ int lmin, lmin_prev = 0;
+ int rmin, rmin_prev = 0;
+ int lmax, lmax_prev = 0;
+ int rmax, rmax_prev = 0;
+
+ if (osc.draw == DRAW_PIXEL)
+ goto plot_start_noprev; /* Doesn't need previous points */
+
+ lmax = lmin = waveform_buffer[0];
+ rmax = rmin = waveform_buffer[1];
+
+ /* Find min-max envelope for interval */
+ for (int i = 2; i < idx; i += 2)
+ {
+ int sl = waveform_buffer[i + 0];
+ int sr = waveform_buffer[i + 1];
+
+ if (sl < lmin)
+ lmin = sl;
+ if (sl > lmax)
+ lmax = sl;
+
+ if (sr < rmin)
+ rmin = sr;
+ if (sr > rmax)
+ rmax = sr;
+ }
+
+ lmin = (1*LCD_WIDTH/4) + WAVEFORM_SCALE_PCM(LCD_WIDTH/4-1, lmin);
+ lmax = (1*LCD_WIDTH/4) + WAVEFORM_SCALE_PCM(LCD_WIDTH/4-1, lmax);
+ rmin = (3*LCD_WIDTH/4) + WAVEFORM_SCALE_PCM(LCD_WIDTH/4-1, rmin);
+ rmax = (3*LCD_WIDTH/4) + WAVEFORM_SCALE_PCM(LCD_WIDTH/4-1, rmax);
+
+ while (1)
+ {
+ y_prev = y;
+ y += y_step;
+ y_a += y_a_step;
+
+ if (y_a >= a_lim)
+ {
+ y_a -= a_lim;
+ y++;
+ }
+
+ if (y >= LCD_HEIGHT)
+ break;
+
+ idx_prev = idx;
+ idx += idx_step;
+ idx_a += idx_a_step;
+
+ if (idx_a > a_lim)
+ {
+ idx_a -= a_lim + 1;
+ idx += 2;
+ }
+
+ lmin_prev = lmin, lmax_prev = lmax;
+ rmin_prev = rmin, rmax_prev = rmax;
+
+ plot_start_noprev:
+ lmax = lmin = waveform_buffer[idx_prev + 0];
+ rmax = rmin = waveform_buffer[idx_prev + 1];
+
+ /* Find min-max envelope for interval */
+ for (int i = idx_prev + 2; i < idx; i += 2)
+ {
+ int sl = waveform_buffer[i + 0];
+ int sr = waveform_buffer[i + 1];
+
+ if (sl < lmin)
+ lmin = sl;
+ if (sl > lmax)
+ lmax = sl;
+
+ if (sr < rmin)
+ rmin = sr;
+ if (sr > rmax)
+ rmax = sr;
+ }
+
+ lmin = (1*LCD_WIDTH/4) + WAVEFORM_SCALE_PCM(LCD_WIDTH/4-1, lmin);
+ lmax = (1*LCD_WIDTH/4) + WAVEFORM_SCALE_PCM(LCD_WIDTH/4-1, lmax);
+ rmin = (3*LCD_WIDTH/4) + WAVEFORM_SCALE_PCM(LCD_WIDTH/4-1, rmin);
+ rmax = (3*LCD_WIDTH/4) + WAVEFORM_SCALE_PCM(LCD_WIDTH/4-1, rmax);
+
+ plot[osc.draw](y, y_prev, lmin, lmax, lmin_prev, lmax_prev);
+ plot[osc.draw](y, y_prev, rmin, rmax, rmin_prev, rmax_prev);
+ }
+
+ waveform_buffer_done();
+
+ osd_lcd_update();
+
+ long delay = get_next_delay();
+ return cur_tick + delay - waveform_buffer_have * HZ / PCM_BYTERATE;
+}
+
+static void anim_waveform_exit(void)
+{
+ /* Remove any buffer hook */
+ rb->mixer_channel_set_buffer_hook(PCM_MIXER_CHAN_PLAYBACK, NULL);
+#ifdef HAVE_SCHEDULER_BOOSTCTRL
+ /* Remove our boost */
+ rb->cancel_cpu_boost();
+#endif
+}
+#endif /* OSCILLOSCOPE_GRAPHMODE */
+
+static void graphmode_reset(void)
+{
+ static long (* const fns[MAX_GRAPH][MAX_OSC])(void) =
+ {
+ [GRAPH_PEAKS] =
+ {
+ [OSC_HORIZ] = anim_peaks_horizontal,
+ [OSC_VERT] = anim_peaks_vertical,
+ },
+#ifdef OSCILLOSCOPE_GRAPHMODE
+ [GRAPH_WAVEFORM] =
+ {
+ [OSC_HORIZ] = anim_waveform_horizontal,
+ [OSC_VERT] = anim_waveform_vertical,
+ },
+#endif /* OSCILLOSCOPE_GRAPHMODE */
+ };
+
+ /* For peaks view */
+ last_left = 0;
+ last_right = 0;
+ last_pos = 0;
+ last_tick = *rb->current_tick;
+ last_delay = 1;
+
+ /* General */
+ osd_lcd_update_prepare();
+ rb->lcd_clear_display();
+ osd_lcd_update();
+
+ one_frame_paused = true;
+ osd_set_timeout(paused ? 4*HZ : HZ);
+ anim_draw = fns[osc.graphmode][osc.orientation];
+}
+
+static void graphmode_pause_unpause(bool paused)
+{
+ if (paused)
+ return;
+
+ last_tick = *rb->current_tick;
+ osd_set_timeout(HZ);
+ one_frame_paused = false;
+#ifdef OSCILLOSCOPE_GRAPHMODE
+ if (osc.graphmode == GRAPH_WAVEFORM)
+ waveform_buffer_done();
+#endif /* OSCILLOSCOPE_GRAPHMODE */
+}
+
+static void update_osc_delay(int value)
+{
+ osc_delay = osc_delay_tbl[value - 1];
+ osc_delay_error = 0;
}
-static void cleanup(void)
+static void graphmode_setup(void)
{
+#ifdef OSCILLOSCOPE_GRAPHMODE
+ if (osc.graphmode == GRAPH_WAVEFORM)
+ {
+ rb->mixer_channel_set_buffer_hook(PCM_MIXER_CHAN_PLAYBACK,
+ waveform_buffer_callback);
+#ifdef HAVE_SCHEDULER_BOOSTCTRL
+ rb->trigger_cpu_boost(); /* Just looks better */
+#endif
+ }
+ else
+ {
+ rb->mixer_channel_set_buffer_hook(PCM_MIXER_CHAN_PLAYBACK,
+ NULL);
+#ifdef HAVE_SCHEDULER_BOOSTCTRL
+ rb->cancel_cpu_boost();
+#endif
+ waveform_buffer_reset();
+ }
+#endif /* OSCILLOSCOPE_GRAPHMODE */
+
+ update_osc_delay(osc.speed[osc.graphmode]);
+ graphmode_reset();
+}
+
+static long oscilloscope_draw(void)
+{
+ if (message != -1)
+ {
+ osc_osd_show_message((enum osc_message_ids)message, msgval);
+ message = -1;
+ }
+ else
+ {
+ osd_monitor_timeout();
+ }
+
+ long delay = HZ/5;
+
+ if (!paused || one_frame_paused)
+ {
+ delay = anim_draw() - *rb->current_tick;
+
+ if (delay > HZ/5)
+ delay = HZ/5;
+ }
+
+ return delay;
+}
+
+static void osc_cleanup(void)
+{
+ osd_init(NULL, 0, NULL);
+
+#ifdef OSCILLOSCOPE_GRAPHMODE
+ anim_waveform_exit();
+#endif
+
#if LCD_DEPTH > 1
rb->lcd_set_foreground(LCD_DEFAULT_FG);
rb->lcd_set_background(LCD_DEFAULT_BG);
#endif
/* Turn on backlight timeout (revert to settings) */
backlight_use_settings();
-}
-enum plugin_status plugin_start(const void* parameter)
-{
- int button, vol;
-#if defined(OSCILLOSCOPE_DRAWMODE_PRE) || defined(OSCILLOSCOPE_ORIENTATION_PRE)
- int lastbutton = BUTTON_NONE;
-#endif
- bool exit = false;
- bool paused = false;
- bool tell_speed;
- (void)parameter;
+ /* save settings if changed */
+ if (rb->memcmp(&osc, &osc_disk, sizeof(osc)))
+ {
+ rb->memcpy(&osc_disk, &osc, sizeof(osc));
+ configfile_save(cfg_filename, disk_config, ARRAYLEN(disk_config),
+ CFGFILE_VERSION);
+ }
+}
- atexit(cleanup);
- configfile_load(cfg_filename, disk_config,
- sizeof(disk_config) / sizeof(disk_config[0]),
+static void osc_setup(void)
+{
+ atexit(osc_cleanup);
+ configfile_load(cfg_filename, disk_config, ARRAYLEN(disk_config),
CFGFILE_MINVERSION);
- rb->memcpy(&osc, &osc_disk, sizeof(osc)); /* copy to running config */
+ osc = osc_disk; /* copy to running config */
+
+ osc_osd_init();
#if LCD_DEPTH > 1
+ osd_lcd_update_prepare();
rb->lcd_set_foreground(GRAPH_COLOR);
rb->lcd_set_background(BACKG_COLOR);
rb->lcd_set_backdrop(NULL);
rb->lcd_clear_display();
- rb->lcd_update();
+ osd_lcd_update();
#endif
/* Turn off backlight timeout */
backlight_ignore_timeout();
+ graphmode_setup();
+}
+
+enum plugin_status plugin_start(const void* parameter)
+{
+ bool exit = false;
+#ifdef NEED_LASTBUTTON
+ int lastbutton = BUTTON_NONE;
+#endif
- rb->lcd_getstringsize("A", NULL, &font_height);
+ osc_setup();
while (!exit)
{
- if (!paused)
- {
- int left, right;
-
- rb->sleep(MAX(last_tick + osc.delay - *rb->current_tick - 1, 0));
+ long delay = oscilloscope_draw();
-#if (CONFIG_CODEC == MAS3587F) || (CONFIG_CODEC == MAS3539F)
- left = rb->mas_codec_readreg(0xC);
- right = rb->mas_codec_readreg(0xD);
-#elif (CONFIG_CODEC == SWCODEC)
- static struct pcm_peaks peaks;
- rb->mixer_channel_calculate_peaks(PCM_MIXER_CHAN_PLAYBACK,
- &peaks);
- left = peaks.left; right = peaks.right;
-#endif
- if (osc.orientation == OSC_HORIZ)
- anim_horizontal(left, right);
- else
- anim_vertical(left, right);
+ if (delay <= 0)
+ {
+ delay = 0;
+ rb->yield(); /* tmo = 0 won't yield */
}
- tell_speed = false;
- button = rb->button_get(paused);
+ int button = rb->button_get_w_tmo(delay);
+
switch (button)
{
#ifdef OSCILLOSCOPE_RC_QUIT
@@ -904,8 +1909,14 @@ enum plugin_status plugin_start(const void* parameter)
break;
case OSCILLOSCOPE_ADVMODE:
+#ifdef OSCILLOSCOPE_GRAPHMODE
+ if (osc.graphmode == GRAPH_WAVEFORM)
+ break; /* Not applicable */
+#endif
if (++osc.advance >= MAX_ADV)
osc.advance = 0;
+
+ osc_popupmsg(OSC_MSG_ADVMODE, osc.advance);
break;
case OSCILLOSCOPE_DRAWMODE:
@@ -915,7 +1926,26 @@ enum plugin_status plugin_start(const void* parameter)
#endif
if (++osc.draw >= MAX_DRAW)
osc.draw = 0;
+
+ osc_popupmsg(OSC_MSG_DRAWMODE, osc.draw);
break;
+
+ /* Need all keymaps for this (remove extra condition when
+ completed) */
+#ifdef OSCILLOSCOPE_GRAPHMODE
+ case OSCILLOSCOPE_GRAPHMODE:
+#ifdef OSCILLOSCOPE_GRAPHMODE_PRE
+ if (lastbutton != OSCILLOSCOPE_GRAPHMODE_PRE)
+ break;
+#endif
+ if (++osc.graphmode >= MAX_GRAPH)
+ osc.graphmode = 0;
+
+ graphmode_setup();
+
+ osc_popupmsg(OSC_MSG_GRAPHMODE, osc.graphmode);
+ break;
+#endif /* OSCILLOSCOPE_GRAPHMODE */
case OSCILLOSCOPE_ORIENTATION:
#ifdef OSCILLOSCOPE_ORIENTATION_PRE
@@ -924,78 +1954,90 @@ enum plugin_status plugin_start(const void* parameter)
#endif
if (++osc.orientation >= MAX_OSC)
osc.orientation = 0;
- last_pos = 0;
- last_tick = 0;
- displaymsg = false;
- rb->lcd_clear_display();
- rb->lcd_update();
+
+ graphmode_reset();
+ osc_popupmsg(OSC_MSG_ORIENTATION, osc.orientation);
break;
case OSCILLOSCOPE_PAUSE:
+#ifdef OSCILLOSCOPE_PAUSE_PRE
+ if (lastbutton != OSCILLOSCOPE_PAUSE_PRE)
+ break;
+#endif
paused = !paused;
- last_tick = 0;
+ graphmode_pause_unpause(paused);
+ osc_popupmsg(OSC_MSG_PAUSED, paused ? 1 : 0);
break;
case OSCILLOSCOPE_SPEED_UP:
case OSCILLOSCOPE_SPEED_UP | BUTTON_REPEAT:
- if (osc.delay > 1)
- {
- osc.delay--;
- tell_speed = true;
- }
+ {
+ int *val = &osc.speed[osc.graphmode];
+
+ if (*val < MAX_SPEED)
+ ++*val;
+
+ update_osc_delay(*val);
+ osc_popupmsg(OSC_MSG_SPEED, *val);
break;
+ }
case OSCILLOSCOPE_SPEED_DOWN:
case OSCILLOSCOPE_SPEED_DOWN | BUTTON_REPEAT:
- osc.delay++;
- tell_speed = true;
+ {
+ int *val = &osc.speed[osc.graphmode];
+
+ if (*val > MIN_SPEED)
+ --*val;
+
+ update_osc_delay(*val);
+ osc_popupmsg(OSC_MSG_SPEED, *val);
break;
+ }
case OSCILLOSCOPE_VOL_UP:
case OSCILLOSCOPE_VOL_UP | BUTTON_REPEAT:
- vol = rb->global_settings->volume;
+ {
+ int vol = rb->global_settings->volume;
+
if (vol < rb->sound_max(SOUND_VOLUME))
{
vol++;
rb->sound_set(SOUND_VOLUME, vol);
rb->global_settings->volume = vol;
}
+
+ osc_popupmsg(OSC_MSG_VOLUME, vol);
break;
+ }
case OSCILLOSCOPE_VOL_DOWN:
case OSCILLOSCOPE_VOL_DOWN | BUTTON_REPEAT:
- vol = rb->global_settings->volume;
+ {
+ int vol = rb->global_settings->volume;
+
if (vol > rb->sound_min(SOUND_VOLUME))
{
vol--;
rb->sound_set(SOUND_VOLUME, vol);
rb->global_settings->volume = vol;
}
+
+ osc_popupmsg(OSC_MSG_VOLUME, vol);
break;
+ }
default:
exit_on_usb(button);
break;
}
-#if defined(OSCILLOSCOPE_DRAWMODE_PRE) || defined(OSCILLOSCOPE_ORIENTATION_PRE)
+
+#ifdef NEED_LASTBUTTON
if (button != BUTTON_NONE)
lastbutton = button;
#endif
+ } /* while */
- if (tell_speed)
- {
- rb->snprintf(message, sizeof(message), "%s%d",
- (osc.orientation == OSC_VERT) ? "Speed: " : "",
- 100 / osc.delay);
- displaymsg = true;
- }
- }
- if (rb->memcmp(&osc, &osc_disk, sizeof(osc))) /* save settings if changed */
- {
- rb->memcpy(&osc_disk, &osc, sizeof(osc));
- configfile_save(cfg_filename, disk_config,
- sizeof(disk_config) / sizeof(disk_config[0]),
- CFGFILE_VERSION);
- }
return PLUGIN_OK;
+ (void)parameter;
}
diff --git a/firmware/export/pcm_mixer.h b/firmware/export/pcm_mixer.h
index 69d2b894df..d424083002 100644
--- a/firmware/export/pcm_mixer.h
+++ b/firmware/export/pcm_mixer.h
@@ -106,7 +106,8 @@ enum channel_status mixer_channel_status(enum pcm_mixer_channel channel);
size_t mixer_channel_get_bytes_waiting(enum pcm_mixer_channel channel);
/* Return pointer to channel's playing audio data and the size remaining */
-const void * mixer_channel_get_buffer(enum pcm_mixer_channel channel, int *count);
+const void * mixer_channel_get_buffer(enum pcm_mixer_channel channel,
+ int *count);
/* Calculate peak values for channel */
void mixer_channel_calculate_peaks(enum pcm_mixer_channel channel,
@@ -116,6 +117,13 @@ void mixer_channel_calculate_peaks(enum pcm_mixer_channel channel,
void mixer_adjust_channel_address(enum pcm_mixer_channel channel,
off_t offset);
+/* Set a hook that is called upon getting a new source buffer for a channel
+ NOTE: Called for each buffer, not each mixer chunk */
+typedef void (*chan_buffer_hook_fn_type)(const void *start, size_t size);
+
+void mixer_channel_set_buffer_hook(enum pcm_mixer_channel channel,
+ chan_buffer_hook_fn_type fn);
+
/* Stop ALL channels and PCM and reset state */
void mixer_reset(void);
diff --git a/firmware/pcm_mixer.c b/firmware/pcm_mixer.c
index cddff431ec..34852e97e9 100644
--- a/firmware/pcm_mixer.c
+++ b/firmware/pcm_mixer.c
@@ -45,6 +45,7 @@ struct mixer_channel
pcm_play_callback_type get_more; /* Registered callback */
enum channel_status status; /* Playback status */
uint32_t amplitude; /* Amp. factor: 0x0000 = mute, 0x10000 = unity */
+ chan_buffer_hook_fn_type buffer_hook; /* Callback for new buffer */
};
/* Forget about boost here for the moment */
@@ -106,6 +107,12 @@ static void mixer_pcm_callback(const void **addr, size_t *size)
*size = next_size;
}
+static inline void chan_call_buffer_hook(struct mixer_channel *chan)
+{
+ if (UNLIKELY(chan->buffer_hook))
+ chan->buffer_hook(chan->start, chan->size);
+}
+
/* Buffering callback - calls sub-callbacks and mixes the data for next
buffer to be sent from mixer_pcm_callback() */
static enum pcm_dma_status MIXER_CALLBACK_ICODE
@@ -149,6 +156,8 @@ fill_frame:
channel_stopped(chan);
continue;
}
+
+ chan_call_buffer_hook(chan);
}
/* Channel will play for at least part of this frame */
@@ -298,6 +307,7 @@ void mixer_channel_play_data(enum pcm_mixer_channel channel,
chan->get_more = get_more;
mixer_activate_channel(chan);
+ chan_call_buffer_hook(chan);
mixer_start_pcm();
}
else
@@ -406,6 +416,18 @@ void mixer_adjust_channel_address(enum pcm_mixer_channel channel,
pcm_play_unlock();
}
+/* Set a hook that is called upon getting a new source buffer for a channel
+ NOTE: Called for each buffer, not each mixer chunk */
+void mixer_channel_set_buffer_hook(enum pcm_mixer_channel channel,
+ chan_buffer_hook_fn_type fn)
+{
+ struct mixer_channel *chan = &channels[channel];
+
+ pcm_play_lock();
+ chan->buffer_hook = fn;
+ pcm_play_unlock();
+}
+
/* Stop ALL channels and PCM and reset state */
void mixer_reset(void)
{