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