diff --git a/CMakeLists.txt b/CMakeLists.txt index aaf8a12e..306812f6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -8,7 +8,7 @@ add_subdirectory("recorder") add_subdirectory("soapy") add_subdirectory("file_source") add_subdirectory("rtl_tcp_source") -# add_subdirectory("audio_sink") +add_subdirectory("audio_sink") add_subdirectory("rx888_source") add_subdirectory("demo") diff --git a/audio_sink/src/main.cpp b/audio_sink/src/main.cpp index d2b4b3d3..c5fd5c40 100644 --- a/audio_sink/src/main.cpp +++ b/audio_sink/src/main.cpp @@ -3,16 +3,84 @@ #include #include #include +#include +#include #define CONCAT(a, b) ((std::string(a) + b).c_str()) class AudioSink : SinkManager::Sink { public: + struct AudioDevice_t { + std::string name; + int index; + int channels; + std::vector sampleRates; + std::string txtSampleRates; + }; + AudioSink(SinkManager::Stream* stream) { _stream = stream; + audioStream = _stream->bindStream(); + s2m.init(audioStream); + monoRB.init(&s2m.out); + stereoRB.init(audioStream); + + // Initialize PortAudio + Pa_Initialize(); + devCount = Pa_GetDeviceCount(); + devId = Pa_GetDefaultOutputDevice(); + const PaDeviceInfo *deviceInfo; + PaStreamParameters outputParams; + outputParams.sampleFormat = paFloat32; + outputParams.hostApiSpecificStreamInfo = NULL; + + // Gather hardware info + for(int i = 0; i < devCount; i++) { + deviceInfo = Pa_GetDeviceInfo(i); + if (deviceInfo->maxOutputChannels < 1) { + continue; + } + AudioDevice_t dev; + dev.name = deviceInfo->name; + dev.index = i; + dev.channels = std::min(deviceInfo->maxOutputChannels, 2); + dev.sampleRates.clear(); + dev.txtSampleRates = ""; + for (int j = 0; j < 6; j++) { + outputParams.channelCount = dev.channels; + outputParams.device = dev.index; + outputParams.suggestedLatency = deviceInfo->defaultLowOutputLatency; + PaError err = Pa_IsFormatSupported(NULL, &outputParams, POSSIBLE_SAMP_RATE[j]); + if (err != paFormatIsSupported) { + continue; + } + dev.sampleRates.push_back(POSSIBLE_SAMP_RATE[j]); + dev.txtSampleRates += std::to_string((int)POSSIBLE_SAMP_RATE[j]); + dev.txtSampleRates += '\0'; + } + if (dev.sampleRates.size() == 0) { + continue; + } + if (i == devId) { + devListId = devices.size(); + defaultDev = devListId; + } + devices.push_back(dev); + deviceNames.push_back(deviceInfo->name); + txtDevList += deviceInfo->name; + txtDevList += '\0'; + } } ~AudioSink() { + + } + + void start() { + + } + + void stop() { } @@ -21,7 +89,32 @@ public: } private: + SinkManager::Stream* _stream; + dsp::stream* audioStream; + dsp::StereoToMono s2m; + dsp::RingBufferSink monoRB; + dsp::RingBufferSink stereoRB; + + int srId = 0; + float sampleRate; + int devCount; + int devId = 0; + int devListId = 0; + int defaultDev = 0; + + const double POSSIBLE_SAMP_RATE[6] = { + 48000.0f, + 44100.0f, + 24000.0f, + 22050.0f, + 12000.0f, + 11025.0f + }; + + std::vector devices; + std::vector deviceNames; + std::string txtDevList; }; diff --git a/core/src/gui/menus/sink.cpp b/core/src/gui/menus/sink.cpp new file mode 100644 index 00000000..3bfb4f0b --- /dev/null +++ b/core/src/gui/menus/sink.cpp @@ -0,0 +1,12 @@ +#include +#include + +namespace sinkmenu { + void init() { + + } + + void draw(void* ctx) { + sigpath::sinkManager.showMenu(); + } +}; \ No newline at end of file diff --git a/core/src/gui/menus/sink.h b/core/src/gui/menus/sink.h new file mode 100644 index 00000000..4773541d --- /dev/null +++ b/core/src/gui/menus/sink.h @@ -0,0 +1,6 @@ +#pragma once + +namespace sinkmenu { + void init(); + void draw(void* ctx); +}; \ No newline at end of file diff --git a/core/src/signal_path/sink.cpp b/core/src/signal_path/sink.cpp index 22c862c7..10b38bd7 100644 --- a/core/src/signal_path/sink.cpp +++ b/core/src/signal_path/sink.cpp @@ -1,5 +1,6 @@ #include #include +#include #include SinkManager::SinkManager() { @@ -13,6 +14,14 @@ SinkManager::Stream::Stream(dsp::stream* in, const Event:: splitter.init(_in); } +void SinkManager::Stream::start() { + sink->start(); +} + +void SinkManager::Stream::stop() { + sink->stop(); +} + void SinkManager::Stream::setInput(dsp::stream* in) { std::lock_guard lck(ctrlMtx); _in = in; @@ -42,6 +51,7 @@ void SinkManager::registerSinkProvider(std::string name, SinkProvider provider) return; } providers[name] = provider; + providerNames.push_back(name); } void SinkManager::registerStream(std::string name, SinkManager::Stream* stream) { @@ -75,6 +85,22 @@ void SinkManager::unregisterStream(std::string name) { delete stream; } +void SinkManager::startStream(std::string name) { + if (streams.find(name) == streams.end()) { + spdlog::error("Cannot start stream '{0}', this stream doesn't exist", name); + return; + } + streams[name]->start(); +} + +void SinkManager::stopStream(std::string name) { + if (streams.find(name) == streams.end()) { + spdlog::error("Cannot stop stream '{0}', this stream doesn't exist", name); + return; + } + streams[name]->stop(); +} + dsp::stream* SinkManager::bindStream(std::string name) { if (streams.find(name) == streams.end()) { spdlog::error("Cannot bind to stream '{0}'. Stream doesn't exist", name); @@ -91,8 +117,37 @@ void SinkManager::unbindStream(std::string name, dsp::stream* str streams[name]->unbindStream(stream); } +void SinkManager::setStreamSink(std::string name, std::string providerName) { + +} + void SinkManager::showMenu() { + float menuWidth = ImGui::GetContentRegionAvailWidth(); + int count = 0; + int maxCount = streams.size(); + + std::string provStr = ""; + for (auto const& [name, provider] : providers) { + provStr += name; + provStr += '\0'; + } + for (auto const& [name, stream] : streams) { - + ImGui::SetCursorPosX((menuWidth / 2.0f) - (ImGui::CalcTextSize(name.c_str()).x / 2.0f)); + ImGui::Text("%s", name.c_str()); + + if (ImGui::Combo("", &stream->providerId, provStr.c_str())) { + + } + + stream->sink->menuHandler(); + + ImGui::PopItemWidth(); + count++; + if (count < maxCount) { + ImGui::Spacing(); + ImGui::Separator(); + } + ImGui::Spacing(); } } \ No newline at end of file diff --git a/core/src/signal_path/sink.h b/core/src/signal_path/sink.h index 0b89a28a..12df6f75 100644 --- a/core/src/signal_path/sink.h +++ b/core/src/signal_path/sink.h @@ -6,6 +6,7 @@ #include #include #include +#include class SinkManager { public: @@ -13,6 +14,8 @@ public: class Sink { public: + virtual void start() = 0; + virtual void stop() = 0; virtual void menuHandler() = 0; }; @@ -20,6 +23,9 @@ public: public: Stream(dsp::stream* in, const Event::EventHandler& srChangeHandler, float sampleRate); + void start(); + void stop(); + void setInput(dsp::stream* in); dsp::stream* bindStream(); @@ -29,13 +35,14 @@ public: friend SinkManager::Sink; Event srChange; + SinkManager::Sink* sink; + int providerId = 0; private: void setSampleRate(float sampleRate); dsp::stream* _in; dsp::Splitter splitter; - SinkManager::Sink* sink; std::mutex ctrlMtx; float _sampleRate; @@ -51,15 +58,19 @@ public: void registerStream(std::string name, Stream* stream); void unregisterStream(std::string name); + void startStream(std::string name); + void stopStream(std::string name); + + void setStreamSink(std::string name, std::string providerName); + dsp::stream* bindStream(std::string name); void unbindStream(std::string name, dsp::stream* stream); void showMenu(); private: - // TODO: Replace with std::unordered_map std::map providers; std::map streams; - + std::vector providerNames; }; \ No newline at end of file diff --git a/file_source/src/main.cpp b/file_source/src/main.cpp index 98c03823..bda7fa18 100644 --- a/file_source/src/main.cpp +++ b/file_source/src/main.cpp @@ -30,7 +30,7 @@ public: handler.stream = &stream; sigpath::sourceManager.registerSource("File", &handler); - reader = new WavReader("D:/Downloads/20120416_glv_594_2.wav"); + reader = new WavReader("D:/satpic/raw_recordings/NOAA-18_09-08-2018_21-39-00_baseband_NR.wav"); spdlog::info("Samplerate: {0}, Bit depth: {1}, Channel count: {2}", reader->getSampleRate(), reader->getBitDepth(), reader->getChannelCount()); diff --git a/root_dev/config.json b/root_dev/config.json index 98679290..de42e5b0 100644 --- a/root_dev/config.json +++ b/root_dev/config.json @@ -3,7 +3,7 @@ "Radio": { "device": "Speakers (Realtek High Definiti", "sampleRate": 48000.0, - "volume": 0.385185182094574 + "volume": 0.39259257912635803 }, "Radio 1": { "device": "Speakers (Realtek High Definition Audio)", @@ -20,7 +20,7 @@ "bandPlanEnabled": true, "defaultSink": "Audio", "fftHeight": 296, - "frequency": 14004144, + "frequency": 7300000, "max": 0.0, "maximized": true, "menuOrder": [ @@ -33,7 +33,7 @@ "Display" ], "menuWidth": 300, - "min": -63.235294342041016, + "min": -74.26470184326172, "showWaterfall": true, "source": "", "sourceSettings": {}, diff --git a/root_dev/module_list.json b/root_dev/module_list.json index e068ba01..cfdef7a2 100644 --- a/root_dev/module_list.json +++ b/root_dev/module_list.json @@ -3,5 +3,7 @@ "Recorder": "./recorder/Release/recorder.dll", "Soapy": "./soapy/Release/soapy.dll", "RTLTCPSource": "./rtl_tcp_source/Release/rtl_tcp_source.dll", - "RX888Source": "./rx888_source/Release/rx888_source.dll" + "FileSource": "./file_source/Release/file_source.dll", + "RX888Source": "./rx888_source/Release/rx888_source.dll", + "AudioSink": "./audio_sink/Release/audio_sink.dll" } diff --git a/root_dev/soapy_source_config.json b/root_dev/soapy_source_config.json index af377c41..c0894487 100644 --- a/root_dev/soapy_source_config.json +++ b/root_dev/soapy_source_config.json @@ -1,5 +1,5 @@ { - "device": "Generic RTL2832U OEM :: 00000001", + "device": "CABLE Output (VB-Audio Virtual Cable)", "devices": { "AirSpy HF+ [c852435de0224af7]": { "gains": { @@ -8,6 +8,9 @@ }, "sampleRate": 768000.0 }, + "CABLE Output (VB-Audio Virtual Cable)": { + "sampleRate": 96000.0 + }, "Default Device": { "sampleRate": 32000.0 }, diff --git a/rx888_source/src/main.cpp b/rx888_source/src/main.cpp index c63b52d7..8350c0f4 100644 --- a/rx888_source/src/main.cpp +++ b/rx888_source/src/main.cpp @@ -11,7 +11,7 @@ #define CONCAT(a, b) ((std::string(a) + b).c_str()) -#define ADC_RATE 64000000 +#define ADC_RATE 128000000 #define XFER_TIMEOUT 5000 #define SEL0 (8) // SEL0 GPIO26 @@ -170,7 +170,7 @@ private: // Check if the incomming data is bulk I/Q and end transfer if (EndPt->Attributes == 2) { if (EndPt->FinishDataXfer((PUCHAR)buffer, rLen, &inOvLap, context)) { - if (_this->realStream.aquire() < 0) { return; } + if (_this->realStream.aquire() < 0) { break; } memcpy(_this->realStream.data, buffer, rLen); _this->realStream.write(rLen / 2); }