more or less finished the new stream system

This commit is contained in:
AlexandreRouma 2023-07-09 04:09:18 +02:00
parent ff655caf31
commit 2a741932e0
8 changed files with 554 additions and 277 deletions

View File

@ -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)

View File

@ -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();

View 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();
}
}
};

View File

@ -0,0 +1,6 @@
#pragma once
namespace streamsmenu {
void init();
void draw(void* ctx);
};

View File

@ -5,4 +5,5 @@ namespace sigpath {
VFOManager vfoManager;
SourceManager sourceManager;
SinkManager sinkManager;
StreamManager streamManager;
};

View File

@ -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;
};

View File

@ -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;
}

View File

@ -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;