Add AudioVolumeField -- cleanup (#1107)

* WIP Adding AudioVolumeField

* Fix build break

* Add Bernd to about

---------

Co-authored-by: kallanreed <kallanreed@outlook.com>
This commit is contained in:
Kyle Reed 2023-06-04 12:56:46 -07:00 committed by GitHub
parent 7e8a139732
commit 28319652c1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
22 changed files with 91 additions and 186 deletions

View File

@ -198,11 +198,6 @@ AnalogAudioView::AnalogAudioView(
this->on_show_options_modulation();
};
field_volume.set_value((receiver_model.headphone_volume() - audio::headphone::volume_range().max).decibel() + 99);
field_volume.on_change = [this](int32_t v) {
this->on_headphone_volume_changed(v);
};
record_view.on_error = [&nav](std::string message) {
nav.display_modal("Error", message);
};
@ -391,11 +386,6 @@ void AnalogAudioView::on_reference_ppm_correction_changed(int32_t v) {
persistent_memory::set_correction_ppb(v * 1000);
}
void AnalogAudioView::on_headphone_volume_changed(int32_t v) {
const auto new_volume = volume_t::decibel(v - 99) + audio::headphone::volume_range().max;
receiver_model.set_headphone_volume(new_volume);
}
void AnalogAudioView::update_modulation(const ReceiverModel::Mode modulation) {
audio::output::mute();
record_view.stop();

View File

@ -207,13 +207,8 @@ class AnalogAudioView : public View {
{"SPEC", toUType(ReceiverModel::Mode::SpectrumAnalysis)},
}};
NumberField field_volume{
{28 * 8, 0 * 16},
2,
{0, 99},
1,
' ',
};
AudioVolumeField field_volume{
{28 * 8, 0 * 16}};
Text text_ctcss{
{19 * 8, 1 * 16, 11 * 8, 1 * 16},
@ -239,7 +234,6 @@ class AnalogAudioView : public View {
void on_show_options_modulation();
void on_frequency_step_changed(rf::Frequency f);
void on_reference_ppm_correction_changed(int32_t v);
void on_headphone_volume_changed(int32_t v);
void on_edit_frequency();
void remove_options_widget();

View File

@ -100,11 +100,6 @@ AnalogTvView::AnalogTvView(
this->on_show_options_modulation();
};
field_volume.set_value(0);
field_volume.on_change = [this](int32_t v) {
this->on_headphone_volume_changed(v);
};
tv.on_select = [this](int32_t offset) {
field_frequency.set_value(receiver_model.tuning_frequency() + offset);
};
@ -225,11 +220,6 @@ void AnalogTvView::on_reference_ppm_correction_changed(int32_t v) {
persistent_memory::set_correction_ppb(v * 1000);
}
void AnalogTvView::on_headphone_volume_changed(int32_t v) {
(void)v; // avoid warning
// tv::TVView::set_headphone_volume(this,v);
}
void AnalogTvView::update_modulation(const ReceiverModel::Mode modulation) {
audio::output::mute();

View File

@ -98,13 +98,8 @@ class AnalogTvView : public View {
{"TV ", toUType(ReceiverModel::Mode::WidebandFMAudio)},
}};
NumberField field_volume{
{27 * 8, 0 * 16},
3,
{0, 255},
1,
' ',
};
AudioVolumeField field_volume{
{27 * 8, 0 * 16}};
std::unique_ptr<Widget> options_widget{};
@ -118,7 +113,6 @@ class AnalogTvView : public View {
void on_show_options_modulation();
void on_frequency_step_changed(rf::Frequency f);
void on_reference_ppm_correction_changed(int32_t v);
void on_headphone_volume_changed(int32_t v);
void on_edit_frequency();
void remove_options_widget();

View File

@ -79,18 +79,21 @@ POCSAGAppView::POCSAGAppView(NavigationView& nav) {
update_freq(f);
};
// load app settings
// load app settings TODO: needed?
auto rc = settings.load("rx_pocsag", &app_settings);
if (rc == SETTINGS_OK) {
field_lna.set_value(app_settings.lna);
field_vga.set_value(app_settings.vga);
field_rf_amp.set_value(app_settings.rx_amp);
field_frequency.set_value(app_settings.rx_frequency);
} else
} else {
field_lna.set_value(receiver_model.lna());
field_vga.set_value(receiver_model.vga());
field_rf_amp.set_value(receiver_model.rf_amp());
field_frequency.set_value(receiver_model.tuning_frequency());
}
receiver_model.set_modulation(ReceiverModel::Mode::NarrowbandFMAudio);
receiver_model.set_sampling_rate(3072000);
receiver_model.set_baseband_bandwidth(1750000);
receiver_model.enable();
@ -105,22 +108,10 @@ POCSAGAppView::POCSAGAppView(NavigationView& nav) {
};
};
check_log.set_value(logging);
check_log.on_select = [this](Checkbox&, bool v) {
logging = v;
};
field_volume.set_value((receiver_model.headphone_volume() - audio::headphone::volume_range().max).decibel() + 99);
field_volume.on_change = [this](int32_t v) {
this->on_headphone_volume_changed(v);
};
check_ignore.set_value(ignore);
check_ignore.on_select = [this](Checkbox&, bool v) {
ignore = v;
};
// TODO app setting instead?
ignore_address = persistent_memory::pocsag_ignore_address();
// TODO is this common enough for a helper?
for (size_t c = 0; c < 7; c++) {
sym_ignore.set_sym(6 - c, ignore_address % 10);
ignore_address /= 10;
@ -150,20 +141,6 @@ POCSAGAppView::~POCSAGAppView() {
baseband::shutdown();
}
void POCSAGAppView::focus() {
field_frequency.focus();
}
void POCSAGAppView::on_headphone_volume_changed(int32_t v) {
const auto new_volume = volume_t::decibel(v - 99) + audio::headphone::volume_range().max;
receiver_model.set_headphone_volume(new_volume);
}
// Useless ?
void POCSAGAppView::set_parent_rect(const Rect new_parent_rect) {
View::set_parent_rect(new_parent_rect);
}
void POCSAGAppView::on_packet(const POCSAGPacketMessage* message) {
std::string alphanum_text = "";
@ -172,14 +149,14 @@ void POCSAGAppView::on_packet(const POCSAGPacketMessage* message) {
else {
pocsag_decode_batch(message->packet, &pocsag_state);
if ((ignore) && (pocsag_state.address == sym_ignore.value_dec_u32())) {
if ((ignore()) && (pocsag_state.address == sym_ignore.value_dec_u32())) {
// Ignore (inform, but no log)
// console.write("\n\x1B\x03" + to_string_time(message->packet.timestamp()) +
// " Ignored address " + to_string_dec_uint(pocsag_state.address));
return;
}
// Too many errors for reliable decode
if ((ignore) && (pocsag_state.errors >= 3)) {
if ((ignore()) && (pocsag_state.errors >= 3)) {
return;
}
@ -199,7 +176,7 @@ void POCSAGAppView::on_packet(const POCSAGPacketMessage* message) {
console.write(console_info);
if (logger && logging) {
if (logger && logging()) {
logger->log_decoded(message->packet, to_string_dec_uint(pocsag_state.address) +
" F" + to_string_dec_uint(pocsag_state.function) +
" Address only");
@ -218,15 +195,16 @@ void POCSAGAppView::on_packet(const POCSAGPacketMessage* message) {
console.write(pocsag_state.output);
}
if (logger && logging)
if (logger && logging())
logger->log_decoded(message->packet, to_string_dec_uint(pocsag_state.address) +
" F" + to_string_dec_uint(pocsag_state.function) +
" Alpha: " + pocsag_state.output);
}
}
// TODO: make setting.
// Log raw data whatever it contains
if (logger && logging)
if (logger && logging())
logger->log_raw_data(message->packet, target_frequency());
}

View File

@ -52,20 +52,18 @@ class POCSAGAppView : public View {
POCSAGAppView(NavigationView& nav);
~POCSAGAppView();
void set_parent_rect(const Rect new_parent_rect) override;
void focus() override;
std::string title() const override { return "POCSAG RX"; };
private:
static constexpr uint32_t initial_target_frequency = 466175000;
bool logging() const { return check_log.value(); };
bool ignore() const { return check_ignore.value(); };
// app save settings
std::app_settings settings{};
std::app_settings::AppSettings app_settings{};
bool logging{false};
bool ignore{false};
uint32_t last_address = 0xFFFFFFFF;
pocsag::POCSAGState pocsag_state{};
@ -88,13 +86,9 @@ class POCSAGAppView : public View {
FrequencyField field_frequency{
{0 * 8, 0 * 8},
};
NumberField field_volume{
{28 * 8, 0 * 16},
2,
{0, 99},
1,
' ',
};
AudioVolumeField field_volume{
{28 * 8, 0 * 16}};
Checkbox check_ignore{
{0 * 8, 21},
@ -105,10 +99,11 @@ class POCSAGAppView : public View {
{13 * 8, 25},
7,
SymField::SYMFIELD_DEC};
Checkbox check_log{
{240 - 8 * 8, 21},
3,
"LOG",
"Log",
false};
Console console{
@ -122,8 +117,6 @@ class POCSAGAppView : public View {
void on_packet(const POCSAGPacketMessage* message);
void on_headphone_volume_changed(int32_t v);
uint32_t target_frequency() const;
void set_target_frequency(const uint32_t new_value);

View File

@ -33,6 +33,7 @@ void AboutView::update() {
console.writeln("heurist1,intoxsick,ckuethe");
console.writeln("notpike,jLynx,zigad");
console.writeln("MichalLeonBorsuk,jimilinuxguy");
console.writeln("kallanreed,bernd-herzog");
console.writeln("");
break;

View File

@ -68,9 +68,6 @@ LevelView::LevelView(NavigationView& nav)
rssi.set_vertical_rssi(true);
field_volume.on_change = [this](int32_t v) { this->on_headphone_volume_changed(v); };
field_volume.set_value((receiver_model.headphone_volume() - audio::headphone::volume_range().max).decibel() + 99);
change_mode(NFM_MODULATION); // Start on AM
field_mode.set_by_value(NFM_MODULATION); // Reflect the mode into the manual selector
@ -135,7 +132,7 @@ LevelView::LevelView(NavigationView& nav)
audio::output::stop();
} else if (v == 1) {
audio::output::start();
this->on_headphone_volume_changed((receiver_model.headphone_volume() - audio::headphone::volume_range().max).decibel() + 99);
receiver_model.set_headphone_volume(receiver_model.headphone_volume()); // TODO: Needed?
} else {
}
};
@ -158,11 +155,6 @@ LevelView::LevelView(NavigationView& nav)
freq_stats_db.set_style(&style_white);
}
void LevelView::on_headphone_volume_changed(int32_t v) {
const auto new_volume = volume_t::decibel(v - 99) + audio::headphone::volume_range().max;
receiver_model.set_headphone_volume(new_volume);
}
void LevelView::on_statistics_update(const ChannelStatistics& statistics) {
static int last_max_db = -1000;
static int last_min_rssi = -1000;

View File

@ -117,13 +117,8 @@ class LevelView : public View {
RFAmpField field_rf_amp{
{18 * 8, 0 * 16}};
NumberField field_volume{
{24 * 8, 0 * 16},
2,
{0, 99},
1,
' ',
};
AudioVolumeField field_volume{
{24 * 8, 0 * 16}};
OptionsField field_bw{
{3 * 8, 1 * 16},
@ -202,7 +197,6 @@ class LevelView : public View {
};
void handle_coded_squelch(const uint32_t value);
void on_headphone_volume_changed(int32_t v);
MessageHandlerRegistration message_handler_coded_squelch{
Message::ID::CodedSquelch,

View File

@ -196,13 +196,6 @@ void MicTXView::rxaudio(bool is_on) {
}
}
void MicTXView::on_headphone_volume_changed(int32_t v) {
// if (rx_enabled) {
const auto new_volume = volume_t::decibel(v - 99) + audio::headphone::volume_range().max;
receiver_model.set_headphone_volume(new_volume);
//}
}
void MicTXView::set_ptt_visibility(bool v) {
tx_button.hidden(!v);
}
@ -526,9 +519,6 @@ MicTXView::MicTXView(
set_dirty(); // Refresh interface
};
field_volume.set_value((receiver_model.headphone_volume() - audio::headphone::volume_range().max).decibel() + 99);
field_volume.on_change = [this](int32_t v) { this->on_headphone_volume_changed(v); };
field_rxbw.on_change = [this](size_t, int32_t v) {
if (!(enable_am || enable_usb || enable_lsb || enable_dsb || enable_wfm)) {
// In Previous fw versions, that nbfm_configuration(n) was done in any mode (FM/AM/SSB/DSB)...strictly speaking only need it in (NFM/FM)

View File

@ -76,7 +76,6 @@ class MicTXView : public View {
void configure_baseband();
void rxaudio(bool is_on);
void on_headphone_volume_changed(int32_t v);
void set_ptt_visibility(bool v);
@ -279,13 +278,8 @@ class MicTXView : public View {
"F TX=RX",
false};
NumberField field_volume{
{9 * 8, (23 * 8) + 2},
2,
{0, 99},
1,
' ',
};
AudioVolumeField field_volume{
{9 * 8, (23 * 8) + 2}};
OptionsField field_rxbw{
{19 * 8, (23 * 8) + 2},

View File

@ -250,7 +250,7 @@ bool ReconView::recon_save_config_to_sd() {
void ReconView::audio_output_start() {
audio::output::start();
this->on_headphone_volume_changed((receiver_model.headphone_volume() - audio::headphone::volume_range().max).decibel() + 99);
receiver_model.set_headphone_volume(receiver_model.headphone_volume());
}
void ReconView::recon_redraw() {
@ -868,10 +868,6 @@ ReconView::ReconView(NavigationView& nav)
squelch = v;
};
field_volume.on_change = [this](int32_t v) {
this->on_headphone_volume_changed(v);
};
// PRE-CONFIGURATION:
button_scanner_mode.set_style(&style_blue);
button_scanner_mode.set_text("RECON");
@ -886,8 +882,6 @@ ReconView::ReconView(NavigationView& nav)
field_lock_wait.set_value(recon_lock_duration);
colorize_waits();
field_volume.set_value((receiver_model.headphone_volume() - audio::headphone::volume_range().max).decibel() + 99);
// fill modulation and step options
freqman_set_modulation_option(field_mode);
freqman_set_step_option(step_mode);
@ -1301,11 +1295,6 @@ void ReconView::on_stepper_delta(int32_t v) {
timer = 0;
}
void ReconView::on_headphone_volume_changed(int32_t v) {
const auto new_volume = volume_t::decibel(v - 99) + audio::headphone::volume_range().max;
receiver_model.set_headphone_volume(new_volume);
}
size_t ReconView::change_mode(freqman_index_t new_mod) {
field_mode.on_change = [this](size_t, OptionsField::value_t) {};
field_bw.on_change = [this](size_t, OptionsField::value_t) {};

View File

@ -112,7 +112,6 @@ class ReconView : public View {
void recon_resume();
void frequency_file_load(bool stop_all_before = false);
void on_statistics_update(const ChannelStatistics& statistics);
void on_headphone_volume_changed(int32_t v);
void on_index_delta(int32_t v);
void on_stepper_delta(int32_t v);
void colorize_waits();
@ -196,13 +195,8 @@ class ReconView : public View {
RFAmpField field_rf_amp{
{18 * 8, 0 * 16}};
NumberField field_volume{
{24 * 8, 0 * 16},
2,
{0, 99},
1,
' ',
};
AudioVolumeField field_volume{
{24 * 8, 0 * 16}};
OptionsField field_bw{
{3 * 8, 1 * 16},

View File

@ -558,9 +558,6 @@ ScannerView::ScannerView(
field_squelch.on_change = [this](int32_t v) { squelch = v; };
field_squelch.set_value(-10);
field_volume.set_value((receiver_model.headphone_volume() - audio::headphone::volume_range().max).decibel() + 99);
field_volume.on_change = [this](int32_t v) { this->on_headphone_volume_changed(v); };
// LEARN FREQUENCIES
std::string scanner_txt = "SCANNER";
frequency_file_load(scanner_txt);
@ -667,8 +664,8 @@ void ScannerView::update_squelch_while_paused(int32_t max_db) {
// Update audio & color based on signal level even if paused
if (++color_timer > 2) { // Counter to reduce color toggling when weak signal
if (max_db > squelch) {
audio::output::start(); // Re-enable audio when signal goes above squelch
on_headphone_volume_changed(field_volume.value()); // quick fix to make sure WM8731S chips don't stay silent after pause
audio::output::start(); // Re-enable audio when signal goes above squelch
receiver_model.set_headphone_volume(receiver_model.headphone_volume()); // quick fix to make sure WM8731S chips don't stay silent after pause
bigdisplay_update(BDC_GREEN);
} else {
audio::output::stop(); // Silence audio when signal drops below squelch
@ -730,7 +727,7 @@ void ScannerView::scan_pause() {
scan_thread->set_scanning(false); // WE STOP SCANNING
}
audio::output::start();
on_headphone_volume_changed(field_volume.value()); // quick fix to make sure WM8731S chips don't stay silent after pause
receiver_model.set_headphone_volume(receiver_model.headphone_volume()); // quick fix to make sure WM8731S chips don't stay silent after pause
}
void ScannerView::scan_resume() {
@ -749,11 +746,6 @@ void ScannerView::user_resume() {
userpause = false; // Resume scanning
}
void ScannerView::on_headphone_volume_changed(int32_t v) {
const auto new_volume = volume_t::decibel(v - 99) + audio::headphone::volume_range().max;
receiver_model.set_headphone_volume(new_volume);
}
void ScannerView::change_mode(freqman_index_t new_mod) { // Before this, do a scan_thread->stop(); After this do a start_scan_thread()
using option_t = std::pair<std::string, int32_t>;
using options_t = std::vector<option_t>;

View File

@ -134,7 +134,6 @@ class ScannerView : public View {
void bigdisplay_update(int32_t);
void update_squelch_while_paused(int32_t max_db);
void on_statistics_update(const ChannelStatistics& statistics);
void on_headphone_volume_changed(int32_t v);
void handle_retune(int64_t freq, uint32_t freq_idx);
jammer::jammer_range_t frequency_range{false, 0, 0}; // perfect for manual scan task too...
@ -182,13 +181,8 @@ class ScannerView : public View {
RFAmpField field_rf_amp{
{18 * 8, 0 * 16}};
NumberField field_volume{
{24 * 8, 0 * 16},
2,
{0, 99},
1,
' ',
};
AudioVolumeField field_volume{
{24 * 8, 0 * 16}};
OptionsField field_bw{
{3 * 8, 1 * 16},

View File

@ -128,13 +128,6 @@ SondeView::SondeView(NavigationView& nav) {
if (logger)
logger->append(LOG_ROOT_DIR "/SONDE.TXT");
// initialize audio:
field_volume.set_value((receiver_model.headphone_volume() - audio::headphone::volume_range().max).decibel() + 99);
field_volume.on_change = [this](int32_t v) {
this->on_headphone_volume_changed(v);
};
audio::output::start();
audio::output::speaker_unmute();
@ -251,11 +244,6 @@ void SondeView::on_packet(const sonde::Packet& packet) {
}
}
void SondeView::on_headphone_volume_changed(int32_t v) {
const auto new_volume = volume_t::decibel(v - 99) + audio::headphone::volume_range().max;
receiver_model.set_headphone_volume(new_volume);
}
void SondeView::set_target_frequency(const uint32_t new_value) {
target_frequency_ = new_value;
receiver_model.set_tuning_frequency(target_frequency_);

View File

@ -109,13 +109,8 @@ class SondeView : public View {
{21 * 8, 0, 6 * 8, 4},
};
NumberField field_volume{
{28 * 8, 0 * 16},
2,
{0, 99},
1,
' ',
};
AudioVolumeField field_volume{
{28 * 8, 0 * 16}};
Checkbox check_beep{
{22 * 8, 6 * 16},
@ -181,7 +176,6 @@ class SondeView : public View {
}};
void on_packet(const sonde::Packet& packet);
void on_headphone_volume_changed(int32_t v);
char* float_to_char(float x, char* p);
void set_target_frequency(const uint32_t new_value);

View File

@ -35,6 +35,7 @@ using namespace portapack;
#include "dsp_fir_taps.hpp"
#include "dsp_iir.hpp"
#include "dsp_iir_config.hpp"
#include "utility.hpp"
namespace {
@ -154,6 +155,17 @@ void ReceiverModel::set_headphone_volume(volume_t v) {
update_headphone_volume();
}
int32_t ReceiverModel::normalized_headphone_volume() const {
return (headphone_volume_ - audio::headphone::volume_range().max).decibel() + 99;
}
void ReceiverModel::set_normalized_headphone_volume(int32_t v) {
// TODO: Linear map instead to ensure 0 is minimal value.
v = clip<int32_t>(v, 0, 99);
auto new_volume = volume_t::decibel(v - 99) + audio::headphone::volume_range().max;
set_headphone_volume(new_volume);
}
uint8_t ReceiverModel::squelch_level() const {
return squelch_level_;
}
@ -265,9 +277,6 @@ void ReceiverModel::update_sampling_rate() {
}
void ReceiverModel::update_headphone_volume() {
// TODO: Manipulating audio codec here, and in ui_receiver.cpp. Good to do
// both?
audio::headphone::set_volume(headphone_volume_);
}

View File

@ -74,6 +74,10 @@ class ReceiverModel {
volume_t headphone_volume() const;
void set_headphone_volume(volume_t v);
/* Volume range 0-99, normalized for audio HW. */
int32_t normalized_headphone_volume() const;
void set_normalized_headphone_volume(int32_t v);
uint8_t squelch_level() const;
void set_squelch_level(uint8_t v);

View File

@ -381,4 +381,21 @@ void VGAGainField::on_focus() {
}
}
/* AudioVolumeField *******************************************************/
AudioVolumeField::AudioVolumeField(
Point parent_pos)
: NumberField{
parent_pos,
/* length */ 2,
/* range */ {0, 99},
/* step */ 1,
/* fill char */ ' '} {
set_value(receiver_model.normalized_headphone_volume());
on_change = [](int32_t v) {
receiver_model.set_normalized_headphone_volume(v);
};
}
} /* namespace ui */

View File

@ -337,6 +337,11 @@ class VGAGainField : public NumberField {
void on_focus() override;
};
class AudioVolumeField : public NumberField {
public:
AudioVolumeField(Point parent_pos);
};
} /* namespace ui */
#endif /*__UI_RECEIVER_H__*/

View File

@ -94,13 +94,22 @@ int fast_int_magnitude(int y, int x);
int int_atan2(int y, int x);
int32_t int16_sin_s4(int32_t x);
/* Returns value constrained to min and max. */
template <class T>
constexpr const T& clip(const T& value, const T& minimum, const T& maximum) {
return std::max(std::min(value, maximum), minimum);
}
// TODO: need to decide if this is inclusive or exclusive.
// The implementations are different and cause the subtle
// bugs mentioned below.
template <class T>
struct range_t {
const T minimum;
const T maximum;
constexpr const T& clip(const T& value) const {
return std::max(std::min(value, maximum), minimum);
return ::clip(value, minimum, maximum);
}
constexpr void reset_if_outside(T& value, const T& reset_value) const {