#include #include #include #include namespace XCEngine { namespace Components { AudioSourceComponent::AudioSourceComponent() : m_outputBuffer(BufferSize * 2, 0.0f) { } AudioSourceComponent::~AudioSourceComponent() { if (m_playState == Audio::PlayState::Playing) { Audio::AudioSystem::Get().UnregisterSource(this); } } void AudioSourceComponent::Play() { if (!m_clip || !m_clip->IsValid()) { return; } if (m_playState == Audio::PlayState::Paused) { m_playState = Audio::PlayState::Playing; return; } m_samplePosition = 0; m_lastingTime = 0.0; m_playState = Audio::PlayState::Playing; Audio::AudioSystem::Get().RegisterSource(this); } void AudioSourceComponent::Pause() { if (m_playState == Audio::PlayState::Playing) { m_playState = Audio::PlayState::Paused; Audio::AudioSystem::Get().UnregisterSource(this); } } void AudioSourceComponent::Stop(Audio::StopMode mode) { if (m_playState != Audio::PlayState::Stopped) { m_playState = Audio::PlayState::Stopped; m_samplePosition = 0; Audio::AudioSystem::Get().UnregisterSource(this); } } void AudioSourceComponent::SetClip(Resources::AudioClip* clip) { m_clip = clip; m_isDecoded = false; if (clip && clip->IsValid()) { m_decodedData.resize(clip->GetAudioData().Size() / 2); } } void AudioSourceComponent::SetVolume(float volume) { m_volume = std::max(0.0f, std::min(1.0f, volume)); } void AudioSourceComponent::SetPitch(float pitch) { m_pitch = std::max(0.0f, std::min(3.0f, pitch)); } void AudioSourceComponent::SetPan(float pan) { m_pan = std::max(-1.0f, std::min(1.0f, pan)); } void AudioSourceComponent::SetLooping(bool loop) { m_isLooping = loop; } void AudioSourceComponent::SetSpatialize(bool spatialize) { m_spatialize = spatialize; } void AudioSourceComponent::Set3DParams(const Audio::Audio3DParams& params) { m_3DParams = params; } void AudioSourceComponent::SetDopplerLevel(float level) { m_3DParams.dopplerLevel = level; } void AudioSourceComponent::SetSpread(float spread) { m_3DParams.spread = std::max(0.0f, std::min(1.0f, spread)); } void AudioSourceComponent::SetReverbZoneMix(float mix) { m_3DParams.reverbZoneMix = std::max(0.0f, std::min(1.0f, mix)); } void AudioSourceComponent::SetOutputMixer(Audio::AudioMixer* mixer) { m_outputMixer = mixer; } void AudioSourceComponent::SetTime(float seconds) { if (!m_clip || !m_clip->IsValid()) { return; } Audio::uint32 sampleRate = m_clip->GetSampleRate(); Audio::uint32 channels = m_clip->GetChannels(); Audio::uint64 sampleOffset = static_cast(seconds * sampleRate * channels); m_samplePosition = sampleOffset; m_lastingTime = seconds; } float AudioSourceComponent::GetTime() const { return static_cast(m_lastingTime); } float AudioSourceComponent::GetDuration() const { if (!m_clip || !m_clip->IsValid()) { return 0.0f; } return m_clip->GetDuration(); } void AudioSourceComponent::StartEnergyDetect() { m_isEnergyDetecting = true; m_energyHistory.clear(); } void AudioSourceComponent::StopEnergyDetect() { m_isEnergyDetecting = false; } void AudioSourceComponent::Update(float deltaTime) { if (m_playState != Audio::PlayState::Playing || !m_clip) { return; } m_lastingTime += deltaTime * m_pitch; Audio::uint32 channels = m_clip->GetChannels(); Audio::uint32 sampleRate = m_clip->GetSampleRate(); Audio::uint64 samplesPerSecond = sampleRate * channels; Audio::uint64 samplesToAdvance = static_cast(deltaTime * m_pitch * samplesPerSecond); m_samplePosition += samplesToAdvance; Audio::uint64 totalSamples = static_cast(m_clip->GetAudioData().Size()) / (m_clip->GetBitsPerSample() / 8); if (m_samplePosition >= totalSamples) { if (m_isLooping) { m_samplePosition = m_samplePosition % totalSamples; } else { Stop(); } } } void AudioSourceComponent::OnEnable() { if (m_playState == Audio::PlayState::Playing) { Audio::AudioSystem::Get().RegisterSource(this); } } void AudioSourceComponent::OnDisable() { if (m_playState == Audio::PlayState::Playing) { Audio::AudioSystem::Get().UnregisterSource(this); } } void AudioSourceComponent::OnDestroy() { Stop(); } void AudioSourceComponent::ProcessAudio(float* buffer, Audio::uint32 sampleCount, Audio::uint32 channels, const Math::Vector3& listenerPosition, const Math::Quaternion& listenerRotation) { if (m_playState != Audio::PlayState::Playing || !m_clip) { return; } if (channels == 0 || sampleCount == 0) { return; } float volume = m_volume; if (m_spatialize) { Apply3DAttenuation(listenerPosition); volume *= m_volume; } for (Audio::uint32 i = 0; i < sampleCount && i < BufferSize * 2; ++i) { buffer[i] += m_outputBuffer[i] * volume; } if (m_isEnergyDetecting) { UpdateEnergy(buffer, sampleCount); } } void AudioSourceComponent::Apply3DAttenuation(const Math::Vector3& listenerPosition) { if (!m_gameObject) { return; } Math::Vector3 sourcePosition = transform().GetPosition(); Math::Vector3 direction = sourcePosition - listenerPosition; float distance = direction.Magnitude(); if (distance > m_3DParams.maxDistance) { m_volume = 0.0f; return; } float normalizedDistance = distance / m_3DParams.maxDistance; normalizedDistance = std::max(0.0f, std::min(1.0f, normalizedDistance)); float attenuation = 1.0f - normalizedDistance; attenuation = std::pow(attenuation, 2.0f); m_volume *= attenuation; } void AudioSourceComponent::UpdateEnergy(const float* buffer, Audio::uint32 sampleCount) { if (!buffer || sampleCount == 0) { return; } float sumSquares = 0.0f; for (Audio::uint32 i = 0; i < sampleCount; ++i) { sumSquares += buffer[i] * buffer[i]; } m_energy = std::sqrt(sumSquares / static_cast(sampleCount)); m_energyHistory.push_back(m_energy); if (m_energyHistory.size() > 10) { m_energyHistory.pop_front(); } } } // namespace Components } // namespace XCEngine