looper/playback.cpp

664 lines
20 KiB
C++
Raw Normal View History

2023-04-24 13:45:06 -07:00
#include "playback.h"
#include "SDL_mixer.h"
2024-09-16 15:05:53 -07:00
#include "playback_backend.hpp"
#include <SDL_audio.h>
2024-04-10 18:00:19 -07:00
extern "C" {
#include <vgmstream.h>
#include <util.h>
#include <plugins.h>
}
#include <climits>
#include <SDL.h>
#include <exception>
2023-04-24 13:45:06 -07:00
#include <thread>
#include <cmath>
#ifdef __linux__
#include <pthread.h>
#endif
#include "log.hpp"
#include <filesystem>
2024-04-09 10:15:05 -07:00
#include "dbus.hpp"
2024-04-25 11:23:38 -07:00
#include <string.h>
2024-04-24 09:59:51 -07:00
#include "util.hpp"
2024-08-08 13:12:37 -07:00
#include "file_backend.hpp"
2023-04-24 13:45:06 -07:00
using namespace std::chrono;
2024-04-10 18:00:19 -07:00
int NotSDL_ConvertAudioSamples(const SDL_AudioSpec *src_spec, const Uint8 *src_data, int src_len,
const SDL_AudioSpec *dst_spec, Uint8 **dst_data, int *dst_len)
{
if (dst_data) {
*dst_data = NULL;
}
if (dst_len) {
*dst_len = 0;
}
if (!src_data) {
return 0;
} else if (src_len < 0) {
return 0;
} else if (!dst_data) {
return 0;
} else if (!dst_len) {
return 0;
}
int retval = -1;
Uint8 *dst = NULL;
int dstlen = 0;
SDL_AudioStream *stream = SDL_NewAudioStream(src_spec->format, src_spec->channels, src_spec->freq, dst_spec->format, dst_spec->channels, dst_spec->freq);
if (stream) {
if ((SDL_AudioStreamPut(stream, src_data, src_len) == 0) && (SDL_AudioStreamFlush(stream) == 0)) {
dstlen = SDL_AudioStreamAvailable(stream);
if (dstlen >= 0) {
dst = (Uint8 *)SDL_malloc(dstlen);
if (dst) {
retval = (SDL_AudioStreamGet(stream, dst, dstlen) >= 0) ? 0 : -1;
}
}
}
}
if (retval == -1) {
SDL_free(dst);
} else {
*dst_data = dst;
*dst_len = dstlen;
}
SDL_FreeAudioStream(stream);
return retval;
}
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;
}
2024-03-26 18:39:02 -07:00
void PlaybackInstance::SDLCallbackInner(Uint8 *stream, int len) {
SDL_memset((void*)stream, 0, len);
if (!playback_ready.load()) {
return;
}
if (st == nullptr) {
return;
}
size_t unit = sizeof(SAMPLETYPE) * spec.channels;
2024-09-16 15:05:53 -07:00
size_t bytes_per_iter = (bufsize / unit) * unit;
while (st->numSamples() < (size_t)len) {
2024-08-08 13:12:37 -07:00
if (process == nullptr) {
return;
}
2024-04-10 18:00:19 -07:00
Uint8 *new_buf;
int new_bufsize;
new_buf = buf;
2024-08-08 13:12:37 -07:00
bytes_per_iter = backend_spec.size;
2024-04-10 18:00:19 -07:00
new_bufsize = bytes_per_iter;
2024-08-08 13:12:37 -07:00
if (this->process != nullptr) {
size_t new_bytes = this->process->render(buf, bytes_per_iter);
if (SDL_AudioStreamPut(sdl_stream, buf, new_bytes) < 0) {
ERROR.writefln("SDL_AudioStreamPut: %s", SDL_GetError());
DEBUG.writefln("Current audio backend: %s", process->get_backend_id().c_str());
DEBUG.writefln("SDL_AudioStream *sdl_stream = %0lx", (size_t)sdl_stream);
return;
}
2024-04-10 18:00:19 -07:00
new_bufsize = SDL_AudioStreamGet(sdl_stream, buf, bufsize);
2024-08-08 13:12:37 -07:00
if (new_bufsize < 0) {
ERROR.writefln("SDL_AudioStreamGet: %s", SDL_GetError());
DEBUG.writefln("Current audio backend: %s", process->get_backend_id().c_str());
return;
2024-04-10 18:32:41 -07:00
}
2024-08-08 13:12:37 -07:00
SAMPLETYPE *sbuf = (SAMPLETYPE*)buf;
for (size_t i = 0; i < (new_bufsize / unit) * spec.channels; i++) {
sbuf[i] *= real_volume;
2024-04-10 18:00:19 -07:00
}
2024-08-08 13:12:37 -07:00
st->putSamples(sbuf, new_bufsize / unit);
2024-09-16 15:05:53 -07:00
} else {
return;
}
}
st->receiveSamples((SAMPLETYPE*)stream, len / unit);
}
#ifdef __ANDROID__
oboe::DataCallbackResult PlaybackInstance::onAudioReady(
oboe::AudioStream *audioStream,
void *audioData,
int32_t numFrames) {
SDLCallbackInner((Uint8*)audioData, numFrames * audioStream->getBytesPerFrame());
return oboe::DataCallbackResult::Continue;
}
#endif
2024-03-26 18:39:02 -07:00
void PlaybackInstance::SDLCallback(void *userdata, Uint8 *stream, int len) {
((PlaybackInstance*)userdata)->SDLCallbackInner(stream, len);
}
2024-08-08 13:12:37 -07:00
void PlaybackInstance::Load(const char *file, int idx) {
2024-04-10 18:00:19 -07:00
SDL_LockAudioDevice(device);
2024-08-08 13:12:37 -07:00
playback_ready.store(false);
if (process != nullptr) delete process;
process = new PlaybackProcess(file, idx);
if (process->process_running()) {
auto backend_spec_proxy = process->get_audio_spec();
memset(&backend_spec, 0, sizeof(backend_spec));
backend_spec.channels = backend_spec_proxy->channel_count();
audio_data_t sample_fmt;
sample_fmt.size = backend_spec_proxy->bits() / 8;
sample_fmt.endian = backend_spec_proxy->endian() == EndianID::BIG;
sample_fmt.is_float = backend_spec_proxy->format_type() == FormatType::FLOAT;
sample_fmt.is_signed = backend_spec_proxy->format_type() != FormatType::UNSIGNED;
backend_spec.format = sample_spec_to_sdl(sample_fmt);
backend_spec.freq = backend_spec_proxy->sample_rate();
backend_spec.samples = backend_spec_proxy->has_max_samples() ? backend_spec_proxy->max_samples() : backend_spec_proxy->has_min_samples() ? backend_spec_proxy->min_samples() : 0;
if (backend_spec.samples == 0) backend_spec.samples = 100;
DEBUG.writeln("Backend audio specification:");
DEBUG.writefln("\tFormat: %s", sdl_to_str(backend_spec.format).c_str());
DEBUG.writefln("\tChannels: %d", backend_spec.channels);
DEBUG.writefln("\tSample rate: %d", backend_spec.freq);
DEBUG.writefln("\tSamples: %d", backend_spec.samples);
if (sdl_stream != nullptr) {
SDL_FreeAudioStream(sdl_stream);
2024-04-13 12:56:39 -07:00
}
2024-08-08 13:12:37 -07:00
sdl_stream = SDL_NewAudioStream(backend_spec.format, backend_spec.channels, backend_spec.freq, spec.format, spec.channels, spec.freq);
if (sdl_stream == nullptr) {
ERROR.writefln("SDL_NewAudioStream: %s", SDL_GetError());
2024-09-16 15:05:53 -07:00
DEBUG.writefln("format: AUDIO_%s%d%s", sample_fmt.is_float ? "F" : sample_fmt.is_signed ? "S" : "U", sample_fmt.size * 8, sample_fmt.endian ? "MSB" : "LSB");
2024-08-08 13:12:37 -07:00
set_error("Failed to create SDL audio stream");
set_signal(PlaybackSignalErrorOccurred);
2024-04-13 12:56:39 -07:00
}
2024-08-08 13:12:37 -07:00
size_t required_size = sample_fmt.size * backend_spec.samples;
st->setChannels(spec.channels);
st->setRate(spec.freq);
st->flush();
backend_spec.size = required_size;
bufsize = backend_spec.size;
buf = (Uint8*)malloc(bufsize);
if (buf == nullptr) {
ERROR.writeln("Failed to allocate memory for playback!");
set_error("Failed to allocate memory for playback!");
set_signal(PlaybackSignalErrorOccurred);
bufsize = 0;
2024-04-24 09:59:51 -07:00
}
2024-08-08 13:12:37 -07:00
delete backend_spec_proxy;
2024-09-16 15:05:53 -07:00
playback_ready.store(true);
2024-04-10 18:00:19 -07:00
} else {
2024-08-08 13:12:37 -07:00
ERROR.writeln("Failed to detect valid playback backend for file!");
set_error("Failed to detect valid backend for file.");
set_signal(PlaybackSignalErrorOccurred);
delete process;
process = nullptr;
2024-04-10 18:00:19 -07:00
}
SDL_UnlockAudioDevice(device);
}
2024-08-08 13:12:37 -07:00
void PlaybackInstance::Unload() {
if (process == nullptr) return;
2024-04-10 18:00:19 -07:00
SDL_LockAudioDevice(device);
2024-08-08 13:12:37 -07:00
delete process;
process = nullptr;
2024-04-10 18:00:19 -07:00
SDL_FreeAudioStream(sdl_stream);
sdl_stream = nullptr;
2024-08-08 13:12:37 -07:00
free(buf);
2024-04-10 18:00:19 -07:00
SDL_UnlockAudioDevice(device);
}
2024-03-26 18:39:02 -07:00
void PlaybackInstance::UpdateST() {
if (speed > 0.0f && speed_changed.exchange(false)) {
st->setRate(speed);
set_signal(PlaybackSignalSpeedChanged);
}
if (tempo > 0.0f && tempo_changed.exchange(false)) {
st->setTempo(tempo);
set_signal(PlaybackSignalTempoChanged);
}
if (pitch > 0.0f && pitch_changed.exchange(false)) {
st->setPitch(pitch);
set_signal(PlaybackSignalPitchChanged);
}
}
2024-03-26 18:39:02 -07:00
double PlaybackInstance::GetMaxSeconds() {
return std::max((double)(MaxSpeed * MaxTempo), st->getInputOutputSampleRatio());
}
2024-04-24 09:59:51 -07:00
void PlaybackInstance::InitLoopFunction() {
bool reload = false;
2024-08-08 13:12:37 -07:00
init_audio_data();
speed_changed.store(true);
tempo_changed.store(true);
pitch_changed.store(true);
2024-04-24 09:59:51 -07:00
playback_ready.store(false);
if (!SDL_WasInit(SDL_INIT_AUDIO)) {
if (SDL_InitSubSystem(SDL_INIT_AUDIO) < 0) {
ERROR.writefln("Error initializing SDL: '%s'", SDL_GetError());
set_error("Failed to initialize SDL!");
return;
}
}
SDL_AudioSpec obtained;
SDL_AudioSpec desired;
2024-08-08 13:12:37 -07:00
desired.format =
2024-04-24 09:59:51 -07:00
#ifdef SOUNDTOUCH_INTEGER_SAMPLES
AUDIO_S16SYS;
#else
AUDIO_F32SYS;
#endif
desired.freq = 48000;
2024-09-16 15:05:53 -07:00
desired.samples = 100;
2024-04-24 09:59:51 -07:00
desired.channels = 2;
desired.callback = PlaybackInstance::SDLCallback;
desired.userdata = this;
st = new SoundTouch();
#ifndef __ANDROID__
2024-04-24 09:59:51 -07: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) {
ERROR.writefln("Error opening audio device: '%s'", SDL_GetError());
set_error("Failed to open audio device!");
running = false;
loop_started = false;
return;
}
#else
oboe::AudioStreamBuilder builder;
builder.setDirection(oboe::Direction::Output);
builder.setSharingMode(oboe::SharingMode::Shared);
builder.setPerformanceMode(oboe::PerformanceMode::None);
builder.setFormat(
#ifdef SOUNDTOUCH_INTEGER_SAMPLES
oboe::AudioFormat::I16
#else
oboe::AudioFormat::Float
#endif
);
builder.setDataCallback(this);
auto res = builder.openStream(ostream);
if (res != oboe::Result::OK) {
ERROR.writefln("Error opening audio device.");
set_error("Failed to open audio device!");
running = false;
loop_started = false;
return;
}
obtained = desired;
obtained.channels = ostream->getChannelCount();
obtained.freq = ostream->getSampleRate();
auto bufferFrames = ostream->getBufferSizeInFrames();
auto bytesPerFrame = ostream->getBytesPerFrame();
auto bytesPerSample = ostream->getBytesPerSample();
obtained.size = bufferFrames * bytesPerFrame;
obtained.samples = obtained.size / bytesPerSample;
oboe::AudioFormat format = ostream->getFormat();
DEBUG.writefln("About to start audio stream.\n\
Format: %s\n\
Channel count: %u\n\
Sample rate: %u\n\
Buffer size (frames): %u\n\
Bytes per frame: %u\n\
Total bytes: %u\n\
Bytes per sample: %u\n\
Total samples: %u"
, oboe::convertToText(format)
, obtained.channels
, obtained.freq
, bufferFrames
, bytesPerFrame
, obtained.size
, bytesPerSample
, obtained.samples
);
ostream->requestStart();
#endif
2024-04-24 09:59:51 -07:00
spec = obtained;
st->setSampleRate(spec.freq);
st->setChannels(spec.channels);
UpdateST();
SDL_PauseAudioDevice(device, 0);
2024-08-08 13:12:37 -07:00
Load(filePath.c_str(), 0);
2024-04-24 09:59:51 -07:00
reload = false;
2024-08-08 13:12:37 -07:00
if (process && process->process_running()) {
2024-04-24 09:59:51 -07:00
playback_ready.store(true);
} else {
playback_ready.store(false);
2024-04-24 09:59:51 -07:00
}
load_finished.store(true);
2024-04-24 09:59:51 -07:00
set_signal(PlaybackSignalStarted);
}
void PlaybackInstance::LoopFunction() {
2024-08-08 13:12:37 -07:00
2024-04-24 09:59:51 -07:00
if (file_changed.exchange(false) || load_requested.exchange(false)) {
2024-08-08 13:12:37 -07:00
Unload();
Load(filePath.c_str(), 0);
if (process && process->process_running()) {
2024-04-24 09:59:51 -07:00
playback_ready.store(true);
} else {
playback_ready.store(false);
}
2024-04-24 09:59:51 -07:00
}
if (stream_changed.exchange(false)) {
current_file_mutex.lock();
if (current_file.has_value()) {
std::string file = current_file.value();
if (current_stream >= streams.size() || current_stream < 0 ||
streams[current_stream].name == "" || streams[current_stream].length <= 0) {
if (stream != nullptr) {
current_stream = stream->stream_index;
} else {
current_stream = 0;
}
2024-04-24 09:59:51 -07:00
} else {
2024-08-08 13:12:37 -07:00
SDL_LockAudioDevice(device);
if (process && process->process_running()) process->set_stream_idx(current_stream);
SDL_UnlockAudioDevice(device);
2024-04-24 09:59:51 -07:00
}
2024-08-08 13:12:37 -07:00
if (process && process->process_running()) {
playback_ready.store(true);
} else {
playback_ready.store(false);
2024-04-24 09:59:51 -07:00
}
2024-04-10 18:00:19 -07:00
}
current_file_mutex.unlock();
2024-04-24 09:59:51 -07:00
}
if (flag_mutex.try_lock()) {
if (seeking.exchange(false)) {
2024-08-08 13:12:37 -07:00
if (process && process->process_running()) process->set_position(position);
2024-04-24 09:59:51 -07:00
set_signal(PlaybackSignalSeeked);
}
if (pause_changed.exchange(false)) {
2024-08-08 13:12:37 -07:00
SDL_PauseAudioDevice(device, paused ? 1 : 0);
2024-04-24 09:59:51 -07:00
if (paused) {
set_signal(PlaybackSignalPaused);
} else {
set_signal(PlaybackSignalResumed);
2024-04-13 12:56:39 -07:00
}
2024-04-24 09:59:51 -07:00
}
if (update.exchange(false)) {
SDL_LockAudioDevice(device);
real_volume = volume / 100.0;
UpdateST();
SDL_UnlockAudioDevice(device);
2023-04-24 13:45:06 -07:00
}
2024-04-24 09:59:51 -07:00
flag_mutex.unlock();
2023-04-24 13:45:06 -07:00
}
2024-08-08 13:12:37 -07:00
2024-04-24 09:59:51 -07:00
}
void PlaybackInstance::DeinitLoopFunction() {
playback_ready.store(false);
// ====
2024-08-08 13:12:37 -07:00
Unload();
#ifndef __ANDROID__
2024-04-24 09:59:51 -07:00
SDL_CloseAudioDevice(device);
#else
if (ostream && ostream->getState() != oboe::StreamState::Closed) {
ostream->stop();
ostream->close();
}
ostream.reset();
#endif
2024-04-24 09:59:51 -07:00
SDL_QuitSubSystem(SDL_INIT_AUDIO);
delete st;
free(buf);
current_file_mutex.lock();
current_file = {};
current_file_mutex.unlock();
set_signal(PlaybackSignalStopped);
2023-04-24 13:45:06 -07:00
}
2024-04-24 09:59:51 -07:00
void PlaybackInstance::ThreadFunc() {
#ifdef __linux__
pthread_setname_np(pthread_self(), "Playback control thread");
#endif
start_loop();
while (running && loop_started) {
LoopHook();
std::this_thread::sleep_for(20ms);
}
stop_loop();
}
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;
pitch = 1.0;
tempo = 1.0;
2024-08-08 13:12:37 -07:00
prev_speed = -1.0;
prev_pitch = -1.0;
prev_tempo = -1.0;
tempo_changed.store(true);
speed_changed.store(true);
pitch_changed.store(true);
current_file = {};
playback_ready = false;
bufsize = 0;
2024-08-08 13:12:37 -07:00
process = nullptr;
}
2024-03-26 18:39:02 -07:00
std::optional<std::string> PlaybackInstance::get_current_file() {
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-04-13 12:56:39 -07:00
void PlaybackInstance::Load(std::string filePath) {
2023-04-24 13:45:06 -07:00
this->filePath = filePath;
2024-04-13 12:56:39 -07:00
INFO.writefln("Loading %s...", filePath.c_str());
if (running.exchange(true)) {
load_requested.store(true);
} else {
#if defined(__EMSCRIPTEN__)||defined(__ANDROID__)
2024-04-24 09:59:51 -07:00
start_loop();
#else
2024-04-13 12:56:39 -07:00
thread = std::thread(&PlaybackInstance::ThreadFunc, this);
loop_started = true;
2024-04-24 09:59:51 -07:00
#endif
2024-04-13 12:56:39 -07:00
}
flag_mutex.lock();
this->position = 0.0;
seeking.store(true);
paused = true;
Update();
flag_mutex.unlock();
}
void PlaybackInstance::Start(std::string filePath, int streamIdx) {
Load(filePath);
2024-04-24 09:59:51 -07:00
while (loop_started && !load_finished.exchange(false)) {
#if defined(__EMSCRIPTEN__)||defined(__ANDROID__)
2024-04-24 09:59:51 -07:00
LoopHook();
#endif
2024-04-13 12:56:39 -07:00
std::this_thread::sleep_for(20ms);
}
2024-04-24 09:59:51 -07:00
if (!loop_started) {
return;
}
INFO.writefln("Playing %s...", filePath.c_str());
2023-04-24 13:45:06 -07:00
flag_mutex.lock();
this->position = 0.0;
seeking.store(true);
2023-04-24 13:45:06 -07:00
paused = false;
2024-04-13 12:56:39 -07:00
current_stream = streamIdx;
stream_changed.store(true);
Update();
flag_mutex.unlock();
}
void PlaybackInstance::play_stream(int idx) {
flag_mutex.lock();
this->position = 0.0;
seeking.store(true);
paused = false;
current_stream = idx;
stream_changed.store(true);
2023-04-24 13:45:06 -07:00
Update();
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;
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)) {
#if defined(__EMSCRIPTEN__)||defined(__ANDROID__)
2024-04-24 09:59:51 -07:00
stop_loop();
#else
2023-04-24 13:45:06 -07:00
thread.join();
2024-04-24 09:59:51 -07:00
#endif
2023-04-24 13:45:06 -07:00
}
}
2024-03-26 18:39:02 -07:00
void PlaybackInstance::Update() {
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-04-09 10:15:05 -07:00
void Playback::set_signal(uint16_t signal) {
signal_mutex.lock();
2024-04-09 10:15:05 -07:00
for (auto &kv : signals_occurred) {
kv.second |= signal;
}
signal_mutex.unlock();
}
2024-04-09 10:15:05 -07:00
uint16_t Playback::handle_signals(uint16_t signals, void *handle) {
if (signal_mutex.try_lock()) {
2024-04-09 10:15:05 -07:00
if (!signals_occurred.contains(handle)) {
signals_occurred[handle] = PlaybackSignalNone;
}
uint16_t output = signals_occurred[handle];
2024-08-08 13:12:37 -07:00
signals_occurred[handle] &= ~output;
signal_mutex.unlock();
return output;
} else {
return PlaybackSignalNone;
}
}
2024-04-09 10:15:05 -07:00
void Playback::register_handle(void *handle) {
signal_mutex.lock();
if (!signals_occurred.contains(handle)) {
signals_occurred[handle] = PlaybackSignalNone;
}
signal_mutex.unlock();
error_mutex.lock();
if (!errors_occurred.contains(handle)) {
std::deque<std::string> new_value;
for (size_t i = 0; i < errors.size(); i++) {
new_value.push_back(errors[i]);
}
errors_occurred[handle] = new_value;
}
error_mutex.unlock();
}
void Playback::unregister_handle(void *handle) {
signal_mutex.lock();
if (signals_occurred.contains(handle)) {
signals_occurred.erase(handle);
}
signal_mutex.unlock();
error_mutex.lock();
if (errors_occurred.contains(handle)) {
errors_occurred.erase(handle);
}
error_mutex.unlock();
}
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;
2024-04-09 10:15:05 -07:00
}
void Playback::set_error(std::string desc) {
error_mutex.lock();
errors.push_back(desc);
for (auto &kv : errors_occurred) {
kv.second.push_front(desc);
}
error_mutex.unlock();
set_signal(PlaybackSignalErrorOccurred);
}
Playback *Playback::Create(bool *daemon_found, bool daemon) {
2024-04-24 09:59:51 -07:00
#ifdef DBUS_ENABLED
2024-04-09 10:15:05 -07:00
auto *dbus_proxy = DBusAPISender::Create();
if (dbus_proxy != nullptr) {
if (daemon_found != nullptr) {
*daemon_found = dbus_proxy->IsDaemon();
}
DEBUG.writefln("DBus proxy daemon found: %s", *daemon_found ? "true" : "false");
if (daemon) {
delete dbus_proxy;
return nullptr;
} else {
return dbus_proxy;
}
}
if (daemon_found != nullptr) {
*daemon_found = false;
}
2024-04-24 09:59:51 -07:00
#endif
2024-04-09 10:15:05 -07:00
DEBUG.writeln("Creating new playback instance.");
return new PlaybackInstance();
2024-04-13 12:56:39 -07:00
}
2024-04-14 13:25:49 -07:00
std::vector<PlaybackStream> PlaybackInstance::get_streams() {
std::vector<PlaybackStream> output;
2024-04-13 12:56:39 -07:00
for (auto stream : streams) {
output.push_back(stream);
}
return output;
}
int PlaybackInstance::get_current_stream() {
return current_stream;
2024-08-08 13:12:37 -07:00
}