#include "stream.h" #include Sink::Sink(SinkEntry* entry, dsp::stream* stream, const std::string& name, SinkID id, const std::string& stringId) : entry(entry), stream(stream), streamName(name), id(id), stringId(stringId) {} void Sink::showMenu() {} 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; // Generate string ID stringId = parentStream->getName(); char buf[16]; sprintf(buf, "%d", (int)id); stringId += buf; // Initialize DSP resamp.init(&input, inputSamplerate, inputSamplerate); volumeAdjust.init(&resamp.out, 1.0f, false); // Initialize the sink setType(type); } std::string SinkEntry::getType() { std::lock_guard lck(mtx); return type; } void SinkEntry::setType(const std::string& type) { // Get unique lock on the entry std::lock_guard lck(mtx); // Delete existing sink if (sink) { provider->destroySink(std::move(sink)); } // Get shared lock on sink types auto lck2 = manager->getSinkTypesLock(); // Get the provider or throw error const auto& types = manager->getSinkTypes(); if (std::find(types.begin(), types.end(), type) == types.end()) { this->type.clear(); throw SinkEntryCreateException("Invalid sink type"); } // Create sink this->type = type; provider = manager->providers[type]; sink = provider->createSink(this, &volumeAdjust.out, parentStream->getName(), id, stringId); } SinkID SinkEntry::getID() const { return id; } float SinkEntry::getVolume() { std::lock_guard lck(mtx); return volume; } void SinkEntry::setVolume(float volume) { std::lock_guard lck(mtx); this->volume = volume; volumeAdjust.setVolume(volume); onVolumeChanged(volume); } bool SinkEntry::getMuted() { std::lock_guard lck(mtx); return muted; } void SinkEntry::setMuted(bool muted) { std::lock_guard lck(mtx); this->muted = muted; volumeAdjust.setMuted(muted); onMutedChanged(muted); } float SinkEntry::getPanning() { std::lock_guard lck(mtx); return panning; } void SinkEntry::setPanning(float panning) { std::lock_guard lck(mtx); this->panning = panning; // TODO onPanningChanged(panning); } void SinkEntry::showMenu() { std::lock_guard lck(mtx); sink->showMenu(); } void SinkEntry::startSink() { std::lock_guard lck(mtx); sink->start(); } void SinkEntry::stopSink() { std::lock_guard lck(mtx); sink->stop(); } std::lock_guard SinkEntry::getLock() { return std::lock_guard(mtx); } void SinkEntry::setSamplerate(double samplerate) { std::lock_guard lck(mtx); resamp.setOutSamplerate(samplerate); } void SinkEntry::startDSP() { std::lock_guard lck(mtx); resamp.start(); volumeAdjust.start(); } void SinkEntry::stopDSP() { std::lock_guard lck(mtx); resamp.stop(); volumeAdjust.stop(); } void SinkEntry::destroy(bool forgetSettings) { std::lock_guard lck(mtx); if (sink) { provider->destroySink(std::move(sink)); } type.clear(); } void SinkEntry::setInputSamplerate(double samplerate) { std::lock_guard lck(mtx); resamp.setInSamplerate(samplerate); } std::string SinkEntry::getStringID() { return stringId; } 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); } AudioStream::~AudioStream() { // Copy sink IDs std::vector 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* stream, double samplerate) { std::unique_lock 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 if (running) { 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 if (running) { for (auto& [id, sink] : sinks) { sink->startDSP(); } split.start(); } } void AudioStream::setSamplerate(double samplerate) { std::unique_lock lck(sinksMtx); // Update samplerate this->samplerate = samplerate; // Stop DSP if (running) { split.stop(); for (auto& [id, sink] : sinks) { sink->stopDSP(); } } // Set samplerate for (auto& [id, sink] : sinks) { sink->setInputSamplerate(samplerate); } // Start DSP if (running) { 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 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; } } // Create sink entry std::shared_ptr sink; try { sink = std::make_shared(manager, this, 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(); if (running) { 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); return id; } void AudioStream::removeSink(SinkID id, bool forgetSettings) { // 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(); // Delete instance sink->destroy(forgetSettings); } } std::shared_lock AudioStream::getSinksLock() { return std::shared_lock(sinksMtx); } const std::map>& AudioStream::getSinks() const { return sinks; } void AudioStream::startDSP() { // TODO: Maybe add a different mutex for the stream? std::unique_lock lck(sinksMtx); // Check if already running if (running) { return; } // Start all DSP split.start(); for (auto& [id, sink] : sinks) { sink->startDSP(); } running = true; } void AudioStream::stopDSP() { // TODO: Maybe add a different mutex for the stream? std::unique_lock lck(sinksMtx); // Check if already running if (!running) { return; } // Start all DSP split.stop(); for (auto& [id, sink] : sinks) { sink->stopDSP(); } running = false; } 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); return 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::pair> 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 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](std::pair 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 lck1(providersMtx); std::shared_lock lck2(streamsMtx); for (auto& [name, stream] : streams) { // Aquire lock on sink list auto sLock = stream->getSinksLock(); const auto& sinks = stream->getSinks(); // Find all sinks with the type that is about to be removed std::vector 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 StreamManager::getSinkTypesLock() { return std::shared_lock(providersMtx); } const std::vector& StreamManager::getSinkTypes() const { // TODO: This allows code to modify the names... return sinkTypes; }