mirror of
https://github.com/AlexandreRouma/SDRPlusPlus.git
synced 2024-11-14 22:42:52 +01:00
more or less finished the new stream system
This commit is contained in:
parent
ff655caf31
commit
2a741932e0
@ -1,3 +1,5 @@
|
|||||||
|
set(CMAKE_BUILD_TYPE "Debug") # TODO: Make sure this gets properly disabled when going back to normal builds
|
||||||
|
|
||||||
cmake_minimum_required(VERSION 3.13)
|
cmake_minimum_required(VERSION 3.13)
|
||||||
project(sdrpp)
|
project(sdrpp)
|
||||||
|
|
||||||
|
@ -17,6 +17,7 @@
|
|||||||
#include <gui/menus/display.h>
|
#include <gui/menus/display.h>
|
||||||
#include <gui/menus/bandplan.h>
|
#include <gui/menus/bandplan.h>
|
||||||
#include <gui/menus/sink.h>
|
#include <gui/menus/sink.h>
|
||||||
|
#include <gui/menus/streams.h>
|
||||||
#include <gui/menus/vfo_color.h>
|
#include <gui/menus/vfo_color.h>
|
||||||
#include <gui/menus/module_manager.h>
|
#include <gui/menus/module_manager.h>
|
||||||
#include <gui/menus/theme.h>
|
#include <gui/menus/theme.h>
|
||||||
@ -72,6 +73,7 @@ void MainWindow::init() {
|
|||||||
|
|
||||||
gui::menu.registerEntry("Source", sourcemenu::draw, NULL);
|
gui::menu.registerEntry("Source", sourcemenu::draw, NULL);
|
||||||
gui::menu.registerEntry("Sinks", sinkmenu::draw, NULL);
|
gui::menu.registerEntry("Sinks", sinkmenu::draw, NULL);
|
||||||
|
gui::menu.registerEntry("Streams", streamsmenu::draw, NULL);
|
||||||
gui::menu.registerEntry("Band Plan", bandplanmenu::draw, NULL);
|
gui::menu.registerEntry("Band Plan", bandplanmenu::draw, NULL);
|
||||||
gui::menu.registerEntry("Display", displaymenu::draw, NULL);
|
gui::menu.registerEntry("Display", displaymenu::draw, NULL);
|
||||||
gui::menu.registerEntry("Theme", thememenu::draw, NULL);
|
gui::menu.registerEntry("Theme", thememenu::draw, NULL);
|
||||||
@ -165,6 +167,7 @@ void MainWindow::init() {
|
|||||||
|
|
||||||
sourcemenu::init();
|
sourcemenu::init();
|
||||||
sinkmenu::init();
|
sinkmenu::init();
|
||||||
|
streamsmenu::init();
|
||||||
bandplanmenu::init();
|
bandplanmenu::init();
|
||||||
displaymenu::init();
|
displaymenu::init();
|
||||||
vfo_color_menu::init();
|
vfo_color_menu::init();
|
||||||
|
74
core/src/gui/menus/streams.cpp
Normal file
74
core/src/gui/menus/streams.cpp
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
#include "streams.h"
|
||||||
|
#include <signal_path/signal_path.h>
|
||||||
|
#include <imgui.h>
|
||||||
|
#include <utils/flog.h>
|
||||||
|
#include <gui/style.h>
|
||||||
|
|
||||||
|
#define CONCAT(a, b) ((std::string(a) + b).c_str())
|
||||||
|
|
||||||
|
namespace streamsmenu {
|
||||||
|
std::vector<SinkID> sinksToBeRemoved;
|
||||||
|
|
||||||
|
void init() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void draw(void* ctx) {
|
||||||
|
float menuWidth = ImGui::GetContentRegionAvail().x;
|
||||||
|
auto lck = sigpath::streamManager.getStreamsLock();
|
||||||
|
auto streams = sigpath::streamManager.getStreams();
|
||||||
|
|
||||||
|
int count = 0;
|
||||||
|
int maxCount = streams.size();
|
||||||
|
for (auto& [name, stream] : streams) {
|
||||||
|
// Stream name
|
||||||
|
ImGui::SetCursorPosX((menuWidth / 2.0f) - (ImGui::CalcTextSize(name.c_str()).x / 2.0f));
|
||||||
|
ImGui::Text("%s", name.c_str());
|
||||||
|
|
||||||
|
// Display ever sink
|
||||||
|
if (ImGui::BeginTable(CONCAT("sdrpp_streams_tbl_", name), 1, ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg)) {
|
||||||
|
auto lck2 = stream->getSinksLock();
|
||||||
|
auto sinks = stream->getSinks();
|
||||||
|
for (auto& [id, sink] : sinks) {
|
||||||
|
ImGui::TableNextRow();
|
||||||
|
ImGui::TableSetColumnIndex(0);
|
||||||
|
|
||||||
|
sink->showMenu();
|
||||||
|
|
||||||
|
ImGui::FillWidth();
|
||||||
|
if (ImGui::Button(CONCAT("Remove##sdrpp_streams_remove_type_", name))) {
|
||||||
|
sinksToBeRemoved.push_back(id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
lck2.unlock();
|
||||||
|
|
||||||
|
ImGui::TableNextRow();
|
||||||
|
ImGui::TableSetColumnIndex(0);
|
||||||
|
|
||||||
|
int ssds = 0;
|
||||||
|
ImGui::Combo(CONCAT("##sdrpp_streams_add_type_", name), &ssds, "Test 1\0Test 2\0");
|
||||||
|
ImGui::SameLine();
|
||||||
|
ImGui::FillWidth();
|
||||||
|
if (ImGui::Button(CONCAT("Add##sdrpp_streams_add_btn_", name))) {
|
||||||
|
stream->addSink("Sink 2");
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui::EndTable();
|
||||||
|
|
||||||
|
// Remove sinks that need to be removed
|
||||||
|
if (!sinksToBeRemoved.empty()) {
|
||||||
|
for (auto& id : sinksToBeRemoved) {
|
||||||
|
stream->removeSink(id);
|
||||||
|
}
|
||||||
|
sinksToBeRemoved.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
count++;
|
||||||
|
if (count < maxCount) {
|
||||||
|
ImGui::Spacing();
|
||||||
|
}
|
||||||
|
ImGui::Spacing();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
6
core/src/gui/menus/streams.h
Normal file
6
core/src/gui/menus/streams.h
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
namespace streamsmenu {
|
||||||
|
void init();
|
||||||
|
void draw(void* ctx);
|
||||||
|
};
|
@ -5,4 +5,5 @@ namespace sigpath {
|
|||||||
VFOManager vfoManager;
|
VFOManager vfoManager;
|
||||||
SourceManager sourceManager;
|
SourceManager sourceManager;
|
||||||
SinkManager sinkManager;
|
SinkManager sinkManager;
|
||||||
|
StreamManager streamManager;
|
||||||
};
|
};
|
@ -3,6 +3,7 @@
|
|||||||
#include "vfo_manager.h"
|
#include "vfo_manager.h"
|
||||||
#include "source.h"
|
#include "source.h"
|
||||||
#include "sink.h"
|
#include "sink.h"
|
||||||
|
#include "stream.h"
|
||||||
#include <module.h>
|
#include <module.h>
|
||||||
|
|
||||||
namespace sigpath {
|
namespace sigpath {
|
||||||
@ -10,4 +11,5 @@ namespace sigpath {
|
|||||||
SDRPP_EXPORT VFOManager vfoManager;
|
SDRPP_EXPORT VFOManager vfoManager;
|
||||||
SDRPP_EXPORT SourceManager sourceManager;
|
SDRPP_EXPORT SourceManager sourceManager;
|
||||||
SDRPP_EXPORT SinkManager sinkManager;
|
SDRPP_EXPORT SinkManager sinkManager;
|
||||||
|
SDRPP_EXPORT StreamManager streamManager;
|
||||||
};
|
};
|
@ -1,286 +1,455 @@
|
|||||||
// #include "stream.h"
|
#include "stream.h"
|
||||||
// #include <utils/flog.h>
|
#include <utils/flog.h>
|
||||||
|
|
||||||
// Sink::Sink(dsp::stream<dsp::stereo_t>* stream, const std::string& name, SinkID id) :
|
Sink::Sink(SinkEntry* entry, dsp::stream<dsp::stereo_t>* stream, const std::string& name, SinkID id) :
|
||||||
// stream(stream),
|
entry(entry),
|
||||||
// streamName(name),
|
stream(stream),
|
||||||
// id(id)
|
streamName(name),
|
||||||
// {}
|
id(id)
|
||||||
|
{}
|
||||||
|
|
||||||
// void Sink::showMenu() {}
|
void Sink::showMenu() {}
|
||||||
|
|
||||||
// AudioStream::AudioStream(StreamManager* manager, const std::string& name, dsp::stream<dsp::stereo_t>* stream, double samplerate) :
|
SinkEntry::SinkEntry(StreamManager* manager, AudioStream* parentStream, const std::string& type, SinkID id, double inputSamplerate) :
|
||||||
// manager(manager),
|
manager(manager),
|
||||||
// name(name)
|
parentStream(parentStream),
|
||||||
// {
|
id(id)
|
||||||
// this->samplerate = samplerate;
|
{
|
||||||
|
this->type = type;
|
||||||
// // Initialize DSP
|
this->inputSamplerate = inputSamplerate;
|
||||||
// split.init(stream);
|
|
||||||
// split.start();
|
|
||||||
// }
|
|
||||||
|
|
||||||
// void AudioStream::setInput(dsp::stream<dsp::stereo_t>* stream, double samplerate = 0.0) {
|
|
||||||
// std::lock_guard<std::recursive_mutex> lck1(mtx);
|
|
||||||
|
|
||||||
// // If all that's needed is to set the input, do it and return
|
|
||||||
// if (samplerate == 0.0) {
|
|
||||||
// split.setInput(stream);
|
|
||||||
// return;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// // Lock sink list
|
|
||||||
// {
|
|
||||||
// std::unique_lock<std::shared_mutex> lck2(sinksMtx);
|
|
||||||
// this->samplerate = samplerate;
|
|
||||||
|
|
||||||
// // Stop DSP
|
|
||||||
// split.stop();
|
|
||||||
// for (auto& [id, sink] : sinks) {
|
|
||||||
// sink->stopDSP();
|
|
||||||
// }
|
|
||||||
|
|
||||||
// // Set input and samplerate
|
|
||||||
// split.setInput(stream);
|
|
||||||
// for (auto& [id, sink] : sinks) {
|
|
||||||
// sink->setInputSamplerate(samplerate);
|
|
||||||
// }
|
|
||||||
|
|
||||||
// // Start DSP
|
|
||||||
// for (auto& [id, sink] : sinks) {
|
|
||||||
// sink->startDSP();
|
|
||||||
// }
|
|
||||||
// split.start();
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// SinkID AudioStream::addSink(const std::string& type, SinkID id = -1) {
|
|
||||||
// std::unique_lock<std::shared_mutex> lck(sinksMtx);
|
|
||||||
|
|
||||||
// // Find a free ID if not provided
|
|
||||||
// if (id < 0) {
|
|
||||||
// for (id = 0;; id++) {
|
|
||||||
// if (sinks.find(id) != sinks.end()) { continue; }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// else {
|
|
||||||
// // Check that the provided ID is valid
|
|
||||||
// if (sinks.find(id) != sinks.end()) {
|
|
||||||
// flog::error("Tried to create sink for stream '{}' with existing ID: {}", name, id);
|
|
||||||
// return -1;
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// // Create sink entry
|
// Initialize DSP
|
||||||
// std::shared_ptr<SinkEntry> sink;
|
resamp.init(&input, inputSamplerate, inputSamplerate);
|
||||||
// try {
|
volumeAdjust.init(&resamp.out, 1.0f, false);
|
||||||
// sink = std::make_shared<SinkEntry>(type, id, samplerate);
|
|
||||||
// }
|
|
||||||
// catch (SinkEntryCreateException e) {
|
|
||||||
// flog::error("Tried to create sink for stream '{}' with ID '{}': {}", name, id, e.what());
|
|
||||||
// return -1;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// // Start the sink and DSP
|
// Initialize the sink
|
||||||
// sink->startSink();
|
setType(type);
|
||||||
// sink->startDSP();
|
}
|
||||||
|
|
||||||
// // Bind the sinks's input
|
std::string SinkEntry::getType() {
|
||||||
// split.bindStream(&sink->input);
|
std::lock_guard<std::recursive_mutex> lck(mtx);
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
|
||||||
// // Add sink to list
|
void SinkEntry::setType(const std::string& type) {
|
||||||
// sinks[id] = sink;
|
// Get unique lock on the entry
|
||||||
|
std::lock_guard<std::recursive_mutex> lck(mtx);
|
||||||
|
|
||||||
// // Release lock and emit event
|
// Delete existing sink
|
||||||
// lck.unlock();
|
if (sink) {
|
||||||
// onSinkAdded(sink);
|
provider->destroySink(std::move(sink));
|
||||||
// }
|
}
|
||||||
|
|
||||||
// void AudioStream::removeSink(SinkID id, bool forgetSettings = true) {
|
// Get shared lock on sink types
|
||||||
// // Acquire shared lock
|
auto lck2 = manager->getSinkTypesLock();
|
||||||
// std::shared_ptr<SinkEntry> sink;
|
|
||||||
// {
|
|
||||||
// std::shared_lock<std::shared_mutex> lck(sinksMtx);
|
|
||||||
|
|
||||||
// // Check that the ID exists
|
// Get the provider or throw error
|
||||||
// if (sinks.find(id) == sinks.end()) {
|
auto types = manager->getSinkTypes();
|
||||||
// flog::error("Tried to remove sink with unknown ID: {}", id);
|
if (std::find(types.begin(), types.end(), type) == types.end()) {
|
||||||
// return;
|
this->type.clear();
|
||||||
// }
|
throw SinkEntryCreateException("Invalid sink type");
|
||||||
|
}
|
||||||
|
|
||||||
// // Get sink
|
// Create sink
|
||||||
// sink = sinks[id];
|
this->type = type;
|
||||||
// }
|
provider = manager->providers[type];
|
||||||
|
sink = provider->createSink(this, &volumeAdjust.out, parentStream->getName(), id);
|
||||||
|
}
|
||||||
|
|
||||||
|
SinkID SinkEntry::getID() const {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
float SinkEntry::getVolume() {
|
||||||
|
std::lock_guard<std::recursive_mutex> lck(mtx);
|
||||||
|
return volume;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SinkEntry::setVolume(float volume) {
|
||||||
|
std::lock_guard<std::recursive_mutex> lck(mtx);
|
||||||
|
this->volume = volume;
|
||||||
|
volumeAdjust.setVolume(volume);
|
||||||
|
onVolumeChanged(volume);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SinkEntry::getMuted() {
|
||||||
|
std::lock_guard<std::recursive_mutex> lck(mtx);
|
||||||
|
return muted;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SinkEntry::setMuted(bool muted) {
|
||||||
|
std::lock_guard<std::recursive_mutex> lck(mtx);
|
||||||
|
this->muted = muted;
|
||||||
|
volumeAdjust.setMuted(muted);
|
||||||
|
onMutedChanged(muted);
|
||||||
|
}
|
||||||
|
|
||||||
|
float SinkEntry::getPanning() {
|
||||||
|
std::lock_guard<std::recursive_mutex> lck(mtx);
|
||||||
|
return panning;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SinkEntry::setPanning(float panning) {
|
||||||
|
std::lock_guard<std::recursive_mutex> lck(mtx);
|
||||||
|
this->panning = panning;
|
||||||
|
// TODO
|
||||||
|
onPanningChanged(panning);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SinkEntry::showMenu() {
|
||||||
|
std::lock_guard<std::recursive_mutex> lck(mtx);
|
||||||
|
sink->showMenu();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SinkEntry::startSink() {
|
||||||
|
std::lock_guard<std::recursive_mutex> lck(mtx);
|
||||||
|
sink->start();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SinkEntry::stopSink() {
|
||||||
|
std::lock_guard<std::recursive_mutex> lck(mtx);
|
||||||
|
sink->stop();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SinkEntry::startDSP() {
|
||||||
|
std::lock_guard<std::recursive_mutex> lck(mtx);
|
||||||
|
resamp.start();
|
||||||
|
volumeAdjust.start();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SinkEntry::stopDSP() {
|
||||||
|
std::lock_guard<std::recursive_mutex> lck(mtx);
|
||||||
|
resamp.stop();
|
||||||
|
volumeAdjust.stop();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SinkEntry::destroy(bool forgetSettings) {
|
||||||
|
std::lock_guard<std::recursive_mutex> lck(mtx);
|
||||||
|
if (sink) {
|
||||||
|
provider->destroySink(std::move(sink));
|
||||||
|
}
|
||||||
|
type.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SinkEntry::setInputSamplerate(double samplerate) {
|
||||||
|
std::lock_guard<std::recursive_mutex> lck(mtx);
|
||||||
|
resamp.setInSamplerate(samplerate);
|
||||||
|
}
|
||||||
|
|
||||||
|
AudioStream::AudioStream(StreamManager* manager, const std::string& name, dsp::stream<dsp::stereo_t>* stream, double samplerate) :
|
||||||
|
manager(manager),
|
||||||
|
name(name)
|
||||||
|
{
|
||||||
|
this->samplerate = samplerate;
|
||||||
|
|
||||||
|
// Initialize DSP
|
||||||
|
split.init(stream);
|
||||||
|
split.start();
|
||||||
|
}
|
||||||
|
|
||||||
|
AudioStream::~AudioStream() {
|
||||||
|
// Copy sink IDs
|
||||||
|
std::vector<SinkID> ids;
|
||||||
|
for (auto& [id, sink] : sinks) {
|
||||||
|
ids.push_back(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove them all
|
||||||
|
for (auto& id : ids) {
|
||||||
|
removeSink(id, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void AudioStream::setInput(dsp::stream<dsp::stereo_t>* stream, double samplerate) {
|
||||||
|
std::unique_lock<std::shared_mutex> lck(sinksMtx);
|
||||||
|
|
||||||
|
// If all that's needed is to set the input, do it and return
|
||||||
|
if (samplerate == 0.0) {
|
||||||
|
split.setInput(stream);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update samplerate
|
||||||
|
this->samplerate = samplerate;
|
||||||
|
|
||||||
|
// Stop DSP
|
||||||
|
split.stop();
|
||||||
|
for (auto& [id, sink] : sinks) {
|
||||||
|
sink->stopDSP();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set input and samplerate
|
||||||
|
split.setInput(stream);
|
||||||
|
for (auto& [id, sink] : sinks) {
|
||||||
|
sink->setInputSamplerate(samplerate);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start DSP
|
||||||
|
for (auto& [id, sink] : sinks) {
|
||||||
|
sink->startDSP();
|
||||||
|
}
|
||||||
|
split.start();
|
||||||
|
}
|
||||||
|
|
||||||
|
void AudioStream::setSamplerate(double samplerate) {
|
||||||
|
std::unique_lock<std::shared_mutex> lck(sinksMtx);
|
||||||
|
|
||||||
|
// Update samplerate
|
||||||
|
this->samplerate = samplerate;
|
||||||
|
|
||||||
|
// Stop DSP
|
||||||
|
split.stop();
|
||||||
|
for (auto& [id, sink] : sinks) {
|
||||||
|
sink->stopDSP();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set samplerate
|
||||||
|
for (auto& [id, sink] : sinks) {
|
||||||
|
sink->setInputSamplerate(samplerate);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start DSP
|
||||||
|
for (auto& [id, sink] : sinks) {
|
||||||
|
sink->startDSP();
|
||||||
|
}
|
||||||
|
split.start();
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::string& AudioStream::getName() const {
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
SinkID AudioStream::addSink(const std::string& type, SinkID id) {
|
||||||
|
std::unique_lock<std::shared_mutex> lck(sinksMtx);
|
||||||
|
|
||||||
|
// Find a free ID if not provided
|
||||||
|
if (id < 0) {
|
||||||
|
for (id = 0; sinks.find(id) != sinks.end(); id++);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// Check that the provided ID is valid
|
||||||
|
if (sinks.find(id) != sinks.end()) {
|
||||||
|
flog::error("Tried to create sink for stream '{}' with existing ID: {}", name, id);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// // Emit event
|
// Create sink entry
|
||||||
// onSinkRemove(sink);
|
std::shared_ptr<SinkEntry> sink;
|
||||||
|
try {
|
||||||
|
sink = std::make_shared<SinkEntry>(manager, this, type, id, samplerate);
|
||||||
|
}
|
||||||
|
catch (SinkEntryCreateException e) {
|
||||||
|
flog::error("Tried to create sink for stream '{}' with ID '{}': {}", name, id, e.what());
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
// // Acquire unique lock
|
// Start the sink and DSP
|
||||||
// {
|
sink->startSink();
|
||||||
// std::unique_lock<std::shared_mutex> lck(sinksMtx);
|
sink->startDSP();
|
||||||
|
|
||||||
// // Check that it's still in the list
|
// Bind the sinks's input
|
||||||
// if (sinks.find(id) == sinks.end()) {
|
split.bindStream(&sink->input);
|
||||||
// flog::error("Tried to remove sink with unknown ID: {}", id);
|
|
||||||
// return;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// // Remove from list
|
// Add sink to list
|
||||||
// sinks.erase(id);
|
sinks[id] = sink;
|
||||||
|
|
||||||
// // Unbind the sink's steam
|
// Release lock and emit event
|
||||||
// split.unbindStream(&sink->input);
|
lck.unlock();
|
||||||
|
onSinkAdded(sink);
|
||||||
|
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AudioStream::removeSink(SinkID id, bool forgetSettings) {
|
||||||
|
// Acquire shared lock
|
||||||
|
std::shared_ptr<SinkEntry> sink;
|
||||||
|
{
|
||||||
|
std::shared_lock<std::shared_mutex> lck(sinksMtx);
|
||||||
|
|
||||||
|
// Check that the ID exists
|
||||||
|
if (sinks.find(id) == sinks.end()) {
|
||||||
|
flog::error("Tried to remove sink with unknown ID: {}", id);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get sink
|
||||||
|
sink = sinks[id];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Emit event
|
||||||
|
onSinkRemove(sink);
|
||||||
|
|
||||||
|
// Acquire unique lock
|
||||||
|
{
|
||||||
|
std::unique_lock<std::shared_mutex> lck(sinksMtx);
|
||||||
|
|
||||||
|
// Check that it's still in the list
|
||||||
|
if (sinks.find(id) == sinks.end()) {
|
||||||
|
flog::error("Tried to remove sink with unknown ID: {}", id);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove from list
|
||||||
|
sinks.erase(id);
|
||||||
|
|
||||||
|
// Unbind the sink's steam
|
||||||
|
split.unbindStream(&sink->input);
|
||||||
|
|
||||||
// // Stop the sink and DSP
|
// Stop the sink and DSP
|
||||||
// sink->stopDSP();
|
sink->stopDSP();
|
||||||
// sink->stopSink();
|
sink->stopSink();
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// std::shared_lock<std::shared_mutex> AudioStream::getSinksLock() {
|
// Delete instance
|
||||||
// return std::shared_lock<std::shared_mutex>(sinksMtx);
|
sink->destroy(forgetSettings);
|
||||||
// }
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// const std::map<SinkID, std::shared_ptr<SinkEntry>>& AudioStream::getSinks() const {
|
std::shared_lock<std::shared_mutex> AudioStream::getSinksLock() {
|
||||||
// return sinks;
|
return std::shared_lock<std::shared_mutex>(sinksMtx);
|
||||||
// }
|
}
|
||||||
|
|
||||||
// std::shared_ptr<AudioStream> StreamManager::createStream(const std::string& name, dsp::stream<dsp::stereo_t>* stream, double samplerate) {
|
const std::map<SinkID, std::shared_ptr<SinkEntry>>& AudioStream::getSinks() const {
|
||||||
// std::unique_lock<std::shared_mutex> lck(streamsMtx);
|
return sinks;
|
||||||
|
}
|
||||||
|
|
||||||
// // Check that no stream with that name already exists
|
std::shared_ptr<AudioStream> StreamManager::createStream(const std::string& name, dsp::stream<dsp::stereo_t>* stream, double samplerate) {
|
||||||
// if (streams.find(name) != streams.end()) {
|
std::unique_lock<std::shared_mutex> lck(streamsMtx);
|
||||||
// flog::error("Tried to created stream with an existing name: {}", name);
|
|
||||||
// return NULL;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// // Create and save stream
|
// Check that no stream with that name already exists
|
||||||
// auto newStream = std::make_shared<AudioStream>(this, name, stream, samplerate);
|
if (streams.find(name) != streams.end()) {
|
||||||
// streams[name] = newStream;
|
flog::error("Tried to created stream with an existing name: {}", name);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
// // Release lock and emit event
|
// Create and save stream
|
||||||
// lck.unlock();
|
auto newStream = std::make_shared<AudioStream>(this, name, stream, samplerate);
|
||||||
// onStreamCreated(newStream);
|
streams[name] = newStream;
|
||||||
// }
|
|
||||||
|
|
||||||
// void StreamManager::destroyStream(std::shared_ptr<AudioStream>& stream) {
|
// Release lock and emit event
|
||||||
// // Emit event
|
lck.unlock();
|
||||||
// onStreamDestroy(stream);
|
onStreamCreated(newStream);
|
||||||
|
|
||||||
// // Aquire complete lock on the stream list
|
return newStream;
|
||||||
// {
|
}
|
||||||
// std::unique_lock<std::shared_mutex> lck(streamsMtx);
|
|
||||||
|
|
||||||
// // Get iterator of the stream
|
void StreamManager::destroyStream(std::shared_ptr<AudioStream>& stream) {
|
||||||
// auto it = std::find_if(streams.begin(), streams.end(), [&stream](std::shared_ptr<AudioStream>& s) {
|
// Emit event
|
||||||
// return s == stream;
|
onStreamDestroy(stream);
|
||||||
// });
|
|
||||||
// if (it == streams.end()) {
|
|
||||||
// flog::error("Tried to delete a stream using an invalid pointer. Stream not found in list");
|
|
||||||
// return;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// // Delete entry from list
|
// Aquire complete lock on the stream list
|
||||||
// flog::debug("Stream pointer uses, should be 2 and is {}", stream.use_count());
|
{
|
||||||
// streams.erase(it);
|
std::unique_lock<std::shared_mutex> lck(streamsMtx);
|
||||||
// }
|
|
||||||
|
|
||||||
// // Reset passed pointer
|
// Get iterator of the stream
|
||||||
// stream.reset();
|
auto it = std::find_if(streams.begin(), streams.end(), [&stream](std::pair<const std::string, std::shared_ptr<AudioStream>> e) {
|
||||||
// }
|
return e.second == stream;
|
||||||
|
});
|
||||||
|
if (it == streams.end()) {
|
||||||
|
flog::error("Tried to delete a stream using an invalid pointer. Stream not found in list");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// std::shared_lock<std::shared_mutex> StreamManager::getStreamsLock() {
|
// Delete entry from list
|
||||||
// return std::shared_lock<std::shared_mutex>(streamsMtx);
|
flog::debug("Stream pointer uses, should be 2 and is {}", (int)stream.use_count());
|
||||||
// }
|
streams.erase(it);
|
||||||
|
}
|
||||||
|
|
||||||
// const std::map<std::string, std::shared_ptr<AudioStream>>& StreamManager::getStreams() const {
|
// Reset passed pointer
|
||||||
// return streams;
|
stream.reset();
|
||||||
// }
|
}
|
||||||
|
|
||||||
// void StreamManager::registerSinkProvider(const std::string& name, SinkProvider* provider) {
|
std::shared_lock<std::shared_mutex> StreamManager::getStreamsLock() {
|
||||||
// std::unique_lock<std::shared_mutex> lck(providersMtx);
|
return std::shared_lock<std::shared_mutex>(streamsMtx);
|
||||||
|
}
|
||||||
|
|
||||||
// // Check that a provider with that name doesn't already exist
|
const std::map<std::string, std::shared_ptr<AudioStream>>& StreamManager::getStreams() const {
|
||||||
// if (providers.find(name) != providers.end()) {
|
return streams;
|
||||||
// flog::error("Tried to register a sink provider with an existing name: {}", name);
|
}
|
||||||
// return;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// // Add provider to the list and sort name list
|
void StreamManager::registerSinkProvider(const std::string& name, SinkProvider* provider) {
|
||||||
// providers[name] = provider;
|
std::unique_lock<std::shared_mutex> lck(providersMtx);
|
||||||
// sinkTypes.push_back(name);
|
|
||||||
// std::sort(sinkTypes.begin(), sinkTypes.end());
|
|
||||||
|
|
||||||
// // Release lock and emit event
|
// Check that a provider with that name doesn't already exist
|
||||||
// lck.unlock();
|
if (providers.find(name) != providers.end()) {
|
||||||
// onSinkProviderRegistered(name);
|
flog::error("Tried to register a sink provider with an existing name: {}", name);
|
||||||
// }
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// void StreamManager::unregisterSinkProvider(SinkProvider* provider) {
|
// Add provider to the list and sort name list
|
||||||
// // Get provider name for event
|
providers[name] = provider;
|
||||||
// std::string type;
|
sinkTypes.push_back(name);
|
||||||
// {
|
std::sort(sinkTypes.begin(), sinkTypes.end());
|
||||||
// std::shared_lock<std::shared_mutex> lck(providersMtx);
|
|
||||||
// auto it = std::find_if(providers.begin(), providers.end(), [&provider](SinkProvider* p) {
|
|
||||||
// p == provider;
|
|
||||||
// });
|
|
||||||
// if (it == providers.end()) {
|
|
||||||
// flog::error("Tried to unregister sink provider using invalid pointer");
|
|
||||||
// return;
|
|
||||||
// }
|
|
||||||
// type = (*it).first;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// // Emit event
|
// Release lock and emit event
|
||||||
// onSinkProviderUnregister(type);
|
lck.unlock();
|
||||||
|
onSinkProviderRegistered(name);
|
||||||
|
}
|
||||||
|
|
||||||
// // Acquire shared lock on streams
|
void StreamManager::unregisterSinkProvider(SinkProvider* provider) {
|
||||||
// {
|
// Get provider name for event
|
||||||
// std::shared_lock<std::shared_mutex> lck(streamsMtx);
|
std::string type;
|
||||||
// for (auto& [name, stream] : streams) {
|
{
|
||||||
// std::vector<SinkID> toRemove;
|
std::shared_lock<std::shared_mutex> lck(providersMtx);
|
||||||
|
auto it = std::find_if(providers.begin(), providers.end(), [&provider](std::pair<const std::string, SinkProvider *> e) {
|
||||||
|
return e.second == provider;
|
||||||
|
});
|
||||||
|
if (it == providers.end()) {
|
||||||
|
flog::error("Tried to unregister sink provider using invalid pointer");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
type = (*it).first;
|
||||||
|
}
|
||||||
|
|
||||||
// // Aquire lock on sink list
|
// Emit event
|
||||||
// auto sLock = stream->getSinksLock();
|
onSinkProviderUnregister(type);
|
||||||
// auto sinks = stream->getSinks();
|
|
||||||
|
|
||||||
// // Find all sinks with the type that is about to be removed
|
// Acquire shared lock on streams
|
||||||
// for (auto& [id, sink] : sinks) {
|
{
|
||||||
// if (sink->getType() != type) { continue; }
|
std::unique_lock<std::shared_mutex> lck1(providersMtx);
|
||||||
// toRemove.push_back(id);
|
std::shared_lock<std::shared_mutex> lck2(streamsMtx);
|
||||||
// }
|
for (auto& [name, stream] : streams) {
|
||||||
|
// Aquire lock on sink list
|
||||||
|
auto sLock = stream->getSinksLock();
|
||||||
|
auto sinks = stream->getSinks();
|
||||||
|
|
||||||
// // Remove them all (TODO: THERE IS RACE CONDITION IF A SINK IS CHANGED AFTER LISTING)
|
// Find all sinks with the type that is about to be removed
|
||||||
// for (auto& id : toRemove) {
|
std::vector<SinkID> toRemove;
|
||||||
// stream->removeSink(id);
|
for (auto& [id, sink] : sinks) {
|
||||||
// }
|
if (sink->getType() != type) { continue; }
|
||||||
// }
|
toRemove.push_back(id);
|
||||||
// }
|
}
|
||||||
|
|
||||||
// // Remove from the lists
|
// Remove them all (TODO: THERE IS RACE CONDITION IF A SINK IS CHANGED AFTER LISTING)
|
||||||
// {
|
sLock.unlock();
|
||||||
// std::unique_lock<std::shared_mutex> lck(providersMtx);
|
for (auto& id : toRemove) {
|
||||||
// if (providers.find(type) != providers.end()) {
|
stream->removeSink(id);
|
||||||
// providers.erase(type);
|
}
|
||||||
// }
|
}
|
||||||
// else {
|
|
||||||
// flog::error("Could not remove sink provider from list");
|
|
||||||
// }
|
|
||||||
|
|
||||||
// auto it = std::find(sinkTypes.begin(), sinkTypes.end(), type);
|
// Remove from the lists
|
||||||
// if (it != sinkTypes.end()) {
|
if (providers.find(type) != providers.end()) {
|
||||||
// sinkTypes.erase(it);
|
providers.erase(type);
|
||||||
// }
|
}
|
||||||
// else {
|
else {
|
||||||
// flog::error("Could not remove sink provider from sink type list");
|
flog::error("Could not remove sink provider from list");
|
||||||
// }
|
}
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// std::shared_lock<std::shared_mutex> StreamManager::getSinkTypesLock() {
|
auto it = std::find(sinkTypes.begin(), sinkTypes.end(), type);
|
||||||
// return std::shared_lock<std::shared_mutex>(providersMtx);
|
if (it != sinkTypes.end()) {
|
||||||
// }
|
sinkTypes.erase(it);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
flog::error("Could not remove sink provider from sink type list");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// const std::vector<std::string>& StreamManager::getSinkTypes() const {
|
std::shared_lock<std::shared_mutex> StreamManager::getSinkTypesLock() {
|
||||||
// return sinkTypes;
|
return std::shared_lock<std::shared_mutex>(providersMtx);
|
||||||
// }
|
}
|
||||||
|
|
||||||
|
const std::vector<std::string>& StreamManager::getSinkTypes() const {
|
||||||
|
// TODO: This allows code to modify the names...
|
||||||
|
return sinkTypes;
|
||||||
|
}
|
@ -15,35 +15,61 @@ class AudioStream;
|
|||||||
|
|
||||||
using SinkID = int;
|
using SinkID = int;
|
||||||
|
|
||||||
|
class SinkEntry;
|
||||||
|
|
||||||
class Sink {
|
class Sink {
|
||||||
public:
|
public:
|
||||||
Sink(dsp::stream<dsp::stereo_t>* stream, const std::string& name, SinkID id);
|
Sink(SinkEntry* entry, dsp::stream<dsp::stereo_t>* stream, const std::string& name, SinkID id);
|
||||||
|
virtual ~Sink() {}
|
||||||
|
|
||||||
virtual void start() = 0;
|
virtual void start() = 0;
|
||||||
virtual void stop() = 0;
|
virtual void stop() = 0;
|
||||||
virtual void showMenu();
|
virtual void showMenu();
|
||||||
|
|
||||||
private:
|
protected:
|
||||||
|
SinkEntry* const entry;
|
||||||
dsp::stream<dsp::stereo_t>* const stream;
|
dsp::stream<dsp::stereo_t>* const stream;
|
||||||
const std::string streamName;
|
const std::string streamName;
|
||||||
const SinkID id;
|
const SinkID id;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class SinkProvider {
|
||||||
|
friend Sink;
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* Create a sink instance.
|
||||||
|
* @param name Name of the audio stream.
|
||||||
|
* @param index Index of the sink in the menu. Should be use to keep settings.
|
||||||
|
*/
|
||||||
|
virtual std::unique_ptr<Sink> createSink(SinkEntry* entry, dsp::stream<dsp::stereo_t>* stream, const std::string& name, SinkID index) = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Destroy a sink instance. This function is so that the provide knows at all times how many instances there are.
|
||||||
|
* @param sink Instance of the sink.
|
||||||
|
*/
|
||||||
|
virtual void destroySink(std::unique_ptr<Sink> sink) {
|
||||||
|
sink.reset();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
class SinkEntryCreateException : public std::runtime_error {
|
class SinkEntryCreateException : public std::runtime_error {
|
||||||
public:
|
public:
|
||||||
SinkEntryCreateException(const char* what) : std::runtime_error(what) {}
|
SinkEntryCreateException(const char* what) : std::runtime_error(what) {}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class StreamManager;
|
||||||
|
|
||||||
|
// TODO: Would be cool to have data and audio sinks instead of just audio.
|
||||||
class SinkEntry {
|
class SinkEntry {
|
||||||
friend AudioStream;
|
friend AudioStream;
|
||||||
SinkEntry(const std::string& type, SinkID id, double inputSamplerate);
|
|
||||||
public:
|
public:
|
||||||
|
SinkEntry(StreamManager* manager, AudioStream* parentStream, const std::string& type, SinkID id, double inputSamplerate);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the type of the sink.
|
* Get the type of the sink.
|
||||||
* @return Type of the sink.
|
* @return Type of the sink.
|
||||||
*/
|
*/
|
||||||
const std::string& getType();
|
std::string getType();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Change the type of the sink.
|
* Change the type of the sink.
|
||||||
@ -55,13 +81,13 @@ public:
|
|||||||
* Get the ID of the sink.
|
* Get the ID of the sink.
|
||||||
* @return ID of the sink.
|
* @return ID of the sink.
|
||||||
*/
|
*/
|
||||||
SinkID getID();
|
SinkID getID() const;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get sink volume.
|
* Get sink volume.
|
||||||
* @return Volume as value between 0.0 and 1.0.
|
* @return Volume as value between 0.0 and 1.0.
|
||||||
*/
|
*/
|
||||||
float getVolume() const;
|
float getVolume();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set sink volume.
|
* Set sink volume.
|
||||||
@ -73,7 +99,7 @@ public:
|
|||||||
* Check if the sink is muted.
|
* Check if the sink is muted.
|
||||||
* @return True if muted, false if not.
|
* @return True if muted, false if not.
|
||||||
*/
|
*/
|
||||||
bool getMuted() const;
|
bool getMuted();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set wether or not the sink is muted
|
* Set wether or not the sink is muted
|
||||||
@ -85,7 +111,7 @@ public:
|
|||||||
* Get sink panning.
|
* Get sink panning.
|
||||||
* @return Panning as value between -1.0 and 1.0 meaning panning to the left and right respectively.
|
* @return Panning as value between -1.0 and 1.0 meaning panning to the left and right respectively.
|
||||||
*/
|
*/
|
||||||
float getPanning() const;
|
float getPanning();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set sink panning.
|
* Set sink panning.
|
||||||
@ -93,6 +119,11 @@ public:
|
|||||||
*/
|
*/
|
||||||
void setPanning(float panning);
|
void setPanning(float panning);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Show the sink type-specific menu.
|
||||||
|
*/
|
||||||
|
void showMenu();
|
||||||
|
|
||||||
// Emitted when the type of the sink was changed
|
// Emitted when the type of the sink was changed
|
||||||
NewEvent<const std::string&> onTypeChanged;
|
NewEvent<const std::string&> onTypeChanged;
|
||||||
// Emmited when volume of the sink was changed
|
// Emmited when volume of the sink was changed
|
||||||
@ -107,15 +138,23 @@ private:
|
|||||||
void stopSink();
|
void stopSink();
|
||||||
void startDSP();
|
void startDSP();
|
||||||
void stopDSP();
|
void stopDSP();
|
||||||
|
void destroy(bool forgetSettings);
|
||||||
void setInputSamplerate(double samplerate);
|
void setInputSamplerate(double samplerate);
|
||||||
|
|
||||||
|
std::recursive_mutex mtx;
|
||||||
dsp::stream<dsp::stereo_t> input;
|
dsp::stream<dsp::stereo_t> input;
|
||||||
dsp::multirate::RationalResampler<dsp::stereo_t> resamp;
|
dsp::multirate::RationalResampler<dsp::stereo_t> resamp;
|
||||||
dsp::audio::Volume volumeAdjust;
|
dsp::audio::Volume volumeAdjust;
|
||||||
|
|
||||||
|
|
||||||
|
SinkProvider* provider = NULL;
|
||||||
std::unique_ptr<Sink> sink;
|
std::unique_ptr<Sink> sink;
|
||||||
double inputSamplerate;
|
std::string type;
|
||||||
const SinkID id;
|
const SinkID id;
|
||||||
|
double inputSamplerate;
|
||||||
|
AudioStream* const parentStream;
|
||||||
|
StreamManager* const manager;
|
||||||
|
|
||||||
float volume = 1.0f;
|
float volume = 1.0f;
|
||||||
bool muted = false;
|
bool muted = false;
|
||||||
float panning = 0.0f;
|
float panning = 0.0f;
|
||||||
@ -125,8 +164,9 @@ class StreamManager;
|
|||||||
|
|
||||||
class AudioStream {
|
class AudioStream {
|
||||||
friend StreamManager;
|
friend StreamManager;
|
||||||
AudioStream(StreamManager* manager, const std::string& name, dsp::stream<dsp::stereo_t>* stream, double samplerate);
|
|
||||||
public:
|
public:
|
||||||
|
AudioStream(StreamManager* manager, const std::string& name, dsp::stream<dsp::stereo_t>* stream, double samplerate);
|
||||||
|
~AudioStream();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set DSP stream input.
|
* Set DSP stream input.
|
||||||
@ -135,6 +175,8 @@ public:
|
|||||||
*/
|
*/
|
||||||
void setInput(dsp::stream<dsp::stereo_t>* stream, double samplerate = 0.0);
|
void setInput(dsp::stream<dsp::stereo_t>* stream, double samplerate = 0.0);
|
||||||
|
|
||||||
|
// TODO: There must be a way to pre-stop things to avoid having weird shit happen
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Set the samplerate of the input stream.
|
* Set the samplerate of the input stream.
|
||||||
* @param samplerate Samplerate in Hz.
|
* @param samplerate Samplerate in Hz.
|
||||||
@ -147,9 +189,6 @@ public:
|
|||||||
*/
|
*/
|
||||||
const std::string& getName() const;
|
const std::string& getName() const;
|
||||||
|
|
||||||
// TODO: USING AN INDEX IS BAD!
|
|
||||||
// THIS IS BECAUSE THEY WILL CHANGE AS SOME ARE REMOVED OR ADDED
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add a sink to the stream.
|
* Add a sink to the stream.
|
||||||
* @param type Type of the sink.
|
* @param type Type of the sink.
|
||||||
@ -185,8 +224,7 @@ public:
|
|||||||
NewEvent<std::shared_ptr<SinkEntry>> onSinkRemove;
|
NewEvent<std::shared_ptr<SinkEntry>> onSinkRemove;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::recursive_mutex mtx;
|
StreamManager* const manager;
|
||||||
const StreamManager* manager;
|
|
||||||
const std::string name;
|
const std::string name;
|
||||||
double samplerate;
|
double samplerate;
|
||||||
dsp::routing::Splitter<dsp::stereo_t> split;
|
dsp::routing::Splitter<dsp::stereo_t> split;
|
||||||
@ -195,23 +233,8 @@ private:
|
|||||||
std::shared_mutex sinksMtx;
|
std::shared_mutex sinksMtx;
|
||||||
};
|
};
|
||||||
|
|
||||||
class SinkProvider {
|
|
||||||
public:
|
|
||||||
/**
|
|
||||||
* Create a sink instance.
|
|
||||||
* @param name Name of the audio stream.
|
|
||||||
* @param index Index of the sink in the menu. Should be use to keep settings.
|
|
||||||
*/
|
|
||||||
virtual std::unique_ptr<Sink> createSink(dsp::stream<dsp::stereo_t>* stream, const std::string& name, int index) = 0;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Destroy a sink instance. This function is so that the provide knows at all times how many instances there are.
|
|
||||||
* @param sink Instance of the sink.
|
|
||||||
*/
|
|
||||||
virtual void destroySink(std::unique_ptr<Sink> sink) = 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
class StreamManager {
|
class StreamManager {
|
||||||
|
friend SinkEntry;
|
||||||
public:
|
public:
|
||||||
/**
|
/**
|
||||||
* Create an audio stream.
|
* Create an audio stream.
|
||||||
@ -278,9 +301,6 @@ private:
|
|||||||
std::map<std::string, std::shared_ptr<AudioStream>> streams;
|
std::map<std::string, std::shared_ptr<AudioStream>> streams;
|
||||||
std::shared_mutex streamsMtx;
|
std::shared_mutex streamsMtx;
|
||||||
|
|
||||||
// TODO: Switch all this shit to a recursive mutex RW locks are shit
|
|
||||||
// Or maybe not actually
|
|
||||||
|
|
||||||
std::map<std::string, SinkProvider*> providers;
|
std::map<std::string, SinkProvider*> providers;
|
||||||
std::vector<std::string> sinkTypes;
|
std::vector<std::string> sinkTypes;
|
||||||
std::shared_mutex providersMtx;
|
std::shared_mutex providersMtx;
|
||||||
|
Loading…
Reference in New Issue
Block a user