SDRPlusPlus/core/src/signal_path/stream.h

334 wiersze
9.3 KiB
C++

#pragma once
#include <memory>
#include <vector>
#include <map>
#include <dsp/stream.h>
#include <dsp/types.h>
#include <dsp/routing/splitter.h>
#include <dsp/multirate/rational_resampler.h>
#include <dsp/audio/volume.h>
#include <utils/new_event.h>
#include <shared_mutex>
#include <stdexcept>
class SinkEntry;
class Stream;
class MasterStream;
class StreamManager;
using SinkID = int;
class Sink {
public:
Sink(SinkEntry* entry, dsp::stream<dsp::stereo_t>* stream, const std::string& name, SinkID id, const std::string& stringId);
virtual ~Sink() {}
virtual void start() = 0;
virtual void stop() = 0;
virtual void showMenu();
protected:
SinkEntry* const entry;
dsp::stream<dsp::stereo_t>* const stream;
const std::string streamName;
const SinkID id;
const std::string stringId;
};
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 id, const std::string& stringId) = 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) {}
};
// TODO: Would be cool to have data and audio sinks instead of just audio.
class SinkEntry {
friend Sink;
friend Stream;
friend MasterStream;
public:
SinkEntry(StreamManager* manager, Stream* parentStream, const std::string& type, SinkID id, double inputSamplerate);
/**
* Get the type of the sink.
* @return Type of the sink.
*/
std::string getType() const;
/**
* Change the type of the sink.
* @param type New sink type.
*/
void setType(const std::string& type);
/**
* Get the ID of the sink.
* @return ID of the sink.
*/
SinkID getID() const;
/**
* Get sink volume.
* @return Volume as value between 0.0 and 1.0.
*/
float getVolume() const;
/**
* Set sink volume.
* @param volume Volume as value between 0.0 and 1.0.
*/
void setVolume(float volume);
/**
* Check if the sink is muted.
* @return True if muted, false if not.
*/
bool getMuted() const;
/**
* Set wether or not the sink is muted
* @param muted True to mute, false to unmute.
*/
void setMuted(bool muted);
/**
* Get sink panning.
* @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.0 and 1.0 meaning panning to the left and right respectively.
*/
void setPanning(float panning);
/**
* Show the sink type-specific menu.
*/
void showMenu();
/**
* Get the string form ID unique to both the sink and stream. Be used to reference settings.
* @return Unique string ID.
*/
std::string getStringID() const;
// Emitted when the type of the sink was changed
NewEvent<const std::string&> onTypeChanged;
// Emmited when volume of the sink was changed
NewEvent<float> onVolumeChanged;
// Emitted when the muted state of the sink was changed
NewEvent<bool> onMutedChanged;
// Emitted when the panning of the sink was changed
NewEvent<float> onPanningChanged;
// TODO: Need to allow the sink to change the entry samplerate and start/stop the DSP
// This will also require allowing it to get a lock on the sink so others don't attempt to mess with it.
std::lock_guard<std::recursive_mutex> getLock() const;
void startDSP();
void stopDSP();
void setSamplerate(double samplerate);
private:
void startSink();
void stopSink();
void destroy(bool forgetSettings);
void setInputSamplerate(double samplerate);
mutable 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;
std::string type;
const SinkID id;
double inputSamplerate;
Stream* const parentStream;
StreamManager* const manager;
std::string stringId;
float volume = 1.0f;
bool muted = false;
float panning = 0.0f;
};
class Stream {
protected:
Stream(StreamManager* manager, const std::string& name, dsp::stream<dsp::stereo_t>* stream, double samplerate);
public:
~Stream();
/**
* Get the name of the stream.
* @return Name of the stream.
*/
const std::string& getName() const;
/**
* Add a sink to the stream.
* @param type Type of the sink.
* @param id ID of the sink. Optional, -1 if automatic.
* @return ID of the new sink or -1 on error.
*/
SinkID addSink(const std::string& type, SinkID id = -1);
/**
* Remove a sink from a stream.
* @param id ID of the sink.
* @param forgetSettings Forget the settings for the sink.
*/
void removeSink(SinkID id, bool forgetSettings = true);
/**
* Aquire a lock for the sink list.
* @return Shared lock for the sink list.
*/
std::shared_lock<std::shared_mutex> getSinksLock();
/**
* Get the list of all sinks belonging to this stream.
* @return Sink list.
*/
const std::map<SinkID, std::shared_ptr<SinkEntry>>& getSinks() const;
// Emitted when the samplerate of the stream was changed
NewEvent<double> onSamplerateChanged;
// Emitted when a sink was added
NewEvent<std::shared_ptr<SinkEntry>> onSinkAdded;
// Emitted when a sink is being removed
NewEvent<std::shared_ptr<SinkEntry>> onSinkRemove;
protected:
StreamManager* const manager;
const std::string name;
double samplerate;
dsp::routing::Splitter<dsp::stereo_t> split;
bool running = false;
std::map<SinkID, std::shared_ptr<SinkEntry>> sinks;
std::shared_mutex sinksMtx;
};
class MasterStream : public Stream {
friend StreamManager;
MasterStream(StreamManager* manager, const std::string& name, dsp::stream<dsp::stereo_t>* stream, double samplerate);
public:
/**
* Set DSP stream input.
* @param stream DSP stream.
* @param samplerate New samplerate (optional, 0.0 if not used).
*/
void setInput(dsp::stream<dsp::stereo_t>* stream, double samplerate = 0.0);
/**
* Set the samplerate of the input stream.
* @param samplerate Samplerate in Hz.
*/
void setSamplerate(double samplerate);
/**
* Start the DSP.
*/
void startDSP();
/**
* Stop the DSP.
*/
void stopDSP();
};
class StreamManager {
friend SinkEntry;
public:
/**
* Create an audio stream.
* @param name Name of the stream.
* @param stream DSP stream that outputs the audio.
* @param samplerate Samplerate of the audio data.
* @return Audio stream instance.
*/
std::shared_ptr<MasterStream> createStream(const std::string& name, dsp::stream<dsp::stereo_t>* stream, double samplerate);
/**
* Destroy an audio stream.
* @param stream Stream to destroy. The passed shared pointer will be automatically reset.
*/
void destroyStream(std::shared_ptr<MasterStream>& stream);
/**
* Aquire a lock for the stream list.
* @return Shared lock for the stream list.
*/
std::shared_lock<std::shared_mutex> getStreamsLock();
/**
* Get a list of streams and their associated names.
* @return Map of names to stream instance.
*/
const std::map<std::string, std::shared_ptr<Stream>>& getStreams() const;
/**
* Register a sink provider.
* @param name Name of the sink type.
* @param provider Sink provider instance.
*/
void registerSinkProvider(const std::string& name, SinkProvider* provider);
/**
* Unregister a sink provider.
* @param name Name of the sink type.
*/
void unregisterSinkProvider(SinkProvider* provider);
/**
* Aquire a lock for the sink type list.
* @return Shared lock for the sink type list.
*/
std::shared_lock<std::shared_mutex> getSinkTypesLock();
/**
* Get a list of sink types.
* @return List of sink type names in alphabetical order.
*/
const std::vector<std::string>& getSinkTypes() const;
// Emitted when a stream was created
NewEvent<std::shared_ptr<Stream>> onStreamCreated;
// Emitted when a stream is about to be destroyed
NewEvent<std::shared_ptr<Stream>> onStreamDestroy;
// Emitted when a sink provider was registered
NewEvent<const std::string&> onSinkProviderRegistered;
// Emitted when a sink provider is about to be unregistered
NewEvent<const std::string&> onSinkProviderUnregister;
private:
std::map<std::string, std::shared_ptr<Stream>> streams;
std::shared_mutex streamsMtx;
std::map<std::string, SinkProvider*> providers;
std::vector<std::string> sinkTypes;
std::shared_mutex providersMtx;
};