AkMixerPlatform.cpp 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520
  1. /*******************************************************************************
  2. The content of this file includes portions of the proprietary AUDIOKINETIC Wwise
  3. Technology released in source code form as part of the game integration package.
  4. The content of this file may not be used without valid licenses to the
  5. AUDIOKINETIC Wwise Technology.
  6. Note that the use of the game engine is subject to the Unreal(R) Engine End User
  7. License Agreement at https://www.unrealengine.com/en-US/eula/unreal
  8. License Usage
  9. Licensees holding valid licenses to the AUDIOKINETIC Wwise Technology may use
  10. this file in accordance with the end user license agreement provided with the
  11. software or, alternatively, in accordance with the terms contained
  12. in a written agreement between you and Audiokinetic Inc.
  13. Copyright (c) 2023 Audiokinetic Inc.
  14. *******************************************************************************/
  15. #include "AkMixerPlatform.h"
  16. #include "AkAudioEvent.h"
  17. #include "AkAudioModule.h"
  18. #include "AudioMixerTypes.h"
  19. #include "CoreGlobals.h"
  20. #include "UObject/UObjectGlobals.h"
  21. #include "AkSettings.h"
  22. #include "AudioDevice.h"
  23. #include "AudioMixerInputComponent.h"
  24. #if WITH_ENGINE
  25. #include "AudioPluginUtilities.h"
  26. #include "OpusAudioInfo.h"
  27. #include "VorbisAudioInfo.h"
  28. #include "ADPCMAudioInfo.h"
  29. #endif // WITH_ENGINE
  30. #if UE_5_0_OR_LATER
  31. #include "BinkAudioInfo.h"
  32. #endif
  33. DECLARE_LOG_CATEGORY_EXTERN(LogAkAudioMixer, Log, All);
  34. DEFINE_LOG_CATEGORY(LogAkAudioMixer);
  35. FName FAkMixerPlatform::NAME_OGG(TEXT("OGG"));
  36. FName FAkMixerPlatform::NAME_OPUS(TEXT("OPUS"));
  37. FName FAkMixerPlatform::NAME_ADPCM(TEXT("ADPCM"));
  38. FAkMixerPlatform::FAkMixerPlatform() :
  39. AkAudioMixerInputComponent(nullptr),
  40. bIsInitialized(false),
  41. bIsDeviceOpen(false),
  42. InputEvent(nullptr),
  43. OutputBuffer(nullptr),
  44. OutputBufferByteLength(0)
  45. {
  46. #if !WITH_EDITOR
  47. LoadVorbisLibraries();
  48. #endif
  49. }
  50. FAkMixerPlatform::~FAkMixerPlatform()
  51. {
  52. if (bIsInitialized)
  53. {
  54. TeardownHardware();
  55. }
  56. }
  57. void FAkMixerPlatform::OnAkAudioModuleInit()
  58. {
  59. Audio::FAudioMixerOpenStreamParams CurrentStreamParams = OpenStreamParams;
  60. CloseAudioStream();
  61. OpenAudioStream(CurrentStreamParams);
  62. StartAudioStream();
  63. }
  64. bool FAkMixerPlatform::OnNextBuffer(uint32 NumChannels, uint32 NumSamples, float** OutBufferToFill)
  65. {
  66. if (!bIsDeviceInitialized
  67. || !(AudioStreamInfo.StreamState == Audio::EAudioOutputStreamState::Open
  68. || AudioStreamInfo.StreamState == Audio::EAudioOutputStreamState::Running))
  69. {
  70. return false;
  71. }
  72. checkf(OutputBufferByteLength == NumChannels * NumSamples * sizeof(float), TEXT("Please ensure the Wwise \"Samples per frame\" initialization setting matches the Unreal Audio \"Callback Buffer Size\" setting for the current platform."));
  73. OutputBuffer = OutBufferToFill;
  74. ReadNextBuffer();
  75. return true;
  76. }
  77. bool FAkMixerPlatform::InitializeHardware()
  78. {
  79. if (!FAkAudioDevice::IsInitialized())
  80. {
  81. AkAudioModuleInitHandle = FAkAudioModule::OnModuleInitialized.AddRaw(this, &FAkMixerPlatform::OnAkAudioModuleInit);
  82. }
  83. #if ENGINE_MAJOR_VERSION >= 5
  84. if(Audio::IAudioMixer::ShouldRecycleThreads())
  85. {
  86. // Pre-create the null render device thread, so we can simple wake it up when we need it.
  87. // Give it nothing to do, with a slow tick as the default, but ask it to wait for a signal to wake up.
  88. // Ensuring this exists prevents a crash in FMixerNullCallback::Run if the callback does not already exist.
  89. CreateNullDeviceThread([] {}, 1.0f, true);
  90. }
  91. #endif
  92. bIsInitialized = true;
  93. // Must always return true at Editor startup at least, since a failed
  94. // initialization results in a failed initialized AudioDevice, which later
  95. // results in a crash.
  96. return true;
  97. }
  98. bool FAkMixerPlatform::TeardownHardware()
  99. {
  100. if (!bIsInitialized)
  101. {
  102. return true;
  103. }
  104. StopAudioStream();
  105. CloseAudioStream();
  106. if (AkAudioMixerInputComponent)
  107. {
  108. delete AkAudioMixerInputComponent;
  109. AkAudioMixerInputComponent = nullptr;
  110. }
  111. AkAudioModuleInitHandle.Reset();
  112. bIsInitialized = false;
  113. return true;
  114. }
  115. bool FAkMixerPlatform::IsInitialized() const
  116. {
  117. return bIsInitialized;
  118. }
  119. bool FAkMixerPlatform::GetNumOutputDevices(uint32& OutNumOutputDevices)
  120. {
  121. // TODO Define constant
  122. OutNumOutputDevices = 1;
  123. return true;
  124. }
  125. bool FAkMixerPlatform::GetOutputDeviceInfo(const uint32 InDeviceIndex, Audio::FAudioPlatformDeviceInfo& OutInfo)
  126. {
  127. AkAudioFormat AudioFormat;
  128. AkAudioMixerInputComponent->GetChannelConfig(AudioFormat);
  129. OutInfo.Format = Audio::EAudioMixerStreamDataFormat::Float;
  130. OutInfo.Name = GetDefaultDeviceName();
  131. OutInfo.DeviceId = TEXT("WwiseDevice");
  132. OutInfo.NumChannels = AudioFormat.GetNumChannels();
  133. OutInfo.SampleRate = AudioFormat.uSampleRate;
  134. OutInfo.OutputChannelArray.SetNum(OutInfo.NumChannels);
  135. for (int32 ChannelNum = 0; ChannelNum < OutInfo.NumChannels; ++ChannelNum)
  136. {
  137. OutInfo.OutputChannelArray.Add(EAudioMixerChannel::Type(ChannelNum));
  138. }
  139. return true;
  140. }
  141. bool FAkMixerPlatform::GetDefaultOutputDeviceIndex(uint32& OutDefaultDeviceIndex) const
  142. {
  143. OutDefaultDeviceIndex = AUDIO_MIXER_DEFAULT_DEVICE_INDEX;
  144. return true;
  145. }
  146. bool FAkMixerPlatform::OpenAudioStream(const Audio::FAudioMixerOpenStreamParams& Params)
  147. {
  148. if (!bIsInitialized || AudioStreamInfo.StreamState != Audio::EAudioOutputStreamState::Closed)
  149. {
  150. return false;
  151. }
  152. OpenStreamParams = Params;
  153. AudioStreamInfo.Reset();
  154. AudioStreamInfo.OutputDeviceIndex = OpenStreamParams.OutputDeviceIndex;
  155. AudioStreamInfo.NumOutputFrames = OpenStreamParams.NumFrames;
  156. AudioStreamInfo.NumBuffers = OpenStreamParams.NumBuffers;
  157. AudioStreamInfo.AudioMixer = OpenStreamParams.AudioMixer;
  158. // Allow negotiating output format with the Sound Engine
  159. if (!GetOutputDeviceInfo(AudioStreamInfo.OutputDeviceIndex, AudioStreamInfo.DeviceInfo))
  160. {
  161. return false;
  162. }
  163. OutputBufferByteLength = OpenStreamParams.NumFrames * AudioStreamInfo.DeviceInfo.NumChannels * GetAudioStreamChannelSize();
  164. UE_LOG(LogAkAudioMixer, Verbose, TEXT("Opening Audio stream for device: %s"), *GetDeviceId())
  165. AudioStreamInfo.StreamState = Audio::EAudioOutputStreamState::Open;
  166. if (FAkAudioDevice::IsInitialized())
  167. {
  168. bool bOpenStreamError = false;
  169. AkAudioMixerInputComponent = new FAudioMixerInputComponent();
  170. AkAudioMixerInputComponent->OnNextBuffer = FAkGlobalAudioInputDelegate::CreateRaw(this, &FAkMixerPlatform::OnNextBuffer);
  171. UAkSettings* AkSettings = GetMutableDefault<UAkSettings>();
  172. if (AkSettings != nullptr)
  173. {
  174. AkSettings->GetAudioInputEvent(InputEvent);
  175. }
  176. if (!InputEvent)
  177. {
  178. UE_LOG(LogAkAudioMixer, Error, TEXT("Unable to open audio stream. Ak Audio Event is not set."));
  179. bOpenStreamError = true;
  180. }
  181. else
  182. {
  183. InputEvent->AddToRoot(); // Make sure the event can't be garbage collected.
  184. AkPlayingID PlayingID = AkAudioMixerInputComponent->PostAssociatedAudioInputEvent(InputEvent);
  185. if (PlayingID == AK_INVALID_PLAYING_ID)
  186. {
  187. UE_LOG(LogAkAudioMixer, Error, TEXT("Unable to open audio stream. Could not post Ak Audio Event."));
  188. bOpenStreamError = true;
  189. }
  190. }
  191. if (!bOpenStreamError)
  192. {
  193. UE_LOG(LogAkAudioMixer, Verbose, TEXT("Opened Audio stream for device: %s"), *GetDeviceId())
  194. bIsDeviceOpen = true;
  195. }
  196. }
  197. else
  198. {
  199. UE_LOG(LogAkAudioMixer, Verbose, TEXT("Audio stream deferred for device %s, AkAudioDevice is not yet initialized"), *GetDeviceId())
  200. }
  201. // Must always return true Editor startup at least, since a failed
  202. // initialization results in a failed initialized AudioDevice, which later
  203. // results in a crash.
  204. return true;
  205. }
  206. bool FAkMixerPlatform::CloseAudioStream()
  207. {
  208. if (AudioStreamInfo.StreamState == Audio::EAudioOutputStreamState::Closed)
  209. {
  210. return false;
  211. }
  212. UE_LOG(LogAkAudioMixer, Verbose, TEXT("Closing Audio stream for device: %s"), *GetDeviceId())
  213. if (!StopAudioStream())
  214. {
  215. return false;
  216. }
  217. if (bIsUsingNullDevice)
  218. {
  219. StopRunningNullDevice();
  220. }
  221. {
  222. FScopeLock ScopedLock(&OutputBufferMutex);
  223. OutputBuffer = nullptr;
  224. OutputBufferByteLength = 0;
  225. }
  226. if (FAkAudioDevice::IsInitialized())
  227. {
  228. if (AkAudioMixerInputComponent)
  229. {
  230. AkAudioMixerInputComponent->PostUnregisterGameObject();
  231. delete AkAudioMixerInputComponent;
  232. AkAudioMixerInputComponent = nullptr;
  233. }
  234. if (InputEvent)
  235. {
  236. InputEvent->RemoveFromRoot(); // Allow garbage collection on the event.
  237. InputEvent = nullptr;
  238. }
  239. }
  240. bIsDeviceOpen = false;
  241. AudioStreamInfo.StreamState = Audio::EAudioOutputStreamState::Closed;
  242. return true;
  243. }
  244. bool FAkMixerPlatform::StartAudioStream()
  245. {
  246. if (!bIsInitialized || (AudioStreamInfo.StreamState != Audio::EAudioOutputStreamState::Open
  247. && AudioStreamInfo.StreamState != Audio::EAudioOutputStreamState::Stopped))
  248. {
  249. return false;
  250. }
  251. UE_LOG(LogAkAudioMixer, Verbose, TEXT("Starting Audio stream for device: %s"), *GetDeviceId())
  252. BeginGeneratingAudio();
  253. if (!bIsDeviceOpen)
  254. {
  255. check(!bIsUsingNullDevice);
  256. StartRunningNullDevice();
  257. }
  258. check(AudioStreamInfo.StreamState == Audio::EAudioOutputStreamState::Running);
  259. return true;
  260. }
  261. bool FAkMixerPlatform::StopAudioStream()
  262. {
  263. if (AudioStreamInfo.StreamState != Audio::EAudioOutputStreamState::Stopped
  264. && AudioStreamInfo.StreamState != Audio::EAudioOutputStreamState::Closed)
  265. {
  266. UE_LOG(LogAkAudioMixer, Verbose, TEXT("Stopping Audio stream for device: %s"), *GetDeviceId())
  267. if (AudioStreamInfo.StreamState == Audio::EAudioOutputStreamState::Running)
  268. {
  269. StopGeneratingAudio();
  270. }
  271. check(AudioStreamInfo.StreamState == Audio::EAudioOutputStreamState::Stopped);
  272. }
  273. return true;
  274. }
  275. Audio::FAudioPlatformDeviceInfo FAkMixerPlatform::GetPlatformDeviceInfo() const
  276. {
  277. return AudioStreamInfo.DeviceInfo;
  278. }
  279. void FAkMixerPlatform::SubmitBuffer(const uint8* Buffer)
  280. {
  281. FScopeLock ScopedLock(&OutputBufferMutex);
  282. if (OutputBuffer)
  283. {
  284. for (int32 Channel = 0; Channel < AudioStreamInfo.DeviceInfo.NumChannels; Channel++)
  285. {
  286. for (int32 Frame = 0; Frame < AudioStreamInfo.NumOutputFrames; Frame++)
  287. {
  288. FMemory::Memcpy(&OutputBuffer[Channel][Frame],
  289. &Buffer[sizeof(float) * ((AudioStreamInfo.DeviceInfo.NumChannels * Frame) + Channel)],
  290. sizeof(float));
  291. }
  292. }
  293. }
  294. }
  295. #if UE_5_0_OR_LATER
  296. FName FAkMixerPlatform::GetRuntimeFormat(const USoundWave* InSoundWave) const
  297. {
  298. const FName RuntimeFormat = Audio::ToName(InSoundWave->GetSoundAssetCompressionType());
  299. if (RuntimeFormat == Audio::NAME_PLATFORM_SPECIFIC)
  300. {
  301. #if defined(PLATFORM_PS5) && PLATFORM_PS5
  302. if (InSoundWave->IsStreaming() && InSoundWave->IsSeekable())
  303. {
  304. return Audio::NAME_ADPCM;
  305. }
  306. checkf(false, TEXT("Please set your Unreal audio sources to Streaming and Seekable in order to play them through Wwise on the PS5"));
  307. return NAME_ADPCM;
  308. #else
  309. if (InSoundWave->IsStreaming())
  310. {
  311. if (InSoundWave->IsSeekable())
  312. {
  313. return Audio::NAME_ADPCM;
  314. }
  315. return Audio::NAME_OPUS;
  316. }
  317. return Audio::NAME_OGG;
  318. #endif
  319. }
  320. return RuntimeFormat;
  321. }
  322. ICompressedAudioInfo* FAkMixerPlatform::CreateCompressedAudioInfo(const FName& InRuntimeFormat) const
  323. {
  324. ICompressedAudioInfo* Decoder = nullptr;
  325. if (InRuntimeFormat == Audio::NAME_OGG)
  326. {
  327. Decoder = new FVorbisAudioInfo();
  328. }
  329. else if (InRuntimeFormat == Audio::NAME_OPUS)
  330. {
  331. Decoder = new FOpusAudioInfo();
  332. }
  333. #if WITH_BINK_AUDIO
  334. else if (InRuntimeFormat == Audio::NAME_BINKA)
  335. {
  336. Decoder = new FBinkAudioInfo();
  337. }
  338. #endif // WITH_BINK_AUDIO
  339. else
  340. {
  341. Decoder = Audio::CreateSoundAssetDecoder(InRuntimeFormat);
  342. }
  343. ensureMsgf(Decoder != nullptr, TEXT("Failed to create a sound asset decoder for compression type: %s"), *InRuntimeFormat.ToString());
  344. return Decoder;
  345. }
  346. #else // UE4.27
  347. FName FAkMixerPlatform::GetRuntimeFormat(USoundWave* InSoundWave)
  348. {
  349. #if WITH_ENGINE
  350. check(InSoundWave);
  351. #if defined(PLATFORM_PS5) && PLATFORM_PS5
  352. static FName NAME_ADPCM(TEXT("ADPCM"));
  353. if (InSoundWave->IsStreaming() && InSoundWave->IsSeekableStreaming())
  354. {
  355. return NAME_ADPCM;
  356. }
  357. checkf(false, TEXT("Please set your Unreal audio sources to Streaming and Seekable in order to play them through Wwise on the PS5"));
  358. return NAME_ADPCM;
  359. #else
  360. if (InSoundWave->IsStreaming(nullptr))
  361. {
  362. if (InSoundWave->IsSeekableStreaming())
  363. {
  364. return NAME_ADPCM;
  365. }
  366. return NAME_OPUS;
  367. }
  368. return NAME_OGG;
  369. #endif
  370. #else
  371. checkNoEntry();
  372. return FName();
  373. #endif // WITH_ENGINE
  374. }
  375. bool FAkMixerPlatform::HasCompressedAudioInfoClass(USoundWave* InSoundWave)
  376. {
  377. return true;
  378. }
  379. ICompressedAudioInfo* FAkMixerPlatform::CreateCompressedAudioInfo(USoundWave* InSoundWave)
  380. {
  381. #if WITH_ENGINE
  382. check(InSoundWave);
  383. #if defined(PLATFORM_PS5) && PLATFORM_PS5
  384. if (InSoundWave->IsStreaming() && InSoundWave->IsSeekableStreaming())
  385. {
  386. return new FADPCMAudioInfo();
  387. }
  388. checkf(false, TEXT("Please set your Unreal audio sources to Streaming and Seekable in order to play them through Wwise on the PS5"));
  389. return new FADPCMAudioInfo();
  390. #else
  391. if (InSoundWave->IsStreaming())
  392. {
  393. if (InSoundWave->IsSeekableStreaming())
  394. {
  395. return new FADPCMAudioInfo();
  396. }
  397. return new FOpusAudioInfo();
  398. }
  399. if (InSoundWave->HasCompressedData(NAME_OGG))
  400. {
  401. return new FVorbisAudioInfo();
  402. }
  403. return new FADPCMAudioInfo();
  404. #endif
  405. #else
  406. checkNoEntry();
  407. return nullptr;
  408. #endif // WITH_ENGINE
  409. }
  410. #endif
  411. FString FAkMixerPlatform::GetDefaultDeviceName() {
  412. static FString DefaultName(TEXT("Wwise Audio Mixer Device."));
  413. return DefaultName;
  414. }
  415. FString FAkMixerPlatform::GetDeviceId() const
  416. {
  417. return AudioStreamInfo.DeviceInfo.DeviceId;
  418. }
  419. FAudioPlatformSettings FAkMixerPlatform::GetPlatformSettings() const
  420. {
  421. return FAudioPlatformSettings::GetPlatformSettings(FPlatformProperties::GetRuntimeSettingsClassName());
  422. }