#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()) { DecodeAudioData(); } } 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::DecodeAudioData() { if (!m_clip || !m_clip->IsValid()) { return; } if (m_isDecoded) { return; } const auto& audioData = m_clip->GetAudioData(); if (audioData.Empty()) { return; } Audio::uint32 channels = m_clip->GetChannels(); Audio::uint32 bitsPerSample = m_clip->GetBitsPerSample(); uint32_t bytesPerSample = bitsPerSample / 8; uint32_t totalSamples = static_cast(audioData.Size()) / bytesPerSample; m_decodedData.resize(totalSamples); const uint8_t* rawData = audioData.Data(); if (bitsPerSample == 16) { const int16_t* samples16 = reinterpret_cast(rawData); for (uint32_t i = 0; i < totalSamples; ++i) { m_decodedData[i] = samples16[i] / 32768.0f; } } else if (bitsPerSample == 8) { for (uint32_t i = 0; i < totalSamples; ++i) { m_decodedData[i] = (rawData[i] - 128) / 128.0f; } } else if (bitsPerSample == 24) { for (uint32_t i = 0; i < totalSamples; ++i) { int32_t sample = (rawData[i * 3] | (rawData[i * 3 + 1] << 8) | (rawData[i * 3 + 2] << 16)); if (sample & 0x800000) { sample |= 0xFF000000; } m_decodedData[i] = sample / 8388608.0f; } } else if (bitsPerSample == 32) { const int32_t* samples32 = reinterpret_cast(rawData); for (uint32_t i = 0; i < totalSamples; ++i) { m_decodedData[i] = samples32[i] / 2147483648.0f; } } m_isDecoded = true; } 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 || !m_isDecoded) { return; } if (channels == 0 || sampleCount == 0) { return; } if (m_decodedData.empty()) { return; } float volume = m_volume; if (m_spatialize) { Apply3DAttenuation(listenerPosition); volume *= m_volume; } Audio::uint32 clipChannels = m_clip->GetChannels(); Audio::uint64 totalSamples = static_cast(m_decodedData.size()); Audio::uint64 samplesPerFrame = sampleCount * channels; for (Audio::uint32 i = 0; i < sampleCount; ++i) { for (Audio::uint32 ch = 0; ch < channels; ++ch) { Audio::uint64 outputIndex = m_samplePosition + i * channels + ch; if (outputIndex >= totalSamples) { if (m_isLooping && totalSamples > 0) { outputIndex = outputIndex % totalSamples; } else { buffer[i * channels + ch] += 0.0f; continue; } } Audio::uint64 decodedChannel = (ch < clipChannels) ? ch : (clipChannels - 1); Audio::uint64 decodedIndex = (outputIndex / channels) * clipChannels + decodedChannel; float sample = m_decodedData[decodedIndex]; buffer[i * channels + ch] += sample * volume; } } m_samplePosition += samplesPerFrame; if (m_samplePosition >= totalSamples) { if (m_isLooping) { m_samplePosition = m_samplePosition % totalSamples; } else { Stop(); } } if (m_isEnergyDetecting) { UpdateEnergy(buffer, sampleCount * channels); } } 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