// Copyright (C) 2022 Jérôme "Lynix" Leclercq (lynix680@gmail.com) // This file is part of the "Nazara Engine - Audio module" // For conditions of distribution and use, see copyright notice in Config.hpp #include #include #include #include #include #include #include #include namespace Nz { OpenALSource::~OpenALSource() { GetDevice().MakeContextCurrent(); m_library.alDeleteSources(1, &m_sourceId); } void OpenALSource::EnableLooping(bool loop) { GetDevice().MakeContextCurrent(); m_library.alSourcei(m_sourceId, AL_LOOPING, loop); } void OpenALSource::EnableSpatialization(bool spatialization) { GetDevice().MakeContextCurrent(); m_library.alSourcei(m_sourceId, AL_SOURCE_RELATIVE, !spatialization); } float OpenALSource::GetAttenuation() const { GetDevice().MakeContextCurrent(); ALfloat attenuation; m_library.alGetSourcefv(m_sourceId, AL_ROLLOFF_FACTOR, &attenuation); return attenuation; } float OpenALSource::GetMinDistance() const { GetDevice().MakeContextCurrent(); ALfloat minDistance; m_library.alGetSourcefv(m_sourceId, AL_REFERENCE_DISTANCE, &minDistance); return minDistance; } float OpenALSource::GetPitch() const { GetDevice().MakeContextCurrent(); ALfloat pitch; m_library.alGetSourcefv(m_sourceId, AL_PITCH, &pitch); return pitch; } Time OpenALSource::GetPlayingOffset() const { GetDevice().MakeContextCurrent(); #ifdef AL_SOFT_source_latency if (GetDevice().IsExtensionSupported(OpenALExtension::SourceLatency)) { // alGetSourcedvSOFT has extra precision thanks to double ALdouble playingOffset; m_library.alGetSourcedvSOFT(m_sourceId, AL_SEC_OFFSET, &playingOffset); return Time::Seconds(playingOffset); } else #endif { ALfloat playingOffset; m_library.alGetSourcefv(m_sourceId, AL_SEC_OFFSET, &playingOffset); return Time::Seconds(playingOffset); } } Vector3f OpenALSource::GetPosition() const { GetDevice().MakeContextCurrent(); Vector3f position; m_library.alGetSourcefv(m_sourceId, AL_POSITION, &position.x); return position; } UInt32 OpenALSource::GetSampleOffset() const { GetDevice().MakeContextCurrent(); ALint samples = 0; m_library.alGetSourcei(m_sourceId, AL_SAMPLE_OFFSET, &samples); return SafeCast(samples); } auto OpenALSource::GetSampleOffsetAndLatency() const -> OffsetWithLatency { OffsetWithLatency offsetWithLatency; #ifdef AL_SOFT_source_latency if (GetDevice().IsExtensionSupported(OpenALExtension::SourceLatency)) { GetDevice().MakeContextCurrent(); std::array values; m_library.alGetSourcei64vSOFT(m_sourceId, AL_SAMPLE_OFFSET_LATENCY_SOFT, values.data()); offsetWithLatency.sampleOffset = ((values[0] & 0xFFFFFFFF00000000) >> 32) * 1'000; offsetWithLatency.sourceLatency = Time::Nanoseconds(values[1] / 1'000); } else #endif { offsetWithLatency.sampleOffset = GetSampleOffset() * 1'000; offsetWithLatency.sourceLatency = Time::Zero(); } return offsetWithLatency; } Vector3f OpenALSource::GetVelocity() const { GetDevice().MakeContextCurrent(); Vector3f velocity; m_library.alGetSourcefv(m_sourceId, AL_VELOCITY, &velocity.x); return velocity; } SoundStatus OpenALSource::GetStatus() const { GetDevice().MakeContextCurrent(); ALint state; m_library.alGetSourcei(m_sourceId, AL_SOURCE_STATE, &state); switch (state) { case AL_INITIAL: case AL_STOPPED: return SoundStatus::Stopped; case AL_PAUSED: return SoundStatus::Paused; case AL_PLAYING: return SoundStatus::Playing; default: NazaraInternalError("Source state unrecognized"); } return SoundStatus::Stopped; } float OpenALSource::GetVolume() const { GetDevice().MakeContextCurrent(); ALfloat volume; m_library.alGetSourcefv(m_sourceId, AL_GAIN, &volume); return volume; } bool OpenALSource::IsLooping() const { GetDevice().MakeContextCurrent(); ALint looping; m_library.alGetSourcei(m_sourceId, AL_LOOPING, &looping); return looping == AL_TRUE; } bool OpenALSource::IsSpatializationEnabled() const { GetDevice().MakeContextCurrent(); ALint relative; m_library.alGetSourcei(m_sourceId, AL_SOURCE_RELATIVE, &relative); return relative == AL_FALSE; } void OpenALSource::QueueBuffer(std::shared_ptr audioBuffer) { NazaraAssert(audioBuffer, "invalid buffer"); NazaraAssert(audioBuffer->IsCompatibleWith(*GetAudioDevice()), "incompatible buffer"); std::shared_ptr newBuffer = std::static_pointer_cast(std::move(audioBuffer)); GetDevice().MakeContextCurrent(); ALuint bufferId = newBuffer->GetBufferId(); m_library.alSourceQueueBuffers(m_sourceId, 1, &bufferId); m_queuedBuffers.emplace_back(std::move(newBuffer)); } void OpenALSource::Pause() { GetDevice().MakeContextCurrent(); m_library.alSourcePause(m_sourceId); } void OpenALSource::Play() { GetDevice().MakeContextCurrent(); m_library.alSourcePlay(m_sourceId); } void OpenALSource::SetAttenuation(float attenuation) { GetDevice().MakeContextCurrent(); m_library.alSourcef(m_sourceId, AL_ROLLOFF_FACTOR, attenuation); } void OpenALSource::SetBuffer(std::shared_ptr audioBuffer) { NazaraAssert(audioBuffer->IsCompatibleWith(*GetAudioDevice()), "incompatible buffer"); std::shared_ptr newBuffer = std::static_pointer_cast(std::move(audioBuffer)); GetDevice().MakeContextCurrent(); if (newBuffer) m_library.alSourcei(m_sourceId, AL_BUFFER, newBuffer->GetBufferId()); else m_library.alSourcei(m_sourceId, AL_BUFFER, AL_NONE); m_currentBuffer = std::move(newBuffer); } void OpenALSource::SetMinDistance(float minDistance) { GetDevice().MakeContextCurrent(); m_library.alSourcef(m_sourceId, AL_REFERENCE_DISTANCE, minDistance); } void OpenALSource::SetPitch(float pitch) { GetDevice().MakeContextCurrent(); m_library.alSourcef(m_sourceId, AL_PITCH, pitch); } void OpenALSource::SetPlayingOffset(Time offset) { GetDevice().MakeContextCurrent(); #ifdef AL_SOFT_source_latency if (GetDevice().IsExtensionSupported(OpenALExtension::SourceLatency)) // alGetSourcedvSOFT has extra precision thanks to double m_library.alSourcedSOFT(m_sourceId, AL_SEC_OFFSET, offset.AsSeconds()); else #endif m_library.alSourcef(m_sourceId, AL_SEC_OFFSET, offset.AsSeconds()); } void OpenALSource::SetPosition(const Vector3f& position) { GetDevice().MakeContextCurrent(); m_library.alSource3f(m_sourceId, AL_POSITION, position.x, position.y, position.z); } void OpenALSource::SetSampleOffset(UInt32 offset) { GetDevice().MakeContextCurrent(); m_library.alSourcei(m_sourceId, AL_SAMPLE_OFFSET, offset); } void OpenALSource::SetVelocity(const Vector3f& velocity) { GetDevice().MakeContextCurrent(); m_library.alSource3f(m_sourceId, AL_VELOCITY, velocity.x, velocity.y, velocity.z); } void OpenALSource::SetVolume(float volume) { GetDevice().MakeContextCurrent(); m_library.alSourcef(m_sourceId, AL_GAIN, volume); } void OpenALSource::Stop() { GetDevice().MakeContextCurrent(); m_library.alSourceStop(m_sourceId); } std::shared_ptr OpenALSource::TryUnqueueProcessedBuffer() { GetDevice().MakeContextCurrent(); ALint processedCount = 0; m_library.alGetSourcei(m_sourceId, AL_BUFFERS_PROCESSED, &processedCount); if (processedCount == 0) return {}; ALuint bufferId; m_library.alSourceUnqueueBuffers(m_sourceId, 1, &bufferId); auto it = std::find_if(m_queuedBuffers.begin(), m_queuedBuffers.end(), [=](const std::shared_ptr& alBuffer) { return alBuffer->GetBufferId() == bufferId; }); assert(it != m_queuedBuffers.end()); std::shared_ptr buffer = *it; m_queuedBuffers.erase(it); return buffer; } void OpenALSource::UnqueueAllBuffers() { GetDevice().MakeContextCurrent(); ALint queuedBufferCount = 0; m_library.alGetSourcei(m_sourceId, AL_BUFFERS_QUEUED, &queuedBufferCount); StackArray buffers = NazaraStackArrayNoInit(ALuint, queuedBufferCount); m_library.alSourceUnqueueBuffers(m_sourceId, queuedBufferCount, buffers.data()); m_queuedBuffers.clear(); } OpenALDevice& OpenALSource::GetDevice() { return SafeCast(*GetAudioDevice()); } const OpenALDevice& OpenALSource::GetDevice() const { return SafeCast(*GetAudioDevice()); } }