diff --git a/Utilities/Thread.cpp b/Utilities/Thread.cpp index 1af3f5da95..a3831a3f98 100644 --- a/Utilities/Thread.cpp +++ b/Utilities/Thread.cpp @@ -1,5 +1,6 @@ #include "stdafx.h" #include "Log.h" +#include "rpcs3/Ini.h" #include "Emu/System.h" #include "Emu/CPU/CPUThread.h" #include "Emu/SysCalls/SysCalls.h" @@ -285,7 +286,7 @@ void signal_handler(int sig, siginfo_t* info, void* uct) ucontext_t* const ctx = (ucontext_t*)uct; const u64 addr64 = (u64)info->si_addr - (u64)Memory.GetBaseAddr(); //const bool is_writing = false; // TODO: get it correctly - if (addr64 < 0x100000000ull) + if (addr64 < 0x100000000ull && GetCurrentNamedThread()) { const u32 addr = (u32)addr64; if (addr - RAW_SPU_BASE_ADDR < (6 * RAW_SPU_OFFSET) && (addr % RAW_SPU_OFFSET) >= RAW_SPU_PROB_OFFSET) // RawSPU MMIO registers @@ -384,7 +385,7 @@ NamedThreadBase* GetCurrentNamedThread() void SetCurrentNamedThread(NamedThreadBase* value) { - auto old_value = g_tls_this_thread; + const auto old_value = g_tls_this_thread; if (old_value == value) { @@ -536,23 +537,63 @@ bool ThreadBase::TestDestroy() const return m_destroy; } -thread::thread(const std::string& name, std::function func) : m_name(name) +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::thread(const std::string& name) : m_name(name) +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::thread() +thread_t::thread_t() + : m_state(TS_NON_EXISTENT) + , m_autojoin(false) { } -void thread::start(std::function func) +void thread_t::set_name(const std::string& name) { + m_name = name; +} + +thread_t::~thread_t() +{ + if (m_state == TS_JOINABLE) + { + if (m_autojoin) + { + m_thr.join(); + } + else + { + m_thr.detach(); + } + } +} + +void thread_t::start(std::function func) +{ + if (m_state.exchange(TS_NON_EXISTENT) == TS_JOINABLE) + { + m_thr.join(); // forcefully join previously created thread + } + std::string name = m_name; - m_thr = std::thread([func, name]() { SetCurrentThreadDebugName(name.c_str()); @@ -567,6 +608,11 @@ void thread::start(std::function func) SetCurrentNamedThread(&info); g_thread_count++; + if (Ini.HLELogging.GetValue()) + { + LOG_NOTICE(HLE, name + " started"); + } + try { func(); @@ -580,6 +626,15 @@ void thread::start(std::function func) LOG_ERROR(GENERAL, "%s: %s", name.c_str(), e.c_str()); } + if (Emu.IsStopped()) + { + LOG_NOTICE(HLE, name + " aborted"); + } + else if (Ini.HLELogging.GetValue()) + { + LOG_NOTICE(HLE, name + " ended"); + } + SetCurrentNamedThread(nullptr); g_thread_count--; @@ -587,21 +642,41 @@ void thread::start(std::function func) _set_se_translator(old_se_translator); #endif }); + + if (m_state.exchange(TS_JOINABLE) == TS_JOINABLE) + { + assert(!"thread_t::start() failed"); // probably started from another thread + } } -void thread::detach() +void thread_t::detach() { - m_thr.detach(); + if (m_state.exchange(TS_NON_EXISTENT) == TS_JOINABLE) + { + m_thr.detach(); + } + else + { + assert(!"thread_t::detach() failed"); // probably joined or detached + } } -void thread::join() +void thread_t::join() { - m_thr.join(); + if (m_state.exchange(TS_NON_EXISTENT) == TS_JOINABLE) + { + m_thr.join(); + } + else + { + assert(!"thread_t::join() failed"); // probably joined or detached + } } -bool thread::joinable() const +bool thread_t::joinable() const { - return m_thr.joinable(); + //return m_thr.joinable(); + return m_state == TS_JOINABLE; } bool waiter_map_t::is_stopped(u64 signal_id) @@ -665,7 +740,10 @@ void waiter_map_t::notify(u64 signal_id) } } -bool squeue_test_exit(const volatile bool* do_exit) +const std::function SQUEUE_ALWAYS_EXIT = [](){ return true; }; +const std::function SQUEUE_NEVER_EXIT = [](){ return false; }; + +bool squeue_test_exit() { - return Emu.IsStopped() || (do_exit && *do_exit); + return Emu.IsStopped(); } diff --git a/Utilities/Thread.h b/Utilities/Thread.h index 4b54317bec..4e51438636 100644 --- a/Utilities/Thread.h +++ b/Utilities/Thread.h @@ -54,18 +54,34 @@ public: virtual void Task() = 0; }; -class thread +class thread_t { + enum thread_state_t + { + TS_NON_EXISTENT, + TS_JOINABLE, + }; + + std::atomic m_state; std::string m_name; std::thread m_thr; + bool m_autojoin; public: - thread(const std::string& name, std::function func); - thread(const std::string& name); - thread(); + 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(); + ~thread_t(); + thread_t(const thread_t& right) = delete; + thread_t(thread_t&& right) = delete; + + thread_t& operator =(const thread_t& right) = delete; + thread_t& operator =(thread_t&& right) = delete; public: + void set_name(const std::string& name); void start(std::function func); void detach(); void join(); @@ -148,7 +164,10 @@ public: void notify(u64 signal_id); }; -bool squeue_test_exit(const volatile bool* do_exit); +extern const std::function SQUEUE_ALWAYS_EXIT; +extern const std::function SQUEUE_NEVER_EXIT; + +bool squeue_test_exit(); template class squeue_t @@ -199,7 +218,7 @@ public: return m_sync.read_relaxed().count == sq_size; } - bool push(const T& data, const volatile bool* do_exit = nullptr) + bool push(const T& data, const std::function& test_exit) { u32 pos = 0; @@ -222,7 +241,7 @@ public: return SQSVR_OK; })) { - if (res == SQSVR_FAILED && squeue_test_exit(do_exit)) + if (res == SQSVR_FAILED && (test_exit() || squeue_test_exit())) { return false; } @@ -247,14 +266,22 @@ public: return true; } - bool try_push(const T& data) + bool push(const T& data, const volatile bool* do_exit) { - static const volatile bool no_wait = true; - - return push(data, &no_wait); + return push(data, [do_exit](){ return do_exit && *do_exit; }); } - bool pop(T& data, const volatile bool* do_exit = nullptr) + __forceinline bool push(const T& data) + { + return push(data, SQUEUE_NEVER_EXIT); + } + + __forceinline bool try_push(const T& data) + { + return push(data, SQUEUE_ALWAYS_EXIT); + } + + bool pop(T& data, const std::function& test_exit) { u32 pos = 0; @@ -277,7 +304,7 @@ public: return SQSVR_OK; })) { - if (res == SQSVR_FAILED && squeue_test_exit(do_exit)) + if (res == SQSVR_FAILED && (test_exit() || squeue_test_exit())) { return false; } @@ -307,14 +334,22 @@ public: return true; } - bool try_pop(T& data) + bool pop(T& data, const volatile bool* do_exit) { - static const volatile bool no_wait = true; - - return pop(data, &no_wait); + return pop(data, [do_exit](){ return do_exit && *do_exit; }); } - bool peek(T& data, u32 start_pos = 0, const volatile bool* do_exit = nullptr) + __forceinline bool pop(T& data) + { + return pop(data, SQUEUE_NEVER_EXIT); + } + + __forceinline bool try_pop(T& data) + { + return pop(data, SQUEUE_ALWAYS_EXIT); + } + + bool peek(T& data, u32 start_pos, const std::function& test_exit) { assert(start_pos < sq_size); u32 pos = 0; @@ -332,13 +367,13 @@ public: { return SQSVR_LOCKED; } - + sync.pop_lock = 1; pos = sync.position + start_pos; return SQSVR_OK; })) { - if (res == SQSVR_FAILED && squeue_test_exit(do_exit)) + if (res == SQSVR_FAILED && (test_exit() || squeue_test_exit())) { return false; } @@ -361,11 +396,19 @@ public: return true; } - bool try_peek(T& data, u32 start_pos = 0) + bool peek(T& data, u32 start_pos, const volatile bool* do_exit) { - static const volatile bool no_wait = true; + return peek(data, start_pos, [do_exit](){ return do_exit && *do_exit; }); + } - return peek(data, start_pos, &no_wait); + __forceinline bool peek(T& data, u32 start_pos = 0) + { + return peek(data, start_pos, SQUEUE_NEVER_EXIT); + } + + __forceinline bool try_peek(T& data, u32 start_pos = 0) + { + return peek(data, start_pos, SQUEUE_ALWAYS_EXIT); } class squeue_data_t diff --git a/rpcs3/Emu/ARMv7/Modules/sceLibc.cpp b/rpcs3/Emu/ARMv7/Modules/sceLibc.cpp index 95a1339e2d..9abcf593e4 100644 --- a/rpcs3/Emu/ARMv7/Modules/sceLibc.cpp +++ b/rpcs3/Emu/ARMv7/Modules/sceLibc.cpp @@ -26,7 +26,7 @@ namespace sce_libc_func }); } - void printf(vm::psv::ptr fmt) + void printf(vm::psv::ptr fmt) // va_args... { sceLibc.Error("printf(fmt=0x%x)", fmt); diff --git a/rpcs3/Emu/ARMv7/PSVFuncList.cpp b/rpcs3/Emu/ARMv7/PSVFuncList.cpp index d1d3d2b1a6..84262b3b0f 100644 --- a/rpcs3/Emu/ARMv7/PSVFuncList.cpp +++ b/rpcs3/Emu/ARMv7/PSVFuncList.cpp @@ -1,4 +1,5 @@ #include "stdafx.h" +#include #include "Utilities/Log.h" #include "Emu/System.h" #include "PSVFuncList.h" @@ -7,29 +8,19 @@ std::vector g_psv_func_list; void add_psv_func(psv_func& data) { + // setup special functions (without NIDs) if (!g_psv_func_list.size()) { psv_func unimplemented; - unimplemented.nid = 0x00000000; // must not be a valid id - unimplemented.name = "INVALID FUNCTION (0x0)"; - unimplemented.func.reset(new psv_func_detail::func_binder([]() -> u32 - { - LOG_ERROR(HLE, "Unimplemented function executed"); - Emu.Pause(); - - return 0xffffffffu; - })); + unimplemented.nid = 0; + unimplemented.name = "Special function (unimplemented stub)"; + unimplemented.func.reset(new psv_func_detail::func_binder([](ARMv7Thread& CPU){ CPU.m_last_syscall = vm::psv::read32(CPU.PC + 4); throw "Unimplemented function executed"; })); g_psv_func_list.push_back(unimplemented); psv_func hle_return; - hle_return.nid = 0x00000001; // must not be a valid id - hle_return.name = "INVALID FUNCTION (0x1)"; - hle_return.func.reset(new psv_func_detail::func_binder([](ARMv7Thread& CPU) - { - CPU.FastStop(); - - return; - })); + hle_return.nid = 1; + hle_return.name = "Special function (return from HLE)"; + hle_return.func.reset(new psv_func_detail::func_binder([](ARMv7Thread& CPU){ CPU.FastStop(); })); g_psv_func_list.push_back(hle_return); } @@ -40,7 +31,7 @@ psv_func* get_psv_func_by_nid(u32 nid) { for (auto& f : g_psv_func_list) { - if (f.nid == nid) + if (f.nid == nid && &f - g_psv_func_list.data() >= 2 /* special functions count */) { return &f; } @@ -61,8 +52,13 @@ u32 get_psv_func_index(psv_func* func) void execute_psv_func_by_index(ARMv7Thread& CPU, u32 index) { assert(index < g_psv_func_list.size()); + + auto old_last_syscall = CPU.m_last_syscall; + CPU.m_last_syscall = g_psv_func_list[index].nid; (*g_psv_func_list[index].func)(CPU); + + CPU.m_last_syscall = old_last_syscall; } extern psv_log_base sceLibc; diff --git a/rpcs3/Emu/Audio/AL/OpenALThread.cpp b/rpcs3/Emu/Audio/AL/OpenALThread.cpp index f63fda6dc6..14fcd2dc5e 100644 --- a/rpcs3/Emu/Audio/AL/OpenALThread.cpp +++ b/rpcs3/Emu/Audio/AL/OpenALThread.cpp @@ -11,8 +11,6 @@ ALCenum g_last_alc_error = ALC_NO_ERROR; #define checkForAlError(sit) if((g_last_al_error = alGetError()) != AL_NO_ERROR) printAlError(g_last_al_error, sit) #define checkForAlcError(sit) if((g_last_alc_error = alcGetError(m_device)) != ALC_NO_ERROR) printAlcError(g_last_alc_error, sit) -static const ALenum g_audio_format = Ini.AudioConvertToU16.GetValue() ? AL_FORMAT_STEREO16 : AL_FORMAT_STEREO_FLOAT32; - void printAlError(ALenum err, const char* situation) { if (err != AL_NO_ERROR) @@ -102,7 +100,7 @@ void OpenALThread::Open(const void* src, int size) for (uint i = 0; iStopEngine(); m_xaudio2_instance->Release(); m_xaudio2_instance = nullptr; + +#if (_WIN32_WINNT < 0x0602) + CoUninitialize(); +#endif } void XAudio2Thread::Play() @@ -87,13 +93,16 @@ void XAudio2Thread::Open(const void* src, int size) { HRESULT hr; + WORD sample_size = Ini.AudioConvertToU16.GetValue() ? sizeof(u16) : sizeof(float); + WORD channels = 8; + WAVEFORMATEX waveformatex; - waveformatex.wFormatTag = WAVE_FORMAT_IEEE_FLOAT; - waveformatex.nChannels = 2; + waveformatex.wFormatTag = Ini.AudioConvertToU16.GetValue() ? WAVE_FORMAT_PCM : WAVE_FORMAT_IEEE_FLOAT; + waveformatex.nChannels = channels; waveformatex.nSamplesPerSec = 48000; - waveformatex.nAvgBytesPerSec = 48000 * 2 * sizeof(float); - waveformatex.nBlockAlign = 2 * sizeof(float); - waveformatex.wBitsPerSample = 32; + waveformatex.nAvgBytesPerSec = 48000 * (DWORD)channels * (DWORD)sample_size; + waveformatex.nBlockAlign = channels * sample_size; + waveformatex.wBitsPerSample = sample_size * 8; waveformatex.cbSize = 0; hr = m_xaudio2_instance->CreateSourceVoice(&m_source_voice, &waveformatex, 0, XAUDIO2_DEFAULT_FREQ_RATIO); @@ -129,4 +138,4 @@ void XAudio2Thread::AddData(const void* src, int size) Emu.Pause(); } } -#endif \ No newline at end of file +#endif diff --git a/rpcs3/Emu/CPU/CPUThread.cpp b/rpcs3/Emu/CPU/CPUThread.cpp index 826a593806..df64539f20 100644 --- a/rpcs3/Emu/CPU/CPUThread.cpp +++ b/rpcs3/Emu/CPU/CPUThread.cpp @@ -1,10 +1,11 @@ #include "stdafx.h" #include "rpcs3/Ini.h" -#include "Emu/SysCalls/SysCalls.h" #include "Utilities/Log.h" #include "Emu/Memory/Memory.h" #include "Emu/System.h" #include "Emu/DbgCommand.h" +#include "Emu/SysCalls/SysCalls.h" +#include "Emu/ARMv7/PSVFuncList.h" #include "CPUDecoder.h" #include "CPUThread.h" @@ -256,6 +257,70 @@ void CPUThread::ExecOnce() void CPUThread::Task() { + auto get_syscall_name = [this](u64 syscall) -> std::string + { + switch (GetType()) + { + case CPU_THREAD_ARMv7: + { + if ((u32)syscall == syscall) + { + if (syscall) + { + if (auto func = get_psv_func_by_nid((u32)syscall)) + { + return func->name; + } + } + else + { + return{}; + } + } + + return "unknown function"; + } + + case CPU_THREAD_PPU: + { + if ((u32)syscall == syscall) + { + if (syscall) + { + if (syscall < 1024) + { + // TODO: + //return SysCalls::GetSyscallName((u32)syscall); + return "unknown syscall"; + } + else + { + return SysCalls::GetHLEFuncName((u32)syscall); + } + } + else + { + return{}; + } + } + + return "unknown function"; + } + + case CPU_THREAD_SPU: + case CPU_THREAD_RAW_SPU: + default: + { + if (!syscall) + { + return{}; + } + + return "unknown function"; + } + } + }; + if (Ini.HLELogging.GetValue()) LOG_NOTICE(GENERAL, "%s enter", CPUThread::GetFName().c_str()); const std::vector& bp = Emu.GetBreakPoints(); @@ -310,13 +375,13 @@ void CPUThread::Task() } catch (const std::string& e) { - LOG_ERROR(GENERAL, "Exception: %s (is_alive=%d, m_last_syscall=0x%llx (%s))", e, IsAlive(), m_last_syscall, SysCalls::GetHLEFuncName((u32)m_last_syscall)); + LOG_ERROR(GENERAL, "Exception: %s (is_alive=%d, m_last_syscall=0x%llx (%s))", e, IsAlive(), m_last_syscall, get_syscall_name(m_last_syscall)); LOG_NOTICE(GENERAL, RegsToString()); Emu.Pause(); } catch (const char* e) { - LOG_ERROR(GENERAL, "Exception: %s (is_alive=%d, m_last_syscall=0x%llx (%s))", e, IsAlive(), m_last_syscall, SysCalls::GetHLEFuncName((u32)m_last_syscall)); + LOG_ERROR(GENERAL, "Exception: %s (is_alive=%d, m_last_syscall=0x%llx (%s))", e, IsAlive(), m_last_syscall, get_syscall_name(m_last_syscall)); LOG_NOTICE(GENERAL, RegsToString()); Emu.Pause(); } diff --git a/rpcs3/Emu/Cell/PPUThread.h b/rpcs3/Emu/Cell/PPUThread.h index 1ce02422cd..98dc31264a 100644 --- a/rpcs3/Emu/Cell/PPUThread.h +++ b/rpcs3/Emu/Cell/PPUThread.h @@ -841,9 +841,9 @@ public: template struct cast_ppu_gpr { - static_assert(sizeof(T) <= 8, "Type for cast_ppu_gpr is invalid (too big)"); + static_assert(sizeof(T) <= 8, "Invalid type for cast_ppu_gpr"); - static u64 func(const T& value) + __forceinline static u64 to_gpr(const T& value) { u64 result = 0; (T&)result = value; @@ -854,7 +854,7 @@ struct cast_ppu_gpr template struct cast_ppu_gpr { - static u64 func(const T& value) + __forceinline static u64 to_gpr(const T& value) { return (u8&)value; } @@ -863,7 +863,7 @@ struct cast_ppu_gpr template struct cast_ppu_gpr { - static u64 func(const T& value) + __forceinline static u64 to_gpr(const T& value) { return (u16&)value; } @@ -872,7 +872,7 @@ struct cast_ppu_gpr template struct cast_ppu_gpr { - static u64 func(const T& value) + __forceinline static u64 to_gpr(const T& value) { return (u32&)value; } @@ -881,7 +881,7 @@ struct cast_ppu_gpr template struct cast_ppu_gpr { - static u64 func(const T& value) + __forceinline static u64 to_gpr(const T& value) { return (u64&)value; } @@ -890,7 +890,7 @@ struct cast_ppu_gpr template<> struct cast_ppu_gpr { - static u64 func(const s8& value) + __forceinline static u64 to_gpr(const s8& value) { return value; } @@ -899,7 +899,7 @@ struct cast_ppu_gpr template<> struct cast_ppu_gpr { - static u64 func(const s16& value) + __forceinline static u64 to_gpr(const s16& value) { return value; } @@ -908,7 +908,7 @@ struct cast_ppu_gpr template<> struct cast_ppu_gpr { - static u64 func(const s32& value) + __forceinline static u64 to_gpr(const s32& value) { return value; } @@ -917,8 +917,14 @@ struct cast_ppu_gpr template<> struct cast_ppu_gpr { - static u64 func(const s64& value) + __forceinline static u64 to_gpr(const s64& value) { return value; } }; + +template +__forceinline static u64 cast_to_ppu_gpr(const T& value) +{ + return cast_ppu_gpr::to_gpr(value); +} diff --git a/rpcs3/Emu/RSX/RSXThread.cpp b/rpcs3/Emu/RSX/RSXThread.cpp index 28333b1e59..8043de02e9 100644 --- a/rpcs3/Emu/RSX/RSXThread.cpp +++ b/rpcs3/Emu/RSX/RSXThread.cpp @@ -2348,22 +2348,15 @@ void RSXThread::Task() OnInitThread(); m_last_flip_time = get_system_time() - 1000000; - volatile bool is_vblank_stopped = false; - thread vblank("VBlank thread", [&]() + thread_t vblank("VBlank thread", true /* autojoin */, [this]() { const u64 start_time = get_system_time(); m_vblank_count = 0; - while (!TestDestroy()) + while (!TestDestroy() && !Emu.IsStopped()) { - if (Emu.IsStopped()) - { - LOG_WARNING(RSX, "VBlank thread aborted"); - return; - } - if (get_system_time() - start_time > m_vblank_count * 1000000 / 60) { m_vblank_count++; @@ -2380,17 +2373,14 @@ void RSXThread::Task() std::this_thread::sleep_for (std::chrono::milliseconds(1)); // hack } - - is_vblank_stopped = true; }); - vblank.detach(); while (!TestDestroy()) try { if (Emu.IsStopped()) { LOG_WARNING(RSX, "RSX thread aborted"); - return; + break; } std::lock_guard lock(m_cs_main); @@ -2409,7 +2399,7 @@ void RSXThread::Task() m_sem_flush.post_and_wait(); } - std::this_thread::sleep_for (std::chrono::milliseconds(1)); // hack + std::this_thread::sleep_for(std::chrono::milliseconds(1)); // hack continue; } @@ -2476,24 +2466,17 @@ void RSXThread::Task() value += (count + 1) * 4; }); } - catch (const std::string& e) { LOG_ERROR(RSX, "Exception: %s", e.c_str()); Emu.Pause(); } - catch (const char* e) { LOG_ERROR(RSX, "Exception: %s", e); Emu.Pause(); } - while (!is_vblank_stopped) - { - std::this_thread::sleep_for (std::chrono::milliseconds(1)); // hack - } - LOG_NOTICE(RSX, "RSX thread ended"); OnExitThread(); diff --git a/rpcs3/Emu/SysCalls/CB_FUNC.h b/rpcs3/Emu/SysCalls/CB_FUNC.h index 129fd5c109..ac49cb90b5 100644 --- a/rpcs3/Emu/SysCalls/CB_FUNC.h +++ b/rpcs3/Emu/SysCalls/CB_FUNC.h @@ -26,7 +26,7 @@ namespace cb_detail __forceinline static void set_value(PPUThread& CPU, const T& arg) { - CPU.GPR[g_count + 2] = cast_ppu_gpr::func(arg); + CPU.GPR[g_count + 2] = cast_to_ppu_gpr(arg); } }; @@ -63,7 +63,7 @@ namespace cb_detail { const int stack_pos = 0x70 + (g_count - 9) * 8 - FIXED_STACK_FRAME_SIZE; static_assert(stack_pos < 0, "TODO: Increase fixed stack frame size (arg count limit broken)"); - vm::write64(CPU.GPR[1] + stack_pos, cast_ppu_gpr::func(arg)); + vm::write64(CPU.GPR[1] + stack_pos, cast_to_ppu_gpr(arg)); } }; diff --git a/rpcs3/Emu/SysCalls/Callback.cpp b/rpcs3/Emu/SysCalls/Callback.cpp index e01b341fbe..b0d10c5316 100644 --- a/rpcs3/Emu/SysCalls/Callback.cpp +++ b/rpcs3/Emu/SysCalls/Callback.cpp @@ -82,7 +82,7 @@ void CallbackManager::Init() static_cast(m_cb_thread)->DoRun(); } - thread cb_async_thread("CallbackManager::Async() thread", [this]() + thread_t cb_async_thread("CallbackManager thread", [this]() { SetCurrentNamedThread(m_cb_thread); @@ -108,8 +108,6 @@ void CallbackManager::Init() m_cb_thread->WaitForAnySignal(); } }); - - cb_async_thread.detach(); } void CallbackManager::Clear() @@ -119,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/cellAdec.cpp b/rpcs3/Emu/SysCalls/Modules/cellAdec.cpp index fe7c74d1a7..52f8e06a64 100644 --- a/rpcs3/Emu/SysCalls/Modules/cellAdec.cpp +++ b/rpcs3/Emu/SysCalls/Modules/cellAdec.cpp @@ -224,7 +224,7 @@ u32 adecOpen(AudioDecoder* adec_ptr) adec.id = adec_id; adec.adecCb = (PPUThread*)&Emu.GetCPU().AddThread(CPU_THREAD_PPU); - adec.adecCb->SetName("Audio Decoder[" + std::to_string(adec_id) + "] Callback"); + adec.adecCb->SetName(fmt::format("AudioDecoder[%d] Callback", adec_id)); adec.adecCb->SetEntry(0); adec.adecCb->SetPrio(1001); adec.adecCb->SetStackSize(0x10000); @@ -232,11 +232,9 @@ u32 adecOpen(AudioDecoder* adec_ptr) adec.adecCb->InitRegs(); adec.adecCb->DoRun(); - thread t("Audio Decoder[" + std::to_string(adec_id) + "] Thread", [adec_ptr, sptr]() + thread_t t(fmt::format("AudioDecoder[%d] Thread", adec_id), [adec_ptr, sptr]() { AudioDecoder& adec = *adec_ptr; - cellAdec->Notice("Audio Decoder thread started"); - AdecTask& task = adec.task; while (true) @@ -471,18 +469,14 @@ u32 adecOpen(AudioDecoder* adec_ptr) default: { - ADEC_ERROR("Audio Decoder thread error: unknown task(%d)", task.type); + ADEC_ERROR("AudioDecoder thread error: unknown task(%d)", task.type); } } } adec.is_finished = true; - if (adec.is_closed) cellAdec->Notice("Audio Decoder thread ended"); - if (Emu.IsStopped()) cellAdec->Warning("Audio Decoder thread aborted"); }); - t.detach(); - return adec_id; } diff --git a/rpcs3/Emu/SysCalls/Modules/cellAudio.cpp b/rpcs3/Emu/SysCalls/Modules/cellAudio.cpp index 6ebbce0d14..7f84d1ec2f 100644 --- a/rpcs3/Emu/SysCalls/Modules/cellAudio.cpp +++ b/rpcs3/Emu/SysCalls/Modules/cellAudio.cpp @@ -2,6 +2,7 @@ #include "Emu/Memory/Memory.h" #include "Emu/System.h" #include "Emu/SysCalls/Modules.h" +#include "Emu/SysCalls/Callback.h" #include "Emu/Memory/atomic_type.h" #include "rpcs3/Ini.h" @@ -11,743 +12,752 @@ #include "Emu/Event.h" #include "Emu/Audio/AudioManager.h" #include "Emu/Audio/AudioDumper.h" -#include "Emu/Audio/cellAudio.h" + +#include "cellAudio.h" Module *cellAudio = nullptr; -static std::mutex audioMutex; +AudioConfig g_audio; -AudioConfig m_config; - -static const bool g_is_u16 = Ini.AudioConvertToU16.GetValue(); - -// libaudio Functions - -#define BUFFER_NUM 32 -#define BUFFER_SIZE 256 -int cellAudioInit() +s32 cellAudioInit() { cellAudio->Warning("cellAudioInit()"); - if (m_config.m_is_audio_initialized) + if (!g_audio.state.compare_and_swap_test(AUDIO_STATE_NOT_INITIALIZED, AUDIO_STATE_INITIALIZED)) { return CELL_AUDIO_ERROR_ALREADY_INIT; } - m_config.m_is_audio_initialized = true; - m_config.start_time = 0; - m_config.counter = 0; + // clear ports + for (auto& port : g_audio.ports) + { + port.state.write_relaxed(AUDIO_PORT_STATE_CLOSED); + } - // alloc memory - m_config.m_buffer = (u32)Memory.Alloc(128 * 1024 * m_config.AUDIO_PORT_COUNT, 1024); - memset(vm::get_ptr(m_config.m_buffer), 0, 128 * 1024 * m_config.AUDIO_PORT_COUNT); - m_config.m_indexes = (u32)Memory.Alloc(sizeof(u64) * m_config.AUDIO_PORT_COUNT, 16); - memset(vm::get_ptr(m_config.m_indexes), 0, sizeof(u64) * m_config.AUDIO_PORT_COUNT); + // reset variables + g_audio.start_time = 0; + g_audio.counter = 0; + g_audio.keys.clear(); + g_audio.start_time = get_system_time(); - thread t("Audio Thread", []() + // alloc memory (only once until the emulator is stopped) + 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, 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(); + + AudioDumper m_dump; + if (do_dump && !m_dump.Init(2)) // Init AudioDumper for 2 channels { - AudioDumper m_dump(8); // WAV file header (8 ch) + throw "AudioDumper::Init() failed"; + } - bool do_dump = Ini.AudioDumpToFile.GetValue(); - - if (do_dump && !m_dump.Init()) - { - cellAudio->Error("cellAudioInit(): AudioDumper::Init() failed"); - return; - } + float buf2ch[2 * BUFFER_SIZE]; // intermediate buffer for 2 channels + float buf8ch[8 * BUFFER_SIZE]; // intermediate buffer for 8 channels - cellAudio->Notice("Audio thread started"); + static const size_t out_buffer_size = 8 * BUFFER_SIZE; // output buffer for 8 channels - if (Ini.AudioDumpToFile.GetValue()) - m_dump.WriteHeader(); + std::unique_ptr out_buffer[BUFFER_NUM]; - float buf2ch[2 * BUFFER_SIZE]; // intermediate buffer for 2 channels - float buf8ch[8 * BUFFER_SIZE]; // intermediate buffer for 8 channels + for (u32 i = 0; i < BUFFER_NUM; i++) + { + out_buffer[i].reset(new float[out_buffer_size] {}); + } - uint oal_buffer_offset = 0; - const uint oal_buffer_size = 2 * BUFFER_SIZE; + squeue_t out_queue; - std::unique_ptr oal_buffer[BUFFER_NUM]; - std::unique_ptr oal_buffer_float[BUFFER_NUM]; + std::vector keys; - for (u32 i = 0; i < BUFFER_NUM; i++) - { - oal_buffer[i] = std::unique_ptr(new s16[oal_buffer_size] {} ); - oal_buffer_float[i] = std::unique_ptr(new float[oal_buffer_size] {} ); - } - - squeue_t queue; - squeue_t queue_float; - - std::vector keys; + thread_t iat("Internal Audio Thread", true /* autojoin */, [&out_queue]() + { + const bool use_u16 = Ini.AudioConvertToU16.GetValue(); Emu.GetAudioManager().GetAudioOut().Init(); - // Note: What if the ini value changes? - if (g_is_u16) - Emu.GetAudioManager().GetAudioOut().Open(oal_buffer[0].get(), oal_buffer_size * sizeof(s16)); - else - Emu.GetAudioManager().GetAudioOut().Open(oal_buffer_float[0].get(), oal_buffer_size * sizeof(float)); - + bool opened = false; + float* buffer; - m_config.start_time = get_system_time(); - - volatile bool internal_finished = false; - - thread iat("Internal Audio Thread", [oal_buffer_size, &queue, &queue_float, &internal_finished]() + while (out_queue.pop(buffer, [](){ return g_audio.state.read_relaxed() != AUDIO_STATE_INITIALIZED; })) { - while (true) - { - s16* oal_buffer = nullptr; - float* oal_buffer_float = nullptr; - - if (g_is_u16) - queue.pop(oal_buffer); - else - queue_float.pop(oal_buffer_float); - - if (g_is_u16) - { - if (oal_buffer) - { - Emu.GetAudioManager().GetAudioOut().AddData(oal_buffer, oal_buffer_size * sizeof(s16)); - continue; - } - } - else - { - if (oal_buffer_float) - { - Emu.GetAudioManager().GetAudioOut().AddData(oal_buffer_float, oal_buffer_size * sizeof(float)); - continue; - } - } - internal_finished = true; - return; - } - }); - iat.detach(); - - while (m_config.m_is_audio_initialized) - { - if (Emu.IsStopped()) - { - cellAudio->Warning("Audio thread aborted"); - goto abort; - } - - const u64 stamp0 = get_system_time(); - - // TODO: send beforemix event (in ~2,6 ms before mixing) - - // precise time of sleeping: 5,(3) ms (or 256/48000 sec) - if (m_config.counter * 256000000 / 48000 >= stamp0 - m_config.start_time) - { - std::this_thread::sleep_for(std::chrono::milliseconds(1)); - continue; - } - - m_config.counter++; - - const u32 oal_pos = m_config.counter % BUFFER_NUM; - - if (Emu.IsPaused()) - { - continue; - } - - bool first_mix = true; - - // mixing: - for (u32 i = 0; i < m_config.AUDIO_PORT_COUNT; i++) - { - if (!m_config.m_ports[i].m_is_audio_port_started) continue; - - AudioPortConfig& port = m_config.m_ports[i]; - - const u32 block_size = port.channel * 256; - const u32 position = port.tag % port.block; // old value - const u32 buf_addr = m_config.m_buffer + (i * 128 * 1024) + (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; - - if (port.channel == 2) - { - if (first_mix) - { - for (u32 i = 0; i < (sizeof(buf2ch) / sizeof(float)); i += 2) - { - // reverse byte order - const float left = buf[i + 0] * m; - const float right = buf[i + 1] * m; - - buf2ch[i + 0] = left; - buf2ch[i + 1] = right; - - buf8ch[i * 4 + 0] = left; - buf8ch[i * 4 + 1] = right; - buf8ch[i * 4 + 2] = 0.0f; - buf8ch[i * 4 + 3] = 0.0f; - buf8ch[i * 4 + 4] = 0.0f; - buf8ch[i * 4 + 5] = 0.0f; - 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 + 0] * m; - const float right = buf[i + 1] * m; - - buf2ch[i + 0] += left; - buf2ch[i + 1] += right; - - buf8ch[i * 4 + 0] += left; - buf8ch[i * 4 + 1] += right; - } - } - } - 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) - { - const float left = buf[i * 4 + 0] * m; - const float right = buf[i * 4 + 1] * m; - const float center = buf[i * 4 + 2] * m; - const float low_freq = buf[i * 4 + 3] * m; - const float rear_left = buf[i * 4 + 4] * m; - const float rear_right = buf[i * 4 + 5] * m; - const float side_left = buf[i * 4 + 6] * m; - const float side_right = buf[i * 4 + 7] * m; - - const float mid = (center + low_freq) * 0.708f; - buf2ch[i + 0] = (left + rear_left + side_left + mid) * k; - buf2ch[i + 1] = (right + rear_right + side_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] = side_left; - buf8ch[i * 4 + 7] = side_right; - } - first_mix = false; - } - else - { - for (u32 i = 0; i < (sizeof(buf2ch) / sizeof(float)); i += 2) - { - const float left = buf[i * 4 + 0] * m; - const float right = buf[i * 4 + 1] * m; - const float center = buf[i * 4 + 2] * m; - const float low_freq = buf[i * 4 + 3] * m; - const float rear_left = buf[i * 4 + 4] * m; - const float rear_right = buf[i * 4 + 5] * m; - const float side_left = buf[i * 4 + 6] * m; - const float side_right = buf[i * 4 + 7] * m; - - const float mid = (center + low_freq) * 0.708f; - buf2ch[i + 0] += (left + rear_left + side_left + mid) * k; - buf2ch[i + 1] += (right + rear_right + side_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] += side_left; - buf8ch[i * 4 + 7] += side_right; - } - } - } - - memset(buf, 0, block_size * sizeof(float)); - } - - // convert the data from float to u16 with clipping: - if (!first_mix) + 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) - if (g_is_u16) + u16 buf_u16[out_buffer_size]; + for (size_t i = 0; i < out_buffer_size; i += 8) { - for (u32 i = 0; i < (sizeof(buf2ch) / sizeof(float)); i += 8) - { - static const __m128 float2u16 = { 0x8000, 0x8000, 0x8000, 0x8000 }; - (__m128i&)(oal_buffer[oal_pos][oal_buffer_offset + i]) = _mm_packs_epi32( - _mm_cvtps_epi32(_mm_mul_ps((__m128&)(buf2ch[i]), float2u16)), - _mm_cvtps_epi32(_mm_mul_ps((__m128&)(buf2ch[i + 4]), float2u16))); - } + 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))); } - else - for (u32 i = 0; i < (sizeof(buf2ch) / sizeof(float)); i++) + + if (!opened) { - oal_buffer_float[oal_pos][oal_buffer_offset + i] = buf2ch[i]; - } - } - - //const u64 stamp1 = get_system_time(); - - if (first_mix) - { - if (g_is_u16) memset(&oal_buffer[oal_pos][0], 0, oal_buffer_size * sizeof(s16)); - else memset(&oal_buffer_float[oal_pos][0], 0, oal_buffer_size * sizeof(float)); - } - oal_buffer_offset += sizeof(buf2ch) / sizeof(float); - - if(oal_buffer_offset >= oal_buffer_size) - { - if (g_is_u16) - queue.push(&oal_buffer[oal_pos][0]); - - queue_float.push(&oal_buffer_float[oal_pos][0]); - oal_buffer_offset = 0; - } - - //const u64 stamp2 = get_system_time(); - - // send aftermix event (normal audio event) - { - std::lock_guard lock(audioMutex); - // update indexes: - auto indexes = vm::ptr::make(m_config.m_indexes); - for (u32 i = 0; i < m_config.AUDIO_PORT_COUNT; i++) - { - if (!m_config.m_ports[i].m_is_audio_port_started) continue; - - AudioPortConfig& port = m_config.m_ports[i]; - - u32 position = port.tag % port.block; // old value - port.counter = m_config.counter; - port.tag++; // absolute index of block that will be read - indexes[i] = (position + 1) % port.block; // write new value - } - // load keys: - keys.resize(m_config.m_keys.size()); - memcpy(keys.data(), m_config.m_keys.data(), sizeof(u64) * keys.size()); - } - for (u32 i = 0; i < keys.size(); i++) - { - // TODO: check event source - Emu.GetEventManager().SendEvent(keys[i], 0x10103000e010e07, 0, 0, 0); - } - - //const u64 stamp3 = get_system_time(); - - if (do_dump && !first_mix) - { - if (m_dump.GetCh() == 8) - { - if (m_dump.WriteData(&buf8ch, sizeof(buf8ch)) != sizeof(buf8ch)) // write file data - { - cellAudio->Error("cellAudioInit(): AudioDumper::WriteData() failed"); - goto abort; - } - } - else if (m_dump.GetCh() == 2) - { - if (m_dump.WriteData(&buf2ch, sizeof(buf2ch)) != sizeof(buf2ch)) // write file data - { - cellAudio->Error("cellAudioInit(): AudioDumper::WriteData() failed"); - goto abort; - } + Emu.GetAudioManager().GetAudioOut().Open(buf_u16, out_buffer_size * sizeof(u16)); + opened = true; } else { - cellAudio->Error("cellAudioInit(): unknown AudioDumper::GetCh() value (%d)", m_dump.GetCh()); - goto abort; + Emu.GetAudioManager().GetAudioOut().AddData(buf_u16, out_buffer_size * sizeof(u16)); + } + } + 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)); } } - - //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); - } - cellAudio->Notice("Audio thread ended"); -abort: - queue.push(nullptr); - queue_float.push(nullptr); - - if(do_dump) - m_dump.Finalize(); - - m_config.m_is_audio_initialized = false; - - m_config.m_keys.clear(); - for (u32 i = 0; i < m_config.AUDIO_PORT_COUNT; i++) - { - AudioPortConfig& port = m_config.m_ports[i]; - port.m_is_audio_port_opened = false; - port.m_is_audio_port_started = false; - } - m_config.m_port_in_use = 0; - - while (!internal_finished) - { - std::this_thread::sleep_for(std::chrono::milliseconds(1)); // hack } - m_config.m_is_audio_finalized = true; + Emu.GetAudioManager().GetAudioOut().Quit(); }); - t.detach(); - while (!m_config.start_time) // waiting for initialization - { - if (Emu.IsStopped()) + u64 last_pause_time; + std::atomic added_time(0); + NamedThreadBase* audio_thread = GetCurrentNamedThread(); + + PauseCallbackRegisterer pcb(Emu.GetCallbackManager(), [&last_pause_time, &added_time, audio_thread](bool is_paused) { - cellAudio->Warning("cellAudioInit() aborted"); - return CELL_OK; + if (is_paused) + { + last_pause_time = get_system_time(); + } + else + { + added_time += get_system_time() - last_pause_time; + audio_thread->Notify(); + } + }); + + while (g_audio.state.read_relaxed() == AUDIO_STATE_INITIALIZED && !Emu.IsStopped()) + { + if (Emu.IsPaused()) + { + GetCurrentNamedThread()->WaitForAnySignal(); + continue; + } + + if (added_time) + { + g_audio.start_time += added_time.exchange(0); + } + + const u64 stamp0 = get_system_time(); + + // TODO: send beforemix event (in ~2,6 ms before mixing) + + // precise time of sleeping: 5,(3) ms (or 256/48000 sec) + const u64 expected_time = g_audio.counter * AUDIO_SAMPLES * MHZ / 48000; + if (expected_time >= stamp0 - g_audio.start_time) + { + std::this_thread::sleep_for(std::chrono::milliseconds(1)); + continue; + } + + // crutch to hide giant lags caused by debugger + const u64 missed_time = stamp0 - g_audio.start_time - expected_time; + if (missed_time > AUDIO_SAMPLES * MHZ / 48000) + { + cellAudio->Notice("%f ms adjusted", (float)missed_time / 1000); + g_audio.start_time += missed_time; + } + + g_audio.counter++; + + const u32 out_pos = g_audio.counter % BUFFER_NUM; + + //if (Emu.IsPaused()) + //{ + // continue; + //} + + bool first_mix = true; + + // mixing: + for (auto& port : g_audio.ports) + { + if (port.state.read_relaxed() != AUDIO_PORT_STATE_STARTED) continue; + + 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; + + 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) + { + if (first_mix) + { + 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; + + buf2ch[i + 0] = left; + buf2ch[i + 1] = right; + + buf8ch[i * 4 + 0] = left; + buf8ch[i * 4 + 1] = right; + buf8ch[i * 4 + 2] = 0.0f; + buf8ch[i * 4 + 3] = 0.0f; + buf8ch[i * 4 + 4] = 0.0f; + buf8ch[i * 4 + 5] = 0.0f; + 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) + { + step_volume(port); + + const float left = buf[i + 0] * m; + const float right = buf[i + 1] * m; + + buf2ch[i + 0] += left; + buf2ch[i + 1] += right; + + buf8ch[i * 4 + 0] += left; + buf8ch[i * 4 + 1] += 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; + const float low_freq = buf[i * 4 + 3] * m; + const float rear_left = buf[i * 4 + 4] * m; + const float rear_right = buf[i * 4 + 5] * m; + const float side_left = buf[i * 4 + 6] * m; + const float side_right = buf[i * 4 + 7] * m; + + const float mid = (center + low_freq) * 0.708f; + buf2ch[i + 0] = (left + rear_left + side_left + mid) * k; + buf2ch[i + 1] = (right + rear_right + side_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] = side_left; + buf8ch[i * 4 + 7] = side_right; + } + first_mix = false; + } + else + { + 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; + const float low_freq = buf[i * 4 + 3] * m; + const float rear_left = buf[i * 4 + 4] * m; + const float rear_right = buf[i * 4 + 5] * m; + const float side_left = buf[i * 4 + 6] * m; + const float side_right = buf[i * 4 + 7] * m; + + const float mid = (center + low_freq) * 0.708f; + buf2ch[i + 0] += (left + rear_left + side_left + mid) * k; + buf2ch[i + 1] += (right + rear_right + side_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] += side_left; + buf8ch[i * 4 + 7] += side_right; + } + } + } + else + { + throw fmt::format("Unknown channel count (port=%d, channel=%d)", &port - g_audio.ports, port.channel); + } + + memset(buf, 0, block_size * sizeof(float)); + } + + + if (!first_mix) + { + // copy output data (2 ch) + //for (u32 i = 0; i < (sizeof(buf2ch) / sizeof(float)); i++) + //{ + // out_buffer[out_pos][i] = buf2ch[i]; + //} + + // copy output data (8 ch) + for (u32 i = 0; i < (sizeof(buf8ch) / sizeof(float)); i++) + { + out_buffer[out_pos][i] = buf8ch[i]; + } + } + + //const u64 stamp1 = get_system_time(); + + if (first_mix) + { + memset(out_buffer[out_pos].get(), 0, out_buffer_size * sizeof(float)); + } + + if (!out_queue.push(out_buffer[out_pos].get(), [](){ return g_audio.state.read_relaxed() != AUDIO_STATE_INITIALIZED; })) + { + break; + } + + //const u64 stamp2 = get_system_time(); + + // send aftermix event (normal audio event) + { + std::lock_guard lock(g_audio.mutex); + // update indexes: + auto indexes = vm::ptr::make(g_audio.indexes); + for (u32 i = 0; i < AUDIO_PORT_COUNT; i++) + { + AudioPortConfig& port = g_audio.ports[i]; + + if (port.state.read_relaxed() != AUDIO_PORT_STATE_STARTED) continue; + + u32 position = port.tag % port.block; // old value + port.counter = g_audio.counter; + port.tag++; // absolute index of block that will be read + indexes[i] = (position + 1) % port.block; // write new value + } + // load keys: + keys.resize(g_audio.keys.size()); + memcpy(keys.data(), g_audio.keys.data(), sizeof(u64) * keys.size()); + } + for (u32 i = 0; i < keys.size(); i++) + { + // TODO: check event source + Emu.GetEventManager().SendEvent(keys[i], 0x10103000e010e07, 0, 0, 0); + } + + //const u64 stamp3 = get_system_time(); + + if (do_dump && !first_mix) + { + if (m_dump.GetCh() == 8) + { + if (m_dump.WriteData(&buf8ch, sizeof(buf8ch)) != sizeof(buf8ch)) // write file data (8 ch) + { + throw "AudioDumper::WriteData() failed (8 ch)"; + } + } + else if (m_dump.GetCh() == 2) + { + if (m_dump.WriteData(&buf2ch, sizeof(buf2ch)) != sizeof(buf2ch)) // write file data (2 ch) + { + throw "AudioDumper::WriteData() failed (2 ch)"; + } + } + else + { + 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); } - std::this_thread::sleep_for(std::chrono::milliseconds(1)); // hack - } + }); return CELL_OK; } -int cellAudioQuit() +s32 cellAudioQuit() { cellAudio->Warning("cellAudioQuit()"); - if (!m_config.m_is_audio_initialized) + if (!g_audio.state.compare_and_swap_test(AUDIO_STATE_INITIALIZED, AUDIO_STATE_FINALIZED)) { return CELL_AUDIO_ERROR_NOT_INIT; } - m_config.m_is_audio_initialized = false; - - while (!m_config.m_is_audio_finalized) - { - std::this_thread::sleep_for(std::chrono::milliseconds(1)); // hack - if (Emu.IsStopped()) - { - cellAudio->Warning("cellAudioQuit(): aborted"); - return CELL_OK; - } - } - - Memory.Free(m_config.m_buffer); - Memory.Free(m_config.m_indexes); - + g_audio.audio_thread.join(); + g_audio.state.exchange(AUDIO_STATE_NOT_INITIALIZED); 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; } - if (m_config.m_port_in_use >= m_config.AUDIO_PORT_COUNT) + 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) + { + return CELL_AUDIO_ERROR_PARAM; + } + + 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; } - for (u32 i = 0; i < m_config.AUDIO_PORT_COUNT; i++) + 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) { - if (!m_config.m_ports[i].m_is_audio_port_opened) - { - AudioPortConfig& port = m_config.m_ports[i]; - - port.channel = (u8)audioParam->nChannel; - port.block = (u8)audioParam->nBlock; - port.attr = audioParam->attr; - port.addr = m_config.m_buffer + (128 * 1024 * i); - port.read_index_addr = m_config.m_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.m_is_audio_port_opened = true; - port.m_is_audio_port_started = false; - port.tag = 0; - - m_config.m_port_in_use++; - return CELL_OK; - } + port.level = audioParam->level; + } + else + { + port.level = 1.0f; } - return CELL_AUDIO_ERROR_PORT_FULL; + 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 >= m_config.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; } - if (!m_config.m_ports[portNum].m_is_audio_port_opened) - { - portConfig->status = CELL_AUDIO_STATUS_CLOSE; - } - else if (m_config.m_ports[portNum].m_is_audio_port_started) - { - portConfig->status = CELL_AUDIO_STATUS_RUN; - } - else - { - portConfig->status = CELL_AUDIO_STATUS_READY; - } + AudioPortConfig& port = g_audio.ports[portNum]; - AudioPortConfig& port = m_config.m_ports[portNum]; + portConfig->readIndexAddr = port.read_index_addr; + + switch (auto state = port.state.read_sync()) + { + 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); + } 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 (portNum >= m_config.AUDIO_PORT_COUNT) + 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 (!m_config.m_ports[portNum].m_is_audio_port_opened) + switch (auto state = g_audio.ports[portNum].state.compare_and_swap(AUDIO_PORT_STATE_OPENED, AUDIO_PORT_STATE_STARTED)) { - return CELL_AUDIO_ERROR_PORT_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); } - - if (m_config.m_ports[portNum].m_is_audio_port_started) - { - return CELL_AUDIO_ERROR_PORT_ALREADY_RUN; - } - - m_config.m_ports[portNum].m_is_audio_port_started = true; - - return CELL_OK; } -int cellAudioPortClose(u32 portNum) +s32 cellAudioPortClose(u32 portNum) { cellAudio->Warning("cellAudioPortClose(portNum=0x%x)", portNum); - if (portNum >= m_config.AUDIO_PORT_COUNT) + 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 (!m_config.m_ports[portNum].m_is_audio_port_opened) + switch (auto state = g_audio.ports[portNum].state.exchange(AUDIO_PORT_STATE_CLOSED)) { - 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); } - - m_config.m_ports[portNum].m_is_audio_port_started = false; - m_config.m_ports[portNum].m_is_audio_port_opened = false; - m_config.m_port_in_use--; - return CELL_OK; } -int cellAudioPortStop(u32 portNum) +s32 cellAudioPortStop(u32 portNum) { - cellAudio->Warning("cellAudioPortStop(portNum=0x%x)",portNum); - - if (portNum >= m_config.AUDIO_PORT_COUNT) + 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; } - if (!m_config.m_ports[portNum].m_is_audio_port_opened) + switch (auto state = g_audio.ports[portNum].state.compare_and_swap(AUDIO_PORT_STATE_STARTED, AUDIO_PORT_STATE_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_NOT_RUN; + default: throw fmt::format("cellAudioPortStop(%d): invalid port state (0x%x)", portNum, state); } - - if (!m_config.m_ports[portNum].m_is_audio_port_started) - { - return CELL_AUDIO_ERROR_PORT_NOT_RUN; - } - - m_config.m_ports[portNum].m_is_audio_port_started = false; - return CELL_OK; } -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 (portNum >= m_config.AUDIO_PORT_COUNT) + 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 (!m_config.m_ports[portNum].m_is_audio_port_opened) + AudioPortConfig& port = g_audio.ports[portNum]; + + if (port.state.read_relaxed() == AUDIO_PORT_STATE_CLOSED) { return CELL_AUDIO_ERROR_PORT_NOT_OPEN; } - if (!m_config.m_ports[portNum].m_is_audio_port_started) - { - return CELL_AUDIO_ERROR_PORT_NOT_RUN; - } + // TODO: check tag (CELL_AUDIO_ERROR_TAG_NOT_FOUND error) - AudioPortConfig& port = m_config.m_ports[portNum]; + std::lock_guard lock(g_audio.mutex); - std::lock_guard lock(audioMutex); - - *stamp = m_config.start_time + (port.counter + (tag - port.tag)) * 256000000 / 48000; + *stamp = g_audio.start_time + (port.counter + (tag - port.tag)) * 256000000 / 48000; 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 (portNum >= m_config.AUDIO_PORT_COUNT) + 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 (!m_config.m_ports[portNum].m_is_audio_port_opened) + AudioPortConfig& port = g_audio.ports[portNum]; + + if (port.state.read_relaxed() == AUDIO_PORT_STATE_CLOSED) { return CELL_AUDIO_ERROR_PORT_NOT_OPEN; } - if (!m_config.m_ports[portNum].m_is_audio_port_started) - { - return CELL_AUDIO_ERROR_PORT_NOT_RUN; - } - - AudioPortConfig& port = m_config.m_ports[portNum]; - if (blockNo >= port.block) { - cellAudio->Error("cellAudioGetPortBlockTag: wrong blockNo(%lld)", blockNo); return CELL_AUDIO_ERROR_PARAM; } - std::lock_guard lock(audioMutex); + std::lock_guard lock(g_audio.mutex); u64 tag_base = port.tag; if (tag_base % port.block > blockNo) { - tag_base &= ~(port.block-1); + tag_base &= ~(port.block - 1); tag_base += port.block; } else { - tag_base &= ~(port.block-1); + tag_base &= ~(port.block - 1); } *tag = tag_base + blockNo; 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); - AudioPortConfig& port = m_config.m_ports[portNum]; + if (g_audio.state.read_relaxed() != AUDIO_STATE_INITIALIZED) + { + return CELL_AUDIO_ERROR_NOT_INIT; + } - if (portNum >= m_config.AUDIO_PORT_COUNT) + if (portNum >= AUDIO_PORT_COUNT) { return CELL_AUDIO_ERROR_PARAM; } - if (!port.m_is_audio_port_opened) + AudioPortConfig& port = g_audio.ports[portNum]; + + if (port.state.read_relaxed() == AUDIO_PORT_STATE_CLOSED) { return CELL_AUDIO_ERROR_PORT_NOT_OPEN; } - if (!port.m_is_audio_port_started) + if (level >= 0.0f) { - return CELL_AUDIO_ERROR_PORT_NOT_RUN; + std::lock_guard lock(g_audio.mutex); + + 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); } - - std::lock_guard lock(audioMutex); - - port.level = level; // TODO return CELL_OK; } @@ -757,7 +767,7 @@ int cellAudioCreateNotifyEventQueue(vm::ptr id, vm::ptr key) { cellAudio->Warning("cellAudioCreateNotifyEventQueue(id_addr=0x%x, key_addr=0x%x)", id.addr(), key.addr()); - std::lock_guard lock(audioMutex); + std::lock_guard lock(g_audio.mutex); u64 event_key = 0; while (Emu.GetEventManager().CheckKey((event_key << 48) | 0x80004d494f323221)) @@ -790,21 +800,21 @@ int cellAudioSetNotifyEventQueue(u64 key) { cellAudio->Warning("cellAudioSetNotifyEventQueue(key=0x%llx)", key); - std::lock_guard lock(audioMutex); + std::lock_guard lock(g_audio.mutex); - for (u32 i = 0; i < m_config.m_keys.size(); i++) // check for duplicates + for (u32 i = 0; i < g_audio.keys.size(); i++) // check for duplicates { - if (m_config.m_keys[i] == key) + if (g_audio.keys[i] == key) { return CELL_AUDIO_ERROR_PARAM; } } - m_config.m_keys.push_back(key); + g_audio.keys.push_back(key); /*EventQueue* eq; if (!Emu.GetEventManager().GetEventQueue(key, eq)) { - return CELL_AUDIO_ERROR_PARAM; + return CELL_AUDIO_ERROR_PARAM; }*/ // TODO: connect port (?????) @@ -822,14 +832,14 @@ int cellAudioRemoveNotifyEventQueue(u64 key) { cellAudio->Warning("cellAudioRemoveNotifyEventQueue(key=0x%llx)", key); - std::lock_guard lock(audioMutex); + std::lock_guard lock(g_audio.mutex); bool found = false; - for (u32 i = 0; i < m_config.m_keys.size(); i++) + for (u32 i = 0; i < g_audio.keys.size(); i++) { - if (m_config.m_keys[i] == key) + if (g_audio.keys[i] == key) { - m_config.m_keys.erase(m_config.m_keys.begin() + i); + g_audio.keys.erase(g_audio.keys.begin() + i); found = true; break; } @@ -844,7 +854,7 @@ int cellAudioRemoveNotifyEventQueue(u64 key) /*EventQueue* eq; if (!Emu.GetEventManager().GetEventQueue(key, eq)) { - return CELL_AUDIO_ERROR_PARAM; + return CELL_AUDIO_ERROR_PARAM; }*/ // TODO: disconnect port @@ -860,14 +870,14 @@ int cellAudioRemoveNotifyEventQueueEx(u64 key, u32 iFlags) s32 cellAudioAddData(u32 portNum, vm::ptr src, u32 samples, float volume) { - cellAudio->Log("cellAudioAddData(portNum=%d, src_addr=0x%x, samples=%d, volume=%f)", portNum, src.addr(), samples, volume); - - if (!m_config.m_is_audio_initialized) + cellAudio->Log("cellAudioAddData(portNum=%d, src=0x%x, samples=%d, volume=%f)", portNum, src, samples, volume); + + if (g_audio.state.read_relaxed() != AUDIO_STATE_INITIALIZED) { return CELL_AUDIO_ERROR_NOT_INIT; } - if (portNum >= m_config.AUDIO_PORT_COUNT || !src || src.addr() % 4) + if (portNum >= AUDIO_PORT_COUNT || !src || src.addr() % 4) { return CELL_AUDIO_ERROR_PARAM; } @@ -879,8 +889,8 @@ s32 cellAudioAddData(u32 portNum, vm::ptr src, u32 samples, float volume) return CELL_AUDIO_ERROR_PARAM; } - const AudioPortConfig& port = m_config.m_ports[portNum]; - + const AudioPortConfig& port = g_audio.ports[portNum]; + const auto dst = vm::ptr::make(port.addr + (port.tag % port.block) * port.channel * 256 * sizeof(float)); for (u32 i = 0; i < samples * port.channel; i++) @@ -893,19 +903,14 @@ s32 cellAudioAddData(u32 portNum, vm::ptr src, u32 samples, float volume) s32 cellAudioAdd2chData(u32 portNum, vm::ptr src, u32 samples, float volume) { - cellAudio->Log("cellAudioAdd2chData(portNum=%d, src_addr=0x%x, samples=%d, volume=%f)", portNum, src.addr(), samples, volume); + cellAudio->Log("cellAudioAdd2chData(portNum=%d, src=0x%x, samples=%d, volume=%f)", portNum, src, samples, volume); - if (!m_config.m_is_audio_initialized) + if (g_audio.state.read_relaxed() != AUDIO_STATE_INITIALIZED) { return CELL_AUDIO_ERROR_NOT_INIT; } - if (portNum >= m_config.AUDIO_PORT_COUNT) - { - return CELL_AUDIO_ERROR_PARAM; - } - - if (portNum >= m_config.AUDIO_PORT_COUNT || !src || src.addr() % 4) + if (portNum >= AUDIO_PORT_COUNT || !src || src.addr() % 4) { return CELL_AUDIO_ERROR_PARAM; } @@ -917,7 +922,7 @@ s32 cellAudioAdd2chData(u32 portNum, vm::ptr src, u32 samples, float volu return CELL_AUDIO_ERROR_PARAM; } - const AudioPortConfig& port = m_config.m_ports[portNum]; + const AudioPortConfig& port = g_audio.ports[portNum]; const auto dst = vm::ptr::make(port.addr + (port.tag % port.block) * port.channel * 256 * sizeof(float)); @@ -961,24 +966,19 @@ s32 cellAudioAdd2chData(u32 portNum, vm::ptr src, u32 samples, float volu s32 cellAudioAdd6chData(u32 portNum, vm::ptr src, float volume) { - cellAudio->Log("cellAudioAdd6chData(portNum=%d, src_addr=0x%x, volume=%f)", portNum, src.addr(), volume); + cellAudio->Log("cellAudioAdd6chData(portNum=%d, src=0x%x, volume=%f)", portNum, src, volume); - if (!m_config.m_is_audio_initialized) + if (g_audio.state.read_relaxed() != AUDIO_STATE_INITIALIZED) { return CELL_AUDIO_ERROR_NOT_INIT; } - if (portNum >= m_config.AUDIO_PORT_COUNT) + if (portNum >= AUDIO_PORT_COUNT || !src || src.addr() % 4) { return CELL_AUDIO_ERROR_PARAM; } - if (portNum >= m_config.AUDIO_PORT_COUNT || !src || src.addr() % 4) - { - return CELL_AUDIO_ERROR_PARAM; - } - - const AudioPortConfig& port = m_config.m_ports[portNum]; + const AudioPortConfig& port = g_audio.ports[portNum]; const auto dst = vm::ptr::make(port.addr + (port.tag % port.block) * port.channel * 256 * sizeof(float)); @@ -1004,7 +1004,7 @@ s32 cellAudioAdd6chData(u32 portNum, vm::ptr src, float volume) { cellAudio->Error("cellAudioAdd6chData(portNum=%d): invalid port.channel value (%d)", portNum, port.channel); } - + return CELL_OK; } @@ -1036,6 +1036,10 @@ void cellAudio_init(Module *pxThis) { cellAudio = pxThis; + g_audio.state.write_relaxed(AUDIO_STATE_NOT_INITIALIZED); + g_audio.buffer = 0; + g_audio.indexes = 0; + REG_FUNC(cellAudio, cellAudioInit); REG_FUNC(cellAudio, cellAudioPortClose); REG_FUNC(cellAudio, cellAudioPortStop); @@ -1060,3 +1064,8 @@ void cellAudio_init(Module *pxThis) REG_FUNC(cellAudio, cellAudioSetPersonalDevice); REG_FUNC(cellAudio, cellAudioUnsetPersonalDevice); } + +void cellAudio_load() +{ + // never called :( +} diff --git a/rpcs3/Emu/Audio/cellAudio.h b/rpcs3/Emu/SysCalls/Modules/cellAudio.h similarity index 74% rename from rpcs3/Emu/Audio/cellAudio.h rename to rpcs3/Emu/SysCalls/Modules/cellAudio.h index b6cabcea59..ca165476c1 100644 --- a/rpcs3/Emu/Audio/cellAudio.h +++ b/rpcs3/Emu/SysCalls/Modules/cellAudio.h @@ -72,51 +72,76 @@ struct CellAudioPortConfig be_t portAddr; }; +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 +{ + AUDIO_STATE_NOT_INITIALIZED, + AUDIO_STATE_INITIALIZED, + AUDIO_STATE_FINALIZED, +}; + +enum AudioPortState : u32 +{ + AUDIO_PORT_STATE_CLOSED, + AUDIO_PORT_STATE_OPENED, + AUDIO_PORT_STATE_STARTED, +}; + struct AudioPortConfig { - bool m_is_audio_port_opened; - bool m_is_audio_port_started; - u8 channel; - u8 block; - float level; + std::mutex mutex; + atomic_le_t state; + + 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 { - enum - { - AUDIO_PORT_COUNT = 8, - }; - AudioPortConfig m_ports[AUDIO_PORT_COUNT]; - u32 m_buffer; // 1 MB memory for audio ports - u32 m_indexes; // current block indexes and other info - bool m_is_audio_initialized; - bool m_is_audio_finalized; - u32 m_port_in_use; + std::mutex mutex; + atomic_le_t state; + thread_t audio_thread; + + AudioPortConfig ports[AUDIO_PORT_COUNT]; + u32 buffer; // 1 MB memory for audio ports + u32 indexes; // current block indexes and other info u64 counter; u64 start_time; - std::vector m_keys; + std::vector keys; - AudioConfig() - : m_is_audio_initialized(false) - , m_is_audio_finalized(false) - , m_port_in_use(0) - , counter(0) + AudioConfig() : audio_thread("Audio Thread") { - memset(&m_ports, 0, sizeof(AudioPortConfig) * AUDIO_PORT_COUNT); } - void Clear() + u32 open_port() { - memset(&m_ports, 0, sizeof(AudioPortConfig) * AUDIO_PORT_COUNT); - m_port_in_use = 0; + 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 m_config; +extern AudioConfig g_audio; diff --git a/rpcs3/Emu/SysCalls/Modules/cellDmux.cpp b/rpcs3/Emu/SysCalls/Modules/cellDmux.cpp index ff4cc079df..a2a80f1719 100644 --- a/rpcs3/Emu/SysCalls/Modules/cellDmux.cpp +++ b/rpcs3/Emu/SysCalls/Modules/cellDmux.cpp @@ -306,7 +306,7 @@ u32 dmuxOpen(Demuxer* dmux_ptr) dmux.id = dmux_id; dmux.dmuxCb = (PPUThread*)&Emu.GetCPU().AddThread(CPU_THREAD_PPU); - dmux.dmuxCb->SetName("Demuxer[" + std::to_string(dmux_id) + "] Callback"); + dmux.dmuxCb->SetName(fmt::format("Demuxer[%d] Callback", dmux_id)); dmux.dmuxCb->SetEntry(0); dmux.dmuxCb->SetPrio(1001); dmux.dmuxCb->SetStackSize(0x10000); @@ -314,10 +314,9 @@ u32 dmuxOpen(Demuxer* dmux_ptr) dmux.dmuxCb->InitRegs(); dmux.dmuxCb->DoRun(); - thread t("Demuxer[" + std::to_string(dmux_id) + "] Thread", [dmux_ptr, sptr]() + thread_t t(fmt::format("Demuxer[%d] Thread", dmux_id), [dmux_ptr, sptr]() { Demuxer& dmux = *dmux_ptr; - cellDmux->Notice("Demuxer thread started (mem=0x%x, size=0x%x, cb_addr=0x%x, arg=0x%x)", dmux.memAddr, dmux.memSize, dmux.cbFunc.addr(), dmux.cbArg); DemuxerTask task; DemuxerStream stream = {}; @@ -760,12 +759,8 @@ u32 dmuxOpen(Demuxer* dmux_ptr) } dmux.is_finished = true; - if (Emu.IsStopped()) cellDmux->Warning("Demuxer thread aborted"); - if (dmux.is_closed) cellDmux->Notice("Demuxer thread ended"); }); - t.detach(); - return dmux_id; } diff --git a/rpcs3/Emu/SysCalls/Modules/cellMsgDialog.cpp b/rpcs3/Emu/SysCalls/Modules/cellMsgDialog.cpp index a652d134f9..64107c9461 100644 --- a/rpcs3/Emu/SysCalls/Modules/cellMsgDialog.cpp +++ b/rpcs3/Emu/SysCalls/Modules/cellMsgDialog.cpp @@ -123,7 +123,7 @@ s32 cellMsgDialogOpen2(u32 type, vm::ptr msgString, vm::ptr msgString, vm::ptrstate = CELL_AUDIO_OUT_DEVICE_STATE_AVAILABLE; info->latency = 1000; info->availableModes[0].type = CELL_AUDIO_IN_CODING_TYPE_LPCM; - info->availableModes[0].channel = CELL_AUDIO_OUT_CHNUM_2; + info->availableModes[0].channel = CELL_AUDIO_OUT_CHNUM_8; info->availableModes[0].fs = CELL_AUDIO_OUT_FS_48KHZ; - info->availableModes[0].layout = CELL_AUDIO_OUT_SPEAKER_LAYOUT_2CH; + info->availableModes[0].layout = CELL_AUDIO_OUT_SPEAKER_LAYOUT_8CH_LREClrxy; return CELL_AUDIO_OUT_SUCCEEDED; } diff --git a/rpcs3/Emu/SysCalls/Modules/cellVdec.cpp b/rpcs3/Emu/SysCalls/Modules/cellVdec.cpp index 7776ebfbc0..e44573d2c7 100644 --- a/rpcs3/Emu/SysCalls/Modules/cellVdec.cpp +++ b/rpcs3/Emu/SysCalls/Modules/cellVdec.cpp @@ -214,7 +214,7 @@ u32 vdecOpen(VideoDecoder* vdec_ptr) vdec.id = vdec_id; vdec.vdecCb = (PPUThread*)&Emu.GetCPU().AddThread(CPU_THREAD_PPU); - vdec.vdecCb->SetName("Video Decoder[" + std::to_string(vdec_id) + "] Callback"); + vdec.vdecCb->SetName(fmt::format("VideoDecoder[%d] Callback", vdec_id)); vdec.vdecCb->SetEntry(0); vdec.vdecCb->SetPrio(1001); vdec.vdecCb->SetStackSize(0x10000); @@ -222,11 +222,9 @@ u32 vdecOpen(VideoDecoder* vdec_ptr) vdec.vdecCb->InitRegs(); vdec.vdecCb->DoRun(); - thread t("Video Decoder[" + std::to_string(vdec_id) + "] Thread", [vdec_ptr, sptr]() + thread_t t(fmt::format("VideoDecoder[%d] Thread", vdec_id), [vdec_ptr, sptr]() { VideoDecoder& vdec = *vdec_ptr; - cellVdec->Notice("Video Decoder thread started"); - VdecTask& task = vdec.task; while (true) @@ -431,7 +429,15 @@ u32 vdecOpen(VideoDecoder* vdec_ptr) { if (vdec.last_pts == -1) { - vdec.last_pts = 0x8000; //av_frame_get_best_effort_timestamp(frame.data); + u64 ts = av_frame_get_best_effort_timestamp(frame.data); + if (ts != AV_NOPTS_VALUE) + { + vdec.last_pts = ts; + } + else + { + vdec.last_pts = 0; + } } else switch (vdec.frc_set) { @@ -539,18 +545,14 @@ u32 vdecOpen(VideoDecoder* vdec_ptr) default: { - VDEC_ERROR("Video Decoder thread error: unknown task(%d)", task.type); + VDEC_ERROR("VideoDecoder thread error: unknown task(%d)", task.type); } } } vdec.is_finished = true; - if (Emu.IsStopped()) cellVdec->Warning("Video Decoder thread aborted"); - if (vdec.is_closed) cellVdec->Notice("Video Decoder thread ended"); }); - t.detach(); - return vdec_id; } diff --git a/rpcs3/Emu/SysCalls/Modules/libmixer.cpp b/rpcs3/Emu/SysCalls/Modules/libmixer.cpp index a6859cf19b..0030923d06 100644 --- a/rpcs3/Emu/SysCalls/Modules/libmixer.cpp +++ b/rpcs3/Emu/SysCalls/Modules/libmixer.cpp @@ -5,14 +5,12 @@ #include "Emu/SysCalls/CB_FUNC.h" #include "Emu/CPU/CPUThreadManager.h" -#include "Emu/Audio/cellAudio.h" +#include "cellAudio.h" #include "libmixer.h" 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,35 +294,37 @@ 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 = m_config.m_ports[SUR_PORT]; - - if (port.m_is_audio_port_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; port.level = 1.0f; + port.tag = 0; libmixer->Warning("*** audio port opened(default)"); - port.m_is_audio_port_opened = true; - port.tag = 0; - m_config.m_port_in_use++; - - libmixer->Warning("*** surMixer created (ch1=%d, ch2=%d, ch6=%d, ch8=%d)", - (u32)surMixer.chStrips1, (u32)surMixer.chStrips2, (u32)surMixer.chStrips6, (u32)surMixer.chStrips8); - mixcount = 0; surMixerCb.set(0); - thread t("Surmixer Thread", []() + 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 = m_config.m_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"); @@ -335,21 +335,15 @@ int cellSurMixerCreate(vm::ptr config) cb_thread.InitRegs(); cb_thread.DoRun(); - while (port.m_is_audio_port_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 continue; } - if (port.m_is_audio_port_started) + if (port.state.read_relaxed() == AUDIO_PORT_STATE_STARTED) { //u64 stamp0 = get_system_time(); @@ -440,7 +434,7 @@ int cellSurMixerCreate(vm::ptr config) //u64 stamp2 = get_system_time(); - auto buf = vm::get_ptr>(m_config.m_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++) { @@ -464,7 +458,6 @@ int cellSurMixerCreate(vm::ptr config) Emu.GetCPU().RemoveThread(cb_thread.GetId()); surMixerCb.set(0); }); - t.detach(); return CELL_OK; } @@ -515,13 +508,13 @@ int cellSurMixerStart() { libmixer->Warning("cellSurMixerStart()"); - AudioPortConfig& port = m_config.m_ports[SUR_PORT]; - - if (port.m_is_audio_port_opened) + if (g_surmx.audio_port >= AUDIO_PORT_COUNT) { - port.m_is_audio_port_started = true; + 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; } @@ -535,19 +528,17 @@ int cellSurMixerFinalize() { libmixer->Warning("cellSurMixerFinalize()"); - AudioPortConfig& port = m_config.m_ports[SUR_PORT]; - - if (port.m_is_audio_port_opened) + if (g_surmx.audio_port >= AUDIO_PORT_COUNT) { - port.m_is_audio_port_started = false; - port.m_is_audio_port_opened = false; - m_config.m_port_in_use--; + 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) { @@ -564,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; @@ -581,13 +571,13 @@ int cellSurMixerPause(u32 type) { libmixer->Warning("cellSurMixerPause(type=%d)", type); - AudioPortConfig& port = m_config.m_ports[SUR_PORT]; - - if (port.m_is_audio_port_opened) + if (g_surmx.audio_port >= AUDIO_PORT_COUNT) { - port.m_is_audio_port_started = false; + 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; } @@ -603,7 +593,7 @@ int cellSurMixerGetTimestamp(u64 tag, vm::ptr stamp) { libmixer->Log("cellSurMixerGetTimestamp(tag=0x%llx, stamp_addr=0x%x)", tag, stamp.addr()); - *stamp = m_config.start_time + (tag) * 256000000 / 48000; // ??? + *stamp = g_audio.start_time + (tag) * 256000000 / 48000; // ??? return CELL_OK; } @@ -638,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/SC_FUNC.h b/rpcs3/Emu/SysCalls/SC_FUNC.h index d1d5f38dd1..d3ad6739b5 100644 --- a/rpcs3/Emu/SysCalls/SC_FUNC.h +++ b/rpcs3/Emu/SysCalls/SC_FUNC.h @@ -77,7 +77,7 @@ namespace detail static __forceinline void func(PPUThread& CPU, const T& result) { - CPU.GPR[3] = cast_ppu_gpr::func(result); + CPU.GPR[3] = cast_to_ppu_gpr(result); } }; @@ -88,7 +88,7 @@ namespace detail static __forceinline void func(PPUThread& CPU, const T& result) { - CPU.FPR[1] = (double)result; + CPU.FPR[1] = result; } }; diff --git a/rpcs3/Emu/SysCalls/lv2/cellFs.cpp b/rpcs3/Emu/SysCalls/lv2/cellFs.cpp index 3fb8bf1db7..4f83878a83 100644 --- a/rpcs3/Emu/SysCalls/lv2/cellFs.cpp +++ b/rpcs3/Emu/SysCalls/lv2/cellFs.cpp @@ -966,11 +966,7 @@ int cellFsAioRead(vm::ptr aio, vm::ptr id, vm::ptr + diff --git a/rpcs3/emucore.vcxproj.filters b/rpcs3/emucore.vcxproj.filters index 2f0baf5d6f..1ef837c8fd 100644 --- a/rpcs3/emucore.vcxproj.filters +++ b/rpcs3/emucore.vcxproj.filters @@ -1279,5 +1279,8 @@ Emu\Audio\XAudio2 + + Emu\SysCalls\Modules + \ No newline at end of file