convert network sink to new stream system

This commit is contained in:
AlexandreRouma 2024-01-30 22:04:36 +01:00
parent 139f63ad25
commit cd7dabda51
3 changed files with 116 additions and 104 deletions

View File

@ -17,6 +17,8 @@ namespace streamsmenu {
std::map<std::string, int> selectedSinkTypeId; std::map<std::string, int> selectedSinkTypeId;
std::map<std::string, int> addSinkTypeId; std::map<std::string, int> addSinkTypeId;
int addType = 0;
void updateSinkTypeList(const std::string& removed = "") { void updateSinkTypeList(const std::string& removed = "") {
std::lock_guard<std::recursive_mutex> lck1(sinkTypesMtx); std::lock_guard<std::recursive_mutex> lck1(sinkTypesMtx);
auto lck2 = sigpath::streamManager.getSinkTypesLock(); auto lck2 = sigpath::streamManager.getSinkTypesLock();
@ -32,6 +34,9 @@ namespace streamsmenu {
// Update the list // Update the list
updateSinkTypeList(); updateSinkTypeList();
// Update the ID of the Add dropdown
// TODO
// Update the selected ID of each drop down // Update the selected ID of each drop down
// TODO // TODO
} }
@ -40,6 +45,9 @@ namespace streamsmenu {
// Update the list // Update the list
updateSinkTypeList(type); updateSinkTypeList(type);
// Update the ID of the Add dropdown
// TODO
// Update the selected ID of each drop down // Update the selected ID of each drop down
// TODO // TODO
} }
@ -137,18 +145,16 @@ namespace streamsmenu {
float tableWidth = ImGui::GetContentRegionAvail().x; float tableWidth = ImGui::GetContentRegionAvail().x;
ImGui::Spacing(); ImGui::Spacing();
int ssds = 0;
int startCur = ImGui::GetCursorPosX(); int startCur = ImGui::GetCursorPosX();
{ {
std::lock_guard<std::recursive_mutex> lck(sinkTypesMtx); std::lock_guard<std::recursive_mutex> lck(sinkTypesMtx);
ImGui::Combo(CONCAT("##sdrpp_streams_add_type_", name), &ssds, sinkTypes.txt); ImGui::Combo(CONCAT("##sdrpp_streams_add_type_", name), &addType, sinkTypes.txt);
ImGui::SameLine();
if (ImGui::Button(CONCAT("Add##sdrpp_streams_add_btn_", name), ImVec2(tableWidth - (ImGui::GetCursorPosX() - startCur), 0))) {
stream->addSink(sinkTypes.value(addType));
}
ImGui::Spacing();
} }
ImGui::SameLine();
if (ImGui::Button(CONCAT("Add##sdrpp_streams_add_btn_", name), ImVec2(tableWidth - (ImGui::GetCursorPosX() - startCur), 0))) {
stream->addSink("Audio");
}
ImGui::Spacing();
ImGui::EndTable(); ImGui::EndTable();

View File

@ -16,7 +16,7 @@ SDRPP_MOD_INFO{
/* Name: */ "audio_sink", /* Name: */ "audio_sink",
/* Description: */ "Audio sink module for SDR++", /* Description: */ "Audio sink module for SDR++",
/* Author: */ "Ryzerth", /* Author: */ "Ryzerth",
/* Version: */ 0, 1, 0, /* Version: */ 0, 2, 0,
/* Max instances */ 1 /* Max instances */ 1
}; };

View File

@ -3,13 +3,14 @@
#include <module.h> #include <module.h>
#include <gui/gui.h> #include <gui/gui.h>
#include <signal_path/signal_path.h> #include <signal_path/signal_path.h>
#include <signal_path/sink.h> #include <signal_path/stream.h>
#include <dsp/buffer/packer.h> #include <dsp/buffer/packer.h>
#include <dsp/convert/stereo_to_mono.h> #include <dsp/convert/stereo_to_mono.h>
#include <dsp/sink/handler_sink.h> #include <dsp/sink/handler_sink.h>
#include <utils/flog.h> #include <utils/flog.h>
#include <config.h> #include <config.h>
#include <gui/style.h> #include <gui/style.h>
#include <utils/optionlist.h>
#include <core.h> #include <core.h>
#define CONCAT(a, b) ((std::string(a) + b).c_str()) #define CONCAT(a, b) ((std::string(a) + b).c_str())
@ -18,84 +19,97 @@ SDRPP_MOD_INFO{
/* Name: */ "network_sink", /* Name: */ "network_sink",
/* Description: */ "Network sink module for SDR++", /* Description: */ "Network sink module for SDR++",
/* Author: */ "Ryzerth", /* Author: */ "Ryzerth",
/* Version: */ 0, 1, 0, /* Version: */ 0, 2, 0,
/* Max instances */ 1 /* Max instances */ 1
}; };
ConfigManager config; ConfigManager config;
enum { enum SinkMode {
SINK_MODE_TCP, SINK_MODE_TCP,
SINK_MODE_UDP SINK_MODE_UDP
}; };
const char* sinkModesTxt = "TCP\0UDP\0"; const char* sinkModesTxt = "TCP\0UDP\0";
class NetworkSink : SinkManager::Sink { class NetworkSink : public Sink {
public: public:
NetworkSink(SinkManager::Stream* stream, std::string streamName) { NetworkSink(SinkEntry* entry, dsp::stream<dsp::stereo_t>* stream, const std::string& name, SinkID id, const std::string& stringId) :
_stream = stream; Sink(entry, stream, name, id, stringId)
_streamName = streamName; {
// Define modes
modes.define("TCP", SINK_MODE_TCP);
modes.define("UDP", SINK_MODE_UDP);
// Load config // Create a list of sample rates
config.acquire(); std::vector<int> srList;
if (!config.conf.contains(_streamName)) { for (int sr = 12000; sr <= 200000; sr += 12000) {
config.conf[_streamName]["hostname"] = "localhost"; srList.push_back(sr);
config.conf[_streamName]["port"] = 7355; }
config.conf[_streamName]["protocol"] = SINK_MODE_UDP; // UDP for (int sr = 11025; sr <= 192000; sr += 11025) {
config.conf[_streamName]["sampleRate"] = 48000.0; srList.push_back(sr);
config.conf[_streamName]["stereo"] = false;
config.conf[_streamName]["listening"] = false;
} }
std::string host = config.conf[_streamName]["hostname"];
strcpy(hostname, host.c_str());
port = config.conf[_streamName]["port"];
modeId = config.conf[_streamName]["protocol"];
sampleRate = config.conf[_streamName]["sampleRate"];
stereo = config.conf[_streamName]["stereo"];
bool startNow = config.conf[_streamName]["listening"];
config.release(true);
// Sort sample rate list
std::sort(srList.begin(), srList.end(), [](double a, double b) { return (a < b); });
// Define samplerate options
for (int sr : srList) {
char buf[16];
sprintf(buf, "%d", sr);
samplerates.define(sr, buf, sr);
}
// Allocate buffer
netBuf = new int16_t[STREAM_BUFFER_SIZE]; netBuf = new int16_t[STREAM_BUFFER_SIZE];
packer.init(_stream->sinkOut, 512); // Init DSP
packer.init(stream, 512);
s2m.init(&packer.out); s2m.init(&packer.out);
monoSink.init(&s2m.out, monoHandler, this); monoSink.init(&s2m.out, monoHandler, this);
stereoSink.init(&packer.out, stereoHandler, this); stereoSink.init(&packer.out, stereoHandler, this);
// Load config
// Create a list of sample rates config.acquire();
for (int sr = 12000; sr < 200000; sr += 12000) { bool startNow = false;
sampleRates.push_back(sr); if (config.conf[stringId].contains("hostname")) {
std::string host = config.conf[stringId]["hostname"];
strcpy(hostname, host.c_str());
} }
for (int sr = 11025; sr < 192000; sr += 11025) { if (config.conf[stringId].contains("port")) {
sampleRates.push_back(sr); port = config.conf[stringId]["port"];
} }
if (config.conf[stringId].contains("mode")) {
// Sort sample rate list std::string modeStr = config.conf[stringId]["mode"];
std::sort(sampleRates.begin(), sampleRates.end(), [](double a, double b) { return (a < b); }); if (modes.keyExists(modeStr)) {
mode = modes.value(modes.keyId(modeStr));
// Generate text list for UI }
char buffer[128]; else {
int id = 0; mode = SINK_MODE_TCP;
int _48kId;
bool found = false;
for (auto sr : sampleRates) {
sprintf(buffer, "%d", (int)sr);
sampleRatesTxt += buffer;
sampleRatesTxt += '\0';
if (sr == sampleRate) {
srId = id;
found = true;
} }
if (sr == 48000.0) { _48kId = id; }
id++;
} }
if (!found) { if (config.conf[stringId].contains("samplerate")) {
srId = _48kId; int nSr = config.conf[stringId]["samplerate"];
sampleRate = 48000.0; if (samplerates.keyExists(nSr)) {
sampleRate = samplerates.value(samplerates.keyId(nSr));
}
else {
sampleRate = 48000;
}
} }
_stream->setSampleRate(sampleRate); if (config.conf[stringId].contains("stereo")) {
stereo = config.conf[stringId]["stereo"];
}
if (config.conf[stringId].contains("running")) {
startNow = config.conf[stringId]["running"];
}
config.release();
// Set mode ID
modeId = modes.valueId(mode);
// Set samplerate ID
srId = samplerates.valueId(sampleRate);
// Start if needed // Start if needed
if (startNow) { startServer(); } if (startNow) { startServer(); }
@ -122,30 +136,30 @@ public:
running = false; running = false;
} }
void menuHandler() { void showMenu() {
float menuWidth = ImGui::GetContentRegionAvail().x; float menuWidth = ImGui::GetContentRegionAvail().x;
bool listening = (listener && listener->isListening()) || (conn && conn->isOpen()); bool listening = (listener && listener->isListening()) || (conn && conn->isOpen());
if (listening) { style::beginDisabled(); } if (listening) { style::beginDisabled(); }
if (ImGui::InputText(CONCAT("##_network_sink_host_", _streamName), hostname, 1023)) { if (ImGui::InputText(CONCAT("##_network_sink_host_", stringId), hostname, 1023)) {
config.acquire(); config.acquire();
config.conf[_streamName]["hostname"] = hostname; config.conf[stringId]["hostname"] = hostname;
config.release(true); config.release(true);
} }
ImGui::SameLine(); ImGui::SameLine();
ImGui::SetNextItemWidth(menuWidth - ImGui::GetCursorPosX()); ImGui::SetNextItemWidth(menuWidth - ImGui::GetCursorPosX());
if (ImGui::InputInt(CONCAT("##_network_sink_port_", _streamName), &port, 0, 0)) { if (ImGui::InputInt(CONCAT("##_network_sink_port_", stringId), &port, 0, 0)) {
config.acquire(); config.acquire();
config.conf[_streamName]["port"] = port; config.conf[stringId]["port"] = port;
config.release(true); config.release(true);
} }
ImGui::LeftLabel("Protocol"); ImGui::LeftLabel("Protocol");
ImGui::SetNextItemWidth(menuWidth - ImGui::GetCursorPosX()); ImGui::SetNextItemWidth(menuWidth - ImGui::GetCursorPosX());
if (ImGui::Combo(CONCAT("##_network_sink_mode_", _streamName), &modeId, sinkModesTxt)) { if (ImGui::Combo(CONCAT("##_network_sink_mode_", stringId), &modeId, sinkModesTxt)) {
config.acquire(); config.acquire();
config.conf[_streamName]["protocol"] = modeId; config.conf[stringId]["mode"] = modeId;
config.release(true); config.release(true);
} }
@ -153,33 +167,33 @@ public:
ImGui::LeftLabel("Samplerate"); ImGui::LeftLabel("Samplerate");
ImGui::SetNextItemWidth(menuWidth - ImGui::GetCursorPosX()); ImGui::SetNextItemWidth(menuWidth - ImGui::GetCursorPosX());
if (ImGui::Combo(CONCAT("##_network_sink_sr_", _streamName), &srId, sampleRatesTxt.c_str())) { if (ImGui::Combo(CONCAT("##_network_sink_sr_", stringId), &srId, samplerates.txt)) {
sampleRate = sampleRates[srId]; sampleRate = samplerates.value(srId);
_stream->setSampleRate(sampleRate); entry->setSamplerate(sampleRate);
packer.setSampleCount(sampleRate / 60); packer.setSampleCount(sampleRate / 60);
config.acquire(); config.acquire();
config.conf[_streamName]["sampleRate"] = sampleRate; config.conf[stringId]["samplerate"] = sampleRate;
config.release(true); config.release(true);
} }
if (ImGui::Checkbox(CONCAT("Stereo##_network_sink_stereo_", _streamName), &stereo)) { if (ImGui::Checkbox(CONCAT("Stereo##_network_sink_stereo_", stringId), &stereo)) {
stop(); stop();
start(); start();
config.acquire(); config.acquire();
config.conf[_streamName]["stereo"] = stereo; config.conf[stringId]["stereo"] = stereo;
config.release(true); config.release(true);
} }
if (listening && ImGui::Button(CONCAT("Stop##_network_sink_stop_", _streamName), ImVec2(menuWidth, 0))) { if (listening && ImGui::Button(CONCAT("Stop##_network_sink_stop_", stringId), ImVec2(menuWidth, 0))) {
stopServer(); stopServer();
config.acquire(); config.acquire();
config.conf[_streamName]["listening"] = false; config.conf[stringId]["running"] = false;
config.release(true); config.release(true);
} }
else if (!listening && ImGui::Button(CONCAT("Start##_network_sink_stop_", _streamName), ImVec2(menuWidth, 0))) { else if (!listening && ImGui::Button(CONCAT("Start##_network_sink_stop_", stringId), ImVec2(menuWidth, 0))) {
startServer(); startServer();
config.acquire(); config.acquire();
config.conf[_streamName]["listening"] = true; config.conf[stringId]["running"] = true;
config.release(true); config.release(true);
} }
@ -271,47 +285,40 @@ private:
_this->listener->acceptAsync(clientHandler, _this); _this->listener->acceptAsync(clientHandler, _this);
} }
SinkManager::Stream* _stream; // DSP
dsp::buffer::Packer<dsp::stereo_t> packer; dsp::buffer::Packer<dsp::stereo_t> packer;
dsp::convert::StereoToMono s2m; dsp::convert::StereoToMono s2m;
dsp::sink::Handler<float> monoSink; dsp::sink::Handler<float> monoSink;
dsp::sink::Handler<dsp::stereo_t> stereoSink; dsp::sink::Handler<dsp::stereo_t> stereoSink;
std::string _streamName; OptionList<std::string, SinkMode> modes;
OptionList<int, double> samplerates;
int srId = 0;
bool running = false;
char hostname[1024]; char hostname[1024];
int port = 4242; int port = 7355;
SinkMode mode = SINK_MODE_TCP;
int modeId = 1; int modeId;
int sampleRate = 48000;
std::vector<unsigned int> sampleRates; int srId;
std::string sampleRatesTxt;
unsigned int sampleRate = 48000;
bool stereo = false; bool stereo = false;
bool running = false;
int16_t* netBuf; int16_t* netBuf;
net::Listener listener; net::Listener listener;
net::Conn conn; net::Conn conn;
std::mutex connMtx; std::mutex connMtx;
}; };
class NetworkSinkModule : public ModuleManager::Instance { class NetworkSinkModule : public ModuleManager::Instance, SinkProvider {
public: public:
NetworkSinkModule(std::string name) { NetworkSinkModule(std::string name) {
this->name = name; // Register self as provider
provider.create = create_sink; sigpath::streamManager.registerSinkProvider("Network", this);
provider.ctx = this;
sigpath::sinkManager.registerSinkProvider("Network", provider);
} }
~NetworkSinkModule() { ~NetworkSinkModule() {
// Unregister sink, this will automatically stop and delete all instances of the audio sink // Unregister self
sigpath::sinkManager.unregisterSinkProvider("Network"); sigpath::streamManager.unregisterSinkProvider(this);
} }
void postInit() {} void postInit() {}
@ -328,14 +335,13 @@ public:
return enabled; return enabled;
} }
private: std::unique_ptr<Sink> createSink(SinkEntry* entry, dsp::stream<dsp::stereo_t>* stream, const std::string& name, SinkID id, const std::string& stringId) {
static SinkManager::Sink* create_sink(SinkManager::Stream* stream, std::string streamName, void* ctx) { return std::make_unique<NetworkSink>(entry, stream, name, id, stringId);
return (SinkManager::Sink*)(new NetworkSink(stream, streamName));
} }
private:
std::string name; std::string name;
bool enabled = true; bool enabled = true;
SinkManager::SinkProvider provider;
}; };
MOD_EXPORT void _INIT_() { MOD_EXPORT void _INIT_() {