Skip to content
Snippets Groups Projects
Commit a6008d7d authored by moon2's avatar moon2 :speech_balloon:
Browse files

audio: make input interface rly nice

- thru and engine inputs coexist peacefully
- auto switching gets u whatever source is available
- jacksense task takes care of source switching
- users can block access to sources such as onboard mic
- gain for all sources
- nice docs
parent 841617d1
No related branches found
No related tags found
No related merge requests found
......@@ -51,7 +51,10 @@ typedef enum {
// Headset microphone on left jack.
flow3r_bsp_audio_input_source_headset_mic = 2,
// Onboard microphone (enabled red LED).
flow3r_bsp_audio_input_source_onboard_mic = 3
flow3r_bsp_audio_input_source_onboard_mic = 3,
// auto switching depending on availability
// line in preferred to headset mic preferred to onboard mic.
flow3r_bsp_audio_input_source_auto = 4
} flow3r_bsp_audio_input_source_t;
// Initialize the audio subsystem of the badge, including the codec and I2S data
......
......@@ -262,6 +262,8 @@ void flow3r_bsp_max98091_input_set_source(
max98091_check(MAX98091_LEFT_ADC_MIXER, 0);
max98091_check(MAX98091_RIGHT_ADC_MIXER, 0);
break;
case flow3r_bsp_audio_input_source_auto:
break;
}
}
......
......@@ -195,22 +195,22 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_0(mp_get_volume_relative_obj,
mp_get_volume_relative);
STATIC mp_obj_t mp_headphones_line_in_set_hardware_thru(mp_obj_t enable) {
st3m_audio_headphones_line_in_set_hardware_thru(mp_obj_get_int(enable));
return mp_const_none;
// st3m_audio_headphones_line_in_set_hardware_thru(mp_obj_get_int(enable));
}
STATIC MP_DEFINE_CONST_FUN_OBJ_1(mp_headphones_line_in_set_hardware_thru_obj,
mp_headphones_line_in_set_hardware_thru);
STATIC mp_obj_t mp_speaker_line_in_set_hardware_thru(mp_obj_t enable) {
st3m_audio_speaker_line_in_set_hardware_thru(mp_obj_get_int(enable));
return mp_const_none;
// st3m_audio_speaker_line_in_set_hardware_thru(mp_obj_get_int(enable));
}
STATIC MP_DEFINE_CONST_FUN_OBJ_1(mp_speaker_line_in_set_hardware_thru_obj,
mp_speaker_line_in_set_hardware_thru);
STATIC mp_obj_t mp_line_in_set_hardware_thru(mp_obj_t enable) {
st3m_audio_line_in_set_hardware_thru(mp_obj_get_int(enable));
return mp_const_none;
// st3m_audio_line_in_set_hardware_thru(mp_obj_get_int(enable));
}
STATIC MP_DEFINE_CONST_FUN_OBJ_1(mp_line_in_set_hardware_thru_obj,
mp_line_in_set_hardware_thru);
......@@ -219,29 +219,111 @@ STATIC mp_obj_t mp_line_in_is_connected() {
}
STATIC MP_DEFINE_CONST_FUN_OBJ_0(mp_line_in_is_connected_obj,
mp_line_in_is_connected);
STATIC mp_obj_t mp_input_set_source(mp_obj_t source) {
st3m_audio_input_set_source(mp_obj_get_int(source));
// <INPUT SETUP>
// engine
STATIC mp_obj_t mp_input_engine_set_source(mp_obj_t source) {
st3m_audio_input_engine_set_source(mp_obj_get_int(source));
return mp_const_none;
}
STATIC MP_DEFINE_CONST_FUN_OBJ_1(mp_input_set_source_obj, mp_input_set_source);
STATIC MP_DEFINE_CONST_FUN_OBJ_1(mp_input_engine_set_source_obj,
mp_input_engine_set_source);
STATIC mp_obj_t mp_input_engine_get_source(void) {
return mp_obj_new_int(st3m_audio_input_engine_get_source());
}
STATIC MP_DEFINE_CONST_FUN_OBJ_0(mp_input_engine_get_source_obj,
mp_input_engine_get_source);
STATIC mp_obj_t mp_input_engine_get_target_source(void) {
return mp_obj_new_int(st3m_audio_input_engine_get_target_source());
}
STATIC MP_DEFINE_CONST_FUN_OBJ_0(mp_input_engine_get_target_source_obj,
mp_input_engine_get_target_source);
STATIC mp_obj_t mp_input_engine_get_source_avail(mp_obj_t source) {
return mp_obj_new_int(
st3m_audio_input_engine_get_source_avail(mp_obj_get_int(source)));
}
STATIC MP_DEFINE_CONST_FUN_OBJ_1(mp_input_engine_get_source_avail_obj,
mp_input_engine_get_source_avail);
// thru
STATIC mp_obj_t mp_input_thru_set_source(mp_obj_t source) {
st3m_audio_input_thru_set_source(mp_obj_get_int(source));
return mp_const_none;
}
STATIC MP_DEFINE_CONST_FUN_OBJ_1(mp_input_thru_set_source_obj,
mp_input_thru_set_source);
STATIC mp_obj_t mp_input_thru_get_source(void) {
return mp_obj_new_int(st3m_audio_input_thru_get_source());
}
STATIC MP_DEFINE_CONST_FUN_OBJ_0(mp_input_thru_get_source_obj,
mp_input_thru_get_source);
STATIC mp_obj_t mp_input_thru_get_target_source(void) {
return mp_obj_new_int(st3m_audio_input_thru_get_target_source());
}
STATIC MP_DEFINE_CONST_FUN_OBJ_0(mp_input_thru_get_target_source_obj,
mp_input_thru_get_target_source);
STATIC mp_obj_t mp_input_thru_get_source_avail(mp_obj_t source) {
return mp_obj_new_int(
st3m_audio_input_thru_get_source_avail(mp_obj_get_int(source)));
}
STATIC MP_DEFINE_CONST_FUN_OBJ_1(mp_input_thru_get_source_avail_obj,
mp_input_thru_get_source_avail);
// actual source
STATIC mp_obj_t mp_input_get_source(void) {
return mp_obj_new_int(st3m_audio_input_get_source());
}
STATIC MP_DEFINE_CONST_FUN_OBJ_0(mp_input_get_source_obj, mp_input_get_source);
STATIC mp_obj_t mp_headset_set_gain_dB(mp_obj_t gain_dB) {
st3m_audio_headset_set_gain_dB(mp_obj_get_int(gain_dB));
return mp_const_none;
// gain
STATIC mp_obj_t mp_headset_mic_set_gain_dB(mp_obj_t gain_dB) {
float ret = st3m_audio_headset_mic_set_gain_dB(mp_obj_get_float(gain_dB));
return mp_obj_new_float(ret);
}
STATIC MP_DEFINE_CONST_FUN_OBJ_1(mp_headset_mic_set_gain_dB_obj,
mp_headset_mic_set_gain_dB);
STATIC mp_obj_t mp_headset_mic_get_gain_dB(void) {
return mp_obj_new_float(st3m_audio_headset_mic_get_gain_dB());
}
STATIC MP_DEFINE_CONST_FUN_OBJ_0(mp_headset_mic_get_gain_dB_obj,
mp_headset_mic_get_gain_dB);
STATIC mp_obj_t mp_onboard_mic_set_gain_dB(mp_obj_t gain_dB) {
float ret = st3m_audio_onboard_mic_set_gain_dB(mp_obj_get_float(gain_dB));
return mp_obj_new_float(ret);
}
STATIC MP_DEFINE_CONST_FUN_OBJ_1(mp_onboard_mic_set_gain_dB_obj,
mp_onboard_mic_set_gain_dB);
STATIC mp_obj_t mp_onboard_mic_get_gain_dB(void) {
return mp_obj_new_float(st3m_audio_onboard_mic_get_gain_dB());
}
STATIC MP_DEFINE_CONST_FUN_OBJ_0(mp_onboard_mic_get_gain_dB_obj,
mp_onboard_mic_get_gain_dB);
STATIC mp_obj_t mp_line_in_set_gain_dB(mp_obj_t gain_dB) {
float ret = st3m_audio_line_in_set_gain_dB(mp_obj_get_float(gain_dB));
return mp_obj_new_float(ret);
}
STATIC MP_DEFINE_CONST_FUN_OBJ_1(mp_headset_set_gain_dB_obj,
mp_headset_set_gain_dB);
STATIC MP_DEFINE_CONST_FUN_OBJ_1(mp_line_in_set_gain_dB_obj,
mp_line_in_set_gain_dB);
STATIC mp_obj_t mp_headset_get_gain_dB(void) {
return mp_obj_new_int(st3m_audio_headset_get_gain_dB());
STATIC mp_obj_t mp_line_in_get_gain_dB(void) {
return mp_obj_new_float(st3m_audio_line_in_get_gain_dB());
}
STATIC MP_DEFINE_CONST_FUN_OBJ_0(mp_headset_get_gain_dB_obj,
mp_headset_get_gain_dB);
STATIC MP_DEFINE_CONST_FUN_OBJ_0(mp_line_in_get_gain_dB_obj,
mp_line_in_get_gain_dB);
STATIC mp_obj_t mp_input_thru_set_volume_dB(mp_obj_t vol_dB) {
return mp_obj_new_float(
......@@ -269,6 +351,8 @@ STATIC mp_obj_t mp_input_thru_get_mute() {
STATIC MP_DEFINE_CONST_FUN_OBJ_0(mp_input_thru_get_mute_obj,
mp_input_thru_get_mute);
// eq
STATIC mp_obj_t mp_speaker_get_eq_on() {
return mp_obj_new_int(st3m_audio_speaker_get_eq_on());
}
......@@ -282,6 +366,61 @@ STATIC mp_obj_t mp_speaker_set_eq_on(mp_obj_t eq_on) {
STATIC MP_DEFINE_CONST_FUN_OBJ_1(mp_speaker_set_eq_on_obj,
mp_speaker_set_eq_on);
// permissions
STATIC mp_obj_t mp_headset_mic_set_allowed(mp_obj_t allowed) {
st3m_audio_headset_mic_set_allowed(mp_obj_get_int(allowed));
return mp_const_none;
}
STATIC MP_DEFINE_CONST_FUN_OBJ_1(mp_headset_mic_set_allowed_obj,
mp_headset_mic_set_allowed);
STATIC mp_obj_t mp_headset_mic_get_allowed() {
return mp_obj_new_int(st3m_audio_headset_mic_get_allowed());
}
STATIC MP_DEFINE_CONST_FUN_OBJ_0(mp_headset_mic_get_allowed_obj,
mp_headset_mic_get_allowed);
STATIC mp_obj_t mp_onboard_mic_set_allowed(mp_obj_t allowed) {
st3m_audio_onboard_mic_set_allowed(mp_obj_get_int(allowed));
return mp_const_none;
}
STATIC MP_DEFINE_CONST_FUN_OBJ_1(mp_onboard_mic_set_allowed_obj,
mp_onboard_mic_set_allowed);
STATIC mp_obj_t mp_onboard_mic_get_allowed() {
return mp_obj_new_int(st3m_audio_onboard_mic_get_allowed());
}
STATIC MP_DEFINE_CONST_FUN_OBJ_0(mp_onboard_mic_get_allowed_obj,
mp_onboard_mic_get_allowed);
STATIC mp_obj_t mp_line_in_set_allowed(mp_obj_t allowed) {
st3m_audio_line_in_set_allowed(mp_obj_get_int(allowed));
return mp_const_none;
}
STATIC MP_DEFINE_CONST_FUN_OBJ_1(mp_line_in_set_allowed_obj,
mp_line_in_set_allowed);
STATIC mp_obj_t mp_line_in_get_allowed() {
return mp_obj_new_int(st3m_audio_line_in_get_allowed());
}
STATIC MP_DEFINE_CONST_FUN_OBJ_0(mp_line_in_get_allowed_obj,
mp_line_in_get_allowed);
STATIC mp_obj_t mp_onboard_mic_to_speaker_set_allowed(mp_obj_t allowed) {
st3m_audio_onboard_mic_to_speaker_set_allowed(mp_obj_get_int(allowed));
return mp_const_none;
}
STATIC MP_DEFINE_CONST_FUN_OBJ_1(mp_onboard_mic_to_speaker_set_allowed_obj,
mp_onboard_mic_to_speaker_set_allowed);
STATIC mp_obj_t mp_onboard_mic_to_speaker_get_allowed() {
return mp_obj_new_int(st3m_audio_onboard_mic_to_speaker_get_allowed());
}
STATIC MP_DEFINE_CONST_FUN_OBJ_0(mp_onboard_mic_to_speaker_get_allowed_obj,
mp_onboard_mic_to_speaker_get_allowed);
// </INPUT SETUP>
STATIC mp_obj_t mp_codec_i2c_write(mp_obj_t reg_in, mp_obj_t data_in) {
#if defined(CONFIG_FLOW3R_HW_GEN_P3) || defined(CONFIG_FLOW3R_HW_GEN_P4) || \
defined(CONFIG_FLOW3R_HW_GEN_C23)
......@@ -372,15 +511,44 @@ STATIC const mp_rom_map_elem_t mp_module_audio_globals_table[] = {
{ MP_ROM_QSTR(MP_QSTR_line_in_is_connected),
MP_ROM_PTR(&mp_line_in_is_connected_obj) },
{ MP_ROM_QSTR(MP_QSTR_input_set_source),
MP_ROM_PTR(&mp_input_set_source_obj) },
{ MP_ROM_QSTR(MP_QSTR_input_engine_set_source),
MP_ROM_PTR(&mp_input_engine_set_source_obj) },
{ MP_ROM_QSTR(MP_QSTR_input_engine_get_source),
MP_ROM_PTR(&mp_input_engine_get_source_obj) },
{ MP_ROM_QSTR(MP_QSTR_input_engine_get_target_source),
MP_ROM_PTR(&mp_input_engine_get_target_source_obj) },
{ MP_ROM_QSTR(MP_QSTR_input_engine_get_source_avail),
MP_ROM_PTR(&mp_input_engine_get_source_avail_obj) },
{ MP_ROM_QSTR(MP_QSTR_input_thru_set_source),
MP_ROM_PTR(&mp_input_thru_set_source_obj) },
{ MP_ROM_QSTR(MP_QSTR_input_thru_get_source),
MP_ROM_PTR(&mp_input_thru_get_source_obj) },
{ MP_ROM_QSTR(MP_QSTR_input_thru_get_target_source),
MP_ROM_PTR(&mp_input_thru_get_target_source_obj) },
{ MP_ROM_QSTR(MP_QSTR_input_thru_get_source_avail),
MP_ROM_PTR(&mp_input_thru_get_source_avail_obj) },
{ MP_ROM_QSTR(MP_QSTR_input_get_source),
MP_ROM_PTR(&mp_input_get_source_obj) },
// TODO: DEPRECATE
{ MP_ROM_QSTR(MP_QSTR_input_set_source),
MP_ROM_PTR(&mp_input_engine_set_source_obj) },
{ MP_ROM_QSTR(MP_QSTR_headset_set_gain_dB),
MP_ROM_PTR(&mp_headset_set_gain_dB_obj) },
{ MP_ROM_QSTR(MP_QSTR_headset_get_gain_dB),
MP_ROM_PTR(&mp_headset_get_gain_dB_obj) },
{ MP_ROM_QSTR(MP_QSTR_headset_mic_set_gain_dB),
MP_ROM_PTR(&mp_headset_mic_set_gain_dB_obj) },
{ MP_ROM_QSTR(MP_QSTR_headset_mic_get_gain_dB),
MP_ROM_PTR(&mp_headset_mic_get_gain_dB_obj) },
{ MP_ROM_QSTR(MP_QSTR_onboard_mic_set_gain_dB),
MP_ROM_PTR(&mp_onboard_mic_set_gain_dB_obj) },
{ MP_ROM_QSTR(MP_QSTR_onboard_mic_get_gain_dB),
MP_ROM_PTR(&mp_onboard_mic_get_gain_dB_obj) },
{ MP_ROM_QSTR(MP_QSTR_line_in_set_gain_dB),
MP_ROM_PTR(&mp_line_in_set_gain_dB_obj) },
{ MP_ROM_QSTR(MP_QSTR_line_in_get_gain_dB),
MP_ROM_PTR(&mp_line_in_get_gain_dB_obj) },
{ MP_ROM_QSTR(MP_QSTR_input_thru_set_volume_dB),
MP_ROM_PTR(&mp_input_thru_set_volume_dB_obj) },
......@@ -402,11 +570,30 @@ STATIC const mp_rom_map_elem_t mp_module_audio_globals_table[] = {
MP_ROM_INT(st3m_audio_input_source_headset_mic) },
{ MP_ROM_QSTR(MP_QSTR_INPUT_SOURCE_ONBOARD_MIC),
MP_ROM_INT(st3m_audio_input_source_onboard_mic) },
{ MP_ROM_QSTR(MP_QSTR_INPUT_SOURCE_AUTO),
MP_ROM_INT(st3m_audio_input_source_auto) },
{ MP_ROM_QSTR(MP_QSTR_speaker_get_eq_on),
MP_ROM_PTR(&mp_speaker_get_eq_on_obj) },
{ MP_ROM_QSTR(MP_QSTR_speaker_set_eq_on),
MP_ROM_PTR(&mp_speaker_set_eq_on_obj) },
{ MP_ROM_QSTR(MP_QSTR_headset_mic_get_allowed),
MP_ROM_PTR(&mp_headset_mic_get_allowed_obj) },
{ MP_ROM_QSTR(MP_QSTR_headset_mic_set_allowed),
MP_ROM_PTR(&mp_headset_mic_set_allowed_obj) },
{ MP_ROM_QSTR(MP_QSTR_onboard_mic_get_allowed),
MP_ROM_PTR(&mp_onboard_mic_get_allowed_obj) },
{ MP_ROM_QSTR(MP_QSTR_onboard_mic_set_allowed),
MP_ROM_PTR(&mp_onboard_mic_set_allowed_obj) },
{ MP_ROM_QSTR(MP_QSTR_line_in_get_allowed),
MP_ROM_PTR(&mp_line_in_get_allowed_obj) },
{ MP_ROM_QSTR(MP_QSTR_line_in_set_allowed),
MP_ROM_PTR(&mp_line_in_set_allowed_obj) },
{ MP_ROM_QSTR(MP_QSTR_onboard_mic_to_speaker_get_allowed),
MP_ROM_PTR(&mp_onboard_mic_to_speaker_get_allowed_obj) },
{ MP_ROM_QSTR(MP_QSTR_onboard_mic_to_speaker_set_allowed),
MP_ROM_PTR(&mp_onboard_mic_to_speaker_set_allowed_obj) },
};
STATIC MP_DEFINE_CONST_DICT(mp_module_audio_globals,
......
......@@ -31,7 +31,7 @@ static bool _headphones_connected(void);
const static float headphones_maximum_volume_system_dB = 3;
const static float speaker_maximum_volume_system_dB = 14;
// Output, either speakers or headphones. Holds volume/mute state and limits,
// Output, either speaker or headphones. Holds volume/mute state and limits,
// and calculated software volume.
//
// An output's apply function configures the actual physical output, ie. by
......@@ -184,12 +184,25 @@ typedef struct {
st3m_audio_output_t headphones;
st3m_audio_output_t speaker;
// Denormalized setting data that can be read back by user.
st3m_audio_input_source_t source;
int8_t headset_gain;
float headset_mic_gain_dB;
int16_t headset_mic_gain_software;
float onboard_mic_gain_dB;
int16_t onboard_mic_gain_software;
float line_in_gain_dB;
int16_t line_in_gain_software;
uint8_t speaker_eq_on;
bool headset_mic_allowed;
bool onboard_mic_allowed;
bool line_in_allowed;
bool onboard_mic_to_speaker_allowed;
st3m_audio_input_source_t engine_source;
st3m_audio_input_source_t engine_target_source;
st3m_audio_input_source_t thru_source;
st3m_audio_input_source_t thru_target_source;
st3m_audio_input_source_t source;
// Software-based audio pipe settings.
int32_t input_thru_vol;
int32_t input_thru_vol_int;
......@@ -230,11 +243,28 @@ static st3m_audio_state_t state = {
.apply = _audio_speaker_apply,
},
.source = st3m_audio_input_source_none,
.headset_gain = 0,
.speaker_eq_on = true,
.headset_mic_allowed = true,
.onboard_mic_allowed = true,
.line_in_allowed = true,
.onboard_mic_to_speaker_allowed = false,
.headset_mic_gain_dB = 0,
.onboard_mic_gain_dB = 0,
.line_in_gain_dB = 0,
.headset_mic_gain_software = 256,
.onboard_mic_gain_software = 256,
.line_in_gain_software = 256,
.input_thru_vol = 0,
.input_thru_vol_int = 0,
.input_thru_mute = true,
.input_thru_vol_int = 32768,
.input_thru_mute = false, // deprecated
.engine_target_source = st3m_audio_input_source_none,
.engine_source = st3m_audio_input_source_none,
.thru_source = st3m_audio_input_source_none,
.thru_target_source = st3m_audio_input_source_none,
.source = st3m_audio_input_source_none,
.function = st3m_audio_player_function_dummy,
};
......@@ -246,11 +276,243 @@ static bool _headphones_connected(void) {
return state.jacksense.headphones || state.headphones_detection_override;
}
static void _update_jacksense() {
static void _audio_input_set_source(st3m_audio_input_source_t source) {
LOCK;
st3m_audio_input_source_t prev_source = state.source;
UNLOCK;
if (source == prev_source) return;
flow3r_bsp_audio_input_source_t fsource;
switch (source) {
case st3m_audio_input_source_line_in:
fsource = flow3r_bsp_audio_input_source_line_in;
break;
case st3m_audio_input_source_onboard_mic:
fsource = flow3r_bsp_audio_input_source_onboard_mic;
break;
case st3m_audio_input_source_headset_mic:
fsource = flow3r_bsp_audio_input_source_headset_mic;
break;
case st3m_audio_input_source_none:
fsource = flow3r_bsp_audio_input_source_none;
break;
case st3m_audio_input_source_auto:
fsource = flow3r_bsp_audio_input_source_none;
break;
}
LOCK;
state.source = source;
UNLOCK;
flow3r_bsp_audio_input_set_source(fsource);
}
static bool _check_engine_source_avail(st3m_audio_input_source_t source) {
switch (source) {
case st3m_audio_input_source_none:
return true;
case st3m_audio_input_source_auto:
return true;
case st3m_audio_input_source_line_in:
return state.line_in_allowed && state.jacksense.line_in;
case st3m_audio_input_source_headset_mic:
return state.headset_mic_allowed && state.jacksense.headset;
case st3m_audio_input_source_onboard_mic:
return state.onboard_mic_allowed;
}
return false;
}
bool st3m_audio_input_engine_get_source_avail(
st3m_audio_input_source_t source) {
bool ret = false;
if (source == st3m_audio_input_source_auto) {
LOCK;
ret =
ret || _check_engine_source_avail(st3m_audio_input_source_line_in);
ret = ret ||
_check_engine_source_avail(st3m_audio_input_source_headset_mic);
ret = ret ||
_check_engine_source_avail(st3m_audio_input_source_onboard_mic);
UNLOCK;
} else {
LOCK;
ret = _check_engine_source_avail(source);
UNLOCK;
}
return ret;
}
void _update_engine_source() {
st3m_audio_input_source_t source;
LOCK;
source = state.engine_target_source;
if (source == st3m_audio_input_source_auto) {
if (_check_engine_source_avail(st3m_audio_input_source_line_in)) {
source = st3m_audio_input_source_line_in;
} else if (_check_engine_source_avail(
st3m_audio_input_source_headset_mic)) {
source = st3m_audio_input_source_headset_mic;
} else if (_check_engine_source_avail(
st3m_audio_input_source_onboard_mic)) {
source = st3m_audio_input_source_onboard_mic;
} else {
source = st3m_audio_input_source_none;
}
}
bool avail = _check_engine_source_avail(source);
UNLOCK;
source = avail ? source : st3m_audio_input_source_none;
LOCK;
bool return_early = state.source == source;
state.engine_source = source;
UNLOCK;
if (return_early) return;
if (avail) {
switch (source) {
case st3m_audio_input_source_none:
break;
case st3m_audio_input_source_auto:
break;
case st3m_audio_input_source_line_in:
if (avail) {
_audio_input_set_source(st3m_audio_input_source_line_in);
}
break;
case st3m_audio_input_source_headset_mic:
if (avail) {
_audio_input_set_source(
st3m_audio_input_source_headset_mic);
}
break;
case st3m_audio_input_source_onboard_mic:
if (avail) {
_audio_input_set_source(
st3m_audio_input_source_onboard_mic);
}
break;
}
}
}
void st3m_audio_input_engine_set_source(st3m_audio_input_source_t source) {
LOCK;
state.engine_target_source = source;
UNLOCK;
}
static bool _check_thru_source_avail(st3m_audio_input_source_t source) {
bool avail = _check_engine_source_avail(source);
if ((source == st3m_audio_input_source_onboard_mic) &&
(!state.onboard_mic_to_speaker_allowed) &&
(!state.jacksense.headphones)) {
avail = false;
}
if ((state.engine_source != st3m_audio_input_source_none) &&
(state.engine_source != source)) {
avail = false;
}
return avail;
}
bool st3m_audio_input_thru_get_source_avail(st3m_audio_input_source_t source) {
bool ret = false;
if (source == st3m_audio_input_source_auto) {
LOCK;
ret = ret || _check_thru_source_avail(st3m_audio_input_source_line_in);
ret = ret ||
_check_thru_source_avail(st3m_audio_input_source_headset_mic);
ret = ret ||
_check_thru_source_avail(st3m_audio_input_source_onboard_mic);
UNLOCK;
} else {
LOCK;
ret = _check_thru_source_avail(source);
UNLOCK;
}
return ret;
}
void _update_thru_source() {
st3m_audio_input_source_t source;
LOCK;
source = state.thru_target_source;
if (source == st3m_audio_input_source_auto) {
if (state.engine_source != st3m_audio_input_source_none) {
source = state.engine_source;
} else if (_check_thru_source_avail(st3m_audio_input_source_line_in)) {
source = st3m_audio_input_source_line_in;
} else if (_check_thru_source_avail(
st3m_audio_input_source_headset_mic)) {
source = st3m_audio_input_source_headset_mic;
} else if (_check_thru_source_avail(
st3m_audio_input_source_onboard_mic)) {
source = st3m_audio_input_source_onboard_mic;
} else {
source = st3m_audio_input_source_none;
}
}
bool avail = _check_thru_source_avail(source);
UNLOCK;
source = avail ? source : st3m_audio_input_source_none;
bool return_flag = false;
LOCK;
if (state.engine_source != st3m_audio_input_source_none) {
if (state.engine_source == source) {
state.thru_source = state.engine_source;
} else {
state.thru_source = st3m_audio_input_source_none;
}
return_flag = true;
}
state.thru_source = source;
if (state.thru_source == state.source) {
return_flag = true;
}
UNLOCK;
if (return_flag) return;
switch (source) {
case st3m_audio_input_source_none:
if (avail) {
_audio_input_set_source(st3m_audio_input_source_none);
}
break;
case st3m_audio_input_source_line_in:
if (avail) {
_audio_input_set_source(st3m_audio_input_source_line_in);
}
break;
case st3m_audio_input_source_headset_mic:
if (avail) {
_audio_input_set_source(st3m_audio_input_source_headset_mic);
}
break;
case st3m_audio_input_source_onboard_mic:
if (avail) {
_audio_input_set_source(st3m_audio_input_source_onboard_mic);
}
break;
case st3m_audio_input_source_auto:
// should not be possible
break;
}
}
void st3m_audio_input_thru_set_source(st3m_audio_input_source_t source) {
LOCK;
state.thru_target_source = source;
UNLOCK;
}
void _update_sink() {
flow3r_bsp_audio_jacksense_state_t st;
flow3r_bsp_audio_read_jacksense(&st);
static bool _speaker_eq_on_prev = false;
// Update volume to trigger mutes if needed. But only do that if the
// jacks actually changed.
LOCK;
......@@ -268,6 +530,13 @@ static void _update_jacksense() {
UNLOCK;
}
static void _update_routing() {
// order important
_update_sink();
_update_engine_source();
_update_thru_source();
}
void st3m_audio_player_function_dummy(int16_t *rx, int16_t *tx, uint16_t len) {
for (uint16_t i = 0; i < len; i++) {
tx[i] = 0;
......@@ -281,9 +550,7 @@ void st3m_audio_init(void) {
flow3r_bsp_audio_init();
st3m_audio_input_thru_set_volume_dB(-20);
state.speaker_eq_on = true;
_update_jacksense();
_update_routing();
_output_apply(&state.speaker);
_output_apply(&state.headphones);
bool _speaker_eq = (!_headphones_connected()) && state.speaker_eq_on;
......@@ -300,11 +567,13 @@ static void _audio_player_task(void *data) {
int16_t buffer_tx[FLOW3R_BSP_AUDIO_DMA_BUFFER_SIZE * 2];
int16_t buffer_rx[FLOW3R_BSP_AUDIO_DMA_BUFFER_SIZE * 2];
int16_t buffer_rx_dummy[FLOW3R_BSP_AUDIO_DMA_BUFFER_SIZE * 2];
memset(buffer_tx, 0, sizeof(buffer_tx));
memset(buffer_rx, 0, sizeof(buffer_rx));
memset(buffer_rx_dummy, 0, sizeof(buffer_rx));
size_t count;
bool hwmute = flow3r_bsp_audio_has_hardware_mute();
st3m_audio_input_source_t source_prev = st3m_audio_input_source_none;
while (true) {
count = 0;
......@@ -321,40 +590,89 @@ static void _audio_player_task(void *data) {
}
LOCK;
st3m_audio_input_source_t source = state.source;
st3m_audio_input_source_t engine_source = state.engine_source;
st3m_audio_input_source_t thru_source = state.thru_source;
bool headphones = _headphones_connected();
st3m_audio_player_function_t function = state.function;
int32_t software_volume = headphones ? state.headphones.volume_software
: state.speaker.volume_software;
bool software_mute =
headphones ? state.headphones.mute : state.speaker.mute;
bool input_thru_mute = state.input_thru_mute;
int32_t input_thru_vol_int = state.input_thru_vol_int;
UNLOCK;
(*function)(buffer_rx, buffer_tx, FLOW3R_BSP_AUDIO_DMA_BUFFER_SIZE * 2);
for (uint16_t i = 0; i < FLOW3R_BSP_AUDIO_DMA_BUFFER_SIZE; i++) {
st3m_scope_write(buffer_tx[2 * i] >> 2);
// <RX SIGNAL PREPROCESSING>
int16_t rx_gain = 256; // unity
uint8_t rx_chan = 3; // stereo = 0; left = 1; right = 2; off = 3;
if (source != source_prev) {
// state change: throw away buffer
source_prev = source;
memset(buffer_rx, 0, sizeof(buffer_rx));
} else if (source == st3m_audio_input_source_headset_mic) {
// headset has its own gain thing going on, leave at unity
rx_chan = 1;
} else if (source == st3m_audio_input_source_line_in) {
LOCK;
int16_t gain = state.line_in_gain_software;
UNLOCK;
rx_gain = gain;
rx_chan = 0;
} else if (source == st3m_audio_input_source_onboard_mic) {
LOCK;
int16_t gain = state.onboard_mic_gain_software;
UNLOCK;
rx_gain = gain;
rx_chan = 1;
}
if (!hwmute && software_mute) {
// Software muting needed. Only used on P1.
for (int i = 0; i < (FLOW3R_BSP_AUDIO_DMA_BUFFER_SIZE * 2);
i += 2) {
buffer_tx[i] = 0;
if (rx_chan == 0) {
// keep stereo image
for (uint16_t i = 0; i < FLOW3R_BSP_AUDIO_DMA_BUFFER_SIZE * 2;
i++) {
buffer_rx[i] = (((int32_t)buffer_rx[i]) * rx_gain) >> 8;
}
} else if (rx_chan < 3) {
// mix one of the input channels to both rx stereo chans (easier
// mono sources)
for (uint16_t i = 0; i < FLOW3R_BSP_AUDIO_DMA_BUFFER_SIZE * 2;
i++) {
uint16_t j = (i / 2) * 2 + rx_chan - 1;
buffer_rx[i] = (((int32_t)buffer_rx[j]) * rx_gain) >> 8;
}
}
int16_t *engine_rx;
if (engine_source == st3m_audio_input_source_none) {
engine_rx = buffer_rx_dummy;
} else {
for (int i = 0; i < (FLOW3R_BSP_AUDIO_DMA_BUFFER_SIZE * 2);
i += 1) {
engine_rx = buffer_rx;
}
// <ACTUAL ENGINE CALL>
(*function)(engine_rx, buffer_tx, FLOW3R_BSP_AUDIO_DMA_BUFFER_SIZE * 2);
// </ACTUAL ENGINE CALL>
for (uint16_t i = 0; i < FLOW3R_BSP_AUDIO_DMA_BUFFER_SIZE; i++) {
st3m_scope_write(buffer_tx[2 * i] >> 2);
}
for (int i = 0; i < (FLOW3R_BSP_AUDIO_DMA_BUFFER_SIZE * 2); i += 1) {
int32_t acc = buffer_tx[i];
acc = (acc * software_volume) >> 15;
if (!input_thru_mute) {
if ((thru_source != st3m_audio_input_source_none) &&
((engine_source == thru_source) ||
(engine_source == st3m_audio_input_source_none)) &&
(!input_thru_mute)) {
acc += (((int32_t)buffer_rx[i]) * input_thru_vol_int) >> 15;
}
buffer_tx[i] = acc;
}
}
flow3r_bsp_audio_write(buffer_tx, sizeof(buffer_tx), &count, 1000);
if (count != sizeof(buffer_tx)) {
......@@ -371,21 +689,24 @@ static void _jacksense_update_task(void *data) {
TickType_t last_wake = xTaskGetTickCount();
while (1) {
vTaskDelayUntil(&last_wake, pdMS_TO_TICKS(50)); // 20 Hz
_update_jacksense();
_update_routing();
}
}
// BSP wrappers that don't need locking.
void st3m_audio_headphones_line_in_set_hardware_thru(bool enable) {
return;
flow3r_bsp_audio_headphones_line_in_set_hardware_thru(enable);
}
void st3m_audio_speaker_line_in_set_hardware_thru(bool enable) {
return;
flow3r_bsp_audio_speaker_line_in_set_hardware_thru(enable);
}
void st3m_audio_line_in_set_hardware_thru(bool enable) {
return;
flow3r_bsp_audio_line_in_set_hardware_thru(enable);
}
......@@ -412,44 +733,62 @@ GETTER(float, audio_headphones_get_maximum_volume_dB,
GETTER(float, audio_speaker_get_maximum_volume_dB, state.speaker.volume_max)
GETTER(bool, audio_headphones_get_mute, state.headphones.mute)
GETTER(bool, audio_speaker_get_mute, state.speaker.mute)
GETTER(st3m_audio_input_source_t, audio_input_engine_get_source,
state.engine_source)
GETTER(st3m_audio_input_source_t, audio_input_thru_get_source,
state.thru_source)
GETTER(st3m_audio_input_source_t, audio_input_engine_get_target_source,
state.engine_target_source)
GETTER(st3m_audio_input_source_t, audio_input_thru_get_target_source,
state.thru_target_source)
GETTER(st3m_audio_input_source_t, audio_input_get_source, state.source)
GETTER(uint8_t, audio_headset_get_gain_dB, state.headset_gain)
GETTER(float, audio_headset_mic_get_gain_dB, state.headset_mic_gain_dB)
GETTER(float, audio_onboard_mic_get_gain_dB, state.onboard_mic_gain_dB)
GETTER(float, audio_line_in_get_gain_dB, state.line_in_gain_dB)
GETTER(float, audio_input_thru_get_volume_dB, state.input_thru_vol)
GETTER(bool, audio_input_thru_get_mute, state.input_thru_mute)
GETTER(bool, audio_speaker_get_eq_on, state.speaker_eq_on)
GETTER(bool, audio_headset_mic_get_allowed, state.headset_mic_allowed)
GETTER(bool, audio_onboard_mic_get_allowed, state.onboard_mic_allowed)
GETTER(bool, audio_line_in_get_allowed, state.line_in_allowed)
GETTER(bool, audio_onboard_mic_to_speaker_get_allowed,
state.onboard_mic_to_speaker_allowed)
#undef GETTER
// Locked global API functions.
void st3m_audio_headset_set_gain_dB(int8_t gain_dB) {
gain_dB = flow3r_bsp_audio_headset_set_gain_dB(gain_dB);
float st3m_audio_headset_mic_set_gain_dB(float gain_dB) {
if (gain_dB > 42) gain_dB = 42;
if (gain_dB < -9) gain_dB = -9;
int8_t hw_gain = flow3r_bsp_audio_headset_set_gain_dB(gain_dB);
int16_t software_gain = 256. * expf((gain_dB - hw_gain) * NAT_LOG_DB);
LOCK;
state.headset_gain = gain_dB;
state.headset_mic_gain_dB = gain_dB;
state.headset_mic_gain_software = software_gain;
UNLOCK;
return gain_dB;
}
void st3m_audio_input_set_source(st3m_audio_input_source_t source) {
switch (source) {
case st3m_audio_input_source_none:
flow3r_bsp_audio_input_set_source(
flow3r_bsp_audio_input_source_none);
break;
case st3m_audio_input_source_line_in:
flow3r_bsp_audio_input_set_source(
flow3r_bsp_audio_input_source_line_in);
break;
case st3m_audio_input_source_headset_mic:
flow3r_bsp_audio_input_set_source(
flow3r_bsp_audio_input_source_headset_mic);
break;
case st3m_audio_input_source_onboard_mic:
flow3r_bsp_audio_input_set_source(
flow3r_bsp_audio_input_source_onboard_mic);
break;
float st3m_audio_line_in_set_gain_dB(float gain_dB) {
if (gain_dB > 30) gain_dB = 30;
if (gain_dB < -24) gain_dB = -24;
int16_t software_gain = 256. * expf(gain_dB * NAT_LOG_DB);
LOCK;
state.line_in_gain_dB = gain_dB;
state.line_in_gain_software = software_gain;
UNLOCK;
return gain_dB;
}
float st3m_audio_onboard_mic_set_gain_dB(float gain_dB) {
if (gain_dB > 42) gain_dB = 42;
if (gain_dB < 0) gain_dB = 0;
int16_t software_gain = 256. * expf(gain_dB * NAT_LOG_DB);
LOCK;
state.source = source;
state.onboard_mic_gain_dB = gain_dB;
state.onboard_mic_gain_software = software_gain;
UNLOCK;
return gain_dB;
}
void st3m_audio_speaker_set_eq_on(bool enabled) {
......@@ -464,10 +803,35 @@ void st3m_audio_input_thru_set_mute(bool mute) {
UNLOCK;
}
void st3m_audio_headset_mic_set_allowed(bool allowed) {
LOCK;
state.headset_mic_allowed = allowed;
UNLOCK;
}
void st3m_audio_onboard_mic_set_allowed(bool allowed) {
LOCK;
state.onboard_mic_allowed = allowed;
UNLOCK;
}
void st3m_audio_line_in_set_allowed(bool allowed) {
LOCK;
state.line_in_allowed = allowed;
UNLOCK;
}
void st3m_audio_onboard_mic_to_speaker_set_allowed(bool allowed) {
LOCK;
state.onboard_mic_to_speaker_allowed = allowed;
UNLOCK;
}
float st3m_audio_input_thru_set_volume_dB(float vol_dB) {
if (vol_dB > 0) vol_dB = 0;
int16_t vol = 32768. * expf(vol_dB * NAT_LOG_DB);
LOCK;
state.input_thru_vol_int = (int32_t)(32768. * expf(vol_dB * NAT_LOG_DB));
state.input_thru_vol_int = vol;
state.input_thru_vol = vol_dB;
UNLOCK;
return vol_dB;
......
......@@ -10,7 +10,10 @@ typedef enum {
// Headset microphone on left jack.
st3m_audio_input_source_headset_mic = 2,
// Onboard microphone (enabled red LED).
st3m_audio_input_source_onboard_mic = 3
st3m_audio_input_source_onboard_mic = 3,
// auto switching depending on availability
// line in preferred to headset mic preferred to onboard mic.
st3m_audio_input_source_auto = 4
} st3m_audio_input_source_t;
typedef void (*st3m_audio_player_function_t)(int16_t* tx, int16_t* rx,
......@@ -151,20 +154,31 @@ void st3m_audio_line_in_set_hardware_thru(bool enable);
* Note: The onboard digital mic turns on an LED on the top board if it receives
* a clock signal which is considered a good proxy for its capability of reading
* data.
*
* TODO: check if sources are available
*/
void st3m_audio_input_set_source(st3m_audio_input_source_t source);
void st3m_audio_input_engine_set_source(st3m_audio_input_source_t source);
st3m_audio_input_source_t st3m_audio_input_engine_get_source(void);
st3m_audio_input_source_t st3m_audio_input_engine_get_target_source(void);
void st3m_audio_input_thru_set_source(st3m_audio_input_source_t source);
st3m_audio_input_source_t st3m_audio_input_thru_get_source(void);
st3m_audio_input_source_t st3m_audio_input_thru_get_target_source(void);
/* Returns the currently selected input source.
*/
st3m_audio_input_source_t st3m_audio_input_get_source(void);
/* Hardware preamp gain, 0dB-50dB. TODO: figure out if int/float inconsistency
* is a good thing here compared to all other _dB functions.
/* Gain of headset mic source
*/
float st3m_audio_headset_mic_set_gain_dB(float gain_dB);
float st3m_audio_headset_mic_get_gain_dB(void);
/* Gain of onboard mic source
*/
float st3m_audio_onboard_mic_set_gain_dB(float gain_dB);
float st3m_audio_onboard_mic_get_gain_dB(void);
/* Gain of line in source
*/
void st3m_audio_headset_set_gain_dB(int8_t gain_dB);
uint8_t st3m_audio_headset_get_gain_dB(void);
float st3m_audio_line_in_set_gain_dB(float gain_dB);
float st3m_audio_line_in_get_gain_dB(void);
/* You can route whatever source is selected with st3m_audio_input_set_source to
* the audio output. Use these to control volume and mute.
......@@ -180,6 +194,21 @@ bool st3m_audio_input_thru_get_mute(void);
void st3m_audio_speaker_set_eq_on(bool enable);
bool st3m_audio_speaker_get_eq_on(void);
void st3m_audio_headset_mic_set_allowed(bool allowed);
bool st3m_audio_headset_mic_get_allowed(void);
void st3m_audio_onboard_mic_set_allowed(bool allowed);
bool st3m_audio_onboard_mic_get_allowed(void);
void st3m_audio_line_in_set_allowed(bool allowed);
bool st3m_audio_line_in_get_allowed(void);
void st3m_audio_onboard_mic_to_speaker_set_allowed(bool allowed);
bool st3m_audio_onboard_mic_to_speaker_get_allowed(void);
bool st3m_audio_input_engine_get_source_avail(st3m_audio_input_source_t source);
bool st3m_audio_input_thru_get_source_avail(st3m_audio_input_source_t source);
/*
HEADPHONE PORT POLICY
......
......@@ -3,9 +3,11 @@
``audio`` module
================
Many of these functions are available in three variants: headphone volume,
speaker volume, and volume. If :code:`headphones_are_connected()` returns 1
the "headphone" variant is chosen, else the "speaker" variant is chosen.
The audio module provides the backbone for handling basic audio bookkeeping such as volume and signal routing.
Actual sound is created by the engines, i.e. bl00mbox and media player at the moment.
Jack Detection
--------------
.. py:function:: headset_is_connected() -> bool
......@@ -22,6 +24,132 @@ the "headphone" variant is chosen, else the "speaker" variant is chosen.
Returns 1 if the line-in jack was connected at the last call
of audio_update_jacksense.
Input Sources
-------------
.. note::
The onboard digital mic turns on an LED on the top board if it receives
a clock signal which is considered a good proxy for its capability of reading
data. Access to the onboard mic can be disabled entirely in the audio config
menu.
The codec can receive data from the line in jack, the headset mic pin in the headphone jack, or the
internal microphone. We distinguish between two use cases: 1) sending the signal to the audio engines
to be mangled with, and 2) directly mix it to the output with a variable volume level. We provide two
different APIs for each use case.
Sources may or may not be available; for line in and the headset mic they might simply not be plugged in,
but also users can configure in the settings which inputs are available in the first place. To handle this
uncertainity gracefully, instead of momentarily trying (and potentially failing) to set up a connection
you set a desired target and the backend will attempt to connect to it continuously until the target is reset
to none. The target states are not cleared when exiting applications, if you don't intend to also configure
the source for other applications please reset them to whatever state you found them in like so:
.. code-block:: python
on_enter(self, vm):
# save original target
self.orig_engine_target_source = audio.input_engine_get_target_source()
# switch to your preferred one
audio.input_engine_set_target_source(audio.INPUT_SOURCE_AUTO)
on_exit(self):
# restore original target
audio.input_engine_set_source(self.orig_engine_target_source)
Since the codec can only send data from one source at a time. in case of a disagreement between the engine
source and the thru source, the engine source wins and the through source is temporarily set to none.
For thru to follow the engine source if available and not none use ``audio.input_thru_set_source(audio.INPUT_SOURCE_AUTO)``.
The available sources for both engine and thru are slightly different: The engine only looks for permissions
and hardware state, while thru can not access the onboard mic if playback is happening through the speakers.
This is set up to prevent accidential feedback loops. However the user can give permission to acces this mode
in the user config.
.. py:data:: INPUT_SOURCE_NONE
No source, datastream suspended.
.. py:data:: INPUT_SOURCE_LINE_IN
Stream data from line in if available.
.. py:data:: INPUT_SOURCE_HEADSET_MIC
Stream data from headset mic if available and allowed.
.. py:data:: INPUT_SOURCE_ONBOARD_MIC
Stream data from onboard mic if allowed.
.. py:data:: INPUT_SOURCE_AUTO
Stream data from available input, line in is preferred to headset mic is preferred to onboard mic.
For ``input_thru_source`` matching ``input_engine_source`` is preferred to line in.
.. py:function:: input_engine_set_source(source : int) -> int
Set up a continuous connection query for routing the given source to the input for the audio engines.
Check for success with ``input_engine_get_source()`` and clean up by passing ``INPUT_SOURCE_NONE``
.. py:function:: input_engine_get_target_source() -> int
Returns target source last set with input_engine_set_source.
.. py:function:: input_engine_get_source() -> int
Returns source currently connected to the audio engines.
.. py:function:: input_engine_get_source_avail(source : int) -> bool
Returns true if it is currently possible to connect the audio engines to a given source.
If given ``INPUT_SOURCE_AUTO`` returns true if any source can be connected to the engines.
.. py:function:: input_thru_set_source(source : int) -> int
Set up a continuous connection query for routing the given source to the output mixer of the codec.
Check for success with ``input_thru_get_source()`` and clean up by passing ``INPUT_SOURCE_NONE``
.. py:function:: input_thru_get_target_source() -> int
Returns target source last set with input_thru_set_source.
.. py:function:: input_thru_get_source() -> int
Returns the source currently mixed directly to output.
.. py:function:: input_get_source() -> int
Returns the source the codec is connected to at the moment.
.. py:function:: input_thru_get_source_avail(source : int) -> bool
Returns true if it is currently possible to a given source to thru.
If given ``INPUT_SOURCE_AUTO`` returns true if any source can be connected to thru.
.. py:function:: input_thru_set_volume_dB(vol_dB : float)
.. py:function:: input_thru_get_volume_dB() -> float
.. py:function:: input_thru_set_mute(mute : bool)
.. py:function:: input_thru_get_mute() -> bool
Volume and mute control for input_thru. Please don't use this as a replacement for terminating
a connection, ``input_thru_set_source(audio.INPUT_SOURCE_NONE)`` instead!
.. py:function:: input_line_in_get_allowed(mute : bool)
.. py:function:: input_headset_mic_get_allowed(mute : bool)
.. py:function:: input_onboard_mic_get_allowed(mute : bool)
.. py:function:: input_onboard_mic_to_speaker_get_allowed(mute : bool)
Returns if the user has forbidden access to the resource.
OS development
--------------
Many of these functions are available in three variants: headphone volume,
speaker volume, and volume. If :code:`headphones_are_connected()` returns 1
the "headphone" variant is chosen, else the "speaker" variant is chosen.
.. py:function:: headphones_detection_override(enable : bool)
If a sleeve contact mic doesn't pull the detection pin low enough the
......@@ -102,49 +230,19 @@ the "headphone" variant is chosen, else the "speaker" variant is chosen.
:code:`audio_{headphones_/speaker_/}set_{maximum/minimum}_volume_` and 0 if
in a fake mute condition.
.. py:function:: headphones_line_in_set_hardware_thru(enable : bool)
.. py:function:: speaker_line_in_set_hardware_thru(enable : bool)
.. py:function:: line_in_set_hardware_thru(enable : bool)
These route whatever is on the line in port directly to the headphones or
speaker respectively (enable = 1), or don't (enable = 0). Is affected by mute
and coarse hardware volume settings, however software fine volume is not
applied.
Good for testing, might deprecate later, idk~
.. py:function:: input_set_source(source : int)
.. py:function:: input_get_source() -> int
The codec can transmit audio data from different sources. This function
enables one or no source as provided by the ``INPUT_SOURCE_*`` constants.
Note: The onboard digital mic turns on an LED on the top board if it receives
a clock signal which is considered a good proxy for its capability of reading
data.
.. py:data:: INPUT_SOURCE_NONE
.. py:data:: INPUT_SOURCE_LINE_IN
.. py:data:: INPUT_SOURCE_HEADSET_MIC
.. py:data:: INPUT_SOURCE_ONBOARD_MIC
.. py:function:: headset_set_gain_dB(gain_dB : int)
.. py:function:: headset_get_gain_dB() -> int
Hardware preamp gain, 0dB-50dB. TODO: figure out if int/float inconsistency
is a good thing here compared to all other _dB functions.
.. py:function:: input_thru_set_volume_dB(vol_dB : float)
.. py:function:: input_thru_get_volume_dB() -> float
.. py:function:: input_thru_set_mute(mute : bool)
.. py:function:: input_thru_get_mute() -> bool
.. py:function:: headset_mic_set_gain_dB(gain_dB : float)
.. py:function:: headset_mic_get_gain_dB() -> float
.. py:function:: onboard_mic_set_gain_dB(gain_dB : float)
.. py:function:: onboard_mic_get_gain_dB() -> float
.. py:function:: line_in_set_gain_dB(gain_dB : float)
.. py:function:: line_in_get_gain_dB() -> float
You can route whatever source is selected with input_set_source() to
the audio output. Use these to control volume and mute.
Set and get gain for the respective input channels.
.. py:function:: codec_i2c_write(reg : int, data : int)
Write audio codec register. Obviously very unsafe. Have fun.
Write audio codec register. Obviously very unsafe. Do not use in applications that you
distribute to users. This can fry your speakers with DC>
Headphone port policy
......
......@@ -13,49 +13,69 @@ class Drawable:
self.y = 0
self.font_size = 20
self.active = False
# override these w ur own val :3
self.focused_widget = 2
self.mid_x = 30
self.num_widgets = 2
self.overhang = -70
self.line_height = 24
self.ctx = None
self.press = press
def draw_heading(self, label):
self.focus_pos_limit_min = -60
self.focus_pos_limit_max = 60
self.focus_pos_limit_first = -60
self.focus_pos_limit_last = 80
self.first_widget_pos = 0
self.last_widget_pos = 0
self.focus_widget_pos_min = 0
self.focus_widget_pos_max = 0
self._focus_widget = 2
self._focus_widget_prev = 1
@property
def focus_widget(self):
return self._focus_widget
@property
def focus_widget_prev(self):
return self._focus_widget_prev
@focus_widget.setter
def focus_widget(self, val):
if val < 2:
val = 2
if val > self.num_widgets - 1:
val = self.num_widgets - 1
self._focus_widget_prev = self._focus_widget
self._focus_widget = val
@property
def at_first_widget(self):
return self.focus_widget <= 2
@property
def at_last_widget(self):
return self.focus_widget >= (self.num_widgets - 1)
def draw_heading(self, label, col=(0.8, 0.8, 0.8), embiggen=6, margin=2):
ctx = self.ctx
if ctx is None:
return
self.widget_no += 1
if not self.active:
if self.press.select_pressed and self.focused_widget > 0:
self.active = True
self.press.select_pressed = False
elif self.press.left_pressed:
self.focused_widget -= 1
if self.focused_widget < 2:
self.focused_widget = 2
self.press.left_pressed = False
elif self.press.right_pressed:
self.focused_widget += 1
if self.focused_widget > self.num_widgets - 1:
self.focused_widget = self.num_widgets - 1
self.press.right_pressed = False
if self.widget_no == self.focused_widget and not self.active:
ctx.rectangle(-130, int(self.y - self.font_size * 0.8), 260, self.font_size)
ctx.line_width = 2.0
ctx.rgba(*colours.GO_GREEN, 1.0)
ctx.stroke()
self.y += embiggen + margin
if self.widget_no == self.focus_widget:
if self.focus_widget > self.focus_widget_prev:
self.focus_widget += 1
else:
self.focus_widget -= 1
ctx.gray(1)
ctx.move_to(self.mid_x, self.y)
ctx.save()
ctx.rgb(0.8, 0.8, 0.8)
ctx.rgb(*col)
ctx.move_to(0, self.y)
ctx.text_align = ctx.CENTER
ctx.font_size += 6
ctx.font_size += embiggen
ctx.text(label)
ctx.restore()
self.y += self.line_height + 8
self.y += self.line_height + embiggen + margin
def draw_widget(self, label):
ctx = self.ctx
......@@ -63,24 +83,25 @@ class Drawable:
return
self.widget_no += 1
if not self.active:
if self.press.select_pressed and self.focused_widget > 0:
if self.press.select_pressed and self.focus_widget > 0:
self.active = True
self.press.select_pressed = False
elif self.press.left_pressed:
self.focused_widget -= 1
if self.focused_widget < 2:
self.focused_widget = 2
self.focus_widget -= 1
self.press.left_pressed = False
elif self.press.right_pressed:
self.focused_widget += 1
if self.focused_widget > self.num_widgets - 1:
self.focused_widget = self.num_widgets - 1
self.focus_widget += 1
self.press.right_pressed = False
if self.widget_no == self.focused_widget and not self.active:
ctx.rectangle(-130, int(self.y - self.font_size * 0.8), 260, self.font_size)
if self.widget_no == self.focus_widget:
self.focus_widget_pos_min = self.y
if not self.active:
ctx.rectangle(
-130, int(self.y - self.font_size * 0.8), 260, self.font_size
)
ctx.line_width = 2.0
ctx.rgba(*colours.GO_GREEN, 1.0)
ctx.stroke()
self.focus_widget_pos_max = self.y + self.line_height
ctx.gray(1)
ctx.move_to(self.mid_x, self.y)
ctx.save()
......@@ -95,7 +116,7 @@ class Drawable:
if ctx is None:
return
self.draw_widget(label)
if self.widget_no == self.focused_widget and self.active:
if self.widget_no == self.focus_widget and self.active:
if self.press.left_pressed:
no -= 1
if no < 0:
......@@ -108,7 +129,7 @@ class Drawable:
self.active = False
self.press.select_pressed = False
for a in range(len(choices)):
if a == no and self.active and self.widget_no == self.focused_widget:
if a == no and self.active and self.widget_no == self.focus_widget:
ctx.save()
ctx.rgba(*colours.GO_GREEN, 1.0)
ctx.rectangle(
......@@ -135,13 +156,13 @@ class Drawable:
ctx.text(choices[a] + " ")
return no
def draw_number(self, label, step_size, no, unit=""):
def draw_number(self, label, step_size, no, unit="", val_col=(0.8, 0.8, 0.8)):
ctx = self.ctx
if ctx is None:
return
self.draw_widget(label)
ret = no
if self.widget_no == self.focused_widget and self.active:
if self.widget_no == self.focus_widget and self.active:
if self.press.left_pressed:
ret -= step_size
elif self.press.right_pressed:
......@@ -150,7 +171,7 @@ class Drawable:
self.active = False
self.press.select_pressed = False
if self.active and self.widget_no == self.focused_widget:
if self.active and self.widget_no == self.focus_widget:
ctx.save()
ctx.rgba(*colours.GO_GREEN, 1.0)
ctx.rectangle(
......@@ -160,24 +181,57 @@ class Drawable:
self.font_size,
).stroke()
ctx.restore()
ctx.save()
ctx.rgb(*val_col)
ctx.text(str(no)[:4] + unit)
else:
ctx.text(str(no)[:4] + unit)
ctx.restore()
return ret
def draw_boolean(self, label, value, on_str="on", off_str="off"):
def draw_boolean(
self,
label,
value,
on_str="on",
off_str="off",
val_col=(0.8, 0.8, 0.8),
on_hint=None,
off_hint=None,
):
ctx = self.ctx
if ctx is None:
return
self.draw_widget(label)
if self.widget_no == self.focused_widget and self.active:
if self.widget_no == self.focus_widget and self.active:
value = not value
self.active = False
ctx.save()
ctx.rgb(*val_col)
if value:
ctx.text(on_str)
else:
ctx.text(off_str)
ctx.restore()
if self.widget_no == self.focus_widget:
if value:
hint = on_hint
else:
hint = off_hint
if hint is not None:
ctx.save()
ctx.font_size -= 4
ctx.text_align = ctx.CENTER
ctx.rgb(0.9, 0.9, 0.9)
lines = hint.split("\n")
self.y -= 3
for line in lines:
ctx.move_to(0, self.y)
ctx.text(line)
self.y += self.line_height - 5
ctx.restore()
if self.y > 115:
self.focus_widget_pos_max = self.y
return value
def draw_bg(self):
......@@ -186,25 +240,29 @@ class Drawable:
return
ctx.gray(1.0)
ctx.move_to(-100, -80)
wig = self.focused_widget - 1
if wig < 2:
wig = 2
if wig > self.num_widgets - 3:
wig = self.num_widgets - 3
focus_pos = self.overhang + (wig - 0.5) * self.line_height
if focus_pos > 40:
self.overhang -= 7
if focus_pos < -40:
self.overhang += 7
scroll_val = 0
scroll_speed = 7
if self.at_last_widget:
if self.focus_widget_pos_max > self.focus_pos_limit_last:
scroll_val = -self.focus_widget_pos_max + self.focus_pos_limit_last
elif self.at_first_widget:
if self.focus_widget_pos_min < self.focus_pos_limit_first:
scroll_val = 9999
elif self.focus_widget_pos_max > self.focus_pos_limit_max:
scroll_val = -9999
elif self.focus_widget_pos_min < self.focus_pos_limit_min:
scroll_val = 9999
if scroll_val > 0:
self.overhang += min(scroll_val, scroll_speed)
else:
self.overhang += max(scroll_val, -scroll_speed)
self.y = self.overhang
self.widget_no = 0
ctx.rectangle(-120, -120, 240, 240)
ctx.gray(0)
ctx.fill()
ctx.save()
ctx.font_size = 20
ctx.gray(0.8)
ctx.restore()
ctx.font_size = self.font_size
......@@ -226,9 +284,12 @@ class SpeakerMenu(Submenu):
def __init__(self, press):
super().__init__(press)
self.num_widgets = 6
self.focused_widget = 2
self.overhang = -40
self.mid_x = 50
self.focus_pos_limit_min = -100
self.focus_pos_limit_max = 100
self.focus_pos_limit_first = -100
self.focus_pos_limit_last = 100
def _draw(self, ctx):
self.ctx = ctx
......@@ -272,9 +333,12 @@ class HeadphonesMenu(Submenu):
def __init__(self, press):
super().__init__(press)
self.num_widgets = 5
self.focused_widget = 2
self.overhang = -40
self.mid_x = 50
self.focus_pos_limit_min = -100
self.focus_pos_limit_max = 100
self.focus_pos_limit_first = -100
self.focus_pos_limit_last = 100
def _draw(self, ctx):
self.ctx = ctx
......@@ -316,9 +380,12 @@ class VolumeControlMenu(Submenu):
def __init__(self, press):
super().__init__(press)
self.num_widgets = 6
self.focused_widget = 2
self.overhang = -40
self.mid_x = 25
self.focus_pos_limit_min = -100
self.focus_pos_limit_max = 100
self.focus_pos_limit_first = -100
self.focus_pos_limit_last = 100
def _draw(self, ctx):
self.ctx = ctx
......@@ -370,21 +437,121 @@ class VolumeControlMenu(Submenu):
class InputMenu(Submenu):
def __init__(self, press):
super().__init__(press)
self.num_widgets = 6
self.focused_widget = 2
self.overhang = -40
self.mid_x = 50
self.num_widgets = 11
self.overhang = -85
self.mid_x = 0
def _draw(self, ctx):
self.ctx = ctx
self.draw_bg()
self.draw_heading("inputs")
avail_col = (0.0, 0.9, 0.6)
warn_col = (0.9, 0.0, 0.0)
allow_col = (0.0, 0.7, 0.5)
not_allow_col = (0.8, 0.3, 0.3)
not_avail_col = (0.6, 0.6, 0.6)
self.draw_heading("line in", embiggen=5, margin=0)
if audio.line_in_get_allowed():
if audio.input_engine_get_source_avail(audio.INPUT_SOURCE_LINE_IN):
col = avail_col
else:
col = allow_col
else:
col = not_allow_col
tmp = self.draw_boolean(
"line in",
settings.onoff_line_in_allowed.value,
on_str="allowed",
off_str="blocked",
val_col=col,
)
if settings.onoff_line_in_allowed.value != tmp:
audio.line_in_set_allowed(tmp)
settings.onoff_line_in_allowed.set_value(tmp)
tmp = self.draw_number(
"gain",
1.5,
float(settings.num_line_in_gain_db.value),
unit="dB",
)
if settings.num_line_in_gain_db.value != tmp:
audio.line_in_set_gain_dB(tmp)
settings.num_line_in_gain_db.set_value(audio.line_in_get_gain_dB())
self.draw_heading("headset mic", embiggen=5, margin=0)
if audio.headset_mic_get_allowed():
if audio.input_engine_get_source_avail(audio.INPUT_SOURCE_HEADSET_MIC):
col = avail_col
else:
col = allow_col
else:
col = not_allow_col
tmp = self.draw_boolean(
"access",
settings.onoff_headset_mic_allowed.value,
on_str="allowed",
off_str="blocked",
val_col=col,
)
if settings.onoff_headset_mic_allowed.value != tmp:
audio.headset_mic_set_allowed(tmp)
settings.onoff_headset_mic_allowed.set_value(tmp)
tmp = self.draw_number(
"headset gain", 1, int(settings.num_headset_gain_db.value), unit="dB"
"gain",
1.5,
float(settings.num_headset_mic_gain_db.value),
unit="dB",
)
if settings.num_headset_mic_gain_db.value != tmp:
tmp = audio.headset_mic_set_gain_dB(tmp)
settings.num_headset_mic_gain_db.set_value(tmp)
self.draw_heading("onboard mic", embiggen=5, margin=0)
if audio.onboard_mic_get_allowed():
col = avail_col
else:
col = not_allow_col
tmp = self.draw_boolean(
"access",
settings.onoff_onboard_mic_allowed.value,
on_str="allowed",
off_str="blocked",
val_col=col,
)
if settings.onoff_onboard_mic_allowed.value != tmp:
audio.onboard_mic_set_allowed(tmp)
settings.onoff_onboard_mic_allowed.set_value(tmp)
tmp = self.draw_number(
"gain",
1.5,
float(settings.num_onboard_mic_gain_db.value),
unit="dB",
)
if settings.num_onboard_mic_gain_db.value != tmp:
tmp = audio.onboard_mic_set_gain_dB(tmp)
settings.num_onboard_mic_gain_db.set_value(tmp)
if not audio.onboard_mic_to_speaker_get_allowed():
col = not_allow_col
else:
col = warn_col
tmp = self.draw_boolean(
"thru",
settings.onoff_onboard_mic_to_speaker_allowed.value,
on_str="allow",
off_str="phones",
val_col=col,
on_hint=" /!\ feedback possible /!\ ",
)
if settings.num_headset_gain_db.value != tmp:
audio.headset_set_gain_dB(int(tmp))
settings.num_headset_gain_db.set_value(audio.headset_get_gain_dB())
if settings.onoff_onboard_mic_to_speaker_allowed.value != tmp:
audio.onboard_mic_to_speaker_set_allowed(tmp)
settings.onoff_onboard_mic_to_speaker_allowed.set_value(tmp)
class Press:
......
......@@ -8,14 +8,14 @@ import math
# Assume this is an enum
ForceModes = ["AUTO", "FORCE_LINE_IN", "FORCE_LINE_OUT", "FORCE_MIC", "FORCE_NONE"]
ForceModes = ["AUTO", "FORCE_LINE_IN", "FORCE_LINE_OUT", "FORCE_MIC"]
STATE_TEXT: dict[int, str] = {
audio.INPUT_SOURCE_HEADSET_MIC: "using headset mic (line out)",
audio.INPUT_SOURCE_LINE_IN: "using line in",
audio.INPUT_SOURCE_ONBOARD_MIC: "using onboard mic",
audio.INPUT_SOURCE_NONE: "plug cable to line in/out",
audio.INPUT_SOURCE_AUTO: "auto",
audio.INPUT_SOURCE_HEADSET_MIC: "headset mic",
audio.INPUT_SOURCE_LINE_IN: "line in",
audio.INPUT_SOURCE_ONBOARD_MIC: "onboard mic",
}
......@@ -25,6 +25,9 @@ class AudioPassthrough(Application):
self._button_0_pressed = False
self._button_5_pressed = False
self._force_mode: str = "AUTO"
self._mute = True
self._source = None
self.target_source = audio.INPUT_SOURCE_AUTO
def on_enter(self, vm: Optional[ViewManager]) -> None:
super().on_enter(vm)
......@@ -53,36 +56,42 @@ class AudioPassthrough(Application):
ctx.font_size = 25
ctx.move_to(0, 0)
ctx.save()
if audio.input_thru_get_mute():
if self._mute:
# 0xff4500, red
ctx.rgb(1, 0.41, 0)
else:
# 0x3cb043, green
ctx.rgb(0.24, 0.69, 0.26)
ctx.text("passthrough off" if audio.input_thru_get_mute() else "passthrough on")
ctx.text("passthrough off" if self._mute else "passthrough on")
ctx.restore()
# bottom text
ctx.move_to(0, 25)
ctx.save()
ctx.font_size = 15
ctx.text(STATE_TEXT.get(audio.input_get_source(), ""))
ctx.text(STATE_TEXT.get(self.target_source, ""))
# have red text when sleep mode isn't auto
if self._force_mode != "AUTO":
ctx.move_to(0, 40)
if self.source_connected:
# 0x3cb043, green
ctx.rgb(0.24, 0.69, 0.26)
else:
# 0xff4500, red
ctx.rgb(1, 0.41, 0)
ctx.move_to(0, 40)
ctx.text("(auto)" if self._force_mode == "AUTO" else "(forced)")
# mic has a loopback risk so has precautions
# so we warn users about it to not confuse them
if self._force_mode == "FORCE_MIC":
ctx.move_to(0, 55)
ctx.text("headphones only")
ctx.move_to(0, 70)
ctx.text("will not persist app exit")
if self._mute:
ctx.text("standby")
elif self._force_mode == "AUTO":
src = audio.input_thru_get_source()
if src != audio.INPUT_SOURCE_NONE:
ctx.text("connected to")
ctx.move_to(0, 56)
ctx.text(STATE_TEXT.get(src, ""))
else:
ctx.text("waiting...")
elif self._force_mode == "FORCE_MIC":
ctx.text("connected" if self.source_connected else "(headphones only)")
else:
ctx.text("connected" if self.source_connected else "waiting...")
ctx.restore()
# bottom button
......@@ -94,48 +103,58 @@ class AudioPassthrough(Application):
ctx.restore()
ctx.move_to(0, 90)
ctx.text("force line in/out")
ctx.text("next source")
def on_exit(self) -> None:
# Mic passthrough has a loopback risk
if self._force_mode == "FORCE_MIC":
self._force_mode = "FORCE_NONE"
audio.input_set_source(audio.INPUT_SOURCE_NONE)
audio.input_thru_set_mute(True)
@property
def source_connected(self):
if self.source != audio.INPUT_SOURCE_NONE:
return self.source == audio.input_thru_get_source()
else:
return False
@property
def source(self):
if self._source is None:
self._source = audio.input_thru_get_source()
return self._source
@source.setter
def source(self, source):
audio.input_thru_set_source(source)
self._source = audio.input_thru_get_source()
def think(self, ins: InputState, delta_ms: int) -> None:
super().think(ins, delta_ms)
headset_connected = audio.headset_is_connected()
if self._force_mode == "FORCE_MIC":
audio.input_set_source(audio.INPUT_SOURCE_ONBOARD_MIC)
elif (
audio.line_in_is_connected() and self._force_mode == "AUTO"
) or self._force_mode == "FORCE_LINE_IN":
audio.input_set_source(audio.INPUT_SOURCE_LINE_IN)
elif headset_connected or self._force_mode == "FORCE_LINE_OUT":
audio.input_set_source(audio.INPUT_SOURCE_HEADSET_MIC)
else:
audio.input_set_source(audio.INPUT_SOURCE_NONE)
if ins.captouch.petals[0].pressed:
if not self._button_0_pressed:
self._button_0_pressed = True
audio.input_thru_set_mute(not audio.input_thru_get_mute())
self._mute = not self._mute
else:
self._button_0_pressed = False
if ins.captouch.petals[5].pressed:
if not self._button_5_pressed:
self._button_5_pressed = True
self._force_mode = ForceModes[ForceModes.index(self._force_mode) + 1]
if ForceModes.index(self._force_mode) >= ForceModes.index("FORCE_NONE"):
self._force_mode = "AUTO"
index = ForceModes.index(self._force_mode)
index = (index + 1) % 4
self._force_mode = ForceModes[index]
else:
self._button_5_pressed = False
if self._force_mode == "FORCE_MIC" and not audio.headphones_are_connected():
self._force_mode = "AUTO"
if self._mute:
self.source = audio.INPUT_SOURCE_NONE
else:
if self._force_mode == "FORCE_MIC":
self.target_source = audio.INPUT_SOURCE_ONBOARD_MIC
elif self._force_mode == "AUTO":
self.target_source = audio.INPUT_SOURCE_AUTO
elif self._force_mode == "FORCE_LINE_IN":
self.target_source = audio.INPUT_SOURCE_LINE_IN
elif self._force_mode == "FORCE_LINE_OUT":
self.target_source = audio.INPUT_SOURCE_HEADSET_MIC
self.source = self.target_source
# For running with `mpremote run`:
......
......@@ -34,7 +34,6 @@ class TinySampler(Application):
self.blm = bl00mbox.Channel("tiny sampler")
self.samplers: List[bl00mbox.patches._Patch | Any] = [None] * 5
self.line_in = self.blm.new(bl00mbox.plugins.bl00mbox_line_in)
self.line_in.signals.gain = 32000
for i in range(5):
self.samplers[i] = self.blm.new(bl00mbox.patches.sampler, 1000)
self.samplers[i].signals.output = self.blm.mixer
......@@ -208,6 +207,20 @@ class TinySampler(Application):
elif not ct.petals[i].pressed and self.ct_prev.petals[i].pressed:
self.release_event[i] = True
if self.mode == 0:
if self.orig_source == audio.INPUT_SOURCE_NONE:
if audio.input_engine_get_source_avail(audio.INPUT_SOURCE_ONBOARD_MIC):
audio.input_engine_set_source(audio.INPUT_SOURCE_ONBOARD_MIC)
else:
audio.input_engine_set_source(audio.INPUT_SOURCE_AUTO)
else:
if audio.input_engine_get_source_avail(self.orig_source):
audio.input_engine_set_source(audio.self.orig_source)
else:
audio.input_engine_set_source(audio.INPUT_SOURCE_AUTO)
else:
audio.input_engine_set_source(audio.INPUT_SOURCE_NONE)
if self.mode == 0 or release_all:
for i in range(5):
if not self.is_recording[i]:
......@@ -268,18 +281,18 @@ class TinySampler(Application):
self.ct_prev = ct
def on_enter(self, vm) -> None:
self.mode = 0
super().on_enter(vm)
audio.input_set_source(audio.INPUT_SOURCE_ONBOARD_MIC)
self.mode = 0
self.orig_source = audio.input_engine_get_source()
if self.blm is None:
self._build_synth()
def on_exit(self) -> None:
audio.input_engine_set_source(self.orig_source)
for i in range(5):
if self.is_recording[i]:
self.samplers[i].signals.rec_trigger.stop()
self.is_recording[i] = False
audio.input_set_source(audio.INPUT_SOURCE_NONE)
if self.blm is not None:
self.blm.clear()
self.blm.free = True
......
......@@ -186,6 +186,18 @@ def run_main() -> None:
audio.speaker_set_minimum_volume_dB(settings.num_speaker_min_db.value)
audio.headphones_set_maximum_volume_dB(settings.num_headphones_max_db.value)
audio.speaker_set_maximum_volume_dB(settings.num_speaker_max_db.value)
audio.headset_mic_set_gain_dB(settings.num_headset_mic_gain_db.value)
audio.onboard_mic_set_gain_dB(settings.num_onboard_mic_gain_db.value)
audio.line_in_set_gain_dB(settings.num_line_in_gain_db.value)
audio.headset_mic_set_allowed(settings.onoff_headset_mic_allowed.value)
audio.onboard_mic_set_allowed(settings.onoff_onboard_mic_allowed.value)
audio.line_in_set_allowed(settings.onoff_line_in_allowed.value)
audio.onboard_mic_to_speaker_set_allowed(
settings.onoff_onboard_mic_to_speaker_allowed.value
)
leds.set_brightness(settings.num_leds_brightness.value)
sys_display.set_backlight(settings.num_display_brightness.value)
......
......@@ -241,9 +241,29 @@ num_speaker_max_db = StringTunable(
)
onoff_speaker_eq_on = StringTunable("Speaker EQ On", "system.audio.speaker_eq_on", True)
onoff_headset_mic_allowed = StringTunable(
"Headset Mic Allowed", "system.audio.headset_mic_allowed", True
)
onoff_onboard_mic_allowed = StringTunable(
"Onboard Mic Allowed", "system.audio.onboard_mic_allowed", True
)
onoff_line_in_allowed = StringTunable(
"Line In Allowed", "system.audio.line_in_allowed", True
)
onoff_onboard_mic_to_speaker_allowed = StringTunable(
"Onboard Mic To Speaker Allowed",
"system.audio.onboard_mic_to_speaker_allowed",
False,
)
num_headset_gain_db = StringTunable(
"Headset Mic Gain dB", "system.audio.headset_gain_dB", 10
num_headset_mic_gain_db = StringTunable(
"Headset Mic Gain dB", "system.audio.headset_mic_gain_dB", 0
)
num_onboard_mic_gain_db = StringTunable(
"Onboard Mic Gain dB", "system.audio.onboard_mic_gain_dB", 0
)
num_line_in_gain_db = StringTunable(
"Line In Gain dB", "system.audio.line_in_gain_dB", 0
)
num_display_brightness = StringTunable(
......@@ -282,7 +302,13 @@ load_save_settings: List[UnaryTunable] = [
num_headphones_max_db,
num_speaker_max_db,
onoff_speaker_eq_on,
num_headset_gain_db,
onoff_headset_mic_allowed,
onoff_onboard_mic_allowed,
onoff_line_in_allowed,
onoff_onboard_mic_to_speaker_allowed,
num_headset_mic_gain_db,
num_onboard_mic_gain_db,
num_line_in_gain_db,
num_display_brightness,
num_leds_brightness,
num_leds_speed,
......
......@@ -71,9 +71,57 @@ def get_mute() -> bool:
return _muted
def headset_set_gain_dB(v: float) -> None:
def headset_mic_set_gain_dB(v: float) -> None:
pass
def headset_get_gain_dB() -> float:
def headset_mic_get_gain_dB() -> float:
return 10
def onboard_mic_set_gain_dB(v: float) -> None:
pass
def onboard_mic_get_gain_dB() -> float:
return 10
def line_in_set_gain_dB(v: float) -> None:
pass
def line_in_get_gain_dB() -> float:
return 10
def headset_mic_set_allowed(v: bool) -> None:
pass
def onboard_mic_set_allowed(v: bool) -> None:
pass
def line_in_set_allowed(v: bool) -> None:
pass
def onboard_mic_to_speaker_set_allowed(v: bool) -> None:
pass
def headget_mic_get_allowed() -> bool:
return True
def onboard_mic_get_allowed() -> bool:
return True
def line_in_get_allowed() -> bool:
return True
def onboard_mic_to_speaker_get_allowed() -> bool:
return False
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment