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);