Use CSD in ImGui backend, update QT backend, prepare for sample-based positioning, and disable multiprocess on Emscripten
Some checks failed
Build / build-gentoo (push) Successful in 1m56s
Build / download-system-deps (push) Successful in 5m25s
Build / get-source-code (push) Successful in 12m33s
Build / build-deb (push) Failing after 5m56s
Build / build-appimage (push) Successful in 4m47s
Build / build-android (push) Failing after 3m11s
Build / build-windows (push) Has been cancelled

This commit is contained in:
Zachary Hall 2025-01-14 15:01:53 -08:00
parent 6920d3c0ff
commit 0b2b6bc459
18 changed files with 202 additions and 57 deletions

View file

@ -136,7 +136,7 @@ void FluidSynthBackend::load(const char *filename) {
} }
sample_positions.push_back({samples, cur_pos}); sample_positions.push_back({samples, cur_pos});
} }
this->length = ((double)samples) / 96000.0; this->length = samples;
} }
extern SDL_AudioSpec obtained; extern SDL_AudioSpec obtained;
void FluidSynthBackend::switch_stream(int idx) { void FluidSynthBackend::switch_stream(int idx) {
@ -158,7 +158,7 @@ void FluidSynthBackend::cleanup() {
size_t FluidSynthBackend::render(void *buf, size_t maxlen) { size_t FluidSynthBackend::render(void *buf, size_t maxlen) {
size_t sample_type_len = sizeof(float); size_t sample_type_len = sizeof(float);
maxlen /= sample_type_len * 2; maxlen /= sample_type_len * 2;
position += ((double)maxlen) / 96000.0; position += maxlen;
maxlen *= sample_type_len * 2; maxlen *= sample_type_len * 2;
if (fluid_synth_write_float(synth, maxlen / 2 / sample_type_len, buf, 0, 2, buf, 1, 2) == FLUID_FAILED) { if (fluid_synth_write_float(synth, maxlen / 2 / sample_type_len, buf, 0, 2, buf, 1, 2) == FLUID_FAILED) {
return 0; return 0;
@ -187,9 +187,9 @@ bool FluidSynthBackend::is_fluidsynth_setting(std::string path) {
if (path.length() < strlen(prefix)) return false; if (path.length() < strlen(prefix)) return false;
return path.substr(0, strlen(prefix)) == std::string(prefix); return path.substr(0, strlen(prefix)) == std::string(prefix);
} }
void FluidSynthBackend::seek(double position) { void FluidSynthBackend::seek_samples(uint64_t position) {
size_t tick = 0; size_t tick = 0;
size_t sample = position * 96000; size_t sample = position;
size_t prev_sample = 0; size_t prev_sample = 0;
size_t next_sample = 0; size_t next_sample = 0;
for (auto &pair : sample_positions) { for (auto &pair : sample_positions) {
@ -197,11 +197,11 @@ void FluidSynthBackend::seek(double position) {
next_sample = pair.first; next_sample = pair.first;
if (next_sample > sample) { if (next_sample > sample) {
tick = pair.second - 1; tick = pair.second - 1;
this->position = ((double)prev_sample) / 96000.0; this->position = prev_sample;
break; break;
} else if (next_sample == sample) { } else if (next_sample == sample) {
tick = pair.second; tick = pair.second;
this->position = ((double)next_sample) / 96000.0; this->position = next_sample;
prev_sample = next_sample; prev_sample = next_sample;
break; break;
} }
@ -211,7 +211,7 @@ void FluidSynthBackend::seek(double position) {
if (sample > prev_sample) fluid_synth_write_float(synth, sample - prev_sample, &fakebuf, 0, 0, &fakebuf, 0, 0); if (sample > prev_sample) fluid_synth_write_float(synth, sample - prev_sample, &fakebuf, 0, 0, &fakebuf, 0, 0);
this->position = position; this->position = position;
} }
double FluidSynthBackend::get_position() { uint64_t FluidSynthBackend::get_position_samples() {
return position; return position;
} }
int FluidSynthBackend::get_stream_idx() { int FluidSynthBackend::get_stream_idx() {

View file

@ -127,12 +127,12 @@ class FluidSynthBackend : public PlaybackBackend {
return "MIDI player"; return "MIDI player";
} }
std::vector<Property> get_property_list() override; std::vector<Property> get_property_list() override;
void seek(double position) override; void seek_samples(uint64_t position) override;
void load(const char *filename) override; void load(const char *filename) override;
void switch_stream(int idx) override; void switch_stream(int idx) override;
void cleanup() override; void cleanup() override;
int get_stream_idx() override; int get_stream_idx() override;
size_t render(void *buf, size_t maxlen) override; size_t render(void *buf, size_t maxlen) override;
double get_position() override; uint64_t get_position_samples() override;
inline ~FluidSynthBackend() override { } inline ~FluidSynthBackend() override { }
}; };

View file

@ -113,7 +113,7 @@ size_t SDLMixerXBackend::render(void *buf, size_t maxlen) {
} }
return i; return i;
} }
double SDLMixerXBackend::get_position() { double SDLMixerXBackend::get_position_time() {
if (music == nullptr || file == nullptr) { if (music == nullptr || file == nullptr) {
open = false; open = false;
return 0.0; return 0.0;

View file

@ -16,7 +16,13 @@ class SDLMixerXBackend : public PlaybackBackend {
} }
std::optional<uint64_t> get_max_samples() override; std::optional<uint64_t> get_max_samples() override;
void seek(double position) override; void seek(double position) override;
double get_position() override; inline void seek_samples(uint64_t position) override {
seek(samples_to_time(position));
}
double get_position_time() override;
inline uint64_t get_position_samples() override {
return time_to_samples(get_position_time());
}
void load(const char *filename) override; void load(const char *filename) override;
void switch_stream(int idx) override; void switch_stream(int idx) override;
void cleanup() override; void cleanup() override;

View file

@ -115,23 +115,23 @@ size_t VgmStreamBackend::render(void *buf, size_t maxlen) {
size_t new_samples = render_vgmstream((sample_t*)(buf), (int)maxlen, vf); size_t new_samples = render_vgmstream((sample_t*)(buf), (int)maxlen, vf);
if (maxlen > new_samples) { if (maxlen > new_samples) {
reset_vgmstream(vf); reset_vgmstream(vf);
position = 0.0; position = 0;
} else { } else {
position += (double)new_samples / (double)vf->sample_rate; position += new_samples;
} }
return new_samples * sizeof(sample_t); return new_samples * sizeof(sample_t);
} }
void VgmStreamBackend::seek(double position) { void VgmStreamBackend::seek_samples(uint64_t position) {
if (vf == nullptr || sf == nullptr || file == nullptr) { if (vf == nullptr || sf == nullptr || file == nullptr) {
open = false; open = false;
return; return;
} }
auto pos32 = (int32_t)(position * vf->sample_rate); auto pos32 = (int32_t)(position);
seek_vgmstream(vf, pos32); seek_vgmstream(vf, pos32);
this->position = position; this->position = position;
} }
double VgmStreamBackend::get_position() { uint64_t VgmStreamBackend::get_position_samples() {
return position; return position;
} }
int VgmStreamBackend::get_stream_idx() { int VgmStreamBackend::get_stream_idx() {

View file

@ -17,13 +17,13 @@ class VgmStreamBackend : public PlaybackBackend {
inline std::string get_name() override { inline std::string get_name() override {
return "VGMStream"; return "VGMStream";
} }
void seek(double position) override; void seek_samples(uint64_t position) override;
void load(const char *filename) override; void load(const char *filename) override;
void switch_stream(int idx) override; void switch_stream(int idx) override;
void cleanup() override; void cleanup() override;
int get_stream_idx() override; int get_stream_idx() override;
size_t render(void *buf, size_t maxlen) override; size_t render(void *buf, size_t maxlen) override;
double get_position() override; uint64_t get_position_samples() override;
void add_licenses() override; void add_licenses() override;
inline ~VgmStreamBackend() override { } inline ~VgmStreamBackend() override { }
}; };

View file

@ -106,31 +106,31 @@ void ZsmBackend::load(const char *filename) {
} }
file->seek(music_data_start, SeekType::SET); file->seek(music_data_start, SeekType::SET);
this->loop_point = std::max(this->loop_point, (uint32_t)music_data_start); this->loop_point = std::max(this->loop_point, (uint32_t)music_data_start);
double prev_time = 0.0; uint64_t prev_time = 0;
double time = 0.0; uint64_t time = 0;
double tmpDelayTicks = 0.0; double tmpDelayTicks = 0.0;
loop_pos = -1.0; loop_pos = UINT64_MAX;
uint32_t prev_pos = music_data_start; uint32_t prev_pos = music_data_start;
while (true) { while (true) {
tmpDelayTicks -= get_delay_per_frame(); tmpDelayTicks -= get_delay_per_frame();
if (tmpDelayTicks < 0.0) { if (tmpDelayTicks < 0.0) {
ZsmCommand cmd = get_command(); ZsmCommand cmd = get_command();
size_t cur_pos = file->get_pos(); size_t cur_pos = file->get_pos();
if (cur_pos >= this->loop_point && this->loop_pos < 0) { if (cur_pos >= this->loop_point && this->loop_pos == UINT64_MAX) {
loop_pos = time; loop_pos = time;
this->loop_point = cur_pos; this->loop_point = cur_pos;
} }
if (cmd.id == ZsmEOF) { if (cmd.id == ZsmEOF) {
break; break;
} else if (cmd.id == Delay) { } else if (cmd.id == Delay) {
time += ((double)cmd.delay) / ((double)(tick_rate)); time += (((double)cmd.delay) / ((double)(tick_rate))) * PSG_FREQ;
tmpDelayTicks += cmd.delay; tmpDelayTicks += cmd.delay;
} }
prev_pos = file->get_pos(); prev_pos = file->get_pos();
prev_time = time; prev_time = time;
} }
} }
if (this->loop_pos < 0.0) { if (this->loop_pos == UINT64_MAX) {
this->loop_pos = 0.0; this->loop_pos = 0.0;
this->loop_point = music_data_start; this->loop_point = music_data_start;
} }
@ -223,7 +223,7 @@ void ZsmBackend::tick(bool step) {
} break; } break;
case Delay: { case Delay: {
delayTicks += cmd.delay; delayTicks += cmd.delay;
position += ((double)cmd.delay) / ((double)(tick_rate)); position += (((double)cmd.delay) / ((double)(tick_rate))) * spec.freq;
} break; } break;
case ExtCmd: { case ExtCmd: {
//cmd.extcmd.channel //cmd.extcmd.channel
@ -384,16 +384,14 @@ ZsmCommand::~ZsmCommand() {
} }
} }
} }
void ZsmBackend::seek_internal(double position, bool loop) { void ZsmBackend::seek_internal(uint64_t position, bool loop) {;
this->position = std::floor(this->position * PSG_FREQ) / PSG_FREQ;
position = std::floor(position * PSG_FREQ) / PSG_FREQ;
if (this->position > position) { if (this->position > position) {
switch_stream(0); switch_stream(0);
file->seek(music_data_start, SeekType::SET); file->seek(music_data_start, SeekType::SET);
this->cpuClocks = 0.0; this->cpuClocks = 0.0;
this->delayTicks = 0; this->delayTicks = 0;
this->ticks = 0.0; this->ticks = 0.0;
this->position = 0.0; this->position = 0;
} else if (this->position == position) { } else if (this->position == position) {
audio_buf.clear(); audio_buf.clear();
return; return;
@ -408,7 +406,7 @@ void ZsmBackend::seek_internal(double position, bool loop) {
this->cpuClocks = 0.0; this->cpuClocks = 0.0;
this->delayTicks = 0; this->delayTicks = 0;
this->ticks = 0.0; this->ticks = 0.0;
this->position = 0.0; this->position = 0;
audio_buf.clear(); audio_buf.clear();
return; return;
} }
@ -419,15 +417,18 @@ void ZsmBackend::seek_internal(double position, bool loop) {
} }
this->position = position; this->position = position;
} }
void ZsmBackend::seek(double position) { void ZsmBackend::seek_samples(uint64_t position) {
seek_internal(position, false); seek_internal(position, false);
} }
double ZsmBackend::get_position() { uint64_t ZsmBackend::get_position_samples() {
return position; return position;
} }
int ZsmBackend::get_stream_idx() { int ZsmBackend::get_stream_idx() {
return 0; return 0;
} }
uint64_t ZsmBackend::get_loop_start_samples() {
return loop_start;
}
void ZsmBackend::audio_step(size_t samples) { void ZsmBackend::audio_step(size_t samples) {
if (samples == 0) return; if (samples == 0) return;

View file

@ -108,7 +108,7 @@ class ZsmBackend : public PlaybackBackend {
return audio_buf.pop((int16_t*)buf, len); return audio_buf.pop((int16_t*)buf, len);
} }
uint32_t loop_point; uint32_t loop_point;
double loop_pos = 0.0; uint64_t loop_pos = 0;
uint32_t pcm_offset; uint32_t pcm_offset;
uint8_t fm_mask; uint8_t fm_mask;
uint16_t psg_channel_mask; uint16_t psg_channel_mask;
@ -123,7 +123,7 @@ class ZsmBackend : public PlaybackBackend {
return 1.0; return 1.0;
} }
void tick(bool step = true);\ void tick(bool step = true);\
void seek_internal(double position, bool loop = true); void seek_internal(uint64_t position, bool loop = true);
ZsmCommand get_command(); ZsmCommand get_command();
public: public:
uint64_t get_min_samples() override; uint64_t get_min_samples() override;
@ -136,15 +136,16 @@ class ZsmBackend : public PlaybackBackend {
} }
void add_licenses() override; void add_licenses() override;
std::vector<Property> get_property_list() override; std::vector<Property> get_property_list() override;
void seek(double position) override; void seek_samples(uint64_t position) override;
void load(const char *filename) override; void load(const char *filename) override;
void switch_stream(int idx) override; void switch_stream(int idx) override;
void cleanup() override; void cleanup() override;
int get_stream_idx() override; int get_stream_idx() override;
size_t render(void *buf, size_t maxlen) override; size_t render(void *buf, size_t maxlen) override;
double get_position() override; uint64_t get_position_samples() override;
inline double get_length() override { inline uint64_t get_length_samples() override {
return length; return length;
} }
uint64_t get_loop_start_samples() override;
inline ~ZsmBackend() override { } inline ~ZsmBackend() override { }
}; };

View file

@ -151,13 +151,23 @@ void RendererBackend::LoopFunction() {
ImGui::NewFrame(); ImGui::NewFrame();
// Run the GUI // Run the GUI
GuiFunction(); GuiFunction();
if (!main_menu_bar_used) {
BeginMainMenuBar();
EndMainMenuBar();
}
// Rendering // Rendering
ImGui::Render(); ImGui::Render();
SDL_SetRenderDrawColor(rend, (Uint8)(clear_color.x * clear_color.w * 255), (Uint8)(clear_color.y * clear_color.w * 255), (Uint8)(clear_color.z * clear_color.w * 255), (Uint8)(clear_color.w * 255)); SDL_SetRenderDrawColor(rend, (Uint8)(clear_color.x * clear_color.w * 255), (Uint8)(clear_color.y * clear_color.w * 255), (Uint8)(clear_color.z * clear_color.w * 255), (Uint8)(clear_color.w * 255));
SDL_RenderClear(rend); SDL_RenderClear(rend);
// Tell ImGui to render. // Tell ImGui to render.
ImGui_ImplSDLRenderer2_RenderDrawData(ImGui::GetDrawData(), rend); ImGui_ImplSDLRenderer2_RenderDrawData(ImGui::GetDrawData(), rend);
SDL_Rect rect;
SDL_GetWindowSize(window, &rect.w, &rect.h);
rect.x = 0;
rect.y = 0;
ImVec4 color = ImGui::GetStyle().Colors[ImGuiCol_Border];
SDL_SetRenderDrawColor(rend, color.x * 255, color.y * 255, color.z * 255, color.w * 255);
SDL_RenderDrawRect(rend, &rect);
// Swap the buffers, and do VSync if enabled. // Swap the buffers, and do VSync if enabled.
SDL_RenderPresent(rend); SDL_RenderPresent(rend);
@ -291,6 +301,10 @@ static EM_BOOL resize_callback(int event_type, const EmscriptenUiEvent *event, v
return EM_FALSE; return EM_FALSE;
} }
#endif #endif
static SDL_HitTestResult hit_test(SDL_Window *window, const SDL_Point *area, void *data) {
return ((RendererBackend*)data)->HitTest(window, area);
}
void RendererBackend::BackendInit() { void RendererBackend::BackendInit() {
setup_locale("neko_player"); setup_locale("neko_player");
DEBUG.writefln("Loaded locale '%s' from '%s'...", CURRENT_LANGUAGE, LOCALE_DIR); DEBUG.writefln("Loaded locale '%s' from '%s'...", CURRENT_LANGUAGE, LOCALE_DIR);
@ -319,7 +333,7 @@ void RendererBackend::BackendInit() {
#ifdef SDL_HINT_IME_SHOW_UI #ifdef SDL_HINT_IME_SHOW_UI
SDL_SetHint(SDL_HINT_IME_SHOW_UI, "1"); SDL_SetHint(SDL_HINT_IME_SHOW_UI, "1");
#endif #endif
SDL_WindowFlags window_flags = (SDL_WindowFlags)(SDL_WINDOW_RESIZABLE | SDL_WINDOW_ALLOW_HIGHDPI | SDL_WINDOW_HIDDEN); SDL_WindowFlags window_flags = (SDL_WindowFlags)(SDL_WINDOW_RESIZABLE | SDL_WINDOW_ALLOW_HIGHDPI | SDL_WINDOW_HIDDEN | SDL_WINDOW_BORDERLESS);
SDL_CreateWindowAndRenderer(window_width, window_height, window_flags, &window, &rend); SDL_CreateWindowAndRenderer(window_width, window_height, window_flags, &window, &rend);
#ifndef __ANDROID__ #ifndef __ANDROID__
@ -330,6 +344,7 @@ void RendererBackend::BackendInit() {
#endif #endif
SDL_EventState(SDL_DROPFILE, SDL_ENABLE); SDL_EventState(SDL_DROPFILE, SDL_ENABLE);
SDL_Surface* icon = IMG_Load_RW(SDL_RWFromConstMem(icon_data, icon_size), 1); SDL_Surface* icon = IMG_Load_RW(SDL_RWFromConstMem(icon_data, icon_size), 1);
icon_texture = SDL_CreateTextureFromSurface(rend, icon);
SDL_SetWindowIcon(window, icon); SDL_SetWindowIcon(window, icon);
// Setup Dear ImGui context // Setup Dear ImGui context
@ -389,10 +404,88 @@ void RendererBackend::BackendInit() {
SDL_RenderSetVSync(rend, vsync ? 1 : 0); SDL_RenderSetVSync(rend, vsync ? 1 : 0);
#endif #endif
theme->Apply(accent_color, (float)scale); theme->Apply(accent_color, (float)scale);
SDL_SetWindowHitTest(window, &hit_test, this);
Init(); Init();
SDL_ShowWindow(window); SDL_ShowWindow(window);
started = true; started = true;
} }
bool RendererBackend::BeginMainMenuBar() {
main_menu_bar_used = true;
if (ImGui::BeginMainMenuBar()) {
ImVec2 winsize = ImGui::GetWindowSize();
float texsize = winsize.y;
ImGui::SetCursorPosX(0);
ImGui::SetCursorPosY(0);
ImGui::Image((ImTextureID)(icon_texture), ImVec2(texsize, texsize));
//ImGui::SetCursorPosY((winsize.y - ImGui::GetFrameHeight()) / 2);
ImGui::TextUnformatted(SDL_GetWindowTitle(window));
menubar_start = ImGui::GetCursorPosX();
return true;
} else {
return false;
}
}
SDL_HitTestResult RendererBackend::HitTest(SDL_Window *window, const SDL_Point *area) {
int w, h;
bool rtop, rbottom, rleft, rright;
SDL_GetWindowSize(window, &w, &h);
rtop = area->y <= 4;
rleft = area->x <= 4;
rright = area->x >= w-5;
rbottom = area->y >= h-5;
if (rtop) {
if (rright) {
return SDL_HITTEST_RESIZE_TOPRIGHT;
} else if (rleft) {
return SDL_HITTEST_RESIZE_TOPLEFT;
} else {
return SDL_HITTEST_RESIZE_TOP;
}
} else if (rbottom) {
if (rright) {
return SDL_HITTEST_RESIZE_BOTTOMRIGHT;
} else if (rleft) {
return SDL_HITTEST_RESIZE_BOTTOMLEFT;
} else {
return SDL_HITTEST_RESIZE_BOTTOM;
}
} else if (rleft) {
return SDL_HITTEST_RESIZE_LEFT;
} else if (rright) {
return SDL_HITTEST_RESIZE_RIGHT;
} else if (area->y < (16 * this->scale) && (area->x < menubar_start || (area->x > menubar_end && area->x < title_btn_start))) {
return SDL_HITTEST_DRAGGABLE;
} else {
return SDL_HITTEST_NORMAL;
}
}
void RendererBackend::EndMainMenuBar() {
#ifndef __EMSCRIPTEN
menubar_end = ImGui::GetCursorPosX();
ImVec2 size = ImGui::GetWindowSize();
float btnw = size.y;
int btn_count = 3;
ImVec2 btn_size(btnw, btnw);
ImGui::SetCursorPosY(0);
ImGui::SetCursorPosX(size.x - (btnw * 3));
if (ImGui::Button(ICON_FK_WINDOW_MINIMIZE, btn_size)) {
SDL_MinimizeWindow(window);
}
ImGui::SetCursorPosX(size.x - (btnw * 2));
if (SDL_GetWindowFlags(window) & SDL_WINDOW_MAXIMIZED) {
if (ImGui::Button(ICON_FK_WINDOW_RESTORE, btn_size)) SDL_RestoreWindow(window);
} else {
if (ImGui::Button(ICON_FK_WINDOW_MAXIMIZE, btn_size)) SDL_MaximizeWindow(window);
}
ImGui::SetCursorPosX(size.x - btnw);
if (ImGui::Button(ICON_FK_WINDOW_CLOSE, btn_size)) {
done = true;
}
title_btn_start = size.x - (btnw * 3);
#endif
ImGui::EndMainMenuBar();
}
int RendererBackend::Run() { int RendererBackend::Run() {
framerate = 60; framerate = 60;
started = false; started = false;

View file

@ -25,7 +25,14 @@ class RendererBackend {
bool resize_needed = true; bool resize_needed = true;
void on_resize(); void on_resize();
bool update_scale = false; bool update_scale = false;
SDL_Texture *icon_texture;
bool main_menu_bar_used = false;
int menubar_start;
int menubar_end;
int title_btn_start;
public: public:
int window_border_radius = 8;
SDL_HitTestResult HitTest(SDL_Window *window, const SDL_Point *area);
std::optional<bool> touchScreenModeOverride; std::optional<bool> touchScreenModeOverride;
std::optional<double> scaleOverride; std::optional<double> scaleOverride;
bool isTouchScreenMode(); bool isTouchScreenMode();
@ -57,6 +64,8 @@ class RendererBackend {
void AddFonts(); void AddFonts();
void SetWindowSize(int w, int h); void SetWindowSize(int w, int h);
void GetWindowsize(int *w, int *h); void GetWindowsize(int *w, int *h);
bool BeginMainMenuBar();
void EndMainMenuBar();
RendererBackend(); RendererBackend();
virtual ~RendererBackend(); virtual ~RendererBackend();
friend void main_loop(); friend void main_loop();

View file

@ -225,7 +225,7 @@ void MainLoop::GuiFunction() {
// 1. Show the big demo window (Most of the sample code is in ImGui::ShowDemoWindow()! You can browse its code to learn more about Dear ImGui!). // 1. Show the big demo window (Most of the sample code is in ImGui::ShowDemoWindow()! You can browse its code to learn more about Dear ImGui!).
if (show_demo_window) if (show_demo_window)
ImGui::ShowDemoWindow(&show_demo_window); ImGui::ShowDemoWindow(&show_demo_window);
if (ImGui::BeginMainMenuBar()) { if (BeginMainMenuBar()) {
if (ImGui::BeginMenu(_TRI_CTX(ICON_FK_FILE, "Main menu", "File"))) { if (ImGui::BeginMenu(_TRI_CTX(ICON_FK_FILE, "Main menu", "File"))) {
if (ImGui::MenuItem(_TRI_CTX(ICON_FK_FOLDER_OPEN, "Main menu | File", "Open"))) { if (ImGui::MenuItem(_TRI_CTX(ICON_FK_FOLDER_OPEN, "Main menu | File", "Open"))) {
// Set translatable strings here so that they are in the correct language even when it changes at runtime. // Set translatable strings here so that they are in the correct language even when it changes at runtime.
@ -274,7 +274,7 @@ void MainLoop::GuiFunction() {
} }
ImGui::EndMenu(); ImGui::EndMenu();
} }
ImGui::EndMainMenuBar(); EndMainMenuBar();
} }
ImGui::SetNextWindowDockID(dockid); ImGui::SetNextWindowDockID(dockid);
ImGui::Begin(_TRI_CTX(ICON_FK_PLAY, "Main window title", "Player"), nullptr, 0); ImGui::Begin(_TRI_CTX(ICON_FK_PLAY, "Main window title", "Player"), nullptr, 0);

View file

@ -87,7 +87,11 @@ LooperWindow::LooperWindow(Playback *playback) : QMainWindow() {
cat_disp->setAlignment(Qt::Alignment::enum_type::AlignRight); cat_disp->setAlignment(Qt::Alignment::enum_type::AlignRight);
cat_disp->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Ignored); cat_disp->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Ignored);
root_layout->addWidget(cat_disp); root_layout->addWidget(cat_disp);
QBoxLayout *top_row = new QBoxLayout(QBoxLayout::LeftToRight, this); QWidget *controls_widget = new QWidget(this);
QBoxLayout *controls_layout = new QBoxLayout(QBoxLayout::TopToBottom, controls_widget);
controls_widget->setLayout(controls_layout);
controls_widget->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Fixed);
QBoxLayout *top_row = new QBoxLayout(QBoxLayout::LeftToRight, controls_widget);
pause_resume_btn = new QPushButton("Pause", this); pause_resume_btn = new QPushButton("Pause", this);
QObject::connect(pause_resume_btn, &QPushButton::pressed, [=,this]() { QObject::connect(pause_resume_btn, &QPushButton::pressed, [=,this]() {
playback->Pause(); playback->Pause();
@ -119,7 +123,7 @@ LooperWindow::LooperWindow(Playback *playback) : QMainWindow() {
QWidget *top_row_widget = new QWidget(this); QWidget *top_row_widget = new QWidget(this);
top_row_widget->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Minimum); top_row_widget->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Minimum);
top_row_widget->setLayout(top_row); top_row_widget->setLayout(top_row);
root_layout->addWidget(top_row_widget); controls_layout->addWidget(top_row_widget);
QBoxLayout *bottom_row = new QBoxLayout(QBoxLayout::LeftToRight, this); QBoxLayout *bottom_row = new QBoxLayout(QBoxLayout::LeftToRight, this);
speed_slider = new LooperSlider("speed", "Speed", 0.25, 4.0, 0.01, true); speed_slider = new LooperSlider("speed", "Speed", 0.25, 4.0, 0.01, true);
pitch_slider = new LooperSlider("pitch", "Pitch", 0.25, 4.0, 0.01, true); pitch_slider = new LooperSlider("pitch", "Pitch", 0.25, 4.0, 0.01, true);
@ -142,7 +146,8 @@ LooperWindow::LooperWindow(Playback *playback) : QMainWindow() {
QWidget *bottom_row_widget = new QWidget(this); QWidget *bottom_row_widget = new QWidget(this);
bottom_row_widget->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Minimum); bottom_row_widget->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Minimum);
bottom_row_widget->setLayout(bottom_row); bottom_row_widget->setLayout(bottom_row);
root_layout->addWidget(bottom_row_widget); controls_layout->addWidget(bottom_row_widget);
root_layout->addWidget(controls_widget);
QTimer *timer = new QTimer(this); QTimer *timer = new QTimer(this);
QObject::connect(timer, &QTimer::timeout, [=,this]() { QObject::connect(timer, &QTimer::timeout, [=,this]() {
Pulse(); Pulse();

View file

@ -26,6 +26,8 @@ using namespace Looper::Log;
extern "C" { extern "C" {
void quit(); void quit();
} }
#else
#include <curl/curl.h>
#endif #endif
std::vector<CatData> &get_cat_data() { std::vector<CatData> &get_cat_data() {
static std::vector<CatData> data; static std::vector<CatData> data;
@ -373,6 +375,9 @@ int main(int argc, char **argv) {
app.add_option("-l, --log-level", LogStream::log_level, "Sets the minimum log level to display in the logs."); app.add_option("-l, --log-level", LogStream::log_level, "Sets the minimum log level to display in the logs.");
app.allow_extras(); app.allow_extras();
init_logging(); init_logging();
#ifndef __EMSCRIPTEN__
//curl_global_init(CURL_GLOBAL_ALL);
#endif
try { try {
app.parse(argc, argv); app.parse(argc, argv);
executable_path = argv[0]; executable_path = argv[0];

View file

@ -6,6 +6,7 @@
#include <optional> #include <optional>
#include <SDL.h> #include <SDL.h>
#include <iterator> #include <iterator>
#include <limits.h>
#include <limits> #include <limits>
#include "util.hpp" #include "util.hpp"
#include "ipc/internal.pb.h" #include "ipc/internal.pb.h"
@ -20,13 +21,13 @@ class PlaybackBackend {
size_t minSamples; size_t minSamples;
size_t maxSamples; size_t maxSamples;
protected: protected:
double length; uint64_t length;
std::vector<PlaybackStream> streams; std::vector<PlaybackStream> streams;
std::string current_file; std::string current_file;
std::string current_title; std::string current_title;
bool open; bool open;
SDL_AudioSpec spec; SDL_AudioSpec spec;
double position; uint64_t position;
std::map<std::string, google::protobuf::Any> property_defaults; std::map<std::string, google::protobuf::Any> property_defaults;
std::map<std::string, google::protobuf::Any> properties; std::map<std::string, google::protobuf::Any> properties;
size_t min_sample_estimate; size_t min_sample_estimate;
@ -34,8 +35,8 @@ class PlaybackBackend {
size_t max_sample_requirement = std::numeric_limits<size_t>::max(); size_t max_sample_requirement = std::numeric_limits<size_t>::max();
size_t min_sample_requirement = std::numeric_limits<size_t>::min(); size_t min_sample_requirement = std::numeric_limits<size_t>::min();
double rate; double rate;
double loop_start = 0.0; uint64_t loop_start = 0;
double loop_end = -1.0; uint64_t loop_end = UINT64_MAX;
void setMinSamples(size_t samples) { void setMinSamples(size_t samples) {
this->minSamples = samples; this->minSamples = samples;
adjustSampleEstimates(); adjustSampleEstimates();
@ -53,6 +54,12 @@ class PlaybackBackend {
max_sample_estimate = (size_t)ceill(tmpMaxSamples * tmpRate); max_sample_estimate = (size_t)ceill(tmpMaxSamples * tmpRate);
if (max_sample_estimate > max_sample_requirement) max_sample_estimate = max_sample_requirement; if (max_sample_estimate > max_sample_requirement) max_sample_estimate = max_sample_requirement;
} }
inline double samples_to_time(uint64_t samples) {
return ((double)samples) / ((double)spec.freq);
}
inline uint64_t time_to_samples(double time) {
return time * spec.freq;
}
public: public:
using map = std::map<std::string, PlaybackBackend*>; using map = std::map<std::string, PlaybackBackend*>;
using iterator = map::iterator; using iterator = map::iterator;
@ -62,8 +69,12 @@ class PlaybackBackend {
inline virtual void add_licenses() { } inline virtual void add_licenses() { }
inline virtual std::string get_id() {return "";} inline virtual std::string get_id() {return "";}
inline virtual std::string get_name() {return "";} inline virtual std::string get_name() {return "";}
inline virtual void seek(double position) { } inline virtual void seek(double position) { seek_samples(time_to_samples(position)); }
inline virtual double get_position() { inline virtual void seek_samples(uint64_t position) { }
inline virtual double get_position_time() {
return ((double)position) / ((double)spec.freq);
}
inline virtual uint64_t get_position_samples() {
return position; return position;
} }
inline virtual SDL_AudioSpec get_spec() { inline virtual SDL_AudioSpec get_spec() {
@ -102,13 +113,22 @@ class PlaybackBackend {
virtual void cleanup(); virtual void cleanup();
virtual size_t render(void *buf, size_t maxlen); virtual size_t render(void *buf, size_t maxlen);
inline virtual double get_length() { inline virtual double get_length() {
return open ? length : 0.0; return samples_to_time(get_length_samples());
}
inline virtual uint64_t get_length_samples() {
return open ? length : 0;
}
inline virtual uint64_t get_loop_start_samples() {
return open ? loop_start : 0;
} }
inline virtual double get_loop_start() { inline virtual double get_loop_start() {
return open ? loop_start : 0.0; return samples_to_time(get_loop_start_samples());
}
inline virtual uint64_t get_loop_end_samples() {
return open ? loop_end == UINT64_MAX ? get_length_samples() : loop_end : 0;
} }
inline virtual double get_loop_end() { inline virtual double get_loop_end() {
return open ? loop_end < 0.0 ? get_length() : loop_end : 0.0; return samples_to_time(get_loop_end_samples());
} }
inline virtual std::optional<std::string> get_current_file() { inline virtual std::optional<std::string> get_current_file() {
return open ? current_file : std::optional<std::string>(); return open ? current_file : std::optional<std::string>();

View file

@ -184,7 +184,7 @@ PropertyDataOrError PlaybackProcessServiceImpl::Get(const GetProperty *request)
} break; } break;
case PropertyId::PositionProperty: { case PropertyId::PositionProperty: {
DoubleProperty pos; DoubleProperty pos;
pos.set_value(cur_backend->get_position()); pos.set_value(cur_backend->get_position_time());
data->mutable_value()->PackFrom(pos); data->mutable_value()->PackFrom(pos);
} break; } break;
case PropertyId::BackendSpecific: { case PropertyId::BackendSpecific: {
@ -338,6 +338,7 @@ MaybeError PlaybackProcessServiceImpl::Init(const InitCommand *cmd) {
} }
lock.set(backend.second, false); lock.set(backend.second, false);
DEBUG.writefln("Using backend: %s", backend.second->get_name().c_str()); DEBUG.writefln("Using backend: %s", backend.second->get_name().c_str());
backend.second->seek(0.0);
break; break;
} }
if (!cur_backend_lock.has_value()) { if (!cur_backend_lock.has_value()) {
@ -419,7 +420,11 @@ PlaybackProcess::PlaybackProcess(std::vector<std::string> args) {
} }
PlaybackProcess::PlaybackProcess(std::string filename, int idx) { PlaybackProcess::PlaybackProcess(std::string filename, int idx) {
// multi_process = Looper::Options::get_option<bool>("playback.multi_process", true); // multi_process = Looper::Options::get_option<bool>("playback.multi_process", true);
#ifdef __EMSCRIPTEN__
multi_process = false;
#else
multi_process = true; multi_process = true;
#endif
done = false; done = false;
this->done = false; this->done = false;
if (multi_process) { if (multi_process) {

@ -1 +1 @@
Subproject commit 22aed1f6bcfa6912a34d3241edf3bd90498a6bc2 Subproject commit 0f7e551b59372cdad6c7475d50b6d0b8a9ac2765

@ -1 +1 @@
Subproject commit 0379bf3a5d52d8542aec1874677c9df5ff9ba5f9 Subproject commit e3ddede6c4ee818825c4e5a6dfa1d384860c27d9

@ -1 +1 @@
Subproject commit 8214f717e7c7d361f002b6c3d1b1086ddd096315 Subproject commit dca8a24cf8da1fc61b5cf0422cad03474124196c