diff --git a/Libraries/LibAudio/Buffer.cpp b/Libraries/LibAudio/Buffer.cpp index 29bb24e908..c23576482b 100644 --- a/Libraries/LibAudio/Buffer.cpp +++ b/Libraries/LibAudio/Buffer.cpp @@ -97,14 +97,14 @@ static double read_norm_sample_8(InputMemoryStream& stream) stream >> sample; return double(sample) / NumericLimits::max(); } - + RefPtr Buffer::from_pcm_data(ReadonlyBytes data, ResampleHelper& resampler, int num_channels, int bits_per_sample) { InputMemoryStream stream { data }; return from_pcm_stream(stream, resampler, num_channels, bits_per_sample, data.size() / (bits_per_sample / 8)); } -RefPtr Buffer::from_pcm_stream(InputMemoryStream &stream, ResampleHelper& resampler, int num_channels, int bits_per_sample, int num_samples) +RefPtr Buffer::from_pcm_stream(InputMemoryStream& stream, ResampleHelper& resampler, int num_channels, int bits_per_sample, int num_samples) { Vector fdata; fdata.ensure_capacity(num_samples); diff --git a/Libraries/LibAudio/Loader.cpp b/Libraries/LibAudio/Loader.cpp index f559ea6406..f1444318b5 100644 --- a/Libraries/LibAudio/Loader.cpp +++ b/Libraries/LibAudio/Loader.cpp @@ -36,4 +36,12 @@ Loader::Loader(const StringView& path) m_plugin = nullptr; } +Loader::Loader(const ByteBuffer& buffer) +{ + m_plugin = make(buffer); + if (m_plugin->sniff()) + return; + m_plugin = nullptr; +} + } diff --git a/Libraries/LibAudio/Loader.h b/Libraries/LibAudio/Loader.h index 9d37be373b..bb2e2f342f 100644 --- a/Libraries/LibAudio/Loader.h +++ b/Libraries/LibAudio/Loader.h @@ -26,6 +26,7 @@ #pragma once +#include #include #include #include @@ -58,6 +59,7 @@ public: class Loader : public RefCounted { public: static NonnullRefPtr create(const StringView& path) { return adopt(*new Loader(path)); } + static NonnullRefPtr create(const ByteBuffer& buffer) { return adopt(*new Loader(buffer)); } bool has_error() const { return m_plugin ? m_plugin->has_error() : true; } const char* error_string() const { return m_plugin ? m_plugin->error_string() : "No loader plugin available"; } @@ -84,6 +86,7 @@ public: private: Loader(const StringView& path); + Loader(const ByteBuffer& buffer); mutable OwnPtr m_plugin; }; diff --git a/Libraries/LibAudio/WavLoader.cpp b/Libraries/LibAudio/WavLoader.cpp index 49737b337c..adac8c7a0d 100644 --- a/Libraries/LibAudio/WavLoader.cpp +++ b/Libraries/LibAudio/WavLoader.cpp @@ -24,9 +24,9 @@ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#include #include #include +#include #include #include #include @@ -48,6 +48,21 @@ WavLoaderPlugin::WavLoaderPlugin(const StringView& path) m_resampler = make(m_sample_rate, 44100); } +WavLoaderPlugin::WavLoaderPlugin(const ByteBuffer& buffer) +{ + m_stream = make(buffer); + if (!m_stream) { + m_error_string = String::formatted("Can't open memory stream"); + return; + } + + valid = parse_header(); + if (!valid) + return; + + m_resampler = make(m_sample_rate, 44100); +} + bool WavLoaderPlugin::sniff() { return valid; @@ -58,14 +73,18 @@ RefPtr WavLoaderPlugin::get_more_samples(size_t max_bytes_to_read_from_i #ifdef AWAVLOADER_DEBUG dbgln("Read WAV of format PCM with num_channels {} sample rate {}, bits per sample {}", m_num_channels, m_sample_rate, m_bits_per_sample); #endif - - auto raw_samples = m_file->read(max_bytes_to_read_from_input); - if (raw_samples.is_empty()) - return nullptr; - - auto buffer = Buffer::from_pcm_data(raw_samples, *m_resampler, m_num_channels, m_bits_per_sample); + size_t samples_to_read = static_cast(max_bytes_to_read_from_input) / (m_num_channels * (m_bits_per_sample / 8)); + RefPtr buffer; + if (m_file) { + auto raw_samples = m_file->read(max_bytes_to_read_from_input); + if (raw_samples.is_empty()) + return nullptr; + buffer = Buffer::from_pcm_data(raw_samples, *m_resampler, m_num_channels, m_bits_per_sample); + } else { + buffer = Buffer::from_pcm_stream(*m_stream, *m_resampler, m_num_channels, m_bits_per_sample, samples_to_read); + } //Buffer contains normalized samples, but m_loaded_samples should contain the amount of actually loaded samples - m_loaded_samples += static_cast(max_bytes_to_read_from_input) / (m_num_channels * (m_bits_per_sample / 8)); + m_loaded_samples += samples_to_read; m_loaded_samples = min(m_total_samples, m_loaded_samples); return buffer; } @@ -76,7 +95,12 @@ void WavLoaderPlugin::seek(const int position) return; m_loaded_samples = position; - m_file->seek(position * m_num_channels * (m_bits_per_sample / 8)); + size_t byte_position = position * m_num_channels * (m_bits_per_sample / 8); + + if (m_file) + m_file->seek(byte_position); + else + m_stream->seek(byte_position); } void WavLoaderPlugin::reset() @@ -86,73 +110,95 @@ void WavLoaderPlugin::reset() bool WavLoaderPlugin::parse_header() { - Core::IODeviceStreamReader stream(*m_file); + OwnPtr file_stream; + bool ok = true; -#define CHECK_OK(msg) \ - do { \ - if (stream.handle_read_failure()) { \ - m_error_string = String::formatted("Premature stream EOF at {}", msg); \ - return {}; \ - } \ - if (!ok) { \ - m_error_string = String::formatted("Parsing failed: {}", msg); \ - return {}; \ - } else { \ - dbgln("{} is OK!", msg); \ - } \ + if (m_file) + file_stream = make(*m_file); + + auto read_u8 = [&]() -> u8 { + u8 value; + if (m_file) { + *file_stream >> value; + if (file_stream->handle_read_failure()) { + ok = false; + } + } + return value; + }; + + auto read_u16 = [&]() -> u16 { + u16 value; + if (m_file) { + *file_stream >> value; + if (file_stream->handle_read_failure()) { + ok = false; + } + } + return value; + }; + + auto read_u32 = [&]() -> u32 { + u32 value; + if (m_file) { + *file_stream >> value; + if (file_stream->handle_read_failure()) { + ok = false; + } + } + return value; + }; + +#define CHECK_OK(msg) \ + do { \ + if (!ok) { \ + m_error_string = String::formatted("Parsing failed: {}", msg); \ + return {}; \ + } \ } while (0); - bool ok = true; - u32 riff; - stream >> riff; + u32 riff = read_u32(); ok = ok && riff == 0x46464952; // "RIFF" CHECK_OK("RIFF header"); - u32 sz; - stream >> sz; + u32 sz = read_u32(); ok = ok && sz < 1024 * 1024 * 1024; // arbitrary CHECK_OK("File size"); ASSERT(sz < 1024 * 1024 * 1024); - u32 wave; - stream >> wave; + u32 wave = read_u32(); ok = ok && wave == 0x45564157; // "WAVE" CHECK_OK("WAVE header"); - u32 fmt_id; - stream >> fmt_id; + u32 fmt_id = read_u32(); ok = ok && fmt_id == 0x20746D66; // "FMT" CHECK_OK("FMT header"); - u32 fmt_size; - stream >> fmt_size; + u32 fmt_size = read_u32(); ok = ok && fmt_size == 16; CHECK_OK("FMT size"); ASSERT(fmt_size == 16); - u16 audio_format; - stream >> audio_format; + u16 audio_format = read_u16(); CHECK_OK("Audio format"); // incomplete read check ok = ok && audio_format == 1; // WAVE_FORMAT_PCM ASSERT(audio_format == 1); CHECK_OK("Audio format"); // value check - stream >> m_num_channels; + m_num_channels = read_u16(); ok = ok && (m_num_channels == 1 || m_num_channels == 2); CHECK_OK("Channel count"); - stream >> m_sample_rate; + m_sample_rate = read_u32(); CHECK_OK("Sample rate"); - u32 byte_rate; - stream >> byte_rate; + read_u32(); CHECK_OK("Byte rate"); - u16 block_align; - stream >> block_align; + read_u16(); CHECK_OK("Block align"); - stream >> m_bits_per_sample; + m_bits_per_sample = read_u16(); CHECK_OK("Bits per sample"); // incomplete read check ok = ok && (m_bits_per_sample == 8 || m_bits_per_sample == 16 || m_bits_per_sample == 24); ASSERT(m_bits_per_sample == 8 || m_bits_per_sample == 16 || m_bits_per_sample == 24); @@ -163,23 +209,22 @@ bool WavLoaderPlugin::parse_header() u32 data_sz = 0; u8 search_byte = 0; while (true) { - stream >> search_byte; + search_byte = read_u8(); CHECK_OK("Reading byte searching for data"); if (search_byte != 0x64) //D continue; - stream >> search_byte; + search_byte = read_u8(); CHECK_OK("Reading next byte searching for data"); if (search_byte != 0x61) //A continue; - u16 search_remaining = 0; - stream >> search_remaining; + u16 search_remaining = read_u16(); CHECK_OK("Reading remaining bytes searching for data"); if (search_remaining != 0x6174) //TA continue; - stream >> data_sz; + data_sz = read_u32(); found_data = true; break; } @@ -194,9 +239,6 @@ bool WavLoaderPlugin::parse_header() int bytes_per_sample = (m_bits_per_sample / 8) * m_num_channels; m_total_samples = data_sz / bytes_per_sample; - // Just make sure we're good before we read the data... - ASSERT(!stream.handle_read_failure()); - return true; } @@ -224,105 +266,4 @@ bool ResampleHelper::read_sample(double& next_l, double& next_r) return false; } -template -static void read_samples_from_stream(InputMemoryStream& stream, SampleReader read_sample, Vector& samples, ResampleHelper& resampler, int num_channels) -{ - double norm_l = 0; - double norm_r = 0; - - switch (num_channels) { - case 1: - for (;;) { - while (resampler.read_sample(norm_l, norm_r)) { - samples.append(Sample(norm_l)); - } - norm_l = read_sample(stream); - - if (stream.handle_any_error()) { - break; - } - resampler.process_sample(norm_l, norm_r); - } - break; - case 2: - for (;;) { - while (resampler.read_sample(norm_l, norm_r)) { - samples.append(Sample(norm_l, norm_r)); - } - norm_l = read_sample(stream); - norm_r = read_sample(stream); - - if (stream.handle_any_error()) { - break; - } - resampler.process_sample(norm_l, norm_r); - } - break; - default: - ASSERT_NOT_REACHED(); - } -} - -static double read_norm_sample_24(InputMemoryStream& stream) -{ - u8 byte = 0; - stream >> byte; - u32 sample1 = byte; - stream >> byte; - u32 sample2 = byte; - stream >> byte; - u32 sample3 = byte; - - i32 value = 0; - value = sample1 << 8; - value |= (sample2 << 16); - value |= (sample3 << 24); - return double(value) / NumericLimits::max(); -} - -static double read_norm_sample_16(InputMemoryStream& stream) -{ - LittleEndian sample; - stream >> sample; - return double(sample) / NumericLimits::max(); -} - -static double read_norm_sample_8(InputMemoryStream& stream) -{ - u8 sample = 0; - stream >> sample; - return double(sample) / NumericLimits::max(); -} - -RefPtr Buffer::from_pcm_data(ReadonlyBytes data, ResampleHelper& resampler, int num_channels, int bits_per_sample) -{ - InputMemoryStream stream { data }; - Vector fdata; - fdata.ensure_capacity(data.size() / (bits_per_sample / 8)); -#ifdef AWAVLOADER_DEBUG - dbgln("Reading {} bits and {} channels, total bytes: {}", bits_per_sample, num_channels, data.size()); -#endif - - switch (bits_per_sample) { - case 8: - read_samples_from_stream(stream, read_norm_sample_8, fdata, resampler, num_channels); - break; - case 16: - read_samples_from_stream(stream, read_norm_sample_16, fdata, resampler, num_channels); - break; - case 24: - read_samples_from_stream(stream, read_norm_sample_24, fdata, resampler, num_channels); - break; - default: - ASSERT_NOT_REACHED(); - } - - // We should handle this in a better way above, but for now -- - // just make sure we're good. Worst case we just write some 0s where they - // don't belong. - ASSERT(!stream.handle_any_error()); - - return Buffer::create_with_samples(move(fdata)); -} - } diff --git a/Libraries/LibAudio/WavLoader.h b/Libraries/LibAudio/WavLoader.h index 8a23ef438d..543d33dca2 100644 --- a/Libraries/LibAudio/WavLoader.h +++ b/Libraries/LibAudio/WavLoader.h @@ -26,6 +26,8 @@ #pragma once +#include +#include #include #include #include @@ -40,7 +42,8 @@ class Buffer; // Parses a WAV file and produces an Audio::Buffer. class WavLoaderPlugin : public LoaderPlugin { public: - explicit WavLoaderPlugin(const StringView& path); + WavLoaderPlugin(const StringView& path); + WavLoaderPlugin(const ByteBuffer& buffer); virtual bool sniff() override; @@ -64,6 +67,7 @@ private: bool valid { false }; RefPtr m_file; + OwnPtr m_stream; String m_error_string; OwnPtr m_resampler;