MiniAudioPlaybackComponentController.cpp 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444
  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 "MiniAudioPlaybackComponentController.h"
  9. #include <AzCore/Asset/AssetSerializer.h>
  10. #include <AzCore/Component/TransformBus.h>
  11. #include <AzCore/Serialization/EditContext.h>
  12. #include "AzCore/Math/MathUtils.h"
  13. #include "MiniAudioIncludes.h"
  14. namespace MiniAudio
  15. {
  16. MiniAudioPlaybackComponentController::MiniAudioPlaybackComponentController()
  17. {
  18. }
  19. // placement of this destructor is intentional. It forces unique_ptr<ma_sound> to declare its destructor here
  20. // instead of in the header before inclusion of the giant MiniAudioIncludes.h file
  21. MiniAudioPlaybackComponentController::~MiniAudioPlaybackComponentController()
  22. {
  23. }
  24. MiniAudioPlaybackComponentController::MiniAudioPlaybackComponentController(const MiniAudioPlaybackComponentConfig& config)
  25. {
  26. m_config = config;
  27. }
  28. void MiniAudioPlaybackComponentController::Reflect(AZ::ReflectContext* context)
  29. {
  30. MiniAudioPlaybackComponentConfig::Reflect(context);
  31. if (auto serialize = azrtti_cast<AZ::SerializeContext*>(context))
  32. {
  33. serialize->Class<MiniAudioPlaybackComponentController>()
  34. ->Field("Config", &MiniAudioPlaybackComponentController::m_config)
  35. ->Version(1);
  36. }
  37. }
  38. void MiniAudioPlaybackComponentController::GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided)
  39. {
  40. provided.push_back(AZ_CRC_CE("MiniAudioPlaybackComponent"));
  41. }
  42. void MiniAudioPlaybackComponentController::Activate(const AZ::EntityComponentIdPair& entityComponentIdPair)
  43. {
  44. m_entityComponentIdPair = entityComponentIdPair;
  45. m_config.m_innerAngleInRadians = AZ::DegToRad(m_config.m_innerAngleInDegrees);
  46. m_config.m_outerAngleInRadians = AZ::DegToRad(m_config.m_outerAngleInDegrees);
  47. MiniAudioPlaybackRequestBus::Handler::BusConnect(m_entityComponentIdPair.GetEntityId());
  48. OnConfigurationUpdated();
  49. }
  50. void MiniAudioPlaybackComponentController::SetConfiguration(const MiniAudioPlaybackComponentConfig& config)
  51. {
  52. m_config = config;
  53. OnConfigurationUpdated();
  54. }
  55. const MiniAudioPlaybackComponentConfig& MiniAudioPlaybackComponentController::GetConfiguration() const
  56. {
  57. return m_config;
  58. }
  59. void MiniAudioPlaybackComponentController::Deactivate()
  60. {
  61. m_entityMovedHandler.Disconnect();
  62. UnloadSound();
  63. m_config.m_sound.Release();
  64. MiniAudioPlaybackRequestBus::Handler::BusDisconnect();
  65. AZ::Data::AssetBus::MultiHandler::BusDisconnect();
  66. }
  67. void MiniAudioPlaybackComponentController::Play()
  68. {
  69. if (m_sound)
  70. {
  71. ma_sound_start(m_sound.get());
  72. }
  73. }
  74. void MiniAudioPlaybackComponentController::Stop()
  75. {
  76. if (m_sound)
  77. {
  78. ma_sound_stop(m_sound.get());
  79. ma_sound_seek_to_pcm_frame(m_sound.get(), 0);
  80. }
  81. }
  82. void MiniAudioPlaybackComponentController::Pause()
  83. {
  84. if (m_sound)
  85. {
  86. ma_sound_stop(m_sound.get());
  87. }
  88. }
  89. float MiniAudioPlaybackComponentController::GetVolumePercentage() const
  90. {
  91. return ma_sound_get_volume(m_sound.get()) * 100.f;
  92. }
  93. void MiniAudioPlaybackComponentController::SetVolumePercentage(float volume)
  94. {
  95. m_config.m_volume = AZ::GetClamp(volume, 0.f, 100.f);
  96. if (m_sound)
  97. {
  98. ma_sound_set_volume(m_sound.get(), (m_config.m_volume / 100.f));
  99. }
  100. }
  101. float MiniAudioPlaybackComponentController::GetVolumeDecibels() const
  102. {
  103. return ma_volume_linear_to_db(ma_sound_get_volume(m_sound.get()));
  104. }
  105. void MiniAudioPlaybackComponentController::SetVolumeDecibels(float volumeDecibels)
  106. {
  107. m_config.m_volume = ma_volume_db_to_linear(volumeDecibels) * 100.f;
  108. if (m_sound)
  109. {
  110. ma_sound_set_volume(m_sound.get(), (m_config.m_volume / 100.f));
  111. }
  112. }
  113. void MiniAudioPlaybackComponentController::SetLooping(bool loop)
  114. {
  115. m_config.m_loop = loop;
  116. if (m_sound)
  117. {
  118. ma_sound_set_looping(m_sound.get(), loop);
  119. }
  120. }
  121. bool MiniAudioPlaybackComponentController::IsLooping() const
  122. {
  123. if (m_sound)
  124. {
  125. return ma_sound_is_looping(m_sound.get());
  126. }
  127. return false;
  128. }
  129. AZ::Data::Asset<SoundAsset> MiniAudioPlaybackComponentController::GetSoundAsset() const
  130. {
  131. return m_config.m_sound;
  132. }
  133. void MiniAudioPlaybackComponentController::SetSoundAsset(AZ::Data::Asset<SoundAsset> soundAsset)
  134. {
  135. if (m_config.m_sound.GetId() != soundAsset.GetId())
  136. {
  137. m_config.m_sound = soundAsset;
  138. OnConfigurationUpdated();
  139. }
  140. }
  141. SoundAssetRef MiniAudioPlaybackComponentController::GetSoundAssetRef() const
  142. {
  143. SoundAssetRef ref;
  144. ref.SetAsset(GetSoundAsset());
  145. return ref;
  146. }
  147. void MiniAudioPlaybackComponentController::SetSoundAssetRef(const SoundAssetRef& soundAssetRef)
  148. {
  149. SetSoundAsset(soundAssetRef.GetAsset());
  150. }
  151. float MiniAudioPlaybackComponentController::GetInnerAngleInRadians() const
  152. {
  153. return m_config.m_innerAngleInRadians;
  154. }
  155. void MiniAudioPlaybackComponentController::SetInnerAngleInRadians(float innerAngleInRadians)
  156. {
  157. m_config.m_innerAngleInRadians = innerAngleInRadians;
  158. m_config.m_innerAngleInDegrees = AZ::RadToDeg(m_config.m_innerAngleInRadians);
  159. OnConfigurationUpdated();
  160. }
  161. float MiniAudioPlaybackComponentController::GetInnerAngleInDegrees() const
  162. {
  163. return m_config.m_innerAngleInDegrees;
  164. }
  165. void MiniAudioPlaybackComponentController::SetInnerAngleInDegrees(float innerAngleInDegrees)
  166. {
  167. m_config.m_innerAngleInDegrees = innerAngleInDegrees;
  168. m_config.m_innerAngleInRadians = AZ::DegToRad(m_config.m_innerAngleInDegrees);
  169. OnConfigurationUpdated();
  170. }
  171. float MiniAudioPlaybackComponentController::GetOuterAngleInRadians() const
  172. {
  173. return m_config.m_outerAngleInRadians;
  174. }
  175. void MiniAudioPlaybackComponentController::SetOuterAngleInRadians(float outerAngleInRadians)
  176. {
  177. m_config.m_outerAngleInRadians = outerAngleInRadians;
  178. m_config.m_outerAngleInDegrees = AZ::RadToDeg(m_config.m_outerAngleInRadians);
  179. OnConfigurationUpdated();
  180. }
  181. float MiniAudioPlaybackComponentController::GetOuterAngleInDegrees() const
  182. {
  183. return m_config.m_outerAngleInDegrees;
  184. }
  185. void MiniAudioPlaybackComponentController::SetOuterAngleInDegrees(float outerAngleInDegrees)
  186. {
  187. m_config.m_outerAngleInDegrees = outerAngleInDegrees;
  188. m_config.m_outerAngleInRadians = AZ::DegToRad(m_config.m_outerAngleInDegrees);
  189. OnConfigurationUpdated();
  190. }
  191. float MiniAudioPlaybackComponentController::GetOuterVolumePercentage() const
  192. {
  193. return m_config.m_outerVolume;
  194. }
  195. void MiniAudioPlaybackComponentController::SetOuterVolumePercentage(float outerVolume)
  196. {
  197. m_config.m_outerVolume = AZ::GetClamp(outerVolume, 0.f, 100.f);
  198. OnConfigurationUpdated();
  199. }
  200. float MiniAudioPlaybackComponentController::GetOuterVolumeDecibels() const
  201. {
  202. return ma_volume_linear_to_db(m_config.m_outerVolume / 100.f);
  203. }
  204. void MiniAudioPlaybackComponentController::SetOuterVolumeDecibels(float outerVolumeDecibels)
  205. {
  206. m_config.m_outerVolume = ma_volume_db_to_linear(outerVolumeDecibels) * 100.f;
  207. OnConfigurationUpdated();
  208. }
  209. bool MiniAudioPlaybackComponentController::GetFixedDirecion() const
  210. {
  211. return m_config.m_fixedDirection;
  212. }
  213. void MiniAudioPlaybackComponentController::SetFixedDirecion(bool fixedDirection)
  214. {
  215. m_config.m_fixedDirection = fixedDirection;
  216. }
  217. float MiniAudioPlaybackComponentController::GetDirectionalAttenuationFactor() const
  218. {
  219. return ma_sound_get_directional_attenuation_factor(m_sound.get());
  220. }
  221. void MiniAudioPlaybackComponentController::SetDirectionalAttenuationFactor(float directionalAttenuationFactor)
  222. {
  223. m_config.m_directionalAttenuationFactor = directionalAttenuationFactor;
  224. OnConfigurationUpdated();
  225. }
  226. AZ::Vector3 MiniAudioPlaybackComponentController::GetDirection() const
  227. {
  228. ma_vec3f direction = ma_sound_get_direction(m_sound.get());
  229. return AZ::Vector3(direction.x, direction.y, direction.z);
  230. }
  231. void MiniAudioPlaybackComponentController::SetDirection(const AZ::Vector3& direction)
  232. {
  233. m_config.m_direction = direction;
  234. ma_sound_set_direction(m_sound.get(), m_config.m_direction.GetX(), m_config.m_direction.GetY(), m_config.m_direction.GetZ());
  235. }
  236. void MiniAudioPlaybackComponentController::OnAssetReady(AZ::Data::Asset<AZ::Data::AssetData> asset)
  237. {
  238. AZ::Data::AssetBus::MultiHandler::BusDisconnect(asset.GetId());
  239. // Re-assign the sound before attempting to load it if it was
  240. // released and the asset is now ready.
  241. // This can happen in the Editor when returning from game mode
  242. if (!m_config.m_sound.IsReady())
  243. {
  244. m_config.m_sound = asset;
  245. }
  246. LoadSound();
  247. }
  248. void MiniAudioPlaybackComponentController::OnWorldTransformChanged(const AZ::Transform& world)
  249. {
  250. if (m_sound)
  251. {
  252. ma_sound_set_position(
  253. m_sound.get(), world.GetTranslation().GetX(), world.GetTranslation().GetY(), world.GetTranslation().GetZ());
  254. // Set the forward direction for the sound source
  255. if (!m_config.m_fixedDirection)
  256. {
  257. AZ::Transform worldTm = AZ::Transform::CreateIdentity();
  258. AZ::TransformBus::EventResult(worldTm, m_entityComponentIdPair.GetEntityId(), &AZ::TransformBus::Events::GetWorldTM);
  259. ma_sound_set_direction(m_sound.get(), worldTm.GetBasisY().GetX(), worldTm.GetBasisY().GetY(), worldTm.GetBasisY().GetZ());
  260. }
  261. }
  262. }
  263. void MiniAudioPlaybackComponentController::OnConfigurationUpdated()
  264. {
  265. if (m_config.m_sound.IsReady() == false)
  266. {
  267. AZ::Data::AssetBus::MultiHandler::BusConnect(m_config.m_sound.GetId());
  268. m_config.m_sound.QueueLoad();
  269. }
  270. else
  271. {
  272. LoadSound();
  273. }
  274. }
  275. void MiniAudioPlaybackComponentController::LoadSound()
  276. {
  277. UnloadSound();
  278. if (ma_engine* engine = MiniAudioInterface::Get()->GetSoundEngine())
  279. {
  280. if (GetConfiguration().m_sound.IsReady())
  281. {
  282. m_soundName = GetConfiguration().m_sound.GetHint();
  283. const auto& assetBuffer = GetConfiguration().m_sound->m_data;
  284. if (assetBuffer.empty())
  285. {
  286. return;
  287. }
  288. ma_result result = ma_resource_manager_register_encoded_data(
  289. ma_engine_get_resource_manager(engine), m_soundName.c_str(), assetBuffer.data(), assetBuffer.size());
  290. if (result != MA_SUCCESS)
  291. {
  292. // An error occurred.
  293. return;
  294. }
  295. if (m_sound)
  296. {
  297. ma_sound_uninit(m_sound.get());
  298. m_sound.reset();
  299. }
  300. m_sound = AZStd::make_unique<ma_sound>();
  301. const ma_uint32 flags = MA_SOUND_FLAG_DECODE;
  302. result = ma_sound_init_from_file(engine, m_soundName.c_str(), flags, nullptr, nullptr, m_sound.get());
  303. if (result != MA_SUCCESS)
  304. {
  305. // An error occurred.
  306. return;
  307. }
  308. ma_sound_set_volume(m_sound.get(), (m_config.m_volume / 100.f));
  309. ma_sound_set_looping(m_sound.get(), m_config.m_loop);
  310. ma_sound_set_spatialization_enabled(m_sound.get(), m_config.m_enableSpatialization);
  311. if (m_config.m_enableSpatialization)
  312. {
  313. ma_sound_set_min_distance(m_sound.get(), m_config.m_minimumDistance);
  314. ma_sound_set_max_distance(m_sound.get(), m_config.m_maximumDistance);
  315. ma_sound_set_attenuation_model(m_sound.get(), static_cast<ma_attenuation_model>(m_config.m_attenuationModel));
  316. ma_sound_set_directional_attenuation_factor(m_sound.get(), m_config.m_directionalAttenuationFactor);
  317. // Set the forward direction for the sound source
  318. if (!m_config.m_fixedDirection)
  319. {
  320. AZ::Transform worldTm = AZ::Transform::CreateIdentity();
  321. AZ::TransformBus::EventResult(
  322. worldTm, m_entityComponentIdPair.GetEntityId(), &AZ::TransformBus::Events::GetWorldTM);
  323. ma_sound_set_direction(
  324. m_sound.get(), worldTm.GetBasisY().GetX(), worldTm.GetBasisY().GetY(), worldTm.GetBasisY().GetZ());
  325. }
  326. else
  327. {
  328. ma_sound_set_direction(
  329. m_sound.get(), m_config.m_direction.GetX(), m_config.m_direction.GetY(), m_config.m_direction.GetZ());
  330. }
  331. ma_sound_set_cone(
  332. m_sound.get(), m_config.m_innerAngleInRadians, m_config.m_outerAngleInRadians, (m_config.m_outerVolume / 100.f));
  333. }
  334. if (m_config.m_autoFollowEntity)
  335. {
  336. m_entityMovedHandler.Disconnect();
  337. AZ::TransformBus::Event(
  338. m_entityComponentIdPair.GetEntityId(),
  339. &AZ::TransformBus::Events::BindTransformChangedEventHandler,
  340. m_entityMovedHandler);
  341. AZ::Transform worldTm = AZ::Transform::CreateIdentity();
  342. AZ::TransformBus::EventResult(worldTm, m_entityComponentIdPair.GetEntityId(), &AZ::TransformBus::Events::GetWorldTM);
  343. OnWorldTransformChanged(worldTm);
  344. }
  345. else
  346. {
  347. m_entityMovedHandler.Disconnect();
  348. }
  349. // Automatically play after the sound loads if requested
  350. // This will play automatically in Editor and Game
  351. if (m_config.m_autoplayOnActivate)
  352. {
  353. Play();
  354. }
  355. }
  356. }
  357. }
  358. void MiniAudioPlaybackComponentController::UnloadSound()
  359. {
  360. if (ma_engine* engine = MiniAudioInterface::Get()->GetSoundEngine())
  361. {
  362. if (m_sound)
  363. {
  364. ma_sound_stop(m_sound.get());
  365. ma_sound_uninit(m_sound.get());
  366. m_sound.reset();
  367. }
  368. if (m_soundName.empty() == false)
  369. {
  370. ma_resource_manager_unregister_data(ma_engine_get_resource_manager(engine), m_soundName.c_str());
  371. m_soundName.clear();
  372. }
  373. }
  374. }
  375. } // namespace MiniAudio