// Copyright 2014 Citra Emulator Project // Licensed under GPLv2 or any later version // Refer to the license.txt file included. #pragma once #include #include #include #include "core/hle/kernel/mutex.h" #include "core/hle/kernel/shared_memory.h" #include "core/hle/service/service.h" namespace Core { class System; } namespace Service::CSND { enum class Encoding : u8 { Pcm8 = 0, Pcm16 = 1, Adpcm = 2, Psg = 3, }; enum class LoopMode : u8 { Manual = 0, // Play block 1 endlessly ignoring the size Normal = 1, // Play block 1 once, then repeat with block 2. Block size is reloaded every time a // new block is started OneShot = 2, // Play block 1 once and stop ConstantSize = 3, // Similar to Normal, but only load block size once at the beginning }; struct AdpcmState { s16 predictor = 0; u8 step_index = 0; private: template void serialize(Archive& ar, const unsigned int) { ar& predictor; ar& step_index; } friend class boost::serialization::access; }; struct Channel { PAddr block1_address = 0; PAddr block2_address = 0; u32 block1_size = 0; u32 block2_size = 0; AdpcmState block1_adpcm_state; AdpcmState block2_adpcm_state; bool block2_adpcm_reload = false; u16 left_channel_volume = 0; u16 right_channel_volume = 0; u16 left_capture_volume = 0; u16 right_capture_volume = 0; u32 sample_rate = 0; bool linear_interpolation = false; LoopMode loop_mode = LoopMode::Manual; Encoding encoding = Encoding::Pcm8; u8 psg_duty = 0; private: template void serialize(Archive& ar, const unsigned int) { ar& block1_address; ar& block2_address; ar& block1_size; ar& block2_size; ar& block1_adpcm_state; ar& block2_adpcm_state; ar& block2_adpcm_reload; ar& left_channel_volume; ar& right_channel_volume; ar& left_capture_volume; ar& right_capture_volume; ar& sample_rate; ar& linear_interpolation; ar& loop_mode; ar& encoding; ar& psg_duty; } friend class boost::serialization::access; }; class CSND_SND final : public ServiceFramework { public: explicit CSND_SND(Core::System& system); ~CSND_SND() = default; private: /** * CSND_SND::Initialize service function * Inputs: * 0 : Header Code[0x00010140] * 1 : Shared memory block size, for mem-block creation * 2 : offset to master state located in the shared-memory, region size=8 * 3 : offset to channel state located in the shared-memory, region size=12*num_channels * 4 : offset to capture state located in the shared-memory, region size=8*num_captures * 5 : offset to type 1 commands (?) located in the shared-memory. * Outputs: * 1 : Result of function, 0 on success, otherwise error code * 2 : Handle-list header * 3 : Mutex handle * 4 : Shared memory block handle */ void Initialize(Kernel::HLERequestContext& ctx); /** * CSND_SND::Shutdown service function * Inputs: * 0 : Header Code[0x00020000] * Outputs: * 1 : Result of function, 0 on success, otherwise error code */ void Shutdown(Kernel::HLERequestContext& ctx); /** * CSND_SND::ExecuteCommands service function * Inputs: * 0 : Header Code[0x00030040] * 1 : Command offset in shared memory. * Outputs: * 1 : Result of function, 0 on success, otherwise error code */ void ExecuteCommands(Kernel::HLERequestContext& ctx); /** * CSND_SND::ExecuteType1Commands service function * Inputs: * 0 : Header Code[0x00040080] * 1 : unknown * 2 : unknown * Outputs: * 1 : Result of function, 0 on success, otherwise error code */ void ExecuteType1Commands(Kernel::HLERequestContext& ctx); /** * CSND_SND::AcquireSoundChannels service function * Inputs: * 0 : Header Code[0x00050000] * Outputs: * 1 : Result of function, 0 on success, otherwise error code * 2 : Available channel bit mask */ void AcquireSoundChannels(Kernel::HLERequestContext& ctx); /** * CSND_SND::ReleaseSoundChannels service function * Inputs: * 0 : Header Code[0x00060000] * Outputs: * 1 : Result of function, 0 on success, otherwise error code */ void ReleaseSoundChannels(Kernel::HLERequestContext& ctx); /** * CSND_SND::AcquireCapUnit service function * This function tries to acquire one capture device (max: 2). * Returns index of which capture device was acquired. * Inputs: * 0 : Header Code[0x00070000] * Outputs: * 1 : Result of function, 0 on success, otherwise error code * 2 : Capture Unit */ void AcquireCapUnit(Kernel::HLERequestContext& ctx); /** * CSND_SND::ReleaseCapUnit service function * Inputs: * 0 : Header Code[0x00080040] * 1 : Capture Unit * Outputs: * 1 : Result of function, 0 on success, otherwise error code */ void ReleaseCapUnit(Kernel::HLERequestContext& ctx); /** * CSND_SND::FlushDataCache service function * * This Function is a no-op, We aren't emulating the CPU cache any time soon. * * Inputs: * 0 : Header Code[0x00090082] * 1 : Address * 2 : Size * 3 : Value 0, some descriptor for the KProcess Handle * 4 : KProcess handle * Outputs: * 1 : Result of function, 0 on success, otherwise error code */ void FlushDataCache(Kernel::HLERequestContext& ctx); /** * CSND_SND::StoreDataCache service function * * This Function is a no-op, We aren't emulating the CPU cache any time soon. * * Inputs: * 0 : Header Code[0x000A0082] * 1 : Address * 2 : Size * 3 : Value 0, some descriptor for the KProcess Handle * 4 : KProcess handle * Outputs: * 1 : Result of function, 0 on success, otherwise error code */ void StoreDataCache(Kernel::HLERequestContext& ctx); /** * CSND_SND::InvalidateDataCache service function * * This Function is a no-op, We aren't emulating the CPU cache any time soon. * * Inputs: * 0 : Header Code[0x000B0082] * 1 : Address * 2 : Size * 3 : Value 0, some descriptor for the KProcess Handle * 4 : KProcess handle * Outputs: * 1 : Result of function, 0 on success, otherwise error code */ void InvalidateDataCache(Kernel::HLERequestContext& ctx); /** * CSND_SND::Reset service function * Inputs: * 0 : Header Code[0x000C0000] * Outputs: * 1 : Result of function, 0 on success, otherwise error code */ void Reset(Kernel::HLERequestContext& ctx); Core::System& system; std::shared_ptr mutex = nullptr; std::shared_ptr shared_memory = nullptr; static constexpr u32 MaxCaptureUnits = 2; std::array capture_units = {false, false}; static constexpr u32 ChannelCount = 32; std::array channels; u32 master_state_offset = 0; u32 channel_state_offset = 0; u32 capture_state_offset = 0; u32 type1_command_offset = 0; u32 acquired_channel_mask = 0; template void serialize(Archive& ar, const unsigned int) { ar& boost::serialization::base_object(*this); ar& mutex; ar& shared_memory; ar& capture_units; ar& channels; ar& master_state_offset; ar& channel_state_offset; ar& capture_state_offset; ar& type1_command_offset; ar& acquired_channel_mask; } friend class boost::serialization::access; }; /// Initializes the CSND_SND Service void InstallInterfaces(Core::System& system); } // namespace Service::CSND BOOST_CLASS_EXPORT_KEY(Service::CSND::CSND_SND) SERVICE_CONSTRUCT(Service::CSND::CSND_SND)