#pragma once #include "sys_sync.h" #include "sys_event.h" #include "Utilities/simple_ringbuf.h" #include "Utilities/transactional_storage.h" #include "Utilities/cond.h" #include "Emu/system_config_types.h" #include "Emu/Memory/vm_ptr.h" #include "Emu/Cell/ErrorCodes.h" #include "Emu/Audio/AudioDumper.h" #include "Emu/Audio/AudioBackend.h" #include "Emu/Audio/audio_resampler.h" #if defined(unix) || defined(__unix) || defined(__unix__) // For BSD detection #include #endif #ifdef _WIN32 #include #elif defined(BSD) || defined(__APPLE__) #include #endif enum : u32 { SYS_RSXAUDIO_SERIAL_STREAM_CNT = 4, SYS_RSXAUDIO_STREAM_DATA_BLK_CNT = 4, SYS_RSXAUDIO_DATA_BLK_SIZE = 256, SYS_RSXAUDIO_STREAM_SIZE = SYS_RSXAUDIO_DATA_BLK_SIZE * SYS_RSXAUDIO_STREAM_DATA_BLK_CNT, SYS_RSXAUDIO_CH_PER_STREAM = 2, SYS_RSXAUDIO_SERIAL_MAX_CH = 8, SYS_RSXAUDIO_SPDIF_MAX_CH = 2, SYS_RSXAUDIO_STREAM_SAMPLE_CNT = SYS_RSXAUDIO_STREAM_SIZE / SYS_RSXAUDIO_CH_PER_STREAM / sizeof(f32), SYS_RSXAUDIO_RINGBUF_BLK_SZ_SERIAL = SYS_RSXAUDIO_STREAM_SIZE * SYS_RSXAUDIO_SERIAL_STREAM_CNT, SYS_RSXAUDIO_RINGBUF_BLK_SZ_SPDIF = SYS_RSXAUDIO_STREAM_SIZE, SYS_RSXAUDIO_RINGBUF_SZ = 16, SYS_RSXAUDIO_AVPORT_CNT = 5, SYS_RSXAUDIO_FREQ_BASE_384K = 384000, SYS_RSXAUDIO_FREQ_BASE_352K = 352800, SYS_RSXAUDIO_PORT_CNT = 3, SYS_RSXAUDIO_SPDIF_CNT = 2, }; enum class RsxaudioAvportIdx : u8 { HDMI_0 = 0, HDMI_1 = 1, AVMULTI = 2, SPDIF_0 = 3, SPDIF_1 = 4, }; enum class RsxaudioPort : u8 { SERIAL = 0, SPDIF_0 = 1, SPDIF_1 = 2, INVALID = 0xFF, }; enum class RsxaudioSampleSize : u8 { _16BIT = 2, _32BIT = 4, }; struct rsxaudio_shmem { struct ringbuf_t { struct entry_t { be_t valid{}; be_t unk1{}; be_t audio_blk_idx{}; be_t timestamp{}; be_t buf_addr{}; be_t dma_addr{}; }; be_t active{}; be_t unk2{}; be_t read_idx{}; be_t write_idx{}; be_t rw_max_idx{}; be_t queue_notify_idx{}; be_t queue_notify_step{}; be_t unk6{}; be_t dma_silence_addr{}; be_t unk7{}; be_t next_blk_idx{}; entry_t entries[16]{}; }; struct uf_event_t { be_t unk1{}; be_t uf_event_cnt{}; u8 unk2[244]{}; }; struct ctrl_t { ringbuf_t ringbuf[SYS_RSXAUDIO_PORT_CNT]{}; be_t unk1{}; be_t event_queue_1_id{}; u8 unk2[16]{}; be_t event_queue_2_id{}; be_t spdif_ch0_channel_data_lo{}; be_t spdif_ch0_channel_data_hi{}; be_t spdif_ch0_channel_data_tx_cycles{}; be_t unk3{}; be_t event_queue_3_id{}; be_t spdif_ch1_channel_data_lo{}; be_t spdif_ch1_channel_data_hi{}; be_t spdif_ch1_channel_data_tx_cycles{}; be_t unk4{}; be_t intr_thread_prio{}; be_t unk5{}; u8 unk6[248]{}; uf_event_t channel_uf[SYS_RSXAUDIO_PORT_CNT]{}; u8 pad[0x3530]{}; }; u8 dma_serial_region[0x10000]{}; u8 dma_spdif_0_region[0x4000]{}; u8 dma_spdif_1_region[0x4000]{}; u8 dma_silence_region[0x4000]{}; ctrl_t ctrl{}; }; static_assert(sizeof(rsxaudio_shmem::ringbuf_t) == 0x230U, "rsxAudioRingBufSizeTest"); static_assert(sizeof(rsxaudio_shmem::uf_event_t) == 0x100U, "rsxAudioUfEventTest"); static_assert(sizeof(rsxaudio_shmem::ctrl_t) == 0x4000U, "rsxAudioCtrlSizeTest"); static_assert(sizeof(rsxaudio_shmem) == 0x20000U, "rsxAudioShmemSizeTest"); enum rsxaudio_dma_flag : u32 { IO_BASE = 0, IO_ID = 1 }; struct lv2_rsxaudio final : lv2_obj { static constexpr u32 id_base = 0x60000000; static constexpr u64 dma_io_id = 1; static constexpr u32 dma_io_base = 0x30000000; shared_mutex mutex{}; bool init = false; vm::addr_t shmem{}; std::array, SYS_RSXAUDIO_PORT_CNT> event_queue{}; // lv2 uses port memory addresses for their names static constexpr std::array event_port_name{ 0x8000000000400100, 0x8000000000400200, 0x8000000000400300 }; lv2_rsxaudio() noexcept = default; lv2_rsxaudio(utils::serial& ar) noexcept; void save(utils::serial& ar); void page_lock() { ensure(shmem && vm::page_protect(shmem, sizeof(rsxaudio_shmem), 0, 0, vm::page_readable | vm::page_writable | vm::page_executable)); } void page_unlock() { ensure(shmem && vm::page_protect(shmem, sizeof(rsxaudio_shmem), 0, vm::page_readable | vm::page_writable)); } rsxaudio_shmem* get_rw_shared_page() const { return reinterpret_cast(vm::g_sudo_addr + u32{shmem}); } }; class rsxaudio_periodic_tmr { public: enum class wait_result { SUCCESS, INVALID_PARAM, TIMEOUT, TIMER_ERROR, TIMER_CANCELED, }; rsxaudio_periodic_tmr(); ~rsxaudio_periodic_tmr(); rsxaudio_periodic_tmr(const rsxaudio_periodic_tmr&) = delete; rsxaudio_periodic_tmr& operator=(const rsxaudio_periodic_tmr&) = delete; // Wait until timer fires and calls callback. wait_result wait(const std::function &callback); // Cancel wait() call void cancel_wait(); // VTimer funtions void vtimer_access_sec(std::invocable<> auto func) { std::lock_guard lock(mutex); std::invoke(func); // Adjust timer expiration cancel_timer_unlocked(); sched_timer(); } void enable_vtimer(u32 vtimer_id, u32 rate, u64 crnt_time); void disable_vtimer(u32 vtimer_id); bool is_vtimer_behind(u32 vtimer_id, u64 crnt_time) const; void vtimer_skip_periods(u32 vtimer_id, u64 crnt_time); void vtimer_incr(u32 vtimer_id, u64 crnt_time); bool is_vtimer_active(u32 vtimer_id) const; u64 vtimer_get_sched_time(u32 vtimer_id) const; private: static constexpr u64 MAX_BURST_PERIODS = SYS_RSXAUDIO_RINGBUF_SZ; static constexpr u32 VTIMER_MAX = 4; struct vtimer { u64 blk_cnt = 0; f64 blk_time = 0.0; bool active = false; }; std::array vtmr_pool{}; shared_mutex mutex{}; bool in_wait = false; bool zero_period = false; #if defined(_WIN32) HANDLE cancel_event{}; HANDLE timer_handle{}; #elif defined(__linux__) int cancel_event{}; int timer_handle{}; int epoll_fd{}; #elif defined(BSD) || defined(__APPLE__) static constexpr u64 TIMER_ID = 0; static constexpr u64 CANCEL_ID = 1; int kq{}; struct kevent handle[2]{}; #else #error "Implement" #endif void sched_timer(); void cancel_timer_unlocked(); void reset_cancel_flag(); bool is_vtimer_behind(const vtimer& vtimer, u64 crnt_time) const; u64 get_crnt_blk(u64 crnt_time, f64 blk_time) const; f64 get_blk_time(u32 data_rate) const; u64 get_rel_next_time(); }; struct rsxaudio_hw_param_t { struct serial_param_t { bool dma_en = false; bool buf_empty_en = false; bool muted = true; bool en = false; u8 freq_div = 8; RsxaudioSampleSize depth = RsxaudioSampleSize::_16BIT; }; struct spdif_param_t { bool dma_en = false; bool buf_empty_en = false; bool muted = true; bool en = false; bool use_serial_buf = true; u8 freq_div = 8; RsxaudioSampleSize depth = RsxaudioSampleSize::_16BIT; std::array cs_data = { 0x00, 0x90, 0x00, 0x40, 0x80, 0x00 }; // HW supports only 6 bytes (uart pkt has 8) }; struct hdmi_param_t { struct hdmi_ch_cfg_t { std::array map{}; AudioChannelCnt total_ch_cnt = AudioChannelCnt::STEREO; }; static constexpr u8 MAP_SILENT_CH = umax; bool init = false; hdmi_ch_cfg_t ch_cfg{}; std::array info_frame{}; // TODO: check chstat and info_frame for info on audio layout, add default values std::array chstat{}; bool muted = true; bool force_mute = true; bool use_spdif_1 = false; // TODO: unused for now }; u32 serial_freq_base = SYS_RSXAUDIO_FREQ_BASE_384K; u32 spdif_freq_base = SYS_RSXAUDIO_FREQ_BASE_352K; bool avmulti_av_muted = true; serial_param_t serial{}; spdif_param_t spdif[2]{}; hdmi_param_t hdmi[2]{}; std::array avport_src = { RsxaudioPort::INVALID, RsxaudioPort::INVALID, RsxaudioPort::INVALID, RsxaudioPort::INVALID, RsxaudioPort::INVALID }; }; // 16-bit PCM converted into float, so buffer must be twice as big using ra_stream_blk_t = std::array; class rsxaudio_data_container { public: struct buf_t { std::array serial{}; std::array spdif[SYS_RSXAUDIO_SPDIF_CNT]{}; }; using data_blk_t = std::array; rsxaudio_data_container(const rsxaudio_hw_param_t& hw_param, const buf_t& buf, bool serial_rdy, bool spdif_0_rdy, bool spdif_1_rdy); u32 get_data_size(RsxaudioAvportIdx avport); void get_data(RsxaudioAvportIdx avport, data_blk_t& data_out); bool data_was_used(); private: const rsxaudio_hw_param_t& hwp; const buf_t& out_buf; std::array avport_data_avail{}; u8 hdmi_stream_cnt[2]{}; bool data_was_written = false; rsxaudio_data_container(const rsxaudio_data_container&) = delete; rsxaudio_data_container& operator=(const rsxaudio_data_container&) = delete; rsxaudio_data_container(rsxaudio_data_container&&) = delete; rsxaudio_data_container& operator=(rsxaudio_data_container&&) = delete; // Mix individual channels into final PCM stream. Channels in channel map that are > input_ch_cnt treated as silent. template requires (output_ch_cnt > 0 && output_ch_cnt <= 8 && input_ch_cnt > 0) constexpr void mix(const std::array &ch_map, RsxaudioSampleSize sample_size, const std::array &input_channels, data_blk_t& data_out) { const ra_stream_blk_t silent_channel{}; // Build final map std::array real_input_ch = {}; for (u64 ch_idx = 0; ch_idx < output_ch_cnt; ch_idx++) { if (ch_map[ch_idx] >= input_ch_cnt) { real_input_ch[ch_idx] = &silent_channel; } else { real_input_ch[ch_idx] = &input_channels[ch_map[ch_idx]]; } } const u32 samples_in_buf = sample_size == RsxaudioSampleSize::_16BIT ? SYS_RSXAUDIO_STREAM_SAMPLE_CNT * 2 : SYS_RSXAUDIO_STREAM_SAMPLE_CNT; for (u32 sample_idx = 0; sample_idx < samples_in_buf * output_ch_cnt; sample_idx += output_ch_cnt) { const u32 src_sample_idx = sample_idx / output_ch_cnt; if constexpr (output_ch_cnt >= 1) data_out[sample_idx + 0] = (*real_input_ch[0])[src_sample_idx]; if constexpr (output_ch_cnt >= 2) data_out[sample_idx + 1] = (*real_input_ch[1])[src_sample_idx]; if constexpr (output_ch_cnt >= 3) data_out[sample_idx + 2] = (*real_input_ch[2])[src_sample_idx]; if constexpr (output_ch_cnt >= 4) data_out[sample_idx + 3] = (*real_input_ch[3])[src_sample_idx]; if constexpr (output_ch_cnt >= 5) data_out[sample_idx + 4] = (*real_input_ch[4])[src_sample_idx]; if constexpr (output_ch_cnt >= 6) data_out[sample_idx + 5] = (*real_input_ch[5])[src_sample_idx]; if constexpr (output_ch_cnt >= 7) data_out[sample_idx + 6] = (*real_input_ch[6])[src_sample_idx]; if constexpr (output_ch_cnt >= 8) data_out[sample_idx + 7] = (*real_input_ch[7])[src_sample_idx]; } } }; namespace audio { void configure_rsxaudio(); } class rsxaudio_backend_thread { public: struct port_config { AudioFreq freq = AudioFreq::FREQ_48K; AudioChannelCnt ch_cnt = AudioChannelCnt::STEREO; auto operator<=>(const port_config&) const = default; }; struct avport_bit { bool hdmi_0 : 1; bool hdmi_1 : 1; bool avmulti : 1; bool spdif_0 : 1; bool spdif_1 : 1; }; rsxaudio_backend_thread(); ~rsxaudio_backend_thread(); void operator()(); rsxaudio_backend_thread& operator=(thread_state state); void set_new_stream_param(const std::array &cfg, avport_bit muted_avports); void set_mute_state(avport_bit muted_avports); void add_data(rsxaudio_data_container& cont); void update_emu_cfg(); u32 get_sample_rate() const; u8 get_channel_count() const; static constexpr auto thread_name = "RsxAudio Backend Thread"sv; SAVESTATE_INIT_POS(8.91); // Depends on audio_out_configuration private: struct emu_audio_cfg { std::string audio_device{}; s64 desired_buffer_duration = 0; f64 time_stretching_threshold = 0; bool buffering_enabled = false; bool convert_to_s16 = false; bool enable_time_stretching = false; bool dump_to_file = false; AudioChannelCnt channels = AudioChannelCnt::STEREO; audio_channel_layout channel_layout = audio_channel_layout::automatic; audio_renderer renderer = audio_renderer::null; audio_provider provider = audio_provider::none; RsxaudioAvportIdx avport = RsxaudioAvportIdx::HDMI_0; auto operator<=>(const emu_audio_cfg&) const = default; }; struct rsxaudio_state { std::array port{}; }; struct alignas(16) callback_config { static constexpr u16 VOL_NOMINAL = 10000; static constexpr f32 VOL_NOMINAL_INV = 1.0f / VOL_NOMINAL; u32 freq : 20 = 48000; u16 target_volume = 10000; u16 initial_volume = 10000; u16 current_volume = 10000; RsxaudioAvportIdx avport_idx = RsxaudioAvportIdx::HDMI_0; u8 mute_state : SYS_RSXAUDIO_AVPORT_CNT = 0b11111; u8 input_ch_cnt : 4 = 2; u8 output_channel_layout : 4 = static_cast(audio_channel_layout::stereo); bool ready : 1 = false; bool convert_to_s16 : 1 = false; bool cfg_changed : 1 = false; bool callback_active : 1 = false; }; static_assert(sizeof(callback_config) <= 16); struct backend_config { port_config cfg{}; RsxaudioAvportIdx avport = RsxaudioAvportIdx::HDMI_0; }; static constexpr u64 ERROR_SERVICE_PERIOD = 500'000; static constexpr u64 SERVICE_PERIOD = 10'000; static constexpr f64 SERVICE_PERIOD_SEC = SERVICE_PERIOD / 1'000'000.0; static constexpr u64 SERVICE_THRESHOLD = 1'500; static constexpr f64 TIME_STRETCHING_STEP = 0.1f; u64 start_time = get_system_time(); u64 time_period_idx = 1; emu_audio_cfg new_emu_cfg{}; bool emu_cfg_changed = true; rsxaudio_state new_ra_state{}; bool ra_state_changed = true; shared_mutex state_update_m{}; cond_variable state_update_c{}; simple_ringbuf ringbuf{}; simple_ringbuf aux_ringbuf{}; std::vector thread_tmp_buf{}; std::vector callback_tmp_buf{}; bool use_aux_ringbuf = false; shared_mutex ringbuf_mutex{}; std::shared_ptr backend{}; backend_config backend_current_cfg{ {}, new_emu_cfg.avport }; atomic_t callback_cfg{}; bool backend_error_occured = false; bool backend_device_changed = false; AudioDumper dumper{}; audio_resampler resampler{}; // Backend void backend_init(const rsxaudio_state& ra_state, const emu_audio_cfg& emu_cfg, bool reset_backend = true); void backend_start(); void backend_stop(); bool backend_playing(); u32 write_data_callback(u32 bytes, void* buf); void state_changed_callback(AudioStateEvent event); // Time management u64 get_time_until_service(); void update_service_time(); void reset_service_time(); // Helpers static emu_audio_cfg get_emu_cfg(); static u8 gen_mute_state(avport_bit avports); static RsxaudioAvportIdx convert_avport(audio_avport avport); }; class rsxaudio_data_thread { public: // Prevent creation of multiple rsxaudio contexts atomic_t rsxaudio_ctx_allocated = false; shared_mutex rsxaudio_obj_upd_m{}; shared_ptr rsxaudio_obj_ptr{}; void operator()(); rsxaudio_data_thread& operator=(thread_state state); rsxaudio_data_thread(); void update_hw_param(std::function update_callback); void update_mute_state(RsxaudioPort port, bool muted); void update_av_mute_state(RsxaudioAvportIdx avport, bool muted, bool force_mute, bool set = true); void reset_hw(); static constexpr auto thread_name = "RsxAudioData Thread"sv; private: rsxaudio_data_container::buf_t output_buf{}; transactional_storage hw_param_ts{std::make_shared(), std::make_shared()}; rsxaudio_periodic_tmr timer{}; void advance_all_timers(); void extract_audio_data(); static std::pair get_ringbuf_addr(RsxaudioPort dst, const lv2_rsxaudio& rsxaudio_obj); static f32 pcm_to_float(s32 sample); static f32 pcm_to_float(s16 sample); static void pcm_serial_process_channel(RsxaudioSampleSize word_bits, ra_stream_blk_t& buf_out_l, ra_stream_blk_t& buf_out_r, const void* buf_in, u8 src_stream); static void pcm_spdif_process_channel(RsxaudioSampleSize word_bits, ra_stream_blk_t& buf_out_l, ra_stream_blk_t& buf_out_r, const void* buf_in); bool enqueue_data(RsxaudioPort dst, bool silence, const void* src_addr, const rsxaudio_hw_param_t& hwp); static rsxaudio_backend_thread::avport_bit calc_avport_mute_state(const rsxaudio_hw_param_t& hwp); static bool calc_port_active_state(RsxaudioPort port, const rsxaudio_hw_param_t& hwp); }; using rsx_audio_backend = named_thread; using rsx_audio_data = named_thread; // SysCalls error_code sys_rsxaudio_initialize(vm::ptr handle); error_code sys_rsxaudio_finalize(u32 handle); error_code sys_rsxaudio_import_shared_memory(u32 handle, vm::ptr addr); error_code sys_rsxaudio_unimport_shared_memory(u32 handle, vm::ptr addr); error_code sys_rsxaudio_create_connection(u32 handle); error_code sys_rsxaudio_close_connection(u32 handle); error_code sys_rsxaudio_prepare_process(u32 handle); error_code sys_rsxaudio_start_process(u32 handle); error_code sys_rsxaudio_stop_process(u32 handle); error_code sys_rsxaudio_get_dma_param(u32 handle, u32 flag, vm::ptr out);