diff --git a/USER_MANUAL.md b/USER_MANUAL.md index 92fa45340..eb01d7890 100644 --- a/USER_MANUAL.md +++ b/USER_MANUAL.md @@ -896,6 +896,7 @@ LDPC | Low Density Parity Check Codes - a family of powerful FEC codes 2. Enhancements: * Show green line indicating RX frequency. (PR #725) * Update configuration of the Voice Keyer feature based on user feedback. (PR #730) + * Add monitor volume adjustment. (PR #733) 3. Build system: * Allow overrriding the version tag when building. (PR #727) * Update wxWidgets to 3.2.5. (PR #731) diff --git a/src/config/FreeDVConfiguration.cpp b/src/config/FreeDVConfiguration.cpp index 0e965e2ed..efeef8ad3 100644 --- a/src/config/FreeDVConfiguration.cpp +++ b/src/config/FreeDVConfiguration.cpp @@ -107,7 +107,9 @@ FreeDVConfiguration::FreeDVConfiguration() , tabLayout("/MainFrame/TabLayout", _("")) , monitorVoiceKeyerAudio("/Monitor/VoiceKeyerAudio", false) + , monitorVoiceKeyerAudioVol("/Monitor/VoiceKeyerAudioVol", 0) , monitorTxAudio("/Monitor/TransmitAudio", false) + , monitorTxAudioVol("/Monitor/TransmitAudioVol", 0) , txRxDelayMilliseconds("/Audio/TxRxDelayMilliseconds", 0) @@ -220,6 +222,8 @@ void FreeDVConfiguration::load(wxConfigBase* config) load_(config, monitorVoiceKeyerAudio); load_(config, monitorTxAudio); + load_(config, monitorVoiceKeyerAudioVol); + load_(config, monitorTxAudioVol); quickRecordPath.setDefaultVal(documentsDir); load_(config, quickRecordPath); @@ -312,6 +316,8 @@ void FreeDVConfiguration::save(wxConfigBase* config) save_(config, monitorVoiceKeyerAudio); save_(config, monitorTxAudio); + save_(config, monitorVoiceKeyerAudioVol); + save_(config, monitorTxAudioVol); save_(config, txRxDelayMilliseconds); diff --git a/src/config/FreeDVConfiguration.h b/src/config/FreeDVConfiguration.h index 6f763f768..e9fb48d84 100644 --- a/src/config/FreeDVConfiguration.h +++ b/src/config/FreeDVConfiguration.h @@ -118,7 +118,9 @@ class FreeDVConfiguration : public WxWidgetsConfigStore ConfigurationDataElement tabLayout; ConfigurationDataElement monitorVoiceKeyerAudio; + ConfigurationDataElement monitorVoiceKeyerAudioVol; ConfigurationDataElement monitorTxAudio; + ConfigurationDataElement monitorTxAudioVol; ConfigurationDataElement txRxDelayMilliseconds; diff --git a/src/gui/dialogs/CMakeLists.txt b/src/gui/dialogs/CMakeLists.txt index a505496b3..d6430f0ce 100644 --- a/src/gui/dialogs/CMakeLists.txt +++ b/src/gui/dialogs/CMakeLists.txt @@ -4,7 +4,8 @@ add_library(fdv_gui_dialogs STATIC dlg_filter.cpp dlg_options.cpp dlg_ptt.cpp - freedv_reporter.cpp) + freedv_reporter.cpp + monitor_volume_adj.cpp) target_include_directories(fdv_gui_dialogs PRIVATE ${CODEC2_INCLUDE_DIRS} ${CMAKE_CURRENT_SOURCE_DIR}/../.. ${CMAKE_CURRENT_BINARY_DIR}/../..) diff --git a/src/gui/dialogs/monitor_volume_adj.cpp b/src/gui/dialogs/monitor_volume_adj.cpp new file mode 100644 index 000000000..117919db6 --- /dev/null +++ b/src/gui/dialogs/monitor_volume_adj.cpp @@ -0,0 +1,59 @@ +//========================================================================== +// Name: monitor_volume_adj.cpp +// Purpose: Popup to allow adjustment of monitor volume +// Created: Jul 12, 2024 +// Authors: Mooneer Salem +// +// License: +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License version 2.1, +// as published by the Free Software Foundation. This program is +// distributed in the hope that it will be useful, but WITHOUT ANY +// WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public +// License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, see . +// +//========================================================================== + +#include +#include +#include +#include + +#include "monitor_volume_adj.h" + +MonitorVolumeAdjPopup::MonitorVolumeAdjPopup( wxWindow* parent, ConfigurationDataElement& configVal ) + : wxPopupTransientWindow(parent) + , configVal_(configVal) +{ + wxStaticBoxSizer* mainSizer = new wxStaticBoxSizer(wxVERTICAL, this, _("Monitor volume (dB)")); + + volumeSlider_ = new wxSlider(mainSizer->GetStaticBox(), wxID_ANY, configVal, -40, 0, wxDefaultPosition, wxSize(250, -1), wxSL_AUTOTICKS | wxSL_LABELS); + mainSizer->Add(volumeSlider_, 0, wxALL | wxEXPAND, 2); + + SetSizerAndFit(mainSizer); + Layout(); + + // Link event handlers + volumeSlider_->Connect(wxEVT_SLIDER, wxCommandEventHandler(MonitorVolumeAdjPopup::OnSliderAdjusted), NULL, this); + + // Make popup show up to the left of (and above) mouse cursor position + wxPoint pt = wxGetMousePosition(); + wxSize sz = GetSize(); + pt -= wxPoint(sz.GetWidth(), sz.GetHeight()); + SetPosition( pt ); +} + +MonitorVolumeAdjPopup::~MonitorVolumeAdjPopup() +{ + volumeSlider_->Disconnect(wxEVT_SLIDER, wxCommandEventHandler(MonitorVolumeAdjPopup::OnSliderAdjusted), NULL, this); +} + +void MonitorVolumeAdjPopup::OnSliderAdjusted(wxCommandEvent& event) +{ + configVal_ = volumeSlider_->GetValue(); +} diff --git a/src/gui/dialogs/monitor_volume_adj.h b/src/gui/dialogs/monitor_volume_adj.h new file mode 100644 index 000000000..8f22ee9a5 --- /dev/null +++ b/src/gui/dialogs/monitor_volume_adj.h @@ -0,0 +1,47 @@ +//========================================================================== +// Name: monitor_volume_adj.h +// Purpose: Popup to allow adjustment of monitor volume +// Created: Jul 12, 2024 +// Authors: Mooneer Salem +// +// License: +// +// This program is free software; you can redistribute it and/or modify +// it under the terms of the GNU General Public License version 2.1, +// as published by the Free Software Foundation. This program is +// distributed in the hope that it will be useful, but WITHOUT ANY +// WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public +// License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, see . +// +//========================================================================== + +#ifndef __MONITOR_VOLUME_ADJ__ +#define __MONITOR_VOLUME_ADJ__ + +#include +#include + +#include "config/ConfigurationDataElement.h" + +//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-= +// Class MonitorVolumeAdjPopup +//-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=--=-=-=-= +class MonitorVolumeAdjPopup : public wxPopupTransientWindow +{ + public: + MonitorVolumeAdjPopup( wxWindow* parent, ConfigurationDataElement& configVal ); + ~MonitorVolumeAdjPopup(); + + protected: + void OnSliderAdjusted(wxCommandEvent& event); + + private: + wxSlider* volumeSlider_; + ConfigurationDataElement& configVal_; +}; + +#endif // __MONITOR_VOLUME_ADJ__ diff --git a/src/main.cpp b/src/main.cpp index 8040cb885..fa43f7428 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -760,16 +760,16 @@ MainFrame::MainFrame(wxWindow *parent) : TopFrame(parent, wxID_ANY, _("FreeDV ") voiceKeyerPopupMenu_ = new wxMenu(); assert(voiceKeyerPopupMenu_ != nullptr); - auto chooseVKFileMenuItem = voiceKeyerPopupMenu_->Append(wxID_ANY, _("&Use another voice keyer file...")); + chooseVKFileMenuItem_ = voiceKeyerPopupMenu_->Append(wxID_ANY, _("&Use another voice keyer file...")); voiceKeyerPopupMenu_->Connect( - chooseVKFileMenuItem->GetId(), wxEVT_COMMAND_MENU_SELECTED, + chooseVKFileMenuItem_->GetId(), wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(MainFrame::OnChooseAlternateVoiceKeyerFile), NULL, this); - auto recordNewVoiceKeyerFileMenuItem = voiceKeyerPopupMenu_->Append(wxID_ANY, _("&Record new voice keyer file...")); + recordNewVoiceKeyerFileMenuItem_ = voiceKeyerPopupMenu_->Append(wxID_ANY, _("&Record new voice keyer file...")); voiceKeyerPopupMenu_->Connect( - recordNewVoiceKeyerFileMenuItem->GetId(), wxEVT_COMMAND_MENU_SELECTED, + recordNewVoiceKeyerFileMenuItem_->GetId(), wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(MainFrame::OnRecordNewVoiceKeyerFile), NULL, this); @@ -784,6 +784,15 @@ MainFrame::MainFrame(wxWindow *parent) : TopFrame(parent, wxID_ANY, _("FreeDV ") NULL, this); + adjustMonitorVKVolMenuItem_ = voiceKeyerPopupMenu_->Append(wxID_ANY, _("Adjust Monitor Volume...")); + adjustMonitorVKVolMenuItem_->Enable(wxGetApp().appConfiguration.monitorVoiceKeyerAudio); + voiceKeyerPopupMenu_->Connect( + adjustMonitorVKVolMenuItem_->GetId(), wxEVT_COMMAND_MENU_SELECTED, + wxCommandEventHandler(MainFrame::OnSetMonitorVKAudioVol), + NULL, + this + ); + // Create PTT popup menu pttPopupMenu_ = new wxMenu(); assert(pttPopupMenu_ != nullptr); @@ -795,6 +804,15 @@ MainFrame::MainFrame(wxWindow *parent) : TopFrame(parent, wxID_ANY, _("FreeDV ") wxCommandEventHandler(MainFrame::OnSetMonitorTxAudio), NULL, this); + + adjustMonitorPttVolMenuItem_ = pttPopupMenu_->Append(wxID_ANY, _("Adjust Monitor Volume...")); + adjustMonitorPttVolMenuItem_->Enable(wxGetApp().appConfiguration.monitorTxAudio); + pttPopupMenu_->Connect( + adjustMonitorPttVolMenuItem_->GetId(), wxEVT_COMMAND_MENU_SELECTED, + wxCommandEventHandler(MainFrame::OnSetMonitorTxAudioVol), + NULL, + this + ); m_RxRunning = false; diff --git a/src/main.h b/src/main.h index 7695494cd..f49e43a1e 100644 --- a/src/main.h +++ b/src/main.h @@ -435,6 +435,9 @@ class MainFrame : public TopFrame void OnSetMonitorVKAudio( wxCommandEvent& event ); void OnSetMonitorTxAudio( wxCommandEvent& event ); + void OnSetMonitorVKAudioVol( wxCommandEvent& event ); + void OnSetMonitorTxAudioVol( wxCommandEvent& event ); + private: std::shared_ptr rxInSoundDevice; std::shared_ptr rxOutSoundDevice; @@ -480,6 +483,10 @@ class MainFrame : public TopFrame wxMenu* voiceKeyerPopupMenu_; wxMenu* pttPopupMenu_; + wxMenuItem* adjustMonitorPttVolMenuItem_; + wxMenuItem* adjustMonitorVKVolMenuItem_; + wxMenuItem* chooseVKFileMenuItem_; + wxMenuItem* recordNewVoiceKeyerFileMenuItem_; int getSoundCardIDFromName(wxString& name, bool input); bool validateSoundCardSetup(); diff --git a/src/ongui.cpp b/src/ongui.cpp index d3cb6c4d7..7a540fa44 100644 --- a/src/ongui.cpp +++ b/src/ongui.cpp @@ -19,6 +19,7 @@ #include "gui/dialogs/dlg_options.h" #include "gui/dialogs/dlg_ptt.h" #include "gui/dialogs/freedv_reporter.h" +#include "gui/dialogs/monitor_volume_adj.h" #if defined(WIN32) #include "rig_control/omnirig/OmniRigController.h" @@ -836,6 +837,13 @@ int MainApp::FilterEvent(wxEvent& event) void MainFrame::OnSetMonitorTxAudio( wxCommandEvent& event ) { wxGetApp().appConfiguration.monitorTxAudio = event.IsChecked(); + adjustMonitorPttVolMenuItem_->Enable(wxGetApp().appConfiguration.monitorTxAudio); +} + +void MainFrame::OnSetMonitorTxAudioVol( wxCommandEvent& event ) +{ + auto popup = new MonitorVolumeAdjPopup(this, wxGetApp().appConfiguration.monitorTxAudioVol); + popup->Popup(); } //------------------------------------------------------------------------- diff --git a/src/pipeline/TxRxThread.cpp b/src/pipeline/TxRxThread.cpp index f8ce03397..524ca4d91 100644 --- a/src/pipeline/TxRxThread.cpp +++ b/src/pipeline/TxRxThread.cpp @@ -385,6 +385,21 @@ void TxRxThread::initializePipeline_() auto monitorPipeline = new AudioPipeline(inputSampleRate_, outputSampleRate_); monitorPipeline->appendPipelineStep(equalizedMicAudioLink_->getOutputPipelineStep()); + + auto monitorLevelStep = std::make_shared(outputSampleRate_, [&]() { + double volInDb = 0; + if (g_voice_keyer_tx && wxGetApp().appConfiguration.monitorVoiceKeyerAudio) + { + volInDb = wxGetApp().appConfiguration.monitorVoiceKeyerAudioVol; + } + else + { + volInDb = wxGetApp().appConfiguration.monitorTxAudioVol; + } + + return std::exp(volInDb/20.0f * std::log(10.0f)); + }); + monitorPipeline->appendPipelineStep(monitorLevelStep); auto muteStep = new MuteStep(outputSampleRate_); diff --git a/src/voicekeyer.cpp b/src/voicekeyer.cpp index 83f5c026b..728af3812 100644 --- a/src/voicekeyer.cpp +++ b/src/voicekeyer.cpp @@ -5,6 +5,7 @@ */ #include "main.h" +#include "gui/dialogs/monitor_volume_adj.h" extern SNDFILE *g_sfRecMicFile; bool g_recVoiceKeyerFile; @@ -157,17 +158,28 @@ void MainFrame::OnChooseAlternateVoiceKeyerFile( wxCommandEvent& event ) void MainFrame::OnTogBtnVoiceKeyerRightClick( wxContextMenuEvent& event ) { - // Only handle right-click if idle - if (vk_state == VK_IDLE && !m_btnTogPTT->GetValue()) - { - auto sz = m_togBtnVoiceKeyer->GetSize(); - m_togBtnVoiceKeyer->PopupMenu(voiceKeyerPopupMenu_, wxPoint(-sz.GetWidth() - 25, 0)); - } + // Only enable VK file selection on idle + bool enabled = vk_state == VK_IDLE && !m_btnTogPTT->GetValue(); + chooseVKFileMenuItem_->Enable(enabled); + recordNewVoiceKeyerFileMenuItem_->Enable(enabled); + + // Trigger right-click menu popup in a location that will prevent it from + // ending up off the screen. + auto sz = m_togBtnVoiceKeyer->GetSize(); + m_togBtnVoiceKeyer->PopupMenu(voiceKeyerPopupMenu_, wxPoint(-sz.GetWidth() - 25, 0)); } void MainFrame::OnSetMonitorVKAudio( wxCommandEvent& event ) { wxGetApp().appConfiguration.monitorVoiceKeyerAudio = event.IsChecked(); + adjustMonitorVKVolMenuItem_->Enable(wxGetApp().appConfiguration.monitorVoiceKeyerAudio); + +} + +void MainFrame::OnSetMonitorVKAudioVol( wxCommandEvent& event ) +{ + auto popup = new MonitorVolumeAdjPopup(this, wxGetApp().appConfiguration.monitorVoiceKeyerAudioVol); + popup->Popup(); } extern SNDFILE *g_sfPlayFile;