Audio: Add dummy device (in case OpenAL fails to load) and unifiate unit tests
This commit is contained in:
@@ -6,6 +6,7 @@
|
||||
#include <Nazara/Audio/AudioBuffer.hpp>
|
||||
#include <Nazara/Audio/AudioSource.hpp>
|
||||
#include <Nazara/Audio/Config.hpp>
|
||||
#include <Nazara/Audio/DummyAudioDevice.hpp>
|
||||
#include <Nazara/Audio/Enums.hpp>
|
||||
#include <Nazara/Audio/OpenALLibrary.hpp>
|
||||
#include <Nazara/Audio/Formats/drwavLoader.hpp>
|
||||
@@ -32,11 +33,12 @@ namespace Nz
|
||||
* \brief Audio class that represents the module initializer of Audio
|
||||
*/
|
||||
|
||||
Audio::Audio(Config /*config*/) :
|
||||
ModuleBase("Audio", this)
|
||||
Audio::Audio(Config config) :
|
||||
ModuleBase("Audio", this),
|
||||
m_hasDummyDevice(config.allowDummyDevice)
|
||||
{
|
||||
// Load OpenAL
|
||||
if (!s_openalLibrary.Load())
|
||||
if (!config.noAudio && !s_openalLibrary.Load())
|
||||
throw std::runtime_error("failed to load OpenAL");
|
||||
|
||||
// Loaders
|
||||
@@ -49,7 +51,10 @@ namespace Nz
|
||||
m_soundBufferLoader.RegisterLoader(Loaders::GetSoundBufferLoader_minimp3());
|
||||
m_soundStreamLoader.RegisterLoader(Loaders::GetSoundStreamLoader_minimp3());
|
||||
|
||||
m_defaultDevice = s_openalLibrary.OpenDevice();
|
||||
if (s_openalLibrary.IsLoaded())
|
||||
m_defaultDevice = s_openalLibrary.OpenDevice();
|
||||
else
|
||||
m_defaultDevice = std::make_shared<DummyAudioDevice>();
|
||||
}
|
||||
|
||||
Audio::~Audio()
|
||||
@@ -101,17 +106,30 @@ namespace Nz
|
||||
|
||||
std::shared_ptr<AudioDevice> Audio::OpenOutputDevice(const std::string& deviceName)
|
||||
{
|
||||
if (deviceName == "dummy")
|
||||
return std::make_shared<DummyAudioDevice>();
|
||||
|
||||
return s_openalLibrary.OpenDevice(deviceName.c_str());
|
||||
}
|
||||
|
||||
std::vector<std::string> Audio::QueryInputDevices() const
|
||||
{
|
||||
if (!s_openalLibrary.IsLoaded())
|
||||
return {};
|
||||
|
||||
return s_openalLibrary.QueryInputDevices();
|
||||
}
|
||||
|
||||
std::vector<std::string> Audio::QueryOutputDevices() const
|
||||
{
|
||||
return s_openalLibrary.QueryOutputDevices();
|
||||
std::vector<std::string> outputDevices;
|
||||
if (s_openalLibrary.IsLoaded())
|
||||
outputDevices = s_openalLibrary.QueryOutputDevices();
|
||||
|
||||
if (m_hasDummyDevice)
|
||||
outputDevices.push_back("dummy");
|
||||
|
||||
return outputDevices;
|
||||
}
|
||||
|
||||
Audio* Audio::s_instance = nullptr;
|
||||
|
||||
50
src/Nazara/Audio/DummyAudioBuffer.cpp
Normal file
50
src/Nazara/Audio/DummyAudioBuffer.cpp
Normal file
@@ -0,0 +1,50 @@
|
||||
// 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/DummyAudioBuffer.hpp>
|
||||
#include <Nazara/Audio/Algorithm.hpp>
|
||||
#include <Nazara/Audio/AudioDevice.hpp>
|
||||
#include <Nazara/Core/Algorithm.hpp>
|
||||
#include <Nazara/Audio/Debug.hpp>
|
||||
|
||||
namespace Nz
|
||||
{
|
||||
AudioFormat DummyAudioBuffer::GetAudioFormat() const
|
||||
{
|
||||
return m_format;
|
||||
}
|
||||
|
||||
UInt32 DummyAudioBuffer::GetDuration() const
|
||||
{
|
||||
return SafeCast<UInt32>((1000ULL * m_sampleCount / (GetChannelCount(m_format) * m_sampleRate)));
|
||||
}
|
||||
|
||||
UInt64 DummyAudioBuffer::GetSampleCount() const
|
||||
{
|
||||
return m_sampleCount;
|
||||
}
|
||||
|
||||
UInt64 DummyAudioBuffer::GetSize() const
|
||||
{
|
||||
return m_sampleCount * sizeof(Int16);
|
||||
}
|
||||
|
||||
UInt32 DummyAudioBuffer::GetSampleRate() const
|
||||
{
|
||||
return m_sampleRate;
|
||||
}
|
||||
|
||||
bool DummyAudioBuffer::IsCompatibleWith(const AudioDevice& device) const
|
||||
{
|
||||
return GetAudioDevice()->GetSubSystemIdentifier() == device.GetSubSystemIdentifier();
|
||||
}
|
||||
|
||||
bool DummyAudioBuffer::Reset(AudioFormat format, UInt64 sampleCount, UInt32 sampleRate, const void* /*samples*/)
|
||||
{
|
||||
m_format = format;
|
||||
m_sampleCount = sampleCount;
|
||||
m_sampleRate = sampleRate;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
110
src/Nazara/Audio/DummyAudioDevice.cpp
Normal file
110
src/Nazara/Audio/DummyAudioDevice.cpp
Normal file
@@ -0,0 +1,110 @@
|
||||
// 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/DummyAudioDevice.hpp>
|
||||
#include <Nazara/Audio/DummyAudioBuffer.hpp>
|
||||
#include <Nazara/Audio/DummyAudioSource.hpp>
|
||||
#include <cstring>
|
||||
#include <stdexcept>
|
||||
#include <Nazara/Audio/Debug.hpp>
|
||||
|
||||
namespace Nz
|
||||
{
|
||||
DummyAudioDevice::DummyAudioDevice() :
|
||||
m_listenerRotation(Quaternionf::Identity()),
|
||||
m_listenerPosition(Vector3f::Zero()),
|
||||
m_dopplerFactor(1.f),
|
||||
m_globalVolume(1.f),
|
||||
m_speedOfSound(343.3f)
|
||||
{
|
||||
}
|
||||
|
||||
std::shared_ptr<AudioBuffer> DummyAudioDevice::CreateBuffer()
|
||||
{
|
||||
return std::make_shared<DummyAudioBuffer>(shared_from_this());
|
||||
}
|
||||
|
||||
std::shared_ptr<AudioSource> DummyAudioDevice::CreateSource()
|
||||
{
|
||||
return std::make_shared<DummyAudioSource>(shared_from_this());
|
||||
}
|
||||
|
||||
float DummyAudioDevice::GetDopplerFactor() const
|
||||
{
|
||||
return m_dopplerFactor;
|
||||
}
|
||||
|
||||
float DummyAudioDevice::GetGlobalVolume() const
|
||||
{
|
||||
return m_globalVolume;
|
||||
}
|
||||
|
||||
Vector3f DummyAudioDevice::GetListenerDirection(Vector3f* up) const
|
||||
{
|
||||
if (up)
|
||||
*up = m_listenerRotation * Vector3f::Up();
|
||||
|
||||
return m_listenerRotation * Vector3f::Forward();
|
||||
}
|
||||
|
||||
Vector3f DummyAudioDevice::GetListenerPosition() const
|
||||
{
|
||||
return m_listenerPosition;
|
||||
}
|
||||
|
||||
Quaternionf DummyAudioDevice::GetListenerRotation() const
|
||||
{
|
||||
return m_listenerRotation;
|
||||
}
|
||||
|
||||
Vector3f DummyAudioDevice::GetListenerVelocity() const
|
||||
{
|
||||
return m_listenerVelocity;
|
||||
}
|
||||
|
||||
float DummyAudioDevice::GetSpeedOfSound() const
|
||||
{
|
||||
return m_speedOfSound;
|
||||
}
|
||||
|
||||
const void* DummyAudioDevice::GetSubSystemIdentifier() const
|
||||
{
|
||||
return this;
|
||||
}
|
||||
|
||||
bool DummyAudioDevice::IsFormatSupported(AudioFormat /*format*/) const
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
void DummyAudioDevice::SetDopplerFactor(float dopplerFactor)
|
||||
{
|
||||
m_dopplerFactor = dopplerFactor;
|
||||
}
|
||||
|
||||
void DummyAudioDevice::SetGlobalVolume(float volume)
|
||||
{
|
||||
m_globalVolume = volume;
|
||||
}
|
||||
|
||||
void DummyAudioDevice::SetListenerDirection(const Vector3f& direction, const Vector3f& up)
|
||||
{
|
||||
m_listenerRotation = Quaternionf::LookAt(direction, up);
|
||||
}
|
||||
|
||||
void DummyAudioDevice::SetListenerPosition(const Vector3f& position)
|
||||
{
|
||||
m_listenerPosition = position;
|
||||
}
|
||||
|
||||
void DummyAudioDevice::SetListenerVelocity(const Vector3f& velocity)
|
||||
{
|
||||
m_listenerVelocity = velocity;
|
||||
}
|
||||
|
||||
void DummyAudioDevice::SetSpeedOfSound(float speed)
|
||||
{
|
||||
m_speedOfSound = speed;
|
||||
}
|
||||
}
|
||||
262
src/Nazara/Audio/DummyAudioSource.cpp
Normal file
262
src/Nazara/Audio/DummyAudioSource.cpp
Normal file
@@ -0,0 +1,262 @@
|
||||
// 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/DummyAudioSource.hpp>
|
||||
#include <Nazara/Audio/Algorithm.hpp>
|
||||
#include <Nazara/Audio/DummyAudioBuffer.hpp>
|
||||
#include <Nazara/Core/Algorithm.hpp>
|
||||
#include <Nazara/Core/Error.hpp>
|
||||
#include <Nazara/Core/Log.hpp>
|
||||
#include <Nazara/Core/StackArray.hpp>
|
||||
#include <algorithm>
|
||||
#include <Nazara/Audio/Debug.hpp>
|
||||
|
||||
namespace Nz
|
||||
{
|
||||
void DummyAudioSource::EnableLooping(bool loop)
|
||||
{
|
||||
m_isLooping = loop;
|
||||
}
|
||||
|
||||
void DummyAudioSource::EnableSpatialization(bool spatialization)
|
||||
{
|
||||
m_isSpatialized = spatialization;
|
||||
}
|
||||
|
||||
float DummyAudioSource::GetAttenuation() const
|
||||
{
|
||||
return m_attenuation;
|
||||
}
|
||||
|
||||
float DummyAudioSource::GetMinDistance() const
|
||||
{
|
||||
return m_minDistance;
|
||||
}
|
||||
|
||||
float DummyAudioSource::GetPitch() const
|
||||
{
|
||||
return m_pitch;
|
||||
}
|
||||
|
||||
Vector3f DummyAudioSource::GetPosition() const
|
||||
{
|
||||
return m_position;
|
||||
}
|
||||
|
||||
UInt32 DummyAudioSource::GetSampleOffset() const
|
||||
{
|
||||
UInt64 bufferTime = UpdateTime();
|
||||
|
||||
UInt64 sampleOffset = 0;
|
||||
// All processed buffers count in sample offset
|
||||
for (const auto& processedBuffer : m_processedBuffers)
|
||||
sampleOffset += processedBuffer->GetSampleCount() / GetChannelCount(processedBuffer->GetAudioFormat());
|
||||
|
||||
if (!m_queuedBuffers.empty())
|
||||
{
|
||||
auto& frontBuffer = m_queuedBuffers.front();
|
||||
UInt64 bufferOffset = bufferTime * frontBuffer->GetSampleRate() / 1000;
|
||||
UInt64 bufferDuration = frontBuffer->GetSampleCount() / GetChannelCount(frontBuffer->GetAudioFormat());
|
||||
|
||||
sampleOffset += std::min(bufferOffset, bufferDuration);
|
||||
}
|
||||
|
||||
return SafeCast<UInt32>(sampleOffset);
|
||||
}
|
||||
|
||||
Vector3f DummyAudioSource::GetVelocity() const
|
||||
{
|
||||
return m_velocity;
|
||||
}
|
||||
|
||||
SoundStatus DummyAudioSource::GetStatus() const
|
||||
{
|
||||
UpdateTime();
|
||||
|
||||
return m_status;
|
||||
}
|
||||
|
||||
float DummyAudioSource::GetVolume() const
|
||||
{
|
||||
return m_volume;
|
||||
}
|
||||
|
||||
bool DummyAudioSource::IsLooping() const
|
||||
{
|
||||
return m_isLooping;
|
||||
}
|
||||
|
||||
bool DummyAudioSource::IsSpatializationEnabled() const
|
||||
{
|
||||
return m_isSpatialized;
|
||||
}
|
||||
|
||||
void DummyAudioSource::QueueBuffer(std::shared_ptr<AudioBuffer> audioBuffer)
|
||||
{
|
||||
NazaraAssert(audioBuffer, "invalid buffer");
|
||||
NazaraAssert(audioBuffer->IsCompatibleWith(*GetAudioDevice()), "incompatible buffer");
|
||||
|
||||
m_queuedBuffers.emplace_back(std::static_pointer_cast<DummyAudioBuffer>(audioBuffer));
|
||||
}
|
||||
|
||||
void DummyAudioSource::Pause()
|
||||
{
|
||||
m_playClock.Pause();
|
||||
m_status = SoundStatus::Paused;
|
||||
}
|
||||
|
||||
void DummyAudioSource::Play()
|
||||
{
|
||||
if (m_status == SoundStatus::Paused)
|
||||
m_playClock.Unpause();
|
||||
else
|
||||
{
|
||||
// playing or stopped, restart
|
||||
RequeueBuffers();
|
||||
m_playClock.Restart();
|
||||
}
|
||||
|
||||
m_status = SoundStatus::Playing;
|
||||
}
|
||||
|
||||
void DummyAudioSource::SetAttenuation(float attenuation)
|
||||
{
|
||||
m_attenuation = attenuation;
|
||||
}
|
||||
|
||||
void DummyAudioSource::SetBuffer(std::shared_ptr<AudioBuffer> audioBuffer)
|
||||
{
|
||||
NazaraAssert(audioBuffer->IsCompatibleWith(*GetAudioDevice()), "incompatible buffer");
|
||||
|
||||
m_queuedBuffers.clear();
|
||||
m_queuedBuffers.emplace_back(std::static_pointer_cast<DummyAudioBuffer>(audioBuffer));
|
||||
m_processedBuffers.clear();
|
||||
}
|
||||
|
||||
void DummyAudioSource::SetMinDistance(float minDistance)
|
||||
{
|
||||
m_minDistance = minDistance;
|
||||
}
|
||||
|
||||
void DummyAudioSource::SetPitch(float pitch)
|
||||
{
|
||||
m_pitch = pitch;
|
||||
}
|
||||
|
||||
void DummyAudioSource::SetPosition(const Vector3f& position)
|
||||
{
|
||||
m_position = position;
|
||||
}
|
||||
|
||||
void DummyAudioSource::SetSampleOffset(UInt32 offset)
|
||||
{
|
||||
RequeueBuffers();
|
||||
|
||||
if (m_queuedBuffers.empty())
|
||||
return;
|
||||
|
||||
std::size_t processedBufferIndex = 0;
|
||||
for (; processedBufferIndex < m_queuedBuffers.size(); ++processedBufferIndex)
|
||||
{
|
||||
UInt32 bufferFrameCount = m_queuedBuffers[processedBufferIndex]->GetSampleCount() / GetChannelCount(m_queuedBuffers[processedBufferIndex]->GetAudioFormat());
|
||||
if (offset < bufferFrameCount)
|
||||
break;
|
||||
|
||||
offset -= bufferFrameCount;
|
||||
m_processedBuffers.emplace_back(std::move(m_queuedBuffers[processedBufferIndex]));
|
||||
}
|
||||
m_queuedBuffers.erase(m_queuedBuffers.begin(), m_queuedBuffers.begin() + processedBufferIndex);
|
||||
|
||||
assert(!m_queuedBuffers.empty());
|
||||
|
||||
UInt64 timeOffset = 1'000'000ULL * offset / m_queuedBuffers.front()->GetSampleRate();
|
||||
m_playClock.Restart(timeOffset, m_playClock.IsPaused());
|
||||
}
|
||||
|
||||
void DummyAudioSource::SetVelocity(const Vector3f& velocity)
|
||||
{
|
||||
m_velocity = velocity;
|
||||
}
|
||||
|
||||
void DummyAudioSource::SetVolume(float volume)
|
||||
{
|
||||
m_volume = volume;
|
||||
}
|
||||
|
||||
void DummyAudioSource::Stop()
|
||||
{
|
||||
m_playClock.Restart(0, true);
|
||||
}
|
||||
|
||||
std::shared_ptr<AudioBuffer> DummyAudioSource::TryUnqueueProcessedBuffer()
|
||||
{
|
||||
UpdateTime();
|
||||
|
||||
if (m_processedBuffers.empty())
|
||||
return {};
|
||||
|
||||
auto processedBuffer = std::move(m_processedBuffers.front());
|
||||
m_processedBuffers.erase(m_processedBuffers.begin());
|
||||
|
||||
return processedBuffer;
|
||||
}
|
||||
|
||||
void DummyAudioSource::UnqueueAllBuffers()
|
||||
{
|
||||
m_processedBuffers.clear();
|
||||
m_queuedBuffers.clear();
|
||||
Stop();
|
||||
}
|
||||
|
||||
void DummyAudioSource::RequeueBuffers()
|
||||
{
|
||||
// Put back all processed buffers in the queued buffer queue (for simplicity)
|
||||
if (!m_processedBuffers.empty())
|
||||
{
|
||||
m_queuedBuffers.resize(m_processedBuffers.size() + m_queuedBuffers.size());
|
||||
std::move(m_queuedBuffers.begin(), m_queuedBuffers.begin() + m_processedBuffers.size(), m_queuedBuffers.begin() + m_processedBuffers.size());
|
||||
std::move(m_processedBuffers.begin(), m_processedBuffers.end(), m_queuedBuffers.begin());
|
||||
m_processedBuffers.clear();
|
||||
}
|
||||
}
|
||||
|
||||
UInt64 DummyAudioSource::UpdateTime() const
|
||||
{
|
||||
UInt64 currentTime = m_playClock.GetMilliseconds();
|
||||
|
||||
while (!m_queuedBuffers.empty() && currentTime >= m_queuedBuffers.front()->GetDuration())
|
||||
{
|
||||
auto processedBuffer = std::move(m_queuedBuffers.front());
|
||||
m_queuedBuffers.erase(m_queuedBuffers.begin());
|
||||
|
||||
currentTime -= processedBuffer->GetDuration();
|
||||
|
||||
m_processedBuffers.emplace_back(std::move(processedBuffer));
|
||||
}
|
||||
|
||||
if (m_queuedBuffers.empty())
|
||||
{
|
||||
// If looping, replay processed buffers
|
||||
if (m_isLooping)
|
||||
{
|
||||
while (!m_processedBuffers.empty())
|
||||
{
|
||||
auto queuedBuffer = std::move(m_processedBuffers.front());
|
||||
m_processedBuffers.erase(m_processedBuffers.begin());
|
||||
|
||||
m_queuedBuffers.emplace_back(std::move(queuedBuffer));
|
||||
if (m_queuedBuffers.back()->GetDuration() > currentTime)
|
||||
break;
|
||||
|
||||
currentTime -= m_queuedBuffers.back()->GetDuration();
|
||||
}
|
||||
}
|
||||
else
|
||||
m_status = SoundStatus::Stopped;
|
||||
}
|
||||
|
||||
m_playClock.Restart(currentTime * 1000, m_playClock.IsPaused()); //< Adjust time
|
||||
return currentTime;
|
||||
}
|
||||
}
|
||||
@@ -133,7 +133,6 @@ namespace Nz
|
||||
std::lock_guard<std::mutex> lock(m_bufferLock);
|
||||
|
||||
UInt32 sampleOffset = m_source->GetSampleOffset();
|
||||
|
||||
return static_cast<UInt32>((1000ULL * (sampleOffset + (m_processedSamples / GetChannelCount(m_stream->GetFormat())))) / m_sampleRate);
|
||||
}
|
||||
|
||||
@@ -173,6 +172,8 @@ namespace Nz
|
||||
{
|
||||
NazaraAssert(m_stream, "Music not created");
|
||||
|
||||
std::lock_guard<std::mutex> lock(m_bufferLock);
|
||||
|
||||
SoundStatus status = m_source->GetStatus();
|
||||
|
||||
// To compensate any delays (or the timelaps between Play() and the thread startup)
|
||||
@@ -250,6 +251,8 @@ namespace Nz
|
||||
*/
|
||||
void Music::Pause()
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m_bufferLock);
|
||||
|
||||
m_source->Pause();
|
||||
}
|
||||
|
||||
@@ -285,24 +288,7 @@ namespace Nz
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
std::mutex mutex;
|
||||
std::condition_variable cv;
|
||||
|
||||
// Starting streaming thread
|
||||
m_streaming = true;
|
||||
|
||||
std::exception_ptr exceptionPtr;
|
||||
|
||||
std::unique_lock<std::mutex> lock(mutex);
|
||||
m_thread = std::thread(&Music::MusicThread, this, std::ref(cv), std::ref(mutex), std::ref(exceptionPtr));
|
||||
|
||||
// Wait until thread signal it has properly started (or an error occurred)
|
||||
cv.wait(lock);
|
||||
|
||||
if (exceptionPtr)
|
||||
std::rethrow_exception(exceptionPtr);
|
||||
}
|
||||
StartThread(false);
|
||||
}
|
||||
|
||||
/*!
|
||||
@@ -319,9 +305,10 @@ namespace Nz
|
||||
NazaraAssert(m_stream, "Music not created");
|
||||
|
||||
bool isPlaying = m_streaming;
|
||||
bool isPaused = GetStatus() == SoundStatus::Paused;
|
||||
|
||||
if (isPlaying)
|
||||
Stop();
|
||||
StopThread();
|
||||
|
||||
UInt64 sampleOffset = UInt64(offset) * m_sampleRate * GetChannelCount(m_stream->GetFormat()) / 1000ULL;
|
||||
|
||||
@@ -329,7 +316,7 @@ namespace Nz
|
||||
m_streamOffset = sampleOffset;
|
||||
|
||||
if (isPlaying)
|
||||
Play();
|
||||
StartThread(isPaused);
|
||||
}
|
||||
|
||||
/*!
|
||||
@@ -380,7 +367,7 @@ namespace Nz
|
||||
return sampleRead != sampleCount; // End of stream (Does not happen when looping)
|
||||
}
|
||||
|
||||
void Music::MusicThread(std::condition_variable& cv, std::mutex& m, std::exception_ptr& err)
|
||||
void Music::MusicThread(std::condition_variable& cv, std::mutex& m, std::exception_ptr& err, bool startPaused)
|
||||
{
|
||||
// Allocation of streaming buffers
|
||||
CallOnExit unqueueBuffers([&]
|
||||
@@ -406,8 +393,14 @@ namespace Nz
|
||||
cv.notify_all();
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
m_source->Play();
|
||||
if (startPaused)
|
||||
{
|
||||
// little hack to start paused (required by SetPlayingOffset)
|
||||
m_source->Pause();
|
||||
m_source->SetSampleOffset(0);
|
||||
}
|
||||
|
||||
CallOnExit stopSource([&]
|
||||
{
|
||||
@@ -424,6 +417,11 @@ namespace Nz
|
||||
// Reading loop (Filling new buffers as playing)
|
||||
while (m_streaming)
|
||||
{
|
||||
// Wait until buffers are processed
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(50));
|
||||
|
||||
std::lock_guard<std::mutex> lock(m_bufferLock);
|
||||
|
||||
SoundStatus status = m_source->GetStatus();
|
||||
if (status == SoundStatus::Stopped)
|
||||
{
|
||||
@@ -432,24 +430,37 @@ namespace Nz
|
||||
break;
|
||||
}
|
||||
|
||||
// We treat read buffers
|
||||
while (std::shared_ptr<AudioBuffer> buffer = m_source->TryUnqueueProcessedBuffer())
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(m_bufferLock);
|
||||
m_processedSamples += buffer->GetSampleCount();
|
||||
|
||||
// We treat read buffers
|
||||
while (std::shared_ptr<AudioBuffer> buffer = m_source->TryUnqueueProcessedBuffer())
|
||||
{
|
||||
m_processedSamples += buffer->GetSampleCount();
|
||||
|
||||
if (FillAndQueueBuffer(std::move(buffer)))
|
||||
break;
|
||||
}
|
||||
if (FillAndQueueBuffer(std::move(buffer)))
|
||||
break;
|
||||
}
|
||||
|
||||
// We go back to sleep
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(50));
|
||||
}
|
||||
}
|
||||
|
||||
void Music::StartThread(bool startPaused)
|
||||
{
|
||||
std::mutex mutex;
|
||||
std::condition_variable cv;
|
||||
|
||||
// Starting streaming thread
|
||||
m_streaming = true;
|
||||
|
||||
std::exception_ptr exceptionPtr;
|
||||
|
||||
std::unique_lock<std::mutex> lock(mutex);
|
||||
m_thread = std::thread(&Music::MusicThread, this, std::ref(cv), std::ref(mutex), std::ref(exceptionPtr), startPaused);
|
||||
|
||||
// Wait until thread signal it has properly started (or an error occurred)
|
||||
cv.wait(lock);
|
||||
|
||||
if (exceptionPtr)
|
||||
std::rethrow_exception(exceptionPtr);
|
||||
}
|
||||
|
||||
void Music::StopThread()
|
||||
{
|
||||
if (m_streaming)
|
||||
|
||||
@@ -17,7 +17,7 @@ namespace Nz
|
||||
m_library.alDeleteBuffers(1, &m_bufferId);
|
||||
}
|
||||
|
||||
UInt32 OpenALBuffer::GetSampleCount() const
|
||||
UInt64 OpenALBuffer::GetSampleCount() const
|
||||
{
|
||||
GetDevice().MakeContextCurrent();
|
||||
|
||||
@@ -25,21 +25,21 @@ namespace Nz
|
||||
m_library.alGetBufferi(m_bufferId, AL_BITS, &bits);
|
||||
m_library.alGetBufferi(m_bufferId, AL_SIZE, &size);
|
||||
|
||||
UInt32 sampleCount = 0;
|
||||
UInt64 sampleCount = 0;
|
||||
if (bits != 0)
|
||||
sampleCount += (8 * SafeCast<UInt32>(size)) / SafeCast<UInt32>(bits);
|
||||
sampleCount += (8 * SafeCast<UInt64>(size)) / SafeCast<UInt64>(bits);
|
||||
|
||||
return sampleCount;
|
||||
}
|
||||
|
||||
UInt32 OpenALBuffer::GetSize() const
|
||||
UInt64 OpenALBuffer::GetSize() const
|
||||
{
|
||||
GetDevice().MakeContextCurrent();
|
||||
|
||||
ALint size;
|
||||
m_library.alGetBufferi(m_bufferId, AL_SIZE, &size);
|
||||
|
||||
return SafeCast<UInt32>(size);
|
||||
return SafeCast<UInt64>(size);
|
||||
}
|
||||
|
||||
UInt32 OpenALBuffer::GetSampleRate() const
|
||||
@@ -49,7 +49,7 @@ namespace Nz
|
||||
ALint sampleRate;
|
||||
m_library.alGetBufferi(m_bufferId, AL_FREQUENCY, &sampleRate);
|
||||
|
||||
return SafeCast<UInt32>(sampleRate);
|
||||
return SafeCast<UInt64>(sampleRate);
|
||||
}
|
||||
|
||||
bool OpenALBuffer::IsCompatibleWith(const AudioDevice& device) const
|
||||
|
||||
@@ -100,7 +100,7 @@ namespace Nz
|
||||
|
||||
/*!
|
||||
* \brief Gets the global volume
|
||||
* \return Float between [0, inf) with 100.f being the default
|
||||
* \return Float between [0, inf) with 1.f being the default
|
||||
*/
|
||||
float OpenALDevice::GetGlobalVolume() const
|
||||
{
|
||||
@@ -109,7 +109,7 @@ namespace Nz
|
||||
ALfloat gain = 0.f;
|
||||
m_library.alGetListenerf(AL_GAIN, &gain);
|
||||
|
||||
return gain * 100.f;
|
||||
return gain;
|
||||
}
|
||||
|
||||
/*!
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
#include <Nazara/Audio/OpenALLibrary.hpp>
|
||||
#include <Nazara/Core/Algorithm.hpp>
|
||||
#include <Nazara/Core/CallOnExit.hpp>
|
||||
#include <Nazara/Core/Error.hpp>
|
||||
#include <Nazara/Core/ErrorFlags.hpp>
|
||||
#include <Nazara/Core/Log.hpp>
|
||||
@@ -20,6 +21,8 @@ namespace Nz
|
||||
{
|
||||
Unload();
|
||||
|
||||
CallOnExit unloadOnFailure([this] { Unload(); });
|
||||
|
||||
#if defined(NAZARA_PLATFORM_WINDOWS)
|
||||
std::array libs{
|
||||
"soft_oal.dll",
|
||||
@@ -69,15 +72,21 @@ namespace Nz
|
||||
continue;
|
||||
}
|
||||
|
||||
unloadOnFailure.Reset();
|
||||
return true;
|
||||
}
|
||||
|
||||
m_hasCaptureSupport = alcIsExtensionPresent(nullptr, "ALC_EXT_CAPTURE");
|
||||
|
||||
NazaraError("failed to load OpenAL library");
|
||||
return false;
|
||||
}
|
||||
|
||||
std::vector<std::string> OpenALLibrary::QueryInputDevices()
|
||||
{
|
||||
if (!m_hasCaptureSupport)
|
||||
return {};
|
||||
|
||||
return ParseDevices(alcGetString(nullptr, ALC_CAPTURE_DEVICE_SPECIFIER));
|
||||
}
|
||||
|
||||
|
||||
@@ -131,10 +131,10 @@ namespace Nz
|
||||
{
|
||||
GetDevice().MakeContextCurrent();
|
||||
|
||||
ALint relative;
|
||||
m_library.alGetSourcei(m_sourceId, AL_LOOPING, &relative);
|
||||
ALint looping;
|
||||
m_library.alGetSourcei(m_sourceId, AL_LOOPING, &looping);
|
||||
|
||||
return relative == AL_FALSE;
|
||||
return looping == AL_TRUE;
|
||||
}
|
||||
|
||||
bool OpenALSource::IsSpatializationEnabled() const
|
||||
|
||||
@@ -125,7 +125,7 @@ namespace Nz
|
||||
* Restarts the clock, putting it's time counter back to zero (as if the clock got constructed).
|
||||
* It also compute the elapsed microseconds since the last Restart() call without any time loss (a problem that the combination of GetElapsedMicroseconds and Restart have).
|
||||
*/
|
||||
UInt64 Clock::Restart()
|
||||
UInt64 Clock::Restart(UInt64 startingValue, bool paused)
|
||||
{
|
||||
Nz::UInt64 now = GetElapsedMicroseconds();
|
||||
|
||||
@@ -133,9 +133,9 @@ namespace Nz
|
||||
if (!m_paused)
|
||||
elapsedTime += (now - m_refTime);
|
||||
|
||||
m_elapsedTime = 0;
|
||||
m_elapsedTime = startingValue;
|
||||
m_refTime = now;
|
||||
m_paused = false;
|
||||
m_paused = paused;
|
||||
|
||||
return elapsedTime;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user