Refactor the way resources are loaded (#191)

* WIP

* WIP

* Font works

* WIP: Only Music remains

* Looks like it's working

* Fix oopsie

* Core/ObjectRef: Add cast functions

* Update ChangeLog.md

* Audio/SoundStream: Make sound stream thread-safe
This commit is contained in:
Jérôme Leclercq
2018-10-28 01:53:11 +02:00
committed by GitHub
parent fa7cbc21e5
commit ed46c87781
64 changed files with 1058 additions and 1071 deletions

View File

@@ -14,7 +14,9 @@
#include <Nazara/Core/Error.hpp>
#include <Nazara/Core/File.hpp>
#include <Nazara/Core/MemoryView.hpp>
#include <Nazara/Core/Mutex.hpp>
#include <Nazara/Core/Stream.hpp>
#include <iostream>
#include <memory>
#include <set>
#include <vector>
@@ -97,6 +99,11 @@ namespace Nz
return m_format;
}
Mutex& GetMutex() override
{
return m_mutex;
}
UInt64 GetSampleCount() const override
{
return m_sampleCount;
@@ -124,7 +131,7 @@ namespace Nz
bool Open(const void* data, std::size_t size, bool forceMono)
{
m_ownedStream.reset(new MemoryView(data, size));
m_ownedStream = std::make_unique<MemoryView>(data, size);
return Open(*m_ownedStream, forceMono);
}
@@ -201,12 +208,18 @@ namespace Nz
sf_seek(m_handle, offset*m_sampleRate / 1000, SEEK_SET);
}
UInt64 Tell() override
{
return sf_seek(m_handle, 0, SEEK_CUR) * 1000 / m_sampleRate;
}
private:
std::vector<Int16> m_mixBuffer;
std::unique_ptr<Stream> m_ownedStream;
AudioFormat m_format;
SNDFILE* m_handle;
bool m_mixToMono;
Mutex m_mutex;
UInt32 m_duration;
UInt32 m_sampleRate;
UInt64 m_sampleCount;
@@ -222,7 +235,7 @@ namespace Nz
return supportedExtensions.find(extension) != supportedExtensions.end();
}
Ternary CheckMusic(Stream& stream, const MusicParams& parameters)
Ternary CheckSoundStream(Stream& stream, const SoundStreamParams& parameters)
{
NazaraUnused(parameters);
@@ -240,67 +253,43 @@ namespace Nz
return Ternary_False;
}
bool LoadMusicFile(Music* music, const String& filePath, const MusicParams& parameters)
SoundStreamRef LoadSoundStreamFile(const String& filePath, const SoundStreamParams& parameters)
{
std::unique_ptr<sndfileStream> musicStream(new sndfileStream);
if (!musicStream->Open(filePath, parameters.forceMono))
std::unique_ptr<sndfileStream> soundStream(new sndfileStream);
if (!soundStream->Open(filePath, parameters.forceMono))
{
NazaraError("Failed to open music stream");
return false;
NazaraError("Failed to open sound stream");
return nullptr;
}
if (!music->Create(musicStream.get())) // Transfert de propriété
{
NazaraError("Failed to create music");
return false;
}
// Le pointeur a été transféré avec succès, nous pouvons laisser tomber la propriété
musicStream.release();
return true;
soundStream->SetPersistent(false);
return soundStream.release();
}
bool LoadMusicMemory(Music* music, const void* data, std::size_t size, const MusicParams& parameters)
SoundStreamRef LoadSoundStreamMemory(const void* data, std::size_t size, const SoundStreamParams& parameters)
{
std::unique_ptr<sndfileStream> musicStream(new sndfileStream);
if (!musicStream->Open(data, size, parameters.forceMono))
std::unique_ptr<sndfileStream> soundStream(new sndfileStream);
if (!soundStream->Open(data, size, parameters.forceMono))
{
NazaraError("Failed to open music stream");
return false;
return nullptr;
}
if (!music->Create(musicStream.get())) // Transfert de propriété
{
NazaraError("Failed to create music");
return false;
}
// Le pointeur a été transféré avec succès, nous pouvons laisser tomber la propriété
musicStream.release();
return true;
soundStream->SetPersistent(false);
return soundStream.release();
}
bool LoadMusicStream(Music* music, Stream& stream, const MusicParams& parameters)
SoundStreamRef LoadSoundStreamStream(Stream& stream, const SoundStreamParams& parameters)
{
std::unique_ptr<sndfileStream> musicStream(new sndfileStream);
if (!musicStream->Open(stream, parameters.forceMono))
std::unique_ptr<sndfileStream> soundStream(new sndfileStream);
if (!soundStream->Open(stream, parameters.forceMono))
{
NazaraError("Failed to open music stream");
return false;
return nullptr;
}
if (!music->Create(musicStream.get())) // Transfert de propriété
{
NazaraError("Failed to create music");
return false;
}
// Le pointeur a été transféré avec succès, nous pouvons laisser tomber la propriété
musicStream.release();
return true;
soundStream->SetPersistent(false);
return soundStream.release();
}
Ternary CheckSoundBuffer(Stream& stream, const SoundBufferParams& parameters)
@@ -320,7 +309,7 @@ namespace Nz
return Ternary_False;
}
bool LoadSoundBuffer(SoundBuffer* soundBuffer, Stream& stream, const SoundBufferParams& parameters)
SoundBufferRef LoadSoundBuffer(Stream& stream, const SoundBufferParams& parameters)
{
SF_INFO info;
info.format = 0;
@@ -329,7 +318,7 @@ namespace Nz
if (!file)
{
NazaraError("Failed to load sound file: " + String(sf_strerror(file)));
return false;
return nullptr;
}
// Lynix utilise RAII...
@@ -344,7 +333,7 @@ namespace Nz
if (format == AudioFormat_Unknown)
{
NazaraError("Channel count not handled");
return false;
return nullptr;
}
// https://github.com/LaurentGomila/SFML/issues/271
@@ -359,7 +348,7 @@ namespace Nz
if (sf_read_short(file, samples.get(), sampleCount) != sampleCount)
{
NazaraError("Failed to read samples");
return false;
return nullptr;
}
// Une conversion en mono est-elle nécessaire ?
@@ -372,13 +361,7 @@ namespace Nz
sampleCount = static_cast<unsigned int>(info.frames);
}
if (!soundBuffer->Create(format, sampleCount, info.samplerate, samples.get()))
{
NazaraError("Failed to create sound buffer");
return false;
}
return true;
return SoundBuffer::New(format, sampleCount, info.samplerate, samples.get());
}
}
@@ -386,14 +369,14 @@ namespace Nz
{
void Register_sndfile()
{
MusicLoader::RegisterLoader(Detail::IsSupported, Detail::CheckMusic, Detail::LoadMusicStream, Detail::LoadMusicFile, Detail::LoadMusicMemory);
SoundBufferLoader::RegisterLoader(Detail::IsSupported, Detail::CheckSoundBuffer, Detail::LoadSoundBuffer);
SoundStreamLoader::RegisterLoader(Detail::IsSupported, Detail::CheckSoundStream, Detail::LoadSoundStreamStream, Detail::LoadSoundStreamFile, Detail::LoadSoundStreamMemory);
}
void Unregister_sndfile()
{
MusicLoader::UnregisterLoader(Detail::IsSupported, Detail::CheckMusic, Detail::LoadMusicStream, Detail::LoadMusicFile, Detail::LoadMusicMemory);
SoundBufferLoader::UnregisterLoader(Detail::IsSupported, Detail::CheckSoundBuffer, Detail::LoadSoundBuffer);
SoundStreamLoader::UnregisterLoader(Detail::IsSupported, Detail::CheckSoundStream, Detail::LoadSoundStreamStream, Detail::LoadSoundStreamFile, Detail::LoadSoundStreamMemory);
}
}
}

