2023-04-24 13:45:06 -07:00
|
|
|
#include "playback.h"
|
2023-07-10 12:45:24 -07:00
|
|
|
#include "SDL_mixer.h"
|
|
|
|
#include <SDL_audio.h>
|
2023-07-15 14:52:49 -07:00
|
|
|
#include <SDL.h>
|
2023-07-10 12:45:24 -07:00
|
|
|
#include <exception>
|
2023-04-24 13:45:06 -07:00
|
|
|
#include <thread>
|
2023-09-03 11:54:07 -07:00
|
|
|
#include <cmath>
|
|
|
|
#ifdef __linux__
|
|
|
|
#include <pthread.h>
|
|
|
|
#endif
|
2024-03-23 18:41:26 -07:00
|
|
|
#include "log.hpp"
|
|
|
|
#include <filesystem>
|
2023-04-24 13:45:06 -07:00
|
|
|
using namespace std::chrono;
|
2023-09-03 11:54:07 -07:00
|
|
|
size_t CalculateBufSize(SDL_AudioSpec *obtained, double seconds, double max_seconds, size_t samples_override = 0) {
|
|
|
|
return ((((samples_override == 0) ? obtained->samples : samples_override) * std::min(seconds, max_seconds)) + 1) * sizeof(SAMPLETYPE) * obtained->channels;
|
2023-07-15 14:52:49 -07:00
|
|
|
}
|
2024-03-26 18:39:02 -07:00
|
|
|
void PlaybackInstance::SDLCallbackInner(Uint8 *stream, int len) {
|
2023-09-03 11:54:07 -07:00
|
|
|
SDL_memset((void*)stream, 0, len);
|
|
|
|
if (!playback_ready.load()) {
|
|
|
|
return;
|
|
|
|
}
|
2023-12-22 14:13:48 -08:00
|
|
|
if (st == nullptr) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
size_t i = 0;
|
|
|
|
size_t max = 0;
|
|
|
|
size_t unit = sizeof(SAMPLETYPE) * spec.channels;
|
|
|
|
size_t bytes_per_iter = std::min(((bufsize / unit)) * unit, (size_t)fakespec.size);
|
|
|
|
while (st->numSamples() < (size_t)len) {
|
|
|
|
if (general_mixer == nullptr) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
general_mixer(nullptr, buf + i, (int)bytes_per_iter);
|
|
|
|
i += bytes_per_iter;
|
|
|
|
max = i + bytes_per_iter;
|
|
|
|
if (max >= bufsize) {
|
|
|
|
st->putSamples((SAMPLETYPE*)buf, i/unit);
|
2023-09-03 11:54:07 -07:00
|
|
|
i = 0;
|
2023-12-22 14:13:48 -08:00
|
|
|
max = i + bytes_per_iter;
|
2023-09-03 11:54:07 -07:00
|
|
|
}
|
2023-12-22 14:13:48 -08:00
|
|
|
}
|
2023-09-03 11:54:07 -07:00
|
|
|
st->receiveSamples((SAMPLETYPE*)stream, len / unit);
|
2023-07-15 14:52:49 -07:00
|
|
|
}
|
2024-03-26 18:39:02 -07:00
|
|
|
void PlaybackInstance::SDLCallback(void *userdata, Uint8 *stream, int len) {
|
|
|
|
((PlaybackInstance*)userdata)->SDLCallbackInner(stream, len);
|
2023-07-15 14:52:49 -07:00
|
|
|
}
|
2024-03-26 18:39:02 -07:00
|
|
|
Mix_Music *PlaybackInstance::Load(const char *file) {
|
2024-03-23 18:41:26 -07:00
|
|
|
{
|
|
|
|
std::filesystem::path fpath = std::string(file);
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
}
|
2023-07-10 12:45:24 -07:00
|
|
|
Mix_Music *output = Mix_LoadMUS(file);
|
2023-12-22 14:13:48 -08:00
|
|
|
if (output == nullptr) {
|
2024-03-23 18:41:26 -07:00
|
|
|
ERROR.writefln("Error loading music '%s': %s", file, Mix_GetError());
|
2023-12-22 14:13:48 -08:00
|
|
|
error_mutex.lock();
|
|
|
|
errors.emplace("Error loading music!");
|
|
|
|
error_mutex.unlock();
|
|
|
|
return nullptr;
|
2023-07-10 12:45:24 -07:00
|
|
|
}
|
|
|
|
Mix_PlayMusicStream(output, -1);
|
|
|
|
length = Mix_MusicDuration(output);
|
|
|
|
update.store(true);
|
2024-03-23 18:41:26 -07:00
|
|
|
current_file_mutex.lock();
|
|
|
|
current_file = std::string(file);
|
2024-03-26 18:39:02 -07:00
|
|
|
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.value());
|
|
|
|
current_title = path.stem().string();
|
|
|
|
} else {
|
|
|
|
current_title = std::string(title_tag);
|
|
|
|
}
|
2024-03-23 18:41:26 -07:00
|
|
|
current_file_mutex.unlock();
|
|
|
|
set_signal(PlaybackSignalFileChanged);
|
2023-07-10 12:45:24 -07:00
|
|
|
return output;
|
|
|
|
}
|
2024-03-26 18:39:02 -07:00
|
|
|
void PlaybackInstance::Unload(Mix_Music *music) {
|
2023-07-10 12:45:24 -07:00
|
|
|
Mix_HaltMusicStream(music);
|
|
|
|
Mix_FreeMusic(music);
|
2024-03-23 18:41:26 -07:00
|
|
|
current_file_mutex.lock();
|
|
|
|
current_file = {};
|
2024-03-26 18:39:02 -07:00
|
|
|
current_title = {};
|
2024-03-23 18:41:26 -07:00
|
|
|
current_file_mutex.unlock();
|
2023-07-10 12:45:24 -07:00
|
|
|
}
|
2024-03-26 18:39:02 -07:00
|
|
|
void PlaybackInstance::UpdateST() {
|
2024-03-23 18:41:26 -07:00
|
|
|
if (speed > 0.0f && speed_changed.exchange(false)) {
|
2023-09-03 11:54:07 -07:00
|
|
|
st->setRate(speed);
|
2024-03-23 18:41:26 -07:00
|
|
|
set_signal(PlaybackSignalSpeedChanged);
|
2023-07-15 14:52:49 -07:00
|
|
|
}
|
2024-03-23 18:41:26 -07:00
|
|
|
if (tempo > 0.0f && tempo_changed.exchange(false)) {
|
2023-09-03 11:54:07 -07:00
|
|
|
st->setTempo(tempo);
|
2024-03-23 18:41:26 -07:00
|
|
|
set_signal(PlaybackSignalTempoChanged);
|
2023-07-15 14:52:49 -07:00
|
|
|
}
|
2024-03-23 18:41:26 -07:00
|
|
|
if (pitch > 0.0f && pitch_changed.exchange(false)) {
|
2023-09-03 11:54:07 -07:00
|
|
|
st->setPitch(pitch);
|
2024-03-23 18:41:26 -07:00
|
|
|
set_signal(PlaybackSignalPitchChanged);
|
2023-09-03 11:54:07 -07:00
|
|
|
}
|
|
|
|
}
|
2024-03-26 18:39:02 -07:00
|
|
|
double PlaybackInstance::GetMaxSeconds() {
|
2023-12-22 14:13:48 -08:00
|
|
|
return std::max((double)(MaxSpeed * MaxTempo), st->getInputOutputSampleRatio());
|
2023-09-03 11:54:07 -07:00
|
|
|
}
|
2024-03-26 18:39:02 -07:00
|
|
|
void PlaybackInstance::ThreadFunc() {
|
2023-09-03 11:54:07 -07:00
|
|
|
#ifdef __linux__
|
|
|
|
pthread_setname_np(pthread_self(), "Playback control thread");
|
|
|
|
#endif
|
|
|
|
bool reload = false;
|
2024-03-23 18:41:26 -07:00
|
|
|
speed_changed.store(true);
|
|
|
|
tempo_changed.store(true);
|
|
|
|
pitch_changed.store(true);
|
2023-04-24 13:45:06 -07:00
|
|
|
while (running) {
|
2023-09-03 11:54:07 -07:00
|
|
|
playback_ready.store(false);
|
|
|
|
if (!SDL_WasInit(SDL_INIT_AUDIO)) {
|
|
|
|
if (SDL_InitSubSystem(SDL_INIT_AUDIO) < 0) {
|
2024-03-23 18:41:26 -07:00
|
|
|
ERROR.writefln("Error initializing SDL: '%s'", SDL_GetError());
|
2023-12-22 14:13:48 -08:00
|
|
|
error_mutex.lock();
|
|
|
|
errors.emplace("Failed to initialize SDL!");
|
|
|
|
error_mutex.unlock();
|
|
|
|
return;
|
2023-04-24 13:45:06 -07:00
|
|
|
}
|
2023-09-03 11:54:07 -07:00
|
|
|
}
|
|
|
|
SDL_AudioSpec obtained;
|
|
|
|
SDL_AudioSpec desired;
|
|
|
|
desired.format =
|
|
|
|
#ifdef SOUNDTOUCH_INTEGER_SAMPLES
|
|
|
|
AUDIO_S16SYS;
|
|
|
|
#else
|
|
|
|
AUDIO_F32SYS;
|
|
|
|
#endif
|
|
|
|
desired.freq = 48000;
|
|
|
|
desired.samples = 1024;
|
|
|
|
desired.channels = 2;
|
2024-03-26 18:39:02 -07:00
|
|
|
desired.callback = PlaybackInstance::SDLCallback;
|
2023-09-03 11:54:07 -07:00
|
|
|
desired.userdata = this;
|
|
|
|
st = new SoundTouch();
|
|
|
|
Mix_Init(MIX_INIT_FLAC|MIX_INIT_MID|MIX_INIT_MOD|MIX_INIT_MP3|MIX_INIT_OGG|MIX_INIT_OPUS|MIX_INIT_WAVPACK);
|
2023-12-22 14:13:48 -08:00
|
|
|
if ((device = SDL_OpenAudioDevice(nullptr, 0, &desired, &obtained, SDL_AUDIO_ALLOW_CHANNELS_CHANGE|SDL_AUDIO_ALLOW_FREQUENCY_CHANGE|SDL_AUDIO_ALLOW_SAMPLES_CHANGE)) == 0) {
|
2024-03-23 18:41:26 -07:00
|
|
|
ERROR.writefln("Error opening audio device: '%s'", SDL_GetError());
|
2023-12-22 14:13:48 -08:00
|
|
|
error_mutex.lock();
|
|
|
|
errors.emplace("Failed to open audio device!");
|
|
|
|
error_mutex.unlock();
|
|
|
|
running = false;
|
|
|
|
break;
|
2023-09-03 11:54:07 -07:00
|
|
|
}
|
|
|
|
spec = obtained;
|
|
|
|
st->setSampleRate(spec.freq);
|
|
|
|
st->setChannels(spec.channels);
|
|
|
|
UpdateST();
|
|
|
|
bufsize = 0;
|
2023-10-16 10:44:25 -07:00
|
|
|
fakespec = spec;
|
|
|
|
double maxSeconds = GetMaxSeconds();
|
|
|
|
fakespec.size *= maxSeconds;
|
|
|
|
fakespec.samples *= maxSeconds;
|
2023-09-03 11:54:07 -07:00
|
|
|
size_t new_bufsize = CalculateBufSize(&spec, GetMaxSeconds(), MaxSeconds);
|
|
|
|
buf = (Uint8*)malloc(new_bufsize);
|
2023-12-22 14:13:48 -08:00
|
|
|
if (buf == nullptr) {
|
2024-03-23 18:41:26 -07:00
|
|
|
ERROR.writeln("Failed to allocate memory for playback!");
|
2023-12-22 14:13:48 -08:00
|
|
|
error_mutex.lock();
|
|
|
|
errors.emplace("Failed to allocate memory for playback!");
|
|
|
|
error_mutex.unlock();
|
2024-03-23 18:41:26 -07:00
|
|
|
set_signal(PlaybackSignalErrorOccurred);
|
2023-12-22 14:13:48 -08:00
|
|
|
running = false;
|
|
|
|
break;
|
2023-09-03 11:54:07 -07:00
|
|
|
}
|
|
|
|
bufsize = new_bufsize;
|
|
|
|
general_mixer = Mix_GetGeneralMixer();
|
2023-12-22 14:13:48 -08:00
|
|
|
Mix_InitMixer(&fakespec, SDL_FALSE);
|
2023-09-03 11:54:07 -07:00
|
|
|
SDL_PauseAudioDevice(device, 0);
|
|
|
|
Mix_Music *music = Load(filePath.c_str());
|
|
|
|
reload = false;
|
2023-12-22 14:13:48 -08:00
|
|
|
if (music) {
|
|
|
|
playback_ready.store(true);
|
|
|
|
} else {
|
|
|
|
playback_ready.store(false);
|
|
|
|
}
|
2024-03-23 18:41:26 -07:00
|
|
|
set_signal(PlaybackSignalStarted);
|
2023-12-22 14:13:48 -08:00
|
|
|
while (running) {
|
2023-09-03 11:54:07 -07:00
|
|
|
if (file_changed.exchange(false)) {
|
|
|
|
Unload(music);
|
|
|
|
music = Load(filePath.c_str());
|
2023-12-22 14:13:48 -08:00
|
|
|
if (music) {
|
|
|
|
playback_ready.store(true);
|
|
|
|
} else {
|
|
|
|
playback_ready.store(false);
|
|
|
|
}
|
2023-04-24 13:45:06 -07:00
|
|
|
}
|
2023-09-03 11:54:07 -07:00
|
|
|
if (flag_mutex.try_lock()) {
|
|
|
|
if (seeking.exchange(false)) {
|
|
|
|
Mix_SetMusicPositionStream(music, position);
|
2024-03-23 18:41:26 -07:00
|
|
|
set_signal(PlaybackSignalSeeked);
|
2023-07-15 14:52:49 -07:00
|
|
|
}
|
2024-03-23 18:41:26 -07:00
|
|
|
if (pause_changed.exchange(false)) {
|
|
|
|
if (paused) {
|
|
|
|
Mix_PauseMusicStream(music);
|
|
|
|
set_signal(PlaybackSignalPaused);
|
|
|
|
} else {
|
|
|
|
Mix_ResumeMusicStream(music);
|
|
|
|
set_signal(PlaybackSignalResumed);
|
|
|
|
}
|
2023-07-15 14:52:49 -07:00
|
|
|
}
|
2023-09-03 11:54:07 -07:00
|
|
|
if (update.exchange(false)) {
|
2023-12-22 14:13:48 -08:00
|
|
|
Mix_VolumeMusicStream(music, (int)(volume / 100.0 * MIX_MAX_VOLUME));
|
2023-09-03 11:54:07 -07:00
|
|
|
SDL_LockAudioDevice(device);
|
|
|
|
UpdateST();
|
|
|
|
size_t correct_buf_size = CalculateBufSize(&spec, GetMaxSeconds(), MaxSeconds);
|
|
|
|
size_t max_buf_size = correct_buf_size * 10;
|
|
|
|
bool too_large = max_buf_size < bufsize;
|
|
|
|
bool too_small = correct_buf_size > bufsize;
|
|
|
|
if (too_large) {
|
2024-03-23 18:41:26 -07:00
|
|
|
ERROR.writes("Bufsize is too large - ");
|
2023-09-03 11:54:07 -07:00
|
|
|
} else if (too_small) {
|
2024-03-23 18:41:26 -07:00
|
|
|
ERROR.writes("Bufsize is too small - ");
|
2023-09-03 11:54:07 -07:00
|
|
|
}
|
|
|
|
if (too_large || too_small) {
|
2024-03-23 18:41:26 -07:00
|
|
|
ERROR.writeln("Resizing buffer...");
|
2023-09-03 11:54:07 -07:00
|
|
|
general_mixer = nullptr;
|
|
|
|
bufsize = 0;
|
|
|
|
buf = (Uint8*)realloc((void*)buf, correct_buf_size);
|
2023-12-22 14:13:48 -08:00
|
|
|
if (buf == nullptr) {
|
2024-03-23 18:41:26 -07:00
|
|
|
ERROR.writes("Failed to allocate memory for playback!");
|
2023-12-22 14:13:48 -08:00
|
|
|
error_mutex.lock();
|
|
|
|
errors.emplace("Failed to allocate memory for playback!");
|
|
|
|
error_mutex.unlock();
|
2024-03-23 18:41:26 -07:00
|
|
|
set_signal(PlaybackSignalErrorOccurred);
|
2023-12-22 14:13:48 -08:00
|
|
|
running = false;
|
2023-09-03 11:54:07 -07:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
bufsize = correct_buf_size;
|
|
|
|
}
|
|
|
|
SDL_UnlockAudioDevice(device);
|
2023-07-15 14:52:49 -07:00
|
|
|
}
|
2023-09-03 11:54:07 -07:00
|
|
|
flag_mutex.unlock();
|
2023-04-24 13:45:06 -07:00
|
|
|
}
|
2023-09-03 11:54:07 -07:00
|
|
|
position = Mix_GetMusicPosition(music);
|
|
|
|
std::this_thread::sleep_for(20ms);
|
2023-04-24 13:45:06 -07:00
|
|
|
}
|
2023-09-03 11:54:07 -07:00
|
|
|
playback_ready.store(false);
|
|
|
|
// ====
|
|
|
|
Unload(music);
|
|
|
|
SDL_CloseAudioDevice(device);
|
|
|
|
Mix_CloseAudio();
|
|
|
|
Mix_Quit();
|
|
|
|
SDL_QuitSubSystem(SDL_INIT_AUDIO);
|
|
|
|
delete st;
|
|
|
|
free(buf);
|
2023-04-24 13:45:06 -07:00
|
|
|
}
|
2024-03-23 18:41:26 -07:00
|
|
|
current_file_mutex.lock();
|
|
|
|
current_file = {};
|
|
|
|
current_file_mutex.unlock();
|
|
|
|
set_signal(PlaybackSignalStopped);
|
2023-04-24 13:45:06 -07:00
|
|
|
}
|
|
|
|
|
2024-03-26 18:39:02 -07:00
|
|
|
PlaybackInstance::PlaybackInstance() {
|
2023-04-24 13:45:06 -07:00
|
|
|
running = false;
|
|
|
|
paused = true;
|
|
|
|
position = 0;
|
|
|
|
length = 0;
|
|
|
|
volume = 100.0;
|
|
|
|
speed = 1.0;
|
2023-07-15 14:52:49 -07:00
|
|
|
pitch = 1.0;
|
|
|
|
tempo = 1.0;
|
2024-03-23 18:41:26 -07:00
|
|
|
tempo_changed.store(true);
|
|
|
|
speed_changed.store(true);
|
|
|
|
pitch_changed.store(true);
|
|
|
|
current_file = {};
|
|
|
|
playback_ready = false;
|
|
|
|
bufsize = 0;
|
|
|
|
}
|
2024-03-26 18:39:02 -07:00
|
|
|
std::optional<std::string> PlaybackInstance::get_current_file() {
|
2024-03-23 18:41:26 -07:00
|
|
|
current_file_mutex.lock();
|
|
|
|
std::optional<std::string> output = current_file;
|
|
|
|
current_file_mutex.unlock();
|
|
|
|
return output;
|
2023-04-24 13:45:06 -07:00
|
|
|
}
|
2024-03-26 18:39:02 -07:00
|
|
|
std::optional<std::string> PlaybackInstance::get_current_title() {
|
|
|
|
current_file_mutex.lock();
|
|
|
|
std::optional<std::string> output = current_title;
|
|
|
|
current_file_mutex.unlock();
|
|
|
|
return output;
|
|
|
|
}
|
|
|
|
PlaybackInstance::~PlaybackInstance() {
|
2023-04-24 13:45:06 -07:00
|
|
|
Stop();
|
|
|
|
}
|
|
|
|
|
2024-03-26 18:39:02 -07:00
|
|
|
void PlaybackInstance::Start(std::string filePath) {
|
2023-04-24 13:45:06 -07:00
|
|
|
this->filePath = filePath;
|
2024-03-23 18:41:26 -07:00
|
|
|
INFO.writefln("Playing %s...", filePath.c_str());
|
2023-04-24 13:45:06 -07:00
|
|
|
flag_mutex.lock();
|
2023-09-03 11:54:07 -07:00
|
|
|
this->position = 0.0;
|
|
|
|
seeking.store(true);
|
2023-04-24 13:45:06 -07:00
|
|
|
paused = false;
|
|
|
|
Update();
|
|
|
|
if (running.exchange(true)) {
|
|
|
|
file_changed.store(true);
|
|
|
|
} else {
|
2024-03-26 18:39:02 -07:00
|
|
|
thread = std::thread(&PlaybackInstance::ThreadFunc, this);
|
2023-04-24 13:45:06 -07:00
|
|
|
}
|
|
|
|
flag_mutex.unlock();
|
|
|
|
}
|
2024-03-26 18:39:02 -07:00
|
|
|
double PlaybackInstance::GetPosition() {
|
2023-04-24 13:45:06 -07:00
|
|
|
return position;
|
|
|
|
}
|
2024-03-26 18:39:02 -07:00
|
|
|
double PlaybackInstance::GetLength() {
|
2023-04-24 13:45:06 -07:00
|
|
|
return length;
|
|
|
|
}
|
|
|
|
|
2024-03-26 18:39:02 -07:00
|
|
|
void PlaybackInstance::Seek(double position) {
|
2023-04-24 13:45:06 -07:00
|
|
|
flag_mutex.lock();
|
|
|
|
this->position = position;
|
|
|
|
seeking.store(true);
|
|
|
|
flag_mutex.unlock();
|
|
|
|
}
|
|
|
|
|
2024-03-26 18:39:02 -07:00
|
|
|
void PlaybackInstance::Pause() {
|
2023-04-24 13:45:06 -07:00
|
|
|
flag_mutex.lock();
|
|
|
|
paused = !paused;
|
2024-03-23 18:41:26 -07:00
|
|
|
pause_changed.store(true);
|
2023-04-24 13:45:06 -07:00
|
|
|
flag_mutex.unlock();
|
|
|
|
}
|
|
|
|
|
2024-03-26 18:39:02 -07:00
|
|
|
bool PlaybackInstance::IsPaused() {
|
2023-04-24 13:45:06 -07:00
|
|
|
return paused;
|
|
|
|
}
|
|
|
|
|
2024-03-26 18:39:02 -07:00
|
|
|
void PlaybackInstance::Stop() {
|
2023-04-24 13:45:06 -07:00
|
|
|
if (running.exchange(false)) {
|
|
|
|
thread.join();
|
|
|
|
}
|
|
|
|
}
|
2024-03-26 18:39:02 -07:00
|
|
|
void PlaybackInstance::Update() {
|
2024-03-23 18:41:26 -07:00
|
|
|
if (prev_pitch != pitch) {
|
|
|
|
pitch_changed.store(true);
|
|
|
|
}
|
|
|
|
if (prev_speed != speed) {
|
|
|
|
speed_changed.store(true);
|
|
|
|
}
|
|
|
|
if (prev_tempo != tempo) {
|
|
|
|
tempo_changed.store(true);
|
|
|
|
}
|
2023-04-24 13:45:06 -07:00
|
|
|
update.store(true);
|
|
|
|
}
|
|
|
|
|
2024-03-26 18:39:02 -07:00
|
|
|
bool PlaybackInstance::IsStopped() {
|
2023-04-24 13:45:06 -07:00
|
|
|
return !running;
|
|
|
|
}
|
2024-03-26 18:39:02 -07:00
|
|
|
optional<std::string> PlaybackInstance::GetError() {
|
2023-12-22 14:13:48 -08:00
|
|
|
if (ErrorExists()) {
|
|
|
|
error_mutex.lock();
|
|
|
|
std::string error = errors.back();
|
|
|
|
errors.pop();
|
|
|
|
error_mutex.unlock();
|
|
|
|
return error;
|
|
|
|
} else {
|
|
|
|
return {};
|
|
|
|
}
|
|
|
|
}
|
2024-03-26 18:39:02 -07:00
|
|
|
bool PlaybackInstance::ErrorExists() {
|
2023-12-22 14:13:48 -08:00
|
|
|
error_mutex.lock();
|
|
|
|
bool output = !errors.empty();
|
|
|
|
error_mutex.unlock();
|
|
|
|
return output;
|
2024-03-23 18:41:26 -07:00
|
|
|
}
|
2024-03-26 18:39:02 -07:00
|
|
|
void PlaybackInstance::set_signal(uint16_t signal) {
|
2024-03-23 18:41:26 -07:00
|
|
|
signal_mutex.lock();
|
|
|
|
signals_occurred |= signal;
|
|
|
|
signal_mutex.unlock();
|
|
|
|
}
|
2024-03-26 18:39:02 -07:00
|
|
|
uint16_t PlaybackInstance::handle_signals(uint16_t signals) {
|
2024-03-23 18:41:26 -07:00
|
|
|
if (signal_mutex.try_lock()) {
|
|
|
|
uint16_t output = signals_occurred & signals;
|
|
|
|
signals_occurred &= ~output;
|
|
|
|
signal_mutex.unlock();
|
|
|
|
return output;
|
|
|
|
} else {
|
|
|
|
return PlaybackSignalNone;
|
|
|
|
}
|
|
|
|
}
|
2024-03-26 18:39:02 -07:00
|
|
|
void PlaybackInstance::SetTempo(float tempo) {
|
|
|
|
this->tempo = tempo;
|
|
|
|
Update();
|
|
|
|
}
|
|
|
|
void PlaybackInstance::SetPitch(float pitch) {
|
|
|
|
this->pitch = pitch;
|
|
|
|
Update();
|
|
|
|
}
|
|
|
|
void PlaybackInstance::SetSpeed(float speed) {
|
|
|
|
this->speed = speed;
|
|
|
|
Update();
|
|
|
|
}
|
|
|
|
void PlaybackInstance::SetVolume(float volume) {
|
|
|
|
this->volume = volume;
|
|
|
|
Update();
|
|
|
|
}
|
|
|
|
float PlaybackInstance::GetTempo() {
|
|
|
|
return tempo;
|
|
|
|
}
|
|
|
|
float PlaybackInstance::GetPitch() {
|
|
|
|
return pitch;
|
|
|
|
}
|
|
|
|
float PlaybackInstance::GetSpeed() {
|
|
|
|
return speed;
|
|
|
|
}
|
|
|
|
float PlaybackInstance::GetVolume() {
|
|
|
|
return volume;
|
|
|
|
}
|