From 19e516f206ec5a35dc03c84a49714463ba1ec5ba Mon Sep 17 00:00:00 2001 From: Ryzerth Date: Sun, 29 Nov 2020 20:55:00 +0100 Subject: [PATCH] Push before potential f*ck up --- CMakeLists.txt | 2 +- audio_sink/src/main.cpp | 105 ++++++++++++++++++++++++++++++++-- core/src/gui/main_window.cpp | 3 + core/src/signal_path/sink.cpp | 28 +++++++-- core/src/signal_path/sink.h | 22 +++++-- root_dev/config.json | 1 + 6 files changed, 145 insertions(+), 16 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 39bf477d..98b12b80 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("plutosdr_source") add_subdirectory("demo") diff --git a/audio_sink/src/main.cpp b/audio_sink/src/main.cpp index c5fd5c40..aa6b513a 100644 --- a/audio_sink/src/main.cpp +++ b/audio_sink/src/main.cpp @@ -14,12 +14,14 @@ public: std::string name; int index; int channels; + int srId; std::vector sampleRates; std::string txtSampleRates; }; - AudioSink(SinkManager::Stream* stream) { + AudioSink(SinkManager::Stream* stream, std::string streamName) { _stream = stream; + _streamName = streamName; audioStream = _stream->bindStream(); s2m.init(audioStream); monoRB.init(&s2m.out); @@ -65,11 +67,14 @@ public: devListId = devices.size(); defaultDev = devListId; } + dev.srId = 0; devices.push_back(dev); deviceNames.push_back(deviceInfo->name); txtDevList += deviceInfo->name; txtDevList += '\0'; } + + // Load config from file } ~AudioSink() { @@ -77,31 +82,118 @@ public: } void start() { - + if (running) { + return; + } + doStart(); + running = true; } void stop() { - + if (!running) { + return; + } + doStop(); + running = false; } void menuHandler() { + if (ImGui::Combo(("##_audio_sink_dev_"+_streamName).c_str(), &devListId, txtDevList.c_str())) { + // TODO: Load SR from config + if (running) { + doStop(); + doStart(); + } + // TODO: Save to config + } + AudioDevice_t dev = devices[devListId]; + + if (ImGui::Combo(("##_audio_sink_sr_"+_streamName).c_str(), &dev.srId, txtDevList.c_str())) { + if (running) { + doStop(); + doStart(); + } + // TODO: Save to config + } } private: + void doStart() { + const PaDeviceInfo *deviceInfo; + AudioDevice_t dev = devices[devListId]; + PaStreamParameters outputParams; + deviceInfo = Pa_GetDeviceInfo(dev.index); + outputParams.channelCount = 2; + outputParams.sampleFormat = paFloat32; + outputParams.hostApiSpecificStreamInfo = NULL; + outputParams.device = dev.index; + outputParams.suggestedLatency = Pa_GetDeviceInfo(outputParams.device)->defaultLowOutputLatency; + PaError err; + + float sampleRate = dev.sampleRates[dev.srId]; + int bufferSize = sampleRate / 60.0f; + + if (dev.channels == 2) { + stereoRB.data.setMaxLatency(bufferSize * 2); + err = Pa_OpenStream(&stream, NULL, &outputParams, sampleRate, bufferSize, 0, _stereo_cb, this); + } + else { + monoRB.data.setMaxLatency(bufferSize * 2); + err = Pa_OpenStream(&stream, NULL, &outputParams, sampleRate, bufferSize, 0, _mono_cb, this); + } + + if (err != 0) { + spdlog::error("Error while opening audio stream: ({0}) => {1}", err, Pa_GetErrorText(err)); + return; + } + + err = Pa_StartStream(stream); + if (err != 0) { + spdlog::error("Error while starting audio stream: ({0}) => {1}", err, Pa_GetErrorText(err)); + return; + } + spdlog::info("Audio device open."); + running = true; + } + + void doStop() { + monoRB.data.stopReader(); + stereoRB.data.stopReader(); + Pa_StopStream(stream); + Pa_CloseStream(stream); + monoRB.data.clearReadStop(); + stereoRB.data.clearReadStop(); + } + + static int _mono_cb(const void *input, void *output, unsigned long frameCount, + const PaStreamCallbackTimeInfo* timeInfo, PaStreamCallbackFlags statusFlags, void *userData) { + AudioSink* _this = (AudioSink*)userData; + _this->monoRB.data.read((float*)output, frameCount); + return 0; + } + + static int _stereo_cb(const void *input, void *output, unsigned long frameCount, + const PaStreamCallbackTimeInfo* timeInfo, PaStreamCallbackFlags statusFlags, void *userData) { + AudioSink* _this = (AudioSink*)userData; + _this->stereoRB.data.read((dsp::stereo_t*)output, frameCount); + return 0; + } SinkManager::Stream* _stream; dsp::stream* audioStream; dsp::StereoToMono s2m; dsp::RingBufferSink monoRB; dsp::RingBufferSink stereoRB; + std::string _streamName; + PaStream *stream; int srId = 0; - float sampleRate; int devCount; int devId = 0; int devListId = 0; int defaultDev = 0; + bool running = false; const double POSSIBLE_SAMP_RATE[6] = { 48000.0f, @@ -132,8 +224,8 @@ public: } private: - static SinkManager::Sink* create_sink(SinkManager::Stream* stream, void* ctx) { - return (SinkManager::Sink*)(new AudioSink(stream)); + static SinkManager::Sink* create_sink(SinkManager::Stream* stream, std::string streamName, void* ctx) { + return (SinkManager::Sink*)(new AudioSink(stream, streamName)); } std::string name; @@ -143,6 +235,7 @@ private: MOD_EXPORT void _INIT_() { // Nothing here + // TODO: Do instancing here (in source modules as well) to prevent multiple loads } MOD_EXPORT void* _CREATE_INSTANCE_(std::string name) { diff --git a/core/src/gui/main_window.cpp b/core/src/gui/main_window.cpp index 1dbc79e9..91f19a46 100644 --- a/core/src/gui/main_window.cpp +++ b/core/src/gui/main_window.cpp @@ -26,6 +26,7 @@ #include #include #include +#include #include #include #include @@ -95,6 +96,7 @@ void windowInit() { core::configManager.release(); gui::menu.registerEntry("Source", sourecmenu::draw, NULL); + gui::menu.registerEntry("Sinks", sinkmenu::draw, NULL); gui::menu.registerEntry("Audio", audiomenu::draw, NULL); gui::menu.registerEntry("Scripting", scriptingmenu::draw, NULL); gui::menu.registerEntry("Band Plan", bandplanmenu::draw, NULL); @@ -117,6 +119,7 @@ void windowInit() { mod::loadFromList(ROOT_DIR "/module_list.json"); sourecmenu::init(); + sinkmenu::init(); audiomenu::init(); scriptingmenu::init(); bandplanmenu::init(); diff --git a/core/src/signal_path/sink.cpp b/core/src/signal_path/sink.cpp index 10b38bd7..c4e06e0e 100644 --- a/core/src/signal_path/sink.cpp +++ b/core/src/signal_path/sink.cpp @@ -3,8 +3,12 @@ #include #include +#define CONCAT(a, b) ((std::string(a) + b).c_str()) + SinkManager::SinkManager() { - + SinkManager::SinkProvider prov; + prov.create = SinkManager::NullSink::create; + registerSinkProvider("None", prov); } SinkManager::Stream::Stream(dsp::stream* in, const Event::EventHandler& srChangeHandler, float sampleRate) { @@ -15,11 +19,19 @@ SinkManager::Stream::Stream(dsp::stream* in, const Event:: } void SinkManager::Stream::start() { + if (running) { + return; + } sink->start(); + running = true; } void SinkManager::Stream::stop() { + if (!running) { + return; + } sink->stop(); + running = false; } void SinkManager::Stream::setInput(dsp::stream* in) { @@ -72,7 +84,7 @@ void SinkManager::registerStream(std::string name, SinkManager::Stream* stream) provider = providers[providerName]; } - stream->sink = provider.create(stream, provider.ctx); + stream->sink = provider.create(stream, name, provider.ctx); } void SinkManager::unregisterStream(std::string name) { @@ -136,8 +148,16 @@ void SinkManager::showMenu() { 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())) { - + if (ImGui::Combo(CONCAT("##_sdrpp_sink_select_", name), &stream->providerId, provStr.c_str())) { + if (stream->running) { + stream->sink->stop(); + } + delete stream->sink; + SinkManager::SinkProvider prov = providers[providerNames[stream->providerId]]; + stream->sink = prov.create(stream, name, prov.ctx); + if (stream->running) { + stream->sink->start(); + } } stream->sink->menuHandler(); diff --git a/core/src/signal_path/sink.h b/core/src/signal_path/sink.h index 12df6f75..77a8f1a6 100644 --- a/core/src/signal_path/sink.h +++ b/core/src/signal_path/sink.h @@ -4,6 +4,7 @@ #include #include #include +#include #include #include #include @@ -34,25 +35,36 @@ public: friend SinkManager; friend SinkManager::Sink; - Event srChange; - SinkManager::Sink* sink; - int providerId = 0; + Event srChange; private: void setSampleRate(float sampleRate); dsp::stream* _in; dsp::Splitter splitter; + SinkManager::Sink* sink; std::mutex ctrlMtx; float _sampleRate; - + int providerId = 0; + bool running = false; }; struct SinkProvider { - SinkManager::Sink* (*create)(SinkManager::Stream* stream, void* ctx); + SinkManager::Sink* (*create)(SinkManager::Stream* stream, std::string streamName, void* ctx); void* ctx; }; + class NullSink : SinkManager::Sink { + public: + void start() {} + void stop() {} + void menuHandler() {} + + static SinkManager::Sink* create(SinkManager::Stream* stream, std::string streamName, void* ctx) { + return new SinkManager::NullSink; + } + }; + void registerSinkProvider(std::string name, SinkProvider provider); void registerStream(std::string name, Stream* stream); diff --git a/root_dev/config.json b/root_dev/config.json index 64d9604c..567e3397 100644 --- a/root_dev/config.json +++ b/root_dev/config.json @@ -27,6 +27,7 @@ "Source", "Radio", "Recorder", + "Sinks", "Audio", "Scripting", "Band Plan",