View File

@@ -5,9 +5,12 @@
#include <Nazara/Audio/Music.hpp>
#include <Nazara/Audio/OpenAL.hpp>
#include <Nazara/Audio/SoundStream.hpp>
#include <Nazara/Core/LockGuard.hpp>
#include <Nazara/Core/Mutex.hpp>
#include <Nazara/Core/Thread.hpp>
#include <atomic>
#include <memory>
#include <iostream>
#include <vector>
#include <Nazara/Audio/Debug.hpp>
@@ -21,24 +24,15 @@ namespace Nz
* \remark Module Audio needs to be initialized to use this class
*/
/*!
* \brief Checks whether the parameters for the loading of the music are correct
* \return true If parameters are valid
*/
bool MusicParams::IsValid() const
{
return true;
}
struct MusicImpl
{
ALenum audioFormat;
std::unique_ptr<SoundStream> stream;
std::atomic<UInt64> processedSamples;
std::vector<Int16> chunkSamples;
Mutex bufferLock;
SoundStreamRef stream;
Thread thread;
UInt64 processedSamples;
UInt64 playingOffset;
bool loop = false;
bool streaming = false;
unsigned int sampleRate;
@@ -74,7 +68,7 @@ namespace Nz
m_impl->sampleRate = soundStream->GetSampleRate();
m_impl->audioFormat = OpenAL::AudioFormat[format];
m_impl->chunkSamples.resize(format * m_impl->sampleRate); // One second of samples
m_impl->stream.reset(soundStream);
m_impl->stream = soundStream;
SetPlayingOffset(0);
@@ -221,9 +215,12 @@ namespace Nz
* \param filePath Path to the file
* \param params Parameters for the music
*/
bool Music::OpenFromFile(const String& filePath, const MusicParams& params)
bool Music::OpenFromFile(const String& filePath, const SoundStreamParams& params)
{
return MusicLoader::LoadFromFile(this, filePath, params);
if (SoundStreamRef soundStream = SoundStream::OpenFromFile(filePath, params))
return Create(soundStream);
else
return false;
}
/*!
@@ -236,9 +233,12 @@ namespace Nz
*
* \remark The memory pointer must stay valid (accessible) as long as the music is playing
*/
bool Music::OpenFromMemory(const void* data, std::size_t size, const MusicParams& params)
bool Music::OpenFromMemory(const void* data, std::size_t size, const SoundStreamParams& params)
{
return MusicLoader::LoadFromMemory(this, data, size, params);
if (SoundStreamRef soundStream = SoundStream::OpenFromMemory(data, size, params))
return Create(soundStream);
else
return false;
}
/*!
@@ -250,9 +250,12 @@ namespace Nz
*
* \remark The stream must stay valid as long as the music is playing
*/
bool Music::OpenFromStream(Stream& stream, const MusicParams& params)
bool Music::OpenFromStream(Stream& stream, const SoundStreamParams& params)
{
return MusicLoader::LoadFromStream(this, stream, params);
if (SoundStreamRef soundStream = SoundStream::OpenFromStream(stream, params))
return Create(soundStream);
else
return false;
}
/*!
@@ -324,7 +327,7 @@ namespace Nz
if (isPlaying)
Stop();
m_impl->stream->Seek(offset);
m_impl->playingOffset = offset;
m_impl->processedSamples = UInt64(offset) * m_impl->sampleRate * m_impl->stream->GetFormat() / 1000ULL;
if (isPlaying)
@@ -349,6 +352,10 @@ namespace Nz
std::size_t sampleCount = m_impl->chunkSamples.size();
std::size_t sampleRead = 0;
Nz::LockGuard lock(m_impl->stream->GetMutex());
m_impl->stream->Seek(m_impl->playingOffset);
// Fill the buffer by reading from the stream
for (;;)
{
@@ -364,6 +371,10 @@ namespace Nz
break;
}
m_impl->playingOffset = m_impl->stream->Tell();
lock.Unlock();
// Update the buffer (send it to OpenAL) and queue it if we got any data
if (sampleRead > 0)
{
@@ -380,9 +391,9 @@ namespace Nz
ALuint buffers[NAZARA_AUDIO_STREAMED_BUFFER_COUNT];
alGenBuffers(NAZARA_AUDIO_STREAMED_BUFFER_COUNT, buffers);
for (unsigned int i = 0; i < NAZARA_AUDIO_STREAMED_BUFFER_COUNT; ++i)
for (unsigned int buffer : buffers)
{
if (FillAndQueueBuffer(buffers[i]))
if (FillAndQueueBuffer(buffer))
break; // We have reached the end of the stream, there is no use to add new buffers
}
@@ -448,6 +459,4 @@ namespace Nz
m_impl->thread.Join();
}
}
MusicLoader::LoaderList Music::s_loaders;
}

View File

@@ -151,8 +151,8 @@ namespace Nz
*/
bool Sound::LoadFromFile(const String& filePath, const SoundBufferParams& params)
{
SoundBufferRef buffer = SoundBuffer::New();
if (!buffer->LoadFromFile(filePath, params))
SoundBufferRef buffer = SoundBuffer::LoadFromFile(filePath, params);
if (!buffer)
{
NazaraError("Failed to load buffer from file (" + filePath + ')');
return false;
@@ -174,8 +174,8 @@ namespace Nz
*/
bool Sound::LoadFromMemory(const void* data, std::size_t size, const SoundBufferParams& params)
{
SoundBufferRef buffer = SoundBuffer::New();
if (!buffer->LoadFromMemory(data, size, params))
SoundBufferRef buffer = SoundBuffer::LoadFromMemory(data, size, params);
if (!buffer)
{
NazaraError("Failed to load buffer from memory (" + String::Pointer(data) + ')');
return false;
@@ -196,8 +196,8 @@ namespace Nz
*/
bool Sound::LoadFromStream(Stream& stream, const SoundBufferParams& params)
{
SoundBufferRef buffer = SoundBuffer::New();
if (!buffer->LoadFromStream(stream, params))
SoundBufferRef buffer = SoundBuffer::LoadFromStream(stream, params);
if (!buffer)
{
NazaraError("Failed to load buffer from stream");
return false;

View File

@@ -152,7 +152,7 @@ namespace Nz
m_impl->format = format;
m_impl->sampleCount = sampleCount;
m_impl->sampleRate = sampleRate;
m_impl->samples.reset(new Int16[sampleCount]);
m_impl->samples = std::make_unique<Int16[]>(sampleCount);
std::memcpy(&m_impl->samples[0], samples, sampleCount*sizeof(Int16));
clearBufferOnExit.Reset();
@@ -251,6 +251,17 @@ namespace Nz
return m_impl != nullptr;
}
/*!
* \brief Checks whether the format is supported by the engine
* \return true if it is the case
*
* \param format Format to check
*/
bool SoundBuffer::IsFormatSupported(AudioFormat format)
{
return Audio::IsFormatSupported(format);
}
/*!
* \brief Loads the sound buffer from file
* \return true if loading is successful
@@ -258,9 +269,9 @@ namespace Nz
* \param filePath Path to the file
* \param params Parameters for the sound buffer
*/
bool SoundBuffer::LoadFromFile(const String& filePath, const SoundBufferParams& params)
SoundBufferRef SoundBuffer::LoadFromFile(const String& filePath, const SoundBufferParams& params)
{
return SoundBufferLoader::LoadFromFile(this, filePath, params);
return SoundBufferLoader::LoadFromFile(filePath, params);
}
/*!
@@ -271,9 +282,9 @@ namespace Nz
* \param size Size of the memory
* \param params Parameters for the sound buffer
*/
bool SoundBuffer::LoadFromMemory(const void* data, std::size_t size, const SoundBufferParams& params)
SoundBufferRef SoundBuffer::LoadFromMemory(const void* data, std::size_t size, const SoundBufferParams& params)
{
return SoundBufferLoader::LoadFromMemory(this, data, size, params);
return SoundBufferLoader::LoadFromMemory(data, size, params);
}
/*!
@@ -283,20 +294,9 @@ namespace Nz
* \param stream Stream to the sound buffer
* \param params Parameters for the sound buffer
*/
bool SoundBuffer::LoadFromStream(Stream& stream, const SoundBufferParams& params)
SoundBufferRef SoundBuffer::LoadFromStream(Stream& stream, const SoundBufferParams& params)
{
return SoundBufferLoader::LoadFromStream(this, stream, params);
}
/*!
* \brief Checks whether the format is supported by the engine
* \return true if it is the case
*
* \param format Format to check
*/
bool SoundBuffer::IsFormatSupported(AudioFormat format)
{
return Audio::IsFormatSupported(format);
return SoundBufferLoader::LoadFromStream(stream, params);
}
/*!

View File

@@ -6,6 +6,11 @@
namespace Nz
{
bool SoundStreamParams::IsValid() const
{
return true;
}
/*!
* \ingroup audio
* \class Nz::SoundStream
@@ -15,4 +20,21 @@ namespace Nz
*/
SoundStream::~SoundStream() = default;
SoundStreamRef SoundStream::OpenFromFile(const String& filePath, const SoundStreamParams& params)
{
return SoundStreamLoader::LoadFromFile(filePath, params);
}
SoundStreamRef SoundStream::OpenFromMemory(const void* data, std::size_t size, const SoundStreamParams& params)
{
return SoundStreamLoader::LoadFromMemory(data, size, params);
}
SoundStreamRef SoundStream::OpenFromStream(Stream& stream, const SoundStreamParams& params)
{
return SoundStreamLoader::LoadFromStream(stream, params);
}
SoundStreamLoader::LoaderList SoundStream::s_loaders;
}