Improve ZSM backend
This commit is contained in:
parent
4290d851a4
commit
21f6d7da45
16 changed files with 893 additions and 18 deletions
BIN
assets/test_loud.fur
Normal file
BIN
assets/test_loud.fur
Normal file
Binary file not shown.
BIN
assets/test_loud.zsm
Normal file
BIN
assets/test_loud.zsm
Normal file
Binary file not shown.
7
backends/playback/fluidsynth/CMakeLists.txt
Normal file
7
backends/playback/fluidsynth/CMakeLists.txt
Normal file
|
@ -0,0 +1,7 @@
|
|||
set(BACKEND_FLUIDSYNTH_SRC ${CMAKE_CURRENT_SOURCE_DIR}/fluidsynth_backend.cpp)
|
||||
set(BACKEND_FLUIDSYNTH_INC ${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_SOURCE_DIR})
|
||||
add_playback_backend(fluidsynth_backend ${BACKEND_FLUIDSYNTH_SRC})
|
||||
target_include_directories(fluidsynth_backend PRIVATE ${BACKEND_FLUIDSYNTH_INC})
|
||||
find_package(OpenMP)
|
||||
find_package(FluidSynth)
|
||||
target_link_libraries(fluidsynth_backend PUBLIC OpenMP::OpenMP_CXX FluidSynth::libfluidsynth)
|
4
backends/playback/fluidsynth/backend.json
Normal file
4
backends/playback/fluidsynth/backend.json
Normal file
|
@ -0,0 +1,4 @@
|
|||
{
|
||||
"class_name": "FluidSynthBackend",
|
||||
"include_path": "fluidsynth_backend.hpp"
|
||||
}
|
470
backends/playback/fluidsynth/fluidsynth_backend.cpp
Normal file
470
backends/playback/fluidsynth/fluidsynth_backend.cpp
Normal file
|
@ -0,0 +1,470 @@
|
|||
#include "fluidsynth_backend.hpp"
|
||||
#include <algorithm>
|
||||
#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>
|
||||
type FluidSynthBackend::name() { \
|
||||
std::optional<google::protobuf::Any> value_maybe = get(#name); \
|
||||
if (value_maybe.has_value()) { \
|
||||
return resolve_value<type>(value_maybe.value()); \
|
||||
} \
|
||||
return default_value; \
|
||||
}
|
||||
void fluidsynth_get_property_list_wrapper(void *udata, const char *name, int type) {
|
||||
((FluidSynthBackend*)udata)->fluidsynth_get_property_list(name, type);
|
||||
}
|
||||
std::vector<Property> FluidSynthBackend::get_property_list() {
|
||||
fluid_settings_foreach(settings, (void*)this, &fluidsynth_get_property_list_wrapper);
|
||||
return properties;
|
||||
}
|
||||
void FluidSynthBackend::load(const char *filename) {
|
||||
memset(&spec, 0, sizeof(spec));
|
||||
current_file = filename;
|
||||
spec.format = AUDIO_S16SYS;
|
||||
spec.samples = 100;
|
||||
spec.channels = 2;
|
||||
spec.freq = PSG_FREQ;
|
||||
spec.size = 100 * 2 * sizeof(int16_t);
|
||||
file = open_file(filename);
|
||||
char magic[2];
|
||||
file->read(magic, 2, 1);
|
||||
if (magic[0] != 0x7a || magic[1] != 0x6d) {
|
||||
throw std::exception();
|
||||
}
|
||||
uint8_t version;
|
||||
file->read(&version, 1, 1);
|
||||
uint8_t loop_point[3];
|
||||
file->read(loop_point, 3, 1);
|
||||
this->loop_point = loop_point[0] | ((uint32_t)(loop_point[1]) << 8) | ((uint32_t)(loop_point[2]) << 16);
|
||||
file->read(loop_point, 3, 1);
|
||||
this->pcm_offset = loop_point[0] | ((uint32_t)(loop_point[1]) << 8) | ((uint32_t)(loop_point[2]) << 16);
|
||||
pcm_offset += 3;
|
||||
file->read(&fm_mask, 1, 1);
|
||||
file->read(loop_point, 2, 1);
|
||||
this->psg_channel_mask = loop_point[0] | ((uint16_t)(loop_point[1]) << 8);
|
||||
file->read(loop_point, 2, 1);
|
||||
this->tick_rate = loop_point[0] | ((uint16_t)(loop_point[1]) << 8);
|
||||
file->read(loop_point, 2, 1); // Reserved.
|
||||
music_data_start = file->get_pos();
|
||||
this->loop_point += music_data_start;
|
||||
file->seek(pcm_offset, SeekType::SET);
|
||||
file->read(loop_point, 1, 1);
|
||||
pcm_offset++;
|
||||
pcm_data_offs = ((((uint16_t)loop_point[0]) + 1) * 16) + pcm_offset;
|
||||
for (uint8_t i = 0; i <= loop_point[0]; i++) {
|
||||
uint16_t instdef = (i * 16) + 1;
|
||||
pcm_instrument *inst = new pcm_instrument();
|
||||
file->seek(pcm_offset + instdef, SeekType::SET);
|
||||
file->read(&inst->geom, 1, 1);
|
||||
uint8_t bytes[10];
|
||||
file->read(bytes, 10, 1);
|
||||
inst->loop_rem = bytes[9];
|
||||
inst->loop_rem <<= 8;
|
||||
inst->loop_rem |= bytes[8];
|
||||
inst->loop_rem <<= 8;
|
||||
inst->loop_rem |= bytes[7];
|
||||
inst->loop = loop_rem;
|
||||
inst->islooped = bytes[6] & 0x80;
|
||||
inst->remain = bytes[5];
|
||||
inst->remain <<= 8;
|
||||
inst->remain |= bytes[4];
|
||||
inst->remain <<= 8;
|
||||
inst->remain |= bytes[3];
|
||||
uint32_t cur = bytes[2];
|
||||
cur <<= 8;
|
||||
cur |= bytes[1];
|
||||
cur <<= 8;
|
||||
cur |= bytes[0];
|
||||
cur += pcm_data_offs;
|
||||
inst->data = (uint8_t*)malloc(inst->remain);
|
||||
file->seek(cur, SeekType::SET);
|
||||
file->read(inst->data, 1, inst->remain);
|
||||
inst->loop_rem = inst->remain - inst->loop_rem;
|
||||
instruments.push_back(inst);
|
||||
}
|
||||
file->seek(music_data_start, SeekType::SET);
|
||||
this->loop_point = std::max(this->loop_point, (uint32_t)music_data_start);
|
||||
double prev_time = 0.0;
|
||||
double time = 0.0;
|
||||
double tmpDelayTicks = 0.0;
|
||||
loop_pos = -1.0;
|
||||
uint32_t prev_pos = music_data_start;
|
||||
while (true) {
|
||||
tmpDelayTicks -= get_delay_per_frame();
|
||||
if (tmpDelayTicks < 0.0) {
|
||||
ZsmCommand cmd = get_command();
|
||||
size_t cur_pos = file->get_pos();
|
||||
if (cur_pos >= this->loop_point && this->loop_pos < 0) {
|
||||
loop_pos = time;
|
||||
this->loop_point = cur_pos;
|
||||
}
|
||||
if (cmd.id == ZsmEOF) {
|
||||
break;
|
||||
} else if (cmd.id == Delay) {
|
||||
time += ((double)cmd.delay) / ((double)(tick_rate));
|
||||
tmpDelayTicks += cmd.delay;
|
||||
}
|
||||
prev_pos = file->get_pos();
|
||||
prev_time = time;
|
||||
}
|
||||
}
|
||||
if (this->loop_pos < 0.0) {
|
||||
this->loop_pos = 0.0;
|
||||
this->loop_point = music_data_start;
|
||||
}
|
||||
length = time;
|
||||
music_data_len = file->get_pos();
|
||||
switch_stream(0);
|
||||
loop_end = length;
|
||||
loop_start = this->loop_pos;
|
||||
fm_stream = SDL_NewAudioStream(AUDIO_S16SYS, 2, YM_FREQ, AUDIO_S16SYS, 2, PSG_FREQ);
|
||||
DEBUG.writefln("fm_stream: %ld -> %ld", YM_FREQ, PSG_FREQ);
|
||||
#define _PROPERTY(name, type, default_value) \
|
||||
{ \
|
||||
std::string type_str = #type; \
|
||||
google::protobuf::Any value; \
|
||||
if (type_str == "bool") { \
|
||||
BooleanProperty value_b; \
|
||||
value_b.set_value(default_value); \
|
||||
value.PackFrom(value_b); \
|
||||
} else if (type_str == "double") { \
|
||||
DoubleProperty value_d; \
|
||||
value_d.set_value(default_value); \
|
||||
value.PackFrom(value_d); \
|
||||
} \
|
||||
property_defaults[#name] = value; \
|
||||
}
|
||||
#include "properties.inc"
|
||||
}
|
||||
extern SDL_AudioSpec obtained;
|
||||
void ZsmBackend::switch_stream(int idx) {
|
||||
YM_Create(YM_FREQ);
|
||||
YM_init(YM_FREQ/64, 60);
|
||||
YM_reset();
|
||||
psg_reset();
|
||||
pcm_reset();
|
||||
for (uint8_t i = 0; i < 16; i++) {
|
||||
psg_writereg(i * 4 + 2, 0);
|
||||
}
|
||||
file->seek(music_data_start, SeekType::SET);
|
||||
this->cpuClocks = 0.0;
|
||||
this->delayTicks = 0.0;
|
||||
this->ticks = 0.0;
|
||||
}
|
||||
void ZsmBackend::cleanup() {
|
||||
delete file;
|
||||
file = nullptr;
|
||||
audio_buf.clear();
|
||||
SDL_FreeAudioStream(fm_stream);
|
||||
fm_stream = nullptr;
|
||||
audio_sample = nullptr;
|
||||
for (auto inst : instruments) {
|
||||
delete inst;
|
||||
}
|
||||
instruments.clear();
|
||||
}
|
||||
void ZsmBackend::tick(bool step) {
|
||||
delayTicks -= 1;
|
||||
const double ClocksPerTick = ((double)HZ) / ((double)tick_rate);
|
||||
double prevCpuClocks = cpuClocks;
|
||||
double nextCpuClocks = cpuClocks + ClocksPerTick;
|
||||
double ticks_remaining = ClocksPerTick;
|
||||
while (delayTicks <= 0) {
|
||||
ZsmCommand cmd = get_command();
|
||||
switch (cmd.id) {
|
||||
case ZsmEOF: {
|
||||
if (step) {
|
||||
file->seek(this->loop_point, SeekType::SET);
|
||||
this->position = loop_pos;
|
||||
} else {
|
||||
throw std::exception();
|
||||
}
|
||||
} break;
|
||||
case PsgWrite: {
|
||||
psg_writereg(cmd.psg_write.reg, cmd.psg_write.val);
|
||||
} break;
|
||||
case FmWrite: {
|
||||
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 = 1;
|
||||
ticks_remaining -= clocksToAddForYm;
|
||||
if (ticks_remaining < 0) {
|
||||
delayTicks -= 1;
|
||||
nextCpuClocks += ClocksPerTick;
|
||||
ticks_remaining += ClocksPerTick;
|
||||
}
|
||||
audio_step(clocksToAddForYm);
|
||||
}
|
||||
}
|
||||
} break;
|
||||
case Delay: {
|
||||
delayTicks += cmd.delay;
|
||||
position += ((double)cmd.delay) / ((double)(tick_rate));
|
||||
} break;
|
||||
case ExtCmd: {
|
||||
//cmd.extcmd.channel
|
||||
switch (cmd.extcmd.channel) {
|
||||
case 0: {
|
||||
for (size_t i = 0; i < cmd.extcmd.bytes; i += 2) {
|
||||
switch (cmd.extcmd.pcm[i]) {
|
||||
case 0: { // ctrl
|
||||
uint8_t ctrl = cmd.extcmd.pcm[i + 1];
|
||||
if (ctrl & 0x80) {
|
||||
remain = 0;
|
||||
}
|
||||
pcm_write_ctrl(ctrl);
|
||||
} break;
|
||||
case 1: { // rate
|
||||
pcm_write_rate(cmd.extcmd.pcm[i + 1]);
|
||||
} break;
|
||||
default: { // trigger
|
||||
size_t file_pos = file->get_pos();
|
||||
uint8_t ctrl = pcm_read_ctrl();
|
||||
pcm_write_ctrl(ctrl | 0x80);
|
||||
uint16_t pcm_idx = cmd.extcmd.pcm[i + 1];
|
||||
pcm_instrument *inst = instruments[pcm_idx];
|
||||
ctrl = pcm_read_ctrl() & 0x0F;
|
||||
ctrl |= inst->geom & 0x30;
|
||||
pcm_write_ctrl(ctrl);
|
||||
audio_sample = inst->data;
|
||||
loop = inst->loop;
|
||||
loop_rem = inst->loop_rem;
|
||||
remain = inst->remain;
|
||||
islooped = inst->islooped;
|
||||
cur = 0;
|
||||
} break;
|
||||
}
|
||||
} break;
|
||||
}
|
||||
// Nothing handled yet.
|
||||
}
|
||||
} break;
|
||||
}
|
||||
}
|
||||
size_t nextCpuClocksInt = std::floor(nextCpuClocks);
|
||||
size_t prevCpuClocksInt = std::floor(prevCpuClocks);
|
||||
size_t cpuClocksIntDelta = nextCpuClocksInt - prevCpuClocksInt;
|
||||
audio_step(ticks_remaining);
|
||||
cpuClocks = std::fmod(nextCpuClocks, ClocksPerTick);
|
||||
}
|
||||
size_t ZsmBackend::render(void *buf, size_t maxlen) {
|
||||
size_t sample_type_len = 2;
|
||||
maxlen /= sample_type_len;
|
||||
while (audio_buf.size() <= maxlen) {
|
||||
tick(true);
|
||||
}
|
||||
size_t copied = copy_out(buf, maxlen) * sample_type_len;
|
||||
maxlen *= sample_type_len;
|
||||
return copied;
|
||||
}
|
||||
uint64_t ZsmBackend::get_min_samples() {
|
||||
return spec.size;
|
||||
}
|
||||
std::optional<uint64_t> ZsmBackend::get_max_samples() {
|
||||
return get_min_samples();
|
||||
}
|
||||
ZsmCommand ZsmBackend::get_command() {
|
||||
ZsmCommandId cmdid;
|
||||
uint8_t cmd_byte;
|
||||
file->read(&cmd_byte, 1, 1);
|
||||
if (cmd_byte == 0x80) {
|
||||
cmdid = ZsmEOF;
|
||||
} else {
|
||||
if ((cmd_byte >> 6) == 0) {
|
||||
cmdid = PsgWrite;
|
||||
} else if ((cmd_byte >> 6) == 0b01) {
|
||||
if (cmd_byte == 0b01000000) {
|
||||
cmdid = ExtCmd;
|
||||
} else {
|
||||
cmdid = FmWrite;
|
||||
}
|
||||
} else {
|
||||
cmdid = Delay;
|
||||
}
|
||||
}
|
||||
ZsmCommand output;
|
||||
output.id = cmdid;
|
||||
if (cmdid == ZsmEOF) {
|
||||
return output;
|
||||
} else if (cmdid == PsgWrite) {
|
||||
uint8_t value;
|
||||
file->read(&value, 1, 1);
|
||||
output.psg_write.reg = cmd_byte & 0x3F;
|
||||
output.psg_write.val = value;
|
||||
} else if (cmdid == FmWrite) {
|
||||
uint16_t _value;
|
||||
uint8_t *value = (uint8_t*)(void*)(&_value);
|
||||
uint8_t pairs = cmd_byte & 0b111111;
|
||||
output.fm_write.len = pairs;
|
||||
output.fm_write.regs = (reg_pair*)malloc((sizeof(reg_pair))*pairs);
|
||||
for (uint8_t i = 0; i < pairs; i++) {
|
||||
file->read(value, 2, 1);
|
||||
output.fm_write.regs[i].reg = value[0];
|
||||
output.fm_write.regs[i].val = value[1];
|
||||
}
|
||||
} else if (cmdid == ExtCmd) {
|
||||
uint8_t ext_cmd_byte;
|
||||
file->read(&ext_cmd_byte, 1, 1);
|
||||
uint8_t bytes = ext_cmd_byte & 0x3F;
|
||||
uint8_t ch = ext_cmd_byte >> 6;
|
||||
output.extcmd.channel = ch;
|
||||
output.extcmd.bytes = bytes;
|
||||
if (ch == 1) {
|
||||
output.extcmd.expansion.write_bytes = NULL;
|
||||
} else {
|
||||
output.extcmd.pcm = (uint8_t*)malloc(bytes); // Handles all other cases due to them being in a union, and each having the same type.
|
||||
}
|
||||
for (size_t i = 0; i < bytes; i++) {
|
||||
uint8_t byte;
|
||||
file->read(&byte, 1, 1);
|
||||
switch (ch) {
|
||||
case 0: {
|
||||
output.extcmd.pcm[i] = byte;
|
||||
} break;
|
||||
case 1: {
|
||||
if (i == 0) {
|
||||
output.extcmd.expansion.chip_id = byte;
|
||||
} else if (i == 1) {
|
||||
output.extcmd.expansion.writes = byte;
|
||||
output.extcmd.expansion.write_bytes = (uint8_t*)malloc(byte);
|
||||
} else {
|
||||
output.extcmd.expansion.write_bytes[i - 2] = byte;
|
||||
}
|
||||
} break;
|
||||
case 2: {
|
||||
output.extcmd.sync[i] = byte;
|
||||
} break;
|
||||
case 3: {
|
||||
output.extcmd.custom[i] = byte;
|
||||
} break;
|
||||
}
|
||||
}
|
||||
} else if (cmdid == Delay) {
|
||||
output.delay = cmd_byte & 0x7F;
|
||||
}
|
||||
return output;
|
||||
}
|
||||
ZsmCommand::~ZsmCommand() {
|
||||
switch (id) {
|
||||
case ExtCmd: {
|
||||
if (extcmd.channel == 1) {
|
||||
if (extcmd.expansion.write_bytes != NULL) {
|
||||
free(extcmd.expansion.write_bytes);
|
||||
}
|
||||
} else {
|
||||
free(extcmd.pcm);
|
||||
}
|
||||
} break;
|
||||
case FmWrite: {
|
||||
free(fm_write.regs);
|
||||
}
|
||||
}
|
||||
}
|
||||
void ZsmBackend::seek_internal(double 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) {
|
||||
switch_stream(0);
|
||||
file->seek(music_data_start, SeekType::SET);
|
||||
this->cpuClocks = 0.0;
|
||||
this->delayTicks = 0;
|
||||
this->ticks = 0.0;
|
||||
this->position = 0.0;
|
||||
} else if (this->position == position) {
|
||||
audio_buf.clear();
|
||||
return;
|
||||
}
|
||||
while (this->position < position) {
|
||||
audio_buf.clear();
|
||||
try {
|
||||
tick(false);
|
||||
} catch (std::exception) {
|
||||
switch_stream(0);
|
||||
file->seek(music_data_start, SeekType::SET);
|
||||
this->cpuClocks = 0.0;
|
||||
this->delayTicks = 0;
|
||||
this->ticks = 0.0;
|
||||
this->position = 0.0;
|
||||
audio_buf.clear();
|
||||
return;
|
||||
}
|
||||
}
|
||||
size_t samples = std::min((size_t)((this->position - position) * PSG_FREQ), audio_buf.size());
|
||||
while (samples--) {
|
||||
audio_buf.pop();
|
||||
}
|
||||
this->position = position;
|
||||
}
|
||||
void ZsmBackend::seek(double position) {
|
||||
seek_internal(position, false);
|
||||
}
|
||||
double ZsmBackend::get_position() {
|
||||
return position;
|
||||
}
|
||||
int ZsmBackend::get_stream_idx() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
void ZsmBackend::audio_step(size_t samples) {
|
||||
if (samples == 0) return;
|
||||
while (remain != 0 && pcm_fifo_avail() < samples) {
|
||||
if (pcm_read_rate() == 0) break;
|
||||
if ((--remain) == 0) {
|
||||
if (islooped) {
|
||||
cur = loop;
|
||||
remain = loop_rem;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
size_t oldpos = file->get_pos();
|
||||
uint8_t sample = audio_sample[cur++];
|
||||
pcm_write_fifo(sample);
|
||||
}
|
||||
samples *= 2;
|
||||
int16_t *psg_ptr = psg_buf.get_item_sized<int16_t>(samples);
|
||||
int16_t *pcm_ptr = pcm_buf.get_item_sized<int16_t>(samples);
|
||||
psg_render(psg_ptr, samples / 2);
|
||||
pcm_render(pcm_ptr, samples / 2);
|
||||
int16_t *out_ptr = out_buf.get_item_sized<int16_t>(samples);
|
||||
// The exact amount of samples needed for the stream.
|
||||
double ratio = ((double)YM_FREQ) / ((double)PSG_FREQ);
|
||||
size_t needed_samples = ((size_t)std::floor(samples * ratio)) / 2;
|
||||
int16_t *ym_ptr = ym_buf.get_item_sized<int16_t>(needed_samples * 2);
|
||||
YM_stream_update(ym_ptr, needed_samples);
|
||||
assert(SDL_AudioStreamPut(fm_stream, ym_ptr, needed_samples * 2 * sizeof(int16_t)) == 0);
|
||||
while (SDL_AudioStreamAvailable(fm_stream) < ((samples + 2) * sizeof(int16_t))) {
|
||||
YM_stream_update(ym_ptr, 1);
|
||||
assert(SDL_AudioStreamPut(fm_stream, ym_ptr, 2 * sizeof(int16_t)) == 0);
|
||||
}
|
||||
int16_t *ym_resample_ptr = ym_resample_buf.get_item_sized<int16_t>(samples);
|
||||
ssize_t ym_resample_len = SDL_AudioStreamGet(fm_stream, ym_resample_ptr, (samples + 2) * sizeof(int16_t));
|
||||
assert(ym_resample_len >= 0);
|
||||
ym_resample_len /= sizeof(int16_t);
|
||||
for (size_t i = 0; i < samples / 2; i++) {
|
||||
size_t j = i * 2;
|
||||
int16_t psg[2] = {(int16_t)(psg_ptr[j] >> 1), (int16_t)(psg_ptr[j + 1] >> 1)};
|
||||
int16_t pcm[2] = {(int16_t)(pcm_ptr[j] >> 2), (int16_t)(pcm_ptr[j + 1] >> 2)};
|
||||
if (!pcm_enable()) memset(pcm, 0, sizeof(pcm));
|
||||
if (!psg_enable()) memset(psg, 0, sizeof(psg));
|
||||
pcm[0] *= pcm_volume();
|
||||
pcm[1] *= pcm_volume();
|
||||
psg[0] *= psg_volume();
|
||||
psg[1] *= psg_volume();
|
||||
int16_t vera[2] = {(int16_t)(psg[0] + pcm[0]), (int16_t)(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));
|
||||
fm[0] *= fm_volume();
|
||||
fm[1] *= fm_volume();
|
||||
int16_t mix[2] = {(int16_t)(vera[0] + (fm[0] >> 1)), (int16_t)(vera[1] + (fm[1] >> 1))};
|
||||
out_ptr[j++] = mix[0];
|
||||
out_ptr[j++] = mix[1];
|
||||
}
|
||||
audio_buf.push(out_ptr, samples);
|
||||
}
|
47
backends/playback/fluidsynth/fluidsynth_backend.hpp
Normal file
47
backends/playback/fluidsynth/fluidsynth_backend.hpp
Normal file
|
@ -0,0 +1,47 @@
|
|||
#pragma once
|
||||
#include "playback_backend.hpp"
|
||||
#include <omp.h>
|
||||
#include <cstdint>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <streambuf>
|
||||
#include <vector>
|
||||
#include <util.hpp>
|
||||
#include <SDL.h>
|
||||
#include "file_backend.hpp"
|
||||
class FluidSynthBackend : public PlaybackBackend {
|
||||
File *file;
|
||||
void seek_internal(double position, bool loop = true);
|
||||
std::vector<Property> fluidsynth_properties;
|
||||
fluid_settings_t *settings;
|
||||
void fluidsynth_get_property_list(const char *name, int type);
|
||||
public:
|
||||
uint64_t get_min_samples() override;
|
||||
void set_fluidsynth_property_str(std::string path, std::string val);
|
||||
void set_fluidsynth_property_num(std::string path, double val);
|
||||
void set_fluidsynth_property_int(std::string path, int val);
|
||||
std::string get_fluidsynth_property_str(std::string path);
|
||||
double get_fluidsynth_property_num(std::string path);
|
||||
int get_fluidsynth_property_int(std::string path);
|
||||
std::optional<uint64_t> get_max_samples() override;
|
||||
inline std::string get_id() override {
|
||||
return "fluidsynth";
|
||||
}
|
||||
inline std::string get_name() override {
|
||||
return "MIDI player";
|
||||
}
|
||||
std::vector<Property> get_property_list() override;
|
||||
void seek(double position) override;
|
||||
void load(const char *filename) override;
|
||||
void switch_stream(int idx) override;
|
||||
void cleanup() override;
|
||||
int get_stream_idx() override;
|
||||
size_t render(void *buf, size_t maxlen) override;
|
||||
double get_position() override;
|
||||
inline double get_length() override {
|
||||
return length;
|
||||
}
|
||||
inline ~ZsmBackend() override { }
|
||||
};
|
|
@ -205,17 +205,11 @@ void ZsmBackend::tick(bool step) {
|
|||
} break;
|
||||
case FmWrite: {
|
||||
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 = 1;
|
||||
ticks_remaining -= clocksToAddForYm;
|
||||
if (ticks_remaining < 0) {
|
||||
delayTicks -= 1;
|
||||
nextCpuClocks += ClocksPerTick;
|
||||
ticks_remaining += ClocksPerTick;
|
||||
}
|
||||
audio_step(clocksToAddForYm);
|
||||
}
|
||||
DEBUG.writefln2("YM Pair index: {0}", ym_pairs.size());
|
||||
reg_pair pair;
|
||||
memcpy(&pair, cmd.fm_write.regs + i, sizeof(reg_pair));
|
||||
ym_pairs.push(pair);
|
||||
DEBUG.writefln2("Writing {1} to FM reg {0} later.", pair.reg, pair.val);
|
||||
}
|
||||
} break;
|
||||
case Delay: {
|
||||
|
@ -262,6 +256,20 @@ void ZsmBackend::tick(bool step) {
|
|||
} break;
|
||||
}
|
||||
}
|
||||
while (ym_pairs.size() > 0) {
|
||||
reg_pair pair = ym_pairs.pop();
|
||||
YM_write_reg(pair.reg, pair.val);
|
||||
while (YM_read_status()) {
|
||||
size_t clocksToAddForYm = (size_t)std::ceil(((double)YM_FREQ)/((double)PSG_FREQ));
|
||||
ticks_remaining -= clocksToAddForYm;
|
||||
if (ticks_remaining < 0) {
|
||||
delayTicks -= 1;
|
||||
nextCpuClocks += ClocksPerTick;
|
||||
ticks_remaining += ClocksPerTick;
|
||||
}
|
||||
audio_step(clocksToAddForYm);
|
||||
}
|
||||
}
|
||||
size_t nextCpuClocksInt = std::floor(nextCpuClocks);
|
||||
size_t prevCpuClocksInt = std::floor(prevCpuClocks);
|
||||
size_t cpuClocksIntDelta = nextCpuClocksInt - prevCpuClocksInt;
|
||||
|
|
|
@ -81,6 +81,7 @@ class ZsmBackend : public PlaybackBackend {
|
|||
DynPtr ym_resample_buf;
|
||||
bool ym_recorded = false;
|
||||
uint8_t ym_data[256];
|
||||
Fifo<reg_pair> ym_pairs;
|
||||
uint32_t loop_rem;
|
||||
uint32_t pcm_data_offs;
|
||||
uint8_t pcm_data_instruments;
|
||||
|
|
329
sdl-android-project/.idea/caches/deviceStreaming.xml
Normal file
329
sdl-android-project/.idea/caches/deviceStreaming.xml
Normal file
|
@ -0,0 +1,329 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="DeviceStreaming">
|
||||
<option name="deviceSelectionList">
|
||||
<list>
|
||||
<PersistentDeviceSelectionData>
|
||||
<option name="api" value="27" />
|
||||
<option name="brand" value="DOCOMO" />
|
||||
<option name="codename" value="F01L" />
|
||||
<option name="id" value="F01L" />
|
||||
<option name="manufacturer" value="FUJITSU" />
|
||||
<option name="name" value="F-01L" />
|
||||
<option name="screenDensity" value="360" />
|
||||
<option name="screenX" value="720" />
|
||||
<option name="screenY" value="1280" />
|
||||
</PersistentDeviceSelectionData>
|
||||
<PersistentDeviceSelectionData>
|
||||
<option name="api" value="28" />
|
||||
<option name="brand" value="DOCOMO" />
|
||||
<option name="codename" value="SH-01L" />
|
||||
<option name="id" value="SH-01L" />
|
||||
<option name="manufacturer" value="SHARP" />
|
||||
<option name="name" value="AQUOS sense2 SH-01L" />
|
||||
<option name="screenDensity" value="480" />
|
||||
<option name="screenX" value="1080" />
|
||||
<option name="screenY" value="2160" />
|
||||
</PersistentDeviceSelectionData>
|
||||
<PersistentDeviceSelectionData>
|
||||
<option name="api" value="34" />
|
||||
<option name="brand" value="Lenovo" />
|
||||
<option name="codename" value="TB370FU" />
|
||||
<option name="id" value="TB370FU" />
|
||||
<option name="manufacturer" value="Lenovo" />
|
||||
<option name="name" value="Tab P12" />
|
||||
<option name="screenDensity" value="340" />
|
||||
<option name="screenX" value="1840" />
|
||||
<option name="screenY" value="2944" />
|
||||
</PersistentDeviceSelectionData>
|
||||
<PersistentDeviceSelectionData>
|
||||
<option name="api" value="31" />
|
||||
<option name="brand" value="samsung" />
|
||||
<option name="codename" value="a51" />
|
||||
<option name="id" value="a51" />
|
||||
<option name="manufacturer" value="Samsung" />
|
||||
<option name="name" value="Galaxy A51" />
|
||||
<option name="screenDensity" value="420" />
|
||||
<option name="screenX" value="1080" />
|
||||
<option name="screenY" value="2400" />
|
||||
</PersistentDeviceSelectionData>
|
||||
<PersistentDeviceSelectionData>
|
||||
<option name="api" value="34" />
|
||||
<option name="brand" value="google" />
|
||||
<option name="codename" value="akita" />
|
||||
<option name="id" value="akita" />
|
||||
<option name="manufacturer" value="Google" />
|
||||
<option name="name" value="Pixel 8a" />
|
||||
<option name="screenDensity" value="420" />
|
||||
<option name="screenX" value="1080" />
|
||||
<option name="screenY" value="2400" />
|
||||
</PersistentDeviceSelectionData>
|
||||
<PersistentDeviceSelectionData>
|
||||
<option name="api" value="33" />
|
||||
<option name="brand" value="samsung" />
|
||||
<option name="codename" value="b0q" />
|
||||
<option name="id" value="b0q" />
|
||||
<option name="manufacturer" value="Samsung" />
|
||||
<option name="name" value="Galaxy S22 Ultra" />
|
||||
<option name="screenDensity" value="600" />
|
||||
<option name="screenX" value="1440" />
|
||||
<option name="screenY" value="3088" />
|
||||
</PersistentDeviceSelectionData>
|
||||
<PersistentDeviceSelectionData>
|
||||
<option name="api" value="32" />
|
||||
<option name="brand" value="google" />
|
||||
<option name="codename" value="bluejay" />
|
||||
<option name="id" value="bluejay" />
|
||||
<option name="manufacturer" value="Google" />
|
||||
<option name="name" value="Pixel 6a" />
|
||||
<option name="screenDensity" value="420" />
|
||||
<option name="screenX" value="1080" />
|
||||
<option name="screenY" value="2400" />
|
||||
</PersistentDeviceSelectionData>
|
||||
<PersistentDeviceSelectionData>
|
||||
<option name="api" value="34" />
|
||||
<option name="brand" value="google" />
|
||||
<option name="codename" value="caiman" />
|
||||
<option name="id" value="caiman" />
|
||||
<option name="manufacturer" value="Google" />
|
||||
<option name="name" value="Pixel 9 Pro" />
|
||||
<option name="screenDensity" value="360" />
|
||||
<option name="screenX" value="960" />
|
||||
<option name="screenY" value="2142" />
|
||||
</PersistentDeviceSelectionData>
|
||||
<PersistentDeviceSelectionData>
|
||||
<option name="api" value="34" />
|
||||
<option name="brand" value="google" />
|
||||
<option name="codename" value="comet" />
|
||||
<option name="id" value="comet" />
|
||||
<option name="manufacturer" value="Google" />
|
||||
<option name="name" value="Pixel 9 Pro Fold" />
|
||||
<option name="screenDensity" value="390" />
|
||||
<option name="screenX" value="2076" />
|
||||
<option name="screenY" value="2152" />
|
||||
</PersistentDeviceSelectionData>
|
||||
<PersistentDeviceSelectionData>
|
||||
<option name="api" value="29" />
|
||||
<option name="brand" value="samsung" />
|
||||
<option name="codename" value="crownqlteue" />
|
||||
<option name="id" value="crownqlteue" />
|
||||
<option name="manufacturer" value="Samsung" />
|
||||
<option name="name" value="Galaxy Note9" />
|
||||
<option name="screenDensity" value="420" />
|
||||
<option name="screenX" value="2220" />
|
||||
<option name="screenY" value="1080" />
|
||||
</PersistentDeviceSelectionData>
|
||||
<PersistentDeviceSelectionData>
|
||||
<option name="api" value="34" />
|
||||
<option name="brand" value="samsung" />
|
||||
<option name="codename" value="dm3q" />
|
||||
<option name="id" value="dm3q" />
|
||||
<option name="manufacturer" value="Samsung" />
|
||||
<option name="name" value="Galaxy S23 Ultra" />
|
||||
<option name="screenDensity" value="600" />
|
||||
<option name="screenX" value="1440" />
|
||||
<option name="screenY" value="3088" />
|
||||
</PersistentDeviceSelectionData>
|
||||
<PersistentDeviceSelectionData>
|
||||
<option name="api" value="34" />
|
||||
<option name="brand" value="samsung" />
|
||||
<option name="codename" value="e1q" />
|
||||
<option name="id" value="e1q" />
|
||||
<option name="manufacturer" value="Samsung" />
|
||||
<option name="name" value="Galaxy S24" />
|
||||
<option name="screenDensity" value="480" />
|
||||
<option name="screenX" value="1080" />
|
||||
<option name="screenY" value="2340" />
|
||||
</PersistentDeviceSelectionData>
|
||||
<PersistentDeviceSelectionData>
|
||||
<option name="api" value="33" />
|
||||
<option name="brand" value="google" />
|
||||
<option name="codename" value="felix" />
|
||||
<option name="id" value="felix" />
|
||||
<option name="manufacturer" value="Google" />
|
||||
<option name="name" value="Pixel Fold" />
|
||||
<option name="screenDensity" value="420" />
|
||||
<option name="screenX" value="2208" />
|
||||
<option name="screenY" value="1840" />
|
||||
</PersistentDeviceSelectionData>
|
||||
<PersistentDeviceSelectionData>
|
||||
<option name="api" value="34" />
|
||||
<option name="brand" value="google" />
|
||||
<option name="codename" value="felix" />
|
||||
<option name="id" value="felix" />
|
||||
<option name="manufacturer" value="Google" />
|
||||
<option name="name" value="Pixel Fold" />
|
||||
<option name="screenDensity" value="420" />
|
||||
<option name="screenX" value="2208" />
|
||||
<option name="screenY" value="1840" />
|
||||
</PersistentDeviceSelectionData>
|
||||
<PersistentDeviceSelectionData>
|
||||
<option name="api" value="33" />
|
||||
<option name="brand" value="google" />
|
||||
<option name="codename" value="felix_camera" />
|
||||
<option name="id" value="felix_camera" />
|
||||
<option name="manufacturer" value="Google" />
|
||||
<option name="name" value="Pixel Fold (Camera-enabled)" />
|
||||
<option name="screenDensity" value="420" />
|
||||
<option name="screenX" value="2208" />
|
||||
<option name="screenY" value="1840" />
|
||||
</PersistentDeviceSelectionData>
|
||||
<PersistentDeviceSelectionData>
|
||||
<option name="api" value="33" />
|
||||
<option name="brand" value="samsung" />
|
||||
<option name="codename" value="gts8uwifi" />
|
||||
<option name="id" value="gts8uwifi" />
|
||||
<option name="manufacturer" value="Samsung" />
|
||||
<option name="name" value="Galaxy Tab S8 Ultra" />
|
||||
<option name="screenDensity" value="320" />
|
||||
<option name="screenX" value="1848" />
|
||||
<option name="screenY" value="2960" />
|
||||
</PersistentDeviceSelectionData>
|
||||
<PersistentDeviceSelectionData>
|
||||
<option name="api" value="34" />
|
||||
<option name="brand" value="google" />
|
||||
<option name="codename" value="husky" />
|
||||
<option name="id" value="husky" />
|
||||
<option name="manufacturer" value="Google" />
|
||||
<option name="name" value="Pixel 8 Pro" />
|
||||
<option name="screenDensity" value="390" />
|
||||
<option name="screenX" value="1008" />
|
||||
<option name="screenY" value="2244" />
|
||||
</PersistentDeviceSelectionData>
|
||||
<PersistentDeviceSelectionData>
|
||||
<option name="api" value="30" />
|
||||
<option name="brand" value="motorola" />
|
||||
<option name="codename" value="java" />
|
||||
<option name="id" value="java" />
|
||||
<option name="manufacturer" value="Motorola" />
|
||||
<option name="name" value="G20" />
|
||||
<option name="screenDensity" value="280" />
|
||||
<option name="screenX" value="720" />
|
||||
<option name="screenY" value="1600" />
|
||||
</PersistentDeviceSelectionData>
|
||||
<PersistentDeviceSelectionData>
|
||||
<option name="api" value="34" />
|
||||
<option name="brand" value="google" />
|
||||
<option name="codename" value="komodo" />
|
||||
<option name="id" value="komodo" />
|
||||
<option name="manufacturer" value="Google" />
|
||||
<option name="name" value="Pixel 9 Pro XL" />
|
||||
<option name="screenDensity" value="360" />
|
||||
<option name="screenX" value="1008" />
|
||||
<option name="screenY" value="2244" />
|
||||
</PersistentDeviceSelectionData>
|
||||
<PersistentDeviceSelectionData>
|
||||
<option name="api" value="33" />
|
||||
<option name="brand" value="google" />
|
||||
<option name="codename" value="lynx" />
|
||||
<option name="id" value="lynx" />
|
||||
<option name="manufacturer" value="Google" />
|
||||
<option name="name" value="Pixel 7a" />
|
||||
<option name="screenDensity" value="420" />
|
||||
<option name="screenX" value="1080" />
|
||||
<option name="screenY" value="2400" />
|
||||
</PersistentDeviceSelectionData>
|
||||
<PersistentDeviceSelectionData>
|
||||
<option name="api" value="31" />
|
||||
<option name="brand" value="google" />
|
||||
<option name="codename" value="oriole" />
|
||||
<option name="id" value="oriole" />
|
||||
<option name="manufacturer" value="Google" />
|
||||
<option name="name" value="Pixel 6" />
|
||||
<option name="screenDensity" value="420" />
|
||||
<option name="screenX" value="1080" />
|
||||
<option name="screenY" value="2400" />
|
||||
</PersistentDeviceSelectionData>
|
||||
<PersistentDeviceSelectionData>
|
||||
<option name="api" value="33" />
|
||||
<option name="brand" value="google" />
|
||||
<option name="codename" value="panther" />
|
||||
<option name="id" value="panther" />
|
||||
<option name="manufacturer" value="Google" />
|
||||
<option name="name" value="Pixel 7" />
|
||||
<option name="screenDensity" value="420" />
|
||||
<option name="screenX" value="1080" />
|
||||
<option name="screenY" value="2400" />
|
||||
</PersistentDeviceSelectionData>
|
||||
<PersistentDeviceSelectionData>
|
||||
<option name="api" value="34" />
|
||||
<option name="brand" value="samsung" />
|
||||
<option name="codename" value="q5q" />
|
||||
<option name="id" value="q5q" />
|
||||
<option name="manufacturer" value="Samsung" />
|
||||
<option name="name" value="Galaxy Z Fold5" />
|
||||
<option name="screenDensity" value="420" />
|
||||
<option name="screenX" value="1812" />
|
||||
<option name="screenY" value="2176" />
|
||||
</PersistentDeviceSelectionData>
|
||||
<PersistentDeviceSelectionData>
|
||||
<option name="api" value="34" />
|
||||
<option name="brand" value="samsung" />
|
||||
<option name="codename" value="q6q" />
|
||||
<option name="id" value="q6q" />
|
||||
<option name="manufacturer" value="Samsung" />
|
||||
<option name="name" value="SM-F956B" />
|
||||
<option name="screenDensity" value="420" />
|
||||
<option name="screenX" value="1856" />
|
||||
<option name="screenY" value="2160" />
|
||||
</PersistentDeviceSelectionData>
|
||||
<PersistentDeviceSelectionData>
|
||||
<option name="api" value="30" />
|
||||
<option name="brand" value="google" />
|
||||
<option name="codename" value="r11" />
|
||||
<option name="id" value="r11" />
|
||||
<option name="manufacturer" value="Google" />
|
||||
<option name="name" value="Pixel Watch" />
|
||||
<option name="screenDensity" value="320" />
|
||||
<option name="screenX" value="384" />
|
||||
<option name="screenY" value="384" />
|
||||
<option name="type" value="WEAR_OS" />
|
||||
</PersistentDeviceSelectionData>
|
||||
<PersistentDeviceSelectionData>
|
||||
<option name="api" value="30" />
|
||||
<option name="brand" value="google" />
|
||||
<option name="codename" value="redfin" />
|
||||
<option name="id" value="redfin" />
|
||||
<option name="manufacturer" value="Google" />
|
||||
<option name="name" value="Pixel 5" />
|
||||
<option name="screenDensity" value="440" />
|
||||
<option name="screenX" value="1080" />
|
||||
<option name="screenY" value="2340" />
|
||||
</PersistentDeviceSelectionData>
|
||||
<PersistentDeviceSelectionData>
|
||||
<option name="api" value="34" />
|
||||
<option name="brand" value="google" />
|
||||
<option name="codename" value="shiba" />
|
||||
<option name="id" value="shiba" />
|
||||
<option name="manufacturer" value="Google" />
|
||||
<option name="name" value="Pixel 8" />
|
||||
<option name="screenDensity" value="420" />
|
||||
<option name="screenX" value="1080" />
|
||||
<option name="screenY" value="2400" />
|
||||
</PersistentDeviceSelectionData>
|
||||
<PersistentDeviceSelectionData>
|
||||
<option name="api" value="33" />
|
||||
<option name="brand" value="google" />
|
||||
<option name="codename" value="tangorpro" />
|
||||
<option name="id" value="tangorpro" />
|
||||
<option name="manufacturer" value="Google" />
|
||||
<option name="name" value="Pixel Tablet" />
|
||||
<option name="screenDensity" value="320" />
|
||||
<option name="screenX" value="1600" />
|
||||
<option name="screenY" value="2560" />
|
||||
</PersistentDeviceSelectionData>
|
||||
<PersistentDeviceSelectionData>
|
||||
<option name="api" value="34" />
|
||||
<option name="brand" value="google" />
|
||||
<option name="codename" value="tokay" />
|
||||
<option name="id" value="tokay" />
|
||||
<option name="manufacturer" value="Google" />
|
||||
<option name="name" value="Pixel 9" />
|
||||
<option name="screenDensity" value="420" />
|
||||
<option name="screenX" value="1080" />
|
||||
<option name="screenY" value="2424" />
|
||||
</PersistentDeviceSelectionData>
|
||||
</list>
|
||||
</option>
|
||||
</component>
|
||||
</project>
|
|
@ -1,6 +1,6 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="CompilerConfiguration">
|
||||
<bytecodeTargetLevel target="17" />
|
||||
<bytecodeTargetLevel target="21" />
|
||||
</component>
|
||||
</project>
|
10
sdl-android-project/.idea/deploymentTargetSelector.xml
Normal file
10
sdl-android-project/.idea/deploymentTargetSelector.xml
Normal file
|
@ -0,0 +1,10 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="deploymentTargetSelector">
|
||||
<selectionStates>
|
||||
<SelectionState runConfigName="app">
|
||||
<option name="selectionMode" value="DROPDOWN" />
|
||||
</SelectionState>
|
||||
</selectionStates>
|
||||
</component>
|
||||
</project>
|
|
@ -5,7 +5,7 @@
|
|||
<option name="linkedExternalProjectsSettings">
|
||||
<GradleProjectSettings>
|
||||
<option name="externalProjectPath" value="$PROJECT_DIR$" />
|
||||
<option name="gradleJvm" value="#GRADLE_LOCAL_JAVA_HOME" />
|
||||
<option name="gradleJvm" value="temurin-21" />
|
||||
<option name="modules">
|
||||
<set>
|
||||
<option value="$PROJECT_DIR$" />
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ExternalStorageConfigurationManager" enabled="true" />
|
||||
<component name="ProjectRootManager" version="2" languageLevel="JDK_17" default="true" project-jdk-name="jbr-17" project-jdk-type="JavaSDK">
|
||||
<component name="ProjectRootManager" version="2" languageLevel="JDK_21" default="true" project-jdk-name="temurin-21" project-jdk-type="JavaSDK">
|
||||
<output url="file://$PROJECT_DIR$/build/classes" />
|
||||
</component>
|
||||
<component name="ProjectType">
|
||||
|
|
|
@ -27,7 +27,7 @@ android {
|
|||
def revParseStdout = new ByteArrayOutputStream();
|
||||
def versionNameData = ""
|
||||
exec {
|
||||
commandLine "git", "rev-parse", "--abbrev-rev", "HEAD"
|
||||
commandLine "git", "rev-parse", "--abbrev-ref", "HEAD"
|
||||
standardOutput revParseStdout
|
||||
}
|
||||
versionNameData = revParseStdout.toString() + "-"
|
||||
|
|
|
@ -6,7 +6,7 @@ buildscript {
|
|||
google()
|
||||
}
|
||||
dependencies {
|
||||
classpath 'com.android.tools.build:gradle:8.3.2'
|
||||
classpath 'com.android.tools.build:gradle:8.6.1'
|
||||
|
||||
// NOTE: Do not place your application dependencies here; they belong
|
||||
// in the individual module build.gradle files
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
#Thu Nov 11 18:20:34 PST 2021
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.4-bin.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip
|
||||
distributionPath=wrapper/dists
|
||||
zipStorePath=wrapper/dists
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
|
|
Loading…
Reference in a new issue