General improvements, including hidpi support.
This commit is contained in:
parent
1bcd761880
commit
70f27722df
6 changed files with 213 additions and 83 deletions
|
@ -63,6 +63,45 @@ void RendererBackend::SetWindowTitle(const char *title) {
|
|||
void RendererBackend::GuiFunction() {
|
||||
// Do nothing by default.
|
||||
}
|
||||
void RendererBackend::UpdateScale() {
|
||||
double prevScale = scale;
|
||||
const double defaultDPI =
|
||||
#ifdef __APPLE__
|
||||
72.0;
|
||||
#else
|
||||
96.0;
|
||||
#endif
|
||||
float dpi = defaultDPI;
|
||||
if (SDL_GetDisplayDPI(SDL_GetWindowDisplayIndex(window), NULL, &dpi, NULL) == 0){
|
||||
scale = dpi / defaultDPI;
|
||||
} else {
|
||||
printf("WARNING: DPI couldn't be determined!\n");
|
||||
scale = 1.0;
|
||||
}
|
||||
SDL_SetWindowSize(window, window_width * scale, window_height * scale);
|
||||
AddFonts();
|
||||
}
|
||||
void RendererBackend::SetWindowSize(int w, int h) {
|
||||
window_width = w;
|
||||
window_height = h;
|
||||
SDL_SetWindowSize(window, w * scale, h * scale);
|
||||
}
|
||||
void RendererBackend::GetWindowsize(int *w, int *h) {
|
||||
int ww, wh;
|
||||
SDL_GetWindowSize(window, &ww, &wh);
|
||||
ww /= scale;
|
||||
wh /= scale;
|
||||
if (w) *w = ww;
|
||||
if (h) *h = wh;
|
||||
}
|
||||
void RendererBackend::AddFonts() {
|
||||
ImGui_ImplOpenGL3_DestroyFontsTexture();
|
||||
auto& io = ImGui::GetIO(); (void)io;
|
||||
io.Fonts->Clear();
|
||||
add_font({FontData {notosans_regular_compressed_data_base85, io.Fonts->GetGlyphRangesDefault()}, FontData {notosansjp_regular_compressed_data_base85, io.Fonts->GetGlyphRangesJapanese()}}, 13 * scale);
|
||||
title = add_font({FontData {notosans_thin_compressed_data_base85, io.Fonts->GetGlyphRangesDefault()}, FontData {notosansjp_thin_compressed_data_base85, io.Fonts->GetGlyphRangesJapanese()}}, 48 * scale);
|
||||
ImGui_ImplOpenGL3_CreateFontsTexture();
|
||||
}
|
||||
int RendererBackend::Run() {
|
||||
setlocale(LC_ALL, "");
|
||||
bindtextdomain("neko_player", LOCALE_DIR);
|
||||
|
@ -165,8 +204,7 @@ int RendererBackend::Run() {
|
|||
//io.Fonts->AddFontFromFileTTF("../../misc/fonts/Cousine-Regular.ttf", 15.0f);
|
||||
//ImFont* font = io.Fonts->AddFontFromFileTTF("c:\\Windows\\Fonts\\ArialUni.ttf", 18.0f, nullptr, io.Fonts->GetGlyphRangesJapanese());
|
||||
//IM_ASSERT(font != nullptr);
|
||||
add_font({FontData {notosans_regular_compressed_data_base85, io.Fonts->GetGlyphRangesDefault()}, FontData {notosansjp_regular_compressed_data_base85, io.Fonts->GetGlyphRangesJapanese()}});
|
||||
title = add_font({FontData {notosans_thin_compressed_data_base85, io.Fonts->GetGlyphRangesDefault()}, FontData {notosansjp_thin_compressed_data_base85, io.Fonts->GetGlyphRangesJapanese()}}, 48);
|
||||
UpdateScale();
|
||||
|
||||
|
||||
theme = new Theme(false);
|
||||
|
@ -203,10 +241,13 @@ int RendererBackend::Run() {
|
|||
done = true;
|
||||
if (event.type == SDL_WINDOWEVENT) {
|
||||
if (event.window.event == SDL_WINDOWEVENT_RESIZED) {
|
||||
window_width = event.window.data1;
|
||||
window_height = event.window.data2;
|
||||
window_width = event.window.data1 / scale;
|
||||
window_height = event.window.data2 / scale;
|
||||
//SDL_GetWindowSize(window, &window_width, &window_height);
|
||||
}
|
||||
if (event.window.event == SDL_WINDOWEVENT_DISPLAY_CHANGED) {
|
||||
UpdateScale();
|
||||
}
|
||||
if (event.window.event == SDL_WINDOWEVENT_CLOSE && event.window.windowID == SDL_GetWindowID(window)) {
|
||||
done = true;
|
||||
}
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
static const char* NAME = "Neko Player";
|
||||
class RendererBackend {
|
||||
public:
|
||||
double scale = 1.0;
|
||||
SDL_Window *window;
|
||||
int window_width = 475;
|
||||
int window_height = 354;
|
||||
|
@ -31,6 +32,10 @@ class RendererBackend {
|
|||
virtual void Init();
|
||||
virtual void GuiFunction();
|
||||
virtual void Deinit();
|
||||
void UpdateScale();
|
||||
void AddFonts();
|
||||
void SetWindowSize(int w, int h);
|
||||
void GetWindowsize(int *w, int *h);
|
||||
RendererBackend();
|
||||
~RendererBackend();
|
||||
};
|
2
main.cpp
2
main.cpp
|
@ -374,7 +374,7 @@ void MainLoop::GuiFunction() {
|
|||
if (fileDialog.HasSelected()) {
|
||||
playback->Start(fileDialog.GetSelected().string());
|
||||
// Update the window title.
|
||||
SetWindowTitle((fileDialog.GetSelected().filename().replace_extension("").string() + std::string(" - ") + std::string(NAME)).c_str());
|
||||
SetWindowTitle((fileDialog.GetSelected().filename().string() + std::string(" - ") + std::string(NAME)).c_str());
|
||||
// Make sure to not load the file unnecessarily.
|
||||
fileDialog.ClearSelected();
|
||||
}
|
||||
|
|
|
@ -28,6 +28,7 @@ deps = [
|
|||
subproject('jsoncpp').get_variable('jsoncpp_dep'),
|
||||
dependency('soundtouch'),
|
||||
dependency('intl'),
|
||||
dependency('threads'),
|
||||
smx_subproj.dependency('SDL2_mixer_ext_Static')
|
||||
]
|
||||
|
||||
|
|
226
playback.cpp
226
playback.cpp
|
@ -4,17 +4,39 @@
|
|||
#include <SDL.h>
|
||||
#include <exception>
|
||||
#include <thread>
|
||||
#include <cmath>
|
||||
#ifdef __linux__
|
||||
#include <pthread.h>
|
||||
#endif
|
||||
using namespace std::chrono;
|
||||
size_t CalculateBufSize(SDL_AudioSpec *obtained, double max_seconds, size_t samples_override = 0) {
|
||||
return ((((samples_override == 0) ? obtained->samples : samples_override) * max_seconds) + 1) * sizeof(SAMPLETYPE) * obtained->channels;
|
||||
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;
|
||||
}
|
||||
void Playback::SDLCallbackInner(Uint8 *stream, int len) {
|
||||
while (st->numSamples() <= (uint)len) {
|
||||
general_mixer(NULL, buf, bufsize);
|
||||
st->putSamples((SAMPLETYPE*)buf, bufsize / sizeof(SAMPLETYPE) / spec.channels);
|
||||
}
|
||||
SDL_memset((void*)stream, 0, len);
|
||||
st->receiveSamples((SAMPLETYPE*)stream, len / sizeof(SAMPLETYPE) / spec.channels);
|
||||
if (!playback_ready.load()) {
|
||||
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)len);
|
||||
while (st->numSamples() <= (uint)len) {
|
||||
if (general_mixer == nullptr) {
|
||||
return;
|
||||
}
|
||||
general_mixer(NULL, buf + i, bytes_per_iter);
|
||||
i += bytes_per_iter;
|
||||
max = i + bytes_per_iter;
|
||||
if (i + bytes_per_iter >= bufsize) {
|
||||
st->putSamples((SAMPLETYPE*)buf, i / unit);
|
||||
max = 0;
|
||||
i = 0;
|
||||
SDL_memset((void*)buf, 0, bufsize);
|
||||
}
|
||||
}
|
||||
st->putSamples((SAMPLETYPE*)buf, max / unit);
|
||||
st->receiveSamples((SAMPLETYPE*)stream, len / unit);
|
||||
}
|
||||
void Playback::SDLCallback(void *userdata, Uint8 *stream, int len) {
|
||||
((Playback*)userdata)->SDLCallbackInner(stream, len);
|
||||
|
@ -34,82 +56,129 @@ void Playback::Unload(Mix_Music *music) {
|
|||
Mix_HaltMusicStream(music);
|
||||
Mix_FreeMusic(music);
|
||||
}
|
||||
void Playback::UpdateST() {
|
||||
if (speed > 0.0f) {
|
||||
st->setRate(speed);
|
||||
}
|
||||
if (tempo > 0.0f) {
|
||||
st->setTempo(tempo);
|
||||
}
|
||||
if (pitch > 0.0f) {
|
||||
st->setPitch(pitch);
|
||||
}
|
||||
}
|
||||
double Playback::GetMaxSeconds() {
|
||||
return std::max(MaxSpeed * MaxTempo, st->getInputOutputSampleRatio());
|
||||
}
|
||||
void Playback::ThreadFunc() {
|
||||
if (!SDL_WasInit(SDL_INIT_AUDIO)) {
|
||||
if (SDL_InitSubSystem(SDL_INIT_AUDIO) < 0) {
|
||||
printf("Error initializing SDL: '%s'\n", SDL_GetError());
|
||||
#ifdef __linux__
|
||||
pthread_setname_np(pthread_self(), "Playback control thread");
|
||||
#endif
|
||||
bool reload = false;
|
||||
while (running) {
|
||||
playback_ready.store(false);
|
||||
if (reload) {
|
||||
printf("Resuming playback...\n");
|
||||
}
|
||||
if (!SDL_WasInit(SDL_INIT_AUDIO)) {
|
||||
if (SDL_InitSubSystem(SDL_INIT_AUDIO) < 0) {
|
||||
printf("Error initializing SDL: '%s'\n", SDL_GetError());
|
||||
throw std::exception();
|
||||
}
|
||||
}
|
||||
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;
|
||||
desired.callback = Playback::SDLCallback;
|
||||
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);
|
||||
if ((device = SDL_OpenAudioDevice(NULL, 0, &desired, &obtained, SDL_AUDIO_ALLOW_CHANNELS_CHANGE|SDL_AUDIO_ALLOW_FREQUENCY_CHANGE|SDL_AUDIO_ALLOW_SAMPLES_CHANGE)) == 0) {
|
||||
printf("Error opening audio device: '%s'\n", SDL_GetError());
|
||||
throw std::exception();
|
||||
}
|
||||
}
|
||||
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;
|
||||
desired.callback = Playback::SDLCallback;
|
||||
desired.userdata = this;
|
||||
st = new SoundTouch();
|
||||
st->setSampleRate(desired.freq);
|
||||
st->setChannels(desired.channels);
|
||||
Mix_Init(MIX_INIT_FLAC|MIX_INIT_MID|MIX_INIT_MOD|MIX_INIT_MP3|MIX_INIT_OGG|MIX_INIT_OPUS|MIX_INIT_WAVPACK);
|
||||
if ((device = SDL_OpenAudioDevice(NULL, 0, &desired, &obtained, 0)) == 0) {
|
||||
printf("Error opening audio device: '%s'\n", SDL_GetError());
|
||||
throw std::exception();
|
||||
}
|
||||
spec = obtained;
|
||||
bufsize = spec.size;
|
||||
buf = (Uint8*)malloc(bufsize);
|
||||
general_mixer = Mix_GetGeneralMixer();
|
||||
Mix_InitMixer(&spec, SDL_TRUE);
|
||||
SDL_PauseAudioDevice(device, 0);
|
||||
Mix_Music *music = Load(filePath.c_str());
|
||||
|
||||
while (running) {
|
||||
if (file_changed.exchange(false)) {
|
||||
Unload(music);
|
||||
music = Load(filePath.c_str());
|
||||
spec = obtained;
|
||||
st->setSampleRate(spec.freq);
|
||||
st->setChannels(spec.channels);
|
||||
UpdateST();
|
||||
bufsize = 0;
|
||||
size_t new_bufsize = CalculateBufSize(&spec, GetMaxSeconds(), MaxSeconds);
|
||||
buf = (Uint8*)malloc(new_bufsize);
|
||||
if (buf == NULL) {
|
||||
throw std::exception();
|
||||
}
|
||||
if (flag_mutex.try_lock()) {
|
||||
if (seeking.exchange(false)) {
|
||||
Mix_SetMusicPositionStream(music, position);
|
||||
}
|
||||
if (paused) {
|
||||
Mix_PauseMusicStream(music);
|
||||
} else {
|
||||
Mix_ResumeMusicStream(music);
|
||||
}
|
||||
if (update.exchange(false)) {
|
||||
Mix_VolumeMusicStream(music, (volume / 100.0 * MIX_MAX_VOLUME));
|
||||
SDL_LockAudioDevice(device);
|
||||
if (speed > 0.0f) {
|
||||
st->setRate(speed);
|
||||
}
|
||||
if (tempo > 0.0f) {
|
||||
st->setTempo(tempo);
|
||||
}
|
||||
if (pitch > 0.0f) {
|
||||
st->setPitch(pitch);
|
||||
}
|
||||
SDL_UnlockAudioDevice(device);
|
||||
}
|
||||
flag_mutex.unlock();
|
||||
bufsize = new_bufsize;
|
||||
general_mixer = Mix_GetGeneralMixer();
|
||||
Mix_InitMixer(&spec, SDL_TRUE);
|
||||
SDL_PauseAudioDevice(device, 0);
|
||||
Mix_Music *music = Load(filePath.c_str());
|
||||
if (reload) {
|
||||
Seek(position);
|
||||
}
|
||||
position = Mix_GetMusicPosition(music);
|
||||
std::this_thread::sleep_for(20ms);
|
||||
reload = false;
|
||||
playback_ready.store(true);
|
||||
while (running && !reload) {
|
||||
if (file_changed.exchange(false)) {
|
||||
Unload(music);
|
||||
music = Load(filePath.c_str());
|
||||
}
|
||||
if (flag_mutex.try_lock()) {
|
||||
if (seeking.exchange(false)) {
|
||||
Mix_SetMusicPositionStream(music, position);
|
||||
}
|
||||
if (paused) {
|
||||
Mix_PauseMusicStream(music);
|
||||
} else {
|
||||
Mix_ResumeMusicStream(music);
|
||||
}
|
||||
if (update.exchange(false)) {
|
||||
Mix_VolumeMusicStream(music, (volume / 100.0 * MIX_MAX_VOLUME));
|
||||
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) {
|
||||
printf("Bufsize is too large - ");
|
||||
} else if (too_small) {
|
||||
printf("Bufsize is too small - ");
|
||||
}
|
||||
if (too_large || too_small) {
|
||||
printf("Resizing buffer...\n");
|
||||
general_mixer = nullptr;
|
||||
bufsize = 0;
|
||||
buf = (Uint8*)realloc((void*)buf, correct_buf_size);
|
||||
if (buf == NULL) {
|
||||
break;
|
||||
}
|
||||
bufsize = correct_buf_size;
|
||||
}
|
||||
SDL_UnlockAudioDevice(device);
|
||||
}
|
||||
flag_mutex.unlock();
|
||||
}
|
||||
position = Mix_GetMusicPosition(music);
|
||||
std::this_thread::sleep_for(20ms);
|
||||
}
|
||||
playback_ready.store(false);
|
||||
// ====
|
||||
Unload(music);
|
||||
SDL_CloseAudioDevice(device);
|
||||
Mix_CloseAudio();
|
||||
Mix_Quit();
|
||||
SDL_QuitSubSystem(SDL_INIT_AUDIO);
|
||||
delete st;
|
||||
free(buf);
|
||||
}
|
||||
// ====
|
||||
Unload(music);
|
||||
Mix_CloseAudio();
|
||||
Mix_Quit();
|
||||
SDL_CloseAudioDevice(device);
|
||||
delete st;
|
||||
free(buf);
|
||||
}
|
||||
|
||||
Playback::Playback() {
|
||||
|
@ -131,6 +200,8 @@ void Playback::Start(std::string filePath) {
|
|||
this->filePath = filePath;
|
||||
printf("Playing %s...\n", filePath.c_str());
|
||||
flag_mutex.lock();
|
||||
this->position = 0.0;
|
||||
seeking.store(true);
|
||||
paused = false;
|
||||
Update();
|
||||
if (running.exchange(true)) {
|
||||
|
@ -169,7 +240,6 @@ void Playback::Stop() {
|
|||
thread.join();
|
||||
}
|
||||
}
|
||||
|
||||
void Playback::Update() {
|
||||
update.store(true);
|
||||
}
|
||||
|
|
13
playback.h
13
playback.h
|
@ -9,8 +9,10 @@
|
|||
#include <mutex>
|
||||
#include <SoundTouch.h>
|
||||
#include <span>
|
||||
#include <optional>
|
||||
using namespace soundtouch;
|
||||
using std::span;
|
||||
using std::optional;
|
||||
class Playback {
|
||||
private:
|
||||
std::string filePath;
|
||||
|
@ -18,6 +20,8 @@ private:
|
|||
std::atomic_bool file_changed;
|
||||
std::atomic_bool seeking;
|
||||
std::atomic_bool update;
|
||||
std::atomic_bool restart;
|
||||
std::atomic_bool playback_ready;
|
||||
std::mutex flag_mutex;
|
||||
std::thread thread;
|
||||
double position;
|
||||
|
@ -34,6 +38,8 @@ private:
|
|||
Mix_Music *Load(const char* file);
|
||||
void Unload(Mix_Music* music);
|
||||
void ThreadFunc();
|
||||
void UpdateST();
|
||||
double GetMaxSeconds();
|
||||
public:
|
||||
Playback();
|
||||
~Playback();
|
||||
|
@ -50,4 +56,11 @@ public:
|
|||
float speed;
|
||||
float tempo;
|
||||
float pitch;
|
||||
double MaxSeconds = 100.0;
|
||||
double MaxSpeed = 4.0;
|
||||
double MaxPitch = 4.0;
|
||||
double MaxTempo = 4.0;
|
||||
double MinSpeed = 0.25;
|
||||
double MinPitch = 0.25;
|
||||
double MinTempo = 0.25;
|
||||
};
|
Loading…
Reference in a new issue