Start playback thread on playback engine construction
This commit is contained in:
parent
7e43808b4d
commit
856226e641
3 changed files with 125 additions and 58 deletions
22
config.hpp
Normal file
22
config.hpp
Normal file
|
@ -0,0 +1,22 @@
|
|||
#pragma once
|
||||
#include <soundtouch_config.h>
|
||||
#if defined(__EMSCRIPTEN__)||defined(__ANDROID__)
|
||||
#define NO_THREADS
|
||||
#endif
|
||||
|
||||
#ifdef SOUNDTOUCH_INTEGER_SAMPLES
|
||||
#define SDL_SAMPLE_FMT AUDIO_S16SYS
|
||||
#else
|
||||
#define SDL_SAMPLE_FMT AUDIO_F32SYS
|
||||
#endif
|
||||
#ifdef __ANDROID__
|
||||
#define USE_OBOE
|
||||
#ifdef SOUNDTOUCH_INTEGER_SAMPLES
|
||||
#define SAMPLE_FMT oboe::AudioFormat::I16
|
||||
#else
|
||||
#define SAMPLE_FMT oboe::AudioFormat::Float
|
||||
#endif
|
||||
#else
|
||||
#define SAMPLE_FMT SDL_SAMPLE_FMT
|
||||
#define USE_SDL
|
||||
#endif
|
149
playback.cpp
149
playback.cpp
|
@ -135,6 +135,7 @@ void PlaybackInstance::SDLCallback(void *userdata, Uint8 *stream, int len) {
|
|||
}
|
||||
void PlaybackInstance::Load(const char *file, int idx) {
|
||||
SDL_LockAudioDevice(device);
|
||||
load_finished.store(false);
|
||||
playback_ready.store(false);
|
||||
if (process != nullptr) delete process;
|
||||
try {
|
||||
|
@ -188,7 +189,6 @@ void PlaybackInstance::Load(const char *file, int idx) {
|
|||
}
|
||||
delete backend_spec_proxy;
|
||||
playback_ready.store(true);
|
||||
load_finished.store(true);
|
||||
this->process->set_position(0.0);
|
||||
} else {
|
||||
set_error("Failed to create playback backend.");
|
||||
|
@ -196,8 +196,13 @@ void PlaybackInstance::Load(const char *file, int idx) {
|
|||
delete process;
|
||||
process = nullptr;
|
||||
}
|
||||
initial_render = true;
|
||||
SDL_UnlockAudioDevice(device);
|
||||
load_finished.store(true);
|
||||
flag_mutex.lock();
|
||||
stopped.store(false);
|
||||
paused = false;
|
||||
just_started.store(true);
|
||||
flag_mutex.unlock();
|
||||
}
|
||||
void PlaybackInstance::Unload() {
|
||||
if (process == nullptr) return;
|
||||
|
@ -206,7 +211,8 @@ void PlaybackInstance::Unload() {
|
|||
process = nullptr;
|
||||
SDL_FreeAudioStream(sdl_stream);
|
||||
sdl_stream = nullptr;
|
||||
free(buf);
|
||||
if (buf) free(buf);
|
||||
buf = nullptr;
|
||||
SDL_UnlockAudioDevice(device);
|
||||
}
|
||||
void PlaybackInstance::UpdateST() {
|
||||
|
@ -249,19 +255,14 @@ void PlaybackInstance::InitLoopFunction() {
|
|||
}
|
||||
SDL_AudioSpec obtained;
|
||||
SDL_AudioSpec desired;
|
||||
desired.format =
|
||||
#ifdef SOUNDTOUCH_INTEGER_SAMPLES
|
||||
AUDIO_S16SYS;
|
||||
#else
|
||||
AUDIO_F32SYS;
|
||||
#endif
|
||||
desired.format = SDL_SAMPLE_FMT;
|
||||
desired.freq = 48000;
|
||||
desired.samples = 200;
|
||||
desired.channels = 2;
|
||||
desired.callback = PlaybackInstance::SDLCallback;
|
||||
desired.userdata = this;
|
||||
st = new SoundTouch();
|
||||
#ifndef __ANDROID__
|
||||
#ifdef USE_SDL
|
||||
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!");
|
||||
|
@ -269,18 +270,13 @@ void PlaybackInstance::InitLoopFunction() {
|
|||
loop_started = false;
|
||||
return;
|
||||
}
|
||||
#else
|
||||
SDL_PauseAudioDevice(device, 0);
|
||||
#elif defined(USE_OBOE)
|
||||
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.setFormat(SAMPLE_FMT);
|
||||
builder.setDataCallback(this);
|
||||
auto res = builder.openStream(ostream);
|
||||
if (res != oboe::Result::OK) {
|
||||
|
@ -318,25 +314,46 @@ Total samples: %u"
|
|||
, obtained.samples
|
||||
);
|
||||
ostream->requestStart();
|
||||
#else
|
||||
#error Invalid configuration detected.
|
||||
#endif
|
||||
spec = obtained;
|
||||
st->setSampleRate(spec.freq);
|
||||
st->setChannels(spec.channels);
|
||||
UpdateST();
|
||||
SDL_PauseAudioDevice(device, 0);
|
||||
Load(filePath.c_str(), 0);
|
||||
|
||||
reload = false;
|
||||
if (process && process->process_running()) {
|
||||
playback_ready.store(true);
|
||||
} else {
|
||||
playback_ready.store(false);
|
||||
}
|
||||
load_finished.store(true);
|
||||
flag_mutex.lock();
|
||||
this->position = 0.0;
|
||||
seeking.store(false);
|
||||
paused = true;
|
||||
flag_mutex.unlock();
|
||||
playback_ready.store(false);
|
||||
length = 0.0;
|
||||
memset(&backend_spec, 0, sizeof(backend_spec));
|
||||
if (sdl_stream) SDL_FreeAudioStream(sdl_stream);
|
||||
sdl_stream = nullptr;
|
||||
bufsize = 0;
|
||||
if (buf) free((void*)buf);
|
||||
buf = nullptr;
|
||||
if (process) delete process;
|
||||
process = nullptr;
|
||||
stream_changed.store(false);
|
||||
file_changed.store(false);
|
||||
load_requested.store(false);
|
||||
current_file = {};
|
||||
current_stream = -1;
|
||||
current_title = {};
|
||||
seeking.store(false);
|
||||
pause_changed.store(false);
|
||||
just_stopped.store(true);
|
||||
just_started.store(false);
|
||||
stopped.store(true);
|
||||
set_signal(PlaybackSignalStarted);
|
||||
load_finished.store(true);
|
||||
}
|
||||
void PlaybackInstance::LoopFunction() {
|
||||
|
||||
if (file_changed.exchange(false) || load_requested.exchange(false)) {
|
||||
Unload();
|
||||
Load(filePath.c_str(), 0);
|
||||
|
@ -345,6 +362,8 @@ void PlaybackInstance::LoopFunction() {
|
|||
} else {
|
||||
playback_ready.store(false);
|
||||
}
|
||||
load_finished.store(true);
|
||||
just_started.store(true);
|
||||
}
|
||||
if (stream_changed.exchange(false)) {
|
||||
current_file_mutex.lock();
|
||||
|
@ -375,8 +394,8 @@ void PlaybackInstance::LoopFunction() {
|
|||
if (process && process->process_running()) process->set_position(this->position);
|
||||
set_signal(PlaybackSignalSeeked);
|
||||
}
|
||||
if (pause_changed.exchange(false)) {
|
||||
SDL_PauseAudioDevice(device, paused ? 1 : 0);
|
||||
if (pause_changed.exchange(false) || just_stopped.exchange(false) || just_started.exchange(false)) {
|
||||
SDL_PauseAudioDevice(device, (stopped.load() || paused) ? 1 : 0);
|
||||
if (paused) {
|
||||
set_signal(PlaybackSignalPaused);
|
||||
} else {
|
||||
|
@ -402,9 +421,9 @@ void PlaybackInstance::DeinitLoopFunction() {
|
|||
playback_ready.store(false);
|
||||
// ====
|
||||
Unload();
|
||||
#ifndef __ANDROID__
|
||||
#ifdef USE_SDL
|
||||
SDL_CloseAudioDevice(device);
|
||||
#else
|
||||
#elif defined(USE_OBOE)
|
||||
if (ostream && ostream->getState() != oboe::StreamState::Closed) {
|
||||
ostream->stop();
|
||||
ostream->close();
|
||||
|
@ -413,7 +432,8 @@ void PlaybackInstance::DeinitLoopFunction() {
|
|||
#endif
|
||||
SDL_QuitSubSystem(SDL_INIT_AUDIO);
|
||||
delete st;
|
||||
free(buf);
|
||||
st = nullptr;
|
||||
if (buf) free(buf);
|
||||
current_file_mutex.lock();
|
||||
current_file = {};
|
||||
current_file_mutex.unlock();
|
||||
|
@ -435,14 +455,17 @@ PlaybackInstance::PlaybackInstance() {
|
|||
running = false;
|
||||
paused = true;
|
||||
position = 0;
|
||||
sdl_stream = nullptr;
|
||||
length = 0;
|
||||
volume = 100.0;
|
||||
real_volume = 1.0;
|
||||
speed = 1.0;
|
||||
pitch = 1.0;
|
||||
tempo = 1.0;
|
||||
prev_speed = -1.0;
|
||||
prev_pitch = -1.0;
|
||||
prev_tempo = -1.0;
|
||||
buf = nullptr;
|
||||
tempo_changed.store(true);
|
||||
speed_changed.store(true);
|
||||
pitch_changed.store(true);
|
||||
|
@ -450,6 +473,21 @@ PlaybackInstance::PlaybackInstance() {
|
|||
playback_ready = false;
|
||||
bufsize = 0;
|
||||
process = nullptr;
|
||||
running.store(true);
|
||||
#if NO_THREADS
|
||||
start_loop();
|
||||
#else
|
||||
thread = std::thread(&PlaybackInstance::ThreadFunc, this);
|
||||
loop_started = true;
|
||||
#endif
|
||||
while (loop_started && !load_finished.exchange(false)) {
|
||||
#if NO_THREADS
|
||||
LoopHook();
|
||||
#endif
|
||||
std::this_thread::sleep_for(20ms);
|
||||
}
|
||||
load_finished.store(false);
|
||||
|
||||
}
|
||||
std::optional<std::string> PlaybackInstance::get_current_file() {
|
||||
current_file_mutex.lock();
|
||||
|
@ -465,38 +503,31 @@ std::optional<std::string> PlaybackInstance::get_current_title() {
|
|||
}
|
||||
PlaybackInstance::~PlaybackInstance() {
|
||||
Stop();
|
||||
}
|
||||
void PlaybackInstance::Load(std::string filePath) {
|
||||
this->filePath = filePath;
|
||||
INFO.writefln("Loading %s...", filePath.c_str());
|
||||
if (running.exchange(true)) {
|
||||
load_requested.store(true);
|
||||
} else {
|
||||
#if defined(__EMSCRIPTEN__)||defined(__ANDROID__)
|
||||
start_loop();
|
||||
running.store(false);
|
||||
#ifdef NO_THREADS
|
||||
stop_loop();
|
||||
#else
|
||||
thread = std::thread(&PlaybackInstance::ThreadFunc, this);
|
||||
loop_started = true;
|
||||
thread.join();
|
||||
#endif
|
||||
}
|
||||
void PlaybackInstance::Load(std::string filePath) {
|
||||
load_finished.store(false);
|
||||
this->filePath = filePath;
|
||||
INFO.writefln("Loading %s...", filePath.c_str());
|
||||
flag_mutex.lock();
|
||||
this->position = 0.0;
|
||||
seeking.store(true);
|
||||
paused = true;
|
||||
Update();
|
||||
flag_mutex.unlock();
|
||||
file_changed.store(true);
|
||||
wait([this]() {
|
||||
return load_finished.load();
|
||||
});
|
||||
}
|
||||
void PlaybackInstance::Start(std::string filePath, int streamIdx) {
|
||||
load_finished.store(false);
|
||||
Load(filePath);
|
||||
while (loop_started && !load_finished.exchange(false)) {
|
||||
#if defined(__EMSCRIPTEN__)||defined(__ANDROID__)
|
||||
LoopHook();
|
||||
#endif
|
||||
std::this_thread::sleep_for(20ms);
|
||||
}
|
||||
if (!loop_started) {
|
||||
return;
|
||||
}
|
||||
INFO.writefln("Playing %s...", filePath.c_str());
|
||||
flag_mutex.lock();
|
||||
this->position = 0.0;
|
||||
|
@ -506,6 +537,9 @@ void PlaybackInstance::Start(std::string filePath, int streamIdx) {
|
|||
stream_changed.store(true);
|
||||
Update();
|
||||
flag_mutex.unlock();
|
||||
wait([this]() {
|
||||
return load_finished.load();
|
||||
});
|
||||
}
|
||||
void PlaybackInstance::play_stream(int idx) {
|
||||
flag_mutex.lock();
|
||||
|
@ -515,6 +549,7 @@ void PlaybackInstance::play_stream(int idx) {
|
|||
current_stream = idx;
|
||||
stream_changed.store(true);
|
||||
Update();
|
||||
just_started.store(true);
|
||||
flag_mutex.unlock();
|
||||
}
|
||||
double PlaybackInstance::GetPosition() {
|
||||
|
@ -543,13 +578,11 @@ bool PlaybackInstance::IsPaused() {
|
|||
}
|
||||
|
||||
void PlaybackInstance::Stop() {
|
||||
if (running.exchange(false)) {
|
||||
#if defined(__EMSCRIPTEN__)||defined(__ANDROID__)
|
||||
stop_loop();
|
||||
#else
|
||||
thread.join();
|
||||
#endif
|
||||
}
|
||||
flag_mutex.lock();
|
||||
stopped.store(true);
|
||||
just_started.store(false);
|
||||
just_stopped.store(true);
|
||||
flag_mutex.unlock();
|
||||
}
|
||||
void PlaybackInstance::Update() {
|
||||
if (prev_pitch != pitch) {
|
||||
|
|
12
playback.h
12
playback.h
|
@ -21,6 +21,7 @@ extern "C" {
|
|||
#include <map>
|
||||
#include "file_backend.hpp"
|
||||
#include "playback_backend.hpp"
|
||||
#include "config.hpp"
|
||||
#include "playback_process.hpp"
|
||||
using namespace soundtouch;
|
||||
using std::span;
|
||||
|
@ -243,6 +244,9 @@ private:
|
|||
std::atomic_bool load_requested;
|
||||
std::atomic_bool load_finished;
|
||||
std::atomic_bool stream_changed;
|
||||
std::atomic_bool stopped;
|
||||
std::atomic_bool just_stopped;
|
||||
std::atomic_bool just_started;
|
||||
std::mutex flag_mutex;
|
||||
std::mutex error_mutex;
|
||||
std::thread thread;
|
||||
|
@ -276,6 +280,14 @@ private:
|
|||
float prev_pitch, prev_speed, prev_tempo;
|
||||
FILE_TYPE *file;
|
||||
bool initial_render = false;
|
||||
inline void wait(std::function<bool()> fn) {
|
||||
while (!fn()) {
|
||||
#ifdef NO_THREADS
|
||||
LoopHook();
|
||||
#endif
|
||||
std::this_thread::sleep_for(20ms);
|
||||
}
|
||||
}
|
||||
public:
|
||||
PlaybackInstance();
|
||||
~PlaybackInstance() override;
|
||||
|
|
Loading…
Reference in a new issue