FileIOHandler_wwise.cpp 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485
  1. /*
  2. * Copyright (c) Contributors to the Open 3D Engine Project.
  3. * For complete copyright and license terms please see the LICENSE at the root of this distribution.
  4. *
  5. * SPDX-License-Identifier: Apache-2.0 OR MIT
  6. *
  7. */
  8. #include <FileIOHandler_wwise.h>
  9. #include <AzCore/Casting/numeric_cast.h>
  10. #include <AzCore/IO/FileIO.h>
  11. #include <AzCore/IO/IStreamer.h>
  12. #include <AzCore/IO/Streamer/FileRequest.h>
  13. #include <AzCore/Debug/Profiler.h>
  14. #include <IAudioInterfacesCommonData.h>
  15. #include <AkPlatformFuncs_Platform.h>
  16. #include <AudioEngineWwise_Traits_Platform.h>
  17. #include <cinttypes>
  18. #define MAX_NUMBER_STRING_SIZE (10) // max digits in u32 base-10 number
  19. #define MAX_EXTENSION_SIZE (4) // .xxx
  20. #define MAX_FILETITLE_SIZE (MAX_NUMBER_STRING_SIZE + MAX_EXTENSION_SIZE + 1) // null-terminated
  21. namespace Audio
  22. {
  23. // AkFileHandle must be able to store our AZ::IO::HandleType
  24. static_assert(sizeof(AkFileHandle) >= sizeof(AZ::IO::HandleType), "AkFileHandle must be able to store at least the size of a AZ::IO::HandleType");
  25. namespace Platform
  26. {
  27. AkFileHandle GetAkFileHandle(AZ::IO::HandleType realFileHandle);
  28. AZ::IO::HandleType GetRealFileHandle(AkFileHandle akFileHandle);
  29. void SetThreadProperties(AkThreadProperties& threadProperties);
  30. }
  31. AkFileHandle GetAkFileHandle(AZ::IO::HandleType realFileHandle)
  32. {
  33. if (realFileHandle == AZ::IO::InvalidHandle)
  34. {
  35. return InvalidAkFileHandle;
  36. }
  37. return Platform::GetAkFileHandle(realFileHandle);
  38. }
  39. AZ::IO::HandleType GetRealFileHandle(AkFileHandle akFileHandle)
  40. {
  41. if (akFileHandle == InvalidAkFileHandle)
  42. {
  43. return AZ::IO::InvalidHandle;
  44. }
  45. return Platform::GetRealFileHandle(akFileHandle);
  46. }
  47. CBlockingDevice_wwise::~CBlockingDevice_wwise()
  48. {
  49. Destroy();
  50. }
  51. bool CBlockingDevice_wwise::Init(size_t poolSize)
  52. {
  53. Destroy();
  54. AkDeviceSettings deviceSettings;
  55. AK::StreamMgr::GetDefaultDeviceSettings(deviceSettings);
  56. deviceSettings.uIOMemorySize = aznumeric_cast<AkUInt32>(poolSize);
  57. deviceSettings.uSchedulerTypeFlags = AK_SCHEDULER_BLOCKING;
  58. Platform::SetThreadProperties(deviceSettings.threadProperties);
  59. m_deviceID = AK::StreamMgr::CreateDevice(deviceSettings, this);
  60. return m_deviceID != AK_INVALID_DEVICE_ID;
  61. }
  62. void CBlockingDevice_wwise::Destroy()
  63. {
  64. if (m_deviceID != AK_INVALID_DEVICE_ID)
  65. {
  66. AK::StreamMgr::DestroyDevice(m_deviceID);
  67. m_deviceID = AK_INVALID_DEVICE_ID;
  68. }
  69. }
  70. bool CBlockingDevice_wwise::Open(const char* filename, AkOpenMode openMode, AkFileDesc& fileDesc)
  71. {
  72. AZ::IO::OpenMode azOpenMode = AZ::IO::OpenMode::ModeBinary;
  73. switch (openMode)
  74. {
  75. case AK_OpenModeRead:
  76. azOpenMode |= AZ::IO::OpenMode::ModeRead;
  77. break;
  78. case AK_OpenModeWrite:
  79. azOpenMode |= AZ::IO::OpenMode::ModeWrite;
  80. break;
  81. case AK_OpenModeWriteOvrwr:
  82. azOpenMode |= (AZ::IO::OpenMode::ModeUpdate | AZ::IO::OpenMode::ModeWrite);
  83. break;
  84. case AK_OpenModeReadWrite:
  85. azOpenMode |= (AZ::IO::OpenMode::ModeRead | AZ::IO::OpenMode::ModeWrite);
  86. break;
  87. default:
  88. AZ_Assert(false, "Unknown Wwise file open mode.");
  89. return false;
  90. }
  91. auto fileIO = AZ::IO::FileIOBase::GetInstance();
  92. if (AZ::u64 fileSize = 0;
  93. fileIO->Size(filename, fileSize) && fileSize != 0)
  94. {
  95. AZ::IO::HandleType fileHandle = AZ::IO::InvalidHandle;
  96. fileIO->Open(filename, azOpenMode, fileHandle);
  97. if (fileHandle != AZ::IO::InvalidHandle)
  98. {
  99. fileDesc.hFile = GetAkFileHandle(fileHandle);
  100. fileDesc.iFileSize = aznumeric_cast<AkInt64>(fileSize);
  101. fileDesc.uSector = 0;
  102. fileDesc.deviceID = m_deviceID;
  103. fileDesc.pCustomParam = nullptr;
  104. fileDesc.uCustomParamSize = 0;
  105. return true;
  106. }
  107. }
  108. return false;
  109. }
  110. AKRESULT CBlockingDevice_wwise::Read(AkFileDesc& fileDesc, const AkIoHeuristics&, void* buffer, AkIOTransferInfo& transferInfo)
  111. {
  112. AZ_Assert(buffer, "Wwise didn't provide a valid desination buffer to Read into.");
  113. AZ::IO::HandleType fileHandle = GetRealFileHandle(fileDesc.hFile);
  114. auto fileIO = AZ::IO::FileIOBase::GetInstance();
  115. AZ::u64 currentFileReadPos = 0;
  116. fileIO->Tell(fileHandle, currentFileReadPos);
  117. if (currentFileReadPos != transferInfo.uFilePosition)
  118. {
  119. fileIO->Seek(fileHandle, aznumeric_cast<AZ::s64>(transferInfo.uFilePosition), AZ::IO::SeekType::SeekFromStart);
  120. }
  121. AZ::u64 bytesRead = 0;
  122. fileIO->Read(fileHandle, buffer, aznumeric_cast<AZ::u64>(transferInfo.uRequestedSize), false, &bytesRead);
  123. const bool readOk = (bytesRead == aznumeric_cast<AZ::u64>(transferInfo.uRequestedSize));
  124. AZ_Assert(readOk,
  125. "Number of bytes read (%llu) for read request doesn't match the requested size (%u).",
  126. bytesRead, transferInfo.uRequestedSize);
  127. return readOk ? AK_Success : AK_Fail;
  128. }
  129. AKRESULT CBlockingDevice_wwise::Write(AkFileDesc& fileDesc, const AkIoHeuristics&, void* data, AkIOTransferInfo& transferInfo)
  130. {
  131. AZ_Assert(data, "Wwise didn't provide a valid source buffer to Write from.");
  132. AZ::IO::HandleType fileHandle = GetRealFileHandle(fileDesc.hFile);
  133. auto fileIO = AZ::IO::FileIOBase::GetInstance();
  134. AZ::u64 currentFileWritePos = 0;
  135. fileIO->Tell(fileHandle, currentFileWritePos);
  136. if (currentFileWritePos != transferInfo.uFilePosition)
  137. {
  138. fileIO->Seek(fileHandle, aznumeric_cast<AZ::s64>(transferInfo.uFilePosition), AZ::IO::SeekType::SeekFromStart);
  139. }
  140. AZ::u64 bytesWritten = 0;
  141. fileIO->Write(fileHandle, data, aznumeric_cast<AZ::u64>(transferInfo.uRequestedSize), &bytesWritten);
  142. const bool writeOk = (bytesWritten == aznumeric_cast<AZ::u64>(transferInfo.uRequestedSize));
  143. AZ_Error("Wwise", writeOk,
  144. "Number of bytes written (%llu) for write request doesn't match the requested size (%u).",
  145. bytesWritten, transferInfo.uRequestedSize);
  146. return writeOk ? AK_Success : AK_Fail;
  147. }
  148. AKRESULT CBlockingDevice_wwise::Close(AkFileDesc& fileDesc)
  149. {
  150. auto fileIO = AZ::IO::FileIOBase::GetInstance();
  151. return fileIO->Close(GetRealFileHandle(fileDesc.hFile)) ? AK_Success : AK_Fail;
  152. }
  153. AkUInt32 CBlockingDevice_wwise::GetBlockSize([[maybe_unused]] AkFileDesc& fileDesc)
  154. {
  155. // No constraint on block size (file seeking).
  156. return 1;
  157. }
  158. void CBlockingDevice_wwise::GetDeviceDesc(AkDeviceDesc& deviceDesc)
  159. {
  160. deviceDesc.bCanRead = true;
  161. deviceDesc.bCanWrite = true;
  162. deviceDesc.deviceID = m_deviceID;
  163. AK_CHAR_TO_UTF16(deviceDesc.szDeviceName, "IO::IArchive", AZ_ARRAY_SIZE(deviceDesc.szDeviceName));
  164. deviceDesc.uStringSize = aznumeric_cast<AkUInt32>(AKPLATFORM::AkUtf16StrLen(deviceDesc.szDeviceName));
  165. }
  166. AkUInt32 CBlockingDevice_wwise::GetDeviceData()
  167. {
  168. return 1;
  169. }
  170. ///////////////////////////////////////////////////////////////////////////////////////////////////
  171. CStreamingDevice_wwise::~CStreamingDevice_wwise()
  172. {
  173. Destroy();
  174. }
  175. bool CStreamingDevice_wwise::Init(size_t poolSize)
  176. {
  177. Destroy();
  178. AkDeviceSettings deviceSettings;
  179. AK::StreamMgr::GetDefaultDeviceSettings(deviceSettings);
  180. deviceSettings.uIOMemorySize = aznumeric_cast<AkUInt32>(poolSize);
  181. deviceSettings.uSchedulerTypeFlags = AK_SCHEDULER_DEFERRED_LINED_UP;
  182. Platform::SetThreadProperties(deviceSettings.threadProperties);
  183. m_deviceID = AK::StreamMgr::CreateDevice(deviceSettings, this);
  184. return m_deviceID != AK_INVALID_DEVICE_ID;
  185. }
  186. void CStreamingDevice_wwise::Destroy()
  187. {
  188. if (m_deviceID != AK_INVALID_DEVICE_ID)
  189. {
  190. AK::StreamMgr::DestroyDevice(m_deviceID);
  191. m_deviceID = AK_INVALID_DEVICE_ID;
  192. }
  193. }
  194. bool CStreamingDevice_wwise::Open(const char* filename, [[maybe_unused]] AkOpenMode openMode, AkFileDesc& fileDesc)
  195. {
  196. AZ_Assert(openMode == AK_OpenModeRead, "Wwise Async File IO - Only supports opening files for reading.\n");
  197. auto fileIO = AZ::IO::FileIOBase::GetInstance();
  198. if (AZ::u64 fileSize = 0;
  199. fileIO->Size(filename, fileSize) && fileSize != 0)
  200. {
  201. AZStd::string* filenameStore = azcreate(AZStd::string, (filename));
  202. fileDesc.hFile = AkFileHandle();
  203. fileDesc.iFileSize = aznumeric_cast<AkInt64>(fileSize);
  204. fileDesc.uSector = 0;
  205. fileDesc.deviceID = m_deviceID;
  206. fileDesc.pCustomParam = filenameStore;
  207. fileDesc.uCustomParamSize = sizeof(AZStd::string*);
  208. auto streamer = AZ::Interface<AZ::IO::IStreamer>::Get();
  209. streamer->QueueRequest(streamer->CreateDedicatedCache(*filenameStore));
  210. return true;
  211. }
  212. return false;
  213. }
  214. AKRESULT CStreamingDevice_wwise::Read(AkFileDesc& fileDesc, const AkIoHeuristics& heuristics, AkAsyncIOTransferInfo& transferInfo)
  215. {
  216. AZ_Assert(fileDesc.pCustomParam, "Wwise Async File IO - Reading a file before it has been opened.\n");
  217. auto callback = [&transferInfo](AZ::IO::FileRequestHandle request)
  218. {
  219. AZ_PROFILE_FUNCTION(Audio);
  220. AZ::IO::IStreamerTypes::RequestStatus status = AZ::Interface<AZ::IO::IStreamer>::Get()->GetRequestStatus(request);
  221. switch (status)
  222. {
  223. case AZ::IO::IStreamerTypes::RequestStatus::Completed:
  224. transferInfo.pCallback(&transferInfo, AK_Success);
  225. break;
  226. case AZ::IO::IStreamerTypes::RequestStatus::Canceled:
  227. transferInfo.pCallback(&transferInfo, AK_Cancelled);
  228. break;
  229. default:
  230. transferInfo.pCallback(&transferInfo, AK_Fail);
  231. break;
  232. }
  233. };
  234. // The priorities for Wwise range from 0 (lowest priority) to 100 (highest priority). AZ::IO::Streamer has
  235. // a similar range except between 0 (lowest) and 255 (highest) so remap from one to the other.
  236. static_assert(AK_MIN_PRIORITY == 0, "The minimum priority for Wwise has changed, please update the conversion to AZ::IO::Streamers priority.");
  237. static_assert(AK_DEFAULT_PRIORITY == 50, "The default priority for Wwise has changed, please update the conversion to AZ::IO::Streamers priority.");
  238. static_assert(AK_MAX_PRIORITY == 100, "The maximum priority for Wwise has changed, please update the conversion to AZ::IO::Streamers priority.");
  239. static_assert(AZ::IO::IStreamerTypes::s_priorityLowest == 0, "The priority range for AZ::IO::Streamer has changed, please update Wwise to match.");
  240. static_assert(AZ::IO::IStreamerTypes::s_priorityHighest == 255, "The priority range for AZ::IO::Streamer has changed, please update Wwise to match.");
  241. AZ::u16 wwisePriority = aznumeric_caster(heuristics.priority);
  242. AZ::u8 priority = aznumeric_caster(
  243. (wwisePriority << 1) // 100 -> 200
  244. + (wwisePriority >> 1) // 200 -> 250
  245. + (wwisePriority >> 4) // 250 -> 256
  246. - (wwisePriority >> 6)); // 256 -> 255
  247. auto filename = reinterpret_cast<AZStd::string*>(fileDesc.pCustomParam);
  248. auto offset = aznumeric_cast<size_t>(transferInfo.uFilePosition);
  249. auto readSize = aznumeric_cast<size_t>(transferInfo.uRequestedSize);
  250. auto bufferSize = aznumeric_cast<size_t>(transferInfo.uBufferSize);
  251. auto deadline =
  252. AZStd::chrono::duration_cast<AZ::IO::IStreamerTypes::Deadline>(AZStd::chrono::duration<float, AZStd::milli>(heuristics.fDeadline));
  253. auto streamer = AZ::Interface<AZ::IO::IStreamer>::Get();
  254. AZ::IO::FileRequestPtr request = streamer->Read(*filename, transferInfo.pBuffer, bufferSize, readSize, deadline, priority, offset);
  255. streamer->SetRequestCompleteCallback(request, AZStd::move(callback));
  256. streamer->QueueRequest(AZStd::move(request));
  257. return AK_Success;
  258. }
  259. AKRESULT CStreamingDevice_wwise::Write(AkFileDesc&, const AkIoHeuristics&, AkAsyncIOTransferInfo&)
  260. {
  261. AZ_Assert(false, "Wwise Async File IO - Writing audio data is not supported for AZ::IO::Streamer based device.\n");
  262. return AK_Fail;
  263. }
  264. AKRESULT CStreamingDevice_wwise::Close(AkFileDesc& fileDesc)
  265. {
  266. AZ_Assert(fileDesc.pCustomParam, "Wwise Async File IO - Closing a file before it has been opened.\n");
  267. auto filename = reinterpret_cast<AZStd::string*>(fileDesc.pCustomParam);
  268. auto streamer = AZ::Interface<AZ::IO::IStreamer>::Get();
  269. streamer->QueueRequest(streamer->DestroyDedicatedCache(*filename));
  270. azdestroy(filename);
  271. return AK_Success;
  272. }
  273. AkUInt32 CStreamingDevice_wwise::GetBlockSize([[maybe_unused]] AkFileDesc& fileDesc)
  274. {
  275. // No constraint on block size (file seeking).
  276. return 1;
  277. }
  278. void CStreamingDevice_wwise::GetDeviceDesc(AkDeviceDesc& deviceDesc)
  279. {
  280. deviceDesc.bCanRead = true;
  281. deviceDesc.bCanWrite = false;
  282. deviceDesc.deviceID = m_deviceID;
  283. AK_CHAR_TO_UTF16(deviceDesc.szDeviceName, "IO::IStreamer", AZ_ARRAY_SIZE(deviceDesc.szDeviceName));
  284. deviceDesc.uStringSize = aznumeric_cast<AkUInt32>(AKPLATFORM::AkUtf16StrLen(deviceDesc.szDeviceName));
  285. }
  286. AkUInt32 CStreamingDevice_wwise::GetDeviceData()
  287. {
  288. return 2;
  289. }
  290. ///////////////////////////////////////////////////////////////////////////////////////////////////
  291. ///////////////////////////////////////////////////////////////////////////////////////////////////
  292. CFileIOHandler_wwise::CFileIOHandler_wwise()
  293. : m_useAsyncOpen(false)
  294. {
  295. ::memset(m_bankPath, 0, AK_MAX_PATH * sizeof(AkOSChar));
  296. ::memset(m_languageFolder, 0, AK_MAX_PATH * sizeof(AkOSChar));
  297. }
  298. ///////////////////////////////////////////////////////////////////////////////////////////////////
  299. AKRESULT CFileIOHandler_wwise::Init(size_t poolSize)
  300. {
  301. // If the Stream Manager's File Location Resolver was not set yet, set this object as the
  302. // File Location Resolver (this I/O hook is also able to resolve file location).
  303. if (!AK::StreamMgr::GetFileLocationResolver())
  304. {
  305. AK::StreamMgr::SetFileLocationResolver(this);
  306. }
  307. if (!m_streamingDevice.Init(poolSize))
  308. {
  309. return AK_Fail;
  310. }
  311. if (!m_blockingDevice.Init(poolSize))
  312. {
  313. return AK_Fail;
  314. }
  315. return AK_Success;
  316. }
  317. ///////////////////////////////////////////////////////////////////////////////////////////////////
  318. void CFileIOHandler_wwise::ShutDown()
  319. {
  320. if (AK::StreamMgr::GetFileLocationResolver() == this)
  321. {
  322. AK::StreamMgr::SetFileLocationResolver(nullptr);
  323. }
  324. m_blockingDevice.Destroy();
  325. m_streamingDevice.Destroy();
  326. }
  327. ///////////////////////////////////////////////////////////////////////////////////////////////////
  328. AKRESULT CFileIOHandler_wwise::Open(const AkOSChar* fileName, AkOpenMode openMode, AkFileSystemFlags* flags, bool& syncOpen, AkFileDesc& fileDesc)
  329. {
  330. AKRESULT akResult = AK_Fail;
  331. if (syncOpen || !m_useAsyncOpen)
  332. {
  333. syncOpen = true;
  334. AkOSChar finalFilePath[AK_MAX_PATH] = { '\0' };
  335. AKPLATFORM::SafeStrCat(finalFilePath, m_bankPath, AK_MAX_PATH);
  336. if (flags && openMode == AK_OpenModeRead)
  337. {
  338. // Add language folder if the file is localized.
  339. if (flags->uCompanyID == AKCOMPANYID_AUDIOKINETIC && flags->uCodecID == AKCODECID_BANK && flags->bIsLanguageSpecific)
  340. {
  341. AKPLATFORM::SafeStrCat(finalFilePath, m_languageFolder, AK_MAX_PATH);
  342. }
  343. }
  344. AKPLATFORM::SafeStrCat(finalFilePath, fileName, AK_MAX_PATH);
  345. char* tempStr = nullptr;
  346. CONVERT_OSCHAR_TO_CHAR(finalFilePath, tempStr);
  347. if (openMode == AK_OpenModeRead)
  348. {
  349. return m_streamingDevice.Open(tempStr, openMode, fileDesc) ? AK_Success : AK_Fail;
  350. }
  351. return m_blockingDevice.Open(tempStr, openMode, fileDesc) ? AK_Success : AK_Fail;
  352. }
  353. return akResult;
  354. }
  355. ///////////////////////////////////////////////////////////////////////////////////////////////////
  356. AKRESULT CFileIOHandler_wwise::Open(AkFileID fileID, AkOpenMode openMode, AkFileSystemFlags* flags, bool& syncOpen, AkFileDesc& fileDesc)
  357. {
  358. AKRESULT akResult = AK_Fail;
  359. if (flags && (syncOpen || !m_useAsyncOpen))
  360. {
  361. syncOpen = true;
  362. AkOSChar finalFilePath[AK_MAX_PATH] = { '\0' };
  363. AKPLATFORM::SafeStrCat(finalFilePath, m_bankPath, AK_MAX_PATH);
  364. if (openMode == AK_OpenModeRead)
  365. {
  366. // Add language folder if the file is localized.
  367. if (flags->uCompanyID == AKCOMPANYID_AUDIOKINETIC && flags->bIsLanguageSpecific)
  368. {
  369. AKPLATFORM::SafeStrCat(finalFilePath, m_languageFolder, AK_MAX_PATH);
  370. }
  371. }
  372. AkOSChar fileName[MAX_FILETITLE_SIZE] = { 0 };
  373. if (flags->uCodecID == AKCODECID_BANK)
  374. {
  375. AK_OSPRINTF(fileName, MAX_FILETITLE_SIZE, AKTEXT("%u.bnk"), static_cast<unsigned int>(fileID));
  376. }
  377. else
  378. {
  379. AK_OSPRINTF(fileName, MAX_FILETITLE_SIZE, AKTEXT("%u.wem"), static_cast<unsigned int>(fileID));
  380. }
  381. AKPLATFORM::SafeStrCat(finalFilePath, fileName, AK_MAX_PATH);
  382. char* filePath = nullptr;
  383. CONVERT_OSCHAR_TO_CHAR(finalFilePath, filePath);
  384. if (openMode == AK_OpenModeRead)
  385. {
  386. return m_streamingDevice.Open(filePath, openMode, fileDesc) ? AK_Success : AK_Fail;
  387. }
  388. return m_blockingDevice.Open(filePath, openMode, fileDesc) ? AK_Success : AK_Fail;
  389. }
  390. return akResult;
  391. }
  392. ///////////////////////////////////////////////////////////////////////////////////////////////////
  393. void CFileIOHandler_wwise::SetBankPath(const char* const bankPath)
  394. {
  395. const AkOSChar* akBankPath = nullptr;
  396. CONVERT_CHAR_TO_OSCHAR(bankPath, akBankPath);
  397. AKPLATFORM::SafeStrCpy(m_bankPath, akBankPath, AK_MAX_PATH);
  398. }
  399. ///////////////////////////////////////////////////////////////////////////////////////////////////
  400. void CFileIOHandler_wwise::SetLanguageFolder(const char* const languageFolder)
  401. {
  402. const AkOSChar* akLanguageFolder = nullptr;
  403. CONVERT_CHAR_TO_OSCHAR(languageFolder, akLanguageFolder);
  404. AKPLATFORM::SafeStrCpy(m_languageFolder, akLanguageFolder, AK_MAX_PATH);
  405. }
  406. } // namespace Audio