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() {
|
void RendererBackend::GuiFunction() {
|
||||||
// Do nothing by default.
|
// 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() {
|
int RendererBackend::Run() {
|
||||||
setlocale(LC_ALL, "");
|
setlocale(LC_ALL, "");
|
||||||
bindtextdomain("neko_player", LOCALE_DIR);
|
bindtextdomain("neko_player", LOCALE_DIR);
|
||||||
|
@ -165,8 +204,7 @@ int RendererBackend::Run() {
|
||||||
//io.Fonts->AddFontFromFileTTF("../../misc/fonts/Cousine-Regular.ttf", 15.0f);
|
//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());
|
//ImFont* font = io.Fonts->AddFontFromFileTTF("c:\\Windows\\Fonts\\ArialUni.ttf", 18.0f, nullptr, io.Fonts->GetGlyphRangesJapanese());
|
||||||
//IM_ASSERT(font != nullptr);
|
//IM_ASSERT(font != nullptr);
|
||||||
add_font({FontData {notosans_regular_compressed_data_base85, io.Fonts->GetGlyphRangesDefault()}, FontData {notosansjp_regular_compressed_data_base85, io.Fonts->GetGlyphRangesJapanese()}});
|
UpdateScale();
|
||||||
title = add_font({FontData {notosans_thin_compressed_data_base85, io.Fonts->GetGlyphRangesDefault()}, FontData {notosansjp_thin_compressed_data_base85, io.Fonts->GetGlyphRangesJapanese()}}, 48);
|
|
||||||
|
|
||||||
|
|
||||||
theme = new Theme(false);
|
theme = new Theme(false);
|
||||||
|
@ -203,10 +241,13 @@ int RendererBackend::Run() {
|
||||||
done = true;
|
done = true;
|
||||||
if (event.type == SDL_WINDOWEVENT) {
|
if (event.type == SDL_WINDOWEVENT) {
|
||||||
if (event.window.event == SDL_WINDOWEVENT_RESIZED) {
|
if (event.window.event == SDL_WINDOWEVENT_RESIZED) {
|
||||||
window_width = event.window.data1;
|
window_width = event.window.data1 / scale;
|
||||||
window_height = event.window.data2;
|
window_height = event.window.data2 / scale;
|
||||||
//SDL_GetWindowSize(window, &window_width, &window_height);
|
//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)) {
|
if (event.window.event == SDL_WINDOWEVENT_CLOSE && event.window.windowID == SDL_GetWindowID(window)) {
|
||||||
done = true;
|
done = true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,6 +13,7 @@
|
||||||
static const char* NAME = "Neko Player";
|
static const char* NAME = "Neko Player";
|
||||||
class RendererBackend {
|
class RendererBackend {
|
||||||
public:
|
public:
|
||||||
|
double scale = 1.0;
|
||||||
SDL_Window *window;
|
SDL_Window *window;
|
||||||
int window_width = 475;
|
int window_width = 475;
|
||||||
int window_height = 354;
|
int window_height = 354;
|
||||||
|
@ -31,6 +32,10 @@ class RendererBackend {
|
||||||
virtual void Init();
|
virtual void Init();
|
||||||
virtual void GuiFunction();
|
virtual void GuiFunction();
|
||||||
virtual void Deinit();
|
virtual void Deinit();
|
||||||
|
void UpdateScale();
|
||||||
|
void AddFonts();
|
||||||
|
void SetWindowSize(int w, int h);
|
||||||
|
void GetWindowsize(int *w, int *h);
|
||||||
RendererBackend();
|
RendererBackend();
|
||||||
~RendererBackend();
|
~RendererBackend();
|
||||||
};
|
};
|
2
main.cpp
2
main.cpp
|
@ -374,7 +374,7 @@ void MainLoop::GuiFunction() {
|
||||||
if (fileDialog.HasSelected()) {
|
if (fileDialog.HasSelected()) {
|
||||||
playback->Start(fileDialog.GetSelected().string());
|
playback->Start(fileDialog.GetSelected().string());
|
||||||
// Update the window title.
|
// 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.
|
// Make sure to not load the file unnecessarily.
|
||||||
fileDialog.ClearSelected();
|
fileDialog.ClearSelected();
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,6 +28,7 @@ deps = [
|
||||||
subproject('jsoncpp').get_variable('jsoncpp_dep'),
|
subproject('jsoncpp').get_variable('jsoncpp_dep'),
|
||||||
dependency('soundtouch'),
|
dependency('soundtouch'),
|
||||||
dependency('intl'),
|
dependency('intl'),
|
||||||
|
dependency('threads'),
|
||||||
smx_subproj.dependency('SDL2_mixer_ext_Static')
|
smx_subproj.dependency('SDL2_mixer_ext_Static')
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
114
playback.cpp
114
playback.cpp
|
@ -4,17 +4,39 @@
|
||||||
#include <SDL.h>
|
#include <SDL.h>
|
||||||
#include <exception>
|
#include <exception>
|
||||||
#include <thread>
|
#include <thread>
|
||||||
|
#include <cmath>
|
||||||
|
#ifdef __linux__
|
||||||
|
#include <pthread.h>
|
||||||
|
#endif
|
||||||
using namespace std::chrono;
|
using namespace std::chrono;
|
||||||
size_t CalculateBufSize(SDL_AudioSpec *obtained, double max_seconds, size_t samples_override = 0) {
|
size_t CalculateBufSize(SDL_AudioSpec *obtained, double seconds, double max_seconds, size_t samples_override = 0) {
|
||||||
return ((((samples_override == 0) ? obtained->samples : samples_override) * max_seconds) + 1) * sizeof(SAMPLETYPE) * obtained->channels;
|
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) {
|
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);
|
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) {
|
void Playback::SDLCallback(void *userdata, Uint8 *stream, int len) {
|
||||||
((Playback*)userdata)->SDLCallbackInner(stream, len);
|
((Playback*)userdata)->SDLCallbackInner(stream, len);
|
||||||
|
@ -34,7 +56,30 @@ void Playback::Unload(Mix_Music *music) {
|
||||||
Mix_HaltMusicStream(music);
|
Mix_HaltMusicStream(music);
|
||||||
Mix_FreeMusic(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() {
|
void Playback::ThreadFunc() {
|
||||||
|
#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_WasInit(SDL_INIT_AUDIO)) {
|
||||||
if (SDL_InitSubSystem(SDL_INIT_AUDIO) < 0) {
|
if (SDL_InitSubSystem(SDL_INIT_AUDIO) < 0) {
|
||||||
printf("Error initializing SDL: '%s'\n", SDL_GetError());
|
printf("Error initializing SDL: '%s'\n", SDL_GetError());
|
||||||
|
@ -55,22 +100,32 @@ void Playback::ThreadFunc() {
|
||||||
desired.callback = Playback::SDLCallback;
|
desired.callback = Playback::SDLCallback;
|
||||||
desired.userdata = this;
|
desired.userdata = this;
|
||||||
st = new SoundTouch();
|
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);
|
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) {
|
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());
|
printf("Error opening audio device: '%s'\n", SDL_GetError());
|
||||||
throw std::exception();
|
throw std::exception();
|
||||||
}
|
}
|
||||||
spec = obtained;
|
spec = obtained;
|
||||||
bufsize = spec.size;
|
st->setSampleRate(spec.freq);
|
||||||
buf = (Uint8*)malloc(bufsize);
|
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();
|
||||||
|
}
|
||||||
|
bufsize = new_bufsize;
|
||||||
general_mixer = Mix_GetGeneralMixer();
|
general_mixer = Mix_GetGeneralMixer();
|
||||||
Mix_InitMixer(&spec, SDL_TRUE);
|
Mix_InitMixer(&spec, SDL_TRUE);
|
||||||
SDL_PauseAudioDevice(device, 0);
|
SDL_PauseAudioDevice(device, 0);
|
||||||
Mix_Music *music = Load(filePath.c_str());
|
Mix_Music *music = Load(filePath.c_str());
|
||||||
|
if (reload) {
|
||||||
while (running) {
|
Seek(position);
|
||||||
|
}
|
||||||
|
reload = false;
|
||||||
|
playback_ready.store(true);
|
||||||
|
while (running && !reload) {
|
||||||
if (file_changed.exchange(false)) {
|
if (file_changed.exchange(false)) {
|
||||||
Unload(music);
|
Unload(music);
|
||||||
music = Load(filePath.c_str());
|
music = Load(filePath.c_str());
|
||||||
|
@ -87,14 +142,25 @@ void Playback::ThreadFunc() {
|
||||||
if (update.exchange(false)) {
|
if (update.exchange(false)) {
|
||||||
Mix_VolumeMusicStream(music, (volume / 100.0 * MIX_MAX_VOLUME));
|
Mix_VolumeMusicStream(music, (volume / 100.0 * MIX_MAX_VOLUME));
|
||||||
SDL_LockAudioDevice(device);
|
SDL_LockAudioDevice(device);
|
||||||
if (speed > 0.0f) {
|
UpdateST();
|
||||||
st->setRate(speed);
|
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 (tempo > 0.0f) {
|
if (too_large || too_small) {
|
||||||
st->setTempo(tempo);
|
printf("Resizing buffer...\n");
|
||||||
|
general_mixer = nullptr;
|
||||||
|
bufsize = 0;
|
||||||
|
buf = (Uint8*)realloc((void*)buf, correct_buf_size);
|
||||||
|
if (buf == NULL) {
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
if (pitch > 0.0f) {
|
bufsize = correct_buf_size;
|
||||||
st->setPitch(pitch);
|
|
||||||
}
|
}
|
||||||
SDL_UnlockAudioDevice(device);
|
SDL_UnlockAudioDevice(device);
|
||||||
}
|
}
|
||||||
|
@ -103,13 +169,16 @@ void Playback::ThreadFunc() {
|
||||||
position = Mix_GetMusicPosition(music);
|
position = Mix_GetMusicPosition(music);
|
||||||
std::this_thread::sleep_for(20ms);
|
std::this_thread::sleep_for(20ms);
|
||||||
}
|
}
|
||||||
|
playback_ready.store(false);
|
||||||
// ====
|
// ====
|
||||||
Unload(music);
|
Unload(music);
|
||||||
|
SDL_CloseAudioDevice(device);
|
||||||
Mix_CloseAudio();
|
Mix_CloseAudio();
|
||||||
Mix_Quit();
|
Mix_Quit();
|
||||||
SDL_CloseAudioDevice(device);
|
SDL_QuitSubSystem(SDL_INIT_AUDIO);
|
||||||
delete st;
|
delete st;
|
||||||
free(buf);
|
free(buf);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Playback::Playback() {
|
Playback::Playback() {
|
||||||
|
@ -131,6 +200,8 @@ void Playback::Start(std::string filePath) {
|
||||||
this->filePath = filePath;
|
this->filePath = filePath;
|
||||||
printf("Playing %s...\n", filePath.c_str());
|
printf("Playing %s...\n", filePath.c_str());
|
||||||
flag_mutex.lock();
|
flag_mutex.lock();
|
||||||
|
this->position = 0.0;
|
||||||
|
seeking.store(true);
|
||||||
paused = false;
|
paused = false;
|
||||||
Update();
|
Update();
|
||||||
if (running.exchange(true)) {
|
if (running.exchange(true)) {
|
||||||
|
@ -169,7 +240,6 @@ void Playback::Stop() {
|
||||||
thread.join();
|
thread.join();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Playback::Update() {
|
void Playback::Update() {
|
||||||
update.store(true);
|
update.store(true);
|
||||||
}
|
}
|
||||||
|
|
13
playback.h
13
playback.h
|
@ -9,8 +9,10 @@
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
#include <SoundTouch.h>
|
#include <SoundTouch.h>
|
||||||
#include <span>
|
#include <span>
|
||||||
|
#include <optional>
|
||||||
using namespace soundtouch;
|
using namespace soundtouch;
|
||||||
using std::span;
|
using std::span;
|
||||||
|
using std::optional;
|
||||||
class Playback {
|
class Playback {
|
||||||
private:
|
private:
|
||||||
std::string filePath;
|
std::string filePath;
|
||||||
|
@ -18,6 +20,8 @@ private:
|
||||||
std::atomic_bool file_changed;
|
std::atomic_bool file_changed;
|
||||||
std::atomic_bool seeking;
|
std::atomic_bool seeking;
|
||||||
std::atomic_bool update;
|
std::atomic_bool update;
|
||||||
|
std::atomic_bool restart;
|
||||||
|
std::atomic_bool playback_ready;
|
||||||
std::mutex flag_mutex;
|
std::mutex flag_mutex;
|
||||||
std::thread thread;
|
std::thread thread;
|
||||||
double position;
|
double position;
|
||||||
|
@ -34,6 +38,8 @@ private:
|
||||||
Mix_Music *Load(const char* file);
|
Mix_Music *Load(const char* file);
|
||||||
void Unload(Mix_Music* music);
|
void Unload(Mix_Music* music);
|
||||||
void ThreadFunc();
|
void ThreadFunc();
|
||||||
|
void UpdateST();
|
||||||
|
double GetMaxSeconds();
|
||||||
public:
|
public:
|
||||||
Playback();
|
Playback();
|
||||||
~Playback();
|
~Playback();
|
||||||
|
@ -50,4 +56,11 @@ public:
|
||||||
float speed;
|
float speed;
|
||||||
float tempo;
|
float tempo;
|
||||||
float pitch;
|
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