diff --git a/components/badge23/audio.c b/components/badge23/audio.c
index f51167cc4045bcf03d895a90289d5da1b4face45..51305036c8415ac295765a3ac78bc2a4ad17ed3f 100644
--- a/components/badge23/audio.c
+++ b/components/badge23/audio.c
@@ -30,13 +30,14 @@ static bool headphones_connected = 0;
 static bool headset_connected = 0;
 static bool line_in_connected = 0;
 static int32_t software_volume = 0;
-static float headphones_volume_dB = -90;
-static bool headphones_mute = 2; // 2 is uninitialized
-static float speaker_volume_dB = -90;
-static bool speaker_mute = 2; // 2 is uninitialized
+static float headphones_volume_dB = 0;
+static bool headphones_mute = 0;
+static float speaker_volume_dB = 0;
+static bool speaker_mute = 0;
+static bool headphones_detection_override = 0;
 
 uint8_t audio_headset_is_connected(){ return headset_connected; }
-uint8_t audio_headphones_are_connected(){ return headphones_connected; }
+uint8_t audio_headphones_are_connected(){ return headphones_connected || headphones_detection_override; }
 float audio_headphones_get_volume_dB(){ return headphones_volume_dB; }
 float audio_speaker_get_volume_dB(){ return speaker_volume_dB; }
 uint8_t audio_headphones_get_mute(){ return headphones_mute ? 1 : 0; }
