AkMixerPlatform.cpp 17 KB

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