diff options
Diffstat (limited to 'apps')
| -rw-r--r-- | apps/enc_config.c | 432 | ||||
| -rw-r--r-- | apps/enc_config.h | 73 |
2 files changed, 505 insertions, 0 deletions
diff --git a/apps/enc_config.c b/apps/enc_config.c new file mode 100644 index 0000000..384e679 --- /dev/null +++ b/apps/enc_config.c @@ -0,0 +1,432 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright (C) 2006 Michael Sevakis + * + * 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 <stdio.h> +#include <sprintf.h> +#include <string.h> +#include "config.h" +#include "atoi.h" +#include "lang.h" +#include "misc.h" +#include "talk.h" +#include "general.h" +#include "codecs.h" +#include "menu.h" +#include "statusbar.h" +#include "settings.h" +#include "audio.h" +#include "pcm_record.h" +#include "enc_config.h" + +#define MENU_ITEM_FN(fn) \ + ((bool (*)(void))fn) + +#define ENC_MENU_ITEM_FN(fn) \ + ((bool (*)(struct encoder_config *))fn) + +#define CALL_FN_(fn, ...) \ + if (fn) fn(__VA_ARGS__) + +static bool enc_run_menu(int m, const struct menu_item items[], + struct encoder_config *cfg); +static bool enc_no_config_menu(struct encoder_config *cfg); + +/** Function definitions for each codec - add these to enc_data + list following the definitions **/ + +/** mp3_enc.codec **/ +/* mp3_enc: return encoder capabilities */ +static void mp3_enc_get_caps(const struct encoder_config *cfg, + struct encoder_caps *caps, + bool for_config) +{ + int i; + unsigned long bitr; + + if (!for_config) + { + /* Overall encoder capabilities */ + caps->samplerate_caps = MPEG1_SAMPR_CAPS | MPEG2_SAMPR_CAPS; + caps->channel_caps = CHN_CAP_ALL; + return; + } + + /* Restrict caps based on config */ + i = round_value_to_list32(cfg->mp3_enc.bitrate, mp3_enc_bitr, + MP3_ENC_NUM_BITR, false); + bitr = mp3_enc_bitr[i]; + + /* sample rate caps */ + + /* check if MPEG1 sample rates are available */ + if ((bitr >= 32 && bitr <= 128) || bitr >= 160) + caps->samplerate_caps |= MPEG1_SAMPR_CAPS; + + /* check if MPEG2 sample rates and mono are available */ + if (bitr <= 160) + { + caps->samplerate_caps |= MPEG2_SAMPR_CAPS; + caps->channel_caps |= CHN_CAP_MONO; + } + + /* check if stereo is available */ + if (bitr >= 32) + caps->channel_caps |= CHN_CAP_STEREO; +} /* mp3_enc_get_caps */ + +/* mp3_enc: return the default configuration */ +static void mp3_enc_default_config(struct encoder_config *cfg) +{ + cfg->mp3_enc.bitrate = 128; /* default that works for all types */ +} /* mp3_enc_default_config */ + +static void mp3_enc_convert_config(struct encoder_config *cfg, + bool to_encoder) +{ + if (to_encoder) + { + if ((unsigned)global_settings.mp3_enc_config.bitrate > MP3_ENC_NUM_BITR) + global_settings.mp3_enc_config.bitrate = MP3_ENC_BITRATE_CFG_DEFAULT; + cfg->mp3_enc.bitrate = mp3_enc_bitr[global_settings.mp3_enc_config.bitrate]; + } + else + { + global_settings.mp3_enc_config.bitrate = + round_value_to_list32(cfg->mp3_enc.bitrate, mp3_enc_bitr, + MP3_ENC_NUM_BITR, false); + } +} /* mp3_enc_convert_config */ + +/* mp3_enc: show the bitrate setting options */ +static bool mp3_enc_bitrate(struct encoder_config *cfg) +{ + static const struct opt_items items[] = + { + /* Available in MPEG Version: */ +#ifdef HAVE_MPEG2_SAMPR +#if 0 + /* this sounds awful no matter what */ + { "8 kBit/s", TALK_ID(8, UNIT_KBIT) }, /* 2 */ +#endif + /* mono only */ + { "16 kBit/s", TALK_ID(16, UNIT_KBIT) }, /* 2 */ + { "24 kBit/s", TALK_ID(24, UNIT_KBIT) }, /* 2 */ +#endif + /* stereo/mono */ + { "32 kBit/s", TALK_ID(32, UNIT_KBIT) }, /* 1,2 */ + { "40 kBit/s", TALK_ID(40, UNIT_KBIT) }, /* 1,2 */ + { "48 kBit/s", TALK_ID(48, UNIT_KBIT) }, /* 1,2 */ + { "56 kBit/s", TALK_ID(56, UNIT_KBIT) }, /* 1,2 */ + { "64 kBit/s", TALK_ID(64, UNIT_KBIT) }, /* 1,2 */ + { "80 kBit/s", TALK_ID(80, UNIT_KBIT) }, /* 1,2 */ + { "96 kBit/s", TALK_ID(96, UNIT_KBIT) }, /* 1,2 */ + { "112 kBit/s", TALK_ID(112, UNIT_KBIT) }, /* 1,2 */ + { "128 kBit/s", TALK_ID(128, UNIT_KBIT) }, /* 1,2 */ +#if 0 + /* oddball MPEG2-only rate stuck in the middle */ + { "144 kBit/s", TALK_ID(144, UNIT_KBIT) }, /* 2 */ +#endif + { "160 kBit/s", TALK_ID(160, UNIT_KBIT) }, /* 1,2 */ + /* stereo only */ + { "192 kBit/s", TALK_ID(192, UNIT_KBIT) }, /* 1 */ + { "224 kBit/s", TALK_ID(224, UNIT_KBIT) }, /* 1 */ + { "256 kBit/s", TALK_ID(256, UNIT_KBIT) }, /* 1 */ + { "320 kBit/s", TALK_ID(320, UNIT_KBIT) }, /* 1 */ + }; + + unsigned long rate_list[ARRAYLEN(items)]; + + /* This is rather constant based upon the build but better than + storing and maintaining yet another list of numbers */ + int n_rates = make_list_from_caps32( + MPEG1_BITR_CAPS | MPEG2_BITR_CAPS, mp3_enc_bitr, + MPEG1_BITR_CAPS +#ifdef HAVE_MPEG2_SAMPR + | (MPEG2_BITR_CAPS & ~(MP3_BITR_CAP_144 | MP3_BITR_CAP_8)), +#endif + rate_list); + + int index = round_value_to_list32(cfg->mp3_enc.bitrate, rate_list, + n_rates, false); + bool res = set_option(str(LANG_BITRATE), &index, INT, + items, n_rates, NULL); + index = round_value_to_list32(rate_list[index], mp3_enc_bitr, + MP3_ENC_NUM_BITR, false); + cfg->mp3_enc.bitrate = mp3_enc_bitr[index]; + + return res; +} /* mp3_enc_bitrate */ + +/* mp3_enc: show the configuration menu */ +static bool mp3_enc_menu(struct encoder_config *cfg) +{ + static const struct menu_item items[] = + { + { ID2P(LANG_BITRATE), MENU_ITEM_FN(mp3_enc_bitrate) } + }; + + bool result; + int m = menu_init(items, ARRAYLEN(items), NULL, NULL, NULL, NULL); + result = enc_run_menu(m, items, cfg); + menu_exit(m); + return result; +} /* mp3_enc_menu */ + +/** wav_enc.codec **/ +/* wav_enc: show the configuration menu */ +#if 0 +static bool wav_enc_menu(struct encoder_config *cfg); +#endif + +/** wavpack_enc.codec **/ +/* wavpack_enc: show the configuration menu */ +#if 0 +static bool wavpack_enc_menu(struct encoder_config *cfg); +#endif + +/** config function pointers and/or data for each codec **/ +static const struct encoder_data +{ + void (*get_caps)(const struct encoder_config *, struct encoder_caps *, + bool); + void (*default_cfg)(struct encoder_config *); + void (*convert_cfg)(struct encoder_config *, bool to_encoder); + bool (*menu)(struct encoder_config *); +} enc_data[REC_NUM_FORMATS] = +{ + /* mp3_enc.codec */ + [REC_FORMAT_MPA_L3] = { + mp3_enc_get_caps, + mp3_enc_default_config, + mp3_enc_convert_config, + mp3_enc_menu, + }, + /* wav_enc.codec */ + [REC_FORMAT_PCM_WAV] = { + NULL, + NULL, + NULL, + enc_no_config_menu, + }, + /* wavpack_enc.codec */ + [REC_FORMAT_WAVPACK] = { + NULL, + NULL, + NULL, + enc_no_config_menu, + }, +}; + +static inline bool rec_format_ok(int rec_format) +{ + return (unsigned)rec_format < REC_NUM_FORMATS; +} + +static bool enc_run_menu(int m, const struct menu_item items[], + struct encoder_config *cfg) +{ + int selected; + while (1) + { + switch (selected=menu_show(m)) + { + case MENU_SELECTED_EXIT: + return false; + + case MENU_ATTACHED_USB: + return true; + + default: + if (items[selected].function && + ENC_MENU_ITEM_FN(items[selected].function)(cfg)) + return true; + gui_syncstatusbar_draw(&statusbars, true); + } + } +} /* enc_run_menu */ + +/* menu created when encoder has no configuration options */ +static bool enc_no_config_menu(struct encoder_config *cfg) +{ + static const struct menu_item items[] = + { + { ID2P(LANG_NO_SETTINGS), NULL } + }; + int m; + bool result; + + m = menu_init(items, ARRAYLEN(items), NULL, NULL, NULL, NULL); + result = enc_run_menu(m, items, NULL); + menu_exit(m); + + return result; + (void)cfg; +} /* enc_no_config_menu */ + +/* update settings dependent upon encoder settings */ +static void enc_rec_settings_changed(struct encoder_config *cfg) +{ + struct encoder_config enc_config; + struct encoder_caps caps; + long table[MAX(CHN_NUM_MODES, REC_NUM_FREQ)]; + int n; + + if (cfg == NULL) + { + cfg = &enc_config; + cfg->rec_format = global_settings.rec_format; + global_to_encoder_config(cfg); + } + + /* have to sync other settings when encoder settings change */ + if (!enc_get_caps(cfg, &caps, true)) + return; + + /* rec_channels */ + n = make_list_from_caps32(CHN_CAP_ALL, NULL, + caps.channel_caps, table); + + /* no zero check needed: encoder must support at least one + sample rate that recording supports or it shouldn't be in + available in the recording options */ + n = round_value_to_list32(global_settings.rec_channels, + table, n, true); + global_settings.rec_channels = table[n]; + + /* rec_frequency */ + n = make_list_from_caps32(REC_SAMPR_CAPS, rec_freq_sampr, + caps.samplerate_caps, table); + + n = round_value_to_list32( + rec_freq_sampr[global_settings.rec_frequency], + table, n, false); + + global_settings.rec_frequency = round_value_to_list32( + table[n], rec_freq_sampr, REC_NUM_FREQ, false); +} /* enc_rec_settings_changed */ + +/** public stuff **/ +void global_to_encoder_config(struct encoder_config *cfg) +{ + const struct encoder_data *data = &enc_data[cfg->rec_format]; + CALL_FN_(data->convert_cfg, cfg, true); +} /* global_to_encoder_config */ + +void encoder_config_to_global(const struct encoder_config *cfg) +{ + const struct encoder_data *data = &enc_data[cfg->rec_format]; + CALL_FN_(data->convert_cfg, (struct encoder_config *)cfg, false); +} /* encoder_config_to_global */ + +bool enc_get_caps(const struct encoder_config *cfg, + struct encoder_caps *caps, + bool for_config) +{ + /* get_caps expects caps to be zeroed first */ + memset(caps, 0, sizeof (*caps)); + + if (!rec_format_ok(cfg->rec_format)) + return false; + + if (enc_data[cfg->rec_format].get_caps) + { + enc_data[cfg->rec_format].get_caps(cfg, caps, for_config); + } + else + { + /* If no function provided...defaults to all */ + caps->samplerate_caps = SAMPR_CAP_ALL; + caps->channel_caps = CHN_CAP_ALL; + } + + return true; +} /* enc_get_caps */ + +/* Initializes the config struct with default values */ +bool enc_init_config(struct encoder_config *cfg) +{ + if (!rec_format_ok(cfg->rec_format)) + return false; + CALL_FN_(enc_data[cfg->rec_format].default_cfg, cfg); + return true; +} /* enc_init_config */ + +/** Encoder Menus **/ +bool enc_config_menu(struct encoder_config *cfg) +{ + if (!rec_format_ok(cfg->rec_format)) + return false; + return enc_data[cfg->rec_format].menu(cfg); +} /* enc_config_menu */ + +/** Global Settings **/ + +/* Reset all codecs to defaults */ +void enc_global_settings_reset(void) +{ + struct encoder_config cfg; + cfg.rec_format = 0; + + do + { + global_to_encoder_config(&cfg); + enc_init_config(&cfg); + encoder_config_to_global(&cfg); + if (cfg.rec_format == global_settings.rec_format) + enc_rec_settings_changed(&cfg); + } + while (++cfg.rec_format < REC_NUM_FORMATS); +} /* enc_global_settings_reset */ + +/* Apply new settings */ +void enc_global_settings_apply(void) +{ + struct encoder_config cfg; + if (!rec_format_ok(global_settings.rec_format)) + global_settings.rec_format = REC_FORMAT_DEFAULT; + + cfg.rec_format = global_settings.rec_format; + global_to_encoder_config(&cfg); + enc_rec_settings_changed(&cfg); + encoder_config_to_global(&cfg); +} /* enc_global_settings_apply */ + +/* Show an encoder's config menu based on the global_settings. + Modified settings are placed in global_settings.enc_config. */ +bool enc_global_config_menu(void) +{ + struct encoder_config cfg; + + bool res; + + if (!rec_format_ok(global_settings.rec_format)) + global_settings.rec_format = REC_FORMAT_DEFAULT; + + cfg.rec_format = global_settings.rec_format; + + global_to_encoder_config(&cfg); + + res = enc_config_menu(&cfg); + if (!res) + { + enc_rec_settings_changed(&cfg); + encoder_config_to_global(&cfg); + } + + return res; +} /* enc_global_config_menu */ diff --git a/apps/enc_config.h b/apps/enc_config.h new file mode 100644 index 0000000..53fa763 --- /dev/null +++ b/apps/enc_config.h @@ -0,0 +1,73 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright (C) 2006 Michael Sevakis + * + * 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 ENC_CONFIG_H +#define ENC_CONFIG_H + +#include "misc.h" +#include "enc_base.h" + +/** Capabilities **/ + +/* Capabilities returned by enc_get_caps that depend upon encoder settings */ +struct encoder_caps +{ + unsigned long samplerate_caps; /* Mask composed of SAMPR_CAP_* flags */ + unsigned long channel_caps; /* Mask composed of CHN_CAP_* flags */ +}; + +/* for_config: + * true- the capabilities returned should be contextual based upon the + * settings in the config structure + * false- the overall capabilities are being requested + */ +bool enc_get_caps(const struct encoder_config *cfg, + struct encoder_caps *caps, + bool for_config); + +/** Configuration **/ + +/* These translate to a back between the global format and the per- + instance format */ +void global_to_encoder_config(struct encoder_config *cfg); +void encoder_config_to_global(const struct encoder_config *cfg); + +/* Initializes the config struct with default values. + set afmt member before calling. */ +bool enc_init_config(struct encoder_config *cfg); + +/** Encoder Menus **/ + +/* Shows an encoder's config menu given an encoder config returned by one + of the enc_api functions. Modified settings are not saved to disk but + instead are placed in the structure. Call enc_save_config to commit + the data. */ +bool enc_config_menu(struct encoder_config *cfg); + +/** Global Settings **/ + +/* Reset all codecs to defaults */ +void enc_global_settings_reset(void); + +/* Apply new settings */ +void enc_global_settings_apply(void); + +/* Show an encoder's config menu based on the global_settings. + Modified settings are placed in global_settings.enc_config. */ +bool enc_global_config_menu(void); +#endif /* ENC_CONFIG_H */ |