1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-07-27 06:27:45 +00:00

LibAudio: Move WavLoader to Core::Stream APIs

This makes the code much more readable and concise, reduces the size of
the WavLoader class itself, moves almost all fallible initialization out
of the constructor and should provide better error handling in general.

Also, a lot of now-unnecessary imports are removed.
This commit is contained in:
kleines Filmröllchen 2022-04-23 11:08:45 +02:00 committed by Linus Groh
parent 81a336ea45
commit a32d675164
2 changed files with 42 additions and 69 deletions

View file

@ -7,13 +7,11 @@
#include "WavLoader.h" #include "WavLoader.h"
#include "Buffer.h" #include "Buffer.h"
#include "LoaderError.h"
#include <AK/Debug.h> #include <AK/Debug.h>
#include <AK/FixedArray.h> #include <AK/FixedArray.h>
#include <AK/NumericLimits.h>
#include <AK/OwnPtr.h>
#include <AK/Try.h> #include <AK/Try.h>
#include <LibCore/File.h> #include <LibCore/MemoryStream.h>
#include <LibCore/FileStream.h>
namespace Audio { namespace Audio {
@ -22,35 +20,28 @@ static constexpr size_t const maximum_wav_size = 1 * GiB; // FIXME: is there a m
WavLoaderPlugin::WavLoaderPlugin(StringView path) WavLoaderPlugin::WavLoaderPlugin(StringView path)
: m_file(Core::File::construct(path)) : m_file(Core::File::construct(path))
{ {
if (!m_file->open(Core::OpenMode::ReadOnly)) {
m_error = LoaderError { String::formatted("Can't open file: {}", m_file->error_string()) };
return;
}
m_stream = make<Core::InputFileStream>(*m_file);
} }
MaybeLoaderError WavLoaderPlugin::initialize() MaybeLoaderError WavLoaderPlugin::initialize()
{ {
if (m_error.has_value()) if (m_backing_memory.has_value())
return m_error.release_value(); m_stream = LOADER_TRY(Core::Stream::MemoryStream::construct(m_backing_memory.value()));
else
m_stream = LOADER_TRY(Core::Stream::File::open(m_file->filename(), Core::Stream::OpenMode::Read));
TRY(parse_header()); TRY(parse_header());
return {}; return {};
} }
WavLoaderPlugin::WavLoaderPlugin(Bytes const& buffer) WavLoaderPlugin::WavLoaderPlugin(Bytes const& buffer)
: m_backing_memory(buffer)
{ {
m_stream = make<InputMemoryStream>(buffer);
if (!m_stream) {
m_error = LoaderError { String::formatted("Can't open memory stream") };
return;
}
m_memory_stream = static_cast<InputMemoryStream*>(m_stream.ptr());
} }
LoaderSamples WavLoaderPlugin::get_more_samples(size_t max_samples_to_read_from_input) LoaderSamples WavLoaderPlugin::get_more_samples(size_t max_samples_to_read_from_input)
{ {
if (!m_stream) if (!m_stream)
return LoaderError { LoaderError::Category::Internal, static_cast<size_t>(m_loaded_samples), "No stream" }; return LoaderError { LoaderError::Category::Internal, static_cast<size_t>(m_loaded_samples), "No stream; initialization failed" };
auto remaining_samples = m_total_samples - m_loaded_samples; auto remaining_samples = m_total_samples - m_loaded_samples;
if (remaining_samples <= 0) if (remaining_samples <= 0)
@ -71,13 +62,8 @@ LoaderSamples WavLoaderPlugin::get_more_samples(size_t max_samples_to_read_from_
bytes_to_read, m_num_channels, m_sample_rate, bytes_to_read, m_num_channels, m_sample_rate,
pcm_bits_per_sample(m_sample_format), sample_format_name(m_sample_format)); pcm_bits_per_sample(m_sample_format), sample_format_name(m_sample_format));
auto sample_data_result = ByteBuffer::create_zeroed(bytes_to_read); auto sample_data = LOADER_TRY(ByteBuffer::create_zeroed(bytes_to_read));
if (sample_data_result.is_error()) LOADER_TRY(m_stream->read(sample_data.bytes()));
return LoaderError { LoaderError::Category::IO, m_loaded_samples, "Couldn't allocate sample buffer" };
auto sample_data = sample_data_result.release_value();
m_stream->read_or_error(sample_data.bytes());
if (m_stream->handle_any_error())
return LoaderError { LoaderError::Category::IO, m_loaded_samples, "Stream read error" };
auto buffer = LegacyBuffer::from_pcm_data( auto buffer = LegacyBuffer::from_pcm_data(
sample_data.bytes(), sample_data.bytes(),
@ -100,12 +86,7 @@ MaybeLoaderError WavLoaderPlugin::seek(int sample_index)
size_t sample_offset = m_byte_offset_of_data_samples + static_cast<size_t>(sample_index * m_num_channels * (pcm_bits_per_sample(m_sample_format) / 8)); size_t sample_offset = m_byte_offset_of_data_samples + static_cast<size_t>(sample_index * m_num_channels * (pcm_bits_per_sample(m_sample_format) / 8));
// AK::InputStream does not define seek, hence the special-cases for file and stream. LOADER_TRY(m_stream->seek(sample_offset, Core::Stream::SeekMode::SetPosition));
if (m_file) {
m_file->seek(static_cast<int>(sample_offset));
} else {
m_memory_stream->seek(sample_offset);
}
m_loaded_samples = sample_index; m_loaded_samples = sample_index;
return {}; return {};
@ -120,29 +101,23 @@ MaybeLoaderError WavLoaderPlugin::parse_header()
bool ok = true; bool ok = true;
size_t bytes_read = 0; size_t bytes_read = 0;
auto read_u8 = [&]() -> u8 { auto read_u8 = [&]() -> ErrorOr<u8, LoaderError> {
u8 value; u8 value;
*m_stream >> value; LOADER_TRY(m_stream->read(Bytes { &value, 1 }));
if (m_stream->handle_any_error())
ok = false;
bytes_read += 1; bytes_read += 1;
return value; return value;
}; };
auto read_u16 = [&]() -> u16 { auto read_u16 = [&]() -> ErrorOr<u16, LoaderError> {
u16 value; u16 value;
*m_stream >> value; LOADER_TRY(m_stream->read(Bytes { &value, 2 }));
if (m_stream->handle_any_error())
ok = false;
bytes_read += 2; bytes_read += 2;
return value; return value;
}; };
auto read_u32 = [&]() -> u32 { auto read_u32 = [&]() -> ErrorOr<u32, LoaderError> {
u32 value; u32 value;
*m_stream >> value; LOADER_TRY(m_stream->read(Bytes { &value, 4 }));
if (m_stream->handle_any_error())
ok = false;
bytes_read += 4; bytes_read += 4;
return value; return value;
}; };
@ -153,45 +128,45 @@ MaybeLoaderError WavLoaderPlugin::parse_header()
return LoaderError { category, String::formatted("Parsing failed: {}", msg) }; \ return LoaderError { category, String::formatted("Parsing failed: {}", msg) }; \
} while (0) } while (0)
u32 riff = read_u32(); u32 riff = TRY(read_u32());
ok = ok && riff == 0x46464952; // "RIFF" ok = ok && riff == 0x46464952; // "RIFF"
CHECK_OK(LoaderError::Category::Format, "RIFF header"); CHECK_OK(LoaderError::Category::Format, "RIFF header");
u32 sz = read_u32(); u32 sz = TRY(read_u32());
ok = ok && sz < maximum_wav_size; ok = ok && sz < maximum_wav_size;
CHECK_OK(LoaderError::Category::Format, "File size"); CHECK_OK(LoaderError::Category::Format, "File size");
u32 wave = read_u32(); u32 wave = TRY(read_u32());
ok = ok && wave == 0x45564157; // "WAVE" ok = ok && wave == 0x45564157; // "WAVE"
CHECK_OK(LoaderError::Category::Format, "WAVE header"); CHECK_OK(LoaderError::Category::Format, "WAVE header");
u32 fmt_id = read_u32(); u32 fmt_id = TRY(read_u32());
ok = ok && fmt_id == 0x20746D66; // "fmt " ok = ok && fmt_id == 0x20746D66; // "fmt "
CHECK_OK(LoaderError::Category::Format, "FMT header"); CHECK_OK(LoaderError::Category::Format, "FMT header");
u32 fmt_size = read_u32(); u32 fmt_size = TRY(read_u32());
ok = ok && (fmt_size == 16 || fmt_size == 18 || fmt_size == 40); ok = ok && (fmt_size == 16 || fmt_size == 18 || fmt_size == 40);
CHECK_OK(LoaderError::Category::Format, "FMT size"); CHECK_OK(LoaderError::Category::Format, "FMT size");
u16 audio_format = read_u16(); u16 audio_format = TRY(read_u16());
CHECK_OK(LoaderError::Category::Format, "Audio format"); // incomplete read check CHECK_OK(LoaderError::Category::Format, "Audio format"); // incomplete read check
ok = ok && (audio_format == WAVE_FORMAT_PCM || audio_format == WAVE_FORMAT_IEEE_FLOAT || audio_format == WAVE_FORMAT_EXTENSIBLE); ok = ok && (audio_format == WAVE_FORMAT_PCM || audio_format == WAVE_FORMAT_IEEE_FLOAT || audio_format == WAVE_FORMAT_EXTENSIBLE);
CHECK_OK(LoaderError::Category::Unimplemented, "Audio format PCM/Float"); // value check CHECK_OK(LoaderError::Category::Unimplemented, "Audio format PCM/Float"); // value check
m_num_channels = read_u16(); m_num_channels = TRY(read_u16());
ok = ok && (m_num_channels == 1 || m_num_channels == 2); ok = ok && (m_num_channels == 1 || m_num_channels == 2);
CHECK_OK(LoaderError::Category::Unimplemented, "Channel count"); CHECK_OK(LoaderError::Category::Unimplemented, "Channel count");
m_sample_rate = read_u32(); m_sample_rate = TRY(read_u32());
CHECK_OK(LoaderError::Category::IO, "Sample rate"); CHECK_OK(LoaderError::Category::IO, "Sample rate");
read_u32(); TRY(read_u32());
CHECK_OK(LoaderError::Category::IO, "Data rate"); CHECK_OK(LoaderError::Category::IO, "Data rate");
u16 block_size_bytes = read_u16(); u16 block_size_bytes = TRY(read_u16());
CHECK_OK(LoaderError::Category::IO, "Block size"); CHECK_OK(LoaderError::Category::IO, "Block size");
u16 bits_per_sample = read_u16(); u16 bits_per_sample = TRY(read_u16());
CHECK_OK(LoaderError::Category::IO, "Bits per sample"); CHECK_OK(LoaderError::Category::IO, "Bits per sample");
if (audio_format == WAVE_FORMAT_EXTENSIBLE) { if (audio_format == WAVE_FORMAT_EXTENSIBLE) {
@ -200,12 +175,12 @@ MaybeLoaderError WavLoaderPlugin::parse_header()
// Discard everything until the GUID. // Discard everything until the GUID.
// We've already read 16 bytes from the stream. The GUID starts in another 8 bytes. // We've already read 16 bytes from the stream. The GUID starts in another 8 bytes.
read_u32(); TRY(read_u32());
read_u32(); TRY(read_u32());
CHECK_OK(LoaderError::Category::IO, "Discard until GUID"); CHECK_OK(LoaderError::Category::IO, "Discard until GUID");
// Get the underlying audio format from the first two bytes of GUID // Get the underlying audio format from the first two bytes of GUID
u16 guid_subformat = read_u16(); u16 guid_subformat = TRY(read_u16());
ok = ok && (guid_subformat == WAVE_FORMAT_PCM || guid_subformat == WAVE_FORMAT_IEEE_FLOAT); ok = ok && (guid_subformat == WAVE_FORMAT_PCM || guid_subformat == WAVE_FORMAT_IEEE_FLOAT);
CHECK_OK(LoaderError::Category::Unimplemented, "GUID SubFormat"); CHECK_OK(LoaderError::Category::Unimplemented, "GUID SubFormat");
@ -247,22 +222,22 @@ MaybeLoaderError WavLoaderPlugin::parse_header()
u32 data_size = 0; u32 data_size = 0;
u8 search_byte = 0; u8 search_byte = 0;
while (true) { while (true) {
search_byte = read_u8(); search_byte = TRY(read_u8());
CHECK_OK(LoaderError::Category::IO, "Reading byte searching for data"); CHECK_OK(LoaderError::Category::IO, "Reading byte searching for data");
if (search_byte != 0x64) // D if (search_byte != 0x64) // D
continue; continue;
search_byte = read_u8(); search_byte = TRY(read_u8());
CHECK_OK(LoaderError::Category::IO, "Reading next byte searching for data"); CHECK_OK(LoaderError::Category::IO, "Reading next byte searching for data");
if (search_byte != 0x61) // A if (search_byte != 0x61) // A
continue; continue;
u16 search_remaining = read_u16(); u16 search_remaining = TRY(read_u16());
CHECK_OK(LoaderError::Category::IO, "Reading remaining bytes searching for data"); CHECK_OK(LoaderError::Category::IO, "Reading remaining bytes searching for data");
if (search_remaining != 0x6174) // TA if (search_remaining != 0x6174) // TA
continue; continue;
data_size = read_u32(); data_size = TRY(read_u32());
found_data = true; found_data = true;
break; break;
} }

View file

@ -7,18 +7,15 @@
#pragma once #pragma once
#include <AK/MemoryStream.h>
#include <AK/OwnPtr.h> #include <AK/OwnPtr.h>
#include <AK/RefPtr.h> #include <AK/RefPtr.h>
#include <AK/Span.h> #include <AK/Span.h>
#include <AK/Stream.h>
#include <AK/String.h> #include <AK/String.h>
#include <AK/StringView.h> #include <AK/StringView.h>
#include <AK/WeakPtr.h> #include <AK/WeakPtr.h>
#include <LibAudio/Buffer.h>
#include <LibAudio/Loader.h> #include <LibAudio/Loader.h>
#include <LibCore/File.h> #include <LibCore/File.h>
#include <LibCore/FileStream.h> #include <LibCore/Stream.h>
namespace Audio { namespace Audio {
@ -29,7 +26,7 @@ static constexpr unsigned const WAVE_FORMAT_ALAW = 0x0006; // 8-bit ITU-T
static constexpr unsigned const WAVE_FORMAT_MULAW = 0x0007; // 8-bit ITU-T G.711 µ-law static constexpr unsigned const WAVE_FORMAT_MULAW = 0x0007; // 8-bit ITU-T G.711 µ-law
static constexpr unsigned const WAVE_FORMAT_EXTENSIBLE = 0xFFFE; // Determined by SubFormat static constexpr unsigned const WAVE_FORMAT_EXTENSIBLE = 0xFFFE; // Determined by SubFormat
// Parses a WAV file and produces an Audio::LegacyBuffer. // Parses and reads audio data from a WAV file.
class WavLoaderPlugin : public LoaderPlugin { class WavLoaderPlugin : public LoaderPlugin {
public: public:
explicit WavLoaderPlugin(StringView path); explicit WavLoaderPlugin(StringView path);
@ -56,10 +53,11 @@ public:
private: private:
MaybeLoaderError parse_header(); MaybeLoaderError parse_header();
// This is only kept around for compatibility for now.
RefPtr<Core::File> m_file; RefPtr<Core::File> m_file;
OwnPtr<AK::InputStream> m_stream; OwnPtr<Core::Stream::SeekableStream> m_stream;
AK::InputMemoryStream* m_memory_stream; // The constructor might set this so that we can initialize the data stream later.
Optional<LoaderError> m_error {}; Optional<Bytes const&> m_backing_memory;
u32 m_sample_rate { 0 }; u32 m_sample_rate { 0 };
u16 m_num_channels { 0 }; u16 m_num_channels { 0 };