rockbox/apps/enc_config.c

471 lines
15 KiB
C
Raw Permalink Normal View History

/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
* $Id$
*
* Copyright (C) 2006 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 <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "config.h"
#include "action.h"
#include "lang.h"
#include "misc.h"
#include "talk.h"
#include "general.h"
#include "codecs.h"
#include "menu.h"
#include "settings.h"
#include "audio.h"
#include "pcm_record.h"
#include "enc_config.h"
#include "splash.h"
#define CALL_FN_(fn, ...) \
if (fn) fn(__VA_ARGS__)
static int enc_menuitem_callback(int action,
const struct menu_item_ex *this_item,
struct gui_synclist *this_list);
static int enc_menuitem_enteritem(int action,
const struct menu_item_ex *this_item,
struct gui_synclist *this_list);
static void enc_rec_settings_changed(struct encoder_config *cfg);
/* this is used by all encoder menu items,
MUST be initialised before the call to do_menu() */
static struct menucallback_data {
struct encoder_config *cfg;
bool global;
} menu_callback_data;
/** Function definitions for each codec - add these to enc_data
list following the definitions **/
/** aiff_enc.codec **/
/** 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 global)
{
if (global)
{
global_settings.mp3_enc_config.bitrate =
round_value_to_list32(cfg->mp3_enc.bitrate, mp3_enc_bitr,
MP3_ENC_NUM_BITR, false);
}
else
{
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];
}
} /* mp3_enc_convert_config */
/* mp3_enc: show the bitrate setting options */
static bool mp3_enc_bitrate(struct menucallback_data *data)
{
struct encoder_config *cfg = data->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 /* HAVE_MPEG2_SAMPR */
/* 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 */
/* Leave out 144 when there is both MPEG 1 and 2 */
#if defined(HAVE_MPEG2_SAMPR) && !defined (HAVE_MPEG1_SAMPR)
/* 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 */
#ifdef HAVE_MPEG1_SAMPR
/* 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 */
#endif
};
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,
0
#ifdef HAVE_MPEG1_SAMPR
| MPEG1_BITR_CAPS
#endif
#ifdef HAVE_MPEG2_SAMPR
#ifdef HAVE_MPEG1_SAMPR
| (MPEG2_BITR_CAPS & ~(MP3_BITR_CAP_144 | MP3_BITR_CAP_8))
#else
| (MPEG2_BITR_CAPS & ~(MP3_BITR_CAP_8))
#endif
#endif /* HAVE_MPEG2_SAMPR */
, 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 configuration menu */
MENUITEM_FUNCTION_W_PARAM(mp3_bitrate, 0, ID2P(LANG_BITRATE),
mp3_enc_bitrate, &menu_callback_data,
enc_menuitem_callback, Icon_NOICON);
MAKE_MENU( mp3_enc_menu, ID2P(LANG_ENCODER_SETTINGS),
enc_menuitem_enteritem, Icon_NOICON,
&mp3_bitrate);
/** wav_enc.codec **/
/* wav_enc: show the configuration menu */
#if 0
MAKE_MENU( wav_enc_menu, ID2P(LANG_ENCODER_SETTINGS),
enc_menuitem_enteritem, Icon_NOICON,
);
#endif
/** wavpack_enc.codec **/
/* wavpack_enc: show the configuration menu */
#if 0
MAKE_MENU( wavpack_enc_menu, ID2P(LANG_ENCODER_SETTINGS),
enc_menuitem_enteritem, Icon_NOICON,
);
#endif
/** config function pointers and/or data for each codec **/
static const struct encoder_data
{
void (*get_caps)(const struct encoder_config *cfg,
struct encoder_caps *caps, bool for_config);
void (*default_cfg)(struct encoder_config *cfg);
void (*convert_cfg)(struct encoder_config *cfg , bool global);
const struct menu_item_ex *menu;
} enc_data[REC_NUM_FORMATS] =
{
/* aiff_enc.codec */
[REC_FORMAT_AIFF] = {
NULL,
NULL,
NULL,
NULL,
},
/* 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,
NULL,
},
/* wavpack_enc.codec */
[REC_FORMAT_WAVPACK] = {
NULL,
NULL,
NULL,
NULL,
},
};
static inline bool rec_format_ok(int rec_format)
{
return (unsigned)rec_format < REC_NUM_FORMATS;
}
/* This is called before entering the menu with the encoder settings
Its needed to make sure the settings can take effect. */
static int enc_menuitem_enteritem(int action,
const struct menu_item_ex *this_item,
struct gui_synclist *this_list)
{
(void)this_item;
(void)this_list;
/* this struct must be init'ed before calling do_menu() so this is safe */
struct menucallback_data *data = &menu_callback_data;
if (action == ACTION_STD_OK) /* entering the item */
{
if (data->global)
global_to_encoder_config(data->cfg);
}
return action;
}
/* this is called when a encoder setting is exited
It is used to update the status bar and save the setting */
static int enc_menuitem_callback(int action,
const struct menu_item_ex *this_item,
struct gui_synclist *this_list)
{
(void)this_list;
struct menucallback_data *data =
(struct menucallback_data*)this_item->function_param->param;
if (action == ACTION_EXIT_MENUITEM)
{
/* If the setting being configured is global, it must be placed
in global_settings before updating the status bar for the
change to show upon exiting the item. */
if (data->global)
{
enc_rec_settings_changed(data->cfg);
encoder_config_to_global(data->cfg);
}
}
return action;
}
/* 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((int)CHN_NUM_MODES, (int)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, false);
} /* 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, true);
} /* 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_192;
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 **/
#if 0
bool enc_config_menu(struct encoder_config *cfg)
{
if (!rec_format_ok(cfg->rec_format))
return false;
if (enc_data[cfg->rec_format].menu)
{
menu_callback_data.cfg = &cfg;
menu_callback_data.global = false;
return do_menu(enc_data[cfg->rec_format].menu, NULL, NULL, false)
== MENU_ATTACHED_USB;
}
else
{
splash(HZ, ID2P(LANG_NO_SETTINGS));
return false;
}
} /* enc_config_menu */
#endif
/** 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. */
int enc_global_config_menu(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;
if (enc_data[cfg.rec_format].menu)
{
menu_callback_data.cfg = &cfg;
menu_callback_data.global = true;
int retmenu = do_menu(enc_data[cfg.rec_format].menu, NULL, NULL, false);
return (retmenu == MENU_ATTACHED_USB) ? 1 : 0;
}
else
{
splash(HZ, ID2P(LANG_NO_SETTINGS));
return 0;
}
} /* enc_global_config_menu */