mirror of
				https://github.com/AlexandreRouma/SDRPlusPlus.git
				synced 2025-10-31 08:58:13 +01:00 
			
		
		
		
	Added new deemphasis mode + new image widget
This commit is contained in:
		
							
								
								
									
										66
									
								
								core/src/gui/widgets/image.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										66
									
								
								core/src/gui/widgets/image.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,66 @@ | ||||
| #include <gui/widgets/image.h> | ||||
|  | ||||
| namespace ImGui { | ||||
|     ImageDisplay::ImageDisplay(int width, int height, GLenum format) { | ||||
|         _width = width; | ||||
|         _height = height; | ||||
|         _format = format; | ||||
|         buffer = malloc(_width * _height * 4); | ||||
|         activeBuffer = malloc(_width * _height * 4); | ||||
|         memset(buffer, 0, _width * _height * 4); | ||||
|         memset(activeBuffer, 0, _width * _height * 4); | ||||
|  | ||||
|         glGenTextures(1, &textureId); | ||||
|     } | ||||
|  | ||||
|     ImageDisplay::~ImageDisplay() { | ||||
|         free(buffer); | ||||
|         free(activeBuffer); | ||||
|     } | ||||
|  | ||||
|     void ImageDisplay::draw(const ImVec2& size_arg) { | ||||
|         std::lock_guard<std::mutex> lck(bufferMtx); | ||||
|  | ||||
|         ImGuiWindow* window = GetCurrentWindow(); | ||||
|         ImGuiStyle& style = GetStyle(); | ||||
|         float pad = style.FramePadding.y; | ||||
|         ImVec2 min = window->DC.CursorPos; | ||||
|  | ||||
|         // Calculate scale | ||||
|         float width = CalcItemWidth(); | ||||
|         float height = roundf((width / (float)_width) * (float)_height); | ||||
|  | ||||
|         ImVec2 size = CalcItemSize(size_arg, CalcItemWidth(), height); | ||||
|         ImRect bb(min, ImVec2(min.x+size.x, min.y+size.y)); | ||||
|         float lineHeight = size.y; | ||||
|  | ||||
|         ItemSize(size, style.FramePadding.y); | ||||
|         if (!ItemAdd(bb, 0)) { | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         if (newData) { | ||||
|             newData = false; | ||||
|             updateTexture(); | ||||
|         } | ||||
|          | ||||
|         window->DrawList->AddImage((void*)(intptr_t)textureId, min, ImVec2(min.x + width, min.y + height)); | ||||
|     } | ||||
|  | ||||
|     void ImageDisplay::swap() { | ||||
|         std::lock_guard<std::mutex> lck(bufferMtx); | ||||
|         void* tmp = activeBuffer; | ||||
|         activeBuffer = buffer; | ||||
|         buffer = tmp; | ||||
|         newData = true; | ||||
|         memset(buffer, 0, _width * _height * 4); | ||||
|     } | ||||
|  | ||||
|     void ImageDisplay::updateTexture() { | ||||
|         glBindTexture(GL_TEXTURE_2D, textureId); | ||||
|         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); | ||||
|         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); | ||||
|         glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); | ||||
|         glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, _width, _height, 0, GL_RGBA, GL_UNSIGNED_BYTE, activeBuffer); | ||||
|     } | ||||
| } | ||||
							
								
								
									
										34
									
								
								core/src/gui/widgets/image.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										34
									
								
								core/src/gui/widgets/image.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,34 @@ | ||||
| #pragma once | ||||
|  | ||||
| #include <imgui.h> | ||||
| #include <imgui_internal.h> | ||||
| #include <dsp/stream.h> | ||||
| #include <mutex> | ||||
| #include <GL/glew.h> | ||||
|  | ||||
| namespace ImGui { | ||||
|     class ImageDisplay { | ||||
|     public: | ||||
|         ImageDisplay(int width, int height, GLenum format); | ||||
|         ~ImageDisplay(); | ||||
|         void draw(const ImVec2& size_arg = ImVec2(0, 0)); | ||||
|         void swap(); | ||||
|  | ||||
|         void* buffer; | ||||
|  | ||||
|     private: | ||||
|         void updateTexture(); | ||||
|  | ||||
|         std::mutex bufferMtx; | ||||
|         void* activeBuffer; | ||||
|  | ||||
|         int _width; | ||||
|         int _height; | ||||
|         GLenum _format; | ||||
|  | ||||
|         GLuint textureId; | ||||
|  | ||||
|         bool newData = false; | ||||
|  | ||||
|     }; | ||||
| } | ||||
							
								
								
									
										119
									
								
								core/src/utils/optionlist.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										119
									
								
								core/src/utils/optionlist.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,119 @@ | ||||
