1#include "AudioSystem.h"
2#include "AudioListenerComponent.h"
5#include "Systems/Core/TransformSystem.h"
6#include "Services/Time.h"
8#include "Foundation/Logging/Logger.h"
28 if(initialized_com) CoUninitialize();
32 CoInitializeEx(
nullptr, COINIT_MULTITHREADED);
33 initialized_com =
true;
35 HRESULT hr = XAudio2Create(&audioDevice, 0, XAUDIO2_DEFAULT_PROCESSOR);
38 LOG_ERROR(logger,
"Failed to initialize audio device.");
43 XAUDIO2_DEBUG_CONFIGURATION debugConfiguration = {};
45 debugConfiguration.TraceMask = XAUDIO2_LOG_ERRORS;
47 audioDevice->SetDebugConfiguration(&debugConfiguration);
49 hr = audioDevice->CreateMasteringVoice(&masteringVoice);
52 LOG_ERROR(logger,
"Could not create mastering voice.");
56 DWORD channelMask = 0;
57 masteringVoice->GetChannelMask(&channelMask);
59 hr = X3DAudioInitialize(channelMask, X3DAUDIO_SPEED_OF_SOUND, x3DInstance);
62 LOG_ERROR(logger,
"Failed to initialize X3D Audio.");
69 CComPtr<IXAudio2> audioDevice;
70 IXAudio2MasteringVoice * masteringVoice;
72 X3DAUDIO_HANDLE x3DInstance;
74 bool initialized =
false;
75 bool initialized_com =
false;
80Cogs::Core::AudioSystem::AudioSystem(Memory::Allocator * allocator, SizeType size) :
81 ComponentSystemWithDataPool(allocator, size),
82 subSystem(
std::make_unique<AudioDevice>())
85Cogs::Core::AudioSystem::~AudioSystem()
93 subSystem->initialize();
98 X3DAUDIO_VECTOR toX3D(
const glm::vec3 & v)
100 return *
reinterpret_cast<const X3DAUDIO_VECTOR *
>(&v);
106 if (!subSystem->initialized)
return;
108 const double time = context->
time->getAnimationTime();
109 float elapsed =
static_cast<float>(time - prevTime);
112 X3DAUDIO_LISTENER listener = {};
113 X3DAUDIO_EMITTER emitter = {};
115 XAUDIO2_VOICE_DETAILS voiceDetails = {};
116 subSystem->masteringVoice->GetVoiceDetails(&voiceDetails);
120 if(
auto listenerComponent = listenerHandle.resolveComponent<
AudioListenerComponent>(); listenerComponent) {
121 auto listenerTransformComponent = listenerComponent->getComponent<
TransformComponent>();
122 auto & listenerTransform = context->transformSystem->getLocalToWorld(listenerTransformComponent);
125 auto front = glm::normalize(glm::vec3(listenerTransform * glm::vec4(0, 0, 1, 0)));
126 auto up = glm::normalize(glm::vec3(listenerTransform * glm::vec4(0, 1, 0, 0)));
128 auto listenerPosition = glm::vec3(listenerTransform * glm::vec4(0, 0, 0, 1));
130 listener.Position = toX3D(listenerPosition);
131 listener.OrientFront = toX3D(front);
132 listener.OrientTop = toX3D(up);
134 auto listenerDiff = (listenerPosition - listenerComponent->previousPosition) / elapsed;
135 listener.Velocity = toX3D(listenerDiff);
136 listenerComponent->previousPosition = listenerPosition;
138 volume = listenerComponent->volume;
141 for (
auto & c : pool) {
142 auto & data = getData(&c);
144 if (data.state == AudioState::None && c.enabled && c.sound) {
145 auto hr = subSystem->audioDevice->CreateSourceVoice(&data.sourceVoice, &c.sound->format.Format);
151 hr = subSystem->audioDevice->CreateSubmixVoice(&data.submixVoice, 2, voiceDetails.InputSampleRate, XAUDIO2_VOICE_USEFILTER, 0);
158 std::vector<XAUDIO2_SEND_DESCRIPTOR> descs = {
159 { 0, data.submixVoice }
162 XAUDIO2_VOICE_SENDS sends;
163 sends.SendCount =
static_cast<uint32_t
>(descs.size());
164 sends.pSends = descs.data();
166 hr = data.sourceVoice->SetOutputVoices(&sends);
172 data.state = AudioState::Stopped;
175 if (c.enabled && data.state == AudioState::Stopped) {
176 data.state = AudioState::StartPlaying;
177 }
else if (!c.enabled && data.state == AudioState::Playing) {
178 data.state = AudioState::StopPlaying;
181 if (data.state == AudioState::StopPlaying) {
182 data.sourceVoice->Stop();
185 data.sourceVoice->DestroyVoice();
186 data.submixVoice->DestroyVoice();
187 data.state = AudioState::None;
189 data.state = AudioState::Stopped;
193 if (c.sound && data.state == AudioState::StartPlaying) {
194 XAUDIO2_BUFFER buffer = {};
195 buffer.pAudioData = (
unsigned char *)c.sound->data.data();
196 buffer.AudioBytes =
static_cast<uint32_t
>(c.sound->data.size());
197 buffer.Flags = XAUDIO2_END_OF_STREAM;
198 buffer.LoopCount = c.loop ? XAUDIO2_LOOP_INFINITE : 0;
200 data.sourceVoice->Stop();
201 data.sourceVoice->FlushSourceBuffers();
203 auto hr = data.sourceVoice->SubmitSourceBuffer(&buffer);
209 hr = data.sourceVoice->Start();
215 data.state = AudioState::Playing;
218 if (data.state == AudioState::Playing) {
219 emitter.ChannelCount = 1;
221 std::vector<float> azimuths(emitter.ChannelCount);
222 emitter.pChannelAzimuths = azimuths.data();
225 auto & transform = context->transformSystem->getLocalToWorld(transformComponent);
227 auto emitterPosition = glm::vec3(transform * glm::vec4(0, 0, 0, 1));
229 auto emitterDiff = (emitterPosition - data.previousPosition) / elapsed;
230 emitter.Velocity = toX3D(emitterDiff);
231 data.previousPosition = emitterPosition;
233 auto front = glm::normalize(glm::vec3(transform * glm::vec4(0, 0, 1, 0)));
234 auto up = glm::normalize(glm::vec3(transform * glm::vec4(0, 1, 0, 0)));
236 emitter.Position = toX3D(emitterPosition);
237 emitter.OrientFront = toX3D(front);
238 emitter.OrientTop = toX3D(up);
240 emitter.CurveDistanceScaler = c.radius;
241 emitter.DopplerScaler = c.dopplerScale;
243 std::vector<float> coefficients(2 * voiceDetails.InputChannels);
245 X3DAUDIO_DSP_SETTINGS dspSettings = {};
246 dspSettings.SrcChannelCount = emitter.ChannelCount;
247 dspSettings.DstChannelCount = voiceDetails.InputChannels;
248 dspSettings.pMatrixCoefficients = coefficients.data();
250 X3DAudioCalculate(subSystem->x3DInstance,
253 X3DAUDIO_CALCULATE_MATRIX | X3DAUDIO_CALCULATE_DOPPLER | X3DAUDIO_CALCULATE_REVERB | X3DAUDIO_CALCULATE_LPF_DIRECT,
256 auto hr = data.sourceVoice->SetOutputMatrix(data.submixVoice,
258 voiceDetails.InputChannels,
259 dspSettings.pMatrixCoefficients);
265 hr = data.sourceVoice->SetFrequencyRatio(dspSettings.DopplerFactor * c.frequencyModulation);
271 XAUDIO2_FILTER_PARAMETERS FilterParameters = {
273 2.0f * sinf(X3DAUDIO_PI / 6.0f * dspSettings.LPFDirectCoefficient),
277 data.submixVoice->SetFilterParameters(&FilterParameters);
279 data.sourceVoice->SetVolume(volume * c.volume);
293 auto & data = getData(component);
295 if (data.state == AudioState::Playing || data.state == AudioState::StopPlaying) {
296 data.sourceVoice->Stop();
297 data.sourceVoice->DestroyVoice();
298 data.submixVoice->DestroyVoice();
306 listenerHandle = handle;
void initialize(Context *context) override
Initialize the system.
void destroyComponent(ComponentHandle handle) override
void cleanup(Context *context) override
Provided for custom cleanup logic in derived systems.
virtual void initialize(Context *context)
Initialize the system.
virtual void destroyComponent(ComponentHandle)
Destroy the component held by the given handle.
void update()
Updates the system state to that of the current frame.
A Context instance contains all the services, systems and runtime components needed to use Cogs.
std::unique_ptr< class Time > time
Time service instance.
Log implementation class.
constexpr Log getLogger(const char(&name)[LEN]) noexcept
Contains all Cogs related functionality.
Handle to a Component instance.
ComponentType * resolveComponent() const