Fix hang in Audio when switching from Capture app, & bug fixes. (#1167)

* Fix hang in Audio when switching from Capture app
* Bug fixes
This commit is contained in:
Kyle Reed 2023-06-18 22:48:29 -07:00 committed by GitHub
parent 5daa0dfbb1
commit 7ebf3d3cdd
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
10 changed files with 78 additions and 89 deletions

View file

@ -22,21 +22,19 @@
#include "analog_audio_app.hpp" #include "analog_audio_app.hpp"
#include "audio.hpp"
#include "baseband_api.hpp" #include "baseband_api.hpp"
#include "file.hpp"
#include "freqman.hpp"
#include "portapack.hpp" #include "portapack.hpp"
#include "portapack_persistent_memory.hpp" #include "portapack_persistent_memory.hpp"
using namespace portapack; #include "string_format.hpp"
using namespace tonekey;
#include "audio.hpp"
#include "file.hpp"
#include "utility.hpp" #include "utility.hpp"
#include "string_format.hpp" #include "debug.hpp"
#include "freqman.hpp" using namespace portapack;
using namespace tonekey;
namespace ui { namespace ui {
@ -45,8 +43,8 @@ namespace ui {
static const Style& style_options_group = Styles::bg_blue; static const Style& style_options_group = Styles::bg_blue;
AMOptionsView::AMOptionsView( AMOptionsView::AMOptionsView(
const Rect parent_rect, Rect parent_rect,
const Style* const style) const Style* style)
: View{parent_rect} { : View{parent_rect} {
set_style(style); set_style(style);
@ -65,8 +63,8 @@ AMOptionsView::AMOptionsView(
/* NBFMOptionsView *******************************************************/ /* NBFMOptionsView *******************************************************/
NBFMOptionsView::NBFMOptionsView( NBFMOptionsView::NBFMOptionsView(
const Rect parent_rect, Rect parent_rect,
const Style* const style) const Style* style)
: View{parent_rect} { : View{parent_rect} {
set_style(style); set_style(style);
@ -90,8 +88,8 @@ NBFMOptionsView::NBFMOptionsView(
/* WFMOptionsView *******************************************************/ /* WFMOptionsView *******************************************************/
WFMOptionsView::WFMOptionsView( WFMOptionsView::WFMOptionsView(
const Rect parent_rect, Rect parent_rect,
const Style* const style) const Style* style)
: View{parent_rect} { : View{parent_rect} {
set_style(style); set_style(style);
@ -111,8 +109,8 @@ WFMOptionsView::WFMOptionsView(
SPECOptionsView::SPECOptionsView( SPECOptionsView::SPECOptionsView(
AnalogAudioView* view, AnalogAudioView* view,
const Rect parent_rect, Rect parent_rect,
const Style* const style) const Style* style)
: View{parent_rect} { : View{parent_rect} {
set_style(style); set_style(style);
@ -137,6 +135,10 @@ SPECOptionsView::SPECOptionsView(
AnalogAudioView::AnalogAudioView( AnalogAudioView::AnalogAudioView(
NavigationView& nav) NavigationView& nav)
: nav_(nav) { : nav_(nav) {
// A baseband image _must_ be running before
// interacting with the waterfall view.
baseband::run_image(portapack::spi_flash::image_tag_wideband_spectrum);
add_children({&rssi, add_children({&rssi,
&channel, &channel,
&audio, &audio,
@ -164,9 +166,12 @@ AnalogAudioView::AnalogAudioView(
this->on_show_options_rf_gain(); this->on_show_options_rf_gain();
}; };
const auto modulation = receiver_model.modulation(); auto modulation = receiver_model.modulation();
options_modulation.set_by_value(toUType(modulation)); // This app doesn't handle "Capture" mode.
if (modulation > ReceiverModel::Mode::SpectrumAnalysis)
modulation = ReceiverModel::Mode::SpectrumAnalysis;
options_modulation.set_by_value(toUType(modulation));
options_modulation.on_change = [this](size_t, OptionsField::value_t v) { options_modulation.on_change = [this](size_t, OptionsField::value_t v) {
this->on_modulation_changed(static_cast<ReceiverModel::Mode>(v)); this->on_modulation_changed(static_cast<ReceiverModel::Mode>(v));
}; };
@ -184,8 +189,9 @@ AnalogAudioView::AnalogAudioView(
audio::output::start(); audio::output::start();
update_modulation(static_cast<ReceiverModel::Mode>(modulation)); // This call starts the correct baseband image to run
on_modulation_changed(static_cast<ReceiverModel::Mode>(modulation)); // and sets the radio up as necessary for the given modulation.
on_modulation_changed(modulation);
} }
size_t AnalogAudioView::get_spec_bw_index() { size_t AnalogAudioView::get_spec_bw_index() {
@ -212,40 +218,15 @@ void AnalogAudioView::set_spec_trigger(uint16_t trigger) {
} }
AnalogAudioView::~AnalogAudioView() { AnalogAudioView::~AnalogAudioView() {
// // save app settings
// app_settings.rx_frequency = field_frequency.value();
// app_settings.lna = receiver_model.lna();
// app_settings.vga = receiver_model.vga();
// app_settings.rx_amp = receiver_model.rf_amp();
// app_settings.step = receiver_model.frequency_step();
// app_settings.modulation = (uint8_t)receiver_model.modulation();
// app_settings.am_config_index = receiver_model.am_configuration();
// app_settings.nbfm_config_index = receiver_model.nbfm_configuration();
// app_settings.wfm_config_index = receiver_model.wfm_configuration();
// app_settings.squelch = receiver_model.squelch_level();
// settings.save("rx_audio", &app_settings);
// TODO: Manipulating audio codec here, and in ui_receiver.cpp. Good to do
// both?
audio::output::stop(); audio::output::stop();
receiver_model.set_sampling_rate(3072000); // Just a hack to avoid hanging other apps if the last modulation was SPEC
receiver_model.disable(); receiver_model.disable();
baseband::shutdown(); baseband::shutdown();
} }
void AnalogAudioView::on_hide() { void AnalogAudioView::set_parent_rect(Rect new_parent_rect) {
// TODO: Terrible kludge because widget system doesn't notify Waterfall that
// it's being shown or hidden.
waterfall.on_hide();
View::on_hide();
}
void AnalogAudioView::set_parent_rect(const Rect new_parent_rect) {
View::set_parent_rect(new_parent_rect); View::set_parent_rect(new_parent_rect);
const ui::Rect waterfall_rect{0, header_height, new_parent_rect.width(), new_parent_rect.height() - header_height}; ui::Rect waterfall_rect{0, header_height, new_parent_rect.width(), new_parent_rect.height() - header_height};
waterfall.set_parent_rect(waterfall_rect); waterfall.set_parent_rect(waterfall_rect);
} }
@ -257,13 +238,15 @@ void AnalogAudioView::on_baseband_bandwidth_changed(uint32_t bandwidth_hz) {
receiver_model.set_baseband_bandwidth(bandwidth_hz); receiver_model.set_baseband_bandwidth(bandwidth_hz);
} }
void AnalogAudioView::on_modulation_changed(const ReceiverModel::Mode modulation) { void AnalogAudioView::on_modulation_changed(ReceiverModel::Mode modulation) {
// TODO: Terrible kludge because widget system doesn't notify Waterfall that // This app doesn't know what to do with "Capture" mode.
// it's being shown or hidden. if (modulation > ReceiverModel::Mode::SpectrumAnalysis)
waterfall.on_hide(); modulation = ReceiverModel::Mode::SpectrumAnalysis;
baseband::spectrum_streaming_stop();
update_modulation(modulation); update_modulation(modulation);
on_show_options_modulation(); on_show_options_modulation();
waterfall.on_show(); baseband::spectrum_streaming_start();
} }
void AnalogAudioView::remove_options_widget() { void AnalogAudioView::remove_options_widget() {
@ -315,7 +298,7 @@ void AnalogAudioView::on_show_options_rf_gain() {
void AnalogAudioView::on_show_options_modulation() { void AnalogAudioView::on_show_options_modulation() {
std::unique_ptr<Widget> widget; std::unique_ptr<Widget> widget;
const auto modulation = static_cast<ReceiverModel::Mode>(receiver_model.modulation()); const auto modulation = receiver_model.modulation();
switch (modulation) { switch (modulation) {
case ReceiverModel::Mode::AMAudio: case ReceiverModel::Mode::AMAudio:
widget = std::make_unique<AMOptionsView>(options_view_rect, &style_options_group); widget = std::make_unique<AMOptionsView>(options_view_rect, &style_options_group);
@ -342,6 +325,7 @@ void AnalogAudioView::on_show_options_modulation() {
break; break;
default: default:
chDbgPanic("Unhandled Mode");
break; break;
} }
@ -358,7 +342,7 @@ void AnalogAudioView::on_reference_ppm_correction_changed(int32_t v) {
persistent_memory::set_correction_ppb(v * 1000); persistent_memory::set_correction_ppb(v * 1000);
} }
void AnalogAudioView::update_modulation(const ReceiverModel::Mode modulation) { void AnalogAudioView::update_modulation(ReceiverModel::Mode modulation) {
audio::output::mute(); audio::output::mute();
record_view.stop(); record_view.stop();
@ -379,7 +363,8 @@ void AnalogAudioView::update_modulation(const ReceiverModel::Mode modulation) {
image_tag = portapack::spi_flash::image_tag_wideband_spectrum; image_tag = portapack::spi_flash::image_tag_wideband_spectrum;
break; break;
default: default:
return; chDbgPanic("Unhandled Mode");
break;
} }
baseband::run_image(image_tag); baseband::run_image(image_tag);
@ -418,7 +403,7 @@ void AnalogAudioView::update_modulation(const ReceiverModel::Mode modulation) {
} }
} }
void AnalogAudioView::handle_coded_squelch(const uint32_t value) { void AnalogAudioView::handle_coded_squelch(uint32_t value) {
float diff, min_diff = value; float diff, min_diff = value;
size_t min_idx{0}; size_t min_idx{0};
size_t c; size_t c;

View file

@ -38,7 +38,7 @@ namespace ui {
class AMOptionsView : public View { class AMOptionsView : public View {
public: public:
AMOptionsView(const Rect parent_rect, const Style* const style); AMOptionsView(Rect parent_rect, const Style* style);
private: private:
Text label_config{ Text label_config{
@ -56,7 +56,7 @@ class AMOptionsView : public View {
class NBFMOptionsView : public View { class NBFMOptionsView : public View {
public: public:
NBFMOptionsView(const Rect parent_rect, const Style* const style); NBFMOptionsView(Rect parent_rect, const Style* style);
private: private:
Text label_config{ Text label_config{
@ -84,7 +84,7 @@ class NBFMOptionsView : public View {
class WFMOptionsView : public View { class WFMOptionsView : public View {
public: public:
WFMOptionsView(const Rect parent_rect, const Style* const style); WFMOptionsView(Rect parent_rect, const Style* style);
private: private:
Text label_config{ Text label_config{
@ -103,7 +103,7 @@ class AnalogAudioView;
class SPECOptionsView : public View { class SPECOptionsView : public View {
public: public:
SPECOptionsView(AnalogAudioView* view, const Rect parent_rect, const Style* const style); SPECOptionsView(AnalogAudioView* view, Rect parent_rect, const Style* style);
private: private:
Text label_config{ Text label_config{
@ -140,10 +140,7 @@ class AnalogAudioView : public View {
AnalogAudioView(NavigationView& nav); AnalogAudioView(NavigationView& nav);
~AnalogAudioView(); ~AnalogAudioView();
void on_hide() override; void set_parent_rect(Rect new_parent_rect) override;
void set_parent_rect(const Rect new_parent_rect) override;
void focus() override; void focus() override;
std::string title() const override { return "Audio RX"; }; std::string title() const override { return "Audio RX"; };
@ -170,8 +167,6 @@ class AnalogAudioView : public View {
uint32_t spec_bw = 20000000; uint32_t spec_bw = 20000000;
uint16_t spec_trigger = 63; uint16_t spec_trigger = 63;
// bool exit_on_squelch { false };
RSSI rssi{ RSSI rssi{
{21 * 8, 0, 6 * 8, 4}}; {21 * 8, 0, 6 * 8, 4}};
@ -221,7 +216,7 @@ class AnalogAudioView : public View {
spectrum::WaterfallWidget waterfall{true}; spectrum::WaterfallWidget waterfall{true};
void on_baseband_bandwidth_changed(uint32_t bandwidth_hz); void on_baseband_bandwidth_changed(uint32_t bandwidth_hz);
void on_modulation_changed(const ReceiverModel::Mode modulation); void on_modulation_changed(ReceiverModel::Mode modulation);
void on_show_options_frequency(); void on_show_options_frequency();
void on_show_options_rf_gain(); void on_show_options_rf_gain();
void on_show_options_modulation(); void on_show_options_modulation();
@ -231,22 +226,13 @@ class AnalogAudioView : public View {
void remove_options_widget(); void remove_options_widget();
void set_options_widget(std::unique_ptr<Widget> new_widget); void set_options_widget(std::unique_ptr<Widget> new_widget);
void update_modulation(const ReceiverModel::Mode modulation); void update_modulation(ReceiverModel::Mode modulation);
// void squelched(); void handle_coded_squelch(uint32_t value);
void handle_coded_squelch(const uint32_t value);
/*MessageHandlerRegistration message_handler_squelch_signal {
Message::ID::RequestSignal,
[this](const Message* const p) {
(void)p;
this->squelched();
}
};*/
MessageHandlerRegistration message_handler_coded_squelch{ MessageHandlerRegistration message_handler_coded_squelch{
Message::ID::CodedSquelch, Message::ID::CodedSquelch,
[this](const Message* const p) { [this](const Message* p) {
const auto message = *reinterpret_cast<const CodedSquelchMessage*>(p); const auto message = *reinterpret_cast<const CodedSquelchMessage*>(p);
this->handle_coded_squelch(message.value); this->handle_coded_squelch(message.value);
}}; }};

View file

@ -113,6 +113,8 @@ CaptureAppView::CaptureAppView(NavigationView& nav)
} }
CaptureAppView::~CaptureAppView() { CaptureAppView::~CaptureAppView() {
// Most other apps can't handle "Capture" mode, set to something standard.
receiver_model.set_modulation(ReceiverModel::Mode::WidebandFMAudio);
receiver_model.disable(); receiver_model.disable();
baseband::shutdown(); baseband::shutdown();
} }

View file

@ -26,6 +26,7 @@
#include "ui.hpp" #include "ui.hpp"
#include "portapack.hpp" #include "portapack.hpp"
#include "app_settings.hpp"
#include "baseband_api.hpp" #include "baseband_api.hpp"
#include "radio_state.hpp" #include "radio_state.hpp"
#include "receiver_model.hpp" #include "receiver_model.hpp"
@ -71,6 +72,8 @@ class GlassView : public View {
private: private:
NavigationView& nav_; NavigationView& nav_;
RxRadioState radio_state_{}; RxRadioState radio_state_{};
app_settings::SettingsManager settings_{
"rx_glass", app_settings::Mode::RX};
struct preset_entry { struct preset_entry {
rf::Frequency min{}; rf::Frequency min{};

View file

@ -595,12 +595,6 @@ MicTXView::MicTXView(
} }
}; };
// These shouldn't be necessary, but because
// this app uses both transmitter_model and directly
// configures the baseband, these end up being required.
transmitter_model.set_sampling_rate(sampling_rate);
transmitter_model.set_baseband_bandwidth(1750000);
set_tx(false); set_tx(false);
audio::set_rate(audio::Rate::Hz_24000); audio::set_rate(audio::Rate::Hz_24000);

View file

@ -142,7 +142,9 @@ bool PlaylistView::next_track() {
/* Transmits the current_entry_ */ /* Transmits the current_entry_ */
void PlaylistView::send_current_track() { void PlaylistView::send_current_track() {
// Prepare to send a file.
replay_thread_.reset(); replay_thread_.reset();
transmitter_model.disable();
ready_signal_ = false; ready_signal_ = false;
if (!current_entry_) if (!current_entry_)
@ -172,6 +174,10 @@ void PlaylistView::send_current_track() {
transmitter_model.set_baseband_bandwidth(baseband_bandwidth); transmitter_model.set_baseband_bandwidth(baseband_bandwidth);
transmitter_model.enable(); transmitter_model.enable();
// Set baseband sample rate too for waterfall to be correct.
// TODO: Why doesn't the transmitter_model just handle this?
baseband::set_sample_rate(transmitter_model.sampling_rate());
// Use the ReplayThread class to send the data. // Use the ReplayThread class to send the data.
replay_thread_ = std::make_unique<ReplayThread>( replay_thread_ = std::make_unique<ReplayThread>(
std::move(reader), std::move(reader),

View file

@ -96,7 +96,7 @@ class ScannerView : public View {
private: private:
app_settings::SettingsManager settings_{ app_settings::SettingsManager settings_{
"scanner", app_settings::Mode::RX}; "rx_scanner", app_settings::Mode::RX};
NavigationView& nav_; NavigationView& nav_;
RxRadioState radio_state_{}; RxRadioState radio_state_{};

View file

@ -30,6 +30,12 @@
/* Stashes current radio bandwidth and sampling rate. /* Stashes current radio bandwidth and sampling rate.
* Inits to defaults, and restores previous when destroyed. * Inits to defaults, and restores previous when destroyed.
* The idea here is that an app will eventually call enable
* on the model which will sync all of the model state into
* the radio. We tried lazily setting these using the
* set_configuration_without_update function, but there are
* still various UI components (mainly Waterfall) that need
* the radio set in order to load properly.
* NB: This member must be added before SettingsManager. */ * NB: This member must be added before SettingsManager. */
template <typename TModel, TModel* model> template <typename TModel, TModel* model>
class RadioState { class RadioState {
@ -41,8 +47,9 @@ class RadioState {
RadioState(uint32_t new_bandwidth, uint32_t new_sampling_rate) RadioState(uint32_t new_bandwidth, uint32_t new_sampling_rate)
: prev_bandwidth_{model->baseband_bandwidth()}, : prev_bandwidth_{model->baseband_bandwidth()},
prev_sampling_rate_{model->sampling_rate()} { prev_sampling_rate_{model->sampling_rate()} {
model->set_configuration_without_update( // Update the new settings in the radio.
new_bandwidth, new_sampling_rate); model->set_sampling_rate(new_sampling_rate);
model->set_baseband_bandwidth(new_bandwidth);
} }
~RadioState() { ~RadioState() {

View file

@ -311,10 +311,12 @@ WaterfallWidget::WaterfallWidget(const bool cursor) {
} }
void WaterfallWidget::on_show() { void WaterfallWidget::on_show() {
// TODO: Assert that baseband is not shutdown.
baseband::spectrum_streaming_start(); baseband::spectrum_streaming_start();
} }
void WaterfallWidget::on_hide() { void WaterfallWidget::on_hide() {
// TODO: Assert that baseband is not shutdown.
baseband::spectrum_streaming_stop(); baseband::spectrum_streaming_stop();
} }

View file

@ -104,6 +104,10 @@ class FrequencyScale : public Widget {
void draw_filter_ranges(Painter& painter, const Rect r); void draw_filter_ranges(Painter& painter, const Rect r);
}; };
/* NB: These visualizations rely on having a baseband image running.
* If the baseband is shutdown or otherwise not running when interacting
* with these, they will almost certainly hang the device. */
class WaterfallView : public Widget { class WaterfallView : public Widget {
public: public:
void on_show() override; void on_show() override;