Fix FM synthesis issues, potentially making the audio rendering of ZSM files less optimized.
This commit is contained in:
parent
480311b03e
commit
f5dbdf96d5
11 changed files with 156 additions and 18 deletions
|
@ -6,14 +6,28 @@ extern "C" {
|
|||
#include "x16emu/vera_psg.h"
|
||||
#include "x16emu/ymglue.h"
|
||||
}
|
||||
#include <ipc/common.pb.h>
|
||||
#include <exception>
|
||||
#include <filesystem>
|
||||
#include "file_backend.hpp"
|
||||
#include <stddef.h>
|
||||
#include <string.h>
|
||||
#include <file_backend.hpp>
|
||||
#include <util.hpp>
|
||||
#define HZ (AUDIO_SAMPLERATE)
|
||||
#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) {
|
||||
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;
|
||||
|
|
|
@ -93,6 +93,9 @@ class ZsmBackend : public PlaybackBackend {
|
|||
SDL_AudioStream *fm_stream;
|
||||
std::vector<pcm_instrument*> 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];
|
||||
|
|
|
@ -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<double>("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<double>("ui.imgui.accent_color.h", accent_color.x);
|
||||
|
|
|
@ -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<UIBackend*> backends;
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
|
15
playback.cpp
15
playback.cpp
|
@ -669,3 +669,18 @@ std::vector<PlaybackStream> 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<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 {};
|
||||
}
|
||||
|
|
13
playback.h
13
playback.h
|
@ -23,6 +23,7 @@ extern "C" {
|
|||
#include "playback_backend.hpp"
|
||||
#include "config.hpp"
|
||||
#include "playback_process.hpp"
|
||||
#include <google/protobuf/any.h>
|
||||
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<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;
|
||||
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<google::protobuf::Any> get_property(std::string path) override;
|
||||
std::optional<google::protobuf::Any> reset_property(std::string path) override;
|
||||
float volume;
|
||||
float speed;
|
||||
float tempo;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -618,6 +618,31 @@ std::vector<PlaybackStream> 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<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) {
|
||||
RenderCommand rend_cmd = RenderCommand();
|
||||
rend_cmd.set_len(maxlen);
|
||||
|
|
|
@ -31,12 +31,6 @@ class PlaybackProcessServiceImpl {
|
|||
MaybeError Quit(const QuitCmd *request);
|
||||
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
|
||||
void print_ipc_message(const google::protobuf::Message &msg, size_t level = 0);
|
||||
class PlaybackProcess {
|
||||
|
|
45
util.hpp
45
util.hpp
|
@ -17,6 +17,7 @@
|
|||
#include <cassert>
|
||||
#include <SDL.h>
|
||||
#include "log.hpp"
|
||||
#include <ipc/common.pb.h>
|
||||
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<T> 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);
|
||||
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;
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue