looper/playback_backend.hpp

235 lines
7.9 KiB
C++

#pragma once
#include <stddef.h>
#include <vector>
#include <string>
#include <map>
#include <optional>
#include <SDL.h>
#include <iterator>
#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:
double length;
std::vector<PlaybackStream> streams;
std::string current_file;
std::string current_title;
bool open;
SDL_AudioSpec spec;
double 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;
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<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 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<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 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<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);