From ff655caf31cd0be1c4eabd0422cdd3262381a938 Mon Sep 17 00:00:00 2001 From: AlexandreRouma Date: Wed, 17 May 2023 03:55:47 +0200 Subject: [PATCH] saving work --- core/src/signal_path/stream.cpp | 286 ++++++++++++++++++++++++++++++++ core/src/signal_path/stream.h | 103 +++++++++--- 2 files changed, 364 insertions(+), 25 deletions(-) create mode 100644 core/src/signal_path/stream.cpp diff --git a/core/src/signal_path/stream.cpp b/core/src/signal_path/stream.cpp new file mode 100644 index 00000000..2f2bf28c --- /dev/null +++ b/core/src/signal_path/stream.cpp @@ -0,0 +1,286 @@ +// #include "stream.h" +// #include + +// Sink::Sink(dsp::stream* stream, const std::string& name, SinkID id) : +// stream(stream), +// streamName(name), +// id(id) +// {} + +// void Sink::showMenu() {} + +// AudioStream::AudioStream(StreamManager* manager, const std::string& name, dsp::stream* stream, double samplerate) : +// manager(manager), +// name(name) +// { +// this->samplerate = samplerate; + +// // Initialize DSP +// split.init(stream); +// split.start(); +// } + +// void AudioStream::setInput(dsp::stream* stream, double samplerate = 0.0) { +// std::lock_guard 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 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 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 +// std::shared_ptr sink; +// try { +// sink = std::make_shared(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 +// sink->startSink(); +// sink->startDSP(); + +// // Bind the sinks's input +// split.bindStream(&sink->input); + +// // Add sink to list +// sinks[id] = sink; + +// // Release lock and emit event +// lck.unlock(); +// onSinkAdded(sink); +// } + +// void AudioStream::removeSink(SinkID id, bool forgetSettings = true) { +// // Acquire shared lock +// std::shared_ptr sink; +// { +// std::shared_lock 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 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(); +// } +// } + +// std::shared_lock AudioStream::getSinksLock() { +// return std::shared_lock(sinksMtx); +// } + +// const std::map>& AudioStream::getSinks() const { +// return sinks; +// } + +// std::shared_ptr StreamManager::createStream(const std::string& name, dsp::stream* stream, double samplerate) { +// std::unique_lock 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(this, name, stream, samplerate); +// streams[name] = newStream; + +// // Release lock and emit event +// lck.unlock(); +// onStreamCreated(newStream); +// } + +// void StreamManager::destroyStream(std::shared_ptr& stream) { +// // Emit event +// onStreamDestroy(stream); + +// // Aquire complete lock on the stream list +// { +// std::unique_lock lck(streamsMtx); + +// // Get iterator of the stream +// auto it = std::find_if(streams.begin(), streams.end(), [&stream](std::shared_ptr& 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; +// } + +// // Delete entry from list +// flog::debug("Stream pointer uses, should be 2 and is {}", stream.use_count()); +// streams.erase(it); +// } + +// // Reset passed pointer +// stream.reset(); +// } + +// std::shared_lock StreamManager::getStreamsLock() { +// return std::shared_lock(streamsMtx); +// } + +// const std::map>& StreamManager::getStreams() const { +// return streams; +// } + +// void StreamManager::registerSinkProvider(const std::string& name, SinkProvider* provider) { +// std::unique_lock 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 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 +// onSinkProviderUnregister(type); + +// // Acquire shared lock on streams +// { +// std::shared_lock lck(streamsMtx); +// for (auto& [name, stream] : streams) { +// std::vector toRemove; + +// // 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 +// 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) +// for (auto& id : toRemove) { +// stream->removeSink(id); +// } +// } +// } + +// // Remove from the lists +// { +// std::unique_lock lck(providersMtx); +// 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 StreamManager::getSinkTypesLock() { +// return std::shared_lock(providersMtx); +// } + +// const std::vector& StreamManager::getSinkTypes() const { +// return sinkTypes; +// } \ No newline at end of file diff --git a/core/src/signal_path/stream.h b/core/src/signal_path/stream.h index cc92a894..35651233 100644 --- a/core/src/signal_path/stream.h +++ b/core/src/signal_path/stream.h @@ -9,25 +9,41 @@ #include #include #include +#include class AudioStream; +using SinkID = int; + class Sink { public: + Sink(dsp::stream* stream, const std::string& name, SinkID id); + virtual void start() = 0; virtual void stop() = 0; virtual void showMenu(); private: - dsp::stream* stream; - AudioStream* audioStream = NULL; + dsp::stream* const stream; + const std::string streamName; + const SinkID id; +}; + +class SinkEntryCreateException : public std::runtime_error { +public: + SinkEntryCreateException(const char* what) : std::runtime_error(what) {} }; class SinkEntry { friend AudioStream; - SinkEntry(dsp::stream* stream, const std::string& type, int index); + SinkEntry(const std::string& type, SinkID id, double inputSamplerate); public: - ~SinkEntry(); + + /** + * Get the type of the sink. + * @return Type of the sink. + */ + const std::string& getType(); /** * Change the type of the sink. @@ -35,15 +51,21 @@ public: */ void setType(const std::string& type); + /** + * Get the ID of the sink. + * @return ID of the sink. + */ + SinkID getID(); + /** * Get sink volume. - * @return Volume as value between 0 and 1. + * @return Volume as value between 0.0 and 1.0. */ float getVolume() const; /** * Set sink volume. - * @param volume Volume as value between 0 and 1. + * @param volume Volume as value between 0.0 and 1.0. */ void setVolume(float volume); @@ -61,21 +83,39 @@ public: /** * Get sink panning. - * @return Panning as value between -1 and 1 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; /** * Set sink panning. - * @param panning Panning as value between -1 and 1 meaning panning to the left and right respectively. + * @param panning Panning as value between -1.0 and 1.0 meaning panning to the left and right respectively. */ void setPanning(float panning); + // Emitted when the type of the sink was changed + NewEvent onTypeChanged; + // Emmited when volume of the sink was changed + NewEvent onVolumeChanged; + // Emitted when the muted state of the sink was changed + NewEvent onMutedChanged; + // Emitted when the panning of the sink was changed + NewEvent onPanningChanged; + private: + void startSink(); + void stopSink(); + void startDSP(); + void stopDSP(); + void setInputSamplerate(double samplerate); + + dsp::stream input; + dsp::multirate::RationalResampler resamp; dsp::audio::Volume volumeAdjust; std::unique_ptr sink; - const int index; + double inputSamplerate; + const SinkID id; float volume = 1.0f; bool muted = false; float panning = 0.0f; @@ -91,8 +131,9 @@ public: /** * Set DSP stream input. * @param stream DSP stream. + * @param samplerate New samplerate (optional, 0.0 if not used). */ - void setInput(dsp::stream* stream); + void setInput(dsp::stream* stream, double samplerate = 0.0); /** * Set the samplerate of the input stream. @@ -106,18 +147,23 @@ 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. - * @return Index of the new sink or -1 on error. + * @param id ID of the sink. Optional, -1 if automatic. + * @return ID of the new sink or -1 on error. */ - int addSink(const std::string& type); + SinkID addSink(const std::string& type, SinkID id = -1); /** * Remove a sink from a stream. - * @param index Index of the sink. + * @param id ID of the sink. + * @param forgetSettings Forget the settings for the sink. */ - void removeSink(int index); + void removeSink(SinkID id, bool forgetSettings = true); /** * Aquire a lock for the sink list. @@ -129,16 +175,23 @@ public: * Get the list of all sinks belonging to this stream. * @return Sink list. */ - const std::vector>& getSinks() const; + const std::map>& getSinks() const; + + // Emitted when the samplerate of the stream was changed + NewEvent onSamplerateChanged; + // Emitted when a sink was added + NewEvent> onSinkAdded; + // Emitted when a sink is being removed + NewEvent> onSinkRemove; private: std::recursive_mutex mtx; - StreamManager* manager; - std::string name; + const StreamManager* manager; + const std::string name; double samplerate; dsp::routing::Splitter split; - std::vector> sinks; + std::map> sinks; std::shared_mutex sinksMtx; }; @@ -159,7 +212,6 @@ public: }; class StreamManager { - friend AudioStream; public: /** * Create an audio stream. @@ -180,9 +232,7 @@ public: * Aquire a lock for the stream list. * @return Shared lock for the stream list. */ - std::shared_lock getStreamsLock() { - return std::shared_lock(streamsMtx); - } + std::shared_lock getStreamsLock(); /** * Get a list of streams and their associated names. @@ -211,7 +261,7 @@ public: /** * Get a list of sink types. - * @return List of sink type names. + * @return List of sink type names in alphabetical order. */ const std::vector& getSinkTypes() const; @@ -220,14 +270,17 @@ public: // Emitted when a stream is about to be destroyed NewEvent> onStreamDestroy; // Emitted when a sink provider was registered - NewEvent onSinkProviderRegistered; + NewEvent onSinkProviderRegistered; // Emitted when a sink provider is about to be unregistered - NewEvent onSinkProviderUnregister; + NewEvent onSinkProviderUnregister; private: std::map> streams; std::shared_mutex streamsMtx; + // TODO: Switch all this shit to a recursive mutex RW locks are shit + // Or maybe not actually + std::map providers; std::vector sinkTypes; std::shared_mutex providersMtx;