looper/backends/playback/sdl_mixer_x/sdl_mixer_x.cpp
2024-09-28 10:31:18 -07:00

155 lines
4.5 KiB
C++

#include "sdl_mixer_x.hpp"
#include <endian.h>
#include <filesystem>
#include "file_backend.hpp"
#include <stddef.h>
#include <log.hpp>
#include <util.hpp>
#include <string.h>
std::optional<uint64_t> SDLMixerXBackend::get_max_samples() {
return 4096;
}
void SDLMixerXBackend::load(const char *filename) {
memset(&spec, 0, sizeof(spec));
spec.callback = NULL;
spec.channels = 2;
spec.userdata = NULL;
spec.format = AUDIO_F32SYS;
spec.freq = 48000;
spec.samples = get_max_samples().value();
spec.size = spec.samples * 4 * spec.channels;
Mix_InitMixer(&spec, SDL_FALSE);
mixer = Mix_GetGeneralMixer();
{
std::filesystem::path fpath = std::string(filename);
fpath = fpath.parent_path() / fpath.stem();
std::string fpath_str = fpath.string();
std::vector<std::string> soundfonts = {fpath_str + ".sf2", fpath_str + ".dls"};
std::string sf_path_str = "";
bool any_path_exists = false;
for (auto sf_path : soundfonts) {
if (std::filesystem::exists(sf_path)) {
any_path_exists = true;
sf_path_str += ";" + sf_path;
}
}
if (any_path_exists) {
sf_path_str = sf_path_str.substr(1);
Mix_SetSoundFonts(sf_path_str.c_str());
} else {
Mix_SetSoundFonts(NULL);
}
}
DEBUG.writefln("Opening file: %s", filename);
file = open_file(filename);
DEBUG.writeln("Loading file...");
this->music = nullptr;
Mix_Music *output = Mix_LoadMUS_RW(get_sdl_file(this->file), SDL_TRUE);
if (output == nullptr) {
ERROR.writefln("Error occurred: %s", Mix_GetError());
throw std::exception();
}
if (Mix_PlayMusicStream(output, -1) < 0) {
ERROR.writefln("Error occurred: %s", Mix_GetError());
this->music = output;
throw std::exception();
}
Mix_ResumeMusicStream(output);
length = Mix_MusicDuration(output);
current_file = std::string(filename);
const char *title_tag = Mix_GetMusicTitleTag(output);
// Check for an empty string, which indicates there's no title tag.
if (title_tag[0] == '\0') {
std::filesystem::path path(current_file);
current_title = path.stem().string();
} else {
current_title = std::string(title_tag);
}
this->music = output;
streams.clear();
PlaybackStream stream;
stream.id = 0;
stream.length = Mix_MusicDuration(output);
stream.name = current_title;
streams.push_back(stream);
open = true;
initial = true;
}
void SDLMixerXBackend::switch_stream(int idx) {
seek(0.0);
}
void SDLMixerXBackend::seek(double position) {
if (music == nullptr || file == nullptr) {
open = false;
return;
}
Mix_SetMusicPositionStream(music, position);
}
size_t SDLMixerXBackend::render(void *buf, size_t maxlen) {
if (music == nullptr || file == nullptr) {
open = false;
return 0;
}
if (initial) {
seek(0.0);
}
size_t i = 0;
size_t bytes_per_iter = maxlen;
if (bytes_per_iter > spec.size) {
bytes_per_iter = spec.size;
}
// Remove partial sample frames.
bytes_per_iter /= sizeof(float);
bytes_per_iter *= sizeof(float);
while (i < maxlen) {
if (i + bytes_per_iter > maxlen) {
bytes_per_iter = maxlen - i;
// Remove partial sample frames.
bytes_per_iter /= sizeof(float);
bytes_per_iter *= sizeof(float);
}
mixer(NULL, (Uint8*)(buf) + i, bytes_per_iter);
i += bytes_per_iter;
}
if (get_position() > (((double)maxlen) / ((double)spec.freq))) {
if (initial) {
seek(0.0);
}
initial = false;
}
return i;
}
double SDLMixerXBackend::get_position() {
if (music == nullptr || file == nullptr) {
open = false;
return 0.0;
}
return Mix_GetMusicPosition(music);
}
SDLMixerXBackend::SDLMixerXBackend() {
Mix_Init(MIX_INIT_FLAC|MIX_INIT_MID|MIX_INIT_MOD|MIX_INIT_MP3|MIX_INIT_OGG|MIX_INIT_OPUS|MIX_INIT_WAVPACK);
}
SDLMixerXBackend::~SDLMixerXBackend() {
Mix_Quit();
}
void SDLMixerXBackend::cleanup() {
streams.clear();
position = 0.0;
rate = 0.0;
memset(&spec, 0, sizeof(spec));
current_file = "";
current_title = "";
length = 0.0;
if (music != nullptr) {
Mix_HaltMusicStream(music);
Mix_FreeMusic(music);
}
Mix_SetSoundFonts(NULL);
mixer = nullptr;
music = nullptr;
delete file;
file = nullptr;
open = false;
Mix_CloseAudio();
}