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:
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
/*!
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user