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(); + ** + ** 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 }; - -#define CFGFILE_VERSION 0 /* Current config file version */ -#define CFGFILE_MINVERSION 0 /* Minimum config file version to accept */ - - -#define MAX_PEAK 0x8000 - -#if defined(SIMULATOR) && (CONFIG_CODEC != SWCODEC) -#define mas_codec_readreg(x) rand()%MAX_PEAK +enum +{ + GRAPH_PEAKS, +#ifdef OSCILLOSCOPE_GRAPHMODE + GRAPH_WAVEFORM, #endif + MAX_GRAPH +}; + +#define CFGFILE_VERSION 1 /* Current config file version */ +#define CFGFILE_MINVERSION 0 /* Minimum config file version to accept */ /* global variables */ +/* + * / 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 +}; + +#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) -{ - int cur_x, x; - int left, right, dl, dr; - long cur_tick = *rb->current_tick; - long d = (cur_tick - last_tick) / osc.delay; - bool full_update = false; - if (d == 0) /* too early, bail out */ +/** 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 d = (cur_tick - last_tick) / last_delay; + bool full_update = false; + + 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) - { - last_pos -= font_height - 1; - rb->lcd_putsxy(0, last_pos, 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_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); -#endif + anim_draw_cursor_v(cur_y + 1); /* cursor bar */ if (cur_y > last_pos) { - rb->lcd_update_rect(0, last_pos, LCD_WIDTH, cur_y - last_pos + 2); + osd_lcd_update_rect(0, last_pos, LCD_WIDTH, cur_y - last_pos + 2); } else { - rb->lcd_update_rect(0, last_pos, LCD_WIDTH, LCD_HEIGHT - last_pos); - rb->lcd_update_rect(0, 0, LCD_WIDTH, cur_y + 2); + 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; + + last_delay = get_next_delay(); + return cur_tick + last_delay; } -static void cleanup(void) + +/** 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) + { + 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 + { + /* 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 + + 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) + { + x_a -= a_lim; + x++; + } + + if (x >= LCD_WIDTH) + 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_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); + } + + 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 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(); + + + /* 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); + } } -enum plugin_status plugin_start(const void* parameter) +static void osc_setup(void) { - 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; - - atexit(cleanup); - configfile_load(cfg_filename, disk_config, - sizeof(disk_config) / sizeof(disk_config[0]), + 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(); +} - rb->lcd_getstringsize("A", NULL, &font_height); +enum plugin_status plugin_start(const void* parameter) +{ + bool exit = false; +#ifdef NEED_LASTBUTTON + int lastbutton = BUTTON_NONE; +#endif + + osc_setup(); while (!exit) { - if (!paused) + long delay = oscilloscope_draw(); + + if (delay <= 0) { - int left, right; - - rb->sleep(MAX(last_tick + osc.delay - *rb->current_tick - 1, 0)); - -#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); + 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) {