diff --git a/components/micropython/usermodule/mp_audio.c b/components/micropython/usermodule/mp_audio.c index 4762e87b33e51794d950dea9d1b7654b21d05649..557c4973bd7b8078d6bbd88773cdf37ea215b802 100644 --- a/components/micropython/usermodule/mp_audio.c +++ b/components/micropython/usermodule/mp_audio.c @@ -28,12 +28,20 @@ STATIC mp_obj_t mp_headphones_are_connected() { STATIC MP_DEFINE_CONST_FUN_OBJ_0(mp_headphones_are_connected_obj, mp_headphones_are_connected); -STATIC mp_obj_t mp_headphones_detection_override(mp_obj_t enable) { - st3m_audio_headphones_detection_override(mp_obj_get_int(enable)); +STATIC mp_obj_t mp_headphones_detection_override(size_t n_args, + const mp_obj_t *args) { + bool enable = mp_obj_get_int(args[0]); + bool override_state = true; + if (n_args > 1) { + override_state = mp_obj_get_int(args[1]); + } + + st3m_audio_headphones_detection_override(enable, override_state); return mp_const_none; } -STATIC MP_DEFINE_CONST_FUN_OBJ_1(mp_headphones_detection_override_obj, - mp_headphones_detection_override); +STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(mp_headphones_detection_override_obj, + 1, 2, + mp_headphones_detection_override); STATIC mp_obj_t mp_headphones_set_volume_dB(mp_obj_t vol_dB) { return mp_obj_new_float( diff --git a/components/st3m/st3m_audio.c b/components/st3m/st3m_audio.c index 19eb0422438d0de7a749679ffcb73fe016b1d787..214bd6726e699c5081d623f83abcfe19ff06a807 100644 --- a/components/st3m/st3m_audio.c +++ b/components/st3m/st3m_audio.c @@ -216,9 +216,14 @@ static void _audio_speaker_apply(st3m_audio_output_t *out) { typedef struct { flow3r_bsp_audio_jacksense_state_t jacksense; - // True if system should pretend headphones are plugged in. + // True if system should ignore jacksense and use + // headphones_detection_override_state to determine + // whether headphones are connected (true) or not (false) bool headphones_detection_override; + // Defaults to true + bool headphones_detection_override_state; + // The two output channels. st3m_audio_output_t headphones; st3m_audio_output_t speaker; @@ -262,6 +267,7 @@ static st3m_audio_state_t state = { .line_in = false, }, .headphones_detection_override = false, + .headphones_detection_override_state = true, .headphones = { .volume = 0, @@ -310,7 +316,10 @@ static st3m_audio_state_t state = { // // Lock must be taken. static bool _headphones_connected(void) { - return state.jacksense.headphones || state.headphones_detection_override; + if (state.headphones_detection_override) { + return state.headphones_detection_override_state; + } + return state.jacksense.headphones; } static void _audio_input_set_source(st3m_audio_input_source_t source) { @@ -915,9 +924,11 @@ float st3m_audio_headphones_set_volume_dB(float vol_dB) { LOCKED(float, _output_set_volume(&state.headphones, vol_dB)); } -void st3m_audio_headphones_detection_override(bool enable) { +void st3m_audio_headphones_detection_override(bool enable, + bool override_state) { LOCK; state.headphones_detection_override = enable; + state.headphones_detection_override_state = override_state; _output_apply(&state.headphones); _output_apply(&state.speaker); UNLOCK; diff --git a/components/st3m/st3m_audio.h b/components/st3m/st3m_audio.h index 6830be463c9c6dc13f558a88ca27e225d669ee13..51ea9339f27a33d33e28671f71561e15a47b114b 100644 --- a/components/st3m/st3m_audio.h +++ b/components/st3m/st3m_audio.h @@ -76,12 +76,19 @@ bool st3m_audio_headset_is_connected(void); /* Returns true if the line-in jack is connected to a cable. */ bool st3m_audio_line_in_is_connected(void); -/* If a sleeve contact mic doesn't pull the detection pin low enough the - * codec's built in headphone detection might fail. Calling this function - * with 'enable = 1' overrides the detection and assumes there's headphones - * plugged in. Call with 'enable = 0' to revert to automatic detection. +/* Set to 'enable = 1' if the system should ignore jacksense and use + * headphones_detection_override_state to determine whether headphones are + * connected (true) or not (false). + * + * Use cases: + * - If a sleeve contact mic doesn't pull the detection pin low enough the + * codec's built in headphone detection might fail. + * - If the headset only has a mic connected but we wish to use the internal + * speaker anyway + * + * Call with 'enable = 0' to revert to automatic detection. */ -void st3m_audio_headphones_detection_override(bool enable); +void st3m_audio_headphones_detection_override(bool enable, bool override_state); /* Attempts to set target volume for the headphone output/onboard speakers * respectively, clamps/rounds if necessary and returns the actual volume. diff --git a/docs/api/audio.rst b/docs/api/audio.rst index bb3a8cb7690d194e1c0bea02928db8f873231f24..9b980ecb723e095ff056ca37999831b3ff296c07 100644 --- a/docs/api/audio.rst +++ b/docs/api/audio.rst @@ -152,16 +152,28 @@ in the user config. OS development -------------- +.. warning:: + + These functions are not to be used in applications, but only by OS settings. + 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) +.. py:function:: headphones_detection_override(enable : bool, override_state : bool) + + Set to 'enable = True' if the system should ignore jacksense and use + override_state to determine whether headphones are connected (True) or not + (False). + + Use cases: + + - If a sleeve contact mic doesn't pull the detection pin low enough the + codec's built in headphone detection might fail. + - If the headset only has a mic connected but we wish to use the internal + speaker anyway - If a sleeve contact mic doesn't pull the detection pin low enough the - codec's built in headphone detection might fail. Calling this function - with 'enable = 1' overrides the detection and assumes there's headphones - plugged in. Call with 'enable = 0' to revert to automatic detection. + Call with 'enable = 0' to revert to automatic detection. .. py:function:: headphones_set_volume_dB(vol_dB : float) -> float .. py:function:: speaker_set_volume_dB(vol_dB : float) -> float diff --git a/python_payload/apps/audio_config/__init__.py b/python_payload/apps/audio_config/__init__.py index db87a1cfe5f59a6e0c61ba93f12a88b8ad78a864..c0177976e9c9e08ddb56043c41159a0fde37c381 100644 --- a/python_payload/apps/audio_config/__init__.py +++ b/python_payload/apps/audio_config/__init__.py @@ -334,12 +334,12 @@ class SpeakerMenu(Submenu): class HeadphonesMenu(Submenu): def __init__(self, press): super().__init__(press) - self.num_widgets = 5 - self.overhang = -40 + self.num_widgets = 6 + self.overhang = -80 self.mid_x = 50 - self.focus_pos_limit_min = -100 + self.focus_pos_limit_min = -20 self.focus_pos_limit_max = 100 - self.focus_pos_limit_first = -100 + self.focus_pos_limit_first = -40 self.focus_pos_limit_last = 100 def _draw(self, ctx): @@ -377,6 +377,43 @@ class HeadphonesMenu(Submenu): audio.headphones_get_maximum_volume_dB() ) + self.y += 8 + + # note: jack detection is the inverse of headphones detection override + # jack detection off means headphones detection override on + tmp = self.draw_boolean( + "jack detection", + settings.onoff_headphones_detection_override.value, + off_str="on", # sic + on_str="off", # sic + ) + if settings.onoff_headphones_detection_override.value != tmp: + settings.onoff_headphones_detection_override.set_value(tmp) + audio.headphones_detection_override( + settings.onoff_headphones_detection_override.value, + settings.onoff_headphones_detection_override_state.value, + ) + + if tmp: + self.num_widgets = 7 + tmp = self.draw_boolean( + "headphone state", + settings.onoff_headphones_detection_override_state.value, + on_str="on", + off_str="off", + on_hint="sound will always play\nthrough headphones", + off_hint="sound will always play\nthrough speaker", + ) + if settings.onoff_headphones_detection_override_state.value != tmp: + settings.onoff_headphones_detection_override_state.set_value(tmp) + audio.headphones_detection_override( + settings.onoff_headphones_detection_override.value, + settings.onoff_headphones_detection_override_state.value, + ) + else: + self.num_widgets = 6 + self.overhang = -80 + class VolumeControlMenu(Submenu): def __init__(self, press): diff --git a/python_payload/st3m/run.py b/python_payload/st3m/run.py index 644fa5c60732310ba089eb7379c2e308c5fdf2cc..357cf372fe4d9379fc429eb0750e8ad129b996c7 100644 --- a/python_payload/st3m/run.py +++ b/python_payload/st3m/run.py @@ -159,6 +159,11 @@ def run_main() -> None: audio.onboard_mic_to_speaker_set_allowed( settings.onoff_onboard_mic_to_speaker_allowed.value ) + if settings.onoff_headphones_detection_override.value: + audio.headphones_detection_override( + settings.onoff_headphones_detection_override.value, + settings.onoff_headphones_detection_override_state.value, + ) leds.set_brightness(settings.num_leds_brightness.value) sys_display.set_backlight(settings.num_display_brightness.value) diff --git a/python_payload/st3m/settings.py b/python_payload/st3m/settings.py index 611ea14afeefd2d976323bf8b7e21ccf26ea76a6..979a95b320996f2ad1f6c7bbfaad6c8f7e5a95cd 100644 --- a/python_payload/st3m/settings.py +++ b/python_payload/st3m/settings.py @@ -255,6 +255,16 @@ onoff_onboard_mic_to_speaker_allowed = OnOffTunable( "system.audio.onboard_mic_to_speaker_allowed", False, ) +onoff_headphones_detection_override = OnOffTunable( + "Headphones detection override enabled", + "system.audio.headphones_detection_override", + False, +) +onoff_headphones_detection_override_state = OnOffTunable( + "Headphones detection override state", + "system.audio.headphones_detection_override_state", + True, +) num_headset_mic_gain_db = NumberTunable( "Headset Mic Gain dB", "system.audio.headset_mic_gain_dB", 0 @@ -306,6 +316,8 @@ load_save_settings: List[UnaryTunable] = [ onoff_onboard_mic_allowed, onoff_line_in_allowed, onoff_onboard_mic_to_speaker_allowed, + onoff_headphones_detection_override, + onoff_headphones_detection_override_state, num_headset_mic_gain_db, num_onboard_mic_gain_db, num_line_in_gain_db,