Fix FM synthesis issues, potentially making the audio rendering of ZSM files less optimized.

This commit is contained in:
Zachary Hall 2024-10-18 10:45:22 -07:00
parent 480311b03e
commit f5dbdf96d5
11 changed files with 156 additions and 18 deletions

View file

@ -6,14 +6,28 @@ extern "C" {
#include "x16emu/vera_psg.h" #include "x16emu/vera_psg.h"
#include "x16emu/ymglue.h" #include "x16emu/ymglue.h"
} }
#include <ipc/common.pb.h>
#include <exception> #include <exception>
#include <filesystem> #include <filesystem>
#include "file_backend.hpp" #include "file_backend.hpp"
#include <stddef.h> #include <stddef.h>
#include <string.h> #include <string.h>
#include <file_backend.hpp> #include <file_backend.hpp>
#include <util.hpp>
#define HZ (AUDIO_SAMPLERATE) #define HZ (AUDIO_SAMPLERATE)
#define BUFFERS 32 #define BUFFERS 32
#define _PROPERTY(name, type, default_value) \
bool ZsmBackend::name() { \
std::optional<google::protobuf::Any> 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) { void ZsmBackend::load(const char *filename) {
memset(&spec, 0, sizeof(spec)); memset(&spec, 0, sizeof(spec));
spec.format = AUDIO_S16SYS; spec.format = AUDIO_S16SYS;
@ -167,7 +181,7 @@ void ZsmBackend::tick(bool step) {
for (uint8_t i = 0; i < cmd.fm_write.len; i++) { 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); YM_write_reg(cmd.fm_write.regs[i].reg, cmd.fm_write.regs[i].val);
while (YM_read_status()) { while (YM_read_status()) {
size_t clocksToAddForYm = 64; size_t clocksToAddForYm = 1;
ticks_remaining -= clocksToAddForYm; ticks_remaining -= clocksToAddForYm;
if (ticks_remaining < 0) { if (ticks_remaining < 0) {
delayTicks -= 1; delayTicks -= 1;

View file

@ -93,6 +93,9 @@ class ZsmBackend : public PlaybackBackend {
SDL_AudioStream *fm_stream; SDL_AudioStream *fm_stream;
std::vector<pcm_instrument*> instruments; std::vector<pcm_instrument*> instruments;
uint8_t *audio_sample = nullptr; uint8_t *audio_sample = nullptr;
bool pcm_enable();
bool psg_enable();
bool fm_enable();
int16_t combine_audio(int16_t a, int16_t b) { int16_t combine_audio(int16_t a, int16_t b) {
return (int16_t)((((int32_t)a) + ((int32_t)b)) >> 1); return (int16_t)((((int32_t)a) + ((int32_t)b)) >> 1);
} }
@ -136,8 +139,11 @@ class ZsmBackend : public PlaybackBackend {
size_t j = i * 2; size_t j = i * 2;
int16_t psg[2] = {psg_ptr[j] >> 1, psg_ptr[j + 1] >> 1}; 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}; 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 vera[2] = {psg[0] + pcm[0], psg[1] + pcm[1]};
int16_t fm[2] = {ym_resample_ptr[j], ym_resample_ptr[j + 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)}; int16_t mix[2] = {vera[0] + (fm[0] >> 1), vera[1] + (fm[1] >> 1)};
out_ptr[j++] = mix[0]; out_ptr[j++] = mix[0];
out_ptr[j++] = mix[1]; out_ptr[j++] = mix[1];

View file

@ -196,6 +196,9 @@ void MainLoop::GuiFunction() {
set_option<double>("ui.imgui.demo_window", show_demo_window); set_option<double>("ui.imgui.demo_window", show_demo_window);
} }
ImGui::EndMenu(); 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::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)) { if (ImGui::MenuItem(_TRI_CTX(ICON_FK_INFO, "Main menu | Help", "About"), nullptr, about_window)) {
@ -297,6 +300,24 @@ void MainLoop::GuiFunction() {
ImGui::PopItemWidth(); ImGui::PopItemWidth();
} }
ImGui::End(); 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) { if (prefs_window) {
ImGui::SetNextWindowDockID(dockid); ImGui::SetNextWindowDockID(dockid);
ImGui::Begin(_TRI_CTX(ICON_FK_COG, "Window title, window opened by menu item", "Preferences..."), &prefs_window); ImGui::Begin(_TRI_CTX(ICON_FK_COG, "Window title, window opened by menu item", "Preferences..."), &prefs_window);

View file

@ -46,6 +46,7 @@ class MainLoop : public RendererBackend {
bool theme_editor = false; bool theme_editor = false;
bool about_window = false; bool about_window = false;
bool debug_mode = false; bool debug_mode = false;
bool property_editor = false;
bool restart_needed = false; bool restart_needed = false;
bool stopped = true; bool stopped = true;
std::vector<UIBackend*> backends; std::vector<UIBackend*> backends;

View file

@ -21,6 +21,12 @@ message StringProperty {
message DoubleProperty { message DoubleProperty {
double value = 1; double value = 1;
}; };
message BooleanProperty {
bool value = 1;
};
message BytesProperty {
bytes value = 1;
};
message StreamId { message StreamId {
uint64 id = 504; uint64 id = 504;
}; };

View file

@ -669,3 +669,18 @@ std::vector<PlaybackStream> PlaybackInstance::get_streams() {
int PlaybackInstance::get_current_stream() { int PlaybackInstance::get_current_stream() {
return 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<google::protobuf::Any> PlaybackInstance::get_property(std::string path) {
if (this->process != nullptr) {
return this->process->get_property(path);
}
return {};
}
std::optional<google::protobuf::Any> PlaybackInstance::reset_property(std::string path) {
return {};
}

View file

@ -23,6 +23,7 @@ extern "C" {
#include "playback_backend.hpp" #include "playback_backend.hpp"
#include "config.hpp" #include "config.hpp"
#include "playback_process.hpp" #include "playback_process.hpp"
#include <google/protobuf/any.h>
using namespace soundtouch; using namespace soundtouch;
using std::span; using std::span;
using std::optional; using std::optional;
@ -195,6 +196,15 @@ class Playback {
} }
virtual void DeinitLoopFunction() { virtual void DeinitLoopFunction() {
}
virtual void set_property(std::string path, google::protobuf::Any value) {
}
virtual std::optional<google::protobuf::Any> get_property(std::string path) {
return {};
}
virtual std::optional<google::protobuf::Any> reset_property(std::string path) {
return {};
} }
bool loop_started = false; bool loop_started = false;
virtual void start_loop() { virtual void start_loop() {
@ -320,6 +330,9 @@ public:
void InitLoopFunction() override; void InitLoopFunction() override;
void DeinitLoopFunction() override; void DeinitLoopFunction() override;
void LoopFunction() override; void LoopFunction() override;
void set_property(std::string path, google::protobuf::Any) override;
std::optional<google::protobuf::Any> get_property(std::string path) override;
std::optional<google::protobuf::Any> reset_property(std::string path) override;
float volume; float volume;
float speed; float speed;
float tempo; float tempo;

View file

@ -15,6 +15,16 @@ struct PlaybackStream {
int id; int id;
}; };
class PlaybackBackendHelper; class PlaybackBackendHelper;
enum PropertyType {
Double,
String,
Bytes,
Boolean
};
struct Property {
PropertyType type;
std::string path;
};
class PlaybackBackend { class PlaybackBackend {
double prevRate; double prevRate;
size_t minSamples; size_t minSamples;

View file

@ -618,6 +618,31 @@ std::vector<PlaybackStream> PlaybackProcess::get_playback_streams() {
delete list; delete list;
return output; 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<google::protobuf::Any> 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) { size_t PlaybackProcess::render(void *buf, size_t maxlen) {
RenderCommand rend_cmd = RenderCommand(); RenderCommand rend_cmd = RenderCommand();
rend_cmd.set_len(maxlen); rend_cmd.set_len(maxlen);

View file

@ -31,12 +31,6 @@ class PlaybackProcessServiceImpl {
MaybeError Quit(const QuitCmd *request); MaybeError Quit(const QuitCmd *request);
MaybeError Init(const InitCommand *cmd); MaybeError Init(const InitCommand *cmd);
}; };
template<class T>
inline T *resolve_any(google::protobuf::Any value) {
T *output = new T();
value.UnpackTo(output);
return output;
}
//#define DEBUG_PRINT_IPC //#define DEBUG_PRINT_IPC
void print_ipc_message(const google::protobuf::Message &msg, size_t level = 0); void print_ipc_message(const google::protobuf::Message &msg, size_t level = 0);
class PlaybackProcess { class PlaybackProcess {

View file

@ -17,6 +17,7 @@
#include <cassert> #include <cassert>
#include <SDL.h> #include <SDL.h>
#include "log.hpp" #include "log.hpp"
#include <ipc/common.pb.h>
namespace fs = std::filesystem; namespace fs = std::filesystem;
std::string PadZeros(std::string input, size_t required_length); std::string PadZeros(std::string input, size_t required_length);
uint8_t TimeToComponentCount(double time_code); uint8_t TimeToComponentCount(double time_code);
@ -485,3 +486,35 @@ void blocking_write(int fd, const void *buf, const size_t len);
void blocking_read(int fd, 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, 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); const SDL_AudioSpec *dst_spec, Uint8 **dst_data, int *dst_len);
template<class T>
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<BooleanProperty>(value);
bool output = property->value();
delete property;
return output;
}
inline std::string resolve_string(google::protobuf::Any value) {
StringProperty *property = resolve_any<StringProperty>(value);
std::string output = property->value();
delete property;
return output;
}
inline std::string resolve_bytes(google::protobuf::Any value) {
BytesProperty *property = resolve_any<BytesProperty>(value);
std::string output = property->value();
delete property;
return output;
}
inline double resolve_double(google::protobuf::Any value) {
DoubleProperty *property = resolve_any<DoubleProperty>(value);
double output = property->value();
delete property;
return output;
}