| #pragma once | ||||
| #include <string> | ||||
| #include <vector> | ||||
| #include <stdexcept> | ||||
|  | ||||
| template <class K, class T> | ||||
| class OptionList { | ||||
| public: | ||||
|     void define(K key, std::string name, T value) { | ||||
|         if (keyExists(key)) { throw std::runtime_error("Key already exists"); } | ||||
|         if (nameExists(name)) { throw std::runtime_error("Name already exists"); } | ||||
|         if (valueExists(value)) { throw std::runtime_error("Value already exists"); } | ||||
|         keys.push_back(key); | ||||
|         names.push_back(name); | ||||
|         values.push_back(value); | ||||
|         updateText(); | ||||
|     } | ||||
|  | ||||
|     void define(std::string name, T value) { | ||||
|         define(name, name, value); | ||||
|     } | ||||
|  | ||||
|     void undefined(int id) { | ||||
|         keys.erase(id); | ||||
|         names.erase(id); | ||||
|         values.erase(id); | ||||
|         updateText(); | ||||
|     } | ||||
|  | ||||
|     void undefineKey(K key) { | ||||
|         undefined(keyId(key)); | ||||
|     } | ||||
|  | ||||
|     void undefineName(std::string name) { | ||||
|         undefined(nameId(name)); | ||||
|     } | ||||
|  | ||||
|     void undefineValue(T value) { | ||||
|         undefined(valueId(value)); | ||||
|     } | ||||
|  | ||||
|     void clear() { | ||||
|         keys.clear(); | ||||
|         names.clear(); | ||||
|         values.clear(); | ||||
|         updateText(); | ||||
|     } | ||||
|  | ||||
|     int size() { | ||||
|         return keys.size(); | ||||
|     } | ||||
|  | ||||
|     bool keyExists(K key) { | ||||
|         if (std::find(keys.begin(), keys.end(), key) != keys.end()) { return true; } | ||||
|         return false; | ||||
|     } | ||||
|  | ||||
|     bool nameExists(std::string name) { | ||||
|         if (std::find(names.begin(), names.end(), name) != names.end()) { return true; } | ||||
|         return false; | ||||
|     } | ||||
|  | ||||
|     bool valueExists(T value) { | ||||
|         if (std::find(values.begin(), values.end(), value) != values.end()) { return true; } | ||||
|         return false; | ||||
|     } | ||||
|  | ||||
|     int keyId(K key) { | ||||
|         auto it = std::find(keys.begin(), keys.end(), key); | ||||
|         if (it == keys.end()) { throw std::runtime_error("Key doesn't exists"); } | ||||
|         return std::distance(keys.begin(), it); | ||||
|     } | ||||
|  | ||||
|     int nameId(std::string name) { | ||||
|         auto it = std::find(names.begin(), names.end(), name); | ||||
|         if (it == names.end()) { throw std::runtime_error("Name doesn't exists"); } | ||||
|         return std::distance(names.begin(), it); | ||||
|     } | ||||
|  | ||||
|     int valueId(T value) { | ||||
|         auto it = std::find(values.begin(), values.end(), value); | ||||
|         if (it == values.end()) { throw std::runtime_error("Value doesn't exists"); } | ||||
|         return std::distance(values.begin(), it); | ||||
|     } | ||||
|  | ||||
|     K key(int id) { | ||||
|         return keys[id]; | ||||
|     } | ||||
|  | ||||
|     std::string name(int id) { | ||||
|         return names[id]; | ||||
|     } | ||||
|  | ||||
|     T value(int id) { | ||||
|         return values[id]; | ||||
|     } | ||||
|  | ||||
|     T operator [](int& id) { | ||||
|         return value(id); | ||||
|     } | ||||
|  | ||||
|     const char* txt = NULL; | ||||
|  | ||||
| private: | ||||
|     void updateText() { | ||||
|         _txt.clear(); | ||||
|         for (auto& name : names) { | ||||
|             _txt += name; | ||||
|             _txt += '\0'; | ||||
|         } | ||||
|         txt = _txt.c_str(); | ||||
|     } | ||||
|  | ||||
|     std::vector<std::string> keys; | ||||
|     std::vector<std::string> names; | ||||
|     std::vector<T> values; | ||||
|     std::string _txt; | ||||
|  | ||||
| }; | ||||
| @@ -6,6 +6,7 @@ | ||||
| #include <utils/event.h> | ||||
|  | ||||
| enum DeemphasisMode { | ||||
|     DEEMP_MODE_22US, | ||||
|     DEEMP_MODE_50US, | ||||
|     DEEMP_MODE_75US, | ||||
|     DEEMP_MODE_NONE, | ||||
|   | ||||
| @@ -8,6 +8,7 @@ | ||||
| #include <dsp/chain.h> | ||||
| #include <dsp/noise_reduction.h> | ||||
| #include <core.h> | ||||
| #include <utils/optionlist.h> | ||||
| #include "radio_interface.h" | ||||
| #include "demod.h" | ||||
|  | ||||
| @@ -15,18 +16,23 @@ ConfigManager config; | ||||
|  | ||||
| #define CONCAT(a, b)    ((std::string(a) + b).c_str()) | ||||
|  | ||||
| const double DeemphasisModes[] { | ||||
|     50e-6, | ||||
|     75e-6 | ||||
| std::map<DeemphasisMode, double> deempTaus = { | ||||
|     {DEEMP_MODE_22US, 22e-6}, | ||||
|     {DEEMP_MODE_50US, 50e-6}, | ||||
|     {DEEMP_MODE_75US, 75e-6} | ||||
| }; | ||||
|  | ||||
| const char* DeemhasisModesTxt = "50µs\00075µs\000None\000"; | ||||
|  | ||||
| class RadioModule : public ModuleManager::Instance { | ||||
| public: | ||||
|     RadioModule(std::string name) { | ||||
|         this->name = name; | ||||
|  | ||||
|         // Intialize option lists | ||||
|         deempModes.define("None", DEEMP_MODE_NONE); | ||||
|         deempModes.define("22us", DEEMP_MODE_22US); | ||||
|         deempModes.define("50us", DEEMP_MODE_50US); | ||||
|         deempModes.define("75us", DEEMP_MODE_75US); | ||||
|  | ||||
|         // Initialize the config if it doesn't exist | ||||
|         bool created = false; | ||||
|         config.acquire(); | ||||
| @@ -243,8 +249,8 @@ private: | ||||
|         if (_this->deempAllowed) { | ||||
|             ImGui::LeftLabel("De-emphasis"); | ||||
|             ImGui::SetNextItemWidth(menuWidth - ImGui::GetCursorPosX()); | ||||
|             if (ImGui::Combo(("##_radio_wfm_deemp_" + _this->name).c_str(), &_this->deempMode, DeemhasisModesTxt)) { | ||||
|                 _this->setDeemphasisMode(_this->deempMode); | ||||
|             if (ImGui::Combo(("##_radio_wfm_deemp_" + _this->name).c_str(), &_this->deempId, _this->deempModes.txt)) { | ||||
|                 _this->setDeemphasisMode(_this->deempModes[_this->deempId]); | ||||
|             } | ||||
|         } | ||||
|  | ||||
| @@ -336,7 +342,7 @@ private: | ||||
|         snapInterval = selectedDemod->getDefaultSnapInterval(); | ||||
|         squelchLevel = MIN_SQUELCH; | ||||
|         deempAllowed = selectedDemod->getDeempAllowed(); | ||||
|         deempMode = selectedDemod->getDefaultDeemphasisMode(); | ||||
|         deempId = deempModes.valueId((DeemphasisMode)selectedDemod->getDefaultDeemphasisMode()); | ||||
|         squelchEnabled = false; | ||||
|         postProcEnabled = selectedDemod->getPostProcEnabled(); | ||||
|         FMIFNRAllowed = selectedDemod->getFMIFNRAllowed(); | ||||
| @@ -359,7 +365,15 @@ private: | ||||
|             squelchEnabled = config.conf[name][selectedDemod->getName()]["squelchEnabled"]; | ||||
|         } | ||||
|         if (config.conf[name][selectedDemod->getName()].contains("deempMode")) { | ||||
|             deempMode = config.conf[name][selectedDemod->getName()]["deempMode"]; | ||||
|             // Upgrade to the text key | ||||
|             if (!config.conf[name][selectedDemod->getName()]["deempMode"].is_string()) { | ||||
|                 config.conf[name][selectedDemod->getName()]["deempMode"] = deempModes.key(deempId); | ||||
|             } | ||||
|  | ||||
|             std::string deempOpt = config.conf[name][selectedDemod->getName()]["deempMode"]; | ||||
|             if (deempModes.keyExists(deempOpt)) { | ||||
|                 deempId = deempModes.keyId(deempOpt); | ||||
|             } | ||||
|         } | ||||
|         if (config.conf[name][selectedDemod->getName()].contains("FMIFNREnabled")) { | ||||
|             FMIFNREnabled = config.conf[name][selectedDemod->getName()]["FMIFNREnabled"]; | ||||
| @@ -374,7 +388,6 @@ private: | ||||
|             nbLevel = config.conf[name][selectedDemod->getName()]["noiseBlankerLevel"]; | ||||
|         } | ||||
|         config.release(); | ||||
|         deempMode = std::clamp<int>(deempMode, 0, _DEEMP_MODE_COUNT-1); | ||||
|  | ||||
|         // Configure VFO | ||||
|         if (vfo) { | ||||
| @@ -411,7 +424,7 @@ private: | ||||
|             afChain.enable(&resamp); | ||||
|  | ||||
|             // Configure deemphasis | ||||
|             setDeemphasisMode(deempMode); | ||||
|             setDeemphasisMode(deempModes[deempId]); | ||||
|         } | ||||
|         else { | ||||
|             // Disable everyting if post processing is disabled | ||||
| @@ -475,18 +488,16 @@ private: | ||||
|         afChain.start(); | ||||
|     } | ||||
|  | ||||
|     void setDeemphasisMode(int mode) { | ||||
|         deempMode = std::clamp<int>(mode, 0, _DEEMP_MODE_COUNT-1); | ||||
|     void setDeemphasisMode(DeemphasisMode mode) { | ||||
|         deempId = deempModes.valueId(mode); | ||||
|         if (!postProcEnabled || !selectedDemod) { return; } | ||||
|         bool deempEnabled = (deempMode != DEEMP_MODE_NONE); | ||||
|         if (deempEnabled) { | ||||
|             deemp.block.setTau(DeemphasisModes[deempMode]); | ||||
|         } | ||||
|         bool deempEnabled = (mode != DEEMP_MODE_NONE); | ||||
|         if (deempEnabled) { deemp.block.setTau(deempTaus[mode]); } | ||||
|         afChain.setState(&deemp, deempEnabled); | ||||
|  | ||||
|         // Save config | ||||
|         config.acquire(); | ||||
|         config.conf[name][selectedDemod->getName()]["deempMode"] = deempMode; | ||||
|         config.conf[name][selectedDemod->getName()]["deempMode"] = deempModes.key(deempId); | ||||
|         config.release(true); | ||||
|     } | ||||
|  | ||||
| @@ -643,6 +654,8 @@ private: | ||||
|     std::array<demod::Demodulator*, _RADIO_DEMOD_COUNT> demods; | ||||
|     demod::Demodulator* selectedDemod = NULL; | ||||
|  | ||||
|     OptionList<std::string, DeemphasisMode> deempModes; | ||||
|  | ||||
|     double audioSampleRate = 48000.0; | ||||
|     float minBandwidth; | ||||
|     float maxBandwidth; | ||||
| @@ -655,7 +668,7 @@ private: | ||||
|     bool squelchEnabled = false; | ||||
|     float squelchLevel; | ||||
|  | ||||
|     int deempMode = DEEMP_MODE_NONE; | ||||
|     int deempId = 0; | ||||
|     bool deempAllowed; | ||||
|  | ||||
|     bool FMIFNRAllowed; | ||||
|   | ||||
		Reference in New Issue
	
	Block a user