diff --git a/CMakeLists.txt b/CMakeLists.txt index 77d547e4..b3266d2c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -179,7 +179,9 @@ add_library(NintendoSDK OBJECT include/nn/util/util_BytePtr.h include/nn/util/util_IntrusiveList.h include/nn/util/util_BitUtil.h + include/nn/util/util_IntrusiveList.h include/nn/util/util_ResDic.h + include/nn/util/util_StringUtil.h include/nn/util/util_StringView.h include/nn/ui2d/detail/TexCoordArray.h include/nn/ui2d/Layout.h @@ -205,16 +207,6 @@ add_library(NintendoSDK OBJECT include/nn/nn.h include/nn/settings.h include/nn/hid.h - include/nn/atk/detail/StreamSoundRuntime.h - include/nn/atk/detail/BasicSound.h - include/nn/atk/detail/WaveSoundRuntime.h - include/nn/atk/detail/SequenceSoundRuntime.h - include/nn/atk/detail/SoundArchiveManager.h - include/nn/atk/detail/AdvancedWaveSoundRuntime.h - include/nn/atk/AuxBus.h - include/nn/atk/SoundArchivePlayer.h - include/nn/atk/SoundPlayer.h - include/nn/atk/SoundDataManager.h include/nn/nfp.h include/nn/nfp/nfp.h include/nn/nfp/nfp_types.h @@ -236,6 +228,20 @@ add_library(NintendoSDK OBJECT include/nn/friends.h include/nn/mem.h + include/nn/audio/audio_Adpcm.h + include/nn/audio/audio_AudioRendererTypes.h + include/nn/audio/audio_Common.h + include/nn/audio/audio_EffectTypes.h + include/nn/audio/audio_FinalMixTypes.h + include/nn/audio/audio_MemoryPoolTypes.h + include/nn/audio/audio_PerformanceMetrics.h + include/nn/audio/audio_PerformanceMetricsTypes.h + include/nn/audio/audio_SampleFormat.h + include/nn/audio/audio_SinkTypes.h + include/nn/audio/audio_SubMixTypes.h + include/nn/audio/audio_VoiceTypes.h + include/nn/audio/audio_WaveBuffer.h + include/nvn/nvn_Cpp.h include/nvn/nvn_CppFuncPtrBase.h include/nvn/nvn_CppMethods.h @@ -284,6 +290,273 @@ add_library(NintendoSDK OBJECT src/NintendoSDK/nvn/nvn_Impl.cpp ) +# Atk Sources +target_sources(NintendoSDK +PUBLIC + include/nn/atk/atk_Adpcm.h + include/nn/atk/atk_AudioRendererPerformanceReader.h + include/nn/atk/atk_Bank.h + include/nn/atk/atk_BankFile.h + include/nn/atk/atk_BankFileReader.h + include/nn/atk/atk_BasicSound.h + include/nn/atk/atk_BasicSoundPlayer.h + include/nn/atk/atk_BinaryFileFormat.h + include/nn/atk/atk_BiquadFilterCallback.h + include/nn/atk/atk_BiquadFilterPresets.h + include/nn/atk/atk_BusMixVolumePacket.h + include/nn/atk/atk_Channel.h + include/nn/atk/atk_ChannelManager.h + include/nn/atk/atk_ChannelMixVolume.h + include/nn/atk/atk_Command.h + include/nn/atk/atk_CommandManager.h + include/nn/atk/atk_Config.h + include/nn/atk/atk_CurveAdshr.h + include/nn/atk/atk_CurveLfo.h + include/nn/atk/atk_Debug.h + include/nn/atk/atk_DeviceOutRecorder.h + include/nn/atk/atk_DisposeCallback.h + include/nn/atk/atk_DisposeCallbackManager.h + include/nn/atk/atk_DriverCommand.h + include/nn/atk/atk_DspadpcmReader.h + include/nn/atk/atk_EffectAux.h + include/nn/atk/atk_EffectBase.h + include/nn/atk/atk_ElementType.h + include/nn/atk/atk_ExternalSoundPlayer.h + include/nn/atk/atk_FinalMix.h + include/nn/atk/atk_FrameHeap.h + include/nn/atk/atk_FsSoundArchive.h + include/nn/atk/atk_Global.h + include/nn/atk/atk_GroupFile.h + include/nn/atk/atk_GroupFileReader.h + include/nn/atk/atk_HardwareManager.h + include/nn/atk/atk_InstancePool.h + include/nn/atk/atk_ItemType.h + include/nn/atk/atk_LoaderManager.h + include/nn/atk/atk_LowLevelVoice.h + include/nn/atk/atk_MmlCommand.h + include/nn/atk/atk_MmlParser.h + include/nn/atk/atk_MmlSequenceTrack.h + include/nn/atk/atk_MmlSequenceTrackAllocator.h + include/nn/atk/atk_MoveValue.h + include/nn/atk/atk_MultiVoice.h + include/nn/atk/atk_MultiVoiceManager.h + include/nn/atk/atk_NoteOnCallback.h + include/nn/atk/atk_OutputAdditionalParam.h + include/nn/atk/atk_OutputMixer.h + include/nn/atk/atk_OutputReceiver.h + include/nn/atk/atk_PlayerHeap.h + include/nn/atk/atk_PlayerHeapDataManager.h + include/nn/atk/atk_ProfileReader.h + include/nn/atk/atk_SequenceSound.h + include/nn/atk/atk_SequenceSoundFile.h + include/nn/atk/atk_SequenceSoundFileReader.h + include/nn/atk/atk_SequenceSoundHandle.h + include/nn/atk/atk_SequenceSoundPlayer.h + include/nn/atk/atk_SequenceSoundRuntime.h + include/nn/atk/atk_SequenceTrack.h + include/nn/atk/atk_SequenceTrackAllocator.h + include/nn/atk/atk_SoundActor.h + include/nn/atk/atk_SoundArchive.h + include/nn/atk/atk_SoundArchiveFile.h + include/nn/atk/atk_SoundArchiveFileReader.h + include/nn/atk/atk_SoundArchiveFilesHook.h + include/nn/atk/atk_SoundArchiveLoader.h + include/nn/atk/atk_SoundArchiveParametersHook.h + include/nn/atk/atk_SoundArchivePlayer.h + include/nn/atk/atk_SoundDataManager.h + include/nn/atk/atk_SoundHandle.h + include/nn/atk/atk_SoundHeap.h + include/nn/atk/atk_SoundInstanceManager.h + include/nn/atk/atk_SoundMemoryAllocatable.h + include/nn/atk/atk_SoundPlayer.h + include/nn/atk/atk_SoundRuntimeUtility.h + include/nn/atk/atk_SoundStartable.h + include/nn/atk/atk_SoundSystem.h + include/nn/atk/atk_SoundThread.h + include/nn/atk/atk_StartInfoReader.h + include/nn/atk/atk_StreamBufferPool.h + include/nn/atk/atk_StreamSound.h + include/nn/atk/atk_StreamSoundFile.h + include/nn/atk/atk_StreamSoundFileLoader.h + include/nn/atk/atk_StreamSoundFileReader.h + include/nn/atk/atk_StreamSoundHandle.h + include/nn/atk/atk_StreamSoundLoader.h + include/nn/atk/atk_StreamSoundPlayer.h + include/nn/atk/atk_StreamSoundPrefetchFile.h + include/nn/atk/atk_StreamSoundPrefetchFileReader.h + include/nn/atk/atk_StreamSoundRuntime.h + include/nn/atk/atk_StreamTrack.h + include/nn/atk/atk_SubMix.h + include/nn/atk/atk_Task.h + include/nn/atk/atk_TaskManager.h + include/nn/atk/atk_TaskProfileReader.h + include/nn/atk/atk_TaskThread.h + include/nn/atk/atk_ThreadInfoReader.h + include/nn/atk/atk_Util.h + include/nn/atk/atk_ValueArray.h + include/nn/atk/atk_Voice.h + include/nn/atk/atk_VoiceCommand.h + include/nn/atk/atk_VolumeThroughModePacket.h + include/nn/atk/atk_WavBinary.h + include/nn/atk/atk_WavOutFileStream.h + include/nn/atk/atk_WaveArchiveFile.h + include/nn/atk/atk_WaveArchiveFileReader.h + include/nn/atk/atk_WaveFile.h + include/nn/atk/atk_WaveFileReader.h + include/nn/atk/atk_WaveSound.h + include/nn/atk/atk_WaveSoundFile.h + include/nn/atk/atk_WaveSoundFileReader.h + include/nn/atk/atk_WaveSoundHandle.h + include/nn/atk/atk_WaveSoundLoader.h + include/nn/atk/atk_WaveSoundPlayer.h + include/nn/atk/atk_WaveSoundRuntime.h + include/nn/atk/detail/atk_AddonSoundArchiveContainer.h + include/nn/atk/detail/atk_AdvancedWaveSound.h + include/nn/atk/detail/atk_AdvancedWaveSoundFile.h + include/nn/atk/detail/atk_AdvancedWaveSoundFileReader.h + include/nn/atk/detail/atk_AdvancedWaveSoundPlayer.h + include/nn/atk/detail/atk_AdvancedWaveSoundRuntime.h + include/nn/atk/detail/atk_BinaryTypes.h + include/nn/atk/detail/atk_IRegionInfoReadable.h + include/nn/atk/detail/atk_IStreamDataDecoder.h + include/nn/atk/detail/atk_IntrusiveList.h + include/nn/atk/detail/atk_MemoryFileStream.h + include/nn/atk/detail/atk_RegionManager.h + include/nn/atk/detail/atk_SoundArchiveManager.h + include/nn/atk/fnd/basis/atkfnd_Config.h + include/nn/atk/fnd/basis/atkfnd_FrameHeapImpl.h + include/nn/atk/fnd/basis/atkfnd_HeapBase.h + include/nn/atk/fnd/basis/atkfnd_Inlines.h + include/nn/atk/fnd/basis/atkfnd_Result.h + include/nn/atk/fnd/basis/atkfnd_RuntimeTypeInfo.h + include/nn/atk/fnd/basis/atkfnd_Time.h + include/nn/atk/fnd/basis/atkfnd_WorkBufferAllocator.h + include/nn/atk/fnd/binary/atkfnd_PrimitiveTypes.h + include/nn/atk/fnd/io/atkfnd_FileStream.h + include/nn/atk/fnd/io/atkfnd_FileStreamImpl.h + include/nn/atk/fnd/io/atkfnd_FileStreamProxy.h + include/nn/atk/fnd/io/atkfnd_Stream.h + include/nn/atk/fnd/io/atkfnd_StreamCache.h + include/nn/atk/fnd/os/atkfnd_CriticalSection.h + include/nn/atk/fnd/os/atkfnd_ScopedLock.h + include/nn/atk/fnd/os/atkfnd_Thread.h + include/nn/atk/fnd/string/atkfnd_String.h + +PRIVATE + src/NintendoWare/atk/atk_FsSoundArchive.cpp + src/NintendoWare/atk/atk_SequenceSoundHandle.cpp + src/NintendoWare/atk/atk_SoundArchive.cpp + src/NintendoWare/atk/atk_SoundArchivePlayer.cpp + src/NintendoWare/atk/atk_SoundDataManager.cpp + src/NintendoWare/atk/atk_SoundHandle.cpp + src/NintendoWare/atk/atk_SoundHeap.cpp + src/NintendoWare/atk/atk_SoundPlayer.cpp + src/NintendoWare/atk/atk_SoundStartable.cpp + src/NintendoWare/atk/atk_SoundSystem.cpp + src/NintendoWare/atk/atk_StreamSoundHandle.cpp + src/NintendoWare/atk/detail/atk_AddonSoundArchiveContainer.cpp + src/NintendoWare/atk/detail/atk_AdvancedWaveSound.cpp + src/NintendoWare/atk/detail/atk_AdvancedWaveSoundFile.cpp + src/NintendoWare/atk/detail/atk_AdvancedWaveSoundFileReader.cpp + src/NintendoWare/atk/detail/atk_AdvancedWaveSoundPlayer.cpp + src/NintendoWare/atk/detail/atk_AdvancedWaveSoundRuntime.cpp + src/NintendoWare/atk/detail/atk_BasicSound.cpp + src/NintendoWare/atk/detail/atk_BasicSoundPlayer.cpp + src/NintendoWare/atk/detail/atk_BusMixVolumePacket.cpp + src/NintendoWare/atk/detail/atk_DisposeCallbackManager.cpp + src/NintendoWare/atk/detail/atk_ExternalSoundPlayer.cpp + src/NintendoWare/atk/detail/atk_GroupFile.cpp + src/NintendoWare/atk/detail/atk_GroupFileReader.cpp + src/NintendoWare/atk/detail/atk_OutputAdditionalParam.cpp + src/NintendoWare/atk/detail/atk_PlayerHeap.cpp + src/NintendoWare/atk/detail/atk_PlayerHeapDataManager.cpp + src/NintendoWare/atk/detail/atk_RegionManager.cpp + src/NintendoWare/atk/detail/atk_SoundArchiveFile.cpp + src/NintendoWare/atk/detail/atk_SoundArchiveFileReader.cpp + src/NintendoWare/atk/detail/atk_SoundArchiveLoader.cpp + src/NintendoWare/atk/detail/atk_SoundArchiveManager.cpp + src/NintendoWare/atk/detail/atk_StartInfoReader.cpp + src/NintendoWare/atk/detail/atk_VolumeThroughModePacket.cpp + src/NintendoWare/atk/detail/atk_WaveArchiveFile.cpp + src/NintendoWare/atk/detail/atk_WaveArchiveFileReader.cpp + src/NintendoWare/atk/detail/atk_WaveFile.cpp + src/NintendoWare/atk/detail/atk_WaveFileReader.cpp + src/NintendoWare/atk/detail/debug/atk_Debug.cpp + src/NintendoWare/atk/detail/dsp/atk_BiquadFilterPresets.cpp + src/NintendoWare/atk/detail/dsp/atk_DecodeAdpcm.cpp + src/NintendoWare/atk/detail/dsp/atk_DspadpcmReader.cpp + src/NintendoWare/atk/detail/dsp/atk_HardwareManager.cpp + src/NintendoWare/atk/detail/seq/atk_Bank.cpp + src/NintendoWare/atk/detail/seq/atk_BankFile.cpp + src/NintendoWare/atk/detail/seq/atk_BankFileReader.cpp + src/NintendoWare/atk/detail/seq/atk_MmlParser.cpp + src/NintendoWare/atk/detail/seq/atk_MmlSequenceTrack.cpp + src/NintendoWare/atk/detail/seq/atk_MmlSequenceTrackAllocator.cpp + src/NintendoWare/atk/detail/seq/atk_SequenceSound.cpp + src/NintendoWare/atk/detail/seq/atk_SequenceSoundFile.cpp + src/NintendoWare/atk/detail/seq/atk_SequenceSoundFileReader.cpp + src/NintendoWare/atk/detail/seq/atk_SequenceSoundPlayer.cpp + src/NintendoWare/atk/detail/seq/atk_SequenceSoundRuntime.cpp + src/NintendoWare/atk/detail/seq/atk_SequenceTrack.cpp + src/NintendoWare/atk/detail/strm/atk_StreamBufferPool.cpp + src/NintendoWare/atk/detail/strm/atk_StreamSound.cpp + src/NintendoWare/atk/detail/strm/atk_StreamSoundFile.cpp + src/NintendoWare/atk/detail/strm/atk_StreamSoundFileLoader.cpp + src/NintendoWare/atk/detail/strm/atk_StreamSoundFileReader.cpp + src/NintendoWare/atk/detail/strm/atk_StreamSoundLoader.cpp + src/NintendoWare/atk/detail/strm/atk_StreamSoundPlayer.cpp + src/NintendoWare/atk/detail/strm/atk_StreamSoundPrefetchFile.cpp + src/NintendoWare/atk/detail/strm/atk_StreamSoundPrefetchFileReader.cpp + src/NintendoWare/atk/detail/strm/atk_StreamSoundRuntime.cpp + src/NintendoWare/atk/detail/strm/atk_StreamTrack.cpp + src/NintendoWare/atk/detail/thread/atk_CommandManager.cpp + src/NintendoWare/atk/detail/thread/atk_DriverCommand.cpp + src/NintendoWare/atk/detail/thread/atk_SoundThread.cpp + src/NintendoWare/atk/detail/thread/atk_Task.cpp + src/NintendoWare/atk/detail/thread/atk_TaskManager.cpp + src/NintendoWare/atk/detail/thread/atk_TaskThread.cpp + src/NintendoWare/atk/detail/util/atk_CurveAdshr.cpp + src/NintendoWare/atk/detail/util/atk_CurveLfo.cpp + src/NintendoWare/atk/detail/util/atk_FrameHeap.cpp + src/NintendoWare/atk/detail/util/atk_InstancePool.cpp + src/NintendoWare/atk/detail/util/atk_MemoryFileStream.cpp + src/NintendoWare/atk/detail/util/atk_Util.cpp + src/NintendoWare/atk/detail/util/atk_WavOutFileStream.cpp + src/NintendoWare/atk/detail/voice/atk_Channel.cpp + src/NintendoWare/atk/detail/voice/atk_ChannelManager.cpp + src/NintendoWare/atk/detail/voice/atk_LowLevelVoice.cpp + src/NintendoWare/atk/detail/voice/atk_MultiVoice.cpp + src/NintendoWare/atk/detail/voice/atk_MultiVoiceManager.cpp + src/NintendoWare/atk/detail/voice/atk_Voice.cpp + src/NintendoWare/atk/detail/voice/atk_VoiceCommand.cpp + src/NintendoWare/atk/detail/wsd/atk_WaveSound.cpp + src/NintendoWare/atk/detail/wsd/atk_WaveSoundFile.cpp + src/NintendoWare/atk/detail/wsd/atk_WaveSoundFileReader.cpp + src/NintendoWare/atk/detail/wsd/atk_WaveSoundLoader.cpp + src/NintendoWare/atk/detail/wsd/atk_WaveSoundPlayer.cpp + src/NintendoWare/atk/detail/wsd/atk_WaveSoundRuntime.cpp + src/NintendoWare/atk/effect/atk_EffectAux.cpp + src/NintendoWare/atk/effect/atk_EffectBase.cpp + src/NintendoWare/atk/fnd/basis/atkfnd_FrameHeapImpl.cpp + src/NintendoWare/atk/fnd/basis/atkfnd_HeapBase.cpp + src/NintendoWare/atk/fnd/basis/atkfnd_Time.win32.cpp + src/NintendoWare/atk/fnd/basis/atkfnd_WorkBufferAllocator.cpp + src/NintendoWare/atk/fnd/io/atkfnd_FileStreamImpl.cpp + src/NintendoWare/atk/fnd/io/atkfnd_FileStreamImpl.win32.cpp + src/NintendoWare/atk/fnd/io/atkfnd_FileStreamProxy.cpp + src/NintendoWare/atk/fnd/io/atkfnd_StreamCache.cpp + src/NintendoWare/atk/fnd/os/atkfnd_Thread.cpp + src/NintendoWare/atk/fnd/os/atkfnd_Thread.win32.cpp + src/NintendoWare/atk/submix/atk_ChannelMixVolume.cpp + src/NintendoWare/atk/submix/atk_FinalMix.cpp + src/NintendoWare/atk/submix/atk_OutputMixer.cpp + src/NintendoWare/atk/submix/atk_SubMix.cpp + src/NintendoWare/atk/util/atk_AudioRendererPerformanceReader.cpp + src/NintendoWare/atk/util/atk_DeviceOutRecorder.cpp + src/NintendoWare/atk/util/atk_ProfileReader.cpp + src/NintendoWare/atk/util/atk_TaskProfileReader.cpp +) + target_include_directories(NintendoSDK PUBLIC include/) target_include_directories(NintendoSDK PRIVATE src/NintendoSDK/) target_compile_options(NintendoSDK PRIVATE -fno-strict-aliasing) diff --git a/include/nn/atk/AuxBus.h b/include/nn/atk/AuxBus.h deleted file mode 100644 index a954c4b5..00000000 --- a/include/nn/atk/AuxBus.h +++ /dev/null @@ -1,9 +0,0 @@ -#pragma once - -#include - -namespace nn { -namespace atk { -enum class AuxBus : s32 { AuxBus_A, AuxBus_B, AuxBus_C }; -} -} // namespace nn diff --git a/include/nn/atk/SoundArchivePlayer.h b/include/nn/atk/SoundArchivePlayer.h deleted file mode 100644 index c3646646..00000000 --- a/include/nn/atk/SoundArchivePlayer.h +++ /dev/null @@ -1,37 +0,0 @@ -/** - * @file SoundArchivePlayer.h - * @brief Basic sound player from a sound archive. - */ - -#pragma once - -#include -#include -#include -#include -#include - -namespace nn { -namespace atk { -class SoundArchivePlayer { -public: - SoundArchivePlayer(); - - virtual ~SoundArchivePlayer(); - - bool IsAvailable() const; - void Finalize(); - void StopAllSound(s32, bool); - void DisposeInstances(); - - nn::atk::detail::SoundArchiveManager mArchiveManager; // _8 - nn::atk::detail::SequenceSoundRuntime mSeqSoundRuntime; // _50 - nn::atk::detail::WaveSoundRuntime mWaveSoundRuntime; // _130 - nn::atk::detail::AdvancedWaveSoundRuntime mAdvancedWaveSound; // _1B0 - nn::atk::detail::StreamSoundRuntime mStreamSoundRuntime; // _1E0 - u64 _290; - u32 _298; - u8 _29C[0x2E8 - 0x29C]; -}; -} // namespace atk -} // namespace nn diff --git a/include/nn/atk/SoundDataManager.h b/include/nn/atk/SoundDataManager.h deleted file mode 100644 index f9366c6e..00000000 --- a/include/nn/atk/SoundDataManager.h +++ /dev/null @@ -1,25 +0,0 @@ -/** - * @file SoundDataManager.h - * @brief Sound data management implementation. - */ - -#pragma once - -#include - -namespace nn { -namespace atk { -class SoundDataManager { -public: - SoundDataManager(); - virtual ~SoundDataManager(); - - virtual void InvalidateData(void const*, void const*); - virtual void SetFileAddressToTable(u32, void const*); - virtual u64 GetFileAddressFromTable(u32) const; - virtual u32 GetFileAddressImpl(u32) const; - - u8 _0[0x240]; -}; -} // namespace atk -} // namespace nn diff --git a/include/nn/atk/SoundPlayer.h b/include/nn/atk/SoundPlayer.h deleted file mode 100644 index 3711c774..00000000 --- a/include/nn/atk/SoundPlayer.h +++ /dev/null @@ -1,56 +0,0 @@ -/** - * @file SoundPlayer.h - * @brief Sound player. - */ - -#pragma once - -#include - -namespace nn { -namespace atk { -enum PauseMode { - -}; - -class SoundPlayer { -public: - SoundPlayer(); - ~SoundPlayer(); - - void StopAllSound(s32); - void Update(); - void DoFreePlayerHeap(); - void detail_SortPriorityList(bool); - void PauseAllSound(s32, bool); - void PauseAllSound(bool, s32, nn::atk::PauseMode); - void SetVolume(f32 vol); - void SetLowPassFilterFrequency(f32 filterFreq); - void SetBiquadFilter(s32 filterType, f32 baseFreq); - void SetDefaultOutputLine(u32 line); - - void detail_SetPlayableSoundLimit(s32 limit); - bool CanPlaySound(s32); - - u64 _0; - u64 _8; - u64 _10; - u64 _18; - u64 _20; - u64 _28; - u64 _30; - u64 _38; - s32 _40; - s32 mPlayableSoundCount; // _44 - s32 _48; - f32 mVolume; // _4C - f32 mLowPassFreq; // _50 - s32 mFilterType; // _54 - f32 mBaseFreq; // _58 - u32 mDefaultOutputLine; // _5C - f32 mOutputVolume; // _60 - u64 _64; - u64 _6C; -}; -} // namespace atk -} // namespace nn diff --git a/include/nn/atk/atk_Adpcm.h b/include/nn/atk/atk_Adpcm.h new file mode 100644 index 00000000..6949c8f6 --- /dev/null +++ b/include/nn/atk/atk_Adpcm.h @@ -0,0 +1,25 @@ +#pragma once + +#include + +#include + +namespace nn::atk { +using AdpcmParam = audio::AdpcmParameter; + +struct alignas(64) AdpcmContext { + audio::AdpcmContext audioAdpcmContext; +}; +static_assert(sizeof(AdpcmContext) == 0x40); + +struct AdpcmContextNotAligned { + audio::AdpcmContext audioAdpcmContext; +}; +static_assert(sizeof(AdpcmContextNotAligned) == 0x6); + +namespace detail { +void DecodeDspAdpcm(position_t playPosition, AdpcmContext& context, + const AdpcmParam& param, const void* adpcmData, + size_t decodeSamples, s16* dest); +} +} // namespace nn::atk \ No newline at end of file diff --git a/include/nn/atk/atk_AudioRendererPerformanceReader.h b/include/nn/atk/atk_AudioRendererPerformanceReader.h new file mode 100644 index 00000000..188585ff --- /dev/null +++ b/include/nn/atk/atk_AudioRendererPerformanceReader.h @@ -0,0 +1,37 @@ +#pragma once + +#include + +#include + +namespace nn::atk { +class AudioRendererPerformanceReader { +public: + static const int PerformanceInfoCountMin = 2; + + struct PerformanceInfo { + void* performanceBuffer; + size_t performanceBufferSize; + os::Tick tick; + }; + + AudioRendererPerformanceReader(); + + static size_t GetRequiredMemorySize(int performanceInfoCount); + + void Initialize(int performanceInfoCount, void* buffer, size_t bufferSize); + const PerformanceInfo* ReadPerformanceInfo(); + + bool IsInitialized() const { return m_IsInitialized; }; + + void Record(const void* performanceFrameBuffer, size_t performanceFrameBufferSize, os::Tick tick); + +private: + PerformanceInfo* m_pPerformanceInfo {}; + int m_PerformanceInfoCount {0}; + std::atomic_int m_WriteIndex {0}; + std::atomic_int m_ReadIndex {0}; + bool m_IsInitialized {false}; +}; +static_assert(sizeof(AudioRendererPerformanceReader) == 0x18); +} // namespace nn::atk \ No newline at end of file diff --git a/include/nn/atk/atk_Bank.h b/include/nn/atk/atk_Bank.h new file mode 100644 index 00000000..8c051f11 --- /dev/null +++ b/include/nn/atk/atk_Bank.h @@ -0,0 +1,20 @@ +#pragma once + +#include +#include +#include + +namespace nn::atk::detail::driver { +class Bank { +public: + Bank(); + ~Bank(); + + Channel* NoteOn(const BankFileReader& bankReader, + const WaveArchiveFileReader& warcReader, + const NoteOnInfo& noteOnInfo) const; + + static float CalcChannelVelocityVolume(u8 velocity); +}; +static_assert(sizeof(Bank) == 0x1); +} // namespace nn::atk::detail::driver \ No newline at end of file diff --git a/include/nn/atk/atk_BankFile.h b/include/nn/atk/atk_BankFile.h new file mode 100644 index 00000000..f81e64fd --- /dev/null +++ b/include/nn/atk/atk_BankFile.h @@ -0,0 +1,94 @@ +#pragma once + +#include + +namespace nn::atk::detail { +struct BankFile { + struct InfoBlock; + struct FileHeader : Util::SoundFileHeader { + const InfoBlock* GetInfoBlock() const; + }; + + struct Instrument; + struct InfoBlockBody { + Util::Reference toWaveIdTable; + Util::Reference toInstrumentReferenceTable; + + const Util::WaveIdTable& GetWaveIdTable() const; + const Util::ReferenceTable& GetInstrumentReferenceTable() const; + + u32 GetWaveIdCount() const { + return GetWaveIdTable().table.count; + } + + s32 GetInstrumentCount() const { + return GetInstrumentReferenceTable().count; + } + + const Util::WaveId* GetWaveId(u32 index) const { + return GetWaveIdTable().GetWaveId(index); + } + + const Instrument* GetInstrument(int programNo) const; + }; + static_assert(sizeof(InfoBlockBody) == 0x10); + + struct InfoBlock { + BinaryBlockHeader header; + InfoBlockBody body; + }; + static_assert(sizeof(InfoBlock) == 0x18); + + struct KeyRegion; + struct Instrument { + Util::Reference toKeyRegionChunk; + + const KeyRegion* GetKeyRegion(u32 key) const; + }; + static_assert(sizeof(Instrument) == 0x8); + + struct VelocityRegion; + struct KeyRegion { + Util::Reference toVelocityRegionChunk; + + const VelocityRegion* GetVelocityRegion(u32 velocity) const; + }; + static_assert(sizeof(KeyRegion) == 0x8); + + struct RegionParameter; + struct VelocityRegion { + u32 waveIdTableIndex; + Util::BitFlag optionParameter; + + u8 GetOriginalKey() const; + u8 GetVolume() const; + u8 GetPan() const; + float GetPitch() const ; + bool IsIgnoreNoteOff() const; + u8 GetKeyGroup() const; + u8 GetInterpolationType() const; + const AdshrCurve& GetAdshrCurve() const; + const RegionParameter* GetRegionParameter() const; + }; + static_assert(sizeof(VelocityRegion) == 0x8); + + struct RegionParameter { + u8 originalKey; + u8 padding1[3]; + u8 volume; + u8 padding2[3]; + u8 pan; + s8 surroundPan; + u8 padding3[2]; + f32 pitch; + bool isIgnoreNoteOff; + u8 keyGroup; + u8 interpolationType; + u8 padding4[1]; + u32 offset; + Util::Reference refToAdshrCurve; + AdshrCurve adshrCurve; + }; + static_assert(sizeof(RegionParameter) == 0x28); +}; +} // namespace nn::atk::detail \ No newline at end of file diff --git a/include/nn/atk/atk_BankFileReader.h b/include/nn/atk/atk_BankFileReader.h new file mode 100644 index 00000000..029c6dbc --- /dev/null +++ b/include/nn/atk/atk_BankFileReader.h @@ -0,0 +1,41 @@ +#pragma once + +#include + +namespace nn::atk::detail { +class BankFileReader { +public: + constexpr static s32 SignatureFile = 0x4b4e4246; // FBNK + + struct VelocityRegionInfo { + u32 waveArchiveId; + u32 waveIndex; + f32 pitch; + AdshrCurve adshrCurve; + u8 originalKey; + u8 volume; + u8 pan; + bool isIgnoreNoteOff; + u8 keyGroup; + u8 interpolationType; + }; + static_assert(sizeof(VelocityRegionInfo) == 0x18); + + BankFileReader(); + explicit BankFileReader(const void* bankFile); + + void Initialize(const void* bankFile); + void Finalize(); + + bool ReadVelocityRegionInfo(VelocityRegionInfo* info, s32 programNo, + s32 key, s32 velocity) const; + + Util::WaveIdTable* GetWaveIdTable() const; + +private: + BankFile::FileHeader* m_pHeader; + BankFile::InfoBlockBody* m_pInfoBlockBody; + bool m_IsInitialized; +}; +static_assert(sizeof(BankFileReader) == 0x18); +} // namespace nn::atk::detail \ No newline at end of file diff --git a/include/nn/atk/atk_BasicSound.h b/include/nn/atk/atk_BasicSound.h new file mode 100644 index 00000000..ea5cc7f5 --- /dev/null +++ b/include/nn/atk/atk_BasicSound.h @@ -0,0 +1,566 @@ +#pragma once + +#include +#include + +#include +#include +#include +#include +#include +#include + +namespace nn::atk { +class SoundActor; +class SoundPlayer; +class SoundHandle; + +class OutputAmbientParam { +public: + OutputAmbientParam(); + + void Initialize(); + + float GetVolume() const; + void SetVolume(float volume); + + float GetPan() const; + void SetPan(float pan); + + float GetSurroundPan() const; + void SetSurroundPan(float surroundPan); + + float GetEffectSend(int) const; + void SetEffectSend(int, float) const; + +private: + f32 m_Volume; + f32 m_Pan; + f32 m_SurroundPan; + f32 m_FxSend[3]; +}; +static_assert(sizeof(OutputAmbientParam) == 0x18); + +class SoundAmbientParam { +public: + static const int OutputLineFlagInherit = -1; + + SoundAmbientParam(); + + float GetVolume() const { return m_Volume; } + void SetVolume(float volume) { m_Volume = volume; } + + float GetPitch() const { return m_Pitch; } + void SetPitch(float pitch) { m_Pitch = pitch; } + + float GetLowPassFilter() const { return m_Lpf; } + void SetLowPassFilter(float lowPassFilter) { m_Lpf = lowPassFilter; } + + float GetBiquadFilterValue() const { return m_BiquadFilterValue; } + void SetBiquadFilterValue(float biquadFilterValue) { m_BiquadFilterValue = biquadFilterValue; } + + int GetBiquadFilterType() const { return m_BiquadFilterType; } + void SetBiquadFilterType(int biquadFilterType) { m_BiquadFilterType = biquadFilterType; } + + int GetPriority() const { return m_Priority; } + void SetPriority(int priority) { m_Priority = priority; } + + u32 GetUserData() const { return m_UserData; } + void SetUserData(u32 userData) { m_UserData = userData; } + + int GetOutputLineFlag() const { return m_OutputLineFlag; } + void SetOutputLineFlag(int outputLineFlag) { m_OutputLineFlag = outputLineFlag; } + + OutputAmbientParam& GetTvParam() { return m_TvParam; } + void SetTvParam(const OutputAmbientParam& tvParam) { m_TvParam = tvParam; } + +private: + float m_Volume; + float m_Pitch; + float m_Lpf; + float m_BiquadFilterValue; + int m_BiquadFilterType; + int m_Priority; + u32 m_UserData; + int m_OutputLineFlag; + OutputAmbientParam m_TvParam; +}; +static_assert(sizeof(SoundAmbientParam) == 0x38); + +class SoundParam { +public: + SoundParam(); + + void Initialize(); + + float GetVolume() const { return m_Volume; } + void SetVolume(float volume) { m_Volume = volume; } + + float GetPitch() const { return m_Pitch; } + void SetPitch(float pitch) { m_Pitch = pitch; } + + float GetLpf() const { return m_Lpf; } + void SetLpf(float lpf) { m_Lpf = lpf; } + + float GetBiquadFilterValue() const { return m_BiquadFilterValue; } + void SetBiquadFilterValue(float biquadFilterValue) { m_BiquadFilterValue = biquadFilterValue; } + + int GetBiquadFilterType() const { return m_BiquadFilterType; } + void SetBiquadFilterType(int biquadFilterType) { m_BiquadFilterType = biquadFilterType; } + + int GetPriority() const { return m_Priority; } + void SetPriority(int priority) { m_Priority = priority; } + + int GetOutputLineFlag() const { return m_OutputLineFlag; } + void SetOutputLineFlag(int outputLineFlag) { m_OutputLineFlag = outputLineFlag; } + + u32 GetUserData() const { return m_UserData; } + void SetUserData(u32 userData) { m_UserData = userData; } + + OutputAmbientParam& GetTvParam() { return m_TvParam; } + const OutputAmbientParam& GetTvParam() const { return m_TvParam; } + void SetTvParam(const OutputAmbientParam& tvParam) { m_TvParam = tvParam; } + +private: + float m_Volume; + float m_Pitch; + float m_Lpf; + float m_BiquadFilterValue; + int m_BiquadFilterType; + int m_Priority; + int m_OutputLineFlag; + u32 m_UserData; + OutputAmbientParam m_TvParam; +}; +static_assert(sizeof(SoundParam) == 0x38); + +struct SoundParamCalculationValues { + struct SoundArchiveParam { + f32 volume; + }; + static_assert(sizeof(SoundArchiveParam) == 0x4); + + struct SoundPlayerParam { + float volume; + float lpf; + int bqfType; + float bqfValue; + float outputVolume[1]; + float outputMainSend[1]; + float outputEffectSend[1][3]; + }; + static_assert(sizeof(SoundPlayerParam) == 0x24); + + struct Sound3DParam { + float volume; + float pitch; + float lpf; + int bqfType; + float bqfValue; + u32 outputLineFlag; + float outputVolume[1]; + float outputPan[1]; + float outputSurroundPan[1]; + float outputEffectSend[1][3]; + int playerPriority; + }; + static_assert(sizeof(Sound3DParam) == 0x34); + + struct SoundActorParam { + float volume; + float pitch; + float lpf; + float outputVolume[1]; + float outputPan[1]; + }; + static_assert(sizeof(SoundActorParam) == 0x14); + + struct SoundHandleParam { + float volume; + float pitch; + float lpf; + int bqfType; + int bqfValue; + u32 outputLineFlag; + float outputVolume[1]; + float outputPan[1]; + float outputSurroundPan[1]; + float outputMainSend[1]; + float outputEffectSend[1][3]; + MixParameter outputMixParameter[1][2]; + MixMode mixMode; + float pan; + float surroundPan; + float mainSend; + float effectSend[3]; + int playerPriority; + }; + static_assert(sizeof(SoundHandleParam) == 0x84); + + struct ResultParam { + float volume; + float pitch; + float lpf; + int bqfType; + float bqfValue; + u32 outputLineFlag; + detail::OutputParam outputParamResult[1]; + int playerPriority; + }; + static_assert(sizeof(ResultParam) == 0x6c); + + struct FadeVolumeParam { + float stopFadeVolume; + float pauseFadeVolume; + float muteFadeVolume; + bool isMuted; + }; + static_assert(sizeof(FadeVolumeParam) == 0x10); + + SoundArchiveParam soundArchiveParam; + SoundPlayerParam soundPlayerParam; + Sound3DParam sound3DParam; + SoundActorParam soundActorParam; + SoundHandleParam soundHandleParam; + ResultParam resultParam; + FadeVolumeParam fadeVolumeParam; +}; +static_assert(sizeof(SoundParamCalculationValues) == 0x170); + +namespace detail { +class PlayerHeap; +class ExternalSoundPlayer; + +struct SoundActorParam { + float volume; + float pitch; + float tvVolume; + float tvPan; + float lpf; +#if NN_SDK_VER >= NN_MAKE_VER(4, 0, 0) + int biquadFilterType; + float biquadFilterValue; +#endif + + SoundActorParam(); + + void Reset(); +}; +#if NN_SDK_VER < NN_MAKE_VER(4, 0, 0) +static_assert(sizeof(SoundActorParam) == 0x14); +#else +static_assert(sizeof(SoundActorParam) == 0x1c); +#endif + +class BasicSound { +// TODO: implement rtti in atk::fnd +public: + enum PlayerState { + PlayerState_Init, + PlayerState_Play, + PlayerState_Stop, + }; + + struct AmbientParamUpdateCallback{}; + struct AmbientArgUpdateCallback{}; + struct AmbientArgAllocatorCallback{}; + + struct AmbientInfo { + AmbientParamUpdateCallback* paramUpdateCallback; + AmbientArgUpdateCallback* argUpdateCallback; + AmbientArgAllocatorCallback* argAllocatorCallback; + void* arg; + u64 argSize; + }; + static_assert(sizeof(AmbientInfo) == 0x28); + + BasicSound(); + virtual ~BasicSound(); + + void Update(); + void StartPrepared(); + + void Stop(int fadeFrames); + void ForceStop(); + + void Pause(bool flag, int fadeFrames); + void Pause(bool flag, int fadeFrames, PauseMode pauseMode); + void Mute(bool flag, int fadeFrames); + + void SetAutoStopCounter(int frames); + void FadeIn(int frames); + +#if NN_SDK_VER < NN_MAKE_VER(4, 0, 0) + virtual bool Initialize(); +#else + virtual bool Initialize(OutputReceiver* pOutputReceiver); +#endif + virtual void Finalize(); + + virtual bool IsPrepared() const = 0; + bool IsPause() const; + bool IsMute() const; + bool IsStarted() const; + + void SetPriority(int priority, int ambientPriority); + void GetPriority(int* priority, int* ambientPriority) const; + + void SetInitialVolume(float volume); + float GetInitialVolume() const; + + void SetVolume(float volume, int frames); + float GetVolume() const; + + void SetPitch(float pitch); + float GetPitch() const; + + void SetLpfFreq(float lpfFreq); + float GetLpfFreq() const; + + void SetBiquadFilter(int type, float value); + void GetBiquadFilter(int* type, float* value) const; + + void SetOutputLine(u32 lineFlag); + u32 GetOutputLine() const; + void ResetOutputLine(); + + void SetPlayerPriority(int priority); + void SetMixMode(MixMode mixMode); + MixMode GetMixMode(); + + void SetPan(float pan); + float GetPan() const; + + void SetSurroundPan(float span); + float GetSurroundPan() const; + + void SetMainSend(float send); + float GetMainSend() const; + + void SetFxSend(AuxBus bus, float send); + float GetFxSend(AuxBus bus) const; + + void SetSend(int bus, float send); + float GetSend(int bus) const; + + void SetVolumeThroughMode(int bus, u8 modeBitFlag); + u8 GetVolumeThroughMode(int bus); + bool IsVolumeThroughModeUsed() const; + + int GetSendBusCount(); + int GetSendChannelCount(); + + void SetPanMode(PanMode mode); + void SetPanCurve(PanCurve curve); +#if NN_SDK_VER >= NN_MAKE_VER(4, 0, 0) + void SetOutputAdditionalParamAddr(OutputDevice device, OutputAdditionalParam* addr, + OutputAdditionalParam* addrForPlayer); +#endif + void SetOutputVolume(OutputDevice device, float volume); + void SetOutputPan(OutputDevice device, float pan); + void SetOutputSurroundPan(OutputDevice device, float span); + void SetOutputMainSend(OutputDevice device, float send); + void SetOutputFxSend(OutputDevice device, AuxBus bus, float send); + void SetOutputFxSend(OutputDevice device, float send); + void SetOutputAdditionalSend(OutputDevice device, int bus, float send); + void SetOutputBusMixVolumeEnabled(OutputDevice device, int subMixBus, bool isEnabled); + void SetOutputBusMixVolume(OutputDevice device, int srcChNo, int subMixBus, ChannelMixVolume param); + void SetOutputChannelMixParameter(OutputDevice device, u32 srcChNo, MixParameter param); + void SetOutputVolumeThroughMode(OutputDevice device, int bus, u8 modeBitFlag); + + float GetOutputVolume(OutputDevice device) const; + float GetOutputPan(OutputDevice device) const; + float GetOutputSurroundPan(OutputDevice device) const; + float GetOutputMainSend(OutputDevice device) const; + float GetOutputFxSend(OutputDevice device, AuxBus bus) const; + float GetOutputFxSend(OutputDevice device) const; + bool IsOutputBusMixVolumeEnabled(OutputDevice device, int subMixBus) const; + ChannelMixVolume GetOutputBusMixVolume(OutputDevice device, int srcChNo, int subMixBus) const; + float GetOutputAdditionalSend(OutputDevice device, int bus) const; + u8 GetOutputVolumeThroughMode(OutputDevice device, int bus) const; + MixParameter GetOutputChannelMixParameter(OutputDevice, u32 srcChNo) const; + int GetRemainingFadeFrames() const; + int GetRemainingPauseFadeFrames() const; + int GetRemainingMuteFadeFrames() const; + int GetPlayerPriority() const; + + void CalculateSoundParamCalculationValues(SoundParamCalculationValues* pOutValue) const; + + void SetId(u32 id); + u32 GetId() const; + u32 GetInstanceId() const; + u32 GetPlayFrameCount() const; + + void SetSetupTick(const os::Tick& tick); + os::Tick GetSetupTick() const; + + int CalcCurrentPlayerPriority() const; + + SoundPlayer* GetSoundPlayer(); + const SoundPlayer* GetSoundPlayer() const; + void AttachSoundPlayer(SoundPlayer* player); + void DetachSoundPlayer(SoundPlayer* player); + + const SoundActor* GetSoundActor() const; + void AttachSoundActor(SoundActor* actor); + void DetachSoundActor(SoundActor* actor); + + void AttachExternalSoundPlayer(ExternalSoundPlayer* extPlayer); + void DetachExternalSoundPlayer(ExternalSoundPlayer* extPlayer); + + void AttachPlayerHeap(PlayerHeap* pHeap); + void DetachPlayerHeap(PlayerHeap* pHeap); + PlayerHeap* GetPlayerHeap(); + + void SetAmbientInfo(const AmbientInfo& ambientArgInfo); + void ClearAmbientArgUpdateCallback(); + void ClearAmbientParamUpdateCallback(); + void ClearAmbientArgAllocatorCallback(); + const SoundParam& GetAmbientParam() const; + static int GetAmbientPriority(const AmbientInfo& ambientInfo, u32 soundId); + + bool IsAttachedGeneralHandle(); + bool IsAttachedTempGeneralHandle(); + virtual bool IsAttachedTempSpecialHandle() = 0; + + void DetachGeneralHandle(); + void DetachTempGeneralHandle(); + virtual void DetachTempSpecialHandle() = 0; + + void SetUserParamBuffer(void* buffer, size_t size); + void* GetUserParam(); + + void SetSoundStopCallback(const SoundStopCallback callback); + void SetSoundArchive(const SoundArchive* soundArchive); + const SoundArchive* GetSoundArchive() const; + + enum PauseState { + PauseState_Normal, + PauseState_Pausing, + PauseState_Paused, + PauseState_Unpausing, + }; + PauseState GetPauseState() const; + + enum MuteState { + MuteState_Normal, + MuteState_Muting, + MuteState_Muted, + MuteState_Unmuting, + }; + MuteState GetMuteState() const; + +protected: + virtual driver::BasicSoundPlayer* GetBasicSoundPlayerHandle() = 0; + virtual void OnUpdatePlayerPriority(); + virtual void UpdateMoveValue(); + virtual void OnUpdateParam(); + + bool IsPlayerAvailable() const; + +private: + void UpdateParam(); + + void ClearIsFinalizedForCannotAllocatedResourceFlag(); + + float CalculateVolume() const; + float CalculatePitch() const; + float CalculateLpfFrequency() const; + void CalculateBiquadFilter(int* pOutBiquadType, float* pOutBiquadValue) const; + u32 CalculateOutLineFlag() const; + void CalculateOutputParam(OutputParam* pOutParam, OutputDevice device) const; + void CalculateOutputBusMixVolume(OutputBusMixVolume* pOutParam, OutputDevice device) const; + + void ApplyCommonParam(OutputParam& param) const; + + enum State { + State_Constructed, + State_Initialized, + State_Finalized, + State_Destructed, + }; + + PlayerHeap* m_pPlayerHeap; + SoundHandle* m_pGeneralHandle; + SoundHandle* m_pTempGeneralHandle; + SoundPlayer* m_pSoundPlayer; + SoundActor* m_pSoundActor; + ExternalSoundPlayer* m_pExtSoundPlayer; + SoundArchive* m_pSoundArchive; + AmbientInfo m_AmbientInfo; + SoundParam m_AmbientParam; + SoundActorParam m_ActorParam; + MoveValue m_FadeVolume; + MoveValue m_PauseFadeVolume; + MoveValue m_MuteFadeVolume; + bool m_StartFlag; + bool m_StartedFlag; + bool m_AutoStopFlag; + bool m_FadeOutFlag; + bool m_PlayerAvailableFlag; + bool m_UnPauseFlag; + PauseMode m_PauseMode; + u8 m_Priority; + s8 m_BiquadFilterType; + State m_State; + u8 m_PlayerState; + u8 m_PauseState; + u8 m_MuteState; + u8 m_Padding[1]; + s32 m_AutoStopCounter; + u32 m_UpdateCounter; + u32 m_PlayingCounter; + u32 m_Id; + u32 m_InstanceId; + os::Tick m_SetupTick; + f32 m_InitVolume; + f32 m_Pitch; + f32 m_LpfFreq; + f32 m_BiquadFilterValue; + u32 m_OutputLineFlag; +#if NN_SDK_VER >= NN_MAKE_VER(4, 0, 0) + OutputReceiver* m_pOutputReceiver; +#endif + + struct CommonParam { + MoveValue volume; + MixMode mixMode; + float pan; + float span; + float send[4]; + + void Initialize(); + void Update(); + + float GetVolume() const; + void SetVolume(float target, int frame); + + CommonParam(); + }; + static_assert(sizeof(CommonParam) == 0x2c); + + CommonParam m_CommonParam; + + OutputParam m_OutputParam[1]; +#if NN_SDK_VER >= NN_MAKE_VER(4, 0, 0) + OutputAdditionalParam* m_pOutputAdditionalParam[1]; +#endif + void* m_pUserParam; + size_t m_UserParamSize; + SoundStopCallback m_SoundStopCallback; + + static u32 g_LastInstanceId; + +public: + util::IntrusiveListNode m_SoundPlayerPlayLink; + util::IntrusiveListNode m_SoundPlayerPriorityLink; + util::IntrusiveListNode m_ExtSoundPlayerPlayLink; + +}; +#if NN_SDK_VER < NN_MAKE_VER(4, 0, 0) +static_assert(sizeof(BasicSound) == 0x1f0); +#else +static_assert(sizeof(BasicSound) == 0x210); +#endif +} // namespace detail +} // namespace nn::atk diff --git a/include/nn/atk/atk_BasicSoundPlayer.h b/include/nn/atk/atk_BasicSoundPlayer.h new file mode 100644 index 00000000..71e4baf1 --- /dev/null +++ b/include/nn/atk/atk_BasicSoundPlayer.h @@ -0,0 +1,63 @@ +#pragma once + +#include + +#include +#include +#include + +namespace nn::atk::detail::driver { +struct PlayerParamSet { + + void Initialize(); + + f32 volume; + f32 pitch; + f32 lpfFreq; + f32 biquadValue; + s8 biquadType; + PanMode panMode; + PanCurve panCurve; + u32 outputLineFlag; + OutputParam tvParam; +}; +static_assert(sizeof(PlayerParamSet) == 0x70); + +class BasicSoundPlayer { +public: + virtual ~BasicSoundPlayer(); +#if NN_SDK_VER < NN_MAKE_VER(4, 0, 0) + virtual bool Initialize(); +#else + virtual bool Initialize(OutputReceiver* pOutputReceiver); +#endif + virtual void Finalize(); + virtual void Start() = 0; + virtual void Stop() = 0; + virtual void Pause(bool isPauseEnabled) = 0; + + BasicSoundPlayer(); + void SetBiquadFilter(s32 type, f32 value); + +private: + os::Event m_Event; +#if NN_SDK_VER >= NN_MAKE_VER(4, 0, 0) + OutputReceiver* m_pOutputReceiver; +#endif + bool m_ActiveFlag; + bool m_StartedFlag; + bool m_PauseFlag; + bool m_FinishFlag; + bool m_IsFinalizedForCannotAllocateResource; + PlayerParamSet m_PlayerParamSet; +#if NN_SDK_VER >= NN_MAKE_VER(4, 0, 0) + OutputAdditionalParam* m_pTvAdditionalParam; +#endif + PlayerHeapDataManager* m_pPlayerHeapDataManager; +}; +#if NN_SDK_VER < NN_MAKE_VER(4, 0, 0) +static_assert(sizeof(BasicSoundPlayer) == 0xb0); +#else +static_assert(sizeof(BasicSoundPlayer) == 0xc0); +#endif +} //namespace nn::atk::detail::driver \ No newline at end of file diff --git a/include/nn/atk/atk_BinaryFileFormat.h b/include/nn/atk/atk_BinaryFileFormat.h new file mode 100644 index 00000000..a1c38709 --- /dev/null +++ b/include/nn/atk/atk_BinaryFileFormat.h @@ -0,0 +1,26 @@ +#pragma once + +#include + +namespace nn::atk::detail { +struct BinaryFileHeader { + s32 signature; + u16 byteOrder; + u16 headerSize; + u32 version; + u32 fileSize; + u16 dataBlocks; + u16 reserved; + + static const u16 ValidByteOrderMark {0xFEFF}; +}; +static_assert(sizeof(BinaryFileHeader) == 0x14); + +struct BinaryBlockHeader { + // Named "kind", but functionally the same as BinaryFileHeader.signature + s32 kind; + u32 size; +}; +static_assert(sizeof(BinaryBlockHeader) == 0x8); + +} // namespace nn::atk::detail \ No newline at end of file diff --git a/include/nn/atk/atk_BiquadFilterCallback.h b/include/nn/atk/atk_BiquadFilterCallback.h new file mode 100644 index 00000000..1647e9ba --- /dev/null +++ b/include/nn/atk/atk_BiquadFilterCallback.h @@ -0,0 +1,15 @@ +#pragma once + +#include + +namespace nn::atk { +class BiquadFilterCallback { +public: + using Coefficients = BiquadFilterCoefficients; + + virtual ~BiquadFilterCallback(); + + virtual void GetCoefficients(Coefficients* pOutValue, s32 type, f32 value) = 0; +}; +static_assert(sizeof(BiquadFilterCallback) == 0x8); +} \ No newline at end of file diff --git a/include/nn/atk/atk_BiquadFilterPresets.h b/include/nn/atk/atk_BiquadFilterPresets.h new file mode 100644 index 00000000..7d0a67cf --- /dev/null +++ b/include/nn/atk/atk_BiquadFilterPresets.h @@ -0,0 +1,128 @@ +#pragma once + +#include + +namespace nn::atk::detail { +class BiquadFilterLpf : BiquadFilterCallback { +public: + constexpr static u32 CoefficientsTableSize = 112; + + static Coefficients CoefficientsTable32000[CoefficientsTableSize]; + + ~BiquadFilterLpf() override; + + void GetCoefficients(Coefficients* pOutValue, s32 type, f32 value) override; +}; +static_assert(sizeof(BiquadFilterLpf) == 0x8); + +class BiquadFilterHpf : BiquadFilterCallback { +public: + constexpr static u32 CoefficientsTableSize = 97; + + static Coefficients CoefficientsTable32000[CoefficientsTableSize]; + + ~BiquadFilterHpf() override; + + void GetCoefficients(Coefficients* pOutValue, s32 type, f32 value) override; +}; +static_assert(sizeof(BiquadFilterHpf) == 0x8); + +class BiquadFilterBpf512 : BiquadFilterCallback { +public: + constexpr static u32 CoefficientsTableSize = 122; + + static Coefficients CoefficientsTable32000[CoefficientsTableSize]; + + ~BiquadFilterBpf512() override; + + void GetCoefficients(Coefficients* pOutValue, s32 type, f32 value) override; +}; +static_assert(sizeof(BiquadFilterBpf512) == 0x8); + +class BiquadFilterBpf1024 : BiquadFilterCallback { +public: + constexpr static u32 CoefficientsTableSize = 93; + + static Coefficients CoefficientsTable32000[CoefficientsTableSize]; + + ~BiquadFilterBpf1024() override; + + void GetCoefficients(Coefficients* pOutValue, s32 type, f32 value) override; +}; +static_assert(sizeof(BiquadFilterBpf1024) == 0x8); + +class BiquadFilterBpf2048 : BiquadFilterCallback { +public: + constexpr static u32 CoefficientsTableSize = 93; + + static Coefficients CoefficientsTable32000[CoefficientsTableSize]; + + ~BiquadFilterBpf2048() override; + + void GetCoefficients(Coefficients* pOutValue, s32 type, f32 value) override; +}; +static_assert(sizeof(BiquadFilterBpf2048) == 0x8); + +class BiquadFilterLpfNw4fCompatible48k : BiquadFilterCallback { +public: + static Coefficients CoefficientsTable48000[BiquadFilterLpf::CoefficientsTableSize]; + + ~BiquadFilterLpfNw4fCompatible48k() override; + + void GetCoefficients(Coefficients* pOutValue, s32 type, f32 value) override; +}; +static_assert(sizeof(BiquadFilterLpfNw4fCompatible48k) == 0x8); + +class BiquadFilterHpfNw4fCompatible48k : BiquadFilterCallback { +public: + static Coefficients CoefficientsTable48000[BiquadFilterHpf::CoefficientsTableSize]; + + ~BiquadFilterHpfNw4fCompatible48k() override; + + void GetCoefficients(Coefficients* pOutValue, s32 type, f32 value) override; +}; +static_assert(sizeof(BiquadFilterHpfNw4fCompatible48k) == 0x8); + +class BiquadFilterBpf512Nw4fCompatible48k : BiquadFilterCallback { +public: + static Coefficients CoefficientsTable48000[BiquadFilterBpf512::CoefficientsTableSize]; + + ~BiquadFilterBpf512Nw4fCompatible48k() override; + + void GetCoefficients(Coefficients* pOutValue, s32 type, f32 value) override; +}; +static_assert(sizeof(BiquadFilterBpf512Nw4fCompatible48k) == 0x8); + +class BiquadFilterBpf1024Nw4fCompatible48k : BiquadFilterCallback { +public: + static Coefficients CoefficientsTable48000[BiquadFilterBpf1024::CoefficientsTableSize]; + + ~BiquadFilterBpf1024Nw4fCompatible48k() override; + + void GetCoefficients(Coefficients* pOutValue, s32 type, f32 value) override; +}; +static_assert(sizeof(BiquadFilterBpf1024Nw4fCompatible48k) == 0x8); + +class BiquadFilterBpf2048Nw4fCompatible48k : BiquadFilterCallback { +public: + static Coefficients CoefficientsTable48000[BiquadFilterBpf2048::CoefficientsTableSize]; + + ~BiquadFilterBpf2048Nw4fCompatible48k() override; + + void GetCoefficients(Coefficients* pOutValue, s32 type, f32 value) override; +}; +static_assert(sizeof(BiquadFilterBpf2048Nw4fCompatible48k) == 0x8); + +namespace driver { +static BiquadFilterLpf BiquadFilterInstanceLpf {}; +static BiquadFilterHpf BiquadFilterInstanceHpf {}; +static BiquadFilterBpf512 BiquadFilterInstanceBpf512 {}; +static BiquadFilterBpf1024 BiquadFilterInstanceBpf1024 {}; +static BiquadFilterBpf2048 BiquadFilterInstanceBpf2048 {}; +static BiquadFilterLpfNw4fCompatible48k BiquadFilterInstanceLpfNw4fCompatible48k {}; +static BiquadFilterHpfNw4fCompatible48k BiquadFilterInstanceHpfNw4fCompatible48k {}; +static BiquadFilterBpf512Nw4fCompatible48k BiquadFilterInstanceBpf512Nw4fCompatible48k {}; +static BiquadFilterBpf1024Nw4fCompatible48k BiquadFilterInstanceBpf1024Nw4fCompatible48k {}; +static BiquadFilterBpf2048Nw4fCompatible48k BiquadFilterInstanceBpf2048Nw4fCompatible48k {}; +}; +} // namespace nn::atk::detail \ No newline at end of file diff --git a/include/nn/atk/atk_BusMixVolumePacket.h b/include/nn/atk/atk_BusMixVolumePacket.h new file mode 100644 index 00000000..f40aa7e7 --- /dev/null +++ b/include/nn/atk/atk_BusMixVolumePacket.h @@ -0,0 +1,60 @@ +#pragma once + +#include + +namespace nn::atk::detail { +struct BusMixVolumePacket { +public: + static size_t GetRequiredMemSize(int busCount); + + BusMixVolumePacket(); + + bool Initialize(void* buffer, size_t size, int busCount); + void Finalize(); + + void Reset(); + + int GetBusCount() const { + return m_BusCount; + } + + float GetBusMixVolume(int waveChannel, int mixChannel) const { + return m_BusMixVolume.volume[waveChannel][mixChannel]; + } + + const OutputBusMixVolume& GetBusMixVolume() const { + return m_BusMixVolume; + } + + void SetBusMixVolume(int waveChannel, int mixChannel, float volume) { + m_BusMixVolume.volume[waveChannel][mixChannel] = volume; + } + + void SetBusMixVolume(const OutputBusMixVolume& busMixVolume) { + m_BusMixVolume = busMixVolume; + } + + bool IsUsed() const { + return m_IsUsed; + } + + void SetUsed(bool isUsed) { + m_IsUsed = isUsed; + } + + bool IsEnabled(int bus) const { + return m_pIsEnabledTable[bus]; + } + + void SetEnabled(int bus, bool isEnabled) { + m_pIsEnabledTable[bus] = isEnabled; + } + +private: + OutputBusMixVolume m_BusMixVolume; + bool m_IsUsed; + bool* m_pIsEnabledTable; + int m_BusCount; +}; +static_assert(sizeof(BusMixVolumePacket) == 0xd8); +} // namespace nn::atk::detail \ No newline at end of file diff --git a/include/nn/atk/atk_Channel.h b/include/nn/atk/atk_Channel.h new file mode 100644 index 00000000..1213a045 --- /dev/null +++ b/include/nn/atk/atk_Channel.h @@ -0,0 +1,157 @@ +#pragma once + +#include +#include +#include +#include +#include +#include + +namespace nn::atk::detail::driver { +class ChannelManager; + +class Channel { +public: + enum ChannelCallbackStatus { + ChannelCallbackStatus_Stopped, + ChannelCallbackStatus_Drop, + ChannelCallbackStatus_Finish, + ChannelCallbackStatus_Cancel, + }; + + enum LfoTarget { + LfoTarget_Pitch, + LfoTarget_Volume, + LfoTarget_Pan, + LfoTarget_Invalid = 0xff, + }; + + using ChannelCallback = void(*)(Channel*,ChannelCallbackStatus,void*); + + constexpr static u32 ChannelCount = 97; + constexpr static u32 ChannelMin = 0; + constexpr static u32 ChannelMax = 96; + + constexpr static u32 PriorityRelease = 1; + + constexpr static u32 KeyInit = 60; + constexpr static u32 OriginalKeyInit = KeyInit; + + constexpr static u8 SilenceVolumeMax = 255; + constexpr static u8 SilenceVolumeMin = 0; + + constexpr static u32 WaveBufferMax = 2; + + constexpr static u32 ModCount = 4; + + constexpr static f32 SilenceVolumeMaxR = 1.0 / SilenceVolumeMax; + + class Disposer : DisposeCallback { + public: + ~Disposer() override; + + void InvalidateData(const void* start, const void* end) override; + + private: + Channel* m_pChannel; + }; + + Channel(); + ~Channel(); + + void InitParam(ChannelCallback callback, void* callbackData); + + void Update(bool doPeriodicProc); + void Stop(); + + s32 GetSweepValue() const; + void UpdateSweep(s32 count); + + void Start(const WaveInfo& waveInfo, s32 length, position_t startOffsetSamples); + void Start(const WaveInfo& waveInfo, s32 length, position_t startOffsetSamples, + bool isContextCalculationSkipMode); + + void AppendWaveBuffer(const WaveInfo& waveInfo, position_t startOffsetSamples); + void AppendWaveBuffer(const WaveInfo& waveInfo, position_t startOffsetSamples, + bool isContextCalculationSkipMode); + + void Release(); + + void NoteOff(); + + void SetSweepParam(f32 sweepPitch, s32 sweepTime, bool autoUpdate); + void SetBiquadFilter(s32 type, f32 value); +#if NN_SDK_VER >= NN_MAKE_VER(4, 0, 0) + void SetTvAdditionalParam(OutputAdditionalParam* param); +#endif + + position_t GetCurrentPlayingSample(bool) const; + + static void VoiceCallbackFunc(MultiVoice* voice, + MultiVoice::VoiceCallbackStatus status, void* arg); + + void CallChannelCallback(ChannelCallbackStatus status); + + static Channel* AllocChannel(s32 voiceChannelCount, s32 priority, + ChannelCallback callback, void* callbackData); + + void FreeChannel(); + void DetachChannel(); + +private: + friend ChannelManager; + + Disposer m_Disposer; + CurveAdshr m_CurveAdshr; + CurveLfo m_Lfo[4]; + u8 m_LfoTarget[4]; + u8 m_PauseFlag; + u8 m_ActiveFlag; + u8 m_AllocFlag; + u8 m_AutoSweep; + u8 m_ReleasePriorityFixFlag; + u8 m_IsIgnoreNoteOff; + u8 m_BiquadType; + u8 m_Padding[1]; + f32 m_UserVolume; + f32 m_UserPitchRatio; + f32 m_UserLpfFreq; + f32 m_BiquadValue; + u32 m_OutputLineFlag; + OutputParam m_TvParam; +#if NN_SDK_VER >= NN_MAKE_VER(4, 0, 0) + detail::OutputAdditionalParam* m_pTvAdditionalParam; +#endif + f32 m_UserPitch; + f32 m_SweepPitch; + s32 m_SweepCounter; + s32 m_SweepLength; + f32 m_InitPan; + f32 m_InitSurroundPan; + f32 m_Tune; + MoveValue m_SilenceVolume; + f32 m_Cent; + f32 m_CentPitch; + s32 m_Length; + PanMode m_PanMode; + PanCurve m_PanCurve; + u8 m_Key; + u8 m_OriginalKey; + u8 m_KeyGroupId; + u8 m_InterpolationType; + f32 m_InstrumentVolume; + f32 m_Velocity; + ChannelCallback m_Callback; + void* m_CallbackData; + MultiVoice* m_pVoice; + Channel* m_pNextLink; + WaveBuffer m_WaveBuffer[2][2]; + AdpcmContext m_AdpcmContext[2]; + AdpcmContext m_AdpcmLoopContext[2]; + position_t m_LoopStartFrame; + position_t m_OriginalLoopStartFrame; + bool m_LoopFlag; + util::IntrusiveListNode m_Link; +}; +static_assert(sizeof(Channel) == 0x400); +} // namespace nn::atk::detail::driver \ No newline at end of file diff --git a/include/nn/atk/atk_ChannelManager.h b/include/nn/atk/atk_ChannelManager.h new file mode 100644 index 00000000..2d4b41a0 --- /dev/null +++ b/include/nn/atk/atk_ChannelManager.h @@ -0,0 +1,45 @@ +#pragma once + +#include +#include + +namespace nn::atk::detail::driver { +class ChannelManager { +public: + using ChannelPool = InstancePool; + using ChannelList = util::IntrusiveList>; + + ChannelManager(); + + size_t GetObjectSize(const SoundInstanceConfig& config); + size_t GetRequiredMemSize(s32 channelCount, const SoundInstanceConfig& config); + + void Initialize(void* mem, size_t memSize, s32 channelCount, const SoundInstanceConfig& config); + void Finalize(); + + void Free(Channel* channel); + + Channel* Alloc(); + + void UpdateAllChannel(); + void UpdateAudioFrameChannel(); + +private: + ChannelPool m_Pool; + ChannelList m_ChannelList; + bool m_IsInitialized; + s32 m_ChannelCount; +#if NN_SDK_VER >= NN_MAKE_VER(4, 0, 0) + InstancePool* m_pAdditionalParamPool; + BufferPool* m_pAdditionalParamBufferPool; + size_t m_AdditionalParamBufferSizePerChannel; + SoundInstanceConfig m_SoundInstanceConfig; +#endif +}; +#if NN_SDK_VER < NN_MAKE_VER(4, 0, 0) +static_assert(sizeof(ChannelManager) == 0x30); +#else +static_assert(sizeof(ChannelManager) == 0x50); +#endif +} // namespace nn::atk::detail::driver \ No newline at end of file diff --git a/include/nn/atk/atk_ChannelMixVolume.h b/include/nn/atk/atk_ChannelMixVolume.h new file mode 100644 index 00000000..d2b081e3 --- /dev/null +++ b/include/nn/atk/atk_ChannelMixVolume.h @@ -0,0 +1,29 @@ +#pragma once + +#include + +namespace nn::atk { +class ChannelMixVolume { +public: + static const int ChannelCountMax = 24; + + ChannelMixVolume(); + explicit ChannelMixVolume(const MixVolume& mixVolume); + ChannelMixVolume(const float* pVolume, int volumeCount); + + bool SetChannelCount(int channelCount); + int GetChannelCount() const; + + bool SetChannelVolume(int channelIndex, float volume); + float GetChannelVolume(int channelIndex) const; + + bool SetChannelVolume(int channelIndex, const float* pVolume, int volumeCount); + + void InitializeChannelVolume(); + +private: + int m_ChannelCount; + float m_ChannelVolume[ChannelCountMax]; +}; +static_assert(sizeof(ChannelMixVolume) == 0x64); +} // namespace nn::atk \ No newline at end of file diff --git a/include/nn/atk/atk_Command.h b/include/nn/atk/atk_Command.h new file mode 100644 index 00000000..05134198 --- /dev/null +++ b/include/nn/atk/atk_Command.h @@ -0,0 +1,13 @@ +#pragma once + +#include + +namespace nn::atk::detail { +struct Command { + Command* next; + u32 id; + u32 tag; + std::uintptr_t memory_next; +}; +static_assert(sizeof(Command) == 0x18); +} // namespace nn::atk::detail \ No newline at end of file diff --git a/include/nn/atk/atk_CommandManager.h b/include/nn/atk/atk_CommandManager.h new file mode 100644 index 00000000..dd49a138 --- /dev/null +++ b/include/nn/atk/atk_CommandManager.h @@ -0,0 +1,96 @@ +#pragma once + +#include +#include + +#include + +namespace nn::atk::detail { +class CommandBuffer { +public: + CommandBuffer(); + ~CommandBuffer(); + + void Finalize(); + + void Initialize(void* buffer, size_t bufferSize); + + void* AllocMemory(size_t size); + + void FreeMemory(Command* command); + + size_t GetCommandBufferSize() const; + size_t GetAllocatableCommandSize() const; + size_t GetAllocatedCommandBufferSize() const; + +private: + u32* m_CommandMemoryArea; + size_t m_CommandMemoryAreaSize; + std::uintptr_t m_CommandMemoryAreaBegin; + std::uintptr_t m_CommandMemoryAreaEnd; + bool m_CommandMemoryAreaZeroFlag; +}; +static_assert(sizeof(CommandBuffer) == 0x28); + +class CommandManager { +public: + using ProcessCommandListFunc = void(*)(Command*); + using RequestProcessCommandFunc = void(*)(); + + constexpr static s32 SendCommandQueueCount = 32; + constexpr static s32 RecvCommandQueueCount = 33; + constexpr static s32 InvalidCommand = -1; + + CommandManager(); + ~CommandManager(); + + void Finalize(); + void Initialize(void* commandBuffer, size_t commandBufferSize, ProcessCommandListFunc func); + + void* AllocMemory(size_t size, bool forceProcessCommandFlag); + + bool TryAllocMemory(size_t size); + + void RecvCommandReply(); + + u32 FlushCommand(bool forceFlag, bool forceProcessCommandFlag); + + void WaitCommandReply(u32 tag); + + void RecvCommandReplySync(); + + u32 PushCommand(Command* command); + u32 FlushCommand(bool forceFlag); + + void FinalizeCommandList(Command* command); + + bool IsFinishCommand(u32) const; + + size_t GetCommandBufferSize() const; + size_t GetAllocatableCommandSize() const; + size_t GetAllocatedCommandBufferSize() const; + + s32 GetAllocatedCommandCount() const; + + bool ProcessCommand(); + +private: + bool m_Available; + ProcessCommandListFunc m_pProcessCommandListFunc; + RequestProcessCommandFunc m_pRequestProcessCommandFunc; + os::MessageQueueType m_SendCommandQueue; + std::uintptr_t m_SendCommandQueueBuffer[SendCommandQueueCount]; + bool m_IsInitializedSendMessageQueue; + os::MessageQueueType m_RecvCommandQueue; + std::uintptr_t m_RecvCommandQueueBuffer[RecvCommandQueueCount]; + bool m_IsInitializedRecvMessageQueue; + Command* m_CommandListBegin; + Command* m_CommandListEnd; + std::atomic_int m_CommandListCount; + u32 m_CommandTag; + u32 m_FinishCommandTag; + CommandBuffer m_CommandBuffer; + s32 m_AllocatedCommandCount; +}; +static_assert(sizeof(CommandManager) == 0x310); +} // namespace nn::atk::detail \ No newline at end of file diff --git a/include/nn/atk/atk_Config.h b/include/nn/atk/atk_Config.h new file mode 100644 index 00000000..6c47205f --- /dev/null +++ b/include/nn/atk/atk_Config.h @@ -0,0 +1,10 @@ +#pragma once + +#include + +#include + +namespace nn::atk { +using position_t = detail::fnd::position_t; + +} // namespace nn::atk \ No newline at end of file diff --git a/include/nn/atk/atk_CurveAdshr.h b/include/nn/atk/atk_CurveAdshr.h new file mode 100644 index 00000000..ac5c78a9 --- /dev/null +++ b/include/nn/atk/atk_CurveAdshr.h @@ -0,0 +1,61 @@ +#pragma once + +#include + +namespace nn::atk::detail { + +class CurveAdshr { +public: + enum Status { + Status_Attack, + Status_Hold, + Status_Decay, + Status_Sustain, + Status_Release, + }; + + static const float VolumeInit; + static const int AttackInit = 127; + static const int HoldInit = 0; + static const int DecayInit = 127; + static const int SustainInit = 127; + static const int ReleaseInit = 127; + + static const int DecibelSquareTableSize = 128; + static const int CalcDecibelScaleMax = 127; + + static const s16 DecibelSquareTable[DecibelSquareTableSize]; + + CurveAdshr(); + + void Initialize(float initDecibel); + void Reset(float initDecibel); + void Update(int msec); + float GetValue() const; + + Status GetStatus() const { return m_Status; } + void SetStatus(Status status) { m_Status = status; } + + void SetAttack(int attack); + void SetHold(int hold); + void SetDecay(int decay); + void SetSustain(int sustain); + void SetRelease(int release); + + static float CalcRelease(int release); + static s16 CalcDecibelSquare(int scale); + +private: + Status m_Status; + float m_Value; + float m_Decay; + float m_Release; + float m_Attack; + u16 m_Hold; + u16 m_HoldCounter; + u8 m_Sustain; + u8 m_Padding[3]; +}; +static_assert(sizeof(CurveAdshr) == 0x1c); + +} // namespace nn::atk::detail \ No newline at end of file diff --git a/include/nn/atk/atk_CurveLfo.h b/include/nn/atk/atk_CurveLfo.h new file mode 100644 index 00000000..42d26daf --- /dev/null +++ b/include/nn/atk/atk_CurveLfo.h @@ -0,0 +1,69 @@ +#pragma once + +#include + +namespace nn::atk::detail { + +struct CurveLfoParam { + float depth; + float speed; + u32 delay; + u8 range; + u8 curve; + u8 phase; + u8 padding[1]; + + enum CurveType { + CurveType_Min, + + CurveType_Sine = CurveType_Min, + CurveType_Triangle, + CurveType_Saw, + CurveType_Square, + CurveType_Random, + + CurveType_UserMin = 64, + CurveType_Max = 127, + CurveType_UserMax = CurveType_Max, + + CurveType_Count, + }; + + CurveLfoParam() = default; + + void Initialize(); +}; +static_assert(sizeof(CurveLfoParam) == 0x10); + +class CurveLfo { +public: + CurveLfo() = default; + + void Reset(); + void Update(int msec); + + float GetValue() const; + + void SetParam(const CurveLfoParam& param) { m_Param = param; } + CurveLfoParam& GetParam() { return m_Param; } + const CurveLfoParam& GetParam() const { return m_Param; } + + static void InitializeCurveTable(); + + using CurveFunc = float (*)(float); + + static CurveFunc RegisterUserCurve(CurveFunc func, u32 index); + static CurveFunc UnregisterUserCurve(u32 index); + +private: + CurveLfoParam m_Param; + u32 m_DelayCounter; + float m_Counter; + mutable float m_RandomValue; + bool m_IsStart; + bool m_IsNext; + [[maybe_unused]] u8 m_Padding[2]; +}; +static_assert(sizeof(CurveLfo) == 0x20); + +} // namespace nn::atk::detail \ No newline at end of file diff --git a/include/nn/atk/atk_Debug.h b/include/nn/atk/atk_Debug.h new file mode 100644 index 00000000..ea034b6b --- /dev/null +++ b/include/nn/atk/atk_Debug.h @@ -0,0 +1,30 @@ +#pragma once + +#include + +namespace nn::atk { +enum DebugWarningFlag { + DebugWarningFlag_NotEnoughInstance, + DebugWarningFlag_NotEnoughSeqsound, + DebugWarningFlag_NotEnoughStrmsound, + DebugWarningFlag_NotEnoughWavesound, + DebugWarningFlag_NotEnoughSeqtrack, + DebugWarningFlag_NotEnoughStrmchannel, +}; + +void Debug_SetWarningFlag(DebugWarningFlag warning, bool enable); + +namespace detail { +enum DebugSoundType { + DebugSoundType_Seqsound, + DebugSoundType_Strmsound, + DebugSoundType_Wavesound, +}; + +bool Debug_GetWarningFlag(DebugWarningFlag warning); +DebugWarningFlag Debug_GetDebugWarningFlagFromSoundType(DebugSoundType type); +const char* Debug_GetSoundTypeString(DebugSoundType type); + +using DebugLogFunc = void(*)(char*); +} // namespace nn::atk::detail +} // namespace nn::atk \ No newline at end of file diff --git a/include/nn/atk/atk_DeviceOutRecorder.h b/include/nn/atk/atk_DeviceOutRecorder.h new file mode 100644 index 00000000..f7414cec --- /dev/null +++ b/include/nn/atk/atk_DeviceOutRecorder.h @@ -0,0 +1,201 @@ +#pragma once + +#include + +#include +#include +#include + +#include +#include +#include + +namespace nn::atk { +class DeviceOutRecorder : public detail::fnd::Thread::Handler { + NN_NO_COPY(DeviceOutRecorder); + +public: + static const u32 RecordingBufferSize = 0x5a000; + static const u32 DefaultWriteBlockPerSamples = 0x10000; + static const u32 RequiredThreadStackSize = 0x10000; + + enum State { + State_NotInitialized, + State_Initialized, + State_Recording, + State_Recorded, + }; + + enum Message { + Message_Prepare, + Message_WriteSamples, + Message_RequestStop, + Message_Exit, + }; + + class InitializationOptions { + public: + InitializationOptions() = default; + + u32 GetPrioritiy() const { return m_Priority; } + void SetPriority(u32 value) { m_Priority = value; } + + int GetIdealCoreNumber() const { return m_IdealCoreNumber; } + void SetIdealCoreNumber(int value) { m_IdealCoreNumber = value; } + + private: + u32 m_Priority; + int m_IdealCoreNumber; + }; + static_assert(sizeof(InitializationOptions) == 0x8); + + class RecordingOptions { + public: + RecordingOptions() = default; + + u32 GetChannels() const { return m_Channels; } + void SetChannels(u32 value) { m_Channels = value; } + + bool IsLeadSilenceTrimmingEnabled() const { return m_IsLeadSilenceTrimmingEnabled; } + void SetLeadSilenceTrimmingEnabled(bool value) { m_IsLeadSilenceTrimmingEnabled = value; } + + u32 GetMaxFrames() const { return m_MaxFrames; } + void SetMaxFrames(u32 value) { m_MaxFrames = value; } + + u32 GetWriteBlockPerSamples() const { return m_WriteBlockPerSamples; } + void SetWriteBlockPerSamples(u32 value) { m_WriteBlockPerSamples = value; } + + private: + u32 m_Channels; + bool m_IsLeadSilenceTrimmingEnabled; + u32 m_MaxFrames; + u32 m_WriteBlockPerSamples; + }; + static_assert(sizeof(RecordingOptions) == 0x10); + + class RecorderBuffer { + struct WriteState { + u32 channelIndex; + u32 writtenSampleCount; + }; + static_assert(sizeof(WriteState) == 0x8); + + explicit RecorderBuffer(const char* deviceName); + + void Initialize(s16* sampleBuffer, u32 maxSamples); + void Finalize(); + + u32 Push(const s16* sampleBuffer, u32 samples); + u32 Pop(u32 samples); + s16* Peek(); + + void SetReadBlockSamples(u32 value); + void Clear(); + + u32 GetReadableCount() const; + u32 GetWritableCount() const; + u32 GetContiguousReadableCount() const; + const char* GetDeviceName() const; + + void UpdateMaxSamples(); + + private: + void Skip(u32 samples); + void Write(const s16* sampleBuffer, u32 samples); + u32 IncrementPosition(u32 position, u32 length) const; + + s16* m_SampleBuffer {nullptr}; + u32 m_MaxBufferSamples {0}; + u32 m_MaxSamples {0}; + std::atomic_uint m_ValidSamples {0}; + u32 m_ReadPosition {0}; + u32 m_WritePosition {0}; + u32 m_ReadBlockSamples {1}; + WriteState m_WriteState; + const char* m_DeviceName; + }; + static_assert(sizeof(RecorderBuffer) == 0x30); + +protected: + explicit DeviceOutRecorder(const char* deviceName); + +public: + ~DeviceOutRecorder() override; + + bool Initialize(void* recordingBuffer, size_t recordingBufferSize, + void* pThreadStack, size_t threadStackSize); + bool Initialize(void* recordingBuffer, size_t recordingBufferSize, + void* pThreadStack, size_t threadStackSize, const InitializationOptions& options); + + void Finalize(); + + size_t GetRequiredMemorySizeForRecording(); + + bool Start(detail::fnd::FileStream& fileStream); + bool Start(detail::fnd::FileStream& fileStream, const RecordingOptions& options); + + void Stop(bool isBlocking); + + bool IsInitialized() const; + + State GetState() const; + + bool IsLeadSilenceTrimming() const; + + u32 GetRecordingChannels() const; + + void RecordSamples(const s16* sampleBuffer, u32 samples); + +protected: + OutputMode GetOutputMode() const; + + virtual u32 GetMaxFrameLength() const = 0; + virtual u32 GetSamplesPerSec() const = 0; + virtual u32 GetValidChannels() const = 0; + + virtual void OnStart(); + virtual void OnStop(); + virtual u32 OnProcessSamples(s16* sampleBuffer, u32 samples); + + s16 ResolveSampleEndian(s16 sample); + + u32 Run(void* param) override; + +private: + s32 GetReadBlockSamples(u32 channels) const; + u32 GetLeadSilenceSamples(const s16* sampleBuffer, u32 samples, u32 channels) const; + u32 GetWritableSamples(u32 samples) const; + bool IsNoMoreSamples() const; + + bool StartThread(u32, s32); + void StopThread(); + + s32 Prepare(); + bool SendMessage(Message message); + bool PostMessage(Message message); + + s32 OnPrepare(); + void OnRequestStop(); + void OnExit(); + bool OnWriteSamples(bool isForceWriteMode); + + volatile State m_State {State_NotInitialized}; + u32 m_Channels {0}; + OutputMode m_OutputMode {OutputMode_Stereo}; + bool m_IsLeadSilenceTrimming {false}; + u32 m_MaxSamples {0}; + u32 m_WrittenSamples {0}; + detail::fnd::Thread m_Thread; + void* m_ThreadStack; + os::MessageQueue m_MessageQueue; + std::uintptr_t m_Message; + s32 m_MessageResult; + os::Event m_MessageDoneEvent; + detail::fnd::FileStream* m_Stream; + detail::WavOutFileStream m_WavOutStream; + RecorderBuffer m_RecordingBuffer; + util::BytePtr m_WorkBuffer; + u32 m_WriteBlockPerSamples; +}; +static_assert(sizeof(DeviceOutRecorder) == 0x310); +} // namespace nn::atk \ No newline at end of file diff --git a/include/nn/atk/atk_DisposeCallback.h b/include/nn/atk/atk_DisposeCallback.h new file mode 100644 index 00000000..a71f557a --- /dev/null +++ b/include/nn/atk/atk_DisposeCallback.h @@ -0,0 +1,19 @@ +#pragma once + +#include + +namespace nn::atk::detail::driver { +class DisposeCallback { +public: + virtual ~DisposeCallback() = default; + + virtual void InvalidateData(const void* start, const void* end) = 0; + +private: + friend class DisposeCallbackManager; + + util::IntrusiveListNode m_DisposeLink; +}; +static_assert(sizeof(DisposeCallback) == 0x18); + +} // namespace nn::atk::detail::driver \ No newline at end of file diff --git a/include/nn/atk/atk_DisposeCallbackManager.h b/include/nn/atk/atk_DisposeCallbackManager.h new file mode 100644 index 00000000..0fa9e60c --- /dev/null +++ b/include/nn/atk/atk_DisposeCallbackManager.h @@ -0,0 +1,27 @@ +#pragma once + +#include + +#include + +namespace nn::atk::detail::driver { +class DisposeCallbackManager { +public: + using CallbackList = util::IntrusiveList>; + + DisposeCallbackManager(); + + void RegisterDisposeCallback(DisposeCallback* callback); + void UnregisterDisposeCallback(DisposeCallback* callback); + + u64 GetCallbackCount() const; + + void Dispose(const void* mem, size_t size); + +private: + CallbackList m_CallbackList; +}; +static_assert(sizeof(DisposeCallbackManager) == 0x10); +} // namespace nn::atk::detail \ No newline at end of file diff --git a/include/nn/atk/atk_DriverCommand.h b/include/nn/atk/atk_DriverCommand.h new file mode 100644 index 00000000..1450a0a7 --- /dev/null +++ b/include/nn/atk/atk_DriverCommand.h @@ -0,0 +1,529 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include + +namespace nn::atk::detail { +enum DriverCommandId { + DriverCommandId_Dummy, + DriverCommandId_Debug, + DriverCommandId_Reply, + DriverCommandId_PlayerInit, + DriverCommandId_PlayerPanmode, + DriverCommandId_PlayerPancurve, + DriverCommandId_PlayerFinalize, + DriverCommandId_PlayerStart, + DriverCommandId_PlayerStop, + DriverCommandId_PlayerPause, + DriverCommandId_PlayerParam, + DriverCommandId_PlayerAdditionalSend, + DriverCommandId_PlayerBusMixVolumeUsed, + DriverCommandId_PlayerBusMixVolume, + DriverCommandId_PlayerBusMixVolumeEnabled, + DriverCommandId_PlayerBinaryVolume, + DriverCommandId_PlayerVolumeThroughModeUsed, + DriverCommandId_PlayerVolumeThroughMode, + DriverCommandId_PlayerClearResourceFlag, + DriverCommandId_SeqSetup, + DriverCommandId_SeqLoad, + DriverCommandId_SeqPrepare, + DriverCommandId_SeqSkip, + DriverCommandId_SeqTempoRatio, + DriverCommandId_SeqChannelPrio, + DriverCommandId_SeqSetVar, + DriverCommandId_SeqSetGvar, + DriverCommandId_SeqSetTvar, + DriverCommandId_SeqTrackMute, + DriverCommandId_SeqTrackSilence, + DriverCommandId_SeqTrackVolume, + DriverCommandId_SeqTrackPitch, + DriverCommandId_SeqTrackLpf, + DriverCommandId_SeqTrackBiquad, + DriverCommandId_SeqTrackBankIndex, + DriverCommandId_SeqTrackTranspose, + DriverCommandId_SeqTrackVelocityRange, + DriverCommandId_SeqTrackOutputline, + DriverCommandId_SeqTrackOutputlineReset, + DriverCommandId_SeqTrackTvVolume, + DriverCommandId_SeqTrackTvMixParameter, + DriverCommandId_SeqTrackTvPan, + DriverCommandId_SeqTrackTvSpan, + DriverCommandId_SeqTrackTvMainSend, + DriverCommandId_SeqTrackTvFxSend, + DriverCommandId_WsdPrepare, + DriverCommandId_WsdLoad, + DriverCommandId_WsdChannelPrio, + DriverCommandId_WsdChannelParam, + DriverCommandId_AwsdPrepare, + DriverCommandId_StrmSetup, + DriverCommandId_StrmPrepare, + DriverCommandId_StrmPreparePrefetch, + DriverCommandId_StrmLoadHeader, + DriverCommandId_StrmLoadData, + DriverCommandId_StrmForceFinish, + DriverCommandId_StrmTrackVolume, + DriverCommandId_StrmTrackInitialVolume, + DriverCommandId_StrmTrackOutputline, + DriverCommandId_StrmTrackOutputlineReset, + DriverCommandId_StrmTrackTvVolume, + DriverCommandId_StrmTrackTvMixParameter, + DriverCommandId_StrmTrackTvPan, + DriverCommandId_StrmTrackTvSpan, + DriverCommandId_StrmTrackTvMainSend, + DriverCommandId_StrmTrackTvFxSend, + DriverCommandId_InvalidateData, + DriverCommandId_RegistDisposeCallback, + DriverCommandId_UnregistDisposeCallback, + DriverCommandId_AppendEffect, + DriverCommandId_AppendEffectAux, + DriverCommandId_RemoveEffect, + DriverCommandId_RemoveEffectAux, + DriverCommandId_ClearEffect, + DriverCommandId_SubMixApplyDestination, + DriverCommandId_SubMixUpdateMixVolume, + DriverCommandId_AuxBusVolume, + DriverCommandId_AllVoicesSync, + DriverCommandId_VoicePlay, + DriverCommandId_VoiceWaveInfo, + DriverCommandId_VoiceAdpcmParam, + DriverCommandId_VoiceAppendWaveBuffer, + DriverCommandId_VoicePriority, + DriverCommandId_VoiceVolume, + DriverCommandId_VoicePitch, + DriverCommandId_VoicePanMode, + DriverCommandId_VoicePanCurve, + DriverCommandId_VoicePan, + DriverCommandId_VoiceSpan, + DriverCommandId_VoiceLpf, + DriverCommandId_VoiceBiquad, + DriverCommandId_VoiceOutputLine, + DriverCommandId_VoiceMainOutVolume, + DriverCommandId_VoiceMainSend, + DriverCommandId_VoiceFxSend, +}; + +struct DriverCommandReply : Command { + bool* ptr; +}; +static_assert(sizeof(DriverCommandReply) == 0x20); + +struct DriverCommandPlayerInit : Command { + driver::BasicSoundPlayer* player; +#if NN_SDK_VER >= NN_MAKE_VER(4, 0, 0) + OutputReceiver* pOutputReceiver; +#endif + bool* availableFlagPtr; +}; +#if NN_SDK_VER < NN_MAKE_VER(4, 0, 0) +static_assert(sizeof(DriverCommandPlayerInit) == 0x28); +#else +static_assert(sizeof(DriverCommandPlayerInit) == 0x30); +#endif + +struct DriverCommandPlayerPanParam : Command { + driver::BasicSoundPlayer* player; + u8 panMode; + u8 panCurve; + u8 padding[2]; +}; +static_assert(sizeof(DriverCommandPlayerPanParam) == 0x28); + +struct DriverCommandPlayer : Command { + driver::BasicSoundPlayer* player; + bool flag; + u8 padding[3]; +}; +static_assert(sizeof(DriverCommandPlayer) == 0x28); + +struct DriverCommandPlayerParam : Command { + driver::BasicSoundPlayer* player; + f32 volume; + f32 pitch; + f32 lpfFreq; + s32 biquadFilterType; + f32 biquadFilterValue; + u32 outputLineFlag; + OutputParam tvParam; +}; +static_assert(sizeof(DriverCommandPlayerParam) == 0x88); + +struct DriverCommandPlayerAdditionalSend : Command { + driver::BasicSoundPlayer* player; + s32 bus; + f32 send; +}; +static_assert(sizeof(DriverCommandPlayerAdditionalSend) == 0x28); + +struct DriverCommandPlayerBusMixVolumeUsed : Command { + driver::BasicSoundPlayer* player; + bool isUsed; +}; +static_assert(sizeof(DriverCommandPlayerBusMixVolumeUsed) == 0x28); + +struct DriverCommandPlayerBusMixVolume : Command { + driver::BasicSoundPlayer* player; + OutputBusMixVolume tvBusMixVolume; +}; +static_assert(sizeof(DriverCommandPlayerBusMixVolume) == 0xe0); + +struct DriverCommandPlayerBusMixVolumeEnabled : Command { + driver::BasicSoundPlayer* player; + s32 bus; + bool isEnabled; +}; +static_assert(sizeof(DriverCommandPlayerBusMixVolumeEnabled) == 0x28); + +struct DriverCommandPlayerBinaryVolume : Command { + driver::BasicSoundPlayer* player; + f32 volume; +}; +static_assert(sizeof(DriverCommandPlayerBinaryVolume) == 0x28); + +struct DriverCommandPlayerVolumeThroughModeUsed : Command { + driver::BasicSoundPlayer* player; + bool isVolumeThroughModeUsed; +}; +static_assert(sizeof(DriverCommandPlayerVolumeThroughModeUsed) == 0x28); + +struct DriverCommandPlayerVolumeThroughMode : Command { + driver::BasicSoundPlayer* player; + s32 bus; + u8 volumeThroughMode; +}; +static_assert(sizeof(DriverCommandPlayerVolumeThroughMode) == 0x28); + +struct DriverCommandPlayerClearResourceFlag : Command { + driver::BasicSoundPlayer* player; +}; +static_assert(sizeof(DriverCommandPlayerClearResourceFlag) == 0x20); + +struct DriverCommandSequenceSoundSetup : Command { + driver::SequenceSoundPlayer* player; + driver::SequenceSoundPlayer::SetupArg arg; + u8 channelPriority; + bool isReleasePriorityFix; + std::uintptr_t userproc; + void* userprocArg; +}; +static_assert(sizeof(DriverCommandSequenceSoundSetup) == 0x50); + +struct DriverCommandSequenceSoundLoad : Command { + driver::SequenceSoundPlayer* player; + driver::SequenceSoundPlayer::StartInfo startInfo; + driver::SequenceSoundLoader::Arg arg; +}; +static_assert(sizeof(DriverCommandSequenceSoundLoad) == 0xa0); + +struct DriverCommandSequenceSoundPrepare : Command { + driver::SequenceSoundPlayer* player; + driver::SequenceSoundPlayer::PrepareArg arg; +}; +static_assert(sizeof(DriverCommandSequenceSoundPrepare) == 0x80); + +struct DriverCommandSequenceSoundSkip : Command { + driver::SequenceSoundPlayer* player; + s32 offsetType; + s32 offset; +}; +static_assert(sizeof(DriverCommandSequenceSoundSkip) == 0x28); + +struct DriverCommandSequenceSoundTempoRatio : Command { + driver::SequenceSoundPlayer* player; + f32 tempoRatio; +}; +static_assert(sizeof(DriverCommandSequenceSoundTempoRatio) == 0x28); + +struct DriverCommandSequenceSoundChannelPrio : Command { + driver::SequenceSoundPlayer* player; + u8 priority; + u8 padding[3]; +}; +static_assert(sizeof(DriverCommandSequenceSoundChannelPrio) == 0x28); + +struct DriverCommandSequenceSoundSetVar : Command { + driver::SequenceSoundPlayer* player; + s32 trackNo; + s32 varNo; + s32 var; +}; +static_assert(sizeof(DriverCommandSequenceSoundSetVar) == 0x30); + +struct DriverCommandSequenceSoundTrack : Command { + driver::SequenceSoundPlayer* player; + u32 trackBitFlag; +}; +static_assert(sizeof(DriverCommandSequenceSoundTrack) == 0x28); + +struct DriverCommandSequenceSoundTrackMute : DriverCommandSequenceSoundTrack { + s32 mute; +}; +static_assert(sizeof(DriverCommandSequenceSoundTrackMute) == 0x28); + +struct DriverCommandSequenceSoundTrackSilence : DriverCommandSequenceSoundTrack { + bool silenceFlag; + s32 fadeFrames; +}; +static_assert(sizeof(DriverCommandSequenceSoundTrackSilence) == 0x30); + +struct DriverCommandSequenceSoundTrackParam : DriverCommandSequenceSoundTrack { + f32 value; + u32 uint32Value; +}; +static_assert(sizeof(DriverCommandSequenceSoundTrackParam) == 0x30); + +struct DriverCommandSequenceSoundTrackBiquad : DriverCommandSequenceSoundTrack { + s32 type; + f32 value; +}; +static_assert(sizeof(DriverCommandSequenceSoundTrackBiquad) == 0x30); + +struct DriverCommandSequenceSoundTrackBankIndex : DriverCommandSequenceSoundTrack { + s32 bankIndex; +}; +static_assert(sizeof(DriverCommandSequenceSoundTrackBankIndex) == 0x28); + +struct DriverCommandSequenceSoundTrackTranspose : DriverCommandSequenceSoundTrack { + s8 transpose; +}; +static_assert(sizeof(DriverCommandSequenceSoundTrackTranspose) == 0x28); + +struct DriverCommandSequenceSoundTrackVelocityRange : DriverCommandSequenceSoundTrack { + u8 range; +}; +static_assert(sizeof(DriverCommandSequenceSoundTrackVelocityRange) == 0x28); + +struct DriverCommandSequenceSoundTrackOutputLine : DriverCommandSequenceSoundTrack { + u32 outputLine; +}; +static_assert(sizeof(DriverCommandSequenceSoundTrackOutputLine) == 0x28); + +struct DriverCommandSequenceSoundTrackMixParameter : DriverCommandSequenceSoundTrack { + u32 trackBitFlag; + u32 srcChNo; + MixParameter param; + u32 drcIndex; +}; +static_assert(sizeof(DriverCommandSequenceSoundTrackMixParameter) == 0x48); + +struct DriverCommandWaveSoundPrepare : Command { + driver::WaveSoundPlayer* player; + driver::WaveSoundPlayer::StartInfo startInfo; + driver::WaveSoundPlayer::PrepareArg arg; +}; +static_assert(sizeof(DriverCommandWaveSoundPrepare) == 0x60); + +struct DriverCommandWaveSoundLoad : Command { + driver::WaveSoundPlayer* player; + driver::WaveSoundPlayer::StartInfo startInfo; + driver::WaveSoundLoader::Arg arg; +}; +static_assert(sizeof(DriverCommandWaveSoundLoad) == 0x78); + +struct DriverCommandWaveSoundChannelPrio : Command { + driver::WaveSoundPlayer* player; + u8 priority; + u8 padding[3]; +}; +static_assert(sizeof(DriverCommandWaveSoundChannelPrio) == 0x28); + +struct DriverCommandWaveSoundChannelParam : Command { + driver::WaveSoundPlayer* player; + u8 priority; + bool isReleasePriorityFix; + u8 padding[2]; +}; +static_assert(sizeof(DriverCommandWaveSoundChannelParam) == 0x28); + +struct DriverCommandAdvancedWaveSoundPrepare : Command { + driver::AdvancedWaveSoundPlayer* player; + driver::AdvancedWaveSoundPlayer::PrepareParameter parameter; +}; +#if NN_SDK_VER < NN_MAKE_VER(4, 0, 0) +static_assert(sizeof(DriverCommandAdvancedWaveSoundPrepare) == 0x40); +#else +static_assert(sizeof(DriverCommandAdvancedWaveSoundPrepare) == 0x38); +#endif + +struct DriverCommandStreamSoundSetup : Command { + driver::StreamSoundPlayer* player; + driver::StreamSoundPlayer::SetupArg arg; +}; +#if NN_SDK_VER < NN_MAKE_VER(4, 0, 0) +static_assert(sizeof(DriverCommandStreamSoundSetup) == 0xb8); +#else +static_assert(sizeof(DriverCommandStreamSoundSetup) == 0xc0); +#endif +struct DriverCommandStreamSoundPrepare : Command { + driver::StreamSoundPlayer* player; + driver::StreamSoundPlayer::PrepareArg arg; +}; +static_assert(sizeof(DriverCommandStreamSoundPrepare) == 0x300); + +struct DriverCommandStreamSoundPreparePrefetch : Command { + driver::StreamSoundPlayer* player; + driver::StreamSoundPlayer::PreparePrefetchArg arg; +}; +static_assert(sizeof(DriverCommandStreamSoundPreparePrefetch) == 0x2f8); + +struct DriverCommandStreamSoundLoadHeader : Command { + driver::StreamSoundPlayer* player; + AdpcmParam* adpcmParam[16]; + bool result; + u16 assignNumber; +}; +static_assert(sizeof(DriverCommandStreamSoundLoadHeader) == 0xa8); + +struct DriverCommandStreamSoundLoadData : Command { + driver::StreamSoundPlayer* player; + LoadDataParam loadDataParam; + bool result; + u16 assignNumber; +}; +static_assert(sizeof(DriverCommandStreamSoundLoadData) == 0xc0); + +struct DriverCommandStreamSoundForceFinish : Command { + driver::StreamSoundPlayer* player; +}; +static_assert(sizeof(DriverCommandStreamSoundForceFinish) == 0x20); + +struct DriverCommandStreamSoundTrackParam : Command { + driver::StreamSoundPlayer* player; + u32 trackBitFlag; + f32 value; + u32 uint32Value; + u32 drcIndex; +}; +static_assert(sizeof(DriverCommandStreamSoundTrackParam) == 0x30); + +struct DriverCommandStreamSoundTrackInitialVolume : Command { + driver::StreamSoundPlayer* player; + u32 trackBitFlag; + u32 value; +}; +static_assert(sizeof(DriverCommandStreamSoundTrackInitialVolume) == 0x28); + +struct DriverCommandStreamSoundTrackMixParameter : Command { + driver::StreamSoundPlayer* player; + u32 trackBitFlag; + u32 srcChNo; + MixParameter param; + u32 drcIndex; +}; +static_assert(sizeof(DriverCommandStreamSoundTrackMixParameter) == 0x48); + +struct DriverCommandInvalidateData : Command { + void* mem; + size_t size; +}; +static_assert(sizeof(DriverCommandInvalidateData) == 0x28); + +struct DriverCommandDisposeCallback : Command { + driver::DisposeCallback* callback; +}; +static_assert(sizeof(DriverCommandDisposeCallback) == 0x20); + +struct DriverCommandEffect : Command { + s32 bus; + EffectBase* effect; + void* effectBuffer; + size_t effectBufferSize; + OutputMixer* pOutputMixer; +}; +static_assert(sizeof(DriverCommandEffect) == 0x40); + +struct DriverCommandEffectAux : Command { + s32 bus; + EffectAux* effect; + void* effectBuffer; + size_t effectBufferSize; + OutputMixer* pOutputMixer; +}; +static_assert(sizeof(DriverCommandEffectAux) == 0x40); + +struct DriverCommandSubMixApplyDestination : Command { +#if NN_SDK_VER >= NN_MAKE_VER(4, 0, 0) + OutputReceiver* pReceiver; +#endif +}; +#if NN_SDK_VER < NN_MAKE_VER(4, 0, 0) +static_assert(sizeof(DriverCommandSubMixApplyDestination) == 0x18); +#else +static_assert(sizeof(DriverCommandSubMixApplyDestination) == 0x20); +#endif + +struct DriverCommandSubMixUpdateMixVolume : Command { +#if NN_SDK_VER >= NN_MAKE_VER(4, 0, 0) + OutputReceiver* pReceiver; +#endif + s32 srcBus; + s32 srcChannel; + s32 dstBus; + s32 dstChannel; +}; +#if NN_SDK_VER < NN_MAKE_VER(4, 0, 0) +static_assert(sizeof(DriverCommandSubMixUpdateMixVolume) == 0x28); +#else +static_assert(sizeof(DriverCommandSubMixUpdateMixVolume) == 0x30); +#endif + +struct DriverCommandAuxBusVolume : Command { + AuxBus bus; + s32 subMixIndex; + f32 volume; + s32 fadeFrames; +}; +static_assert(sizeof(DriverCommandAuxBusVolume) == 0x28); + +struct DriverCommandAllVoicesSync : Command { + u32 syncFlag; +}; +static_assert(sizeof(DriverCommandAllVoicesSync) == 0x20); + +struct DriverCommandVoice : Command { + driver::MultiVoice* voice; +}; +static_assert(sizeof(DriverCommandVoice) == 0x20); + +struct DriverCommandVoicePlay : DriverCommandVoice { + enum State { + State_Start, + State_Stop, + State_PauseOn, + State_PauseOff, + }; + + State state; +}; +static_assert(sizeof(DriverCommandVoicePlay) == 0x28); + +struct DriverCommandVoiceWaveInfo : DriverCommandVoice { + SampleFormat format; + s32 sampleRate; + s32 interpolationType; +}; +static_assert(sizeof(DriverCommandVoiceWaveInfo) == 0x30); + +struct DriverCommandVoiceAdpcmParam : DriverCommandVoice { + s32 channel; + s32 voiceOut; + AdpcmParam* param; +}; +static_assert(sizeof(DriverCommandVoiceAdpcmParam) == 0x30); + +class DriverCommand : CommandManager { +public: + static void ProcessCommandList(Command* commandList); + + DriverCommand(); + + void Initialize(void* commandBuffer, size_t commandBufferSize); + + void RequestProcessCommand(); +}; +static_assert(sizeof(DriverCommand) == 0x310); +} // namespace nn::atk::detail \ No newline at end of file diff --git a/include/nn/atk/atk_DspadpcmReader.h b/include/nn/atk/atk_DspadpcmReader.h new file mode 100644 index 00000000..2b46b74a --- /dev/null +++ b/include/nn/atk/atk_DspadpcmReader.h @@ -0,0 +1,19 @@ +#pragma once + +#include + +namespace nn::atk::detail { +class DspadpcmReader { +public: + DspadpcmReader(); + + void Initialize(const void* dspadpcmData) { + m_pDspadpcmData = dspadpcmData; + } + + bool ReadWaveInfo(WaveInfo* info) const; + +private: + const void* m_pDspadpcmData {}; +}; +} // namespace nn::atk::detail \ No newline at end of file diff --git a/include/nn/atk/atk_EffectAux.h b/include/nn/atk/atk_EffectAux.h new file mode 100644 index 00000000..eb99b032 --- /dev/null +++ b/include/nn/atk/atk_EffectAux.h @@ -0,0 +1,108 @@ +#pragma once + +#include + +#include +#include +#include +#include +#include +#include + +#include + +namespace nn::atk { +class OutputMixer; + +class EffectAux { +public: + constexpr static s32 ChannelCountMax = 6; + + struct BufferSet { + void* sendBuffer; + void* returnBuffer; + void* readBuffer; + }; + static_assert(sizeof(BufferSet) == 0x18); + + struct UpdateSamplesArg { + s32 sampleCountPerAudioFrame; + s32 sampleRate; + s32 audioFrameCount; + s32 channelCount; + s32 readSampleCount; + OutputMode outputMode; + }; + static_assert(sizeof(UpdateSamplesArg) == 0x18); + + EffectAux(); + virtual ~EffectAux(); + virtual void Initialize(); + virtual void Finalize(); + virtual void OnChangeOutputMode(); + virtual void UpdateSamples(s32, const UpdateSamplesArg&); + + void ResetChannelIndex(); + + size_t GetRequiredMemSize(const audio::AudioRendererParameter& parameter) const; + +#if NN_SDK_VER < NN_MAKE_VER(4, 0, 0) + bool AddEffect(audio::AudioRendererConfig* pConfig, + const audio::AudioRendererParameter& parameter, + audio::FinalMixType* pFinalMixType); + bool AddEffect(audio::AudioRendererConfig* pConfig, + const audio::AudioRendererParameter& parameter, + audio::SubMixType* pSubMixType); +#else + bool AddEffect(audio::AudioRendererConfig* pConfig, + const audio::AudioRendererParameter& parameter, + OutputMixer* pOutputMixer); +#endif + + void SplitEffectBuffer(BufferSet* pBufferSet, void* effectBuffer, + size_t effectBufferSize); + + void SetEffectInputOutput(const s8* input, const s8* output, s32 inputCount, s32 outputCount); + +#if NN_SDK_VER < NN_MAKE_VER(4, 0, 0) + void RemoveEffect(audio::AudioRendererConfig* pConfig, audio::FinalMixType* pFinalMixType); + void RemoveEffect(audio::AudioRendererConfig* pConfig, audio::SubMixType* pSubMixType); +#else + void RemoveEffect(audio::AudioRendererConfig* pConfig, OutputMixer* pOutputMixer); +#endif + + bool SetChannelCount(s32 channelCount); + bool SetChannelIndex(const ChannelIndex* pChannel, s32 channelCount); + + s32 GetChannelCount() const; + void GetChannelIndex(ChannelIndex* pChannel, s32 channelCount) const; + + bool SetAudioFrameCount(s32 audioFrameCount); + s32 GetAudioFrameCount() const; + + bool IsRemovable() const; + bool IsClearable(); + bool IsEnabled() const; + + void SetEnabled(bool isEnabled); + void SetEffectBuffer(void* effectBuffer, size_t effectBufferSize); + + void Update(); + +private: + friend OutputMixer; + + util::IntrusiveListNode m_AuxLinkNode; + audio::AuxType m_AuxType; + std::atomic_uint64_t m_AudioRendererUpdateCountWhenAddedAux; + s32 m_AudioFrameCount; + s32 m_ChannelCount; + bool m_IsActive; + bool m_IsEnabled; + void* m_EffectBuffer; + size_t m_EffectBufferSize; + s32* m_AuxReadBuffer; + ChannelIndex m_ChannelSetting[ChannelCountMax]; +}; +static_assert(sizeof(EffectAux) == 0x50 + sizeof(ChannelIndex) * EffectAux::ChannelCountMax); +} // namespace nn::atk \ No newline at end of file diff --git a/include/nn/atk/atk_EffectBase.h b/include/nn/atk/atk_EffectBase.h new file mode 100644 index 00000000..ae6444c9 --- /dev/null +++ b/include/nn/atk/atk_EffectBase.h @@ -0,0 +1,77 @@ +#pragma once + +#include +#include +#include +#include +#include + +#include + +namespace nn::atk { +class OutputMixer; + +class EffectBase { +public: + enum ChannelMode { + ChannelMode_1Ch, + ChannelMode_2Ch, + ChannelMode_4Ch, + ChannelMode_6Ch, + }; + + enum SampleRate { + SampleRate_32000, + SampleRate_48000, + }; + + constexpr static u8 ChannelModeCountMax = 6; + + EffectBase(); + + virtual ~EffectBase(); + virtual void unk1() = 0; + virtual void unk2() = 0; + virtual void unk3() = 0; + virtual void unk4() = 0; + virtual void unk5() = 0; + virtual void unk6() = 0; + virtual void unk7() = 0; + virtual void UpdateBuffer(s32, void**, size_t, SampleFormat, s32, OutputMode); + virtual void GetChannelIndex(ChannelIndex* pChannel, s32 channelCount) const; + virtual s32 GetChannelSettingCountMax() const; + virtual void OnChangeOutputMode(); + virtual void SetEffectBuffer(void* effectBuffer, size_t effectBufferSize); + +#if NN_SDK_VER < NN_MAKE_VER(4, 0, 0) + bool AddEffect(audio::AudioRendererConfig* pConfig, audio::FinalMixType* pFinalMixType); + bool AddEffect(audio::AudioRendererConfig* pConfig, audio::SubMixType* pFinalMixType); +#else + bool AddEffect(audio::AudioRendererConfig* pConfig, OutputMixer* pOutputMixer); +#endif + + void SetEffectInputOutput(const s8* input, const s8* output, s32 inputCount, s32 outputCount); + +#if NN_SDK_VER < NN_MAKE_VER(4, 0, 0) + void RemoveEffect(audio::AudioRendererConfig* pConfig, audio::FinalMixType* pFinalMixType); + void RemoveEffect(audio::AudioRendererConfig* pConfig, audio::SubMixType* pSubMixType); +#else + void RemoveEffect(audio::AudioRendererConfig* pConfig, OutputMixer* pOutputMixer); +#endif + + static s32 ConvertChannelModeToInt(ChannelMode channelMode); + + SampleRate GetSampleRate() const; + bool SetSampleRate(SampleRate sampleRate); + +private: + friend OutputMixer; + + util::IntrusiveListNode m_Link; + bool m_IsActive; + SampleRate m_SampleRate; + void* m_EffectBuffer; + size_t m_EffectBufferSize; +}; +static_assert(sizeof(EffectBase) == 0x30); +} // namespace nn::atk \ No newline at end of file diff --git a/include/nn/atk/atk_ElementType.h b/include/nn/atk/atk_ElementType.h new file mode 100644 index 00000000..fcb4d186 --- /dev/null +++ b/include/nn/atk/atk_ElementType.h @@ -0,0 +1,142 @@ +#pragma once + +namespace nn::atk::detail { +enum ElementType { + ElementType_Category_Tables = 0x100, + ElementType_Category_Parameters = 0x200, + ElementType_Category_Codecs = 0x300, + + ElementType_Category_General = 0x1F00, + + ElementType_Category_SoundArchiveFile_Blocks = 0x2000, + ElementType_Category_SoundArchiveFile_InfoSections = 0x2100, + ElementType_Category_SoundArchiveFile_ItemInfos = 0x2200, + ElementType_Category_SoundArchiveFile_Parameters = 0x2300, + ElementType_Category_SoundArchiveFile_General = 0x2400, + + ElementType_Category_StreamSoundFile_Blocks = 0x4000, + ElementType_Category_StreamSoundFile_ItemInfos = 0x4100, + + ElementType_Category_WaveSoundFile_Blocks = 0x4800, + ElementType_Category_WaveSoundFile_ItemInfos = 0x4900, + + ElementType_Category_SequenceSoundFile_Blocks = 0x5000, + ElementType_Category_SequenceSoundFile_ItemInfos = 0x5100, + + ElementType_Category_BankFile_Blocks = 0x5800, + ElementType_Category_BankFile_Items = 0x5900, + ElementType_Category_BankFile_ItemTables = 0x6000, + + ElementType_Category_WaveArchiveFile_Blocks = 0x6800, + + ElementType_Category_WaveFile_Blocks = 0x7000, + ElementType_Category_WaveFile_ItemInfos = 0x7100, + + ElementType_Category_GroupFile_Blocks = 0x7800, + ElementType_Category_GroupFile_ItemInfos = 0x7900, + + ElementType_Category_AnimSoundFile_Blocks = 0x8000, + ElementType_Category_AnimSoundFile_Items = 0x8100, + + ElementType_Table_EmbeddingTable = ElementType_Category_Tables, + ElementType_Table_ReferenceTable, + ElementType_Table_ReferenceWithSizeTable, + + ElementType_Parameter_Sound3d = ElementType_Category_Parameters, + ElementType_Parameter_Sends, + ElementType_Parameter_Envelope, + ElementType_Parameter_AdshrEnvelope, + + ElementType_Codec_DspAdpcmInfo = ElementType_Category_Codecs, + ElementType_Codec_ImaAdpcmInfo, + + ElementType_General_ByteStream = ElementType_Category_General, + ElementType_General_String, + + ElementType_SoundArchiveFile_StringBlock = ElementType_Category_SoundArchiveFile_Blocks, + ElementType_SoundArchiveFile_InfoBlock, + ElementType_SoundArchiveFile_FileBlock, + + ElementType_SoundArchiveFile_SoundInfoSection = ElementType_Category_SoundArchiveFile_InfoSections, + ElementType_SoundArchiveFile_BankInfoSection, + ElementType_SoundArchiveFile_PlayerInfoSection, + ElementType_SoundArchiveFile_WaveArchiveInfoSection, + ElementType_SoundArchiveFile_SoundGroupInfoSection, + ElementType_SoundArchiveFile_GroupInfoSection, + ElementType_SoundArchiveFile_FileInfoSection, + + ElementType_SoundArchiveFile_SoundInfo = ElementType_Category_SoundArchiveFile_ItemInfos, + ElementType_SoundArchiveFile_StreamSoundInfo, + ElementType_SoundArchiveFile_WaveSoundInfo, + ElementType_SoundArchiveFile_SequenceSoundInfo, + ElementType_SoundArchiveFile_SoundGroupInfo, + ElementType_SoundArchiveFile_WaveSoundGroupInfo, + ElementType_SoundArchiveFile_BankInfo, + ElementType_SoundArchiveFile_WaveArchiveInfo, + ElementType_SoundArchiveFile_GroupInfo, + ElementType_SoundArchiveFile_PlayerInfo, + ElementType_SoundArchiveFile_FileInfo, + ElementType_SoundArchiveFile_SoundArchivePlayerInfo, + ElementType_SoundArchiveFile_InternalFileInfo, + ElementType_SoundArchiveFile_ExternalFileInfo, + ElementType_SoundArchiveFile_StreamSoundTrackInfo, + ElementType_SoundArchiveFile_SendInfo, + ElementType_SoundArchiveFile_StreamSoundExtensionInfo, + + ElementType_SoundArchiveFile_StringTable = ElementType_Category_SoundArchiveFile_General, + ElementType_SoundArchiveFile_PatriciaTree, + + ElementType_StreamSoundFile_InfoBlock = ElementType_Category_StreamSoundFile_Blocks, + ElementType_StreamSoundFile_SeekBlock, + ElementType_StreamSoundFile_DataBlock, + ElementType_StreamSoundFile_RegionBlock, + ElementType_StreamSoundFile_PrefetchDataBlock, + + ElementType_StreamSoundFile_StreamSoundInfo = ElementType_Category_StreamSoundFile_ItemInfos, + ElementType_StreamSoundFile_TrackInfo, + ElementType_StreamSoundFile_ChannelInfo, + + ElementType_WaveSoundFile_WaveSoundMetaData = ElementType_Category_WaveSoundFile_ItemInfos, + ElementType_WaveSoundFile_WaveSoundInfo, + ElementType_WaveSoundFile_NoteInfo, + ElementType_WaveSoundFile_TrackInfo, + ElementType_WaveSoundFile_NoteEvent, + + ElementType_SequenceSoundFile_DataBlock = ElementType_Category_SequenceSoundFile_Blocks, + ElementType_SequenceSoundFile_LabelBlock, + + ElementType_SequenceSoundFile_LabelInfo = ElementType_Category_SequenceSoundFile_ItemInfos, + + ElementType_BankFile_InfoBlock = ElementType_Category_BankFile_Blocks, + + ElementType_BankFile_InstrumentInfo = ElementType_Category_BankFile_Items, + ElementType_BankFile_KeyRegionInfo, + ElementType_BankFile_VelocityRegionInfo, + ElementType_BankFile_NullInfo, + + ElementType_BankFile_DirectReferenceTable = ElementType_Category_BankFile_ItemTables, + ElementType_BankFile_RangeReferenceTable, + ElementType_BankFile_IndexReferenceTable, + + ElementType_WaveArchiveFile_InfoBlock = ElementType_Category_WaveArchiveFile_Blocks, + ElementType_WaveArchiveFile_FileBlock, + + ElementType_WaveSoundFile_InfoBlock = ElementType_Category_WaveArchiveFile_Blocks, + + ElementType_WaveFile_InfoBlock = ElementType_Category_WaveFile_Blocks, + ElementType_WaveFile_DataBlock, + + ElementType_WaveFile_ChannelInfo = ElementType_Category_WaveFile_ItemInfos, + + ElementType_GroupFile_InfoBlock = ElementType_Category_GroupFile_Blocks, + ElementType_GroupFile_FileBlock, + ElementType_GroupFile_InfoExBlock, + + ElementType_GroupFile_GroupItemInfo = ElementType_Category_GroupFile_ItemInfos, + ElementType_GroupFile_GroupItemInfoEx, + + ElementType_AnimSoundFile_DataBlock = ElementType_Category_AnimSoundFile_Blocks, + + ElementType_AnimSoundFile_EventInfo = ElementType_Category_AnimSoundFile_Items +}; +} // namespace nn::atk::detail \ No newline at end of file diff --git a/include/nn/atk/atk_ExternalSoundPlayer.h b/include/nn/atk/atk_ExternalSoundPlayer.h new file mode 100644 index 00000000..155f8508 --- /dev/null +++ b/include/nn/atk/atk_ExternalSoundPlayer.h @@ -0,0 +1,39 @@ +#pragma once + +#include + +#include + +namespace nn::atk::detail { +class ExternalSoundPlayer { +public: + using SoundList = util::IntrusiveList< + BasicSound, util::IntrusiveListMemberNodeTraits< + BasicSound, &BasicSound::m_ExtSoundPlayerPlayLink>>; + + virtual ~ExternalSoundPlayer(); + virtual bool CanPlaySound(s32); + + ExternalSoundPlayer(); + + void StopAllSound(s32 fadeFrames); + void PauseAllSound(bool, s32); + void PauseAllSound(bool, s32, PauseMode); + + void Finalize(SoundActor* actor); + + void RemoveSound(BasicSound* sound); + bool AppendSound(BasicSound* sound); + + BasicSound* GetLowestPrioritySound(); + + void SetPlayableSoundCount(s32 count); + + bool CanPlaySound(); + +private: + SoundList m_SoundList; + s32 m_PlayableCount; +}; +static_assert(sizeof(ExternalSoundPlayer) == 0x20); +} // namespace nn::atk::detail \ No newline at end of file diff --git a/include/nn/atk/atk_FinalMix.h b/include/nn/atk/atk_FinalMix.h new file mode 100644 index 00000000..17919c42 --- /dev/null +++ b/include/nn/atk/atk_FinalMix.h @@ -0,0 +1,43 @@ +#pragma once + +#include +#include +#include + +#include + +namespace nn::atk { +class FinalMix : OutputMixer { +public: + static size_t GetRequiredMemorySize(bool isEffectEnabled); + + bool Initialize(audio::AudioRendererConfig* pConfig, s32 channelCount, + bool isEffectEnabled, void* buffer, size_t bufferSize); + + void Finalize(audio::AudioRendererConfig* pConfig); + + bool AppendEffect(EffectBase* pEffect, void* buffer, size_t bufferSize); + bool AppendEffect(EffectAux* pEffect, void* buffer, size_t bufferSize); + + bool RemoveEffect(EffectBase* pEffect); + bool RemoveEffect(EffectAux* pEffect); + + void ClearEffect(); + + bool IsEffectEnabled() const; + + ReceiverType GetReceiverType() const override; + s32 GetChannelCount() const override; + s32 GetBusCount() const override; + + void AddReferenceCount(s32 value) override; + + bool IsSoundSendClampEnabled(s32 bus) const override; + +private: + audio::FinalMixType m_FinalMix; + std::atomic_uint m_ReferenceCount; + s32 m_ChannelCount; +}; +static_assert(sizeof(FinalMix) == 0x50); +} // namespace nn::atk \ No newline at end of file diff --git a/include/nn/atk/atk_FrameHeap.h b/include/nn/atk/atk_FrameHeap.h new file mode 100644 index 00000000..b41ca69e --- /dev/null +++ b/include/nn/atk/atk_FrameHeap.h @@ -0,0 +1,113 @@ +#pragma once + +#include + +#include +#include + +namespace nn::atk::detail { + +class FrameHeap { +public: + using DisposeCallback = void (*)(void*, size_t, void*); + using HeapCallback = void (*)(void*); + + static const int HeapAlign{64}; + + class Block { + public: + Block(void* buffer, size_t size, DisposeCallback callback, + void* callbackArg, HeapCallback heapCallback, void* heapCallbackArg) + : m_pBuffer{buffer}, m_Size{size}, + m_Callback{callback}, m_pCallbackArg{callbackArg}, + m_HeapCallback{heapCallback}, m_pHeapCallbackArg{heapCallbackArg} {} + + ~Block() = default; + + void* GetBufferAddr() { return m_pBuffer; } + const void* GetBufferAddr() const { return m_pBuffer; } + + size_t GetBufferSize() const { return m_Size; } + DisposeCallback GetDisposeCallback() const { return m_Callback; } + void* GetDisposeCallbackArg() const { return m_pCallbackArg; } + HeapCallback GetHeapCallback() const { return m_HeapCallback; } + void* GetHeapCallbackArg() const { return m_pHeapCallbackArg; } + + util::IntrusiveListNode m_Link; + + private: + void* m_pBuffer; + size_t m_Size; + DisposeCallback m_Callback; + void* m_pCallbackArg; + HeapCallback m_HeapCallback; + void* m_pHeapCallbackArg; + }; + static_assert(sizeof(Block) == 0x40); + + using BlockList = + util::IntrusiveList>; + + class Section { + public: + Section(); + ~Section(); + + void AppendBlock(Block* block); + + const BlockList& GetBlockList() const { return m_BlockList; } + BlockList& GetBlockList() { return m_BlockList; } + + void* GetAddr() { return this; } + + bool GetUseCallback() const { return m_UseCallback; } + void SetUseCallback(bool use) { m_UseCallback = use; } + + void Dump(const SoundDataManager& mgr, const SoundArchive& arc) const; + + util::IntrusiveListNode m_Link; + + private: + BlockList m_BlockList; + bool m_UseCallback{true}; + }; + static_assert(sizeof(Section) == 0x28); + + using SectionList = + util::IntrusiveList>; + + FrameHeap(); + ~FrameHeap(); + + bool Create(void* startAddress, size_t size); + void Destroy(); + + void* Alloc(size_t size, DisposeCallback callback, void* callbackArg, + HeapCallback heapCallback, void* heapCallbackArg); + + void Clear(); + + int SaveState(); + void LoadState(int level); + + int GetCurrentLevel() const; + size_t GetSize() const; + size_t GetFreeSize() const; + + bool IsValid() const { return m_pHeap != nullptr; } + + void Dump(const SoundDataManager& mgr, const SoundArchive& arc) const; + + bool ProcessCallback(int level); + +private: + bool NewSection(); + void ClearSection(); + + fnd::FrameHeapImpl* m_pHeap{}; + SectionList m_SectionList; +}; +static_assert(sizeof(FrameHeap) == 0x18); + +} // namespace nn::atk::detail \ No newline at end of file diff --git a/include/nn/atk/atk_FsSoundArchive.h b/include/nn/atk/atk_FsSoundArchive.h new file mode 100644 index 00000000..b89173cf --- /dev/null +++ b/include/nn/atk/atk_FsSoundArchive.h @@ -0,0 +1,70 @@ +#pragma once + +#include +#include +#include +#include + +namespace nn::atk { +class FsSoundArchive : public SoundArchive { +public: + static const int BufferAlignSize {64}; + + struct OpenArg { + + }; + + FsSoundArchive(); + ~FsSoundArchive() override; + + bool Open(const char* path); + bool Open(const char* path, const OpenArg* arg); + void Close(); + + size_t GetHeaderSize() const; + + bool LoadHeader(void* buffer, size_t size); + + size_t GetLabelStringDataSize() const; + + bool LoadLabelStringData(void* buffer, size_t size); + + size_t detail_GetRequiredStreamBufferSize() const override; + const void* detail_GetFileAddress(ItemId itemId) const override; + + enum FileAccessMode { + FileAccessMode_Always, + FileAccessMode_InFunction + }; + + void SetFileAccessMode(FileAccessMode value); + FileAccessMode GetFileAccessMode() const; + + void FileAccessBegin() const override; + void FileAccessEnd() const override; + +protected: + detail::fnd::FileStream* OpenStream(void* buffer, size_t size, + position_t begin, size_t length) const override; + + detail::fnd::FileStream* OpenExtStream(void* buffer, size_t size, const char* extFilePath, + void* cacheBuffer, size_t cacheSize) const override; + + bool LoadFileHeader(); + +private: + detail::SoundArchiveFileReader m_ArchiveReader; + mutable detail::fnd::FileStreamImpl m_FileStream; + bool m_IsOpened {false}; + u8 m_FileAccessMode {FileAccessMode_Always}; + u8 m_Padding[2]; + mutable u32 m_FileAccessCount {0}; + char m_SoundArchiveFullPath[SoundArchive::FilePathMax]; + mutable detail::fnd::CriticalSection m_FileOpenCloseLock; +}; +#if NN_SDK_VER < NN_MAKE_VER(4, 0, 0) +static_assert(sizeof(FsSoundArchive) == 0x618); +#else +static_assert(sizeof(FsSoundArchive) == 0x610); +#endif +} // namespace nn::atk \ No newline at end of file diff --git a/include/nn/atk/atk_Global.h b/include/nn/atk/atk_Global.h new file mode 100644 index 00000000..8d33ce33 --- /dev/null +++ b/include/nn/atk/atk_Global.h @@ -0,0 +1,456 @@ +#pragma once + +#include +#include + +namespace nn::atk { +enum WaveType { + WaveType_Invalid = -1, + WaveType_Nwwav, + WaveType_Dspadpcm, +}; + +struct WaveBuffer { + enum Status { + Status_Free, + Status_Wait, + Status_Play, + Status_Done, + }; + + void* bufferAddress; + size_t bufferSize; + size_t sampleLength; + position_t sampleOffset; + AdpcmContext* pAdpcmContext; + void* userParam; + bool loopFlag; + Status status; + WaveBuffer* next; +}; +static_assert(sizeof(WaveBuffer) == 0x40); + +enum AuxBus { + AuxBus_A, + AuxBus_B, + AuxBus_C, + AuxBus_Count, +}; + +enum BiquadFilterType { + BiquadFilterType_Inherit = -1, + BiquadFilterType_Min = -1, + BiquadFilterType_DataMin = 0, + + BiquadFilterType_None = 0, + BiquadFilterType_LowPassFilter, + BiquadFilterType_HighPassFilter, + BiquadFilterType_BandPassFilter512, + BiquadFilterType_BandPassFilter1024, + BiquadFilterType_BandPassFilter2048, + BiquadFilterType_LowPassFilterNw4fCompatible48k, + BiquadFilterType_HighPassFilterNw4fCompatible48k, + BiquadFilterType_BandPassFilter512Nw4fCompatible48k, + BiquadFilterType_BandPassFilter1024Nw4fCompatible48k, + BiquadFilterType_BandPassFilter2048Nw4fCompatible48k, + + BiquadFilterType_UserMin = 0x40, + + BiquadFilterType_User0 = BiquadFilterType_UserMin, + BiquadFilterType_User1, + BiquadFilterType_User2, + BiquadFilterType_User3, + BiquadFilterType_User4, + BiquadFilterType_User5, + BiquadFilterType_User6, + BiquadFilterType_User7, + BiquadFilterType_User8, + BiquadFilterType_User9, + BiquadFilterType_User10, + BiquadFilterType_User11, + BiquadFilterType_User12, + BiquadFilterType_User13, + BiquadFilterType_User14, + BiquadFilterType_User15, + BiquadFilterType_User16, + BiquadFilterType_User17, + BiquadFilterType_User18, + BiquadFilterType_User19, + BiquadFilterType_User20, + BiquadFilterType_User21, + BiquadFilterType_User22, + BiquadFilterType_User23, + BiquadFilterType_User24, + BiquadFilterType_User25, + BiquadFilterType_User26, + BiquadFilterType_User27, + BiquadFilterType_User28, + BiquadFilterType_User29, + BiquadFilterType_User30, + BiquadFilterType_User31, + BiquadFilterType_User32, + BiquadFilterType_User33, + BiquadFilterType_User34, + BiquadFilterType_User35, + BiquadFilterType_User36, + BiquadFilterType_User37, + BiquadFilterType_User38, + BiquadFilterType_User39, + BiquadFilterType_User40, + BiquadFilterType_User41, + BiquadFilterType_User42, + BiquadFilterType_User43, + BiquadFilterType_User44, + BiquadFilterType_User45, + BiquadFilterType_User46, + BiquadFilterType_User47, + BiquadFilterType_User48, + BiquadFilterType_User49, + BiquadFilterType_User50, + BiquadFilterType_User51, + BiquadFilterType_User52, + BiquadFilterType_User53, + BiquadFilterType_User54, + BiquadFilterType_User55, + BiquadFilterType_User56, + BiquadFilterType_User57, + BiquadFilterType_User58, + BiquadFilterType_User59, + BiquadFilterType_User60, + BiquadFilterType_User61, + BiquadFilterType_User62, + BiquadFilterType_User63, + + BiquadFilterType_Max = 0x7f, + BiquadFilterType_UserMax = 0x7f, +}; + +enum ChannelIndex { + ChannelIndex_FrontLeft, + ChannelIndex_FrontRight, + ChannelIndex_RearLeft, + ChannelIndex_RearRight, + ChannelIndex_FrontCenter, + ChannelIndex_Lfe, + ChannelIndex_Count, +}; + +enum CircularBufferSinkState { + CircularBufferSinkState_Invalid, + CircularBufferSinkState_Started, + CircularBufferSinkState_Stopped, +}; + +enum FsPriority { + FsPriority_RealTime, + FsPriority_Normal, + FsPriority_Low, +}; + +class AdshrCurve { +public: + AdshrCurve(u8 a, u8 d, u8 s, u8 h, u8 r) + : m_Attack{a}, m_Decay{d}, m_Sustain{s}, m_Hold{h}, m_Release{r} {} + + u8 GetAttack() const { return m_Attack; } + + void SetAttack(u8 attack) { m_Attack = attack; } + + u8 GetDecay() const { return m_Decay; } + + void SetDecay(u8 decay) { m_Decay = decay; } + + u8 GetSustain() const { return m_Sustain; } + + void SetSustain(u8 sustain) { m_Sustain = sustain; } + + u8 GetHold() const { return m_Hold; } + + void SetHold(u8 hold) { m_Hold = hold; } + + u8 GetRelease() const { return m_Release; } + + void SetRelease(u8 release) { m_Release = release; } + +private: + u8 m_Attack; + u8 m_Decay; + u8 m_Sustain; + u8 m_Hold; + u8 m_Release; +}; +static_assert(sizeof(AdshrCurve) == 0x5); + +struct BiquadFilterCoefficients { + s16 b0 {0}; + s16 b1 {0}; + s16 b2 {0}; + s16 a1 {0}; + s16 a2 {0}; +}; +static_assert(sizeof(BiquadFilterCoefficients) == 0xa); + +struct DspAdpcmParam { + u16 coef[8][2]; + u16 predScale; + u16 yn1; + u16 yn2; +}; +static_assert(sizeof(DspAdpcmParam) == 0x26); + +enum MixMode { + MixMode_Pan = 0, + MixMode_MixVolume = 1, + MixMode_Mixparameter = 1, + MixMode_Count = 2, +}; + +struct MixParameter { + union { + struct { + f32 fL; + f32 fR; + f32 rL; + f32 rR; + f32 fC; + f32 lfe; + }; + float ch[6]; + }; +}; +static_assert(sizeof(MixParameter) == 0x18); + +struct MixVolume { + union { + struct { + f32 frontLeft; + f32 frontRight; + f32 rearLeft; + f32 rearRight; + f32 frontCenter; + f32 lowFrequencyEffect; + }; + float channel[6]; + }; +}; +static_assert(sizeof(MixVolume) == 0x18); + +enum OutputDevice { + OutputDevice_Main, + OutputDevice_Count, +}; + +enum OutputDeviceIndex { + OutputDeviceIndex_Main, + OutputDeviceIndex_Count, +}; + +enum OutputLine { + OutputLine_Main = 1, + OutputLine_ReservedMax = 2, + + OutputLine_User0 = 0x10000, + OutputLine_User1 = 0x20000, + OutputLine_User2 = 0x40000, + OutputLine_User3 = 0x80000, +}; + +enum OutputLineIndex { + OutputLineIndex_Main = 1, + OutputLineIndex_ReservedMax = 2, + + OutputLineIndex_User0 = 0x10000, + OutputLineIndex_User1 = 0x20000, + OutputLineIndex_User2 = 0x40000, + OutputLineIndex_User3 = 0x80000, +}; + +enum OutputMode { + OutputMode_Monaural, + OutputMode_Stereo, + OutputMode_Surround, + OutputMode_Dpl2, + OutputMode_Count, +}; + +struct OutputMix { + OutputMix() = default; + + f32 channelGain[24] = {0}; +}; +static_assert(sizeof(OutputMix) == 0x60); + +enum PanCurve { + PanCurve_Sqrt, + PanCurve_Sqrt0Db, + PanCurve_Sqrt0DbClamp, + PanCurve_Sincos, + PanCurve_Sincos0Db, + PanCurve_Sincos0DbClamp, + PanCurve_Linear, + PanCurve_Linear0Db, + PanCurve_Linear0DbClamp, + PanCurve_Invalid, +}; + +enum PanMode { + PanMode_Dual, + PanMode_Balance, + PanMode_Invalid, +}; + +enum PauseMode { + PauseMode_Default = 0, + + PauseMode_Nw4fSndCompatible = PauseMode_Default, + PauseMode_PauseImmediately, +}; + +enum SampleFormat { + SampleFormat_PcmS8, + SampleFormat_PcmS16, + SampleFormat_DspAdpcm, + SampleFormat_PcmS32, +}; + +enum SampleRateConverterType { + SampleRateConverterType_None, + SampleRateConverterType_Linear, + SampleRateConverterType_4Tap, +}; + +enum SequenceMute { + SequenceMute_Off, + SequenceMute_NoStop, + SequenceMute_Release, + SequenceMute_Stop, +}; + +enum SinglePlayType { + SinglePlayType_None, + SinglePlayType_PrioritizeOldest, + SinglePlayType_PrioritizeOldestEffectiveDuration, + SinglePlayType_PrioritizeOldestWithDuration = SinglePlayType_PrioritizeOldestEffectiveDuration, + + SinglePlayType_PrioritizeNewest, + SinglePlayType_PrioritizeNewestEffectiveDuration, + SinglePlayType_PrioritizeNewestWithDuration = SinglePlayType_PrioritizeNewestEffectiveDuration, +}; + +struct StreamDataInfo {}; + +enum UpdateType { + UpdateType_AudioFrame, + UpdateType_GameFrame, +}; + +enum VolumeThroughModeBitFlag { + VolumeThroughMode_Binary = 1, +}; + +namespace detail { +enum DecodeMode { + DecodeMode_Invalid = -1, + DecodeMode_Default, + DecodeMode_Cpu, + DecodeMode_Accelerator, +}; + +struct DspAdpcmLoopParam { + u16 loopPredScale; + u16 loopYn1; + u16 loopYn2; +}; +static_assert(sizeof(DspAdpcmLoopParam) == 0x6); + +struct OutputBusMixVolume { + float volume[2][24]; +}; +static_assert(sizeof(OutputBusMixVolume) == 0xc0); + +struct OutputParam { + float volume; + u32 mixMode; + MixParameter mixParameter[2]; + float pan; + float span; + float send[4]; +}; +static_assert(sizeof(OutputParam) == 0x50); + +struct SoundInstanceConfig { + bool isBusMixVolumeEnabled; + bool isVolumeThroughModeEnabled; + s32 busCount; +}; +static_assert(sizeof(SoundInstanceConfig) == 8); + +enum StreamFileType { + StreamFileType_Bfstm, + StreamFileType_Opus, +}; + +enum VoiceState { + VoiceState_Play, + VoiceState_Stop, + VoiceState_Pause, +}; + +struct VoiceInfo { + VoiceState voiceState; + WaveBuffer::Status waveBufferStatus; + void* waveBufferTag; + u32 playPosition; + void* userId; +}; +static_assert(sizeof(VoiceInfo) == 0x20); + +class VoiceParam { +public: + VoiceParam() = default; + + void Initialize(); + +private: + float m_Volume {0}; + float m_Pitch {0}; + OutputMix m_TvMix {}; + bool m_MonoFilterFlag {false}; + bool m_BiquadFilterFlag {false}; + BiquadFilterCoefficients m_BiquadFilterCoefficients {}; + u16 m_MonoFilterCutoff {0}; + u8 m_InterpolationType {0}; +}; +static_assert(sizeof(VoiceParam) == 0x78); + +struct WaveInfo { + struct ChannelParam { + const void* dataAddress; + int dataSize; + DspAdpcmParam adpcmParam; + DspAdpcmLoopParam adpcmLoopParam; + }; + static_assert(sizeof(ChannelParam) == 0x38); + + SampleFormat sampleFormat; + bool loopFlag; + int channelCount; + int sampleRate; + position_t loopStartFrame; + position_t loopEndFrame; + position_t originalLoopStartFrame; + size_t dataSize; + ChannelParam channelParam[2]; +}; +static_assert(sizeof(WaveInfo) == 0xa0); + +static const OutputMix DefaultTvMix{{1.0, 1.0, 0.0, 0.0, 0.0, 0.0, + 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, + 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, + 0.0, 0.0, 0.0, 0.0, 0.0, 0.0}}; +} // namespace nn::atk::detail + +using SoundFrameUserCallback = void(*)(std::uintptr_t); +using SoundThreadUserCallback = void(*)(std::uintptr_t); +using SoundStopCallback = void(*)(); +} // namespace nn::atk \ No newline at end of file diff --git a/include/nn/atk/atk_GroupFile.h b/include/nn/atk/atk_GroupFile.h new file mode 100644 index 00000000..8a7ecbd0 --- /dev/null +++ b/include/nn/atk/atk_GroupFile.h @@ -0,0 +1,93 @@ +#pragma once + +#include + +namespace nn::atk::detail { +struct GroupFile { + struct InfoBlock; + struct FileBlock; + struct InfoExBlock; + struct FileHeader : Util::SoundFileHeader { + const InfoBlock* GetInfoBlock() const; + const FileBlock* GetFileBlock() const; + const InfoExBlock* GetInfoExBlock() const; + }; + + struct GroupItemInfo; + struct InfoBlockBody { + Util::ReferenceTable referenceTableOfGroupItemInfo; + + u32 GetGroupItemInfoCount() const { + return referenceTableOfGroupItemInfo.count; + } + + const GroupItemInfo* GetGroupItemInfo(u32 index) const { + if (GetGroupItemInfoCount() <= index) + return nullptr; + + return util::ConstBytePtr(this, referenceTableOfGroupItemInfo.item[index].offset) + .Get(); + } + }; + + struct InfoBlock { + BinaryBlockHeader header; + InfoBlockBody body; + }; + + struct FileBlockBody; + struct GroupItemInfo { + u32 fileId; + Util::ReferenceWithSize embeddedItemInfo; + + static const u32 OffsetForLink {0xffffffff}; + static const u32 SizeForLink {0xffffffff}; + + const void* GetFileLocation(const FileBlockBody* fileBlockBody) const { + // TODO: check if this is int or u32 comparison + if (static_cast(embeddedItemInfo.offset) == OffsetForLink) + return nullptr; + + return util::ConstBytePtr(fileBlockBody, embeddedItemInfo.offset).Get(); + } + }; + static_assert(sizeof(GroupItemInfo) == 0x10); + + struct FileBlockBody {/* empty structure */}; + + struct FileBlock { + BinaryBlockHeader header; + FileBlockBody body; + }; + + struct GroupItemInfoEx; + struct InfoExBlockBody { + Util::ReferenceTable referenceTableOfGroupItemInfoEx; + + u32 GetGroupItemInfoExCount() const { + return referenceTableOfGroupItemInfoEx.count; + } + + const GroupItemInfoEx* GetGroupItemInfoEx(u32 index) const { + if (GetGroupItemInfoExCount() <= index) + return nullptr; + + return util::ConstBytePtr(this, referenceTableOfGroupItemInfoEx.item[index].offset) + .Get(); + } + }; + + struct InfoExBlock { + BinaryBlockHeader header; + InfoExBlockBody body; + }; + + struct GroupItemInfoEx { + u32 itemId; + u32 loadFlag; + }; + static_assert(sizeof(GroupItemInfoEx) == 0x8); + +}; + +} // namespace nn::atk::detail \ No newline at end of file diff --git a/include/nn/atk/atk_GroupFileReader.h b/include/nn/atk/atk_GroupFileReader.h new file mode 100644 index 00000000..9d46b3ae --- /dev/null +++ b/include/nn/atk/atk_GroupFileReader.h @@ -0,0 +1,30 @@ +#pragma once + +#include + +namespace nn::atk::detail { +struct GroupItemLocationInfo { + u32 fileId; + void* address; +}; +static_assert(sizeof(GroupItemLocationInfo) == 0x10); + +class GroupFileReader { +public: + constexpr static s64 SignatureFile = 0x50524746; // FGRP + + explicit GroupFileReader(const void* groupFile); + + bool ReadGroupItemLocationInfo(GroupItemLocationInfo* out, u32 index) const; + + u32 GetGroupItemExCount() const; + + bool ReadGroupItemInfoEx(GroupFile::GroupItemInfoEx* out, u32 index) const; + +private: + GroupFile::InfoBlockBody* m_pInfoBlockBody; + GroupFile::FileBlockBody* m_pFileBlockBody; + GroupFile::InfoExBlockBody* m_pInfoExBlockBody; +}; +static_assert(sizeof(GroupFileReader) == 0x18); +} // namespace nn::atk::detail \ No newline at end of file diff --git a/include/nn/atk/atk_HardwareManager.h b/include/nn/atk/atk_HardwareManager.h new file mode 100644 index 00000000..2fc5a44e --- /dev/null +++ b/include/nn/atk/atk_HardwareManager.h @@ -0,0 +1,276 @@ +#pragma once + +#include + +#include +#include + +#include +#include +#include +#include +#include + +namespace nn::atk::detail::driver { +class HardwareManager { // inherits nn::atk::Util::Singleton +public: + using SubMixList = util::IntrusiveList>; + + constexpr static u32 SoundFrameIntervalMsec = 5; + constexpr static u32 SoundFrameIntervalUsec = 5000; + + constexpr static u32 DefaultRendererSampleRate = 48000; + constexpr static u32 DefaultRendererUserEffectCount = 10; + constexpr static u32 DefaultRendererVoiceCountMax = 96; + + constexpr static u32 DefaultRecordingAudioFrameCount = 8; + + constexpr static u32 AtkVoiceCountMax = 192; + constexpr static u32 MixerCount = 3; + constexpr static u32 ChannelCountMax = 6; + constexpr static u32 BusCount = 4; + + constexpr static u32 DefaultRendererSampleCount = 140; + constexpr static u32 DefaultRendererMixBufferCount = 30; + constexpr static u32 DefaultRendererSubMixCount = 1; + constexpr static u32 DefaultRendererSinkCount = 1; + constexpr static u32 DefaultRendererPerformanceFrameCount = 0; + constexpr static u32 DefaultRendererSystemEffectCount = 4; + + constexpr static u32 SubMixCountMax = 2; + + constexpr static u32 SubMixCountForAdditionalEffect = 1; + constexpr static u32 ChannelCountForAdditionalEffect = 2; + constexpr static u32 AuxBusCountForAdditionalEffect = 2; + constexpr static u32 MixBufferCountForAdditionalEffect = 6; + + constexpr static bool DefaultRendererIsVoiceDropEnabled = false; + + class EffectAuxListScopedLock { + public: + EffectAuxListScopedLock(); + ~EffectAuxListScopedLock(); + }; + void LockEffectAuxList(); + void UnlockEffectAuxList(); + + class EffectAuxListForFinalMixScopedLock { + public: + EffectAuxListForFinalMixScopedLock(); + ~EffectAuxListForFinalMixScopedLock(); + }; + void LockEffectAuxListForFinalMix(); + void UnlockEffectAuxListForFinalMix(); + + class EffectAuxListForAdditionalSubMixScopedLock { + public: + EffectAuxListForAdditionalSubMixScopedLock(); + ~EffectAuxListForAdditionalSubMixScopedLock(); + }; + void LockEffectAuxListForAdditionalSubMix(); + void UnlockEffectAuxListForAdditionalSubMix(); + + class SubMixListScopedLock { + public: + SubMixListScopedLock(); + ~SubMixListScopedLock(); + }; + void LockSubMixList(); + void UnlockSubMixList(); + + class UpdateAudioRendererScopedLock { + public: + UpdateAudioRendererScopedLock(); + ~UpdateAudioRendererScopedLock(); + }; + + class HardwareManagerParameter { + public: + void SetSubMixParameter(bool isStereoModeEnabled, bool isEffectEnabled, + bool isSubMixEnabled, bool isAdditionalEffectBusEnabled, + bool isAdditionalSubMixEnabled, bool isCustomSubMixEnabled, + s32 customSubMixCount, s32 customMixTotalChannelCount); + + private: + s32 m_RendererSampleRate; + s32 m_UserEffectCount; + s32 m_VoiceCount; + s32 m_RecordingAudioFrameCount; + s32 m_SubMixCount; + s32 m_MixBufferCount; + bool m_IsProfilerEnabled; + bool m_IsAdditionalEffectBusEnabled; + bool m_IsAdditionalSubMixEnabled; + bool m_IsEffectEnabled; + bool m_IsRecordingEnabled; + bool m_IsUserCircularBufferSinkEnabled; + bool m_IsPresetSubMixEnabled; + bool m_IsStereoModeEnabled; + bool m_IsSoundThreadEnabled; + bool m_IsVoiceDropEnabled; + bool m_IsCompatibleDownMixSettingEnabled; + bool m_IsPreviousSdkVersionLowPassFilterCompatible; + bool m_IsUnusedEffectChannelMutingEnabled; + bool m_IsCompatibleBusVolumeEnabled; + bool m_IsUserThreadRenderingEnabled; + bool m_IsCustomSubMixEnabled; + bool m_IsMemoryPoolAttachCheckEnabled; + }; + static_assert(sizeof(HardwareManagerParameter) == 0x2c); + + HardwareManager(); + + void ResetParameters(); + + audio::MemoryPoolState GetMemoryPoolState(audio::MemoryPoolType* pPool); + + void SetupAudioRendererParameter(audio::AudioRendererParameter* audioRendererParameter, + const HardwareManagerParameter& hardwareManagerParameter) const; + + size_t GetRequiredMemSize(const HardwareManagerParameter& hardwareManagerParameter) const; + size_t GetRequiredMemSizeForMemoryPool(s32 voiceCount) const; + size_t GetRequiredRecorderWorkBufferSize(const HardwareManagerParameter& hardwareManagerParameter) const; + size_t GetRequiredCircularBufferSinkWithMemoryPoolBufferSize(const HardwareManagerParameter& hardwareManagerParameter) const; + size_t GetRequiredCircularBufferSinkBufferSize(const HardwareManagerParameter& hardwareManagerParameter) const; + + s32 GetChannelCountMax() const; + + bool RegisterRecorder(DeviceOutRecorder* pRecorder); + bool UnregisterRecorder(DeviceOutRecorder* pRecorder); + void UpdateRecorder(); + + u64 ReadRecordingCircularBufferSink(void* buffer, size_t bufferSize); + audio::CircularBufferSinkType* AllocateRecordingCircularBufferSink(); + void FreeRecordingCircularBufferSink(audio::CircularBufferSinkType* circularBufferSink); + void StartRecordingCircularBufferSink(); + + void StopUserCircularBufferSink(); + void StartUserCircularBufferSink(bool isForceStartMode); + u64 ReadUserCircularBufferSink(void* buffer, size_t bufferSize); + + void AttachMemoryPool(audio::MemoryPoolType* pPool, void* buffer, + size_t bufferSize, bool isSoundThreadEnabled); + + Result RequestUpdateAudioRenderer(); + + void DetachMemoryPool(audio::MemoryPoolType* pPool, bool isSoundThreadEnabled); + + void ExecuteAudioRendererRendering(); + + void WaitAudioRendererEvent(); + + s32* GetDroppedLowLevelVoiceCount() const; + + size_t GetRequiredPerformanceFramesBufferSize(HardwareManagerParameter* hardwareManagerParameter); + + Result Initialize(void* buffer, size_t bufferSize, void* memoryPoolBuffer, size_t memoryPoolBufferSize, + void* circularBufferSinkBuffer, size_t circularBufferSinkBufferSize, + HardwareManagerParameter* parameter); + + void SetBiquadFilterCallback(s32, const BiquadFilterCallback* callback); + void SetOutputMode(OutputMode mode,OutputDevice device); + + void UpdateEndUserOutputMode(); + + void Finalize(); + + void Update(); + void UpdateEffect(); + + void SuspendAudioRenderer(); + void ResumeAudioRenderer(); + + bool TimedWaitAudioRendererEvent(TimeSpan timeout); + + void SetAudioRendererRenderingTimeLimit(s32 timeLimit); + s32 GetAudioRendererRenderingTimeLimit(); + + void PrepareReset(); + + bool IsResetReady() const; + + void AddSubMix(SubMix* pSubMix); + void RemoveSubMix(SubMix* pSubMix); + + SubMix* GetSubMix(s32 subMixNumber); + SubMix* GetSubMix(s32 subMixNumber) const; + + s32 GetSubMixCount() const; + s32 GetChannelCount() const; + f32 GetOutputVolume() const; + + void SetOutputDeviceFlag(s32, u8); + + void SetMasterVolume(f32 volume, s32 fadeFrames); + void SetSrcType(SampleRateConverterType sampleRateConverter); + + size_t GetRequiredEffectAuxBufferSize(const EffectAux* pEffect) const; + + void SetAuxBusVolume(AuxBus bus, f32 volume, s32 fadeFrames, s32 subMixIndex); + f32 GetAuxBusVolume(AuxBus bus, s32 subMixIndex) const; + + void SetMainBusChannelVolumeForAdditionalEffect(f32 volume, s32 srcChannel, s32 dstChannel); + f32 GetMainBusChannelVolumeForAdditionalEffect(s32 srcChannel, s32 dstChannel) const; + + void SetAuxBusChannelVolumeForAdditionalEffect(AuxBus bus, f32 volume, s32 srcChannel, s32 dstChannel); + f32 GetAuxBusChannelVolumeForAdditionalEffect(AuxBus bus, s32 srcChannel, s32 dstChannel) const; + + static void FlushDataCache(void* address, size_t length); + +private: + bool m_IsInitialized; + audio::AudioRendererHandle m_RendererHandle; + audio::AudioRendererConfig m_Config; + os::SystemEvent m_SystemEvent; + s32 m_AudioRendererSuspendCount; + std::atomic_ulong m_AudioRendererUpdateCount; + void* m_pAudioRendererWorkBuffer; + void* m_pAudioRendererConfigWorkBuffer; + OutputMode m_OutputMode[1]; + OutputMode m_EndUserOutputMode[1]; + SampleRateConverterType m_SrcType; + MoveValue m_MasterVolume; + MoveValue m_VolumeForReset; + BiquadFilterCallback* m_BiquadFilterCallbackTable[128]; + u8 m_OutputDeviceFlag[32]; + LowLevelVoiceAllocator m_LowLevelVoiceAllocator; + FinalMix m_FinalMix; + SubMix m_SubMix[SubMixCountMax]; + SubMix m_AdditionalSubMix; + SubMixList m_SubMixList; + fnd::CriticalSection m_SubMixListLock; + audio::AudioRendererParameter m_AudioRendererParameter; + audio::DeviceSinkType m_Sink; + MoveValue m_AuxUserVolume[MixerCount]; + MoveValue m_AuxUserVolumeForAdditionalEffect[AuxBusCountForAdditionalEffect]; + bool m_IsInitializedEffect; + bool m_IsPresetSubMixEnabled; + bool m_IsAdditionalEffectEnabled; + bool m_IsAdditionalSubMixEnabled; + bool m_IsStereoModeEnabled; + bool m_IsInitializedSoundThread; + bool m_IsPreviousSdkVersionLowPassFilterCompatible; + bool m_IsMemoryPoolAttachCheckEnabled; + fnd::CriticalSection m_UpdateAudioRendererLock; + fnd::CriticalSection m_UpdateHardwareManagerLock; + fnd::CriticalSection m_EffectAuxListLock; + fnd::CriticalSection m_EffectAuxListForFinalMixLock; + fnd::CriticalSection m_EffectAuxListForAdditionalSubMixLock; + audio::CircularBufferSinkType m_RecordingCircularBufferSink; + CircularBufferSinkState m_RecordingCircularBufferSinkState; + audio::MemoryPoolType m_RecordingCircularBufferSinkMemoryPool; + void* m_RecordingCircularBufferSinkBuffer; + bool m_IsRecordingCircularBufferSinkAllocated; + void* m_RecordingBuffer; + size_t m_RecordingBufferSize; + DeviceOutRecorder* m_pRecorder; + audio::CircularBufferSinkType m_UserCircularBufferSink; + audio::MemoryPoolType m_UserCircularBufferSinkMemoryPool; + void* m_UserCircularBufferSinkBuffer; + size_t m_UserCircularBufferSinkBufferSize; + CircularBufferSinkState m_UserCircularBufferSinkState; + bool m_IsCompatibleBusVolumeEnabled; + bool m_IsUserThreadRenderingEnabled; +}; +static_assert(sizeof(HardwareManager) == 0xa58); +} // namespace nn::atk::detail::driver \ No newline at end of file diff --git a/include/nn/atk/atk_InstancePool.h b/include/nn/atk/atk_InstancePool.h new file mode 100644 index 00000000..702584bf --- /dev/null +++ b/include/nn/atk/atk_InstancePool.h @@ -0,0 +1,79 @@ +#pragma once + +#include + +namespace nn::atk::detail { + +class PoolImpl { +public: + static const int DefaultAlignment{4}; + + PoolImpl() = default; + +protected: + int CreateImpl(void* buffer, size_t size, size_t objSize); + int CreateImpl(void* buffer, size_t size, size_t objSize, size_t alignment); + + void DestroyImpl(); + + int CountImpl() const; + + void* AllocImpl(); + + void FreeImpl(void* ptr); + +private: + PoolImpl* m_pNext; + void* m_pBuffer; + size_t m_BufferSize; +}; +static_assert(sizeof(PoolImpl) == 0x18); + +class BufferPool : PoolImpl { +public: + static const int DefaultAlignment{4}; + + BufferPool() = default; + + int Create(void* buffer, size_t size, size_t bufferSizePerAlloc); + int Create(void* buffer, size_t size, size_t bufferSizePerAlloc, size_t alignment); + + void Destroy() { DestroyImpl(); } + + int Count() const { return CountImpl(); } + + void* Alloc() { return AllocImpl(); } + + void Free(void* ptr) { FreeImpl(ptr); } + +}; + +template +class InstancePool : PoolImpl { +public: + static const int DefaultAlignment{4}; + + InstancePool() = default; + + int Create(void* buffer, size_t size) { + size_t objSize{sizeof(T)}; + + return CreateImpl(buffer, size, objSize); + } + + int Create(void* buffer, size_t size, size_t alignment) { + size_t objSize{sizeof(T)}; + + return CreateImpl(buffer, size, objSize, alignment); + } + + void Destroy() { DestroyImpl(); } + + int Count() const { return Count(); } + + T* Alloc() { return reinterpret_cast(AllocImpl()); } + + void Free(T* obj) { FreeImpl(obj); } +}; + +} // namespace nn::atk::detail \ No newline at end of file diff --git a/include/nn/atk/atk_ItemType.h b/include/nn/atk/atk_ItemType.h new file mode 100644 index 00000000..574f6ca7 --- /dev/null +++ b/include/nn/atk/atk_ItemType.h @@ -0,0 +1,12 @@ +#pragma once + +namespace nn::atk::detail { +enum ItemType { + ItemType_Sound = 1, + ItemType_SoundGroup, + ItemType_Bank, + ItemType_Player, + ItemType_WaveArchive, + ItemType_Group, +}; +} // namespace nn::atk::detail \ No newline at end of file diff --git a/include/nn/atk/atk_LoaderManager.h b/include/nn/atk/atk_LoaderManager.h new file mode 100644 index 00000000..98063445 --- /dev/null +++ b/include/nn/atk/atk_LoaderManager.h @@ -0,0 +1,26 @@ +#pragma once + +#include + +namespace nn::atk::detail { +template +class LoaderManager : driver::SoundThread::SoundFrameCallback { +public: + using List = util::IntrusiveList>; + using Iterator = typename List::iterator; + + LoaderManager(); + ~LoaderManager() override; + + void OnBeginSoundFrame() override; + + void Destroy(); + +private: + void* m_pBuffer; + size_t m_BufferSize; + List m_FreeList; + List m_FreeReqList; +}; + +} // namespace nn::atk::detail \ No newline at end of file diff --git a/include/nn/atk/atk_LowLevelVoice.h b/include/nn/atk/atk_LowLevelVoice.h new file mode 100644 index 00000000..e29ba08c --- /dev/null +++ b/include/nn/atk/atk_LowLevelVoice.h @@ -0,0 +1,137 @@ +#pragma once + +#include + +#include +#include +#include + +#include +#include +#include +#include + +namespace nn::atk::detail { + +class Voice; + +class alignas(64) LowLevelVoice { +public: + LowLevelVoice(); + + void Initialize(); + void Finalize(); + + bool IsAvailable() const; + void SetAvailable(bool isAvailable); + + bool IsVoiceDroppedFlagOn() const; + + void AppendWaveBuffer(WaveBuffer* waveBuffer); + void FreeAllWaveBuffer(); + + void UpdateState(OutputMode outputMode); + + void SetVoiceParam(const VoiceParam& voiceParam) { m_VoiceParam = voiceParam; } + + void SetPriority(s32 priority); + void SetState(VoiceState state); + + void SetSampleRate(s32 sampleRate) { m_SampleRate = sampleRate; } + void SetSampleFormat(SampleFormat sampleFormat) { m_SampleFormat = sampleFormat; } + void SetAdpcmParam(const AdpcmParam& adpcmParam) { m_AdpcmParam = adpcmParam; } + + position_t GetPlayPosition() const { return m_PlayPosition; } + + void SetVoice(Voice* pVoice) { m_pVoice = pVoice; } +#if NN_SDK_VER >= NN_MAKE_VER(4, 0, 0) + void SetOutputReceiver(OutputReceiver* pOutputReceiver) { m_pOutputReceiver = pOutputReceiver; } +#endif + + void UpdateVoiceInfo(VoiceInfo* voiceInfo) const; + + audio::NodeId GetNodeId() { return m_NodeId; } + +private: + void UpdateVoiceParam(const VoiceParam& voiceParam, OutputMode outputMode); + bool AllocVoice(); + + void UpdateStatePlay(bool isRun, OutputMode outputMode); + void UpdateStateStop(bool isRun); + void UpdateStatePause(bool isRun, OutputMode outputMode); + void UpdatePlayPosition(); + + void UpdateWaveBuffer(bool isRun, OutputMode outputMode); + void UpdateWaveBufferOnPlayState(); + void UpdateWaveBufferOnStopState(OutputMode outputMode); + bool AppendWaveBufferToVoice(WaveBuffer* waveBuffer); + + void UpdateMixVolume(const OutputMix& outputMix, OutputMode outputMode); + void UpdateMixVolumeOnSubMix(const OutputMix& outputMix, OutputMode outputMode); + void UpdateMixVolumeOnFinalMix(const OutputMix& outputMix, OutputMode outputMode); + + void UpdateVolume(const VoiceParam& voiceParam); + void UpdatePitch(const VoiceParam& voiceParam); + void UpdateBiquadFilter(const VoiceParam& voiceParam); + void UpdateLowPassFilter(const VoiceParam& voiceParam); + + void SetVoiceMixVolume(float mixVolume, int destinationIndex); + + float GetClampedVoiceVolume(float volume); + + AdpcmParam m_AdpcmParam{}; + audio::VoiceType m_Voice{}; + bool m_IsAvailable{false}; + bool m_IsSetVoiceSlot{false}; + VoiceParam m_VoiceParam{}; + int32_t m_Priority{0}; + VoiceState m_State{VoiceState_Stop}; + u32 m_SampleRate{32000}; + SampleFormat m_SampleFormat{SampleFormat_PcmS16}; + position_t m_PlayPosition{0}; +#if NN_SDK_VER < NN_MAKE_VER(4, 0, 0) + u32 _c0{0}; // 0xc0 +#else + OutputReceiver* m_pOutputReceiver{}; +#endif + WaveBuffer* m_WaveBufferListBegin{}; + WaveBuffer* m_WaveBufferListEnd{}; + WaveBuffer* m_LastAppendBuffer{}; + Voice* m_pVoice{}; + audio::NodeId m_NodeId{0}; +#if NN_SDK_VER < NN_MAKE_VER(4, 0, 0) + bool _ec{true}; //0xec +#endif +}; +static_assert(sizeof(LowLevelVoice) == 0x100); + +class LowLevelVoiceAllocator { +public: + constexpr static u32 Unassigned = -1; + + LowLevelVoiceAllocator(); + + size_t GetRequiredMemSize(s32 voiceCount); + + void Initialize(s32 voiceCount, void* mem, size_t memSize); + void Finalize(); + + void UpdateAllVoiceState(OutputMode outputMode); + + LowLevelVoice* AllocVoice(); + u64 GetVoiceArrayIndex(LowLevelVoice* pVoice); + void FreeVoice(LowLevelVoice* pVoice); + + s32* GetDroppedVoiceCount() const; + +private: + void* m_pVoiceArray{}; + LowLevelVoice** m_ppVoiceTable{}; + s32 m_UsingCount{0}; + s32* m_pAssignedTableIndex{}; + s32 m_VoiceCount{}; + std::atomic m_DroppedVoiceCount{0}; +}; +static_assert(sizeof(LowLevelVoiceAllocator) == 0x28); + +} // namespace nn::atk::detail \ No newline at end of file diff --git a/include/nn/atk/atk_MmlCommand.h b/include/nn/atk/atk_MmlCommand.h new file mode 100644 index 00000000..6a9852cc --- /dev/null +++ b/include/nn/atk/atk_MmlCommand.h @@ -0,0 +1,79 @@ +#pragma once + +namespace nn::atk::detail::driver { +class MmlCommand { +public: + enum Mml { + Mml_Wait = 0x80, + Mml_Prg, + + Mml_OpenTrack = 0x88, + Mml_Jump, + Mml_Call, + + Mml_Random = 0xa0, + Mml_Variable, + Mml_If, + Mml_Time, + Mml_TimeRandom, + Mml_TimeVariable, + + Mml_Timebase = 0xb0, + Mml_EnvHold, + Mml_Monophonic, + Mml_VelocityRange, + Mml_BiquadType, + Mml_BiquadValue, + Mml_BankSelect, + + Mml_ModPhase = 0xbd, + Mml_ModCurve, + Mml_FrontBypass, + Mml_Pan, + Mml_Volume, + Mml_MainVolume, + Mml_Transpose, + Mml_PitchBend, + Mml_BendRange, + Mml_Prio, + Mml_NoteWait, + Mml_Tie, + Mml_Porta, + Mml_ModDepth, + Mml_ModSpeed, + Mml_ModType, + Mml_ModRange, + Mml_PortaSw, + Mml_PortaTime, + Mml_Attack, + Mml_Decay, + Mml_Sustain, + Mml_Release, + Mml_LoopStart, + Mml_Volume2, + Mml_Printvar, + Mml_SurroundPan, + Mml_LpfCutoff, + Mml_FxsendA, + Mml_FxsendB, + Mml_Mainsend, + Mml_InitPan, + Mml_Mute, + Mml_FxsendC, + Mml_Damper, + Mml_ModDelay, + Mml_Tempo, + + Mml_SweepPitch = 0xe3, + Mml_ModPeriod, + + Mml_ExCommand = 0xf0, + + Mml_EnvReset = 0xfb, + Mml_LoopEnd, + Mml_Ret, + Mml_AllocTrack, + Mml_Fin, + }; +}; +} // namespace nn::atk::detail::driver \ No newline at end of file diff --git a/include/nn/atk/atk_MmlParser.h b/include/nn/atk/atk_MmlParser.h new file mode 100644 index 00000000..8c91df9d --- /dev/null +++ b/include/nn/atk/atk_MmlParser.h @@ -0,0 +1,48 @@ +#pragma once + +#include + +namespace nn::atk::detail::driver { +class MmlParser { +public: + enum SeqArgType { + SeqArgType_None, + SeqArgType_U8, + SeqArgType_S16, + SeqArgType_Vmidi, + SeqArgType_Random, + SeqArgType_Variable, + }; + + constexpr static u32 PanCenter = 64; + constexpr static u32 SurroundPanCenter = PanCenter; + + constexpr static u32 TempoMin = 0; + constexpr static u32 TempoMax = 1023; + + MmlParser(); + virtual ~MmlParser(); + + SequenceTrack::ParseResult Parse(MmlSequenceTrack* track, bool doNoteOn) const; + + s32 ReadArg(const u8** ptr, SequenceSoundPlayer* player, SequenceTrack* track, + SeqArgType argType) const; + + s32 Read24(const u8** ptr) const; + s16 Read16(const u8** ptr) const; + + void CommandProc(MmlSequenceTrack* track, u32 command, s32 commandArg1, s32 commandArg2) const; + + s16* GetVariablePtr(SequenceSoundPlayer* player, SequenceTrack* track, s32 varNo) const; + + void NoteOnCommandProc(MmlSequenceTrack* track, s32 key, s32 velocity, s32 length, bool tieFlag) const; + + s16 ReadVar(const u8** ptr) const; + + static u32 ParseAllocTrack(const void* baseAddress, u32 seqOffset, u32* allocTrack); + +private: + static bool* mPrintVarEnabledFlag; +}; +static_assert(sizeof(MmlParser) == 0x8); +} // namespace nn::atk::detail::driver \ No newline at end of file diff --git a/include/nn/atk/atk_MmlSequenceTrack.h b/include/nn/atk/atk_MmlSequenceTrack.h new file mode 100644 index 00000000..88c2b270 --- /dev/null +++ b/include/nn/atk/atk_MmlSequenceTrack.h @@ -0,0 +1,18 @@ +#pragma once + +#include + +namespace nn::atk::detail::driver { +class MmlParser; +class MmlSequenceTrack : SequenceTrack { +public: + MmlSequenceTrack(); + ~MmlSequenceTrack() override; + + ParseResult Parse(bool doNoteOn) override; + +private: + MmlParser* m_pParser; +}; +static_assert(sizeof(MmlSequenceTrack) == 0x1f0); +} // namespace nn::atk::detail::driver \ No newline at end of file diff --git a/include/nn/atk/atk_MmlSequenceTrackAllocator.h b/include/nn/atk/atk_MmlSequenceTrackAllocator.h new file mode 100644 index 00000000..caf05f91 --- /dev/null +++ b/include/nn/atk/atk_MmlSequenceTrackAllocator.h @@ -0,0 +1,27 @@ +#pragma once + +#include +#include +#include + +namespace nn::atk::detail::driver { +class MmlSequenceTrackAllocator : SequenceTrackAllocator { +public: + using MmlSequenceTrackPool = InstancePool; + + SequenceTrack* AllocTrack(SequenceSoundPlayer* player) override; + + void FreeTrack(SequenceTrack* track); + + s32 Create(void* buffer, size_t size); + + void Destroy(); + + s32 GetAllocatableTrackCount(); + +private: + MmlParser* m_pParser; + MmlSequenceTrackPool m_TrackPool; +}; +static_assert(sizeof(MmlSequenceTrackAllocator) == 0x28); +} // namespace nn::atk::detail \ No newline at end of file diff --git a/include/nn/atk/atk_MoveValue.h b/include/nn/atk/atk_MoveValue.h new file mode 100644 index 00000000..5543f244 --- /dev/null +++ b/include/nn/atk/atk_MoveValue.h @@ -0,0 +1,33 @@ +#pragma once + +namespace nn::atk::detail { +template +class MoveValue { +public: + MoveValue() = default; + + void InitValue(ValueType value) { + m_Origin = value; + m_Target = value; + m_Counter = 0; + m_Frame = 0; + } + + void SetTarget(ValueType targetValue, CountType frames); + + ValueType GetTarget() const; + ValueType GetValue() const; + + void Update(); + + bool IsFinished() const; + + CountType GetRemainingCount() const; + +private: + ValueType m_Origin {}; + ValueType m_Target {}; + CountType m_Frame {}; + CountType m_Counter {}; +}; +} // namespace nn::atk::detail \ No newline at end of file diff --git a/include/nn/atk/atk_MultiVoice.h b/include/nn/atk/atk_MultiVoice.h new file mode 100644 index 00000000..4e610381 --- /dev/null +++ b/include/nn/atk/atk_MultiVoice.h @@ -0,0 +1,198 @@ +#pragma once + +#include + +#include +#include + +namespace nn::atk::detail::driver { +class MultiVoiceManager; + +class MultiVoice { +public: + struct PreMixVolume { + f32 volume[24]; + }; + static_assert(sizeof(PreMixVolume) == 0x60); + + enum VoiceCallbackStatus { + VoiceCallbackStatus_FinishWave, + VoiceCallbackStatus_Cancel, + VoiceCallbackStatus_DropVoice, + VoiceCallbackStatus_DropDsp, + }; + + using VoiceCallback = void(*)(MultiVoice*,VoiceCallbackStatus,void*); + + constexpr static u32 UpdateStart = 0b0000001; + constexpr static u32 UpdatePause = 0b0000010; + constexpr static u32 UpdateSrc = 0b0000100; + constexpr static u32 UpdateMix = 0b0001000; + constexpr static u32 UpdateLpf = 0b0010000; + constexpr static u32 UpdateBiquad = 0b0100000; + constexpr static u32 UpdateVe = 0b1000000; + + constexpr static u32 PriorityNoDrop = 255; + + constexpr static f32 VolumeMin = 0.0; + constexpr static f32 VolumeDefault = 1.0; + constexpr static f32 VolumeMax = 2.0; + + constexpr static f32 PanLeft = -1.0; + constexpr static f32 PanCenter = 0.0; + constexpr static f32 PanRight = 1.0; + + constexpr static f32 SpanFront = 0.0; + constexpr static f32 SpanCenter = 1.0; + constexpr static f32 SpanRear = 2.0; + + constexpr static f32 CutoffFreqMin = 0.0; + constexpr static f32 CutoffFreqMax = 1.0; + + constexpr static f32 BiquadValueMin = 0.0; + constexpr static f32 BiquadValueMax = 1.0; + + constexpr static f32 SendMin = 0.0; + constexpr static f32 SendMax = 1.0; + + explicit MultiVoice(); +#if NN_SDK_VER >= NN_MAKE_VER(4, 0, 0) + explicit MultiVoice(OutputAdditionalParam* pAdditionalParam); +#endif + ~MultiVoice(); + + bool Alloc(s32 channelCount, s32 priority, VoiceCallback callback, void* callbackData); + void InitParam(VoiceCallback callback, void* callbackData); + void Free(); + + void Start(); + void Stop(); + void StopAllSdkVoice(); + + void UpdateVoiceStatus(); + + bool IsPlayFinished() const; + + void Pause(bool flag); + + void Calc(); + void CalcSrc(bool); + void CalcVe(); + void CalcMix(); + void CalcLpf(); + void CalcBiquadFilter(); + + void Update(); + + void RunAllSdkVoice(); + void PauseAllSdkVoice(); + + void SetSampleFormat(SampleFormat format); + void SetSampleRate(s32 sampleRate); + void SetVolume(f32 volume); + void SetPitch(f32 pitch); + void SetPanMode(PanMode panMode); + void SetPanCurve(PanCurve panCurve); + void SetLpfFreq(f32 lpfFreq); + void SetBiquadFilter(s32 type, f32 value); + void SetPriority(s32 priority); + void SetOutputLine(u32 lineFlag); + void SetOutputParamImpl(const OutputParam& in, const OutputParam& out); +#if NN_SDK_VER >= NN_MAKE_VER(4, 0, 0) + void SetOutputAdditionalParamImpl(const SendArray* pAdditionalSend, + const BusMixVolumePacket* pBusMixVolumePacket, + const OutputBusMixVolume* pBusMixVolume, + const VolumeThroughModePacket* pVolumeThroughModePacket); +#endif + void SetOutputBusMixVolumeImpl(const BusMixVolumePacket& in, + const OutputBusMixVolume& busMixVolume, + const BusMixVolumePacket& out); + void SetOutputVolumeThroughModePacketImpl(const VolumeThroughModePacket&, + const VolumeThroughModePacket&); + void SetTvParam(const OutputParam& param); +#if NN_SDK_VER >= NN_MAKE_VER(4, 0, 0) + void SetTvAdditionalParam(const OutputAdditionalParam& param); + void SetTvAdditionalParam(const SendArray* pAdditionalSend, + const BusMixVolumePacket* pBusMixVolumePacket, + const OutputBusMixVolume* pBusMixVolume, + const VolumeThroughModePacket* pVolumeThroughModePacket); + void SetOutputReceiver(OutputReceiver* pOutputReceiver); +#endif + void SetSubMixIndex(s32 subMixIndex); + +#if NN_SDK_VER < NN_MAKE_VER(4, 0, 0) + void CalcPreMixVolume(PreMixVolume* mix, const OutputParam& param, + s32 channelIndex, OutputDevice device); +#else + void CalcPreMixVolume(PreMixVolume* mix, const OutputParam& param, + OutputAdditionalParam* pAdditionalParam, s32 channelIndex, + OutputDevice device); +#endif + void CalcTvMix(OutputMix* mix, const PreMixVolume& pre); + void CalcMixImpl(OutputMix* mix, u32 outputDeviceIndex, + const OutputParam& param, const PreMixVolume& pre); +#if NN_SDK_VER >= NN_MAKE_VER(4, 0, 0) + void CalcMixImpl(OutputMix* mix, u32 outputDeviceIndex, const OutputParam& param, + OutputAdditionalParam* pAdditionalParam, const PreMixVolume& pre); +#endif + + MultiVoice* detail_GetSdkVoice(s32) const; + + position_t GetCurrentPlayingSample() const; + SampleFormat GetFormat() const; + + bool IsRun() const; + + void SetInterpolationType(u8 interpolationType); + + void AppendWaveBuffer(s32 channelIndex, WaveBuffer* pBuffer, bool lastFlag); + + void SetAdpcmParam(s32 channelIndex, const AdpcmParam& param); + + static u64 FrameToByte(s64, SampleFormat); + static void CalcOffsetAdpcmParam(AdpcmContext* context, const AdpcmParam& param, + position_t offsetSamples, const void* dataAddress); + +private: + friend MultiVoiceManager; + + Voice m_Voice[2]; + s32 m_ChannelCount; + VoiceCallback m_Callback; + void* m_pCallbackData; + bool m_IsActive; + bool m_IsStart; + bool m_IsStarted; + bool m_IsPause; + bool m_IsPausing; + bool m_IsInitialized; + WaveBuffer* m_pLastWaveBuffer; + u16 m_SyncFlag; + u8 m_BiquadType; + bool m_IsEnableFrontBypass; + f32 m_Volume; + f32 m_Pitch; + PanMode m_PanMode; + PanCurve m_PanCurve; + f32 m_LpfFreq; + f32 m_BiquadValue; + s32 m_Priority; + u32 m_OutputLineFlag; + OutputParam m_TvParam; +#if NN_SDK_VER >= NN_MAKE_VER(4, 0, 0) + OutputAdditionalParam* m_pTvAdditionalParam; +#endif + SampleFormat m_Format; + std::uintptr_t m_VoiceUser; + UpdateType m_UpdateType; +#if NN_SDK_VER >= NN_MAKE_VER(4, 0, 0) + OutputReceiver* m_pOutputReceiver; +#endif + util::IntrusiveListNode m_LinkNode; +}; +#if NN_SDK_VER < NN_MAKE_VER(4, 0, 0) +static_assert(sizeof(MultiVoice) == 0x270); +#else +static_assert(sizeof(MultiVoice) == 0x298); +#endif +} // namespace nn::atk::detail \ No newline at end of file diff --git a/include/nn/atk/atk_MultiVoiceManager.h b/include/nn/atk/atk_MultiVoiceManager.h new file mode 100644 index 00000000..2e08495d --- /dev/null +++ b/include/nn/atk/atk_MultiVoiceManager.h @@ -0,0 +1,53 @@ +#pragma once + +#include + +namespace nn::atk::detail::driver { +class MultiVoiceManager { +public: + using VoiceList = util::IntrusiveList>; + + MultiVoiceManager(); + + size_t GetObjectSize(const SoundInstanceConfig& config); + size_t GetRequiredMemSize(s32 voiceCount, const SoundInstanceConfig& config); + + void Initialize(void* mem, size_t memSize, const SoundInstanceConfig& config); + void Finalize(); + + void StopAllVoices(); + + MultiVoice* AllocVoice(s32 voiceChannelCount, s32 priority, + MultiVoice::VoiceCallback callback, void* callbackData); + + bool DropLowestPriorityVoice(s32); + + void AppendVoiceList(MultiVoice* voice); + + void FreeVoice(MultiVoice* voice); + + void RemoveVoiceList(MultiVoice* voice); + + void UpdateAllVoiceStatus(); + void UpdateAudioFrameVoiceStatus(); + void UpdateAllVoices(); + void UpdateAudioFrameVoices(); + + void ChangeVoicePriority(MultiVoice* voice); + + void UpdateAllVoicesSync(u32 syncFlag); + + s32 GetVoiceCount() const; + s32 GetActiveCount() const; + s32 GetFreeCount() const; + + VoiceList* GetVoiceList() const; + +private: + bool m_Initialized; + VoiceList m_PrioVoiceList; + VoiceList m_FreeVoiceList; +}; +static_assert(sizeof(MultiVoiceManager) == 0x28); +} // namespace nn::atk::detail::driver \ No newline at end of file diff --git a/include/nn/atk/atk_NoteOnCallback.h b/include/nn/atk/atk_NoteOnCallback.h new file mode 100644 index 00000000..24c1dda6 --- /dev/null +++ b/include/nn/atk/atk_NoteOnCallback.h @@ -0,0 +1,35 @@ +#pragma once + +#include + +namespace nn::atk::detail::driver { +class SequenceSoundPlayer; + +struct NoteOnInfo { + s32 prgNo; + s32 key; + s32 velocity; + s32 length; + s32 initPan; + s32 priority; + Channel::ChannelCallback channelCallback; + void* channelCallbackData; +#if NN_SDK_VER >= NN_MAKE_VER(4, 0, 0) + OutputReceiver* pOutputReceiver; +#endif + UpdateType updateType; +}; +#if NN_SDK_VER < NN_MAKE_VER(4, 0, 0) +static_assert(sizeof(NoteOnInfo) == 0x30); +#else +static_assert(sizeof(NoteOnInfo) == 0x38); +#endif + +class NoteOnCallback { +public: + virtual ~NoteOnCallback(); + virtual Channel* NoteOn(SequenceSoundPlayer* seqPlayer, u8 bankIndex, + const NoteOnInfo& noteOnInfo) = 0; +}; +static_assert(sizeof(NoteOnCallback) == 0x8); +} // namespace nn::atk::detail \ No newline at end of file diff --git a/include/nn/atk/atk_OutputAdditionalParam.h b/include/nn/atk/atk_OutputAdditionalParam.h new file mode 100644 index 00000000..e135a3e1 --- /dev/null +++ b/include/nn/atk/atk_OutputAdditionalParam.h @@ -0,0 +1,70 @@ +#pragma once + +#include + +#include +#include +#include + +namespace nn::atk::detail { +using SendArray = ValueArray; + +class OutputAdditionalParam { +public: + static size_t GetRequiredMemSize(const SoundInstanceConfig& config); + + void Initialize(void* buffer, size_t bufferSize, const SoundInstanceConfig& config); + void Finalize(); + + void Reset(); + + void* GetBufferAddr(); + + SendArray* GetAdditionalSendAddr(); + SendArray* GetAdditionalSendAddr() const; + + f32 TryGetAdditionalSend(s32 bus) const; + + bool IsAdditionalSendEnabled() const; + + void TrySetAdditionalSend(s32 bus, f32 send); + + BusMixVolumePacket* GetBusMixVolumePacketAddr(); + BusMixVolumePacket* GetBusMixVolumePacketAddr() const; + + f32 GetBusMixVolume(s32 waveChannel, s32 mixChannel) const; + OutputBusMixVolume* GetBusMixVolume() const; + + void SetBusMixVolume(s32 waveChannel, s32 mixChannel, f32 volume); + void SetBusMixVolume(const OutputBusMixVolume& param); + + bool IsBusMixVolumeUsed() const; + void SetBusMixVolumeUsed(bool isUsed); + + bool IsBusMixVolumeEnabledForBus(s32 bus) const; + void SetBusMixVolumeEnabledForBus(s32 bus, bool isEnabled); + + bool IsBusMixVolumeEnabled() const; + + VolumeThroughModePacket* GetVolumeThroughModePacketAddr(); + VolumeThroughModePacket* GetVolumeThroughModePacketAddr() const; + + f32 GetBinaryVolume() const; + void SetBinaryVolume(f32 volume); + + u8 TryGetVolumeThroughMode(s32 bus) const; + void TrySetVolumeThroughMode(s32 bus, u8 volumeThroughMode); + bool IsVolumeThroughModeEnabled() const; + + bool IsVolumeThroughModeUsed(); + void SetVolumeThroughModeUsed(bool isUsed); + + OutputAdditionalParam& operator=(const OutputAdditionalParam& rhs); + +private: + SendArray* m_pAdditionalSend; + BusMixVolumePacket* m_pBusMixVolumePacket; + VolumeThroughModePacket* VolumeThroughModePacket; +}; +static_assert(sizeof(OutputAdditionalParam) == 0x18); +} // namespace nn::atk::detail \ No newline at end of file diff --git a/include/nn/atk/atk_OutputMixer.h b/include/nn/atk/atk_OutputMixer.h new file mode 100644 index 00000000..ebfb5729 --- /dev/null +++ b/include/nn/atk/atk_OutputMixer.h @@ -0,0 +1,54 @@ +#pragma once + +#include + +#include +#include +#include +#include + +namespace nn::atk { +class OutputMixer : protected OutputReceiver { +public: + using EffectList = util::IntrusiveList>; + using EffectAuxList = util::IntrusiveList>; + + OutputMixer(); + + static size_t GetRequiredMemorySize(s32 bus, bool isEffectEnabled); + + void Initialize(s32 bus, bool isEffectEnabled, void* buffer, size_t bufferSize); + void Finalize(); + + bool HasEffect(s32 bus) const; + + bool AppendEffect(EffectBase* pEffect, s32 bus, void* buffer, size_t bufferSize); + bool AppendEffect(EffectAux* pEffect, s32 bus, void* buffer, size_t bufferSize); + + bool RemoveEffect(EffectBase* pEffect, s32 bus); + bool RemoveEffect(EffectAux* pEffect, s32 bus); + + void ClearEffect(s32 bus); + + void UpdateEffectAux(); + + void OnChangeOutputMode(); + + virtual void AppendEffectImpl(EffectBase* pEffect, s32 bus, void* buffer, size_t bufferSize); + virtual void AppendEffectImpl(EffectAux* pEffect, s32 bus, void* buffer, size_t bufferSize); + + void RemoveEffectImpl(EffectBase* pEffect, s32 bus); + void RemoveEffectImpl(EffectAux* pEffect, s32 bus); + + void ClearEffectImpl(s32 bus); + +private: + detail::fnd::CriticalSection m_EffectListLock; + EffectList* m_pEffectList; + EffectAuxList* m_pEffectAuxList; + bool m_IsEffectEnabled; +}; +static_assert(sizeof(OutputMixer) == 0x40); +} // namespace nn::atk \ No newline at end of file diff --git a/include/nn/atk/atk_OutputReceiver.h b/include/nn/atk/atk_OutputReceiver.h new file mode 100644 index 00000000..1630a728 --- /dev/null +++ b/include/nn/atk/atk_OutputReceiver.h @@ -0,0 +1,20 @@ +#pragma once + +#include + +namespace nn::atk { +class OutputReceiver { +public: + enum ReceiverType { + ReceiverType_SubMix, + ReceiverType_FinalMix, + }; + + virtual ReceiverType GetReceiverType() const = 0; + virtual s32 GetChannelCount() const = 0; + virtual s32 GetBusCount() const = 0; + virtual void AddReferenceCount(s32 value) = 0; + virtual bool IsSoundSendClampEnabled(s32 bus) const = 0; +}; +static_assert(sizeof(OutputReceiver) == 0x8); +} // namespace nn::atk \ No newline at end of file diff --git a/include/nn/atk/atk_PlayerHeap.h b/include/nn/atk/atk_PlayerHeap.h new file mode 100644 index 00000000..b7f854b3 --- /dev/null +++ b/include/nn/atk/atk_PlayerHeap.h @@ -0,0 +1,58 @@ +#pragma once + +#include + +#include + +namespace nn::atk { +class SoundPlayer; + +namespace detail { +class PlayerHeap; +class CallbackNode { +private: + friend PlayerHeap; + + util::IntrusiveListNode m_Link; + SoundMemoryAllocatable::DisposeCallback m_Callback; + void* m_CallbackArg; +}; +static_assert(sizeof(CallbackNode) == 0x20); + +class PlayerHeap : SoundMemoryAllocatable { +public: + using CallbackList = util::IntrusiveList< + CallbackNode, util::IntrusiveListMemberNodeTraits< + CallbackNode, &CallbackNode::m_Link>>; + + PlayerHeap(); + ~PlayerHeap() override; + + void Destroy(); + + bool Create(void* startAddress, size_t size); + + void Clear(); + + void* Allocate(size_t size) override; + void* Allocate(size_t size, SoundMemoryAllocatable::DisposeCallback callback, + void* callbackArg) override; + + size_t GetAllocateSize(size_t size, bool needMemoryPool) override; + size_t GetFreeSize() const; + +private: + friend SoundPlayer; + + SoundPlayer* m_pPlayer; + void* m_pStartAddress; + void* m_pEndAddress; + void* m_pAllocAddress; + u8 m_State; + u8 m_Padding[3]; + util::IntrusiveListNode m_Link; + CallbackList m_CallbackList; +}; +static_assert(sizeof(PlayerHeap) == 0x50); +} // namespace nn::atk::detail +} // namespace nn::atk \ No newline at end of file diff --git a/include/nn/atk/atk_PlayerHeapDataManager.h b/include/nn/atk/atk_PlayerHeapDataManager.h new file mode 100644 index 00000000..2f510b7b --- /dev/null +++ b/include/nn/atk/atk_PlayerHeapDataManager.h @@ -0,0 +1,39 @@ +#pragma once + +#include +#include +#include + +namespace nn::atk::detail { +class PlayerHeapDataManager : driver::DisposeCallback, SoundArchiveLoader { +public: + constexpr static u8 FileAddressCount = 9; + + struct FileAddress { + SoundArchive::FileId fileId; + void* address; + }; + static_assert(sizeof(FileAddress) == 0x10); + + PlayerHeapDataManager(); + ~PlayerHeapDataManager() override; + + void Finalize(); + void Initialize(const SoundArchive* arc); + + void* SetFileAddress(SoundArchive::FileId fileId, const void* address); + void* GetFileAddress(SoundArchive::FileId fileId) const; + + void InvalidateData(const void* start, const void* end) override; + + void* SetFileAddressToTable(SoundArchive::FileId fileId, const void* address) override; + void* GetFileAddressFromTable(SoundArchive::FileId fileId) const override; + void* GetFileAddressImpl(SoundArchive::FileId fileId) const override; + +private: + FileAddress m_FileAddress[FileAddressCount]; + bool m_IsInitialized; + bool m_IsFinalized; +}; +static_assert(sizeof(PlayerHeapDataManager) == 0x2c8); +} // namespace nn::atk::detail \ No newline at end of file diff --git a/include/nn/atk/atk_ProfileReader.h b/include/nn/atk/atk_ProfileReader.h new file mode 100644 index 00000000..853e6f4f --- /dev/null +++ b/include/nn/atk/atk_ProfileReader.h @@ -0,0 +1,98 @@ +#pragma once + +#include + +#include +#include +#include + +namespace nn::atk { +struct TimeRange { + os::Tick begin; + os::Tick end; +}; +static_assert(sizeof(TimeRange) == 0x10); + +struct SoundProfile { + TimeRange nwFrameProcess; + TimeRange mainMixProcess; + TimeRange finalMixProcess; + TimeRange voiceProcess; + TimeRange sinkProcess; + TimeRange circularBufferSinkProcess; + TimeRange rendererFrameProcess; + u32 totalVoiceCount; + u32 rendererVoiceCount; + u32 nwVoiceCount; + u64 nwFrameProcessTick; + TimeRange _additionalSubMixProcess; +#if NN_SDK_VER < NN_MAKE_VER(4, 0, 0) + TimeRange _voiceProcessTable[96]; + audio::NodeId _voiceIdTable[96]; +#else + TimeRange _voiceProcessTable[192]; + audio::NodeId _voiceIdTable[192]; +#endif +}; +#if NN_SDK_VER < NN_MAKE_VER(4, 0, 0) +static_assert(sizeof(SoundProfile) == 0x818); +#else +static_assert(sizeof(SoundProfile) == 0xf98); +#endif + +struct SoundThreadUpdateProfile { + TimeRange soundThreadProcess; + TimeRange _updateLowLevelVoiceProcess; + TimeRange _updateRendererProcess; + TimeRange _waitRendererEventProcess; + TimeRange _userEffectFrameProcess; + TimeRange _frameProcess; +}; +static_assert(sizeof(SoundThreadUpdateProfile) == 0x60); + +class ProfileReader { +public: + ProfileReader(); + + size_t Read(SoundProfile*, s32); + + void Record(const SoundProfile& src); + + util::IntrusiveListNode m_Link; + +private: + SoundProfile m_ProfileBuffer[32]; + s32 m_ProfileBufferRead; + s32 m_ProfileBufferWrite; +}; +#if NN_SDK_VER < NN_MAKE_VER(4, 0, 0) +static_assert(sizeof(ProfileReader) == 0x10318); +#else +static_assert(sizeof(ProfileReader) == 0x1f318); +#endif + +using ProfileReaderList = util::IntrusiveList>; + +struct TaskProfile; + +template +class AtkProfileReader { +public: + util::IntrusiveListNode m_List; + +private: + bool m_IsInitialized; + TaskProfile* m_pProfile; + s32 m_ProfileCount; + s32 m_RecordIndex; + s32 m_ReadIndex; + std::atomic_int m_ReadableCount; +}; + +using SoundThreadUpdateProfileReader = AtkProfileReader; +using SoundThreadUpdateProfileReaderList = util::IntrusiveList>; +} // namespace nn::atk \ No newline at end of file diff --git a/include/nn/atk/atk_SequenceSound.h b/include/nn/atk/atk_SequenceSound.h new file mode 100644 index 00000000..771d266e --- /dev/null +++ b/include/nn/atk/atk_SequenceSound.h @@ -0,0 +1,113 @@ +#pragma once + +#include +#include +#include + +namespace nn::atk::detail { +class SequenceSound; +using SequenceSoundInstanceManager = SoundInstanceManager; + +class SequenceSound : BasicSound { +public: + constexpr static u32 BankIndexMin = 0; + constexpr static u32 BankIndexMax = 3; + + constexpr static u8 TransposeMin = 192; + constexpr static u8 TransposeMax = 63; + + constexpr static u8 VelocityRangeMin = 0; + constexpr static u32 VelocityRangeMax = 0x7f00; + + struct Resource { + void* seq; + void* banks[4]; + void* warcs[4]; + bool warcIsIndividuals[4]; + }; + static_assert(sizeof(Resource) == 0x50); + + explicit SequenceSound(SequenceSoundInstanceManager& manager); + ~SequenceSound() override; + +#if NN_SDK_VER < NN_MAKE_VER(4, 0, 0) + bool Initialize() override; +#else + bool Initialize(OutputReceiver* pOutputReceiver) override; +#endif + void Finalize() override; + + void Setup(driver::SequenceTrackAllocator* trackAllocator, u32 allocTracks, + driver::NoteOnCallback* noteOnCallback, s32 channelPriority, + bool isReleasePriorityFix, SequenceUserProcCallback userproc, void* userprocArg); + + void Prepare(const Resource& res, const driver::SequenceSoundPlayer::StartInfo& startInfo); + + void Skip(driver::SequenceSoundPlayer::StartOffsetType, s32); + + void SetTempoRatio(f32 tempoRatio); + + void SetChannelPriority(s32 priority); + void OnUpdatePlayerPriority() override; + + void SetTrackMute(u32 trackBitFlag, SequenceMute mute); + void SetTrackMute(u32 trackBitFlag, bool); + void SetTrackSilence(u32 trackBitFlag, bool, s32); + void SetTrackVolume(u32 trackBitFlag, f32 volume); + void SetTrackPitch(u32 trackBitFlag, f32 pitch); + void SetTrackMainOutVolume(u32 trackBitFlag, f32 volume); + void SetTrackChannelMixParameter(u32 trackBitFlag, u32 srcChNo, const MixParameter& param); + void SetTrackPan(u32 trackBitFlag, f32 pan); + void SetTrackSurroundPan(u32 trackBitFlag, f32 surroundPan); + void SetTrackMainSend(u32 trackBitFlag, f32 send); + void SetTrackFxSend(u32 trackBitFlag, AuxBus bus, f32 send); + void SetTrackLpfFreq(u32 trackBitFlag, f32 lpfFreq); + void SetTrackBiquadFilter(u32 trackBitFlag, s32 type, f32 value); + void SetTrackBankIndex(u32 trackBitFlag, s32 bankIndex); + void SetTrackTranspose(u32 trackBitFlag, s8 transpose); + void SetTrackVelocityRange(u32 trackBitFlag, u8 range); + + void SetTrackOutputLine(u32 trackBitFlag, u32 outputLine); + void ResetTrackOutputLine(u32 trackBitFlag); + + bool ReadVariable(s32 varNo, s16* varPtr) const; + static bool ReadGlobalVariable(s32 varNo, s16* varPtr); + bool ReadTrackVariable(s32, s32, s16*) const; + + void WriteVariable(s32 varNo, s16 var); + static void WriteGlobalVariable(s32 varNo, s16 var); + void WriteTrackVariable(s32, s32, s16 var); + + u64 GetTick() const; + + bool IsAttachedTempSpecialHandle() override; + void DetachTempSpecialHandle() override; + + void RegisterDataLoadTask(const driver::SequenceSoundLoader::LoadInfo& loadInfo, + const driver::SequenceSoundPlayer::StartInfo& startInfo); + + bool IsPrepared() const override; + + driver::BasicSoundPlayer* GetBasicSoundPlayerHandle() override; + + void OnUpdateParam() override; + + +private: + friend SequenceSoundInstanceManager; + + util::IntrusiveListNode m_PriorityLink; + SequenceSoundHandle* m_pTempSpecialHandle; + SequenceSoundInstanceManager* m_Manager; + bool m_InitializeFlag; + bool m_CanUseTask; + bool m_IsCalledPrepare; + u8 m_Padding[1]; + driver::SequenceSoundPlayer m_PlayerInstance; +}; +#if NN_SDK_VER < NN_MAKE_VER(4, 0, 0) +static_assert(sizeof(SequenceSound) == 0x570); +#else +static_assert(sizeof(SequenceSound) == 0x5a0); +#endif +} // namespace nn::atk::detail \ No newline at end of file diff --git a/include/nn/atk/atk_SequenceSoundFile.h b/include/nn/atk/atk_SequenceSoundFile.h new file mode 100644 index 00000000..a2e0612c --- /dev/null +++ b/include/nn/atk/atk_SequenceSoundFile.h @@ -0,0 +1,51 @@ +#pragma once + +#include + +namespace nn::atk::detail { +struct SequenceSoundFile { + struct DataBlock; + struct LabelBlock; + struct FileHeader : Util::SoundFileHeader { + const DataBlock* GetDataBlock() const; + const LabelBlock* GetLabelBlock() const; + }; + + struct DataBlockBody { + u8 sequenceData[1]; + + const void* GetSequenceData() const { return &sequenceData; }; + }; + + struct DataBlock { + BinaryBlockHeader header; + DataBlockBody body; + }; + + struct LabelInfo; + struct LabelBlockBody { + Util::ReferenceTable labelInfoReferenceTable; + + int GetLabelCount() const { return labelInfoReferenceTable.count; } + + const LabelInfo* GetLabelInfo(int index) const; + const char* GetLabel(int index) const; + const char* GetLabelByOffset(u32 offset) const; + + bool GetOffset(int index, u32* offsetPtr) const; + bool GetOffsetByLabel(const char* label, u32* offsetPtr) const; + }; + + struct LabelBlock { + BinaryBlockHeader header; + LabelBlockBody body; + }; + + struct LabelInfo { + Util::Reference referToSequenceData; + u32 labelStringLength; + char label[1]; + }; + static_assert(sizeof(LabelInfo) == 0x10); +}; +} // namespace nn::atk::detail \ No newline at end of file diff --git a/include/nn/atk/atk_SequenceSoundFileReader.h b/include/nn/atk/atk_SequenceSoundFileReader.h new file mode 100644 index 00000000..50f65850 --- /dev/null +++ b/include/nn/atk/atk_SequenceSoundFileReader.h @@ -0,0 +1,23 @@ +#pragma once + +#include + +namespace nn::atk::detail { +class SequenceSoundFileReader { +public: + constexpr static s32 SignatureFile = 0x51455346; // FSEQ + + explicit SequenceSoundFileReader(void* sequenceFile); + + void* GetSequenceData(); + + bool GetOffsetByLabel(const char* label, u32* offsetPtr) const; + bool GetLabelByOffset(u32 offset) const; + +private: + SequenceSoundFile::FileHeader* m_pHeader; + SequenceSoundFile::DataBlockBody* m_pDataBlockBody; + SequenceSoundFile::LabelBlockBody* m_pLabelBlockBody; +}; +static_assert(sizeof(SequenceSoundFileReader) == 0x18); +} // namespace nn::atk::detail \ No newline at end of file diff --git a/include/nn/atk/atk_SequenceSoundHandle.h b/include/nn/atk/atk_SequenceSoundHandle.h new file mode 100644 index 00000000..bde9626e --- /dev/null +++ b/include/nn/atk/atk_SequenceSoundHandle.h @@ -0,0 +1,36 @@ +#pragma once + +#include + +#include + +namespace nn::atk { +namespace detail { +class SequenceSound; +} // namespace nn::atk::detail + +class SequenceSoundHandle { +public: + using TrackBitFlagSet = util::BitFlagSet<16, void>; + + constexpr static u32 BankIndexMin = 0; + constexpr static u32 BankIndexMax = 3; + + constexpr static u8 TransposeMin = 192; + constexpr static u8 TransposeMax = 63; + + constexpr static u8 VelocityRangeMin = 0; + constexpr static u8 VelocityRangeMax = 127; + + constexpr static u32 VariableIndexMax = 15; + constexpr static u32 TrackIndexMax = 15; + + explicit SequenceSoundHandle(SoundHandle* pSoundHandle); + + void detail_AttachSoundAsTempHandle(detail::SequenceSound* pSound); + void DetachSound(); + +private: + detail::SequenceSound* m_pSound; +}; +} // namespace nn::atk \ No newline at end of file diff --git a/include/nn/atk/atk_SequenceSoundPlayer.h b/include/nn/atk/atk_SequenceSoundPlayer.h new file mode 100644 index 00000000..6b25c64f --- /dev/null +++ b/include/nn/atk/atk_SequenceSoundPlayer.h @@ -0,0 +1,318 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace nn::atk { +struct SequenceUserProcCallbackParam { + s16* localVariable; + s16* globalVariable; + s16* trackVariable; + bool cmpFlag; +}; +static_assert(sizeof(SequenceUserProcCallbackParam) == 0x20); + +using SequenceUserProcCallback = void(*)(u16,SequenceUserProcCallbackParam*,void*); + +namespace detail::driver { +class SequenceSoundLoader; +using SequenceSoundLoaderManager = LoaderManager; + +class SequenceSoundLoader { +public: + struct LoadInfo { + LoadInfo(const SoundArchive* arc, const SoundDataManager* mgr, + LoadItemInfo* seq, LoadItemInfo* banks, SoundPlayer* player); + + SoundArchive* soundArchive; + SoundDataManager* soundDataManager; + LoadItemInfo* loadInfoSeq; + LoadItemInfo* loadInfoBanks[4]; + SoundPlayer* soundPlayer; + }; + static_assert(sizeof(LoadInfo) == 0x40); + + struct Arg { + SoundArchive* soundArchive; + SoundDataManager* soundDataManager; + SoundPlayer* soundPlayer; + LoadItemInfo loadInfoSeq; + LoadItemInfo loadInfoBanks[4]; + }; + static_assert(sizeof(Arg) == 0x68); + + struct Data { + void* seqFile; + void* bankFiles[4]; + void* warcFiles[4]; + bool warcIsIndividuals[4]; + }; + static_assert(sizeof(Data) == 0x50); + + class DataLoadTask : Task { + public: + DataLoadTask(); + ~DataLoadTask() override; + + void Initialize(); + + bool TryAllocPlayerHeap(); + + void Execute(TaskProfileLogger& logger) override; + + private: + Arg m_Arg; + Data m_Data; + PlayerHeap* m_pPlayerHeap; + PlayerHeapDataManager* m_pPlayerHeapDataManager; + bool m_IsLoadSuccess; + u8 m_Padding[3]; + }; + static_assert(sizeof(DataLoadTask) == 0x118); + + class FreePlayerHeapTask : Task { + public: + FreePlayerHeapTask(); + ~FreePlayerHeapTask() override; + + void Initialize(); + void Execute(TaskProfileLogger& logger) override; + + private: + Arg m_Arg; + PlayerHeap* m_pPlayerHeap; + PlayerHeapDataManager* m_pPlayerHeapDataManager; + }; + static_assert(sizeof(FreePlayerHeapTask) == 0xc0); + + ~SequenceSoundLoader(); + + void Initialize(const Arg& arg); + + void Finalize(); + + bool IsInUse(); + + bool TryWait(); + +private: + friend SequenceSoundLoaderManager; + + DataLoadTask m_Task; + FreePlayerHeapTask m_FreePlayerHeapTask; + PlayerHeapDataManager m_PlayerHeapDataManager; + util::IntrusiveListNode m_LinkForLoaderManager; +}; +static_assert(sizeof(SequenceSoundLoader) == 0x4b0); + +class SequenceSoundPlayer : BasicSoundPlayer, DisposeCallback, SoundThread::PlayerCallback { +public: + enum StartOffsetType { + StartOffsetType_Tick, + StartOffsetType_Millisec, + }; + + enum ResState { + ResState_Invalid, + ResState_RecvLoadReq, + ResState_AppendLoadTask, + ResState_Assigned, + }; + + constexpr static s32 PlayerVariableCount = 16; + constexpr static s32 GlobalVariableCount = 16; + constexpr static s32 TrackCountPerPlayer = 16; + + constexpr static u32 AllTrackBitFlag = 0x0000FFFF; + + constexpr static s32 VariableDefaultValue = -1; + + constexpr static s32 DefaultTimebase = 48; + constexpr static s32 DefaultTempo = 120; + constexpr static u32 DefaultSkipIntervalTick = 16 * DefaultTimebase; + + struct ParserPlayerParam { + u8 priority; + u8 timebase; + u16 tempo; + MoveValue volume; + NoteOnCallback* callback; + }; + static_assert(sizeof(ParserPlayerParam) == 0x18); + + struct StartInfo { + s32 seqOffset; + StartOffsetType startOffsetType; + s32 startOffset; + s32 delayTime; + s32 delayCount; + UpdateType updateType; + }; + static_assert(sizeof(StartInfo) == 0x18); + + struct PrepareArg { + void* seqFile; + void* bankFiles[4]; + void* warcFiles[4]; + bool warcIsIndividuals[4]; + s32 seqOffset; + s32 delayTime; + s32 delayCount; + UpdateType updateType; + }; + static_assert(sizeof(PrepareArg) == 0x60); + + struct SetupArg { + SequenceTrackAllocator* trackAllocator; + u32 allocTracks; + NoteOnCallback* callback; + }; + static_assert(sizeof(SetupArg) == 0x18); + + static void InitSequenceSoundPlayer(); + + SequenceSoundPlayer(); + ~SequenceSoundPlayer() override; + +#if NN_SDK_VER < NN_MAKE_VER(4, 0, 0) + bool Initialize() override; +#else + bool Initialize(OutputReceiver* pOutputReceiver) override; +#endif + void Finalize() override; + + void FinishPlayer(); + + void FreeLoader(); + + void Setup(const SetupArg& arg); + + void SetPlayerTrack(s32 trackNo, SequenceTrack* track); + + void ForceTrackMute(u32); + + SequenceTrack* GetPlayerTrack(s32 trackNo); + + void Start() override; + void Stop() override; + void Pause(bool flag) override; + void Skip(StartOffsetType offsetType, s32 offset); + + void SetTempoRatio(f32 tempoRatio); + void SetPanRange(f32 panRange); + void SetChannelPriority(s32 priority); + void SetReleasePriorityFix(bool fix); + void SetSequenceUserprocCallback(SequenceUserProcCallback callback, void* arg); + + void CallSequenceUserprocCallback(u16 procId, SequenceTrack* track); + + s16* GetVariablePtr(s32 varNo); + + void GetLocalVariable(s32 varNo) const; + static s16 GetGlobalVariable(s32 varNo); + + void SetLocalVariable(s32 varNo, s16 var); + static void SetGlobalVariable(s32 varNo, s16 var); + + void SetTrackMute(u32 trackBitFlag, SequenceMute mute); + void SetTrackSilence(u64 trackBitFlag, bool silenceFlag, s32 fadeTimes); + void SetTrackVolume(u32 trackBitFlag, f32 volume); + void SetTrackPitch(u32 trackBitFlag, f32 pitch); + void SetTrackLpfFreq(u32 trackBitFlag, f32 lpfFreq); + void SetTrackBiquadFilter(u32 trackBitFlag, s32 type, f32 value); + bool SetTrackBankIndex(u32 trackBitFlag, s32 bankIndex); + void SetTrackTranspose(u32 trackBitFlag, s8 transpose); + void SetTrackVelocityRange(u32 trackBitFlag, u8 range); + + void SetTrackOutputLine(u32 trackBitFlag, u32 outputLine); + void ResetTrackOutputLine(u32 trackBitFlag); + + void SetTrackTvVolume(u32 trackBitFlag, f32 volume); + void SetTrackChannelTvMixParameter(u32 trackBitFlag, u32 srcChNo, const MixParameter& param); + void SetTrackTvPan(u32 trackBitFlag, f32 pan); + void SetTrackTvSurroundPan(u32 trackBitFlag, f32 surroundPan); + void SetTrackTvMainSend(u32 trackBitFlag, f32 send); + void SetTrackTvFxSend(u32 trackBitFlag, AuxBus bus, f32 send); + + void InvalidateData(const void* start, const void* end) override; + + SequenceTrack* GetPlayerTrack(s32 trackNo) const; + void CloseTrack(s32 trackNo); + + void UpdateChannelParam(); + + s32 ParseNextTick(bool doNoteOn); + + void Update(); + + bool TryAllocLoader(); + + void PrepareForPlayerHeap(PrepareArg* arg); + + void SkipTick(); + void UpdateTick(); + + Channel* NoteOn(u8 bankIndex, const NoteOnInfo& noteOnInfo); + + void Prepare(const PrepareArg& arg); + + void RequestLoad(const StartInfo& info, const SequenceSoundLoader::Arg& arg); + + u64 GetProcessTick(const SoundProfile&); + + void PrepareForMidi(const void**, const void**, bool*); + + static void SetSkipIntervalTick(s32); + static s32 GetSkipIntervalTick(); + + void ChannelCallback(Channel* channel); + + void OnUpdateFrameSoundThread() override; + void OnUpdateFrameSoundThreadWithAudioFrameFrequency() override; + void OnShutdownSoundThread() override; + +private: + bool m_ReleasePriorityFixFlag; + bool m_IsPrepared; + f32 m_PanRange; + f32 m_TempoRatio; + f32 m_TickFraction; + u32 m_SkipTickCounter; + f32 m_SkipTimeCounter; + s32 m_DelayCount; + ParserPlayerParam m_ParserParam; + SequenceTrackAllocator* m_pSequenceTrackAllocator; + SequenceUserProcCallback m_SequenceUserprocCallback; + void* m_pSequenceUserprocCallbackArg; + SequenceTrack* m_pTracks[TrackCountPerPlayer]; + s16 m_LocalVariable[PlayerVariableCount]; + u32 m_TickCounter; + WaveArchiveFileReader m_WarcFileReader[4]; + BankFileReader m_BankFileReader[4]; + u8 m_ResState; + bool m_IsInitialized; + bool m_IsRegisterPlayerCallback; + u8 m_Padding[1]; + StartInfo m_StartInfo; + SequenceSoundLoaderManager* m_pLoaderManager; + SequenceSoundLoader* m_pLoader; + SequenceSoundLoader::Arg m_LoaderArg; + UpdateType m_UpdateType; + + static s16 m_GlobalVariable[GlobalVariableCount]; + static s32 m_SkipIntervalTickPerFrame; +}; +#if NN_SDK_VER < NN_MAKE_VER(4, 0, 0) +static_assert(sizeof(SequenceSoundPlayer) == 0x358); +#else +static_assert(sizeof(SequenceSoundPlayer) == 0x368); +#endif +} // namespace nn::atk::detail::driver +} // namespace nn::atk \ No newline at end of file diff --git a/include/nn/atk/atk_SequenceSoundRuntime.h b/include/nn/atk/atk_SequenceSoundRuntime.h new file mode 100644 index 00000000..77b9991e --- /dev/null +++ b/include/nn/atk/atk_SequenceSoundRuntime.h @@ -0,0 +1,120 @@ +#pragma once + +#include +#include +#include +#include +#include + +namespace nn::atk::detail { +class SequenceSoundRuntime { +public: + class SequenceNoteOnCallback : driver::NoteOnCallback { + public: + ~SequenceNoteOnCallback() override; + + driver::Channel* NoteOn(driver::SequenceSoundPlayer* seqPlayer, u8 bankIndex, + const driver::NoteOnInfo& noteOnInfo) override; + private: + SequenceSoundRuntime* m_pSequenceSoundRuntime; + }; + static_assert(sizeof(SequenceNoteOnCallback) == 0x10); + + struct PrepareContext { + SequenceSoundFile* pSequenceSoundFile; + u32 sequenceOffset; + u32 allocateTrackFlags; + LoadItemInfo loadTargetSequenceInfo; + LoadItemInfo loadTargetBankInfos[4]; + LoadItemInfo loadTargetWaveArchiveInfos[4]; + bool isLoadIndividuals[4]; + bool canUsePlayerHeap; + bool isRegisterDataLoadTaskNeeded; + }; + static_assert(sizeof(PrepareContext) == 0xa8); + + SequenceSoundRuntime(); + ~SequenceSoundRuntime(); + + bool Initialize(s32 soundCount, void** pOutAllocatedAddr, const void* endAddr); + void Finalize(); + + void SetupSequenceTrack(s32 trackCount, void** pOutAllocatedAddr, const void* endAddr); + void SetupUserParam(void** pOutAllocatedAddr, size_t adjustSize); + + static size_t GetRequiredMemorySize(const SoundArchive::SoundArchivePlayerInfo& soundArchivePlayerInfo, + s32 alignment); + static size_t GetRequiredSequenceTrackMemorySize(const SoundArchive::SoundArchivePlayerInfo& soundArchivePlayerInfo, + s32 alignment); + + bool IsSoundArchiveAvailable() const; + + s32 GetActiveCount() const; + s32 GetFreeCount() const; + + static void SetSequenceSkipIntervalTick(s32 tick); + static s32 GetSequenceSkipIntervalTick(); + + void Update(); + +#if NN_SDK_VER < NN_MAKE_VER(4, 0, 0) + SequenceSound* AllocSound(SoundArchive::ItemId soundId, s32 priority, s32 ambientPriority, + BasicSound::AmbientInfo* ambientArgInfo); +#else + SequenceSound* AllocSound(SoundArchive::ItemId soundId, s32 priority, s32 ambientPriority, + BasicSound::AmbientInfo* ambientArgInfo, OutputReceiver* pOutputReceiver); +#endif + + SoundStartable::StartResult PrepareImpl(const SoundArchiveManager::SnapShot& snapShot, + SoundArchive::ItemId soundId, + SequenceSound* sound, + const SoundArchive::SoundInfo* commonInfo, + const StartInfoReader& startInfoReader); + + SoundStartable::StartResult SetupSequenceSoundInfo(SoundStartable::StartInfo::SequenceSoundInfo* sequenceSoundInfo, + SoundArchive::ItemId soundId, + const SoundArchive& soundArchive, + const SoundStartable::StartInfo::SequenceSoundInfo* pExternalSequenceSoundInfo); + + SoundStartable::StartResult SetupSequenceSoundFile(PrepareContext* pOutContext, + const SequenceSound& sound, + const SoundArchive& soundArchive, + const SoundDataManager& soundDataManager, + const SoundArchive::SoundInfo& commonInfo, + const SoundStartable::StartInfo::SequenceSoundInfo* pExternalSequenceSoundInfo); + + SoundStartable::StartResult SetupBankFileAndWaveArchiveFile(PrepareContext* pOutContext, + const SequenceSound& sound, + const SoundStartable::StartInfo::SequenceSoundInfo& sequenceSoundInfo, + const SoundArchiveManager::SnapShot& snapShot, + const SoundStartable::StartInfo::SequenceSoundInfo* pExternalSequenceSoundInfo); + + void SetupSequenceSoundPlayerStartInfo(SoundStartable::StartInfo* startInfo, + SoundArchive::ItemId soundId, + const StartInfoReader& startInfoReader); + + void DumpMemory(const SoundArchive*) const; + + void SetupBankFileAndWaveArchiveFileFromHook(PrepareContext* pOutContext, + SequenceSound* sound, + SoundArchive* soundArchive); + + +private: + SequenceSoundInstanceManager m_SequenceSoundInstanceManager; + driver::SequenceSoundLoaderManager m_SequenceSoundLoaderManager; + driver::SequenceTrackAllocator* m_pSequenceTrackAllocator; + driver::MmlSequenceTrackAllocator m_MmlSequenceTrackAllocator; + driver::MmlParser m_MmlParser; + SequenceNoteOnCallback m_SequenceCallback; + SequenceUserProcCallback m_SequenceUserProcCallback; + void* m_pSequenceUserProcCallbackArg; + SoundArchiveManager* m_pSoundArchiveManager; + SoundArchiveFilesHook* m_pSoundArchiveFilesHook; +}; +#if NN_SDK_VER < NN_MAKE_VER(4, 0, 0) +static_assert(sizeof(SequenceSoundRuntime) == 0xe0); +#else +static_assert(sizeof(SequenceSoundRuntime) == 0xe8); +#endif +} // namespace nn::atk::detail diff --git a/include/nn/atk/atk_SequenceTrack.h b/include/nn/atk/atk_SequenceTrack.h new file mode 100644 index 00000000..6be8d991 --- /dev/null +++ b/include/nn/atk/atk_SequenceTrack.h @@ -0,0 +1,155 @@ +#pragma once + +#include + +namespace nn::atk::detail::driver { +class SequenceSoundPlayer; + +class SequenceTrack { +public: + enum ParseResult { + ParseResult_Continue, + ParseResult_Finish, + }; + + constexpr static u32 CallStackDepth = 10; + + constexpr static u32 DefaultPriority = 64; + constexpr static u32 DefaultBendRange = 2; + constexpr static u32 DefaultPortaKey = 60; + + constexpr static s32 InvalidEnvelope = 255; + constexpr static s32 MaxEnvelopeValue = 127; + + constexpr static s32 ParserParamSize = 32; + + constexpr static s32 TrackVariableCount = 16; + + constexpr static s32 PauseReleaseValue = 16; + constexpr static s32 MuteReleaseValue = 127; + + + struct ParserTrackParam { + struct CallStack { + u8 loopFlag; + u8 loopCount; + u8 padding[2]; + u8* address; + }; + static_assert(sizeof(CallStack) == 0x10); + + u8* baseAddr; + u8* currentAddr; + bool cmpFlag; + bool noteWaitFlag; + bool tieFlag; + bool monophonicFlag; + CallStack callStack[CallStackDepth]; + u8 callStackDepth; + bool frontBypassFlag; + bool muteFlag; + bool silenceFlag; + s32 wait; + bool noteFinishWait; + bool portaFlag; + bool damperFlag; + u8 bankIndex; + s32 prgNo; + f32 sweepPitch; + MoveValue volume; + MoveValue volume2; + MoveValue pan; + MoveValue surroundPan; + MoveValue pitchBend; + CurveLfoParam lfoParam[4]; + u8 lfoTarget[4]; + u8 velocityRange; + u8 bendRange; + s8 initPan; + u8 padding1[1]; + s8 transpose; + u8 priority; + u8 portaKey; + u8 portaTime; + u8 attack; + u8 decay; + u8 sustain; + u8 release; + s16 envHold; + s8 biquadType; + u8 mainSend; + u8 fxSend[3]; + u8 padding2[1]; + f32 lpfFreq; + f32 biquadValue; + s32 outputLine; + }; + static_assert(sizeof(ParserTrackParam) == 0x150); + + virtual ~SequenceTrack(); + virtual ParseResult Parse(bool doNoteOn) = 0; + + SequenceTrack(); + + void SetPlayerTrackNo(s32 playerTrackNo); + + void InitParam(); + + void Close(); + + void SetSeqData(const void* seqBase, s32 seqOffset); + + void Open(); + + void ReleaseAllChannel(s32 release); + void FreeAllChannel(); + + void UpdateChannelLength(); + void UpdateChannelRelease(Channel*); + + s32 ParseNextTick(bool doNoteOn); + + void StopAllChannel(); + void UpdateChannelParam(); + void PauseAllChannel(bool flag); + + void AddChannel(Channel*); + + s32 GetChannelCount() const; + + static void ChannelCallbackFunc(Channel* dropChannel, + Channel::ChannelCallbackStatus callbackStatus, void* userData); + + void SetMute(SequenceMute mute); + void ForceMute(); + + void SetSilence(bool silenceFlag, s32 fadeTimes); + void SetBiquadFilter(s32 type, f32 value); + void SetBankIndex(s32 bankIndex); + void SetTranspose(s8 transpose); + void SetVelocityRange(u8 range); + void SetOutputLine(s32 outputLine); + void SetTvMixParameter(u32 srcChNo, s32 mixChNo, f32 param); + + s16 GetTrackVariable(s32 varNo) const; + void SetTrackVariable(s32 varNo, s16 var); + + s16* GetVariablePtr(s32 varNo); + + Channel* NoteOn(s32 key, s32 velocity, s32 length, bool tieFlag); + +private: + u8 m_PlayerTrackNo; + bool m_OpenFlag; + bool m_ForceMute; + f32 m_ExtVolume; + f32 m_ExtPitch; + f32 m_PanRange; + OutputParam m_TvParam; + ParserTrackParam m_ParserTrackParam; + s16 m_TrackVariable[TrackVariableCount]; + SequenceSoundPlayer* m_pSequenceSoundPlayer; + Channel* m_pChannelList; +}; +static_assert(sizeof(SequenceTrack) == 0x1e8); +} // namespace nn::atk::detail::driver \ No newline at end of file diff --git a/include/nn/atk/atk_SequenceTrackAllocator.h b/include/nn/atk/atk_SequenceTrackAllocator.h new file mode 100644 index 00000000..e7d6ca48 --- /dev/null +++ b/include/nn/atk/atk_SequenceTrackAllocator.h @@ -0,0 +1,14 @@ +#pragma once + +#include + +namespace nn::atk::detail::driver { +class SequenceSoundPlayer; + +class SequenceTrackAllocator { +public: + virtual ~SequenceTrackAllocator() = default; + virtual SequenceTrack* AllocTrack(SequenceSoundPlayer* player) = 0; +}; +static_assert(sizeof(SequenceTrackAllocator) == 0x8); +} // namespace nn::atk::detail::driver \ No newline at end of file diff --git a/include/nn/atk/atk_SoundActor.h b/include/nn/atk/atk_SoundActor.h new file mode 100644 index 00000000..8d30f83a --- /dev/null +++ b/include/nn/atk/atk_SoundActor.h @@ -0,0 +1,10 @@ +#pragma once + +#include + +namespace nn::atk { +class SoundActor { +public: + using ActorPlayer = detail::ExternalSoundPlayer; +}; +} // namespace nn::atk \ No newline at end of file diff --git a/include/nn/atk/atk_SoundArchive.h b/include/nn/atk/atk_SoundArchive.h new file mode 100644 index 00000000..9bffa115 --- /dev/null +++ b/include/nn/atk/atk_SoundArchive.h @@ -0,0 +1,341 @@ +#pragma once + +#include +#include +#include + +namespace nn::atk { + +namespace detail { +class SoundArchiveFileReader; +class SoundArchiveParametersHook; +struct SoundArchiveFilesHook; + +namespace driver { +class StreamSoundLoader; +} + +} // namespace detail + +class SoundArchive { +public: + using ItemId = u32; + + static const ItemId InvalidId{0xffffffff}; + + static const int UserParamIndexMax{3}; + static const u32 ResultInvalidSoundId{0}; + static const u32 InvalidUserParam{0xffffffff}; + + enum SoundType { + SoundType_Invalid, + SoundType_Sequence, + SoundType_Stream, + SoundType_Wave, + SoundType_AdvancedWave, + }; + + enum StreamFileType { + StreamFileType_Invalid = 0, + StreamFileType_NwStreamBinary = 1, + StreamFileType_Opus = 3, + }; + + enum DecodeMode { + DecodeMode_Default, + DecodeMode_Cpu, + DecodeMode_Accelerator, + }; + + using FileId = ItemId; + using StringId = ItemId; + + struct SoundInfo { + FileId fileId; + ItemId playerId; + u8 actorPlayerId; + u8 playerPriority; + u8 volume; + u8 remoteFilter; + PanMode panMode; + PanCurve panCurve; + SinglePlayType singlePlayType; + u16 singlePlayEffectiveDuration; + bool isFrontBypass; + }; + static_assert(sizeof(SoundInfo) == 0x1c); + + struct Sound3DInfo { + u32 flags; + float decayRatio; + u8 decayCurve; + u8 dopplerFactor; + }; + static_assert(sizeof(Sound3DInfo) == 0xc); + + static const u32 SequenceBankMax{4}; + struct SequenceSoundInfo { + u32 startOffset; + u32 bankIds[SequenceBankMax]; + u32 allocateTrackFlags; + u8 channelPriority; + bool isReleasePriorityFix; + + SequenceSoundInfo(); + }; + static_assert(sizeof(SequenceSoundInfo) == 0x1c); + + static const u32 StreamTrackCount{8}; + struct StreamTrackInfo { + u8 volume; + u8 pan; + u8 surroundPan; + u8 flags; + u8 mainSend; + u8 fxSend[3]; + u8 lowPassFilterFrequency; + u8 biquadType; + u8 biquadValue; + u8 channelCount; + s8 globalChannelIndex[2]; + + StreamTrackInfo(); + }; + static_assert(sizeof(StreamTrackInfo) == 0xe); + + struct StreamSoundInfo { + u16 allocateTrackFlags; + u16 allocateChannelCount; + float pitch; + u8 mainSend; + u8 fxSend[3]; + StreamTrackInfo trackInfo[StreamTrackCount]; + StreamFileType streamFileType; + DecodeMode decodeMode; + FileId prefetchFileId; + void* streamBufferPool; + + StreamSoundInfo(); + void Setup(); + }; + static_assert(sizeof(StreamSoundInfo) == 0x90); + + struct StreamSoundInfo2 { + bool isLoop; + u32 loopStartFrame; + u32 loopEndFrame; + + StreamSoundInfo2(); + }; + static_assert(sizeof(StreamSoundInfo2) == 0xc); + + struct WaveSoundInfo { + u32 index; + u32 allocateTrackCount; + u8 channelPriority; + bool isReleasePriorityFix; + + WaveSoundInfo(); + }; + static_assert(sizeof(WaveSoundInfo) == 0xc); + + struct AdvancedWaveSoundInfo { + u32 waveArchiveId; + }; + static_assert(sizeof(AdvancedWaveSoundInfo) == 0x4); + + struct BankInfo { + FileId fileId; + + BankInfo(); + }; + static_assert(sizeof(BankInfo) == 0x4); + + struct WaveArchiveInfo { + u32 fileId{SoundArchive::InvalidId}; + u32 waveCount; + bool isLoadIndividual{false}; + u8 padding[3]; + + WaveArchiveInfo() = default; + }; + static_assert(sizeof(WaveArchiveInfo) == 0xc); + + struct PlayerInfo { + int playableSoundMax; + u32 playerHeapSize; + + PlayerInfo(); + }; + static_assert(sizeof(PlayerInfo) == 0x8); + + struct SoundGroupInfo { + ItemId startId; + ItemId endId; + detail::Util::Table fileIdTable; + + SoundGroupInfo(); + }; + static_assert(sizeof(SoundGroupInfo) == 0x10); + + struct GroupInfo { + FileId fileId; + u32 groupFileSize; + + GroupInfo(); + }; + static_assert(sizeof(GroupInfo) == 0x8); + + struct SoundArchivePlayerInfo { + s32 sequenceSoundCount; + s32 sequenceTrackCount; + s32 streamSoundCount; + s32 streamTrackCount; + s32 streamChannelCount; + s32 waveSoundCount; + s32 waveTrackCount; + s32 streamBufferTimes; + bool isAdvancedWaveSoundEnabled; + }; + static_assert(sizeof(SoundArchivePlayerInfo) == 0x24); + + struct FileInfo { + static const u32 InvalidOffset{0xffffffff}; + static const u32 InvalidSize{0xffffffff}; + + u32 fileSize{InvalidSize}; + u32 offsetFromFileBlockHead{InvalidOffset}; + const char* externalFilePath{}; + + FileInfo() = default; + }; + static_assert(sizeof(FileInfo) == 0x10); + +protected: + SoundArchive(); + +public: + virtual ~SoundArchive(); + + bool IsAvailable() const; + + u32 GetSoundCount() const; + u32 GetGroupCount() const; + u32 GetPlayerCount() const; + u32 GetSoundGroupCount() const; + u32 GetBankCount() const; + u32 GetWaveArchiveCount() const; + u32 detail_GetFileCount() const; + + const char* GetItemLabel(ItemId id) const; + ItemId GetItemId(const char* pStr) const; + FileId GetItemFileId(ItemId id) const; + FileId GetItemPrefetchFileId(ItemId id) const; + + static ItemId GetSoundIdFromIndex(u32 index) { + return detail::Util::GetMaskedItemId(index, detail::ItemType_Sound); + } + + static ItemId GetSoundGroupIdFromIndex(u32 index) { + return detail::Util::GetMaskedItemId(index, detail::ItemType_SoundGroup); + } + + static ItemId GetBankIdFromIndex(u32 index) { + return detail::Util::GetMaskedItemId(index, detail::ItemType_Bank); + } + + static ItemId GetPlayerIdFromIndex(u32 index) { + return detail::Util::GetMaskedItemId(index, detail::ItemType_Player); + } + + static ItemId GetWaveArchiveIdFromIndex(u32 index) { + return detail::Util::GetMaskedItemId(index, detail::ItemType_WaveArchive); + } + + static ItemId GetGroupIdFromIndex(u32 index) { + return detail::Util::GetMaskedItemId(index, detail::ItemType_Group); + } + + u32 GetSoundUserParam(ItemId soundId) const; + bool ReadSoundUserParam(u32* pOutValue, ItemId soundId, int index) const; + + SoundType GetSoundType(ItemId soundId) const; + + bool ReadSoundInfo(SoundInfo* pOutValue, ItemId soundId) const; + bool ReadSequenceSoundInfo(SequenceSoundInfo* pOutValue, ItemId soundId) const; + bool ReadBankInfo(BankInfo* pOutValue, ItemId bankId) const; + bool ReadPlayerInfo(PlayerInfo* pOutValue, ItemId playerId) const; + bool ReadSoundArchivePlayerInfo(SoundArchivePlayerInfo* pOutValue) const; + bool ReadStreamSoundInfo(StreamSoundInfo* pOutValue, ItemId soundId) const; + + bool detail_ReadStreamSoundInfo2(ItemId soundId, StreamSoundInfo2* info) const; + bool detail_ReadWaveSoundInfo(ItemId soundId, WaveSoundInfo* info) const; + bool detail_ReadAdvancedWaveSoundInfo(ItemId soundId, AdvancedWaveSoundInfo* info) const; + + bool ReadSound3DInfo(Sound3DInfo* pOutValue, ItemId soundId) const; + bool ReadWaveArchiveInfo(ItemId warcId, WaveArchiveInfo* info) const; + + bool detail_ReadSoundGroupInfo(ItemId soundGroupId, SoundGroupInfo* info) const; + + bool ReadGroupInfo(GroupInfo* pOutValue, ItemId groupId) const; + + bool detail_ReadFileInfo(FileId fileId, FileInfo* info) const; + + const detail::Util::Table* detail_GetWaveArchiveIdTable(ItemId id) const; + virtual const void* detail_GetFileAddress(FileId fileId) const = 0; + virtual size_t detail_GetRequiredStreamBufferSize() const = 0; + + detail::fnd::FileStream* detail_OpenFileStream(FileId fileId, void* buffer, size_t size, + void* cacheBuffer, size_t cacheSize) const; + const detail::Util::Table* detail_GetAttachedGroupTable(FileId fileId) const; + + detail::SoundArchiveParametersHook* detail_GetParametersHook() const { + return m_pParametersHook; + } + + void detail_SetParametersHook(detail::SoundArchiveParametersHook* parametersHook) { + m_pParametersHook = parametersHook; + } + + void SetExternalFileRoot(const char* extFileRoot); + + bool ReadStreamSoundFilePath(char* outFilePathBuffer, size_t filePathBufferSize, + ItemId soundId) const; + + virtual void FileAccessBegin() const; + virtual void FileAccessEnd() const; + + const char* detail_GetExternalFileFullPath(const char* externalFilePath, char* pathBuffer, + size_t bufSize) const; + + virtual bool IsAddon() const; + +protected: + void Initialize(detail::SoundArchiveFileReader* fileReader); + void Finalize(); + + virtual detail::fnd::FileStream* OpenStream(void* buffer, size_t size, position_t begin, + size_t length) const = 0; + virtual detail::fnd::FileStream* OpenExtStream(void* buffer, size_t size, + const char* extFilePath, void* cacheBuffer, + size_t cacheSize) const = 0; + + detail::fnd::FileStream* OpenExtStreamImpl(void* buffer, size_t size, + const char* externalFilePath, void* cacheBuffer, + size_t cacheSize) const; + + static const s32 FilePathMax{639}; + +private: + friend detail::driver::StreamSoundLoader; + + detail::SoundArchiveFileReader* m_pFileReader{}; + detail::SoundArchiveParametersHook* m_pParametersHook{}; + char m_ExtFileRoot[FilePathMax]; + u32 m_FileBlockOffset; +}; +static_assert(sizeof(SoundArchive) == 0x2a0); + +class AddonSoundArchive : public SoundArchive {}; + +} // namespace nn::atk \ No newline at end of file diff --git a/include/nn/atk/atk_SoundArchiveFile.h b/include/nn/atk/atk_SoundArchiveFile.h new file mode 100644 index 00000000..abb4e05b --- /dev/null +++ b/include/nn/atk/atk_SoundArchiveFile.h @@ -0,0 +1,503 @@ +#pragma once + +#include +#include +#include + +namespace nn::atk::detail { +class SoundArchiveParametersHook; + +class SoundArchiveFile { +public: + static const int BlockCount {3}; + + struct FileHeader : BinaryFileHeader { + Util::ReferenceWithSize toBlocks[BlockCount]; + + u32 GetStringBlockSize() const; + u32 GetInfoBlockSize() const; + u32 GetFileBlockSize() const; + + int GetStringBlockOffset() const; + int GetInfoBlockOffset() const; + int GetFileBlockOffset() const; + + private: + const Util::ReferenceWithSize* GetReferenceBy(u16 typeId) const; + }; + static_assert(sizeof(FileHeader) == 0x38); + + struct StringTable; + struct PatriciaTree; + struct StringBlockBody { + enum Sections { + Sections_StringTable = 0, + Sections_PatriciaTree = 1, + Sections_Max = 1, + }; + + Util::Reference toSection[1]; + + const char* GetString(SoundArchive::StringId stringId) const; + + u32 GetStringCount() const { + return GetStringTable()->GetCount(); + } + + u32 GetItemId(const char* str) const { + return GetItemIdImpl(Sections_PatriciaTree, str); + } + + void DumpTree() const; + + private: + const void* GetSection(Sections section) const; + + const StringTable* GetStringTable() const { + return util::ConstBytePtr(GetSection(Sections_StringTable)).Get(); + } + + const PatriciaTree* GetPatriciaTree(Sections section) const { + if (section > Sections_Max) + return nullptr; + + return util::ConstBytePtr(GetSection(section)).Get(); + } + + u32 GetItemIdImpl(Sections section, const char* str) const; + }; + + struct PatriciaTree { + struct NodeData { + u32 stringId; + u32 itemId; + }; + static_assert(sizeof(NodeData) == 0x8); + + struct Node { + static const u16 FlagLeaf {1 << 0}; + + u16 flags; + u16 bit; + u32 leftIdx; + u32 rightIdx; + NodeData nodeData; + }; + static_assert(sizeof(Node) == 0x14); + + u32 rootIdx; + Util::Table nodeTable; + + const NodeData* GetNodeDataBy(const char* str, size_t len = 0) const; + + void* operator[]([[maybe_unused]] int idx) const { + // TODO + return nullptr; + } + + void* operator[]([[maybe_unused]] u32 idx) const { + // TODO + return nullptr; + } + + void* operator[]([[maybe_unused]] const char* str) const { + // TODO + return nullptr; + } + + void* operator()([[maybe_unused]] const char* str, [[maybe_unused]] size_t len) const { + // TODO + return nullptr; + } + + u32 GetDataCount() const; + u32 GetCount() const; + }; + + struct StringBlock { + BinaryBlockHeader header; + StringBlockBody body; + }; + + struct StringTable { + Util::ReferenceWithSizeTable table; + + const char* GetString(int stringId) const { + return util::ConstBytePtr(this, table.item[stringId].offset).Get(); + } + + u32 GetCount() const { + return table.count; + } + }; + + struct SoundInfo; + struct BankInfo; + struct PlayerInfo; + struct SoundGroupInfo; + struct GroupInfo; + struct WaveArchiveInfo; + struct FileInfo; + struct SoundArchivePlayerInfo; + struct InfoBlockBody { + Util::Reference toSoundInfoReferenceTable; + Util::Reference toSoundGroupInfoReferenceTable; + Util::Reference toBankInfoReferenceTable; + Util::Reference toWaveArchiveInfoReferenceTable; + Util::Reference toGroupInfoReferenceTable; + Util::Reference toPlayerInfoReferenceTable; + Util::Reference toFileInfoReferenceTable; + Util::Reference toSoundArchivePlayerInfo; + + u32 GetSoundCount() const { return GetSoundInfoReferenceTable().count; } + u32 GetBankCount() const { return GetBankInfoReferenceTable().count; } + u32 GetPlayerCount() const { return GetPlayerInfoReferenceTable().count; } + u32 GetSoundGroupCount() const { return GetSoundGroupInfoReferenceTable().count; } + u32 GetGroupCount() const { return GetGroupInfoReferenceTable().count; } + u32 GetWaveArchiveCount() const { return GetWaveArchiveInfoReferenceTable().count; } + u32 GetFileCount() const { return GetFileInfoReferenceTable().count; } + + const SoundInfo* GetSoundInfo(SoundArchive::ItemId itemId) const; + const BankInfo* GetBankInfo(SoundArchive::ItemId itemId) const; + const PlayerInfo* GetPlayerInfo(SoundArchive::ItemId itemId) const; + const SoundGroupInfo* GetSoundGroupInfo(SoundArchive::ItemId itemId) const; + const GroupInfo* GetGroupInfo(SoundArchive::ItemId itemId) const; + const WaveArchiveInfo* GetWaveArchiveInfo(SoundArchive::ItemId itemId) const; + const FileInfo* GetFileInfo(SoundArchive::FileId itemId) const; + + const SoundArchivePlayerInfo* GetSoundArchivePlayerInfo() const; + + SoundArchive::FileId GetItemFileId(SoundArchive::ItemId id) const; + SoundArchive::StringId GetItemStringId(SoundArchive::ItemId id) const; + SoundArchive::FileId GetItemPrefetchFileId(SoundArchive::ItemId id) const; + + private: + const Util::ReferenceTable& GetSoundInfoReferenceTable() const; + const Util::ReferenceTable& GetBankInfoReferenceTable() const; + const Util::ReferenceTable& GetPlayerInfoReferenceTable() const; + const Util::ReferenceTable& GetSoundGroupInfoReferenceTable() const; + const Util::ReferenceTable& GetWaveArchiveInfoReferenceTable() const; + const Util::ReferenceTable& GetGroupInfoReferenceTable() const; + const Util::ReferenceTable& GetFileInfoReferenceTable() const; + }; + static_assert(sizeof(InfoBlockBody) == 0x40); + + struct InfoBlock { + BinaryBlockHeader header; + InfoBlockBody body; + }; + static_assert(sizeof(InfoBlock) == 0x48); + + struct StreamSoundInfo; + struct WaveSoundInfo; + struct AdvancedWaveSoundInfo; + struct SequenceSoundInfo; + struct Sound3DInfo; + struct SoundInfo { + u32 fileId; + u32 playerId; + u8 volume; + u8 remoteFilter; + u8 padding[2]; + Util::Reference toDetailSoundInfo; + Util::BitFlag optionParameter; + + SoundArchive::SoundType GetSoundType() const; + const StreamSoundInfo& GetStreamSoundInfo() const; + const WaveSoundInfo& GetWaveSoundInfo() const; + const AdvancedWaveSoundInfo& GetAdvancedWaveSoundInfo() const; + const SequenceSoundInfo& GetSequenceSoundInfo() const; + const Sound3DInfo* GetSound3DInfo() const; + + u32 GetStringId() const; + PanMode GetPanMode() const; + PanCurve GetPanCurve() const; + SinglePlayType GetSinglePlayType() const; + u16 GetSinglePlayEffectiveDuration() const; + u8 GetPlayerPriority() const; + u8 GetActorPlayerId() const; + u32 GetUserParam() const; + + bool ReadUserParam(u32* pOutValue, int index) const; + + bool IsFrontBypass() const; + }; + static_assert(sizeof(SoundInfo) == 0x18); + + struct StreamTrackInfo; + struct StreamTrackInfoTable { + Util::ReferenceTable table; + + const StreamTrackInfo* GetTrackInfo(u32 index) { + return util::ConstBytePtr(table.GetReferedItem(ElementType_SoundArchiveFile_StreamSoundTrackInfo, index)) + .Get(); + } + + u32 GetTrackCount() const { + return table.count; + } + }; + + struct SendValue { + u8 mainSend; + u8 fxSend[3]; + }; + static_assert(sizeof(SendValue) == 0x4); + + struct StreamTrackInfo { + u8 volume; + u8 pan; + u8 span; + u8 flags; + Util::Reference toGlobalChannelIndexTable; + Util::Reference toSendValue; + u8 lpfFreq; + u8 biquadType; + u8 biquadValue; + u8 padding[1]; + + u32 GetTrackChannelCount() const { + return GetGlobalChannelIndexTable().GetCount(); + } + + u8 GetGlobalChannelIndex(u32 index) const { + return GetGlobalChannelIndexTable().GetGlobalIndex(index); + } + + const SendValue& GetSendValue() const { + return *util::ConstBytePtr(this, toSendValue.offset) + .Get(); + } + + const StreamSoundFile::GlobalChannelIndexTable& GetGlobalChannelIndexTable() const { + return *util::ConstBytePtr(this, toGlobalChannelIndexTable.offset) + .Get(); + } + }; + static_assert(sizeof(StreamTrackInfo) == 0x18); + + struct StreamSoundExtension { + u32 streamTypeInfo; + u32 loopStartFrame; + u32 loopEndFrame; + + SoundArchive::StreamFileType GetStreamFileType() const { + switch (Util::DivideBy8bit(streamTypeInfo, 0)) { + case 1: + return SoundArchive::StreamFileType_NwStreamBinary; + case 3: + return SoundArchive::StreamFileType_Opus; + default: + return SoundArchive::StreamFileType_Invalid; + } + } + + bool IsLoop() const { + return Util::DivideBy8bit(streamTypeInfo, 1) != 0; + } + + SoundArchive::DecodeMode GetDecodeMode() const { + switch (Util::DivideBy8bit(streamTypeInfo, 2)) { + case 1: + return SoundArchive::DecodeMode_Cpu; + case 2: + return SoundArchive::DecodeMode_Accelerator; + default: + return SoundArchive::DecodeMode_Default; + } + } + }; + static_assert(sizeof(StreamSoundExtension) == 0xc); + + struct StreamSoundInfo { + u16 allocateTrackFlags; + u16 allocateChannelCount; + Util::Reference toTrackInfoTable; + float pitch; + Util::Reference toSendValue; + Util::Reference toStreamSoundExtension; + u32 prefetchFileId; + + const StreamTrackInfoTable* GetTrackInfoTable() const; + + float GetPitch() const { return pitch; } + const SendValue& GetSendValue() const; + const StreamSoundExtension* GetStreamSoundExtension() const; + }; + static_assert(sizeof(StreamSoundInfo) == 0x24); + + struct WaveSoundInfo { + u32 index; + u32 allocateTrackCount; + Util::BitFlag optionParameter; + + u8 GetChannelPriority() const; + u8 GetIsReleasePriorityFix() const; + }; + static_assert(sizeof(WaveSoundInfo) == 0xc); + + struct AdvancedWaveSoundInfo { + u32 waveArchiveId; + }; + static_assert(sizeof(AdvancedWaveSoundInfo) == 0x4); + + struct SequenceSoundInfo { + Util::Reference toBankIdTable; + u32 allocateTrackFlags; + Util::BitFlag optionParameter; + + void GetBankIds(u32* bankIds) const; + u32 GetStartOffset() const; + u8 GetChannelPriority() const; + bool IsReleasePriorityFix() const; + + const Util::Table& GetBankIdTable() const; + }; + static_assert(sizeof(SequenceSoundInfo) == 0x10); + + struct Sound3DInfo { + u32 flags; + f32 decayRatio; + u8 decayCurve; + u8 dopplerFactor; + u8 padding[2]; + Util::BitFlag optionParameter; + }; + static_assert(sizeof(Sound3DInfo) == 0x10); + + struct BankInfo { + u32 fileId; + Util::Reference toWaveArchiveItemIdTable; + Util::BitFlag optionParameter; + + u32 GetStringId() const; + + const Util::Table* GetWaveArchiveItemIdTable() const { + return util::ConstBytePtr(this, toWaveArchiveItemIdTable.offset) + .Get>(); + } + }; + static_assert(sizeof(BankInfo) == 0x10); + + struct PlayerInfo { + u32 playableSoundMax; + Util::BitFlag optionParameter; + + u32 GetStringId() const; + u32 GetPlayerHeapSize() const; + }; + static_assert(sizeof(PlayerInfo) == 0x8); + + struct WaveSoundGroupInfo; + struct SoundGroupInfo { + u32 startId; + u32 endId; + Util::Reference toFileIdTable; + Util::Reference toDetailSoundGroupInfo; + Util::BitFlag optionParameter; + + u32 GetStringId() const; + + const Util::Table* GetFileIdTable() const { + return util::ConstBytePtr(this, toFileIdTable.offset) + .Get>(); + } + + const WaveSoundGroupInfo* GetWaveSoundGroupInfo() const { + return util::ConstBytePtr(this, toDetailSoundGroupInfo.offset) + .Get(); + } + }; + static_assert(sizeof(SoundGroupInfo) == 0x1c); + + struct WaveSoundGroupInfo { + Util::Reference toWaveArchiveItemIdTable; + Util::BitFlag optionParameter; + + const Util::Table* GetWaveArchiveItemIdTable() const { + return util::ConstBytePtr(this, toWaveArchiveItemIdTable.offset) + .Get>(); + } + }; + static_assert(sizeof(WaveSoundGroupInfo) == 0xc); + + struct GroupInfo { + u32 fileId; + Util::BitFlag optionParameter; + + u32 GetStringId() const; + }; + static_assert(sizeof(GroupInfo) == 0x8); + + struct WaveArchiveInfo { + u32 fileId; + bool isLoadIndividual; + u8 padding[3]; + Util::BitFlag optionParameter; + + u32 GetStringId() const; + u32 GetWaveCount() const; + }; + static_assert(sizeof(WaveArchiveInfo) == 0xc); + + enum FileLocationType { + FileLocationType_Internal, + FileLocationType_External, + FileLocationType_None, + }; + + struct InternalFileInfo; + struct ExternalFileInfo; + struct FileInfo { + Util::Reference toFileLocation; + Util::BitFlag optionParameter; + + FileLocationType GetFileLocationType() const; + const InternalFileInfo* GetInternalFileInfo() const; + const ExternalFileInfo* GetExternalFileInfo() const; + }; + static_assert(sizeof(FileInfo) == 0xc); + + struct InternalFileInfo { + static const u32 InvalidOffset {0xffffffff}; + static const u32 InvalidSize {0xffffffff}; + + Util::ReferenceWithSize toFileImageFromFileBlockBody; + Util::Reference toAttachedGroupIdTable; + + u32 GetFileSize() const { + return toFileImageFromFileBlockBody.size; + } + + u32 GetOffsetFromFileBlockHead() const { + return toFileImageFromFileBlockBody.offset; + } + + const Util::Table* GetAttachedGroupTable() const { + return util::ConstBytePtr(this, toAttachedGroupIdTable.offset) + .Get>(); + } + }; + static_assert(sizeof(InternalFileInfo) == 0x14); + + struct ExternalFileInfo { + char filePath[1]; + }; + + struct SoundArchivePlayerInfo { + u16 sequenceSoundCount; + u16 sequenceTrackCount; + u16 streamSoundCount; + u16 streamTrackCount; + u16 streamChannelCount; + u16 waveSoundCount; + u16 waveTrackCount; + u8 streamBufferTimes; + u8 developFlags; + u32 options; + }; + static_assert(sizeof(SoundArchivePlayerInfo) == 0x14); + + struct FileBlock {}; +}; +} // namespace nn::atk::detail \ No newline at end of file diff --git a/include/nn/atk/atk_SoundArchiveFileReader.h b/include/nn/atk/atk_SoundArchiveFileReader.h new file mode 100644 index 00000000..404556e2 --- /dev/null +++ b/include/nn/atk/atk_SoundArchiveFileReader.h @@ -0,0 +1,84 @@ +#pragma once + +#include +#include + +namespace nn::atk::detail { +class SoundArchiveFileReader { +public: + constexpr static s32 SignatureFile = 0x52415346; // FSAR + constexpr static s32 InvalidOffset = -1; + constexpr static s32 InvalidSize = -1; + + SoundArchiveFileReader(); + + void Initialize(const void* soundArchiveData); + void Finalize(); + + bool IsStreamSendAvailable() const; + bool IsFilterSupportedVersion() const; + bool IsStreamPrefetchAvailable() const; + + void SetStringBlock(const void* stringBlock); + void SetInfoBlock(const void* infoBlock); + + s32 GetStringCount() const; + char* GetString(u32) const; + + void DumpTree() const; + + SoundArchive::ItemId GetItemId(const char* pStr); + const char* GetItemLabel(SoundArchive::ItemId id) const; + + SoundArchive::FileId GetItemFileId(SoundArchive::ItemId id) const; + SoundArchive::FileId GetItemPrefetchFileId(SoundArchive::ItemId id) const; + + u32 GetSoundCount() const; + u32 GetBankCount() const; + u32 GetPlayerCount() const; + u32 GetSoundGroupCount() const; + u32 GetGroupCount() const; + u32 GetWaveArchiveCount() const; + u32 GetFileCount() const; + + bool ReadSoundInfo(SoundArchive::ItemId soundId, SoundArchive::SoundInfo* info) const; + bool ReadBankInfo(SoundArchive::ItemId bankId, SoundArchive::BankInfo* info) const; + bool ReadPlayerInfo(SoundArchive::ItemId playerId, SoundArchive::PlayerInfo* info) const; + bool ReadSoundGroupInfo(SoundArchive::ItemId soundGroupId, SoundArchive::SoundGroupInfo* info) const; + bool ReadGroupInfo(SoundArchive::ItemId groupId, SoundArchive::GroupInfo* info) const; + bool ReadFileInfo(SoundArchive::FileId id, SoundArchive::FileInfo* info, s32 index) const; + bool ReadWaveArchiveInfo(SoundArchive::ItemId warcId, SoundArchive::WaveArchiveInfo* info) const; + bool ReadSoundArchivePlayerInfo(SoundArchive::SoundArchivePlayerInfo* info) const; + bool ReadSound3DInfo(SoundArchive::ItemId soundId, SoundArchive::Sound3DInfo* info) const; + bool ReadSequenceSoundInfo(SoundArchive::ItemId soundId, SoundArchive::SequenceSoundInfo* info) const; + bool ReadStreamSoundInfo(SoundArchive::ItemId soundId, SoundArchive::StreamSoundInfo* info) const; + bool ReadStreamSoundInfo2(SoundArchive::ItemId soundId, SoundArchive::StreamSoundInfo2* info) const; + bool ReadWaveSoundInfo(SoundArchive::ItemId soundId, SoundArchive::WaveSoundInfo* info) const; + bool ReadAdvancedWaveSoundInfo(SoundArchive::ItemId soundId, SoundArchive::AdvancedWaveSoundInfo* info) const; + + Util::Table* GetWaveArchiveIdTable(SoundArchive::ItemId id) const; + SoundArchive::SoundType GetSoundType(SoundArchive::ItemId soundId) const; + u32 GetSoundUserParam(u32) const; + + bool ReadSoundUserParam(u32*, u32, s32) const; + const detail::Util::Table* GetAttachedGroupTable(u32) const; + + int GetInfoBlockOffset() const { return m_Header.GetInfoBlockOffset(); } + int GetFileBlockOffset() const { return m_Header.GetFileBlockOffset(); } + int GetStringBlockOffset() const { return m_Header.GetStringBlockOffset(); } + + u32 GetInfoBlockSize() const { return m_Header.GetInfoBlockSize(); } + u32 GetFileBlockSize() const { return m_Header.GetFileBlockSize(); } + u32 GetStringBlockSize() const { return m_Header.GetStringBlockSize(); } + +private: + friend SoundArchive; + + SoundArchiveFile::FileHeader m_Header; + SoundArchiveFile::StringBlockBody* m_pStringBlockBody; + SoundArchiveFile::InfoBlockBody* m_pInfoBlockBody; +#if NN_SDK_VER < NN_MAKE_VER(4, 0, 0) + SoundArchiveFile::FileBlock* m_pFileBlock; +#endif +}; +} // namespace nn::atk::detail \ No newline at end of file diff --git a/include/nn/atk/atk_SoundArchiveFilesHook.h b/include/nn/atk/atk_SoundArchiveFilesHook.h new file mode 100644 index 00000000..0c1d94ea --- /dev/null +++ b/include/nn/atk/atk_SoundArchiveFilesHook.h @@ -0,0 +1,35 @@ +#pragma once + +#include +#include + +namespace nn::atk::detail { +class SoundArchiveFilesHook { +public: + constexpr static const char ItemTypeWaveSound[] = "wsd"; + constexpr static const char ItemTypeStreamSound[] = "stm"; + constexpr static const char ItemTypeSequenceSound[] = "seq"; + + constexpr static const char FileTypeStreamBinary[] = "bxstm"; + constexpr static const char FileTypeWaveSoundBinary[] = "bxwsd"; + constexpr static const char FileTypeSequenceBinary[] = "bxseq"; + constexpr static const char FileTypeBankBinary[] = "bxbnk"; + constexpr static const char FileTypeWaveArchiveBinary[] = "bxwar"; + constexpr static const char FileTypeStreamPrefetchBinary[] = "bxstp"; + + bool GetIsEnable() const; + void SetIsEnable(bool value); + + bool IsTargetItem(const char* itemLabel); + + void Lock(); + void Unlock(); + + fnd::FileStream* OpenFile(void* buffer, size_t bufferLength, void* cacheBuffer, size_t cacheBufferLength, const char* itemLabel, const char* fileType); + + const void* GetFileAddress(const char* itemLabel, const char* itemType, const char* fileType, u32 fileIndex); + +private: + bool m_IsEnable; +}; +} // namespace nn::atk::detail \ No newline at end of file diff --git a/include/nn/atk/atk_SoundArchiveLoader.h b/include/nn/atk/atk_SoundArchiveLoader.h new file mode 100644 index 00000000..8ae5b3a6 --- /dev/null +++ b/include/nn/atk/atk_SoundArchiveLoader.h @@ -0,0 +1,120 @@ +#pragma once + +#include +#include + +namespace nn::atk::detail { +struct LoadItemInfo { + SoundArchive::ItemId itemId; + const void* address; +}; +static_assert(sizeof(LoadItemInfo) == 0x10); + +class SoundArchiveLoader { +public: + enum LoadFlag { + LoadFlag_All = -1, + LoadFlag_Seq = 1, + LoadFlag_Wsd = 2, + LoadFlag_Bank = 4, + LoadFlag_Warc = 8, + }; + + constexpr static s32 SignatureIndividualWave = 0x56574946; // FWIV + constexpr static u8 WaveBufferAlignSize = 64; + + struct IndividualWaveInfo { + u32 signature; + u32 fileId; + u32 waveIndex; + u32 padding[5]; + u32 padding2[8]; + }; + static_assert(sizeof(IndividualWaveInfo) == 0x40); + + SoundArchiveLoader(); + virtual ~SoundArchiveLoader(); + + virtual void* SetFileAddressToTable(SoundArchive::FileId fileId, const void* address) = 0; + virtual void* GetFileAddressFromTable(SoundArchive::FileId fileId) const = 0; + virtual void* GetFileAddressImpl(SoundArchive::FileId fileId) const = 0; + + void SetSoundArchive(const SoundArchive* arc); + + bool IsAvailable() const; + + bool LoadData(SoundArchive::ItemId itemId, SoundMemoryAllocatable* pAllocator, + u32 loadFlag, size_t loadBlockSize); + bool LoadSequenceSound(SoundArchive::ItemId soundId, SoundMemoryAllocatable* pAllocator, + u32 loadFlag, size_t loadBlockSize); + bool LoadAdvancedWaveSound(SoundArchive::ItemId soundId, SoundMemoryAllocatable* pAllocator, + u32 loadFlag, size_t loadBlockSize); + bool LoadWaveSound(SoundArchive::ItemId soundId, SoundMemoryAllocatable* pAllocator, + u32 loadFlag, size_t loadBlockSize, SoundArchive::ItemId waveSoundSetId); + bool LoadStreamSoundPrefetch(SoundArchive::ItemId soundId, SoundMemoryAllocatable* pAllocator, + size_t loadBlockSize); + bool LoadBank(SoundArchive::ItemId bankId, SoundMemoryAllocatable* pAllocator, + u32 loadFlag, size_t loadBlockSize); + bool LoadWaveArchive(SoundArchive::ItemId bankId, SoundMemoryAllocatable* pAllocator, + u32 loadFlag, size_t loadBlockSize); + bool LoadGroup(SoundArchive::ItemId soundGroupId, SoundMemoryAllocatable* pAllocator, + size_t loadBlockSize); + bool LoadSoundGroup(SoundArchive::ItemId soundGroupId, SoundMemoryAllocatable* pAllocator, + u32 loadFlag, size_t loadBlockSize); + + bool LoadData(char* pItemName, SoundMemoryAllocatable* pAllocator, + u32 loadFlag, size_t loadBlockSize); + void* LoadImpl(SoundArchive::FileId fileId, SoundMemoryAllocatable* pAllocator, + size_t loadBlockSize, bool needMemoryPool); + void* LoadFile(SoundArchive::FileId fileId, SoundMemoryAllocatable* pAllocator, + size_t loadBlockSize, bool needMemoryPool); + + void* LoadWaveArchiveImpl(SoundArchive::ItemId warcId, SoundMemoryAllocatable* pAllocator, + size_t loadBlockSize, bool needMemoryPool); + bool LoadIndividualWave(SoundArchive::ItemId warcId, u32 waveIndex, SoundMemoryAllocatable* pAllocator, + size_t loadBlockSize); + void* LoadWaveArchiveTable(SoundArchive::ItemId warcId, SoundMemoryAllocatable* pAllocator, + size_t loadBlockSize); + + size_t ReadFile(SoundArchive::FileId fileId, void* buffer, size_t size, s32 offset, + size_t loadBlockSize); + + bool PostProcessForLoadedGroupFile(void* pGroupFile, SoundMemoryAllocatable* pAllocator, + size_t loadBlockSize); + + void SetWaveArchiveTableWithSeqInEmbeddedGroup(SoundArchive::ItemId seqId, + SoundMemoryAllocatable* pAllocator); + void SetWaveArchiveTableWithBankInEmbeddedGroup(SoundArchive::ItemId bankId, + SoundMemoryAllocatable* pAllocator); + void SetWaveArchiveTableWithWsdInEmbeddedGroup(SoundArchive::ItemId wsdId, + SoundMemoryAllocatable* pAllocator); + void SetWaveArchiveTableInEmbeddedGroupImpl(SoundArchive::ItemId warcId, + SoundMemoryAllocatable* pAllocator); + + bool IsDataLoaded(const char*, u32) const; + bool IsDataLoaded(u32, u32) const; + + bool IsSequenceSoundDataLoaded(u32, u32) const; + bool IsWaveSoundDataLoaded(u32, u32) const; + bool IsBankDataLoaded(u32, u32) const; + bool IsWaveArchiveDataLoaded(u32, u32) const; + bool IsGroupDataLoaded(u32) const; + bool IsSoundGroupDataLoaded(u32, u32) const; + + void* GetFileAddressFromSoundArchive(SoundArchive::FileId fileId) const; + + void* detail_GetFileAddressByItemId(SoundArchive::ItemId itemId) const; + + bool detail_LoadWaveArchiveByBankFile(void* bankFile, + SoundMemoryAllocatable* pAllocator); + bool detail_LoadWaveArchiveByWaveSoundFile(void* wsdFile, s32 wsdIndex, + SoundMemoryAllocatable* pAllocator); + +private: + SoundArchive* m_pSoundArchive; + u32 m_StreamArea[128]; + u16 m_LoadDataCallCount; + bool m_IsCancelLoading; +}; +static_assert(sizeof(SoundArchiveLoader) == 0x218); +} // namespace nn::atk::detail \ No newline at end of file diff --git a/include/nn/atk/atk_SoundArchiveParametersHook.h b/include/nn/atk/atk_SoundArchiveParametersHook.h new file mode 100644 index 00000000..4edbbc8d --- /dev/null +++ b/include/nn/atk/atk_SoundArchiveParametersHook.h @@ -0,0 +1,160 @@ +#pragma once + +#include + +namespace nn::atk::detail { +class SoundArchiveParametersHook { +public: + bool GetIsEnable() const { return m_IsEnable; } + void SetIsEnable(bool value) { m_IsEnable = value; } + + const char* GetItemLabel(SoundArchive::ItemId id) const { + if (m_IsEnable) + return GetItemLabelImpl(id); + + return nullptr; + } + + SoundArchive::ItemId GetItemId(const char* itemLabel) const { + if (m_IsEnable) + return GetItemIdImpl(itemLabel); + + return SoundArchive::InvalidId; + } + + SoundArchive::SoundType GetSoundType(const char* itemLabel) { + if (m_IsEnable) + return GetSoundTypeImpl(itemLabel); + + return SoundArchive::SoundType_Invalid; + } + + bool ReadSoundInfo(SoundArchive::ItemId soundId, SoundArchive::SoundInfo* info) const { + if (m_IsEnable) + return ReadSoundInfoImpl(soundId, info); + + return false; + } + + bool ReadBankInfo(SoundArchive::ItemId bankId, SoundArchive::BankInfo* info) const { + if (m_IsEnable) + return ReadBankInfoImpl(bankId, info); + + return false; + } + + bool ReadPlayerInfo(SoundArchive::ItemId playerId, SoundArchive::PlayerInfo* info) const { + if (m_IsEnable) + return ReadPlayerInfoImpl(playerId, info); + + return false; + } + + bool ReadSoundGroupInfo(SoundArchive::ItemId soundGroupId, SoundArchive::SoundGroupInfo* info) const { + if (m_IsEnable) + return ReadSoundGroupInfoImpl(soundGroupId, info); + + return false; + } + + bool ReadGroupInfo(SoundArchive::ItemId groupId, SoundArchive::GroupInfo* info) const { + if (m_IsEnable) + return ReadGroupInfoImpl(groupId, info); + + return false; + } + + bool ReadFileInfo(SoundArchive::FileId id, SoundArchive::FileInfo* info, int index) const { + if (m_IsEnable) + return ReadFileInfoImpl(id, info, index); + + return false; + } + + bool ReadSoundArchivePlayerInfo(SoundArchive::SoundArchivePlayerInfo* info) const { + if (m_IsEnable) + return ReadSoundArchivePlayerInfoImpl(info); + + return false; + } + + bool ReadSound3DInfo(SoundArchive::ItemId soundId, SoundArchive::Sound3DInfo* info) const { + if (m_IsEnable) + return ReadSound3DInfoImpl(soundId, info); + + return false; + } + + bool ReadWaveArchiveInfo(SoundArchive::ItemId warcId, SoundArchive::WaveArchiveInfo* info) const { + if (m_IsEnable) + return ReadWaveArchiveInfoImpl(warcId, info); + + return false; + } + + bool ReadSequenceSoundInfo(SoundArchive::ItemId soundId, SoundArchive::SequenceSoundInfo* info) const { + if (m_IsEnable) + return ReadSequenceSoundInfoImpl(soundId, info); + + return false; + } + + bool ReadStreamSoundInfo(SoundArchive::ItemId soundId, SoundArchive::StreamSoundInfo* info) const { + if (m_IsEnable) + return ReadStreamSoundInfoImpl(soundId, info); + + return false; + } + + bool ReadStreamSoundInfo2(SoundArchive::ItemId soundId, SoundArchive::StreamSoundInfo2* info) const { + if (m_IsEnable) + return ReadStreamSoundInfo2Impl(soundId, info); + + return false; + } + + bool ReadWaveSoundInfo(SoundArchive::ItemId soundId, SoundArchive::WaveSoundInfo* info) const { + if (m_IsEnable) + return ReadWaveSoundInfoImpl(soundId, info); + + return false; + } + + bool ReadSoundUserParam(u32* pOutValue, SoundArchive::ItemId soundId, int index) const { + if (m_IsEnable) + return ReadSoundUserParamImpl(pOutValue, soundId, index); + + return false; + } + + u32 GetSoundUserParam(SoundArchive::ItemId soundId, u32 userParam) const { + return GetSoundUserParamImpl(soundId, userParam); + } + +protected: + virtual void Impl0(); + virtual void Impl1(); + virtual void Impl2(); + virtual const char* GetItemLabelImpl(SoundArchive::ItemId id) const; + virtual SoundArchive::ItemId GetItemIdImpl(const char* itemLabel) const; + virtual SoundArchive::SoundType GetSoundTypeImpl(const char* itemLabel) const; + virtual bool ReadSoundInfoImpl(SoundArchive::ItemId soundId, SoundArchive::SoundInfo* info) const; + virtual bool ReadBankInfoImpl(SoundArchive::ItemId bankId, SoundArchive::BankInfo* info) const; + virtual bool ReadPlayerInfoImpl(SoundArchive::ItemId playerId, SoundArchive::PlayerInfo* info) const; + virtual bool ReadSoundGroupInfoImpl(SoundArchive::ItemId soundGroupId, SoundArchive::SoundGroupInfo* info) const; + virtual bool ReadGroupInfoImpl(SoundArchive::ItemId groupId, SoundArchive::GroupInfo* info) const; + virtual bool ReadWaveArchiveInfoImpl(SoundArchive::ItemId warcId, SoundArchive::WaveArchiveInfo* info) const; + virtual bool ReadFileInfoImpl(SoundArchive::FileId id, SoundArchive::FileInfo* info, int index) const; + virtual bool ReadSoundArchivePlayerInfoImpl(SoundArchive::SoundArchivePlayerInfo* info) const; + virtual bool ReadSound3DInfoImpl(SoundArchive::ItemId soundId, SoundArchive::Sound3DInfo* info) const; + virtual bool ReadSequenceSoundInfoImpl(SoundArchive::ItemId soundId, SoundArchive::SequenceSoundInfo* info) const; + virtual bool ReadStreamSoundInfoImpl(SoundArchive::ItemId soundId, SoundArchive::StreamSoundInfo* info) const; + virtual bool ReadStreamSoundInfo2Impl(SoundArchive::ItemId soundId, SoundArchive::StreamSoundInfo2* info) const; + virtual bool ReadWaveSoundInfoImpl(SoundArchive::ItemId soundId, SoundArchive::WaveSoundInfo* info) const; + virtual bool ReadSoundUserParamImpl(u32* pOutValue, SoundArchive::ItemId soundId, int index) const; + virtual u32 GetSoundUserParamImpl(SoundArchive::ItemId soundId, u32 userParam) const; + +private: + bool m_IsEnable; +}; +} // namespace nn::atk::detail \ No newline at end of file diff --git a/include/nn/atk/atk_SoundArchivePlayer.h b/include/nn/atk/atk_SoundArchivePlayer.h new file mode 100644 index 00000000..b0623b5a --- /dev/null +++ b/include/nn/atk/atk_SoundArchivePlayer.h @@ -0,0 +1,216 @@ +#pragma once + +#include + +#include +#include +#include +#include + +namespace nn::atk { +class SoundArchivePlayer : SoundStartable { +public: + constexpr static u32 BufferAlignSize = 4096; + constexpr static u32 StreamBufferTimesMax = 4; + constexpr static u8 UserParamBoundary = 4; + + struct InitializeParam { + SoundArchive* pSoundArchive; + SoundDataManager* pSoundDataManager; + void* pSetupBuffer; + size_t setupBufferSize; + void* pStreamBuffer; + size_t streamBufferSize; + void* pStreamCacheBuffer; + size_t streamCacheSize; + bool enablePreparingStreamInstanceBufferFromSetupBuffer; + void* pStreamInstanceBuffer; + size_t streamInstanceBufferSize; + size_t userParamSizePerSound; + s32 addonSoundArchiveCount; + }; + static_assert(sizeof(InitializeParam) == 0x68); + + struct StreamSoundInstanceState { + s32 activeStreamSoundInstanceCount; + s32 activeStreamChannelCount; + s32 activeStreamTrackCount; + }; + static_assert(sizeof(StreamSoundInstanceState) == 0xc); + + SoundArchivePlayer(); + ~SoundArchivePlayer() override; + + bool IsAvailable() const; + + bool Initialize(const SoundArchive* arc, const SoundDataManager* manager, void* buffer, + size_t size, void* strmBuffer, size_t strmBufferSize, + size_t userParamSizePerSound); + bool Initialize(const InitializeParam& param); + + void Finalize(); + + void StopAllSound(s32, bool); + + void DisposeInstances(); + + static size_t GetRequiredMemSize(const SoundArchive* arc); + static size_t GetRequiredMemSize(const SoundArchive* arc, size_t userParamSizePerSound, + s32 addonSoundArchiveCount); + static size_t GetRequiredMemSize(const SoundArchive* arc, size_t userParamSizePerSound); + static size_t GetRequiredMemSize(const InitializeParam& param); + + static size_t GetRequiredStreamInstanceSize(const SoundArchive* arc); + + size_t GetRequiredStreamBufferSize(const SoundArchive* arc) const; + size_t GetRequiredStreamBufferTimes(const SoundArchive* arc) const; + + static size_t GetRequiredStreamCacheSize(const SoundArchive* arc, size_t); + + + bool SetupMram(const SoundArchive* pArc, void* buffer, size_t size, + size_t userParamSizePerSound, s32 addonSoundArchiveCount, + void* streamSoundInstanceBuffer, size_t streamSoundInstanceBufferSize); + + bool SetupSoundPlayer(const SoundArchive* pArc, void** pOutAllocatedAddr, const void* endAddr); + bool SetupAddonSoundArchiveContainer(s32 containerCount, void** pOutAllocatedAddr, const void* endAddr); + bool SetupUserParamForBasicSound(const SoundArchive::SoundArchivePlayerInfo& playerInfo, + void** pOutAllocatedAddr, const void* endAddr, size_t); + + detail::PlayerHeap* CreatePlayerHeap(void** pOutAllocatedAddr, const void* endAddr, size_t); + + void Update(); + + SoundPlayer* GetSoundPlayer(u32); + + SoundArchive* GetSoundArchive() const; + AddonSoundArchive* GetAddonSoundArchive(const char*) const; + AddonSoundArchive* GetAddonSoundArchive(s32) const; + char* GetAddonSoundArchiveName(s32) const; + os::Tick* GetAddonSoundArchiveAddTick(s32) const; + SoundDataManager* GetAddonSoundDataManager(const char*) const; + + SoundPlayer* GetSoundPlayer(u32) const; + SoundPlayer* GetSoundPlayer(const char*); + SoundPlayer* GetSoundPlayer(const char*) const; + + void* detail_GetFileAddress(SoundArchive::FileId fileId) const; + + void AddAddonSoundArchive(const char*, const AddonSoundArchive*, const SoundDataManager*); + void RemoveAddonSoundArchive(const AddonSoundArchive*); + +#if NN_SDK_VER >= NN_MAKE_VER(4, 0, 0) + void SetDefaultOutputReceiver(OutputReceiver* pOutputReceiver); +#endif + + StartResult detail_SetupSound(SoundHandle* handle, u32 soundId, bool holdFlag, + const char* soundArchiveName, const StartInfo* startInfo) override; + StartResult detail_SetupSoundImpl(SoundHandle* handle, u32 soundId, + detail::BasicSound::AmbientInfo* ambientArgInfo, + SoundActor* actor, bool holdFlag, const char* soundArchiveName, + const StartInfo* startInfo); + + bool IsSoundArchiveFileHooksEnabled() const; + + void LockSoundArchiveFileHooks(); + + bool IsSequenceSoundEdited(const char*) const; + bool IsStreamSoundEdited(const char*) const; + bool IsWaveSoundEdited(const char*) const; + + void EnableHook(const SoundArchive*, bool); + + StartResult PreprocessSinglePlay(const SoundArchive::SoundInfo& info, u32 soundId, + SoundPlayer& player); + + void SetCommonSoundParam(detail::BasicSound* pSound, const SoundArchive::SoundInfo* info); + + void UnlockSoundArchiveFileHooks(); + + void SetSequenceUserProcCallback(SequenceUserProcCallback callback, void* arg); + + static void SetSequenceSkipIntervalTick(s32 tick); + static s32 GetSequenceSkipIntervalTick(); + + Result ReadWaveSoundDataInfo(detail::WaveSoundDataInfo*, u32, const SoundArchive*, + const SoundDataManager*) const; + Result ReadWaveSoundDataInfo(detail::WaveSoundDataInfo*, u32, const char*) const; + Result ReadWaveSoundDataInfo(detail::WaveSoundDataInfo*, const char*, const char*) const; + Result ReadWaveSoundDataInfo(detail::WaveSoundDataInfo*, u32) const; + Result ReadWaveSoundDataInfo(detail::WaveSoundDataInfo*, const char*) const; + + Result ReadStreamSoundDataInfo(detail::StreamSoundDataInfo*, const SoundArchive*, u32) const; + Result ReadStreamSoundDataInfo(detail::StreamSoundDataInfo*, u32, const char*) const; + Result ReadStreamSoundDataInfo(detail::StreamSoundDataInfo*, const char*, const char*) const; + Result ReadStreamSoundDataInfo(detail::StreamSoundDataInfo*, u32) const; + Result ReadStreamSoundDataInfo(detail::StreamSoundDataInfo*, const char*) const; + + static size_t GetRequiredWorkBufferSizeToReadStreamSoundHeader(); + + Result ReadStreamSoundRegionDataInfo(detail::StreamSoundRegionDataInfo*, u32, + const char* const*, s32, const SoundArchive*, + void*, size_t) const; + Result ReadStreamSoundRegionDataInfo(detail::StreamSoundRegionDataInfo*, u32, const char*, + void*, size_t) const; + Result ReadStreamSoundRegionDataInfo(detail::StreamSoundRegionDataInfo*, u32, + const char* const*, s32, void*, size_t, + const char*) const; + Result ReadStreamSoundRegionDataInfo(detail::StreamSoundRegionDataInfo*, const char*, + const char*, void*, size_t) const; + Result ReadStreamSoundRegionDataInfo(detail::StreamSoundRegionDataInfo*, const char*, + const char* const*, s32, void*, size_t, + const char*) const; + Result ReadStreamSoundRegionDataInfo(detail::StreamSoundRegionDataInfo*, u32, + const char* const*, s32, void*, size_t) const; + Result ReadStreamSoundRegionDataInfo(detail::StreamSoundRegionDataInfo*, const char*, + const char* const*, s32, void*, size_t) const; + Result ReadStreamSoundRegionDataInfo(detail::StreamSoundRegionDataInfo*, const char*, + const char*, void*, size_t, + const char* soundArchiveName) const; + Result ReadStreamSoundRegionDataInfo(detail::StreamSoundRegionDataInfo*, u32, const char*, + void*, size_t, const char* soundArchiveName) const; + + void DumpMemory() const; + + bool ReadStreamSoundInstanceState(StreamSoundInstanceState*) const; + + Result CheckStreamSoundFileExisting(u32) const; + Result CheckStreamSoundFileExisting(u32, const char* soundArchiveName) const; + Result CheckStreamSoundFileExisting(char* streamSoundName) const; + Result CheckStreamSoundFileExisting(const char*, const char*) const; + Result CheckStreamSoundFileExisting(const SoundArchive*, u32) const; + + SoundArchive::ItemId detail_GetItemId(char* pString) override; + SoundArchive::ItemId detail_GetItemId(char* pString, const char* soundArchiveName) override; + +private: + detail::SoundArchiveManager m_SoundArchiveManager; + u32 m_SoundPlayerCount; + SoundPlayer* m_pSoundPlayers; + detail::SequenceSoundRuntime m_SequenceSoundRuntime; + detail::WaveSoundRuntime m_WaveSoundRuntime; + detail::AdvancedWaveSoundRuntime m_AdvancedWaveSoundRuntime; + detail::StreamSoundRuntime m_StreamSoundRuntime; + size_t m_SoundUserParamSize; + s32 m_ArchiveContainerCount; + detail::AddonSoundArchiveContainer* m_pArchiveContainers; + os::Tick m_AddonSoundArchiveLastAddTick; + audio::MemoryPoolType m_MemoryPoolForStreamInstance; + bool m_IsMemoryPoolForStreamInstanceAttached; + audio::MemoryPoolType m_MemoryPoolForPlayerHeap; + bool m_IsMemoryPoolForPlayerHeapAttached; + detail::SoundArchiveFilesHook* m_pSoundArchiveFilesHook; + bool m_IsEnableWarningPrint; + bool m_IsInitialized; + bool m_IsAdvancedWaveSoundEnabled; +#if NN_SDK_VER >= NN_MAKE_VER(4, 0, 0) + OutputReceiver* m_pDefaultOutputReceiver; + u8 m_Padding[1]; +#endif +}; +#if NN_SDK_VER < NN_MAKE_VER(4, 0, 0) +static_assert(sizeof(SoundArchivePlayer) == 0x2e0); +#else +static_assert(sizeof(SoundArchivePlayer) == 0x310); +#endif +} // namespace nn::atk diff --git a/include/nn/atk/atk_SoundDataManager.h b/include/nn/atk/atk_SoundDataManager.h new file mode 100644 index 00000000..95355fe6 --- /dev/null +++ b/include/nn/atk/atk_SoundDataManager.h @@ -0,0 +1,56 @@ +#pragma once + +#include +#include +#include + +namespace nn::atk { + +namespace detail { +class SoundFileManager; +} // namespace nn::atk::detail + +class SoundDataManager : detail::driver::DisposeCallback, detail::SoundArchiveLoader { +public: + struct FileAddress { + void* address; + }; + + using FileTable = detail::Util::Table; + + constexpr static u32 BufferAlignSize = 8; + + SoundDataManager(); + ~SoundDataManager() override; + + size_t GetRequiredMemSize(const SoundArchive* arc) const; + + bool Initialize(const SoundArchive* pArchive, void* buffer, size_t size); + + bool CreateTables(void**, const SoundArchive* pArchive, void*); + + void Finalize(); + + void InvalidateData(const void* start, const void* end) override; + + void* detail_GetFileAddress(SoundArchive::FileId fileId) const; + void* GetFileAddressImpl(SoundArchive::FileId fileId) const override; + + void* SetFileAddressToTable(SoundArchive::FileId fileId, const void* address) override; + void* GetFileAddressFromTable(SoundArchive::FileId fileId) const override; + + void* SetFileAddress(SoundArchive::FileId fileId, const void* address); + + SoundArchive::FileId detail_GetFileIdFromTable(const void*) const; + + bool SetFileAddressInGroupFile(const void*, size_t); + void ClearFileAddressInGroupFile(const void*, size_t); + + void InvalidateSoundData(const void*, size_t); + +private: + FileTable* m_pFileTable; + detail::SoundFileManager* m_pFileManager; +}; +static_assert(sizeof(SoundDataManager) == 0x240); +} // namespace nn::atk diff --git a/include/nn/atk/atk_SoundHandle.h b/include/nn/atk/atk_SoundHandle.h new file mode 100644 index 00000000..a10b329b --- /dev/null +++ b/include/nn/atk/atk_SoundHandle.h @@ -0,0 +1,39 @@ +#pragma once + +#include + +namespace nn::atk { +class SoundHandle { +public: + enum MuteState { + MuteState_Normal, + MuteState_Muting, + MuteState_Muted, + MuteState_Unmuting, + MuteState_Invalid, + }; + + enum PauseState { + PauseState_Normal, + PauseState_Pausing, + PauseState_Paused, + PauseState_Unpausing, + PauseState_Invalid + }; + + ~SoundHandle() = default; + + void detail_DuplicateHandle(SoundHandle* other); + + void DetachSound(); + + void detail_AttachSoundAsTempHandle(detail::BasicSound* sound); + void detail_AttachSound(detail::BasicSound* sound); + + void CalculateSoundParamCalculationValues(SoundParamCalculationValues* calcValues) const; + +private: + detail::BasicSound* m_pSound; +}; +static_assert(sizeof(SoundHandle) == 0x8); +} // namespace nn::atk \ No newline at end of file diff --git a/include/nn/atk/atk_SoundHeap.h b/include/nn/atk/atk_SoundHeap.h new file mode 100644 index 00000000..1375705b --- /dev/null +++ b/include/nn/atk/atk_SoundHeap.h @@ -0,0 +1,43 @@ +#pragma once + +#include +#include + +#include +#include +#include + +namespace nn::atk::detail { +class SoundHeap : SoundMemoryAllocatable { +public: + SoundHeap(); + ~SoundHeap() override; + + void Destroy(); + + bool Create(void* startAddress, size_t size); + bool Create(void* startAddress, size_t size, bool); + + void Clear(); + + void* Allocate(size_t size) override; + + static void DisposeCallbackFunc(void* mem, size_t size, void* arg); + + void* Allocate(size_t size, + SoundMemoryAllocatable::DisposeCallback heapCallback, + void* heapCallbackArg) override; + + size_t GetAllocateSize(size_t size, bool needMemoryPool) override; + + s32 SaveState(); + void LoadState(s32 state); + +private: + fnd::CriticalSection m_CriticalSection; + FrameHeap m_FrameHeap; + audio::MemoryPoolType m_MemoryPool; + bool m_IsAutoMemoryPoolManagementEnabled; +}; +static_assert(sizeof(SoundHeap) == 0x50); +} // namespace nn::atk::detail \ No newline at end of file diff --git a/include/nn/atk/atk_SoundInstanceManager.h b/include/nn/atk/atk_SoundInstanceManager.h new file mode 100644 index 00000000..351432c7 --- /dev/null +++ b/include/nn/atk/atk_SoundInstanceManager.h @@ -0,0 +1,35 @@ +#pragma once + +#include +#include + +#include +#include + +namespace nn::atk::detail { +template +class SoundInstanceManager { +public: + using PriorityList = util::IntrusiveList>; + using Iterator = typename PriorityList::iterator; + +#if NN_SDK_VER < NN_MAKE_VER(4, 0, 0) + T* Alloc(s32 priority, s32 ambientPriority); +#else + T* Alloc(s32 priority, s32 ambientPriority, OutputReceiver* pOutputReceiver); +#endif + s32 Create(void* buffer, size_t size, const SoundInstanceConfig& config); + + void SortPriorityList(); + +private: + void* m_pBuffer; + size_t m_BufferSize; +#if NN_SDK_VER >= NN_MAKE_VER(4, 0, 0) + SoundInstanceConfig m_SoundInstanceConfig; +#endif + PriorityList m_PriorityList; + PriorityList m_FreeList; +}; +} // namespace nn::atk::detail \ No newline at end of file diff --git a/include/nn/atk/atk_SoundMemoryAllocatable.h b/include/nn/atk/atk_SoundMemoryAllocatable.h new file mode 100644 index 00000000..eb2a56d3 --- /dev/null +++ b/include/nn/atk/atk_SoundMemoryAllocatable.h @@ -0,0 +1,15 @@ +#pragma once + +#include + +namespace nn::atk { +class SoundMemoryAllocatable { +public: + using DisposeCallback = void(*)(void*); + + virtual ~SoundMemoryAllocatable() = 0; + virtual void* Allocate(size_t size) = 0; + virtual void* Allocate(size_t size, DisposeCallback callback, void* callbackArg) = 0; + virtual size_t GetAllocateSize(size_t size, bool needMemoryPool) = 0; +}; +} // namespace nn::atk \ No newline at end of file diff --git a/include/nn/atk/atk_SoundPlayer.h b/include/nn/atk/atk_SoundPlayer.h new file mode 100644 index 00000000..3bcf9c49 --- /dev/null +++ b/include/nn/atk/atk_SoundPlayer.h @@ -0,0 +1,107 @@ +#pragma once + +#include + +#include +#include +#include + +namespace nn::atk { +class SoundPlayer { +public: + using SoundList = util::IntrusiveList< + detail::BasicSound, util::IntrusiveListMemberNodeTraits< + detail::BasicSound, &detail::BasicSound::m_SoundPlayerPlayLink>>; + using PriorityList = util::IntrusiveList< + detail::BasicSound, util::IntrusiveListMemberNodeTraits< + detail::BasicSound, &detail::BasicSound::m_SoundPlayerPriorityLink>>; + using PlayerHeapList = util::IntrusiveList< + detail::PlayerHeap, util::IntrusiveListMemberNodeTraits< + detail::PlayerHeap, &detail::PlayerHeap::m_Link>>; + struct OutputParam { + f32 volume; + f32 mainSend; + f32 fxSend[3]; + }; + static_assert(sizeof(OutputParam) == 0x14); + + SoundPlayer(); +#if NN_SDK_VER >= NN_MAKE_VER(4, 0, 0) + explicit SoundPlayer(detail::OutputAdditionalParam* pParam); +#endif + ~SoundPlayer(); + + void StopAllSound(s32 fadeFrames); + + void Update(); + + void DoFreePlayerHeap(); + + void detail_SortPriorityList(bool reverse); + + void PauseAllSound(s32, bool); + void PauseAllSound(bool, s32); + void PauseAllSound(bool, s32, PauseMode); + + void SetVolume(f32 volume); + void SetLowPassFilterFrequency(f32 lpfFreq); + void SetBiquadFilter(s32 biquadFilterType, f32 biquadFilterValue); + void SetDefaultOutputLine(u32 line); + + void SetMainSend(f32 send); + f32 GetMainSend() const; + + void SetEffectSend(AuxBus bus, f32 send); + void GetEffectSend(AuxBus bus) const; + + void SetSend(s32 subMixBus, f32 send); + f32 GetSend(s32 subMixBus); + + void SetOutputVolume(OutputDevice device, f32 volume); + + void RemoveSoundList(detail::BasicSound* pSound); + + void InsertPriorityList(detail::BasicSound* pSound); + void RemovePriorityList(detail::BasicSound* pSound); + + void detail_SortPriorityList(detail::BasicSound* pSound); + + bool detail_AppendSound(detail::BasicSound* pSound); + bool detail_RemoveSound(detail::BasicSound* pSound); + + void SetPlayableSoundCount(s32 count); + + void detail_SetPlayableSoundLimit(s32 limit); + + bool detail_CanPlaySound(s32 startPriority); + bool CanPlaySound(s32 startPriority); + + void detail_AppendPlayerHeap(detail::PlayerHeap* pHeap); + detail::PlayerHeap* detail_AllocPlayerHeap(); + void detail_FreePlayerHeap(detail::PlayerHeap* pHeap); + +private: + SoundList m_SoundList; + PriorityList m_PriorityList; + PlayerHeapList m_PlayerHeapFreeList; + PlayerHeapList m_PlayerHeapFreeReqList; + s32 m_PlayableCount; + s32 m_PlayableLimit; + u32 m_PlayerHeapCount; + f32 m_Volume; + f32 m_LpfFreq; + s32 m_BiquadType; + f32 m_BiquadValue; + u32 m_OutputLineFlag; + OutputParam m_TvParam; +#if NN_SDK_VER >= NN_MAKE_VER(4, 0, 0) + detail::OutputAdditionalParam* m_pOutputAdditionalParam; +#endif + bool m_IsFirstComeBased; +}; +#if NN_SDK_VER < NN_MAKE_VER(4, 0, 0) +static_assert(sizeof(SoundPlayer) == 0x78); +#else +static_assert(sizeof(SoundPlayer) == 0x88); +#endif +} // namespace nn::atk diff --git a/include/nn/atk/atk_SoundRuntimeUtility.h b/include/nn/atk/atk_SoundRuntimeUtility.h new file mode 100644 index 00000000..6f3504d2 --- /dev/null +++ b/include/nn/atk/atk_SoundRuntimeUtility.h @@ -0,0 +1,5 @@ +#pragma once + +namespace nn::atk::detail { +class SoundRuntimeUtility {}; +} // namespace nn::atk::detail \ No newline at end of file diff --git a/include/nn/atk/atk_SoundStartable.h b/include/nn/atk/atk_SoundStartable.h new file mode 100644 index 00000000..f9d6730b --- /dev/null +++ b/include/nn/atk/atk_SoundStartable.h @@ -0,0 +1,162 @@ +#pragma once + +#include +#include +#include +#include +#include + +namespace nn::atk { +class SoundStartable { +public: + struct StartInfo { + enum StartOffsetType { + StartOffsetType_MilliSeconds, + StartOffsetType_Tick, + StartOffsetType_Sample, + }; + + enum EnableFlagBit { + EnableFlagBit_StartOffset = 1 << 0, + EnableFlagBit_PlayerId = 1 << 1, + EnableFlagBit_PlayerPriority = 1 << 2, + EnableFlagBit_ActorPlayerId = 1 << 3, + EnableFlagBit_SequenceSoundInfo = 1 << 4, + EnableFlagBit_StreamSoundInfo = 1 << 5, + EnableFlagBit_WaveSoundInfo = 1 << 6, + EnableFlagBit_VoiceRendererType = 1 << 7, + EnableFlagBit_FadeFrame = 1 << 8, + EnableFlagBit_SoundStopCallback = 1 << 9, + EnableFlagBit_StreamSoundMetaInfo = 1 << 10, + EnableFlagBit_StreamSoundMetaInfo2 = 1 << 11, + EnableFlagBit_DelayTime = 1 << 12, + EnableFlagBit_DelayCount = 1 << 13, + EnableFlagBit_UpdateType = 1 << 14, + EnableFlagBit_SubMixIndex = 1 << 15, + EnableFlagBit_OutputReceiver = 1 << 16, + }; + + struct SequenceSoundInfo { + void* sequenceDataAddress; + char* startLocationLabel; + SoundArchive::ItemId bankIds[4]; + }; + static_assert(sizeof(SequenceSoundInfo) == 0x20); + + struct StreamSoundInfo { + char* externalPath; + void* pExternalData; + size_t externalDataSize; + StreamRegionCallback regionCallback; + void* regionCallbackArg; + void* prefetchData; + bool forcePlayPrefetchFlag; + detail::driver::StreamBufferPool* pStreamBufferPool; + }; + static_assert(sizeof(StreamSoundInfo) == 0x40); + + struct WaveSoundInfo { + enum EnableParameterFlagBit { + EnableParameterFlagBit_Release = 1, + EnableParameterFlagBit_ContextCalculationSkipMode, + }; + + void* waveAddress; + s8 waveType; + u8 m_Padding[3]; + s32 enableParameterFlag; + s32 release; + bool isContextCalculationSkipMode; + }; + static_assert(sizeof(WaveSoundInfo) == 0x18); + + u32 enableFlag; + StartOffsetType startOffsetType; + s32 startOffset; + SoundArchive::ItemId playerId; + s32 playerPriority; + s32 actorPlayerId; + SequenceSoundInfo sequenceSoundInfo; + StreamSoundInfo streamSoundInfo; + SoundArchive::StreamSoundInfo streamSoundMetaInfo; + SoundArchive::StreamSoundInfo2 streamSoundMetaInfo2; + WaveSoundInfo waveSoundInfo; + u8 voiceRendererType; + s32 fadeFrame; + SoundStopCallback soundStopCallback; + s32 delayTime; + s32 delayCount; + UpdateType updateType; + s32 subMixIndex; +#if NN_SDK_VER >= NN_MAKE_VER(4, 0, 0) + OutputReceiver* pOutputReceiver; +#endif + }; + + struct StartResult { + enum ResultCode { + ResultCode_Success, + ResultCode_ErrorLowPriority, + ResultCode_ErrorInvalidLabelString, + ResultCode_ErrorInvalidSoundId, + ResultCode_CanceledBySinglePlay, + ResultCode_ErrorCanceledForPrioritizingOldest = ResultCode_CanceledBySinglePlay, + ResultCode_ErrorNotDataLoaded, + ResultCode_ErrorNotSequenceLoaded, + ResultCode_ErrorNotBankLoaded, + ResultCode_ErrorNotWsdLoaded, + ResultCode_ErrorNotWarcLoaded, + ResultCode_ErrorNotEnoughPlayerHeap, + ResultCode_ErrorCannotOpenFile, + ResultCode_ErrorNotAvailable, + ResultCode_ErrorCannotAllocateTrack, + ResultCode_ErrorNotEnoughInstance, + ResultCode_ErrorInvalidParameter, + ResultCode_ErrorInvalidSequenceStartLocationLabel, + ResultCode_ErrorActorNotInitialized, + ResultCode_ErrorInvalidWarcId, + ResultCode_ErrorInvalidBankData, + ResultCode_ErrorInvalidStreamFileId, + ResultCode_ErrorInvalidStreamFilePath, + + ResultCode_ErrorUser = 0x80, + ResultCode_ErrorUnknown = 0xff, + }; + + ResultCode m_Code; + }; + + virtual ~SoundStartable() = 0; + + virtual StartResult detail_SetupSound(SoundHandle* handle, u32 soundId, + bool holdFlag, const char* soundArchiveName, + const StartInfo* startInfo) = 0; + + virtual SoundArchive::ItemId detail_GetItemId(char* pString) = 0; + virtual SoundArchive::ItemId detail_GetItemId(char* pString, const char* soundArchiveName) = 0; + + StartResult StartSound(SoundHandle* handle, u32 soundId, + const char* soundArchiveName, const StartInfo* startInfo); + StartResult StartSound(SoundHandle* handle, u32 soundId, const StartInfo* startInfo); + StartResult StartSound(SoundHandle* handle, const char* pString, + const char* soundArchiveName, const StartInfo* startInfo); + StartResult StartSound(SoundHandle* handle, const char* soundArchiveName, + const StartInfo* startInfo); + + StartResult HoldSound(SoundHandle* handle, u32 soundId, const char* soundArchiveName, + const StartInfo* startInfo); + StartResult HoldSound(SoundHandle* handle, u32 soundId, const StartInfo* startInfo); + StartResult HoldSound(SoundHandle* handle, const char* pString, const StartInfo* startInfo); + StartResult HoldSound(SoundHandle* handle, const char* pString, const char* soundArchiveName, + const StartInfo* startInfo); + + StartResult PrepareSound(SoundHandle* handle, u32 soundId, const char* soundArchiveName, + const StartInfo* startInfo); + StartResult PrepareSound(SoundHandle* handle, u32 soundId, const StartInfo* startInfo); + StartResult PrepareSound(SoundHandle* handle, const char* pString, + const char* soundArchiveName, const StartInfo* startInfo); + StartResult PrepareSound(SoundHandle* handle, const char* soundArchiveName, + const StartInfo* startInfo); +}; +static_assert(sizeof(SoundStartable) == 0x8); +} // namespace nn::atk \ No newline at end of file diff --git a/include/nn/atk/atk_SoundSystem.h b/include/nn/atk/atk_SoundSystem.h new file mode 100644 index 00000000..40f7401b --- /dev/null +++ b/include/nn/atk/atk_SoundSystem.h @@ -0,0 +1,297 @@ +#pragma once + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +namespace nn::atk { +struct SoundSystem { + static bool g_IsInitialized; + static bool g_IsStreamLoadWait; + static bool g_IsEnterSleep; + static bool g_IsInitializedDriverCommandManager; + + static uintptr_t g_LoadThreadStackPtr; + static size_t g_LoadThreadStackSize; + + static uintptr_t g_SoundThreadStackPtr; + static size_t g_SoundThreadStackSize; + + static uintptr_t g_PerformanceFrameBuffer; + static size_t g_PerformanceFrameBufferSize; + + static size_t g_SoundThreadCommandBufferSize; + static size_t g_TaskThreadCommandBufferSize; + static size_t g_VoiceCommandBufferSize; + + static uintptr_t g_MemoryPoolForSoundSystem; + + static s32 g_RendererSampleRate; + static s32 g_CustomSubMixSubMixCount; + static s32 g_CustomSubMixMixBufferCount; + + static bool g_IsProfilerEnabled; + static bool g_IsDetailSoundThreadProfilerEnabled; + static bool g_IsAdditionalEffectBusEnabled; + static bool g_IsAdditionalSubMixEnabled; + static bool g_IsEffectEnabled; + static bool g_IsRecordingEnabled; + static bool g_IsCircularBufferSinkEnabled; + static bool g_IsCircularBufferSinkWarningDisplayed; + static bool g_IsVoiceDropEnabled; + static bool g_IsPreviousSdkVersionLowPassFilterCompatible; + static bool g_IsUnusedEffectChannelMutingEnabled; + static bool g_IsUserThreadRenderingEnabled; + static bool g_IsCustomSubMixEnabled; + static bool g_IsMemoryPoolAttachCheckEnabled; + static bool g_IsBusMixVolumeEnabled; + static bool g_IsVolumeThroughModeEnabled; + + constexpr static u32 VoiceCountMax = 96; + constexpr static u32 WorkMemoryAlignSize = 4; + constexpr static u32 VoiceCommandManagerCountMax = 2; + constexpr static u32 SoundThreadIntervalUsec = 5000; + + constexpr static s32 g_TaskThreadFsPriority = 1; + constexpr static bool g_IsStreamOpenFailureHalt = true; + constexpr static bool g_IsTaskThreadEnabled = true; + constexpr static bool g_IsManagingMemoryPool = true; + constexpr static u32 g_UserEffectCount = 10; + constexpr static bool g_IsSubMixEnabled = true; + constexpr static bool g_IsPresetSubMixEnabled = true; + constexpr static bool g_IsStereoModeEnabled = true; + constexpr static bool g_IsSoundThreadEnabled = true; + constexpr static s32 g_BusCountMax = 4; + + struct SoundSystemParam { + constexpr static u32 DefaultSoundThreadPriority = 0; + constexpr static u32 DefaultTaskThreadPriority = 0; + constexpr static u32 DefaultSoundThreadStackSize = 0x4000; + constexpr static u32 DefaultTaskThreadStackSize = 0x4000; + constexpr static u32 DefaultSoundThreadCommandBufferSize = 0x20000; + constexpr static u32 DefaultTaskThreadCommandBufferSize = 0x2000; + constexpr static u32 DefaultUserEffectCount = 10; + constexpr static u32 DefaultVoiceCountMax = 96; + constexpr static u32 DefaultSoundThreadCoreNumber = 0; + constexpr static u32 DefaultTaskThreadCoreNumber = 0; + constexpr static u32 DefaultRecordingAudioFrameCount = 8; + + SoundSystemParam(); + + s32 soundThreadPriority{4}; + size_t soundThreadStackSize{0x4000}; + size_t soundThreadCommandBufferSize{0x20000}; + size_t voiceCommandBufferSize; + s32 taskThreadPriority{3}; + size_t taskThreadStackSize{0x4000}; + size_t taskThreadCommandBufferSize{0x2000}; + FsPriority taskThreadFsPriority{FsPriority_Normal}; + bool enableNwRenderer{}; + u32 nwVoiceSynthesizeBufferCount; + s32 rendererSampleRate{48000}; + s32 effectCount{10}; + s32 voiceCountMax{96}; + s32 voiceCommandWaveBufferPacketCount{0x200}; + bool enableProfiler{false}; + bool enableDetailSoundThreadProfile{false}; + bool enableRecordingFinalOutputs{false}; + bool enableCircularBufferSink{false}; + s32 recordingAudioFrameCount{8}; + s32 soundThreadCoreNumber; + s32 taskThreadCoreNumber; + bool enableAdditionalEffectBus{false}; + bool enableAdditionalSubMix{false}; + bool enableTaskThread{true}; + bool enableSoundThread{true}; + bool enableMemoryPoolManagement{true}; + bool enableCircularBufferSinkBufferManagement{true}; + bool enableEffect{true}; + bool enableSubMix{true}; + bool enableStereoMode{false}; + bool enableVoiceDrop{true}; + bool enableCompatibleDownMixSetting{false}; + bool enableCompatibleLowPassFilter; + bool enableUnusedEffectChannelMuting; + bool enableCompatibleBusVolume; + bool enableUserThreadRendering; + bool enableCustomSubMix; + s32 subMixCount{0}; + s32 subMixTotalChannelCount{0}; + s32 mixBufferCount{-1}; + s32 busCountMax{4}; + bool enableMemoryPoolAttachCheck{false}; + bool enableBusMixVolume{false}; + bool enableVolumeThroughMode{false}; + }; + static_assert(sizeof(SoundSystemParam) == 0x88); + + struct InitializeBufferSet { + uintptr_t workMem; + size_t workMemSize; + uintptr_t memoryPoolMem; + size_t memoryPoolMemSize; + uintptr_t circularBufferSinkMem; + size_t circularBufferSinkMemSize; + }; + static_assert(sizeof(InitializeBufferSet) == 0x30); + + static size_t GetRequiredMemSize(const SoundSystemParam& param); + static size_t GetRequiredMemSizeForCircularBufferSink(const SoundSystemParam& param); + static size_t GetRequiredMemSizeForMemoryPool(const SoundSystemParam& param); + + static void SetupHardwareManagerParameter(detail::driver::HardwareManager::HardwareManagerParameter* pOutValue, + SoundSystemParam* parameter); + + static bool detail_InitializeSoundSystem(Result* pOutResult, const SoundSystemParam& param, const InitializeBufferSet& bufferSet); + static void detail_InitializeDriverCommandManager(const SoundSystemParam& param, u64, u64, u64, u64); + + static bool Initialize(SoundSystemParam* param, uintptr_t workMem, size_t workMemSize); + static bool Initialize(Result* pOutResult, const SoundSystemParam& param, + uintptr_t workMem, size_t workMemSize); + + static void SetupInitializeBufferSet(InitializeBufferSet* pOutValue, + SoundSystemParam* param, InitializeBufferSet* bufferSet); + + static bool Initialize(SoundSystemParam* param, uintptr_t workMem, size_t workMemSize, + uintptr_t memoryPoolMem, size_t memoryPoolMemSize); + static bool Initialize(Result* pOutResult, const SoundSystemParam& param, uintptr_t workMem, + size_t workMemSize, uintptr_t memoryPoolMem, size_t memoryPoolMemSize); + + static bool Initialize(const SoundSystemParam& param, const InitializeBufferSet& bufferSet); + static bool Initialize(Result* pOutResult, const SoundSystemParam& param, const InitializeBufferSet& bufferSet); + + static void Finalize(); + + static void SetSoundThreadBeginUserCallback(void(*threadBeginUserCallback)(u64), uintptr_t threadBeginUserCallbackArg); + static void ClearSoundThreadBeginUserCallback(); + + static void SetSoundThreadEndUserCallback(void(*threadEndUserCallback)(u64), uintptr_t threadEndUserCallbackArg); + static void ClearSoundThreadEndUserCallback(); + + static bool IsInitialized(); + + static void SuspendAudioRenderer(TimeSpan timeSpan); + static void ResumeAudioRenderer(TimeSpan timeSpan); + + static void ExecuteRendering(); + + static void AttachMemoryPool(audio::MemoryPoolType* pMemoryPool, void* address, size_t size); + static void DetachMemoryPool(audio::MemoryPoolType* pMemoryPool); + + static void DumpMemory(); + + static size_t GetAudioRendererBufferSize(); + + static void SetupHardwareManagerParameterFromCurrentSetting(detail::driver::HardwareManager::HardwareManagerParameter* pHardwareManagerParameter); + + static size_t GetRecorderBufferSize(); + static size_t GetUserCircularBufferSinkBufferSize(); + static size_t GetLowLevelVoiceAllocatorBufferSize(); + static size_t GetMultiVoiceManagerBufferSize(); + static size_t GetChannelManagerBufferSize(); + static size_t GetDriverCommandBufferSize(); + static size_t GetAllocatableDriverCommandSize(); + static size_t GetAllocatedDriverCommandBufferSize(); + static size_t GetAllocatedDriverCommandCount(); + + static void RegisterAudioRendererPerformanceReader(AudioRendererPerformanceReader& audioRendererPerformanceReader); + + static bool AppendEffect(AuxBus auxBus, EffectBase* pEffectBase, + void* buffer, size_t bufferSize); + static bool AppendEffect(AuxBus auxBus, EffectBase* pEffectBase, + void* buffer, size_t bufferSize, OutputDevice device); + static bool AppendEffect(AuxBus auxBus, EffectBase* pEffectBase, + void* buffer, size_t bufferSize, OutputDevice device, s32 subMixNumber); + + static bool AppendEffect(AuxBus auxBus, EffectAux* pEffectAux, + void* buffer, size_t bufferSize); + static bool AppendEffect(AuxBus auxBus, EffectAux* pEffectAux, + void* buffer, size_t bufferSize, OutputDevice device); + static bool AppendEffect(AuxBus auxBus, EffectAux* pEffectAux, + void* buffer, size_t bufferSize, OutputDevice device, s32 subMixNumber); + + static bool AppendEffectToFinalMix(EffectAux* pEffectAux, void* buffer, size_t bufferSize); + static bool AppendEffectToAdditionalSubMix(EffectAux* pEffectAux, void* buffer, size_t bufferSize); + + static size_t GetRequiredEffectAuxBufferSize(const EffectAux* pEffectAux); + + static void RemoveEffect(AuxBus auxBus, EffectBase* pEffectBase); + static void RemoveEffect(AuxBus auxBus, EffectBase* pEffectBase, OutputDevice outputDevice); + static void RemoveEffect(AuxBus auxBus, EffectBase* pEffectBase, OutputDevice outputDevice, + s32 subMixNumber); + + static void RemoveEffect(AuxBus auxBus, EffectAux* pEffectAux); + static void RemoveEffect(AuxBus auxBus, EffectAux* pEffectAux, OutputDevice outputDevice); + static void RemoveEffect(AuxBus auxBus, EffectAux* pEffectAux, OutputDevice outputDevice, + s32 subMixNumber); + + static void RemoveEffectFromFinalMix(EffectAux* pEffectAux); + static void RemoveEffectFromAdditionalSubMix(EffectAux* pEffectAux); + + static void ClearEffect(AuxBus auxBus); + static void ClearEffect(AuxBus auxBus, OutputDevice outputDevice); + static void ClearEffect(AuxBus auxBus, OutputDevice outputDevice, s32 subMixNumber); + + static void ClearEffectFromFinalMix(); + static void ClearEffectFromAdditionalSubMix(); + + static bool IsClearEffectFinished(AuxBus auxBus); + static bool IsClearEffectFinished(AuxBus auxBus, OutputDevice outputDevice); + static bool IsClearEffectFinished(AuxBus auxBus, OutputDevice outputDevice, s32 subMixNumber); + + static bool IsClearEffectFromFinalMixFinished(); + static bool IsClearEffectFromAdditionalSubMixFinished(); + + static void SetAuxBusVolume(AuxBus auxBus, f32 volume, TimeSpan timeSpan); + static void SetAuxBusVolume(AuxBus auxBus, f32 volume, TimeSpan timeSpan, s32); + + static f32 GetAuxBusVolume(AuxBus auxBus); + static f32 GetAuxBusVolume(AuxBus auxBus, s32 subMixIndex); + + static void SetMainBusChannelVolumeForAdditionalEffect(f32 volume, s32 srcChannel, s32 dstChannel); + static f32 GetMainBusChannelVolumeForAdditionalEffect(s32 srcChannel, s32 dstChannel); + + static void SetAuxBusChannelVolumeForAdditionalEffect(AuxBus auxBus, f32 volume, s32 srcChannel, s32 dstChannel); + static f32 GetAuxBusChannelVolumeForAdditionalEffect(AuxBus auxBus, s32 srcChannel, s32 dstChannel); + + static void SetAllAuxBusChannelVolumeForAdditionalEffect(f32 volume, s32 srcChannel, s32 dstChannel); + static void SetAllBusChannelVolumeForAdditionalEffect(f32 volume, s32 srcChannel, s32 dstChannel); + + static void VoiceCommandProcess(UpdateType updateType, u32); + static void VoiceCommandProcess(u32); + + static void VoiceCommandUpdate(); + + static size_t GetPerformanceFrameBufferSize(); + + static s32 GetDroppedLowLevelVoiceCount(); + + static void RegisterSoundThreadUpdateProfileReader(AtkProfileReader&); + static void UnregisterSoundThreadUpdateProfileReader(AtkProfileReader&); + + static void RegisterSoundThreadInfoRecorder(detail::ThreadInfoRecorder&); + static void UnregisterSoundThreadInfoRecorder(detail::ThreadInfoRecorder&); + + static bool ReadCircularBufferSink(void* buffer, size_t bufferSize); + + static size_t GetCircularBufferSinkBufferSize(); + + static u32 GetRendererSampleCount(); + static u32 GetRendererChannelCountMax(); + + static void StopCircularBufferSink(); + static void StartCircularBufferSink(); + + static CircularBufferSinkState GetCircularBufferSinkState(); + static detail::SoundInstanceConfig GetSoundInstanceConfig(); +}; +} // namespace nn::atk \ No newline at end of file diff --git a/include/nn/atk/atk_SoundThread.h b/include/nn/atk/atk_SoundThread.h new file mode 100644 index 00000000..5032b98c --- /dev/null +++ b/include/nn/atk/atk_SoundThread.h @@ -0,0 +1,168 @@ +#pragma once + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +namespace nn::atk::detail::driver { +struct SoundThreadLock {}; +struct AtkStateAndParameterUpdateLock {}; + +class SoundThread : fnd::Thread::Handler { +public: + enum Message { + Message_HwCallback = 0x10000000, + Message_Shutdown = 0x20000000, + Message_ForceWakeup = 0x30000000, + }; + + using ProfileFunc = void(*)(os::Tick*); + + constexpr static u32 ThreadMessageBuffferSize = 32; + + constexpr static u32 RendererEventWaitTimeoutMilliSeconds = 100; + + class SoundFrameCallback { + public: + virtual ~SoundFrameCallback(); + + virtual void OnBeginSoundFrame(); + virtual void OnEndSoundFrame(); + + private: + friend SoundThread; + + util::IntrusiveListNode m_Link; + }; + + using SoundFrameCallbackList = util::IntrusiveList>; + + class PlayerCallback { + public: + virtual ~PlayerCallback(); + + virtual void OnUpdateFrameSoundThread() = 0; + virtual void OnUpdateFrameSoundThreadWithAudioFrameFrequency() = 0; + virtual void OnShutdownSoundThread() = 0; + + private: + friend SoundThread; + + util::IntrusiveListNode m_Link; + }; + + using PlayerCallbackList = util::IntrusiveList>; + + ~SoundThread() override; + + bool CreateSoundThread(s32 threadPriority, void* stackBase, size_t stackSize, + s32 idealCoreNumber, u32 affinityMask); + + void Initialize(void* performanceFrameBuffer, size_t performanceFrameBufferSize, + bool isProfilingEnabled); + void Initialize(void* performanceFrameBuffer, size_t performanceFrameBufferSize, + bool isProfilingEnabled, bool isDetailSoundThreadProfilerEnabled, + bool isUserThreadRenderingEnabled); + + void Destroy(); + + void Finalize(); + + void UpdateLowLevelVoices(); + + void ForceWakeup(); + + void RegisterSoundFrameUserCallback(SoundFrameUserCallback callback, std::uintptr_t callbackArg); + void ClearSoundFrameUserCallback(); + + void RegisterThreadBeginUserCallback(SoundThreadUserCallback, std::uintptr_t callbackArg); + void ClearThreadBeginUserCallback(); + + void RegisterThreadEndUserCallback(SoundThreadUserCallback, std::uintptr_t callbackArg); + void ClearThreadEndUserCallback(); + + void RegisterSoundFrameCallback(SoundFrameCallback* callback); + void UnregisterSoundFrameCallback(SoundFrameCallback* callback); + + void RegisterPlayerCallback(PlayerCallback* callback); + void UnregisterPlayerCallback(PlayerCallback* callback); + + void LockAtkStateAndParameterUpdate(); + void UnlockAtkStateAndParameterUpdate(); + + void RegisterAudioRendererPerformanceReader(AudioRendererPerformanceReader& performanceReader); + + void RegisterSoundThreadUpdateProfileReader(SoundThreadUpdateProfileReader& profileReader); + void UnregisterSoundThreadUpdateProfileReader(SoundThreadUpdateProfileReader& profileReader); + + void RegisterSoundThreadInfoRecorder(ThreadInfoRecorder& recorder); + void UnregisterSoundThreadInfoRecorder(ThreadInfoRecorder& recorder); + + void FrameProcess(UpdateType updateType); + + void RecordPerformanceInfo(audio::PerformanceInfo* src, os::Tick beginTick, + os::Tick endTick, u32 nwVoiceCount); + + void EffectFrameProcess(); + + void RecordUpdateProfile(const SoundThreadUpdateProfile& threadUpdateProfile); + + u32 Run(void* param) override; + +private: + fnd::Thread m_Thread; + os::MessageQueue m_BlockingQueue; + std::uintptr_t m_MsgBuffer[ThreadMessageBuffferSize]; + u32 m_AxCallbackCounter; + fnd::CriticalSection m_CriticalSection; + fnd::CriticalSection m_UpdateAtkStateAndParameterSection; + SoundFrameCallbackList m_SoundFrameCallbackList; + PlayerCallbackList m_PlayerCallbackList; + SoundFrameUserCallback m_UserCallback; + std::uintptr_t m_UserCallbackArg; + SoundThreadUserCallback m_ThreadBeginUserCallback; + std::uintptr_t m_ThreadBeginUserCallbackArg; + SoundThreadUserCallback m_ThreadEndUserCallback; + std::uintptr_t m_ThreadEndUserCallbackArg; + s32 m_SoundThreadAffinityMask; + bool m_CreateFlag; + bool m_PauseFlag; + os::Tick m_LastPerformanceFrameBegin; + os::Tick m_LastPerformanceFrameEnd; + void* m_pPerformanceFrameUpdateBuffer[3]; + size_t m_PerformanceFrameUpdateBufferSize; + s32 m_CurrentPerformanceFrameBufferIndex; + bool m_IsProfilingEnabled; +#if NN_SDK_VER >= NN_MAKE_VER(4, 0, 0) + ProfileFunc m_pSoundThreadProfileFunc; + bool m_IsUserThreadRenderingEnabled; +#endif + ProfileReaderList m_ProfileReaderList; + AudioRendererPerformanceReader* m_pAudioRendererPerformanceReader; +#if NN_SDK_VER >= NN_MAKE_VER(4, 0, 0) + SoundThreadInfoRecorderList m_InfoRecorderList; + fnd::CriticalSection m_LockRecordInfo; +#endif + SoundThreadUpdateProfile m_LastUpdateProfile; + SoundThreadUpdateProfileReaderList m_UpdateProfileReaderList; + fnd::CriticalSection m_LockUpdateProfile; + std::atomic_int m_RendererEventWaitTimeMilliSeconds; +}; +#if NN_SDK_VER < NN_MAKE_VER(4, 0, 0) +static_assert(sizeof(SoundThread) == 0x4c8); +#else +static_assert(sizeof(SoundThread) == 0x508); +#endif +} // namespace nn::atk::detail::driver \ No newline at end of file diff --git a/include/nn/atk/atk_StartInfoReader.h b/include/nn/atk/atk_StartInfoReader.h new file mode 100644 index 00000000..c84db918 --- /dev/null +++ b/include/nn/atk/atk_StartInfoReader.h @@ -0,0 +1,36 @@ +#pragma once + +#include + +namespace nn::atk::detail { +class StartInfoReader { +public: + explicit StartInfoReader(const SoundArchive::SoundInfo& soundInfo); + + void Read(const SoundStartable::StartInfo* startInfo); + +private: + SoundStartable::StartInfo::StartOffsetType m_StartOffsetType; + s32 m_StartOffset; + s32 m_DelayTime; + s32 m_DelayCount; + UpdateType m_UpdateType; + s32 m_PlayerPriority; + SoundArchive::ItemId m_PlayerId; + s32 m_ActorPlayerId; + SoundStartable::StartInfo::SequenceSoundInfo* m_pSeqInfo; + SoundStartable::StartInfo::StreamSoundInfo* m_pStrmInfo; + SoundArchive::StreamSoundInfo* m_pStrmMetaInfo; + SoundArchive::StreamSoundInfo2* m_pStrmMetaInfo2; + SoundStartable::StartInfo::WaveSoundInfo* m_pWsdInfo; + s32 m_SubMixIndex; +#if NN_SDK_VER >= NN_MAKE_VER(4, 0, 0) + OutputReceiver* m_pOutputReceiver; +#endif +}; +#if NN_SDK_VER < NN_MAKE_VER(4, 0, 0) +static_assert(sizeof(StartInfoReader) == 0x50); +#else +static_assert(sizeof(StartInfoReader) == 0x58); +#endif +} // namespace nn::atk::detail \ No newline at end of file diff --git a/include/nn/atk/atk_StreamBufferPool.h b/include/nn/atk/atk_StreamBufferPool.h new file mode 100644 index 00000000..9e44fbbc --- /dev/null +++ b/include/nn/atk/atk_StreamBufferPool.h @@ -0,0 +1,28 @@ +#pragma once + +#include + +namespace nn::atk::detail::driver { +class StreamBufferPool { +public: + void Initialize(void* buffer, size_t size, int blockCount); + void Finalize(); + + void* Alloc(); + void Free(void* pPtr); + + size_t GetBlockSize() const { return m_BlockSize; } + +private: + static const int BlockMax {32}; + static const int BitPerByte {8}; + + void* m_Buffer; + size_t m_BufferSize; + size_t m_BlockSize; + int m_BlockCount; + int m_AllocCount; + u8 m_AllocFlags[BlockMax / BitPerByte]; +}; +static_assert(sizeof(StreamBufferPool) == 0x28); +} // namespace nn::atk::detail \ No newline at end of file diff --git a/include/nn/atk/atk_StreamSound.h b/include/nn/atk/atk_StreamSound.h new file mode 100644 index 00000000..e4a0fe0d --- /dev/null +++ b/include/nn/atk/atk_StreamSound.h @@ -0,0 +1,84 @@ +#pragma once + +#include +#include +#include + +namespace nn::atk::detail { +class StreamSound; +using StreamSoundInstanceManager = SoundInstanceManager; + +class StreamSound : BasicSound { +public: + explicit StreamSound(const StreamSoundInstanceManager& manager); + +#if NN_SDK_VER < NN_MAKE_VER(4, 0, 0) + bool Initialize() override; +#else + bool Initialize(OutputReceiver* pOutputReceiver) override; +#endif + void Finalize() override; + + void Setup(const driver::StreamSoundPlayer::SetupArg& arg); + + void Prepare(const driver::StreamSoundPlayer::PrepareBaseArg& arg); + void PreparePrefetch(const void* strmPrefetchFile, + const driver::StreamSoundPlayer::PrepareBaseArg& arg); + + void UpdateMoveValue() override; + + void OnUpdateParam() override; + + void SetTrackVolume(u32 trackBitFlag, f32 volume, s32); + void SetTrackInitialVolume(u32 trackBitFlag, u32 volume); + + void SetTrackOutputLine(u32 trackBitFlag, u32 outputLine); + void ResetTrackOutputLine(u32 trackBitFlag); + + void SetTrackMainOutVolume(u32 trackBitFlag, f32 volume); + void SetTrackChannelMixParameter(u32 trackBitFlag, u32 srcChNo, const MixParameter& param); + void SetTrackPan(u32 trackBitFlag, f32 pan); + void SetTrackSurroundPan(u32 trackBitFlag, f32 span); + void SetTrackMainSend(u32 trackBitFlag, f32 send); + void SetTrackFxSend(u32 trackBitFlag, AuxBus bus, f32 send); + + void OnUpdatePlayerPriority() override; + + bool IsAttachedTempSpecialHandle() override; + void DetachTempSpecialHandle() override; + + bool ReadStreamDataInfo(StreamDataInfo*) const; + + s32 GetPlayLoopCount() const; + position_t GetPlaySamplePosition(bool) const; + f32 GetFilledBufferPercentage() const; + s32 GetBufferBlockCount(WaveBuffer::Status waveBufferStatus) const; + s32 GetTotalBufferBlockCount() const; + + bool IsPrepared() const override; + bool IsSuspendByLoadingDelay() const; + bool IsLoadingDelayState() const; + + driver::BasicSoundPlayer * GetBasicSoundPlayerHandle() override; + +private: + friend StreamSoundInstanceManager; + + util::IntrusiveListNode m_PriorityLink; + StreamSoundHandle* m_pTempSpecialHandle; + StreamSoundInstanceManager* m_Manager; + MoveValue m_TrackVolume[8]; + u16 m_AllocTrackFlag; + bool m_InitializeFlag; + u8 m_Padding[1]; + u32 m_AvailableTrackBitFlag[2]; + void* m_pCacheBuffer; + size_t m_CacheSize; + driver::StreamSoundPlayer m_PlayerInstance; +}; +#if NN_SDK_VER < NN_MAKE_VER(4, 0, 0) +static_assert(sizeof(StreamSound) == 0x11a00); +#else +static_assert(sizeof(StreamSound) == 0x11a40); +#endif +} // namespace nn::atk::detail \ No newline at end of file diff --git a/include/nn/atk/atk_StreamSoundFile.h b/include/nn/atk/atk_StreamSoundFile.h new file mode 100644 index 00000000..bf019d23 --- /dev/null +++ b/include/nn/atk/atk_StreamSoundFile.h @@ -0,0 +1,164 @@ +#pragma once + +#include + +namespace nn::atk::detail { +struct StreamSoundFile { + struct InfoBlock; + + struct FileHeader : BinaryFileHeader { + private: + static const int BlockCount = 4; + + public: + Util::ReferenceWithSize toBlocks[BlockCount]; + + bool HasSeekBlock() const; + bool HasRegionBlock() const; + + u32 GetInfoBlockSize() const; + u32 GetSeekBlockSize() const; + u32 GetDataBlockSize() const; + u32 GetRegionBlockSize() const; + + u32 GetInfoBlockOffset() const; + u32 GetSeekBlockOffset() const; + u32 GetDataBlockOffset() const; + u32 GetRegionBlockOffset() const; + + const InfoBlock* GetInfoBlock() const { + return util::ConstBytePtr(this).Advance(GetInfoBlockOffset()).Get(); + }; + + private: + const Util::ReferenceWithSize* GetReferenceBy(u16 typeId) const; + }; + static_assert(sizeof(FileHeader) == 0x44); + + struct StreamSoundInfo; + struct TrackInfoTable; + struct ChannelInfoTable; + struct InfoBlockBody { + Util::Reference toStreamSoundInfo; + Util::Reference toTrackInfoTable; + Util::Reference toChannelInfoTable; + + const StreamSoundInfo* GetStreamSoundInfo() const; + const TrackInfoTable* GetTrackInfoTable() const; + const ChannelInfoTable* GetChannelInfoTable() const; + }; + static_assert(sizeof(InfoBlockBody) == 0x18); + + struct InfoBlock { + BinaryBlockHeader header; + InfoBlockBody body; + }; + static_assert(sizeof(InfoBlock) == 0x20); + + struct StreamSoundInfo { + u8 encodeMethod; + bool isLoop; + u8 channelCount; + u8 regionCount; + u32 sampleRate; + u32 loopStart; + u32 frameCount; + u32 blockCount; + u32 oneBlockBytes; + u32 oneBlockSamples; + u32 lastBlockBytes; + u32 lastBlockSamples; + u32 lastBlockPaddedBytes; + u32 sizeofSeekInfoAtom; + u32 seekInfoIntervalSamples; + Util::Reference sampleDataOffset; + u16 regionInfoBytes; + u8 padding[2]; + Util::Reference regionDataOffset; + u32 originalLoopStart; + u32 originalLoopEnd; + u32 crc32Value; + }; + static_assert(sizeof(StreamSoundInfo) == 0x50); + + struct TrackInfo; + struct TrackInfoTable { + Util::ReferenceTable table; + + u32 GetTrackCount() const { return table.count; } + + const TrackInfo* GetTrackInfo(u32 index) const; + }; + + struct GlobalChannelIndexTable; + struct TrackInfo { + u8 volume; + u8 pan; + u8 span; + u8 flags; + + Util::Reference toGlobalChannelIndexTable; + + u32 GetTrackChannelCount() const { + return GetGlobalChannelIndexTable().GetCount(); + } + + u8 GetGlobalChannelIndex(u32 index) const { + return GetGlobalChannelIndexTable().GetGlobalIndex(index); + } + + private: + const GlobalChannelIndexTable& GetGlobalChannelIndexTable() const { + return *util::ConstBytePtr(this) + .Advance(toGlobalChannelIndexTable.offset) + .Get(); + } + }; + static_assert(sizeof(TrackInfo) == 0xc); + + struct GlobalChannelIndexTable { + Util::Table table; + + u32 GetCount() const { return table.count; } + u8 GetGlobalIndex(u32 index) const { return table.item[index]; } + }; + + struct ChannelInfo; + struct ChannelInfoTable { + Util::ReferenceTable table; + + u32 GetChannelCount() const; + const ChannelInfo* GetChannelInfo(u32 index) const; + }; + + struct DspAdpcmChannelInfo; + struct ChannelInfo { + Util::Reference toDetailChannelInfo; + + const DspAdpcmChannelInfo* GetDspAdpcmChannelInfo() const; + }; + static_assert(sizeof(ChannelInfo) == 0x8); + + struct DspAdpcmChannelInfo { + DspAdpcmParam param; + DspAdpcmLoopParam loopParam; + }; + static_assert(sizeof(DspAdpcmChannelInfo) == 0x2c); + + struct RegionInfo { + u32 start; + u32 end; + DspAdpcmLoopParam adpcmContext[16]; + bool isEnabled; + u8 padding[87]; + char regionName[64]; + }; + static_assert(sizeof(RegionInfo) == 0x100); + + struct RegionBlock { + BinaryBlockHeader header; + RegionInfo info; + }; + static_assert(sizeof(RegionBlock) == 0x108); +}; +} // namespace nn::atk::detail \ No newline at end of file diff --git a/include/nn/atk/atk_StreamSoundFileLoader.h b/include/nn/atk/atk_StreamSoundFileLoader.h new file mode 100644 index 00000000..fb1f5082 --- /dev/null +++ b/include/nn/atk/atk_StreamSoundFileLoader.h @@ -0,0 +1,43 @@ +#pragma once + +#include +#include +#include + +namespace nn::atk::detail { +class StreamSoundFileLoader : public IRegionInfoReadable { +public: + StreamSoundFileLoader() = default; + explicit StreamSoundFileLoader(fnd::FileStream* stream) { + Initialize(stream); + } + + void Initialize(fnd::FileStream* stream) { + m_pStream = stream; + m_SeekBlockOffset = 0; + m_RegionDataOffset = 0; + m_RegionInfoBytes = 0; + } + + void Finalize() { + m_pStream = nullptr; + m_SeekBlockOffset = 0; + m_RegionDataOffset = 0; + m_RegionInfoBytes = 0; + } + + bool LoadFileHeader(StreamSoundFileReader* reader, void* buffer, u64 size); + + bool ReadSeekBlockData(u16* yn1, u16* yn2, int blockIndex, int channelCount); + bool ReadRegionInfo(StreamSoundFile::RegionInfo* pInfo, u32 regionIndex) const override; + + ~StreamSoundFileLoader() override; + +private: + fnd::FileStream* m_pStream {}; + u32 m_SeekBlockOffset {0}; + u32 m_RegionDataOffset {0}; + u16 m_RegionInfoBytes {0}; +}; +static_assert(sizeof(StreamSoundFileLoader) == 0x20); +} // namespace nn::atk::detail \ No newline at end of file diff --git a/include/nn/atk/atk_StreamSoundFileReader.h b/include/nn/atk/atk_StreamSoundFileReader.h new file mode 100644 index 00000000..e7e82175 --- /dev/null +++ b/include/nn/atk/atk_StreamSoundFileReader.h @@ -0,0 +1,82 @@ +#pragma once + +#include + +namespace nn::atk::detail { +class StreamSoundFileReader { +public: + struct TrackInfo { + u8 volume; + u8 pan; + u8 span; + u8 flags; + u8 channelCount; + u8 globalChannelIndex[2]; + + TrackInfo(); + }; + + StreamSoundFileReader(); + + void Initialize(const void* streamSoundFile); + void Finalize(); + + bool IsAvailable() const { + return m_pHeader != nullptr; + } + + bool IsTrackInfoAvailable() const; + bool IsOriginalLoopAvailable() const; + bool IsCrc32CheckAvailable() const; + bool IsRegionIndexCheckAvailable() const; + + static bool IsValidFileHeader(const void* streamSoundFile); + + bool ReadStreamSoundInfo(StreamSoundFile::StreamSoundInfo* strmInfo) const; + bool ReadStreamTrackInfo(TrackInfo* pTrackInfo, int trackIndex) const; + bool ReadDspAdpcmChannelInfo(DspAdpcmParam* pParam, DspAdpcmLoopParam* pLoopParam, + int channelIndex) const; + + u32 GetChannelCount() const; + u32 GetTrackCount() const; + + u32 GetSeekBlockOffset() const { + if (m_pHeader != nullptr && m_pHeader->HasSeekBlock()) + return m_pHeader->GetSeekBlockOffset(); + + return 0; + } + + u32 GetSampleDataOffset() const { + u32 result {0}; + if (m_pHeader != nullptr) + result = m_pHeader->GetDataBlockOffset() + + m_pInfoBlockBody->GetStreamSoundInfo()->sampleDataOffset.offset + + sizeof(BinaryBlockHeader); + + return result; + } + + u32 GetRegionDataOffset() const { + u32 result {0}; + if (m_pHeader != nullptr && m_pHeader->HasRegionBlock()) { + result = m_pHeader->GetRegionBlockOffset() + + m_pInfoBlockBody->GetStreamSoundInfo()->regionDataOffset.offset + + sizeof(BinaryBlockHeader); + } + + return result; + } + + u32 GetRegionInfoBytes() const { + return m_pInfoBlockBody->GetStreamSoundInfo()->regionInfoBytes; + } + + static bool IsOriginalLoopAvailableImpl(const StreamSoundFile::FileHeader* pHeader); + +private: + const StreamSoundFile::FileHeader* m_pHeader {}; + const StreamSoundFile::InfoBlockBody* m_pInfoBlockBody {}; +}; +static_assert(sizeof(StreamSoundFileReader) == 0x10); +} // namespace nn::atk::detail \ No newline at end of file diff --git a/include/nn/atk/atk_StreamSoundHandle.h b/include/nn/atk/atk_StreamSoundHandle.h new file mode 100644 index 00000000..6f7fe0a2 --- /dev/null +++ b/include/nn/atk/atk_StreamSoundHandle.h @@ -0,0 +1,24 @@ +#pragma once + +#include + +#include + +namespace nn::atk { +namespace detail { +class StreamSound; +} // namespace nn::atk::detail + +class StreamSoundHandle { +public: + using TrackBitFlagSet = util::BitFlagSet<8, void>; + + explicit StreamSoundHandle(SoundHandle* pSoundHandle); + + void detail_AttachSoundAsTempHandle(detail::StreamSound* pSound); + void DetachSound(); + +private: + detail::StreamSound* m_pSound; +}; +} // namespace nn::atk \ No newline at end of file diff --git a/include/nn/atk/atk_StreamSoundLoader.h b/include/nn/atk/atk_StreamSoundLoader.h new file mode 100644 index 00000000..6027d65a --- /dev/null +++ b/include/nn/atk/atk_StreamSoundLoader.h @@ -0,0 +1,306 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace nn::atk::detail { +struct IStreamDataDecoderManager; +struct DriverCommandStreamSoundLoadHeader; +struct DriverCommandStreamSoundLoadData; + +struct TrackDataInfo { + u8 volume; + u8 pan; + u8 span; + u8 flags; + u8 mainSend; + u8 fxSend[3]; + u8 lpfFreq; + u8 biquadType; + u8 biquadValue; + u8 channelCount; + u8 channelIndex[2]; +}; +static_assert(sizeof(TrackDataInfo) == 0xe); + +struct TrackDataInfos { + TrackDataInfo track[8]; +}; +static_assert(sizeof(TrackDataInfos) == 0x70); + +struct StreamDataInfoDetail { + void SetStreamSoundInfo(const StreamSoundFile::StreamSoundInfo&, bool); + + SampleFormat sampleFormat; + s32 sampleRate; + bool loopFlag; + position_t loopStart; + size_t sampleCount; + position_t originalLoopStart; + position_t originalLoopEnd; + bool isRevisionCheckEnabled; + bool isRegionIndexCheckEnabled; + u32 revisionValue; + size_t blockSampleCount; + size_t blockSize; + size_t lastBlockSize; + size_t lastBlockSampleCount; + s32 channelCount; + s32 trackCount; + TrackDataInfo trackInfo[8]; + s32 regionCount; +}; +static_assert(sizeof(StreamDataInfoDetail) == 0xd8); + +struct LoadDataParam { + u32 blockIndex; + size_t samples; + position_t sampleBegin; + position_t sampleOffset; + size_t sampleBytes; + bool adpcmContextEnable; + AdpcmContextNotAligned adpcmContext[16]; + s32 loopCount; + bool lastBlockFlag; + bool isStartOffsetOfLastBlockApplied; +}; +static_assert(sizeof(LoadDataParam) == 0x98); + +struct FileStreamHookParam { + SoundArchiveFilesHook* pSoundArchiveFilesHook; + char* itemLabel; +}; +static_assert(sizeof(FileStreamHookParam) == 0x10); + +namespace driver { +class StreamSoundPlayer; + +class StreamSoundLoader; +using StreamSoundLoaderManager = LoaderManager; + +class StreamSoundLoader { +public: + constexpr static size_t DataBlockSizeBase = 0x2000; + constexpr static size_t DataBlockSizeMargin = 0x900; + constexpr static size_t DataBlockSizeMax = DataBlockSizeBase + DataBlockSizeMargin; + + constexpr static u32 FileStreamBufferSize = 0x200; + constexpr static u32 LoadBufferChannelCount = 2; + constexpr static size_t LoadBufferSize = 0x5200; + + class StreamHeaderLoadTask : Task { + public: + StreamHeaderLoadTask(); + ~StreamHeaderLoadTask() override; + + void Execute(TaskProfileLogger& logger) override; + + private: + StreamSoundLoader* m_pLoader; + }; + static_assert(sizeof(StreamHeaderLoadTask) == 0x50); + + class StreamCloseTask : Task { + public: + StreamCloseTask(); + ~StreamCloseTask() override; + + void Execute(TaskProfileLogger& logger) override; + + private: + StreamSoundLoader* m_pLoader; + }; + static_assert(sizeof(StreamCloseTask) == 0x50); + + class StreamDataLoadTask : Task { + public: + StreamDataLoadTask(); + ~StreamDataLoadTask() override; + + void Execute(TaskProfileLogger& logger) override; + + private: + friend StreamSoundLoader; + + void* m_BufferAddress[16]; + u32 m_BufferBlockIndex; + position_t m_StartOffsetSamples; + position_t m_PrefetchOffsetSamples; + StreamSoundLoader* m_pLoader; + util::IntrusiveListNode m_Link; + }; + static_assert(sizeof(StreamDataLoadTask) == 0xf8); + + using StreamDataLoadTaskList = util::IntrusiveList>; + + struct AdpcmInfo { + AdpcmParam param; + AdpcmContext beginContext; + AdpcmContext loopContext; + }; + static_assert(sizeof(AdpcmInfo) == 0xc0); + + struct BlockInfo { + size_t size; + size_t samples; + size_t startOffsetSamples; + size_t startOffsetSamplesAlign; + size_t startOffsetByte; + size_t copyByte; + + }; + static_assert(sizeof(BlockInfo) == 0x30); + + StreamSoundLoader(); + ~StreamSoundLoader(); + + void WaitFinalize(); + + void Initialize(); + void Finalize(); + + void CancelRequest(); + void RequestClose(); + + void RegisterStreamDataDecoderManager(IStreamDataDecoderManager* manager); + void UnregisterStreamDataDecoderManager(IStreamDataDecoderManager* manager); + + void* detail_SetFsAccessLog(fnd::FsAccessLog* pFsAccessLog); + + position_t detail_GetCurrentPosition(); + position_t detail_GetCachePosition(); + + size_t detail_GetCachedLength(); + + void RequestLoadHeader(); + void RequestLoadData(void** bufferAddress, u32 bufferBlockIndex, position_t startOffsetSamples, + position_t prefetchOffsetSamples, s32 priority); + + void Update(); + void ForceFinish(); + + bool IsBusy() const; + bool IsInUse(); + + fnd::FndResult Open(); + void Close(); + + void LoadHeader(); + bool LoadHeader1(DriverCommandStreamSoundLoadHeader* command); + bool LoadHeaderForOpus(DriverCommandStreamSoundLoadHeader* command, + StreamFileType type, DecodeMode decodeMode); + + bool ReadTrackInfoFromStreamSoundFile(StreamSoundFileReader& reader); + + bool SetAdpcmInfo(StreamSoundFileReader& reader, s32 channelCount, AdpcmParam** adpcmParam); + + void UpdateLoadingDataBlockIndex(); + + IStreamDataDecoderManager* SelectStreamDataDecoderManager(StreamFileType, DecodeMode); + + void SetStreamSoundInfoForOpus(const IStreamDataDecoder::DataInfo& dataInfo); + + void LoadData(void** bufferAddress, u32 bufferBlockIndex, size_t startOffsetSamples, + size_t prefetchOffsetSamples, TaskProfileLogger& logger); + bool LoadData1(DriverCommandStreamSoundLoadData* command, void** bufferAddress, + u32 bufferBlockIndex, size_t startOffsetSamples, + size_t prefetchOffsetSamples, TaskProfileLogger& logger); + bool LoadDataForOpus(DriverCommandStreamSoundLoadData* command, void** bufferAddress, + u32 bufferBlockIndex, size_t startOffsetSamples, + size_t prefetchOffsetSamples, TaskProfileLogger& logger); + + bool ApplyStartOffset(s64, s32*); + + void CalculateBlockInfo(BlockInfo&); + + bool LoadAdpcmContextForStartOffset(); + + bool LoadOneBlockDataViaCache(void** bufferAddress, const BlockInfo& blockInfo, + position_t destAddressOffset, bool firstBlock, + bool updateAdpcmContext); + bool LoadOneBlockData(void** bufferAddress, const BlockInfo& blockInfo, + position_t destAddressOffset, bool firstBlock, + bool updateAdpcmContext); + + bool MoveNextRegion(s32*); + + bool DecodeStreamData(void**, IStreamDataDecoder::DecodeType); + + void UpdateLoadingDataBlockIndexForOpus(void**); + + bool IsLoopStartFilePos(u32); + + s32 GetLoadChannelCount(s32); + + bool LoadStreamBuffer(u8*, const BlockInfo&, u32); + bool LoadStreamBuffer(u8*, u64); + + bool SkipStreamBuffer(u64); + + void UpdateAdpcmInfoForStartOffset(const void*, s32, const BlockInfo&); + +private: + friend StreamSoundLoaderManager; + + StreamSoundFileLoader m_FileLoader; + StreamSoundPlayer* m_PlayerHandle; + fnd::FileStream* m_pFileStream; + StreamDataInfoDetail* m_DataInfo; + StreamFileType m_FileType; + DecodeMode m_DecodeMode; + FileStreamHookParam m_FileStreamHookParam; + s32 m_ChannelCount; + u16 m_AssignNumber; + bool m_LoopFlag; + bool m_IsStreamOpenFailureHalt; + position_t m_LoopStart; + position_t m_LoopEnd; + char m_FilePath[SoundArchive::FilePathMax]; + void* m_pExternalData; + size_t m_ExternalDataSize; + void* m_pCacheBuffer; + size_t m_CacheSize; + u32 m_LoadingDataBlockIndex; + u32 m_LastBlockIndex; + u32 m_LoopStartBlockIndex; + position_t m_DataStartFilePos; + position_t m_LoopStartFilePos; + position_t m_LoopStartBlockSampleOffset; + bool m_LoopJumpFlag; + bool m_LoadFinishFlag; + RegionManager m_RegionManager; + StreamHeaderLoadTask m_StreamHeaderLoadTask; + StreamCloseTask m_StreamCloseTask; + StreamDataLoadTaskList m_StreamDataLoadTaskList; + InstancePool m_StreamDataLoadTaskPool; + u8 m_StreamDataLoadTaskArea[0x1f00]; + SampleFormat m_SampleFormat; + AdpcmInfo m_AdpcmInfo[16]; + u32 m_FileStreamBuffer[128]; + IStreamDataDecoder* m_pStreamDataDecoder; +#if NN_SDK_VER < NN_MAKE_VER(4, 0, 0) + static IStreamDataDecoderManager* g_pStreamDataDecoderManager; +#else + IStreamDataDecoderManager* m_pStreamDataDecoderManager; +#endif + util::IntrusiveListNode m_LinkForLoaderManager; + + static u8 g_LoadBuffer[LoadBufferSize]; +}; +#if NN_SDK_VER < NN_MAKE_VER(4, 0, 0) +static_assert(sizeof(StreamSoundLoader) == 0x35c0); +#else +static_assert(sizeof(StreamSoundLoader) == 0x3640); +#endif +} // namespace nn::atk::detail::driver +} // namespace nn::atk::detail \ No newline at end of file diff --git a/include/nn/atk/atk_StreamSoundPlayer.h b/include/nn/atk/atk_StreamSoundPlayer.h new file mode 100644 index 00000000..7f243bd2 --- /dev/null +++ b/include/nn/atk/atk_StreamSoundPlayer.h @@ -0,0 +1,344 @@ +#pragma once + +#include +#include +#include +#include +#include + +namespace nn::atk::detail { +struct StreamSoundDataInfo { + bool loopFlag; + s32 sampleRate; + s64 loopStart; + s64 loopEnd; + s64 compatibleLoopStart; + s64 compatibleLoopEnd; + s32 channelCount; +}; +static_assert(sizeof(StreamSoundDataInfo) == 0x30); + +struct StreamSoundRegionDataInfo { + u32 startSamplePosition; + u32 endSamplePosition; + s32 regionNo; + char regionName[64]; +}; +static_assert(sizeof(StreamSoundRegionDataInfo) == 0x4c); + +namespace driver { +class StreamSoundPlayer : BasicSoundPlayer, SoundThread::PlayerCallback { +public: + enum StartOffsetType { + StartOffsetType_Sample, + StartOffsetType_Millisec, + }; + + struct SetupArg; + struct ItemData { + f32 pitch; + f32 mainSend; + f32 fxSend[3]; + + void Set(const SetupArg& arg); + }; + static_assert(sizeof(ItemData) == 0x14); + + struct TrackData { + f32 volume; + f32 lpfFreq; + s32 biquadType; + f32 biquadValue; + f32 pan; + f32 span; + f32 mainSend; + f32 fxSend[3]; + + void Set(const StreamTrack* pStreamTrack); + }; + static_assert(sizeof(TrackData) == 0x28); + + struct WaveBufferInfo { + position_t sampleBegin; + size_t sampleLength; + s32 loopCount; + }; + static_assert(sizeof(WaveBufferInfo) == 0x18); + + struct PrepareBaseArg { + StartOffsetType startOffsetType; + position_t offset; + s32 delayTime; + s32 delayCount; + UpdateType updateType; +#if NN_SDK_VER < NN_MAKE_VER(4, 4 ,1) + u32 subMixIndex; +#endif + StreamRegionCallback regionCallback; + void* regionCallbackArg; + char filePath[639]; + void* pExternalData; + size_t externalDataSize; + FileStreamHookParam fileStreamHookParam; + }; + static_assert(sizeof(PrepareBaseArg) == 0x2d0); + + struct PrepareArg { + PrepareBaseArg baseArg; + void* cacheBuffer; + size_t cacheSize; + }; + static_assert(sizeof(PrepareArg) == 0x2e0); + + struct PreparePrefetchArg { + PrepareBaseArg baseArg; + void* strmPrefetchFile; + }; + static_assert(sizeof(PreparePrefetchArg) == 0x2d8); + + struct SetupArg { + StreamBufferPool* pBufferPool; + u32 allocChannelCount; + u16 allocTrackFlag; + u8 fileType; + bool loopFlag; + TrackDataInfos trackInfos; + position_t loopStart; + position_t loopEnd; + f32 pitch; + u8 mainSend; + u8 fxSend[3]; +#if NN_SDK_VER >= NN_MAKE_VER(4, 4 ,1) + DecodeMode decodeMode; +#endif + }; +#if NN_SDK_VER < NN_MAKE_VER(4, 4 ,1) + static_assert(sizeof(SetupArg) == 0x98); +#else + static_assert(sizeof(SetupArg) == 0xa0); +#endif + + struct PrefetchIndexInfo { + + void Initialize(const StreamDataInfoDetail& streamDataInfo); + + u32 lastBlockIndex; + position_t loopStartInBlock; + u32 loopStartBlockIndex; + s32 loopBlockCount; + }; + static_assert(sizeof(PrefetchIndexInfo) == 0x18); + + struct PrefetchLoadDataParam : LoadDataParam { + u32 prefetchBlockIndex; + size_t prefetchBlockBytes; + }; + static_assert(sizeof(PrefetchLoadDataParam) == 0xa8); + + StreamSoundPlayer(); + ~StreamSoundPlayer() override; + + +#if NN_SDK_VER < NN_MAKE_VER(4, 0, 0) + bool Initialize() override; +#else + bool Initialize(OutputReceiver* pOutputReceiver) override; +#endif + + bool TryAllocLoader(); + + void Finalize() override; + + void FinishPlayer(); + + void FreeStreamBuffers(); + void FreeVoices(); + void FreeLoader(); + + void Setup(const SetupArg& arg); + void SetupTrack(const SetupArg& arg); + + void Prepare(const PrepareArg& arg); + + void SetPrepareBaseArg(const PrepareBaseArg& arg); + + void RequestLoadHeader(const PrepareArg& arg); + + void PreparePrefetch(const PreparePrefetchArg& arg); + + bool ReadPrefetchFile(StreamSoundPrefetchFileReader& reader); + + bool ApplyStreamDataInfo(const StreamDataInfoDetail& streamDataInfo); + + bool SetupPlayer(); + + bool AllocVoices(); + + bool LoadPrefetchBlocks(StreamSoundPrefetchFileReader& reader); + + void Start() override; + void StartPlayer(); + + void Stop() override; + + void Pause(bool flag) override; + + void UpdatePauseStatus(); + + bool IsLoadingDelayState() const; + bool IsBufferEmpty() const; + + bool ReadStreamDataInfo(StreamDataInfo* strmDataInfo) const; + + position_t GetPlaySamplePosition(bool) const; + f32 GetFilledBufferPercentage() const; + s32 GetBufferBlockCount(WaveBuffer::Status waveBufferStatus) const; + s32 GetTotalBufferBlockCount() const; + + bool LoadHeader(bool result, AdpcmParam** adpcmParam, u16 assignNumber); + + bool CheckPrefetchRevision(const StreamDataInfoDetail& streamDataInfo) const; + + bool AllocStreamBuffers(); + + void UpdateLoadingBlockIndex(); + + bool LoadStreamData(bool result, const LoadDataParam& loadDataParam, u16 assignNumber); + bool LoadStreamData(bool result, const LoadDataParam& loadDataParam, u16 assignNumber, + bool usePrefetchFlag, u32 currentPrefetchBlockIndex, + size_t currentPrefetchBlockBytes); + + bool IsStoppedByLoadingDelay() const; + + static void VoiceCallbackFunc(MultiVoice* voice, + MultiVoice::VoiceCallbackStatus status, void* arg); + + void Update(); + void UpdateBuffer(); + void UpdateVoiceParams(StreamTrack* track); + + bool CheckDiskDriveError(); + + void SetOutputParam(const OutputParam*, const OutputParam&, const TrackData&); + +#if NN_SDK_VER < NN_MAKE_VER(4, 0, 0) + void ApplyTvOutputParamForMultiChannel(OutputParam* outputParam, + MultiVoice* MultiVoice, s32 channelIndex, + MixMode mixMode); +#else + void ApplyTvOutputParamForMultiChannel(OutputParam* outputParam, + OutputAdditionalParam* pOutputAdditionalParam, + MultiVoice* MultiVoice, s32 channelIndex, + MixMode mixMode); +#endif + + void MixSettingForOutputParam(OutputParam* outputParam, s32 channelIndex, MixMode mixMode); + + position_t GetOriginalPlaySamplePosition(position_t, const StreamDataInfoDetail& streamDataInfo) const; + + bool IsValidStartOffset(const StreamDataInfoDetail& streamDataInfo); + + void ApplyTrackDataInfo(const StreamDataInfoDetail& streamDataInfo); + + u64 GetStartOffsetSamples(const StreamDataInfoDetail& streamDataInfo); + + void PreparePrefetchOnLastBlock(PrefetchLoadDataParam*, const PrefetchIndexInfo&); + void PreparePrefetchOnLoopStartBlock(PrefetchLoadDataParam*, const PrefetchIndexInfo&, + StreamSoundPrefetchFileReader& reader); + void PreparePrefetchOnLoopBlock(PrefetchLoadDataParam*, const PrefetchIndexInfo&, u32); + bool PreparePrefetchOnNormalBlock(PrefetchLoadDataParam*, u32, + StreamSoundPrefetchFileReader* reader); + + bool SetAdpcmLoopInfo(StreamSoundPrefetchFileReader& reader, + const StreamDataInfoDetail& streamDataInfo, AdpcmParam* adpcmParam, + AdpcmContextNotAligned* adpcmContext); + bool SetAdpcmInfo(StreamSoundPrefetchFileReader& reader, + const StreamDataInfoDetail& streamDataInfo, AdpcmParam* adpcmParam, + AdpcmContextNotAligned* adpcmContext); + + void SetTrackVolume(u32 trackBitFlag, f32 volume); + void SetTrackInitialVolume(u32 trackBitFlag, u32 volume); + + void SetTrackOutputLine(u32 trackBitFlag, u32 outputLine); + void ResetTrackOutputLine(u32 trackBitFlag); + + void SetTrackTvVolume(u32 trackBitFlag, f32 volume); + void SetTrackChannelTvMixParameter(u32 trackBitFlag, u32 srcChNo, const MixParameter& param); + void SetTrackTvPan(u32 trackBitFlag, f32 pan); + void SetTrackTvSurroundPan(u32 trackBitFlag, f32 span); + void SetTrackTvMainSend(u32 trackBitFlag, f32 send); + void SetTrackTvFxSend(u32 trackBitFlag, AuxBus bus, f32 send); + + StreamTrack* GetPlayerTrack(s32 index); + StreamTrack* GetPlayerTrack(s32 index) const; + + void OnUpdateFrameSoundThread() override; + void OnUpdateFrameSoundThreadWithAudioFrameFrequency() override; + void OnShutdownSoundThread() override; + +private: + bool m_IsInitialized; + bool m_IsPrepared; + bool m_IsFinalizing; + bool m_IsPreparedPrefetch; + bool m_PauseStatus; + bool m_LoadWaitFlag; + bool m_LoadFinishFlag; + bool m_ReportLoadingDelayFlag; + bool m_IsStoppedByLoadingDelay; + bool m_IsRegisterPlayerCallback; + bool m_UseDelayCount; +#if NN_SDK_VER >= NN_MAKE_VER(5, 3, 0) + u8 m_Padding1[2]; +#endif + s32 m_LoopCounter; + s32 m_PlayingBlockLoopCounter; + s32 m_PrepareCounter; + StreamSoundLoaderManager* m_pLoaderManager; + StreamSoundLoader* m_pLoader; + detail::driver::StreamBufferPool* m_pBufferPool; + s32 m_BufferBlockCount; + u32 m_LoadingBufferBlockIndex; + u32 m_PlayingBufferBlockIndex; + u32 m_LastPlayFinishBufferBlockIndex; + StartOffsetType m_StartOffsetType; + position_t m_StartOffset; + s32 m_DelayCount; + u16 m_AssignNumber; + u8 m_FileType; +#if NN_SDK_VER >= NN_MAKE_VER(4, 4 ,1) + DecodeMode m_DecodeMode; +#endif + bool m_LoopFlag; + u8 m_Padding2[2]; + StreamDataInfoDetail m_StreamDataInfo; + position_t m_LoopStart; + position_t m_LoopEnd; + ItemData m_ItemData; + void* m_pStreamPrefetchFile; + AdpcmParam m_PrefetchAdpcmParam[16]; + StreamSoundPrefetchFileReader::PrefetchDataInfo m_PrefetchDataInfo; + position_t m_PrefetchOffset; + bool m_IsPrefetchRevisionCheckEnabled; + u32 m_PrefetchRevisionValue; + s32 m_ChannelCount; + s32 m_TrackCount; + StreamChannel m_Channels[16]; + StreamTrack m_Tracks[8]; + UpdateType m_UpdateType; +#if NN_SDK_VER < NN_MAKE_VER(4, 4 ,1) + u32 m_SubMixIndex; +#endif + WaveBufferInfo m_WaveBufferInfo[32]; + PrepareArg m_PrepareArg; + bool m_IsSucceedPrepare; + SetupArg m_SetupArg; + position_t m_PlaySamplePosition; + position_t m_OriginalPlaySamplePosition; + + static u16 g_TaskRequestIndexCount; + static u16 g_AssignNumberCount; +}; +static_assert(sizeof(StreamSoundPlayer) == 0x11740); +} // namespace nn::atk::detail::driver +} // namespace nn::atk::detail \ No newline at end of file diff --git a/include/nn/atk/atk_StreamSoundPrefetchFile.h b/include/nn/atk/atk_StreamSoundPrefetchFile.h new file mode 100644 index 00000000..d880b0c4 --- /dev/null +++ b/include/nn/atk/atk_StreamSoundPrefetchFile.h @@ -0,0 +1,51 @@ +#pragma once + +#include +#include + +namespace nn::atk::detail { +class StreamSoundPrefetchFile { +public: + struct PrefetchDataBlock; + struct FileHeader : Util::SoundFileHeader { + const StreamSoundFile::InfoBlock* GetInfoBlock() const; + const StreamSoundFile::RegionBlock* GetRegionBlock() const; + const PrefetchDataBlock* GetPrefetchDataBlock() const; + + u32 GetPrefetchDataBlockSize() const; + + bool HasRegionBlock() const; + u32 GetRegionBlockSize() const; + u32 GetRegionBlockOffset() const; + }; + + struct PrefetchSample; + struct PrefetchData { + u32 startFrame; + u32 prefetchSize; + u32 reserved[1]; + Util::Reference toPrefetchSample; + + const PrefetchSample* GetPrefetchSample() const; + }; + static_assert(sizeof(PrefetchData) == 0x14); + + struct PrefetchDataBlockBody { + Util::Table prefetchDataTable; + + u32 GetPrefetchDataCount() const { return prefetchDataTable.count; } + const PrefetchData* GetPrefetchData(u32 index) const { return &prefetchDataTable.item[index]; } + }; + + struct PrefetchDataBlock { + BinaryBlockHeader header; + PrefetchDataBlockBody body; + }; + + struct PrefetchSample { + u8 data[1]; + + const void* GetSampleAddress() const; + }; +}; +} // namespace nn::atk::detail \ No newline at end of file diff --git a/include/nn/atk/atk_StreamSoundPrefetchFileReader.h b/include/nn/atk/atk_StreamSoundPrefetchFileReader.h new file mode 100644 index 00000000..86c842bf --- /dev/null +++ b/include/nn/atk/atk_StreamSoundPrefetchFileReader.h @@ -0,0 +1,70 @@ +#pragma once + +#include +#include + +namespace nn::atk::detail { +class StreamSoundPrefetchFileReader : public IRegionInfoReadable { +public: + struct PrefetchDataInfo { + u32 startFrame; + u32 prefetchSize; + const void* dataAddress; + }; + + StreamSoundPrefetchFileReader(); + ~StreamSoundPrefetchFileReader() override; + + void Initialize(const void* streamSoundPrefetchFile); + void Finalize() { + m_pHeader = nullptr; + m_pInfoBlockBody = nullptr; + m_pPrefetchDataBlockBody = nullptr; + m_RegionDataOffset = 0; + m_RegionInfoBytes = 0; + } + + bool IsAvailable() const { + return m_pHeader != nullptr; + } + + bool IsIncludeRegionInfo() const; + bool IsCrc32CheckAvailable() const; + bool IsRegionIndexCheckAvailable() const; + + bool IsValidFileHeader(const void* streamSoundPrefetchFile) const; + + bool ReadStreamSoundInfo(StreamSoundFile::StreamSoundInfo* strmInfo) const; + bool ReadDspAdpcmChannelInfo(DspAdpcmParam* pParam, DspAdpcmLoopParam* pLoopParam, + int channelIndex) const; + bool ReadPrefetchDataInfo(PrefetchDataInfo* pDataInfo, int prefetchIndex) const; + + bool ReadRegionInfo(StreamSoundFile::RegionInfo* pInfo, u32 regionIndex) const override; + + u32 GetChannelCount() const; + u32 GetPrefetchDataCount() const; + + u32 GetRegionDataOffset() const { + u32 result {0}; + + if (IsAvailable() && m_pHeader->HasRegionBlock()) { + result = m_pHeader->GetRegionBlockOffset() + sizeof(BinaryBlockHeader) + + m_pInfoBlockBody->GetStreamSoundInfo()->regionDataOffset.offset; + } + + return result; + } + + u16 GetRegionInfoBytes() const { + return m_pInfoBlockBody->GetStreamSoundInfo()->regionInfoBytes; + } + +private: + const StreamSoundPrefetchFile::FileHeader* m_pHeader {}; + const StreamSoundFile::InfoBlockBody* m_pInfoBlockBody {}; + const StreamSoundPrefetchFile::PrefetchDataBlockBody* m_pPrefetchDataBlockBody {}; + u32 m_RegionDataOffset {0}; + u16 m_RegionInfoBytes {0}; +}; +static_assert(sizeof(StreamSoundPrefetchFileReader) == 0x28); +} // namespace nn::atk::detail \ No newline at end of file diff --git a/include/nn/atk/atk_StreamSoundRuntime.h b/include/nn/atk/atk_StreamSoundRuntime.h new file mode 100644 index 00000000..d37b55de --- /dev/null +++ b/include/nn/atk/atk_StreamSoundRuntime.h @@ -0,0 +1,75 @@ +#pragma once + +#include +#include +#include + +namespace nn::atk::detail { +class StreamSoundRuntime { +public: + constexpr static u32 DefaultStreamBlockCount = 5; + + StreamSoundRuntime(); + ~StreamSoundRuntime(); + + bool Initialize(s32 soundCount, void** pOutAllocatedAddr, const void* endAddr, + void* streamInstanceBuffer, size_t streamInstanceBufferSize); + + static size_t GetRequiredStreamInstanceSize(s32 soundCount); + + bool SetupStreamBuffer(const SoundArchive* pSoundArchive, void* strmBuffer, + size_t strmBufferSize); + bool SetupStreamBuffer(const SoundArchive* pSoundArchive, void* strmBuffer, + size_t strmBufferSize, driver::StreamBufferPool* strmBufferPool); + + size_t GetRequiredStreamBufferSize(const SoundArchive* soundArchive) const; + static u32 GetRequiredStreamBufferTimes(const SoundArchive* soundArchive); + + bool SetupStreamCacheBuffer(const SoundArchive* pSoundArchive, void* streamCacheBuffer, + size_t streamCacheSize); + + void Finalize(); + + static size_t GetRequiredMemorySize(const SoundArchive::SoundArchivePlayerInfo& soundArchivePlayerInfo, + s32 alignment); + + static size_t GetRequiredStreamCacheSize(const SoundArchive* pSoundArchive, + size_t); + + s32 GetActiveCount() const; + s32 GetActiveChannelCount() const; + s32 GetActiveTrackCount() const; + s32 GetFreeCount() const; + + void SetupUserParam(void** pOutAllocatedAddr, size_t adjustSize); + + void Update(); +#if NN_SDK_VER < NN_MAKE_VER(4, 0, 0) + StreamSound* AllocSound(SoundArchive::ItemId soundId, s32 priority, s32 ambientPriority, + BasicSound::AmbientInfo* ambientArgInfo); +#else + StreamSound* AllocSound(SoundArchive::ItemId soundId, s32 priority, s32 ambientPriority, + BasicSound::AmbientInfo* ambientArgInfo, OutputReceiver* pOutputReceiver); +#endif + SoundStartable::StartResult PrepareImpl(const SoundArchive* pSoundArchive, + const SoundDataManager* pSoundDataManager, + SoundArchive::ItemId soundId, + StreamSound* sound, + const SoundArchive::SoundInfo* commonInfo, + const StartInfoReader& startInfoReader); + + void DumpMemory(const SoundArchive* pSoundArchive) const; + +private: + StreamSoundInstanceManager m_StreamSoundInstanceManager; + driver::StreamSoundLoaderManager m_StreamSoundLoaderManager; + driver::StreamBufferPool m_StreamBufferPool; + SoundArchiveFilesHook* m_pSoundArchiveFilesHook; + s32 m_StreamBlockCount; +}; +#if NN_SDK_VER < NN_MAKE_VER(4, 0, 0) +static_assert(sizeof(StreamSoundRuntime) == 0xb0); +#else +static_assert(sizeof(StreamSoundRuntime) == 0xb8); +#endif +} // namespace nn::atk::detail diff --git a/include/nn/atk/atk_StreamTrack.h b/include/nn/atk/atk_StreamTrack.h new file mode 100644 index 00000000..6e132d69 --- /dev/null +++ b/include/nn/atk/atk_StreamTrack.h @@ -0,0 +1,44 @@ +#pragma once + +#include + +#include +#include + +namespace nn::atk::detail::driver { +struct StreamChannel { + void AppendWaveBuffer(WaveBuffer* pBuffer, bool lastFlag); + os::Tick GetProcessTick(const SoundProfile& profile); + + void* m_pBufferAddress; + MultiVoice* m_pVoice; + WaveBuffer m_WaveBuffer[32]; + AdpcmContext m_AdpcmContext[32]; + UpdateType m_UpdateType; +}; +static_assert(sizeof(StreamChannel) == 0x1080); + +class StreamTrack { +public: + StreamTrack() = default; + ~StreamTrack(); + +private: + bool m_ActiveFlag; + StreamChannel* m_pChannels[2]; + u8 channelCount; + u8 volume; + u8 pan; + u8 span; + u8 mainSend; + u8 fxSend[3]; + u8 lpfFreq; + s8 biquadType; + u8 biquadValue; + u8 flags; + f32 m_Volume; + s32 m_OutputLine; + OutputParam m_TvParam; +}; +static_assert(sizeof(StreamTrack) == 0x80); +} // namespace nn::atk::detail::driver \ No newline at end of file diff --git a/include/nn/atk/atk_SubMix.h b/include/nn/atk/atk_SubMix.h new file mode 100644 index 00000000..d5249e3c --- /dev/null +++ b/include/nn/atk/atk_SubMix.h @@ -0,0 +1,188 @@ +#pragma once + +#include +#include + +#include +#include + +namespace nn::atk { +namespace detail::driver { +class HardwareManager; +}; + +class SubMix : OutputMixer { +public: + class SubMixParam { + public: + SubMixParam(); + + void SetSrcBusCount(s32 srcBusCount); + s32 GetSrcBusCount() const; + + void SetSrcChannelCount(s32 srcChannelCount); + s32 GetSrcChannelCount() const; + + void SetDstBusCount(s32 dstBusCount); + s32 GetDstBusCount() const; + + void SetDstChannelCount(s32 dstChannelCount); + s32 GetDstChannelCount() const; + + void SetOutputReceiver(const OutputReceiver* pOutputReceiver); + OutputReceiver* GetOutputReceiver() const; + + void SetEffectEnabled(bool isEffectEnabled); + bool IsEffectEnabled() const; + + void SetSoundSendClampEnabled(s32 index, bool IsSoundSendClampEnabled); + bool GetSoundSendClampEnabled(s32 index) const; + + private: + s32 m_SrcBusCount; + s32 m_SrcChannelCount; + s32 m_DstBusCount; + s32 m_DstChannelCount; + OutputReceiver* m_pOutputReceiver; + bool m_IsEffectEnabled; + bool m_IsSoundSendClampEnabledArray[24]; + }; + static_assert(sizeof(SubMixParam) == 0x38); + + class VolumeData { + public: + VolumeData(); + + bool Update(); + + private: + detail::MoveValue m_Volume; + bool m_IsMute; + bool m_IsPrevMute; + bool m_IsDirtyFlag; + }; + static_assert(sizeof(VolumeData) == 0x14); + + SubMix(); + + static size_t GetRequiredMemorySize(s32 srcBusCount, s32 srcChannelCount, + s32 dstBusCount, s32 dstChannelCount); + static size_t GetRequiredMemorySize(s32 srcBusCount, s32 srcChannelCount, + s32 dstBusCount, s32 dstChannelCount, + bool isEffectEnabled); + static size_t GetRequiredMemorySize(s32 srcBusCount, s32 srcChannelCount, + s32 dstBusCount, s32 dstChannelCount, + bool isEffectEnabled, bool isInternalCall); + + static size_t GetRequiredMemorySizeImpl(const SubMixParam& param); + + static size_t GetRequiredMemorySize(s32 srcBusCount, s32 dstBusCount, + const OutputReceiver* pReceiver, + bool isEffectEnabled); + + static size_t GetRequiredMemorySize(const SubMixParam& param); + + bool Initialize(s32 srcBusCount, s32 srcChannelCount, s32 dstBusCount, + s32 dstChannelCount, void* buffer, size_t bufferSize); + bool Initialize(s32 srcBusCount, s32 srcChannelCount, s32 dstBusCount, + s32 dstChannelCount, bool isEffectEnabled, void* buffer, + size_t bufferSize); + bool Initialize(s32 srcBusCount, s32 srcChannelCount, s32 dstBusCount, + s32 dstChannelCount, bool isEffectEnabled, bool isInternalCall, + void* buffer, size_t bufferSize); + + bool InitializeImpl(const SubMixParam& param, void* buffer, size_t bufferSize); + bool InitializeImpl(const SubMixParam& param, void* buffer, size_t bufferSize, bool isInternalCall); + + bool Initialize(s32 srcBusCount, s32 dstBusCount, const OutputReceiver* pReceiver, + void* buffer, size_t bufferSize); + bool Initialize(s32 srcBusCount, s32 dstBusCount, const OutputReceiver* pReceiver, + bool isEffectEnabled, void* buffer, size_t bufferSize); + bool Initialize(const SubMixParam& param, void* buffer, size_t bufferSize); + + void Finalize(); + + bool IsRemovable() const; + + void Update(); + + void UpdateBusMixVolume(s32 bus); + void UpdateChannelMixVolume(s32 bus); + void UpdateMixVolume(s32 srcBus, s32 srcChannel,s32 dstBus,s32 dstChannel); + + void SetDestination(OutputReceiver* pReceiver); + void ApplyDestination(); + + f32 GetSend(s32 srcBus, s32 dstBus) const; + f32 GetSendImpl(s32 srcBus, s32 srcChannel,s32 dstBus,s32 dstChannel) const; + + void SetSend(s32 srcBus, s32 dstBus, f32 send); + void SetSendImpl(s32 srcBus, s32 srcChannel,s32 dstBus,s32 dstChannel, f32 send); + + void SetBusVolume(s32 bus, f32 volume, s32 fadeFrame); + f32 GetBusVolume(s32 bus) const; + + void SetBusMute(s32 bus, bool isMute); + bool IsBusMuted(s32 bus) const; + + bool IsSoundSendClampEnabled(s32 bus) const override; + + void SetChannelVolume(s32 channel, f32 volume, s32 fadeFrame); + f32 GetChannelVolume(s32 channel) const; + + void SetChannelMute(s32 channel, bool isMute); + bool IsChannelMuted(s32 channel) const; + + void SetSubMixVolume(f32 volume, s32 fadeFrame); + f32 GetSubMixVolume() const; + + void SetSubMixMute(bool isMute); + bool IsSubMixMuted() const; + + void SetMuteUnusedEffectChannel(bool isUnusedEffectChannelMuted); + void IsUnusedEffectChannelMuted() const; + + void MuteUnusedEffectChannel(ChannelIndex* effectChannelIndex, s32 effectChannelCount, s32 bus); + + bool AppendEffect(EffectBase* pEffect, s32 bus, void* buffer, size_t bufferSize); + bool AppendEffect(EffectAux* pEffect, s32 bus, void* buffer, size_t bufferSize); + + bool RemoveEffect(EffectBase* pEffect, s32 bus); + bool RemoveEffect(EffectAux* pEffect, s32 bus); + + bool ClearEffect(s32 bus); + + bool IsEffectEnabled() const; + + void AppendEffectImpl(EffectBase* pEffect, s32 bus, void* buffer, size_t bufferSize) override; + void AppendEffectImpl(EffectAux* pEffect, s32 bus, void* buffer, size_t bufferSize) override; + + ReceiverType GetReceiverType() const override; + s32 GetChannelCount() const override; + s32 GetBusCount() const override; + + void AddReferenceCount(s32 value) override; + +private: + friend detail::driver::HardwareManager; + + util::IntrusiveListNode m_Link; + audio::SubMixType m_SubMix; + std::atomic_uint m_ReferenceCount; + OutputReceiver* m_pReceiver; + VolumeData* m_pBusVolume; + VolumeData* m_pChannelVolume; + VolumeData m_SubMixVolume; + bool* m_pIsSoundSendClampEnabledArray; + f32** m_ppSendVolume; + s32 m_ChannelCount; + s32 m_BusCount; + s32 m_ReceiverChannelCountMax; + detail::fnd::CriticalSection m_VolumeLock; + detail::fnd::CriticalSection m_DestinationLock; + bool m_IsInitialized; + bool m_IsUnusedEffectChannelMuted; + bool m_IsAppliedOutputReceiver; +}; +static_assert(sizeof(SubMix) == 0xf8); +} // namespace nn::atk \ No newline at end of file diff --git a/include/nn/atk/atk_Task.h b/include/nn/atk/atk_Task.h new file mode 100644 index 00000000..aa8148d2 --- /dev/null +++ b/include/nn/atk/atk_Task.h @@ -0,0 +1,46 @@ +#pragma once + +#include + +#include + +namespace nn::atk::detail { +class TaskManager; + +class Task { +public: + enum Status { + Status_Free, + Status_Append, + Status_Execute, + Status_Done, + Status_Cancel, + }; + + Task(); + + virtual ~Task(); + + void SetId(u32 id) { m_Id = id; } + Status GetStatus() const { return m_Status; }; + + void Wait() { m_Event.Wait(); } + bool TryWait() { return m_Event.TryWait(); } + +protected: + virtual void Execute(TaskProfileLogger& logger) = 0; + + void InitializeStatus() { m_Status = Status_Free; } + +private: + NN_NO_COPY(Task); + + friend TaskManager; + + util::IntrusiveListNode m_TaskLink; + os::Event m_Event; + Status m_Status; + u32 m_Id; +}; +static_assert(sizeof(Task) == 0x48); +} // namespace nn::atk::detail \ No newline at end of file diff --git a/include/nn/atk/atk_TaskManager.h b/include/nn/atk/atk_TaskManager.h new file mode 100644 index 00000000..24d3fe03 --- /dev/null +++ b/include/nn/atk/atk_TaskManager.h @@ -0,0 +1,67 @@ +#pragma once + +#include + +#include + +namespace nn::atk::detail { +class TaskManager { +public: + using TaskList = util::IntrusiveList>; + + enum Message { + Message_Append, + }; + + enum TaskPriority { + TaskPriority_Low, + TaskPriority_Middle, + TaskPriority_High, + }; + + constexpr static u32 PriorityCount = 3; + constexpr static u32 ThreadMessageBufferSize = 32; + + TaskManager(); + ~TaskManager(); + + void Initialize(bool isEnableProfiling); + void Finalize(); + + void AppendTask(Task* task, TaskPriority priority); + + Task* GetNextTask(TaskPriority priority, bool); + + Task* PopTask(); + Task* PeekTask(); + + void ExecuteTask(); + void CancelTask(Task* task); + + bool TryRemoveTask(Task* task); + + void CancelTaskById(u32 id); + void RemoveTaskById(u32 id); + + void CancelAllTask(); + + void WaitTask(); + void CancelWaitTask(); + +private: + TaskList m_TaskList[PriorityCount]; +#if NN_SDK_VER < NN_MAKE_VER(4, 0, 0) + Task* m_ActiveTask; +#endif + bool m_IsWaitTaskCancel; + fnd::CriticalSection m_CriticalSection; + os::MessageQueue m_BlockingQueue; + std::uintptr_t m_MsgBuffer[ThreadMessageBufferSize]; + TaskProfileLogger m_TaskProfileLogger; +}; +#if NN_SDK_VER < NN_MAKE_VER(4, 0, 0) +static_assert(sizeof(TaskManager) == 0x1e0); +#else +static_assert(sizeof(TaskManager) == 0x1d8); +#endif +} // namespace nn::atk::detail \ No newline at end of file diff --git a/include/nn/atk/atk_TaskProfileReader.h b/include/nn/atk/atk_TaskProfileReader.h new file mode 100644 index 00000000..0e59224d --- /dev/null +++ b/include/nn/atk/atk_TaskProfileReader.h @@ -0,0 +1,146 @@ +#pragma once + +#include +#include + +#include +#include +#include +#include + +namespace nn::atk { + +namespace detail::driver { +class StreamSoundPlayer; +} // namespace nn::atk::detail::driver + +struct TaskProfile { + enum TaskProfileType { + TaskProfileType_LoadStreamBlock, + TaskProfileType_LoadOpusStreamBlock, + }; + + class LoadStreamBlock { + public: + TimeSpan GetTotalTime() const; + u64 GetBeginTick() const; + u64 GetEndTick() const; + + f32 GetRemainingCachePercentage() const; + size_t GetCachedLength() const; + +#if NN_SDK_VER >= NN_MAKE_VER(4, 0, 0) + detail::driver::StreamSoundPlayer* GetStreamSoundPlayer() const; +#endif + +#if NN_SDK_VER < NN_MAKE_VER(4, 0, 0) + void SetTick(const os::Tick& beginTick, const os::Tick& endTick); +#else + void SetData(const os::Tick& beginTick, const os::Tick& endTick, + const detail::IStreamDataDecoder::CacheProfile& cacheProfile); +#endif + + private: + u64 m_BeginTick; + u64 m_EndTick; +#if NN_SDK_VER >= NN_MAKE_VER(4, 0, 0) + position_t m_CacheStartPosition; + size_t m_CachedLength; + position_t m_CacheCurrentPosition; + detail::driver::StreamSoundPlayer* m_pPlayer; +#endif + }; +#if NN_SDK_VER < NN_MAKE_VER(4, 0, 0) + static_assert(sizeof(LoadStreamBlock) == 0x10); +#else + static_assert(sizeof(LoadStreamBlock) == 0x30); +#endif + + class LoadOpusStreamBlock { + public: + TimeSpan GetTotalTime() const; + u64 GetBeginTick() const; + u64 GetEndTick() const; + + f32 GetRemainingCachePercentage() const; + size_t GetCachedLength() const; + + TimeSpan GetDecodeTime() const; + s32 GetDecodedSampleCount() const; + + TimeSpan GetFsAccessTime(); + size_t GetFsReadSize(); + +#if NN_SDK_VER >= NN_MAKE_VER(4, 0, 0) + detail::driver::StreamSoundPlayer* GetStreamSoundPlayer() const; +#endif + +#if NN_SDK_VER < NN_MAKE_VER(4, 0, 0) + void SetData(const os::Tick& beginTick, const os::Tick& endTick, + detail::IStreamDataDecoder::DecodeProfile* decodeProfile); +#else + void SetData(const os::Tick& beginTick, const os::Tick& endTick, + const detail::IStreamDataDecoder::DecodeProfile& decodeProfile, + const detail::IStreamDataDecoder::CacheProfile& cacheProfile); +#endif + + private: + u64 m_BeginTick; + u64 m_EndTick; +#if NN_SDK_VER >= NN_MAKE_VER(4, 0, 0) + position_t m_CacheStartPosition; + size_t m_CachedLength; + position_t m_CacheCurrentPosition; +#endif + u64 m_DecodeTick; + u64 m_FsAccessTick; + size_t m_FsReadSize; + s32 m_DecodedSampleCount; +#if NN_SDK_VER >= NN_MAKE_VER(4, 0, 0) + detail::driver::StreamSoundPlayer* m_pPlayer; +#endif + }; +#if NN_SDK_VER < NN_MAKE_VER(4, 0, 0) + static_assert(sizeof(LoadOpusStreamBlock) == 0x30); +#else + static_assert(sizeof(LoadOpusStreamBlock) == 0x50); +#endif + + TaskProfileType type; + union { + LoadStreamBlock loadStreamBlock; + LoadOpusStreamBlock loadOpusStreamBlock; + }; +}; +#if NN_SDK_VER < NN_MAKE_VER(4, 0, 0) +static_assert(sizeof(TaskProfile) == 0x38); +#else +static_assert(sizeof(TaskProfile) == 0x58); +#endif + +using TaskProfileReader = AtkProfileReader; + +class TaskProfileLogger { +public: + using TaskProfileReaderList = util::IntrusiveList>; + + TaskProfileLogger(); + + void Record(const TaskProfile& profile); + + void RegisterReader(TaskProfileReader& profileReader); + void UnregisterReader(const TaskProfileReader& profileReader); + + void SetProfilingEnabled(bool isEnabledProfiling); + + void Finalize(); + +private: + TaskProfileReaderList m_List; + detail::fnd::CriticalSection m_Lock; + bool m_IsProfilingEnabled; +}; +static_assert(sizeof(TaskProfileLogger) == 0x38); +} // namespace nn::atk \ No newline at end of file diff --git a/include/nn/atk/atk_TaskThread.h b/include/nn/atk/atk_TaskThread.h new file mode 100644 index 00000000..eae3d2b2 --- /dev/null +++ b/include/nn/atk/atk_TaskThread.h @@ -0,0 +1,28 @@ +#pragma once + +#include +#include +#include + +namespace nn::atk::detail { +class TaskThread : fnd::Thread::Handler { +public: + TaskThread(); + ~TaskThread() override; + + void Destroy(); + + bool Create(s32 priority, void* stackBase, size_t stackSize, + s32 idealCoreNumber, u32 affinityMask, FsPriority fsPriority); + + u32 Run(void* param) override; + +private: + fnd::Thread m_Thread; + fnd::CriticalSection m_CriticalSection; + bool m_IsFinished; + bool m_IsCreated; + FsPriority m_FsPriority; +}; +static_assert(sizeof(TaskThread) == 0x220); +} // namespace nn::atk::detail \ No newline at end of file diff --git a/include/nn/atk/atk_ThreadInfoReader.h b/include/nn/atk/atk_ThreadInfoReader.h new file mode 100644 index 00000000..f9713e88 --- /dev/null +++ b/include/nn/atk/atk_ThreadInfoReader.h @@ -0,0 +1,55 @@ +#pragma once + +#include + +#include + +namespace nn::atk { +namespace detail { +class ThreadInfoRecorder { +public: + static const size_t BufferMinimumSize {}; + + ThreadInfoRecorder(); + + void Initialize(void* buffer, size_t bufferSize) { + m_Buffer = buffer; + m_BufferSize = bufferSize; + } + + int GetRecordedFrameCount() const { return m_RecordFrameCount; } + + const void* GetRingBufferHeadAddress() const { + // TODO + return nullptr; + } + + void MoveToNextFrame() { + // TODO + } + + void RecordFooter() { + // TODO + } + + util::IntrusiveListNode m_List; + +private: + void* AllocateInfoBuffer([[maybe_unused]] u32 count, [[maybe_unused]] size_t size) { return nullptr; } + void* AllocateBuffer([[maybe_unused]] u32 count, [[maybe_unused]] size_t size1, [[maybe_unused]]size_t size2) { return nullptr; } + + void* m_Buffer; + size_t m_BufferSize; + size_t m_WritePosition; + std::atomic_ulong m_ReadPosition; + std::atomic_int m_RecordFrameCount; + std::atomic_bool m_IsAllocationFailed; +}; +static_assert(sizeof(ThreadInfoRecorder) == 0x38); +} // namespace detail + +using SoundThreadInfoRecorder = detail::ThreadInfoRecorder; +using SoundThreadInfoRecorderList = util::IntrusiveList>; +} // namespace nn::atk \ No newline at end of file diff --git a/include/nn/atk/atk_Util.h b/include/nn/atk/atk_Util.h new file mode 100644 index 00000000..4206d681 --- /dev/null +++ b/include/nn/atk/atk_Util.h @@ -0,0 +1,323 @@ +#pragma once + +#include +#include + +#include +#include +#include + +namespace nn::atk { + +class SoundArchive; +class SoundArchivePlayer; +class OutputReceiver; + +namespace detail { +class PlayerHeapDataManager; +class SoundArchiveLoader; +struct LoadItemInfo; +class Util { +public: + static const int VolumeDbMin{-904}; + static const int VolumeDbMax{60}; + + static const int PitchDivisionBit{8}; + static const int PitchDivisionRange{256}; + + enum PanCurve { + PanCurve_Sqrt, + PanCurve_Sincos, + PanCurve_Linear, + }; + + struct PanInfo { + PanCurve curve; + bool centerZeroFlag; + bool zeroClampFlag; + bool isEnableFrontBypass; + }; + static_assert(sizeof(PanInfo) == 0x8); + + static u16 CalcLpfFreq(float scale); + static BiquadFilterCoefficients CalcLowPassFilterCoefficients(int frequency, int sampleRate, + bool isTableUsed); + static int FindLpfFreqTableIndex(int frequency); + static float CalcPanRatio(float pan, const PanInfo& info, OutputMode mode); + static float CalcSurroundPanRatio(float surroundPan, const PanInfo& info); + + static float CalcPitchRatio(int pitch_); + static float CalcVolumeRatio(float dB); + static u16 CalcRandom(); + + static size_t GetSampleByByte(size_t byte, SampleFormat format); + static size_t GetByteBySample(size_t samples, SampleFormat format); + + static bool IsValidMemoryForDsp(const void* ptr, size_t size); + + static const int CalcLpfFreqTableSize{24}; + static const float CalcLpfFreqIntercept; + static const float CalcLpfFreqThreshold; + static const u16 CalcLpfFreqTable[CalcLpfFreqTableSize]; + static const BiquadFilterCoefficients LowPassFilterCoefficientsTable32000[CalcLpfFreqTableSize]; + static const BiquadFilterCoefficients LowPassFilterCoefficientsTable48000[CalcLpfFreqTableSize]; + + template + struct Table { + CountType count; + ItemType item[1]; + }; + + struct Reference { + static const int InvalidOffset = -1; + + bool IsValidTypeId(u16 validId) const { return typeId == validId; } + bool IsValidOffset() const { return offset != InvalidOffset; } + + // Id from ElementType enum + u16 typeId; + u8 padding[2]; + s32 offset; + }; + static_assert(sizeof(Reference) == 0x8); + + struct ReferenceWithSize : public Reference { + u32 size; + }; + static_assert(sizeof(ReferenceWithSize) == 0xc); + + struct ReferenceTable : Table { + const void* GetReferedItem(u32 index) const { + if (count <= index) + return nullptr; + + return util::ConstBytePtr(this, item[index].offset).Get(); + } + + const void* GetReferedItem(u32 index, u16 typeId) const { + if (count <= index || item[index].typeId != typeId) + return nullptr; + + return util::ConstBytePtr(this, item[index].offset).Get(); + } + + const void* FindReferedItemBy(u16 typeId) const; + }; + + struct ReferenceWithSizeTable : Table { + const void* GetReferedItem(u32 index) const { + if (count <= index) + return nullptr; + + return util::ConstBytePtr(this, item[index].offset).Get(); + } + + const void* GetReferedItemBy(u16 typeId) const; + u32 GetReferedItemSize(u32 index) const; + }; + + struct BlockReferenceTable { + ReferenceWithSize item[1]; + + const void* GetReferedItemByIndex(const void* origin, int index, u16 count) const; + + const ReferenceWithSize* GetReference(u16 typeId, u16 count) const { + for (int i{0}; i < count; ++i) { + if (item[i].IsValidTypeId(typeId)) + return &item[i]; + } + + return nullptr; + } + + const void* GetReferedItem(const void* origin, u16 typeId, u16 count) const { + auto* ref{GetReference(typeId, count)}; + if (ref != nullptr && ref->offset != 0) + return util::ConstBytePtr(origin, ref->offset).Get(); + + return nullptr; + } + + u32 GetReferedItemSize(u16 typeId, u16 count) const { + auto* ref{GetReference(typeId, count)}; + if (ref != nullptr) + return ref->size; + + return 0; + } + + u32 GetReferedItemOffset(u16 typeId, u16 count) const { + auto* ref{GetReference(typeId, count)}; + if (ref != nullptr) + return ref->offset; + + return 0; + } + }; + + struct SoundFileHeader { + BinaryFileHeader header; + BlockReferenceTable blockReferenceTable; + + int GetBlockCount() const { return header.dataBlocks; } + + protected: + const void* GetBlock(u16 typeId) const { + return blockReferenceTable.GetReferedItem(this, typeId, header.dataBlocks); + } + + u32 GetBlockSize(u16 typeId) const { + return blockReferenceTable.GetReferedItemSize(typeId, header.dataBlocks); + } + + u32 GetBlockOffset(u16 typeId) const { + return blockReferenceTable.GetReferedItemOffset(typeId, header.dataBlocks); + } + }; + + struct BitFlag { + u32 bitFlag; + + bool GetValue(u32* value, u32 bitNumber) const { + u32 count{GetTrueCount(bitNumber)}; + if (count == 0) + return false; + + *value = (&bitFlag)[count]; + return true; + } + + bool GetValueF32(float* value, u32 bitNumber) const { + u32 count{GetTrueCount(bitNumber)}; + if (count == 0) + return false; + + *value = reinterpret_cast(&bitFlag)[count]; + return true; + } + + private: + static const int BitNumberMax = 31; + u32 GetTrueCount(u32 bitNumber) const { + bool ret{false}; + int count{0}; + + for (u32 i{0}; i <= bitNumber; ++i) { + if ((bitFlag & (1 << i)) != 0) { + ++count; + if (i == bitNumber) + ret = true; + } + } + + if (ret) + return count; + + return 0; + } + }; + static_assert(sizeof(BitFlag) == 0x4); + + static u8 DivideBy8bit(u32 value, int index) { return static_cast(value >> (8 * index)); } + + static u16 DivideBy16bit(u32 value, int index) { + return static_cast(value >> (16 * index)); + } + + static const void* GetWaveFile(u32 waveArchiveId, u32 waveIndex, const SoundArchive& arc, + const SoundArchivePlayer& player); + static const void* GetWaveFile(u32 waveArchiveId, u32 waveIndex, const SoundArchive& arc, + const PlayerHeapDataManager* mgr); + + enum WaveArchiveLoadStatus { + WaveArchiveLoadStatus_Error = -2, + WaveArchiveLoadStatus_NotYet, + WaveArchiveLoadStatus_Ok, + WaveArchiveLoadStatus_Noneed, + WaveArchiveLoadStatus_Partly + }; + + static WaveArchiveLoadStatus GetWaveArchiveOfBank(LoadItemInfo& warcLoadInfo, + bool& isLoadIndividual, const void* bankFile, + const SoundArchive& arc, + const SoundArchiveLoader& mgr); + + static const void* GetWaveFileOfWaveSound(const void* wsdFile, u32 index, + const SoundArchive& arc, + const SoundArchiveLoader& mgr); + + static ItemType GetItemType(u32 id) { return static_cast(id >> 24); } + + static u32 GetItemIndex(u32 id) { return id & 0x00FFFFFF; } + + static u32 GetMaskedItemId(u32 id, ItemType type) { return id | (type << 24); } + + struct WaveId { + u32 waveArchiveId; + u32 waveIndex; + }; + static_assert(sizeof(WaveId) == 0x8); + + struct WaveIdTable { + Table table; + + const WaveId* GetWaveId(u32 index) const { return &table.item[index]; } + + u32 GetCount() const { return table.count; } + }; + + template + class Singleton { + public: + static CHILD& GetInstance(); + }; + + static int GetSubMixBusFromMainBus(); + static int GetSubMixBus(AuxBus bus); + static int GetSubMixBus(int bus); + static int GetOutputReceiverMixBufferIndex(const OutputReceiver* pOutputReceiver, int bus, + int channel); + static int GetAdditionalSendIndex(int bus); + + class WarningLogger : public Singleton { + public: + WarningLogger(); + + void Log(int logId, int arg0, int arg1); + void Print(); + void SwapBuffer(); + + private: + struct LogBuffer { + static const int LogCount = 64; + + struct Element { + int logId; + int arg0; + int arg1; + + void Print(); + }; + static_assert(sizeof(Element) == 0xc); + + Element element[LogCount]; + int counter; + + LogBuffer(); + + void Log(int logId, int arg0, int arg1); + void Print(); + void Reset(); + }; + static_assert(sizeof(LogBuffer) == 0x304); + + LogBuffer m_Buffer0; + LogBuffer m_Buffer1; + LogBuffer* m_pCurrentBuffer; + }; + static_assert(sizeof(WarningLogger) == 0x610); +}; + +} // namespace detail + +} // namespace nn::atk \ No newline at end of file diff --git a/include/nn/atk/atk_ValueArray.h b/include/nn/atk/atk_ValueArray.h new file mode 100644 index 00000000..666946c1 --- /dev/null +++ b/include/nn/atk/atk_ValueArray.h @@ -0,0 +1,87 @@ +#pragma once + +#include +#include + +namespace nn::atk::detail { + +template +class ValueArray { +public: + // UNCHECKED + static size_t GetRequiredMemSize(int count); + + // UNCHECKED + ValueArray() = default; + + // UNCHECKED + bool Initialize(void* buffer, [[maybe_unused]] size_t bufferSize, int count) { + m_pValue = reinterpret_cast(buffer); + m_Count = count; + Reset(); + return true; + } + + // UNCHECKED + bool IsInitialized() const { return m_pValue != nullptr && m_Count != 0; } + + // UNCHECKED + void* GetBufferAddr() { return m_pValue; } + + // UNCHECKED + void Finalize() { + m_pValue = nullptr; + m_Count = 0; + } + + // UNCHECKED + void Reset() { + for (int i{0}; i < m_Count; ++i) + m_pValue[i] = 0; + } + + // UNCHECKED + int GetCount() const { return m_Count; }; + + // UNCHECKED + void SetValue(int index, T value) { m_pValue[index] = value; } + + // UNCHECKED + T GetValue(int index) { return m_pValue[index]; } + + // UNCHECKED + bool TrySetValue(int index, T value) { + if (index >= m_Count) + return false; + + m_pValue[index] = value; + return true; + } + + // UNCHECKED + T TryGetValue(int index) { + if (index >= m_Count) + return 0; + + return m_pValue[index]; + } + + // UNCHECKED + ValueArray& operator=(const ValueArray& rhs) { + const int CopyCount{std::min(m_Count, rhs.m_Count)}; + + for (int i{0}; i < CopyCount; ++i) + this->SetValue(i, rhs.GetValue(i)); + + for (int i{rhs.m_Count}; i > 0; --i) + rhs.SetValue(i, 0); + + return *this; + } + +private: + T* m_pValue{}; + int m_Count{0}; +}; + +} // namespace nn::atk::detail \ No newline at end of file diff --git a/include/nn/atk/atk_Voice.h b/include/nn/atk/atk_Voice.h new file mode 100644 index 00000000..d11d00ed --- /dev/null +++ b/include/nn/atk/atk_Voice.h @@ -0,0 +1,92 @@ +#pragma once + +#include + +#include +#include + +namespace nn::atk::detail { +class Voice { +public: + constexpr static s32 PriorityMin = 0; + constexpr static s32 PriorityMax = 255; + constexpr static s32 PriorityNoDrop = 255; + + Voice(); + ~Voice(); + + void Initialize(u32); + + bool IsAvailable() const; + + bool AllocVoice(u32 priority); + void Free(); + + void SetPriority(u32 priority); + void SetState(VoiceState state); + + void AppendWaveBuffer(WaveBuffer* waveBuffer); + + void FreeAllWaveBuffer(); + + void UpdateParam(); + + position_t GetPlayPosition() const; + + bool SetMonoFilter(bool enable, u16 cutoff); + void SetBiquadFilter(bool enable, const BiquadFilterCoefficients* coef); + + void UpdateVoiceStatus(); + +private: + u32 m_Priority; + VoiceState m_State; + VoiceParam m_VoiceParam; + SampleFormat m_SampleFormat; + u32 m_SampleRate; + AdpcmParam m_AdpcmParam; +#if NN_SDK_VER < NN_MAKE_VER(4, 0, 0) + s32 m_SubMixIndex; +#else + OutputReceiver* m_pOutputReceiver; +#endif + u32 m_VoiceId; + position_t m_PlayPosition; + u32 m_VoiceInfoEnableFlag; + u32 m_CommandTag; + WaveBuffer* m_WaveBufferListBegin; + WaveBuffer* m_WaveBufferListEnd; + LowLevelVoice* m_pLowLevelVoice; +}; +#if NN_SDK_VER < NN_MAKE_VER(4, 0, 0) +static_assert(sizeof(Voice) == 0xd8); +#else +static_assert(sizeof(Voice) == 0xe0); +#endif + +class VirtualVoiceManager : Util::Singleton { +public: + constexpr static s32 InvalidVoiceId = -1; + + constexpr static u32 VirtualVoiceCount = 256; + constexpr static u32 VirtualVoiceElementCount = 8; + + void Initialize(); + + bool AllocVirtualVoice(); + void FreeVirtualVoice(u32); + + void UpdateVoiceInfo(); + + s32 GetAllocatedVirtualVoiceCount() const; + s32 GetUnreleasedLowLevelVoiceCount() const; + +private: + u32 m_VirtualVoiceAllocationTable[VirtualVoiceElementCount]; + u32 m_VoiceInfoTableRead; + LowLevelVoice* m_LowLevelVoiceTable[VirtualVoiceCount]; + VoiceInfo m_VoiceInfoTable[2][VirtualVoiceCount]; + util::BitFlagSet m_VoiceInfoDirtyTable[2]; +}; +static_assert(sizeof(VirtualVoiceManager) == 0x4868); +} // namespace nn::atk::voice \ No newline at end of file diff --git a/include/nn/atk/atk_VoiceCommand.h b/include/nn/atk/atk_VoiceCommand.h new file mode 100644 index 00000000..e5b45530 --- /dev/null +++ b/include/nn/atk/atk_VoiceCommand.h @@ -0,0 +1,96 @@ +#pragma once + +#include + +#include +#include + +namespace nn::atk::detail { +struct VoiceCommandPlay : Command { + u32 voiceId; + SampleFormat sampleFormat; + u32 sampleRate; + AdpcmParam adpcmParam; +#if NN_SDK_VER >= NN_MAKE_VER(4, 0, 0) + OutputReceiver* pOutputReceiver; +#endif +}; +#if NN_SDK_VER < NN_MAKE_VER(4, 0, 0) +static_assert(sizeof(VoiceCommandPlay) == 0x48); +#else +static_assert(sizeof(VoiceCommandPlay) == 0x50); +#endif + +struct VoiceCommandPause : Command { + u32 voiceId; +}; +static_assert(sizeof(VoiceCommandPause) == 0x20); + +struct VoiceCommandFree : Command { + u32 voiceId; +}; +static_assert(sizeof(VoiceCommandFree) == 0x20); + +struct VoiceCommandParam : Command { + u32 voiceId; + VoiceParam voiceParam; +}; +static_assert(sizeof(VoiceCommandParam) == 0x98); + +struct VoiceCommandPriority : Command { + u32 voiceId; + u32 priority; +}; +static_assert(sizeof(VoiceCommandPriority) == 0x20); + +struct VoiceCommandAlloc : Command { + u32 voiceId; + u32 priority; + void* userId; +}; +static_assert(sizeof(VoiceCommandAlloc) == 0x28); + +struct VoiceCommandAppendWaveBuffer : Command { + u32 voiceId; + void* tag; + void* bufferAddress; + size_t bufferSize; + size_t sampleLength; + position_t sampleOffset; + bool adpcmContextEnable; + AdpcmContext adpcmContext; + bool loopFlag; +}; +static_assert(sizeof(VoiceCommandAppendWaveBuffer) == 0x100); + +class VoiceReplyCommand : CommandManager { +public: + static void ProcessCommandList(Command* commandList); + +}; +static_assert(sizeof(VoiceReplyCommand) == 0x310); + +class LowLevelVoiceCommand : CommandManager { +public: + struct WaveBufferPacket { + AdpcmContext adpcmContext; + WaveBuffer waveBuffer; + }; + static_assert(sizeof(WaveBufferPacket) == 0x80); + + void Initialize(void*, size_t, void*, size_t, s32); + + static void ProcessCommandList(Command* commandList); + + static size_t GetRequiredWaveBufferMemSize(s32); + + static void LowLevelVoiceDisposeCallback(LowLevelVoice*, void*); + + void* GetFreeWaveBuffer(); + +private: + WaveBufferPacket* m_pWaveBufferPacket; + s32 m_WaveBufferPacketCount; +}; +static_assert(sizeof(LowLevelVoiceCommand) == 0x320); +} // namespace nn::atk::detail \ No newline at end of file diff --git a/include/nn/atk/atk_VolumeThroughModePacket.h b/include/nn/atk/atk_VolumeThroughModePacket.h new file mode 100644 index 00000000..87e49449 --- /dev/null +++ b/include/nn/atk/atk_VolumeThroughModePacket.h @@ -0,0 +1,28 @@ +#pragma once + +#include + +namespace nn::atk::detail { +class VolumeThroughModePacket { +public: + using VolumeThroughModeArray = ValueArray; + + VolumeThroughModePacket(); + + static size_t GetRequiredMemSize(int busCount); + + bool Initialize(void* buffer, size_t size, int busCount); + void Finalize(); + void Reset(); + + int GetBusCount() const { return m_VolumeThroughMode.GetCount(); } + + VolumeThroughModePacket& operator=(const VolumeThroughModePacket& rhs); + +private: + VolumeThroughModeArray m_VolumeThroughMode; + bool m_IsVolumeThroughModeUsed; + float m_BinaryVolume; +}; +static_assert(sizeof(VolumeThroughModePacket) == 0x18); +} // namespace nn::atk::detail \ No newline at end of file diff --git a/include/nn/atk/atk_WavBinary.h b/include/nn/atk/atk_WavBinary.h new file mode 100644 index 00000000..b79db808 --- /dev/null +++ b/include/nn/atk/atk_WavBinary.h @@ -0,0 +1,74 @@ +#pragma once + +#include + +#include +#include + +namespace nn::atk::detail { + +struct ChunkHeader { + ChunkHeader() = default; + + explicit ChunkHeader(u32 id) : id{id}, size{0} {}; + + ChunkHeader(u32 id, u32 size) : id{id}, size{size} {}; + + u32 id; + fnd::PcBinU32 size; +}; +static_assert(sizeof(ChunkHeader) == 0x8); + +struct RiffChunk { + static const u32 ValidId {0x46464952}; // "RIFF" + + RiffChunk() = default; + + bool IsValid(); + + ChunkHeader header{ValidId}; + u32 formatType; +}; +static_assert(sizeof(RiffChunk) == 0xc); + +struct FmtChunk { + static const u32 ValidId{0x20746d66}; // "fmt " + static const u16 FormatPcm{SampleFormat_PcmS16}; + + FmtChunk() = default; + + static u32 GetValidBodySize(); + + bool IsValid(); + + ChunkHeader header{ValidId, sizeof(FmtChunk) - sizeof(ChunkHeader)}; + fnd::PcBinU16 formatTag; + fnd::PcBinU16 channels; + fnd::PcBinU32 samplesPerSec; + fnd::PcBinU32 avgBytesPerSec; + fnd::PcBinU16 blockAlign; + fnd::PcBinU16 bitsPerSample; +}; +static_assert(sizeof(FmtChunk) == 0x18); + +struct DataChunk { + static const u32 ValidId {0x61746164}; // "data" + + DataChunk() = default; + + bool IsValid(); + + ChunkHeader header{ValidId}; +}; +static_assert(sizeof(DataChunk) == 0x8); + +struct WaveBinaryHeader { + WaveBinaryHeader() = default; + + RiffChunk riffChunk; + FmtChunk fmtChunk; + DataChunk dataChunk; +}; +static_assert(sizeof(WaveBinaryHeader) == 0x2c); + +} // namespace nn::atk::detail \ No newline at end of file diff --git a/include/nn/atk/atk_WavOutFileStream.h b/include/nn/atk/atk_WavOutFileStream.h new file mode 100644 index 00000000..b0367bd0 --- /dev/null +++ b/include/nn/atk/atk_WavOutFileStream.h @@ -0,0 +1,49 @@ +#pragma once + +#include +#include + +namespace nn::atk::detail { + +class WavOutFileStream { +public: + static const int FileIoBufferAlignment = alignof(char); + + WavOutFileStream(); + ~WavOutFileStream(); + + bool Open(fnd::FileStream& stream, int channels, size_t samplesPerSec); + void Close(); + + size_t Write(const void* buf, size_t length); + bool Seek(position_t offset, fnd::FileStream::SeekOrigin origin); + + bool IsAvailable() const { return m_pFileStream != nullptr && m_pFileStream->IsOpened(); } + + size_t GetSize() const; + + position_t Tell() const; + + void SetCacheBuffer(char* buf, size_t length); + +private: + bool WriteHeader(int channels, size_t samplesPerSec); + + bool UpdateRiffChunkSize(); + bool UpdateDataChunkSize(); + size_t CalcRiffChunkSize(size_t dataSize); + + size_t WriteDirect(const void* buf, size_t length, fnd::FndResult* result); + + size_t FlushBuffer(); + + fnd::FileStream* m_pFileStream{}; + size_t m_WaveDataSize{0}; + bool m_IsWaveDataSizeCalculating{false}; + char* m_Buffer{}; + size_t m_BufferLength{0}; + size_t m_ValidBufferLength{0}; +}; +static_assert(sizeof(WavOutFileStream) == 0x30); + +} // namespace nn::atk::detail \ No newline at end of file diff --git a/include/nn/atk/atk_WaveArchiveFile.h b/include/nn/atk/atk_WaveArchiveFile.h new file mode 100644 index 00000000..f4128df4 --- /dev/null +++ b/include/nn/atk/atk_WaveArchiveFile.h @@ -0,0 +1,58 @@ +#pragma once + +#include + +namespace nn::atk::detail { +struct WaveArchiveFile { + static const int BlockCount {2}; + + struct InfoBlock; + struct FileBlock; + struct FileHeader : BinaryFileHeader { + public: + Util::ReferenceWithSize toBlocks[BlockCount]; + + const InfoBlock* GetInfoBlock() const; + const FileBlock* GetFileBlock() const; + + u32 GetInfoBlockSize() const; + u32 GetFileBlockSize() const; + + u32 GetInfoBlockOffset() const; + u32 GetFileBlockOffset() const; + + private: + const Util::ReferenceWithSize* GetReferenceBy(u16 typeId) const; + }; + static_assert(sizeof(FileHeader) == 0x2c); + + struct InfoBlockBody { + Util::Table table; + + u32 GetWaveFileCount() const { return table.count; } + + u32 GetSize(u32 index) const { + return table.item[index].size; + } + + u32 GetOffsetFromFileBlockBody(u32 index) const { + return table.item[index].offset; + } + + static const u32 InvalidOffset = 0xffffffff; + }; + + struct InfoBlock { + BinaryBlockHeader header; + InfoBlockBody body; + }; + + struct FileBlockBody {/* empty structure */}; + + struct FileBlock { + BinaryBlockHeader header; + FileBlockBody body; + }; + static_assert(sizeof(FileBlock) == 0xc); +}; +} // namespace nn::atk::detail \ No newline at end of file diff --git a/include/nn/atk/atk_WaveArchiveFileReader.h b/include/nn/atk/atk_WaveArchiveFileReader.h new file mode 100644 index 00000000..3648bf98 --- /dev/null +++ b/include/nn/atk/atk_WaveArchiveFileReader.h @@ -0,0 +1,42 @@ +#pragma once + +#include + +namespace nn::atk::detail { +class WaveArchiveFileReader { +public: + constexpr static s32 SignatureFile = 0x52415746; // FWAR + constexpr static s32 SignatureWarcTable = 0x54415746; // FWAT + + struct IndividualLoadTable { + void* waveFile[1]; + }; + + WaveArchiveFileReader(); + WaveArchiveFileReader(const void* pWaveArchiveFile, bool isIndividual); + + void Initialize(const void* pWaveArchiveFile, bool isIndividual); + + bool HasIndividualLoadTable() const; + + void Finalize(); + + void InitializeFileTable(); + + u32 GetWaveFileCount() const; + void* GetWaveFile(u32 waveIndex) const; + u32 GetWaveFileSize(u32 waveIndex) const; + u32 GetWaveFileOffsetFromFileHead(u32 waveIndex) const; + + void* SetWaveFile(u32 waveIndex, const void* pWaveFile); + + bool IsLoaded(u32 index) { return m_IsInitialized && GetWaveFile(index) != nullptr; } + +private: + WaveArchiveFile::FileHeader* m_pHeader; + WaveArchiveFile::InfoBlockBody* m_pInfoBlockBody; + IndividualLoadTable* m_pLoadTable; + bool m_IsInitialized; +}; +static_assert(sizeof(WaveArchiveFileReader) == 0x20); +} // namespace nn::atk::detail \ No newline at end of file diff --git a/include/nn/atk/atk_WaveFile.h b/include/nn/atk/atk_WaveFile.h new file mode 100644 index 00000000..01604e5a --- /dev/null +++ b/include/nn/atk/atk_WaveFile.h @@ -0,0 +1,60 @@ +#pragma once + +#include + +namespace nn::atk::detail { +struct WaveFile { + struct InfoBlock; + struct DataBlock; + struct FileHeader : Util::SoundFileHeader { + const InfoBlock* GetInfoBlock() const; + const DataBlock* GetDataBlock() const; + }; + + struct ChannelInfo; + struct InfoBlockBody { + u8 encoding; + u8 isLoop; + u8 padding[2]; + u32 sampleRate; + u32 loopStartFrame; + u32 loopEndFrame; + u32 originalLoopStartFrame; + Util::ReferenceTable channelInfoReferenceTable; + + int GetChannelCount() const { return channelInfoReferenceTable.count; }; + const ChannelInfo& GetChannelInfo(int channelIndex) const; + }; + + struct InfoBlock { + BinaryBlockHeader header; + InfoBlockBody body; + }; + + struct DspAdpcmInfo; + struct ChannelInfo { + Util::Reference referToSamples; + Util::Reference referToAdpcmInfo; + u32 reserved; + + const void* GetSamplesAddress(const void* dataBlockBodyAddress) const; + const DspAdpcmInfo& GetDspAdpcmInfo() const; + }; + static_assert(sizeof(ChannelInfo) == 0x14); + + struct DspAdpcmInfo { + DspAdpcmParam adpcmParam; + DspAdpcmLoopParam adpcmLoopParam; + }; + static_assert(sizeof(DspAdpcmInfo) == 0x2c); + + struct DataBlock { + BinaryBlockHeader header; + union { + s8 pcm8[1]; + s16 pcm16[1]; + u8 byte[1]; + }; + }; +}; +} // namespace nn::atk::detail \ No newline at end of file diff --git a/include/nn/atk/atk_WaveFileReader.h b/include/nn/atk/atk_WaveFileReader.h new file mode 100644 index 00000000..9b518a1a --- /dev/null +++ b/include/nn/atk/atk_WaveFileReader.h @@ -0,0 +1,32 @@ +#pragma once + +#include +#include + +namespace nn::atk::detail { +class WaveFileReader { +public: + static const int SignatureFile = 0x56415746; // FWAV + + static SampleFormat GetSampleFormat(u8 format); + + WaveFileReader(const void* waveFile, s8 waveType); + + bool IsAvailable() const { return m_pHeader != nullptr; } + + bool IsOriginalLoopAvailable() const; + + bool ReadWaveInfo(WaveInfo* info, const void* waveDataOffsetOrigin) const; + + const void* GetWaveDataAddress(const WaveFile::ChannelInfo* info, + [[maybe_unused]] const void* waveDataOffsetOrigin) const; + +private: + const WaveFile::FileHeader* m_pHeader {}; + const WaveFile::InfoBlockBody* m_pInfoBlockBody {}; + const void* m_pDataBlockBody {}; + DspadpcmReader m_DspadpcmReader; + s8 m_WaveType; +}; +static_assert(sizeof(WaveFileReader) == 0x28); +} // namespace nn::atk::detail \ No newline at end of file diff --git a/include/nn/atk/atk_WaveSound.h b/include/nn/atk/atk_WaveSound.h new file mode 100644 index 00000000..9b984fbc --- /dev/null +++ b/include/nn/atk/atk_WaveSound.h @@ -0,0 +1,68 @@ +#pragma once + +#include +#include +#include +#include + +namespace nn::atk::detail { +class WaveSound; +using WaveSoundInstanceManager = SoundInstanceManager; + +class WaveSound : BasicSound { +public: + explicit WaveSound(WaveSoundInstanceManager& manager); + ~WaveSound() override; + +#if NN_SDK_VER < NN_MAKE_VER(4, 0, 0) + bool Initialize() override; +#else + bool Initialize(OutputReceiver* pOutputReceiver) override; +#endif + void Finalize() override; + + void Prepare(const void* wsdFile, const void* waveFile, + const driver::WaveSoundPlayer::StartInfo& startInfo, s8 waveType); + + void RegisterDataLoadTask(const driver::WaveSoundLoader::LoadInfo& loadInfo, + const driver::WaveSoundPlayer::StartInfo& startInfo); + + void SetChannelPriority(s32 priority); + + void InitializeChannelParam(s32 priority, bool isReleasePriorityFix); + + void OnUpdatePlayerPriority() override; + + bool IsAttachedTempSpecialHandle() override; + void DetachTempSpecialHandle() override; + + bool ReadWaveSoundDataInfo(WaveSoundDataInfo*) const; + + position_t GetPlaySamplePosition(bool) const; + + bool IsPrepared() const override; + + driver::BasicSoundPlayer* GetBasicSoundPlayerHandle() override; + + void OnUpdateParam() override; + +private: + friend WaveSoundInstanceManager; + + util::IntrusiveListNode m_PriorityLink; + WaveSoundHandle* m_pTempSpecialHandle; + WaveSoundInstanceManager* m_Manager; + void* m_pWaveFile; + s8 m_WaveType; + bool m_InitializeFlag; + bool m_IsCalledPrepare; + u8 m_Padding[1]; + u32 m_ChannelCount; + driver::WaveSoundPlayer m_PlayerInstance; +}; +#if NN_SDK_VER < NN_MAKE_VER(4, 0, 0) +static_assert(sizeof(WaveSound) == 0x3b0); +#else +static_assert(sizeof(WaveSound) == 0x3e0); +#endif +} // namespace nn::atk::detail \ No newline at end of file diff --git a/include/nn/atk/atk_WaveSoundFile.h b/include/nn/atk/atk_WaveSoundFile.h new file mode 100644 index 00000000..f7c9ccef --- /dev/null +++ b/include/nn/atk/atk_WaveSoundFile.h @@ -0,0 +1,117 @@ +#pragma once + +#include + +namespace nn::atk::detail { +struct WaveSoundFile { + struct InfoBlock; + struct FileHeader : Util::SoundFileHeader { + const InfoBlock* GetInfoBlock() const; + }; + + struct WaveSoundData; + struct InfoBlockBody { + Util::Reference toWaveIdTable; + Util::Reference toWaveSoundDataReferenceTable; + + const Util::WaveIdTable& GetWaveIdTable() const; + const Util::ReferenceTable& GetWaveSoundDataReferenceTable() const; + + u32 GetWaveIdCount() const { + return GetWaveIdTable().GetCount(); + } + + u32 GetWaveSoundCount() const { + return GetWaveSoundDataReferenceTable().count; + } + + const Util::WaveId* GetWaveId(u32 index) const { + return GetWaveIdTable().GetWaveId(index); + } + + const WaveSoundData& GetWaveSoundData(u32 index) const; + }; + static_assert(sizeof(InfoBlockBody) == 0x10); + + struct InfoBlock { + BinaryBlockHeader header; + InfoBlockBody body; + }; + static_assert(sizeof(InfoBlock) == 0x18); + + struct WaveSoundInfo; + struct TrackInfo; + struct NoteInfo; + struct WaveSoundData { + Util::Reference toWaveSoundInfo; + Util::Reference toTrackInfoReferenceTable; + Util::Reference toNoteInfoReferenceTable; + + const WaveSoundInfo& GetWaveSoundInfo() const; + const Util::ReferenceTable& GetTrackInfoReferenceTable() const; + const Util::ReferenceTable& GetNoteInfoReferenceTable() const; + + u32 GetTrackCount() const { + return GetTrackInfoReferenceTable().count; + } + + u32 GetNoteCount() const { + return GetNoteInfoReferenceTable().count; + } + + const TrackInfo& GetTrackInfo(u32 index) const; + const NoteInfo& GetNoteInfo(u32 index) const; + }; + static_assert(sizeof(WaveSoundData) == 0x18); + + struct WaveSoundInfo { + Util::BitFlag optionParameter; + + u8 GetPan() const; + s8 GetSurroundPan() const; + float GetPitch() const; + void GetSendValue(u8* mainSend, u8* fxSend, u8 fxSendCount) const; + const AdshrCurve& GetAdshrCurve() const; + u8 GetLpfFreq() const; + u8 GetBiquadType() const; + u8 GetBiquadValue() const; + }; + static_assert(sizeof(WaveSoundInfo) == 0x4); + + struct NoteEvent; + struct TrackInfo { + Util::Reference toNoteEventReferenceTable; + + const Util::ReferenceTable& GetNoteEventReferenceTable() const; + + u32 GetNoteEventCount() const { + return GetNoteEventReferenceTable().count; + } + + const NoteEvent& GetNoteEvent(u32 index) const; + }; + static_assert(sizeof(TrackInfo) == 0x8); + + struct NoteEvent { + float position; + float length; + u32 noteIndex; + u32 reserved; + }; + static_assert(sizeof(NoteEvent) == 0x10); + + struct NoteInfo { + u32 waveIdTableIndex; + Util::BitFlag optionParameter; + + u8 GetOriginalKey() const; + u8 GetVolume() const; + u8 GetPan() const; + u8 GetSurroundPan() const; + float GetPitch() const; + void GetSendValue(u8* mainSend, u8** fxSend, u8 fxSendCount) const; + const AdshrCurve& GetAdshrCurve() const; + }; + static_assert(sizeof(NoteInfo) == 0x8); +}; +} // namespace nn::atk::detail \ No newline at end of file diff --git a/include/nn/atk/atk_WaveSoundFileReader.h b/include/nn/atk/atk_WaveSoundFileReader.h new file mode 100644 index 00000000..10ead847 --- /dev/null +++ b/include/nn/atk/atk_WaveSoundFileReader.h @@ -0,0 +1,55 @@ +#pragma once + +#include + +namespace nn::atk::detail { +struct WaveSoundInfo { + f32 pitch; + AdshrCurve adshr; + u8 pan; + u8 surroundPan; + u8 mainSend; + u8 fxSend[3]; + u8 lpfFreq; + u8 biquadType; + u8 biquadValue; +}; +static_assert(sizeof(WaveSoundInfo) == 0x14); + +struct WaveSoundNoteInfo { + u32 waveArchiveId; + s32 waveIndex; + AdshrCurve adshr; + u8 originalKey; + u8 pan; + u8 surroundPan; + u8 volume; + f32 pitch; + + WaveSoundNoteInfo() + : adshr(0, 0, 0, 0, 0) {}; +}; +static_assert(sizeof(WaveSoundNoteInfo) == 0x18); + +class WaveSoundFileReader { +public: + constexpr static s32 SignatureFile = 0x44535746; // FWSD + + explicit WaveSoundFileReader(const void* waveSoundFile); + + s32 GetWaveSoundCount() const; + s32 GetNoteInfoCount(u32) const; + s32 GetTrackInfoCount(u32) const; + + bool ReadWaveSoundInfo(WaveSoundInfo* dst, u32 index) const; + + bool IsFilterSupportedVersion() const; + + bool ReadNoteInfo(WaveSoundNoteInfo* dst, u32 index, u32 noteIndex) const; + +private: + WaveSoundFile::FileHeader* m_pHeader; + WaveSoundFile::InfoBlockBody* m_pInfoBlockBody; +}; +static_assert(sizeof(WaveSoundFileReader) == 0x10); +} // namespace nn::atk::detail \ No newline at end of file diff --git a/include/nn/atk/atk_WaveSoundHandle.h b/include/nn/atk/atk_WaveSoundHandle.h new file mode 100644 index 00000000..2d221b3b --- /dev/null +++ b/include/nn/atk/atk_WaveSoundHandle.h @@ -0,0 +1,27 @@ +#pragma once + +#include + +#include + +namespace nn::atk { +namespace detail { +class WaveSound; +} // namespace nn::atk::detail + +class WaveSoundHandle { +public: + using TrackBitFlagSet = util::BitFlagSet<8, void>; + + explicit WaveSoundHandle(SoundHandle* pSoundHandle); + + void detail_AttachSoundAsTempHandle(detail::WaveSound* pSound); + + void ForceStop(); + + void DetachSound(); + +private: + detail::WaveSound* m_pSound; +}; +} // namespace nn::atk \ No newline at end of file diff --git a/include/nn/atk/atk_WaveSoundLoader.h b/include/nn/atk/atk_WaveSoundLoader.h new file mode 100644 index 00000000..240511b1 --- /dev/null +++ b/include/nn/atk/atk_WaveSoundLoader.h @@ -0,0 +1,90 @@ +#pragma once + +#include +#include +#include +#include +#include + +namespace nn::atk::detail::driver { +class WaveSoundLoader; +using WaveSoundLoaderManager = LoaderManager; + +class WaveSoundLoader { +public: + struct Arg { + SoundArchive* soundArchive; + SoundDataManager* soundDataManager; + SoundPlayer* soundPlayer; + LoadItemInfo loadInfoWsd; + s32 index; + }; + static_assert(sizeof(Arg) == 0x30); + + struct Data { + void* wsdFile; + void* waveFile; + }; + static_assert(sizeof(Data) == 0x10); + + struct LoadInfo { + SoundArchive* soundArchive; + SoundDataManager* soundDataManager; + LoadItemInfo* loadInfoWsd; + SoundPlayer* soundPlayer; + }; + static_assert(sizeof(LoadInfo) == 0x20); + + class DataLoadTask : Task { + public: + ~DataLoadTask() override; + + void Initialize(); + + bool TryAllocPlayerHeap(); + + void Execute(TaskProfileLogger& logger) override; + + private: + Arg m_Arg; + Data m_Data; + PlayerHeap* m_pPlayerHeap; + PlayerHeapDataManager* m_pPlayerHeapDataManager; + bool m_IsLoadSuccess; + u8 m_Padding[3]; + }; + static_assert(sizeof(DataLoadTask) == 0xa0); + + class FreePlayerHeapTask : Task { + public: + ~FreePlayerHeapTask() override; + + void Initialize(); + + void Execute(TaskProfileLogger& logger) override; + + private: + Arg m_Arg; + PlayerHeap* m_pPlayerHeap; + PlayerHeapDataManager* m_pPlayerHeapDataManager; + }; + + WaveSoundLoader(); + ~WaveSoundLoader(); + + void Initialize(const Arg& arg); + void Finalize(); + + bool TryWait(); + + bool IsInUse(); + +private: + friend WaveSoundLoaderManager; + + DataLoadTask m_Task; + FreePlayerHeapTask m_FreePlayerHeapTask; + PlayerHeapDataManager m_PlayerHeapDataManager; + util::IntrusiveListNode m_LinkForLoaderManager; +}; +} // namespace nn::atk::detail::driver \ No newline at end of file diff --git a/include/nn/atk/atk_WaveSoundPlayer.h b/include/nn/atk/atk_WaveSoundPlayer.h new file mode 100644 index 00000000..182d82a3 --- /dev/null +++ b/include/nn/atk/atk_WaveSoundPlayer.h @@ -0,0 +1,147 @@ +#pragma once + +#include +#include +#include +#include + +namespace nn::atk::detail { +struct WaveSoundDataInfo { + bool loopFlag; + s32 sampleRate; + s64 loopStart; + s64 loopEnd; + s64 compatibleLoopStart; + s64 compatibleLoopEnd; + s32 channelCount; +}; +static_assert(sizeof(WaveSoundDataInfo) == 0x30); + +namespace driver { +class WaveSoundPlayer : BasicSoundPlayer, DisposeCallback, SoundThread::PlayerCallback { +public: + enum StartOffsetType { + StartOffsetType_Sample, + StartOffsetType_Millisec, + }; + + enum ResState { + ResState_Invalid, + ResState_ReceiveLoadReq, + ResState_AppendLoadTask, + ResState_Assigned, + }; + + constexpr static s32 SignatureFile = 0x44535746; // FWSD + + constexpr static u32 PauseReleaseValue = 127; + constexpr static u32 MuteReleaseValue = 127; + constexpr static u32 DefaultPriority = 64; + + struct StartInfo { + s32 index; + StartOffsetType startOffsetType; + s32 startOffset; + s32 delayTime; + s32 delayCount; + s32 waveSoundParameterFlag; + s32 release; + bool isContextCalculationSkipMode; + UpdateType updateType; + }; + static_assert(sizeof(StartInfo) == 0x24); + + struct PrepareArg { + void* wsdFile; + void* waveFile; + s8 waveType; + u8 padding[3]; + }; + static_assert(sizeof(PrepareArg) == 0x18); + + WaveSoundPlayer(); + ~WaveSoundPlayer() override; + +#if NN_SDK_VER < NN_MAKE_VER(4, 0, 0) + bool Initialize() override; +#else + bool Initialize(OutputReceiver* pOutputReceiver) override; +#endif + void Finalize() override; + + void FinishPlayer(); + + void CloseChannel(); + + void FreeLoader(); + + void Prepare(const StartInfo& info, const PrepareArg& arg); + void PrepareForPlayerHeap(const PrepareArg& arg); + + void RequestLoad(const StartInfo& info, const WaveSoundLoader::Arg& arg); + + void Start() override; + void Stop() override; + void Pause(bool flag) override; + + void SetPanRange(f32 range); + void SetChannelPriority(s32 priority); + void SetReleasePriorityFix(bool fix); + + void InvalidateData(const void* start, const void* end) override; + + position_t GetPlaySamplePosition(bool) const; + + void Update(); + + bool TryAllocLoader(); + + bool StartChannel(); + void UpdateChannel(); + + static void ChannelCallbackFunc(Channel* dropChannel, + Channel::ChannelCallbackStatus status, void* userData); + + void OnUpdateFrameSoundThread() override; + void OnUpdateFrameSoundThreadWithAudioFrameFrequency() override; + void OnShutdownSoundThread() override; + +private: + bool m_WavePlayFlag; + bool m_ReleasePriorityFixFlag; + u8 m_Priority; + s8 m_WaveType; + f32 m_PanRange; + void* m_pWsdFile; + void* m_pWaveFile; + s32 m_WaveSoundIndex; + StartOffsetType m_StartOffsetType; + position_t m_StartOffset; + s32 m_DelayCount; + s32 m_Release; + s32 m_WaveSoundParameterFlag; +#if NN_SDK_VER >= NN_MAKE_VER(4, 0, 0) + bool m_IsContextCalculationSkipMode; +#endif + CurveLfoParam m_LfoParam; + WaveSoundInfo m_WaveSoundInfo; + Channel* m_pChannel; + UpdateType m_UpdateType; +#if NN_SDK_VER < NN_MAKE_VER(4, 0, 0) + u32 m_SubMixIndex; +#endif + u8 m_ResState; + bool m_IsInitialized; + bool m_IsRegisterPlayerCallback; + u8 m_Padding[1]; + WaveSoundLoaderManager* m_pLoaderManager; + WaveSoundLoader* m_pLoader; + WaveSoundLoader::Arg m_LoaderArg; +}; +#if NN_SDK_VER < NN_MAKE_VER(4, 0, 0) +static_assert(sizeof(WaveSoundPlayer) == 0x190); +#else +static_assert(sizeof(WaveSoundPlayer) == 0x1a0); +#endif +} // namespace nn::atk::detail::driver +} // namespace nn::atk::detail \ No newline at end of file diff --git a/include/nn/atk/atk_WaveSoundRuntime.h b/include/nn/atk/atk_WaveSoundRuntime.h new file mode 100644 index 00000000..3b025b34 --- /dev/null +++ b/include/nn/atk/atk_WaveSoundRuntime.h @@ -0,0 +1,52 @@ +#pragma once + +#include +#include + +namespace nn::atk::detail { +class WaveSoundRuntime { +public: + WaveSoundRuntime(); + ~WaveSoundRuntime(); + + bool Initialize(s32 soundCount, void** pOutAllocatedAddr, const void* endAddr); + void Finalize(); + + static size_t GetRequiredMemorySize(const SoundArchive::SoundArchivePlayerInfo& soundArchivePlayerInfo, + s32 alignment); + + s32 GetActiveCount() const; + s32 GetFreeWaveSoundCount() const; + + void SetupUserParam(void** startAddr, size_t adjustSize); + + void Update(); + +#if NN_SDK_VER < NN_MAKE_VER(4, 0, 0) + WaveSound* AllocSound(SoundArchive::ItemId soundId, s32 priority, s32 ambientPriority, + BasicSound::AmbientInfo* ambientArgInfo); +#else + WaveSound* AllocSound(SoundArchive::ItemId soundId, s32 priority, s32 ambientPriority, + BasicSound::AmbientInfo* ambientArgInfo, OutputReceiver* pOutputReceiver); +#endif + + SoundStartable::StartResult PrepareImpl(const SoundArchive* pSoundArchive, + const SoundDataManager* pSoundDataManager, + SoundArchive::ItemId soundId, + WaveSound* sound, + const SoundArchive::SoundInfo* commonInfo, + const StartInfoReader& startInfoReader); + + void DumpMemory(const SoundArchive*) const; + +private: + WaveSoundInstanceManager m_WaveSoundInstanceManager; + driver::WaveSoundLoaderManager m_WaveSoundLoaderManager; + SoundArchiveFilesHook* m_pSoundArchiveFilesHook; +}; +#if NN_SDK_VER < NN_MAKE_VER(4, 0, 0) +static_assert(sizeof(WaveSoundRuntime) == 0x80); +#else +static_assert(sizeof(WaveSoundRuntime) == 0x88); +#endif +} // namespace nn::atk::detail diff --git a/include/nn/atk/detail/AdvancedWaveSoundRuntime.h b/include/nn/atk/detail/AdvancedWaveSoundRuntime.h deleted file mode 100644 index 9807b84d..00000000 --- a/include/nn/atk/detail/AdvancedWaveSoundRuntime.h +++ /dev/null @@ -1,28 +0,0 @@ -/** - * @file AdvancedWaveSoundRuntime.h - * @brief Runtime wave sound api. - */ - -#pragma once - -#include - -namespace nn { -namespace atk { -namespace detail { -class AdvancedWaveSoundRuntime { -public: - AdvancedWaveSoundRuntime(); - ~AdvancedWaveSoundRuntime(); - - void Initialize(s32, void**, void const*); - void Finalize(); - s32 GetActiveCount() const; - void SetupUserParam(void**, u64); - void Update(); - - u8 _0[0x30]; -}; -} // namespace detail -} // namespace atk -} // namespace nn diff --git a/include/nn/atk/detail/BasicSound.h b/include/nn/atk/detail/BasicSound.h deleted file mode 100644 index eb196477..00000000 --- a/include/nn/atk/detail/BasicSound.h +++ /dev/null @@ -1,137 +0,0 @@ -/** - * @file BasicSound.h - * @brief A basic sound. - */ - -#pragma once - -#include - -namespace nn { -namespace atk { -class SoundActor; -class SoundPlayer; - -enum MixMode { - -}; - -namespace detail { -class PlayerHeap; -class ExternalSoundPlayer; - -class BasicSound { -public: - BasicSound(); - virtual ~BasicSound(); - - virtual void Initialize(); - virtual void Finalize(); - virtual bool IsPrepared() const = 0; - virtual bool IsAttachedTempSpecialHandle() = 0; - virtual void DetachTempSpecialHandle() = 0; - virtual void OnUpdatePlayerPriority(); - virtual void UpdateMoveValue(); - virtual void OnUpdateParam(); - - void SetPriority(s32, s32); - void GetPriority(s32*, s32*) const; - void ClearIsFinalizedForCannotAllocatedResourceFlag(); - void SetId(u32 newID); - bool IsAttachedGeneralHandle(); - void DetachGeneralHandle(); - bool IsAttachedTempGeneralHandle(); - void DetachTempGeneralHandle(); - void StartPrepared(); - void Stop(s32); - void SetPlayerPriority(s32); - void ForceStop(); - void Pause(bool, s32); - void Mute(bool, s32); - void SetAutoStopCounter(s32); - void FadeIn(s32); - bool IsPause() const; - bool IsMute() const; - void Update(); - void UpdateParam(); - void CalculateVolume() const; - f32 CalculatePitch() const; - f32 CalculateLpfFrequency() const; - u32 CalculateOutLineFlag() const; - void CalculateBiquadFilter(s32*, f32*) const; - void AttachPlayerHeap(nn::atk::detail::PlayerHeap*); - void DetachPlayerHeap(nn::atk::detail::PlayerHeap*); - void AttachSoundPlayer(nn::atk::SoundPlayer*); - void DetachSoundPlayer(nn::atk::SoundPlayer*); - void AttachSoundActor(nn::atk::SoundActor*); - void DetachSoundActor(nn::atk::SoundActor*); - void AttachExternalSoundPlayer(nn::atk::detail::ExternalSoundPlayer*); - void DetachExternalSoundPlayer(nn::atk::detail::ExternalSoundPlayer*); - u32 GetRemainingFadeFrames() const; - u32 GetRemainingPauseFadeFrames() const; - u32 GetRemainingMuteFadeFrames() const; - void SetInitialVolume(f32 vol); - f32 GetInitialVolume() const; - void SetVolume(f32, s32); - s32 GetVolume() const; - void SetPitch(f32); - f32 GetPitch() const; - void SetLpfFreq(f32); - f32 GetLpfFreq() const; - void SetBiquadFilter(s32, f32); - void GetBiquadFilter(s32*, f32*) const; - void SetOutputLine(u32); - u32 GetOutputLine() const; - void ResetOutputLine(); - void SetMixMode(nn::atk::MixMode); - nn::atk::MixMode GetMixMode(); - void SetPan(f32); - f32 GetPan() const; - void SetSurroundPan(f32); - f32 GetSurroundPan() const; - void SetMainSend(f32); - f32 GetMainSend() const; - - u64* _8; // nn::atk::detail::PlayerHeap* - u64* _10; // nn::atk::SoundHandle* - u64* _18; // nn::atk::SoundHandle* - nn::atk::SoundPlayer* mSoundPlayer; // _20 - u64* _28; // nn::atk::SoundActor* - u64* _30; // nn::atk::detail::ExternalSoundPlayer* - u64* _38; // nn::atk::SoundArchive* - u8 _40[0xF0 - 0x40]; - s32 mPriority; // _F0 - u32 _F4; - u32 _F8; - s32 mAutoStopCounter; // _FC - u64 _100; - u32 mID; // _108 - u32 _10C; - u32 _110; - u32 _114; - f32 mInitialVolume; // _118 - f32 mPitch; // _11C - f32 mLpfFreq; // _120 - f32 _124; - u32 mOutputLine; // _128 - f32 _12C; - f32 mVolume; // _130 - u32 _134; - u32 _138; - nn::atk::MixMode mMixMode; // _13C - f32 mPan; // _140 - f32 mSurroundPan; // _144 - f32 mMainSend; // _148 - u8 _14C[0x158 - 0x14C]; - f32 mOutputVol; // _158 - u8 _15C[0x190 - 0x15C]; - f32 mOutputPan; // _190 - f32 mOutputSurroundPan; // _194 - f32 mOutputMainSend; // _198 - f32 mOutputFxSend; // _19C - - static u64 g_LastInstanceId; -}; -} // namespace detail -} // namespace atk -} // namespace nn diff --git a/include/nn/atk/detail/SequenceSoundRuntime.h b/include/nn/atk/detail/SequenceSoundRuntime.h deleted file mode 100644 index 5401a5f3..00000000 --- a/include/nn/atk/detail/SequenceSoundRuntime.h +++ /dev/null @@ -1,37 +0,0 @@ -/** - * @file SequenceSoundRuntime.h - * @brief Sequenced Sound Runtime Info - */ - -#pragma once - -#include - -namespace nn { -namespace atk { -namespace detail { -class SoundArchiveManager; - -class SequenceSoundRuntime { -public: - SequenceSoundRuntime(); - ~SequenceSoundRuntime(); - - void Initialize(s32, void**, void const*); - void Finalize(); - void SetupSequenceTrack(s32, void**, void const*); - void SetupUserParam(void**, u64); - bool IsSoundArchiveAvailable() const; - s32 GetActiveCount() const; - s32 GetFreeCount() const; - void SetSequenceSkipIntervalTick(s32 tick); - s32 GetSequenceSkipIntervalTick(); - void Update(); - - u8 _0[0xD0]; - nn::atk::detail::SoundArchiveManager* mArchiveManager; // _D0 - u64 _D8; -}; -} // namespace detail -} // namespace atk -} // namespace nn diff --git a/include/nn/atk/detail/SoundArchiveManager.h b/include/nn/atk/detail/SoundArchiveManager.h deleted file mode 100644 index e96b93e3..00000000 --- a/include/nn/atk/detail/SoundArchiveManager.h +++ /dev/null @@ -1,42 +0,0 @@ -/** - * @file SoundArchiveManager.h - * @brief Sound archive manager implementation. - */ - -#pragma once - -#include - -namespace nn { -namespace atk { -class SoundHandle; -class SoundArchive; -class SoundDataManager; - -namespace detail { -class AddonSoundArchiveContainer; - -class SoundArchiveManager { -public: - SoundArchiveManager(); - - virtual ~SoundArchiveManager(); - - void Initialize(nn::atk::SoundArchive const*, nn::atk::SoundDataManager const*); - void ChangeTargetArchive(char const*); - void Finalize(); - bool IsAvailable() const; - nn::atk::detail::AddonSoundArchiveContainer* GetAddonSoundArchive(char const*) const; - - u64 _8; - u64* _10; - nn::atk::detail::AddonSoundArchiveContainer* _18; - u64* _20; - nn::atk::SoundArchive* mSoundArchive; // _28 - u64 _30; - u64 _38; - u64 _40; -}; -} // namespace detail -} // namespace atk -} // namespace nn diff --git a/include/nn/atk/detail/StreamSoundRuntime.h b/include/nn/atk/detail/StreamSoundRuntime.h deleted file mode 100644 index 16863f41..00000000 --- a/include/nn/atk/detail/StreamSoundRuntime.h +++ /dev/null @@ -1,22 +0,0 @@ -/** - * @file StreamSoundRuntime.h - * @brief Stream sound runtime information. - */ - -#pragma once - -#include - -namespace nn { -namespace atk { -namespace detail { -class StreamSoundRuntime { -public: - StreamSoundRuntime(); - ~StreamSoundRuntime(); - - u8 _0[0xB0]; -}; -} // namespace detail -} // namespace atk -} // namespace nn diff --git a/include/nn/atk/detail/WaveSoundRuntime.h b/include/nn/atk/detail/WaveSoundRuntime.h deleted file mode 100644 index c956da89..00000000 --- a/include/nn/atk/detail/WaveSoundRuntime.h +++ /dev/null @@ -1,29 +0,0 @@ -/** - * @file WaveSoundRuntime.h - * @brief Wave sound runtime info. - */ - -#pragma once - -#include - -namespace nn { -namespace atk { -namespace detail { -class WaveSoundRuntime { -public: - WaveSoundRuntime(); - ~WaveSoundRuntime(); - - void Initialize(s32, void**, void const*); - void Finalize(); - s32 GetActiveCount() const; - s32 GetFreeWaveSoundCount() const; - void SetupUserParam(void**, u64); - void Update(); - - u8 _0[0x80]; -}; -} // namespace detail -} // namespace atk -} // namespace nn diff --git a/include/nn/atk/detail/atk_AddonSoundArchiveContainer.h b/include/nn/atk/detail/atk_AddonSoundArchiveContainer.h new file mode 100644 index 00000000..f63bcb38 --- /dev/null +++ b/include/nn/atk/detail/atk_AddonSoundArchiveContainer.h @@ -0,0 +1,46 @@ +#pragma once + +#include +#include + +#include +#include + +namespace nn::atk::detail { +class AddonSoundArchiveContainer { +public: + AddonSoundArchiveContainer(); + ~AddonSoundArchiveContainer(); + + bool Initialize(const char* soundArchiveName, const AddonSoundArchive* pSoundArchive, + const SoundDataManager* pSoundDataManager); + + void Finalize(); + + bool IsSameName(const char* name) const; + + bool IsActive() const { return m_IsActive; } + + const SoundArchive* GetSoundArchive() const { return m_pSoundArchive; } + + const SoundDataManager* GetSoundDataManager() const { return m_pSoundDataManager; } + + const char* GetSoundArchiveName() const { return m_SoundArchiveName; } + + void SetAddTick(const os::Tick& tick); + + os::Tick GetAddTick() const { return m_AddTick; } + + static const int SoundArchiveNameLengthMax = 64; + util::IntrusiveListNode m_ElementLink; + +private: + bool m_IsActive{false}; + [[maybe_unused]] u8 m_Padding[3]; + const SoundArchive* m_pSoundArchive{}; + const SoundDataManager* m_pSoundDataManager{}; + char m_SoundArchiveName[SoundArchiveNameLengthMax]; + os::Tick m_AddTick{0}; +}; +static_assert(sizeof(AddonSoundArchiveContainer) == 0x70); +} // namespace nn::atk::detail \ No newline at end of file diff --git a/include/nn/atk/detail/atk_AdvancedWaveSound.h b/include/nn/atk/detail/atk_AdvancedWaveSound.h new file mode 100644 index 00000000..ec1a6b9b --- /dev/null +++ b/include/nn/atk/detail/atk_AdvancedWaveSound.h @@ -0,0 +1,51 @@ +#pragma once + +#include +#include +#include + +namespace nn::atk::detail { +class AdvancedWaveSound; +class AdvancedWaveSoundHandle; + +using AdvancedWaveSoundInstanceManager = SoundInstanceManager; + +class AdvancedWaveSound : BasicSound { +public: + explicit AdvancedWaveSound(const AdvancedWaveSoundInstanceManager& manager); + ~AdvancedWaveSound() override; + +#if NN_SDK_VER < NN_MAKE_VER(4, 0, 0) + bool Initialize() override; +#else + bool Initialize(OutputReceiver* pOutputReceiver) override; +#endif + void Finalize() override; + + void Prepare(const driver::AdvancedWaveSoundPlayer::PrepareParameter& parameter); + + void OnUpdatePlayerPriority() override; + + bool IsAttachedTempSpecialHandle() override; + void DetachTempSpecialHandle() override; + + bool IsPrepared() const override; + + driver::BasicSoundPlayer* GetBasicSoundPlayerHandle() override; + +private: + friend AdvancedWaveSoundInstanceManager; + + util::IntrusiveListNode m_PriorityLink; + AdvancedWaveSoundHandle* m_pTempSpecialHandle; + AdvancedWaveSoundInstanceManager* m_InstanceManager; + driver::AdvancedWaveSoundPlayer m_PlayerInstance; + bool m_IsInitialized; + u8 m_Padding[3]; +}; +#if NN_SDK_VER < NN_MAKE_VER(4, 0, 0) +static_assert(sizeof(AdvancedWaveSound) == 0x980); +#else +static_assert(sizeof(AdvancedWaveSound) == 0x9b0); +#endif +} // namespace nn::atk::detail diff --git a/include/nn/atk/detail/atk_AdvancedWaveSoundFile.h b/include/nn/atk/detail/atk_AdvancedWaveSoundFile.h new file mode 100644 index 00000000..1cc812ab --- /dev/null +++ b/include/nn/atk/detail/atk_AdvancedWaveSoundFile.h @@ -0,0 +1,55 @@ +#pragma once + +#include + +#include + +namespace nn::atk::detail { +struct AdvancedWaveSoundFile { + struct WaveSoundTrack; + struct InfoBlockBody { + + BinaryTypes::ReferenceTable* GetTrackReferenceTable() const; + WaveSoundTrack* GetWaveSoundTrack(s32 index) const; + + u32 offsetToTrackTableReference; + BinaryTypes::Reference toTrackTable; + }; + static_assert(sizeof(InfoBlockBody) == 0x8); + + struct InfoBlock { + util::BinaryBlockHeader blockHeader; + InfoBlockBody body; + }; + static_assert(sizeof(InfoBlock) == 0x18); + + struct WaveSoundClip; + struct WaveSoundTrack { + + BinaryTypes::ReferenceTable* GetClipReferenceTable() const; + WaveSoundClip* GetWaveSoundClip(s32 index) const; + + u32 offsetToCurveTableReference; + u32 offsetToClipTableReference; + BinaryTypes::Reference toClipTable; + }; + + struct WaveSoundClip { + u32 waveIndex; + u32 position; + u32 duration; + u32 startOffset; + float pitch; + u8 volume; + u8 pan; + u8 padding[2]; + }; + static_assert(sizeof(WaveSoundClip) == 0x18); + + InfoBlock* GetBlock() const; + + util::BinaryFileHeader fileHeader; +}; +static_assert(sizeof(AdvancedWaveSoundFile) == 0x20); + +} // namespace nn::atk::detail \ No newline at end of file diff --git a/include/nn/atk/detail/atk_AdvancedWaveSoundFileReader.h b/include/nn/atk/detail/atk_AdvancedWaveSoundFileReader.h new file mode 100644 index 00000000..2d546c55 --- /dev/null +++ b/include/nn/atk/detail/atk_AdvancedWaveSoundFileReader.h @@ -0,0 +1,46 @@ +#pragma once + +#include + +namespace nn::atk::detail { +struct AdvancedWaveSoundClipInfo { + u32 waveIndex; + u32 position; + u32 duration; + u32 startOffset; + f32 pitch; + u8 volume; + u8 pan; +}; +static_assert(sizeof(AdvancedWaveSoundClipInfo) == 0x18); + +struct AdvancedWaveSoundTrackInfo { + constexpr static u32 AdvancedWaveSoundClipInfoCountMax = 10; + + s32 waveSoundClipCount; + AdvancedWaveSoundClipInfo waveSoundClipInfo[AdvancedWaveSoundClipInfoCountMax]; +}; +static_assert(sizeof(AdvancedWaveSoundTrackInfo) == 0xf4); + +struct AdvancedWaveSoundTrackInfoSet { + constexpr static u32 AdvancedWaveSoundTrackInfoCountMax = 4; + + s32 waveSoundTrackCount; + AdvancedWaveSoundTrackInfo waveSoundTrackInfo[AdvancedWaveSoundTrackInfoCountMax]; +}; +static_assert(sizeof(AdvancedWaveSoundTrackInfoSet) == 0x3d4); + +class AdvancedWaveSoundFileReader { +public: + explicit AdvancedWaveSoundFileReader(const void* pFile); + + s32 GetWaveSoundTrackCount(); + s32 GetWaveSoundClipCount(s32 index); + + bool ReadWaveSoundTrackInfoSet(AdvancedWaveSoundTrackInfoSet* pTrackInfoSet); + +private: + AdvancedWaveSoundFile::InfoBlockBody* m_pInfoBlockBody; +}; +static_assert(sizeof(AdvancedWaveSoundFileReader) == 0x8); +} // namespace nn::atk::detail \ No newline at end of file diff --git a/include/nn/atk/detail/atk_AdvancedWaveSoundPlayer.h b/include/nn/atk/detail/atk_AdvancedWaveSoundPlayer.h new file mode 100644 index 00000000..05444888 --- /dev/null +++ b/include/nn/atk/detail/atk_AdvancedWaveSoundPlayer.h @@ -0,0 +1,107 @@ +#pragma once + +#include +#include +#include +#include +#include + +namespace nn::atk::detail::driver { +class AdvancedWaveSoundPlayer : BasicSoundPlayer, SoundThread::PlayerCallback { +public: + struct ClipParam { + bool isPlayed; + Channel* pChannel; + }; + static_assert(sizeof(ClipParam) == 0x10); + + struct TrackParam { + constexpr static u32 ClipParamCountMax = 10; + + bool isPlayed; + ClipParam clipParam[ClipParamCountMax]; + }; + static_assert(sizeof(TrackParam) == 0xa8); + + struct TrackParamSet { + constexpr static u32 TrackParamCountMax = 4; + + bool isPlayed; + TrackParam trackParam[TrackParamCountMax]; + }; + static_assert(sizeof(TrackParamSet) == 0x2a8); + + struct PrepareParameter { + SoundArchive::AdvancedWaveSoundInfo advancedWaveSoundInfo; + UpdateType updateType; +#if NN_SDK_VER < NN_MAKE_VER(4, 0, 0) + s32 subMixIndex; +#endif + void* pAwsdFile; + void* pWarcFile; + }; +#if NN_SDK_VER < NN_MAKE_VER(4, 0, 0) + static_assert(sizeof(PrepareParameter) == 0x20); +#else + static_assert(sizeof(PrepareParameter) == 0x18); +#endif + + AdvancedWaveSoundPlayer(); + ~AdvancedWaveSoundPlayer() override; + +#if NN_SDK_VER < NN_MAKE_VER(4, 0, 0) + bool Initialize() override; +#else + bool Initialize(OutputReceiver* pOutputReceiver) override; +#endif + void Finalize() override; + + void TearDownPlayer(); + void ReleaseTracks(); + + void Start() override; + void Stop() override; + void Pause(bool isPauseEnabled) override; + + void Prepare(const PrepareParameter& parameter); + + void SetupPlayer(); + bool SetupTracks(); + + void Update(); + bool UpdateTracks(); + + void InitializeTrackParams(); + + bool StartClip(ClipParam* pClipParam, + SoundArchive::AdvancedWaveSoundInfo* pWaveSoundClipInfo); + bool UpdateClip(ClipParam* pClipParam, + SoundArchive::AdvancedWaveSoundInfo* pWaveSoundClipInfo); + void ReleaseClip(ClipParam* pClipParam); + void StopClip(ClipParam* pClipParam); + + void OnUpdateFrameSoundThread() override; + void OnUpdateFrameSoundThreadWithAudioFrameFrequency() override; + void OnShutdownSoundThread() override; + +private: + SoundArchive::AdvancedWaveSoundInfo m_AdvancedWaveSoundInfo; + AdvancedWaveSoundTrackInfoSet m_AdvancedWaveSoundTrackInfoSet; + TrackParamSet m_TrackParamSet; + void* m_pAwsdFile; + void* m_pWarcFile; + UpdateType m_UpdateType; +#if NN_SDK_VER < NN_MAKE_VER(4, 0, 0) + s32 m_SubMixIndex; +#endif + u32 m_CurrentTime; + bool m_IsPrepared; + bool m_IsInitialized; + bool m_IsRegisterPlayerCallback; +}; +#if NN_SDK_VER < NN_MAKE_VER(4, 0, 0) +static_assert(sizeof(AdvancedWaveSoundPlayer) == 0x768); +#else +static_assert(sizeof(AdvancedWaveSoundPlayer) == 0x778); +#endif +} // namespace nn::atk::detail::driver \ No newline at end of file diff --git a/include/nn/atk/detail/atk_AdvancedWaveSoundRuntime.h b/include/nn/atk/detail/atk_AdvancedWaveSoundRuntime.h new file mode 100644 index 00000000..a77b23ab --- /dev/null +++ b/include/nn/atk/detail/atk_AdvancedWaveSoundRuntime.h @@ -0,0 +1,56 @@ +#pragma once + +#include + +#include +#include +#include +#include +#include + +namespace nn::atk::detail { +class AdvancedWaveSoundRuntime { +public: + AdvancedWaveSoundRuntime(); + ~AdvancedWaveSoundRuntime(); + + bool Initialize(s32 soundCount, void** pOutAllocatedAddr, const void* endAddr); + void Finalize(); + + static size_t GetRequiredMemorySize(const SoundArchive::SoundArchivePlayerInfo& soundArchivePlayerInfo, + size_t alignmentSize); + + s32 GetActiveCount() const; + s32 GetFreeAdvancedWaveSoundCount() const; + + void SetupUserParam(void** startAddr, size_t adjustSize); + + void Update(); + +#if NN_SDK_VER < NN_MAKE_VER(4, 0, 0) + AdvancedWaveSound* AllocSound(SoundArchive::ItemId soundId, s32 priority, + s32 ambientPriority, BasicSound::AmbientInfo* ambientArgInfo); +#else + AdvancedWaveSound* AllocSound(SoundArchive::ItemId soundId, s32 priority, + s32 ambientPriority, BasicSound::AmbientInfo* ambientArgInfo, + OutputReceiver* pOutputReceiver); +#endif + + SoundStartable::StartResult PrepareImpl(const SoundArchive* pSoundArchive, + const SoundDataManager* pSoundDataManager, + SoundArchive::ItemId soundId, + AdvancedWaveSound* sound, + const SoundArchive::SoundInfo* commonInfo, + const StartInfoReader& startInfoReader); + + void DumpMemory(const SoundArchive*) const; + +private: + AdvancedWaveSoundInstanceManager m_InstanceManager; +}; +#if NN_SDK_VER < NN_MAKE_VER(4, 0, 0) +static_assert(sizeof(AdvancedWaveSoundRuntime) == 0x30); +#else +static_assert(sizeof(AdvancedWaveSoundRuntime) == 0x38); +#endif +} // namespace nn::atk::detail diff --git a/include/nn/atk/detail/atk_BinaryTypes.h b/include/nn/atk/detail/atk_BinaryTypes.h new file mode 100644 index 00000000..5ce66cec --- /dev/null +++ b/include/nn/atk/detail/atk_BinaryTypes.h @@ -0,0 +1,25 @@ +#pragma once + +#include +#include + +namespace nn::atk::detail { +struct BinaryTypes { + NN_NO_COPY(BinaryTypes); + NN_NO_MOVE(BinaryTypes); + + template + struct Table { + CountType count; + T item[1]; + }; + + struct Reference { + u32 offset; + }; + static_assert(sizeof(Reference) == 0x4); + + struct ReferenceTable : Table {}; +}; + +}; \ No newline at end of file diff --git a/include/nn/atk/detail/atk_IRegionInfoReadable.h b/include/nn/atk/detail/atk_IRegionInfoReadable.h new file mode 100644 index 00000000..676eb144 --- /dev/null +++ b/include/nn/atk/detail/atk_IRegionInfoReadable.h @@ -0,0 +1,14 @@ +#pragma once + +#include + +namespace nn::atk::detail { +class IRegionInfoReadable { +public: + virtual ~IRegionInfoReadable() = default; + + virtual bool ReadRegionInfo(StreamSoundFile::RegionInfo* pInfo, + u32 regionIndex) const = 0; +}; +static_assert(sizeof(IRegionInfoReadable) == 0x8); +} // namespace nn::atk::detail \ No newline at end of file diff --git a/include/nn/atk/detail/atk_IStreamDataDecoder.h b/include/nn/atk/detail/atk_IStreamDataDecoder.h new file mode 100644 index 00000000..62c713b3 --- /dev/null +++ b/include/nn/atk/detail/atk_IStreamDataDecoder.h @@ -0,0 +1,47 @@ +#pragma once + +#include + +#include + +namespace nn::atk::detail { +namespace driver { +class StreamSoundPlayer; +} // namespace nn::atk::detail::driver + +class IStreamDataDecoder { +public: + enum DecodeType { + DecodeType_Normal, + DecodeType_Loop, + DecodeType_Idling, + DecodeType_Count + }; + + struct DataInfo { + s32 channelCount; + s32 sampleRate; + s32 blockSampleCount; + size_t blockSize; + }; + static_assert(sizeof(DataInfo) == 0x18); + + struct DecodeProfile { + os::Tick decodeTick; + s32 decodedSampleCount; + os::Tick fsAccessTick; + size_t fsReadSize; + }; + static_assert(sizeof(DecodeProfile) == 0x20); + + struct CacheProfile { + position_t cacheStartPosition; + size_t cachedLength; + position_t cacheCurrentPosition; + driver::StreamSoundPlayer* player; + }; + static_assert(sizeof(CacheProfile) == 0x20); + + virtual ~IStreamDataDecoder(); +}; +} // namespace nn::atk::detail \ No newline at end of file diff --git a/include/nn/atk/detail/atk_IntrusiveList.h b/include/nn/atk/detail/atk_IntrusiveList.h new file mode 100644 index 00000000..def3e50e --- /dev/null +++ b/include/nn/atk/detail/atk_IntrusiveList.h @@ -0,0 +1,18 @@ +#pragma once + +#include + +namespace nn::atk::detail { +using IntrusiveListNode = util::IntrusiveListNode; + +template +class IntrusiveList { +public: + using ElementList = util::IntrusiveList>; + using Iterator = typename ElementList::iterator; + using ConstIterator = typename ElementList::const_iterator; + +private: + ElementList m_ListImpl; +}; +} // namespace nn::atk::detail \ No newline at end of file diff --git a/include/nn/atk/detail/atk_MemoryFileStream.h b/include/nn/atk/detail/atk_MemoryFileStream.h new file mode 100644 index 00000000..dda5b155 --- /dev/null +++ b/include/nn/atk/detail/atk_MemoryFileStream.h @@ -0,0 +1,57 @@ +#pragma once + +#include +#include + +namespace nn::atk::detail { + +class MemoryFileStream : public fnd::FileStream { +public: + MemoryFileStream(const void* buffer, size_t size); + ~MemoryFileStream() override = default; + + fnd::FndResult Open(const char* filePath, AccessMode openMode) override; + void Close() override; + void Flush() override {} + + bool IsOpened() const override { return m_pBuffer != nullptr; } + + bool CanRead() const override { return true; } + bool CanWrite() const override { return false; } + bool CanSeek() const override { return true; } + + size_t GetSize() const override { return m_Size; } + + size_t Read(void* buf, size_t length, fnd::FndResult* result) override; + + size_t Write([[maybe_unused]] const void* buf, [[maybe_unused]] size_t length, + [[maybe_unused]] fnd::FndResult* result) override { + return 0; + } + + fnd::FndResult Seek(position_t offset, fnd::Stream::SeekOrigin origin) override; + + position_t GetCurrentPosition() const override { return m_Position; } + + void EnableCache([[maybe_unused]] void* buffer, [[maybe_unused]] size_t length) override {} + void DisableCache() override {} + bool IsCacheEnabled() const override { return false; } + + int GetIoBufferAlignment() const override { return alignof(char); } + + bool CanSetFsAccessLog() const override { return false; } + void* SetFsAccessLog([[maybe_unused]] fnd::FsAccessLog* pFsAccessLog) override { + return nullptr; + } + + position_t GetCachePosition() override { return 0; } + size_t GetCachedLength() override { return 0; } + +private: + const void* m_pBuffer; + size_t m_Size; + position_t m_Position; +}; +static_assert(sizeof(MemoryFileStream) == 0x20); + +} // namespace nn::atk::detail \ No newline at end of file diff --git a/include/nn/atk/detail/atk_RegionManager.h b/include/nn/atk/detail/atk_RegionManager.h new file mode 100644 index 00000000..f4410342 --- /dev/null +++ b/include/nn/atk/detail/atk_RegionManager.h @@ -0,0 +1,84 @@ +#pragma once + +#include + +#include +#include +#include + +namespace nn::atk { +enum StreamRegionCallbackResult { + StreamRegionCallbackResult_Finish, + StreamRegionCallbackResult_Continue, +}; + +struct StreamRegionCallbackParam { + s32 regionNo; + char regionName[64]; + bool isRegionNameEnabled; + s32 regionCount; + detail::IRegionInfoReadable* pRegionInfoReader; +}; +static_assert(sizeof(StreamRegionCallbackParam) == 0x58); + +using StreamRegionCallback = StreamRegionCallbackResult(*)(StreamRegionCallbackParam*,void*); + +namespace detail { +struct StreamDataInfoDetail; + +class RegionManager { +public: + struct Region { + position_t current; + position_t begin; + position_t end; +#if NN_SDK_VER >= NN_MAKE_VER(4, 0, 0) + bool isEnabled; +#endif + }; + + void Initialize(); + bool InitializeRegion(IRegionInfoReadable* pRegionReader, + StreamDataInfoDetail* pStreamDataInfo); + + bool IsPreparedForRegionJump() const; + + bool ChangeRegion(s32 currentRegionNo, IRegionInfoReadable* pRegionReader, + StreamDataInfoDetail* pStreamDataInfo); + + bool SetRegionInfo(const StreamSoundFile::RegionInfo* pRegionInfo, + const StreamDataInfoDetail* pStreamDataInfo); + + bool TryMoveNextRegion(IRegionInfoReadable* pRegionReader, + StreamDataInfoDetail* pStreamDataInfo); + + void SetPosition(position_t position); + void AddPosition(position_t position); + + bool IsInFirstRegion() const; + +private: + bool m_IsRegionInfoEnabled; + bool m_IsRegionIndexCheckEnabled; + bool m_IsRegionInitialized; + bool m_IsCurrentRegionNameEnabled; + StreamRegionCallback m_StreamRegionCallbackFunc; + void* m_StreamRegionCallbackArg; + s32 m_CurrentRegionNo; +#if NN_SDK_VER >= NN_MAKE_VER(4, 0, 0) + char* m_pCurrentRegionName; +#endif + Region m_CurrentRegion; + position_t m_AdpcmContextForStartOffsetFrame; + AdpcmContext m_AdpcmContextForStartOffset[16]; +#if NN_SDK_VER >= NN_MAKE_VER(4, 0, 0) + char m_CurrentRegionName[64]; +#endif +}; +#if NN_SDK_VER < NN_MAKE_VER(4, 0, 0) +static_assert(sizeof(RegionManager) == 0x440); +#else +static_assert(sizeof(RegionManager) == 0x4c0); +#endif +} // namespace nn::atk::detail +} // namespace nn::atk \ No newline at end of file diff --git a/include/nn/atk/detail/atk_SoundArchiveManager.h b/include/nn/atk/detail/atk_SoundArchiveManager.h new file mode 100644 index 00000000..58b31c38 --- /dev/null +++ b/include/nn/atk/detail/atk_SoundArchiveManager.h @@ -0,0 +1,53 @@ +#pragma once + +#include +#include +#include +#include + +namespace nn::atk::detail { +class SoundArchiveManager { +public: + using ContainerList = IntrusiveList; + + class SnapShot { + public: + private: + SoundArchive* m_MainSoundArchive; + SoundDataManager* m_MainSoundDataManager; + SoundArchive* m_CurrentSoundArchive; + SoundDataManager* m_CurrentSoundDataManager; + }; + + SoundArchiveManager(); + ~SoundArchiveManager(); + + void Initialize(const SoundArchive* pSoundArchive, const SoundDataManager* pSoundDataManager); + + void ChangeTargetArchive(const char* soundArchiveName); + + void Finalize(); + + void Add(AddonSoundArchiveContainer&); + void Remove(AddonSoundArchiveContainer&); + + bool IsAvailable() const; + + AddonSoundArchive* GetAddonSoundArchive(const char*) const; + SoundDataManager* GetAddonSoundDataManager(const char*) const; + AddonSoundArchiveContainer* GetAddonSoundArchiveContainer(s32) const; + AddonSoundArchiveContainer* GetAddonSoundArchiveContainer(s32); + + void SetParametersHook(SoundArchiveParametersHook*); + SoundArchiveParametersHook* GetParametersHook() const; + +private: + SoundArchive* m_pMainSoundArchive; + SoundDataManager* m_pMainSoundDataManager; + ContainerList m_ContainerList; + SoundArchive* m_pCurrentSoundArchive; + SoundDataManager* m_pCurrentSoundDataManager; + SoundArchiveParametersHook* m_pParametersHook; +}; +static_assert(sizeof(SoundArchiveManager) == 0x38); +} // namespace nn::atk::detail diff --git a/include/nn/atk/fnd/basis/atkfnd_Config.h b/include/nn/atk/fnd/basis/atkfnd_Config.h new file mode 100644 index 00000000..f805308d --- /dev/null +++ b/include/nn/atk/fnd/basis/atkfnd_Config.h @@ -0,0 +1,7 @@ +#pragma once + +#include + +namespace nn::atk::detail::fnd { +using position_t = std::ptrdiff_t; +} // namespace nn::atk::detail::fnd \ No newline at end of file diff --git a/include/nn/atk/fnd/basis/atkfnd_FrameHeapImpl.h b/include/nn/atk/fnd/basis/atkfnd_FrameHeapImpl.h new file mode 100644 index 00000000..a621df9d --- /dev/null +++ b/include/nn/atk/fnd/basis/atkfnd_FrameHeapImpl.h @@ -0,0 +1,52 @@ +#pragma once + +#include + +namespace nn::atk::detail::fnd { +class FrameHeapImpl : public HeapBase { +public: + static const int FreeHeadMode = 1; + static const int FreeTailMode = 2; + static const int FreeAllMode = 3; + + struct HeapState { + u32 tagName; + void* headAllocator; + void* tailAllocator; + HeapState* pPrevState; + + HeapState(); + }; + static_assert(sizeof(HeapState) == 0x20); + + FrameHeapImpl(); + + static FrameHeapImpl* Create(void* startAddress, size_t size, u16 optFlag); + + void* Destroy(); + void* Alloc(size_t size, int alignment); + size_t ResizeForMBlock(void* memBlock, size_t newSize); + size_t GetAllocatableSize(int alignment); + + void Free(int mode); + + bool RecordState(u32 tagName); + bool FreeByState(u32 tagName); + + u32 Adjust(); + +private: + bool IsValid(); + + void* AllocFromHead(size_t size, int alignment); + void* AllocFromTail(size_t size, int alignment); + + void FreeHead(); + void FreeTail(); + + void* m_pHeadAllocator; + void* m_pTailAllocator; + HeapState* m_pState; +}; +static_assert(sizeof(FrameHeapImpl) == 0x58); +} // namespace nn::atk::detail::fnd \ No newline at end of file diff --git a/include/nn/atk/fnd/basis/atkfnd_HeapBase.h b/include/nn/atk/fnd/basis/atkfnd_HeapBase.h new file mode 100644 index 00000000..4459adca --- /dev/null +++ b/include/nn/atk/fnd/basis/atkfnd_HeapBase.h @@ -0,0 +1,90 @@ +#pragma once + +#include +#include + +namespace nn::atk::detail::fnd { +class HeapBase : public util::IntrusiveListBaseNode { +public: + using HeapList = util::IntrusiveList>; + + enum HeapType { + HeapType_Exp, + HeapType_Frame, + HeapType_Unit, + HeapType_Unknown, + }; + + enum FillType { + FillType_NoUse, + FillType_Alloc, + FillType_Free, + FillType_Max, + }; + + static const int DefaultAlignment = 4; + + static const u32 ExpHeapSignature = 0x45585048; // HPXE + static const u32 FrameHeapSignature = 0x46524D48; // HMRF + static const u32 UnitHeapSignature = 0x554E5448; // HTNU + + static const int OptionZeroClear = 1 << 0; + static const int OptionDebugFill = 1 << 1; + static const int OptionThreadSafe = 1 << 2; + + static const int ErrorPrint = 1; + + static const int MIN_ALIGNMENT = DefaultAlignment; + + static HeapBase* FindContainHeap(const void* memBlock); + static HeapBase* FindParentHeap(const HeapBase* pChild); + + void* GetHeapStartAddress(); + void* GetHeapEndAddress(); + + size_t GetTotalSize(); + size_t GetTotalUsableSize(); + + u32 SetFillValue(FillType type, u32 val); + u32 GetFillValue(FillType type); + + HeapType GetHeapType(); + +protected: + void Initialize(u32 signature, void* heapStart, void* heapEnd, u16 optFlag); + void Finalize(); + + u32 GetSignature() const { + return m_Signature; + } + + void* GetHeapStart() const { + return mHeapStart; + } + + void* GetHeapEnd() const { + return mHeapEnd; + } + + void LockHeap(); + void UnlockHeap(); + + void FillFreeMemory(void* address, size_t size); + void FillNoUseMemory(void* address, size_t size); + void FillAllocMemory(void* address, size_t size); + +private: + static HeapBase* FindContainHeap(HeapList* pList, const void* memBlock); + static HeapList* FindListContainHeap(HeapBase* pHeapBase); + + u16 GetOptionFlag(); + void SetOptionFlag(u16 optFlag); + + void* mHeapStart; + void* mHeapEnd; + u32 m_Signature; + HeapList m_ChildList; + u32 m_Attribute; +}; +static_assert(sizeof(HeapBase) == 0x40); +} // namespace nn::atk::detail::fnd \ No newline at end of file diff --git a/include/nn/atk/fnd/basis/atkfnd_Inlines.h b/include/nn/atk/fnd/basis/atkfnd_Inlines.h new file mode 100644 index 00000000..dc181e80 --- /dev/null +++ b/include/nn/atk/fnd/basis/atkfnd_Inlines.h @@ -0,0 +1,51 @@ +#pragma once + +#include + +namespace nn::atk::detail::fnd { + +template +inline T Clamp(T x, T low, T high) { + if (x > high) + return high; + + if (x < low) + return low; + + return x; +} + +inline float FloatClamp(float value, float min, float max) { + if (value >= min) + return value < max ? value : max; + + return min; +} + +template +inline ValueT RoundUp(ValueT x, int base); + +// TODO: figure out if this is correct for +// void* RoundUp(void* x, int base) +template +inline ValueT* RoundUp(ValueT* x, int base); + +template +inline ValueT RoundDown(ValueT x, int base); + +template +inline ValueT* RoundDown(ValueT* x, int base); + +template +inline TDiff GetOffsetFromPtr(const void* start, const void* end) { + return reinterpret_cast(end) - reinterpret_cast(start); +} + +inline ptrdiff_t GetOffsetFromPtr(const void* start, const void* end) { + return reinterpret_cast(end) - reinterpret_cast(start); +} + +template +inline void* AddOffsetToPtr(void* ptr, TOffset offset); + +} // namespace nn::atk::detail::fnd \ No newline at end of file diff --git a/include/nn/atk/fnd/basis/atkfnd_Result.h b/include/nn/atk/fnd/basis/atkfnd_Result.h new file mode 100644 index 00000000..ec54833d --- /dev/null +++ b/include/nn/atk/fnd/basis/atkfnd_Result.h @@ -0,0 +1,76 @@ +#pragma once + +#include + +namespace nn::atk::detail::fnd { +enum FndResultType { + FndResultType_ErrorFlagMask = 1 << 31, + FndResultType_CategoryMask = 0b1111111 << 24, + FndResultType_CodeMask = 0xffffff, + + FndResultType_CategoryBitOffset = 24, + FndResultType_ErrorFlag = FndResultType_ErrorFlagMask, + + FndResultType_CategorySystem = 0, + FndResultType_CategoryIo = 1 << FndResultType_CategoryBitOffset, + + FndResultType_True = FndResultType_CategorySystem, + FndResultType_False, + + FndResultType_Failed = FndResultType_ErrorFlag | FndResultType_CategorySystem, + FndResultType_NotInitialized, + FndResultType_NotSupported, + FndResultType_NotOpened, + FndResultType_OutOfMemory, + FndResultType_InvalidArgument, + FndResultType_InvalidStatus, + + FndResultType_IoError = FndResultType_ErrorFlag | FndResultType_CategoryIo, + FndResultType_IoFileNotFound, + FndResultType_IoInvalidAccess, + FndResultType_IoTargetLocked, +}; + +class FndResult { +public: + FndResult() = default; + + explicit FndResult(u32 value) + : value{value} {}; + + explicit FndResult(FndResultType value) + : value(value) {}; + + bool IsSucceeded() const { + return !IsFailed(); + } + + bool IsTrue() const { + return value == FndResultType_True; + } + + bool IsFalse() const { + return value == FndResultType_False; + } + + bool IsFailed() const { + return value == static_cast(FndResultType_Failed); + } + + const char* ToString(); + + void PrintResult(); + + explicit operator u32() const { + return value; + } + + explicit operator FndResultType() const { + return FndResultType(value); + } +private: + u32 value; + +}; +static_assert(sizeof(FndResult) == 0x4); +} // namespace nn::atk::detail::fnd \ No newline at end of file diff --git a/include/nn/atk/fnd/basis/atkfnd_RuntimeTypeInfo.h b/include/nn/atk/fnd/basis/atkfnd_RuntimeTypeInfo.h new file mode 100644 index 00000000..5e5c2061 --- /dev/null +++ b/include/nn/atk/fnd/basis/atkfnd_RuntimeTypeInfo.h @@ -0,0 +1,10 @@ +#pragma once + +namespace nn::atk::detail::fnd { +class RuntimeTypeInfo { +public: +private: + RuntimeTypeInfo* m_ParentTypeInfo; +}; +static_assert(sizeof(RuntimeTypeInfo) == 0x8); +} // namespace nn::atk::detail::fnd \ No newline at end of file diff --git a/include/nn/atk/fnd/basis/atkfnd_Time.h b/include/nn/atk/fnd/basis/atkfnd_Time.h new file mode 100644 index 00000000..7cc2bbcc --- /dev/null +++ b/include/nn/atk/fnd/basis/atkfnd_Time.h @@ -0,0 +1,27 @@ +#pragma once + +#include + +namespace nn::atk::detail::fnd { +class Time { +public: + s64 Current(); +}; + +class TimeSpan { +public: + using TickType = s64; + + static TimeSpan FromNanoSeconds(TickType); + static TimeSpan FromMicroSeconds(TickType); + static TimeSpan FromMilliSeconds(TickType); + + TickType ToNanoSeconds() const; + TickType ToMicroSeconds() const; + TickType ToMilliSeconds() const; + +private: + TickType m_TickSpan; +}; +static_assert(sizeof(TimeSpan) == 0x8); +} // namespace nn::atk::detail::fnd \ No newline at end of file diff --git a/include/nn/atk/fnd/basis/atkfnd_WorkBufferAllocator.h b/include/nn/atk/fnd/basis/atkfnd_WorkBufferAllocator.h new file mode 100644 index 00000000..49239c6a --- /dev/null +++ b/include/nn/atk/fnd/basis/atkfnd_WorkBufferAllocator.h @@ -0,0 +1,19 @@ +#pragma once + +#include + +namespace nn::atk::detail::fnd { +class WorkBufferAllocator { +public: + WorkBufferAllocator(void* buffer, size_t size); + + void* Allocate(size_t size, size_t alignment); + void* Allocate(size_t size, size_t alignment, s32 count); + +private: + std::uintptr_t m_Buffer; + size_t m_Offset; + size_t m_Size; +}; +static_assert(sizeof(WorkBufferAllocator) == 0x18); +} // namespace nn::atk::detail::fnd \ No newline at end of file diff --git a/include/nn/atk/fnd/binary/atkfnd_PrimitiveTypes.h b/include/nn/atk/fnd/binary/atkfnd_PrimitiveTypes.h new file mode 100644 index 00000000..ab767f25 --- /dev/null +++ b/include/nn/atk/fnd/binary/atkfnd_PrimitiveTypes.h @@ -0,0 +1,8 @@ +#pragma once + +#include + +namespace nn::atk::detail::fnd { +using PcBinU16 = std::uint16_t; +using PcBinU32 = std::uint32_t; +} // namespace nn::atk::detail::fnd \ No newline at end of file diff --git a/include/nn/atk/fnd/io/atkfnd_FileStream.h b/include/nn/atk/fnd/io/atkfnd_FileStream.h new file mode 100644 index 00000000..3d46a490 --- /dev/null +++ b/include/nn/atk/fnd/io/atkfnd_FileStream.h @@ -0,0 +1,51 @@ +#pragma once + +#include +#include + +namespace nn::atk::detail::fnd { +class FileStream : public Stream { +public: + enum AccessMode { + AccessMode_None = 0, + AccessMode_Read = 1 << 0, + AccessMode_Write = 1 << 1, + AccessMode_AllowAppend = 1 << 2, + AccessMode_ReadAndWrite = AccessMode_Read | AccessMode_Write, + AccessMode_AllowAppendAndWrite = AccessMode_AllowAppend | AccessMode_Write, + }; + + ~FileStream() override = default; + + virtual FndResult Open(const char* filePath, AccessMode openMode) = 0; + void Close() override = 0; + virtual void Flush() = 0; + + bool IsOpened() const override = 0; + + bool CanRead() const override = 0; + bool CanWrite() const override = 0; + bool CanSeek() const override = 0; + + size_t GetSize() const override = 0; + + size_t Read(void* buf, size_t length, FndResult* result) override = 0; + size_t Write(const void* buf, size_t length, FndResult* result) override = 0; + FndResult Seek(position_t offset, SeekOrigin origin) override = 0; + + position_t GetCurrentPosition() const override = 0; + + virtual void EnableCache(void* buffer, size_t length) = 0; + virtual void DisableCache() = 0; + virtual bool IsCacheEnabled() const = 0; + + virtual int GetIoBufferAlignment() const = 0; + + virtual bool CanSetFsAccessLog() const = 0; + virtual void* SetFsAccessLog(FsAccessLog* pFsAccessLog) = 0; + + virtual position_t GetCachePosition() = 0; + virtual size_t GetCachedLength() = 0; +}; +static_assert(sizeof(FileStream) == 0x8); +} // namespace nn::atk::detail::fnd \ No newline at end of file diff --git a/include/nn/atk/fnd/io/atkfnd_FileStreamImpl.h b/include/nn/atk/fnd/io/atkfnd_FileStreamImpl.h new file mode 100644 index 00000000..75cf5152 --- /dev/null +++ b/include/nn/atk/fnd/io/atkfnd_FileStreamImpl.h @@ -0,0 +1,104 @@ +#pragma once + +#include + +#include + +namespace nn::atk::detail::fnd { +class FileStreamImpl : public FileStream { + NN_NO_COPY(FileStreamImpl); + +public: + class DirectStream : public Stream { + public: + DirectStream() = default; + + void Initialize(FileStreamImpl& fileStream); + + void Close() override {} + + bool IsOpened() const override { return m_Owner->IsOpened(); } + + bool CanRead() const override { return m_Owner->CanRead(); } + + bool CanWrite() const override { return m_Owner->CanWrite(); } + + bool CanSeek() const override { return m_Owner->CanSeek(); } + + size_t GetSize() const override { return m_Owner->GetSize(); } + + size_t Read(void* buf, size_t length, FndResult* result) override { + return m_Owner->ReadDirect(buf, length, result); + } + + size_t Write(const void* buf, size_t length, FndResult* result) override { + return m_Owner->WriteDirect(buf, length, result); + } + + FndResult Seek(position_t offset, SeekOrigin origin) override { + return m_Owner->SeekDirect(offset, origin); + } + + position_t GetCurrentPosition() const override { return m_Owner->m_CurrentPosition; } + + ~DirectStream() override = default; + + private: + friend FileStreamImpl; + + FileStreamImpl* m_Owner; + }; + static_assert(sizeof(DirectStream) == 0x10); + + FileStreamImpl(); + explicit FileStreamImpl(void*); + ~FileStreamImpl() override = default; + + FndResult Open(const char* filePath, AccessMode accessMode) override; + void Close() override; + void Flush() override; + + bool IsOpened() const override; + + bool CanRead() const override; + bool CanWrite() const override; + bool CanSeek() const override; + + size_t GetSize() const override; + + size_t Read(void* buf, size_t length, FndResult* result) override; + size_t Write(const void* buf, size_t length, FndResult* result) override; + FndResult Seek(position_t offset, SeekOrigin origin) override; + + position_t GetCurrentPosition() const override { return m_CurrentPosition; } + + void EnableCache(void* buffer, size_t length) override; + void DisableCache() override; + bool IsCacheEnabled() const override; + + int GetIoBufferAlignment() const override; + + bool CanSetFsAccessLog() const override; + void* SetFsAccessLog(FsAccessLog* pFsAccessLog) override; + + position_t GetCachePosition() override; + size_t GetCachedLength() override; + +private: + size_t ReadDirect(void* buf, size_t length, FndResult* result); + size_t WriteDirect(const void* buf, size_t length, FndResult* result); + FndResult SeekDirect(position_t offset, SeekOrigin origin); + + void ValidateAlignment([[maybe_unused]] const void* buf) const; + + fs::FileHandle m_FileHandle{0}; + bool m_IsOpened{false}; + u8 m_Padding[3]; + size_t m_FileSize; + position_t m_CurrentPosition{0}; + StreamCache m_StreamCache; + DirectStream m_DirectStream; + FsAccessLog* m_pAccessLog{nullptr}; +}; +static_assert(sizeof(FileStreamImpl) == 0x80); +} // namespace nn::atk::detail::fnd \ No newline at end of file diff --git a/include/nn/atk/fnd/io/atkfnd_FileStreamProxy.h b/include/nn/atk/fnd/io/atkfnd_FileStreamProxy.h new file mode 100644 index 00000000..cfb7afb9 --- /dev/null +++ b/include/nn/atk/fnd/io/atkfnd_FileStreamProxy.h @@ -0,0 +1,48 @@ +#pragma once + +#include + +namespace nn::atk::detail::fnd { +class FileStreamProxy : public FileStream { +public: + FileStreamProxy(FileStream* fileStream, position_t offset, size_t fileSize); + ~FileStreamProxy() override; + + FndResult Open(const char* filePath, AccessMode openMode) override; + void Close() override; + void Flush() override; + + bool IsOpened() const override; + + bool CanRead() const override; + bool CanWrite() const override; + bool CanSeek() const override; + + size_t GetSize() const override; + + size_t Read(void* buffer, size_t length, FndResult* result) override; + size_t Write(const void* buffer, size_t length, FndResult* result) override; + FndResult Seek(position_t offset, fnd::Stream::SeekOrigin origin) override; + + position_t GetCurrentPosition() const override; + + void EnableCache(void* buffer, size_t length) override; + void DisableCache() override; + bool IsCacheEnabled() const override; + + int GetIoBufferAlignment() const override; + + bool CanSetFsAccessLog() const override; + + void* SetFsAccessLog(FsAccessLog* pFsAccessLog) override; + + position_t GetCachePosition() override; + size_t GetCachedLength() override; + +private: + FileStream* m_pFileStream; + position_t m_Offset; + size_t m_FileSize; +}; +static_assert(sizeof(FileStreamProxy) == 0x20); +} // namespace nn::atk::detail::fnd \ No newline at end of file diff --git a/include/nn/atk/fnd/io/atkfnd_Stream.h b/include/nn/atk/fnd/io/atkfnd_Stream.h new file mode 100644 index 00000000..7d92f007 --- /dev/null +++ b/include/nn/atk/fnd/io/atkfnd_Stream.h @@ -0,0 +1,43 @@ +#pragma once + +#include +#include + +#include +#include + +namespace nn::atk::detail::fnd { +class Stream { + NN_NO_COPY(Stream); +public: + static const position_t InvalidPosition = 0xffffffff; + static const size_t InvalidSize = 0xffffffff; + + enum SeekOrigin { + SeekOrigin_Begin, + SeekOrigin_End, + SeekOrigin_Current, + }; + + virtual ~Stream() = default; + +protected: + Stream() = default; + +public: + virtual void Close() = 0; + virtual bool IsOpened() const = 0; + + virtual size_t Read(void* buf, size_t length, FndResult* result); + virtual size_t Write(const void* buf, size_t length, FndResult* result); + virtual FndResult Seek(position_t offset, SeekOrigin origin); + + virtual position_t GetCurrentPosition() const; + virtual size_t GetSize() const; + + virtual bool CanRead() const; + virtual bool CanWrite() const; + virtual bool CanSeek() const; +}; +static_assert(sizeof(Stream) == 0x8); +} // namespace nn::atk::detail::fnd \ No newline at end of file diff --git a/include/nn/atk/fnd/io/atkfnd_StreamCache.h b/include/nn/atk/fnd/io/atkfnd_StreamCache.h new file mode 100644 index 00000000..bae12330 --- /dev/null +++ b/include/nn/atk/fnd/io/atkfnd_StreamCache.h @@ -0,0 +1,51 @@ +#pragma once + +#include + +namespace nn::atk::detail::fnd { +struct FsAccessLog; + +class StreamCache { +public: + static const position_t InvalidPosition = -1; + + StreamCache(); + StreamCache(Stream* sourceStream, void* buffer, size_t length); + + bool IsInitialized() const { + return m_Stream != nullptr && m_CacheBuffer != nullptr && m_CacheBufferLength != 0; + } + + void Initialize(Stream* sourceStream, void* buffer, size_t length); + void Finalize(); + + size_t Read(void* buf, size_t length, FndResult* result, FsAccessLog* log, void* pFileStream); + + void FlushWriteCache(); + + size_t GetReadCacheHitLength(size_t) const; + + FndResult SyncStreamCurrentPosition(position_t position); + + void ClearCache(); + + size_t Write(const void* buf, size_t length, FndResult* result); + + size_t GetWritableCacheLength(size_t) const; + + FndResult Seek(position_t offset, Stream::SeekOrigin origin); + + virtual ~StreamCache() = default; + +private: + Stream* m_Stream{}; + position_t m_CurrentPosition{0}; + void* m_CacheBuffer{}; + size_t m_CacheBufferLength{0}; + position_t m_CachePosition{-1}; + size_t m_CachedLength{0}; + u8 m_CacheState{0}; + u8 m_Padding[3]; +}; +static_assert(sizeof(StreamCache) == 0x40); +} // namespace nn::atk::detail::fnd \ No newline at end of file diff --git a/include/nn/atk/fnd/os/atkfnd_CriticalSection.h b/include/nn/atk/fnd/os/atkfnd_CriticalSection.h new file mode 100644 index 00000000..90d98a71 --- /dev/null +++ b/include/nn/atk/fnd/os/atkfnd_CriticalSection.h @@ -0,0 +1,42 @@ +#pragma once + +#include + +namespace nn::atk::detail::fnd { +class CriticalSection { +public: + CriticalSection() + : m_Mutex(true) {} + + ~CriticalSection() = default; + + void Enter() { + // TODO + } + + bool TryEnter() { + // TODO + return true; + } + + void Leave() { + // TODO + } + + void Lock() { + m_Mutex.Lock(); + } + + bool TryLock() { + return m_Mutex.TryLock(); + } + + void Unlock() { + m_Mutex.Unlock(); + } + +private: + os::Mutex m_Mutex; +}; +static_assert(sizeof(CriticalSection) == 0x20); +} // namespace nn::atk::detail::fnd \ No newline at end of file diff --git a/include/nn/atk/fnd/os/atkfnd_ScopedLock.h b/include/nn/atk/fnd/os/atkfnd_ScopedLock.h new file mode 100644 index 00000000..4ac0e72a --- /dev/null +++ b/include/nn/atk/fnd/os/atkfnd_ScopedLock.h @@ -0,0 +1,25 @@ +#pragma once + +#include + +namespace nn::atk::detail::fnd { +template +class ScopedLock { + NN_NO_COPY(ScopedLock); + +public: + explicit ScopedLock(TLockObject& lockObj) + : m_LockObj(lockObj) + { + m_LockObj.Lock(); + } + + ~ScopedLock() + { + m_LockObj.Unlock(); + } + +private: + TLockObject& m_LockObj; +}; +} // namespace nn::atk::detail::fnd \ No newline at end of file diff --git a/include/nn/atk/fnd/os/atkfnd_Thread.h b/include/nn/atk/fnd/os/atkfnd_Thread.h new file mode 100644 index 00000000..3c66da74 --- /dev/null +++ b/include/nn/atk/fnd/os/atkfnd_Thread.h @@ -0,0 +1,141 @@ +#pragma once + +#include +#include + +namespace nn::atk::detail::fnd { +class TimeSpan; + +class Thread { +public: + enum AffinityMask { + AffinityMask_CoreAll = -1, + AffinityMask_CoreDefault, + AffinityMask_Core0 = 1 << 0, + AffinityMask_Core1 = 1 << 1, + AffinityMask_Core2 = 1 << 2, + AffinityMask_Core3 = 1 << 3, + AffinityMask_Core4 = 1 << 4, + AffinityMask_Core5 = 1 << 5, + AffinityMask_Core6 = 1 << 6, + AffinityMask_Core7 = 1 << 7, + AffinityMask_Core8 = 1 << 8, + AffinityMask_Core9 = 1 << 9, + AffinityMask_Core10 = 1 << 10, + AffinityMask_Core11 = 1 << 11, + AffinityMask_Core12 = 1 << 12, + AffinityMask_Core13 = 1 << 13, + AffinityMask_Core14 = 1 << 14, + AffinityMask_Core15 = 1 << 15, + AffinityMask_Core16 = 1 << 16, + AffinityMask_Core17 = 1 << 17, + AffinityMask_Core18 = 1 << 18, + AffinityMask_Core19 = 1 << 19, + AffinityMask_Core20 = 1 << 20, + AffinityMask_Core21 = 1 << 21, + AffinityMask_Core22 = 1 << 22, + AffinityMask_Core23 = 1 << 23, + AffinityMask_Core24 = 1 << 24, + AffinityMask_Core25 = 1 << 25, + AffinityMask_Core26 = 1 << 26, + AffinityMask_Core27 = 1 << 27, + AffinityMask_Core28 = 1 << 28, + AffinityMask_Core29 = 1 << 29, + AffinityMask_Core30 = 1 << 30, + AffinityMask_Core31 = 1 << 31, + }; + + enum FsPriority { + FsPriority_RealTime, + FsPriority_Normal, + FsPriority_Low, + }; + + enum State { + State_NotRun, + State_Running, + State_Exited, + State_Released, + }; + + using Handle = os::ThreadType; + + constexpr static u64 InvalidId = 0xFFFFFFFF; + + constexpr static u32 DefaultThreadPriority = 16; + constexpr static u32 MinThreadPriority = 0; + constexpr static u32 MaxThreadPriority = 31; + + constexpr static u32 StackAlignment = 4096; + + class Handler { + public: + virtual ~Handler() = 0; + virtual u32 Run(void* param) = 0; + }; + static_assert(sizeof(Handler) == 0x8); + + struct RunArgs { + + RunArgs(); + + bool IsValid() const; + + char* name; + void* stack; + size_t stackSize; + s32 idealCoreNumber; + AffinityMask affinityMask; + s32 priority; + FsPriority fsPriority; + void* param; + Handler* handler; + }; + static_assert(sizeof(RunArgs) == 0x38); + + ~Thread(); + + bool Run(const RunArgs& args); + + void WaitForExit(); + + void Release(); + + void SetState(State state); + + s32 GetPriority() const; + State GetState() const; + + void OnRun(); + void OnExit(); + + Thread(); + + void SetPriority(s32 priority); + + static void Sleep(const TimeSpan& timeSpan); + + bool Create(const Handle& handle, s64& id, const RunArgs& args); + + void Detach(); + + void SetName(const char* name); + void SetAffinityMask(s32 idealCoreNumber, AffinityMask value); + + void Resume(); + void Join(); + + bool IsTerminated() const; + +private: + u32 m_State; + Handle m_Handle; + s64 m_Id; + s32 m_Priority; + FsPriority m_FsPriority; + void* m_Param; + Handler* m_Handler; + bool m_IsTerminated; +}; +static_assert(sizeof(Thread) == 0x1f0); +} // nn::atk::detail::fnd \ No newline at end of file diff --git a/include/nn/atk/fnd/string/atkfnd_String.h b/include/nn/atk/fnd/string/atkfnd_String.h new file mode 100644 index 00000000..50887c58 --- /dev/null +++ b/include/nn/atk/fnd/string/atkfnd_String.h @@ -0,0 +1,19 @@ +#pragma once + +#include + +#include + +namespace nn::atk::detail::fnd { +// UNSURE +inline size_t strncat(char* dst, size_t dstCount, const char* src, size_t srcCount) { + size_t length {strlen(src)}; + if (length + srcCount + 1 < dstCount) + length = srcCount + 1; + else + length = dstCount - 1 - length; + + std::strncat(dst, src, length); + return length; +} +} // namespace atk::detail::fnd \ No newline at end of file diff --git a/include/nn/audio.h b/include/nn/audio.h index ab140895..ba115227 100644 --- a/include/nn/audio.h +++ b/include/nn/audio.h @@ -6,151 +6,29 @@ #pragma once #include -#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include namespace nn { namespace audio { // Common audio -struct AudioDeviceName { - char raw_name[0x100]; -}; - -static_assert(sizeof(AudioDeviceName) == 0x100); - void AcquireAudioDeviceSwitchNotification(nn::os::SystemEvent* event); s32 ListAudioDeviceName(nn::audio::AudioDeviceName* buffer, s32 bufferCount); Result SetAudioDeviceOutputVolume(nn::audio::AudioDeviceName const* device, float volume); u32 GetActiveChannelCount(); -struct AudioRendererConfig { - u64* _0; - u64* _8; - u64* _10; - u64* _18; - u64* _20; - u64* _28; - u64* _30; - u64* _38; - u64* _40; - u64* _48; - u64* _50; -}; - -enum AudioRendererRenderingDevice : u32 { - AudioRendererRenderingDevice_Cpu, - AudioRendererRenderingDevice_Dsp -}; - -enum AudioRendererExecutionMode : u32 { - AudioRendererExecutionMode_Manual, - AudioRendererExecutionMode_Auto, -}; - -enum SampleFormat : u32 { - SampleFormat_Invalid, - SampleFormat_PcmInt8, - SampleFormat_PcmInt16, - SampleFormat_PcmInt24, - SampleFormat_PcmInt32, - SampleFormat_PcmFloat, - SampleFormat_Adpcm, -}; - -enum MemoryPoolState : u32 { - MemoryPoolState_Invalid, - MemoryPoolState_New, - MemoryPoolState_RequestDetach, - MemoryPoolState_Detached, - MemoryPoolState_RequestAttach, - MemoryPoolState_Attached, - MemoryPoolState_Released, -}; - -struct AudioRendererParameter { - u32 sampleRate; - u32 sampleCount; - u32 mixBufferCount; - u32 subMixCount; - u32 voiceCount; - u32 sinkCount; - u32 effectCount; - u32 performanceFrameCount; - bool isVoiceDropEnabled; - u32 splitterCount; - u32 splitterSendChannelCount; - AudioRendererRenderingDevice renderingDevice; - AudioRendererExecutionMode executionMode; - u32 _34; - u32 revision; -}; - -static_assert(sizeof(AudioRendererParameter) == 0x3C); - -struct BiquadFilterParameter { - bool enabled; - s16 numerator[3]; - s16 denominator[2]; -}; - -static_assert(sizeof(BiquadFilterParameter) == 0xC); - -struct WaveBuffer { - void* buffer; - size_t bufferSize; - s32 startSampleOffset; - s32 endSampleOffset; - bool shouldLoop; - bool isEndOfStream; - void* context; - size_t contextSize; -}; - -static_assert(sizeof(WaveBuffer) == 0x30); - -struct AudioRendererHandle { - u64* _0; - u64* _8; -}; - -struct MemoryPoolType { - u64* _0; -}; - -struct CircularBufferSinkType { - u64* _0; -}; - -struct AuxType { - u64* _0; -}; - -struct DelayType { - u64* _0; -}; - -struct FinalMixType { - u64* _0; -}; - -struct SubMixType { - u64* _0; -}; - -struct VoiceType { - u64* _0; - - enum PlayState : u32 { - PlayState_Start, - PlayState_Stop, - PlayState_Pause, - }; -}; - -struct DeviceSinkType { - struct DownMixParameter; - u64* _0; -}; - // Audio Renderer base APIs void InitializeAudioRendererParameter(nn::audio::AudioRendererParameter* inParameter); bool IsValidAudioRendererParameter(nn::audio::AudioRendererParameter const& inParameter); @@ -212,68 +90,6 @@ GetRequiredBufferSizeForPerformanceFrames(nn::audio::AudioRendererParameter cons void* SetPerformanceFrameBuffer(nn::audio::AudioRendererConfig* config, void* buffer, size_t bufferSize); -enum PerformanceEntryType : u8 { - PerformanceEntryType_Invalid, - PerformanceEntryType_Voice, - PerformanceEntryType_SubMix, - PerformanceEntryType_FinalMix, - PerformanceEntryType_Sink -}; - -struct PerformanceEntry { - s32 nodeId; - s32 startTime; - s32 processingTime; - PerformanceEntryType entryType; -}; - -static_assert(sizeof(PerformanceEntry) == 0x10); - -enum PerformanceDetailType : u8 { - PerformanceDetailType_Unknown, - PerformanceDetailType_PcmInt16, - PerformanceDetailType_Adpcm, - PerformanceDetailType_VolumeRamp, - PerformanceDetailType_BiquadFilter, - PerformanceDetailType_Mix, - PerformanceDetailType_Delay, - PerformanceDetailType_Aux, - PerformanceDetailType_Reverb, - PerformanceDetailType_Reverb3d, - PerformanceDetailType_PcmFloat -}; - -struct PerformanceDetail { - s32 nodeId; - s32 startTime; - s32 processingTime; - PerformanceDetailType detailType; - PerformanceEntryType entryType; -}; - -static_assert(sizeof(PerformanceDetail) == 0x10); - -class PerformanceInfo { -public: - PerformanceInfo(); - ~PerformanceInfo(); - - bool SetBuffer(void const* buffer, size_t bufferSize); - bool MoveToNextFrame(); - s32 GetTotalProcessingTime(); - PerformanceEntry GetEntries(s32* count); - PerformanceDetail GetDetails(s32* count); - -private: - void* buffer; - size_t bufferSize; - void* header; - PerformanceEntry* entries; - PerformanceDetail* details; -}; - -static_assert(sizeof(PerformanceInfo) == 0x28); - // Audio Renderer Sink APIs Result AddDeviceSink(nn::audio::AudioRendererConfig* config, nn::audio::DeviceSinkType* sink, nn::audio::FinalMixType* mix, s8 const* input, s32 inputCount, diff --git a/include/nn/audio/audio_Adpcm.h b/include/nn/audio/audio_Adpcm.h new file mode 100644 index 00000000..7597e060 --- /dev/null +++ b/include/nn/audio/audio_Adpcm.h @@ -0,0 +1,16 @@ +#pragma once + +#include + +namespace nn::audio { +struct AdpcmContext { + u16 predScale; + s16 history[2]; +}; +static_assert(sizeof(AdpcmContext) == 0x6); + +struct AdpcmParameter { + u16 coefficients[16] = {0}; +}; +static_assert(sizeof(AdpcmParameter) == 0x20); +} // namespace nn::audio \ No newline at end of file diff --git a/include/nn/audio/audio_AudioRendererTypes.h b/include/nn/audio/audio_AudioRendererTypes.h new file mode 100644 index 00000000..963390ea --- /dev/null +++ b/include/nn/audio/audio_AudioRendererTypes.h @@ -0,0 +1,65 @@ +#pragma once + +#include + +namespace nn::audio { +enum AudioRendererRenderingDevice { + AudioRendererRenderingDevice_AudioCoprocessor, + AudioRendererRenderingDevice_Cpu, +}; + +enum AudioRendererExecutionMode { + AudioRendererExecutionMode_AutoExecution, + AudioRendererExecutionMode_ManualExecution, +}; + +struct AudioRendererHandle { + void* _handle; + void* _context; +}; +static_assert(sizeof(AudioRendererHandle) == 0x10); + +struct AudioRendererParameter { + s32 sampleRate; + s32 sampleCount; + s32 mixBufferCount; + s32 subMixCount; + s32 voiceCount; + s32 sinkCount; + s32 effectCount; + s32 performanceFrameCount; + bool isVoiceDropEnabled; + s32 splitterCount; + s32 splitterSendChannelCount; + AudioRendererRenderingDevice renderingDevice; + AudioRendererExecutionMode executionMode; + u32 _magic; + + static const s32 MixBufferCountMax = 256; + static const s32 SubMixCountMax = 256; + static const s32 VoiceCountMax = 1024; + static const s32 SinkCountMax = 256; + static const s32 EffectCountMax = 256; + static const s32 PerformanceFrameCountMax = 4096; + static const s32 SplitterCountMax = 256; +}; +static_assert(sizeof(AudioRendererParameter) == 0x38); + +struct AudioRendererConfig { + VoiceInfoManager* _pVoiceInfoManager; + MixManager* _pMixManager; + EffectManager* _pEffectManager; + SinkManager* _pSinkManager; + PerformanceBufferManager* _pPerformanceBufferManager; + MemoryPoolManager* _pMemoryPoolManager; + BehaviorManager* _pBehaviorManager; + SplitterInfoManager* _pSplitterInfoManager; + void* _pInParameter; + size_t _pInParameterSize; + void* _pOutStatus; + size_t _pOutStatusSize; + void* _pConfigBuffer; + size_t _configBufferSize; +}; +static_assert(sizeof(AudioRendererConfig) == 0x70); +} // namespace nn::audio \ No newline at end of file diff --git a/include/nn/audio/audio_Common.h b/include/nn/audio/audio_Common.h new file mode 100644 index 00000000..8eb070a7 --- /dev/null +++ b/include/nn/audio/audio_Common.h @@ -0,0 +1,28 @@ +#pragma once + +#include + +namespace nn::audio { +using NodeId = uint32_t; +const int BufferAlignSize = 64; + +struct AudioDeviceName { + char raw_name[0x100]; +}; +static_assert(sizeof(AudioDeviceName) == 0x100); + +struct DelayType { + u64* _0; +}; + +struct MixInfo {}; + +class VoiceInfoManager {}; +class MixManager {}; +class EffectManager {}; +class SinkManager {}; +class PerformanceBufferManager {}; +class MemoryPoolManager {}; +class BehaviorManager {}; +class SplitterInfoManager {}; +} // namespace nn::audio \ No newline at end of file diff --git a/include/nn/audio/audio_EffectTypes.h b/include/nn/audio/audio_EffectTypes.h new file mode 100644 index 00000000..b648196d --- /dev/null +++ b/include/nn/audio/audio_EffectTypes.h @@ -0,0 +1,11 @@ +#pragma once + +#include + +namespace nn::audio { +struct EffectInfo {}; + +struct AuxType { + EffectInfo* _pEffectInfo; +}; +} // namespace nn::audio \ No newline at end of file diff --git a/include/nn/audio/audio_FinalMixTypes.h b/include/nn/audio/audio_FinalMixTypes.h new file mode 100644 index 00000000..80e097c9 --- /dev/null +++ b/include/nn/audio/audio_FinalMixTypes.h @@ -0,0 +1,12 @@ +#pragma once + +#include + +namespace nn::audio { +struct FinalMixType { + MixInfo* _pMixInfo; + + static float GetVolumeMax(); + static float GetVolumeMin(); +}; +} // namespace nn::audio \ No newline at end of file diff --git a/include/nn/audio/audio_MemoryPoolTypes.h b/include/nn/audio/audio_MemoryPoolTypes.h new file mode 100644 index 00000000..3165ea56 --- /dev/null +++ b/include/nn/audio/audio_MemoryPoolTypes.h @@ -0,0 +1,32 @@ +#pragma once + +#include + +namespace nn::audio { +enum MemoryPoolState { + MemoryPoolState_Invalid, + MemoryPoolState_New, + MemoryPoolState_RequestDetach, + MemoryPoolState_Detached, + MemoryPoolState_RequestAttach, + MemoryPoolState_Attached, + MemoryPoolState_Released, +}; + +struct MemoryPoolInfo {}; + +struct MemoryPoolType { + enum State { + State_RequestAttach, + State_Attached, + State_RequestDetach, + State_Detached, + }; + + static const size_t AddressAlignment {4096}; + static const size_t SizeGranularity {4096}; + + MemoryPoolInfo* _pMemoryPoolInfo; +}; +static_assert(sizeof(MemoryPoolType) == 0x8); +} // namespace nn::audio diff --git a/include/nn/audio/audio_PerformanceMetrics.h b/include/nn/audio/audio_PerformanceMetrics.h new file mode 100644 index 00000000..98dad4e4 --- /dev/null +++ b/include/nn/audio/audio_PerformanceMetrics.h @@ -0,0 +1,33 @@ +#pragma once + +#include + +#include + +namespace nn::audio { +class PerformanceInfo { +public: + PerformanceInfo& operator=(const PerformanceInfo&); + PerformanceInfo& operator=(PerformanceInfo&&); + + PerformanceInfo(); + ~PerformanceInfo(); + + bool SetBuffer(const void* buffer, size_t bufferSize); + + bool MoveToNextFrame(); + + s32 GetTotalProcessingTime(); + u32 GetFrameIndex(); + const PerformanceEntry* GetEntries(int*); + const PerformanceDetail* GetDetails(int*); + +private: + const void* m_Buffer; + size_t m_BufferSize; + PerformanceFrameHeader* m_Header; + PerformanceEntry* m_Entries; + PerformanceDetail* m_Details; +}; +static_assert(sizeof(PerformanceInfo) == 0x28); +} // namespace nn::audio \ No newline at end of file diff --git a/include/nn/audio/audio_PerformanceMetricsTypes.h b/include/nn/audio/audio_PerformanceMetricsTypes.h new file mode 100644 index 00000000..483a91f5 --- /dev/null +++ b/include/nn/audio/audio_PerformanceMetricsTypes.h @@ -0,0 +1,40 @@ +#pragma once + +#include + +namespace nn::audio { +enum PerformanceEntryType { + PerformanceEntryType_Unknown, + PerformanceEntryType_Voice, + PerformanceEntryType_SubMix, + PerformanceEntryType_FinalMix, + PerformanceEntryType_Sink, + PerformanceEntryType_Count +}; + +enum PerformanceDetailType : u8 { + PerformanceDetailType_Unknown, + PerformanceDetailType_PcmInt16, + PerformanceDetailType_Adpcm, + PerformanceDetailType_VolumeRamp, + PerformanceDetailType_BiquadFilter, + PerformanceDetailType_Mix, + PerformanceDetailType_Delay, + PerformanceDetailType_Aux, + PerformanceDetailType_Reverb, + PerformanceDetailType_Reverb3d, + PerformanceDetailType_PcmFloat +}; + +struct PerformanceEntry { + NodeId id; + s32 startTime; + s32 processingTime; + s8 entryType; + s8 _padding[3]; +}; +static_assert(sizeof(PerformanceEntry) == 0x10); + +struct PerformanceFrameHeader {}; +struct PerformanceDetail {}; +} // namespace nn::audio \ No newline at end of file diff --git a/include/nn/audio/audio_SampleFormat.h b/include/nn/audio/audio_SampleFormat.h new file mode 100644 index 00000000..4380b223 --- /dev/null +++ b/include/nn/audio/audio_SampleFormat.h @@ -0,0 +1,13 @@ +#pragma once + +namespace nn::audio { +enum SampleFormat { + SampleFormat_Invalid, + SampleFormat_PcmInt8, + SampleFormat_PcmInt16, + SampleFormat_PcmInt24, + SampleFormat_PcmInt32, + SampleFormat_PcmFloat, + SampleFormat_Adpcm, +}; +} // namespace nn::audio \ No newline at end of file diff --git a/include/nn/audio/audio_SinkTypes.h b/include/nn/audio/audio_SinkTypes.h new file mode 100644 index 00000000..d9775fff --- /dev/null +++ b/include/nn/audio/audio_SinkTypes.h @@ -0,0 +1,20 @@ +#pragma once + +namespace nn::audio { +struct SinkInfo {}; + +struct DeviceSinkType { + struct DownMixParameter { + float coeff[16]; + }; + static_assert(sizeof(DownMixParameter) == 0x40); + + SinkInfo* _handle; +}; +static_assert(sizeof(DeviceSinkType) == 0x8); + +struct CircularBufferSinkType { + SinkInfo* _handle; +}; +static_assert(sizeof(CircularBufferSinkType) == 0x8); +} // namespace nn::audio \ No newline at end of file diff --git a/include/nn/audio/audio_SubMixTypes.h b/include/nn/audio/audio_SubMixTypes.h new file mode 100644 index 00000000..ee9ee2c4 --- /dev/null +++ b/include/nn/audio/audio_SubMixTypes.h @@ -0,0 +1,12 @@ +#pragma once + +#include + +namespace nn::audio { +struct SubMixType { + MixInfo* _pMixInfo; + + static float GetVolumeMin(); + static float GetVolumeMax(); +}; +} // namespace nn::audio \ No newline at end of file diff --git a/include/nn/audio/audio_VoiceTypes.h b/include/nn/audio/audio_VoiceTypes.h new file mode 100644 index 00000000..b3a9abed --- /dev/null +++ b/include/nn/audio/audio_VoiceTypes.h @@ -0,0 +1,37 @@ +#pragma once + +#include + +namespace nn::audio { +struct VoiceInfo; + +struct BiquadFilterParameter { + bool enable; + s16 numerator[3]; + s16 denominator[2]; +}; +static_assert(sizeof(BiquadFilterParameter) == 0xc); + +struct VoiceType { + enum PlayState { + PlayState_Play, + PlayState_Stop, + PlayState_Pause, + }; + + static const int PriorityHighest = 0; + static const int PriorityLowest = 255; + static const int WaveBufferCountMax = 4; + static const int ChannelCountMax = 6; + + static float GetPitchMax(); + static float GetPitchMin(); + static float GetVolumeMax(); + static float GetVolumeMin(); + + static const int BiquadFilterCountMax = 2; + + VoiceInfo* _pVoiceInfo{}; +}; +static_assert(sizeof(VoiceType) == 0x8); +} // namespace nn::audio \ No newline at end of file diff --git a/include/nn/audio/audio_WaveBuffer.h b/include/nn/audio/audio_WaveBuffer.h new file mode 100644 index 00000000..aad32139 --- /dev/null +++ b/include/nn/audio/audio_WaveBuffer.h @@ -0,0 +1,17 @@ +#pragma once + +#include + +namespace nn::audio { +struct WaveBuffer { + const void* buffer; + size_t size; + s32 startSampleOffset; + s32 endSampleOffset; + bool loop; + bool isEndOfStream; + const void* pContext; + size_t contextSize; +}; +static_assert(sizeof(WaveBuffer) == 0x30); +} // namespace nn::audio \ No newline at end of file diff --git a/include/nn/os.h b/include/nn/os.h index 6f27d49f..0a7056e8 100644 --- a/include/nn/os.h +++ b/include/nn/os.h @@ -11,17 +11,17 @@ #include #include +#include #include #include -#include #include +#include namespace nn { namespace os { namespace detail { -class MultiWaitObjectList; struct InterProcessEventType { enum State { State_NotInitialized = 0, @@ -39,31 +39,10 @@ struct InterProcessEventType { }; } // namespace detail -struct Tick { - Tick(u64 val) : value(val) {} - - u64 value; -}; - struct LightEventType { std::aligned_storage_t<0xc, 4> storage; }; -struct EventType { - util::TypedStorage _multiWaitObjectList; - bool _signalState; - bool _initiallySignaled; - uint8_t _clearMode; - uint8_t _state; - uint32_t _broadcastCounterLower; - uint32_t _broadcastCounterUpper; - detail::InternalCriticalSectionStorage _csEvent; - detail::InternalConditionVariableStorage _cvSignaled; -}; -static_assert(std::is_trivial::value, "EventType non trivial"); -typedef EventType Event; - -enum EventClearMode { EventClearMode_ManualClear, EventClearMode_AutoClear }; struct ConditionVariableType {}; @@ -154,15 +133,6 @@ void SleepThread(nn::TimeSpan); void WaitThread(nn::os::ThreadType*); void SetThreadCoreMask(nn::os::ThreadType*, int, u64 mask); -// EVENTS -void InitializeEvent(EventType*, bool initiallySignaled, EventClearMode eventClearMode); -void FinalizeEvent(EventType*); -void SignalEvent(EventType*); -void WaitEvent(EventType*); -bool TryWaitEvent(EventType*); -bool TimedWaitEvent(EventType*, nn::TimeSpan); -void ClearEvent(EventType*); - // LIGHT EVENTS void InitializeLightEvent(LightEventType*, bool initiallySignaled, EventClearMode eventClearMode); void FinalizeLightEvent(LightEventType*); @@ -171,8 +141,6 @@ void WaitLightEvent(LightEventType*); bool TimedWaitLightEvent(LightEventType*, nn::TimeSpan); void ClearLightEvent(LightEventType*); -TimeSpan ConvertToTimeSpan(Tick ticks); - // SEMAPHORES void InitializeSemaphore(SemaphoreType* semaphore, s32 initial_count, s32 max_count); void FinalizeSemaphore(SemaphoreType* semaphore); @@ -219,8 +187,6 @@ void SetUserExceptionHandler(void (*)(UserExceptionInfo*), void*, ulong, UserExc // OTHER void GenerateRandomBytes(void*, u64); -nn::os::Tick GetSystemTick(); -nn::os::Tick GetSystemTickFrequency(); u64 GetThreadAvailableCoreMask(); void SetMemoryHeapSize(u64 size); diff --git a/include/nn/os/os_Event.h b/include/nn/os/os_Event.h new file mode 100644 index 00000000..b606dbee --- /dev/null +++ b/include/nn/os/os_Event.h @@ -0,0 +1,48 @@ +#pragma once + +#include +#include +#include +#include + +namespace nn::os { + +// todo: figure out where these go +void InitializeEvent(EventType*, bool initiallySignaled, EventClearMode eventClearMode); +void FinalizeEvent(EventType*); +void SignalEvent(EventType*); +void WaitEvent(EventType*); +bool TryWaitEvent(EventType*); +bool TimedWaitEvent(EventType*, nn::TimeSpan); +void ClearEvent(EventType*); + +class Event { + NN_NO_COPY(Event); + NN_NO_MOVE(Event); + +public: + explicit Event(EventClearMode clearMode) { InitializeEvent(&m_Event, false, clearMode); } + + ~Event() { FinalizeEvent(&m_Event); } + + void Wait() { WaitEvent(&m_Event); } + + bool TryWait() { return TryWaitEvent(&m_Event); } + + bool TimedWait(TimeSpan timeSpan) { return TimedWaitEvent(&m_Event, timeSpan); } + + void Signal() { SignalEvent(&m_Event); } + + void Clear() { ClearEvent(&m_Event); } + + operator EventType&() { return m_Event; } + + operator const EventType&() const { return m_Event; } + + EventType* GetBase() { return &m_Event; } + +private: + EventType m_Event; +}; + +} // namespace nn::os \ No newline at end of file diff --git a/include/nn/os/os_EventCommon.h b/include/nn/os/os_EventCommon.h new file mode 100644 index 00000000..1c75a20e --- /dev/null +++ b/include/nn/os/os_EventCommon.h @@ -0,0 +1,5 @@ +#pragma once + +namespace nn::os { +enum EventClearMode { EventClearMode_ManualClear, EventClearMode_AutoClear }; +} // namespace nn::os \ No newline at end of file diff --git a/include/nn/os/os_EventTypes.h b/include/nn/os/os_EventTypes.h new file mode 100644 index 00000000..497ded56 --- /dev/null +++ b/include/nn/os/os_EventTypes.h @@ -0,0 +1,26 @@ +#pragma once + +#include +#include +#include + +namespace nn::os { + +namespace detail { +class MultiWaitObjectList; +} // namespace detail + +struct EventType { + util::TypedStorage _multiWaitObjectList; + bool _signalState; + bool _initiallySignaled; + uint8_t _clearMode; + uint8_t _state; + uint32_t _broadcastCounterLower; + uint32_t _broadcastCounterUpper; + detail::InternalCriticalSectionStorage _csEvent; + detail::InternalConditionVariableStorage _cvSignaled; +}; +static_assert(std::is_trivial::value, "EventType non trivial"); + +} // namespace nn::os \ No newline at end of file diff --git a/include/nn/os/os_MessageQueue.h b/include/nn/os/os_MessageQueue.h new file mode 100644 index 00000000..22084b10 --- /dev/null +++ b/include/nn/os/os_MessageQueue.h @@ -0,0 +1,10 @@ +#pragma once + +#include + +namespace nn::os { +class MessageQueue { +public: + MessageQueueType m_MessageQueue; +}; +} // namespace nn::os \ No newline at end of file diff --git a/include/nn/os/os_Mutex.h b/include/nn/os/os_Mutex.h index 6656ce46..d091ca66 100644 --- a/include/nn/os/os_Mutex.h +++ b/include/nn/os/os_Mutex.h @@ -6,7 +6,7 @@ namespace nn::os { // todo: figure out where these go -void InitializeMutex(MutexType*, bool, s32); +void InitializeMutex(MutexType*, bool recursive, s32 lockLevel); void FinalizeMutex(MutexType*); void LockMutex(MutexType*); bool TryLockMutex(MutexType*); @@ -20,20 +20,29 @@ class Mutex { public: explicit Mutex(bool recursive) { InitializeMutex(&m_Mutex, recursive, 0); } - Mutex(bool, int); - ~Mutex(); + Mutex(bool recursive, s32 lockLevel) { InitializeMutex(&m_Mutex, recursive, lockLevel); }; + + ~Mutex() { FinalizeMutex(&m_Mutex); } void Lock() { LockMutex(&m_Mutex); } - bool TryLock(); + bool TryLock() { return TryLockMutex(&m_Mutex); } + void Unlock() { UnlockMutex(&m_Mutex); } - bool IsLockedByCurrentThread() const; - void lock(); - bool try_lock(); - void unlock(); - operator MutexType&(); - operator const MutexType&() const; - MutexType* GetBase(); + + bool IsLockedByCurrentThread() const { return IsMutexLockedByCurrentThread(&m_Mutex); }; + + void lock() { Lock(); } + + bool try_lock() { return TryLock(); } + + void unlock() { Unlock(); } + + operator MutexType&() { return m_Mutex; } + + operator const MutexType&() const { return m_Mutex; } + + MutexType* GetBase() { return &m_Mutex; } private: MutexType m_Mutex; diff --git a/include/nn/os/os_TickTypes.h b/include/nn/os/os_TickTypes.h new file mode 100644 index 00000000..b035e915 --- /dev/null +++ b/include/nn/os/os_TickTypes.h @@ -0,0 +1,48 @@ +#pragma once + +#include +#include + +namespace nn::os { + +class Tick; + +Tick GetSystemTick(); +Tick GetSystemTickFrequency(); +Tick ConvertToTick(TimeSpan timeSpan); +TimeSpan ConvertToTimeSpan(Tick ticks); + +class Tick { +public: + Tick(s64 tick) : value(tick) {} + + Tick(TimeSpan timeSpan) : value(ConvertToTick(timeSpan).GetInt64Value()) {} + + s64 GetInt64Value() const { return value; } + + TimeSpan ToTimeSpan() const { return ConvertToTimeSpan(*this); } + + Tick& operator-=(Tick rhs) { + *this = *this - rhs; + return *this; + } + + Tick operator-(Tick rhs) const { return value - rhs.value; } + + Tick& operator+=(Tick rhs) { + *this = *this + rhs; + return *this; + } + + Tick operator+(Tick rhs) const { return value + rhs.value; } + + s64 value; + +// TODO: DWARF shows value as private and named m_Tick. +// It's being kept as it was for now to keep sead +// from not compiling. Fix this before merging to master. +// private: +// s64 m_Tick; +}; + +} // namespace nn::os \ No newline at end of file diff --git a/include/nn/util/detail/util_TypeTraits.h b/include/nn/util/detail/util_TypeTraits.h index cc0aff28..c8abc4f4 100644 --- a/include/nn/util/detail/util_TypeTraits.h +++ b/include/nn/util/detail/util_TypeTraits.h @@ -7,6 +7,11 @@ namespace nn::util::detail { template struct SizedInt; +template <> +struct SizedInt<4> { + typedef uint32_t Unsigned; +}; + template <> struct SizedInt<8> { typedef uint64_t Unsigned; diff --git a/include/nn/util/util_BitUtil.h b/include/nn/util/util_BitUtil.h index 8fdf58c7..51dc2ed9 100644 --- a/include/nn/util/util_BitUtil.h +++ b/include/nn/util/util_BitUtil.h @@ -11,4 +11,10 @@ T align_up(T x, size_t align) { return (x + mask) & ~mask; } +template +T align_down(T x, size_t align) { + typedef typename detail::MakeUnsigned::Type UIntType; + UIntType mask = align - 1; + return x & ~mask; +} } // namespace nn::util \ No newline at end of file diff --git a/include/nn/util/util_IntrusiveList.h b/include/nn/util/util_IntrusiveList.h index 319f43a8..07c760b2 100644 --- a/include/nn/util/util_IntrusiveList.h +++ b/include/nn/util/util_IntrusiveList.h @@ -79,8 +79,18 @@ class IntrusiveListImplementation { reference operator*() const { return *m_Node; } pointer operator->() const; - const_iterator& operator++(); - const_iterator operator++(int); + + const_iterator& operator++() { + m_Node = m_Node->GetNext(); + return *this; + } + + const_iterator operator++(int) { + const_iterator temporary(*this); + ++(*this); + return temporary; + } + const_iterator& operator--(); const_iterator operator--(int); bool operator==(const const_iterator&) const; @@ -135,10 +145,10 @@ class IntrusiveListImplementation { void pop_front() { m_Root.GetNext()->Unlink(); } - reference back(); - reference back() const; - reference front(); - reference front() const; + reference back() { return *m_Root.GetPrev(); } + const_reference back() const { return *m_Root.GetPrev(); } + reference front() { return *m_Root.GetNext(); } + const_reference front() const { return *m_Root.GetNext(); } iterator begin() { return m_Root.GetNext(); } const_iterator begin() const { return m_Root.GetNext(); } @@ -149,7 +159,7 @@ class IntrusiveListImplementation { iterator iterator_to(reference value) { return iterator(&value); } const_iterator iterator_to(reference value) const { return iterator(&value); } - size_type size() const; + size_type size() const { return std::distance(begin(), end()); } bool empty() const { return !m_Root.IsLinked(); } @@ -281,10 +291,11 @@ class IntrusiveList { void push_front(reference); void pop_back(); void pop_front(); - reference front(); - reference front() const; - reference back(); - reference back() const; + + reference front() { return ToReference(m_Implementation.front()); } + const_reference front() const { return ToReference(m_Implementation.front()); } + reference back() { return ToReference(m_Implementation.back()); } + const_reference back() const { return ToReference(m_Implementation.back()); } iterator begin() { return m_Implementation.begin(); } const_iterator begin() const { return m_Implementation.begin(); } @@ -307,8 +318,9 @@ class IntrusiveList { return m_Implementation.iterator_to(ToNode(value)); } - size_type size() const; - bool empty() const; + size_type size() const { return m_Implementation.size(); } + + bool empty() const { return m_Implementation.empty(); } iterator erase(const_iterator position) { detail::IntrusiveListImplementation::iterator result = @@ -330,14 +342,37 @@ class IntrusiveList { private: IntrusiveListNode& ToNode(reference ref) const { return NodeTraits::GetNode(ref); } - - const IntrusiveListNode& ToNode(const_reference) const; - reference ToReference(IntrusiveListNode&) const; - const_reference ToReference(const IntrusiveListNode&) const; + const IntrusiveListNode& ToNode(const_reference ref) const { return NodeTraits::GetNode(ref); } + reference ToReference(IntrusiveListNode& node) const { return NodeTraits::GetItem(node); } + const_reference ToReference(const IntrusiveListNode& node) const { return NodeTraits::GetItem(node); } detail::IntrusiveListImplementation m_Implementation; }; +template +class IntrusiveListBaseNode : public IntrusiveListNode { +public: + IntrusiveListBaseNode(); +}; + +template +class IntrusiveListBaseNodeTraits { +public: + using BaseNodeType = IntrusiveListBaseNode; + + static IntrusiveListNode& GetNode(T& ref) { return ref; } + + static const IntrusiveListNode& GetNode(const T& ref) { return ref; } + + static T& GetItem(IntrusiveListNode& node) { + return reinterpret_cast(node); + } + + static const T& GetItem(const IntrusiveListNode& node) { + return reinterpret_cast(node); + } +}; + template class IntrusiveListMemberNodeTraits { friend class IntrusiveList; @@ -358,5 +393,4 @@ class IntrusiveListMemberNodeTraits { return reinterpret_cast(&(reinterpret_cast(0)->*Member)); } }; - } // namespace nn::util diff --git a/include/nn/util/util_StringUtil.h b/include/nn/util/util_StringUtil.h new file mode 100644 index 00000000..e3051c2e --- /dev/null +++ b/include/nn/util/util_StringUtil.h @@ -0,0 +1,49 @@ +namespace nn::util { +template +inline int Strlcpy(T* pOutDst, const T* pSrc, int count) { + int length = 0; + + if (count > 0) { + while (--count && *pSrc) { + *pOutDst++ = *pSrc++; + ++length; + } + *pOutDst++ = '\0'; + } + + while (*pSrc++) + ++length; + + return length; +} + +template +inline int Strnlen(const T* pStr, int count) { + int length = 0; + + if (count > 0) { + while (count && *pStr) { + ++pStr; + ++length; + --count; + } + } + + return length; +} + +template +inline int Strncmp(const T* pStr1, const T* pStr2, int count) { + if (count == 0) + return 0; + + T c1, c2; + + do { + c1 = *pStr1++; + c2 = *pStr2++; + } while (c1 && c1 == c2 && --count); + + return c1 - c2; +} +} // namespace nn::util \ No newline at end of file diff --git a/src/NintendoWare/atk/atk_FsSoundArchive.cpp b/src/NintendoWare/atk/atk_FsSoundArchive.cpp new file mode 100644 index 00000000..43d02d03 --- /dev/null +++ b/src/NintendoWare/atk/atk_FsSoundArchive.cpp @@ -0,0 +1,142 @@ +#include + +#include + +#include +#include + +namespace nn::atk { +FsSoundArchive::FsSoundArchive() = default; + +FsSoundArchive::~FsSoundArchive() { + Close(); +} + +void FsSoundArchive::Close() { + FileAccessBegin(); + + if (m_IsOpened) { + m_FileStream.Close(); + m_IsOpened = false; + } + + Finalize(); +} + +bool FsSoundArchive::LoadHeader(void* buffer, size_t size) { + const s32 infoChunkOffset {m_ArchiveReader.GetInfoBlockOffset()}; + const u32 infoChunkSize {m_ArchiveReader.GetInfoBlockSize()}; + + if (size >= infoChunkSize) { + FileAccessBegin(); + m_FileStream.Seek(infoChunkOffset, detail::fnd::Stream::SeekOrigin_Begin); + size_t readSize {m_FileStream.Read(buffer, infoChunkSize, nullptr)}; + + if (readSize == infoChunkSize) { + FileAccessEnd(); + m_ArchiveReader.SetInfoBlock(buffer); + return true; + } + } + + return false; +} + +bool FsSoundArchive::LoadLabelStringData(void* buffer, size_t size) { + const s32 stringBlockOffset {m_ArchiveReader.GetStringBlockOffset()}; + const u32 stringBlockSize {m_ArchiveReader.GetStringBlockSize()}; + + if (stringBlockOffset != detail::Util::Reference::InvalidOffset && size >= stringBlockSize) { + FileAccessBegin(); + m_FileStream.Seek(stringBlockOffset, detail::fnd::Stream::SeekOrigin_Begin); + size_t readSize {m_FileStream.Read(buffer, stringBlockSize, nullptr)}; + + if (readSize == stringBlockSize) { + FileAccessEnd(); + m_ArchiveReader.SetStringBlock(buffer); + return true; + } + } + + return false; +} + +size_t FsSoundArchive::detail_GetRequiredStreamBufferSize() const { + return 8; +} + +const void* FsSoundArchive::detail_GetFileAddress([[maybe_unused]] ItemId itemId) const { + return nullptr; +} + +void FsSoundArchive::FileAccessBegin() const { + if (m_FileAccessMode == FileAccessMode_InFunction) { + detail::fnd::ScopedLock lock{m_FileOpenCloseLock}; + if (m_FileAccessCount == 0) + m_FileStream.Open(m_SoundArchiveFullPath, detail::fnd::FileStream::AccessMode_Read); + + ++m_FileAccessCount; + } +} + +void FsSoundArchive::FileAccessEnd() const { + if (m_FileAccessMode == FileAccessMode_InFunction) { + detail::fnd::ScopedLock lock{m_FileOpenCloseLock}; + if (m_FileAccessCount == 1) + m_FileStream.Close(); + + if (m_FileAccessCount != 0) + --m_FileAccessCount; + } +} + +// NON_MATCHING +detail::fnd::FileStream* FsSoundArchive::OpenStream(void* buffer, size_t size, + position_t begin, size_t length) const { + detail::fnd::FileStream* stream {}; + if (sizeof(detail::fnd::FileStreamProxy) <= size && m_IsOpened) { + stream = new (buffer) detail::fnd::FileStreamProxy(&m_FileStream, begin, length); + } + + return stream; +} + +// NON_MATCHING +detail::fnd::FileStream* FsSoundArchive::OpenExtStream(void* buffer, size_t size, const char* extFilePath, + void* cacheBuffer, size_t cacheSize) const { + detail::fnd::FileStream* fileStream {}; + if (size >= sizeof(detail::fnd::FileStreamImpl) && m_IsOpened) { + fileStream = new (buffer) detail::fnd::FileStreamImpl(); + + fileStream->Open(extFilePath, detail::fnd::FileStreamImpl::AccessMode_Read); + + if (!fileStream->IsOpened()) { + fileStream = nullptr; + } + else { + if (cacheBuffer != nullptr && cacheSize != 0) + fileStream->EnableCache(cacheBuffer, cacheSize); + } + } + + return fileStream; +} + +bool FsSoundArchive::LoadFileHeader() { + const int Align = 256; + const size_t headerAlignSize = 256; + + char headerArea[sizeof(detail::SoundArchiveFile::FileHeader) + headerAlignSize + Align]; + void* file {util::BytePtr(headerArea).AlignUp(Align).Get()}; + + size_t readSize {m_FileStream.Read(file, headerAlignSize, nullptr)}; + + if (readSize != headerAlignSize) + return false; + + m_ArchiveReader.Initialize(file); + Initialize(&m_ArchiveReader); + return true; +} +} // namespace nn::atk + diff --git a/src/NintendoWare/atk/.gitkeep b/src/NintendoWare/atk/atk_SequenceSoundHandle.cpp similarity index 100% rename from src/NintendoWare/atk/.gitkeep rename to src/NintendoWare/atk/atk_SequenceSoundHandle.cpp diff --git a/src/NintendoWare/atk/atk_SoundArchive.cpp b/src/NintendoWare/atk/atk_SoundArchive.cpp new file mode 100644 index 00000000..c8328b8a --- /dev/null +++ b/src/NintendoWare/atk/atk_SoundArchive.cpp @@ -0,0 +1,341 @@ +#include + +#include +#include + +#include + +#include +#include +#include + +namespace nn::atk { +SoundArchive::SoundArchive() { + m_ExtFileRoot[0] = '/'; + m_ExtFileRoot[1] = '\0'; +}; + +SoundArchive::~SoundArchive() = default; + +bool SoundArchive::IsAvailable() const { + return m_pFileReader != nullptr; +} + +void SoundArchive::Initialize(detail::SoundArchiveFileReader* fileReader) { + m_pFileReader = fileReader; + m_FileBlockOffset = fileReader->GetFileBlockOffset(); +} + +void SoundArchive::Finalize() { + if (m_pFileReader != nullptr) { + m_pFileReader->Finalize(); + m_pFileReader = nullptr; + } + + m_ExtFileRoot[0] = '/'; + m_ExtFileRoot[1] = '\0'; +} + +u32 SoundArchive::GetSoundCount() const { + return m_pFileReader->GetSoundCount(); +} + +u32 SoundArchive::GetGroupCount() const { + return m_pFileReader->GetGroupCount(); +} + +u32 SoundArchive::GetPlayerCount() const { + return m_pFileReader->GetPlayerCount(); +} + +u32 SoundArchive::GetSoundGroupCount() const { + return m_pFileReader->GetSoundGroupCount(); +} + +u32 SoundArchive::GetBankCount() const { + return m_pFileReader->GetBankCount(); +} + +u32 SoundArchive::GetWaveArchiveCount() const { + return m_pFileReader->GetWaveArchiveCount(); +} + +u32 SoundArchive::detail_GetFileCount() const { + return m_pFileReader->GetFileCount(); +} + +const char* SoundArchive::GetItemLabel(ItemId id) const { + if (m_pParametersHook != nullptr) { + const char* result {m_pParametersHook->GetItemLabel(id)}; + if (result != nullptr) + return result; + } + + return m_pFileReader->GetItemLabel(id); +} + +SoundArchive::ItemId SoundArchive::GetItemId(const char* pStr) const { + if (m_pParametersHook != nullptr) { + ItemId result {m_pParametersHook->GetItemId(pStr)}; + if (result != InvalidId) + return result; + } + + return m_pFileReader->GetItemId(pStr); +} + +SoundArchive::FileId SoundArchive::GetItemFileId(ItemId id) const { + return m_pFileReader->GetItemFileId(id); +} + +SoundArchive::FileId SoundArchive::GetItemPrefetchFileId(ItemId id) const { + return m_pFileReader->GetItemPrefetchFileId(id); +} + +u32 SoundArchive::GetSoundUserParam(ItemId soundId) const { + u32 userParam {m_pFileReader->GetSoundUserParam(soundId)}; + + if (m_pParametersHook != nullptr && m_pParametersHook->GetIsEnable()) + userParam = m_pParametersHook->GetSoundUserParam(soundId, userParam); + + return userParam; +} + +bool SoundArchive::ReadSoundUserParam(u32* pOutValue, ItemId soundId, int index) const { + bool result {m_pFileReader->ReadSoundUserParam(pOutValue, soundId, index)}; + + if (m_pParametersHook != nullptr) + result |= m_pParametersHook->ReadSoundUserParam(pOutValue, soundId, index); + + return result; +} + +SoundArchive::SoundType SoundArchive::GetSoundType(ItemId soundId) const { +#if NN_SDK_VER > NN_MAKE_VER(1, 6, 0) + if (m_pParametersHook != nullptr) { + const char* label {m_pParametersHook->GetItemLabel(soundId)}; + if (label != nullptr) { + SoundType type {m_pParametersHook->GetSoundType(label)}; + if (type != SoundType_Invalid) + return type; + } + } +#endif + return m_pFileReader->GetSoundType(soundId); +} + +bool SoundArchive::ReadSoundInfo(SoundInfo* pOutValue, ItemId soundId) const { + bool result {m_pFileReader->ReadSoundInfo(soundId, pOutValue)}; + + if (m_pParametersHook != nullptr) + result |= m_pParametersHook->ReadSoundInfo(soundId, pOutValue); + + return result; +} + +bool SoundArchive::ReadSequenceSoundInfo(SequenceSoundInfo* pOutValue, ItemId soundId) const { + bool result {m_pFileReader->ReadSequenceSoundInfo(soundId, pOutValue)}; + + if (m_pParametersHook != nullptr) + result |= m_pParametersHook->ReadSequenceSoundInfo(soundId, pOutValue); + + return result; +} + +bool SoundArchive::ReadBankInfo(BankInfo* pOutValue, ItemId bankId) const { + bool result {m_pFileReader->ReadBankInfo(bankId, pOutValue)}; + + if (m_pParametersHook != nullptr) + result |= m_pParametersHook->ReadBankInfo(bankId, pOutValue); + + return result; +} + +bool SoundArchive::ReadPlayerInfo(PlayerInfo* pOutValue, ItemId playerId) const { + bool result {m_pFileReader->ReadPlayerInfo(playerId, pOutValue)}; + + if (m_pParametersHook != nullptr) + result |= m_pParametersHook->ReadPlayerInfo(playerId, pOutValue); + + return result; +} + +bool SoundArchive::ReadSoundArchivePlayerInfo(SoundArchivePlayerInfo* pOutValue) const { + bool result {m_pFileReader->ReadSoundArchivePlayerInfo(pOutValue)}; + + if (m_pParametersHook != nullptr) + result |= m_pParametersHook->ReadSoundArchivePlayerInfo(pOutValue); + + return result; +} + +bool SoundArchive::ReadStreamSoundInfo(StreamSoundInfo* pOutValue, ItemId soundId) const { + bool result {m_pFileReader->ReadStreamSoundInfo(soundId, pOutValue)}; + + if (m_pParametersHook != nullptr) + result |= m_pParametersHook->ReadStreamSoundInfo(soundId, pOutValue); + + return result; +} + +bool SoundArchive::detail_ReadStreamSoundInfo2(ItemId soundId, StreamSoundInfo2* info) const { + bool result {m_pFileReader->ReadStreamSoundInfo2(soundId, info)}; + + if (m_pParametersHook != nullptr) + result |= m_pParametersHook->ReadStreamSoundInfo2(soundId, info); + + return result; +} + +bool SoundArchive::detail_ReadWaveSoundInfo(ItemId soundId, WaveSoundInfo* info) const { + bool result {m_pFileReader->ReadWaveSoundInfo(soundId, info)}; + + if (m_pParametersHook != nullptr) + result |= m_pParametersHook->ReadWaveSoundInfo(soundId, info); + + return result; +} + +bool SoundArchive::detail_ReadAdvancedWaveSoundInfo(ItemId soundId, AdvancedWaveSoundInfo* info) const { + bool result {m_pFileReader->ReadAdvancedWaveSoundInfo(soundId, info)}; + return result; +} + +bool SoundArchive::ReadSound3DInfo(Sound3DInfo* pOutValue, ItemId soundId) const { + bool result {m_pFileReader->ReadSound3DInfo(soundId, pOutValue)}; + + if (m_pParametersHook != nullptr) + result |= m_pParametersHook->ReadSound3DInfo(soundId, pOutValue); + + return result; +} + +bool SoundArchive::ReadWaveArchiveInfo(ItemId warcId, WaveArchiveInfo* info) const { + bool result {m_pFileReader->ReadWaveArchiveInfo(warcId, info)}; + + if (m_pParametersHook != nullptr) + result |= m_pParametersHook->ReadWaveArchiveInfo(warcId, info); + + return result; +} + +bool SoundArchive::detail_ReadSoundGroupInfo(ItemId soundGroupId, SoundGroupInfo* info) const { + bool result {m_pFileReader->ReadSoundGroupInfo(soundGroupId, info)}; + + if (m_pParametersHook != nullptr) + result |= m_pParametersHook->ReadSoundGroupInfo(soundGroupId, info); + + return result; +} + +bool SoundArchive::ReadGroupInfo(GroupInfo* pOutValue, ItemId groupId) const { + bool result {m_pFileReader->ReadGroupInfo(groupId, pOutValue)}; + + if (m_pParametersHook != nullptr) + result |= m_pParametersHook->ReadGroupInfo(groupId, pOutValue); + + return result; +} + +bool SoundArchive::detail_ReadFileInfo(FileId fileId, FileInfo* info) const { + bool result {m_pFileReader->ReadFileInfo(fileId, info, 0)}; + + if (result && m_pParametersHook != nullptr) { + m_pParametersHook->ReadFileInfo(fileId, info, 0); + result = true; + } + + return result; +} + +const detail::Util::Table* SoundArchive::detail_GetWaveArchiveIdTable(ItemId id) const { + return m_pFileReader->GetWaveArchiveIdTable(id); +} + +detail::fnd::FileStream* SoundArchive::detail_OpenFileStream(FileId fileId, void* buffer, size_t size, + void* cacheBuffer, size_t cacheSize) const { + FileInfo fileInfo {}; + detail::fnd::FileStream* stream {}; + if (detail_ReadFileInfo(fileId, &fileInfo)) { + if (fileInfo.externalFilePath == nullptr) { + if (fileInfo.fileSize != FileInfo::InvalidSize && fileInfo.offsetFromFileBlockHead != FileInfo::InvalidOffset) + stream = OpenStream(buffer, size, m_FileBlockOffset + fileInfo.offsetFromFileBlockHead, fileInfo.fileSize); + } + else { + stream = OpenExtStreamImpl(buffer, size, fileInfo.externalFilePath, cacheBuffer, cacheSize); + } + } + + return stream; +} + +const detail::Util::Table* SoundArchive::detail_GetAttachedGroupTable(FileId fileId) const { + return m_pFileReader->GetAttachedGroupTable(fileId); +} + +void SoundArchive::SetExternalFileRoot(const char* extFileRoot) { + size_t len {std::strlen(extFileRoot)}; + size_t nullPos {len + 1}; + + util::Strlcpy(m_ExtFileRoot, extFileRoot, std::min(sizeof(m_ExtFileRoot), nullPos)); + + if (extFileRoot[len - 1] != '/') { + m_ExtFileRoot[len] = '/'; + ++len; + } + m_ExtFileRoot[len] = '\0'; +} + +bool SoundArchive::ReadStreamSoundFilePath(char* outFilePathBuffer, size_t filePathBufferSize, ItemId soundId) const { + if (GetSoundType(soundId) == SoundType_Stream) { + SoundInfo soundInfo; + if (!ReadSoundInfo(&soundInfo, soundId)) + return false; + + FileInfo fileInfo; + if (!detail_ReadFileInfo(soundInfo.fileId, &fileInfo)) + return false; + + const char* result {}; + if (fileInfo.externalFilePath != nullptr) + result = detail_GetExternalFileFullPath(fileInfo.externalFilePath, outFilePathBuffer, filePathBufferSize); + + return result != nullptr; + } + + return false; +} + +void SoundArchive::FileAccessBegin() const {} + +void SoundArchive::FileAccessEnd() const {} + +// NON_MATCHING: still unsure on fnd::strncat's implementation, +// or the whole function really. Leaving as TODO +const char* SoundArchive::detail_GetExternalFileFullPath(const char* externalFilePath, + char* pathBuffer, + size_t bufSize) const { + if (*externalFilePath != '/') { + size_t dirLen {strlen(externalFilePath)}; + size_t fileLen {strlen(m_ExtFileRoot)}; + if (fileLen + dirLen < bufSize) { + if (bufSize > fileLen + 1) + bufSize += 1; + + util::Strlcpy(pathBuffer, externalFilePath, bufSize); + detail::fnd::strncat(pathBuffer, bufSize, m_ExtFileRoot, fileLen); + pathBuffer[bufSize - 1] = '\0'; + externalFilePath = pathBuffer; + } + else { + externalFilePath = nullptr; + } + } + + return externalFilePath; +} + +bool SoundArchive::IsAddon() const { + return false; +} +} // namespace nn::atk \ No newline at end of file diff --git a/src/NintendoWare/atk/atk_SoundArchivePlayer.cpp b/src/NintendoWare/atk/atk_SoundArchivePlayer.cpp new file mode 100644 index 00000000..e69de29b diff --git a/src/NintendoWare/atk/atk_SoundDataManager.cpp b/src/NintendoWare/atk/atk_SoundDataManager.cpp new file mode 100644 index 00000000..e69de29b diff --git a/src/NintendoWare/atk/atk_SoundHandle.cpp b/src/NintendoWare/atk/atk_SoundHandle.cpp new file mode 100644 index 00000000..e69de29b diff --git a/src/NintendoWare/atk/atk_SoundHeap.cpp b/src/NintendoWare/atk/atk_SoundHeap.cpp new file mode 100644 index 00000000..e69de29b diff --git a/src/NintendoWare/atk/atk_SoundPlayer.cpp b/src/NintendoWare/atk/atk_SoundPlayer.cpp new file mode 100644 index 00000000..e69de29b diff --git a/src/NintendoWare/atk/atk_SoundStartable.cpp b/src/NintendoWare/atk/atk_SoundStartable.cpp new file mode 100644 index 00000000..e69de29b diff --git a/src/NintendoWare/atk/atk_SoundSystem.cpp b/src/NintendoWare/atk/atk_SoundSystem.cpp new file mode 100644 index 00000000..e69de29b diff --git a/src/NintendoWare/atk/atk_StreamSoundHandle.cpp b/src/NintendoWare/atk/atk_StreamSoundHandle.cpp new file mode 100644 index 00000000..e69de29b diff --git a/src/NintendoWare/atk/detail/atk_AddonSoundArchiveContainer.cpp b/src/NintendoWare/atk/detail/atk_AddonSoundArchiveContainer.cpp new file mode 100644 index 00000000..55e65764 --- /dev/null +++ b/src/NintendoWare/atk/detail/atk_AddonSoundArchiveContainer.cpp @@ -0,0 +1,47 @@ +#include + +#include + +namespace nn::atk::detail { + +AddonSoundArchiveContainer::AddonSoundArchiveContainer() = default; + +AddonSoundArchiveContainer::~AddonSoundArchiveContainer() { + m_IsActive = false; + m_pSoundArchive = nullptr; + m_pSoundDataManager = nullptr; +} + +bool AddonSoundArchiveContainer::Initialize(const char* soundArchiveName, + const AddonSoundArchive* pSoundArchive, + const SoundDataManager* pSoundDataManager) { + util::Strlcpy(m_SoundArchiveName, soundArchiveName, sizeof(m_SoundArchiveName)); + m_IsActive = true; + m_pSoundArchive = pSoundArchive; + m_pSoundDataManager = pSoundDataManager; + + return true; +} + +void AddonSoundArchiveContainer::Finalize() { + m_IsActive = false; + m_SoundArchiveName[0] = '\0'; + m_pSoundArchive = nullptr; + m_pSoundDataManager = nullptr; +} + +bool AddonSoundArchiveContainer::IsSameName(const char* name) const { + int nameLength{util::Strnlen(name, SoundArchiveNameLengthMax)}; + + if (nameLength == SoundArchiveNameLengthMax) + return false; + + int result{util::Strncmp(name, m_SoundArchiveName, SoundArchiveNameLengthMax)}; + return result == 0; +} + +void AddonSoundArchiveContainer::SetAddTick(const os::Tick& tick) { + m_AddTick = tick; +} + +} // namespace nn::atk::detail \ No newline at end of file diff --git a/src/NintendoWare/atk/detail/atk_AdvancedWaveSound.cpp b/src/NintendoWare/atk/detail/atk_AdvancedWaveSound.cpp new file mode 100644 index 00000000..e69de29b diff --git a/src/NintendoWare/atk/detail/atk_AdvancedWaveSoundFile.cpp b/src/NintendoWare/atk/detail/atk_AdvancedWaveSoundFile.cpp new file mode 100644 index 00000000..e69de29b diff --git a/src/NintendoWare/atk/detail/atk_AdvancedWaveSoundFileReader.cpp b/src/NintendoWare/atk/detail/atk_AdvancedWaveSoundFileReader.cpp new file mode 100644 index 00000000..e69de29b diff --git a/src/NintendoWare/atk/detail/atk_AdvancedWaveSoundPlayer.cpp b/src/NintendoWare/atk/detail/atk_AdvancedWaveSoundPlayer.cpp new file mode 100644 index 00000000..e69de29b diff --git a/src/NintendoWare/atk/detail/atk_AdvancedWaveSoundRuntime.cpp b/src/NintendoWare/atk/detail/atk_AdvancedWaveSoundRuntime.cpp new file mode 100644 index 00000000..e69de29b diff --git a/src/NintendoWare/atk/detail/atk_BasicSound.cpp b/src/NintendoWare/atk/detail/atk_BasicSound.cpp new file mode 100644 index 00000000..e69de29b diff --git a/src/NintendoWare/atk/detail/atk_BasicSoundPlayer.cpp b/src/NintendoWare/atk/detail/atk_BasicSoundPlayer.cpp new file mode 100644 index 00000000..e69de29b diff --git a/src/NintendoWare/atk/detail/atk_BusMixVolumePacket.cpp b/src/NintendoWare/atk/detail/atk_BusMixVolumePacket.cpp new file mode 100644 index 00000000..b6d01310 --- /dev/null +++ b/src/NintendoWare/atk/detail/atk_BusMixVolumePacket.cpp @@ -0,0 +1,11 @@ +#include +#include + +namespace nn::atk::detail { +BusMixVolumePacket::BusMixVolumePacket() = default; + +size_t BusMixVolumePacket::GetRequiredMemSize(int busCount) { + size_t result {util::align_up(busCount, 8)}; + return result; +} +} // namespace nn::atk::detail \ No newline at end of file diff --git a/src/NintendoWare/atk/detail/atk_DisposeCallbackManager.cpp b/src/NintendoWare/atk/detail/atk_DisposeCallbackManager.cpp new file mode 100644 index 00000000..e69de29b diff --git a/src/NintendoWare/atk/detail/atk_ExternalSoundPlayer.cpp b/src/NintendoWare/atk/detail/atk_ExternalSoundPlayer.cpp new file mode 100644 index 00000000..e69de29b diff --git a/src/NintendoWare/atk/detail/atk_GroupFile.cpp b/src/NintendoWare/atk/detail/atk_GroupFile.cpp new file mode 100644 index 00000000..92ff37cb --- /dev/null +++ b/src/NintendoWare/atk/detail/atk_GroupFile.cpp @@ -0,0 +1,20 @@ +#include + +#include + +namespace nn::atk::detail { +const GroupFile::InfoBlock* GroupFile::FileHeader::GetInfoBlock() const { + return util::ConstBytePtr(GetBlock(ElementType_GroupFile_InfoBlock)) + .Get(); +} + +const GroupFile::FileBlock* GroupFile::FileHeader::GetFileBlock() const { + return util::ConstBytePtr(GetBlock(ElementType_GroupFile_FileBlock)) + .Get(); +} + +const GroupFile::InfoExBlock* GroupFile::FileHeader::GetInfoExBlock() const { + return util::ConstBytePtr(GetBlock(ElementType_GroupFile_InfoExBlock)) + .Get(); +} +} // namespace nn::atk::detail \ No newline at end of file diff --git a/src/NintendoWare/atk/detail/atk_GroupFileReader.cpp b/src/NintendoWare/atk/detail/atk_GroupFileReader.cpp new file mode 100644 index 00000000..e69de29b diff --git a/src/NintendoWare/atk/detail/atk_OutputAdditionalParam.cpp b/src/NintendoWare/atk/detail/atk_OutputAdditionalParam.cpp new file mode 100644 index 00000000..e69de29b diff --git a/src/NintendoWare/atk/detail/atk_PlayerHeap.cpp b/src/NintendoWare/atk/detail/atk_PlayerHeap.cpp new file mode 100644 index 00000000..e69de29b diff --git a/src/NintendoWare/atk/detail/atk_PlayerHeapDataManager.cpp b/src/NintendoWare/atk/detail/atk_PlayerHeapDataManager.cpp new file mode 100644 index 00000000..e69de29b diff --git a/src/NintendoWare/atk/detail/atk_RegionManager.cpp b/src/NintendoWare/atk/detail/atk_RegionManager.cpp new file mode 100644 index 00000000..e69de29b diff --git a/src/NintendoWare/atk/detail/atk_SoundArchiveFile.cpp b/src/NintendoWare/atk/detail/atk_SoundArchiveFile.cpp new file mode 100644 index 00000000..b0af246b --- /dev/null +++ b/src/NintendoWare/atk/detail/atk_SoundArchiveFile.cpp @@ -0,0 +1,718 @@ +#include + +#include + +namespace nn::atk::detail { +namespace { +const u32 DefaultStringId {0xffffffff}; +const PanMode DefaultPanMode {PanMode_Dual}; +const PanCurve DefaultPanCurve {PanCurve_Sqrt}; +const SinglePlayType DefaultSinglePlayType {SinglePlayType_None}; +const u16 DefaultSinglePlayEffectiveDuration {0xffff}; +const u8 DefaultPlayerPriority {64}; +const u8 DefaultChannelPriority {64}; +const u8 DefaultActorPlayerId {0}; +const u8 DefaultIsReleasePriorityFix {0}; +const bool DefaultIsFrontBypass {false}; +const u32 DefaultUserParam {0xffffffff}; +const u32 DefaultSeqStartOffset {0}; +const u32 DefaultWarcWaveCount {0}; +const u32 DefaultPlayerHeapSize {0}; + +enum SoundInfoBitFlag { + SoundInfoBitFlag_StringId = 0, + SoundInfoBitFlag_PanParam = 1, + SoundInfoBitFlag_PlayerParam = 2, + SoundInfoBitFlag_SinglePlayParam = 3, + + SoundInfoBitFlag_OffsetTo3dParam = 8, + SoundInfoBitFlag_OffsetToSendParam = 9, + SoundInfoBitFlag_OffsetToModParam = 10, + SoundInfoBitFlag_OffsetToRvlParam = 16, + SoundInfoBitFlag_OffsetToCtrParam = 17, + SoundInfoBitFlag_OffsetToCafeParam = 18, + + SoundInfoBitFlag_UserParam3 = 28, + SoundInfoBitFlag_UserParam2 = 29, + SoundInfoBitFlag_UserParam1 = 30, + SoundInfoBitFlag_UserParam = 31, +}; + +const int UserParamIndex[4] { + SoundInfoBitFlag_UserParam, + SoundInfoBitFlag_UserParam1, + SoundInfoBitFlag_UserParam2, + SoundInfoBitFlag_UserParam3 +}; + +enum WaveSoundInfoBitFlag { + WaveSoundInfoBitFlag_Priority = 0, +}; + +enum SequenceSoundInfoBitFlag { + SequenceSoundInfoBitFlag_StartOffset = 0, + SequenceSoundInfoBitFlag_Priority = 1, +}; + +enum BankInfoBitFlag { + BankInfoBitFlag_StringId = 0, +}; + +enum PlayerInfoBitFlag { + PlayerInfoBitFlag_StringId = 0, + PlayerInfoBitFlag_HeapSize = 1, +}; + +enum SoundGroupInfoBitFlag { + SoundGroupInfoBitFlag_StringId = 0, +}; + +enum GroupInfoBitFlag { + GroupInfoBitFlag_StringId = 0, +}; + +enum WaveArchiveInfoBitFlag { + WaveArchiveInfoBitFlag_StringId = 0, + WaveArchiveInfoBitFlag_WaveCount = 1, +}; +} // anonymous namespace + +const Util::ReferenceWithSize* SoundArchiveFile::FileHeader::GetReferenceBy(u16 typeId) const { + for (int i {0}; i < BlockCount; ++i) { + if (toBlocks[i].typeId == typeId) + return &toBlocks[i]; + } + + return nullptr; +} + +u32 SoundArchiveFile::FileHeader::GetStringBlockSize() const { + return GetReferenceBy(ElementType_SoundArchiveFile_StringBlock)->size; +} + +u32 SoundArchiveFile::FileHeader::GetInfoBlockSize() const { + return GetReferenceBy(ElementType_SoundArchiveFile_InfoBlock)->size; +} + +u32 SoundArchiveFile::FileHeader::GetFileBlockSize() const { + return GetReferenceBy(ElementType_SoundArchiveFile_FileBlock)->size; +} + +int SoundArchiveFile::FileHeader::GetStringBlockOffset() const { + return GetReferenceBy(ElementType_SoundArchiveFile_StringBlock)->offset; +} + +int SoundArchiveFile::FileHeader::GetInfoBlockOffset() const { + return GetReferenceBy(ElementType_SoundArchiveFile_InfoBlock)->offset; +} + +int SoundArchiveFile::FileHeader::GetFileBlockOffset() const { + return GetReferenceBy(ElementType_SoundArchiveFile_FileBlock)->offset; +} + +const void* SoundArchiveFile::StringBlockBody::GetSection(Sections section) const { + if (section > Sections_Max) + return nullptr; + + return util::ConstBytePtr(this, toSection[section].offset).Get(); +} + +const char* SoundArchiveFile::StringBlockBody::GetString(SoundArchive::StringId stringId) const { + if (stringId == SoundArchive::InvalidId) + return nullptr; + + const StringTable* table {GetStringTable()}; + if (table == nullptr) + return nullptr; + + return table->GetString(stringId); +} + +void SoundArchiveFile::StringBlockBody::DumpTree() const { + // TODO: Empty only in release builds +} + +u32 SoundArchiveFile::StringBlockBody::GetItemIdImpl(Sections section, const char* str) const { + const PatriciaTree* tree {GetPatriciaTree(section)}; + const PatriciaTree::NodeData* nodeData {tree->GetNodeDataBy(str)}; + + if (nodeData == nullptr) + return SoundArchive::InvalidId; + + const char* nodeDataStr {GetString(nodeData->stringId)}; + if (std::strcmp(str, nodeDataStr) != 0) + return SoundArchive::InvalidId; + + return nodeData->itemId; +} + +const SoundArchiveFile::PatriciaTree::NodeData* SoundArchiveFile::PatriciaTree::GetNodeDataBy(const char* str, size_t len) const { + if (rootIdx >= nodeTable.count) + return nullptr; + + const Node* node = &nodeTable.item[rootIdx]; + if (len == 0) + len = std::strlen(str); + + while ((node->flags & Node::FlagLeaf) == 0) { + const int pos = node->bit >> 3; + const int bit = node->bit & 7; + u32 nodeIdx; + + if (pos < static_cast(len) && str[pos] & (1 << (7 - bit))) + nodeIdx = node->rightIdx; + else + nodeIdx = node->leftIdx; + + node = &nodeTable.item[nodeIdx]; + } + + return &node->nodeData; +} + +const SoundArchiveFile::SoundInfo* SoundArchiveFile::InfoBlockBody::GetSoundInfo(SoundArchive::ItemId itemId) const { + if (Util::GetItemType(itemId) != ItemType_Sound) + return nullptr; + + u32 index {Util::GetItemIndex(itemId)}; + const Util::ReferenceTable& table {GetSoundInfoReferenceTable()}; + + if (index >= table.count) + return nullptr; + + return util::ConstBytePtr(table.GetReferedItem(index)).Get(); +} + +const SoundArchiveFile::BankInfo* SoundArchiveFile::InfoBlockBody::GetBankInfo(SoundArchive::ItemId itemId) const { + if (Util::GetItemType(itemId) != ItemType_Bank) + return nullptr; + + u32 index {Util::GetItemIndex(itemId)}; + const Util::ReferenceTable& table {GetBankInfoReferenceTable()}; + + if (index >= table.count) + return nullptr; + + return util::ConstBytePtr(table.GetReferedItem(index)).Get(); +} + +const SoundArchiveFile::PlayerInfo* SoundArchiveFile::InfoBlockBody::GetPlayerInfo(SoundArchive::ItemId itemId) const { + if (Util::GetItemType(itemId) != ItemType_Player) + return nullptr; + + u32 index {Util::GetItemIndex(itemId)}; + const Util::ReferenceTable& table {GetPlayerInfoReferenceTable()}; + + if (index >= table.count) + return nullptr; + + return util::ConstBytePtr(table.GetReferedItem(index)).Get(); +} + +const SoundArchiveFile::SoundGroupInfo* SoundArchiveFile::InfoBlockBody::GetSoundGroupInfo(SoundArchive::ItemId itemId) const { + if (Util::GetItemType(itemId) != ItemType_SoundGroup) + return nullptr; + + u32 index {Util::GetItemIndex(itemId)}; + const Util::ReferenceTable& table {GetSoundGroupInfoReferenceTable()}; + + if (index >= table.count) + return nullptr; + + return util::ConstBytePtr(table.GetReferedItem(index)).Get(); +} + +const SoundArchiveFile::GroupInfo* SoundArchiveFile::InfoBlockBody::GetGroupInfo(SoundArchive::ItemId itemId) const { + if (Util::GetItemType(itemId) != ItemType_Group) + return nullptr; + + u32 index {Util::GetItemIndex(itemId)}; + const Util::ReferenceTable& table {GetGroupInfoReferenceTable()}; + + if (index >= table.count) + return nullptr; + + return util::ConstBytePtr(table.GetReferedItem(index)).Get(); +} + +const SoundArchiveFile::WaveArchiveInfo* SoundArchiveFile::InfoBlockBody::GetWaveArchiveInfo(SoundArchive::ItemId itemId) const { + if (Util::GetItemType(itemId) != ItemType_WaveArchive) + return nullptr; + + u32 index {Util::GetItemIndex(itemId)}; + const Util::ReferenceTable& table {GetWaveArchiveInfoReferenceTable()}; + + if (index >= table.count) + return nullptr; + + return util::ConstBytePtr(table.GetReferedItem(index)).Get(); +} + +const SoundArchiveFile::FileInfo* SoundArchiveFile::InfoBlockBody::GetFileInfo(SoundArchive::FileId itemId) const { + u32 index {Util::GetItemIndex(itemId)}; + const Util::ReferenceTable& table {GetFileInfoReferenceTable()}; + + if (index >= table.count) + return nullptr; + + return util::ConstBytePtr(table.GetReferedItem(index)).Get(); +} + +u32 SoundArchiveFile::SoundInfo::GetStringId() const { + u32 value; + bool result {optionParameter.GetValue(&value, SoundInfoBitFlag_StringId)}; + + if (!result) + return DefaultStringId; + + return value; +} + +u32 SoundArchiveFile::BankInfo::GetStringId() const { + u32 value; + bool result {optionParameter.GetValue(&value, BankInfoBitFlag_StringId)}; + + if (!result) + return DefaultStringId; + + return value; +} + +u32 SoundArchiveFile::WaveArchiveInfo::GetStringId() const { + u32 value; + bool result {optionParameter.GetValue(&value, SoundInfoBitFlag_StringId)}; + + if (!result) + return DefaultStringId; + + return value; +} + +u32 SoundArchiveFile::SoundGroupInfo::GetStringId() const { + u32 value; + bool result {optionParameter.GetValue(&value, SoundInfoBitFlag_StringId)}; + + if (!result) + return DefaultStringId; + + return value; +} + +u32 SoundArchiveFile::GroupInfo::GetStringId() const { + u32 value; + bool result {optionParameter.GetValue(&value, GroupInfoBitFlag_StringId)}; + + if (!result) + return DefaultStringId; + + return value; +} + +u32 SoundArchiveFile::PlayerInfo::GetStringId() const { + u32 value; + bool result {optionParameter.GetValue(&value, PlayerInfoBitFlag_StringId)}; + + if (!result) + return DefaultStringId; + + return value; +} + +const SoundArchiveFile::SoundArchivePlayerInfo* SoundArchiveFile::InfoBlockBody::GetSoundArchivePlayerInfo() const { + return util::ConstBytePtr(this, toSoundArchivePlayerInfo.offset) + .Get(); +} + +SoundArchive::FileId SoundArchiveFile::InfoBlockBody::GetItemFileId(SoundArchive::ItemId id) const { + SoundArchive::FileId fileId {SoundArchive::InvalidId}; + + switch (Util::GetItemType(id)) { + case ItemType_Sound: { + const SoundInfo* info {GetSoundInfo(id)}; + if (info != nullptr) + fileId = info->fileId; + break; + } + case ItemType_Bank: { + const BankInfo* info {GetBankInfo(id)}; + if (info != nullptr) + fileId = info->fileId; + break; + } + case ItemType_WaveArchive: { + const WaveArchiveInfo* info {GetWaveArchiveInfo(id)}; + if (info != nullptr) + fileId = info->fileId; + break; + } + case ItemType_Group: { + const GroupInfo* info {GetGroupInfo(id)}; + if (info != nullptr) + fileId = info->fileId; + break; + } + case ItemType_SoundGroup: { + const SoundGroupInfo* info {GetSoundGroupInfo(id)}; + if (info != nullptr) { + SoundArchive::ItemId soundId {info->startId}; + const SoundInfo* soundInfo {GetSoundInfo(soundId)}; + if (soundInfo != nullptr) + fileId = soundInfo->fileId; + } + break; + } + case ItemType_Player: + break; + } + + return fileId; +} + +SoundArchive::StringId SoundArchiveFile::InfoBlockBody::GetItemStringId(SoundArchive::ItemId id) const { + SoundArchive::StringId stringId {SoundArchive::InvalidId}; + + switch (Util::GetItemType(id)) { + case ItemType_Sound: { + const SoundInfo* info {GetSoundInfo(id)}; + if (info != nullptr) + stringId = info->GetStringId(); + break; + } + case ItemType_Bank: { + const BankInfo* info {GetBankInfo(id)}; + if (info != nullptr) + stringId = info->GetStringId(); + break; + } + case ItemType_WaveArchive: { + const WaveArchiveInfo* info {GetWaveArchiveInfo(id)}; + if (info != nullptr) + stringId = info->GetStringId(); + break; + } + case ItemType_Group: { + const GroupInfo* info {GetGroupInfo(id)}; + if (info != nullptr) + stringId = info->GetStringId(); + break; + } + case ItemType_SoundGroup: { + const SoundGroupInfo* info {GetSoundGroupInfo(id)}; + if (info != nullptr) + stringId = info->GetStringId(); + break; + } + case ItemType_Player: + const PlayerInfo* info {GetPlayerInfo(id)}; + if (info != nullptr) + stringId = info->GetStringId(); + break; + } + + return stringId; +} + +SoundArchive::FileId SoundArchiveFile::InfoBlockBody::GetItemPrefetchFileId(SoundArchive::ItemId id) const { + SoundArchive::FileId fileId {SoundArchive::InvalidId}; + + const SoundInfo* info {GetSoundInfo(id)}; + + if (info != nullptr && info->GetSoundType() == SoundArchive::SoundType_Stream) { + const SoundArchiveFile::StreamSoundInfo& streamSoundInfo {info->GetStreamSoundInfo()}; + fileId = streamSoundInfo.prefetchFileId; + } + + return fileId; +} + +const Util::ReferenceTable& SoundArchiveFile::InfoBlockBody::GetSoundInfoReferenceTable() const { + return *util::ConstBytePtr(this, toSoundInfoReferenceTable.offset) + .Get(); +} + +const Util::ReferenceTable& SoundArchiveFile::InfoBlockBody::GetBankInfoReferenceTable() const { + return *util::ConstBytePtr(this, toBankInfoReferenceTable.offset) + .Get(); +} + +const Util::ReferenceTable& SoundArchiveFile::InfoBlockBody::GetPlayerInfoReferenceTable() const { + return *util::ConstBytePtr(this, toPlayerInfoReferenceTable.offset) + .Get(); +} + +const Util::ReferenceTable& SoundArchiveFile::InfoBlockBody::GetSoundGroupInfoReferenceTable() const { + return *util::ConstBytePtr(this, toSoundGroupInfoReferenceTable.offset) + .Get(); +} + +const Util::ReferenceTable& SoundArchiveFile::InfoBlockBody::GetWaveArchiveInfoReferenceTable() const { + return *util::ConstBytePtr(this, toWaveArchiveInfoReferenceTable.offset) + .Get(); +} + +const Util::ReferenceTable& SoundArchiveFile::InfoBlockBody::GetGroupInfoReferenceTable() const { + return *util::ConstBytePtr(this, toGroupInfoReferenceTable.offset) + .Get(); +} + +const Util::ReferenceTable& SoundArchiveFile::InfoBlockBody::GetFileInfoReferenceTable() const { + return *util::ConstBytePtr(this, toFileInfoReferenceTable.offset) + .Get(); +} + +SoundArchive::SoundType SoundArchiveFile::SoundInfo::GetSoundType() const { + switch (toDetailSoundInfo.typeId) { + case ElementType_SoundArchiveFile_SequenceSoundInfo: + return SoundArchive::SoundType_Sequence; + + case ElementType_SoundArchiveFile_StreamSoundInfo: + return SoundArchive::SoundType_Stream; + + case ElementType_SoundArchiveFile_WaveSoundInfo: + return SoundArchive::SoundType_Wave; + + default: + return SoundArchive::SoundType_Invalid; + } +} + +const SoundArchiveFile::StreamSoundInfo& SoundArchiveFile::SoundInfo::GetStreamSoundInfo() const { + return *util::ConstBytePtr(this, toDetailSoundInfo.offset).Get(); +} + +const SoundArchiveFile::WaveSoundInfo& SoundArchiveFile::SoundInfo::GetWaveSoundInfo() const { + return *util::ConstBytePtr(this, toDetailSoundInfo.offset).Get(); +} + +const SoundArchiveFile::AdvancedWaveSoundInfo& SoundArchiveFile::SoundInfo::GetAdvancedWaveSoundInfo() const { + return *util::ConstBytePtr(this, toDetailSoundInfo.offset).Get(); +} + +const SoundArchiveFile::SequenceSoundInfo& SoundArchiveFile::SoundInfo::GetSequenceSoundInfo() const { + return *util::ConstBytePtr(this, toDetailSoundInfo.offset).Get(); +} + +const SoundArchiveFile::Sound3DInfo* SoundArchiveFile::SoundInfo::GetSound3DInfo() const { + u32 offset; + bool result {optionParameter.GetValue(&offset, SoundInfoBitFlag_OffsetTo3dParam)}; + + if (!result) + return nullptr; + + return util::ConstBytePtr(this, offset).Get(); +} + +PanMode SoundArchiveFile::SoundInfo::GetPanMode() const { + u32 value; + bool result {optionParameter.GetValue(&value, SoundInfoBitFlag_PanParam)}; + + if (!result) + return DefaultPanMode; + + return static_cast(Util::DivideBy8bit(value, 0)); +} + +PanCurve SoundArchiveFile::SoundInfo::GetPanCurve() const { + u32 value; + bool result {optionParameter.GetValue(&value, SoundInfoBitFlag_PanParam)}; + + if (!result) + return DefaultPanCurve; + + return static_cast(Util::DivideBy8bit(value, 1)); +} + +SinglePlayType SoundArchiveFile::SoundInfo::GetSinglePlayType() const { + u32 value; + bool result {optionParameter.GetValue(&value, SoundInfoBitFlag_SinglePlayParam)}; + + if (!result) + return DefaultSinglePlayType; + + return static_cast(Util::DivideBy8bit(value, 0)); +} + +u16 SoundArchiveFile::SoundInfo::GetSinglePlayEffectiveDuration() const { + u32 value; + bool result {optionParameter.GetValue(&value, SoundInfoBitFlag_SinglePlayParam)}; + + if (!result) + return DefaultSinglePlayEffectiveDuration; + + return Util::DivideBy16bit(value, 1); +} + +u8 SoundArchiveFile::SoundInfo::GetPlayerPriority() const { + u32 value; + bool result {optionParameter.GetValue(&value, SoundInfoBitFlag_PlayerParam)}; + + if (!result) + return DefaultPlayerPriority; + + return Util::DivideBy8bit(value, 0); +} + +u8 SoundArchiveFile::SoundInfo::GetActorPlayerId() const { + u32 value; + bool result {optionParameter.GetValue(&value, SoundInfoBitFlag_PlayerParam)}; + + if (!result) + return DefaultActorPlayerId; + + return Util::DivideBy8bit(value, 1); +} + +u32 SoundArchiveFile::SoundInfo::GetUserParam() const { + u32 value; + bool result {optionParameter.GetValue(&value, SoundInfoBitFlag_UserParam)}; + + if (!result) + return DefaultUserParam; + + return value; +} + +bool SoundArchiveFile::SoundInfo::ReadUserParam(u32* pOutValue, int index) const { + return optionParameter.GetValue(pOutValue, UserParamIndex[index]); +} + +bool SoundArchiveFile::SoundInfo::IsFrontBypass() const { + u32 value; + bool result {optionParameter.GetValue(&value, SoundInfoBitFlag_OffsetToCtrParam)}; + + if (!result) + return DefaultIsFrontBypass; + + return value & 1; +} + +const SoundArchiveFile::StreamTrackInfoTable* SoundArchiveFile::StreamSoundInfo::GetTrackInfoTable() const { + if (!toTrackInfoTable.IsValidTypeId(ElementType_Table_ReferenceTable)) + return nullptr; + + return util::ConstBytePtr(this, toTrackInfoTable.offset).Get(); +} + +const SoundArchiveFile::StreamSoundExtension* SoundArchiveFile::StreamSoundInfo::GetStreamSoundExtension() const { + if (!toStreamSoundExtension.IsValidOffset() || + !toStreamSoundExtension.IsValidTypeId(ElementType_SoundArchiveFile_StreamSoundExtensionInfo)) + return nullptr; + + return util::ConstBytePtr(this, toStreamSoundExtension.offset).Get(); +} + +const SoundArchiveFile::SendValue& SoundArchiveFile::StreamSoundInfo::GetSendValue() const { + return *util::ConstBytePtr(this, toSendValue.offset).Get(); +} + +u8 SoundArchiveFile::WaveSoundInfo::GetChannelPriority() const { + u32 value; + bool result {optionParameter.GetValue(&value, WaveSoundInfoBitFlag_Priority)}; + + if (!result) + return DefaultChannelPriority; + + return Util::DivideBy8bit(value, 0); +} + +u8 SoundArchiveFile::WaveSoundInfo::GetIsReleasePriorityFix() const { + u32 value; + bool result {optionParameter.GetValue(&value, WaveSoundInfoBitFlag_Priority)}; + + if (!result) + return DefaultIsReleasePriorityFix; + + return Util::DivideBy8bit(value, 1); +} + +const Util::Table& SoundArchiveFile::SequenceSoundInfo::GetBankIdTable() const { + return *util::ConstBytePtr(this, toBankIdTable.offset).Get>(); +} + +void SoundArchiveFile::SequenceSoundInfo::GetBankIds(u32* bankIds) const { + const Util::Table& table {GetBankIdTable()}; + + for (u32 i {0}; i < SoundArchive::SequenceBankMax; ++i) + bankIds[i] = i < table.count ? table.item[i] : SoundArchive::InvalidId; +} + +u32 SoundArchiveFile::SequenceSoundInfo::GetStartOffset() const { + u32 value; + bool result {optionParameter.GetValue(&value, SequenceSoundInfoBitFlag_StartOffset)}; + + if (!result) + return DefaultSeqStartOffset; + + return value; +} + +u8 SoundArchiveFile::SequenceSoundInfo::GetChannelPriority() const { + u32 value; + bool result {optionParameter.GetValue(&value, SequenceSoundInfoBitFlag_Priority)}; + + if (!result) + return DefaultChannelPriority; + + return Util::DivideBy8bit(value, 0); +} + +bool SoundArchiveFile::SequenceSoundInfo::IsReleasePriorityFix() const { + u32 value; + bool result {optionParameter.GetValue(&value, SequenceSoundInfoBitFlag_Priority)}; + + if (!result) + return DefaultIsReleasePriorityFix == 0; + + return Util::DivideBy8bit(value, 1) != 0; +} + +u32 SoundArchiveFile::PlayerInfo::GetPlayerHeapSize() const { + u32 value; + bool result {optionParameter.GetValue(&value, PlayerInfoBitFlag_HeapSize)}; + + if (!result) + return DefaultPlayerHeapSize; + + return value; +} + +u32 SoundArchiveFile::WaveArchiveInfo::GetWaveCount() const { + u32 value; + bool result {optionParameter.GetValue(&value, WaveArchiveInfoBitFlag_WaveCount)}; + + if (!result) + return DefaultWarcWaveCount; + + return value; +} + +SoundArchiveFile::FileLocationType SoundArchiveFile::FileInfo::GetFileLocationType() const { + switch (toFileLocation.typeId) { + case ElementType_SoundArchiveFile_InternalFileInfo: + return FileLocationType_Internal; + + case ElementType_SoundArchiveFile_ExternalFileInfo: + return FileLocationType_External; + + case 0: + return FileLocationType_None; + + default: + return FileLocationType_None; + } +} + +const SoundArchiveFile::InternalFileInfo* SoundArchiveFile::FileInfo::GetInternalFileInfo() const { + if (GetFileLocationType() != FileLocationType_Internal) + return nullptr; + + return util::ConstBytePtr(this, toFileLocation.offset).Get(); +} + +const SoundArchiveFile::ExternalFileInfo* SoundArchiveFile::FileInfo::GetExternalFileInfo() const { + if (GetFileLocationType() != FileLocationType_External) + return nullptr; + + return util::ConstBytePtr(this, toFileLocation.offset).Get(); +} +} // namespace nn::atk::detail \ No newline at end of file diff --git a/src/NintendoWare/atk/detail/atk_SoundArchiveFileReader.cpp b/src/NintendoWare/atk/detail/atk_SoundArchiveFileReader.cpp new file mode 100644 index 00000000..e69de29b diff --git a/src/NintendoWare/atk/detail/atk_SoundArchiveLoader.cpp b/src/NintendoWare/atk/detail/atk_SoundArchiveLoader.cpp new file mode 100644 index 00000000..e69de29b diff --git a/src/NintendoWare/atk/detail/atk_SoundArchiveManager.cpp b/src/NintendoWare/atk/detail/atk_SoundArchiveManager.cpp new file mode 100644 index 00000000..e69de29b diff --git a/src/NintendoWare/atk/detail/atk_StartInfoReader.cpp b/src/NintendoWare/atk/detail/atk_StartInfoReader.cpp new file mode 100644 index 00000000..e69de29b diff --git a/src/NintendoWare/atk/detail/atk_VolumeThroughModePacket.cpp b/src/NintendoWare/atk/detail/atk_VolumeThroughModePacket.cpp new file mode 100644 index 00000000..22663deb --- /dev/null +++ b/src/NintendoWare/atk/detail/atk_VolumeThroughModePacket.cpp @@ -0,0 +1,7 @@ +#include + +namespace nn::atk::detail { + + + +} // namespace nn::atk::detail \ No newline at end of file diff --git a/src/NintendoWare/atk/detail/atk_WaveArchiveFile.cpp b/src/NintendoWare/atk/detail/atk_WaveArchiveFile.cpp new file mode 100644 index 00000000..00b50b47 --- /dev/null +++ b/src/NintendoWare/atk/detail/atk_WaveArchiveFile.cpp @@ -0,0 +1,39 @@ +#include + +#include + +namespace nn::atk::detail { +const WaveArchiveFile::InfoBlock* WaveArchiveFile::FileHeader::GetInfoBlock() const { // 28 + return util::ConstBytePtr(this, GetInfoBlockOffset()).Get(); +} + +const WaveArchiveFile::FileBlock* WaveArchiveFile::FileHeader::GetFileBlock() const { // 34 + return util::ConstBytePtr(this, GetFileBlockOffset()).Get(); +} + +u32 WaveArchiveFile::FileHeader::GetInfoBlockSize() const { + return GetReferenceBy(ElementType_WaveArchiveFile_InfoBlock)->size; +} + +u32 WaveArchiveFile::FileHeader::GetFileBlockSize() const { + return GetReferenceBy(ElementType_WaveArchiveFile_FileBlock)->size; +} + +u32 WaveArchiveFile::FileHeader::GetInfoBlockOffset() const { + return GetReferenceBy(ElementType_WaveArchiveFile_InfoBlock)->offset; +} + +u32 WaveArchiveFile::FileHeader::GetFileBlockOffset() const { + return GetReferenceBy(ElementType_WaveArchiveFile_FileBlock)->offset; +} + +const Util::ReferenceWithSize* WaveArchiveFile::FileHeader::GetReferenceBy(u16 typeId) const { + for (int i {0}; i < BlockCount; ++i) { + if (toBlocks[i].IsValidTypeId(typeId)) { + return &toBlocks[i]; + } + } + + return nullptr; +} +} // namespace nn::atk::detail \ No newline at end of file diff --git a/src/NintendoWare/atk/detail/atk_WaveArchiveFileReader.cpp b/src/NintendoWare/atk/detail/atk_WaveArchiveFileReader.cpp new file mode 100644 index 00000000..e69de29b diff --git a/src/NintendoWare/atk/detail/atk_WaveFile.cpp b/src/NintendoWare/atk/detail/atk_WaveFile.cpp new file mode 100644 index 00000000..a852bbc0 --- /dev/null +++ b/src/NintendoWare/atk/detail/atk_WaveFile.cpp @@ -0,0 +1,29 @@ +#include + +#include + +namespace nn::atk::detail { +const WaveFile::InfoBlock* WaveFile::FileHeader::GetInfoBlock() const { + return util::ConstBytePtr(GetBlock(ElementType_WaveFile_InfoBlock)) + .Get(); +} + +const WaveFile::DataBlock* WaveFile::FileHeader::GetDataBlock() const { + return util::ConstBytePtr(GetBlock(ElementType_WaveFile_DataBlock)) + .Get(); +} + +const WaveFile::ChannelInfo& WaveFile::InfoBlockBody::GetChannelInfo(int channelIndex) const { + return *util::ConstBytePtr(channelInfoReferenceTable.GetReferedItem(channelIndex)) + .Get(); +} + +const void* WaveFile::ChannelInfo::GetSamplesAddress(const void* dataBlockBodyAddress) const { + return util::ConstBytePtr(dataBlockBodyAddress).Advance(referToSamples.offset).Get(); +} + +const WaveFile::DspAdpcmInfo& WaveFile::ChannelInfo::GetDspAdpcmInfo() const { + return *util::ConstBytePtr(this).Advance(referToAdpcmInfo.offset) + .Get(); +} +} // namespace nn::atk::detail \ No newline at end of file diff --git a/src/NintendoWare/atk/detail/atk_WaveFileReader.cpp b/src/NintendoWare/atk/detail/atk_WaveFileReader.cpp new file mode 100644 index 00000000..8ec15ff0 --- /dev/null +++ b/src/NintendoWare/atk/detail/atk_WaveFileReader.cpp @@ -0,0 +1,122 @@ +#include + +namespace nn::atk::detail { +const u32 SignatureInfoBlockWav {0x4f464e49}; // INFO +const u32 SignatureDataBlockWav {0x41544144}; // DATA + +const u32 SupportedFileVersionWav {0x10000}; +const u32 CurrentFileVersionWav {0x10200}; + +const u32 IncludeOriginalLoopVersionWav {0x10200}; + +namespace { +bool IsValidFileHeaderWav(const void* waveFile) { + const BinaryFileHeader& header {*util::ConstBytePtr(waveFile).Get()}; + + bool isSupportedVersion {header.signature == WaveFileReader::SignatureFile + && header.byteOrder == BinaryFileHeader::ValidByteOrderMark + && header.version >= SupportedFileVersionWav + && header.version <= CurrentFileVersionWav}; + + return isSupportedVersion; +} +} + +SampleFormat WaveFileReader::GetSampleFormat(u8 format) { + switch (format) { + case 0: + return SampleFormat_PcmS8; + case 1: + return SampleFormat_PcmS16; + case 2: + return SampleFormat_DspAdpcm; + default: + return SampleFormat_DspAdpcm; + } +} + +WaveFileReader::WaveFileReader(const void* waveFile, s8 waveType) { + m_WaveType = waveType; + switch (m_WaveType) { + case WaveType::WaveType_Nwwav: + if (IsValidFileHeaderWav(waveFile)) { + m_pHeader = util::ConstBytePtr(waveFile).Get(); + const WaveFile::InfoBlock* infoBlock {m_pHeader->GetInfoBlock()}; + const WaveFile::DataBlock* dataBlock {m_pHeader->GetDataBlock()}; + + if (infoBlock != nullptr && infoBlock->header.kind == SignatureInfoBlockWav + && dataBlock != nullptr && dataBlock->header.kind == SignatureDataBlockWav) { + m_pInfoBlockBody = &infoBlock->body; + m_pDataBlockBody = &dataBlock->byte; + } + } + break; + case WaveType::WaveType_Dspadpcm: + m_DspadpcmReader.Initialize(waveFile); + break; + case WaveType::WaveType_Invalid: + break; + } +} + +bool WaveFileReader::IsOriginalLoopAvailable() const { + const BinaryFileHeader& header {*util::ConstBytePtr(m_pHeader).Get()}; + + return header.version >= IncludeOriginalLoopVersionWav; +} + +bool WaveFileReader::ReadWaveInfo(WaveInfo* info, const void* waveDataOffsetOrigin) const { + switch (m_WaveType) { + case WaveType_Nwwav: { + const SampleFormat format {GetSampleFormat(m_pInfoBlockBody->encoding)}; + const int channelCount {m_pInfoBlockBody->GetChannelCount()}; + + info->sampleFormat = format; + info->channelCount = channelCount; + info->sampleRate = m_pInfoBlockBody->sampleRate; + info->loopFlag = m_pInfoBlockBody->isLoop == 1; + info->loopStartFrame = m_pInfoBlockBody->loopStartFrame; + info->loopEndFrame = m_pInfoBlockBody->loopEndFrame; + info->dataSize = m_pHeader->header.fileSize - sizeof(Util::SoundFileHeader); + + if (IsOriginalLoopAvailable()) + info->originalLoopStartFrame = m_pInfoBlockBody->originalLoopStartFrame; + else + info->originalLoopStartFrame = m_pInfoBlockBody->loopStartFrame; + + for (int i {0}; i < channelCount; ++i) { + if (i < 2) { + WaveInfo::ChannelParam& channelParam {info->channelParam[i]}; + const WaveFile::ChannelInfo& channelInfo {m_pInfoBlockBody->GetChannelInfo(i)}; + + if (channelInfo.referToAdpcmInfo.offset != 0) { + const WaveFile::DspAdpcmInfo& adpcmInfo {channelInfo.GetDspAdpcmInfo()}; + channelParam.adpcmParam = adpcmInfo.adpcmParam; + channelParam.adpcmLoopParam = adpcmInfo.adpcmLoopParam; + } + + channelParam.dataAddress = GetWaveDataAddress(&channelInfo, waveDataOffsetOrigin); + + const WaveFile::ChannelInfo& lastChannelInfo {m_pInfoBlockBody->GetChannelInfo(channelCount - 1)}; + channelParam.dataSize = m_pHeader->GetDataBlock()->header.size + - sizeof(BinaryBlockHeader) + - lastChannelInfo.referToSamples.offset; + } + } + break; + } + case WaveType_Dspadpcm: + m_DspadpcmReader.ReadWaveInfo(info); + break; + case WaveType_Invalid: + break; + } + + return true; +} + +const void* WaveFileReader::GetWaveDataAddress(const WaveFile::ChannelInfo* info, + [[maybe_unused]] const void* waveDataOffsetOrigin) const { + return info->GetSamplesAddress(m_pDataBlockBody); +} +} // namespace nn::atk::detail \ No newline at end of file diff --git a/src/NintendoWare/atk/detail/debug/atk_Debug.cpp b/src/NintendoWare/atk/detail/debug/atk_Debug.cpp new file mode 100644 index 00000000..3dd4cb29 --- /dev/null +++ b/src/NintendoWare/atk/detail/debug/atk_Debug.cpp @@ -0,0 +1,97 @@ +#include + +namespace nn::atk { +namespace { +const u32 NotEnoughSeqSound {0b00001}; +const u32 NotEnoughStrmSound {0b00010}; +const u32 NotEnoughWaveSound {0b00100}; +const u32 NotEnoughSeqTrack {0b01000}; +const u32 NotEnoughStrmChannel {0b10000}; +const u32 NotEnoughInstance {0b11111}; + +u32 gWarningFlag = NotEnoughInstance; + +u32 GetWarningBitFlag(DebugWarningFlag warning) { + u32 bitFlag {0}; + + switch (warning) { + case DebugWarningFlag_NotEnoughInstance: + bitFlag = NotEnoughInstance; + break; + + case DebugWarningFlag_NotEnoughSeqsound: + bitFlag = NotEnoughSeqSound; + break; + + case DebugWarningFlag_NotEnoughStrmsound: + bitFlag = NotEnoughStrmSound; + break; + + case DebugWarningFlag_NotEnoughWavesound: + bitFlag = NotEnoughWaveSound; + break; + + case DebugWarningFlag_NotEnoughSeqtrack: + bitFlag = NotEnoughSeqTrack; + break; + + case DebugWarningFlag_NotEnoughStrmchannel: + bitFlag = NotEnoughStrmChannel; + break; + } + + return bitFlag; +} +} // anonymous namespace + +void Debug_SetWarningFlag(DebugWarningFlag warning, bool enable) { + u32 bitFlag {GetWarningBitFlag(warning)}; + + if (enable) + gWarningFlag |= bitFlag; + else + gWarningFlag &= ~bitFlag; +} + +namespace detail { +DebugLogFunc g_DebugLogHookFunc; + +bool Debug_GetWarningFlag(DebugWarningFlag warning) { + u32 bitFlag {GetWarningBitFlag(warning)}; + + return (bitFlag & ~gWarningFlag) == 0; +} + +DebugWarningFlag Debug_GetDebugWarningFlagFromSoundType(DebugSoundType type) { + switch (type) { + case DebugSoundType_Seqsound: + return DebugWarningFlag_NotEnoughSeqsound; + + case DebugSoundType_Strmsound: + return DebugWarningFlag_NotEnoughStrmsound; + + case DebugSoundType_Wavesound: + return DebugWarningFlag_NotEnoughWavesound; + + default: + return DebugWarningFlag_NotEnoughSeqsound; + } +} + +const char* Debug_GetSoundTypeString(DebugSoundType type) { + switch (type) { + case DebugSoundType_Seqsound: + return "seq"; + + case DebugSoundType_Strmsound: + return "strm"; + + case DebugSoundType_Wavesound: + return "wave"; + + default: + return ""; + } +} +} // namespace nn::atk::detail +} // namespace nn::atk \ No newline at end of file diff --git a/src/NintendoWare/atk/detail/dsp/atk_BiquadFilterPresets.cpp b/src/NintendoWare/atk/detail/dsp/atk_BiquadFilterPresets.cpp new file mode 100644 index 00000000..e69de29b diff --git a/src/NintendoWare/atk/detail/dsp/atk_DecodeAdpcm.cpp b/src/NintendoWare/atk/detail/dsp/atk_DecodeAdpcm.cpp new file mode 100644 index 00000000..fa33e6bb --- /dev/null +++ b/src/NintendoWare/atk/detail/dsp/atk_DecodeAdpcm.cpp @@ -0,0 +1,67 @@ +#include + +namespace nn::atk::detail { +void DecodeDspAdpcm(position_t playPosition, + AdpcmContext& context, + const AdpcmParam& param, + const void* adpcmData, + size_t decodeSamples, + s16* dest) +{ + position_t frame = playPosition / 14; + position_t frameFrac = playPosition - frame * 14; + const u8* frameBegin = reinterpret_cast(adpcmData) + (frame * 8); + + s32 pred = context.audioAdpcmContext.predScale >> 4; + s32 scale = context.audioAdpcmContext.predScale & 0xF; + + for (u32 i {0}; i < decodeSamples; ++i) { + if (frameFrac == 0) { + const u8 pred_scale = *frameBegin; + context.audioAdpcmContext.predScale = pred_scale; + pred = pred_scale >> 4; + scale = pred_scale & 0xF; + } + + u8 code = frameBegin[frameFrac / 2 + 1]; + if (frameFrac & 1) + code &= 0xF; + else + code >>= 4; + + s16 nibble = code; + nibble <<= 12; + nibble >>= 1; + + s16 a1 = static_cast(param.coefficients[pred * 2 + 0]); + s16 a2 = static_cast(param.coefficients[pred * 2 + 1]); + s16 gain = static_cast(1 << scale); + + s32 val = a1 * context.audioAdpcmContext.history[0]; + val += a2 * context.audioAdpcmContext.history[1]; + val += gain * nibble; + val >>= 10; + val += 1; + val >>= 1; + + if (val > SHRT_MAX) + val = SHRT_MAX; + else if (val < SHRT_MIN) + val = SHRT_MIN; + + s16 smp = static_cast(val); + + context.audioAdpcmContext.history[1] = context.audioAdpcmContext.history[0]; + context.audioAdpcmContext.history[0] = smp; + + *dest = smp; + ++dest; + + ++frameFrac; + if (frameFrac == 14) { + frameBegin += 8; + frameFrac = 0; + } + } +} +} // namespace nn::atk::detail \ No newline at end of file diff --git a/src/NintendoWare/atk/detail/dsp/atk_DspadpcmReader.cpp b/src/NintendoWare/atk/detail/dsp/atk_DspadpcmReader.cpp new file mode 100644 index 00000000..5a419c2f --- /dev/null +++ b/src/NintendoWare/atk/detail/dsp/atk_DspadpcmReader.cpp @@ -0,0 +1,51 @@ +#include + +#include + +#include + +namespace nn::atk::detail { +namespace { +struct InternalDSPADPCMInfo { + u32 sampleCount; + u32 adpcmNibbleCount; + u32 sampleRate; + u16 loopFlag; + u16 format; + u32 sa; + u32 ea; + u32 ca; + u16 coef[16]; + u16 gain; + u16 ps; + u16 yn1; + u16 yn2; + u16 lps; + u16 lyn1; + u16 lyn2; + u16 pad[11]; +}; +static_assert(sizeof(InternalDSPADPCMInfo) == 0x60); +} // anonymous namespace + +DspadpcmReader::DspadpcmReader() = default; + +bool DspadpcmReader::ReadWaveInfo(WaveInfo* info) const { + const InternalDSPADPCMInfo& data {*util::ConstBytePtr(m_pDspadpcmData).Get()}; + + info->sampleFormat = SampleFormat_DspAdpcm; + info->loopFlag = false; + info->channelCount = 1; + info->sampleRate = data.sampleRate; + info->loopStartFrame = 0; + info->loopEndFrame = data.sampleCount; + + info->channelParam[0].dataAddress = util::ConstBytePtr(m_pDspadpcmData, sizeof(InternalDSPADPCMInfo)).Get(); + std::memcpy(info->channelParam[0].adpcmParam.coef, data.coef, sizeof(data.coef)); + int yn1 = data.yn1; + info->channelParam[0].adpcmParam.predScale = data.ps; + info->channelParam[0].adpcmParam.yn1 = yn1; + info->channelParam[0].adpcmParam.yn2 = data.yn2; + return true; +} +} // namespace nn::atk::detail \ No newline at end of file diff --git a/src/NintendoWare/atk/detail/dsp/atk_HardwareManager.cpp b/src/NintendoWare/atk/detail/dsp/atk_HardwareManager.cpp new file mode 100644 index 00000000..e69de29b diff --git a/src/NintendoWare/atk/detail/seq/atk_Bank.cpp b/src/NintendoWare/atk/detail/seq/atk_Bank.cpp new file mode 100644 index 00000000..e69de29b diff --git a/src/NintendoWare/atk/detail/seq/atk_BankFile.cpp b/src/NintendoWare/atk/detail/seq/atk_BankFile.cpp new file mode 100644 index 00000000..3839c724 --- /dev/null +++ b/src/NintendoWare/atk/detail/seq/atk_BankFile.cpp @@ -0,0 +1,274 @@ +#include + +#include + +namespace nn::atk::detail { +namespace { +const u8 DefaultOriginalKey {60}; +const u8 DefaultVolume {127}; +const u8 DefaultPan {64}; +const float DefaultPitch {1.0}; +const bool DefaultIgnoreNoteOff {false}; +const u8 DefaultKeyGroup {0}; +const u8 DefaultInterpolationType {0}; +const AdshrCurve DefaultAdshrCurve {127, 127, 127, 127, 127}; + +enum VelocityRegionBitFlag { + VelocityRegionBitFlag_Key = 0, + VelocityRegionBitFlag_Volume = 1, + VelocityRegionBitFlag_Pan = 2, + VelocityRegionBitFlag_Pitch = 3, + VelocityRegionBitFlag_InstrumentNoteParam = 4, + VelocityRegionBitFlag_Sends = 8, + VelocityRegionBitFlag_Envelope = 9, + VelocityRegionBitFlag_Randomizer = 10, + VelocityRegionBitFlag_Lfo = 11, + VelocityRegionBitFlag_BasicParamFlag = 0b01000011111 +}; + +struct DirectChunk { + Util::Reference toRegion; + + const void* GetRegion() const { + return util::ConstBytePtr(this, toRegion.offset).Get(); + } +}; +static_assert(sizeof(DirectChunk) == 0x8); + +struct RangeChunk { + Util::Table borderTable; + + const Util::Reference& GetRegionTableAddress(int index) const { + return *util::ConstBytePtr(this, sizeof(borderTable.count) + + util::align_up(borderTable.count, 4) + + sizeof(Util::Reference) * index).Get(); + } + + const void* GetRegion(u32 index) const { + bool isFoundRangeChunkIndex {false}; + u32 regionTableIndex {0}; + for (u32 i {0}; i < borderTable.count; ++i) { + if (index <= borderTable.item[i]) { + regionTableIndex = i; + isFoundRangeChunkIndex = true; + break; + } + } + + if (!isFoundRangeChunkIndex) + return nullptr; + + const Util::Reference& ref {GetRegionTableAddress(regionTableIndex)}; + return util::ConstBytePtr(this, ref.offset).Get(); + } +}; +static_assert(sizeof(RangeChunk) == 0x8); + +struct IndexChunk { + u8 min; + u8 max; + u8 reserved[2]; + Util::Reference toRegion[1]; + + const void* GetRegion(u32 index) const { + if (index >= min && index <= max) + return util::ConstBytePtr(this, toRegion[index - min].offset).Get(); + + return nullptr; + } +}; +static_assert(sizeof(IndexChunk) == 0xc); + +enum RegionType { + RegionType_Direct, + RegionType_Range, + RegionType_Index, + RegionType_Unknown +}; + +RegionType GetRegionType(u16 typeId) { + switch (typeId) { + case ElementType_BankFile_DirectReferenceTable: + return RegionType_Direct; + + case ElementType_BankFile_RangeReferenceTable: + return RegionType_Range; + + case ElementType_BankFile_IndexReferenceTable: + return RegionType_Index; + + default: + return RegionType_Unknown; + + } +} + +const void* GetDirectChunk(const void* regionChunk) { + const DirectChunk& directChunk {*reinterpret_cast(regionChunk)}; + + return directChunk.GetRegion(); +} + +const void* GetRangeChunk(const void* regionChunk, u32 index) { + const RangeChunk& rangeChunk {*reinterpret_cast(regionChunk)}; + + return rangeChunk.GetRegion(index); +} + +const void* GetIndexChunk(const void* regionChunk, u32 index) { + const IndexChunk& indexChunk {*reinterpret_cast(regionChunk)}; + + return indexChunk.GetRegion(index); +} + +const void* GetRegion(const void* startPtr, u16 typeId, u32 offset, u32 index) { + const void* regionChunk {util::ConstBytePtr(startPtr, offset).Get()}; + const void* region {nullptr}; + + switch (GetRegionType(typeId)) { + case RegionType_Direct: + region = GetDirectChunk(regionChunk); + break; + + case RegionType_Range: + region = GetRangeChunk(regionChunk, index); + break; + + case RegionType_Index: + region = GetIndexChunk(regionChunk, index); + break; + + case RegionType_Unknown: + default: + region = nullptr; + break; + } + + return region; +} +} // anonymous namespace + +const BankFile::InfoBlock* BankFile::FileHeader::GetInfoBlock() const { + return util::ConstBytePtr(GetBlock(ElementType_BankFile_InfoBlock)) + .Get(); +} + +const Util::WaveIdTable& BankFile::InfoBlockBody::GetWaveIdTable() const { + return *util::ConstBytePtr(this).Advance(toWaveIdTable.offset) + .Get(); +} + +const Util::ReferenceTable& BankFile::InfoBlockBody::GetInstrumentReferenceTable() const { + return *util::ConstBytePtr(this).Advance(toInstrumentReferenceTable.offset) + .Get(); +} + +const BankFile::Instrument* BankFile::InfoBlockBody::GetInstrument(int programNo) const { + auto& table {GetInstrumentReferenceTable()}; + auto& ref {table.item[programNo]}; + + if (ref.IsValidTypeId(ElementType_BankFile_InstrumentInfo)) + return util::ConstBytePtr(table.GetReferedItem(programNo)).Get(); + + return nullptr; +} + +const BankFile::KeyRegion* BankFile::Instrument::GetKeyRegion(u32 key) const { + return util::ConstBytePtr( + GetRegion(this, + toKeyRegionChunk.typeId, + toKeyRegionChunk.offset, + key) + ).Get(); +} + +const BankFile::VelocityRegion* BankFile::KeyRegion::GetVelocityRegion(u32 velocity) const { + return util::ConstBytePtr( + GetRegion(this, + toVelocityRegionChunk.typeId, + toVelocityRegionChunk.offset, + velocity) + ).Get(); +} + +u8 BankFile::VelocityRegion::GetOriginalKey() const { + u32 value; + bool result {optionParameter.GetValue(&value, VelocityRegionBitFlag_Key)}; + if (result) + return value; + + return DefaultOriginalKey; +} + +u8 BankFile::VelocityRegion::GetVolume() const { + u32 value; + bool result {optionParameter.GetValue(&value, VelocityRegionBitFlag_Volume)}; + if (result) + return Util::DivideBy8bit(value, 0); + + return DefaultVolume; +} + +u8 BankFile::VelocityRegion::GetPan() const { + u32 value; + bool result {optionParameter.GetValue(&value, VelocityRegionBitFlag_Pan)}; + if (result) + return Util::DivideBy8bit(value, 0); + + return DefaultPan; +} + +float BankFile::VelocityRegion::GetPitch() const { + float value; + bool result {optionParameter.GetValueF32(&value, VelocityRegionBitFlag_Pitch)}; + if (result) + return value; + + return DefaultPitch; +} + +bool BankFile::VelocityRegion::IsIgnoreNoteOff() const { + u32 value; + bool result {optionParameter.GetValue(&value, VelocityRegionBitFlag_InstrumentNoteParam)}; + if (result) + return Util::DivideBy8bit(value, 0) != 0; + + return DefaultIgnoreNoteOff; +} + +u8 BankFile::VelocityRegion::GetKeyGroup() const { + u32 value; + bool result {optionParameter.GetValue(&value, VelocityRegionBitFlag_InstrumentNoteParam)}; + if (result) + return Util::DivideBy8bit(value, 1); + + return DefaultKeyGroup; +} + +u8 BankFile::VelocityRegion::GetInterpolationType() const { + u32 value; + bool result {optionParameter.GetValue(&value, VelocityRegionBitFlag_InstrumentNoteParam)}; + if (result) + return Util::DivideBy8bit(value, 2); + + return DefaultInterpolationType; +} + +const AdshrCurve& BankFile::VelocityRegion::GetAdshrCurve() const { + u32 offsetToReference; + bool result {optionParameter.GetValue(&offsetToReference, VelocityRegionBitFlag_Envelope)}; + if (result) { + const auto& ref {*util::ConstBytePtr(this, offsetToReference).Get()}; + return *util::ConstBytePtr(&ref, ref.offset).Get(); + } + + return DefaultAdshrCurve; +} + +const BankFile::RegionParameter* BankFile::VelocityRegion::GetRegionParameter() const { + if (optionParameter.bitFlag != VelocityRegionBitFlag_BasicParamFlag) + return nullptr; + + return util::ConstBytePtr(this, sizeof(VelocityRegion)).Get(); +} +} // namespace nn::atk::detail \ No newline at end of file diff --git a/src/NintendoWare/atk/detail/seq/atk_BankFileReader.cpp b/src/NintendoWare/atk/detail/seq/atk_BankFileReader.cpp new file mode 100644 index 00000000..e69de29b diff --git a/src/NintendoWare/atk/detail/seq/atk_MmlParser.cpp b/src/NintendoWare/atk/detail/seq/atk_MmlParser.cpp new file mode 100644 index 00000000..e69de29b diff --git a/src/NintendoWare/atk/detail/seq/atk_MmlSequenceTrack.cpp b/src/NintendoWare/atk/detail/seq/atk_MmlSequenceTrack.cpp new file mode 100644 index 00000000..e69de29b diff --git a/src/NintendoWare/atk/detail/seq/atk_MmlSequenceTrackAllocator.cpp b/src/NintendoWare/atk/detail/seq/atk_MmlSequenceTrackAllocator.cpp new file mode 100644 index 00000000..e69de29b diff --git a/src/NintendoWare/atk/detail/seq/atk_SequenceSound.cpp b/src/NintendoWare/atk/detail/seq/atk_SequenceSound.cpp new file mode 100644 index 00000000..e69de29b diff --git a/src/NintendoWare/atk/detail/seq/atk_SequenceSoundFile.cpp b/src/NintendoWare/atk/detail/seq/atk_SequenceSoundFile.cpp new file mode 100644 index 00000000..7a8b630a --- /dev/null +++ b/src/NintendoWare/atk/detail/seq/atk_SequenceSoundFile.cpp @@ -0,0 +1,55 @@ +#include + +#include + +#include + +namespace nn::atk::detail { +const SequenceSoundFile::DataBlock* SequenceSoundFile::FileHeader::GetDataBlock() const { + return util::ConstBytePtr(GetBlock(ElementType_SequenceSoundFile_DataBlock)) + .Get(); +} + +const SequenceSoundFile::LabelBlock* SequenceSoundFile::FileHeader::GetLabelBlock() const { + return util::ConstBytePtr(GetBlock(ElementType_SequenceSoundFile_LabelBlock)) + .Get(); +} + +const SequenceSoundFile::LabelInfo* SequenceSoundFile::LabelBlockBody::GetLabelInfo(int index) const { + return util::ConstBytePtr(labelInfoReferenceTable.GetReferedItem(index)).Get(); +} + +const char* SequenceSoundFile::LabelBlockBody::GetLabel(int index) const { + const LabelInfo* labelInfo {GetLabelInfo(index)}; + return labelInfo->label; +} + +const char* SequenceSoundFile::LabelBlockBody::GetLabelByOffset(u32 offset) const { + for (int i {0}; i < GetLabelCount(); ++i) { + const LabelInfo* labelInfo {GetLabelInfo(i)}; + if (static_cast(labelInfo->referToSequenceData.offset) == offset) + return labelInfo->label; + } + + return nullptr; +} + +bool SequenceSoundFile::LabelBlockBody::GetOffset(int index, u32* offsetPtr) const { + const LabelInfo* labelInfo {GetLabelInfo(index)}; + *offsetPtr = labelInfo->referToSequenceData.offset; + return true; +} + +bool SequenceSoundFile::LabelBlockBody::GetOffsetByLabel(const char* label, u32* offsetPtr) const { + const size_t labelLength {strlen(label)}; + for (int i {0}; i < GetLabelCount(); ++i) { + const LabelInfo* labelInfo {GetLabelInfo(i)}; + if (strncmp(label, labelInfo->label, labelLength) == 0) { + *offsetPtr = labelInfo->referToSequenceData.offset; + return true; + } + } + + return false; +} +} // namespace nn::atk::detail \ No newline at end of file diff --git a/src/NintendoWare/atk/detail/seq/atk_SequenceSoundFileReader.cpp b/src/NintendoWare/atk/detail/seq/atk_SequenceSoundFileReader.cpp new file mode 100644 index 00000000..e69de29b diff --git a/src/NintendoWare/atk/detail/seq/atk_SequenceSoundPlayer.cpp b/src/NintendoWare/atk/detail/seq/atk_SequenceSoundPlayer.cpp new file mode 100644 index 00000000..e69de29b diff --git a/src/NintendoWare/atk/detail/seq/atk_SequenceSoundRuntime.cpp b/src/NintendoWare/atk/detail/seq/atk_SequenceSoundRuntime.cpp new file mode 100644 index 00000000..e69de29b diff --git a/src/NintendoWare/atk/detail/seq/atk_SequenceTrack.cpp b/src/NintendoWare/atk/detail/seq/atk_SequenceTrack.cpp new file mode 100644 index 00000000..e69de29b diff --git a/src/NintendoWare/atk/detail/strm/atk_StreamBufferPool.cpp b/src/NintendoWare/atk/detail/strm/atk_StreamBufferPool.cpp new file mode 100644 index 00000000..fc01b1c8 --- /dev/null +++ b/src/NintendoWare/atk/detail/strm/atk_StreamBufferPool.cpp @@ -0,0 +1,66 @@ +#include + +#include +#include "nn/types.h" + +namespace nn::atk::detail::driver { +void StreamBufferPool::Initialize(void* buffer, size_t size, int blockCount) { + if (blockCount == 0) + return; + + Util::IsValidMemoryForDsp(buffer, size); + + m_Buffer = buffer; + m_BufferSize = size; + m_BlockCount = blockCount; + m_BlockSize = util::align_down(size / blockCount, 64); + m_AllocCount = 0; + for (int i {0}; i < BlockMax / BitPerByte; ++i) + m_AllocFlags[i] = 0; +} + +void StreamBufferPool::Finalize() { + m_Buffer = nullptr; + m_BufferSize = 0; + m_BlockSize = 0; + m_BlockCount = 0; +} + +// WIP +void* StreamBufferPool::Alloc() { + if (m_AllocCount >= m_BlockCount) + return nullptr; + + const int availableByte {util::align_up(m_BlockCount, BitPerByte)}; + + for (int byteIndex {0}; byteIndex < availableByte / BitPerByte; ++byteIndex) { + if (m_AllocFlags[byteIndex] != 0xff) { + const u8 byte {m_AllocFlags[byteIndex]}; + + u8 mask {1 << 0}; + for (int bitIndex {0}; bitIndex < BitPerByte; ++bitIndex) { + if ((byte & mask) == 0) { + const int totalIndex {bitIndex}; + void* buffer {util::BytePtr(m_Buffer).Advance(m_BlockSize * (totalIndex | byte)).Get()}; + return buffer; + } + } + } + } + + return nullptr; +} + +void StreamBufferPool::Free(void* pPtr) { + ptrdiff_t offset {util::BytePtr(m_Buffer).Distance(pPtr)}; + + const u64 totalIndex {offset / m_BlockSize}; + + const int mask {1 << (totalIndex & 0b111)}; + const u64 byteIndex {totalIndex}; + const u64 bitIndex {byteIndex / BitPerByte}; + + m_AllocFlags[bitIndex] &= ~mask; + --m_AllocCount; +} +} // namespace nn::atk::detail::driver \ No newline at end of file diff --git a/src/NintendoWare/atk/detail/strm/atk_StreamSound.cpp b/src/NintendoWare/atk/detail/strm/atk_StreamSound.cpp new file mode 100644 index 00000000..e69de29b diff --git a/src/NintendoWare/atk/detail/strm/atk_StreamSoundFile.cpp b/src/NintendoWare/atk/detail/strm/atk_StreamSoundFile.cpp new file mode 100644 index 00000000..4170fd45 --- /dev/null +++ b/src/NintendoWare/atk/detail/strm/atk_StreamSoundFile.cpp @@ -0,0 +1,95 @@ +#include + +#include + +namespace nn::atk::detail { +bool StreamSoundFile::FileHeader::HasSeekBlock() const { + return GetReferenceBy(ElementType_StreamSoundFile_SeekBlock) != nullptr; +} + +bool StreamSoundFile::FileHeader::HasRegionBlock() const { + return GetReferenceBy(ElementType_StreamSoundFile_RegionBlock) != nullptr; +} + +u32 StreamSoundFile::FileHeader::GetInfoBlockSize() const { + return GetReferenceBy(ElementType_StreamSoundFile_InfoBlock)->size; +} + +u32 StreamSoundFile::FileHeader::GetSeekBlockSize() const { + return GetReferenceBy(ElementType_StreamSoundFile_SeekBlock)->size; +} + +u32 StreamSoundFile::FileHeader::GetDataBlockSize() const { + return GetReferenceBy(ElementType_StreamSoundFile_DataBlock)->size; +} + +u32 StreamSoundFile::FileHeader::GetRegionBlockSize() const { + return GetReferenceBy(ElementType_StreamSoundFile_RegionBlock)->size; +} + +u32 StreamSoundFile::FileHeader::GetInfoBlockOffset() const { + return GetReferenceBy(ElementType_StreamSoundFile_InfoBlock)->offset; +} + +u32 StreamSoundFile::FileHeader::GetSeekBlockOffset() const { + return GetReferenceBy(ElementType_StreamSoundFile_SeekBlock)->offset; +} + +u32 StreamSoundFile::FileHeader::GetDataBlockOffset() const { + return GetReferenceBy(ElementType_StreamSoundFile_DataBlock)->offset; +} + +u32 StreamSoundFile::FileHeader::GetRegionBlockOffset() const { + return GetReferenceBy(ElementType_StreamSoundFile_RegionBlock)->offset; +} + +const Util::ReferenceWithSize* StreamSoundFile::FileHeader::GetReferenceBy(u16 typeId) const { + for (int i {0}; i < dataBlocks; ++i) { + const Util::ReferenceWithSize* p {&toBlocks[i]}; + if (p->typeId == typeId) + return p; + } + + return nullptr; +} + +const StreamSoundFile::StreamSoundInfo* StreamSoundFile::InfoBlockBody::GetStreamSoundInfo() const { + if (toStreamSoundInfo.IsValidTypeId(ElementType_StreamSoundFile_StreamSoundInfo)) + return util::ConstBytePtr(this).Advance(toStreamSoundInfo.offset).Get(); + + return nullptr; +} + +const StreamSoundFile::TrackInfoTable* StreamSoundFile::InfoBlockBody::GetTrackInfoTable() const { + if (toTrackInfoTable.IsValidTypeId(ElementType_Table_ReferenceTable)) + return util::ConstBytePtr(this).Advance(toTrackInfoTable.offset).Get(); + + return nullptr; +} + +const StreamSoundFile::ChannelInfoTable* StreamSoundFile::InfoBlockBody::GetChannelInfoTable() const { + if (toChannelInfoTable.IsValidTypeId(ElementType_Table_ReferenceTable)) + return util::ConstBytePtr(this).Advance(toChannelInfoTable.offset).Get(); + + return nullptr; +} + +const StreamSoundFile::TrackInfo* StreamSoundFile::TrackInfoTable::GetTrackInfo(u32 index) const { + return static_cast(table.GetReferedItem(index, ElementType_StreamSoundFile_TrackInfo)); +} + +u32 StreamSoundFile::ChannelInfoTable::GetChannelCount() const { + return table.count; +} + +const StreamSoundFile::ChannelInfo* StreamSoundFile::ChannelInfoTable::GetChannelInfo(u32 index) const { + return static_cast(table.GetReferedItem(index, ElementType_StreamSoundFile_ChannelInfo)); +} + +const StreamSoundFile::DspAdpcmChannelInfo* StreamSoundFile::ChannelInfo::GetDspAdpcmChannelInfo() const { + if (toDetailChannelInfo.IsValidTypeId(ElementType_Codec_DspAdpcmInfo)) + return util::ConstBytePtr(this).Advance(toDetailChannelInfo.offset).Get(); + + return nullptr; +} +} // namespace nn::atk::detail \ No newline at end of file diff --git a/src/NintendoWare/atk/detail/strm/atk_StreamSoundFileLoader.cpp b/src/NintendoWare/atk/detail/strm/atk_StreamSoundFileLoader.cpp new file mode 100644 index 00000000..2c48c0d6 --- /dev/null +++ b/src/NintendoWare/atk/detail/strm/atk_StreamSoundFileLoader.cpp @@ -0,0 +1,75 @@ +#include + +namespace nn::atk::detail { +bool StreamSoundFileLoader::LoadFileHeader(StreamSoundFileReader* reader, void* buffer, u64 size) { + const size_t HeaderSize {0x60}; + const size_t AlignSize {64}; + char buffer2[HeaderSize + AlignSize]; + + m_pStream->Seek(0, fnd::FileStream::SeekOrigin_Begin); + size_t readSize {m_pStream->Read(util::BytePtr(buffer2).AlignUp(AlignSize).Get(), HeaderSize, nullptr)}; + auto* header {util::BytePtr(buffer2).AlignUp(AlignSize).Get()}; + + if (readSize == HeaderSize && reader->IsValidFileHeader(header)) { + u32 loadSize {header->GetInfoBlockOffset() + header->GetInfoBlockSize()}; + if (loadSize <= size) { + m_pStream->Seek(0, fnd::FileStream::SeekOrigin_Begin); + readSize = m_pStream->Read(buffer, loadSize, nullptr); + if (static_cast(readSize) == static_cast(loadSize)) { + reader->Initialize(buffer); + m_SeekBlockOffset = reader->GetSeekBlockOffset(); + m_RegionDataOffset = reader->GetRegionDataOffset(); + m_RegionInfoBytes = reader->GetRegionInfoBytes(); + return true; + } + } + } + + return false; +} + +bool StreamSoundFileLoader::ReadSeekBlockData(u16* yn1, u16* yn2, int blockIndex, int channelCount) { + const size_t SeekInfoMaxSize {channelCount * sizeof(u16) * 2}; + size_t readDataSize {SeekInfoMaxSize}; + size_t readOffset {m_SeekBlockOffset + sizeof(BinaryBlockHeader) + blockIndex * readDataSize}; + + m_pStream->Seek(static_cast(readOffset), fnd::FileStream::SeekOrigin_Begin); + if (readDataSize <= 64) { + const int Align {64}; + u16 bufferBase[128]; + u16* buffer {util::BytePtr(bufferBase).AlignUp(Align).Get()}; + size_t readSize {m_pStream->Read(buffer, readDataSize, nullptr)}; + + if (readSize == readDataSize) { + for (int i {0}; i < channelCount; ++i) { + yn1[i] = buffer[i * sizeof(u16)]; + yn2[i] = buffer[i * sizeof(u16) + 1]; + } + return true; + } + } + + return false; +} + +bool StreamSoundFileLoader::ReadRegionInfo(StreamSoundFile::RegionInfo* pInfo, u32 regionIndex) const { + if (m_RegionDataOffset != 0 && m_RegionInfoBytes != 0) { + position_t offset {m_RegionDataOffset + static_cast(m_RegionInfoBytes) * regionIndex}; + m_pStream->Seek(offset, fnd::FileStream::SeekOrigin_Begin); + + const int Align {64}; + const size_t ReadSize {sizeof(StreamSoundFile::RegionInfo)}; + u8 bufferBase[ReadSize + Align]; + u8* buffer {util::BytePtr(bufferBase).AlignUp(Align).Get()}; + + size_t readSize {m_pStream->Read(buffer, ReadSize, nullptr)}; + if (readSize == ReadSize) { + *pInfo = *reinterpret_cast(buffer); + return true; + } + return false; + } + + return false; +} +} // namespace nn::atk::detail \ No newline at end of file diff --git a/src/NintendoWare/atk/detail/strm/atk_StreamSoundFileReader.cpp b/src/NintendoWare/atk/detail/strm/atk_StreamSoundFileReader.cpp new file mode 100644 index 00000000..15a08568 --- /dev/null +++ b/src/NintendoWare/atk/detail/strm/atk_StreamSoundFileReader.cpp @@ -0,0 +1,143 @@ +#include + +namespace nn::atk::detail { +const u32 SignatureFileStm {0x4d545346}; // FSTM +const u32 SignatureInfoBlockStm {0x4f464e49}; // INFO + +const u32 SupportedFileVersionStm {0x10000}; +const u32 CurrentFileVersionStm {0x60200}; + +const u32 IncludeTrackinfoVersionStm {0x20000}; +const u32 IncludeOriginalloopVersionStm {0x40000}; +const u32 IncludeCrc32CheckVersionStm {0x50000}; +const u32 IncludeRegionIndexCheckVersionStm {0x60100}; + +StreamSoundFileReader::StreamSoundFileReader() = default; + +bool StreamSoundFileReader::IsValidFileHeader(const void* streamSoundFile) { + const BinaryFileHeader& header {*util::ConstBytePtr(streamSoundFile).Get()}; + + bool isSupportedVersion {header.signature == SignatureFileStm + && header.byteOrder == BinaryFileHeader::ValidByteOrderMark + && header.version >= SupportedFileVersionStm + && header.version <= CurrentFileVersionStm}; + + return isSupportedVersion; +} + +void StreamSoundFileReader::Initialize(const void* streamSoundFile) { + if (IsValidFileHeader(streamSoundFile)) { + m_pHeader = util::ConstBytePtr(streamSoundFile).Get(); + const StreamSoundFile::InfoBlock* infoBlock {m_pHeader->GetInfoBlock()}; + if (infoBlock->header.kind == SignatureInfoBlockStm) + m_pInfoBlockBody = &infoBlock->body; + } +} + +void StreamSoundFileReader::Finalize() { + m_pHeader = nullptr; + m_pInfoBlockBody = nullptr; +} + +bool StreamSoundFileReader::IsTrackInfoAvailable() const { + auto& header {*util::ConstBytePtr(m_pHeader).Get()}; + + return header.version <= IncludeTrackinfoVersionStm; +} + +bool StreamSoundFileReader::IsOriginalLoopAvailable() const { + return IsOriginalLoopAvailableImpl(m_pHeader); +} + +bool StreamSoundFileReader::IsOriginalLoopAvailableImpl(const StreamSoundFile::FileHeader* pHeader) { + return pHeader->version >= IncludeOriginalloopVersionStm; +} + +bool StreamSoundFileReader::IsCrc32CheckAvailable() const { + auto& header {*util::ConstBytePtr(m_pHeader).Get()}; + + return header.version >= IncludeCrc32CheckVersionStm; +} + +bool StreamSoundFileReader::IsRegionIndexCheckAvailable() const { + auto& header {*util::ConstBytePtr(m_pHeader).Get()}; + + return header.version >= IncludeRegionIndexCheckVersionStm; +} + +bool StreamSoundFileReader::ReadStreamSoundInfo(StreamSoundFile::StreamSoundInfo* strmInfo) const { + auto* info {m_pInfoBlockBody->GetStreamSoundInfo()}; + + strmInfo->encodeMethod = info->encodeMethod; + strmInfo->isLoop = info->isLoop; + strmInfo->channelCount = info->channelCount; + strmInfo->regionCount = info->regionCount; + strmInfo->sampleRate = info->sampleRate; + strmInfo->loopStart = info->loopStart; + strmInfo->frameCount = info->frameCount; + strmInfo->blockCount = info->blockCount; + strmInfo->oneBlockBytes = info->oneBlockBytes; + strmInfo->oneBlockSamples = info->oneBlockSamples; + strmInfo->lastBlockBytes = info->lastBlockBytes; + strmInfo->lastBlockSamples = info->lastBlockSamples; + strmInfo->lastBlockPaddedBytes = info->lastBlockPaddedBytes; + strmInfo->sizeofSeekInfoAtom = info->sizeofSeekInfoAtom; + strmInfo->seekInfoIntervalSamples = info->seekInfoIntervalSamples; + strmInfo->sampleDataOffset = info->sampleDataOffset; + strmInfo->regionInfoBytes = info->regionInfoBytes; + strmInfo->regionDataOffset = info->regionDataOffset; + + if (IsOriginalLoopAvailable()) { + strmInfo->originalLoopStart = info->originalLoopStart; + strmInfo->originalLoopEnd = info->originalLoopEnd; + } + else { + strmInfo->originalLoopStart = info->loopStart; + strmInfo->originalLoopEnd = info->frameCount; + } + + if (IsCrc32CheckAvailable()) { + strmInfo->crc32Value = info->crc32Value; + } + else { + strmInfo->crc32Value = 0; + } + + return true; +} + +bool StreamSoundFileReader::ReadStreamTrackInfo(TrackInfo* pTrackInfo, int trackIndex) const { + auto* table {m_pInfoBlockBody->GetTrackInfoTable()}; + if (table != nullptr && trackIndex < static_cast(table->GetTrackCount())) { + auto* src {table->GetTrackInfo(trackIndex)}; + pTrackInfo->volume = src->volume; + pTrackInfo->pan = src->pan; + pTrackInfo->span = src->span; + pTrackInfo->flags = src->flags; + pTrackInfo->channelCount = src->GetTrackChannelCount(); + + u32 count {pTrackInfo->channelCount}; + if (count > 1) + count = 2; + + for (u32 i {0}; i < count; ++i) + pTrackInfo->globalChannelIndex[i] = src->GetGlobalChannelIndex(i); + + return true; + } + + return false; +} + +bool StreamSoundFileReader::ReadDspAdpcmChannelInfo(DspAdpcmParam* pParam, DspAdpcmLoopParam* pLoopParam, + int channelIndex) const { + auto* src {m_pInfoBlockBody->GetChannelInfoTable()->GetChannelInfo(channelIndex)->GetDspAdpcmChannelInfo()}; + if (src != nullptr) { + *pParam = src->param; + *pLoopParam = src->loopParam; + return true; + } + + return false; +} +} // namespace nn::atk::detail \ No newline at end of file diff --git a/src/NintendoWare/atk/detail/strm/atk_StreamSoundLoader.cpp b/src/NintendoWare/atk/detail/strm/atk_StreamSoundLoader.cpp new file mode 100644 index 00000000..e69de29b diff --git a/src/NintendoWare/atk/detail/strm/atk_StreamSoundPlayer.cpp b/src/NintendoWare/atk/detail/strm/atk_StreamSoundPlayer.cpp new file mode 100644 index 00000000..e69de29b diff --git a/src/NintendoWare/atk/detail/strm/atk_StreamSoundPrefetchFile.cpp b/src/NintendoWare/atk/detail/strm/atk_StreamSoundPrefetchFile.cpp new file mode 100644 index 00000000..9c902916 --- /dev/null +++ b/src/NintendoWare/atk/detail/strm/atk_StreamSoundPrefetchFile.cpp @@ -0,0 +1,43 @@ +#include + +#include + +namespace nn::atk::detail { +const StreamSoundFile::InfoBlock* StreamSoundPrefetchFile::FileHeader::GetInfoBlock() const { + return util::ConstBytePtr(GetBlock(ElementType_StreamSoundFile_InfoBlock)) + .Get(); +} + +const StreamSoundFile::RegionBlock* StreamSoundPrefetchFile::FileHeader::GetRegionBlock() const { + return util::ConstBytePtr(GetBlock(ElementType_StreamSoundFile_RegionBlock)) + .Get(); +} + +const StreamSoundPrefetchFile::PrefetchDataBlock* StreamSoundPrefetchFile::FileHeader::GetPrefetchDataBlock() const { + return util::ConstBytePtr(GetBlock(ElementType_StreamSoundFile_PrefetchDataBlock)).Get(); +} + +u32 StreamSoundPrefetchFile::FileHeader::GetPrefetchDataBlockSize() const { + return GetBlockSize(ElementType_StreamSoundFile_PrefetchDataBlock); +} + +bool StreamSoundPrefetchFile::FileHeader::HasRegionBlock() const { + return GetBlock(ElementType_StreamSoundFile_RegionBlock) != nullptr; +} + +u32 StreamSoundPrefetchFile::FileHeader::GetRegionBlockSize() const { + return GetBlockSize(ElementType_StreamSoundFile_RegionBlock); +} + +u32 StreamSoundPrefetchFile::FileHeader::GetRegionBlockOffset() const { + return GetBlockOffset(ElementType_StreamSoundFile_RegionBlock); +} + +const StreamSoundPrefetchFile::PrefetchSample* StreamSoundPrefetchFile::PrefetchData::GetPrefetchSample() const { + return util::ConstBytePtr(this).Advance(toPrefetchSample.offset).Get(); +} + +const void* StreamSoundPrefetchFile::PrefetchSample::GetSampleAddress() const { + return util::ConstBytePtr(data).Get(); +} +} // namespace nn::atk::detail \ No newline at end of file diff --git a/src/NintendoWare/atk/detail/strm/atk_StreamSoundPrefetchFileReader.cpp b/src/NintendoWare/atk/detail/strm/atk_StreamSoundPrefetchFileReader.cpp new file mode 100644 index 00000000..cfac34ba --- /dev/null +++ b/src/NintendoWare/atk/detail/strm/atk_StreamSoundPrefetchFileReader.cpp @@ -0,0 +1,100 @@ +#include + +namespace nn::atk::detail { +const u32 SignatureFileStp {0x50545346}; // FSTP +const u32 SignatureInfoBlockStp {0x4f464e49}; // INFO +const u32 SignaturePrefetchdataBlockStp {0x54414450}; // PDAT + +const int SupportedFileVersionStp {0x10000}; +const int CurrentFileVersionStp {0x50200}; + +const int IncludeRegionInfoVersionStp {0x30000}; +const int IncludeCrc32CheckVersionStp {0x40000}; +const int IncludeRegionIndexCheckVersionStp {0x50100}; + +StreamSoundPrefetchFileReader::StreamSoundPrefetchFileReader() = default; + +StreamSoundPrefetchFileReader::~StreamSoundPrefetchFileReader() { + Finalize(); +} + +bool StreamSoundPrefetchFileReader::IsValidFileHeader(const void* streamSoundPrefetchFile) const { + const BinaryFileHeader& header {*util::ConstBytePtr(streamSoundPrefetchFile).Get()}; + + bool isSupportedVersion {header.signature == SignatureFileStp + && header.byteOrder == BinaryFileHeader::ValidByteOrderMark + && header.version >= SupportedFileVersionStp + && header.version <= CurrentFileVersionStp}; + + return isSupportedVersion; +} + +void StreamSoundPrefetchFileReader::Initialize(const void* streamSoundPrefetchFile) { + if (IsValidFileHeader(streamSoundPrefetchFile)) { + m_pHeader = util::ConstBytePtr(streamSoundPrefetchFile).Get(); + auto* infoBlock {m_pHeader->GetInfoBlock()}; + if (infoBlock->header.kind == SignatureInfoBlockStp) { + auto* dataBlock {m_pHeader->GetPrefetchDataBlock()}; + if (dataBlock->header.kind == SignaturePrefetchdataBlockStp) { + m_pInfoBlockBody = &infoBlock->body; + m_pPrefetchDataBlockBody = &dataBlock->body; + + m_RegionDataOffset = GetRegionDataOffset(); + m_RegionInfoBytes = GetRegionInfoBytes(); + } + } + } +} + +bool StreamSoundPrefetchFileReader::IsIncludeRegionInfo() const { + return m_pHeader->header.version >= IncludeRegionInfoVersionStp; +} + +bool StreamSoundPrefetchFileReader::IsCrc32CheckAvailable() const { + return m_pHeader->header.version >= IncludeCrc32CheckVersionStp; +} + +bool StreamSoundPrefetchFileReader::IsRegionIndexCheckAvailable() const { + return m_pHeader->header.version >= IncludeRegionIndexCheckVersionStp; +} + +bool StreamSoundPrefetchFileReader::ReadStreamSoundInfo(StreamSoundFile::StreamSoundInfo* strmInfo) const { + *strmInfo = *m_pInfoBlockBody->GetStreamSoundInfo(); + return true; +} + +bool StreamSoundPrefetchFileReader::ReadDspAdpcmChannelInfo(DspAdpcmParam* pParam, DspAdpcmLoopParam* pLoopParam, + int channelIndex) const { + auto* src {m_pInfoBlockBody->GetChannelInfoTable()->GetChannelInfo(channelIndex)->GetDspAdpcmChannelInfo()}; + if (src != nullptr) { + *pParam = src->param; + *pLoopParam = src->loopParam; + return true; + } + + return false; +} + +bool StreamSoundPrefetchFileReader::ReadPrefetchDataInfo(PrefetchDataInfo* pDataInfo, int prefetchIndex) const { + auto* data {m_pPrefetchDataBlockBody->GetPrefetchData(prefetchIndex)}; + pDataInfo->startFrame = data->startFrame; + pDataInfo->prefetchSize = data->prefetchSize; + + auto* sample {data->GetPrefetchSample()}; + pDataInfo->dataAddress = sample->GetSampleAddress(); + + return true; +} + +bool StreamSoundPrefetchFileReader::ReadRegionInfo(StreamSoundFile::RegionInfo* pInfo, u32 regionIndex) const { // 179 + if (m_RegionDataOffset != 0 && m_RegionInfoBytes != 0) { + position_t offset = m_RegionDataOffset + static_cast(m_RegionInfoBytes) * regionIndex; + auto bytePtr {util::ConstBytePtr(m_pHeader)}; + *pInfo = *bytePtr.Advance(offset).Get(); + return true; + } + + return false; +} + +} // namespace nn::atk::detail \ No newline at end of file diff --git a/src/NintendoWare/atk/detail/strm/atk_StreamSoundRuntime.cpp b/src/NintendoWare/atk/detail/strm/atk_StreamSoundRuntime.cpp new file mode 100644 index 00000000..e69de29b diff --git a/src/NintendoWare/atk/detail/strm/atk_StreamTrack.cpp b/src/NintendoWare/atk/detail/strm/atk_StreamTrack.cpp new file mode 100644 index 00000000..65ef85c1 --- /dev/null +++ b/src/NintendoWare/atk/detail/strm/atk_StreamTrack.cpp @@ -0,0 +1,8 @@ +#include + +namespace nn::atk::detail::driver { +void StreamChannel::AppendWaveBuffer(WaveBuffer* pBuffer, bool lastFlag) { + if (m_pVoice != nullptr) + m_pVoice->AppendWaveBuffer(0, pBuffer, lastFlag); +} +} // namespace nn::atk::detail::driver \ No newline at end of file diff --git a/src/NintendoWare/atk/detail/thread/atk_CommandManager.cpp b/src/NintendoWare/atk/detail/thread/atk_CommandManager.cpp new file mode 100644 index 00000000..e69de29b diff --git a/src/NintendoWare/atk/detail/thread/atk_DriverCommand.cpp b/src/NintendoWare/atk/detail/thread/atk_DriverCommand.cpp new file mode 100644 index 00000000..e69de29b diff --git a/src/NintendoWare/atk/detail/thread/atk_SoundThread.cpp b/src/NintendoWare/atk/detail/thread/atk_SoundThread.cpp new file mode 100644 index 00000000..e69de29b diff --git a/src/NintendoWare/atk/detail/thread/atk_Task.cpp b/src/NintendoWare/atk/detail/thread/atk_Task.cpp new file mode 100644 index 00000000..8a620743 --- /dev/null +++ b/src/NintendoWare/atk/detail/thread/atk_Task.cpp @@ -0,0 +1,14 @@ +#include + +namespace nn::atk::detail { +Task::Task() + : m_Event(os::EventClearMode_ManualClear) { + + InitializeStatus(); + SetId(0); + + m_Event.Signal(); +} + +Task::~Task() = default; +} // namespace nn::atk::detail \ No newline at end of file diff --git a/src/NintendoWare/atk/detail/thread/atk_TaskManager.cpp b/src/NintendoWare/atk/detail/thread/atk_TaskManager.cpp new file mode 100644 index 00000000..e69de29b diff --git a/src/NintendoWare/atk/detail/thread/atk_TaskThread.cpp b/src/NintendoWare/atk/detail/thread/atk_TaskThread.cpp new file mode 100644 index 00000000..e69de29b diff --git a/src/NintendoWare/atk/detail/util/atk_CurveAdshr.cpp b/src/NintendoWare/atk/detail/util/atk_CurveAdshr.cpp new file mode 100644 index 00000000..abc5729a --- /dev/null +++ b/src/NintendoWare/atk/detail/util/atk_CurveAdshr.cpp @@ -0,0 +1,153 @@ +#include + +namespace nn::atk::detail { + +const float CurveAdshr::VolumeInit{-90.4}; + +const s16 CurveAdshr::DecibelSquareTable[CurveAdshr::DecibelSquareTableSize] = { + -723, -722, -721, -651, -601, -562, -530, -503, -480, -460, -442, -425, -410, -396, -383, -371, + -360, -349, -339, -330, -321, -313, -305, -297, -289, -282, -276, -269, -263, -257, -251, -245, + -239, -234, -229, -224, -219, -214, -210, -205, -201, -196, -192, -188, -184, -180, -176, -173, + -169, -165, -162, -158, -155, -152, -149, -145, -142, -139, -136, -133, -130, -127, -125, -122, + -119, -116, -114, -111, -109, -106, -103, -101, -99, -96, -94, -91, -89, -87, -85, -82, + -80, -78, -76, -74, -72, -70, -68, -66, -64, -62, -60, -58, -56, -54, -52, -50, + -49, -47, -45, -43, -42, -40, -38, -36, -35, -33, -31, -30, -28, -27, -25, -23, + -22, -20, -19, -17, -16, -14, -13, -11, -10, -8, -7, -6, -4, -3, -1, 0}; + +CurveAdshr::CurveAdshr() { + Initialize(VolumeInit); +} + +void CurveAdshr::Initialize(float initDecibel) { + SetAttack(AttackInit); + SetHold(HoldInit); + SetDecay(DecayInit); + SetSustain(SustainInit); + SetRelease(ReleaseInit); + + Reset(initDecibel); +} + +void CurveAdshr::Reset(float initDecibel) { + m_Value = initDecibel * 10; + m_Status = Status_Attack; +} + +void CurveAdshr::Update(int msec) { + switch (m_Status) { + case Status_Attack: + for (; msec > 0; --msec) { + m_Value *= m_Attack; + + if (m_Value > -1.0f / 32.0f) { + m_Value = 0.0f; + m_Status = Status_Hold; + m_HoldCounter = m_Hold; + break; + } + } + + break; + + case Status_Hold: + if (msec < m_HoldCounter) { + m_HoldCounter -= static_cast(msec); + } else { + msec -= m_HoldCounter; + m_HoldCounter = 0; + m_Status = Status_Decay; + } + + if (m_Status != Status_Decay) + break; + + case Status_Decay: { + const float sustainDecay = CurveAdshr::CalcDecibelSquare(m_Sustain); + + m_Value -= m_Decay * static_cast(msec); + if (m_Value < sustainDecay) { + m_Value = sustainDecay; + m_Status = Status_Sustain; + } + + break; + } + + case Status_Sustain: + break; + + case Status_Release: + m_Value -= m_Release * static_cast(msec); + break; + } +} + +float CurveAdshr::GetValue() const { + if (m_Status == Status_Attack && m_Attack == 0) + return 0; + + return m_Value / 10.0f; +} + +void CurveAdshr::SetAttack(int attack) { + // Table to optimize calculations + // More info here: + // https://github.com/stupidestmodder/TuneBloom/blob/master/TuneBloom/src/snd-ply/CurveAdshr.cpp#L127 + static const float attackTable[DecibelSquareTableSize] = { + 0.99921750, 0.99843258, 0.99764520, 0.99685530, 0.99606287, 0.99526790, 0.99447040, + 0.99367040, 0.99286770, 0.99206250, 0.99125460, 0.99044410, 0.98963088, 0.98881510, + 0.98799650, 0.98717520, 0.98635119, 0.98552440, 0.98469490, 0.98386250, 0.98302728, + 0.98218930, 0.98134828, 0.98050450, 0.97965780, 0.97880810, 0.97795550, 0.97709990, + 0.97624129, 0.97537970, 0.97451500, 0.97364718, 0.97277629, 0.97190230, 0.97102510, + 0.97014480, 0.96926120, 0.96837437, 0.96748440, 0.96659100, 0.96569440, 0.96479440, + 0.96389100, 0.96298420, 0.96207398, 0.96116040, 0.96024328, 0.95932257, 0.95839840, + 0.95747060, 0.95653920, 0.95560420, 0.95466548, 0.95372307, 0.95277690, 0.95182699, + 0.95087320, 0.94991570, 0.94895420, 0.94798880, 0.94701950, 0.94604617, 0.94506890, + 0.94408750, 0.94310200, 0.94211239, 0.94111860, 0.94012058, 0.93911839, 0.93811178, + 0.93710089, 0.93608558, 0.93506590, 0.93404168, 0.93301308, 0.93197978, 0.93094200, + 0.92989950, 0.92885230, 0.92780040, 0.92674360, 0.92568210, 0.92461560, 0.92354420, + 0.92246780, 0.92138640, 0.92029980, 0.91920810, 0.91811120, 0.91700910, 0.91590160, + 0.91478870, 0.91367030, 0.91254650, 0.91141710, 0.91028208, 0.90914140, 0.90799490, + 0.90684270, 0.90568447, 0.90452039, 0.90335017, 0.90217400, 0.90099160, 0.89980290, + 0.89860800, 0.89740658, 0.89619880, 0.89498440, 0.89005989, 0.88246220, 0.87592470, + 0.86918610, 0.86364060, 0.85357880, 0.84301889, 0.82861350, 0.81490988, 0.80021720, + 0.77806628, 0.75547498, 0.72421250, 0.68282390, 0.63291690, 0.55921350, 0.45514110, + 0.32987699, 0.00000000}; + + m_Attack = attackTable[attack]; +} + +void CurveAdshr::SetHold(int hold) { + m_Hold = static_cast((hold + 1) * (hold + 1) / 4); +} + +void CurveAdshr::SetDecay(int decay) { + m_Decay = CalcRelease(decay); +} + +void CurveAdshr::SetSustain(int sustain) { + m_Sustain = sustain; +} + +void CurveAdshr::SetRelease(int release) { + m_Release = CalcRelease(release); +} + +float CurveAdshr::CalcRelease(int release) { + if (release == 127) + return 4.0f * 128 * 128 - 1; + + if (release == 126) + return 120 / 5.0f; + + if (release < 50) + return static_cast((release * 2) + 1) / 128.0f / 5.0f; + + return (60.0f / static_cast(126 - release)) / 5.0f; +} + +s16 CurveAdshr::CalcDecibelSquare(int scale) { + return DecibelSquareTable[scale]; +} + +} // namespace nn::atk::detail \ No newline at end of file diff --git a/src/NintendoWare/atk/detail/util/atk_CurveLfo.cpp b/src/NintendoWare/atk/detail/util/atk_CurveLfo.cpp new file mode 100644 index 00000000..d55d0b92 --- /dev/null +++ b/src/NintendoWare/atk/detail/util/atk_CurveLfo.cpp @@ -0,0 +1,142 @@ +#include + +#include + +#include + +#include + +namespace nn::atk::detail { + +namespace { + +float CurveSine(float arg) { + return util::SinTable(static_cast(arg * UINT_MAX)); +} + +float CurveSquare(float arg) { + return arg < 0.5f ? 0.0f : 1.0f; +} + +float CurveSaw(float arg) { + return arg; +} + +float CurveTriangle(float arg) { + static const float gradients[5]{4.0, -4.0, -4.0, 4.0, 4.0}; + static const float intercepts[5]{0.0, 2.0, 2.0, -4.0, 0.0}; + + u32 tmp{static_cast(arg * 4)}; + + return gradients[tmp] * arg + intercepts[tmp]; +} + +float CurveRandom([[maybe_unused]] float arg) { + u16 rand{Util::CalcRandom()}; + float ret{rand / 65535.0f}; + + return ret; +} + +CurveLfo::CurveFunc g_CurveFuncTable[128]; + +} // anonymous namespace + +void CurveLfo::InitializeCurveTable() { + std::memset(static_cast(g_CurveFuncTable), 0, sizeof(g_CurveFuncTable)); + g_CurveFuncTable[0] = CurveSine; + g_CurveFuncTable[1] = CurveTriangle; + g_CurveFuncTable[2] = CurveRandom; + g_CurveFuncTable[3] = CurveSaw; + g_CurveFuncTable[4] = CurveSquare; +} + +CurveLfo::CurveFunc CurveLfo::RegisterUserCurve(CurveFunc func, u32 index) { + s32 tableIndex = {static_cast(index + CurveLfoParam::CurveType_UserMin)}; + + CurveFunc tmp{g_CurveFuncTable[tableIndex]}; + g_CurveFuncTable[tableIndex] = func; + + return tmp; +} + +CurveLfo::CurveFunc CurveLfo::UnregisterUserCurve(u32 index) { + s32 tableIndex = {static_cast(index + CurveLfoParam::CurveType_UserMin)}; + + CurveFunc tmp{g_CurveFuncTable[tableIndex]}; + g_CurveFuncTable[tableIndex] = nullptr; + + return tmp; +} + +void CurveLfo::Reset() { + m_Counter = 0.0f; + m_RandomValue = 1.0f; + m_DelayCounter = 0; + m_IsStart = false; + m_IsNext = false; +} + +void CurveLfo::Update(int msec) { + if (m_DelayCounter < m_Param.delay && m_DelayCounter + msec <= m_Param.delay) { + m_DelayCounter += msec; + return; + } + + if (m_DelayCounter < m_Param.delay) { + msec -= m_Param.delay - m_DelayCounter; + m_DelayCounter = m_Param.delay; + } + + if (m_Param.speed > 0.0f) { + if (!m_IsStart) { + m_Counter = m_Param.phase / 127.0f; + m_IsStart = true; + } + + m_Counter += (m_Param.speed * msec) / 1000.0f; + m_IsNext = m_Counter >= 1.0f; + m_Counter -= static_cast(m_Counter); + } +} + +float CurveLfo::GetValue() const { + if (m_Param.depth == 0.0f) + return 0.0f; + + if (m_DelayCounter < m_Param.delay) + return 0.0f; + + CurveFunc func{g_CurveFuncTable[m_Param.curve]}; + + float value{0}; + + if (func != nullptr) { + if (m_Param.curve == CurveLfoParam::CurveType_Random) { + if (m_IsNext) + m_RandomValue = func(m_Counter); + + value = m_RandomValue; + } else { + value = func(m_Counter); + } + } else { + value = 1.0f; + } + + value *= m_Param.depth; + value *= static_cast(m_Param.range); + + return value; +} + +void CurveLfoParam::Initialize() { + depth = 0.0f; + speed = 6.25f; + delay = 0; + range = 1; + curve = CurveType_Sine; + phase = 0; +} + +} // namespace nn::atk::detail \ No newline at end of file diff --git a/src/NintendoWare/atk/detail/util/atk_FrameHeap.cpp b/src/NintendoWare/atk/detail/util/atk_FrameHeap.cpp new file mode 100644 index 00000000..a41b8b99 --- /dev/null +++ b/src/NintendoWare/atk/detail/util/atk_FrameHeap.cpp @@ -0,0 +1,128 @@ +#include + +#include + +#include + +#include + +namespace nn::atk::detail { + +FrameHeap::FrameHeap() = default; + +FrameHeap::~FrameHeap() { + Destroy(); +} + +bool FrameHeap::Create(void* startAddress, size_t size) { + if (IsValid()) + Destroy(); + + void* endAddress{util::BytePtr(startAddress, size).Get()}; + startAddress = util::BytePtr(startAddress).AlignUp(fnd::HeapBase::DefaultAlignment).Get(); + + if (startAddress <= endAddress) { + m_pHeap = fnd::FrameHeapImpl::Create(startAddress, + fnd::GetOffsetFromPtr(startAddress, endAddress), 0); + + if (m_pHeap != nullptr) + return NewSection(); + } + + return false; +} + +void FrameHeap::Destroy() { + if (IsValid()) { + ClearSection(); + m_pHeap->Free(fnd::FrameHeapImpl::FreeAllMode); + m_pHeap->Destroy(); + m_pHeap = nullptr; + } +} + +void* FrameHeap::Alloc(size_t size, DisposeCallback callback, void* callbackArg, + HeapCallback heapCallback, void* heapCallbackArg) { + const size_t blockSize{sizeof(Block)}; + const size_t allocSize{(size + 127) & ~63}; + + void* mem{m_pHeap->Alloc(allocSize, blockSize)}; + + if (mem == nullptr) + return nullptr; + + void* buffer{util::BytePtr(mem, blockSize).Get()}; + + Block* block{new (mem) + Block(buffer, size, callback, callbackArg, heapCallback, heapCallbackArg)}; + m_SectionList.back().AppendBlock(block); + + return buffer; +} + +void FrameHeap::Clear() { + ClearSection(); + m_pHeap->Free(fnd::FrameHeapImpl::FreeAllMode); + [[maybe_unused]] bool result = NewSection(); +} + +// NON_MATCHING: incorrect branching +int FrameHeap::SaveState() { + if (m_pHeap->RecordState(m_SectionList.size())) { + if (NewSection()) { + int result; + + result = m_SectionList.size() - 1; + return result; + } + + m_pHeap->FreeByState(0); + } + + return -1; +} + +bool FrameHeap::NewSection() { + void* buffer{m_pHeap->Alloc(sizeof(Section), fnd::HeapBase::DefaultAlignment)}; + + if (buffer != nullptr) { + std::memset(buffer, 0, sizeof(Section)); + auto* section{new (buffer) Section}; + m_SectionList.push_back(*section); + + return true; + } + + return false; +} + +// NON_MATCHING: bad implementation +// Commented out to prevent inlining in other functions +// void FrameHeap::ClearSection() { +// bool alreadyUseCallback{ProcessCallback(0)}; + +// if (!m_SectionList.empty()) { +// auto& section{m_SectionList.back()}; + +// if (alreadyUseCallback) +// section.SetUseCallback(false); + +// section.~Section(); +// for (auto it{m_SectionList.begin()}; it != m_SectionList.end(); ++it) { +// m_SectionList.erase(it); + +// } +// } +// } + +FrameHeap::Section::Section() = default; + +FrameHeap::Section::~Section() { + m_BlockList.clear(); +} + +void FrameHeap::Section::AppendBlock(Block* block) { + m_BlockList.push_back(*block); +} + +} // namespace nn::atk::detail \ No newline at end of file diff --git a/src/NintendoWare/atk/detail/util/atk_InstancePool.cpp b/src/NintendoWare/atk/detail/util/atk_InstancePool.cpp new file mode 100644 index 00000000..89b546a7 --- /dev/null +++ b/src/NintendoWare/atk/detail/util/atk_InstancePool.cpp @@ -0,0 +1,72 @@ +#include + +#include + +namespace nn::atk::detail { + +// NON_MATCHING: incomplete implementation +int PoolImpl::CreateImpl(void* buffer, size_t size, size_t objSize, size_t alignment) { + if (buffer == nullptr) + return 0; + + char* ptr{util::BytePtr(buffer).AlignUp(alignment).Get()}; + objSize = util::align_up(objSize, alignment); + int objectCount = + (reinterpret_cast(buffer) + size - reinterpret_cast(ptr)) / objSize; + + for (int i{0}; i < objectCount; ++i) { + if (objectCount - 1 > 2) { + for (PoolImpl* head{m_pNext}; head != nullptr; head = head->m_pNext) { + reinterpret_cast(ptr)->m_pNext = head; + head = &reinterpret_cast(ptr)->m_pNext[i]; + } + } + } + + m_pBuffer = buffer; + m_BufferSize = size; + + return objectCount; +} + +void PoolImpl::DestroyImpl() { + void* begin{util::BytePtr(m_pBuffer).Get()}; + void* end{util::BytePtr(m_pBuffer, m_BufferSize).Get()}; + + PoolImpl* ptr{m_pNext}; + PoolImpl* prev{this}; + + for (; ptr != nullptr; ptr = ptr->m_pNext) { + if (ptr >= begin && ptr < end) + prev->m_pNext = ptr->m_pNext; + else + prev = ptr; + } +} + +int PoolImpl::CountImpl() const { + int count{0}; + + for (PoolImpl* ptr{m_pNext}; ptr != nullptr; ptr = ptr->m_pNext) + ++count; + + return count; +} + +void* PoolImpl::AllocImpl() { + PoolImpl* head{m_pNext}; + + if (head != nullptr) + m_pNext = head->m_pNext; + + return head; +} + +void PoolImpl::FreeImpl(void* ptr) { + PoolImpl* head{reinterpret_cast(ptr)}; + + head->m_pNext = m_pNext; + m_pNext = head; +} + +} // namespace nn::atk::detail \ No newline at end of file diff --git a/src/NintendoWare/atk/detail/util/atk_MemoryFileStream.cpp b/src/NintendoWare/atk/detail/util/atk_MemoryFileStream.cpp new file mode 100644 index 00000000..41f7603d --- /dev/null +++ b/src/NintendoWare/atk/detail/util/atk_MemoryFileStream.cpp @@ -0,0 +1,52 @@ +#include + +#include + +namespace nn::atk::detail { + +MemoryFileStream::MemoryFileStream(const void* buffer, size_t size) + : m_pBuffer(buffer), m_Size(size), m_Position(0) {} + +fnd::FndResult MemoryFileStream::Open([[maybe_unused]] const char* filePath, + [[maybe_unused]] AccessMode openMode) { + return fnd::FndResult{fnd::FndResultType_Failed}; +} + +void MemoryFileStream::Close() { + m_pBuffer = nullptr; + m_Size = 0; + m_Position = 0; +} + +size_t MemoryFileStream::Read(void* buf, size_t length, [[maybe_unused]] fnd::FndResult* result) { + size_t readLen{std::min(length, m_Size - m_Position)}; + + std::memcpy(buf, reinterpret_cast(m_pBuffer) + m_Position, readLen); + + m_Position += static_cast(readLen); + + return readLen; +} + +fnd::FndResult MemoryFileStream::Seek(position_t offset, fnd::Stream::SeekOrigin origin) { + switch (origin) { + case SeekOrigin_Begin: + m_Position = offset; + break; + + case SeekOrigin_End: + m_Position = static_cast(m_Size) - offset; + break; + + case SeekOrigin_Current: + m_Position += offset; + break; + + default: + return fnd::FndResult{fnd::FndResultType_Failed}; + } + + return fnd::FndResult{fnd::FndResultType_True}; +} + +} // namespace nn::atk::detail \ No newline at end of file diff --git a/src/NintendoWare/atk/detail/util/atk_Util.cpp b/src/NintendoWare/atk/detail/util/atk_Util.cpp new file mode 100644 index 00000000..56729786 --- /dev/null +++ b/src/NintendoWare/atk/detail/util/atk_Util.cpp @@ -0,0 +1,868 @@ +#include + +#include + +#include + +#include +#include + +namespace nn::atk::detail { + +namespace { +const float NoteTable[12]{ + 1.00000000000000f, 1.05946309435930f, 1.12246204830937f, 1.18920711500272f, + 1.25992104989487f, 1.33483985417003f, 1.41421356237310f, 1.49830707687668f, + 1.58740105196820f, 1.68179283050743f, 1.78179743628068f, 1.88774862536339f, +}; + +const float PitchTable[Util::PitchDivisionRange]{ + 1.00000000000000f, 1.00022565930507f, 1.00045136953226f, 1.00067713069307f, 1.00090294279898f, + 1.00112880586149f, 1.00135471989211f, 1.00158068490233f, 1.00180670090365f, 1.00203276790759f, + 1.00225888592566f, 1.00248505496936f, 1.00271127505020f, 1.00293754617972f, 1.00316386836942f, + 1.00339024163082f, 1.00361666597546f, 1.00384314141486f, 1.00406966796055f, 1.00429624562407f, + 1.00452287441694f, 1.00474955435071f, 1.00497628543691f, 1.00520306768709f, 1.00542990111280f, + 1.00565678572558f, 1.00588372153699f, 1.00611070855857f, 1.00633774680189f, 1.00656483627850f, + 1.00679197699996f, 1.00701916897784f, 1.00724641222370f, 1.00747370674912f, 1.00770105256566f, + 1.00792844968490f, 1.00815589811842f, 1.00838339787779f, 1.00861094897460f, 1.00883855142043f, + 1.00906620522687f, 1.00929391040551f, 1.00952166696794f, 1.00974947492577f, 1.00997733429057f, + 1.01020524507396f, 1.01043320728755f, 1.01066122094292f, 1.01088928605170f, 1.01111740262549f, + 1.01134557067591f, 1.01157379021458f, 1.01180206125310f, 1.01203038380312f, 1.01225875787623f, + 1.01248718348409f, 1.01271566063830f, 1.01294418935052f, 1.01317276963236f, 1.01340140149547f, + 1.01363008495149f, 1.01385882001206f, 1.01408760668882f, 1.01431644499343f, 1.01454533493752f, + 1.01477427653277f, 1.01500326979081f, 1.01523231472332f, 1.01546141134194f, 1.01569055965835f, + 1.01591975968421f, 1.01614901143119f, 1.01637831491095f, 1.01660767013518f, 1.01683707711556f, + 1.01706653586375f, 1.01729604639144f, 1.01752560871032f, 1.01775522283207f, 1.01798488876839f, + 1.01821460653096f, 1.01844437613148f, 1.01867419758165f, 1.01890407089317f, 1.01913399607774f, + 1.01936397314707f, 1.01959400211286f, 1.01982408298683f, 1.02005421578069f, 1.02028440050616f, + 1.02051463717495f, 1.02074492579879f, 1.02097526638940f, 1.02120565895850f, 1.02143610351784f, + 1.02166660007913f, 1.02189714865412f, 1.02212774925453f, 1.02235840189212f, 1.02258910657863f, + 1.02281986332579f, 1.02305067214536f, 1.02328153304909f, 1.02351244604873f, 1.02374341115603f, + 1.02397442838276f, 1.02420549774068f, 1.02443661924155f, 1.02466779289714f, 1.02489901871921f, + 1.02513029671954f, 1.02536162690990f, 1.02559300930208f, 1.02582444390784f, 1.02605593073898f, + 1.02628746980727f, 1.02651906112451f, 1.02675070470248f, 1.02698240055299f, 1.02721414868781f, + 1.02744594911876f, 1.02767780185764f, 1.02790970691624f, 1.02814166430638f, 1.02837367403986f, + 1.02860573612850f, 1.02883785058410f, 1.02907001741849f, 1.02930223664349f, 1.02953450827092f, + 1.02976683231260f, 1.02999920878037f, 1.03023163768604f, 1.03046411904146f, 1.03069665285846f, + 1.03092923914889f, 1.03116187792457f, 1.03139456919736f, 1.03162731297909f, 1.03186010928163f, + 1.03209295811682f, 1.03232585949652f, 1.03255881343258f, 1.03279181993686f, 1.03302487902123f, + 1.03325799069755f, 1.03349115497769f, 1.03372437187351f, 1.03395764139691f, 1.03419096355973f, + 1.03442433837388f, 1.03465776585123f, 1.03489124600365f, 1.03512477884305f, 1.03535836438130f, + 1.03559200263031f, 1.03582569360196f, 1.03605943730815f, 1.03629323376078f, 1.03652708297176f, + 1.03676098495299f, 1.03699493971638f, 1.03722894727384f, 1.03746300763728f, 1.03769712081862f, + 1.03793128682977f, 1.03816550568267f, 1.03839977738923f, 1.03863410196138f, 1.03886847941105f, + 1.03910290975017f, 1.03933739299068f, 1.03957192914452f, 1.03980651822362f, 1.04004116023993f, + 1.04027585520539f, 1.04051060313196f, 1.04074540403158f, 1.04098025791621f, 1.04121516479780f, + 1.04145012468832f, 1.04168513759972f, 1.04192020354397f, 1.04215532253304f, 1.04239049457890f, + 1.04262571969352f, 1.04286099788887f, 1.04309632917694f, 1.04333171356970f, 1.04356715107914f, + 1.04380264171725f, 1.04403818549601f, 1.04427378242741f, 1.04450943252346f, 1.04474513579614f, + 1.04498089225746f, 1.04521670191942f, 1.04545256479402f, 1.04568848089328f, 1.04592445022919f, + 1.04616047281379f, 1.04639654865907f, 1.04663267777707f, 1.04686886017980f, 1.04710509587929f, + 1.04734138488756f, 1.04757772721665f, 1.04781412287858f, 1.04805057188539f, 1.04828707424912f, + 1.04852362998181f, 1.04876023909550f, 1.04899690160224f, 1.04923361751407f, 1.04947038684306f, + 1.04970720960124f, 1.04994408580069f, 1.05018101545345f, 1.05041799857160f, 1.05065503516719f, + 1.05089212525229f, 1.05112926883898f, 1.05136646593932f, 1.05160371656540f, 1.05184102072929f, + 1.05207837844307f, 1.05231578971883f, 1.05255325456865f, 1.05279077300463f, 1.05302834503885f, + 1.05326597068341f, 1.05350364995040f, 1.05374138285194f, 1.05397916940012f, 1.05421700960704f, + 1.05445490348482f, 1.05469285104557f, 1.05493085230140f, 1.05516890726443f, 1.05540701594677f, + 1.05564517836056f, 1.05588339451791f, 1.05612166443095f, 1.05635998811181f, 1.05659836557263f, + 1.05683679682555f, 1.05707528188269f, 1.05731382075621f, 1.05755241345824f, 1.05779106000093f, + 1.05802976039644f, 1.05826851465692f, 1.05850732279451f, 1.05874618482139f, 1.05898510074970f, + 1.05922407059161f, +}; + +const float Decibel2RatioTable[Util::VolumeDbMax - Util::VolumeDbMin + 1]{ + 0.000000000f, 3.05492E-05f, 3.09030E-05f, 3.12608E-05f, // -90.4dB = -infinity + 3.16228E-05f, 3.19890E-05f, 3.23594E-05f, 3.27341E-05f, 3.31131E-05f, // -90.0 dB + 3.34965E-05f, 3.38844E-05f, 3.42768E-05f, 3.46737E-05f, 3.50752E-05f, + 3.54813E-05f, 3.58922E-05f, 3.63078E-05f, 3.67282E-05f, 3.71535E-05f, + 3.75837E-05f, 3.80189E-05f, 3.84592E-05f, 3.89045E-05f, 3.93550E-05f, + 3.98107E-05f, 4.02717E-05f, 4.07380E-05f, 4.12098E-05f, 4.16869E-05f, + 4.21697E-05f, 4.26580E-05f, 4.31519E-05f, 4.36516E-05f, 4.41570E-05f, + 4.46684E-05f, 4.51856E-05f, 4.57088E-05f, 4.62381E-05f, 4.67735E-05f, + 4.73151E-05f, 4.78630E-05f, 4.84172E-05f, 4.89779E-05f, 4.95450E-05f, + 5.01187E-05f, 5.06991E-05f, 5.12861E-05f, 5.18800E-05f, 5.24807E-05f, + 5.30884E-05f, 5.37032E-05f, 5.43250E-05f, 5.49541E-05f, 5.55904E-05f, + 5.62341E-05f, 5.68853E-05f, 5.75440E-05f, 5.82103E-05f, 5.88844E-05f, + 5.95662E-05f, 6.02560E-05f, 6.09537E-05f, 6.16595E-05f, 6.23735E-05f, + 6.30957E-05f, 6.38263E-05f, 6.45654E-05f, 6.53131E-05f, 6.60693E-05f, // -84.0 dB + 6.68344E-05f, 6.76083E-05f, 6.83912E-05f, 6.91831E-05f, 6.99842E-05f, + 7.07946E-05f, 7.16143E-05f, 7.24436E-05f, 7.32825E-05f, 7.41310E-05f, + 7.49894E-05f, 7.58578E-05f, 7.67361E-05f, 7.76247E-05f, 7.85236E-05f, + 7.94328E-05f, 8.03526E-05f, 8.12831E-05f, 8.22243E-05f, 8.31764E-05f, + 8.41395E-05f, 8.51138E-05f, 8.60994E-05f, 8.70964E-05f, 8.81049E-05f, + 8.91251E-05f, 9.01571E-05f, 9.12011E-05f, 9.22571E-05f, 9.33254E-05f, + 9.44061E-05f, 9.54993E-05f, 9.66051E-05f, 9.77237E-05f, 9.88553E-05f, + 0.000100000f, 0.000101158f, 0.000102329f, 0.000103514f, 0.000104713f, // -80.0 dB + 0.000105925f, 0.000107152f, 0.000108393f, 0.000109648f, 0.000110917f, + 0.000112202f, 0.000113501f, 0.000114815f, 0.000116145f, 0.000117490f, + 0.000118850f, 0.000120226f, 0.000121619f, 0.000123027f, 0.000124451f, + 0.000125893f, 0.000127350f, 0.000128825f, 0.000130317f, 0.000131826f, // -78.0 dB + 0.000133352f, 0.000134896f, 0.000136458f, 0.000138038f, 0.000139637f, + 0.000141254f, 0.000142889f, 0.000144544f, 0.000146218f, 0.000147911f, + 0.000149624f, 0.000151356f, 0.000153109f, 0.000154882f, 0.000156675f, + 0.000158489f, 0.000160325f, 0.000162181f, 0.000164059f, 0.000165959f, + 0.000167880f, 0.000169824f, 0.000171791f, 0.000173780f, 0.000175792f, + 0.000177828f, 0.000179887f, 0.000181970f, 0.000184077f, 0.000186209f, + 0.000188365f, 0.000190546f, 0.000192752f, 0.000194984f, 0.000197242f, + 0.000199526f, 0.000201837f, 0.000204174f, 0.000206538f, 0.000208930f, + 0.000211349f, 0.000213796f, 0.000216272f, 0.000218776f, 0.000221309f, + 0.000223872f, 0.000226464f, 0.000229087f, 0.000231739f, 0.000234423f, + 0.000237137f, 0.000239883f, 0.000242661f, 0.000245471f, 0.000248313f, + 0.000251189f, 0.000254097f, 0.000257040f, 0.000260016f, 0.000263027f, // -72.0 dB + 0.000266073f, 0.000269153f, 0.000272270f, 0.000275423f, 0.000278612f, + 0.000281838f, 0.000285102f, 0.000288403f, 0.000291743f, 0.000295121f, + 0.000298538f, 0.000301995f, 0.000305492f, 0.000309030f, 0.000312608f, + 0.000316228f, 0.000319890f, 0.000323594f, 0.000327341f, 0.000331131f, + 0.000334965f, 0.000338844f, 0.000342768f, 0.000346737f, 0.000350752f, + 0.000354813f, 0.000358922f, 0.000363078f, 0.000367282f, 0.000371535f, + 0.000375837f, 0.000380189f, 0.000384592f, 0.000389045f, 0.000393550f, + 0.000398107f, 0.000402717f, 0.000407380f, 0.000412098f, 0.000416869f, + 0.000421697f, 0.000426580f, 0.000431519f, 0.000436516f, 0.000441570f, + 0.000446684f, 0.000451856f, 0.000457088f, 0.000462381f, 0.000467735f, + 0.000473151f, 0.000478630f, 0.000484172f, 0.000489779f, 0.000495450f, + 0.000501187f, 0.000506991f, 0.000512861f, 0.000518800f, 0.000524807f, // -66.0 dB + 0.000530884f, 0.000537032f, 0.000543250f, 0.000549541f, 0.000555904f, + 0.000562341f, 0.000568853f, 0.000575440f, 0.000582103f, 0.000588844f, + 0.000595662f, 0.000602560f, 0.000609537f, 0.000616595f, 0.000623735f, + 0.000630957f, 0.000638263f, 0.000645654f, 0.000653131f, 0.000660693f, + 0.000668344f, 0.000676083f, 0.000683912f, 0.000691831f, 0.000699842f, + 0.000707946f, 0.000716143f, 0.000724436f, 0.000732825f, 0.000741310f, + 0.000749894f, 0.000758578f, 0.000767361f, 0.000776247f, 0.000785236f, + 0.000794328f, 0.000803526f, 0.000812831f, 0.000822243f, 0.000831764f, + 0.000841395f, 0.000851138f, 0.000860994f, 0.000870964f, 0.000881049f, + 0.000891251f, 0.000901571f, 0.000912011f, 0.000922571f, 0.000933254f, + 0.000944061f, 0.000954993f, 0.000966051f, 0.000977237f, 0.000988553f, + 0.001000000f, 0.001011579f, 0.001023293f, 0.001035142f, 0.001047129f, // -60.0.0dB ( 1/1000 ) + 0.001059254f, 0.001071519f, 0.001083927f, 0.001096478f, 0.001109175f, + 0.001122018f, 0.001135011f, 0.001148154f, 0.001161449f, 0.001174898f, + 0.001188502f, 0.001202264f, 0.001216186f, 0.001230269f, 0.001244515f, + 0.001258925f, 0.001273503f, 0.001288250f, 0.001303167f, 0.001318257f, + 0.001333521f, 0.001348963f, 0.001364583f, 0.001380384f, 0.001396368f, + 0.001412538f, 0.001428894f, 0.001445440f, 0.001462177f, 0.001479108f, + 0.001496236f, 0.001513561f, 0.001531087f, 0.001548817f, 0.001566751f, + 0.001584893f, 0.001603245f, 0.001621810f, 0.001640590f, 0.001659587f, + 0.001678804f, 0.001698244f, 0.001717908f, 0.001737801f, 0.001757924f, + 0.001778279f, 0.001798871f, 0.001819701f, 0.001840772f, 0.001862087f, + 0.001883649f, 0.001905461f, 0.001927525f, 0.001949845f, 0.001972423f, + 0.001995262f, 0.002018366f, 0.002041738f, 0.002065380f, 0.002089296f, // -54.0 dB + 0.002113489f, 0.002137962f, 0.002162719f, 0.002187762f, 0.002213095f, + 0.002238721f, 0.002264644f, 0.002290868f, 0.002317395f, 0.002344229f, + 0.002371374f, 0.002398833f, 0.002426610f, 0.002454709f, 0.002483133f, + 0.002511886f, 0.002540973f, 0.002570396f, 0.002600160f, 0.002630268f, + 0.002660725f, 0.002691535f, 0.002722701f, 0.002754229f, 0.002786121f, + 0.002818383f, 0.002851018f, 0.002884032f, 0.002917427f, 0.002951209f, + 0.002985383f, 0.003019952f, 0.003054921f, 0.003090295f, 0.003126079f, + 0.003162278f, 0.003198895f, 0.003235937f, 0.003273407f, 0.003311311f, + 0.003349654f, 0.003388442f, 0.003427678f, 0.003467369f, 0.003507519f, + 0.003548134f, 0.003589219f, 0.003630781f, 0.003672823f, 0.003715352f, + 0.003758374f, 0.003801894f, 0.003845918f, 0.003890451f, 0.003935501f, + 0.003981072f, 0.004027170f, 0.004073803f, 0.004120975f, 0.004168694f, // -48.0 dB + 0.004216965f, 0.004265795f, 0.004315191f, 0.004365158f, 0.004415704f, + 0.004466836f, 0.004518559f, 0.004570882f, 0.004623810f, 0.004677351f, + 0.004731513f, 0.004786301f, 0.004841724f, 0.004897788f, 0.004954502f, + 0.005011872f, 0.005069907f, 0.005128614f, 0.005188000f, 0.005248075f, + 0.005308844f, 0.005370318f, 0.005432503f, 0.005495409f, 0.005559043f, + 0.005623413f, 0.005688529f, 0.005754399f, 0.005821032f, 0.005888437f, + 0.005956621f, 0.006025596f, 0.006095369f, 0.006165950f, 0.006237348f, + 0.006309573f, 0.006382635f, 0.006456542f, 0.006531306f, 0.006606934f, + 0.006683439f, 0.006760830f, 0.006839116f, 0.006918310f, 0.006998420f, + 0.007079458f, 0.007161434f, 0.007244360f, 0.007328245f, 0.007413102f, + 0.007498942f, 0.007585776f, 0.007673615f, 0.007762471f, 0.007852356f, + 0.007943282f, 0.008035261f, 0.008128305f, 0.008222426f, 0.008317638f, // -42.0 dB + 0.008413951f, 0.008511380f, 0.008609938f, 0.008709636f, 0.008810489f, + 0.008912509f, 0.009015711f, 0.009120108f, 0.009225714f, 0.009332543f, + 0.009440609f, 0.009549926f, 0.009660509f, 0.009772372f, 0.009885531f, + 0.010000000f, 0.010115795f, 0.010232930f, 0.010351422f, 0.010471285f, // -40.0.0dB ( 1/100 ) + 0.010592537f, 0.010715193f, 0.010839269f, 0.010964782f, 0.011091748f, + 0.011220185f, 0.011350108f, 0.011481536f, 0.011614486f, 0.011748976f, + 0.011885022f, 0.012022644f, 0.012161860f, 0.012302688f, 0.012445146f, + 0.012589254f, 0.012735031f, 0.012882496f, 0.013031668f, 0.013182567f, + 0.013335214f, 0.013489629f, 0.013645831f, 0.013803843f, 0.013963684f, + 0.014125375f, 0.014288940f, 0.014454398f, 0.014621772f, 0.014791084f, + 0.014962357f, 0.015135612f, 0.015310875f, 0.015488166f, 0.015667511f, + 0.015848932f, 0.016032454f, 0.016218101f, 0.016405898f, 0.016595869f, // -36.0 dB + 0.016788040f, 0.016982437f, 0.017179084f, 0.017378008f, 0.017579236f, + 0.017782794f, 0.017988709f, 0.018197009f, 0.018407720f, 0.018620871f, + 0.018836491f, 0.019054607f, 0.019275249f, 0.019498446f, 0.019724227f, + 0.019952623f, 0.020183664f, 0.020417379f, 0.020653802f, 0.020892961f, + 0.021134890f, 0.021379621f, 0.021627185f, 0.021877616f, 0.022130947f, + 0.022387211f, 0.022646443f, 0.022908677f, 0.023173946f, 0.023442288f, + 0.023713737f, 0.023988329f, 0.024266101f, 0.024547089f, 0.024831331f, + 0.025118864f, 0.025409727f, 0.025703958f, 0.026001596f, 0.026302680f, + 0.026607251f, 0.026915348f, 0.027227013f, 0.027542287f, 0.027861212f, + 0.028183829f, 0.028510183f, 0.028840315f, 0.029174270f, 0.029512092f, + 0.029853826f, 0.030199517f, 0.030549211f, 0.030902954f, 0.031260794f, + 0.031622777f, 0.031988951f, 0.032359366f, 0.032734069f, 0.033113112f, // -30.0 dB + 0.033496544f, 0.033884416f, 0.034276779f, 0.034673685f, 0.035075187f, + 0.035481339f, 0.035892193f, 0.036307805f, 0.036728230f, 0.037153523f, + 0.037583740f, 0.038018940f, 0.038459178f, 0.038904514f, 0.039355008f, + 0.039810717f, 0.040271703f, 0.040738028f, 0.041209752f, 0.041686938f, + 0.042169650f, 0.042657952f, 0.043151908f, 0.043651583f, 0.044157045f, + 0.044668359f, 0.045185594f, 0.045708819f, 0.046238102f, 0.046773514f, + 0.047315126f, 0.047863009f, 0.048417237f, 0.048977882f, 0.049545019f, + 0.050118723f, 0.050699071f, 0.051286138f, 0.051880004f, 0.052480746f, + 0.053088444f, 0.053703180f, 0.054325033f, 0.054954087f, 0.055590426f, + 0.056234133f, 0.056885293f, 0.057543994f, 0.058210322f, 0.058884366f, + 0.059566214f, 0.060255959f, 0.060953690f, 0.061659500f, 0.062373484f, + 0.063095734f, 0.063826349f, 0.064565423f, 0.065313055f, 0.066069345f, // -24.0 dB + 0.066834392f, 0.067608298f, 0.068391165f, 0.069183097f, 0.069984200f, + 0.070794578f, 0.071614341f, 0.072443596f, 0.073282453f, 0.074131024f, + 0.074989421f, 0.075857758f, 0.076736149f, 0.077624712f, 0.078523563f, + 0.079432823f, 0.080352612f, 0.081283052f, 0.082224265f, 0.083176377f, + 0.084139514f, 0.085113804f, 0.086099375f, 0.087096359f, 0.088104887f, + 0.089125094f, 0.090157114f, 0.091201084f, 0.092257143f, 0.093325430f, + 0.094406088f, 0.095499259f, 0.096605088f, 0.097723722f, 0.098855309f, + 0.100000000f, 0.101157945f, 0.102329299f, 0.103514217f, 0.104712855f, // -20.0.0dB ( 1/10 ) + 0.105925373f, 0.107151931f, 0.108392691f, 0.109647820f, 0.110917482f, + 0.112201845f, 0.113501082f, 0.114815362f, 0.116144861f, 0.117489755f, + 0.118850223f, 0.120226443f, 0.121618600f, 0.123026877f, 0.124451461f, + 0.125892541f, 0.127350308f, 0.128824955f, 0.130316678f, 0.131825674f, // -18.0 dB + 0.133352143f, 0.134896288f, 0.136458314f, 0.138038426f, 0.139636836f, + 0.141253754f, 0.142889396f, 0.144543977f, 0.146217717f, 0.147910839f, + 0.149623566f, 0.151356125f, 0.153108746f, 0.154881662f, 0.156675107f, + 0.158489319f, 0.160324539f, 0.162181010f, 0.164058977f, 0.165958691f, + 0.167880402f, 0.169824365f, 0.171790839f, 0.173780083f, 0.175792361f, + 0.177827941f, 0.179887092f, 0.181970086f, 0.184077200f, 0.186208714f, + 0.188364909f, 0.190546072f, 0.192752491f, 0.194984460f, 0.197242274f, + 0.199526231f, 0.201836636f, 0.204173794f, 0.206538016f, 0.208929613f, + 0.211348904f, 0.213796209f, 0.216271852f, 0.218776162f, 0.221309471f, + 0.223872114f, 0.226464431f, 0.229086765f, 0.231739465f, 0.234422882f, + 0.237137371f, 0.239883292f, 0.242661010f, 0.245470892f, 0.248313311f, + 0.251188643f, 0.254097271f, 0.257039578f, 0.260015956f, 0.263026799f, // -12.0dB ( 1/4 ) + 0.266072506f, 0.269153480f, 0.272270131f, 0.275422870f, 0.278612117f, + 0.281838293f, 0.285101827f, 0.288403150f, 0.291742701f, 0.295120923f, + 0.298538262f, 0.301995172f, 0.305492111f, 0.309029543f, 0.312607937f, + 0.316227766f, 0.319889511f, 0.323593657f, 0.327340695f, 0.331131121f, + 0.334965439f, 0.338844156f, 0.342767787f, 0.346736850f, 0.350751874f, + 0.354813389f, 0.358921935f, 0.363078055f, 0.367282300f, 0.371535229f, + 0.375837404f, 0.380189396f, 0.384591782f, 0.389045145f, 0.393550075f, + 0.398107171f, 0.402717034f, 0.407380278f, 0.412097519f, 0.416869383f, + 0.421696503f, 0.426579519f, 0.431519077f, 0.436515832f, 0.441570447f, + 0.446683592f, 0.451855944f, 0.457088190f, 0.462381021f, 0.467735141f, + 0.473151259f, 0.478630092f, 0.484172368f, 0.489778819f, 0.495450191f, + 0.501187234f, 0.506990708f, 0.512861384f, 0.518800039f, 0.524807460f, // -6.0.0dB ( 1/2 ) + 0.530884444f, 0.537031796f, 0.543250331f, 0.549540874f, 0.555904257f, + 0.562341325f, 0.568852931f, 0.575439937f, 0.582103218f, 0.588843655f, + 0.595662144f, 0.602559586f, 0.609536897f, 0.616595002f, 0.623734835f, + 0.630957344f, 0.638263486f, 0.645654229f, 0.653130553f, 0.660693448f, + 0.668343918f, 0.676082975f, 0.683911647f, 0.691830971f, 0.699841996f, + 0.707945784f, 0.716143410f, 0.724435960f, 0.732824533f, 0.741310241f, + 0.749894209f, 0.758577575f, 0.767361489f, 0.776247117f, 0.785235635f, + 0.794328235f, 0.803526122f, 0.812830516f, 0.822242650f, 0.831763771f, // -2.0 dB + 0.841395142f, 0.851138038f, 0.860993752f, 0.870963590f, 0.881048873f, + 0.891250938f, 0.901571138f, 0.912010839f, 0.922571427f, 0.933254301f, // -1.0 dB + 0.944060876f, 0.954992586f, 0.966050879f, 0.977237221f, 0.988553095f, + 1.000000000f, 1.011579454f, 1.023292992f, 1.035142167f, 1.047128548f, // 0.0 dB + 1.059253725f, 1.071519305f, 1.083926914f, 1.096478196f, 1.109174815f, + 1.122018454f, 1.135010816f, 1.148153621f, 1.161448614f, 1.174897555f, // 1.0 dB + 1.188502227f, 1.202264435f, 1.216186001f, 1.230268771f, 1.244514612f, + 1.258925412f, 1.273503081f, 1.288249552f, 1.303166778f, 1.318256739f, // 2.0 dB + 1.333521432f, 1.348962883f, 1.364583137f, 1.380384265f, 1.396368361f, + 1.412537545f, 1.428893959f, 1.445439771f, 1.462177174f, 1.479108388f, + 1.496235656f, 1.513561248f, 1.531087462f, 1.548816619f, 1.566751070f, + 1.584893192f, 1.603245391f, 1.621810097f, 1.640589773f, 1.659586907f, + 1.678804018f, 1.698243652f, 1.717908387f, 1.737800829f, 1.757923614f, + 1.778279410f, 1.798870915f, 1.819700859f, 1.840772001f, 1.862087137f, + 1.883649089f, 1.905460718f, 1.927524913f, 1.949844600f, 1.972422736f, + 1.995262315f, // 6.0dB ( *2 ) +}; + +const int PanTableMax{256}; + +const int PanTableCenter{PanTableMax / 2}; + +const float Pan2RatioTableSqrt[PanTableMax + 1]{ + 1.000000000f, 0.998044964f, 0.996086091f, 0.994123358f, 0.992156742f, 0.990186220f, + 0.988211769f, 0.986233365f, 0.984250984f, 0.982264603f, 0.980274196f, 0.978279740f, + 0.976281209f, 0.974278579f, 0.972271824f, 0.970260919f, 0.968245837f, 0.966226552f, + 0.964203039f, 0.962175270f, 0.960143218f, 0.958106857f, 0.956066159f, 0.954021095f, + 0.951971638f, 0.949917760f, 0.947859431f, 0.945796622f, 0.943729304f, 0.941657448f, + 0.939581024f, 0.937500000f, 0.935414347f, 0.933324033f, 0.931229027f, 0.929129297f, + 0.927024811f, 0.924915537f, 0.922801441f, 0.920682491f, 0.918558654f, 0.916429894f, + 0.914296177f, 0.912157470f, 0.910013736f, 0.907864940f, 0.905711047f, 0.903552018f, + 0.901387819f, 0.899218411f, 0.897043756f, 0.894863816f, 0.892678554f, 0.890487928f, + 0.888291900f, 0.886090430f, 0.883883476f, 0.881670999f, 0.879452955f, 0.877229303f, + 0.875000000f, 0.872765003f, 0.870524267f, 0.868277749f, 0.866025404f, 0.863767185f, + 0.861503047f, 0.859232943f, 0.856956825f, 0.854674646f, 0.852386356f, 0.850091907f, + 0.847791248f, 0.845484329f, 0.843171098f, 0.840851503f, 0.838525492f, 0.836193010f, + 0.833854004f, 0.831508418f, 0.829156198f, 0.826797285f, 0.824431622f, 0.822059152f, + 0.819679816f, 0.817293552f, 0.814900301f, 0.812500000f, 0.810092587f, 0.807677999f, + 0.805256170f, 0.802827036f, 0.800390530f, 0.797946583f, 0.795495129f, 0.793036096f, + 0.790569415f, 0.788095013f, 0.785612818f, 0.783122755f, 0.780624750f, 0.778118725f, + 0.775604603f, 0.773082305f, 0.770551750f, 0.768012858f, 0.765465545f, 0.762909726f, + 0.760345316f, 0.757772228f, 0.755190373f, 0.752599661f, 0.750000000f, 0.747391296f, + 0.744773455f, 0.742146380f, 0.739509973f, 0.736864133f, 0.734208758f, 0.731543744f, + 0.728868987f, 0.726184377f, 0.723489806f, 0.720785162f, 0.718070331f, 0.715345196f, + 0.712609641f, 0.709863543f, 0.707106781f, 0.704339229f, 0.701560760f, 0.698771243f, + 0.695970545f, 0.693158532f, 0.690335064f, 0.687500000f, 0.684653197f, 0.681794507f, + 0.678923781f, 0.676040864f, 0.673145601f, 0.670237831f, 0.667317391f, 0.664384113f, + 0.661437828f, 0.658478360f, 0.655505530f, 0.652519157f, 0.649519053f, 0.646505027f, + 0.643476884f, 0.640434423f, 0.637377439f, 0.634305723f, 0.631219059f, 0.628117226f, + 0.625000000f, 0.621867148f, 0.618718434f, 0.615553613f, 0.612372436f, 0.609174647f, + 0.605959982f, 0.602728173f, 0.599478940f, 0.596212001f, 0.592927061f, 0.589623821f, + 0.586301970f, 0.582961191f, 0.579601156f, 0.576221529f, 0.572821962f, 0.569402099f, + 0.565961571f, 0.562500000f, 0.559016994f, 0.555512151f, 0.551985054f, 0.548435274f, + 0.544862368f, 0.541265877f, 0.537645329f, 0.534000234f, 0.530330086f, 0.526634361f, + 0.522912517f, 0.519163991f, 0.515388203f, 0.511584548f, 0.507752400f, 0.503891109f, + 0.500000000f, 0.496078371f, 0.492125492f, 0.488140605f, 0.484122918f, 0.480071609f, + 0.475985819f, 0.471864652f, 0.467707173f, 0.463512405f, 0.459279327f, 0.455006868f, + 0.450693909f, 0.446339277f, 0.441941738f, 0.437500000f, 0.433012702f, 0.428478413f, + 0.423895624f, 0.419262746f, 0.414578099f, 0.409839908f, 0.405046294f, 0.400195265f, + 0.395284708f, 0.390312375f, 0.385275875f, 0.380172658f, 0.375000000f, 0.369754986f, + 0.364434493f, 0.359035165f, 0.353553391f, 0.347985273f, 0.342326598f, 0.336572800f, + 0.330718914f, 0.324759526f, 0.318688720f, 0.312500000f, 0.306186218f, 0.299739470f, + 0.293150985f, 0.286410981f, 0.279508497f, 0.272431184f, 0.265165043f, 0.257694102f, + 0.250000000f, 0.242061459f, 0.233853587f, 0.225346955f, 0.216506351f, 0.207289049f, + 0.197642354f, 0.187500000f, 0.176776695f, 0.165359457f, 0.153093109f, 0.139754249f, + 0.125000000f, 0.108253175f, 0.088388348f, 0.062500000f, 0.000000000f, +}; + +const float Pan2RatioTableSqrtSurround[PanTableMax + 1]{ + 0.800000000f, 0.799274270f, 0.798548500f, 0.797822830f, 0.797097090f, 0.796371340f, + 0.795645650f, 0.794919900f, 0.794194160f, 0.793468480f, 0.792742700f, 0.792016980f, + 0.791291240f, 0.790565550f, 0.789839800f, 0.789114060f, 0.788388370f, 0.787662600f, + 0.786936880f, 0.786211190f, 0.785485450f, 0.784759700f, 0.784033950f, 0.783308270f, + 0.782582500f, 0.781856780f, 0.781131090f, 0.780405340f, 0.779679600f, 0.778953850f, + 0.778228160f, 0.777502400f, 0.776776670f, 0.776050980f, 0.775325240f, 0.774599490f, + 0.773873800f, 0.773148060f, 0.772422300f, 0.771696570f, 0.770970880f, 0.770245130f, + 0.769519390f, 0.768793700f, 0.768067960f, 0.767342200f, 0.766616500f, 0.765890780f, + 0.765165030f, 0.764439340f, 0.763713600f, 0.762987850f, 0.762262100f, 0.761536400f, + 0.760810670f, 0.760084900f, 0.759359240f, 0.758633490f, 0.757907750f, 0.757182060f, + 0.756456300f, 0.755730570f, 0.755004800f, 0.754279140f, 0.753553390f, 0.752827640f, + 0.752101960f, 0.751376200f, 0.750650470f, 0.749924700f, 0.749199030f, 0.748473290f, + 0.747747540f, 0.747021850f, 0.746296100f, 0.745570360f, 0.744844680f, 0.744118900f, + 0.743393180f, 0.742667440f, 0.741941750f, 0.741216000f, 0.740490260f, 0.739764570f, + 0.739038800f, 0.738313080f, 0.737587390f, 0.736861650f, 0.736135900f, 0.735410200f, + 0.734684470f, 0.733958700f, 0.733232980f, 0.732507290f, 0.731781540f, 0.731055800f, + 0.730330100f, 0.729604360f, 0.728878600f, 0.728152930f, 0.727427180f, 0.726701440f, + 0.725975690f, 0.725250000f, 0.724524260f, 0.723798500f, 0.723072800f, 0.722347080f, + 0.721621330f, 0.720895590f, 0.720169900f, 0.719444160f, 0.718718400f, 0.717992700f, + 0.717266980f, 0.716541230f, 0.715815540f, 0.715089800f, 0.714364050f, 0.713638300f, + 0.712912600f, 0.712186870f, 0.711461100f, 0.710735440f, 0.710009690f, 0.709283950f, + 0.708558260f, 0.707832500f, 0.707106770f, 0.704339200f, 0.701560740f, 0.698771240f, + 0.695970540f, 0.693158500f, 0.690335040f, 0.687500000f, 0.684653200f, 0.681794500f, + 0.678923790f, 0.676040890f, 0.673145590f, 0.670237840f, 0.667317390f, 0.664384100f, + 0.661437800f, 0.658478380f, 0.655505540f, 0.652519170f, 0.649519000f, 0.646505000f, + 0.643476900f, 0.640434440f, 0.637377440f, 0.634305700f, 0.631219000f, 0.628117200f, + 0.625000000f, 0.621867100f, 0.618718450f, 0.615553600f, 0.612372460f, 0.609174670f, + 0.605960000f, 0.602728190f, 0.599478960f, 0.596212000f, 0.592927040f, 0.589623800f, + 0.586301980f, 0.582961200f, 0.579601170f, 0.576221500f, 0.572821970f, 0.569402100f, + 0.565961600f, 0.562500000f, 0.559017000f, 0.555512130f, 0.551985000f, 0.548435270f, + 0.544862390f, 0.541265900f, 0.537645340f, 0.534000200f, 0.530330060f, 0.526634340f, + 0.522912500f, 0.519163970f, 0.515388190f, 0.511584500f, 0.507752400f, 0.503891100f, + 0.500000000f, 0.496078370f, 0.492125480f, 0.488140600f, 0.484122930f, 0.480071600f, + 0.475985830f, 0.471864640f, 0.467707190f, 0.463512390f, 0.459279330f, 0.455006870f, + 0.450693900f, 0.446339280f, 0.441941740f, 0.437500000f, 0.433012690f, 0.428478420f, + 0.423895630f, 0.419262740f, 0.414578100f, 0.409839900f, 0.405046280f, 0.400195270f, + 0.395284700f, 0.390312370f, 0.385275870f, 0.380172670f, 0.375000000f, 0.369755000f, + 0.364434480f, 0.359035160f, 0.353553380f, 0.347985270f, 0.342326600f, 0.336572800f, + 0.330718900f, 0.324759500f, 0.318688720f, 0.312500000f, 0.306186230f, 0.299739480f, + 0.293150990f, 0.286410990f, 0.279508500f, 0.272431190f, 0.265165030f, 0.257694100f, + 0.250000000f, 0.242061470f, 0.233853590f, 0.225346950f, 0.216506350f, 0.207289050f, + 0.197642360f, 0.187500000f, 0.176776690f, 0.165359450f, 0.153093110f, 0.139754250f, + 0.125000000f, 0.108253170f, 0.088388346f, 0.062500000f, 0.000000000f, +}; + +const float Pan2RatioTableSinCos[PanTableMax + 1]{ + 1.000000000f, 0.999981175f, 0.999924702f, 0.999830582f, 0.999698819f, 0.999529418f, + 0.999322385f, 0.999077728f, 0.998795456f, 0.998475581f, 0.998118113f, 0.997723067f, + 0.997290457f, 0.996820299f, 0.996312612f, 0.995767414f, 0.995184727f, 0.994564571f, + 0.993906970f, 0.993211949f, 0.992479535f, 0.991709754f, 0.990902635f, 0.990058210f, + 0.989176510f, 0.988257568f, 0.987301418f, 0.986308097f, 0.985277642f, 0.984210092f, + 0.983105487f, 0.981963869f, 0.980785280f, 0.979569766f, 0.978317371f, 0.977028143f, + 0.975702130f, 0.974339383f, 0.972939952f, 0.971503891f, 0.970031253f, 0.968522094f, + 0.966976471f, 0.965394442f, 0.963776066f, 0.962121404f, 0.960430519f, 0.958703475f, + 0.956940336f, 0.955141168f, 0.953306040f, 0.951435021f, 0.949528181f, 0.947585591f, + 0.945607325f, 0.943593458f, 0.941544065f, 0.939459224f, 0.937339012f, 0.935183510f, + 0.932992799f, 0.930766961f, 0.928506080f, 0.926210242f, 0.923879533f, 0.921514039f, + 0.919113852f, 0.916679060f, 0.914209756f, 0.911706032f, 0.909167983f, 0.906595705f, + 0.903989293f, 0.901348847f, 0.898674466f, 0.895966250f, 0.893224301f, 0.890448723f, + 0.887639620f, 0.884797098f, 0.881921264f, 0.879012226f, 0.876070094f, 0.873094978f, + 0.870086991f, 0.867046246f, 0.863972856f, 0.860866939f, 0.857728610f, 0.854557988f, + 0.851355193f, 0.848120345f, 0.844853565f, 0.841554977f, 0.838224706f, 0.834862875f, + 0.831469612f, 0.828045045f, 0.824589303f, 0.821102515f, 0.817584813f, 0.814036330f, + 0.810457198f, 0.806847554f, 0.803207531f, 0.799537269f, 0.795836905f, 0.792106577f, + 0.788346428f, 0.784556597f, 0.780737229f, 0.776888466f, 0.773010453f, 0.769103338f, + 0.765167266f, 0.761202385f, 0.757208847f, 0.753186799f, 0.749136395f, 0.745057785f, + 0.740951125f, 0.736816569f, 0.732654272f, 0.728464390f, 0.724247083f, 0.720002508f, + 0.715730825f, 0.711432196f, 0.707106781f, 0.702754744f, 0.698376249f, 0.693971461f, + 0.689540545f, 0.685083668f, 0.680600998f, 0.676092704f, 0.671558955f, 0.666999922f, + 0.662415778f, 0.657806693f, 0.653172843f, 0.648514401f, 0.643831543f, 0.639124445f, + 0.634393284f, 0.629638239f, 0.624859488f, 0.620057212f, 0.615231591f, 0.610382806f, + 0.605511041f, 0.600616479f, 0.595699304f, 0.590759702f, 0.585797857f, 0.580813958f, + 0.575808191f, 0.570780746f, 0.565731811f, 0.560661576f, 0.555570233f, 0.550457973f, + 0.545324988f, 0.540171473f, 0.534997620f, 0.529803625f, 0.524589683f, 0.519355990f, + 0.514102744f, 0.508830143f, 0.503538384f, 0.498227667f, 0.492898192f, 0.487550160f, + 0.482183772f, 0.476799230f, 0.471396737f, 0.465976496f, 0.460538711f, 0.455083587f, + 0.449611330f, 0.444122145f, 0.438616239f, 0.433093819f, 0.427555093f, 0.422000271f, + 0.416429560f, 0.410843171f, 0.405241314f, 0.399624200f, 0.393992040f, 0.388345047f, + 0.382683432f, 0.377007410f, 0.371317194f, 0.365612998f, 0.359895037f, 0.354163525f, + 0.348418680f, 0.342660717f, 0.336889853f, 0.331106306f, 0.325310292f, 0.319502031f, + 0.313681740f, 0.307849640f, 0.302005949f, 0.296150888f, 0.290284677f, 0.284407537f, + 0.278519689f, 0.272621355f, 0.266712757f, 0.260794118f, 0.254865660f, 0.248927606f, + 0.242980180f, 0.237023606f, 0.231058108f, 0.225083911f, 0.219101240f, 0.213110320f, + 0.207111376f, 0.201104635f, 0.195090322f, 0.189068664f, 0.183039888f, 0.177004220f, + 0.170961889f, 0.164913120f, 0.158858143f, 0.152797185f, 0.146730474f, 0.140658239f, + 0.134580709f, 0.128498111f, 0.122410675f, 0.116318631f, 0.110222207f, 0.104121634f, + 0.098017140f, 0.091908956f, 0.085797312f, 0.079682438f, 0.073564564f, 0.067443920f, + 0.061320736f, 0.055195244f, 0.049067674f, 0.042938257f, 0.036807223f, 0.030674803f, + 0.024541229f, 0.018406730f, 0.012271538f, 0.006135885f, 0.000000000f}; + +const float Pan2RatioTableSinCosSurround[PanTableMax + 1]{ + 0.800000000f, 0.799274270f, 0.798548500f, 0.797822830f, 0.797097090f, 0.796371340f, + 0.795645650f, 0.794919900f, 0.794194160f, 0.793468480f, 0.792742700f, 0.792016980f, + 0.791291240f, 0.790565550f, 0.789839800f, 0.789114060f, 0.788388370f, 0.787662600f, + 0.786936880f, 0.786211190f, 0.785485450f, 0.784759700f, 0.784033950f, 0.783308270f, + 0.782582500f, 0.781856780f, 0.781131090f, 0.780405340f, 0.779679600f, 0.778953850f, + 0.778228160f, 0.777502400f, 0.776776670f, 0.776050980f, 0.775325240f, 0.774599490f, + 0.773873800f, 0.773148060f, 0.772422300f, 0.771696570f, 0.770970880f, 0.770245130f, + 0.769519390f, 0.768793700f, 0.768067960f, 0.767342200f, 0.766616500f, 0.765890780f, + 0.765165030f, 0.764439340f, 0.763713600f, 0.762987850f, 0.762262100f, 0.761536400f, + 0.760810670f, 0.760084900f, 0.759359240f, 0.758633490f, 0.757907750f, 0.757182060f, + 0.756456300f, 0.755730570f, 0.755004800f, 0.754279140f, 0.753553390f, 0.752827640f, + 0.752101960f, 0.751376200f, 0.750650470f, 0.749924700f, 0.749199030f, 0.748473290f, + 0.747747540f, 0.747021850f, 0.746296100f, 0.745570360f, 0.744844680f, 0.744118900f, + 0.743393180f, 0.742667440f, 0.741941750f, 0.741216000f, 0.740490260f, 0.739764570f, + 0.739038800f, 0.738313080f, 0.737587390f, 0.736861650f, 0.736135900f, 0.735410200f, + 0.734684470f, 0.733958700f, 0.733232980f, 0.732507290f, 0.731781540f, 0.731055800f, + 0.730330100f, 0.729604360f, 0.728878600f, 0.728152930f, 0.727427180f, 0.726701440f, + 0.725975690f, 0.725250000f, 0.724524260f, 0.723798500f, 0.723072800f, 0.722347080f, + 0.721621330f, 0.720895590f, 0.720169900f, 0.719444160f, 0.718718400f, 0.717992700f, + 0.717266980f, 0.716541230f, 0.715815540f, 0.715089800f, 0.714364050f, 0.713638300f, + 0.712912600f, 0.712186870f, 0.711461100f, 0.710735440f, 0.710009690f, 0.709283950f, + 0.708558260f, 0.707832500f, 0.707106770f, 0.702754740f, 0.698376240f, 0.693971460f, + 0.689540570f, 0.685083690f, 0.680601000f, 0.676092680f, 0.671558980f, 0.666999940f, + 0.662415800f, 0.657806690f, 0.653172850f, 0.648514390f, 0.643831550f, 0.639124450f, + 0.634393270f, 0.629638250f, 0.624859500f, 0.620057200f, 0.615231570f, 0.610382800f, + 0.605511070f, 0.600616460f, 0.595699300f, 0.590759690f, 0.585797850f, 0.580813940f, + 0.575808170f, 0.570780750f, 0.565731800f, 0.560661550f, 0.555570240f, 0.550457950f, + 0.545324980f, 0.540171440f, 0.534997640f, 0.529803630f, 0.524589660f, 0.519356000f, + 0.514102760f, 0.508830130f, 0.503538370f, 0.498227660f, 0.492898200f, 0.487550170f, + 0.482183780f, 0.476799220f, 0.471396740f, 0.465976500f, 0.460538720f, 0.455083580f, + 0.449611340f, 0.444122140f, 0.438616250f, 0.433093820f, 0.427555080f, 0.422000260f, + 0.416429550f, 0.410843160f, 0.405241300f, 0.399624200f, 0.393992040f, 0.388345030f, + 0.382683430f, 0.377007420f, 0.371317200f, 0.365612980f, 0.359895050f, 0.354163530f, + 0.348418680f, 0.342660730f, 0.336889860f, 0.331106300f, 0.325310290f, 0.319502030f, + 0.313681750f, 0.307849650f, 0.302005950f, 0.296150890f, 0.290284660f, 0.284407530f, + 0.278519690f, 0.272621360f, 0.266712750f, 0.260794100f, 0.254865650f, 0.248927610f, + 0.242980180f, 0.237023600f, 0.231058100f, 0.225083920f, 0.219101240f, 0.213110310f, + 0.207111370f, 0.201104640f, 0.195090320f, 0.189068660f, 0.183039890f, 0.177004220f, + 0.170961890f, 0.164913120f, 0.158858150f, 0.152797190f, 0.146730470f, 0.140658240f, + 0.134580700f, 0.128498100f, 0.122410680f, 0.116318630f, 0.110222210f, 0.104121630f, + 0.098017140f, 0.091908954f, 0.085797310f, 0.079682440f, 0.073564567f, 0.067443920f, + 0.061320737f, 0.055195242f, 0.049067672f, 0.042938258f, 0.036807224f, 0.030674802f, + 0.024541229f, 0.018406730f, 0.012271538f, 0.006135885f, 0.000000000f, +}; + +const float Pan2RatioTableLinear[PanTableMax + 1]{ + 1.000000000f, 0.996093750f, 0.992187500f, 0.988281250f, 0.984375000f, 0.980468750f, + 0.976562500f, 0.972656250f, 0.968750000f, 0.964843750f, 0.960937500f, 0.957031250f, + 0.953125000f, 0.949218750f, 0.945312500f, 0.941406250f, 0.937500000f, 0.933593750f, + 0.929687500f, 0.925781250f, 0.921875000f, 0.917968750f, 0.914062500f, 0.910156250f, + 0.906250000f, 0.902343750f, 0.898437500f, 0.894531250f, 0.890625000f, 0.886718750f, + 0.882812500f, 0.878906250f, 0.875000000f, 0.871093750f, 0.867187500f, 0.863281250f, + 0.859375000f, 0.855468750f, 0.851562500f, 0.847656250f, 0.843750000f, 0.839843750f, + 0.835937500f, 0.832031250f, 0.828125000f, 0.824218750f, 0.820312500f, 0.816406250f, + 0.812500000f, 0.808593750f, 0.804687500f, 0.800781250f, 0.796875000f, 0.792968750f, + 0.789062500f, 0.785156250f, 0.781250000f, 0.777343750f, 0.773437500f, 0.769531250f, + 0.765625000f, 0.761718750f, 0.757812500f, 0.753906250f, 0.750000000f, 0.746093750f, + 0.742187500f, 0.738281250f, 0.734375000f, 0.730468750f, 0.726562500f, 0.722656250f, + 0.718750000f, 0.714843750f, 0.710937500f, 0.707031250f, 0.703125000f, 0.699218750f, + 0.695312500f, 0.691406250f, 0.687500000f, 0.683593750f, 0.679687500f, 0.675781250f, + 0.671875000f, 0.667968750f, 0.664062500f, 0.660156250f, 0.656250000f, 0.652343750f, + 0.648437500f, 0.644531250f, 0.640625000f, 0.636718750f, 0.632812500f, 0.628906250f, + 0.625000000f, 0.621093750f, 0.617187500f, 0.613281250f, 0.609375000f, 0.605468750f, + 0.601562500f, 0.597656250f, 0.593750000f, 0.589843750f, 0.585937500f, 0.582031250f, + 0.578125000f, 0.574218750f, 0.570312500f, 0.566406250f, 0.562500000f, 0.558593750f, + 0.554687500f, 0.550781250f, 0.546875000f, 0.542968750f, 0.539062500f, 0.535156250f, + 0.531250000f, 0.527343750f, 0.523437500f, 0.519531250f, 0.515625000f, 0.511718750f, + 0.507812500f, 0.503906250f, 0.500000000f, 0.496093750f, 0.492187500f, 0.488281250f, + 0.484375000f, 0.480468750f, 0.476562500f, 0.472656250f, 0.468750000f, 0.464843750f, + 0.460937500f, 0.457031250f, 0.453125000f, 0.449218750f, 0.445312500f, 0.441406250f, + 0.437500000f, 0.433593750f, 0.429687500f, 0.425781250f, 0.421875000f, 0.417968750f, + 0.414062500f, 0.410156250f, 0.406250000f, 0.402343750f, 0.398437500f, 0.394531250f, + 0.390625000f, 0.386718750f, 0.382812500f, 0.378906250f, 0.375000000f, 0.371093750f, + 0.367187500f, 0.363281250f, 0.359375000f, 0.355468750f, 0.351562500f, 0.347656250f, + 0.343750000f, 0.339843750f, 0.335937500f, 0.332031250f, 0.328125000f, 0.324218750f, + 0.320312500f, 0.316406250f, 0.312500000f, 0.308593750f, 0.304687500f, 0.300781250f, + 0.296875000f, 0.292968750f, 0.289062500f, 0.285156250f, 0.281250000f, 0.277343750f, + 0.273437500f, 0.269531250f, 0.265625000f, 0.261718750f, 0.257812500f, 0.253906250f, + 0.250000000f, 0.246093750f, 0.242187500f, 0.238281250f, 0.234375000f, 0.230468750f, + 0.226562500f, 0.222656250f, 0.218750000f, 0.214843750f, 0.210937500f, 0.207031250f, + 0.203125000f, 0.199218750f, 0.195312500f, 0.191406250f, 0.187500000f, 0.183593750f, + 0.179687500f, 0.175781250f, 0.171875000f, 0.167968750f, 0.164062500f, 0.160156250f, + 0.156250000f, 0.152343750f, 0.148437500f, 0.144531250f, 0.140625000f, 0.136718750f, + 0.132812500f, 0.128906250f, 0.125000000f, 0.121093750f, 0.117187500f, 0.113281250f, + 0.109375000f, 0.105468750f, 0.101562500f, 0.097656250f, 0.093750000f, 0.089843750f, + 0.085937500f, 0.082031250f, 0.078125000f, 0.074218750f, 0.070312500f, 0.066406250f, + 0.062500000f, 0.058593750f, 0.054687500f, 0.050781250f, 0.046875000f, 0.042968750f, + 0.039062500f, 0.035156250f, 0.031250000f, 0.027343750f, 0.023437500f, 0.019531250f, + 0.015625000f, 0.011718750f, 0.007812500f, 0.003906250f, 0.000000000f, +}; + +const float Pan2RatioTableLinearSurround[PanTableMax + 1]{ + 0.800000000f, 0.797656240f, 0.795312500f, 0.792968750f, 0.790624980f, 0.788281260f, + 0.785937490f, 0.783593770f, 0.781250000f, 0.778906200f, 0.776562500f, 0.774218740f, + 0.771875000f, 0.769531250f, 0.767187480f, 0.764843760f, 0.762499990f, 0.760156270f, + 0.757812500f, 0.755468700f, 0.753125000f, 0.750781240f, 0.748437500f, 0.746093750f, + 0.743749980f, 0.741406260f, 0.739062490f, 0.736718770f, 0.734375000f, 0.732031200f, + 0.729687500f, 0.727343740f, 0.725000000f, 0.722656250f, 0.720312480f, 0.717968760f, + 0.715624990f, 0.713281270f, 0.710937500f, 0.708593700f, 0.706250000f, 0.703906240f, + 0.701562500f, 0.699218750f, 0.696874980f, 0.694531260f, 0.692187490f, 0.689843770f, + 0.687500000f, 0.685156200f, 0.682812500f, 0.680468740f, 0.678125000f, 0.675781250f, + 0.673437480f, 0.671093760f, 0.668749990f, 0.666406270f, 0.664062500f, 0.661718700f, + 0.659375000f, 0.657031240f, 0.654687500f, 0.652343750f, 0.649999980f, 0.647656260f, + 0.645312490f, 0.642968770f, 0.640625000f, 0.638281200f, 0.635937500f, 0.633593740f, + 0.631250000f, 0.628906250f, 0.626562480f, 0.624218760f, 0.621874990f, 0.619531270f, + 0.617187500f, 0.614843700f, 0.612500000f, 0.610156240f, 0.607812500f, 0.605468750f, + 0.603124980f, 0.600781260f, 0.598437490f, 0.596093770f, 0.593750000f, 0.591406200f, + 0.589062500f, 0.586718740f, 0.584375000f, 0.582031250f, 0.579687480f, 0.577343760f, + 0.574999990f, 0.572656270f, 0.570312500f, 0.567968700f, 0.565625000f, 0.563281240f, + 0.560937500f, 0.558593750f, 0.556249980f, 0.553906260f, 0.551562490f, 0.549218770f, + 0.546875000f, 0.544531200f, 0.542187500f, 0.539843740f, 0.537500000f, 0.535156250f, + 0.532812480f, 0.530468760f, 0.528124990f, 0.525781270f, 0.523437500f, 0.521093700f, + 0.518750000f, 0.516406240f, 0.514062500f, 0.511718750f, 0.509374980f, 0.507031260f, + 0.504687490f, 0.502343770f, 0.500000000f, 0.496093750f, 0.492187500f, 0.488281250f, + 0.484375000f, 0.480468750f, 0.476562500f, 0.472656250f, 0.468750000f, 0.464843750f, + 0.460937500f, 0.457031250f, 0.453125000f, 0.449218750f, 0.445312500f, 0.441406250f, + 0.437500000f, 0.433593750f, 0.429687500f, 0.425781250f, 0.421875000f, 0.417968750f, + 0.414062500f, 0.410156250f, 0.406250000f, 0.402343750f, 0.398437500f, 0.394531250f, + 0.390625000f, 0.386718750f, 0.382812500f, 0.378906250f, 0.375000000f, 0.371093750f, + 0.367187500f, 0.363281250f, 0.359375000f, 0.355468750f, 0.351562500f, 0.347656250f, + 0.343750000f, 0.339843750f, 0.335937500f, 0.332031250f, 0.328125000f, 0.324218750f, + 0.320312500f, 0.316406250f, 0.312500000f, 0.308593750f, 0.304687500f, 0.300781250f, + 0.296875000f, 0.292968750f, 0.289062500f, 0.285156250f, 0.281250000f, 0.277343750f, + 0.273437500f, 0.269531250f, 0.265625000f, 0.261718750f, 0.257812500f, 0.253906250f, + 0.250000000f, 0.246093750f, 0.242187500f, 0.238281250f, 0.234375000f, 0.230468750f, + 0.226562500f, 0.222656250f, 0.218750000f, 0.214843750f, 0.210937500f, 0.207031250f, + 0.203125000f, 0.199218750f, 0.195312500f, 0.191406250f, 0.187500000f, 0.183593750f, + 0.179687500f, 0.175781250f, 0.171875000f, 0.167968750f, 0.164062500f, 0.160156250f, + 0.156250000f, 0.152343750f, 0.148437500f, 0.144531250f, 0.140625000f, 0.136718750f, + 0.132812500f, 0.128906250f, 0.125000000f, 0.121093750f, 0.117187500f, 0.113281250f, + 0.109375000f, 0.105468750f, 0.101562500f, 0.097656250f, 0.093750000f, 0.089843750f, + 0.085937500f, 0.082031250f, 0.078125000f, 0.074218750f, 0.070312500f, 0.066406250f, + 0.062500000f, 0.058593750f, 0.054687500f, 0.050781250f, 0.046875000f, 0.042968750f, + 0.039062500f, 0.035156250f, 0.031250000f, 0.027343750f, 0.023437500f, 0.019531250f, + 0.015625000f, 0.011718750f, 0.007812500f, 0.003906250f, 0.000000000f, +}; + +const float* PanTableTable[3]{Pan2RatioTableSqrt, Pan2RatioTableSinCos, Pan2RatioTableLinear}; + +const float* PanTableTableForSurround[3]{Pan2RatioTableSqrtSurround, Pan2RatioTableSinCosSurround, + Pan2RatioTableLinearSurround}; + +} // anonymous namespace + +const float Util::CalcLpfFreqIntercept{0.135614380f}; + +const float Util::CalcLpfFreqThreshold{0.9f}; + +const u16 Util::CalcLpfFreqTable[Util::CalcLpfFreqTableSize]{ + 80, 100, 128, 160, 200, 256, 320, 400, 500, 640, 800, 1000, + 1280, 1600, 2000, 2560, 3200, 4000, 5120, 6400, 8000, 10240, 12800, 16000, +}; + +const BiquadFilterCoefficients + Util::LowPassFilterCoefficientsTable32000[Util::CalcLpfFreqTableSize]{ + {299, 0, 0, 16085, 0}, {333, 0, 0, 16051, 0}, {387, 0, 0, 15997, 0}, + {519, 0, 0, 15865, 0}, {645, 0, 0, 15739, 0}, {796, 0, 0, 15588, 0}, + {1007, 0, 0, 15377, 0}, {1239, 0, 0, 15145, 0}, {1526, 0, 0, 14858, 0}, + {1932, 0, 0, 14452, 0}, {2381, 0, 0, 14003, 0}, {2910, 0, 0, 13474, 0}, + {3626, 0, 0, 12758, 0}, {4387, 0, 0, 11997, 0}, {5266, 0, 0, 11118, 0}, + {6372, 0, 0, 10012, 0}, {7470, 0, 0, 8914, 0}, {8628, 0, 0, 7756, 0}, + {9918, 0, 0, 6466, 0}, {11021, 0, 0, 5363, 0}, {11994, 0, 0, 4390, 0}, + {12850, 0, 0, 3534, 0}, {13369, 0, 0, 3015, 0}, {13573, 0, 0, 2811, 0}, + }; + +const BiquadFilterCoefficients + Util::LowPassFilterCoefficientsTable48000[Util::CalcLpfFreqTableSize]{ + {244, 0, 0, 16140, 0}, {273, 0, 0, 16111, 0}, {308, 0, 0, 16076, 0}, + {344, 0, 0, 16040, 0}, {412, 0, 0, 15972, 0}, {555, 0, 0, 15829, 0}, + {682, 0, 0, 15702, 0}, {835, 0, 0, 15549, 0}, {1045, 0, 0, 15339, 0}, + {1323, 0, 0, 15061, 0}, {1631, 0, 0, 14753, 0}, {2011, 0, 0, 14373, 0}, + {2523, 0, 0, 13861, 0}, {3089, 0, 0, 13295, 0}, {3757, 0, 0, 12627, 0}, + {4630, 0, 0, 11754, 0}, {5542, 0, 0, 10842, 0}, {6566, 0, 0, 9818, 0}, + {7801, 0, 0, 8583, 0}, {8968, 0, 0, 7416, 0}, {10126, 0, 0, 6258, 0}, + {11318, 0, 0, 5066, 0}, {12243, 0, 0, 4141, 0}, {12965, 0, 0, 3419, 0}, + }; + +u16 Util::CalcLpfFreq(float scale) { + scale = fnd::FloatClamp(scale, 0.0f, 1.0f); + + u16 freq{0}; + + if (scale < CalcLpfFreqIntercept) { + freq = CalcLpfFreqTable[0]; + } else if (scale >= CalcLpfFreqThreshold) { + freq = CalcLpfFreqTable[CalcLpfFreqTableSize - 1]; + } else { + int idx{static_cast((scale - CalcLpfFreqIntercept) / (0.1f / 3.0f))}; + freq = CalcLpfFreqTable[idx]; + } + + return freq; +} + +// NON_MATCHING: wrong order of operations +BiquadFilterCoefficients Util::CalcLowPassFilterCoefficients(int frequency, int sampleRate, + bool isTableUsed) { + if (isTableUsed) { + int index{FindLpfFreqTableIndex(frequency)}; + + if (sampleRate == 32000) + return LowPassFilterCoefficientsTable32000[index]; + + if (sampleRate == 48000) + return LowPassFilterCoefficientsTable48000[index]; + + NN_UNEXPECTED_DEFAULT; + } else { + float coef; + float filterParam; + util::AngleIndex angle = (static_cast(frequency) << 32) / sampleRate; + coef = 2.0f - util::CosTable(angle); + filterParam = sqrt(coef * coef - 1.0f); + + BiquadFilterCoefficients parameter; + + parameter.b1 = filterParam; + parameter.b0 = (filterParam - coef) * -16384.0f; + parameter.b2 = coef; + + return parameter; + } +} + +int Util::FindLpfFreqTableIndex(int frequency) { + int lowIndex{0}; + int highIndex{CalcLpfFreqTableSize - 1}; + + while (lowIndex <= highIndex) { + const int index{(lowIndex + highIndex) / 2}; + + if (CalcLpfFreqTable[index] < frequency) + lowIndex = index + 1; + + else if (CalcLpfFreqTable[index] > frequency) + highIndex = index - 1; + + else + return index; + } + + return -1; +} + +float Util::CalcPanRatio(float pan, const PanInfo& info, OutputMode mode) { + pan = (fnd::FloatClamp(pan, -1.0f, 1.0f) + 1.0f) / 2.0f; + + const float* table; + if (!info.isEnableFrontBypass && mode == OutputMode_Surround) + table = PanTableTableForSurround[info.curve]; + else + table = PanTableTable[info.curve]; + + float ratio{table[static_cast(pan * PanTableMax + 0.5f)]}; + + if (info.centerZeroFlag) + ratio /= table[PanTableCenter]; + + if (info.zeroClampFlag) + ratio = fnd::Clamp(ratio, 0.0f, 1.0f); + else + ratio = fnd::Clamp(ratio, 0.0f, 2.0f); + + return ratio; +} + +float Util::CalcSurroundPanRatio(float surroundPan, const PanInfo& info) { + surroundPan = fnd::FloatClamp(surroundPan, 0.0f, 2.0f) / 2.0f; + + const float* table{PanTableTableForSurround[info.curve]}; + + float ratio{table[static_cast(surroundPan * PanTableMax + 0.5f)]}; + + ratio = fnd::Clamp(ratio, 0.0f, 2.0f); + + return ratio; +} + +float Util::CalcVolumeRatio(float dB) { + dB = fnd::Clamp(dB, -90.4f, 6.0f); + + int index{static_cast(dB * 10.0f) - VolumeDbMin}; + + return Decibel2RatioTable[index]; +} + +u16 Util::CalcRandom() { + static u32 u; + + u *= 0x19660d; + u += 0x3c6ef35f; + return u >> 16; +} + +size_t Util::GetSampleByByte(size_t byte, SampleFormat format) { + size_t samples{0}; + size_t frac; + + switch (format) { + case SampleFormat_DspAdpcm: + samples = (byte / 8) * 14; + frac = byte & 7; + + if (frac != 0) + samples += frac * 2 - 2; + + break; + + case SampleFormat_PcmS8: + samples = byte; + break; + + case SampleFormat_PcmS16: + samples = byte >> 1; + break; + + default: + break; + } + + return samples; +} + +size_t Util::GetByteBySample(size_t samples, SampleFormat format) { + size_t byte{0}; + size_t frac; + + switch (format) { + case SampleFormat_DspAdpcm: + byte = (samples / 14) * 8; + frac = samples % 14; + + if (frac != 0) + byte += (frac + 1) / 2 + 1; + + break; + + case SampleFormat_PcmS8: + byte = samples; + break; + + case SampleFormat_PcmS16: + byte = samples << 1; + break; + + default: + break; + } + + return byte; +} + +bool Util::IsValidMemoryForDsp([[maybe_unused]] const void* ptr, [[maybe_unused]] size_t size) { + return true; +} + +const void* Util::GetWaveFile(u32 waveArchiveId, u32 waveIndex, const SoundArchive& arc, + const SoundArchivePlayer& player) { + SoundArchive::WaveArchiveInfo info; + if (!arc.ReadWaveArchiveInfo(waveArchiveId, &info)) + return nullptr; + + const void* warcFile{player.detail_GetFileAddress(info.fileId)}; + if (warcFile == nullptr) + return nullptr; + + WaveArchiveFileReader reader{warcFile, info.isLoadIndividual}; + const void* waveFile{reader.GetWaveFile(waveIndex)}; + + return waveFile; +} + +const void* Util::GetWaveFile(u32 waveArchiveId, u32 waveIndex, const SoundArchive& arc, + const PlayerHeapDataManager* mgr) { + SoundArchive::WaveArchiveInfo info; + if (!arc.ReadWaveArchiveInfo(waveArchiveId, &info)) + return nullptr; + + const void* warcFile{mgr->GetFileAddress(info.fileId)}; + if (warcFile == nullptr) + return nullptr; + + WaveArchiveFileReader reader{warcFile, info.isLoadIndividual}; + const void* waveFile{reader.GetWaveFile(waveIndex)}; + + return waveFile; +} + +Util::WaveArchiveLoadStatus Util::GetWaveArchiveOfBank(LoadItemInfo& warcLoadInfo, + bool& isLoadIndividual, const void* bankFile, + const SoundArchive& arc, + const SoundArchiveLoader& mgr) { + warcLoadInfo.itemId = SoundArchive::InvalidId; + warcLoadInfo.address = nullptr; + isLoadIndividual = false; + + BankFileReader reader{bankFile}; + const WaveIdTable* table{reader.GetWaveIdTable()}; + if (table == nullptr) + return WaveArchiveLoadStatus_Error; + + if (table->GetCount() == 0) + return WaveArchiveLoadStatus_Noneed; + + const WaveId* pWaveId{table->GetWaveId(0)}; + + const void* waveArchiveFile{mgr.detail_GetFileAddressByItemId(pWaveId->waveArchiveId)}; + if (waveArchiveFile == nullptr) + return WaveArchiveLoadStatus_NotYet; + + SoundArchive::WaveArchiveInfo warcInfo; + [[maybe_unused]] bool isReadWarcInfo{arc.ReadWaveArchiveInfo(pWaveId->waveArchiveId, &warcInfo)}; + + warcLoadInfo.itemId = pWaveId->waveArchiveId; + warcLoadInfo.address = waveArchiveFile; + + isLoadIndividual = warcInfo.isLoadIndividual; + + if (isLoadIndividual) { + WaveArchiveFileReader reader{waveArchiveFile, isLoadIndividual}; + for (u32 i{0}; i < table->GetCount(); ++i) { + if (!reader.IsLoaded(pWaveId->waveIndex)) + return WaveArchiveLoadStatus_Partly; + } + } + + return WaveArchiveLoadStatus_Ok; +} + +const void* Util::GetWaveFileOfWaveSound(const void* wsdFile, u32 index, const SoundArchive& arc, + const SoundArchiveLoader& mgr) { + WaveSoundFileReader reader{wsdFile}; + WaveSoundNoteInfo noteInfo{}; + + [[maybe_unused]] bool isReadNoteInfo{reader.ReadNoteInfo(¬eInfo, index, 0)}; + // if (!isReadNoteInfo) + // return nullptr; + + const void* waveArchiveFile{mgr.detail_GetFileAddressByItemId(noteInfo.waveArchiveId)}; + if (waveArchiveFile == nullptr) + return nullptr; + + SoundArchive::WaveArchiveInfo warcInfo; + [[maybe_unused]] bool isReadWarcInfo{arc.ReadWaveArchiveInfo(noteInfo.waveArchiveId, &warcInfo)}; + // if (!isReadWarcInfo) + // return nullptr; + + WaveArchiveFileReader warcReader{waveArchiveFile, warcInfo.isLoadIndividual}; + const void* waveFile{warcReader.GetWaveFile(noteInfo.waveIndex)}; + if (waveFile == nullptr) + return nullptr; + + return waveFile; +} + +} // namespace nn::atk::detail \ No newline at end of file diff --git a/src/NintendoWare/atk/detail/util/atk_WavOutFileStream.cpp b/src/NintendoWare/atk/detail/util/atk_WavOutFileStream.cpp new file mode 100644 index 00000000..6c51c67d --- /dev/null +++ b/src/NintendoWare/atk/detail/util/atk_WavOutFileStream.cpp @@ -0,0 +1,52 @@ +#include + +#include + +#include +#include + +namespace nn::atk::detail { + +WavOutFileStream::WavOutFileStream() = default; + +WavOutFileStream::~WavOutFileStream() = default; + +// NON_MATCHING: requires WavOutFileStream::WriteHeader +bool WavOutFileStream::Open(fnd::FileStream& stream, int channels, size_t samplesPerSec) { + m_pFileStream = &stream; + + if (WriteHeader(channels, samplesPerSec)) { + m_WaveDataSize = 0; + m_IsWaveDataSizeCalculating = true; + return true; + } + + return false; +} + +void WavOutFileStream::SetCacheBuffer(char* buf, size_t length) { + m_Buffer = buf; + m_BufferLength = length; +} + +// NON_MATCHING: reordering of instructions +bool WavOutFileStream::WriteHeader(int channels, size_t samplesPerSec) { + char buffer[sizeof(WaveBinaryHeader) + FileIoBufferAlignment]; + void* alignedBuffer {util::BytePtr(buffer).AlignUp(FileIoBufferAlignment).Get()}; + + WaveBinaryHeader* header {new (alignedBuffer) WaveBinaryHeader}; + + header->riffChunk.formatType = 0x45564157; // "WAVE" + header->riffChunk.header.size = sizeof(WaveBinaryHeader) - sizeof(ChunkHeader); + header->fmtChunk.bitsPerSample = 16; + header->fmtChunk.blockAlign = channels * 2; + header->fmtChunk.samplesPerSec = samplesPerSec; + header->fmtChunk.avgBytesPerSec = header->fmtChunk.blockAlign * header->fmtChunk.samplesPerSec; + header->fmtChunk.channels = channels; + header->fmtChunk.formatTag = FmtChunk::FormatPcm; + + size_t result {Write(header, sizeof(WaveBinaryHeader))}; + return result == sizeof(WaveBinaryHeader); +} + +} // namespace nn::atk::detail \ No newline at end of file diff --git a/src/NintendoWare/atk/detail/voice/atk_Channel.cpp b/src/NintendoWare/atk/detail/voice/atk_Channel.cpp new file mode 100644 index 00000000..e69de29b diff --git a/src/NintendoWare/atk/detail/voice/atk_ChannelManager.cpp b/src/NintendoWare/atk/detail/voice/atk_ChannelManager.cpp new file mode 100644 index 00000000..e69de29b diff --git a/src/NintendoWare/atk/detail/voice/atk_LowLevelVoice.cpp b/src/NintendoWare/atk/detail/voice/atk_LowLevelVoice.cpp new file mode 100644 index 00000000..33e2d294 --- /dev/null +++ b/src/NintendoWare/atk/detail/voice/atk_LowLevelVoice.cpp @@ -0,0 +1,18 @@ +#include + +namespace nn::atk::detail { + +LowLevelVoice::LowLevelVoice() = default; + +void LowLevelVoice::AppendWaveBuffer(WaveBuffer* waveBuffer) { + waveBuffer->next = nullptr; + waveBuffer->status = WaveBuffer::Status_Wait; + if (m_WaveBufferListEnd == nullptr) + m_WaveBufferListBegin = waveBuffer; + else + m_WaveBufferListEnd->next = waveBuffer; + + m_WaveBufferListEnd = waveBuffer; +} + +} // namespace nn::atk::detail \ No newline at end of file diff --git a/src/NintendoWare/atk/detail/voice/atk_MultiVoice.cpp b/src/NintendoWare/atk/detail/voice/atk_MultiVoice.cpp new file mode 100644 index 00000000..e69de29b diff --git a/src/NintendoWare/atk/detail/voice/atk_MultiVoiceManager.cpp b/src/NintendoWare/atk/detail/voice/atk_MultiVoiceManager.cpp new file mode 100644 index 00000000..e69de29b diff --git a/src/NintendoWare/atk/detail/voice/atk_Voice.cpp b/src/NintendoWare/atk/detail/voice/atk_Voice.cpp new file mode 100644 index 00000000..e69de29b diff --git a/src/NintendoWare/atk/detail/voice/atk_VoiceCommand.cpp b/src/NintendoWare/atk/detail/voice/atk_VoiceCommand.cpp new file mode 100644 index 00000000..e69de29b diff --git a/src/NintendoWare/atk/detail/wsd/atk_WaveSound.cpp b/src/NintendoWare/atk/detail/wsd/atk_WaveSound.cpp new file mode 100644 index 00000000..e69de29b diff --git a/src/NintendoWare/atk/detail/wsd/atk_WaveSoundFile.cpp b/src/NintendoWare/atk/detail/wsd/atk_WaveSoundFile.cpp new file mode 100644 index 00000000..90ad80f2 --- /dev/null +++ b/src/NintendoWare/atk/detail/wsd/atk_WaveSoundFile.cpp @@ -0,0 +1,273 @@ +#include + +#include + +namespace nn::atk::detail { +namespace { +const u8 WsdDefaultPan {64}; +const s8 WsdDefaultSurroundPan {0}; +const float WsdDefaultPitch {1.0}; +const u8 WsdDefaultMainSend {127}; +const u8 WsdDefaultFxSend {0}; +const AdshrCurve WsdDefaultAdshrCurve { + 127, + 127, + 127, + 127, + 127 +}; +const u8 WsdDefaultLpfFreq {64}; +const u8 WsdDefaultBiquadType {0}; +const u8 WsdDefaultBiquadValue {0}; +const u8 WsdDefaultKey {64}; +const u8 WsdDefaultVolume {96}; + +enum WaveSoundInfoBitFlagWsd { + WaveSoundInfoBitFlagWsd_Pan = 0, + WaveSoundInfoBitFlagWsd_Pitch = 1, + WaveSoundInfoBitFlagWsd_Filter = 2, + WaveSoundInfoBitFlagWsd_Send = 8, + WaveSoundInfoBitFlagWsd_Envelope = 9, + WaveSoundInfoBitFlagWsd_Randomizer = 10, +}; + +enum NoteInfoBitFlag { + NoteInfoBitFlag_Key = 0, + NoteInfoBitFlag_Volume = 1, + NoteInfoBitFlag_Pan = 2, + NoteInfoBitFlag_Pitch = 3, + NoteInfoBitFlag_Send = 8, + NoteInfoBitFlag_Envelope = 9, + NoteInfoBitFlag_Randomizer = 10, + NoteInfoBitFlag_Lfo = 11, +}; + +struct SendValueWsd { + u8 mainSend; + Util::Table fxSend; +}; +} // anonymous namespace + +const WaveSoundFile::InfoBlock* WaveSoundFile::FileHeader::GetInfoBlock() const { + return util::ConstBytePtr(GetBlock(ElementType_WaveSoundFile_InfoBlock)) + .Get(); +} + +const WaveSoundFile::WaveSoundData& WaveSoundFile::InfoBlockBody::GetWaveSoundData(u32 index) const { + return *util::ConstBytePtr( + GetWaveSoundDataReferenceTable() + .GetReferedItem(index, ElementType_WaveSoundFile_WaveSoundMetaData) + ).Get(); +} + +const Util::ReferenceTable& WaveSoundFile::InfoBlockBody::GetWaveSoundDataReferenceTable() const { + return *util::ConstBytePtr(this, toWaveSoundDataReferenceTable.offset) + .Get(); +} + +const Util::WaveIdTable& WaveSoundFile::InfoBlockBody::GetWaveIdTable() const { + return *util::ConstBytePtr(this, toWaveIdTable.offset).Get(); +} + +const WaveSoundFile::WaveSoundInfo& WaveSoundFile::WaveSoundData::GetWaveSoundInfo() const { + return *util::ConstBytePtr(this, toWaveSoundInfo.offset).Get(); +} + +const Util::ReferenceTable& WaveSoundFile::WaveSoundData::GetTrackInfoReferenceTable() const { + return *util::ConstBytePtr(this, toTrackInfoReferenceTable.offset) + .Get(); +} + +const Util::ReferenceTable& WaveSoundFile::WaveSoundData::GetNoteInfoReferenceTable() const { + return *util::ConstBytePtr(this, toNoteInfoReferenceTable.offset) + .Get(); +} + +const WaveSoundFile::TrackInfo& WaveSoundFile::WaveSoundData::GetTrackInfo(u32 index) const { + const void* pTrackInfo; + pTrackInfo = GetTrackInfoReferenceTable().GetReferedItem(index, ElementType_WaveSoundFile_TrackInfo); + + return *util::ConstBytePtr(pTrackInfo).Get(); +} + +const WaveSoundFile::NoteInfo& WaveSoundFile::WaveSoundData::GetNoteInfo(u32 index) const { + const void* pNoteInfo; + pNoteInfo = GetNoteInfoReferenceTable().GetReferedItem(index, ElementType_WaveSoundFile_NoteInfo); + + return *util::ConstBytePtr(pNoteInfo).Get(); +} + +u8 WaveSoundFile::WaveSoundInfo::GetPan() const { + u32 value; + bool result {optionParameter.GetValue(&value, WaveSoundInfoBitFlagWsd_Pan)}; + if (result) + return Util::DivideBy8bit(value, 0); + + return WsdDefaultPan; +} + +s8 WaveSoundFile::WaveSoundInfo::GetSurroundPan() const { + u32 value; + bool result {optionParameter.GetValue(&value, WaveSoundInfoBitFlagWsd_Pan)}; + if (result) + return static_cast(Util::DivideBy8bit(value, 1)); + + return WsdDefaultSurroundPan; +} + +float WaveSoundFile::WaveSoundInfo::GetPitch() const { + float value; + bool result {optionParameter.GetValueF32(&value, WaveSoundInfoBitFlagWsd_Pitch)}; + if (result) + return value; + + return WsdDefaultPitch; +} + +void WaveSoundFile::WaveSoundInfo::GetSendValue(u8* mainSend, u8* fxSend, u8 fxSendCount) const { + u32 value; + bool result {optionParameter.GetValue(&value, WaveSoundInfoBitFlagWsd_Send)}; + + if (result) { + const SendValueWsd& sendValue = *util::ConstBytePtr(this, value).Get(); + + *mainSend = sendValue.mainSend; + int countSize {sendValue.fxSend.count > AuxBus_Count ? AuxBus_Count : sendValue.fxSend.count}; + + for (int i {0}; i < countSize; ++i) + fxSend[i] = sendValue.fxSend.item[i]; + } + else { + *mainSend = WsdDefaultMainSend; + for (int i {0}; i < fxSendCount; ++i) + fxSend[i] = WsdDefaultFxSend; + } +} + +const AdshrCurve& WaveSoundFile::WaveSoundInfo::GetAdshrCurve() const { + u32 offsetToReference; + bool result {optionParameter.GetValue(&offsetToReference, WaveSoundInfoBitFlagWsd_Envelope)}; + if (result) { + const auto& ref {*util::ConstBytePtr(this, offsetToReference).Get()}; + return *util::ConstBytePtr(&ref, ref.offset).Get(); + } + + return WsdDefaultAdshrCurve; +} + +u8 WaveSoundFile::WaveSoundInfo::GetLpfFreq() const { + u32 value; + bool result {optionParameter.GetValue(&value, WaveSoundInfoBitFlagWsd_Filter)}; + if (result) + return Util::DivideBy8bit(value, 0); + + return WsdDefaultLpfFreq; +} + +u8 WaveSoundFile::WaveSoundInfo::GetBiquadType() const { + u32 value; + bool result {optionParameter.GetValue(&value, WaveSoundInfoBitFlagWsd_Filter)}; + if (result) + return Util::DivideBy8bit(value, 1); + + return WsdDefaultBiquadType; +} + +u8 WaveSoundFile::WaveSoundInfo::GetBiquadValue() const { + u32 value; + bool result {optionParameter.GetValue(&value, WaveSoundInfoBitFlagWsd_Filter)}; + if (result) + return Util::DivideBy8bit(value, 2); + + return WsdDefaultBiquadValue; +} + +const Util::ReferenceTable& WaveSoundFile::TrackInfo::GetNoteEventReferenceTable() const { + return *util::ConstBytePtr(this, toNoteEventReferenceTable.offset) + .Get(); +} + +const WaveSoundFile::NoteEvent& WaveSoundFile::TrackInfo::GetNoteEvent(u32 index) const { + const void* pNoteEvent; + pNoteEvent = GetNoteEventReferenceTable().GetReferedItem(index, ElementType_WaveSoundFile_NoteEvent); + + return *util::ConstBytePtr(pNoteEvent).Get(); +} + +u8 WaveSoundFile::NoteInfo::GetOriginalKey() const { + u32 value; + bool result {optionParameter.GetValue(&value, NoteInfoBitFlag_Key)}; + if (result) + return Util::DivideBy8bit(value, 0); + + return WsdDefaultKey; +} + +u8 WaveSoundFile::NoteInfo::GetVolume() const { + u32 value; + bool result {optionParameter.GetValue(&value, NoteInfoBitFlag_Volume)}; + if (result) + return Util::DivideBy8bit(value, 0); + + return WsdDefaultVolume; +} + +u8 WaveSoundFile::NoteInfo::GetPan() const { + u32 value; + bool result {optionParameter.GetValue(&value, NoteInfoBitFlag_Pan)}; + if (result) + return Util::DivideBy8bit(value, 0); + + return WsdDefaultPan; +} + +u8 WaveSoundFile::NoteInfo::GetSurroundPan() const { + u32 value; + bool result {optionParameter.GetValue(&value, NoteInfoBitFlag_Pan)}; + if (result) + return Util::DivideBy8bit(value, 1); + + return WsdDefaultSurroundPan; +} + +float WaveSoundFile::NoteInfo::GetPitch() const { + float value; + bool result {optionParameter.GetValueF32(&value, NoteInfoBitFlag_Pitch)}; + if (result) + return value; + + return WsdDefaultPitch; +} + +// NON_MATCHING +void WaveSoundFile::NoteInfo::GetSendValue(u8* mainSend, u8** fxSend, u8 fxSendCount) const { + u32 value; + bool result {optionParameter.GetValue(&value, NoteInfoBitFlag_Send)}; + + if (!result) { + *mainSend = WsdDefaultMainSend; + for (int i {0}; i < fxSendCount; ++i) + fxSend[i][0] = WsdDefaultFxSend; + } + else { + const SendValueWsd& sendValue = *util::ConstBytePtr(this, value).Get(); + + *mainSend = sendValue.mainSend; + int countSize {sendValue.fxSend.count > AuxBus_Count ? AuxBus_Count : sendValue.fxSend.count}; + + for (int i {0}; i < countSize; ++i) + fxSend[i][0] = sendValue.fxSend.item[i]; + } +} + +const AdshrCurve& WaveSoundFile::NoteInfo::GetAdshrCurve() const { + u32 offsetToReference; + bool result {optionParameter.GetValue(&offsetToReference, NoteInfoBitFlag_Envelope)}; + if (result) { + const auto& ref {*util::ConstBytePtr(this, offsetToReference).Get()}; + return *util::ConstBytePtr(&ref, ref.offset).Get(); + } + + return WsdDefaultAdshrCurve; +} +} // namespace nn::atk::detail \ No newline at end of file diff --git a/src/NintendoWare/atk/detail/wsd/atk_WaveSoundFileReader.cpp b/src/NintendoWare/atk/detail/wsd/atk_WaveSoundFileReader.cpp new file mode 100644 index 00000000..e69de29b diff --git a/src/NintendoWare/atk/detail/wsd/atk_WaveSoundLoader.cpp b/src/NintendoWare/atk/detail/wsd/atk_WaveSoundLoader.cpp new file mode 100644 index 00000000..e69de29b diff --git a/src/NintendoWare/atk/detail/wsd/atk_WaveSoundPlayer.cpp b/src/NintendoWare/atk/detail/wsd/atk_WaveSoundPlayer.cpp new file mode 100644 index 00000000..e69de29b diff --git a/src/NintendoWare/atk/detail/wsd/atk_WaveSoundRuntime.cpp b/src/NintendoWare/atk/detail/wsd/atk_WaveSoundRuntime.cpp new file mode 100644 index 00000000..e69de29b diff --git a/src/NintendoWare/atk/effect/atk_EffectAux.cpp b/src/NintendoWare/atk/effect/atk_EffectAux.cpp new file mode 100644 index 00000000..e69de29b diff --git a/src/NintendoWare/atk/effect/atk_EffectBase.cpp b/src/NintendoWare/atk/effect/atk_EffectBase.cpp new file mode 100644 index 00000000..e69de29b diff --git a/src/NintendoWare/atk/fnd/basis/atkfnd_FrameHeapImpl.cpp b/src/NintendoWare/atk/fnd/basis/atkfnd_FrameHeapImpl.cpp new file mode 100644 index 00000000..29f297b7 --- /dev/null +++ b/src/NintendoWare/atk/fnd/basis/atkfnd_FrameHeapImpl.cpp @@ -0,0 +1,5 @@ +#include + +namespace nn::atk::detail::fnd { + +} // namespace nn::atk::detail::fnd \ No newline at end of file diff --git a/src/NintendoWare/atk/fnd/basis/atkfnd_HeapBase.cpp b/src/NintendoWare/atk/fnd/basis/atkfnd_HeapBase.cpp new file mode 100644 index 00000000..ebdf4be4 --- /dev/null +++ b/src/NintendoWare/atk/fnd/basis/atkfnd_HeapBase.cpp @@ -0,0 +1,150 @@ +#include + +#include +#include + +namespace nn::atk::detail::fnd { +namespace { +HeapBase::HeapList sRootList; +u32 sFillVals[HeapBase::FillType_Max] = { 0xC3C3C3C3, 0xF3F3F3F3, 0xD3D3D3D3 }; +} // anonymous namespace + +HeapBase::HeapList* HeapBase::FindListContainHeap(HeapBase* pHeapBase) { + HeapList* pList {&sRootList}; + HeapBase* pContainHeapBase {FindContainHeap(pList, pHeapBase)}; + + if (pContainHeapBase != nullptr) + pList = &pContainHeapBase->m_ChildList; + + return pList; +} + +// NON_MATCHING +// Commented out because it needs to not be inlined for other functions to match +// HeapBase* HeapBase::FindContainHeap(HeapList* pList, const void* memBlock) { +// uintptr_t memBlockAddress {reinterpret_cast(memBlock)}; + +// for (auto itr {pList->begin()}; pList->end() != itr; ++itr) { +// auto curItr {++itr}; + +// if (&pList->front() == &*curItr) +// return nullptr; + +// if (reinterpret_cast(curItr->mHeapStart) <= memBlockAddress && memBlockAddress < reinterpret_cast(curItr->mHeapEnd)) { +// HeapBase* pChildHeapBase {FindContainHeap(&curItr->m_ChildList, &*curItr)}; + +// if (pChildHeapBase != nullptr) +// return pChildHeapBase; + +// } +// } + +// return nullptr; +// } + +HeapBase* HeapBase::FindContainHeap(const void* memBlock) { + return FindContainHeap(&sRootList, memBlock); +} + +HeapBase* HeapBase::FindParentHeap(const HeapBase* pChild) { + u64 heapAddress {reinterpret_cast(pChild)}; + + for (auto itr {sRootList.begin()}; itr != sRootList.end();) { + auto curItr {itr++}; + + if (reinterpret_cast(curItr->mHeapStart) <= heapAddress && reinterpret_cast(curItr->mHeapEnd) > heapAddress) + return FindContainHeap(&curItr->m_ChildList, &*curItr); + } + + return nullptr; +} + +u32 HeapBase::SetFillValue(FillType type, u32 val) { + u32 oldVal {sFillVals[type]}; + sFillVals[type] = val; + + return oldVal; +} + +u32 HeapBase::GetFillValue(FillType type) { + return sFillVals[type]; +} + +HeapBase::HeapType HeapBase::GetHeapType() { + switch (m_Signature) { + case ExpHeapSignature: + return HeapType::HeapType_Exp; + + case FrameHeapSignature: + return HeapType::HeapType_Frame; + + case UnitHeapSignature: + return HeapType::HeapType_Unit; + + default: + return HeapType::HeapType_Unknown; + } +} + +void HeapBase::Initialize(u32 signature, void* heapStart, void* heapEnd, u16 optFlag) { + m_Signature = signature; + mHeapStart = heapStart; + mHeapEnd = heapEnd; + + SetOptionFlag(optFlag); + u32 diff = GetOffsetFromPtr(heapStart, heapEnd); + FillNoUseMemory(heapStart, diff); + + HeapList* pList {FindListContainHeap(this)}; + + pList->push_back(*this); +} + +// NON_MATCHING +void HeapBase::Finalize() { + HeapList* pList {FindListContainHeap(this)}; + + if (pList != nullptr) + pList->erase(pList->begin()); + + m_Signature = 0; +} + +void HeapBase::LockHeap() {} + +void HeapBase::UnlockHeap() {} + +void HeapBase::FillFreeMemory(void* address, size_t size) { + if ((m_Attribute & OptionDebugFill) != 0) + memset(address, static_cast(GetFillValue(FillType_Free)), size); +} + +void HeapBase::FillNoUseMemory(void* address, size_t size) { + if ((m_Attribute & OptionDebugFill) != 0) + memset(address, static_cast(GetFillValue(FillType_NoUse)), size); +} + +void HeapBase::FillAllocMemory(void* address, size_t size) { + if (m_Attribute & OptionZeroClear) { + memset(address, 0, size); + return; + } + + if ((m_Attribute & OptionDebugFill) != 0) + memset(address, static_cast(GetFillValue(FillType_Alloc)), size); +} + + +u16 HeapBase::GetOptionFlag() { + return m_Attribute & 0xFF; +} + +// NON_MATCHING: matches inlined, but not standalone +void HeapBase::SetOptionFlag(u16 optFlag) { + if (optFlag >= 0) { + u32 maskBits {0xff}; + u32 newVal = optFlag & maskBits; + m_Attribute = newVal; + } +} +} // namespace nn::atk::detail::fnd \ No newline at end of file diff --git a/src/NintendoWare/atk/fnd/basis/atkfnd_Time.win32.cpp b/src/NintendoWare/atk/fnd/basis/atkfnd_Time.win32.cpp new file mode 100644 index 00000000..e69de29b diff --git a/src/NintendoWare/atk/fnd/basis/atkfnd_WorkBufferAllocator.cpp b/src/NintendoWare/atk/fnd/basis/atkfnd_WorkBufferAllocator.cpp new file mode 100644 index 00000000..e69de29b diff --git a/src/NintendoWare/atk/fnd/io/atkfnd_FileStreamImpl.cpp b/src/NintendoWare/atk/fnd/io/atkfnd_FileStreamImpl.cpp new file mode 100644 index 00000000..f4c2570a --- /dev/null +++ b/src/NintendoWare/atk/fnd/io/atkfnd_FileStreamImpl.cpp @@ -0,0 +1,61 @@ +#include + +#include + +#include + +namespace nn::atk::detail::fnd { +size_t FileStreamImpl::Read(void* buf, size_t length, FndResult* result) { + if (IsCacheEnabled()) + return m_StreamCache.Read(buf, length, result, m_pAccessLog, this); + + return ReadDirect(buf, length, result); +} + +size_t FileStreamImpl::Write(const void* buf, size_t length, FndResult* result) { + if (IsCacheEnabled()) + return m_StreamCache.Write(buf, length, result); + + return WriteDirect(buf, length, result); +} + +FndResult FileStreamImpl::Seek(position_t offset, SeekOrigin origin) { + if (IsCacheEnabled()) + return m_StreamCache.Seek(offset, origin); + + return SeekDirect(offset, origin); +} + +bool FileStreamImpl::CanRead() const { + return IsOpened(); +} + +bool FileStreamImpl::CanWrite() const { + return IsOpened(); +} + +bool FileStreamImpl::CanSeek() const { + return IsOpened(); +} + +void FileStreamImpl::EnableCache(void* buffer, size_t length) { + if (m_StreamCache.IsInitialized()) + m_StreamCache.Finalize(); + + void* alignedBuffer {util::BytePtr(buffer).AlignUp(GetIoBufferAlignment()).Get()}; + + m_StreamCache.Initialize( + &m_DirectStream, + alignedBuffer, + length + GetOffsetFromPtr(alignedBuffer, buffer) + ); +} + +void FileStreamImpl::DisableCache() { + m_StreamCache.Finalize(); +} + +void FileStreamImpl::ValidateAlignment([[maybe_unused]] const void* buf) const { + GetIoBufferAlignment(); +} +} // namespace nn::atk::detail::fnd \ No newline at end of file diff --git a/src/NintendoWare/atk/fnd/io/atkfnd_FileStreamImpl.win32.cpp b/src/NintendoWare/atk/fnd/io/atkfnd_FileStreamImpl.win32.cpp new file mode 100644 index 00000000..33241eb4 --- /dev/null +++ b/src/NintendoWare/atk/fnd/io/atkfnd_FileStreamImpl.win32.cpp @@ -0,0 +1,129 @@ +#include + +#include + +#include + +#include + +namespace nn::atk::detail::fnd { + +namespace { +fs::OpenMode ConvertAccessMode(FileStream::AccessMode accessMode) {} + +position_t GetSeekPosition(FileStreamImpl& target, position_t offset, Stream::SeekOrigin origin) { + position_t targetSizeEnd = target.GetSize(); + position_t result{0}; + + switch (origin) { + case Stream::SeekOrigin_Begin: + result = std::max(offset, 0L); + break; + case Stream::SeekOrigin_End: + result = targetSizeEnd - std::min(offset, 0L); + break; + case Stream::SeekOrigin_Current: + if (offset > 0) + result = Clamp(target.GetCurrentPosition() + offset, LONG_MIN, targetSizeEnd); + + else if (offset >= 0) + result = target.GetCurrentPosition(); + + else if (target.GetCurrentPosition() > -offset) + result = target.GetCurrentPosition() + offset; + + break; + default: + result = 0; + break; + } + + return result; +} +} // anonymous namespace + +FileStreamImpl::FileStreamImpl() { + m_DirectStream.m_Owner = this; +}; + +void FileStreamImpl::Close() { + if (IsOpened()) { + m_CurrentPosition = 0; + m_IsOpened = false; + fs::CloseFile(m_FileHandle); + } +} + +void FileStreamImpl::Flush() { + if (IsOpened()) + fs::FlushFile(m_FileHandle); +} + +bool FileStreamImpl::IsOpened() const { + return m_IsOpened; +} + +// NON_MATCHING: needs symbol for nn::diag::detail::AbortImpl +size_t FileStreamImpl::GetSize() const { + s64 fileSize; + Result result{fs::GetFileSize(&fileSize, m_FileHandle)}; + + if (result.IsSuccess()) + return fileSize & 0xffffffff; + + // diag::detail::AbortImpl("", "", "", 0); +} + +int FileStreamImpl::GetIoBufferAlignment() const { + return 1; +} + +size_t FileStreamImpl::ReadDirect(void* buf, size_t length, FndResult* result) { + ValidateAlignment(buf); + + size_t readFileLength{0}; + Result nnResult{fs::ReadFile(&readFileLength, m_FileHandle, m_CurrentPosition, buf, length)}; + + FndResult readResult; + + if (nnResult.IsSuccess()) { + readResult = FndResult{readFileLength != length}; + m_CurrentPosition += readFileLength; + } else { + readResult = FndResult{FndResultType_IoError}; + } + + if (result != nullptr) + *result = readResult; + + return readFileLength; +} + +size_t FileStreamImpl::WriteDirect(const void* buf, size_t length, FndResult* result) { + ValidateAlignment(buf); + + FndResult writeResult; + + fs::WriteOption option; + Result nnResult{fs::WriteFile(m_FileHandle, m_CurrentPosition, buf, length, option)}; + + if (nnResult.IsSuccess()) { + writeResult = FndResult{FndResultType_True}; + m_CurrentPosition += length; + } else { + writeResult = FndResult{FndResultType_IoError}; + } + + if (result != nullptr) + *result = writeResult; + + return length; +} + +FndResult FileStreamImpl::SeekDirect(position_t offset, SeekOrigin origin) { + position_t seekPosition{GetSeekPosition(*this, offset, origin)}; + m_CurrentPosition = seekPosition; + return FndResult{FndResultType_True}; +} + +} // namespace nn::atk::detail::fnd \ No newline at end of file diff --git a/src/NintendoWare/atk/fnd/io/atkfnd_FileStreamProxy.cpp b/src/NintendoWare/atk/fnd/io/atkfnd_FileStreamProxy.cpp new file mode 100644 index 00000000..b24c8376 --- /dev/null +++ b/src/NintendoWare/atk/fnd/io/atkfnd_FileStreamProxy.cpp @@ -0,0 +1,115 @@ +#include + +#include +#include "nn/atk/fnd/basis/atkfnd_Config.h" + +namespace nn::atk::detail::fnd { +FileStreamProxy::FileStreamProxy(FileStream* fileStream, position_t offset, size_t fileSize) + : m_pFileStream{fileStream}, m_Offset{offset}, m_FileSize{fileSize} {} + +FileStreamProxy::~FileStreamProxy() { + m_Offset = 0; + m_FileSize = 0; + m_pFileStream = nullptr; +} + +FndResult FileStreamProxy::Open(const char* filePath, AccessMode openMode) { + return m_pFileStream->Open(filePath, openMode); +} + +void FileStreamProxy::Close() { + m_pFileStream->Close(); +} + +void FileStreamProxy::Flush() { + m_pFileStream->Flush(); +} + +bool FileStreamProxy::IsOpened() const { + return m_pFileStream->IsOpened(); +} + +bool FileStreamProxy::CanRead() const { + return m_pFileStream->CanRead(); +} + +bool FileStreamProxy::CanWrite() const { + return m_pFileStream->CanWrite(); +} + +bool FileStreamProxy::CanSeek() const { + return m_pFileStream->CanSeek(); +} + +size_t FileStreamProxy::GetSize() const { + return m_pFileStream->GetSize(); +} + +size_t FileStreamProxy::Read(void* buffer, size_t length, FndResult* result) { + return m_pFileStream->Read(buffer, length, result); +} + +size_t FileStreamProxy::Write(const void* buffer, size_t length, FndResult* result) { + return m_pFileStream->Write(buffer, length, result); +} + +// NON_MATCHING: wrong csel cond and wrong use of registers +FndResult FileStreamProxy::Seek(position_t offset, fnd::Stream::SeekOrigin origin) { + position_t fileSizeEndPosition = m_FileSize; + + switch (origin) { + case SeekOrigin_Begin: + offset = m_Offset + offset; + break; + case SeekOrigin_End: + offset = m_Offset + (fileSizeEndPosition - offset); + break; + case SeekOrigin_Current: + offset = m_pFileStream->GetCurrentPosition() + offset; + break; + default: + return FndResult{FndResultType_Failed}; + } + + offset = std::min(m_Offset + fileSizeEndPosition, offset); + offset = std::min(m_Offset, offset); + + return m_pFileStream->Seek(offset, origin); +} + +position_t FileStreamProxy::GetCurrentPosition() const { + return m_pFileStream->GetCurrentPosition(); +} + +void FileStreamProxy::EnableCache(void* buffer, size_t length) { + m_pFileStream->EnableCache(buffer, length); +} + +void FileStreamProxy::DisableCache() { + m_pFileStream->DisableCache(); +} + +bool FileStreamProxy::IsCacheEnabled() const { + return m_pFileStream->IsCacheEnabled(); +} + +int FileStreamProxy::GetIoBufferAlignment() const { + return m_pFileStream->GetIoBufferAlignment(); +} + +bool FileStreamProxy::CanSetFsAccessLog() const { + return m_pFileStream->CanSetFsAccessLog(); +} + +void* FileStreamProxy::SetFsAccessLog(FsAccessLog* pFsAccessLog) { + return m_pFileStream->SetFsAccessLog(pFsAccessLog); +} + +position_t FileStreamProxy::GetCachePosition() { + return m_pFileStream->GetCachePosition(); +} + +size_t FileStreamProxy::GetCachedLength() { + return m_pFileStream->GetCachedLength(); +} +} // namespace nn::atk::detail::fnd \ No newline at end of file diff --git a/src/NintendoWare/atk/fnd/io/atkfnd_StreamCache.cpp b/src/NintendoWare/atk/fnd/io/atkfnd_StreamCache.cpp new file mode 100644 index 00000000..ad3da737 --- /dev/null +++ b/src/NintendoWare/atk/fnd/io/atkfnd_StreamCache.cpp @@ -0,0 +1,5 @@ +#include + +namespace nn::atk::detail::fnd { + +} // namespace nn::atk::detail::fnd \ No newline at end of file diff --git a/src/NintendoWare/atk/fnd/os/atkfnd_Thread.cpp b/src/NintendoWare/atk/fnd/os/atkfnd_Thread.cpp new file mode 100644 index 00000000..e69de29b diff --git a/src/NintendoWare/atk/fnd/os/atkfnd_Thread.win32.cpp b/src/NintendoWare/atk/fnd/os/atkfnd_Thread.win32.cpp new file mode 100644 index 00000000..e69de29b diff --git a/src/NintendoWare/atk/submix/atk_ChannelMixVolume.cpp b/src/NintendoWare/atk/submix/atk_ChannelMixVolume.cpp new file mode 100644 index 00000000..d6785e88 --- /dev/null +++ b/src/NintendoWare/atk/submix/atk_ChannelMixVolume.cpp @@ -0,0 +1 @@ +#include \ No newline at end of file diff --git a/src/NintendoWare/atk/submix/atk_FinalMix.cpp b/src/NintendoWare/atk/submix/atk_FinalMix.cpp new file mode 100644 index 00000000..e69de29b diff --git a/src/NintendoWare/atk/submix/atk_OutputMixer.cpp b/src/NintendoWare/atk/submix/atk_OutputMixer.cpp new file mode 100644 index 00000000..e69de29b diff --git a/src/NintendoWare/atk/submix/atk_SubMix.cpp b/src/NintendoWare/atk/submix/atk_SubMix.cpp new file mode 100644 index 00000000..e69de29b diff --git a/src/NintendoWare/atk/util/atk_AudioRendererPerformanceReader.cpp b/src/NintendoWare/atk/util/atk_AudioRendererPerformanceReader.cpp new file mode 100644 index 00000000..709ae439 --- /dev/null +++ b/src/NintendoWare/atk/util/atk_AudioRendererPerformanceReader.cpp @@ -0,0 +1,65 @@ +#include + +#include + +#include + +namespace nn::atk { +AudioRendererPerformanceReader::AudioRendererPerformanceReader() = default; + +size_t AudioRendererPerformanceReader::GetRequiredMemorySize(int performanceInfoCount) { + size_t size {performanceInfoCount * + (SoundSystem::GetPerformanceFrameBufferSize() + sizeof(PerformanceInfo))}; + return size; +} + +void AudioRendererPerformanceReader::Initialize(int performanceInfoCount, void* buffer, [[maybe_unused]] size_t bufferSize) { + m_PerformanceInfoCount = performanceInfoCount; + + auto ptr {util::BytePtr(buffer)}; + m_pPerformanceInfo = ptr.Get(); + + ptr += static_cast(performanceInfoCount * sizeof(PerformanceInfo)); + + const size_t performanceBufferSize {SoundSystem::GetPerformanceFrameBufferSize()}; + for (int i {0}; i < m_PerformanceInfoCount; ++i) { + m_pPerformanceInfo[i].performanceBuffer = ptr.Get(); + m_pPerformanceInfo[i].performanceBufferSize = performanceBufferSize; + ptr += static_cast(performanceBufferSize); + } + + m_WriteIndex = 0; + m_ReadIndex = m_PerformanceInfoCount - 1; + m_IsInitialized = true; +} + +const AudioRendererPerformanceReader::PerformanceInfo* AudioRendererPerformanceReader::ReadPerformanceInfo() { + int nextReadIndex {0}; + const int readIndex {m_ReadIndex}; + + if (readIndex + 1 < m_PerformanceInfoCount) + nextReadIndex = readIndex + 1; + + if (nextReadIndex != m_WriteIndex) { + m_ReadIndex = nextReadIndex; + return &m_pPerformanceInfo[nextReadIndex]; + } + + return nullptr; +} + +void AudioRendererPerformanceReader::Record(const void* performanceFrameBuffer, + size_t performanceFrameBufferSize, os::Tick tick) { + if (m_WriteIndex != m_ReadIndex) { + const int writeIndex {m_WriteIndex}; + m_pPerformanceInfo[writeIndex].tick = tick; + memcpy(m_pPerformanceInfo[writeIndex].performanceBuffer, performanceFrameBuffer, performanceFrameBufferSize); + + int nextWriteIndex {0}; + if (writeIndex + 1 < m_PerformanceInfoCount) + nextWriteIndex = writeIndex + 1; + + m_WriteIndex = nextWriteIndex; + } +} +} // namespace nn::atk \ No newline at end of file diff --git a/src/NintendoWare/atk/util/atk_DeviceOutRecorder.cpp b/src/NintendoWare/atk/util/atk_DeviceOutRecorder.cpp new file mode 100644 index 00000000..cc3866ed --- /dev/null +++ b/src/NintendoWare/atk/util/atk_DeviceOutRecorder.cpp @@ -0,0 +1,5 @@ +#include + +namespace nn::atk { + +} // namespace nn::atk \ No newline at end of file diff --git a/src/NintendoWare/atk/util/atk_ProfileReader.cpp b/src/NintendoWare/atk/util/atk_ProfileReader.cpp new file mode 100644 index 00000000..37e72087 --- /dev/null +++ b/src/NintendoWare/atk/util/atk_ProfileReader.cpp @@ -0,0 +1,5 @@ +#include + +namespace nn::atk { + +} // namespace nn::atk \ No newline at end of file diff --git a/src/NintendoWare/atk/util/atk_TaskProfileReader.cpp b/src/NintendoWare/atk/util/atk_TaskProfileReader.cpp new file mode 100644 index 00000000..e69de29b