diff --git a/firmware/application/apps/ui_settings.cpp b/firmware/application/apps/ui_settings.cpp index 3352c2a8..79dce1eb 100644 --- a/firmware/application/apps/ui_settings.cpp +++ b/firmware/application/apps/ui_settings.cpp @@ -259,11 +259,16 @@ SetUIView::SetUIView(NavigationView& nav) { &toggle_converter, &toggle_bias_tee, &toggle_clock, - &toggle_speaker, + &toggle_mute, &toggle_sd_card, &button_save, &button_cancel}); + // Display "Disable speaker" option only if AK4951 Codec which has separate speaker/headphone control + if (audio::speaker_disable_supported()) { + add_child(&toggle_speaker); + } + checkbox_disable_touchscreen.set_value(pmem::disable_touchscreen()); checkbox_showsplash.set_value(pmem::config_splash()); checkbox_showclock.set_value(!pmem::hide_clock()); @@ -287,6 +292,7 @@ SetUIView::SetUIView(NavigationView& nav) { toggle_bias_tee.set_value(!pmem::ui_hide_bias_tee()); toggle_clock.set_value(!pmem::ui_hide_clock()); toggle_speaker.set_value(!pmem::ui_hide_speaker()); + toggle_mute.set_value(!pmem::ui_hide_mute()); toggle_sd_card.set_value(!pmem::ui_hide_sd_card()); button_save.on_select = [&nav, this](Button&) { @@ -312,6 +318,7 @@ SetUIView::SetUIView(NavigationView& nav) { pmem::set_ui_hide_bias_tee(!toggle_bias_tee.value()); pmem::set_ui_hide_clock(!toggle_clock.value()); pmem::set_ui_hide_speaker(!toggle_speaker.value()); + pmem::set_ui_hide_mute(!toggle_mute.value()); pmem::set_ui_hide_sd_card(!toggle_sd_card.value()); send_system_refresh(); @@ -549,16 +556,13 @@ void SetPersistentMemoryView::focus() { SetAudioView::SetAudioView(NavigationView& nav) { add_children({&labels, &field_tone_mix, - &checkbox_speaker_disable, &button_save, &button_cancel}); field_tone_mix.set_value(pmem::tone_mix()); - checkbox_speaker_disable.set_value(pmem::config_speaker_disable()); button_save.on_select = [&nav, this](Button&) { pmem::set_tone_mix(field_tone_mix.value()); - pmem::set_config_speaker_disable(checkbox_speaker_disable.value()); audio::output::update_audio_mute(); nav.pop(); }; diff --git a/firmware/application/apps/ui_settings.hpp b/firmware/application/apps/ui_settings.hpp index 54ca3079..2d90c830 100644 --- a/firmware/application/apps/ui_settings.hpp +++ b/firmware/application/apps/ui_settings.hpp @@ -280,12 +280,16 @@ class SetUIView : public View { {17 * 8, 14 * 16 + 2, 8, 16}, &bitmap_icon_clk_ext}; - ImageToggle toggle_speaker{ + ImageToggle toggle_mute{ {18 * 8, 14 * 16 + 2, 16, 16}, + &bitmap_icon_speaker_and_headphones_mute}; + + ImageToggle toggle_speaker{ + {20 * 8, 14 * 16 + 2, 16, 16}, &bitmap_icon_speaker_mute}; ImageToggle toggle_sd_card{ - {20 * 8, 14 * 16 + 2, 16, 16}, + {22 * 8, 14 * 16 + 2, 16, 16}, &bitmap_sd_card_ok}; Button button_save{ @@ -425,11 +429,6 @@ class SetAudioView : public View { 1, '0'}; - Checkbox checkbox_speaker_disable{ - {2 * 8, 6 * 16}, - 25, - "Disable AK speaker amp"}; - Button button_save{ {2 * 8, 16 * 16, 12 * 8, 32}, "Save"}; diff --git a/firmware/application/audio.cpp b/firmware/application/audio.cpp index 7b414a59..d7f1036d 100644 --- a/firmware/application/audio.cpp +++ b/firmware/application/audio.cpp @@ -184,6 +184,9 @@ void speaker_unmute() { void update_audio_mute() { cfg_speaker_disable = portapack::persistent_memory::config_speaker_disable(); + if (cfg_speaker_disable) { + audio_codec->speaker_disable(); + } if (portapack::persistent_memory::config_audio_mute()) speaker_mute(); @@ -269,4 +272,8 @@ void set_rate(const Rate rate) { clock_manager.set_base_audio_clock_divider(toUType(rate)); } +bool speaker_disable_supported() { + return audio_codec->speaker_disable_supported(); +} + } /* namespace audio */ diff --git a/firmware/application/audio.hpp b/firmware/application/audio.hpp index 86cad12c..bf09f753 100644 --- a/firmware/application/audio.hpp +++ b/firmware/application/audio.hpp @@ -43,6 +43,7 @@ class Codec { virtual void speaker_enable() = 0; virtual void speaker_disable() = 0; + virtual bool speaker_disable_supported() const = 0; virtual void headphone_enable() = 0; virtual void headphone_disable() = 0; @@ -107,6 +108,7 @@ size_t reg_bits(); void init(audio::Codec* const codec); void shutdown(); +bool speaker_disable_supported(); enum class Rate { Hz_12000 = 4, diff --git a/firmware/application/bitmap.hpp b/firmware/application/bitmap.hpp index a1073327..9e2a23b3 100644 --- a/firmware/application/bitmap.hpp +++ b/firmware/application/bitmap.hpp @@ -5493,6 +5493,82 @@ static constexpr Bitmap bitmap_arrow_right{ {16, 16}, bitmap_arrow_right_data}; +static constexpr uint8_t bitmap_icon_speaker_and_headphones_data[] = { + 0x20, + 0x10, + 0x30, + 0x20, + 0x38, + 0x44, + 0x3E, + 0x48, + 0x3E, + 0x91, + 0x3E, + 0x92, + 0x38, + 0x92, + 0x30, + 0x92, + 0x20, + 0x92, + 0x00, + 0x92, + 0x30, + 0x91, + 0x48, + 0x48, + 0x84, + 0x44, + 0x84, + 0x20, + 0x86, + 0x11, + 0x86, + 0x01, +}; +static constexpr Bitmap bitmap_icon_speaker_and_headphones{ + {16, 16}, + bitmap_icon_speaker_and_headphones_data}; + +static constexpr uint8_t bitmap_icon_speaker_and_headphones_mute_data[] = { + 0x40, + 0x00, + 0x60, + 0x44, + 0x70, + 0x6C, + 0x7C, + 0x38, + 0x7C, + 0x10, + 0x7C, + 0x38, + 0x70, + 0x6C, + 0x60, + 0x44, + 0x40, + 0x00, + 0x00, + 0x44, + 0x30, + 0x6C, + 0x48, + 0x38, + 0x84, + 0x10, + 0x84, + 0x38, + 0x86, + 0x6D, + 0x86, + 0x45, +}; +static constexpr Bitmap bitmap_icon_speaker_and_headphones_mute{ + {16, 16}, + bitmap_icon_speaker_and_headphones_mute_data}; + } /* namespace ui */ #endif /*__BITMAP_HPP__*/ diff --git a/firmware/application/ui_navigation.cpp b/firmware/application/ui_navigation.cpp index 740b19ad..6233ebb8 100644 --- a/firmware/application/ui_navigation.cpp +++ b/firmware/application/ui_navigation.cpp @@ -183,6 +183,10 @@ SystemStatusView::SystemStatusView( this->on_speaker(); }; + button_mute.on_select = [this](ImageButton&) { + this->on_mute(); + }; + button_stealth.on_select = [this](ImageButton&) { this->on_stealth(); }; @@ -218,13 +222,18 @@ void SystemStatusView::refresh() { if (!pmem::ui_hide_converter()) status_icons.add(&button_converter); if (!pmem::ui_hide_bias_tee()) status_icons.add(&button_bias_tee); if (!pmem::ui_hide_clock()) status_icons.add(&button_clock_status); - if (!pmem::ui_hide_speaker()) status_icons.add(&button_speaker); + if (!pmem::ui_hide_mute()) status_icons.add(&button_mute); + + // Display "Disable speaker" icon only if AK4951 Codec which has separate speaker/headphone control + if (audio::speaker_disable_supported() && !pmem::ui_hide_speaker()) + status_icons.add(&button_speaker); + if (!pmem::ui_hide_sd_card()) status_icons.add(&sd_card_status_view); status_icons.update_layout(); // Update icon display (try to keep all in on place). - // Speaker - if (pmem::config_audio_mute()) { + // Speaker Enable/Disable (AK4951 boards only) + if (pmem::config_speaker_disable()) { button_speaker.set_foreground(Color::light_grey()); button_speaker.set_bitmap(&bitmap_icon_speaker_mute); } else { @@ -232,6 +241,15 @@ void SystemStatusView::refresh() { button_speaker.set_bitmap(&bitmap_icon_speaker); } + // Audio Mute (both headphones & speaker) + if (pmem::config_audio_mute()) { + button_mute.set_foreground(Color::light_grey()); + button_mute.set_bitmap(&bitmap_icon_speaker_and_headphones_mute); + } else { + button_mute.set_foreground(Color::green()); + button_mute.set_bitmap(&bitmap_icon_speaker_and_headphones); + } + // Clock status bool external_clk = portapack::clock_manager.get_reference().source == ClockManager::ReferenceSource::External; button_clock_status.set_bitmap(external_clk ? &bitmap_icon_clk_ext : &bitmap_icon_clk_int); @@ -295,6 +313,12 @@ void SystemStatusView::on_converter() { } void SystemStatusView::on_speaker() { + pmem::set_config_speaker_disable(!pmem::config_speaker_disable()); + audio::output::update_audio_mute(); + refresh(); +} + +void SystemStatusView::on_mute() { pmem::set_config_audio_mute(!pmem::config_audio_mute()); audio::output::update_audio_mute(); refresh(); diff --git a/firmware/application/ui_navigation.hpp b/firmware/application/ui_navigation.hpp index 312ec833..29174e6a 100644 --- a/firmware/application/ui_navigation.hpp +++ b/firmware/application/ui_navigation.hpp @@ -185,7 +185,13 @@ class SystemStatusView : public View { // TODO: Convert to ImageToggle buttons. ImageButton button_speaker{ {0, 0, 2 * 8, 1 * 16}, - &bitmap_icon_speaker_mute, + &bitmap_icon_speaker, + Color::light_grey(), + Color::dark_grey()}; + + ImageButton button_mute{ + {0, 0, 2 * 8, 1 * 16}, + &bitmap_icon_speaker_and_headphones, Color::light_grey(), Color::dark_grey()}; @@ -230,6 +236,7 @@ class SystemStatusView : public View { void on_converter(); void on_speaker(); + void on_mute(); void on_stealth(); void on_bias_tee(); void on_camera(); diff --git a/firmware/common/ak4951.hpp b/firmware/common/ak4951.hpp index c90b858d..0fc69eca 100644 --- a/firmware/common/ak4951.hpp +++ b/firmware/common/ak4951.hpp @@ -837,6 +837,10 @@ class AK4951 : public audio::Codec { void speaker_enable(); void speaker_disable(); + bool speaker_disable_supported() const override { + return true; + } + void set_headphone_volume(const volume_t volume) override; void headphone_mute(); diff --git a/firmware/common/portapack_persistent_memory.cpp b/firmware/common/portapack_persistent_memory.cpp index 945c1658..77618c1f 100644 --- a/firmware/common/portapack_persistent_memory.cpp +++ b/firmware/common/portapack_persistent_memory.cpp @@ -265,9 +265,11 @@ struct ui_config2_t { bool hide_clock : 1; bool hide_sd_card : 1; + bool hide_mute : 1; + bool UNUSED : 7; + uint8_t placeholder_1; uint8_t placeholder_2; - uint8_t placeholder_3; }; static_assert(sizeof(ui_config2_t) == sizeof(uint32_t)); @@ -861,6 +863,9 @@ void set_recon_auto_record_locked(const bool v) { bool ui_hide_speaker() { return data->ui_config2.hide_speaker; } +bool ui_hide_mute() { + return data->ui_config2.hide_mute; +} bool ui_hide_converter() { return data->ui_config2.hide_converter; } @@ -886,6 +891,9 @@ bool ui_hide_sd_card() { void set_ui_hide_speaker(bool v) { data->ui_config2.hide_speaker = v; } +void set_ui_hide_mute(bool v) { + data->ui_config2.hide_mute = v; +} void set_ui_hide_converter(bool v) { data->ui_config2.hide_converter = v; if (v) @@ -1084,6 +1092,7 @@ bool debug_dump() { pmem_dump_file.write_line("ui_config2 hide_bias_tee: " + to_string_dec_uint(data->ui_config2.hide_bias_tee)); pmem_dump_file.write_line("ui_config2 hide_clock: " + to_string_dec_uint(data->ui_config2.hide_clock)); pmem_dump_file.write_line("ui_config2 hide_sd_card: " + to_string_dec_uint(data->ui_config2.hide_sd_card)); + pmem_dump_file.write_line("ui_config2 hide_mute: " + to_string_dec_uint(data->ui_config2.hide_mute)); // misc_config bits pmem_dump_file.write_line("misc_config config_audio_mute: " + to_string_dec_int(config_audio_mute())); diff --git a/firmware/common/portapack_persistent_memory.hpp b/firmware/common/portapack_persistent_memory.hpp index 08a4c4df..4e3162da 100644 --- a/firmware/common/portapack_persistent_memory.hpp +++ b/firmware/common/portapack_persistent_memory.hpp @@ -252,6 +252,7 @@ void set_recon_match_mode(const bool v); /* UI Config 2 */ bool ui_hide_speaker(); +bool ui_hide_mute(); bool ui_hide_converter(); bool ui_hide_stealth(); bool ui_hide_camera(); @@ -260,6 +261,7 @@ bool ui_hide_bias_tee(); bool ui_hide_clock(); bool ui_hide_sd_card(); void set_ui_hide_speaker(bool v); +void set_ui_hide_mute(bool v); void set_ui_hide_converter(bool v); void set_ui_hide_stealth(bool v); void set_ui_hide_camera(bool v); diff --git a/firmware/common/wm8731.hpp b/firmware/common/wm8731.hpp index 4ad22926..197d8dcf 100644 --- a/firmware/common/wm8731.hpp +++ b/firmware/common/wm8731.hpp @@ -345,6 +345,9 @@ class WM8731 : public audio::Codec { void speaker_enable(){}; void speaker_disable(){}; + bool speaker_disable_supported() const override { + return false; + } void microphone_enable(int8_t wm8731_boost_GUI) override { microphone_mute(true); // c/m to reduce "plop noise" when changing wm8731_boost_GUI. diff --git a/firmware/graphics/icon_speaker_and_headphones.png b/firmware/graphics/icon_speaker_and_headphones.png new file mode 100644 index 00000000..701cfdea Binary files /dev/null and b/firmware/graphics/icon_speaker_and_headphones.png differ diff --git a/firmware/graphics/icon_speaker_and_headphones_mute.png b/firmware/graphics/icon_speaker_and_headphones_mute.png new file mode 100644 index 00000000..fe1bccf4 Binary files /dev/null and b/firmware/graphics/icon_speaker_and_headphones_mute.png differ