diff --git a/Utilities/Thread.cpp b/Utilities/Thread.cpp index d7e30f9214..01ff0787be 100644 --- a/Utilities/Thread.cpp +++ b/Utilities/Thread.cpp @@ -537,16 +537,32 @@ bool ThreadBase::TestDestroy() const return m_destroy; } -thread_t::thread_t(const std::string& name, std::function func) : m_name(name), m_state(TS_NON_EXISTENT) +thread_t::thread_t(const std::string& name, bool autojoin, std::function func) + : m_name(name) + , m_state(TS_NON_EXISTENT) + , m_autojoin(autojoin) { start(func); } -thread_t::thread_t(const std::string& name) : m_name(name), m_state(TS_NON_EXISTENT) +thread_t::thread_t(const std::string& name, std::function func) + : m_name(name) + , m_state(TS_NON_EXISTENT) + , m_autojoin(false) +{ + start(func); +} + +thread_t::thread_t(const std::string& name) + : m_name(name) + , m_state(TS_NON_EXISTENT) + , m_autojoin(false) { } -thread_t::thread_t() : m_state(TS_NON_EXISTENT) +thread_t::thread_t() + : m_state(TS_NON_EXISTENT) + , m_autojoin(false) { } @@ -559,7 +575,14 @@ thread_t::~thread_t() { if (m_state == TS_JOINABLE) { - m_thr.detach(); + if (m_autojoin) + { + m_thr.join(); + } + else + { + m_thr.detach(); + } } } @@ -717,6 +740,9 @@ void waiter_map_t::notify(u64 signal_id) } } +static const std::function SQUEUE_ALWAYS_EXIT = [](){ return true; }; +static const std::function SQUEUE_NEVER_EXIT = [](){ return false; }; + bool squeue_test_exit() { return Emu.IsStopped(); diff --git a/Utilities/Thread.h b/Utilities/Thread.h index a80097932b..4e51438636 100644 --- a/Utilities/Thread.h +++ b/Utilities/Thread.h @@ -65,8 +65,10 @@ class thread_t std::atomic m_state; std::string m_name; std::thread m_thr; + bool m_autojoin; public: + thread_t(const std::string& name, bool autojoin, std::function func); thread_t(const std::string& name, std::function func); thread_t(const std::string& name); thread_t(); @@ -162,6 +164,9 @@ public: void notify(u64 signal_id); }; +extern const std::function SQUEUE_ALWAYS_EXIT; +extern const std::function SQUEUE_NEVER_EXIT; + bool squeue_test_exit(); template @@ -266,14 +271,14 @@ public: return push(data, [do_exit](){ return do_exit && *do_exit; }); } - bool push(const T& data) + __forceinline bool push(const T& data) { - return push(data, [](){ return false; }); + return push(data, SQUEUE_NEVER_EXIT); } - bool try_push(const T& data) + __forceinline bool try_push(const T& data) { - return push(data, [](){ return true; }); + return push(data, SQUEUE_ALWAYS_EXIT); } bool pop(T& data, const std::function& test_exit) @@ -334,14 +339,14 @@ public: return pop(data, [do_exit](){ return do_exit && *do_exit; }); } - bool pop(T& data) + __forceinline bool pop(T& data) { - return pop(data, [](){ return false; }); + return pop(data, SQUEUE_NEVER_EXIT); } - bool try_pop(T& data) + __forceinline bool try_pop(T& data) { - return pop(data, [](){ return true; }); + return pop(data, SQUEUE_ALWAYS_EXIT); } bool peek(T& data, u32 start_pos, const std::function& test_exit) @@ -362,7 +367,7 @@ public: { return SQSVR_LOCKED; } - + sync.pop_lock = 1; pos = sync.position + start_pos; return SQSVR_OK; @@ -396,14 +401,14 @@ public: return peek(data, start_pos, [do_exit](){ return do_exit && *do_exit; }); } - bool peek(T& data, u32 start_pos = 0) + __forceinline bool peek(T& data, u32 start_pos = 0) { - return peek(data, start_pos, [](){ return false; }); + return peek(data, start_pos, SQUEUE_NEVER_EXIT); } - bool try_peek(T& data, u32 start_pos = 0) + __forceinline bool try_peek(T& data, u32 start_pos = 0) { - return peek(data, start_pos, [](){ return true; }); + return peek(data, start_pos, SQUEUE_ALWAYS_EXIT); } class squeue_data_t diff --git a/rpcs3/Emu/SysCalls/Callback.cpp b/rpcs3/Emu/SysCalls/Callback.cpp index c74fc84871..b0d10c5316 100644 --- a/rpcs3/Emu/SysCalls/Callback.cpp +++ b/rpcs3/Emu/SysCalls/Callback.cpp @@ -117,3 +117,40 @@ void CallbackManager::Clear() m_cb_list.clear(); m_async_list.clear(); } + +u64 CallbackManager::AddPauseCallback(const std::function& func) +{ + std::lock_guard lock(m_mutex); + + m_pause_cb_list.push_back({ func, next_tag }); + return next_tag++; +} + +void CallbackManager::RemovePauseCallback(const u64 tag) +{ + std::lock_guard lock(m_mutex); + + for (auto& data : m_pause_cb_list) + { + if (data.tag == tag) + { + m_pause_cb_list.erase(m_pause_cb_list.begin() + (&data - m_pause_cb_list.data())); + return; + } + } + + assert(!"CallbackManager()::RemovePauseCallback(): tag not found"); +} + +void CallbackManager::RunPauseCallbacks(const bool is_paused) +{ + std::lock_guard lock(m_mutex); + + for (auto& data : m_pause_cb_list) + { + if (data.cb) + { + data.cb(is_paused); + } + } +} diff --git a/rpcs3/Emu/SysCalls/Callback.h b/rpcs3/Emu/SysCalls/Callback.h index 7cb6b8699b..2e8c64cf92 100644 --- a/rpcs3/Emu/SysCalls/Callback.h +++ b/rpcs3/Emu/SysCalls/Callback.h @@ -3,21 +3,62 @@ class CPUThread; class PPUThread; +typedef void(PauseResumeCB)(bool is_paused); + class CallbackManager { - std::vector> m_cb_list; - std::vector> m_async_list; - CPUThread* m_cb_thread; std::mutex m_mutex; + std::vector> m_cb_list; + std::vector> m_async_list; + CPUThread* m_cb_thread; + + struct PauseResumeCBS + { + std::function cb; + u64 tag; + }; + + u64 next_tag; // not initialized, only increased + std::vector m_pause_cb_list; public: - void Register(const std::function& func); // register callback (called in Check() method) + void Register(const std::function& func); // register callback (called in Check() method) - void Async(const std::function& func); // register callback for callback thread (called immediately) + void Async(const std::function& func); // register callback for callback thread (called immediately) bool Check(CPUThread& CPU, s32& result); // call one callback registered by Register() method void Init(); void Clear(); + + u64 AddPauseCallback(const std::function& func); // register callback for pausing/resuming emulation events + void RemovePauseCallback(const u64 tag); // unregister callback (uses the result of AddPauseCallback() function) + void RunPauseCallbacks(const bool is_paused); +}; + +class PauseCallbackRegisterer +{ + CallbackManager& cb_manager; + u64 cb_tag; + +public: + PauseCallbackRegisterer(CallbackManager& cb_manager, const std::function& func) + : cb_manager(cb_manager) + , cb_tag(cb_manager.AddPauseCallback(func)) + { + } + + PauseCallbackRegisterer() = delete; + PauseCallbackRegisterer(const PauseCallbackRegisterer& right) = delete; + PauseCallbackRegisterer(PauseCallbackRegisterer&& right) = delete; + + ~PauseCallbackRegisterer() + { + cb_manager.RemovePauseCallback(cb_tag); + } + + PauseCallbackRegisterer& operator =(const PauseCallbackRegisterer& right) = delete; + PauseCallbackRegisterer& operator =(PauseCallbackRegisterer&& right) = delete; + }; diff --git a/rpcs3/Emu/SysCalls/Modules/cellAudio.cpp b/rpcs3/Emu/SysCalls/Modules/cellAudio.cpp index 815668386d..4d65c06c51 100644 --- a/rpcs3/Emu/SysCalls/Modules/cellAudio.cpp +++ b/rpcs3/Emu/SysCalls/Modules/cellAudio.cpp @@ -18,7 +18,7 @@ Module *cellAudio = nullptr; AudioConfig g_audio; -int cellAudioInit() +s32 cellAudioInit() { cellAudio->Warning("cellAudioInit()"); @@ -27,23 +27,27 @@ int cellAudioInit() return CELL_AUDIO_ERROR_ALREADY_INIT; } + // clear ports for (auto& port : g_audio.ports) { - port.state.write_relaxed(AUDIO_PORT_STATE_NOT_OPENED); + port.state.write_relaxed(AUDIO_PORT_STATE_CLOSED); } + // reset variables g_audio.start_time = 0; g_audio.counter = 0; g_audio.keys.clear(); + g_audio.start_time = get_system_time(); // alloc memory (only once until the emulator is stopped) - g_audio.buffer = g_audio.buffer ? g_audio.buffer : vm::cast(Memory.MainMem.AllocAlign(128 * 1024 * AUDIO_PORT_COUNT, 4096)); + g_audio.buffer = g_audio.buffer ? g_audio.buffer : vm::cast(Memory.MainMem.AllocAlign(AUDIO_PORT_OFFSET * AUDIO_PORT_COUNT, 4096)); g_audio.indexes = g_audio.indexes ? g_audio.indexes : vm::cast(Memory.MainMem.AllocAlign(sizeof(u64) * AUDIO_PORT_COUNT, __alignof(u64))); // clear memory - memset(vm::get_ptr(g_audio.buffer), 0, 128 * 1024 * AUDIO_PORT_COUNT); + memset(vm::get_ptr(g_audio.buffer), 0, AUDIO_PORT_OFFSET * AUDIO_PORT_COUNT); memset(vm::get_ptr(g_audio.indexes), 0, sizeof(u64) * AUDIO_PORT_COUNT); + // start audio thread g_audio.audio_thread.start([]() { const bool do_dump = Ini.AudioDumpToFile.GetValue(); @@ -51,8 +55,7 @@ int cellAudioInit() AudioDumper m_dump; if (do_dump && !m_dump.Init(8)) // Init AudioDumper for 8 channels { - cellAudio->Error("AudioDumper::Init() failed"); - return; + throw "AudioDumper::Init() failed"; } float buf2ch[2 * BUFFER_SIZE]; // intermediate buffer for 2 channels @@ -67,69 +70,60 @@ int cellAudioInit() out_buffer[i].reset(new float[2 * BUFFER_SIZE] {}); } - squeue_t out_queue; + squeue_t out_queue; std::vector keys; - g_audio.start_time = get_system_time(); - - thread_t iat("Internal Audio Thread", [&out_queue]() + thread_t iat("Internal Audio Thread", true /* autojoin */, [&out_queue]() { const bool use_u16 = Ini.AudioConvertToU16.GetValue(); Emu.GetAudioManager().GetAudioOut().Init(); bool opened = false; + float* buffer; - while (g_audio.state.read_relaxed() == AUDIO_STATE_INITIALIZED && !Emu.IsStopped()) + while (out_queue.pop(buffer, [](){ return g_audio.state.read_relaxed() != AUDIO_STATE_INITIALIZED; })) { - float* buffer; - if (out_queue.pop(buffer, [](){ return g_audio.state.read_relaxed() != AUDIO_STATE_INITIALIZED; })) + if (use_u16) { - if (use_u16) + // convert the data from float to u16 with clipping: + // 2x MULPS + // 2x MAXPS (optional) + // 2x MINPS (optional) + // 2x CVTPS2DQ (converts float to s32) + // PACKSSDW (converts s32 to s16 with signed saturation) + + u16 buf_u16[out_buffer_size]; + for (size_t i = 0; i < out_buffer_size; i += 8) { - // convert the data from float to u16 with clipping: - // 2x MULPS - // 2x MAXPS (optional) - // 2x MINPS (optional) - // 2x CVTPS2DQ (converts float to s32) - // PACKSSDW (converts s32 to s16 with signed saturation) + static const __m128 float2u16 = { 0x8000, 0x8000, 0x8000, 0x8000 }; + (__m128i&)(buf_u16[i]) = _mm_packs_epi32( + _mm_cvtps_epi32(_mm_mul_ps((__m128&)(buffer[i]), float2u16)), + _mm_cvtps_epi32(_mm_mul_ps((__m128&)(buffer[i + 4]), float2u16))); + } - u16 buf_u16[out_buffer_size]; - for (size_t i = 0; i < out_buffer_size; i += 8) - { - static const __m128 float2u16 = { 0x8000, 0x8000, 0x8000, 0x8000 }; - (__m128i&)(buf_u16[i]) = _mm_packs_epi32( - _mm_cvtps_epi32(_mm_mul_ps((__m128&)(buffer[i]), float2u16)), - _mm_cvtps_epi32(_mm_mul_ps((__m128&)(buffer[i + 4]), float2u16))); - } - - if (!opened) - { - Emu.GetAudioManager().GetAudioOut().Open(buf_u16, out_buffer_size * sizeof(u16)); - opened = true; - } - else - { - Emu.GetAudioManager().GetAudioOut().AddData(buf_u16, out_buffer_size * sizeof(u16)); - } + if (!opened) + { + Emu.GetAudioManager().GetAudioOut().Open(buf_u16, out_buffer_size * sizeof(u16)); + opened = true; } else { - if (!opened) - { - Emu.GetAudioManager().GetAudioOut().Open(buffer, out_buffer_size * sizeof(float)); - opened = true; - } - else - { - Emu.GetAudioManager().GetAudioOut().AddData(buffer, out_buffer_size * sizeof(float)); - } + Emu.GetAudioManager().GetAudioOut().AddData(buf_u16, out_buffer_size * sizeof(u16)); } } else { - break; + if (!opened) + { + Emu.GetAudioManager().GetAudioOut().Open(buffer, out_buffer_size * sizeof(float)); + opened = true; + } + else + { + Emu.GetAudioManager().GetAudioOut().AddData(buffer, out_buffer_size * sizeof(float)); + } } } @@ -143,7 +137,7 @@ int cellAudioInit() // TODO: send beforemix event (in ~2,6 ms before mixing) // precise time of sleeping: 5,(3) ms (or 256/48000 sec) - if (g_audio.counter * 256000000 / 48000 >= stamp0 - g_audio.start_time) + if (g_audio.counter * AUDIO_SAMPLES * MHZ / 48000 >= stamp0 - g_audio.start_time) { std::this_thread::sleep_for(std::chrono::milliseconds(1)); continue; @@ -165,14 +159,39 @@ int cellAudioInit() { if (port.state.read_relaxed() != AUDIO_PORT_STATE_STARTED) continue; - const u32 block_size = port.channel * 256; + const u32 block_size = port.channel * AUDIO_SAMPLES; const u32 position = port.tag % port.block; // old value const u32 buf_addr = port.addr + position * block_size * sizeof(float); auto buf = vm::get_ptr>(buf_addr); static const float k = 1.0f; // may be 1.0f - const float m = port.level; + const float& m = port.level; + + auto step_volume = [](AudioPortConfig& port) // part of cellAudioSetPortLevel functionality + { + if (port.level_inc) + { + port.level += port.level_inc; + + if (port.level_inc > 0.0f) + { + if (port.level_set - port.level <= 0.0f) + { + port.level = port.level_set; + port.level_inc = 0.0f; + } + } + else + { + if (port.level_set - port.level >= 0.0f) + { + port.level = port.level_set; + port.level_inc = 0.0f; + } + } + } + }; if (port.channel == 2) { @@ -180,6 +199,8 @@ int cellAudioInit() { for (u32 i = 0; i < (sizeof(buf2ch) / sizeof(float)); i += 2) { + step_volume(port); + // reverse byte order const float left = buf[i + 0] * m; const float right = buf[i + 1] * m; @@ -202,6 +223,8 @@ int cellAudioInit() { for (u32 i = 0; i < (sizeof(buf2ch) / sizeof(float)); i += 2) { + step_volume(port); + const float left = buf[i + 0] * m; const float right = buf[i + 1] * m; @@ -213,64 +236,14 @@ int cellAudioInit() } } } - else if (port.channel == 6) - { - if (first_mix) - { - for (u32 i = 0; i < (sizeof(buf2ch) / sizeof(float)); i += 2) - { - const float left = buf[i * 3 + 0] * m; - const float right = buf[i * 3 + 1] * m; - const float center = buf[i * 3 + 2] * m; - const float low_freq = buf[i * 3 + 3] * m; - const float rear_left = buf[i * 3 + 4] * m; - const float rear_right = buf[i * 3 + 5] * m; - - const float mid = (center + low_freq) * 0.708f; - buf2ch[i + 0] = (left + rear_left + mid) * k; - buf2ch[i + 1] = (right + rear_right + mid) * k; - - buf8ch[i * 4 + 0] = left; - buf8ch[i * 4 + 1] = right; - buf8ch[i * 4 + 2] = center; - buf8ch[i * 4 + 3] = low_freq; - buf8ch[i * 4 + 4] = rear_left; - buf8ch[i * 4 + 5] = rear_right; - buf8ch[i * 4 + 6] = 0.0f; - buf8ch[i * 4 + 7] = 0.0f; - } - first_mix = false; - } - else - { - for (u32 i = 0; i < (sizeof(buf2ch) / sizeof(float)); i += 2) - { - const float left = buf[i * 3 + 0] * m; - const float right = buf[i * 3 + 1] * m; - const float center = buf[i * 3 + 2] * m; - const float low_freq = buf[i * 3 + 3] * m; - const float rear_left = buf[i * 3 + 4] * m; - const float rear_right = buf[i * 3 + 5] * m; - - const float mid = (center + low_freq) * 0.708f; - buf2ch[i + 0] += (left + rear_left + mid) * k; - buf2ch[i + 1] += (right + rear_right + mid) * k; - - buf8ch[i * 4 + 0] += left; - buf8ch[i * 4 + 1] += right; - buf8ch[i * 4 + 2] += center; - buf8ch[i * 4 + 3] += low_freq; - buf8ch[i * 4 + 4] += rear_left; - buf8ch[i * 4 + 5] += rear_right; - } - } - } else if (port.channel == 8) { if (first_mix) { for (u32 i = 0; i < (sizeof(buf2ch) / sizeof(float)); i += 2) { + step_volume(port); + const float left = buf[i * 4 + 0] * m; const float right = buf[i * 4 + 1] * m; const float center = buf[i * 4 + 2] * m; @@ -299,6 +272,8 @@ int cellAudioInit() { for (u32 i = 0; i < (sizeof(buf2ch) / sizeof(float)); i += 2) { + step_volume(port); + const float left = buf[i * 4 + 0] * m; const float right = buf[i * 4 + 1] * m; const float center = buf[i * 4 + 2] * m; @@ -323,6 +298,10 @@ int cellAudioInit() } } } + else + { + throw fmt::format("Unknown channel count (port=%d, channel=%d)", &port - g_audio.ports, port.channel); + } memset(buf, 0, block_size * sizeof(float)); } @@ -384,36 +363,31 @@ int cellAudioInit() { if (m_dump.WriteData(&buf8ch, sizeof(buf8ch)) != sizeof(buf8ch)) // write file data { - cellAudio->Error("AudioDumper::WriteData() failed"); - break; + throw "AudioDumper::WriteData() failed (2 ch)"; } } else if (m_dump.GetCh() == 2) { if (m_dump.WriteData(&buf2ch, sizeof(buf2ch)) != sizeof(buf2ch)) // write file data { - cellAudio->Error("AudioDumper::WriteData() failed"); - break; + throw "AudioDumper::WriteData() failed (8 ch)"; } } else { - cellAudio->Error("AudioDumper::GetCh() returned unknown value (%d)", m_dump.GetCh()); - break; + throw fmt::format("AudioDumper::GetCh() returned unknown value (%d)", m_dump.GetCh()); } } //LOG_NOTICE(HLE, "Audio perf: start=%d (access=%d, AddData=%d, events=%d, dump=%d)", //stamp0 - m_config.start_time, stamp1 - stamp0, stamp2 - stamp1, stamp3 - stamp2, get_system_time() - stamp3); } - - iat.join(); }); return CELL_OK; } -int cellAudioQuit() +s32 cellAudioQuit() { cellAudio->Warning("cellAudioQuit()"); @@ -427,84 +401,153 @@ int cellAudioQuit() return CELL_OK; } -int cellAudioPortOpen(vm::ptr audioParam, vm::ptr portNum) +s32 cellAudioPortOpen(vm::ptr audioParam, vm::ptr portNum) { - cellAudio->Warning("cellAudioPortOpen(audioParam_addr=0x%x, portNum_addr=0x%x)", audioParam.addr(), portNum.addr()); + cellAudio->Warning("cellAudioPortOpen(audioParam=0x%x, portNum=0x%x)", audioParam, portNum); - if (audioParam->nChannel > 8 || audioParam->nBlock > 16) + if (g_audio.state.read_relaxed() != AUDIO_STATE_INITIALIZED) + { + return CELL_AUDIO_ERROR_NOT_INIT; + } + + if (!audioParam || !portNum) { return CELL_AUDIO_ERROR_PARAM; } - for (u32 i = 0; i < AUDIO_PORT_COUNT; i++) + const u64 channel = audioParam->nChannel; + const u64 block = audioParam->nBlock; + const u64 attr = audioParam->attr; + + // check attributes + if (channel != CELL_AUDIO_PORT_2CH && + channel != CELL_AUDIO_PORT_8CH && + channel) { - if (g_audio.ports[i].state.compare_and_swap_test(AUDIO_PORT_STATE_NOT_OPENED, AUDIO_PORT_STATE_OPENED)) - { - AudioPortConfig& port = g_audio.ports[i]; - - port.channel = (u8)audioParam->nChannel; - port.block = (u8)audioParam->nBlock; - port.attr = audioParam->attr; - port.addr = g_audio.buffer + (128 * 1024 * i); - port.read_index_addr = g_audio.indexes + (sizeof(u64) * i); - port.size = port.channel * port.block * 256 * sizeof(float); - if (port.attr & CELL_AUDIO_PORTATTR_INITLEVEL) - { - port.level = audioParam->level; - } - else - { - port.level = 1.0f; - } - - *portNum = i; - cellAudio->Warning("*** audio port opened(nChannel=%d, nBlock=%d, attr=0x%llx, level=%f): port = %d", - port.channel, port.block, port.attr, port.level, i); - - port.tag = 0; - return CELL_OK; - } + return CELL_AUDIO_ERROR_PARAM; } - return CELL_AUDIO_ERROR_PORT_FULL; + if (block != CELL_AUDIO_BLOCK_8 && + block != CELL_AUDIO_BLOCK_16 && + block != 2 && + block != 4 && + block != 32) + { + return CELL_AUDIO_ERROR_PARAM; + } + + // list unsupported flags + if (attr & CELL_AUDIO_PORTATTR_BGM) + { + cellAudio->Todo("cellAudioPortOpen(): CELL_AUDIO_PORTATTR_BGM"); + } + if (attr & CELL_AUDIO_PORTATTR_OUT_SECONDARY) + { + cellAudio->Todo("cellAudioPortOpen(): CELL_AUDIO_PORTATTR_OUT_SECONDARY"); + } + if (attr & CELL_AUDIO_PORTATTR_OUT_PERSONAL_0) + { + cellAudio->Todo("cellAudioPortOpen(): CELL_AUDIO_PORTATTR_OUT_PERSONAL_0"); + } + if (attr & CELL_AUDIO_PORTATTR_OUT_PERSONAL_1) + { + cellAudio->Todo("cellAudioPortOpen(): CELL_AUDIO_PORTATTR_OUT_PERSONAL_1"); + } + if (attr & CELL_AUDIO_PORTATTR_OUT_PERSONAL_2) + { + cellAudio->Todo("cellAudioPortOpen(): CELL_AUDIO_PORTATTR_OUT_PERSONAL_2"); + } + if (attr & CELL_AUDIO_PORTATTR_OUT_PERSONAL_3) + { + cellAudio->Todo("cellAudioPortOpen(): CELL_AUDIO_PORTATTR_OUT_PERSONAL_3"); + } + if (attr & CELL_AUDIO_PORTATTR_OUT_NO_ROUTE) + { + cellAudio->Todo("cellAudioPortOpen(): CELL_AUDIO_PORTATTR_OUT_NO_ROUTE"); + } + if (attr & 0xFFFFFFFFF0EFEFEEULL) + { + cellAudio->Todo("cellAudioPortOpen(): unknown attributes set (0x%llx)", attr); + } + + // open audio port + const u32 port_index = g_audio.open_port(); + + if (!~port_index) + { + return CELL_AUDIO_ERROR_PORT_FULL; + } + + AudioPortConfig& port = g_audio.ports[port_index]; + + port.channel = (u32)channel; + port.block = (u32)block; + port.attr = attr; + port.addr = g_audio.buffer + AUDIO_PORT_OFFSET * port_index; + port.read_index_addr = g_audio.indexes + sizeof(u64) * port_index; + port.size = port.channel * port.block * AUDIO_SAMPLES * sizeof(float); + port.tag = 0; + + if (port.attr & CELL_AUDIO_PORTATTR_INITLEVEL) + { + port.level = audioParam->level; + } + else + { + port.level = 1.0f; + } + + port.level_set = port.level; + port.level_inc = 0.0f; + + *portNum = port_index; + cellAudio->Warning("*** audio port opened(nChannel=%d, nBlock=%d, attr=0x%llx, level=%f): port = %d", channel, block, attr, port.level, port_index); + + return CELL_OK; } -int cellAudioGetPortConfig(u32 portNum, vm::ptr portConfig) +s32 cellAudioGetPortConfig(u32 portNum, vm::ptr portConfig) { - cellAudio->Warning("cellAudioGetPortConfig(portNum=0x%x, portConfig_addr=0x%x)", portNum, portConfig.addr()); + cellAudio->Warning("cellAudioGetPortConfig(portNum=0x%x, portConfig=0x%x)", portNum, portConfig); - if (portNum >= AUDIO_PORT_COUNT) + if (g_audio.state.read_relaxed() != AUDIO_STATE_INITIALIZED) + { + return CELL_AUDIO_ERROR_NOT_INIT; + } + + if (!portConfig || portNum >= AUDIO_PORT_COUNT) { return CELL_AUDIO_ERROR_PARAM; } - switch (auto state = g_audio.ports[portNum].state.read_sync()) + AudioPortConfig& port = g_audio.ports[portNum]; + + portConfig->readIndexAddr = port.read_index_addr; + + switch (auto state = port.state.read_sync()) { - case AUDIO_PORT_STATE_NOT_OPENED: portConfig->status = CELL_AUDIO_STATUS_CLOSE; break; + case AUDIO_PORT_STATE_CLOSED: portConfig->status = CELL_AUDIO_STATUS_CLOSE; break; case AUDIO_PORT_STATE_OPENED: portConfig->status = CELL_AUDIO_STATUS_READY; break; case AUDIO_PORT_STATE_STARTED: portConfig->status = CELL_AUDIO_STATUS_RUN; break; default: throw fmt::format("cellAudioGetPortConfig(%d): invalid port state (0x%x)", portNum, state); } - AudioPortConfig& port = g_audio.ports[portNum]; - portConfig->nChannel = port.channel; portConfig->nBlock = port.block; portConfig->portSize = port.size; - portConfig->portAddr = port.addr; // 0x20020000 - portConfig->readIndexAddr = port.read_index_addr; // 0x20010010 on ps3 - - cellAudio->Log("*** port config: nChannel=%d, nBlock=%d, portSize=0x%x, portAddr=0x%x, readIndexAddr=0x%x", - (u32)portConfig->nChannel, (u32)portConfig->nBlock, (u32)portConfig->portSize, (u32)portConfig->portAddr, (u32)portConfig->readIndexAddr); - // portAddr - readIndexAddr == 0xFFF0 on ps3 - + portConfig->portAddr = port.addr; return CELL_OK; } -int cellAudioPortStart(u32 portNum) +s32 cellAudioPortStart(u32 portNum) { cellAudio->Warning("cellAudioPortStart(portNum=0x%x)", portNum); + if (g_audio.state.read_relaxed() != AUDIO_STATE_INITIALIZED) + { + return CELL_AUDIO_ERROR_NOT_INIT; + } + if (portNum >= AUDIO_PORT_COUNT) { return CELL_AUDIO_ERROR_PARAM; @@ -512,35 +555,45 @@ int cellAudioPortStart(u32 portNum) switch (auto state = g_audio.ports[portNum].state.compare_and_swap(AUDIO_PORT_STATE_OPENED, AUDIO_PORT_STATE_STARTED)) { - case AUDIO_PORT_STATE_NOT_OPENED: return CELL_AUDIO_ERROR_PORT_NOT_OPEN; + case AUDIO_PORT_STATE_CLOSED: return CELL_AUDIO_ERROR_PORT_NOT_OPEN; case AUDIO_PORT_STATE_STARTED: return CELL_AUDIO_ERROR_PORT_ALREADY_RUN; case AUDIO_PORT_STATE_OPENED: return CELL_OK; default: throw fmt::format("cellAudioPortStart(%d): invalid port state (0x%x)", portNum, state); } } -int cellAudioPortClose(u32 portNum) +s32 cellAudioPortClose(u32 portNum) { cellAudio->Warning("cellAudioPortClose(portNum=0x%x)", portNum); + if (g_audio.state.read_relaxed() != AUDIO_STATE_INITIALIZED) + { + return CELL_AUDIO_ERROR_NOT_INIT; + } + if (portNum >= AUDIO_PORT_COUNT) { return CELL_AUDIO_ERROR_PARAM; } - switch (auto state = g_audio.ports[portNum].state.exchange(AUDIO_PORT_STATE_NOT_OPENED)) + switch (auto state = g_audio.ports[portNum].state.exchange(AUDIO_PORT_STATE_CLOSED)) { - case AUDIO_PORT_STATE_NOT_OPENED: return CELL_AUDIO_ERROR_PORT_NOT_OPEN; + case AUDIO_PORT_STATE_CLOSED: return CELL_AUDIO_ERROR_PORT_NOT_OPEN; case AUDIO_PORT_STATE_STARTED: return CELL_OK; case AUDIO_PORT_STATE_OPENED: return CELL_OK; default: throw fmt::format("cellAudioPortClose(%d): invalid port state (0x%x)", portNum, state); } } -int cellAudioPortStop(u32 portNum) +s32 cellAudioPortStop(u32 portNum) { cellAudio->Warning("cellAudioPortStop(portNum=0x%x)", portNum); + if (g_audio.state.read_relaxed() != AUDIO_STATE_INITIALIZED) + { + return CELL_AUDIO_ERROR_NOT_INIT; + } + if (portNum >= AUDIO_PORT_COUNT) { return CELL_AUDIO_ERROR_PARAM; @@ -548,34 +601,36 @@ int cellAudioPortStop(u32 portNum) switch (auto state = g_audio.ports[portNum].state.compare_and_swap(AUDIO_PORT_STATE_STARTED, AUDIO_PORT_STATE_OPENED)) { - case AUDIO_PORT_STATE_NOT_OPENED: return CELL_AUDIO_ERROR_PORT_NOT_OPEN; + case AUDIO_PORT_STATE_CLOSED: return CELL_AUDIO_ERROR_PORT_NOT_RUN; case AUDIO_PORT_STATE_STARTED: return CELL_OK; - case AUDIO_PORT_STATE_OPENED: return CELL_AUDIO_ERROR_PORT_OPEN; + case AUDIO_PORT_STATE_OPENED: return CELL_AUDIO_ERROR_PORT_NOT_RUN; default: throw fmt::format("cellAudioPortStop(%d): invalid port state (0x%x)", portNum, state); } } -int cellAudioGetPortTimestamp(u32 portNum, u64 tag, vm::ptr stamp) +s32 cellAudioGetPortTimestamp(u32 portNum, u64 tag, vm::ptr stamp) { - cellAudio->Log("cellAudioGetPortTimestamp(portNum=0x%x, tag=0x%llx, stamp_addr=0x%x)", portNum, tag, stamp.addr()); + cellAudio->Log("cellAudioGetPortTimestamp(portNum=0x%x, tag=0x%llx, stamp=0x%x)", portNum, tag, stamp); + + if (g_audio.state.read_relaxed() != AUDIO_STATE_INITIALIZED) + { + return CELL_AUDIO_ERROR_NOT_INIT; + } if (portNum >= AUDIO_PORT_COUNT) { return CELL_AUDIO_ERROR_PARAM; } - //if (!g_audio.ports[portNum].is_audio_port_opened) - //{ - // return CELL_AUDIO_ERROR_PORT_NOT_OPEN; - //} - - //if (!g_audio.ports[portNum].is_audio_port_started) - //{ - // return CELL_AUDIO_ERROR_PORT_NOT_RUN; - //} - AudioPortConfig& port = g_audio.ports[portNum]; + if (port.state.read_relaxed() == AUDIO_PORT_STATE_CLOSED) + { + return CELL_AUDIO_ERROR_PORT_NOT_OPEN; + } + + // TODO: check tag (CELL_AUDIO_ERROR_TAG_NOT_FOUND error) + std::lock_guard lock(g_audio.mutex); *stamp = g_audio.start_time + (port.counter + (tag - port.tag)) * 256000000 / 48000; @@ -583,30 +638,29 @@ int cellAudioGetPortTimestamp(u32 portNum, u64 tag, vm::ptr stamp) return CELL_OK; } -int cellAudioGetPortBlockTag(u32 portNum, u64 blockNo, vm::ptr tag) +s32 cellAudioGetPortBlockTag(u32 portNum, u64 blockNo, vm::ptr tag) { - cellAudio->Log("cellAudioGetPortBlockTag(portNum=0x%x, blockNo=0x%llx, tag_addr=0x%x)", portNum, blockNo, tag.addr()); + cellAudio->Log("cellAudioGetPortBlockTag(portNum=0x%x, blockNo=0x%llx, tag=0x%x)", portNum, blockNo, tag); + + if (g_audio.state.read_relaxed() != AUDIO_STATE_INITIALIZED) + { + return CELL_AUDIO_ERROR_NOT_INIT; + } if (portNum >= AUDIO_PORT_COUNT) { return CELL_AUDIO_ERROR_PARAM; } - //if (!g_audio.ports[portNum].is_audio_port_opened) - //{ - // return CELL_AUDIO_ERROR_PORT_NOT_OPEN; - //} - - //if (!g_audio.ports[portNum].is_audio_port_started) - //{ - // return CELL_AUDIO_ERROR_PORT_NOT_RUN; - //} - AudioPortConfig& port = g_audio.ports[portNum]; + if (port.state.read_relaxed() == AUDIO_PORT_STATE_CLOSED) + { + return CELL_AUDIO_ERROR_PORT_NOT_OPEN; + } + if (blockNo >= port.block) { - cellAudio->Error("cellAudioGetPortBlockTag: wrong blockNo(%lld)", blockNo); return CELL_AUDIO_ERROR_PARAM; } @@ -627,9 +681,14 @@ int cellAudioGetPortBlockTag(u32 portNum, u64 blockNo, vm::ptr tag) return CELL_OK; } -int cellAudioSetPortLevel(u32 portNum, float level) +s32 cellAudioSetPortLevel(u32 portNum, float level) { - cellAudio->Todo("cellAudioSetPortLevel(portNum=0x%x, level=%f)", portNum, level); + cellAudio->Log("cellAudioSetPortLevel(portNum=0x%x, level=%f)", portNum, level); + + if (g_audio.state.read_relaxed() != AUDIO_STATE_INITIALIZED) + { + return CELL_AUDIO_ERROR_NOT_INIT; + } if (portNum >= AUDIO_PORT_COUNT) { @@ -638,19 +697,22 @@ int cellAudioSetPortLevel(u32 portNum, float level) AudioPortConfig& port = g_audio.ports[portNum]; - //if (!port.is_audio_port_opened) - //{ - // return CELL_AUDIO_ERROR_PORT_NOT_OPEN; - //} + if (port.state.read_relaxed() == AUDIO_PORT_STATE_CLOSED) + { + return CELL_AUDIO_ERROR_PORT_NOT_OPEN; + } - //if (!port.is_audio_port_started) - //{ - // return CELL_AUDIO_ERROR_PORT_NOT_RUN; - //} + if (level >= 0.0f) + { + std::lock_guard lock(g_audio.mutex); - std::lock_guard lock(g_audio.mutex); - - port.level = level; // TODO + port.level_set = level; + port.level_inc = (port.level - level) / 624.0f; + } + else + { + cellAudio->Todo("cellAudioSetPortLevel(portNum=0x%x): negative level value (%f)", portNum, level); + } return CELL_OK; } diff --git a/rpcs3/Emu/SysCalls/Modules/cellAudio.h b/rpcs3/Emu/SysCalls/Modules/cellAudio.h index 83fd4fc097..ca165476c1 100644 --- a/rpcs3/Emu/SysCalls/Modules/cellAudio.h +++ b/rpcs3/Emu/SysCalls/Modules/cellAudio.h @@ -77,6 +77,8 @@ enum : u32 BUFFER_NUM = 32, BUFFER_SIZE = 256, AUDIO_PORT_COUNT = 8, + AUDIO_PORT_OFFSET = 256 * 1024, + AUDIO_SAMPLES = CELL_AUDIO_BLOCK_SAMPLES, }; enum AudioState : u32 @@ -88,7 +90,7 @@ enum AudioState : u32 enum AudioPortState : u32 { - AUDIO_PORT_STATE_NOT_OPENED, + AUDIO_PORT_STATE_CLOSED, AUDIO_PORT_STATE_OPENED, AUDIO_PORT_STATE_STARTED, }; @@ -98,15 +100,17 @@ struct AudioPortConfig std::mutex mutex; atomic_le_t state; - u8 channel; - u8 block; - float level; + u32 channel; + u32 block; u64 attr; u64 tag; u64 counter; // copy of global counter u32 addr; u32 read_index_addr; u32 size; + float level; + float level_set; + float level_inc; }; struct AudioConfig //custom structure @@ -125,6 +129,19 @@ struct AudioConfig //custom structure AudioConfig() : audio_thread("Audio Thread") { } + + u32 open_port() + { + for (u32 i = 0; i < AUDIO_PORT_COUNT; i++) + { + if (ports[i].state.compare_and_swap_test(AUDIO_PORT_STATE_CLOSED, AUDIO_PORT_STATE_OPENED)) + { + return i; + } + } + + return ~0; + } }; extern AudioConfig g_audio; diff --git a/rpcs3/Emu/SysCalls/Modules/libmixer.cpp b/rpcs3/Emu/SysCalls/Modules/libmixer.cpp index 9591474ec7..0030923d06 100644 --- a/rpcs3/Emu/SysCalls/Modules/libmixer.cpp +++ b/rpcs3/Emu/SysCalls/Modules/libmixer.cpp @@ -10,9 +10,7 @@ Module *libmixer = nullptr; -CellSurMixerConfig surMixer; - -#define SUR_PORT (7) +SurMixerConfig g_surmx; vm::ptr surMixerCb; vm::ptr surMixerCbArg; std::mutex mixer_mutex; @@ -31,13 +29,13 @@ int cellAANAddData(u32 aan_handle, u32 aan_port, u32 offset, vm::ptr addr switch (type) { case CELL_SURMIXER_CHSTRIP_TYPE1A: - if (port >= surMixer.chStrips1) type = 0; break; + if (port >= g_surmx.ch_strips_1) type = 0; break; case CELL_SURMIXER_CHSTRIP_TYPE2A: - if (port >= surMixer.chStrips2) type = 0; break; + if (port >= g_surmx.ch_strips_2) type = 0; break; case CELL_SURMIXER_CHSTRIP_TYPE6A: - if (port >= surMixer.chStrips6) type = 0; break; + if (port >= g_surmx.ch_strips_6) type = 0; break; case CELL_SURMIXER_CHSTRIP_TYPE8A: - if (port >= surMixer.chStrips8) type = 0; break; + if (port >= g_surmx.ch_strips_8) type = 0; break; default: type = 0; break; } @@ -296,15 +294,21 @@ int cellSurMixerCreate(vm::ptr config) { libmixer->Warning("cellSurMixerCreate(config_addr=0x%x)", config.addr()); - surMixer = *config; + g_surmx.audio_port = g_audio.open_port(); - AudioPortConfig& port = g_audio.ports[SUR_PORT]; - - if (!port.state.compare_and_swap_test(AUDIO_PORT_STATE_NOT_OPENED, AUDIO_PORT_STATE_OPENED)) + if (!~g_surmx.audio_port) { return CELL_LIBMIXER_ERROR_FULL; } + g_surmx.priority = config->priority; + g_surmx.ch_strips_1 = config->chStrips1; + g_surmx.ch_strips_2 = config->chStrips2; + g_surmx.ch_strips_6 = config->chStrips6; + g_surmx.ch_strips_8 = config->chStrips8; + + AudioPortConfig& port = g_audio.ports[g_surmx.audio_port]; + port.channel = 8; port.block = 16; port.attr = 0; @@ -316,12 +320,11 @@ int cellSurMixerCreate(vm::ptr config) mixcount = 0; surMixerCb.set(0); - libmixer->Warning("*** surMixer created (ch1=%d, ch2=%d, ch6=%d, ch8=%d)", - (u32)surMixer.chStrips1, (u32)surMixer.chStrips2, (u32)surMixer.chStrips6, (u32)surMixer.chStrips8); + libmixer->Warning("*** surMixer created (ch1=%d, ch2=%d, ch6=%d, ch8=%d)", config->chStrips1, config->chStrips2, config->chStrips6, config->chStrips8); thread_t t("Surmixer Thread", []() { - AudioPortConfig& port = g_audio.ports[SUR_PORT]; + AudioPortConfig& port = g_audio.ports[g_surmx.audio_port]; PPUThread& cb_thread = *(PPUThread*)&Emu.GetCPU().AddThread(CPU_THREAD_PPU); cb_thread.SetName("Surmixer Callback Thread"); @@ -332,14 +335,8 @@ int cellSurMixerCreate(vm::ptr config) cb_thread.InitRegs(); cb_thread.DoRun(); - while (port.state.read_relaxed() != AUDIO_PORT_STATE_NOT_OPENED) + while (port.state.read_relaxed() != AUDIO_PORT_STATE_CLOSED && !Emu.IsStopped()) { - if (Emu.IsStopped()) - { - libmixer->Warning("Surmixer aborted"); - break; - } - if (mixcount > (port.tag + 0)) // adding positive value (1-15): preemptive buffer filling (hack) { std::this_thread::sleep_for(std::chrono::milliseconds(1)); // hack @@ -437,7 +434,7 @@ int cellSurMixerCreate(vm::ptr config) //u64 stamp2 = get_system_time(); - auto buf = vm::get_ptr>(g_audio.buffer + (128 * 1024 * SUR_PORT) + (mixcount % port.block) * port.channel * 256 * sizeof(float)); + auto buf = vm::get_ptr>(port.addr + (mixcount % port.block) * port.channel * AUDIO_SAMPLES * sizeof(float)); for (u32 i = 0; i < (sizeof(mixdata) / sizeof(float)); i++) { @@ -511,7 +508,12 @@ int cellSurMixerStart() { libmixer->Warning("cellSurMixerStart()"); - g_audio.ports[SUR_PORT].state.compare_and_swap(AUDIO_PORT_STATE_OPENED, AUDIO_PORT_STATE_STARTED); + if (g_surmx.audio_port >= AUDIO_PORT_COUNT) + { + return CELL_LIBMIXER_ERROR_NOT_INITIALIZED; + } + + g_audio.ports[g_surmx.audio_port].state.compare_and_swap(AUDIO_PORT_STATE_OPENED, AUDIO_PORT_STATE_STARTED); return CELL_OK; } @@ -526,12 +528,17 @@ int cellSurMixerFinalize() { libmixer->Warning("cellSurMixerFinalize()"); - g_audio.ports[SUR_PORT].state.compare_and_swap(AUDIO_PORT_STATE_OPENED, AUDIO_PORT_STATE_NOT_OPENED); + if (g_surmx.audio_port >= AUDIO_PORT_COUNT) + { + return CELL_LIBMIXER_ERROR_NOT_INITIALIZED; + } + + g_audio.ports[g_surmx.audio_port].state.compare_and_swap(AUDIO_PORT_STATE_OPENED, AUDIO_PORT_STATE_CLOSED); return CELL_OK; } -int cellSurMixerSurBusAddData(u32 busNo, u32 offset, u32 addr, u32 samples) +int cellSurMixerSurBusAddData(u32 busNo, u32 offset, vm::ptr addr, u32 samples) { if (busNo < 8 && samples == 256 && offset == 0) { @@ -548,8 +555,7 @@ int cellSurMixerSurBusAddData(u32 busNo, u32 offset, u32 addr, u32 samples) for (u32 i = 0; i < samples; i++) { // reverse byte order and mix - u32 v = vm::read32(addr + i * sizeof(float)); - mixdata[i*8+busNo] += (float&)v; + mixdata[i * 8 + busNo] += addr[i]; } return CELL_OK; @@ -565,7 +571,12 @@ int cellSurMixerPause(u32 type) { libmixer->Warning("cellSurMixerPause(type=%d)", type); - g_audio.ports[SUR_PORT].state.compare_and_swap(AUDIO_PORT_STATE_STARTED, AUDIO_PORT_STATE_OPENED); + if (g_surmx.audio_port >= AUDIO_PORT_COUNT) + { + return CELL_LIBMIXER_ERROR_NOT_INITIALIZED; + } + + g_audio.ports[g_surmx.audio_port].state.compare_and_swap(AUDIO_PORT_STATE_STARTED, AUDIO_PORT_STATE_OPENED); return CELL_OK; } @@ -617,6 +628,8 @@ void libmixer_init(Module *pxThis) { libmixer = pxThis; + g_surmx.audio_port = ~0; + REG_SUB(libmixer, "surmxAAN", cellAANAddData, 0xffffffff7c691b78, 0xffffffff7c0802a6, diff --git a/rpcs3/Emu/SysCalls/Modules/libmixer.h b/rpcs3/Emu/SysCalls/Modules/libmixer.h index 45f8a26af9..9a7978f9a5 100644 --- a/rpcs3/Emu/SysCalls/Modules/libmixer.h +++ b/rpcs3/Emu/SysCalls/Modules/libmixer.h @@ -1,6 +1,7 @@ #pragma once -enum //libmixer Error Codes +// Error Codes +enum { CELL_LIBMIXER_ERROR_NOT_INITIALIZED = 0x80310002, CELL_LIBMIXER_ERROR_INVALID_PARAMATER = 0x80310003, @@ -164,6 +165,16 @@ struct CellSurMixerChStripParam be_t intVal; }; +struct SurMixerConfig +{ + u32 audio_port; + s32 priority; + u32 ch_strips_1; + u32 ch_strips_2; + u32 ch_strips_6; + u32 ch_strips_8; +}; + struct SSPlayer { bool m_created; // SSPlayerCreate/Remove diff --git a/rpcs3/Emu/SysCalls/lv2/sys_time.h b/rpcs3/Emu/SysCalls/lv2/sys_time.h index 34d755db8a..dacff931e0 100644 --- a/rpcs3/Emu/SysCalls/lv2/sys_time.h +++ b/rpcs3/Emu/SysCalls/lv2/sys_time.h @@ -1,6 +1,9 @@ #pragma once -#define MHZ (1000000) +enum : u32 +{ + MHZ = 1000000, +}; // Auxiliary functions u64 get_time(); diff --git a/rpcs3/Emu/System.cpp b/rpcs3/Emu/System.cpp index ee84464bd8..8696a631d0 100644 --- a/rpcs3/Emu/System.cpp +++ b/rpcs3/Emu/System.cpp @@ -328,6 +328,8 @@ void Emulator::Pause() if (InterlockedCompareExchange((volatile u32*)&m_status, Paused, Running) == Running) { SendDbgCommand(DID_PAUSED_EMU); + + GetCallbackManager().RunPauseCallbacks(true); } } @@ -341,6 +343,8 @@ void Emulator::Resume() CheckStatus(); SendDbgCommand(DID_RESUMED_EMU); + + GetCallbackManager().RunPauseCallbacks(false); } void Emulator::Stop()