#include "vgmstream.hpp" extern "C" { #include #include #include } #include #include "file_backend.hpp" #include #include 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; spec.channels = vf->channels; spec.freq = vf->sample_rate; } 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; }