MiniAudioPlaybackComponentController.cpp 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279
  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 "MiniAudioIncludes.h"
  13. namespace MiniAudio
  14. {
  15. MiniAudioPlaybackComponentController::MiniAudioPlaybackComponentController()
  16. {
  17. }
  18. // placement of this destructor is intentional. It forces unique_ptr<ma_sound> to declare its destructor here
  19. // instead of in the header before inclusion of the giant MiniAudioIncludes.h file
  20. MiniAudioPlaybackComponentController::~MiniAudioPlaybackComponentController()
  21. {
  22. }
  23. MiniAudioPlaybackComponentController::MiniAudioPlaybackComponentController(
  24. 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. MiniAudioPlaybackRequestBus::Handler::BusConnect(m_entityComponentIdPair.GetEntityId());
  46. OnConfigurationUpdated();
  47. }
  48. void MiniAudioPlaybackComponentController::SetConfiguration(const MiniAudioPlaybackComponentConfig& config)
  49. {
  50. m_config = config;
  51. OnConfigurationUpdated();
  52. }
  53. const MiniAudioPlaybackComponentConfig& MiniAudioPlaybackComponentController::GetConfiguration() const
  54. {
  55. return m_config;
  56. }
  57. void MiniAudioPlaybackComponentController::Deactivate()
  58. {
  59. m_entityMovedHandler.Disconnect();
  60. UnloadSound();
  61. m_config.m_sound.Release();
  62. MiniAudioPlaybackRequestBus::Handler::BusDisconnect();
  63. AZ::Data::AssetBus::MultiHandler::BusDisconnect();
  64. }
  65. void MiniAudioPlaybackComponentController::Play()
  66. {
  67. if (m_sound)
  68. {
  69. ma_sound_seek_to_pcm_frame(m_sound.get(), 0);
  70. ma_sound_start(m_sound.get());
  71. }
  72. }
  73. void MiniAudioPlaybackComponentController::Stop()
  74. {
  75. if (m_sound)
  76. {
  77. ma_sound_stop(m_sound.get());
  78. }
  79. }
  80. void MiniAudioPlaybackComponentController::SetVolume(float volume)
  81. {
  82. if (m_sound)
  83. {
  84. ma_sound_set_volume(m_sound.get(), volume);
  85. }
  86. }
  87. void MiniAudioPlaybackComponentController::SetLooping(bool loop)
  88. {
  89. m_config.m_loop = loop;
  90. if (m_sound)
  91. {
  92. ma_sound_set_looping(m_sound.get(), loop);
  93. }
  94. }
  95. bool MiniAudioPlaybackComponentController::IsLooping() const
  96. {
  97. if (m_sound)
  98. {
  99. return ma_sound_is_looping(m_sound.get());
  100. }
  101. return false;
  102. }
  103. AZ::Data::Asset<SoundAsset> MiniAudioPlaybackComponentController::GetSoundAsset() const
  104. {
  105. return m_config.m_sound;
  106. }
  107. void MiniAudioPlaybackComponentController::SetSoundAsset(AZ::Data::Asset<SoundAsset> soundAsset)
  108. {
  109. if (m_config.m_sound.GetId() != soundAsset.GetId())
  110. {
  111. m_config.m_sound = soundAsset;
  112. OnConfigurationUpdated();
  113. }
  114. }
  115. SoundAssetRef MiniAudioPlaybackComponentController::GetSoundAssetRef() const
  116. {
  117. SoundAssetRef ref;
  118. ref.SetAsset(GetSoundAsset());
  119. return ref;
  120. }
  121. void MiniAudioPlaybackComponentController::SetSoundAssetRef(const SoundAssetRef& soundAssetRef)
  122. {
  123. SetSoundAsset(soundAssetRef.GetAsset());
  124. }
  125. void MiniAudioPlaybackComponentController::OnAssetReady(AZ::Data::Asset<AZ::Data::AssetData> asset)
  126. {
  127. AZ::Data::AssetBus::MultiHandler::BusDisconnect(asset.GetId());
  128. // Re-assign the sound before attempting to load it if it was
  129. // released and the asset is now ready.
  130. // This can happen in the Editor when returning from game mode
  131. if (!m_config.m_sound.IsReady())
  132. {
  133. m_config.m_sound = asset;
  134. }
  135. LoadSound();
  136. }
  137. void MiniAudioPlaybackComponentController::OnWorldTransformChanged(const AZ::Transform& world)
  138. {
  139. if (m_sound)
  140. {
  141. ma_sound_set_position(m_sound.get(), world.GetTranslation().GetX(), world.GetTranslation().GetY(), world.GetTranslation().GetZ());
  142. }
  143. }
  144. void MiniAudioPlaybackComponentController::OnConfigurationUpdated()
  145. {
  146. if (m_config.m_sound.IsReady() == false)
  147. {
  148. AZ::Data::AssetBus::MultiHandler::BusConnect(m_config.m_sound.GetId());
  149. m_config.m_sound.QueueLoad();
  150. }
  151. else
  152. {
  153. LoadSound();
  154. }
  155. }
  156. void MiniAudioPlaybackComponentController::LoadSound()
  157. {
  158. UnloadSound();
  159. if (ma_engine* engine = MiniAudioInterface::Get()->GetSoundEngine())
  160. {
  161. if (GetConfiguration().m_sound.IsReady())
  162. {
  163. m_soundName = GetConfiguration().m_sound.GetHint();
  164. const auto& assetBuffer = GetConfiguration().m_sound->m_data;
  165. if (assetBuffer.empty())
  166. {
  167. return;
  168. }
  169. ma_result result = ma_resource_manager_register_encoded_data(ma_engine_get_resource_manager(engine),
  170. m_soundName.c_str(), assetBuffer.data(), assetBuffer.size());
  171. if (result != MA_SUCCESS)
  172. {
  173. // An error occurred.
  174. return;
  175. }
  176. if (m_sound)
  177. {
  178. ma_sound_uninit(m_sound.get());
  179. m_sound.reset();
  180. }
  181. m_sound = AZStd::make_unique<ma_sound>();
  182. const ma_uint32 flags = MA_SOUND_FLAG_DECODE;
  183. result = ma_sound_init_from_file(engine, m_soundName.c_str(), flags, nullptr, nullptr, m_sound.get());
  184. if (result != MA_SUCCESS)
  185. {
  186. // An error occurred.
  187. return;
  188. }
  189. ma_sound_set_volume(m_sound.get(), m_config.m_volume);
  190. ma_sound_set_looping(m_sound.get(), m_config.m_loop);
  191. ma_sound_set_spatialization_enabled(m_sound.get(), m_config.m_enableSpatialization);
  192. if (m_config.m_enableSpatialization)
  193. {
  194. ma_sound_set_min_distance(m_sound.get(), m_config.m_minimumDistance);
  195. ma_sound_set_max_distance(m_sound.get(), m_config.m_maximumDistance);
  196. ma_sound_set_attenuation_model(m_sound.get(), static_cast<ma_attenuation_model>(m_config.m_attenuationModel));
  197. }
  198. if (m_config.m_autoFollowEntity)
  199. {
  200. m_entityMovedHandler.Disconnect();
  201. AZ::TransformBus::Event(m_entityComponentIdPair.GetEntityId(), &AZ::TransformBus::Events::BindTransformChangedEventHandler, m_entityMovedHandler);
  202. AZ::Transform worldTm = AZ::Transform::CreateIdentity();
  203. AZ::TransformBus::EventResult(worldTm, m_entityComponentIdPair.GetEntityId(), &AZ::TransformBus::Events::GetWorldTM);
  204. OnWorldTransformChanged(worldTm);
  205. }
  206. else
  207. {
  208. m_entityMovedHandler.Disconnect();
  209. }
  210. // Automatically play after the sound loads if requested
  211. // This will play automatically in Editor and Game
  212. if (m_config.m_autoplayOnActivate)
  213. {
  214. Play();
  215. }
  216. }
  217. }
  218. }
  219. void MiniAudioPlaybackComponentController::UnloadSound()
  220. {
  221. if (ma_engine* engine = MiniAudioInterface::Get()->GetSoundEngine())
  222. {
  223. if (m_sound)
  224. {
  225. ma_sound_stop(m_sound.get());
  226. ma_sound_uninit(m_sound.get());
  227. m_sound.reset();
  228. }
  229. if (m_soundName.empty() == false)
  230. {
  231. ma_resource_manager_unregister_data(ma_engine_get_resource_manager(engine), m_soundName.c_str());
  232. m_soundName.clear();
  233. }
  234. }
  235. }
  236. } // namespace MiniAudio