mirror of
				https://github.com/AlexandreRouma/SDRPlusPlus.git
				synced 2025-10-31 08:58:13 +01:00 
			
		
		
		
	Added audio source
This commit is contained in:
		
							
								
								
									
										2
									
								
								.github/workflows/build_all.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								.github/workflows/build_all.yml
									
									
									
									
										vendored
									
									
								
							| @@ -116,7 +116,7 @@ jobs: | ||||
|  | ||||
|         - name: Prepare CMake | ||||
|           working-directory: ${{runner.workspace}}/build | ||||
|           run: cmake $GITHUB_WORKSPACE -DOPT_BUILD_PLUTOSDR_SOURCE=ON -DOPT_BUILD_SOAPY_SOURCE=OFF -DOPT_BUILD_BLADERF_SOURCE=ON -DOPT_BUILD_SDRPLAY_SOURCE=ON -DOPT_BUILD_LIMESDR_SOURCE=ON -DOPT_BUILD_AUDIO_SINK=OFF -DOPT_BUILD_PORTAUDIO_SINK=ON -DOPT_BUILD_NEW_PORTAUDIO_SINK=ON -DOPT_BUILD_M17_DECODER=ON -DUSE_BUNDLE_DEFAULTS=ON -DCMAKE_BUILD_TYPE=Release | ||||
|           run: cmake $GITHUB_WORKSPACE -DOPT_BUILD_PLUTOSDR_SOURCE=ON -DOPT_BUILD_SOAPY_SOURCE=OFF -DOPT_BUILD_BLADERF_SOURCE=ON -DOPT_BUILD_SDRPLAY_SOURCE=ON -DOPT_BUILD_LIMESDR_SOURCE=ON -DOPT_BUILD_AUDIO_SINK=OFF -DOPT_BUILD_PORTAUDIO_SINK=ON -DOPT_BUILD_NEW_PORTAUDIO_SINK=ON -DOPT_BUILD_M17_DECODER=ON -DOPT_BUILD_AUDIO_SOURCE=OFF -DUSE_BUNDLE_DEFAULTS=ON -DCMAKE_BUILD_TYPE=Release | ||||
|  | ||||
|         - name: Build | ||||
|           working-directory: ${{runner.workspace}}/build | ||||
|   | ||||
| @@ -11,6 +11,7 @@ option(OPT_OVERRIDE_STD_FILESYSTEM "Use a local version of std::filesystem on sy | ||||
| # Sources | ||||
| option(OPT_BUILD_AIRSPY_SOURCE "Build Airspy Source Module (Dependencies: libairspy)" ON) | ||||
| option(OPT_BUILD_AIRSPYHF_SOURCE "Build Airspy HF+ Source Module (Dependencies: libairspyhf)" ON) | ||||
| option(OPT_BUILD_AUDIO_SOURCE "Build Audio Source Module (Dependencies: rtaudio)" ON) | ||||
| option(OPT_BUILD_BLADERF_SOURCE "Build BladeRF Source Module (Dependencies: libbladeRF)" OFF) | ||||
| option(OPT_BUILD_FILE_SOURCE "Wav file source" ON) | ||||
| option(OPT_BUILD_HACKRF_SOURCE "Build HackRF Source Module (Dependencies: libhackrf)" ON) | ||||
| @@ -116,6 +117,10 @@ if (OPT_BUILD_AIRSPYHF_SOURCE) | ||||
| add_subdirectory("source_modules/airspyhf_source") | ||||
| endif (OPT_BUILD_AIRSPYHF_SOURCE) | ||||
|  | ||||
| if (OPT_BUILD_AUDIO_SOURCE) | ||||
| add_subdirectory("source_modules/audio_source") | ||||
| endif (OPT_BUILD_AUDIO_SOURCE) | ||||
|  | ||||
| if (OPT_BUILD_BLADERF_SOURCE) | ||||
| add_subdirectory("source_modules/bladerf_source") | ||||
| endif (OPT_BUILD_BLADERF_SOURCE) | ||||
|   | ||||
| @@ -14,7 +14,7 @@ android { | ||||
|  | ||||
|         externalNativeBuild { | ||||
|             cmake { | ||||
|                 arguments "-DOPT_BACKEND_GLFW=OFF", "-DOPT_BACKEND_ANDROID=ON", "-DOPT_BUILD_SOAPY_SOURCE=OFF", "-DOPT_BUILD_ANDROID_AUDIO_SINK=ON", "-DOPT_BUILD_AUDIO_SINK=OFF", "-DOPT_BUILD_DISCORD_PRESENCE=OFF", "-DOPT_BUILD_M17_DECODER=ON", "-DOPT_BUILD_PLUTOSDR_SOURCE=ON" | ||||
|                 arguments "-DOPT_BACKEND_GLFW=OFF", "-DOPT_BACKEND_ANDROID=ON", "-DOPT_BUILD_SOAPY_SOURCE=OFF", "-DOPT_BUILD_ANDROID_AUDIO_SINK=ON", "-DOPT_BUILD_AUDIO_SINK=OFF", "-DOPT_BUILD_DISCORD_PRESENCE=OFF", "-DOPT_BUILD_M17_DECODER=ON", "-DOPT_BUILD_PLUTOSDR_SOURCE=ON", "-DOPT_BUILD_AUDIO_SOURCE=OFF" | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|   | ||||
| @@ -163,6 +163,8 @@ int sdrpp_main(int argc, char* argv[]) { | ||||
|     defConfig["moduleInstances"]["Airspy Source"]["enabled"] = true; | ||||
|     defConfig["moduleInstances"]["AirspyHF+ Source"]["module"] = "airspyhf_source"; | ||||
|     defConfig["moduleInstances"]["AirspyHF+ Source"]["enabled"] = true; | ||||
|     defConfig["moduleInstances"]["Audio Source"]["module"] = "audio_source"; | ||||
|     defConfig["moduleInstances"]["Audio Source"]["enabled"] = true; | ||||
|     defConfig["moduleInstances"]["BladeRF Source"]["module"] = "bladerf_source"; | ||||
|     defConfig["moduleInstances"]["BladeRF Source"]["enabled"] = true; | ||||
|     defConfig["moduleInstances"]["File Source"]["module"] = "file_source"; | ||||
|   | ||||
							
								
								
									
										25
									
								
								source_modules/audio_source/CMakeLists.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										25
									
								
								source_modules/audio_source/CMakeLists.txt
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,25 @@ | ||||
| cmake_minimum_required(VERSION 3.13) | ||||
| project(audio_source) | ||||
|  | ||||
| file(GLOB SRC "src/*.cpp") | ||||
|  | ||||
| include(${SDRPP_MODULE_CMAKE}) | ||||
|  | ||||
| if (MSVC) | ||||
|     # Lib path | ||||
|     target_link_directories(audio_source PRIVATE "C:/Program Files (x86)/RtAudio/lib") | ||||
|  | ||||
|     # Misc headers | ||||
|     target_include_directories(audio_source PRIVATE "C:/Program Files (x86)/RtAudio/include/rtaudio") | ||||
|  | ||||
|     target_link_libraries(audio_source PRIVATE rtaudio) | ||||
| else (MSVC) | ||||
|     find_package(PkgConfig) | ||||
|  | ||||
|     pkg_check_modules(RTAUDIO REQUIRED rtaudio) | ||||
|  | ||||
|     target_include_directories(audio_source PRIVATE ${RTAUDIO_INCLUDE_DIRS}) | ||||
|     target_link_directories(audio_source PRIVATE ${RTAUDIO_LIBRARY_DIRS}) | ||||
|     target_link_libraries(audio_source PRIVATE ${RTAUDIO_LIBRARIES}) | ||||
|  | ||||
| endif () | ||||
							
								
								
									
										293
									
								
								source_modules/audio_source/src/main.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										293
									
								
								source_modules/audio_source/src/main.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,293 @@ | ||||
| #include <spdlog/spdlog.h> | ||||
| #include <module.h> | ||||
| #include <gui/gui.h> | ||||
| #include <signal_path/signal_path.h> | ||||
| #include <core.h> | ||||
| #include <gui/style.h> | ||||
| #include <config.h> | ||||
| #include <gui/smgui.h> | ||||
| #include <gui/widgets/stepped_slider.h> | ||||
| #include <utils/optionlist.h> | ||||
| #include <RtAudio.h> | ||||
|  | ||||
| #define CONCAT(a, b) ((std::string(a) + b).c_str()) | ||||
|  | ||||
| SDRPP_MOD_INFO{ | ||||
|     /* Name:            */ "audio_source", | ||||
|     /* Description:     */ "Audio source module for SDR++", | ||||
|     /* Author:          */ "Ryzerth", | ||||
|     /* Version:         */ 0, 1, 0, | ||||
|     /* Max instances    */ 1 | ||||
| }; | ||||
|  | ||||
| ConfigManager config; | ||||
|  | ||||
| struct DeviceInfo { | ||||
|     RtAudio::DeviceInfo info; | ||||
|     int id; | ||||
|     bool operator==(const struct DeviceInfo& other) { | ||||
|         return other.id == id; | ||||
|     } | ||||
| }; | ||||
|  | ||||
| class AudioSourceModule : public ModuleManager::Instance { | ||||
| public: | ||||
|     AudioSourceModule(std::string name) { | ||||
|         this->name = name; | ||||
|  | ||||
|         sampleRate = 48000.0; | ||||
|  | ||||
|         handler.ctx = this; | ||||
|         handler.selectHandler = menuSelected; | ||||
|         handler.deselectHandler = menuDeselected; | ||||
|         handler.menuHandler = menuHandler; | ||||
|         handler.startHandler = start; | ||||
|         handler.stopHandler = stop; | ||||
|         handler.tuneHandler = tune; | ||||
|         handler.stream = &stream; | ||||
|  | ||||
|         // Refresh devices | ||||
|         refresh(); | ||||
|  | ||||
|         // Select device | ||||
|         std::string device = ""; | ||||
|         config.acquire(); | ||||
|         if (config.conf.contains("device")) { | ||||
|             device = config.conf["device"]; | ||||
|         } | ||||
|         config.release(); | ||||
|         select(device); | ||||
|          | ||||
|         sigpath::sourceManager.registerSource("Audio", &handler); | ||||
|     } | ||||
|  | ||||
|     ~AudioSourceModule() { | ||||
|         stop(this); | ||||
|         sigpath::sourceManager.unregisterSource("Audio"); | ||||
|     } | ||||
|  | ||||
|     void postInit() {} | ||||
|  | ||||
|     void enable() { | ||||
|         enabled = true; | ||||
|     } | ||||
|  | ||||
|     void disable() { | ||||
|         enabled = false; | ||||
|     } | ||||
|  | ||||
|     bool isEnabled() { | ||||
|         return enabled; | ||||
|     } | ||||
|  | ||||
|     void refresh() { | ||||
|         devices.clear(); | ||||
|  | ||||
|         int count = audio.getDeviceCount(); | ||||
|         for (int i = 0; i < count; i++) { | ||||
|             try { | ||||
|                 // Get info | ||||
|                 auto info = audio.getDeviceInfo(i); | ||||
|  | ||||
|                 // Check that it has a stereo input | ||||
|                 if (info.probed && info.inputChannels < 2) { continue; } | ||||
|  | ||||
|                 // Save info | ||||
|                 DeviceInfo dinfo = { info, i }; | ||||
|                 devices.define(info.name, info.name, dinfo); | ||||
|             } | ||||
|             catch (std::exception e) { | ||||
|                 spdlog::error("Error getting audio device info: {0}", e.what()); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     void select(std::string name) { | ||||
|         if (devices.empty()) { | ||||
|             selectedDevice.clear(); | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         // Check that such a device exist. If not select first | ||||
|         if (!devices.keyExists(name)) { | ||||
|             select(devices.key(0)); | ||||
|             return; | ||||
|         } | ||||
|          | ||||
|         // Get device info | ||||
|         devId = devices.keyId(name); | ||||
|         auto info = devices.value(devId).info; | ||||
|         selectedDevice = name; | ||||
|  | ||||
|         // List samplerates and save ID of the preference one | ||||
|         sampleRates.clear(); | ||||
|         for (const auto& sr : info.sampleRates) { | ||||
|             std::string name = getBandwdithScaled(sr); | ||||
|             sampleRates.define(sr, name, sr); | ||||
|             if (sr == info.preferredSampleRate) { | ||||
|                 srId = sampleRates.valueId(sr); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         // Load samplerate from config | ||||
|         config.acquire(); | ||||
|         if (config.conf["devices"][selectedDevice].contains("sampleRate")) { | ||||
|             sampleRate = config.conf["devices"][selectedDevice]["sampleRate"]; | ||||
|             if (sampleRates.keyExists(sampleRate)) { | ||||
|                 srId = sampleRates.keyId(sampleRate); | ||||
|             } | ||||
|         } | ||||
|         config.release(); | ||||
|  | ||||
|         // Update samplerate from ID | ||||
|         sampleRate = sampleRates[srId]; | ||||
|         core::setInputSampleRate(sampleRate); | ||||
|     } | ||||
|  | ||||
| private: | ||||
|     std::string getBandwdithScaled(double bw) { | ||||
|         char buf[1024]; | ||||
|         if (bw >= 1000000.0) { | ||||
|             sprintf(buf, "%.1lfMHz", bw / 1000000.0); | ||||
|         } | ||||
|         else if (bw >= 1000.0) { | ||||
|             sprintf(buf, "%.1lfKHz", bw / 1000.0); | ||||
|         } | ||||
|         else { | ||||
|             sprintf(buf, "%.1lfHz", bw); | ||||
|         } | ||||
|         return std::string(buf); | ||||
|     } | ||||
|  | ||||
|     static void menuSelected(void* ctx) { | ||||
|         AudioSourceModule* _this = (AudioSourceModule*)ctx; | ||||
|         core::setInputSampleRate(_this->sampleRate); | ||||
|         spdlog::info("AudioSourceModule '{0}': Menu Select!", _this->name); | ||||
|     } | ||||
|  | ||||
|     static void menuDeselected(void* ctx) { | ||||
|         AudioSourceModule* _this = (AudioSourceModule*)ctx; | ||||
|         spdlog::info("AudioSourceModule '{0}': Menu Deselect!", _this->name); | ||||
|     } | ||||
|  | ||||
|     static void start(void* ctx) { | ||||
|         AudioSourceModule* _this = (AudioSourceModule*)ctx; | ||||
|         if (_this->running) { return; } | ||||
|          | ||||
|         // Stream options | ||||
|         RtAudio::StreamParameters parameters; | ||||
|         parameters.deviceId = _this->devices[_this->devId].id; | ||||
|         parameters.nChannels = 2; | ||||
|         unsigned int bufferFrames = _this->sampleRate / 200; | ||||
|         RtAudio::StreamOptions opts; | ||||
|         opts.flags = RTAUDIO_MINIMIZE_LATENCY; | ||||
|         opts.streamName = "SDR++ Audio Source"; | ||||
|  | ||||
|         // Open and start stream | ||||
|         try { | ||||
|             _this->audio.openStream(NULL, ¶meters, RTAUDIO_FLOAT32, _this->sampleRate, &bufferFrames, callback, _this, &opts); | ||||
|             _this->audio.startStream(); | ||||
|             _this->running = true; | ||||
|         } | ||||
|         catch (std::exception e) { | ||||
|             spdlog::error("Error opening audio device: {0}", e.what()); | ||||
|         } | ||||
|          | ||||
|         spdlog::info("AudioSourceModule '{0}': Start!", _this->name); | ||||
|     } | ||||
|  | ||||
|     static void stop(void* ctx) { | ||||
|         AudioSourceModule* _this = (AudioSourceModule*)ctx; | ||||
|         if (!_this->running) { return; } | ||||
|         _this->running = false; | ||||
|          | ||||
|         _this->audio.stopStream(); | ||||
|         _this->audio.closeStream(); | ||||
|  | ||||
|         spdlog::info("AudioSourceModule '{0}': Stop!", _this->name); | ||||
|     } | ||||
|  | ||||
|     static void tune(double freq, void* ctx) { | ||||
|         // Not possible | ||||
|     } | ||||
|  | ||||
|     static void menuHandler(void* ctx) { | ||||
|         AudioSourceModule* _this = (AudioSourceModule*)ctx; | ||||
|  | ||||
|         if (_this->running) { SmGui::BeginDisabled(); } | ||||
|  | ||||
|         SmGui::FillWidth(); | ||||
|         SmGui::ForceSync(); | ||||
|         if (SmGui::Combo(CONCAT("##_audio_dev_sel_", _this->name), &_this->devId, _this->devices.txt)) { | ||||
|             std::string dev = _this->devices.key(_this->devId); | ||||
|             _this->select(dev); | ||||
|             config.acquire(); | ||||
|             config.conf["device"] = dev; | ||||
|             config.release(true); | ||||
|         } | ||||
|  | ||||
|         if (SmGui::Combo(CONCAT("##_audio_sr_sel_", _this->name), &_this->srId, _this->sampleRates.txt)) { | ||||
|             _this->sampleRate = _this->sampleRates[_this->srId]; | ||||
|             core::setInputSampleRate(_this->sampleRate); | ||||
|             if (!_this->selectedDevice.empty()) { | ||||
|                 config.acquire(); | ||||
|                 config.conf["devices"][_this->selectedDevice]["sampleRate"] = _this->sampleRate; | ||||
|                 config.release(true); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         SmGui::SameLine(); | ||||
|         SmGui::FillWidth(); | ||||
|         SmGui::ForceSync(); | ||||
|         if (SmGui::Button(CONCAT("Refresh##_audio_refr_", _this->name))) { | ||||
|             _this->refresh(); | ||||
|             _this->select(_this->selectedDevice); | ||||
|         } | ||||
|  | ||||
|         if (_this->running) { SmGui::EndDisabled(); } | ||||
|     } | ||||
|  | ||||
|     static int callback(void* outputBuffer, void* inputBuffer, unsigned int nBufferFrames, double streamTime, RtAudioStreamStatus status, void* userData) { | ||||
|         AudioSourceModule* _this = (AudioSourceModule*)userData; | ||||
|         memcpy(_this->stream.writeBuf, inputBuffer, nBufferFrames * sizeof(dsp::complex_t)); | ||||
|         _this->stream.swap(nBufferFrames); | ||||
|         return 0; | ||||
|     } | ||||
|  | ||||
|     std::string name; | ||||
|     bool enabled = true; | ||||
|     dsp::stream<dsp::complex_t> stream; | ||||
|     double sampleRate; | ||||
|     SourceManager::SourceHandler handler; | ||||
|     bool running = false; | ||||
|      | ||||
|     OptionList<std::string, DeviceInfo> devices; | ||||
|     OptionList<double, double> sampleRates; | ||||
|     std::string selectedDevice = ""; | ||||
|     int devId = 0; | ||||
|     int srId = 0; | ||||
|  | ||||
|     RtAudio audio; | ||||
| }; | ||||
|  | ||||
| MOD_EXPORT void _INIT_() { | ||||
|     json def = json({}); | ||||
|     def["devices"] = json({}); | ||||
|     def["device"] = ""; | ||||
|     config.setPath(core::args["root"].s() + "/audio_source_config.json"); | ||||
|     config.load(def); | ||||
|     config.enableAutoSave(); | ||||
| } | ||||
|  | ||||
| MOD_EXPORT ModuleManager::Instance* _CREATE_INSTANCE_(std::string name) { | ||||
|     return new AudioSourceModule(name); | ||||
| } | ||||
|  | ||||
| MOD_EXPORT void _DELETE_INSTANCE_(ModuleManager::Instance* instance) { | ||||
|     delete (AudioSourceModule*)instance; | ||||
| } | ||||
|  | ||||
| MOD_EXPORT void _END_() { | ||||
|     config.disableAutoSave(); | ||||
|     config.save(); | ||||
| } | ||||
		Reference in New Issue
	
	Block a user