#pragma once #include "playback_backend.hpp" #include #include "x16emu/ymglue.h" #include #include #include #include #include #include #include #include #include extern "C" { #include "x16emu/audio.h" #include "x16emu/vera_pcm.h" #include "x16emu/vera_psg.h" #include "x16emu/ymglue.h" } #include "file_backend.hpp" #define YM_FREQ (3579545/64) #define PSG_FREQ (AUDIO_SAMPLERATE) enum ZsmCommandId { PsgWrite, ExtCmd, FmWrite, ZsmEOF, Delay }; struct reg_pair { uint8_t reg; uint8_t val; }; struct ZsmCommand { ZsmCommandId id; union { struct { uint8_t reg; uint8_t val; } psg_write; struct { uint8_t channel; uint8_t bytes; union { uint8_t *pcm; struct { uint8_t chip_id; uint8_t writes; uint8_t *write_bytes; } expansion; uint8_t *sync; uint8_t *custom; }; } extcmd; struct { uint8_t len; reg_pair *regs; } fm_write; uint8_t delay; }; ~ZsmCommand(); }; struct pcm_instrument { uint8_t geom; uint32_t loop_rem; uint32_t loop; bool islooped; uint32_t remain; uint8_t *data = nullptr; inline ~pcm_instrument() { if (data != nullptr) free((void*)data); } }; class ZsmBackend : public PlaybackBackend { File *file; Fifo audio_buf; DynPtr psg_buf; DynPtr pcm_buf; DynPtr out_buf; DynPtr ym_buf; DynPtr ym_resample_buf; bool ym_recorded = false; uint8_t ym_data[256]; Fifo ym_pairs; uint32_t loop_rem; uint32_t pcm_data_offs; uint8_t pcm_data_instruments; uint32_t loop; bool islooped; uint32_t remain; uint32_t cur; uint32_t pcm_loop_point; uint32_t rem_point; SDL_AudioStream *fm_stream; std::vector instruments; uint8_t *audio_sample = nullptr; bool pcm_enable(); bool psg_enable(); bool fm_enable(); double pcm_volume(); double psg_volume(); double fm_volume(); void audio_step(size_t samples); inline void *reserve(size_t len) { return (void*)audio_buf.reserve(len); } inline size_t copy_out(void *buf, size_t len) { return audio_buf.pop((int16_t*)buf, len); } uint32_t loop_point; double loop_pos = 0.0; uint32_t pcm_offset; uint8_t fm_mask; uint16_t psg_channel_mask; uint16_t tick_rate; size_t music_data_start; size_t music_data_len; double ticks; ssize_t delayTicks = 0; double position; double cpuClocks = 0; inline double get_delay_per_frame() { return 1.0; } void tick(bool step = true);\ void seek_internal(double position, bool loop = true); ZsmCommand get_command(); public: uint64_t get_min_samples() override; std::optional get_max_samples() override; inline std::string get_id() override { return "zsm"; } inline std::string get_name() override { return "ZSM player"; } void add_licenses() override; std::vector 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 { } };