361 lines
8.5 KiB
C++
361 lines
8.5 KiB
C++
// 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 <Nazara/Audio/OpenALSource.hpp>
|
|
#include <Nazara/Audio/OpenALBuffer.hpp>
|
|
#include <Nazara/Audio/OpenALDevice.hpp>
|
|
#include <Nazara/Audio/OpenALLibrary.hpp>
|
|
#include <Nazara/Core/Algorithm.hpp>
|
|
#include <Nazara/Core/Error.hpp>
|
|
#include <Nazara/Utils/StackArray.hpp>
|
|
#include <Nazara/Audio/Debug.hpp>
|
|
|
|
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<UInt32>(samples);
|
|
}
|
|
|
|
auto OpenALSource::GetSampleOffsetAndLatency() const -> OffsetWithLatency
|
|
{
|
|
OffsetWithLatency offsetWithLatency;
|
|
#ifdef AL_SOFT_source_latency
|
|
if (GetDevice().IsExtensionSupported(OpenALExtension::SourceLatency))
|
|
{
|
|
GetDevice().MakeContextCurrent();
|
|
|
|
std::array<ALint64SOFT, 2> 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> audioBuffer)
|
|
{
|
|
NazaraAssert(audioBuffer, "invalid buffer");
|
|
NazaraAssert(audioBuffer->IsCompatibleWith(*GetAudioDevice()), "incompatible buffer");
|
|
|
|
std::shared_ptr<OpenALBuffer> newBuffer = std::static_pointer_cast<OpenALBuffer>(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> audioBuffer)
|
|
{
|
|
NazaraAssert(audioBuffer->IsCompatibleWith(*GetAudioDevice()), "incompatible buffer");
|
|
|
|
std::shared_ptr<OpenALBuffer> newBuffer = std::static_pointer_cast<OpenALBuffer>(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<ALdouble>());
|
|
else
|
|
#endif
|
|
m_library.alSourcef(m_sourceId, AL_SEC_OFFSET, offset.AsSeconds<ALfloat>());
|
|
}
|
|
|
|
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<AudioBuffer> 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<OpenALBuffer>& alBuffer)
|
|
{
|
|
return alBuffer->GetBufferId() == bufferId;
|
|
});
|
|
assert(it != m_queuedBuffers.end());
|
|
|
|
std::shared_ptr<AudioBuffer> 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<ALuint> buffers = NazaraStackArrayNoInit(ALuint, queuedBufferCount);
|
|
m_library.alSourceUnqueueBuffers(m_sourceId, queuedBufferCount, buffers.data());
|
|
|
|
m_queuedBuffers.clear();
|
|
}
|
|
|
|
OpenALDevice& OpenALSource::GetDevice()
|
|
{
|
|
return SafeCast<OpenALDevice&>(*GetAudioDevice());
|
|
}
|
|
|
|
const OpenALDevice& OpenALSource::GetDevice() const
|
|
{
|
|
return SafeCast<OpenALDevice&>(*GetAudioDevice());
|
|
}
|
|
}
|