diff --git a/Libraries/LibMedia/Audio/FFmpegLoader.cpp b/Libraries/LibMedia/Audio/FFmpegLoader.cpp deleted file mode 100644 index 8e687d084e5d..000000000000 --- a/Libraries/LibMedia/Audio/FFmpegLoader.cpp +++ /dev/null @@ -1,293 +0,0 @@ -/* - * Copyright (c) 2024, Jelle Raaijmakers - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include "FFmpegLoader.h" -#include -#include - -#if LIBAVCODEC_VERSION_INT >= AV_VERSION_INT(59, 24, 100) -# define USE_FFMPEG_CH_LAYOUT -#endif -#if LIBAVFORMAT_VERSION_INT >= AV_VERSION_INT(59, 0, 100) -# define USE_CONSTIFIED_POINTERS -#endif - -namespace Audio { - -static constexpr int BUFFER_MAX_PROBE_SIZE = 64 * KiB; - -FFmpegLoaderPlugin::FFmpegLoaderPlugin(NonnullOwnPtr stream, NonnullOwnPtr io_context) - : LoaderPlugin(move(stream)) - , m_io_context(move(io_context)) -{ -} - -FFmpegLoaderPlugin::~FFmpegLoaderPlugin() -{ - if (m_frame != nullptr) - av_frame_free(&m_frame); - if (m_packet != nullptr) - av_packet_free(&m_packet); - if (m_codec_context != nullptr) - avcodec_free_context(&m_codec_context); - if (m_format_context != nullptr) - avformat_close_input(&m_format_context); -} - -ErrorOr> FFmpegLoaderPlugin::create(NonnullOwnPtr stream) -{ - auto io_context = TRY(Media::FFmpeg::FFmpegIOContext::create(*stream)); - auto loader = make(move(stream), move(io_context)); - TRY(loader->initialize()); - return loader; -} - -ErrorOr FFmpegLoaderPlugin::initialize() -{ - // Open the container - m_format_context = avformat_alloc_context(); - if (m_format_context == nullptr) - return Error::from_string_literal("Failed to allocate format context"); - m_format_context->pb = m_io_context->avio_context(); - if (avformat_open_input(&m_format_context, nullptr, nullptr, nullptr) < 0) - return Error::from_string_literal("Failed to open input for format parsing"); - - // Read stream info; doing this is required for headerless formats like MPEG - if (avformat_find_stream_info(m_format_context, nullptr) < 0) - return Error::from_string_literal("Failed to find stream info"); - -#ifdef USE_CONSTIFIED_POINTERS - AVCodec const* codec {}; -#else - AVCodec* codec {}; -#endif - // Find the best stream to play within the container - int best_stream_index = av_find_best_stream(m_format_context, AVMediaType::AVMEDIA_TYPE_AUDIO, -1, -1, &codec, 0); - if (best_stream_index == AVERROR_STREAM_NOT_FOUND) - return Error::from_string_literal("No audio stream found in container"); - if (best_stream_index == AVERROR_DECODER_NOT_FOUND) - return Error::from_string_literal("No suitable decoder found for stream"); - if (best_stream_index < 0) - return Error::from_string_literal("Failed to find an audio stream"); - m_audio_stream = m_format_context->streams[best_stream_index]; - - // Set up the context to decode the audio stream - m_codec_context = avcodec_alloc_context3(codec); - if (m_codec_context == nullptr) - return Error::from_string_literal("Failed to allocate the codec context"); - - if (avcodec_parameters_to_context(m_codec_context, m_audio_stream->codecpar) < 0) - return Error::from_string_literal("Failed to copy codec parameters"); - - m_codec_context->pkt_timebase = m_audio_stream->time_base; - m_codec_context->thread_count = AK::min(static_cast(Core::System::hardware_concurrency()), 4); - - if (avcodec_open2(m_codec_context, codec, nullptr) < 0) - return Error::from_string_literal("Failed to open input for decoding"); - - // This is an initial estimate of the total number of samples in the stream. - // During decoding, we might need to increase the number as more frames come in. - auto duration_in_seconds = TRY([this] -> ErrorOr { - if (m_audio_stream->duration >= 0) { - auto time_base = av_q2d(m_audio_stream->time_base); - return static_cast(m_audio_stream->duration) * time_base; - } - - // If the stream doesn't specify the duration, fallback to what the container says the duration is. - // If the container doesn't know the duration, then we're out of luck. Return an error. - if (m_format_context->duration < 0) - return Error::from_string_literal("Negative stream duration"); - - return static_cast(m_format_context->duration) / AV_TIME_BASE; - }()); - - m_total_samples = AK::round_to(sample_rate() * duration_in_seconds); - - // Allocate packet (logical chunk of data) and frame (video / audio frame) buffers - m_packet = av_packet_alloc(); - if (m_packet == nullptr) - return Error::from_string_literal("Failed to allocate packet"); - - m_frame = av_frame_alloc(); - if (m_frame == nullptr) - return Error::from_string_literal("Failed to allocate frame"); - - return {}; -} - -double FFmpegLoaderPlugin::time_base() const -{ - return av_q2d(m_audio_stream->time_base); -} - -bool FFmpegLoaderPlugin::sniff(SeekableStream& stream) -{ - auto io_context = MUST(Media::FFmpeg::FFmpegIOContext::create(stream)); -#ifdef USE_CONSTIFIED_POINTERS - AVInputFormat const* detected_format {}; -#else - AVInputFormat* detected_format {}; -#endif - auto score = av_probe_input_buffer2(io_context->avio_context(), &detected_format, nullptr, nullptr, 0, BUFFER_MAX_PROBE_SIZE); - return score > 0; -} - -static ErrorOr> extract_samples_from_frame(AVFrame& frame) -{ - size_t number_of_samples = frame.nb_samples; - VERIFY(number_of_samples > 0); - -#ifdef USE_FFMPEG_CH_LAYOUT - size_t number_of_channels = frame.ch_layout.nb_channels; -#else - size_t number_of_channels = frame.channels; -#endif - auto format = static_cast(frame.format); - auto packed_format = av_get_packed_sample_fmt(format); - auto is_planar = av_sample_fmt_is_planar(format) == 1; - - // FIXME: handle number_of_channels > 2 - if (number_of_channels != 1 && number_of_channels != 2) - return Error::from_string_view("Unsupported number of channels"sv); - - switch (format) { - case AV_SAMPLE_FMT_FLTP: - case AV_SAMPLE_FMT_S16: - case AV_SAMPLE_FMT_S32: - break; - default: - // FIXME: handle other formats - return Error::from_string_view("Unsupported sample format"sv); - } - - auto get_plane_pointer = [&](size_t channel_index) -> uint8_t* { - return is_planar ? frame.extended_data[channel_index] : frame.extended_data[0]; - }; - auto index_in_plane = [&](size_t sample_index, size_t channel_index) { - if (is_planar) - return sample_index; - return sample_index * number_of_channels + channel_index; - }; - auto read_sample = [&](uint8_t* data, size_t index) -> float { - switch (packed_format) { - case AV_SAMPLE_FMT_FLT: - return reinterpret_cast(data)[index]; - case AV_SAMPLE_FMT_S16: - return reinterpret_cast(data)[index] / static_cast(NumericLimits::max()); - case AV_SAMPLE_FMT_S32: - return reinterpret_cast(data)[index] / static_cast(NumericLimits::max()); - default: - VERIFY_NOT_REACHED(); - } - }; - - auto samples = TRY(FixedArray::create(number_of_samples)); - for (size_t sample = 0; sample < number_of_samples; ++sample) { - if (number_of_channels == 1) { - samples.unchecked_at(sample) = Sample { read_sample(get_plane_pointer(0), index_in_plane(sample, 0)) }; - } else { - samples.unchecked_at(sample) = Sample { - read_sample(get_plane_pointer(0), index_in_plane(sample, 0)), - read_sample(get_plane_pointer(1), index_in_plane(sample, 1)), - }; - } - } - return samples; -} - -ErrorOr>> FFmpegLoaderPlugin::load_chunks(size_t samples_to_read_from_input) -{ - Vector> chunks {}; - - do { - // Obtain a packet - auto read_frame_error = av_read_frame(m_format_context, m_packet); - if (read_frame_error < 0) { - if (read_frame_error == AVERROR_EOF) - break; - return Error::from_string_literal("Failed to read frame"); - } - if (m_packet->stream_index != m_audio_stream->index) { - av_packet_unref(m_packet); - continue; - } - - // Send the packet to the decoder - if (avcodec_send_packet(m_codec_context, m_packet) < 0) - return Error::from_string_literal("Failed to send packet"); - av_packet_unref(m_packet); - - // Ask the decoder for a new frame. We might not have sent enough data yet - auto receive_frame_error = avcodec_receive_frame(m_codec_context, m_frame); - if (receive_frame_error != 0) { - if (receive_frame_error == AVERROR(EAGAIN)) - continue; - if (receive_frame_error == AVERROR_EOF) - break; - return Error::from_string_literal("Failed to receive frame"); - } - - chunks.append(TRY(extract_samples_from_frame(*m_frame))); - - // Use the frame's presentation timestamp to set the number of loaded samples - m_loaded_samples = static_cast(m_frame->pts * sample_rate() * time_base()); - if (m_loaded_samples > m_total_samples) [[unlikely]] - m_total_samples = m_loaded_samples; - - samples_to_read_from_input -= AK::min(samples_to_read_from_input, m_frame->nb_samples); - } while (samples_to_read_from_input > 0); - - return chunks; -} - -ErrorOr FFmpegLoaderPlugin::reset() -{ - return seek(0); -} - -ErrorOr FFmpegLoaderPlugin::seek(int sample_index) -{ - auto sample_position_in_seconds = static_cast(sample_index) / sample_rate(); - auto sample_timestamp = AK::round_to(sample_position_in_seconds / time_base()); - - if (av_seek_frame(m_format_context, m_audio_stream->index, sample_timestamp, AVSEEK_FLAG_ANY) < 0) - return Error::from_string_literal("Failed to seek"); - avcodec_flush_buffers(m_codec_context); - - m_loaded_samples = sample_index; - return {}; -} - -u32 FFmpegLoaderPlugin::sample_rate() -{ - VERIFY(m_codec_context != nullptr); - return m_codec_context->sample_rate; -} - -u16 FFmpegLoaderPlugin::num_channels() -{ - VERIFY(m_codec_context != nullptr); -#ifdef USE_FFMPEG_CH_LAYOUT - return m_codec_context->ch_layout.nb_channels; -#else - return m_codec_context->channels; -#endif -} - -PcmSampleFormat FFmpegLoaderPlugin::pcm_format() -{ - // FIXME: pcm_format() is unused, always return Float for now - return PcmSampleFormat::Float32; -} - -ByteString FFmpegLoaderPlugin::format_name() -{ - if (!m_format_context) - return "unknown"; - return m_format_context->iformat->name; -} - -} diff --git a/Libraries/LibMedia/Audio/FFmpegLoader.h b/Libraries/LibMedia/Audio/FFmpegLoader.h deleted file mode 100644 index b00878481dc0..000000000000 --- a/Libraries/LibMedia/Audio/FFmpegLoader.h +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright (c) 2024, Jelle Raaijmakers - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include "Loader.h" -#include -#include -#include - -extern "C" { -#include -#include -} - -namespace Audio { - -class FFmpegLoaderPlugin : public LoaderPlugin { -public: - explicit FFmpegLoaderPlugin(NonnullOwnPtr, NonnullOwnPtr); - virtual ~FFmpegLoaderPlugin(); - - static bool sniff(SeekableStream& stream); - static ErrorOr> create(NonnullOwnPtr); - - virtual ErrorOr>> load_chunks(size_t samples_to_read_from_input) override; - - virtual ErrorOr reset() override; - virtual ErrorOr seek(int sample_index) override; - - virtual int loaded_samples() override { return m_loaded_samples; } - virtual int total_samples() override { return m_total_samples; } - virtual u32 sample_rate() override; - virtual u16 num_channels() override; - virtual PcmSampleFormat pcm_format() override; - virtual ByteString format_name() override; - -private: - ErrorOr initialize(); - double time_base() const; - - AVStream* m_audio_stream; - AVCodecContext* m_codec_context { nullptr }; - AVFormatContext* m_format_context { nullptr }; - AVFrame* m_frame { nullptr }; - NonnullOwnPtr m_io_context; - int m_loaded_samples { 0 }; - AVPacket* m_packet { nullptr }; - int m_total_samples { 0 }; -}; - -} diff --git a/Libraries/LibMedia/Audio/Loader.cpp b/Libraries/LibMedia/Audio/Loader.cpp deleted file mode 100644 index 2271ab5e97c4..000000000000 --- a/Libraries/LibMedia/Audio/Loader.cpp +++ /dev/null @@ -1,111 +0,0 @@ -/* - * Copyright (c) 2018-2023, the SerenityOS developers. - * Copyright (c) 2024, Jelle Raaijmakers - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include "Loader.h" -#include "FFmpegLoader.h" -#include -#include - -namespace Audio { - -LoaderPlugin::LoaderPlugin(NonnullOwnPtr stream) - : m_stream(move(stream)) -{ -} - -Loader::Loader(NonnullOwnPtr plugin) - : m_plugin(move(plugin)) -{ -} - -struct LoaderPluginInitializer { - bool (*sniff)(SeekableStream&); - ErrorOr> (*create)(NonnullOwnPtr); -}; - -static constexpr LoaderPluginInitializer s_initializers[] = { - { FFmpegLoaderPlugin::sniff, FFmpegLoaderPlugin::create }, -}; - -ErrorOr> Loader::create(StringView path) -{ - auto stream = TRY(Core::MappedFile::map(path, Core::MappedFile::Mode::ReadOnly)); - auto plugin = TRY(Loader::create_plugin(move(stream))); - return adopt_ref(*new (nothrow) Loader(move(plugin))); -} - -ErrorOr> Loader::create(ReadonlyBytes buffer) -{ - auto stream = TRY(try_make(buffer)); - auto plugin = TRY(Loader::create_plugin(move(stream))); - return adopt_ref(*new (nothrow) Loader(move(plugin))); -} - -ErrorOr> Loader::create_plugin(NonnullOwnPtr stream) -{ - for (auto const& loader : s_initializers) { - if (loader.sniff(*stream)) { - TRY(stream->seek(0, SeekMode::SetPosition)); - return loader.create(move(stream)); - } - TRY(stream->seek(0, SeekMode::SetPosition)); - } - - return Error::from_string_literal("No loader plugin available"); -} - -ErrorOr Loader::get_more_samples(size_t samples_to_read_from_input) -{ - if (m_plugin_at_end_of_stream && m_buffer.is_empty()) - return Samples {}; - - size_t remaining_samples = total_samples() - loaded_samples(); - size_t samples_to_read = min(remaining_samples, samples_to_read_from_input); - auto samples = TRY(Samples::create(samples_to_read)); - - size_t sample_index = 0; - - if (m_buffer.size() > 0) { - size_t to_transfer = min(m_buffer.size(), samples_to_read); - AK::TypedTransfer::move(samples.data(), m_buffer.data(), to_transfer); - if (to_transfer < m_buffer.size()) - m_buffer.remove(0, to_transfer); - else - m_buffer.clear_with_capacity(); - - sample_index += to_transfer; - } - - while (sample_index < samples_to_read) { - auto chunk_data = TRY(m_plugin->load_chunks(samples_to_read - sample_index)); - chunk_data.remove_all_matching([](auto& chunk) { return chunk.is_empty(); }); - if (chunk_data.is_empty()) { - m_plugin_at_end_of_stream = true; - break; - } - for (auto& chunk : chunk_data) { - if (sample_index < samples_to_read) { - auto count = min(samples_to_read - sample_index, chunk.size()); - AK::TypedTransfer::move(samples.span().offset(sample_index), chunk.data(), count); - // We didn't read all of the chunk; transfer the rest into the buffer. - if (count < chunk.size()) { - auto remaining_samples_count = chunk.size() - count; - // We will always have an empty buffer at this point! - TRY(m_buffer.try_append(chunk.span().offset(count), remaining_samples_count)); - } - } else { - // We're now past what the user requested. Transfer the entirety of the data into the buffer. - TRY(m_buffer.try_append(chunk.data(), chunk.size())); - } - sample_index += chunk.size(); - } - } - - return samples; -} - -} diff --git a/Libraries/LibMedia/Audio/Loader.h b/Libraries/LibMedia/Audio/Loader.h deleted file mode 100644 index 3065563c049e..000000000000 --- a/Libraries/LibMedia/Audio/Loader.h +++ /dev/null @@ -1,115 +0,0 @@ -/* - * Copyright (c) 2018-2022, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include "Sample.h" -#include "SampleFormats.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -namespace Audio { - -// Experimentally determined to be a decent buffer size on i686: -// 4K (the default) is slightly worse, and 64K is much worse. -// At sufficiently large buffer sizes, the advantage of infrequent read() calls is outweighed by the memmove() overhead. -// There was no intensive fine-tuning done to determine this value, so improvements may definitely be possible. -constexpr size_t const loader_buffer_size = 8 * KiB; - -// Two seek points should ideally not be farther apart than this. -// This variable is a heuristic for seek table-constructing loaders. -constexpr u64 const maximum_seekpoint_distance_ms = 1000; -// Seeking should be at least as precise as this. -// That means: The actual achieved seek position must not be more than this amount of time before the requested seek position. -constexpr u64 const seek_tolerance_ms = 5000; - -using Samples = FixedArray; - -class LoaderPlugin { -public: - explicit LoaderPlugin(NonnullOwnPtr stream); - virtual ~LoaderPlugin() = default; - - // Load as many audio chunks as necessary to get up to the required samples. - // A chunk can be anything that is convenient for the plugin to load in one go without requiring to move samples around different buffers. - // For example: A FLAC, MP3 or QOA frame. - // The chunks are returned in a vector, so the loader can simply add chunks until the requested sample amount is reached. - // The sample count MAY be surpassed, but only as little as possible. It CAN be undershot when the end of the stream is reached. - // If the loader has no chunking limitations (e.g. WAV), it may return a single exact-sized chunk. - virtual ErrorOr>> load_chunks(size_t samples_to_read_from_input) = 0; - - virtual ErrorOr reset() = 0; - - virtual ErrorOr seek(int const sample_index) = 0; - - // total_samples() and loaded_samples() should be independent - // of the number of channels. - // - // For example, with a three-second-long, stereo, 44.1KHz audio file: - // num_channels() should return 2 - // sample_rate() should return 44100 (each channel is sampled at this rate) - // total_samples() should return 132300 (sample_rate * three seconds) - virtual int loaded_samples() = 0; - virtual int total_samples() = 0; - virtual u32 sample_rate() = 0; - virtual u16 num_channels() = 0; - - // Human-readable name of the file format, of the form (.) - virtual ByteString format_name() = 0; - virtual PcmSampleFormat pcm_format() = 0; - -protected: - NonnullOwnPtr m_stream; -}; - -class MEDIA_API Loader : public RefCounted { -public: - static ErrorOr> create(StringView path); - static ErrorOr> create(ReadonlyBytes buffer); - - // Will only read less samples if we're at the end of the stream. - ErrorOr get_more_samples(size_t samples_to_read_from_input = 128 * KiB); - - ErrorOr reset() const - { - m_plugin_at_end_of_stream = false; - return m_plugin->reset(); - } - ErrorOr seek(int const position) const - { - m_buffer.clear_with_capacity(); - m_plugin_at_end_of_stream = false; - return m_plugin->seek(position); - } - - int loaded_samples() const { return m_plugin->loaded_samples() - (int)m_buffer.size(); } - int total_samples() const { return m_plugin->total_samples(); } - u32 sample_rate() const { return m_plugin->sample_rate(); } - u16 num_channels() const { return m_plugin->num_channels(); } - ByteString format_name() const { return m_plugin->format_name(); } - u16 bits_per_sample() const { return pcm_bits_per_sample(m_plugin->pcm_format()); } - PcmSampleFormat pcm_format() const { return m_plugin->pcm_format(); } - -private: - static ErrorOr> create_plugin(NonnullOwnPtr stream); - - explicit Loader(NonnullOwnPtr); - - mutable NonnullOwnPtr m_plugin; - // The plugin can signal an end of stream by returning no (or only empty) chunks. - mutable bool m_plugin_at_end_of_stream { false }; - mutable Vector m_buffer; -}; - -} diff --git a/Libraries/LibMedia/CMakeLists.txt b/Libraries/LibMedia/CMakeLists.txt index a7a4bd5cc1cc..b938a9cd91ea 100644 --- a/Libraries/LibMedia/CMakeLists.txt +++ b/Libraries/LibMedia/CMakeLists.txt @@ -3,7 +3,6 @@ include(audio) include(ffmpeg) set(SOURCES - Audio/Loader.cpp Audio/SampleFormats.cpp Color/ColorConverter.cpp Color/ColorPrimaries.cpp @@ -24,7 +23,6 @@ ladybird_lib(LibMedia media EXPLICIT_SYMBOL_EXPORT) target_link_libraries(LibMedia PRIVATE LibCore LibCrypto LibIPC LibGfx LibThreading LibUnicode) target_sources(LibMedia PRIVATE - Audio/FFmpegLoader.cpp FFmpeg/FFmpegAudioDecoder.cpp FFmpeg/FFmpegDemuxer.cpp FFmpeg/FFmpegIOContext.cpp diff --git a/Meta/Lagom/Fuzzers/AudioFuzzerCommon.h b/Meta/Lagom/Fuzzers/AudioFuzzerCommon.h deleted file mode 100644 index b85461ae40a0..000000000000 --- a/Meta/Lagom/Fuzzers/AudioFuzzerCommon.h +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright (c) 2022, Luke Wilde - * Copyright (c) 2023, kleines Filmröllchen - * Copyright (c) 2021-2023, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include -#include -#include -#include -#include - -template -requires(IsBaseOf) -int fuzz_audio_loader(uint8_t const* data, size_t size) -{ - auto const bytes = ReadonlyBytes { data, size }; - auto stream = try_make(bytes).release_value(); - auto audio_or_error = LoaderPluginType::create(move(stream)); - - if (audio_or_error.is_error()) - return 0; - - auto audio = audio_or_error.release_value(); - - for (;;) { - auto samples = audio->load_chunks(4 * KiB); - if (samples.is_error()) - return 0; - if (samples.value().size() == 0) - break; - } - - return 0; -} diff --git a/Tests/LibMedia/CMakeLists.txt b/Tests/LibMedia/CMakeLists.txt index 1b3b1982a887..2d6b61dde989 100644 --- a/Tests/LibMedia/CMakeLists.txt +++ b/Tests/LibMedia/CMakeLists.txt @@ -7,7 +7,6 @@ set(TEST_SOURCES TestPlaybackStream.cpp TestVorbisDecode.cpp TestVP9Decode.cpp - TestWav.cpp ) foreach(source IN LISTS TEST_SOURCES) diff --git a/Tests/LibMedia/TestWav.cpp b/Tests/LibMedia/TestWav.cpp deleted file mode 100644 index d93af32a5280..000000000000 --- a/Tests/LibMedia/TestWav.cpp +++ /dev/null @@ -1,77 +0,0 @@ -/* - * Copyright (c) 2024, Lee Hanken - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include - -static void run_test(StringView file_name, int const num_samples, int const channels, u32 const rate) -{ - constexpr auto format = "wav"; - constexpr int bits = 32; - - ByteString in_path = ByteString::formatted("WAV/{}", file_name); - - auto loader = TRY_OR_FAIL(Audio::Loader::create(in_path)); - - EXPECT_EQ(loader->format_name(), format); - EXPECT_EQ(loader->sample_rate(), rate); - EXPECT_EQ(loader->num_channels(), channels); - EXPECT_EQ(loader->bits_per_sample(), bits); - EXPECT_EQ(loader->total_samples(), num_samples); -} - -// 5 seconds, 16-bit audio samples - -TEST_CASE(mono_8khz) -{ - run_test("tone_8000_mono.wav"sv, 40000, 1, 8000); -} - -TEST_CASE(stereo_8khz) -{ - run_test("tone_8000_stereo.wav"sv, 40000, 2, 8000); -} - -TEST_CASE(mono_11khz) -{ - run_test("tone_11025_mono.wav"sv, 55125, 1, 11025); -} - -TEST_CASE(stereo_11khz) -{ - run_test("tone_11025_stereo.wav"sv, 55125, 2, 11025); -} - -TEST_CASE(mono_16khz) -{ - run_test("tone_16000_mono.wav"sv, 80000, 1, 16000); -} - -TEST_CASE(stereo_16khz) -{ - run_test("tone_16000_stereo.wav"sv, 80000, 2, 16000); -} - -TEST_CASE(mono_22khz) -{ - run_test("tone_22050_mono.wav"sv, 110250, 1, 22050); -} - -TEST_CASE(stereo_22khz) -{ - run_test("tone_22050_stereo.wav"sv, 110250, 2, 22050); -} - -TEST_CASE(mono_44khz) -{ - run_test("tone_44100_mono.wav"sv, 220500, 1, 44100); -} - -TEST_CASE(stereo_44khz) -{ - run_test("tone_44100_stereo.wav"sv, 220500, 2, 44100); -} diff --git a/Utilities/CMakeLists.txt b/Utilities/CMakeLists.txt index d17950bb0306..99fa52b164c2 100644 --- a/Utilities/CMakeLists.txt +++ b/Utilities/CMakeLists.txt @@ -8,7 +8,6 @@ else() endif() lagom_utility(xml SOURCES xml.cpp LIBS LibFileSystem LibMain LibXML LibURL) -lagom_utility(abench SOURCES abench.cpp LIBS LibMain LibFileSystem LibMedia) lagom_utility(dns SOURCES dns.cpp LIBS LibDNS LibMain LibTLS LibCrypto) if (ENABLE_GUI_TARGETS) diff --git a/Utilities/abench.cpp b/Utilities/abench.cpp deleted file mode 100644 index 58f13b5e9c5d..000000000000 --- a/Utilities/abench.cpp +++ /dev/null @@ -1,66 +0,0 @@ -/* - * Copyright (c) 2021, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -// The Kernel has problems with large anonymous buffers, so let's limit sample reads ourselves. -static constexpr size_t MAX_CHUNK_SIZE = 1 * MiB / 2; - -ErrorOr ladybird_main(Main::Arguments arguments) -{ - StringView path {}; - int sample_count = -1; - - Core::ArgsParser args_parser; - args_parser.set_general_help("Benchmark audio loading"); - args_parser.add_positional_argument(path, "Path to audio file", "path"); - args_parser.add_option(sample_count, "How many samples to load at maximum", "sample-count", 's', "samples"); - args_parser.parse(arguments); - - auto maybe_loader = Audio::Loader::create(path); - if (maybe_loader.is_error()) { - warnln("Failed to load audio file: {}", maybe_loader.error()); - return 1; - } - auto loader = maybe_loader.release_value(); - - Core::ElapsedTimer sample_timer { Core::TimerType::Precise }; - i64 total_loader_time = 0; - int remaining_samples = sample_count > 0 ? sample_count : NumericLimits::max(); - unsigned total_loaded_samples = 0; - - for (;;) { - if (remaining_samples > 0) { - sample_timer = sample_timer.start_new(); - auto samples = loader->get_more_samples(min(MAX_CHUNK_SIZE, remaining_samples)); - total_loader_time += sample_timer.elapsed_milliseconds(); - if (!samples.is_error()) { - remaining_samples -= samples.value().size(); - total_loaded_samples += samples.value().size(); - if (samples.value().size() == 0) - break; - } else { - warnln("Error while loading audio: {}", samples.error()); - return 1; - } - } else - break; - } - - auto time_per_sample = static_cast(total_loader_time) / static_cast(total_loaded_samples) * 1000.; - auto playback_time_per_sample = (1. / static_cast(loader->sample_rate())) * 1000'000.; - - outln("Loaded {:10d} samples in {:06.3f} s, {:9.3f} µs/sample, {:6.1f}% speed (realtime {:9.3f} µs/sample)", total_loaded_samples, static_cast(total_loader_time) / 1000., time_per_sample, playback_time_per_sample / time_per_sample * 100., playback_time_per_sample); - - return 0; -}