193 lines
5.8 KiB
C++
193 lines
5.8 KiB
C++
#include "gme_backend.hpp"
|
|
#include <algorithm>
|
|
#include <ipc/common.pb.h>
|
|
#include <exception>
|
|
#include <filesystem>
|
|
#include "file_backend.hpp"
|
|
#include <stddef.h>
|
|
#include <string.h>
|
|
#include <file_backend.hpp>
|
|
#include <util.hpp>
|
|
#include <license.hpp>
|
|
#include <assets/assets.h>
|
|
#include <limits.h>
|
|
#include <gme/gme.h>
|
|
std::vector<Property> GmeBackend::get_property_list() {
|
|
std::vector<Property> output;
|
|
for (auto &kv : voices) {
|
|
Property prop;
|
|
prop.set_id(PropertyId::BackendSpecific);
|
|
prop.set_name(fmt::format("gme/voice/{}/enable", kv.first));
|
|
prop.set_type(PropertyType::Boolean);
|
|
prop.set_path(prop.name());
|
|
output.push_back(prop);
|
|
}
|
|
return output;
|
|
}
|
|
std::optional<int> GmeBackend::get_voice_id(std::string path) {
|
|
std::string prefix = "gme/voice/";
|
|
std::string suffix = "/enable";
|
|
if (path.substr(0, prefix.length()) != prefix) {
|
|
return {};
|
|
}
|
|
if (path.substr(path.length() - suffix.length()) != suffix) {
|
|
return {};
|
|
}
|
|
path = path.substr(prefix.length());
|
|
path = path.substr(0, path.length() - suffix.length());
|
|
if (!voices.contains(path)) {
|
|
return {};
|
|
}
|
|
return voices[path];
|
|
}
|
|
bool GmeBackend::set(std::string path, google::protobuf::Any value) {
|
|
auto maybe_voice_id = get_voice_id(path);
|
|
if (maybe_voice_id.has_value()) {
|
|
bool enable = resolve_value<bool>(value);
|
|
gme_mute_voice(gme_backend, maybe_voice_id.value(), !enable);
|
|
properties[path] = value;
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
std::optional<google::protobuf::Any> GmeBackend::get(std::string path) {
|
|
auto maybe_voice_id = get_voice_id(path);
|
|
if (maybe_voice_id.has_value()) {
|
|
if (properties.contains(path)) {
|
|
return properties[path];
|
|
} else {
|
|
return reset(path);
|
|
}
|
|
} else {
|
|
return {};
|
|
}
|
|
}
|
|
std::optional<google::protobuf::Any> GmeBackend::reset(std::string path) {
|
|
auto maybe_voice_id = get_voice_id(path);
|
|
if (maybe_voice_id.has_value()) {
|
|
google::protobuf::Any prop;
|
|
BooleanProperty bool_prop;
|
|
bool_prop.set_value(true);
|
|
prop.PackFrom(bool_prop);
|
|
properties[path] = prop;
|
|
gme_mute_voice(gme_backend, maybe_voice_id.value(), false);
|
|
return prop;
|
|
} else {
|
|
return {};
|
|
}
|
|
}
|
|
void GmeBackend::load(const char *filename) {
|
|
memset(&spec, 0, sizeof(spec));
|
|
current_file = filename;
|
|
spec.format = AUDIO_S16SYS;
|
|
spec.samples = 100;
|
|
spec.channels = 2;
|
|
spec.freq = 48000;
|
|
spec.size = 100 * 2 * sizeof(int16_t);
|
|
gme_open_file(filename, &gme_backend, spec.freq);
|
|
if (gme_backend == NULL) {
|
|
throw std::exception();
|
|
}
|
|
int track_count = gme_track_count(gme_backend);
|
|
streams.clear();
|
|
for (int i = 0; i < track_count; i++) {
|
|
gme_info_t *info;
|
|
gme_track_info(gme_backend, &info, i);
|
|
PlaybackStream stream;
|
|
if (info->song[0] == '\0') {
|
|
stream.name = fmt::format("Song {}", i);
|
|
} else {
|
|
stream.name = info->song;
|
|
}
|
|
|
|
stream.length = ((double)info->length) / 1000.0;
|
|
if (stream.length < 0.0) stream.length = 0.0;
|
|
stream.id = i;
|
|
streams.push_back(stream);
|
|
gme_free_info(info);
|
|
}
|
|
}
|
|
extern SDL_AudioSpec obtained;
|
|
void GmeBackend::switch_stream(int idx) {
|
|
if (gme_backend == NULL) {
|
|
throw std::exception();
|
|
}
|
|
if (idx > streams.size()) {
|
|
throw std::exception();
|
|
}
|
|
gme_start_track(gme_backend, idx);
|
|
gme_info_t *info;
|
|
gme_track_info(gme_backend, &info, idx);
|
|
if (info->length < 0) {
|
|
info->length = 0;
|
|
}
|
|
uint64_t tmp = info->length;
|
|
this->loop_len = info->loop_length;
|
|
this->loop_start = info->intro_length;
|
|
if (tmp == 0) tmp = this->loop_len + this->loop_start;
|
|
tmp *= spec.freq;
|
|
tmp /= 1000;
|
|
this->length = tmp;
|
|
if (info->song[0] == '\0') {
|
|
this->current_title = {};
|
|
} else {
|
|
this->current_title = info->song;
|
|
}
|
|
int voice_count = gme_voice_count(gme_backend);
|
|
for (int i = 0; i < voice_count; i++) {
|
|
const char *voice_name = gme_voice_name(gme_backend, i);
|
|
voices[voice_name] = i;
|
|
}
|
|
gme_free_info(info);
|
|
gme_ignore_silence(gme_backend, true);
|
|
open = true;
|
|
}
|
|
void GmeBackend::cleanup() {
|
|
gme_delete(gme_backend);
|
|
gme_backend = NULL;
|
|
streams.clear();
|
|
}
|
|
size_t GmeBackend::render(void *buf, size_t maxlen) {
|
|
if (gme_backend == NULL) return 0;
|
|
const char *err;
|
|
size_t sample_type_len = 4;
|
|
maxlen /= sample_type_len;
|
|
if (err = gme_play(gme_backend, maxlen * 2, (short*)buf)) {
|
|
ERROR.writefln("Failed to play audio: %s", err);
|
|
return 0;
|
|
}
|
|
int pos_samples = gme_tell_samples(gme_backend);
|
|
int loop_end = loop_len + loop_start;
|
|
uint64_t loop_end_samples = loop_end;
|
|
loop_end_samples *= spec.freq;
|
|
loop_end_samples /= 1000;
|
|
uint64_t loop_start_samples = loop_start;
|
|
loop_start_samples *= spec.freq;
|
|
loop_start_samples /= 1000;
|
|
if (pos_samples >= loop_end_samples || gme_track_ended(gme_backend)) {
|
|
gme_seek_samples(gme_backend, loop_start_samples + (pos_samples - loop_end_samples));
|
|
}
|
|
maxlen *= sample_type_len;
|
|
return maxlen;
|
|
}
|
|
uint64_t GmeBackend::get_min_samples() {
|
|
return spec.size;
|
|
}
|
|
std::optional<uint64_t> GmeBackend::get_max_samples() {
|
|
return get_min_samples();
|
|
}
|
|
void GmeBackend::seek_samples(uint64_t position) {
|
|
gme_seek_samples(gme_backend, position);
|
|
}
|
|
uint64_t GmeBackend::get_position_samples() {
|
|
return gme_tell_samples(gme_backend);
|
|
}
|
|
int GmeBackend::get_stream_idx() {
|
|
return streamidx;
|
|
}
|
|
void GmeBackend::add_licenses() {
|
|
auto &license_data = get_license_data();
|
|
auto gme = LicenseData("gme", "lgpl-2.1");
|
|
LOAD_LICENSE(gme, lgpl_2_1);
|
|
license_data.insert(gme);
|
|
}
|