#pragma once #include #include #include #include #include #include #include #include #include "util.hpp" #include "ipc/internal.pb.h" struct PlaybackStream { double length; std::string name; int id; }; class PlaybackBackendHelper; enum PropertyType { Double, String, Bytes, Boolean }; struct Property { PropertyType type; std::string path; }; class PlaybackBackend { double prevRate; size_t minSamples; size_t maxSamples; protected: double length; std::vector streams; std::string current_file; std::string current_title; bool open; SDL_AudioSpec spec; double position; std::map property_defaults; std::map properties; size_t min_sample_estimate; size_t max_sample_estimate; size_t max_sample_requirement = std::numeric_limits::max(); size_t min_sample_requirement = std::numeric_limits::min(); double rate; double loop_start = 0.0; double loop_end = -1.0; void setMinSamples(size_t samples) { this->minSamples = samples; adjustSampleEstimates(); } void setMaxSamples(size_t samples) { this->maxSamples = samples; adjustSampleEstimates(); } void adjustSampleEstimates() { long double tmpMinSamples = minSamples; long double tmpMaxSamples = maxSamples; long double tmpRate = ABS(rate); min_sample_estimate = (size_t)floorl(tmpMinSamples * tmpRate); if (min_sample_estimate < min_sample_requirement) min_sample_estimate = min_sample_requirement; max_sample_estimate = (size_t)ceill(tmpMaxSamples * tmpRate); if (max_sample_estimate > max_sample_requirement) max_sample_estimate = max_sample_requirement; } public: using map = std::map; using iterator = map::iterator; using const_iterator = map::const_iterator; using reverse_iterator = map::reverse_iterator; using const_reverse_iterator = map::const_reverse_iterator; inline virtual std::string get_id() {return "";} inline virtual std::string get_name() {return "";} inline virtual void seek(double position) { } inline virtual double get_position() { return position; } inline virtual SDL_AudioSpec get_spec() { return spec; } inline void set_rate(double value) { this->rate = value; } /*** * @brief Gets the rate used by the playback engine. This should not be used to adjust the rate of the rendering, except to guess the amount of samples will likely be required. */ inline double get_rate() { return this->rate; } inline virtual bool set(std::string path, google::protobuf::Any value) {properties[path] = value; return true;} inline virtual std::optional get(std::string path) {return properties.contains(path) ? properties[path] : std::optional();} inline virtual std::optional reset(std::string path) { if (property_defaults.contains(path)) { properties[path] = property_defaults[path]; } else { properties.erase(path); } return get(path); } virtual void load(const char *filename); virtual void init(const char *filename, int idx = 0); virtual void switch_stream(int idx); inline virtual std::vector get_streams() { return streams; } inline virtual uint64_t get_min_samples() {return 0;} inline virtual std::optional get_max_samples() {return {};} virtual void cleanup(); virtual size_t render(void *buf, size_t maxlen); inline virtual double get_length() { return open ? length : 0.0; } inline virtual double get_loop_start() { return open ? loop_start : 0.0; } inline virtual double get_loop_end() { return open ? loop_end < 0.0 ? get_length() : loop_end : 0.0; } inline virtual std::optional get_current_file() { return open ? current_file : std::optional(); } inline virtual std::optional get_title() { return open ? current_title : std::optional(); } inline virtual int get_stream_idx() {return 0;} inline virtual ~PlaybackBackend() { } static map backends; template static inline std::string get_backend_id() { PlaybackBackend *backend = new T(); auto output = backend->get_id(); delete backend; return output; } static inline PlaybackBackend* get_first_backend() { if (backends.empty()) { return nullptr; } return (*backends.begin()).second; } static void register_backend(PlaybackBackend *backend) { std::string backend_id = backend->get_id(); if (backends.contains(backend_id)) { // Guard against potential memory leak due to reassigning a new pointer without deallocating the previous one delete backend; return; } backends[backend_id] = backend; } template static void register_backend() { PlaybackBackend *backend = new T(); backend->register_self(); } static inline void unregister_backend(std::string id) { if (backends.contains(id)) { auto backend = backends[id]; delete backend; backends.erase(id); } } inline void unregister_self() { PlaybackBackend::unregister_backend(get_id()); } inline void register_self() { PlaybackBackend::register_backend(this); } template static void unregister_backend() { unregister_backend(get_backend_id()); } static void deinit_backends(); static inline std::optional get_backend(std::string id) { if (backends.contains(id)) { return backends[id]; } else { return {}; } } template static inline std::optional get_backend() { auto id = get_backend_id(); auto output = get_backend(id); if (output.has_value()) { return (T*)output.value(); } else { return {}; } } }; class PlaybackBackendHelper { using iterator = PlaybackBackend::iterator; using const_iterator = PlaybackBackend::const_iterator; using reverse_iterator = PlaybackBackend::reverse_iterator; using const_reverse_iterator = PlaybackBackend::const_reverse_iterator; public: inline iterator begin() { return PlaybackBackend::backends.begin(); } inline const_iterator cbegin() const { return PlaybackBackend::backends.cbegin(); } inline iterator end() { return PlaybackBackend::backends.end(); } inline const_iterator cend() const { return PlaybackBackend::backends.cend(); } inline reverse_iterator rbegin() { return PlaybackBackend::backends.rbegin(); } inline const_reverse_iterator crbegin() const { return PlaybackBackend::backends.crbegin(); } inline reverse_iterator rend() { return PlaybackBackend::backends.rend(); } inline const_reverse_iterator crend() const { return PlaybackBackend::backends.crend(); } inline size_t size() { return PlaybackBackend::backends.size(); } inline bool empty() { return PlaybackBackend::backends.empty(); } }; void init_playback_backends(); struct audio_data_t { size_t size; bool endian; bool is_signed; bool is_float; }; void init_audio_data(); size_t size_of_sample_type(int type); bool sample_type_has_endian(int type); bool sample_type_endian_msb(int type); bool sample_type_endian_lsb(int type); bool sample_type_is_float(int type); bool smaple_type_is_integer(int type); bool sample_type_is_signed(int type); bool sample_type_is_unsigned(int type); int sample_spec_to_sdl(audio_data_t spec); audio_data_t sdl_to_sample_spec(int type); std::string sdl_to_str(int type);