2024-08-08 13:12:37 -07:00
|
|
|
#include "vgmstream.hpp"
|
|
|
|
extern "C" {
|
|
|
|
#include <vgmstream.h>
|
|
|
|
#include <util.h>
|
|
|
|
#include <plugins.h>
|
|
|
|
}
|
|
|
|
#include <filesystem>
|
|
|
|
#include "file_backend.hpp"
|
|
|
|
#include <stddef.h>
|
|
|
|
#include <string.h>
|
2024-11-12 14:53:44 -08:00
|
|
|
#include <license.hpp>
|
|
|
|
#include <assets/assets.h>
|
2024-08-08 13:12:37 -07:00
|
|
|
void VgmStreamBackend::load(const char *filename) {
|
|
|
|
spec.format = AUDIO_S16SYS;
|
|
|
|
file = open_file(filename);
|
|
|
|
sf = get_sf_from_file(file);
|
|
|
|
if (!sf) {
|
|
|
|
throw std::exception();
|
|
|
|
}
|
|
|
|
auto *output = init_vgmstream_from_STREAMFILE(sf);
|
|
|
|
if (!output) {
|
|
|
|
throw std::exception();
|
|
|
|
}
|
|
|
|
int stream_count = output->num_streams;
|
|
|
|
close_vgmstream(output);
|
|
|
|
streams.clear();
|
|
|
|
for (int i = 0; i < stream_count; i++) {
|
|
|
|
PlaybackStream stream;
|
|
|
|
stream.id = i;
|
|
|
|
stream.length = 0;
|
|
|
|
stream.name = "";
|
|
|
|
if (!sf) {
|
|
|
|
streams.push_back(stream);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
sf->stream_index = i;
|
|
|
|
auto *tmp = init_vgmstream_from_STREAMFILE(sf);
|
|
|
|
if (!tmp) {
|
|
|
|
streams.push_back(stream);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
reset_vgmstream(tmp);
|
|
|
|
stream.length = (double)tmp->num_samples / (double)tmp->sample_rate;
|
|
|
|
char *buf = (char*)calloc(STREAM_NAME_SIZE + 1, 1);
|
|
|
|
strncpy(buf, tmp->stream_name, STREAM_NAME_SIZE);
|
|
|
|
if (buf[0] == '\0') {
|
|
|
|
free(buf);
|
|
|
|
buf = strdup("Unknown");
|
|
|
|
}
|
|
|
|
if (i == 0) {
|
|
|
|
char *buf2 = NULL;
|
|
|
|
const char *const DEFAULT_FORMAT_STR = "Default (%s)";
|
|
|
|
size_t buflen = snprintf(NULL, 0, DEFAULT_FORMAT_STR, buf) + 1;
|
|
|
|
buf2 = (char*)calloc(buflen, 1);
|
|
|
|
snprintf(buf2, buflen, DEFAULT_FORMAT_STR, buf);
|
|
|
|
stream.name = buf2;
|
|
|
|
free(buf2);
|
|
|
|
} else {
|
|
|
|
stream.name = buf;
|
|
|
|
}
|
|
|
|
streams.push_back(stream);
|
|
|
|
free(buf);
|
|
|
|
close_vgmstream(tmp);
|
|
|
|
}
|
|
|
|
switch_stream(0);
|
|
|
|
}
|
|
|
|
void VgmStreamBackend::switch_stream(int idx) {
|
|
|
|
if (vf == nullptr || sf == nullptr || file == nullptr) {
|
|
|
|
open = false;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
sf->stream_index = idx;
|
|
|
|
if (idx >= streams.size()) {
|
|
|
|
throw std::exception();
|
|
|
|
}
|
|
|
|
vf = init_vgmstream_from_STREAMFILE(sf);
|
|
|
|
if (!vf) {
|
|
|
|
throw std::exception();
|
|
|
|
}
|
|
|
|
vgmstream_cfg_t cfg = {0};
|
|
|
|
cfg.allow_play_forever = 1;
|
|
|
|
cfg.play_forever = 1;
|
|
|
|
vgmstream_apply_config(vf, &cfg);
|
|
|
|
spec.channels = vf->channels;
|
|
|
|
spec.freq = vf->sample_rate;
|
|
|
|
length = streams[idx].length;
|
|
|
|
const char *title_tag = vf->stream_name;
|
|
|
|
if (title_tag[0] == '\0') {
|
|
|
|
std::filesystem::path path(current_file);
|
|
|
|
current_title = path.stem().string();
|
|
|
|
} else {
|
|
|
|
char *buf = (char*)calloc(STREAM_NAME_SIZE + 1, 1);
|
|
|
|
strncpy(buf, title_tag, STREAM_NAME_SIZE);
|
|
|
|
current_title = buf;
|
|
|
|
free(buf);
|
|
|
|
}
|
|
|
|
open = true;
|
|
|
|
}
|
|
|
|
void VgmStreamBackend::cleanup() {
|
|
|
|
streams.clear();
|
|
|
|
close_vgmstream(vf);
|
|
|
|
close_streamfile(sf);
|
|
|
|
vf = nullptr;
|
|
|
|
sf = nullptr;
|
|
|
|
delete file;
|
|
|
|
file = nullptr;
|
|
|
|
open = false;
|
|
|
|
}
|
|
|
|
size_t VgmStreamBackend::render(void *buf, size_t maxlen) {
|
|
|
|
if (vf == nullptr || sf == nullptr || file == nullptr) {
|
|
|
|
open = false;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
maxlen /= sizeof(sample_t);
|
|
|
|
size_t new_samples = render_vgmstream((sample_t*)(buf), (int)maxlen, vf);
|
|
|
|
if (maxlen > new_samples) {
|
|
|
|
reset_vgmstream(vf);
|
|
|
|
position = 0.0;
|
|
|
|
} else {
|
|
|
|
position += (double)new_samples / (double)vf->sample_rate;
|
|
|
|
}
|
|
|
|
return new_samples * sizeof(sample_t);
|
|
|
|
}
|
|
|
|
void VgmStreamBackend::seek(double position) {
|
|
|
|
|
|
|
|
if (vf == nullptr || sf == nullptr || file == nullptr) {
|
|
|
|
open = false;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
auto pos32 = (int32_t)(position * vf->sample_rate);
|
|
|
|
seek_vgmstream(vf, pos32);
|
|
|
|
this->position = position;
|
|
|
|
}
|
|
|
|
double VgmStreamBackend::get_position() {
|
|
|
|
return position;
|
|
|
|
}
|
|
|
|
int VgmStreamBackend::get_stream_idx() {
|
|
|
|
return vf->stream_index;
|
2024-11-12 14:53:44 -08:00
|
|
|
}
|
|
|
|
void VgmStreamBackend::add_licenses() {
|
|
|
|
auto &license_data = get_license_data();
|
|
|
|
auto vgmstream = LicenseData("VGMSTREAM", "ISC");
|
|
|
|
LOAD_LICENSE(vgmstream, vgmstream);
|
|
|
|
license_data.insert(vgmstream);
|
|
|
|
}
|