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)
|
||||
project(sdrpp)
|
||||
|
||||
|
@ -17,6 +17,7 @@
|
||||
#include <gui/menus/display.h>
|
||||
#include <gui/menus/bandplan.h>
|
||||
#include <gui/menus/sink.h>
|
||||
#include <gui/menus/streams.h>
|
||||
#include <gui/menus/vfo_color.h>
|
||||
#include <gui/menus/module_manager.h>
|
||||
#include <gui/menus/theme.h>
|
||||
@ -72,6 +73,7 @@ void MainWindow::init() {
|
||||
|
||||
gui::menu.registerEntry("Source", sourcemenu::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("Display", displaymenu::draw, NULL);
|
||||
gui::menu.registerEntry("Theme", thememenu::draw, NULL);
|
||||
@ -165,6 +167,7 @@ void MainWindow::init() {
|
||||
|
||||
sourcemenu::init();
|
||||
sinkmenu::init();
|
||||
streamsmenu::init();
|
||||
bandplanmenu::init();
|
||||
displaymenu::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;
|
||||
SourceManager sourceManager;
|
||||
SinkManager sinkManager;
|
||||
StreamManager streamManager;
|
||||
};
|
@ -3,6 +3,7 @@
|
||||
#include "vfo_manager.h"
|
||||
#include "source.h"
|
||||
#include "sink.h"
|
||||
#include "stream.h"
|
||||
#include <module.h>
|
||||
|
||||
namespace sigpath {
|
||||
@ -10,4 +11,5 @@ namespace sigpath {
|
||||
SDRPP_EXPORT VFOManager vfoManager;
|
||||
SDRPP_EXPORT SourceManager sourceManager;
|
||||
SDRPP_EXPORT SinkManager sinkManager;
|
||||
SDRPP_EXPORT StreamManager streamManager;
|
||||
};
|
@ -1,286 +1,455 @@
|
||||
// #include "stream.h"
|
||||
// #include <utils/flog.h>
|
||||
#include "stream.h"
|
||||
#include <utils/flog.h>
|
||||
|
||||
// Sink::Sink(dsp::stream<dsp::stereo_t>* stream, const std::string& name, SinkID id) :
|
||||
// stream(stream),
|
||||
// streamName(name),
|
||||
// id(id)
|
||||
// {}
|
||||
Sink::Sink(SinkEntry* entry, dsp::stream<dsp::stereo_t>* stream, const std::string& name, SinkID id) :
|
||||
entry(entry),
|
||||
stream(stream),
|
||||
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) :
|
||||
// manager(manager),
|
||||
// name(name)
|
||||
// {
|
||||
// this->samplerate = samplerate;
|
||||
SinkEntry::SinkEntry(StreamManager* manager, AudioStream* parentStream, const std::string& type, SinkID id, double inputSamplerate) :
|
||||
manager(manager),
|
||||
parentStream(parentStream),
|
||||
id(id)
|
||||
{
|
||||
this->type = type;
|
||||
this->inputSamplerate = inputSamplerate;
|
||||
|
||||
// // Initialize DSP
|
||||
// split.init(stream);
|
||||
// split.start();
|
||||
// }
|
||||
// Initialize DSP
|
||||
resamp.init(&input, inputSamplerate, inputSamplerate);
|
||||
volumeAdjust.init(&resamp.out, 1.0f, false);
|
||||
|
||||
// void AudioStream::setInput(dsp::stream<dsp::stereo_t>* stream, double samplerate = 0.0) {
|
||||
// std::lock_guard<std::recursive_mutex> lck1(mtx);
|
||||
// Initialize the sink
|
||||
setType(type);
|
||||
}
|
||||
|
||||
// // If all that's needed is to set the input, do it and return
|
||||
// if (samplerate == 0.0) {
|
||||
// split.setInput(stream);
|
||||
// return;
|
||||
// }
|
||||
std::string SinkEntry::getType() {
|
||||
std::lock_guard<std::recursive_mutex> lck(mtx);
|
||||
return type;
|
||||
}
|
||||
|
||||
// // Lock sink list
|
||||
// {
|
||||
// std::unique_lock<std::shared_mutex> lck2(sinksMtx);
|
||||
// this->samplerate = samplerate;
|
||||
void SinkEntry::setType(const std::string& type) {
|
||||
// Get unique lock on the entry
|
||||
std::lock_guard<std::recursive_mutex> lck(mtx);
|
||||
|
||||
// // Stop DSP
|
||||
// split.stop();
|
||||
// for (auto& [id, sink] : sinks) {
|
||||
// sink->stopDSP();
|
||||
// }
|
||||
// Delete existing sink
|
||||
if (sink) {
|
||||
provider->destroySink(std::move(sink));
|
||||
}
|
||||
|
||||
// // Set input and samplerate
|
||||
// split.setInput(stream);
|
||||
// for (auto& [id, sink] : sinks) {
|
||||
// sink->setInputSamplerate(samplerate);
|
||||
// }
|
||||
// Get shared lock on sink types
|
||||
auto lck2 = manager->getSinkTypesLock();
|
||||
|
||||
// // Start DSP
|
||||
// for (auto& [id, sink] : sinks) {
|
||||
// sink->startDSP();
|
||||
// }
|
||||
// split.start();
|
||||
// }
|
||||
// }
|
||||
// Get the provider or throw error
|
||||
auto types = manager->getSinkTypes();
|
||||
if (std::find(types.begin(), types.end(), type) == types.end()) {
|
||||
this->type.clear();
|
||||
throw SinkEntryCreateException("Invalid sink type");
|
||||
}
|
||||
|
||||
// SinkID AudioStream::addSink(const std::string& type, SinkID id = -1) {
|
||||
// std::unique_lock<std::shared_mutex> lck(sinksMtx);
|
||||
// Create sink
|
||||
this->type = type;
|
||||
provider = manager->providers[type];
|
||||
sink = provider->createSink(this, &volumeAdjust.out, parentStream->getName(), id);
|
||||
}
|
||||
|
||||
// // 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;
|
||||
// }
|
||||
// }
|
||||
SinkID SinkEntry::getID() const {
|
||||
return id;
|
||||
}
|
||||
|
||||
// // Create sink entry
|
||||
// std::shared_ptr<SinkEntry> sink;
|
||||
// try {
|
||||
// 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;
|
||||
// }
|
||||
float SinkEntry::getVolume() {
|
||||
std::lock_guard<std::recursive_mutex> lck(mtx);
|
||||
return volume;
|
||||
}
|
||||
|
||||
// // Start the sink and DSP
|
||||
// sink->startSink();
|
||||
// sink->startDSP();
|
||||
void SinkEntry::setVolume(float volume) {
|
||||
std::lock_guard<std::recursive_mutex> lck(mtx);
|
||||
this->volume = volume;
|
||||
volumeAdjust.setVolume(volume);
|
||||
onVolumeChanged(volume);
|
||||
}
|
||||
|
||||
// // Bind the sinks's input
|
||||
// split.bindStream(&sink->input);
|
||||
bool SinkEntry::getMuted() {
|
||||
std::lock_guard<std::recursive_mutex> lck(mtx);
|
||||
return muted;
|
||||
}
|
||||
|
||||
// // Add sink to list
|
||||
// sinks[id] = sink;
|
||||
void SinkEntry::setMuted(bool muted) {
|
||||
std::lock_guard<std::recursive_mutex> lck(mtx);
|
||||
this->muted = muted;
|
||||
volumeAdjust.setMuted(muted);
|
||||
onMutedChanged(muted);
|
||||
}
|
||||
|
||||
// // Release lock and emit event
|
||||
// lck.unlock();
|
||||
// onSinkAdded(sink);
|
||||
// }
|
||||
float SinkEntry::getPanning() {
|
||||
std::lock_guard<std::recursive_mutex> lck(mtx);
|
||||
return panning;
|
||||
}
|
||||
|
||||
// void AudioStream::removeSink(SinkID id, bool forgetSettings = true) {
|
||||
// // Acquire shared lock
|
||||
// std::shared_ptr<SinkEntry> sink;
|
||||
// {
|
||||
// std::shared_lock<std::shared_mutex> lck(sinksMtx);
|
||||
void SinkEntry::setPanning(float panning) {
|
||||
std::lock_guard<std::recursive_mutex> lck(mtx);
|
||||
this->panning = panning;
|
||||
// TODO
|
||||
onPanningChanged(panning);
|
||||
}
|
||||
|
||||
// // Check that the ID exists
|
||||
// if (sinks.find(id) == sinks.end()) {
|
||||
// flog::error("Tried to remove sink with unknown ID: {}", id);
|
||||
// return;
|
||||
// }
|
||||
void SinkEntry::showMenu() {
|
||||
std::lock_guard<std::recursive_mutex> lck(mtx);
|
||||
sink->showMenu();
|
||||
}
|
||||
|
||||
// // Get sink
|
||||
// sink = sinks[id];
|
||||
// }
|
||||
void SinkEntry::startSink() {
|
||||
std::lock_guard<std::recursive_mutex> lck(mtx);
|
||||
sink->start();
|
||||
}
|
||||
|
||||
// // Emit event
|
||||
// onSinkRemove(sink);
|
||||
void SinkEntry::stopSink() {
|
||||
std::lock_guard<std::recursive_mutex> lck(mtx);
|
||||
sink->stop();
|
||||
}
|
||||
|
||||
// // Acquire unique lock
|
||||
// {
|
||||
// std::unique_lock<std::shared_mutex> lck(sinksMtx);
|
||||
void SinkEntry::startDSP() {
|
||||
std::lock_guard<std::recursive_mutex> lck(mtx);
|
||||
resamp.start();
|
||||
volumeAdjust.start();
|
||||
}
|
||||
|
||||
// // 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;
|
||||
// }
|
||||
void SinkEntry::stopDSP() {
|
||||
std::lock_guard<std::recursive_mutex> lck(mtx);
|
||||
resamp.stop();
|
||||
volumeAdjust.stop();
|
||||
}
|
||||
|
||||
// // Remove from list
|
||||
// sinks.erase(id);
|
||||
void SinkEntry::destroy(bool forgetSettings) {
|
||||
std::lock_guard<std::recursive_mutex> lck(mtx);
|
||||
if (sink) {
|
||||
provider->destroySink(std::move(sink));
|
||||
}
|
||||
type.clear();
|
||||
}
|
||||
|
||||
// // Unbind the sink's steam
|
||||
// split.unbindStream(&sink->input);
|
||||
void SinkEntry::setInputSamplerate(double samplerate) {
|
||||
std::lock_guard<std::recursive_mutex> lck(mtx);
|
||||
resamp.setInSamplerate(samplerate);
|
||||
}
|
||||
|
||||
// // Stop the sink and DSP
|
||||
// sink->stopDSP();
|
||||
// sink->stopSink();
|
||||
// }
|
||||
// }
|
||||
AudioStream::AudioStream(StreamManager* manager, const std::string& name, dsp::stream<dsp::stereo_t>* stream, double samplerate) :
|
||||
manager(manager),
|
||||
name(name)
|
||||
{
|
||||
this->samplerate = samplerate;
|
||||
|
||||
// std::shared_lock<std::shared_mutex> AudioStream::getSinksLock() {
|
||||
// return std::shared_lock<std::shared_mutex>(sinksMtx);
|
||||
// }
|
||||
// Initialize DSP
|
||||
split.init(stream);
|
||||
split.start();
|
||||
}
|
||||
|
||||
// const std::map<SinkID, std::shared_ptr<SinkEntry>>& AudioStream::getSinks() const {
|
||||
// return sinks;
|
||||
// }
|
||||
AudioStream::~AudioStream() {
|
||||
// Copy sink IDs
|
||||
std::vector<SinkID> ids;
|
||||
for (auto& [id, sink] : sinks) {
|
||||
ids.push_back(id);
|
||||
}
|
||||
|
||||
// std::shared_ptr<AudioStream> StreamManager::createStream(const std::string& name, dsp::stream<dsp::stereo_t>* stream, double samplerate) {
|
||||
// std::unique_lock<std::shared_mutex> lck(streamsMtx);
|
||||
// Remove them all
|
||||
for (auto& id : ids) {
|
||||
removeSink(id, false);
|
||||
}
|
||||
}
|
||||
|
||||
// // Check that no stream with that name already exists
|
||||
// if (streams.find(name) != streams.end()) {
|
||||
// flog::error("Tried to created stream with an existing name: {}", name);
|
||||
// return NULL;
|
||||
// }
|
||||
void AudioStream::setInput(dsp::stream<dsp::stereo_t>* stream, double samplerate) {
|
||||
std::unique_lock<std::shared_mutex> lck(sinksMtx);
|
||||
|
||||
// // Create and save stream
|
||||
// auto newStream = std::make_shared<AudioStream>(this, name, stream, samplerate);
|
||||
// streams[name] = newStream;
|
||||
// If all that's needed is to set the input, do it and return
|
||||
if (samplerate == 0.0) {
|
||||
split.setInput(stream);
|
||||
return;
|
||||
}
|
||||
|
||||
// // Release lock and emit event
|
||||
// lck.unlock();
|
||||
// onStreamCreated(newStream);
|
||||
// }
|
||||
// Update samplerate
|
||||
this->samplerate = samplerate;
|
||||
|
||||
// void StreamManager::destroyStream(std::shared_ptr<AudioStream>& stream) {
|
||||
// // Emit event
|
||||
// onStreamDestroy(stream);
|
||||
// Stop DSP
|
||||
split.stop();
|
||||
for (auto& [id, sink] : sinks) {
|
||||
sink->stopDSP();
|
||||
}
|
||||
|
||||
// // Aquire complete lock on the stream list
|
||||
// {
|
||||
// std::unique_lock<std::shared_mutex> lck(streamsMtx);
|
||||
// Set input and samplerate
|
||||
split.setInput(stream);
|
||||
for (auto& [id, sink] : sinks) {
|
||||
sink->setInputSamplerate(samplerate);
|
||||
}
|
||||
|
||||
// // Get iterator of the stream
|
||||
// auto it = std::find_if(streams.begin(), streams.end(), [&stream](std::shared_ptr<AudioStream>& s) {
|
||||
// return s == stream;
|
||||
// });
|
||||
// if (it == streams.end()) {
|
||||
// flog::error("Tried to delete a stream using an invalid pointer. Stream not found in list");
|
||||
// return;
|
||||
// }
|
||||
// Start DSP
|
||||
for (auto& [id, sink] : sinks) {
|
||||
sink->startDSP();
|
||||
}
|
||||
split.start();
|
||||
}
|
||||
|
||||
// // Delete entry from list
|
||||
// flog::debug("Stream pointer uses, should be 2 and is {}", stream.use_count());
|
||||
// streams.erase(it);
|
||||
// }
|
||||
void AudioStream::setSamplerate(double samplerate) {
|
||||
std::unique_lock<std::shared_mutex> lck(sinksMtx);
|
||||
|
||||
// // Reset passed pointer
|
||||
// stream.reset();
|
||||
// }
|
||||
// Update samplerate
|
||||
this->samplerate = samplerate;
|
||||
|
||||
// std::shared_lock<std::shared_mutex> StreamManager::getStreamsLock() {
|
||||
// return std::shared_lock<std::shared_mutex>(streamsMtx);
|
||||
// }
|
||||
// Stop DSP
|
||||
split.stop();
|
||||
for (auto& [id, sink] : sinks) {
|
||||
sink->stopDSP();
|
||||
}
|
||||
|
||||
// const std::map<std::string, std::shared_ptr<AudioStream>>& StreamManager::getStreams() const {
|
||||
// return streams;
|
||||
// }
|
||||
// Set samplerate
|
||||
for (auto& [id, sink] : sinks) {
|
||||
sink->setInputSamplerate(samplerate);
|
||||
}
|
||||
|
||||
// void StreamManager::registerSinkProvider(const std::string& name, SinkProvider* provider) {
|
||||
// std::unique_lock<std::shared_mutex> lck(providersMtx);
|
||||
// Start DSP
|
||||
for (auto& [id, sink] : sinks) {
|
||||
sink->startDSP();
|
||||
}
|
||||
split.start();
|
||||
}
|
||||
|
||||
// // Check that a provider with that name doesn't already exist
|
||||
// if (providers.find(name) != providers.end()) {
|
||||
// flog::error("Tried to register a sink provider with an existing name: {}", name);
|
||||
// return;
|
||||
// }
|
||||
const std::string& AudioStream::getName() const {
|
||||
return name;
|
||||
}
|
||||
|
||||
// // Add provider to the list and sort name list
|
||||
// providers[name] = provider;
|
||||
// sinkTypes.push_back(name);
|
||||
// std::sort(sinkTypes.begin(), sinkTypes.end());
|
||||
SinkID AudioStream::addSink(const std::string& type, SinkID id) {
|
||||
std::unique_lock<std::shared_mutex> lck(sinksMtx);
|
||||
|
||||
// // Release lock and emit event
|
||||
// lck.unlock();
|
||||
// onSinkProviderRegistered(name);
|
||||
// }
|
||||
// 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;
|
||||
}
|
||||
}
|
||||
|
||||
// void StreamManager::unregisterSinkProvider(SinkProvider* provider) {
|
||||
// // Get provider name for event
|
||||
// std::string type;
|
||||
// {
|
||||
// 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;
|
||||
// }
|
||||
// Create sink entry
|
||||
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;
|
||||
}
|
||||
|
||||
// // Emit event
|
||||
// onSinkProviderUnregister(type);
|
||||
// Start the sink and DSP
|
||||
sink->startSink();
|
||||
sink->startDSP();
|
||||
|
||||
// // Acquire shared lock on streams
|
||||
// {
|
||||
// std::shared_lock<std::shared_mutex> lck(streamsMtx);
|
||||
// for (auto& [name, stream] : streams) {
|
||||
// std::vector<SinkID> toRemove;
|
||||
// Bind the sinks's input
|
||||
split.bindStream(&sink->input);
|
||||
|
||||
// // Aquire lock on sink list
|
||||
// auto sLock = stream->getSinksLock();
|
||||
// auto sinks = stream->getSinks();
|
||||
// Add sink to list
|
||||
sinks[id] = sink;
|
||||
|
||||
// // Find all sinks with the type that is about to be removed
|
||||
// for (auto& [id, sink] : sinks) {
|
||||
// if (sink->getType() != type) { continue; }
|
||||
// toRemove.push_back(id);
|
||||
// }
|
||||
// Release lock and emit event
|
||||
lck.unlock();
|
||||
onSinkAdded(sink);
|
||||
|
||||
// // Remove them all (TODO: THERE IS RACE CONDITION IF A SINK IS CHANGED AFTER LISTING)
|
||||
// for (auto& id : toRemove) {
|
||||
// stream->removeSink(id);
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
return id;
|
||||
}
|
||||
|
||||
// // Remove from the lists
|
||||
// {
|
||||
// std::unique_lock<std::shared_mutex> lck(providersMtx);
|
||||
// if (providers.find(type) != providers.end()) {
|
||||
// providers.erase(type);
|
||||
// }
|
||||
// else {
|
||||
// flog::error("Could not remove sink provider from list");
|
||||
// }
|
||||
void AudioStream::removeSink(SinkID id, bool forgetSettings) {
|
||||
// Acquire shared lock
|
||||
std::shared_ptr<SinkEntry> sink;
|
||||
{
|
||||
std::shared_lock<std::shared_mutex> lck(sinksMtx);
|
||||
|
||||
// auto it = std::find(sinkTypes.begin(), sinkTypes.end(), type);
|
||||
// if (it != sinkTypes.end()) {
|
||||
// sinkTypes.erase(it);
|
||||
// }
|
||||
// else {
|
||||
// flog::error("Could not remove sink provider from sink type list");
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// Check that the ID exists
|
||||
if (sinks.find(id) == sinks.end()) {
|
||||
flog::error("Tried to remove sink with unknown ID: {}", id);
|
||||
return;
|
||||
}
|
||||
|
||||
// std::shared_lock<std::shared_mutex> StreamManager::getSinkTypesLock() {
|
||||
// return std::shared_lock<std::shared_mutex>(providersMtx);
|
||||
// }
|
||||
// Get sink
|
||||
sink = sinks[id];
|
||||
}
|
||||
|
||||
// const std::vector<std::string>& StreamManager::getSinkTypes() const {
|
||||
// return sinkTypes;
|
||||
// }
|
||||
// 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
|
||||
sink->stopDSP();
|
||||
sink->stopSink();
|
||||
|
||||
// Delete instance
|
||||
sink->destroy(forgetSettings);
|
||||
}
|
||||
}
|
||||
|
||||
std::shared_lock<std::shared_mutex> AudioStream::getSinksLock() {
|
||||
return std::shared_lock<std::shared_mutex>(sinksMtx);
|
||||
}
|
||||
|
||||
const std::map<SinkID, std::shared_ptr<SinkEntry>>& AudioStream::getSinks() const {
|
||||
return sinks;
|
||||
}
|
||||
|
||||
std::shared_ptr<AudioStream> StreamManager::createStream(const std::string& name, dsp::stream<dsp::stereo_t>* stream, double samplerate) {
|
||||
std::unique_lock<std::shared_mutex> lck(streamsMtx);
|
||||
|
||||
// Check that no stream with that name already exists
|
||||
if (streams.find(name) != streams.end()) {
|
||||
flog::error("Tried to created stream with an existing name: {}", name);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Create and save stream
|
||||
auto newStream = std::make_shared<AudioStream>(this, name, stream, samplerate);
|
||||
streams[name] = newStream;
|
||||
|
||||
// Release lock and emit event
|
||||
lck.unlock();
|
||||
onStreamCreated(newStream);
|
||||
|
||||
return newStream;
|
||||
}
|
||||
|
||||
void StreamManager::destroyStream(std::shared_ptr<AudioStream>& stream) {
|
||||
// Emit event
|
||||
onStreamDestroy(stream);
|
||||
|
||||
// Aquire complete lock on the stream list
|
||||
{
|
||||
std::unique_lock<std::shared_mutex> lck(streamsMtx);
|
||||
|
||||
// Get iterator of the stream
|
||||
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;
|
||||
}
|
||||
|
||||
// Delete entry from list
|
||||
flog::debug("Stream pointer uses, should be 2 and is {}", (int)stream.use_count());
|
||||
streams.erase(it);
|
||||
}
|
||||
|
||||
// Reset passed pointer
|
||||
stream.reset();
|
||||
}
|
||||
|
||||
std::shared_lock<std::shared_mutex> StreamManager::getStreamsLock() {
|
||||
return std::shared_lock<std::shared_mutex>(streamsMtx);
|
||||
}
|
||||
|
||||
const std::map<std::string, std::shared_ptr<AudioStream>>& StreamManager::getStreams() const {
|
||||
return streams;
|
||||
}
|
||||
|
||||
void StreamManager::registerSinkProvider(const std::string& name, SinkProvider* provider) {
|
||||
std::unique_lock<std::shared_mutex> lck(providersMtx);
|
||||
|
||||
// Check that a provider with that name doesn't already exist
|
||||
if (providers.find(name) != providers.end()) {
|
||||
flog::error("Tried to register a sink provider with an existing name: {}", name);
|
||||
return;
|
||||
}
|
||||
|
||||
// Add provider to the list and sort name list
|
||||
providers[name] = provider;
|
||||
sinkTypes.push_back(name);
|
||||
std::sort(sinkTypes.begin(), sinkTypes.end());
|
||||
|
||||
// Release lock and emit event
|
||||
lck.unlock();
|
||||
onSinkProviderRegistered(name);
|
||||
}
|
||||
|
||||
void StreamManager::unregisterSinkProvider(SinkProvider* provider) {
|
||||
// Get provider name for event
|
||||
std::string type;
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
// Emit event
|
||||
onSinkProviderUnregister(type);
|
||||
|
||||
// Acquire shared lock on streams
|
||||
{
|
||||
std::unique_lock<std::shared_mutex> lck1(providersMtx);
|
||||
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();
|
||||
|
||||
// Find all sinks with the type that is about to be removed
|
||||
std::vector<SinkID> toRemove;
|
||||
for (auto& [id, sink] : sinks) {
|
||||
if (sink->getType() != type) { continue; }
|
||||
toRemove.push_back(id);
|
||||
}
|
||||
|
||||
// Remove them all (TODO: THERE IS RACE CONDITION IF A SINK IS CHANGED AFTER LISTING)
|
||||
sLock.unlock();
|
||||
for (auto& id : toRemove) {
|
||||
stream->removeSink(id);
|
||||
}
|
||||
}
|
||||
|
||||
// Remove from the lists
|
||||
if (providers.find(type) != providers.end()) {
|
||||
providers.erase(type);
|
||||
}
|
||||
else {
|
||||
flog::error("Could not remove sink provider from list");
|
||||
}
|
||||
|
||||
auto it = std::find(sinkTypes.begin(), sinkTypes.end(), type);
|
||||
if (it != sinkTypes.end()) {
|
||||
sinkTypes.erase(it);
|
||||
}
|
||||
else {
|
||||
flog::error("Could not remove sink provider from sink type list");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::shared_lock<std::shared_mutex> StreamManager::getSinkTypesLock() {
|
||||
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;
|
||||
|
||||
class SinkEntry;
|
||||
|
||||
class Sink {
|
||||
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 stop() = 0;
|
||||
virtual void showMenu();
|
||||
|
||||
private:
|
||||
protected:
|
||||
SinkEntry* const entry;
|
||||
dsp::stream<dsp::stereo_t>* const stream;
|
||||
const std::string streamName;
|
||||
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 {
|
||||
public:
|
||||
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 {
|
||||
friend AudioStream;
|
||||
SinkEntry(const std::string& type, SinkID id, double inputSamplerate);
|
||||
public:
|
||||
SinkEntry(StreamManager* manager, AudioStream* parentStream, const std::string& type, SinkID id, double inputSamplerate);
|
||||
|
||||
/**
|
||||
* Get the type of the sink.
|
||||
* @return Type of the sink.
|
||||
*/
|
||||
const std::string& getType();
|
||||
std::string getType();
|
||||
|
||||
/**
|
||||
* Change the type of the sink.
|
||||
@ -55,13 +81,13 @@ public:
|
||||
* Get the ID of the sink.
|
||||
* @return ID of the sink.
|
||||
*/
|
||||
SinkID getID();
|
||||
SinkID getID() const;
|
||||
|
||||
/**
|
||||
* Get sink volume.
|
||||
* @return Volume as value between 0.0 and 1.0.
|
||||
*/
|
||||
float getVolume() const;
|
||||
float getVolume();
|
||||
|
||||
/**
|
||||
* Set sink volume.
|
||||
@ -73,7 +99,7 @@ public:
|
||||
* Check if the sink is muted.
|
||||
* @return True if muted, false if not.
|
||||
*/
|
||||
bool getMuted() const;
|
||||
bool getMuted();
|
||||
|
||||
/**
|
||||
* Set wether or not the sink is muted
|
||||
@ -85,7 +111,7 @@ public:
|
||||
* Get sink panning.
|
||||
* @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.
|
||||
@ -93,6 +119,11 @@ public:
|
||||
*/
|
||||
void setPanning(float panning);
|
||||
|
||||
/**
|
||||
* Show the sink type-specific menu.
|
||||
*/
|
||||
void showMenu();
|
||||
|
||||
// Emitted when the type of the sink was changed
|
||||
NewEvent<const std::string&> onTypeChanged;
|
||||
// Emmited when volume of the sink was changed
|
||||
@ -107,15 +138,23 @@ private:
|
||||
void stopSink();
|
||||
void startDSP();
|
||||
void stopDSP();
|
||||
void destroy(bool forgetSettings);
|
||||
void setInputSamplerate(double samplerate);
|
||||
|
||||
std::recursive_mutex mtx;
|
||||
dsp::stream<dsp::stereo_t> input;
|
||||
dsp::multirate::RationalResampler<dsp::stereo_t> resamp;
|
||||
dsp::audio::Volume volumeAdjust;
|
||||
|
||||
|
||||
SinkProvider* provider = NULL;
|
||||
std::unique_ptr<Sink> sink;
|
||||
double inputSamplerate;
|
||||
std::string type;
|
||||
const SinkID id;
|
||||
double inputSamplerate;
|
||||
AudioStream* const parentStream;
|
||||
StreamManager* const manager;
|
||||
|
||||
float volume = 1.0f;
|
||||
bool muted = false;
|
||||
float panning = 0.0f;
|
||||
@ -125,8 +164,9 @@ class StreamManager;
|
||||
|
||||
class AudioStream {
|
||||
friend StreamManager;
|
||||
AudioStream(StreamManager* manager, const std::string& name, dsp::stream<dsp::stereo_t>* stream, double samplerate);
|
||||
public:
|
||||
AudioStream(StreamManager* manager, const std::string& name, dsp::stream<dsp::stereo_t>* stream, double samplerate);
|
||||
~AudioStream();
|
||||
|
||||
/**
|
||||
* Set DSP stream input.
|
||||
@ -135,6 +175,8 @@ public:
|
||||
*/
|
||||
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.
|
||||
* @param samplerate Samplerate in Hz.
|
||||
@ -147,9 +189,6 @@ public:
|
||||
*/
|
||||
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.
|
||||
* @param type Type of the sink.
|
||||
@ -185,8 +224,7 @@ public:
|
||||
NewEvent<std::shared_ptr<SinkEntry>> onSinkRemove;
|
||||
|
||||
private:
|
||||
std::recursive_mutex mtx;
|
||||
const StreamManager* manager;
|
||||
StreamManager* const manager;
|
||||
const std::string name;
|
||||
double samplerate;
|
||||
dsp::routing::Splitter<dsp::stereo_t> split;
|
||||
@ -195,23 +233,8 @@ private:
|
||||
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 {
|
||||
friend SinkEntry;
|
||||
public:
|
||||
/**
|
||||
* Create an audio stream.
|
||||
@ -278,9 +301,6 @@ private:
|
||||
std::map<std::string, std::shared_ptr<AudioStream>> streams;
|
||||
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::vector<std::string> sinkTypes;
|
||||
std::shared_mutex providersMtx;
|
||||
|
Loading…
Reference in New Issue
Block a user