Zachary Hall
80f29047bd
Some checks failed
Build / build-gentoo (push) Successful in 2m19s
Build / download-system-deps (push) Successful in 6m53s
Build / get-source-code (push) Successful in 14m32s
Build / build-deb (push) Failing after 5m36s
Build / build-appimage (push) Successful in 4m42s
Build / build-android (push) Failing after 3m5s
Build / build-windows (push) Failing after 7m18s
259 lines
8.9 KiB
C++
259 lines
8.9 KiB
C++
#pragma once
|
|
#include <stddef.h>
|
|
#include <vector>
|
|
#include <string>
|
|
#include <map>
|
|
#include <optional>
|
|
#include <SDL.h>
|
|
#include <iterator>
|
|
#include <limits.h>
|
|
#include <limits>
|
|
#include "util.hpp"
|
|
#include "ipc/internal.pb.h"
|
|
struct PlaybackStream {
|
|
double length;
|
|
std::string name;
|
|
int id;
|
|
};
|
|
class PlaybackBackendHelper;
|
|
class PlaybackBackend {
|
|
double prevRate;
|
|
size_t minSamples;
|
|
size_t maxSamples;
|
|
protected:
|
|
uint64_t length;
|
|
std::vector<PlaybackStream> streams;
|
|
std::string current_file;
|
|
std::string current_title;
|
|
bool open;
|
|
SDL_AudioSpec spec;
|
|
uint64_t position;
|
|
std::map<std::string, google::protobuf::Any> property_defaults;
|
|
std::map<std::string, google::protobuf::Any> properties;
|
|
size_t min_sample_estimate;
|
|
size_t max_sample_estimate;
|
|
size_t max_sample_requirement = std::numeric_limits<size_t>::max();
|
|
size_t min_sample_requirement = std::numeric_limits<size_t>::min();
|
|
double rate;
|
|
uint64_t loop_start = 0;
|
|
uint64_t loop_end = UINT64_MAX;
|
|
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;
|
|
}
|
|
inline double samples_to_time(uint64_t samples) {
|
|
return ((double)samples) / ((double)spec.freq);
|
|
}
|
|
inline uint64_t time_to_samples(double time) {
|
|
return time * spec.freq;
|
|
}
|
|
public:
|
|
using map = std::map<std::string, PlaybackBackend*>;
|
|
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 void add_licenses() { }
|
|
inline virtual std::string get_id() {return "";}
|
|
inline virtual std::string get_name() {return "";}
|
|
inline virtual void seek(double position) { seek_samples(time_to_samples(position)); }
|
|
inline virtual void seek_samples(uint64_t position) { }
|
|
inline virtual double get_position_time() {
|
|
return ((double)get_position_samples()) / ((double)spec.freq);
|
|
}
|
|
inline virtual uint64_t get_position_samples() {
|
|
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 std::vector<Property> get_property_list() {
|
|
return std::vector<Property>();
|
|
}
|
|
inline virtual bool set(std::string path, google::protobuf::Any value) {properties[path] = value; return true;}
|
|
inline virtual std::optional<google::protobuf::Any> get(std::string path) {return properties.contains(path) ? properties[path] : std::optional<google::protobuf::Any>();}
|
|
inline virtual std::optional<google::protobuf::Any> 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<PlaybackStream> get_streams() {
|
|
return streams;
|
|
}
|
|
inline virtual uint64_t get_min_samples() {return 0;}
|
|
inline virtual std::optional<uint64_t> get_max_samples() {return {};}
|
|
virtual void cleanup();
|
|
virtual size_t render(void *buf, size_t maxlen);
|
|
inline virtual double get_length() {
|
|
return samples_to_time(get_length_samples());
|
|
}
|
|
inline virtual uint64_t get_length_samples() {
|
|
return open ? length : 0;
|
|
}
|
|
inline virtual uint64_t get_loop_start_samples() {
|
|
return open ? loop_start : 0;
|
|
}
|
|
inline virtual double get_loop_start() {
|
|
return samples_to_time(get_loop_start_samples());
|
|
}
|
|
inline virtual uint64_t get_loop_end_samples() {
|
|
return open ? loop_end == UINT64_MAX ? get_length_samples() : loop_end : 0;
|
|
}
|
|
inline virtual double get_loop_end() {
|
|
return samples_to_time(get_loop_end_samples());
|
|
}
|
|
inline virtual std::optional<std::string> get_current_file() {
|
|
return open ? current_file : std::optional<std::string>();
|
|
}
|
|
inline virtual std::optional<std::string> get_title() {
|
|
return open ? current_title : std::optional<std::string>();
|
|
}
|
|
inline virtual int get_stream_idx() {return 0;}
|
|
inline virtual ~PlaybackBackend() { }
|
|
static map backends;
|
|
template<class T>
|
|
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<class T>
|
|
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<class T>
|
|
static void unregister_backend() {
|
|
unregister_backend(get_backend_id<T>());
|
|
}
|
|
static void deinit_backends();
|
|
static inline std::optional<PlaybackBackend*> get_backend(std::string id) {
|
|
if (backends.contains(id)) {
|
|
return backends[id];
|
|
} else {
|
|
return {};
|
|
}
|
|
}
|
|
template<class T>
|
|
static inline std::optional<T*> get_backend() {
|
|
auto id = get_backend_id<T>();
|
|
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);
|