From ba2407d6911f37aa097e8425aa82f32db84ab114 Mon Sep 17 00:00:00 2001 From: Mark Thompson <129641948+NotherNgineer@users.noreply.github.com> Date: Sat, 17 Jun 2023 16:15:21 -0500 Subject: [PATCH] Persistent audio mute support (#1161) * Persistent speaker mute support #1100 * Moving persistent mute support from WM8731/AK4951 codec files to audio.cpp module for simplification --- firmware/application/apps/pocsag_app.cpp | 1 - firmware/application/apps/ui_settings.cpp | 10 +-- firmware/application/apps/ui_settings.hpp | 24 +++--- firmware/application/apps/ui_sonde.cpp | 1 - firmware/application/audio.cpp | 49 +++++++++--- firmware/application/audio.hpp | 2 + firmware/application/portapack.cpp | 17 ++-- firmware/application/portapack.hpp | 4 +- firmware/application/ui_navigation.cpp | 34 ++++---- .../common/portapack_persistent_memory.cpp | 79 +++++++++++++++---- .../common/portapack_persistent_memory.hpp | 6 +- firmware/common/wm8731.hpp | 12 ++- 12 files changed, 159 insertions(+), 80 deletions(-) diff --git a/firmware/application/apps/pocsag_app.cpp b/firmware/application/apps/pocsag_app.cpp index 2371a406..671ed102 100644 --- a/firmware/application/apps/pocsag_app.cpp +++ b/firmware/application/apps/pocsag_app.cpp @@ -102,7 +102,6 @@ POCSAGAppView::POCSAGAppView(NavigationView& nav) { logger->append(LOG_ROOT_DIR "/POCSAG.TXT"); audio::output::start(); - audio::output::unmute(); baseband::set_pocsag(); } diff --git a/firmware/application/apps/ui_settings.cpp b/firmware/application/apps/ui_settings.cpp index bf7335d7..75dc5f74 100644 --- a/firmware/application/apps/ui_settings.cpp +++ b/firmware/application/apps/ui_settings.cpp @@ -235,7 +235,6 @@ SetFrequencyCorrectionModel SetRadioView::form_collect() { SetUIView::SetUIView(NavigationView& nav) { add_children({&checkbox_disable_touchscreen, - &checkbox_speaker, &checkbox_bloff, &options_bloff, &checkbox_showsplash, @@ -246,7 +245,6 @@ SetUIView::SetUIView(NavigationView& nav) { &button_cancel}); checkbox_disable_touchscreen.set_value(persistent_memory::disable_touchscreen()); - checkbox_speaker.set_value(persistent_memory::config_speaker()); checkbox_showsplash.set_value(persistent_memory::config_splash()); checkbox_showclock.set_value(!persistent_memory::hide_clock()); checkbox_guireturnflag.set_value(persistent_memory::show_gui_return_icon()); @@ -272,11 +270,6 @@ SetUIView::SetUIView(NavigationView& nav) { persistent_memory::set_clock_with_date(false); } - if (checkbox_speaker.value()) audio::output::speaker_mute(); // Just mute audio if speaker is disabled - persistent_memory::set_config_speaker(checkbox_speaker.value()); // Store Speaker status - StatusRefreshMessage message{}; // Refresh status bar with/out speaker - EventDispatcher::send_message(message); - persistent_memory::set_config_splash(checkbox_showsplash.value()); persistent_memory::set_clock_hidden(!checkbox_showclock.value()); persistent_memory::set_gui_return_icon(checkbox_guireturnflag.value()); @@ -524,13 +517,16 @@ 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(persistent_memory::tone_mix()); + checkbox_speaker_disable.set_value(persistent_memory::config_speaker_disable()); button_save.on_select = [&nav, this](Button&) { persistent_memory::set_tone_mix(field_tone_mix.value()); + persistent_memory::set_config_speaker_disable(checkbox_speaker_disable.value()); nav.pop(); }; diff --git a/firmware/application/apps/ui_settings.hpp b/firmware/application/apps/ui_settings.hpp index 1611f399..c7558864 100644 --- a/firmware/application/apps/ui_settings.hpp +++ b/firmware/application/apps/ui_settings.hpp @@ -212,17 +212,12 @@ class SetUIView : public View { 20, "Disable touchscreen"}; - Checkbox checkbox_speaker{ - {3 * 8, 4 * 16}, - 20, - "Hide H1 Speaker option"}; - Checkbox checkbox_bloff{ - {3 * 8, 6 * 16}, + {3 * 8, 4 * 16}, 20, "Backlight off after:"}; OptionsField options_bloff{ - {52, 7 * 16 + 8}, + {60, 5 * 16 + 8}, 20, { {"5 seconds", backlight_timeout_t::Timeout5Sec}, @@ -236,25 +231,25 @@ class SetUIView : public View { }}; Checkbox checkbox_showsplash{ - {3 * 8, 9 * 16}, + {3 * 8, 7 * 16}, 20, "Show splash"}; Checkbox checkbox_showclock{ - {3 * 8, 11 * 16}, + {3 * 8, 9 * 16}, 20, "Show clock with:"}; OptionsField options_clockformat{ - {52, 12 * 16 + 8}, + {60, 10 * 16 + 8}, 20, {{"time only", 0}, {"time and date", 1}}}; Checkbox checkbox_guireturnflag{ - {3 * 8, 14 * 16}, + {3 * 8, 12 * 16}, 25, - "add return icon in GUI"}; + "Show return icon in GUI"}; Button button_save{ {2 * 8, 16 * 16, 12 * 8, 32}, @@ -393,6 +388,11 @@ 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/apps/ui_sonde.cpp b/firmware/application/apps/ui_sonde.cpp index ca19e2ac..6a61e2e7 100644 --- a/firmware/application/apps/ui_sonde.cpp +++ b/firmware/application/apps/ui_sonde.cpp @@ -117,7 +117,6 @@ SondeView::SondeView(NavigationView& nav) { logger->append(LOG_ROOT_DIR "/SONDE.TXT"); audio::output::start(); - audio::output::speaker_unmute(); // inject a PitchRSSIConfigureMessage in order to arm // the pitch rssi events that will be used by the diff --git a/firmware/application/audio.cpp b/firmware/application/audio.cpp index c4366bf0..cfd37b28 100644 --- a/firmware/application/audio.cpp +++ b/firmware/application/audio.cpp @@ -134,6 +134,10 @@ static audio::Codec* audio_codec = nullptr; namespace output { +static bool cfg_speaker_disable = false; +static bool nav_requested_mute = false; +static bool app_requested_mute = false; + void start() { i2s::i2s0::tx_start(); unmute(); @@ -145,23 +149,46 @@ void stop() { } void mute() { + app_requested_mute = true; i2s::i2s0::tx_mute(); audio_codec->headphone_disable(); -} - -void unmute() { - i2s::i2s0::tx_unmute(); - audio_codec->headphone_enable(); -} - -void speaker_mute() { - i2s::i2s0::tx_mute(); audio_codec->speaker_disable(); } +void unmute() { + app_requested_mute = false; + if (!nav_requested_mute) { + i2s::i2s0::tx_unmute(); + audio_codec->headphone_enable(); + if (!cfg_speaker_disable) { + audio_codec->speaker_enable(); + } + } +} + +void speaker_disable() { + cfg_speaker_disable = true; + audio_codec->speaker_disable(); +} + +void speaker_enable() { + cfg_speaker_disable = false; +} + +// The following functions are used by the navigation-bar Speaker Mute only, +// and override all other audio mute/unmute requests from apps +void speaker_mute() { + nav_requested_mute = true; + i2s::i2s0::tx_mute(); + audio_codec->speaker_disable(); + audio_codec->headphone_disable(); +} + void speaker_unmute() { - i2s::i2s0::tx_unmute(); - audio_codec->speaker_enable(); + nav_requested_mute = false; + if (!app_requested_mute) { + unmute(); + } } } /* namespace output */ diff --git a/firmware/application/audio.hpp b/firmware/application/audio.hpp index e817889b..2ab41a43 100644 --- a/firmware/application/audio.hpp +++ b/firmware/application/audio.hpp @@ -65,6 +65,8 @@ void stop(); void mute(); void unmute(); +void speaker_disable(); +void speaker_enable(); void speaker_mute(); void speaker_unmute(); diff --git a/firmware/application/portapack.cpp b/firmware/application/portapack.cpp index c5b1a279..a89feb62 100644 --- a/firmware/application/portapack.cpp +++ b/firmware/application/portapack.cpp @@ -96,13 +96,18 @@ bool get_antenna_bias() { return antenna_bias; } -bool speaker_mode{false}; -void set_speaker_mode(const bool v) { - speaker_mode = v; - if (speaker_mode) - audio::output::speaker_unmute(); - else +void set_audio_mute(const bool v) { + if (v) audio::output::speaker_mute(); + else + audio::output::speaker_unmute(); +} + +void set_speaker_disable(const bool v) { + if (v) + audio::output::speaker_disable(); + else + audio::output::speaker_enable(); } static constexpr uint32_t systick_count(const uint32_t clock_source_f) { diff --git a/firmware/application/portapack.hpp b/firmware/application/portapack.hpp index 5b27bdcc..d6ba0e15 100644 --- a/firmware/application/portapack.hpp +++ b/firmware/application/portapack.hpp @@ -53,8 +53,8 @@ extern ClockManager clock_manager; extern ReceiverModel receiver_model; extern TransmitterModel transmitter_model; -extern bool speaker_mode; -void set_speaker_mode(const bool v); +void set_audio_mute(const bool v); +void set_speaker_disable(const bool v); extern uint32_t bl_tick_counter; extern bool antenna_bias; diff --git a/firmware/application/ui_navigation.cpp b/firmware/application/ui_navigation.cpp index 9166cd5a..042c5476 100644 --- a/firmware/application/ui_navigation.cpp +++ b/firmware/application/ui_navigation.cpp @@ -126,11 +126,6 @@ SystemStatusView::SystemStatusView( portapack::persistent_memory::load_persistent_settings_from_file(); } - if (portapack::persistent_memory::config_speaker()) - button_speaker.hidden(false); - else - button_speaker.hidden(true); - if (portapack::persistent_memory::config_hide_converter()) { button_converter.hidden(true); } else { @@ -217,16 +212,16 @@ void SystemStatusView::refresh() { button_converter.set_foreground(Color::light_grey()); } } - // Poke tunings to take converter change in account. - receiver_model.set_target_frequency(receiver_model.target_frequency()); - transmitter_model.set_target_frequency(transmitter_model.target_frequency()); - if (!portapack::persistent_memory::config_speaker()) { + portapack::set_speaker_disable(portapack::persistent_memory::config_speaker_disable()); + portapack::set_audio_mute(portapack::persistent_memory::config_audio_mute()); + + if (portapack::persistent_memory::config_audio_mute()) { button_speaker.set_foreground(Color::light_grey()); button_speaker.set_bitmap(&bitmap_icon_speaker_mute); - button_speaker.hidden(false); } else { - button_speaker.hidden(true); + button_speaker.set_foreground(Color::green()); + button_speaker.set_bitmap(&bitmap_icon_speaker); } if (portapack::get_antenna_bias()) { @@ -285,20 +280,21 @@ void SystemStatusView::on_converter() { button_converter.set_foreground(Color::light_grey()); } - // Poke to update tuning. + // Poke to update tuning + // NOTE: Code assumes here that a TX app isn't active, since RX & TX have diff tuning offsets + // (and there's only one tuner in the radio so can't update tuner for both). receiver_model.set_target_frequency(receiver_model.target_frequency()); } void SystemStatusView::on_speaker() { - if (!portapack::speaker_mode) { - portapack::set_speaker_mode(true); - button_speaker.set_foreground(Color::green()); - button_speaker.set_bitmap(&bitmap_icon_speaker); + if (portapack::persistent_memory::config_audio_mute()) { + portapack::set_audio_mute(false); + portapack::persistent_memory::set_config_audio_mute(false); } else { - portapack::set_speaker_mode(false); - button_speaker.set_foreground(Color::light_grey()); - button_speaker.set_bitmap(&bitmap_icon_speaker_mute); + portapack::set_audio_mute(true); + portapack::persistent_memory::set_config_audio_mute(true); } + refresh(); } void SystemStatusView::on_stealth() { diff --git a/firmware/common/portapack_persistent_memory.cpp b/firmware/common/portapack_persistent_memory.cpp index 54fd1cc0..f4ef2909 100644 --- a/firmware/common/portapack_persistent_memory.cpp +++ b/firmware/common/portapack_persistent_memory.cpp @@ -97,7 +97,7 @@ struct ui_config_t { HideClock = 25, ClockWithDate = 26, ClkOutEnabled = 27, - ConfigSpeaker = 28, + ConfigSpeakerHidden = 28, StealthMode = 29, ConfigLogin = 30, ConfigSplash = 31, @@ -216,14 +216,6 @@ struct ui_config_t { bit_write(bits_t::ClkOutEnabled, v); } - constexpr bool config_speaker() const { - return bit_read(bits_t::ConfigSpeaker); - } - - constexpr void set_config_speaker(bool v) { - bit_write(bits_t::ConfigSpeaker, v); - } - constexpr bool stealth_mode() const { return bit_read(bits_t::StealthMode); } @@ -250,7 +242,52 @@ struct ui_config_t { constexpr ui_config_t() : values( - (1 << ConfigSplash) | (1 << ConfigSpeaker) | (clkout_freq_reset_value << ClkoutFreqLSB) | (7 << BacklightTimeoutLSB)) { + (1 << ConfigSplash) | (clkout_freq_reset_value << ClkoutFreqLSB) | (7 << BacklightTimeoutLSB)) { + } +}; + +struct misc_config_t { + private: + enum bits_t { + ConfigAudioMute = 0, + ConfigSpeakerDisable = 1, + }; + + // misc_config_t bits: + // ConfigAudioMute = set to mute all audio output (speakers & headphones) + // ConfigSpeakerDisable = set to disable only the speaker and leave headphones enabled (only supported on AK4951 codec) + + uint32_t values; + + constexpr bool bit_read(const bits_t n) const { + return ((values >> n) & 1) != 0; + } + + constexpr void bit_write(const bits_t n, const bool v) { + if (bit_read(n) != v) { + values ^= 1 << n; + } + } + + public: + constexpr bool config_audio_mute() const { + return bit_read(bits_t::ConfigAudioMute); + } + + constexpr void set_config_audio_mute(bool v) { + bit_write(bits_t::ConfigAudioMute, v); + } + + constexpr bool config_speaker_disable() const { + return bit_read(bits_t::ConfigSpeakerDisable); + } + + constexpr void set_config_speaker_disable(bool v) { + bit_write(bits_t::ConfigSpeakerDisable, v); + } + + constexpr misc_config_t() + : values(0) { } }; @@ -311,6 +348,9 @@ struct data_t { // Headphone volume in centibels. int32_t headphone_volume_cb; + // Misc flags + misc_config_t misc_config; + constexpr data_t() : structure_version(data_structure_version_enum::VERSION_CURRENT), target_frequency(target_frequency_reset_value), @@ -348,7 +388,8 @@ struct data_t { frequency_tx_correction(0), updown_frequency_tx_correction(0), encoder_dial_sensitivity(0), - headphone_volume_cb(-600) { + headphone_volume_cb(-600), + misc_config() { } }; @@ -608,8 +649,12 @@ bool clkout_enabled() { return data->ui_config.clkout_enabled(); } -bool config_speaker() { - return data->ui_config.config_speaker(); +bool config_audio_mute() { + return data->misc_config.config_audio_mute(); +} + +bool config_speaker_disable() { + return data->misc_config.config_speaker_disable(); } bool stealth_mode() { @@ -664,8 +709,12 @@ void set_clkout_enabled(bool v) { data->ui_config.set_clkout_enabled(v); } -void set_config_speaker(bool v) { - data->ui_config.set_config_speaker(v); +void set_config_audio_mute(bool v) { + data->misc_config.set_config_audio_mute(v); +} + +void set_config_speaker_disable(bool v) { + data->misc_config.set_config_speaker_disable(v); } void set_stealth_mode(bool v) { diff --git a/firmware/common/portapack_persistent_memory.hpp b/firmware/common/portapack_persistent_memory.hpp index 7d4d4754..49a70f91 100644 --- a/firmware/common/portapack_persistent_memory.hpp +++ b/firmware/common/portapack_persistent_memory.hpp @@ -184,7 +184,8 @@ bool show_bigger_qr_code(); bool hide_clock(); bool clock_with_date(); bool config_login(); -bool config_speaker(); +bool config_audio_mute(); +bool config_speaker_disable(); backlight_config_t config_backlight_timer(); bool disable_touchscreen(); @@ -212,7 +213,8 @@ void set_config_freq_rx_correction(uint32_t v); void set_clock_hidden(bool v); void set_clock_with_date(bool v); void set_config_login(bool v); -void set_config_speaker(bool v); +void set_config_audio_mute(bool v); +void set_config_speaker_disable(bool v); void set_config_backlight_timer(const backlight_config_t& new_value); void set_disable_touchscreen(bool v); uint8_t config_encoder_dial_sensitivity(); diff --git a/firmware/common/wm8731.hpp b/firmware/common/wm8731.hpp index 4668a279..4ad22926 100644 --- a/firmware/common/wm8731.hpp +++ b/firmware/common/wm8731.hpp @@ -306,8 +306,7 @@ class WM8731 : public audio::Codec { }); } - void set_headphone_volume(const volume_t volume) override { - headphone_volume = volume; + void set_wm_headphone_volume(const volume_t volume) { const auto normalized = headphone_gain_range().normalize(volume); auto n = normalized.centibel() / 10; @@ -319,6 +318,11 @@ class WM8731 : public audio::Codec { }); } + void set_headphone_volume(const volume_t volume) override { + headphone_volume = volume; + set_wm_headphone_volume(volume); + } + volume_range_t headphone_gain_range() const override { return {-121.0_dB, 6.0_dB}; } @@ -328,11 +332,11 @@ class WM8731 : public audio::Codec { } void headphone_mute() { - set_headphone_volume(headphone_gain_range().min); + set_wm_headphone_volume(headphone_gain_range().min); } void headphone_enable() override { - set_headphone_volume(headphone_volume); + set_wm_headphone_volume(headphone_volume); } void headphone_disable() override {