From 88abdd97b25eb458466a84f614a518b7173265a8 Mon Sep 17 00:00:00 2001 From: Dan Everton Date: Tue, 7 Feb 2006 14:07:46 +0000 Subject: [PATCH] Add Equalizer configuration to Sound Settings menu. git-svn-id: svn://svn.rockbox.org/rockbox/trunk@8606 a1c6a512-1295-4272-9138-f99709370657 --- apps/SOURCES | 1 + apps/dsp.c | 61 +++- apps/dsp.h | 1 + apps/eq.c | 8 +- apps/eq_menu.c | 678 +++++++++++++++++++++++++++++++++++++++++ apps/eq_menu.h | 28 ++ apps/lang/english.lang | 91 ++++++ apps/onplay.c | 22 +- apps/settings.c | 23 ++ apps/settings.h | 34 +++ apps/sound_menu.c | 2 + tools/buildzip.pl | 1 + 12 files changed, 942 insertions(+), 8 deletions(-) create mode 100644 apps/eq_menu.c create mode 100644 apps/eq_menu.h diff --git a/apps/SOURCES b/apps/SOURCES index 35f78c455e..ef65789588 100644 --- a/apps/SOURCES +++ b/apps/SOURCES @@ -78,4 +78,5 @@ eq_cf.S #elif defined(CPU_ARM) && !defined(SIMULATOR) eq_arm.S #endif +eq_menu.c #endif diff --git a/apps/dsp.c b/apps/dsp.c index 789cf72b20..e4d28bd083 100644 --- a/apps/dsp.c +++ b/apps/dsp.c @@ -145,6 +145,7 @@ struct dsp_config bool dither_enabled; bool new_gain; bool crossfeed_enabled; + bool eq_enabled; }; struct resample_data @@ -618,6 +619,61 @@ static void apply_crossfeed(long* src[], int count) } #endif +#define EQ_CUTOFF_USER2REAL(x) (0xffffffff / NATIVE_FREQUENCY * (x)) +#define EQ_Q_USER2REAL(x) (((x) << 16) / 10) +#define EQ_GAIN_USER2REAL(x) (((x) << 16) / 10) + +/* Synchronize the EQ filters with the global settings */ +void dsp_eq_update_data(bool enabled) +{ + int i; + int *setting; + int gain, cutoff, q, maxgain; + + dsp->eq_enabled = enabled; + setting = &global_settings.eq_band0_cutoff; + maxgain = 0; + + #if defined(CPU_COLDFIRE) && !defined(SIMULATOR) + /* set emac unit for dsp processing, and save old macsr, we're running in + codec thread context at this point, so can't clobber it */ + unsigned long old_macsr = coldfire_get_macsr(); + coldfire_set_macsr(EMAC_FRACTIONAL | EMAC_SATURATE); + #endif + + /* Iterate over each band and update the appropriate filter */ + for(i = 0; i < 5; i++) { + cutoff = *setting++; + q = *setting++; + gain = *setting++; + + /* Keep track of maxgain for the pre-amp */ + if (gain > maxgain) + maxgain = gain; + + if (gain == 0) { + eq_data.enabled[i] = 0; + } else { + if (i == 0) + eq_ls_coefs(EQ_CUTOFF_USER2REAL(cutoff), EQ_Q_USER2REAL(q), + EQ_GAIN_USER2REAL(gain), eq_data.filters[0].coefs); + else if (i == 4) + eq_hs_coefs(EQ_CUTOFF_USER2REAL(cutoff), EQ_Q_USER2REAL(q), + EQ_GAIN_USER2REAL(gain), eq_data.filters[4].coefs); + else + eq_pk_coefs(EQ_CUTOFF_USER2REAL(cutoff), EQ_Q_USER2REAL(q), + EQ_GAIN_USER2REAL(gain), eq_data.filters[i].coefs); + + eq_data.enabled[i] = 1; + } + } + + #if defined(CPU_COLDFIRE) && !defined(SIMULATOR) + /* set old macsr again */ + coldfire_set_macsr(old_macsr); + #endif +} + /* Apply EQ filters to those bands that have got it switched on. */ static void eq_process(long **x, unsigned num) { @@ -745,9 +801,8 @@ long dsp_process(char* dst, char* src[], long size) samples = resample(tmp, samples); if (dsp->crossfeed_enabled && dsp->stereo_mode != STEREO_MONO) apply_crossfeed(tmp, samples); - /* TODO: Might want to wrap this with a generic eq_enabled when the - settings are in place */ - eq_process(tmp, samples); + if (dsp->eq_enabled) + eq_process(tmp, samples); write_samples((short*) dst, tmp, samples); written += samples; dst += samples * sizeof(short) * 2; diff --git a/apps/dsp.h b/apps/dsp.h index f86a4d9989..364c8d80f5 100644 --- a/apps/dsp.h +++ b/apps/dsp.h @@ -54,6 +54,7 @@ int dsp_stereo_mode(void); bool dsp_configure(int setting, void *value); void dsp_set_replaygain(bool always); void dsp_set_crossfeed(bool enable); +void dsp_eq_update_data(bool enabled); void sound_set_pitch(int r); int sound_get_pitch(void); #endif diff --git a/apps/eq.c b/apps/eq.c index 17165be541..7921028403 100644 --- a/apps/eq.c +++ b/apps/eq.c @@ -32,11 +32,11 @@ */ #define DIV64(x, y, z) (long)(((long long)(x) << (z))/(y)) -/* TODO: This macro requires the EMAC unit to be in fractional mode - when the coef generator routines are called. If this can be guaranteeed, - then remove the "&& 0" below for faster coef calculation on Coldfire. +/* This macro requires the EMAC unit to be in fractional mode + when the coef generator routines are called. If this can't be guaranteeed, + then add "&& 0" below. This will use a slower coef calculation on Coldfire. */ -#if defined(CPU_COLDFIRE) && !defined(SIMULATOR) && 0 +#if defined(CPU_COLDFIRE) && !defined(SIMULATOR) #define FRACMUL(x, y) \ ({ \ long t; \ diff --git a/apps/eq_menu.c b/apps/eq_menu.c new file mode 100644 index 0000000000..0e3355fcd9 --- /dev/null +++ b/apps/eq_menu.c @@ -0,0 +1,678 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright (C) 2006 Dan Everton + * + * All files in this archive are subject to the GNU General Public License. + * See the file COPYING in the source tree root for full license agreement. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ****************************************************************************/ + +#include "config.h" +#include +#include +#include +#include "eq_menu.h" +#include "system.h" +#include "kernel.h" +#include "lcd.h" +#include "menu.h" +#include "button.h" +#include "mp3_playback.h" +#include "settings.h" +#include "statusbar.h" +#include "screens.h" +#include "icons.h" +#include "font.h" +#include "widgets.h" +#include "lang.h" +#include "sprintf.h" +#include "talk.h" +#include "misc.h" +#include "sound.h" +#include "splash.h" +#include "dsp.h" +#include "tree.h" +#include "talk.h" +#include "screen_access.h" +#include "keyboard.h" + +/* Key definitions */ +#if (CONFIG_KEYPAD == IRIVER_H100_PAD || \ + CONFIG_KEYPAD == IRIVER_H300_PAD) + +#define EQ_BTN_NEXT_BAND BUTTON_DOWN +#define EQ_BTN_PREV_BAND BUTTON_UP +#define EQ_BTN_CHANGE_MODE BUTTON_SELECT +#define EQ_BTN_EXIT BUTTON_OFF + +#elif (CONFIG_KEYPAD == IPOD_4G_PAD) + +#define EQ_BTN_NEXT_BAND BUTTON_SCROLL_FWD +#define EQ_BTN_PREV_BAND BUTTON_SCROLL_BACK +#define EQ_BTN_CHANGE_MODE BUTTON_SELECT +#define EQ_BTN_EXIT BUTTON_MENU + +#endif + + +#define EQ_CUTOFF_MIN 20 +#define EQ_CUTOFF_MAX 22040 +#define EQ_CUTOFF_STEP 10 +#define EQ_GAIN_MIN (-240) +#define EQ_GAIN_MAX 240 +#define EQ_GAIN_STEP 1 +#define EQ_Q_MIN 5 +#define EQ_Q_MAX 64 +#define EQ_Q_STEP 1 + +#define EQ_USER_DIVISOR 10 + +static bool eq_enabled(void) +{ + bool result = set_bool(str(LANG_EQUALIZER_ENABLED), + &global_settings.eq_enabled); + + dsp_eq_update_data(global_settings.eq_enabled); + + return result; +} + +static void eq_gain_format(char* buffer, int buffer_size, int value, const char* unit) +{ + int v = abs(value); + + snprintf(buffer, buffer_size, "%s%d.%d %s", value < 0 ? "-" : "", + v / EQ_USER_DIVISOR, v % EQ_USER_DIVISOR, unit); +} + +static void eq_q_format(char* buffer, int buffer_size, int value, const char* unit) +{ + snprintf(buffer, buffer_size, "%d.%d %s", value / EQ_USER_DIVISOR, value % EQ_USER_DIVISOR, unit); +} + +/* Possibly dodgy way of simplifying the code a bit. */ +#define eq_make_gain_label(buf, bufsize, frequency) snprintf((buf), \ + (bufsize), str(LANG_EQUALIZER_GAIN_ITEM), (frequency)) + +#define eq_set_cutoff(band) \ +static bool eq_set_band ## band ## _cutoff(void) \ +{ \ + bool result = set_int(str(LANG_EQUALIZER_BAND_CUTOFF), "Hertz", UNIT_HERTZ, \ + &global_settings.eq_band ## band ## _cutoff, NULL, \ + EQ_CUTOFF_STEP, EQ_CUTOFF_MIN, EQ_CUTOFF_MAX, NULL); \ + dsp_eq_update_data(global_settings.eq_enabled); \ + return result; \ +} + +#define eq_set_q(band) \ +static bool eq_set_band ## band ## _q(void) \ +{ \ + bool result = set_int(str(LANG_EQUALIZER_BAND_Q), "Q", UNIT_INT, \ + &global_settings.eq_band ## band ## _q, NULL, \ + EQ_Q_STEP, EQ_Q_MIN, EQ_Q_MAX, eq_q_format); \ + dsp_eq_update_data(global_settings.eq_enabled); \ + return result; \ +} + +#define eq_set_gain(band) \ +static bool eq_set_band ## band ## _gain(void) \ +{ \ + bool result = set_int("Band " #band, str(LANG_UNIT_DB), UNIT_DB, \ + &global_settings.eq_band ## band ## _gain, NULL, \ + EQ_GAIN_STEP, EQ_GAIN_MIN, EQ_GAIN_MAX, eq_gain_format); \ + dsp_eq_update_data(global_settings.eq_enabled); \ + return result; \ +} + +eq_set_cutoff(0); +eq_set_cutoff(1); +eq_set_cutoff(2); +eq_set_cutoff(3); +eq_set_cutoff(4); + +eq_set_q(0); +eq_set_q(1); +eq_set_q(2); +eq_set_q(3); +eq_set_q(4); + +eq_set_gain(0); +eq_set_gain(1); +eq_set_gain(2); +eq_set_gain(3); +eq_set_gain(4); + +static bool eq_gain_menu(void) +{ + int m, i; + int *setting; + bool result; + char gain_label[5][24]; + static struct menu_item items[5] = { + { NULL, eq_set_band0_gain }, + { NULL, eq_set_band1_gain }, + { NULL, eq_set_band2_gain }, + { NULL, eq_set_band3_gain }, + { NULL, eq_set_band4_gain }, + }; + + setting = &global_settings.eq_band0_cutoff; + + /* Construct menu labels */ + for(i = 0; i < 5; i++) { + eq_make_gain_label(gain_label[i], sizeof(gain_label[i]), + *setting); + items[i].desc = gain_label[i]; + + /* Skip to next band */ + setting += 3; + } + + m=menu_init( items, sizeof(items) / sizeof(*items), NULL, + NULL, NULL, NULL); + result = menu_run(m); + menu_exit(m); + + return result; +} + +static bool eq_set_band0(void) +{ + int m; + bool result; + static const struct menu_item items[] = { + { ID2P(LANG_EQUALIZER_BAND_CUTOFF), eq_set_band0_cutoff }, + { ID2P(LANG_EQUALIZER_BAND_Q), eq_set_band0_q }, + { ID2P(LANG_EQUALIZER_BAND_GAIN), eq_set_band0_gain }, + }; + + m=menu_init( items, sizeof(items) / sizeof(*items), NULL, + NULL, NULL, NULL); + result = menu_run(m); + menu_exit(m); + + return result; +} + +static bool eq_set_band1(void) +{ + int m; + bool result; + static const struct menu_item items[] = { + { ID2P(LANG_EQUALIZER_BAND_CUTOFF), eq_set_band1_cutoff }, + { ID2P(LANG_EQUALIZER_BAND_Q), eq_set_band1_q }, + { ID2P(LANG_EQUALIZER_BAND_GAIN), eq_set_band1_gain }, + }; + + m=menu_init( items, sizeof(items) / sizeof(*items), NULL, + NULL, NULL, NULL); + result = menu_run(m); + menu_exit(m); + + return result; +} + +static bool eq_set_band2(void) +{ + int m; + bool result; + static const struct menu_item items[] = { + { ID2P(LANG_EQUALIZER_BAND_CUTOFF), eq_set_band2_cutoff }, + { ID2P(LANG_EQUALIZER_BAND_Q), eq_set_band2_q }, + { ID2P(LANG_EQUALIZER_BAND_GAIN), eq_set_band2_gain }, + }; + + m=menu_init( items, sizeof(items) / sizeof(*items), NULL, + NULL, NULL, NULL); + result = menu_run(m); + menu_exit(m); + + return result; +} + +static bool eq_set_band3(void) +{ + int m; + bool result; + static const struct menu_item items[] = { + { ID2P(LANG_EQUALIZER_BAND_CUTOFF), eq_set_band3_cutoff }, + { ID2P(LANG_EQUALIZER_BAND_Q), eq_set_band3_q }, + { ID2P(LANG_EQUALIZER_BAND_GAIN), eq_set_band3_gain }, + }; + + m=menu_init( items, sizeof(items) / sizeof(*items), NULL, + NULL, NULL, NULL); + result = menu_run(m); + menu_exit(m); + + return result; +} + +static bool eq_set_band4(void) +{ + int m; + bool result; + static const struct menu_item items[] = { + { ID2P(LANG_EQUALIZER_BAND_CUTOFF), eq_set_band4_cutoff }, + { ID2P(LANG_EQUALIZER_BAND_Q), eq_set_band4_q }, + { ID2P(LANG_EQUALIZER_BAND_GAIN), eq_set_band4_gain }, + }; + + m=menu_init( items, sizeof(items) / sizeof(*items), NULL, + NULL, NULL, NULL); + result = menu_run(m); + menu_exit(m); + + return result; +} + +static bool eq_advanced_menu(void) +{ + int m, i; + bool result; + char peak_band_label[3][32]; + static struct menu_item items[] = { + { ID2P(LANG_EQUALIZER_BAND_LOW_SHELF), eq_set_band0 }, + { NULL, eq_set_band1 }, + { NULL, eq_set_band2 }, + { NULL, eq_set_band3 }, + { ID2P(LANG_EQUALIZER_BAND_HIGH_SHELF), eq_set_band4 }, + }; + + /* Construct menu labels */ + for(i = 1; i < 4; i++) { + snprintf(peak_band_label[i-1], sizeof(peak_band_label[i-1]), + str(LANG_EQUALIZER_BAND_PEAK), i); + items[i].desc = peak_band_label[i-1]; + } + + m=menu_init( items, sizeof(items) / sizeof(*items), NULL, + NULL, NULL, NULL); + result = menu_run(m); + menu_exit(m); + + return result; +} + +#define SLIDER_KNOB_HEIGHT 6 +#define SLIDER_KNOB_WIDTH 4 + +enum eq_slider_mode { + GAIN, + CUTOFF, + Q, +}; + +enum eq_type { + LOW_SHELF, + PEAK, + HIGH_SHELF +}; + +/* Draw a slider */ +static void draw_slider(const struct screen * screen, int x, int y, + int width, int steps, int current_step) +{ + + int knob_x = (width / steps) * current_step + (SLIDER_KNOB_WIDTH / 2); + + /* Draw groove */ + screen->fillrect(x, y + 2, width, 2); + + /* Draw knob */ + screen->fillrect(x + knob_x, y, SLIDER_KNOB_WIDTH, SLIDER_KNOB_HEIGHT); +} + +/* Draw the UI for a whole EQ band */ +static int draw_eq_slider(const struct screen * screen, int x, int y, + int width, int cutoff, int q, int gain, bool selected, + enum eq_slider_mode mode, enum eq_type type) +{ + char buf[26]; + const char separator[2] = " "; + int steps = (abs(EQ_GAIN_MIN) + abs(EQ_GAIN_MAX)) / EQ_USER_DIVISOR; + int abs_q = abs(q); + int abs_gain = abs(gain); + int current_x, total_height, separator_width, separator_height; + int w, h; + + /* Start two pixels in, one for border, one for margin */ + current_x = x + 2; + + /* Figure out how large our separator string is */ + screen->getstringsize(separator, &separator_width, &separator_height); + + /* Total height includes margins, text, and line selector */ + total_height = separator_height + SLIDER_KNOB_HEIGHT + 2 + 3; + + /* Print out the band label */ + if (type == LOW_SHELF) { + screen->putsxy(current_x, y + 2, "LS:"); + screen->getstringsize("LS:", &w, &h); + } else if (type == HIGH_SHELF) { + screen->putsxy(current_x, y + 2, "HS:"); + screen->getstringsize("HS:", &w, &h); + } else { + screen->putsxy(current_x, y + 2, "PK:"); + screen->getstringsize("PK:", &w, &h); + } + current_x += w; + + /* Print separator */ + screen->set_drawmode(DRMODE_SOLID); + screen->putsxy(current_x, y + 2, separator); + current_x += separator_width; + + /* Print out gain part of status line */ + snprintf(buf, sizeof(buf), "%s%2d.%ddB", gain < 0 ? "-" : " ", + abs_gain / EQ_USER_DIVISOR, abs_gain % EQ_USER_DIVISOR); + + if (mode == GAIN && selected) + screen->set_drawmode(DRMODE_SOLID | DRMODE_INVERSEVID); + + screen->putsxy(current_x, y + 2, buf); + screen->getstringsize(buf, &w, &h); + current_x += w; + + /* Print separator */ + screen->set_drawmode(DRMODE_SOLID); + screen->putsxy(current_x, y + 2, separator); + current_x += separator_width; + + /* Print out cutoff part of status line */ + snprintf(buf, sizeof(buf), "%5dHz", cutoff); + + if (mode == CUTOFF && selected) + screen->set_drawmode(DRMODE_SOLID | DRMODE_INVERSEVID); + + screen->putsxy(current_x, y + 2, buf); + screen->getstringsize(buf, &w, &h); + current_x += w; + + /* Print separator */ + screen->set_drawmode(DRMODE_SOLID); + screen->putsxy(current_x, y + 2, separator); + current_x += separator_width; + + /* Print out Q part of status line */ + snprintf(buf, sizeof(buf), "%d.%d Q", abs_q / EQ_USER_DIVISOR, + abs_q % EQ_USER_DIVISOR); + + if (mode == Q && selected) + screen->set_drawmode(DRMODE_SOLID | DRMODE_INVERSEVID); + + screen->putsxy(current_x, y + 2, buf); + screen->getstringsize(buf, &w, &h); + current_x += w; + + screen->set_drawmode(DRMODE_SOLID); + + /* Draw selection box */ + if (selected) { + screen->drawrect(x, y, width, total_height); + } + + /* Draw horizontal slider */ + draw_slider(screen, x + 3, y + h + 3, width - 6, steps, + (EQ_GAIN_MAX + gain) / EQ_USER_DIVISOR); + + return total_height; +} + +/* Draw's all the EQ sliders. Returns the total height of the sliders drawn */ +static int draw_eq_sliders(int current_band, enum eq_slider_mode mode) +{ + int i, gain, q, cutoff; + int height = 2; /* Two pixel margin */ + int slider_width = screens[SCREEN_MAIN].width - 4; /* two pixel margin on each side */ + int *setting = &global_settings.eq_band0_cutoff; + enum eq_type type; + + for( i = 0; i < 5; ++i) + { + cutoff = *setting++; + q = *setting++; + gain = *setting++; + + if (i == 0) { + type = LOW_SHELF; + } else if (i == 4) { + type = HIGH_SHELF; + } else { + type = PEAK; + } + + height += draw_eq_slider(&(screens[SCREEN_MAIN]), 2, height, + slider_width, cutoff, q, gain, i == current_band, mode, type); + + /* add a margin */ + height += 2; + } + + return height; +} + +/* Provides a graphical means of editing the EQ settings */ +bool eq_menu_graphical(void) +{ + bool exit_request = false; + bool result = true; + bool has_changed = false; + int button; + int *setting; + int current_band, y, step, min, max, voice_unit; + enum eq_slider_mode mode; + enum eq_type current_type; + char buf[24]; + + screens[SCREEN_MAIN].setfont(FONT_SYSFIXED); + screens[SCREEN_MAIN].clear_display(); + + /* Start off editing gain on the first band */ + mode = GAIN; + current_type = LOW_SHELF; + current_band = 0; + + while (!exit_request) { + /* Clear the screen. The drawing routines expect this */ + screens[SCREEN_MAIN].clear_display(); + + /* Draw equalizer band details */ + y = draw_eq_sliders(current_band, mode); + + /* Set pointer to the band data currently editable */ + if (mode == GAIN) { + /* gain */ + setting = &global_settings.eq_band0_gain; + setting += current_band * 3; + + step = EQ_GAIN_STEP; + min = EQ_GAIN_MIN; + max = EQ_GAIN_MAX; + voice_unit = UNIT_DB; + + snprintf(buf, sizeof(buf), str(LANG_EQUALIZER_EDIT_MODE), + str(LANG_EQUALIZER_BAND_GAIN)); + + screens[SCREEN_MAIN].putsxy(2, y, buf); + } else if (mode == CUTOFF) { + /* cutoff */ + setting = &global_settings.eq_band0_cutoff; + setting += current_band * 3; + + step = EQ_CUTOFF_STEP; + min = EQ_CUTOFF_MIN; + max = EQ_CUTOFF_MAX; + voice_unit = UNIT_HERTZ; + + snprintf(buf, sizeof(buf), str(LANG_EQUALIZER_EDIT_MODE), + str(LANG_EQUALIZER_BAND_CUTOFF)); + + screens[SCREEN_MAIN].putsxy(2, y, buf); + } else { + /* Q */ + setting = &global_settings.eq_band0_q; + setting += current_band * 3; + + step = EQ_Q_STEP; + min = EQ_Q_MIN; + max = EQ_Q_MAX; + voice_unit = UNIT_INT; + + snprintf(buf, sizeof(buf), str(LANG_EQUALIZER_EDIT_MODE), + str(LANG_EQUALIZER_BAND_Q)); + + screens[SCREEN_MAIN].putsxy(2, y, buf); + } + + screens[SCREEN_MAIN].update(); + + button = button_get_w_tmo(HZ/10); + + switch (button) { + case BUTTON_LEFT: + case BUTTON_LEFT | BUTTON_REPEAT: + *(setting) -= step; + has_changed = true; + if (*(setting) < min) + *(setting) = min; + break; + + case BUTTON_RIGHT: + case BUTTON_RIGHT | BUTTON_REPEAT: + *(setting) += step; + has_changed = true; + if (*(setting) > max) + *(setting) = max; + break; + + case EQ_BTN_PREV_BAND: + case EQ_BTN_PREV_BAND | BUTTON_REPEAT: + current_band--; + if (current_band < 0) + current_band = 4; /* wrap around */ + break; + + case EQ_BTN_NEXT_BAND: + case EQ_BTN_NEXT_BAND | BUTTON_REPEAT: + current_band++; + if (current_band > 4) + current_band = 0; /* wrap around */ + break; + + case EQ_BTN_CHANGE_MODE: + case EQ_BTN_CHANGE_MODE | BUTTON_REPEAT: + mode++; + if (mode > Q) + mode = GAIN; /* wrap around */ + break; + + case EQ_BTN_EXIT: + case EQ_BTN_EXIT | BUTTON_REPEAT: + exit_request = true; + result = false; + break; + + default: + if(default_event_handler(button) == SYS_USB_CONNECTED) { + exit_request = true; + result = true; + } + break; + } + + /* Update the filter if the user changed something */ + if (has_changed) + dsp_eq_update_data(global_settings.eq_enabled); + } + + /* Reset screen settings */ + screens[SCREEN_MAIN].setfont(FONT_UI); + screens[SCREEN_MAIN].clear_display(); + + return result; +} + +/* Preset saver. + * TODO: Can the settings system be used to do this instead? + */ +static bool eq_save_preset(void) +{ + int fd, i; + char filename[MAX_PATH]; + int *setting; + + create_numbered_filename(filename, EQS_DIR, "eq", ".cfg", 2); + + /* allow user to modify filename */ + while (true) { + if (!kbd_input(filename, sizeof filename)) { + fd = creat(filename,0); + if (fd < 0) + gui_syncsplash(HZ, true, str(LANG_FAILED)); + else + break; + } + else { + gui_syncsplash(HZ, true, str(LANG_RESET_DONE_CANCEL)); + return false; + } + } + + /* TODO: Should we really do this? */ + fdprintf(fd, "eq enabled: yes\r\n"); + + setting = &global_settings.eq_band0_cutoff; + + for(i = 0; i < 5; ++i) { + fdprintf(fd, "eq band %d cutoff: %d\r\n", i, *setting++); + fdprintf(fd, "eq band %d q: %d\r\n", i, *setting++); + fdprintf(fd, "eq band %d gain: %d\r\n", i, *setting++); + } + + close(fd); + + gui_syncsplash(HZ, true, (unsigned char *)"%s %s", str(LANG_SETTINGS_SAVED1), + str(LANG_SETTINGS_SAVED2)); + + return true; +} + +/* Allows browsing of preset files */ +bool eq_browse_presets(void) +{ + return rockbox_browse(EQS_DIR, SHOW_CFG); +} + +/* Full equalizer menu */ +bool eq_menu(void) +{ + int m; + bool result; + static const struct menu_item items[] = { + { ID2P(LANG_EQUALIZER_ENABLED), eq_enabled }, + { ID2P(LANG_EQUALIZER_GRAPHICAL), eq_menu_graphical }, + { ID2P(LANG_EQUALIZER_GAIN), eq_gain_menu }, + { ID2P(LANG_EQUALIZER_ADVANCED), eq_advanced_menu }, + { ID2P(LANG_EQUALIZER_SAVE), eq_save_preset }, + { ID2P(LANG_EQUALIZER_BROWSE), eq_browse_presets }, + }; + + m=menu_init( items, sizeof(items) / sizeof(*items), NULL, + NULL, NULL, NULL); + result = menu_run(m); + menu_exit(m); + + return result; +} + diff --git a/apps/eq_menu.h b/apps/eq_menu.h new file mode 100644 index 0000000000..a22c1e51b8 --- /dev/null +++ b/apps/eq_menu.h @@ -0,0 +1,28 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright (C) 2006 Dan Everton + * + * All files in this archive are subject to the GNU General Public License. + * See the file COPYING in the source tree root for full license agreement. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ****************************************************************************/ +#ifndef _EQ_MENU_H +#define _EQ_MENU_H + +#include "menu.h" + +bool eq_browse_presets(void); +bool eq_menu_graphical(void); +bool eq_menu(void); + +#endif diff --git a/apps/lang/english.lang b/apps/lang/english.lang index c3d458e84c..fa02e06016 100644 --- a/apps/lang/english.lang +++ b/apps/lang/english.lang @@ -3610,3 +3610,94 @@ desc: prompt shown when about to erase a modified dynamic playlist eng: "Erase dynamic playlist?" voice "" new: + +id: LANG_EQUALIZER +desc: in the sound settings menu +eng: "Equalizer" +voice: "Equalizer" +new: + +id: LANG_EQUALIZER_ENABLED +desc: in the equalizer settings menu +eng: "Enable EQ" +voice: "Enable EQ" +new: + +id: LANG_EQUALIZER_GRAPHICAL +desc: in the equalizer settings menu +eng: "Graphical EQ" +voice: "Graphical EQ" +new: + +id: LANG_EQUALIZER_GAIN +desc: in the equalizer settings menu +eng: "Simple EQ Settings" +voice: "Simple EQ Settings" +new: + +id: LANG_EQUALIZER_ADVANCED +desc: in the equalizer settings menu +eng: "Advanced EQ Settings" +voice: "Advanced EQ Settings" +new: + +id: LANG_EQUALIZER_SAVE +desc: in the equalizer settings menu +eng: "Save EQ Preset" +voice: "Save EQ Preset" +new: + +id: LANG_EQUALIZER_BROWSE +desc: in the equalizer settings menu +eng: "Browse EQ Presets" +voice: "Browse EQ Presets" +new: + +id: LANG_EQUALIZER_GAIN_ITEM +desc: in the equalizer settings menu +eng: "%d Hz Band Gain" +voice: "" +new: + +id: LANG_EQUALIZER_BAND_CUTOFF +desc: in the equalizer settings menu +eng: "Cutoff" +voice: "Cutoff" +new: + +id: LANG_EQUALIZER_BAND_Q +desc: in the equalizer settings menu +eng: "Q" +voice: "Q" +new: + +id: LANG_EQUALIZER_BAND_GAIN +desc: in the equalizer settings menu +eng: "Gain" +voice: "Gain" +new: + +id: LANG_EQUALIZER_EDIT_MODE +desc: in the equalizer settings menu +eng: "Edit mode: %s" +voice: "" +new: + +id: LANG_EQUALIZER_BAND_LOW_SHELF +desc: in the equalizer settings menu +eng: "Low Shelf Filter" +voice: "" +new: + +id: LANG_EQUALIZER_BAND_PEAK +desc: in the equalizer settings menu +eng: "Peak Filter %d" +voice: "" +new: + +id: LANG_EQUALIZER_BAND_HIGH_SHELF +desc: in the equalizer settings menu +eng: "High Shelf Filter" +voice: "" +new: + diff --git a/apps/onplay.c b/apps/onplay.c index 17b4d1591b..9b94be0959 100644 --- a/apps/onplay.c +++ b/apps/onplay.c @@ -58,6 +58,9 @@ #include "main_menu.h" #include "sound_menu.h" #include "database.h" +#if CONFIG_CODEC == SWCODEC +#include "eq_menu.h" +#endif static int context; static char* selected_file = NULL; @@ -537,7 +540,11 @@ static int onplay_callback(int key, int menu) int onplay(char* file, int attr, int from) { - struct menu_item items[8]; /* increase this if you add entries! */ +#if CONFIG_CODEC == SWCODEC + struct menu_item items[10]; /* increase this if you add entries! */ +#else + struct menu_item items[8]; +#endif int m, i=0, result; #ifdef HAVE_LCD_COLOR char *suffix; @@ -641,6 +648,19 @@ int onplay(char* file, int attr, int from) i++; } +#if CONFIG_CODEC == SWCODEC + /* Equalizer menu items */ + if (context == CONTEXT_WPS) + { + items[i].desc = ID2P(LANG_EQUALIZER_GRAPHICAL); + items[i].function = eq_menu_graphical; + i++; + items[i].desc = ID2P(LANG_EQUALIZER_BROWSE); + items[i].function = eq_browse_presets; + i++; + } +#endif + /* DIY menu handling, since we want to exit after selection */ if (i) { diff --git a/apps/settings.c b/apps/settings.c index d13f56891e..ea8c562a04 100644 --- a/apps/settings.c +++ b/apps/settings.c @@ -511,6 +511,28 @@ static const struct bit_entry hd_bits[] = {1, S_O(warnon_erase_dynplaylist), false, "warn when erasing dynamic playlist", off_on }, +#if CONFIG_CODEC == SWCODEC + {1, S_O(eq_enabled), false, "eq enabled", off_on }, + /* 0..32768 Hz */ + {15, S_O(eq_band0_cutoff), 60, "eq band 0 cutoff", NULL }, + {15, S_O(eq_band1_cutoff), 200, "eq band 1 cutoff", NULL }, + {15, S_O(eq_band2_cutoff), 800, "eq band 2 cutoff", NULL }, + {15, S_O(eq_band3_cutoff), 4000, "eq band 3 cutoff", NULL }, + {15, S_O(eq_band4_cutoff), 12000, "eq band 4 cutoff", NULL }, + /* 0..64 (or 0.0 to 6.4) */ + {6, S_O(eq_band0_q), 7, "eq band 0 q", NULL }, + {6, S_O(eq_band1_q), 10, "eq band 1 q", NULL }, + {6, S_O(eq_band2_q), 10, "eq band 2 q", NULL }, + {6, S_O(eq_band3_q), 10, "eq band 3 q", NULL }, + {6, S_O(eq_band4_q), 7, "eq band 4 q", NULL }, + /* -240..240 (or -24db to +24db) */ + {9|SIGNED, S_O(eq_band0_gain), 0, "eq band 0 gain", NULL }, + {9|SIGNED, S_O(eq_band1_gain), 0, "eq band 1 gain", NULL }, + {9|SIGNED, S_O(eq_band2_gain), 0, "eq band 2 gain", NULL }, + {9|SIGNED, S_O(eq_band3_gain), 0, "eq band 3 gain", NULL }, + {9|SIGNED, S_O(eq_band4_gain), 0, "eq band 4 gain", NULL }, +#endif + /* If values are just added to the end, no need to bump the version. */ /* new stuff to be added at the end */ @@ -1023,6 +1045,7 @@ void settings_apply(void) audio_set_crossfade(global_settings.crossfade); dsp_set_replaygain(true); dsp_set_crossfeed(global_settings.crossfeed); + dsp_eq_update_data(global_settings.eq_enabled); #endif #ifdef HAVE_SPDIF_POWER diff --git a/apps/settings.h b/apps/settings.h index 3255f61ff3..6efaacbaf3 100644 --- a/apps/settings.h +++ b/apps/settings.h @@ -39,6 +39,7 @@ #define PLUGIN_DIR ROCKBOX_DIR"/rocks" #define BACKDROP_DIR ROCKBOX_DIR"/backdrops" #define REC_BASE_DIR "/recordings" +#define EQS_DIR ROCKBOX_DIR "/eqs" #define MAX_FILENAME 20 @@ -408,6 +409,39 @@ struct user_settings int brightness; /* iriver h300: backlight PWM value: 2..15 (0 and 1 are black) */ #endif + +#if CONFIG_CODEC == SWCODEC + bool eq_enabled; /* Enable equalizer */ + + /* Order is important here, must be cutoff, q, then gain for each band. + See dsp_eq_update_data in dsp.c for why. */ + + /* Band 0 settings */ + int eq_band0_cutoff; /* Hz */ + int eq_band0_q; + int eq_band0_gain; /* +/- dB */ + + /* Band 1 settings */ + int eq_band1_cutoff; /* Hz */ + int eq_band1_q; + int eq_band1_gain; /* +/- dB */ + + /* Band 2 settings */ + int eq_band2_cutoff; /* Hz */ + int eq_band2_q; + int eq_band2_gain; /* +/- dB */ + + /* Band 3 settings */ + int eq_band3_cutoff; /* Hz */ + int eq_band3_q; + int eq_band3_gain; /* +/- dB */ + + /* Band 4 settings */ + int eq_band4_cutoff; /* Hz */ + int eq_band4_q; + int eq_band4_gain; /* +/- dB */ +#endif + #ifdef HAVE_LCD_COLOR unsigned char backdrop_file[MAX_FILENAME+1]; /* backdrop bitmap file */ #endif diff --git a/apps/sound_menu.c b/apps/sound_menu.c index e44aa68231..2bd0ff6e59 100644 --- a/apps/sound_menu.c +++ b/apps/sound_menu.c @@ -45,6 +45,7 @@ #include "splash.h" #if CONFIG_CODEC == SWCODEC #include "dsp.h" +#include "eq_menu.h" #endif int selected_setting; /* Used by the callback */ @@ -379,6 +380,7 @@ bool sound_menu(void) { ID2P(LANG_STEREO_WIDTH), stereo_width }, #if CONFIG_CODEC == SWCODEC { ID2P(LANG_CROSSFEED), crossfeed }, + { ID2P(LANG_EQUALIZER), eq_menu }, #endif #if (CONFIG_CODEC == MAS3587F) || (CONFIG_CODEC == MAS3539F) { ID2P(LANG_LOUDNESS), loudness }, diff --git a/tools/buildzip.pl b/tools/buildzip.pl index a9af7619db..fad73624ed 100755 --- a/tools/buildzip.pl +++ b/tools/buildzip.pl @@ -91,6 +91,7 @@ sub buildzip { mkdir ".rockbox/wps", 0777; mkdir ".rockbox/themes", 0777; mkdir ".rockbox/backdrops", 0777; + mkdir ".rockbox/eqs", 0777; my $c = 'find apps -name "*.codec" ! -empty -exec cp {} .rockbox/codecs/ \;'; print `$c`;