diff --git a/backends/playback/zsm/zsm_backend.cpp b/backends/playback/zsm/zsm_backend.cpp index dcc4ad5..91cb1d4 100644 --- a/backends/playback/zsm/zsm_backend.cpp +++ b/backends/playback/zsm/zsm_backend.cpp @@ -6,14 +6,28 @@ extern "C" { #include "x16emu/vera_psg.h" #include "x16emu/ymglue.h" } +#include #include #include #include "file_backend.hpp" #include #include #include +#include #define HZ (AUDIO_SAMPLERATE) #define BUFFERS 32 +#define _PROPERTY(name, type, default_value) \ +bool ZsmBackend::name() { \ + std::optional value_maybe = get(#name); \ + if (value_maybe.has_value()) { \ + return resolve_##type(value_maybe.value()); \ + } \ + return default_value; \ +} +#define BOOL_PROPERTY(name, default_value) _PROPERTY(name, bool, default_value) +BOOL_PROPERTY(pcm_enable, true) +BOOL_PROPERTY(psg_enable, true) +BOOL_PROPERTY(fm_enable, true) void ZsmBackend::load(const char *filename) { memset(&spec, 0, sizeof(spec)); spec.format = AUDIO_S16SYS; @@ -167,7 +181,7 @@ void ZsmBackend::tick(bool step) { for (uint8_t i = 0; i < cmd.fm_write.len; i++) { YM_write_reg(cmd.fm_write.regs[i].reg, cmd.fm_write.regs[i].val); while (YM_read_status()) { - size_t clocksToAddForYm = 64; + size_t clocksToAddForYm = 1; ticks_remaining -= clocksToAddForYm; if (ticks_remaining < 0) { delayTicks -= 1; diff --git a/backends/playback/zsm/zsm_backend.hpp b/backends/playback/zsm/zsm_backend.hpp index c1c29b4..2b5cf54 100644 --- a/backends/playback/zsm/zsm_backend.hpp +++ b/backends/playback/zsm/zsm_backend.hpp @@ -93,6 +93,9 @@ class ZsmBackend : public PlaybackBackend { SDL_AudioStream *fm_stream; std::vector instruments; uint8_t *audio_sample = nullptr; + bool pcm_enable(); + bool psg_enable(); + bool fm_enable(); int16_t combine_audio(int16_t a, int16_t b) { return (int16_t)((((int32_t)a) + ((int32_t)b)) >> 1); } @@ -136,8 +139,11 @@ class ZsmBackend : public PlaybackBackend { size_t j = i * 2; int16_t psg[2] = {psg_ptr[j] >> 1, psg_ptr[j + 1] >> 1}; int16_t pcm[2] = {pcm_ptr[j] >> 1, pcm_ptr[j + 1] >> 1}; + if (!pcm_enable()) memset(pcm, 0, sizeof(pcm)); + if (!psg_enable()) memset(psg, 0, sizeof(psg)); int16_t vera[2] = {psg[0] + pcm[0], psg[1] + pcm[1]}; int16_t fm[2] = {ym_resample_ptr[j], ym_resample_ptr[j + 1]}; + if (!fm_enable()) memset(fm, 0, sizeof(fm)); int16_t mix[2] = {vera[0] + (fm[0] >> 1), vera[1] + (fm[1] >> 1)}; out_ptr[j++] = mix[0]; out_ptr[j++] = mix[1]; diff --git a/backends/ui/imgui/main.cpp b/backends/ui/imgui/main.cpp index 8890615..2ef410c 100644 --- a/backends/ui/imgui/main.cpp +++ b/backends/ui/imgui/main.cpp @@ -78,7 +78,7 @@ void MainLoop::Init() { delete theme; theme = newTheme; } catch (std::exception _) { - + } #else if (exists(themePath)) { @@ -196,6 +196,9 @@ void MainLoop::GuiFunction() { set_option("ui.imgui.demo_window", show_demo_window); } ImGui::EndMenu(); + if (ImGui::MenuItem(_TR_CTX("Main menu | Debug", "Edit properties"), nullptr, property_editor)) { + property_editor = !property_editor; + } } if (ImGui::BeginMenu(_TRI_CTX(ICON_FK_INFO_CIRCLE, "Main menu", "Help"))) { if (ImGui::MenuItem(_TRI_CTX(ICON_FK_INFO, "Main menu | Help", "About"), nullptr, about_window)) { @@ -257,9 +260,9 @@ void MainLoop::GuiFunction() { } ImGui::SameLine(); const int NEXT_SLIDER_COUNT = 1; - + double seek_bar_width = -(ImGui::GetFontSize() * (1 + (8 * NEXT_SLIDER_COUNT))) - ((ImGui::GetStyle().ItemSpacing.x + ImGui::GetStyle().FramePadding.x) * (NEXT_SLIDER_COUNT + 1)); - + if (lengthKnown) { ImGui::SetNextItemWidth(seek_bar_width); uint8_t components = TimeToComponentCount(playback->GetLength()); @@ -297,11 +300,29 @@ void MainLoop::GuiFunction() { ImGui::PopItemWidth(); } ImGui::End(); + if (property_editor) { + ImGui::SetNextWindowDockID(dockid); + ImGui::Begin("Property Editor", &property_editor); + { + static std::string property_name; + static bool property_value; + ImGui::InputText("Property Name", &property_name); + ImGui::Checkbox("Enabled", &property_value); + if (ImGui::Button("Set")) { + BooleanProperty property; + property.set_value(property_value); + google::protobuf::Any value; + value.PackFrom(property); + playback->set_property(property_name, value); + } + } + ImGui::End(); + } if (prefs_window) { ImGui::SetNextWindowDockID(dockid); ImGui::Begin(_TRI_CTX(ICON_FK_COG, "Window title, window opened by menu item", "Preferences..."), &prefs_window); { - + static std::string set_backend_name = cur_backend->get_name(); static std::string set_backend_id = cur_backend->get_id(); if (restart_needed) { @@ -442,7 +463,7 @@ void MainLoop::GuiFunction() { ImGui::EndTable(); } } - ImGui::EndChildFrame(); + ImGui::EndChildFrame(); ImGui::SetNextItemWidth(ImGui::GetWindowWidth() - (ImGui::GetStyle().WindowPadding.x * 2)); if (ImGui::ColorEdit4("##AccentColor", &accent_color.x, ImGuiColorEditFlags_InputHSV|ImGuiColorEditFlags_DisplayHSV|ImGuiColorEditFlags_Float)) { set_option("ui.imgui.accent_color.h", accent_color.x); diff --git a/backends/ui/imgui/main.h b/backends/ui/imgui/main.h index 5779249..d368792 100644 --- a/backends/ui/imgui/main.h +++ b/backends/ui/imgui/main.h @@ -46,6 +46,7 @@ class MainLoop : public RendererBackend { bool theme_editor = false; bool about_window = false; bool debug_mode = false; + bool property_editor = false; bool restart_needed = false; bool stopped = true; std::vector backends; diff --git a/ipc/common.proto b/ipc/common.proto index 2a9eee4..70488ef 100644 --- a/ipc/common.proto +++ b/ipc/common.proto @@ -21,6 +21,12 @@ message StringProperty { message DoubleProperty { double value = 1; }; +message BooleanProperty { + bool value = 1; +}; +message BytesProperty { + bytes value = 1; +}; message StreamId { uint64 id = 504; }; diff --git a/playback.cpp b/playback.cpp index 7abc29f..0addb19 100644 --- a/playback.cpp +++ b/playback.cpp @@ -669,3 +669,18 @@ std::vector PlaybackInstance::get_streams() { int PlaybackInstance::get_current_stream() { return current_stream; } + +void PlaybackInstance::set_property(std::string path, google::protobuf::Any value) { + if (this->process != nullptr) { + this->process->set_property(path, value); + } +} +std::optional PlaybackInstance::get_property(std::string path) { + if (this->process != nullptr) { + return this->process->get_property(path); + } + return {}; +} +std::optional PlaybackInstance::reset_property(std::string path) { + return {}; +} diff --git a/playback.h b/playback.h index 15bb366..e1fd6d1 100644 --- a/playback.h +++ b/playback.h @@ -23,6 +23,7 @@ extern "C" { #include "playback_backend.hpp" #include "config.hpp" #include "playback_process.hpp" +#include using namespace soundtouch; using std::span; using std::optional; @@ -195,6 +196,15 @@ class Playback { } virtual void DeinitLoopFunction() { + } + virtual void set_property(std::string path, google::protobuf::Any value) { + + } + virtual std::optional get_property(std::string path) { + return {}; + } + virtual std::optional reset_property(std::string path) { + return {}; } bool loop_started = false; virtual void start_loop() { @@ -320,6 +330,9 @@ public: void InitLoopFunction() override; void DeinitLoopFunction() override; void LoopFunction() override; + void set_property(std::string path, google::protobuf::Any) override; + std::optional get_property(std::string path) override; + std::optional reset_property(std::string path) override; float volume; float speed; float tempo; diff --git a/playback_backend.hpp b/playback_backend.hpp index 857e7d4..98cfafe 100644 --- a/playback_backend.hpp +++ b/playback_backend.hpp @@ -15,6 +15,16 @@ struct PlaybackStream { int id; }; class PlaybackBackendHelper; +enum PropertyType { + Double, + String, + Bytes, + Boolean +}; +struct Property { + PropertyType type; + std::string path; +}; class PlaybackBackend { double prevRate; size_t minSamples; diff --git a/playback_process.cpp b/playback_process.cpp index d4cfa3f..713e241 100644 --- a/playback_process.cpp +++ b/playback_process.cpp @@ -618,6 +618,31 @@ std::vector PlaybackProcess::get_playback_streams() { delete list; return output; } +bool PlaybackProcess::set_property(std::string path, google::protobuf::Any value) { + SetProperty set_property; + set_property.set_id(PropertyId::BackendSpecific); + set_property.mutable_value()->CopyFrom(value); + set_property.set_path(path); + RPCCall call; + call.mutable_set()->CopyFrom(set_property); + RPCResponse output = SendCommand(&call); + if (output.has_err()) { + return false; + } + return true; +} +std::optional PlaybackProcess::get_property(std::string path) { + GetProperty get_property; + get_property.set_id(PropertyId::BackendSpecific); + get_property.set_path(path); + RPCCall call; + call.mutable_get()->CopyFrom(get_property); + RPCResponse output = SendCommand(&call); + if (output.has_err()) { + return {}; + } + return output.data().value(); +} size_t PlaybackProcess::render(void *buf, size_t maxlen) { RenderCommand rend_cmd = RenderCommand(); rend_cmd.set_len(maxlen); diff --git a/playback_process.hpp b/playback_process.hpp index 42b796e..a274c06 100644 --- a/playback_process.hpp +++ b/playback_process.hpp @@ -31,12 +31,6 @@ class PlaybackProcessServiceImpl { MaybeError Quit(const QuitCmd *request); MaybeError Init(const InitCommand *cmd); }; -template -inline T *resolve_any(google::protobuf::Any value) { - T *output = new T(); - value.UnpackTo(output); - return output; -} //#define DEBUG_PRINT_IPC void print_ipc_message(const google::protobuf::Message &msg, size_t level = 0); class PlaybackProcess { diff --git a/util.hpp b/util.hpp index 0c97f6a..ebb9b1c 100644 --- a/util.hpp +++ b/util.hpp @@ -17,6 +17,7 @@ #include #include #include "log.hpp" +#include namespace fs = std::filesystem; std::string PadZeros(std::string input, size_t required_length); uint8_t TimeToComponentCount(double time_code); @@ -382,11 +383,11 @@ public: void resize(size_t new_capacity) { std::vector new_buffer(new_capacity); size_t new_count = std::min(count, new_capacity); - + for (size_t i = 0; i < new_count; ++i) { new_buffer[i] = buffer[(head + i) % buffer.size()]; } - + buffer = std::move(new_buffer); head = 0; tail = new_count; @@ -413,7 +414,7 @@ public: if (count + length > buffer.size()) { resize(std::max(buffer.size() * 2, count + length)); } - + for (size_t i = 0; i < length; ++i) { buffer[tail] = values[i]; tail = (tail + 1) % buffer.size(); @@ -445,11 +446,11 @@ public: if (count + length > buffer.size()) { resize(std::max(buffer.size() * 2, count + length)); } - + T* start = &buffer[tail]; tail = (tail + length) % buffer.size(); count += length; - + return start; } @@ -484,4 +485,36 @@ public: void blocking_write(int fd, const void *buf, const size_t len); void blocking_read(int fd, void *buf, const size_t len); int NotSDL_ConvertAudioSamples(const SDL_AudioSpec *src_spec, const Uint8 *src_data, int src_len, - const SDL_AudioSpec *dst_spec, Uint8 **dst_data, int *dst_len); \ No newline at end of file + const SDL_AudioSpec *dst_spec, Uint8 **dst_data, int *dst_len); + +template +inline T *resolve_any(google::protobuf::Any value) { + T *output = new T(); + value.UnpackTo(output); + return output; +} +inline bool resolve_bool(google::protobuf::Any value) { + BooleanProperty *property = resolve_any(value); + bool output = property->value(); + delete property; + return output; +} +inline std::string resolve_string(google::protobuf::Any value) { + StringProperty *property = resolve_any(value); + std::string output = property->value(); + delete property; + return output; +} +inline std::string resolve_bytes(google::protobuf::Any value) { + BytesProperty *property = resolve_any(value); + std::string output = property->value(); + delete property; + return output; +} + +inline double resolve_double(google::protobuf::Any value) { + DoubleProperty *property = resolve_any(value); + double output = property->value(); + delete property; + return output; +}