Major ResourceLoader rework (using Nz::Result)
This commit is contained in:
@@ -51,31 +51,14 @@ namespace Nz
|
||||
|
||||
bool IsWavSupported(const std::string_view& extension)
|
||||
{
|
||||
return extension == "riff" || extension == "rf64" || extension == "wav" || extension == "w64";
|
||||
return extension == ".riff" || extension == ".rf64" || extension == ".wav" || extension == ".w64";
|
||||
}
|
||||
|
||||
Ternary CheckWav(Stream& stream, const ResourceParameters& parameters)
|
||||
{
|
||||
bool skip;
|
||||
if (parameters.custom.GetBooleanParameter("SkipBuiltinWavLoader", &skip) && skip)
|
||||
return Ternary::False;
|
||||
|
||||
drwav wav;
|
||||
if (!drwav_init(&wav, &ReadWavCallback, &SeekWavCallback, &stream, nullptr))
|
||||
return Ternary::False;
|
||||
|
||||
drwav_uninit(&wav);
|
||||
return Ternary::True;
|
||||
}
|
||||
|
||||
std::shared_ptr<SoundBuffer> LoadWavSoundBuffer(Stream& stream, const SoundBufferParams& parameters)
|
||||
Result<std::shared_ptr<SoundBuffer>, ResourceLoadingError> LoadWavSoundBuffer(Stream& stream, const SoundBufferParams& parameters)
|
||||
{
|
||||
drwav wav;
|
||||
if (!drwav_init(&wav, &ReadWavCallback, &SeekWavCallback, &stream, nullptr))
|
||||
{
|
||||
NazaraError("failed to decode wav stream");
|
||||
return {};
|
||||
}
|
||||
return Err(ResourceLoadingError::Unrecognized);
|
||||
|
||||
CallOnExit uninitOnExit([&] { drwav_uninit(&wav); });
|
||||
|
||||
@@ -83,7 +66,7 @@ namespace Nz
|
||||
if (!formatOpt)
|
||||
{
|
||||
NazaraError("unexpected channel count: " + std::to_string(wav.channels));
|
||||
return {};
|
||||
return Err(ResourceLoadingError::Unsupported);
|
||||
}
|
||||
|
||||
AudioFormat format = *formatOpt;
|
||||
@@ -94,7 +77,7 @@ namespace Nz
|
||||
if (drwav_read_pcm_frames_s16(&wav, wav.totalPCMFrameCount, samples.get()) != wav.totalPCMFrameCount)
|
||||
{
|
||||
NazaraError("failed to read stream content");
|
||||
return {};
|
||||
return Err(ResourceLoadingError::DecodingError);
|
||||
}
|
||||
|
||||
if (parameters.forceMono && format != AudioFormat::I16_Mono)
|
||||
@@ -150,32 +133,29 @@ namespace Nz
|
||||
return m_sampleRate;
|
||||
}
|
||||
|
||||
bool Open(const std::filesystem::path& filePath, bool forceMono)
|
||||
Result<void, ResourceLoadingError> Open(const std::filesystem::path& filePath, const SoundStreamParams& parameters)
|
||||
{
|
||||
std::unique_ptr<File> file = std::make_unique<File>();
|
||||
if (!file->Open(filePath, OpenMode::ReadOnly))
|
||||
{
|
||||
NazaraError("failed to open stream from file: " + Error::GetLastError());
|
||||
return false;
|
||||
return Err(ResourceLoadingError::FailedToOpenFile);
|
||||
}
|
||||
|
||||
m_ownedStream = std::move(file);
|
||||
return Open(*m_ownedStream, forceMono);
|
||||
return Open(*m_ownedStream, parameters);
|
||||
}
|
||||
|
||||
bool Open(const void* data, std::size_t size, bool forceMono)
|
||||
Result<void, ResourceLoadingError> Open(const void* data, std::size_t size, const SoundStreamParams& parameters)
|
||||
{
|
||||
m_ownedStream = std::make_unique<MemoryView>(data, size);
|
||||
return Open(*m_ownedStream, forceMono);
|
||||
return Open(*m_ownedStream, parameters);
|
||||
}
|
||||
|
||||
bool Open(Stream& stream, bool forceMono)
|
||||
Result<void, ResourceLoadingError> Open(Stream& stream, const SoundStreamParams& parameters)
|
||||
{
|
||||
if (!drwav_init(&m_decoder, &ReadWavCallback, &SeekWavCallback, &stream, nullptr))
|
||||
{
|
||||
NazaraError("failed to decode wav stream");
|
||||
return {};
|
||||
}
|
||||
return Err(ResourceLoadingError::Unrecognized);
|
||||
|
||||
CallOnExit resetOnError([this]
|
||||
{
|
||||
@@ -187,7 +167,7 @@ namespace Nz
|
||||
if (!formatOpt)
|
||||
{
|
||||
NazaraError("unexpected channel count: " + std::to_string(m_decoder.channels));
|
||||
return false;
|
||||
return Err(ResourceLoadingError::Unsupported);
|
||||
}
|
||||
|
||||
m_format = *formatOpt;
|
||||
@@ -197,7 +177,7 @@ namespace Nz
|
||||
m_sampleRate = m_decoder.sampleRate;
|
||||
|
||||
// Mixing to mono will be done on the fly
|
||||
if (forceMono && m_format != AudioFormat::I16_Mono)
|
||||
if (parameters.forceMono && m_format != AudioFormat::I16_Mono)
|
||||
{
|
||||
m_mixToMono = true;
|
||||
m_sampleCount = m_decoder.totalPCMFrameCount;
|
||||
@@ -207,7 +187,7 @@ namespace Nz
|
||||
|
||||
resetOnError.Reset();
|
||||
|
||||
return true;
|
||||
return Ok();
|
||||
}
|
||||
|
||||
UInt64 Read(void* buffer, UInt64 sampleCount) override
|
||||
@@ -257,40 +237,28 @@ namespace Nz
|
||||
bool m_mixToMono;
|
||||
};
|
||||
|
||||
std::shared_ptr<SoundStream> LoadWavSoundStreamFile(const std::filesystem::path& filePath, const SoundStreamParams& parameters)
|
||||
Result<std::shared_ptr<SoundStream>, ResourceLoadingError> LoadWavSoundStreamFile(const std::filesystem::path& filePath, const SoundStreamParams& parameters)
|
||||
{
|
||||
std::shared_ptr<drwavStream> soundStream = std::make_shared<drwavStream>();
|
||||
if (!soundStream->Open(filePath, parameters.forceMono))
|
||||
{
|
||||
NazaraError("failed to open sound stream");
|
||||
return {};
|
||||
}
|
||||
Result<void, ResourceLoadingError> status = soundStream->Open(filePath, parameters);
|
||||
|
||||
return soundStream;
|
||||
return status.Map([&] { return std::move(soundStream); });
|
||||
}
|
||||
|
||||
std::shared_ptr<SoundStream> LoadWavSoundStreamMemory(const void* data, std::size_t size, const SoundStreamParams& parameters)
|
||||
Result<std::shared_ptr<SoundStream>, ResourceLoadingError> LoadWavSoundStreamMemory(const void* data, std::size_t size, const SoundStreamParams& parameters)
|
||||
{
|
||||
std::shared_ptr<drwavStream> soundStream = std::make_shared<drwavStream>();
|
||||
if (!soundStream->Open(data, size, parameters.forceMono))
|
||||
{
|
||||
NazaraError("failed to open music stream");
|
||||
return {};
|
||||
}
|
||||
Result<void, ResourceLoadingError> status = soundStream->Open(data, size, parameters);
|
||||
|
||||
return soundStream;
|
||||
return status.Map([&] { return std::move(soundStream); });
|
||||
}
|
||||
|
||||
std::shared_ptr<SoundStream> LoadWavSoundStreamStream(Stream& stream, const SoundStreamParams& parameters)
|
||||
Result<std::shared_ptr<SoundStream>, ResourceLoadingError> LoadWavSoundStreamStream(Stream& stream, const SoundStreamParams& parameters)
|
||||
{
|
||||
std::shared_ptr<drwavStream> soundStream = std::make_shared<drwavStream>();
|
||||
if (!soundStream->Open(stream, parameters.forceMono))
|
||||
{
|
||||
NazaraError("failed to open music stream");
|
||||
return {};
|
||||
}
|
||||
Result<void, ResourceLoadingError> status = soundStream->Open(stream, parameters);
|
||||
|
||||
return soundStream;
|
||||
return status.Map([&] { return std::move(soundStream); });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -300,8 +268,15 @@ namespace Nz
|
||||
{
|
||||
SoundBufferLoader::Entry loaderEntry;
|
||||
loaderEntry.extensionSupport = IsWavSupported;
|
||||
loaderEntry.streamChecker = [](Stream& stream, const SoundBufferParams& parameters) { return CheckWav(stream, parameters); };
|
||||
loaderEntry.streamLoader = LoadWavSoundBuffer;
|
||||
loaderEntry.parameterFilter = [](const SoundBufferParams& parameters)
|
||||
{
|
||||
bool skip;
|
||||
if (parameters.custom.GetBooleanParameter("SkipBuiltinWavLoader", &skip) && skip)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
return loaderEntry;
|
||||
}
|
||||
@@ -310,10 +285,17 @@ namespace Nz
|
||||
{
|
||||
SoundStreamLoader::Entry loaderEntry;
|
||||
loaderEntry.extensionSupport = IsWavSupported;
|
||||
loaderEntry.streamChecker = [](Stream& stream, const SoundStreamParams& parameters) { return CheckWav(stream, parameters); };
|
||||
loaderEntry.fileLoader = LoadWavSoundStreamFile;
|
||||
loaderEntry.memoryLoader = LoadWavSoundStreamMemory;
|
||||
loaderEntry.streamLoader = LoadWavSoundStreamStream;
|
||||
loaderEntry.parameterFilter = [](const SoundStreamParams& parameters)
|
||||
{
|
||||
bool skip;
|
||||
if (parameters.custom.GetBooleanParameter("SkipBuiltinWavLoader", &skip) && skip)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
return loaderEntry;
|
||||
}
|
||||
|
||||
@@ -138,46 +138,10 @@ namespace Nz
|
||||
|
||||
bool IsFlacSupported(const std::string_view& extension)
|
||||
{
|
||||
return extension == "flac";
|
||||
return extension == ".flac";
|
||||
}
|
||||
|
||||
Ternary CheckFlac(Stream& stream, const ResourceParameters& parameters)
|
||||
{
|
||||
bool skip;
|
||||
if (parameters.custom.GetBooleanParameter("SkipBuiltinFlacLoader", &skip) && skip)
|
||||
return Ternary::False;
|
||||
|
||||
FLAC__StreamDecoder* decoder = FLAC__stream_decoder_new();
|
||||
CallOnExit freeDecoder([&] { FLAC__stream_decoder_delete(decoder); });
|
||||
|
||||
bool hasError = false;
|
||||
|
||||
FlacUserdata ud;
|
||||
ud.stream = &stream;
|
||||
ud.errorCallback = [&](const FLAC__StreamDecoder* /*decoder*/, FLAC__StreamDecoderErrorStatus /*status*/)
|
||||
{
|
||||
hasError = true;
|
||||
};
|
||||
|
||||
FLAC__StreamDecoderInitStatus status = FLAC__stream_decoder_init_stream(decoder, &FlacReadCallback, &FlacSeekCallback, &FlacTellCallback, &FlacLengthCallback, &FlacEofCallback, &WriteCallback, &MetadataCallback, &ErrorCallback, &ud);
|
||||
if (status != FLAC__STREAM_DECODER_INIT_STATUS_OK)
|
||||
{
|
||||
NazaraWarning(FLAC__StreamDecoderInitStatusString[status]); //< an error shouldn't happen at this state
|
||||
return Ternary::False;
|
||||
}
|
||||
|
||||
CallOnExit finishDecoder([&] { FLAC__stream_decoder_finish(decoder); });
|
||||
|
||||
if (!FLAC__stream_decoder_process_until_end_of_metadata(decoder))
|
||||
return Ternary::False;
|
||||
|
||||
if (hasError)
|
||||
return Ternary::False;
|
||||
|
||||
return Ternary::True;
|
||||
}
|
||||
|
||||
std::shared_ptr<SoundBuffer> LoadFlacSoundBuffer(Stream& stream, const SoundBufferParams& parameters)
|
||||
Result<std::shared_ptr<SoundBuffer>, ResourceLoadingError> LoadFlacSoundBuffer(Stream& stream, const SoundBufferParams& parameters)
|
||||
{
|
||||
FLAC__StreamDecoder* decoder = FLAC__stream_decoder_new();
|
||||
CallOnExit freeDecoder([&] { FLAC__stream_decoder_delete(decoder); });
|
||||
@@ -236,34 +200,37 @@ namespace Nz
|
||||
if (status != FLAC__STREAM_DECODER_INIT_STATUS_OK)
|
||||
{
|
||||
NazaraWarning(FLAC__StreamDecoderInitStatusString[status]); //< an error shouldn't happen at this state
|
||||
return {};
|
||||
return Err(ResourceLoadingError::Internal);
|
||||
}
|
||||
|
||||
CallOnExit finishDecoder([&] { FLAC__stream_decoder_finish(decoder); });
|
||||
|
||||
if (!FLAC__stream_decoder_process_until_end_of_metadata(decoder))
|
||||
return Err(ResourceLoadingError::Unrecognized);
|
||||
|
||||
if (!FLAC__stream_decoder_process_until_end_of_stream(decoder))
|
||||
{
|
||||
NazaraError("flac decoding failed");
|
||||
return {};
|
||||
return Err(ResourceLoadingError::DecodingError);
|
||||
}
|
||||
|
||||
if (hasError)
|
||||
{
|
||||
NazaraError("an error occurred during decoding");
|
||||
return {};
|
||||
return Err(ResourceLoadingError::DecodingError);
|
||||
}
|
||||
|
||||
if (channelCount == 0 || frameCount == 0 || sampleCount == 0 || sampleRate == 0)
|
||||
{
|
||||
NazaraError("invalid metadata");
|
||||
return {};
|
||||
return Err(ResourceLoadingError::DecodingError);
|
||||
}
|
||||
|
||||
std::optional<AudioFormat> formatOpt = GuessAudioFormat(channelCount);
|
||||
if (!formatOpt)
|
||||
{
|
||||
NazaraError("unexpected channel count: " + std::to_string(channelCount));
|
||||
return {};
|
||||
return Err(ResourceLoadingError::Unsupported);
|
||||
}
|
||||
|
||||
AudioFormat format = *formatOpt;
|
||||
@@ -326,26 +293,26 @@ namespace Nz
|
||||
return m_sampleRate;
|
||||
}
|
||||
|
||||
bool Open(const std::filesystem::path& filePath, bool forceMono)
|
||||
Result<void, ResourceLoadingError> Open(const std::filesystem::path& filePath, const SoundStreamParams& parameters)
|
||||
{
|
||||
std::unique_ptr<File> file = std::make_unique<File>();
|
||||
if (!file->Open(filePath, OpenMode::ReadOnly))
|
||||
{
|
||||
NazaraError("failed to open stream from file: " + Error::GetLastError());
|
||||
return false;
|
||||
return Err(ResourceLoadingError::FailedToOpenFile);
|
||||
}
|
||||
|
||||
m_ownedStream = std::move(file);
|
||||
return Open(*m_ownedStream, forceMono);
|
||||
return Open(*m_ownedStream, parameters);
|
||||
}
|
||||
|
||||
bool Open(const void* data, std::size_t size, bool forceMono)
|
||||
Result<void, ResourceLoadingError> Open(const void* data, std::size_t size, const SoundStreamParams& parameters)
|
||||
{
|
||||
m_ownedStream = std::make_unique<MemoryView>(data, size);
|
||||
return Open(*m_ownedStream, forceMono);
|
||||
return Open(*m_ownedStream, parameters);
|
||||
}
|
||||
|
||||
bool Open(Stream& stream, bool forceMono)
|
||||
Result<void, ResourceLoadingError> Open(Stream& stream, const SoundStreamParams& parameters)
|
||||
{
|
||||
m_userData.stream = &stream;
|
||||
m_userData.errorCallback = [this](const FLAC__StreamDecoder* /*decoder*/, FLAC__StreamDecoderErrorStatus status)
|
||||
@@ -373,28 +340,25 @@ namespace Nz
|
||||
if (status != FLAC__STREAM_DECODER_INIT_STATUS_OK)
|
||||
{
|
||||
NazaraWarning(FLAC__StreamDecoderInitStatusString[status]); //< an error shouldn't happen at this state
|
||||
return {};
|
||||
return Err(ResourceLoadingError::Internal);
|
||||
}
|
||||
|
||||
CallOnExit finishDecoder([&] { FLAC__stream_decoder_finish(decoder); });
|
||||
|
||||
if (!FLAC__stream_decoder_process_until_end_of_metadata(decoder))
|
||||
{
|
||||
NazaraError("failed to decode metadata");
|
||||
return false;
|
||||
}
|
||||
return Err(ResourceLoadingError::Unrecognized);
|
||||
|
||||
std::optional<AudioFormat> formatOpt = GuessAudioFormat(m_channelCount);
|
||||
if (!formatOpt)
|
||||
{
|
||||
NazaraError("unexpected channel count: " + std::to_string(m_channelCount));
|
||||
return false;
|
||||
return Err(ResourceLoadingError::Unrecognized);
|
||||
}
|
||||
|
||||
m_format = *formatOpt;
|
||||
|
||||
// Mixing to mono will be done on the fly
|
||||
if (forceMono && m_format != AudioFormat::I16_Mono)
|
||||
if (parameters.forceMono && m_format != AudioFormat::I16_Mono)
|
||||
{
|
||||
m_mixToMono = true;
|
||||
m_sampleCount = frameCount;
|
||||
@@ -406,7 +370,7 @@ namespace Nz
|
||||
freeDecoder.Reset();
|
||||
m_decoder = decoder;
|
||||
|
||||
return true;
|
||||
return Ok();
|
||||
}
|
||||
|
||||
UInt64 Read(void* buffer, UInt64 sampleCount) override
|
||||
@@ -522,40 +486,28 @@ namespace Nz
|
||||
bool m_mixToMono;
|
||||
};
|
||||
|
||||
std::shared_ptr<SoundStream> LoadFlacSoundStreamFile(const std::filesystem::path& filePath, const SoundStreamParams& parameters)
|
||||
Result<std::shared_ptr<SoundStream>, ResourceLoadingError> LoadFlacSoundStreamFile(const std::filesystem::path& filePath, const SoundStreamParams& parameters)
|
||||
{
|
||||
std::shared_ptr<libflacStream> soundStream = std::make_shared<libflacStream>();
|
||||
if (!soundStream->Open(filePath, parameters.forceMono))
|
||||
{
|
||||
NazaraError("failed to open sound stream");
|
||||
return {};
|
||||
}
|
||||
Result<void, ResourceLoadingError> status = soundStream->Open(filePath, parameters);
|
||||
|
||||
return soundStream;
|
||||
return status.Map([&] { return std::move(soundStream); });
|
||||
}
|
||||
|
||||
std::shared_ptr<SoundStream> LoadFlacSoundStreamMemory(const void* data, std::size_t size, const SoundStreamParams& parameters)
|
||||
Result<std::shared_ptr<SoundStream>, ResourceLoadingError> LoadFlacSoundStreamMemory(const void* data, std::size_t size, const SoundStreamParams& parameters)
|
||||
{
|
||||
std::shared_ptr<libflacStream> soundStream = std::make_shared<libflacStream>();
|
||||
if (!soundStream->Open(data, size, parameters.forceMono))
|
||||
{
|
||||
NazaraError("failed to open music stream");
|
||||
return {};
|
||||
}
|
||||
Result<void, ResourceLoadingError> status = soundStream->Open(data, size, parameters);
|
||||
|
||||
return soundStream;
|
||||
return status.Map([&] { return std::move(soundStream); });
|
||||
}
|
||||
|
||||
std::shared_ptr<SoundStream> LoadFlacSoundStreamStream(Stream& stream, const SoundStreamParams& parameters)
|
||||
Result<std::shared_ptr<SoundStream>, ResourceLoadingError> LoadFlacSoundStreamStream(Stream& stream, const SoundStreamParams& parameters)
|
||||
{
|
||||
std::shared_ptr<libflacStream> soundStream = std::make_shared<libflacStream>();
|
||||
if (!soundStream->Open(stream, parameters.forceMono))
|
||||
{
|
||||
NazaraError("failed to open music stream");
|
||||
return {};
|
||||
}
|
||||
Result<void, ResourceLoadingError> status = soundStream->Open(stream, parameters);
|
||||
|
||||
return soundStream;
|
||||
return status.Map([&] { return std::move(soundStream); });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -565,8 +517,15 @@ namespace Nz
|
||||
{
|
||||
SoundBufferLoader::Entry loaderEntry;
|
||||
loaderEntry.extensionSupport = IsFlacSupported;
|
||||
loaderEntry.streamChecker = [](Stream& stream, const SoundBufferParams& parameters) { return CheckFlac(stream, parameters); };
|
||||
loaderEntry.streamLoader = LoadFlacSoundBuffer;
|
||||
loaderEntry.streamLoader = LoadFlacSoundBuffer;
|
||||
loaderEntry.parameterFilter = [](const SoundBufferParams& parameters)
|
||||
{
|
||||
bool skip;
|
||||
if (parameters.custom.GetBooleanParameter("SkipBuiltinFlacLoader", &skip) && skip)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
return loaderEntry;
|
||||
}
|
||||
@@ -575,10 +534,17 @@ namespace Nz
|
||||
{
|
||||
SoundStreamLoader::Entry loaderEntry;
|
||||
loaderEntry.extensionSupport = IsFlacSupported;
|
||||
loaderEntry.streamChecker = [](Stream& stream, const SoundStreamParams& parameters) { return CheckFlac(stream, parameters); };
|
||||
loaderEntry.fileLoader = LoadFlacSoundStreamFile;
|
||||
loaderEntry.memoryLoader = LoadFlacSoundStreamMemory;
|
||||
loaderEntry.streamLoader = LoadFlacSoundStreamStream;
|
||||
loaderEntry.fileLoader = LoadFlacSoundStreamFile;
|
||||
loaderEntry.memoryLoader = LoadFlacSoundStreamMemory;
|
||||
loaderEntry.streamLoader = LoadFlacSoundStreamStream;
|
||||
loaderEntry.parameterFilter = [](const SoundStreamParams& parameters)
|
||||
{
|
||||
bool skip;
|
||||
if (parameters.custom.GetBooleanParameter("SkipBuiltinFlacLoader", &skip) && skip)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
return loaderEntry;
|
||||
}
|
||||
|
||||
@@ -14,8 +14,9 @@
|
||||
#include <Nazara/Core/Stream.hpp>
|
||||
#include <Nazara/Utils/CallOnExit.hpp>
|
||||
#include <Nazara/Utils/Endianness.hpp>
|
||||
#include <frozen/string.h>
|
||||
#include <frozen/unordered_set.h>
|
||||
#include <optional>
|
||||
#include <set>
|
||||
|
||||
#define OV_EXCLUDE_STATIC_CALLBACKS
|
||||
#include <vorbis/vorbisfile.h>
|
||||
@@ -75,16 +76,16 @@ namespace Nz
|
||||
{
|
||||
switch (errCode)
|
||||
{
|
||||
case 0: return "no error";
|
||||
case 0: return "no error";
|
||||
case OV_EBADHEADER: return "invalid Vorbis bitstream header";
|
||||
case OV_EBADLINK: return "an invalid stream section was supplied to libvorbisfile, or the requested link is corrupt";
|
||||
case OV_EFAULT: return "internal logic fault";
|
||||
case OV_EINVAL: return "an invalid stream section was supplied to libvorbisfile, or the requested link is corrupt";
|
||||
case OV_EBADLINK: return "an invalid stream section was supplied to libvorbisfile, or the requested link is corrupt";
|
||||
case OV_EFAULT: return "internal logic fault";
|
||||
case OV_EINVAL: return "an invalid stream section was supplied to libvorbisfile, or the requested link is corrupt";
|
||||
case OV_ENOTVORBIS: return "bitstream does not contain any Vorbis data";
|
||||
case OV_EREAD: return "a read from media returned an error";
|
||||
case OV_EVERSION: return "Vorbis version mismatch";
|
||||
case OV_HOLE: return "there was an interruption in the data";
|
||||
default: return "unknown error";
|
||||
case OV_EREAD: return "a read from media returned an error";
|
||||
case OV_EVERSION: return "Vorbis version mismatch";
|
||||
case OV_HOLE: return "there was an interruption in the data";
|
||||
default: return "unknown error";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -116,38 +117,19 @@ namespace Nz
|
||||
return sampleCount - remainingBytes / sizeof(Int16);
|
||||
}
|
||||
|
||||
bool IsVorbisSupported(const std::string_view& extension)
|
||||
{
|
||||
static std::set<std::string_view> supportedExtensions = {
|
||||
"oga", "ogg", "ogm", "ogv", "ogx", "opus", "spx"
|
||||
};
|
||||
constexpr auto s_supportedExtensions = frozen::make_unordered_set<frozen::string>({ ".oga", ".ogg", ".ogm", ".ogv", ".ogx", ".opus", ".spx" });
|
||||
|
||||
return supportedExtensions.find(extension) != supportedExtensions.end();
|
||||
bool IsVorbisSupported(std::string_view extension)
|
||||
{
|
||||
return s_supportedExtensions.find(extension) != s_supportedExtensions.end();
|
||||
}
|
||||
|
||||
Ternary CheckOgg(Stream& stream, const ResourceParameters& parameters)
|
||||
{
|
||||
bool skip;
|
||||
if (parameters.custom.GetBooleanParameter("SkipBuiltinVorbisLoader", &skip) && skip)
|
||||
return Ternary::False;
|
||||
|
||||
OggVorbis_File file;
|
||||
if (ov_test_callbacks(&stream, &file, nullptr, 0, s_vorbisCallbacks) != 0)
|
||||
return Ternary::False;
|
||||
|
||||
ov_clear(&file);
|
||||
return Ternary::True;
|
||||
}
|
||||
|
||||
std::shared_ptr<SoundBuffer> LoadVorbisSoundBuffer(Stream& stream, const SoundBufferParams& parameters)
|
||||
Result<std::shared_ptr<SoundBuffer>, ResourceLoadingError> LoadVorbisSoundBuffer(Stream& stream, const SoundBufferParams& parameters)
|
||||
{
|
||||
OggVorbis_File file;
|
||||
int err = ov_open_callbacks(&stream, &file, nullptr, 0, s_vorbisCallbacks);
|
||||
if (err != 0)
|
||||
{
|
||||
NazaraError(VorbisErrToString(err));
|
||||
return {};
|
||||
}
|
||||
return Err(ResourceLoadingError::Unrecognized);
|
||||
|
||||
CallOnExit clearOnExit([&] { ov_clear(&file); });
|
||||
|
||||
@@ -158,7 +140,7 @@ namespace Nz
|
||||
if (!formatOpt)
|
||||
{
|
||||
NazaraError("unexpected channel count: " + std::to_string(info->channels));
|
||||
return {};
|
||||
return Err(ResourceLoadingError::Unsupported);
|
||||
}
|
||||
|
||||
AudioFormat format = *formatOpt;
|
||||
@@ -169,12 +151,12 @@ namespace Nz
|
||||
|
||||
UInt64 readSample = ReadOgg(&file, samples.get(), sampleCount);
|
||||
if (readSample == 0)
|
||||
return {};
|
||||
return Err(ResourceLoadingError::DecodingError);
|
||||
|
||||
if (readSample != sampleCount)
|
||||
{
|
||||
NazaraError("failed to read the whole file");
|
||||
return {};
|
||||
return Err(ResourceLoadingError::DecodingError);
|
||||
}
|
||||
|
||||
if (parameters.forceMono && format != AudioFormat::I16_Mono)
|
||||
@@ -230,33 +212,30 @@ namespace Nz
|
||||
return m_sampleRate;
|
||||
}
|
||||
|
||||
bool Open(const std::filesystem::path& filePath, bool forceMono)
|
||||
Result<void, ResourceLoadingError> Open(const std::filesystem::path& filePath, const SoundStreamParams& parameters)
|
||||
{
|
||||
std::unique_ptr<File> file = std::make_unique<File>();
|
||||
if (!file->Open(filePath, OpenMode::ReadOnly))
|
||||
{
|
||||
NazaraError("failed to open stream from file: " + Error::GetLastError());
|
||||
return false;
|
||||
return Err(ResourceLoadingError::FailedToOpenFile);
|
||||
}
|
||||
|
||||
m_ownedStream = std::move(file);
|
||||
return Open(*m_ownedStream, forceMono);
|
||||
return Open(*m_ownedStream, parameters);
|
||||
}
|
||||
|
||||
bool Open(const void* data, std::size_t size, bool forceMono)
|
||||
Result<void, ResourceLoadingError> Open(const void* data, std::size_t size, const SoundStreamParams& parameters)
|
||||
{
|
||||
m_ownedStream = std::make_unique<MemoryView>(data, size);
|
||||
return Open(*m_ownedStream, forceMono);
|
||||
return Open(*m_ownedStream, parameters);
|
||||
}
|
||||
|
||||
bool Open(Stream& stream, bool forceMono)
|
||||
Result<void, ResourceLoadingError> Open(Stream& stream, const SoundStreamParams& parameters)
|
||||
{
|
||||
int err = ov_open_callbacks(&stream, &m_decoder, nullptr, 0, s_vorbisCallbacks);
|
||||
if (err != 0)
|
||||
{
|
||||
NazaraError(VorbisErrToString(err));
|
||||
return {};
|
||||
}
|
||||
return Err(ResourceLoadingError::Unrecognized);
|
||||
|
||||
CallOnExit clearOnError([&]
|
||||
{
|
||||
@@ -271,7 +250,7 @@ namespace Nz
|
||||
if (!formatOpt)
|
||||
{
|
||||
NazaraError("unexpected channel count: " + std::to_string(info->channels));
|
||||
return {};
|
||||
return Err(ResourceLoadingError::Unsupported);
|
||||
}
|
||||
|
||||
m_format = *formatOpt;
|
||||
@@ -284,7 +263,7 @@ namespace Nz
|
||||
m_sampleRate = info->rate;
|
||||
|
||||
// Mixing to mono will be done on the fly
|
||||
if (forceMono && m_format != AudioFormat::I16_Mono)
|
||||
if (parameters.forceMono && m_format != AudioFormat::I16_Mono)
|
||||
{
|
||||
m_mixToMono = true;
|
||||
m_sampleCount = frameCount;
|
||||
@@ -294,7 +273,7 @@ namespace Nz
|
||||
|
||||
clearOnError.Reset();
|
||||
|
||||
return true;
|
||||
return Ok();
|
||||
}
|
||||
|
||||
UInt64 Read(void* buffer, UInt64 sampleCount) override
|
||||
@@ -347,40 +326,28 @@ namespace Nz
|
||||
bool m_mixToMono;
|
||||
};
|
||||
|
||||
std::shared_ptr<SoundStream> LoadVorbisSoundStreamFile(const std::filesystem::path& filePath, const SoundStreamParams& parameters)
|
||||
Result<std::shared_ptr<SoundStream>, ResourceLoadingError> LoadVorbisSoundStreamFile(const std::filesystem::path& filePath, const SoundStreamParams& parameters)
|
||||
{
|
||||
std::shared_ptr<libvorbisStream> soundStream = std::make_shared<libvorbisStream>();
|
||||
if (!soundStream->Open(filePath, parameters.forceMono))
|
||||
{
|
||||
NazaraError("failed to open sound stream");
|
||||
return {};
|
||||
}
|
||||
Result<void, ResourceLoadingError> status = soundStream->Open(filePath, parameters);
|
||||
|
||||
return soundStream;
|
||||
return status.Map([&] { return std::move(soundStream); });
|
||||
}
|
||||
|
||||
std::shared_ptr<SoundStream> LoadVorbisSoundStreamMemory(const void* data, std::size_t size, const SoundStreamParams& parameters)
|
||||
Result<std::shared_ptr<SoundStream>, ResourceLoadingError> LoadVorbisSoundStreamMemory(const void* data, std::size_t size, const SoundStreamParams& parameters)
|
||||
{
|
||||
std::shared_ptr<libvorbisStream> soundStream = std::make_shared<libvorbisStream>();
|
||||
if (!soundStream->Open(data, size, parameters.forceMono))
|
||||
{
|
||||
NazaraError("failed to open music stream");
|
||||
return {};
|
||||
}
|
||||
Result<void, ResourceLoadingError> status = soundStream->Open(data, size, parameters);
|
||||
|
||||
return soundStream;
|
||||
return status.Map([&] { return std::move(soundStream); });
|
||||
}
|
||||
|
||||
std::shared_ptr<SoundStream> LoadVorbisSoundStreamStream(Stream& stream, const SoundStreamParams& parameters)
|
||||
Result<std::shared_ptr<SoundStream>, ResourceLoadingError> LoadVorbisSoundStreamStream(Stream& stream, const SoundStreamParams& parameters)
|
||||
{
|
||||
std::shared_ptr<libvorbisStream> soundStream = std::make_shared<libvorbisStream>();
|
||||
if (!soundStream->Open(stream, parameters.forceMono))
|
||||
{
|
||||
NazaraError("failed to open music stream");
|
||||
return {};
|
||||
}
|
||||
Result<void, ResourceLoadingError> status = soundStream->Open(stream, parameters);
|
||||
|
||||
return soundStream;
|
||||
return status.Map([&] { return std::move(soundStream); });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -390,8 +357,15 @@ namespace Nz
|
||||
{
|
||||
SoundBufferLoader::Entry loaderEntry;
|
||||
loaderEntry.extensionSupport = IsVorbisSupported;
|
||||
loaderEntry.streamChecker = [](Stream& stream, const SoundBufferParams& parameters) { return CheckOgg(stream, parameters); };
|
||||
loaderEntry.streamLoader = LoadVorbisSoundBuffer;
|
||||
loaderEntry.parameterFilter = [](const SoundBufferParams& parameters)
|
||||
{
|
||||
bool skip;
|
||||
if (parameters.custom.GetBooleanParameter("SkipBuiltinVorbisLoader", &skip) && skip)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
return loaderEntry;
|
||||
}
|
||||
@@ -400,10 +374,17 @@ namespace Nz
|
||||
{
|
||||
SoundStreamLoader::Entry loaderEntry;
|
||||
loaderEntry.extensionSupport = IsVorbisSupported;
|
||||
loaderEntry.streamChecker = [](Stream& stream, const SoundStreamParams& parameters) { return CheckOgg(stream, parameters); };
|
||||
loaderEntry.fileLoader = LoadVorbisSoundStreamFile;
|
||||
loaderEntry.memoryLoader = LoadVorbisSoundStreamMemory;
|
||||
loaderEntry.streamLoader = LoadVorbisSoundStreamStream;
|
||||
loaderEntry.parameterFilter = [](const SoundStreamParams& parameters)
|
||||
{
|
||||
bool skip;
|
||||
if (parameters.custom.GetBooleanParameter("SkipBuiltinVorbisLoader", &skip) && skip)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
return loaderEntry;
|
||||
}
|
||||
|
||||
@@ -53,26 +53,10 @@ namespace Nz
|
||||
|
||||
bool IsMP3Supported(const std::string_view& extension)
|
||||
{
|
||||
return extension == "mp3";
|
||||
return extension == ".mp3";
|
||||
}
|
||||
|
||||
Ternary CheckMP3(Stream& stream, const ResourceParameters& parameters)
|
||||
{
|
||||
bool skip;
|
||||
if (parameters.custom.GetBooleanParameter("SkipBuiltinMP3Loader", &skip) && skip)
|
||||
return Ternary::False;
|
||||
|
||||
mp3dec_io_t io;
|
||||
io.read = &MP3ReadCallback;
|
||||
io.read_data = &stream;
|
||||
io.seek = &MP3SeekCallback;
|
||||
io.seek_data = &stream;
|
||||
|
||||
std::vector<UInt8> buffer(MINIMP3_BUF_SIZE);
|
||||
return (mp3dec_detect_cb(&io, buffer.data(), buffer.size()) == 0) ? Ternary::True : Ternary::False;
|
||||
}
|
||||
|
||||
std::shared_ptr<SoundBuffer> LoadMP3SoundBuffer(Stream& stream, const SoundBufferParams& parameters)
|
||||
Result<std::shared_ptr<SoundBuffer>, ResourceLoadingError> LoadMP3SoundBuffer(Stream& stream, const SoundBufferParams& parameters)
|
||||
{
|
||||
static_assert(std::is_same_v<mp3d_sample_t, Int16>);
|
||||
|
||||
@@ -91,12 +75,20 @@ namespace Nz
|
||||
|
||||
mp3dec_t dec;
|
||||
mp3dec_file_info_t info;
|
||||
std::vector<UInt8> buffer(MINIMP3_BUF_SIZE);
|
||||
int err = mp3dec_load_cb(&dec, &io, buffer.data(), buffer.size(), &info, nullptr, &userdata);
|
||||
|
||||
Nz::UInt64 cursorPos = stream.GetCursorPos();
|
||||
|
||||
std::unique_ptr<UInt8[]> buffer = std::make_unique<UInt8[]>(MINIMP3_BUF_SIZE);
|
||||
if (mp3dec_detect_cb(&io, buffer.get(), MINIMP3_BUF_SIZE) != 0)
|
||||
return Err(ResourceLoadingError::Unrecognized);
|
||||
|
||||
stream.SetCursorPos(cursorPos);
|
||||
|
||||
int err = mp3dec_load_cb(&dec, &io, buffer.get(), MINIMP3_BUF_SIZE, &info, nullptr, &userdata);
|
||||
if (err != 0)
|
||||
{
|
||||
NazaraError(MP3ErrorToString(err));
|
||||
return {};
|
||||
return Err(ResourceLoadingError::DecodingError);
|
||||
}
|
||||
|
||||
CallOnExit freeBuffer([&] { std::free(info.buffer); });
|
||||
@@ -105,7 +97,7 @@ namespace Nz
|
||||
if (!formatOpt)
|
||||
{
|
||||
NazaraError("unexpected channel count: " + std::to_string(info.channels));
|
||||
return {};
|
||||
return Err(ResourceLoadingError::Unsupported);
|
||||
}
|
||||
|
||||
AudioFormat format = *formatOpt;
|
||||
@@ -166,37 +158,45 @@ namespace Nz
|
||||
return m_sampleRate;
|
||||
}
|
||||
|
||||
bool Open(const std::filesystem::path& filePath, bool forceMono)
|
||||
Result<void, ResourceLoadingError> Open(const std::filesystem::path& filePath, const SoundStreamParams& parameters)
|
||||
{
|
||||
std::unique_ptr<File> file = std::make_unique<File>();
|
||||
if (!file->Open(filePath, OpenMode::ReadOnly))
|
||||
{
|
||||
NazaraError("failed to open stream from file: " + Error::GetLastError());
|
||||
return false;
|
||||
return Err(ResourceLoadingError::FailedToOpenFile);
|
||||
}
|
||||
|
||||
m_ownedStream = std::move(file);
|
||||
return Open(*m_ownedStream, forceMono);
|
||||
return Open(*m_ownedStream, parameters);
|
||||
}
|
||||
|
||||
bool Open(const void* data, std::size_t size, bool forceMono)
|
||||
Result<void, ResourceLoadingError> Open(const void* data, std::size_t size, const SoundStreamParams& parameters)
|
||||
{
|
||||
m_ownedStream = std::make_unique<MemoryView>(data, size);
|
||||
return Open(*m_ownedStream, forceMono);
|
||||
return Open(*m_ownedStream, parameters);
|
||||
}
|
||||
|
||||
bool Open(Stream& stream, bool forceMono)
|
||||
Result<void, ResourceLoadingError> Open(Stream& stream, const SoundStreamParams& parameters)
|
||||
{
|
||||
m_io.read = &MP3ReadCallback;
|
||||
m_io.read_data = &stream;
|
||||
m_io.seek = &MP3SeekCallback;
|
||||
m_io.seek_data = &stream;
|
||||
|
||||
Nz::UInt64 cursorPos = stream.GetCursorPos();
|
||||
|
||||
std::unique_ptr<UInt8[]> buffer = std::make_unique<UInt8[]>(MINIMP3_BUF_SIZE);
|
||||
if (mp3dec_detect_cb(&m_io, buffer.get(), MINIMP3_BUF_SIZE) != 0)
|
||||
return Err(ResourceLoadingError::Unrecognized);
|
||||
|
||||
stream.SetCursorPos(cursorPos);
|
||||
|
||||
int err = mp3dec_ex_open_cb(&m_decoder, &m_io, MP3D_SEEK_TO_SAMPLE);
|
||||
if (err != 0)
|
||||
{
|
||||
NazaraError(MP3ErrorToString(err));
|
||||
return {};
|
||||
return Err(ResourceLoadingError::DecodingError);
|
||||
}
|
||||
|
||||
CallOnExit resetOnError([this]
|
||||
@@ -209,7 +209,7 @@ namespace Nz
|
||||
if (!formatOpt)
|
||||
{
|
||||
NazaraError("unexpected channel count: " + std::to_string(m_decoder.info.channels));
|
||||
return false;
|
||||
return Err(ResourceLoadingError::Unsupported);
|
||||
}
|
||||
|
||||
m_format = *formatOpt;
|
||||
@@ -219,7 +219,7 @@ namespace Nz
|
||||
m_sampleRate = m_decoder.info.hz;
|
||||
|
||||
// Mixing to mono will be done on the fly
|
||||
if (forceMono && m_format != AudioFormat::I16_Mono)
|
||||
if (parameters.forceMono && m_format != AudioFormat::I16_Mono)
|
||||
{
|
||||
m_mixToMono = true;
|
||||
m_sampleCount = static_cast<UInt32>(m_decoder.samples / m_decoder.info.channels);
|
||||
@@ -229,7 +229,7 @@ namespace Nz
|
||||
|
||||
resetOnError.Reset();
|
||||
|
||||
return true;
|
||||
return Ok();
|
||||
}
|
||||
|
||||
UInt64 Read(void* buffer, UInt64 sampleCount) override
|
||||
@@ -282,40 +282,28 @@ namespace Nz
|
||||
bool m_mixToMono;
|
||||
};
|
||||
|
||||
std::shared_ptr<SoundStream> MP3LoadSoundStreamFile(const std::filesystem::path& filePath, const SoundStreamParams& parameters)
|
||||
Result<std::shared_ptr<SoundStream>, ResourceLoadingError> LoadMP3SoundStreamFile(const std::filesystem::path& filePath, const SoundStreamParams& parameters)
|
||||
{
|
||||
std::shared_ptr<minimp3Stream> soundStream = std::make_shared<minimp3Stream>();
|
||||
if (!soundStream->Open(filePath, parameters.forceMono))
|
||||
{
|
||||
NazaraError("failed to open sound stream");
|
||||
return {};
|
||||
}
|
||||
Result<void, ResourceLoadingError> status = soundStream->Open(filePath, parameters);
|
||||
|
||||
return soundStream;
|
||||
return status.Map([&] { return std::move(soundStream); });
|
||||
}
|
||||
|
||||
std::shared_ptr<SoundStream> MP3LoadSoundStreamMemory(const void* data, std::size_t size, const SoundStreamParams& parameters)
|
||||
Result<std::shared_ptr<SoundStream>, ResourceLoadingError> LoadMP3SoundStreamMemory(const void* data, std::size_t size, const SoundStreamParams& parameters)
|
||||
{
|
||||
std::shared_ptr<minimp3Stream> soundStream = std::make_shared<minimp3Stream>();
|
||||
if (!soundStream->Open(data, size, parameters.forceMono))
|
||||
{
|
||||
NazaraError("failed to open music stream");
|
||||
return {};
|
||||
}
|
||||
Result<void, ResourceLoadingError> status = soundStream->Open(data, size, parameters);
|
||||
|
||||
return soundStream;
|
||||
return status.Map([&] { return std::move(soundStream); });
|
||||
}
|
||||
|
||||
std::shared_ptr<SoundStream> MP3LoadSoundStreamStream(Stream& stream, const SoundStreamParams& parameters)
|
||||
Result<std::shared_ptr<SoundStream>, ResourceLoadingError> LoadMP3SoundStreamStream(Stream& stream, const SoundStreamParams& parameters)
|
||||
{
|
||||
std::shared_ptr<minimp3Stream> soundStream = std::make_shared<minimp3Stream>();
|
||||
if (!soundStream->Open(stream, parameters.forceMono))
|
||||
{
|
||||
NazaraError("failed to open music stream");
|
||||
return {};
|
||||
}
|
||||
Result<void, ResourceLoadingError> status = soundStream->Open(stream, parameters);
|
||||
|
||||
return soundStream;
|
||||
return status.Map([&] { return std::move(soundStream); });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -325,8 +313,15 @@ namespace Nz
|
||||
{
|
||||
SoundBufferLoader::Entry loaderEntry;
|
||||
loaderEntry.extensionSupport = IsMP3Supported;
|
||||
loaderEntry.streamChecker = [](Stream& stream, const SoundBufferParams& parameters) { return CheckMP3(stream, parameters); };
|
||||
loaderEntry.streamLoader = LoadMP3SoundBuffer;
|
||||
loaderEntry.parameterFilter = [](const SoundBufferParams& parameters)
|
||||
{
|
||||
bool skip;
|
||||
if (parameters.custom.GetBooleanParameter("SkipBuiltinMP3Loader", &skip) && skip)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
return loaderEntry;
|
||||
}
|
||||
@@ -335,10 +330,17 @@ namespace Nz
|
||||
{
|
||||
SoundStreamLoader::Entry loaderEntry;
|
||||
loaderEntry.extensionSupport = IsMP3Supported;
|
||||
loaderEntry.streamChecker = [](Stream& stream, const SoundStreamParams& parameters) { return CheckMP3(stream, parameters); };
|
||||
loaderEntry.fileLoader = MP3LoadSoundStreamFile;
|
||||
loaderEntry.memoryLoader = MP3LoadSoundStreamMemory;
|
||||
loaderEntry.streamLoader = MP3LoadSoundStreamStream;
|
||||
loaderEntry.fileLoader = LoadMP3SoundStreamFile;
|
||||
loaderEntry.memoryLoader = LoadMP3SoundStreamMemory;
|
||||
loaderEntry.streamLoader = LoadMP3SoundStreamStream;
|
||||
loaderEntry.parameterFilter = [](const SoundStreamParams& parameters)
|
||||
{
|
||||
bool skip;
|
||||
if (parameters.custom.GetBooleanParameter("SkipBuiltinMP3Loader", &skip) && skip)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
return loaderEntry;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user