@@ -183,25 +184,17 @@ typedef struct {
 const uint8_t speaker_map_len = 40;
 const vol_map_t speaker_map[] = {{0x3F, +14}, {0x3E, +13.5}, {0x3D, +13}, {0x3C, +12.5}, {0x3B, +12}, {0x3A, +11.5}, {0x39, +11}, {0x38, +10.5}, {0x37, +10}, {0x36, +9.5}, {0x35, +9}, {0x34, +8}, {0x33, +7}, {0x32, +6}, {0x31, +5}, {0x30, +4}, {0x2F, +3}, {0x2E, +2}, {0x2D, +1}, {0x2C, +0}, {0x2B, -1}, {0x2A, -2}, {0x29, -3}, {0x28, -4}, {0x27, -5}, {0x26, -6}, {0x25, -8}, {0x24, -10}, {0x23, -12}, {0x22, -14}, {0x21, -17}, {0x20, -20}, {0x1F, -23}, {0x1E, -26}, {0x1D, -29}, {0x1C, -32}, {0x1B, -36}, {0x1A, -40}, {0x19, -44}, {0x18, -48}};
 
-const uint8_t headphones_map_len = 36;
+const uint8_t headphones_map_len = 32;
 const vol_map_t headphones_map[] = {{0x1F, +3}, {0x1E, +2.5}, {0x1D, +2}, {0x1C, +1.5}, {0x1B, +1}, {0x1A, +0}, {0x19, -1}, {0x18, -2}, {0x17, -3}, {0x16, -4}, {0x15, -5}, {0x14, -7}, {0x13, -9}, {0x12, -11}, {0x11, -13}, {0x10, -15}, {0x0F, -17}, {0x0E, -19}, {0x0D, -22}, {0x0C, -25}, {0x0B, -28}, {0x0A, -31}, {0x09, -34}, {0x08, -37}, {0x07, -40}, {0x06, -43}, {0x06, -47}, {0x04, -51}, {0x03, -55}, {0x02, -59}, {0x01, -63}, {0x00, -67}};
 
-static uint8_t headphones_volume_register = 0x1A;
-static uint8_t speaker_volume_register = 0x2C;
-
 float audio_headphones_set_volume_dB(float vol_dB){
     uint8_t map_index = headphones_map_len - 1;
     for(; map_index; map_index--){
-        if(headphones_map[map_index].volume_dB > vol_dB) break; 
+        if(headphones_map[map_index].volume_dB >= vol_dB) break; 
     }
     uint8_t reg = headphones_map[map_index].register_value;
-    //headphones_volume_dB = headphones_map[map_index].volume_dB;
-
-    headphones_volume_register = reg;
-
-    uint8_t headphones_on = (!headphones_mute) && headphones_connected;
-
-    reg = (headphones_on ?  (1 << 7) : 0) | reg;
+    uint8_t headphones_on = (!headphones_mute) && audio_headphones_are_connected();
+    reg = (headphones_on ?  0 : (1 << 7)) | reg;
     max98091_i2c_write(0x2C, reg); //left chan
     max98091_i2c_write(0x2D, reg); //right chan
     // note: didn't check if chan physically mapped to l/r or flipped.
@@ -209,45 +202,41 @@ float audio_headphones_set_volume_dB(float vol_dB){
     // do the fine steps in software
     // note: synchronizing both hw and software volume changes is somewhat tricky
     float hardware_volume_dB = headphones_map[map_index].volume_dB;
-    float software_volume_dB = hardware_volume_dB - vol_dB;
+    float software_volume_dB = vol_dB - hardware_volume_dB;
     if(software_volume_dB > 0) software_volume_dB = 0;
     //if(!software_volume_enabled) software_volume_dB = 0; // breaks p1, might add option once it is retired
-    software_volume = (int32_t) (32768 * exp(software_volume * NAT_LOG_DB));
+    software_volume = (int32_t) (32768 * exp(software_volume_dB * NAT_LOG_DB));
     headphones_volume_dB = hardware_volume_dB + software_volume_dB;
-    return speaker_volume_dB;
+    return headphones_volume_dB;
 }
 
 float audio_speaker_set_volume_dB(float vol_dB){
     uint8_t map_index = speaker_map_len - 1;
     for(; map_index; map_index--){
-        if(speaker_map[map_index].volume_dB > vol_dB) break; 
+        if(speaker_map[map_index].volume_dB >= vol_dB) break; 
     }
-    uint8_t reg = speaker_map[map_index].register_value;
-    speaker_volume_dB = speaker_map[map_index].volume_dB;
-
-    speaker_volume_register = reg;
 
-    uint8_t speaker_on = (!speaker_mute) && (!headphones_connected);
-
-    reg = (speaker_on ?  (1 << 7) : 0) | reg;
+    uint8_t reg = speaker_map[map_index].register_value;
+    uint8_t speaker_on = (!speaker_mute) && (!audio_headphones_are_connected());
+    reg = (speaker_on ?  0 : (1 << 7)) | reg;
     max98091_i2c_write(0x31, reg); //left chan
     max98091_i2c_write(0x32, reg); //right chan
     //note: didn't check if chan physically mapped to l/r or flipped.
 
     // do the fine steps in software
     // note: synchronizing both hw and software volume changes is somewhat tricky
-    float hardware_volume_dB = headphones_map[map_index].volume_dB;
-    float software_volume_dB = hardware_volume_dB - vol_dB;
+    float hardware_volume_dB = speaker_map[map_index].volume_dB;
+    float software_volume_dB = vol_dB - hardware_volume_dB;
     if(software_volume_dB > 0) software_volume_dB = 0;
     //if(!software_volume_enabled) software_volume_dB = 0; // breaks p1, might add option once it is retired
-    software_volume = (int32_t) (32768. * exp(software_volume * NAT_LOG_DB));
+    software_volume = (int32_t) (32768. * exp(software_volume_dB * NAT_LOG_DB));
     speaker_volume_dB = hardware_volume_dB + software_volume_dB;
     return speaker_volume_dB;
 }
 
 void audio_headphones_set_mute(uint8_t mute){
     headphones_mute = mute;
-    audio_headphones_set_volume_dB(speaker_volume_dB);
+    audio_headphones_set_volume_dB(headphones_volume_dB);
 }
 
 void audio_speaker_set_mute(uint8_t mute){
@@ -257,7 +246,7 @@ void audio_speaker_set_mute(uint8_t mute){
 
 #elif defined(CONFIG_BADGE23_HW_GEN_P1)
 
-#define MAX_VOLUME_DB 20
+#define MAX_VOLUME_DB 10
 #define MIN_VOLUME_DB (-80)
 
 int32_t software_volume_premute; // ugly but this is an old prototype that will be phased out soon
@@ -293,9 +282,9 @@ float audio_speaker_set_volume_dB(float vol_dB){
     if(vol_dB < (MIN_VOLUME_DB)) vol_dB = MIN_VOLUME_DB;
     if(vol_dB > (MAX_VOLUME_DB)) vol_dB = MAX_VOLUME_DB;
 
-    int32_t buf =  3000 * exp(vol_dB * NAT_LOG_DB);
+    int32_t buf =  32767 * exp(vol_dB * NAT_LOG_DB);
     software_volume_premute = buf;
-    if(speaker_mute){
+    if(speaker_mute || headphones_detection_override){
         software_volume = 0;
     } else {
         software_volume = software_volume_premute;
@@ -326,6 +315,28 @@ void audio_speaker_set_mute(uint8_t mute){
 #error "audio not implemented for this badge generation"
 #endif
 
+void audio_headphones_detection_override(uint8_t enable){
+    headphones_detection_override = enable;
+    audio_headphones_set_volume_dB(headphones_volume_dB);
+    audio_speaker_set_volume_dB(speaker_volume_dB);
+}
+
+float audio_headphones_adjust_volume_dB(float vol_dB){
+    return audio_headphones_set_volume_dB(headphones_volume_dB + vol_dB);
+}
+
+float audio_speaker_adjust_volume_dB(float vol_dB){
+    return audio_speaker_set_volume_dB(speaker_volume_dB + vol_dB);
+}
+
+float audio_adjust_volume_dB(float vol_dB){
+    if(audio_headphones_are_connected()){
+        return audio_headphones_set_volume_dB(headphones_volume_dB + vol_dB);
+    } else {
+        return audio_speaker_set_volume_dB(speaker_volume_dB + vol_dB);
+    }
+}
+
 float audio_set_volume_dB(float vol_dB){
     if(audio_headphones_are_connected()){
         return audio_headphones_set_volume_dB(vol_dB);
@@ -371,23 +382,24 @@ void audio_update_jacksense(){
     headphones_connected = 0;
     headset_connected = 0;
 #elif defined(CONFIG_BADGE23_HW_GEN_P3) || defined(CONFIG_BADGE23_HW_GEN_P4)  || defined(CONFIG_BADGE23_HW_GEN_P6)
+    static uint8_t jck_prev = 255; // unreachable value -> initial comparision always untrue
     uint8_t jck = max98091_i2c_read(0x02);
     if(jck == 6){
         headphones_connected = 0;
         headset_connected = 0;
-        if(speaker_mute == 1) audio_speaker_set_mute(0);
-        if(headphones_mute == 0) audio_headphones_set_mute(1);
     } else if(jck == 0){
         headphones_connected = 1;
         headset_connected = 0;
-        if(speaker_mute == 0) audio_speaker_set_mute(1);
-        if(headphones_mute == 1) audio_headphones_set_mute(0);
     } else if(jck == 2){
         headphones_connected = 1;
         headset_connected = 1;
-        if(speaker_mute == 0) audio_speaker_set_mute(1);
-        if(headphones_mute == 1) audio_headphones_set_mute(0);
     }
+
+    if(jck != jck_prev){ // update volume to trigger mutes if needed
+        audio_speaker_set_volume_dB(speaker_volume_dB);
+        audio_headphones_set_volume_dB(headphones_volume_dB);
+    }
+    jck_prev = jck;
 #endif
 }
 
@@ -496,7 +508,7 @@ static void audio_player_task(void* arg) {
                 audio_source = audio_source->next;
             }
             write_to_scope((int16_t) (1600. * sample));
-            sample = software_volume * sample;
+            sample = software_volume * (sample/10);
             if(sample > 32767) sample = 32767;
             if(sample < -32767) sample = -32767;
             buffer[i] = (int16_t) sample;
diff --git a/components/badge23/include/badge23/audio.h b/components/badge23/include/badge23/audio.h
index ef2390a36d218a8f2a2a4004352930d7742c0e9e..eab45f738e4475e712b8e1a74a51509a0d95746e 100644
--- a/components/badge23/include/badge23/audio.h
+++ b/components/badge23/include/badge23/audio.h
@@ -32,11 +32,17 @@ uint8_t audio_headphones_are_connected();
  */
 uint8_t audio_headset_is_connected();
 
+/* 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.
+ */
+void audio_headphones_detection_override(uint8_t enable);
+
 /* Attempts to set target volume for the headphone output/onboard speakers
  * respectively, clamps/rounds if necessary and returns the actual volume.
  * Absolute reference arbitrary.
- * Does not unmute, use audio_{headphones/speaker/}_set_mute;
- * as needed.
+ * Does not unmute, use audio_{headphones_/speaker_/}set_mute as needed.
  *
  * Note: This function uses a hardware PGA for the coarse value and software
  * for the fine value. These two methods are as of yet not synced so that there
@@ -47,6 +53,13 @@ float audio_headphones_set_volume_dB(float vol_dB);
 float audio_speaker_set_volume_dB(float vol_dB);
 float audio_set_volume_dB(float vol_dB);
 
+/* Like the audio_{headphones_/speaker_/}set_volume family but changes relative
+ * to last volume value.
+ */
+float audio_headphones_adjust_volume_dB(float vol_dB);
+float audio_speaker_adjust_volume_dB(float vol_dB);
+float audio_adjust_volume_dB(float vol_dB);
+
 /* Returns volume as set with audio_{headphones/speaker}_set_volume_dB.
  * The unspecified variant automatically chooses the adequate channel (**).
  */
diff --git a/python_payload/utils.py b/python_payload/utils.py
index 2341cf9a0c40a3587afe2eb618c91876be3ba70c..633d8b5547b0fe65bb57491118937b01b7c95250 100644
--- a/python_payload/utils.py
+++ b/python_payload/utils.py
@@ -24,6 +24,10 @@ def long_bottom_petal_captouch_blocking(num, ms):
     return False
 
 def draw_volume_slider(ctx, volume):
+    if(volume > 20):
+        volume = 20
+    if(volume < -60):
+        volume = -60
     length = 96 + ((volume - 20) * 1.6)
     if length > 96:
         length = 96
diff --git a/usermodule/mp_audio.c b/usermodule/mp_audio.c
index 7e9e8bbaa0a0c67902e79a065bddfe84b633f749..ca46016eba89c3699106b0c672235e02ed2b679f 100644
--- a/usermodule/mp_audio.c
+++ b/usermodule/mp_audio.c
@@ -14,11 +14,7 @@
 #include "badge23/audio.h"
 #include "badge23_hwconfig.h"
 
-/*
-uint8_t audio_speaker_is_on();
-uint8_t audio_headset_is_connected();
-uint8_t audio_headphones_are_connected();
-*/
+// documentation: these are all super thin wrappers for the c api in components/badge23/include/badge23/audio.h
 
 STATIC mp_obj_t mp_headset_is_connected() {
     return mp_obj_new_int(audio_headset_is_connected());
@@ -30,10 +26,110 @@ 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) {
+    audio_headphones_detection_override(mp_obj_get_int(enable));
+    return mp_const_none;
+}
+STATIC MP_DEFINE_CONST_FUN_OBJ_1(mp_headphones_detection_override_obj, mp_headphones_detection_override);
+
+STATIC mp_obj_t mp_headphones_set_volume_dB(mp_obj_t vol_dB) {
+    return mp_obj_new_float(audio_headphones_set_volume_dB(mp_obj_get_float(vol_dB)));
+}
+STATIC MP_DEFINE_CONST_FUN_OBJ_1(mp_headphones_set_volume_dB_obj, mp_headphones_set_volume_dB);
+
+STATIC mp_obj_t mp_speaker_set_volume_dB(mp_obj_t vol_dB) {
+    return mp_obj_new_float(audio_speaker_set_volume_dB(mp_obj_get_float(vol_dB)));
+}
+STATIC MP_DEFINE_CONST_FUN_OBJ_1(mp_speaker_set_volume_dB_obj, mp_speaker_set_volume_dB);
+
+STATIC mp_obj_t mp_set_volume_dB(mp_obj_t vol_dB) {
+    return mp_obj_new_float(audio_set_volume_dB(mp_obj_get_float(vol_dB)));
+}
+STATIC MP_DEFINE_CONST_FUN_OBJ_1(mp_set_volume_dB_obj, mp_set_volume_dB);
+
+STATIC mp_obj_t mp_headphones_adjust_volume_dB(mp_obj_t vol_dB) {
+    return mp_obj_new_float(audio_headphones_adjust_volume_dB(mp_obj_get_float(vol_dB)));
+}
+STATIC MP_DEFINE_CONST_FUN_OBJ_1(mp_headphones_adjust_volume_dB_obj, mp_headphones_adjust_volume_dB);
+
+STATIC mp_obj_t mp_speaker_adjust_volume_dB(mp_obj_t vol_dB) {
+    return mp_obj_new_float(audio_speaker_adjust_volume_dB(mp_obj_get_float(vol_dB)));
+}
+STATIC MP_DEFINE_CONST_FUN_OBJ_1(mp_speaker_adjust_volume_dB_obj, mp_speaker_adjust_volume_dB);
+
+STATIC mp_obj_t mp_adjust_volume_dB(mp_obj_t vol_dB) {
+    return mp_obj_new_float(audio_adjust_volume_dB(mp_obj_get_float(vol_dB)));
+}
+STATIC MP_DEFINE_CONST_FUN_OBJ_1(mp_adjust_volume_dB_obj, mp_adjust_volume_dB);
+
+STATIC mp_obj_t mp_headphones_get_volume_dB() {
+    return mp_obj_new_float(audio_headphones_get_volume_dB());
+}
+STATIC MP_DEFINE_CONST_FUN_OBJ_0(mp_headphones_get_volume_dB_obj, mp_headphones_get_volume_dB);
+
+STATIC mp_obj_t mp_speaker_get_volume_dB() {
+    return mp_obj_new_float(audio_speaker_get_volume_dB());
+}
+STATIC MP_DEFINE_CONST_FUN_OBJ_0(mp_speaker_get_volume_dB_obj, mp_speaker_get_volume_dB);
+
+STATIC mp_obj_t mp_get_volume_dB() {
+    return mp_obj_new_float(audio_get_volume_dB());
+}
+STATIC MP_DEFINE_CONST_FUN_OBJ_0(mp_get_volume_dB_obj, mp_get_volume_dB);
+
+STATIC mp_obj_t mp_headphones_get_mute() {
+    return mp_obj_new_int(audio_headphones_get_mute());
+}
+STATIC MP_DEFINE_CONST_FUN_OBJ_0(mp_headphones_get_mute_obj, mp_headphones_get_mute);
+
+STATIC mp_obj_t mp_speaker_get_mute() {
+    return mp_obj_new_int(audio_speaker_get_mute());
+}
+STATIC MP_DEFINE_CONST_FUN_OBJ_0(mp_speaker_get_mute_obj, mp_speaker_get_mute);
+
+STATIC mp_obj_t mp_get_mute() {
+    return mp_obj_new_int(audio_get_mute());
+}
+STATIC MP_DEFINE_CONST_FUN_OBJ_0(mp_get_mute_obj, mp_get_mute);
+
+STATIC mp_obj_t mp_headphones_set_mute(mp_obj_t mute) {
+    audio_headphones_set_mute(mp_obj_get_int(mute));
+    return mp_const_none;
+}
+STATIC MP_DEFINE_CONST_FUN_OBJ_1(mp_headphones_set_mute_obj, mp_headphones_set_mute);
+
+STATIC mp_obj_t mp_speaker_set_mute(mp_obj_t mute) {
+    audio_speaker_set_mute(mp_obj_get_int(mute));
+    return mp_const_none;
+}
+STATIC MP_DEFINE_CONST_FUN_OBJ_1(mp_speaker_set_mute_obj, mp_speaker_set_mute);
+
+STATIC mp_obj_t mp_set_mute(mp_obj_t mute) {
+    audio_set_mute(mp_obj_get_int(mute));
+    return mp_const_none;
+}
+STATIC MP_DEFINE_CONST_FUN_OBJ_1(mp_set_mute_obj, mp_set_mute);
+
 STATIC const mp_rom_map_elem_t mp_module_audio_globals_table[] = {
     { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_audio) },
     { MP_ROM_QSTR(MP_QSTR_headset_is_connected), MP_ROM_PTR(&mp_headset_is_connected_obj) },
     { MP_ROM_QSTR(MP_QSTR_headphones_are_connected), MP_ROM_PTR(&mp_headphones_are_connected_obj) },
+    { MP_ROM_QSTR(MP_QSTR_headphones_detection_override), MP_ROM_PTR(&mp_headphones_detection_override_obj) },
+    { MP_ROM_QSTR(MP_QSTR_headphones_set_volume_dB), MP_ROM_PTR(&mp_headphones_set_volume_dB_obj) },
+    { MP_ROM_QSTR(MP_QSTR_speaker_set_volume_dB), MP_ROM_PTR(&mp_speaker_set_volume_dB_obj) },
+    { MP_ROM_QSTR(MP_QSTR_set_volume_dB), MP_ROM_PTR(&mp_set_volume_dB_obj) },
+    { MP_ROM_QSTR(MP_QSTR_headphones_adjust_volume_dB), MP_ROM_PTR(&mp_headphones_adjust_volume_dB_obj) },
+    { MP_ROM_QSTR(MP_QSTR_speaker_adjust_volume_dB), MP_ROM_PTR(&mp_speaker_adjust_volume_dB_obj) },
+    { MP_ROM_QSTR(MP_QSTR_adjust_volume_dB), MP_ROM_PTR(&mp_adjust_volume_dB_obj) },
+    { MP_ROM_QSTR(MP_QSTR_headphones_get_volume_dB), MP_ROM_PTR(&mp_headphones_get_volume_dB_obj) },
+    { MP_ROM_QSTR(MP_QSTR_speaker_get_volume_dB), MP_ROM_PTR(&mp_speaker_get_volume_dB_obj) },
+    { MP_ROM_QSTR(MP_QSTR_get_volume_dB), MP_ROM_PTR(&mp_get_volume_dB_obj) },
+    { MP_ROM_QSTR(MP_QSTR_headphones_get_mute), MP_ROM_PTR(&mp_headphones_get_mute_obj) },
+    { MP_ROM_QSTR(MP_QSTR_speaker_get_mute), MP_ROM_PTR(&mp_speaker_get_mute_obj) },
+    { MP_ROM_QSTR(MP_QSTR_get_mute), MP_ROM_PTR(&mp_get_mute_obj) },
+    { MP_ROM_QSTR(MP_QSTR_headphones_set_mute), MP_ROM_PTR(&mp_headphones_set_mute_obj) },
+    { MP_ROM_QSTR(MP_QSTR_speaker_set_mute), MP_ROM_PTR(&mp_speaker_set_mute_obj) },
+    { MP_ROM_QSTR(MP_QSTR_set_mute), MP_ROM_PTR(&mp_set_mute_obj) },
 };
 
 STATIC MP_DEFINE_CONST_DICT(mp_module_audio_globals, mp_module_audio_globals_table);