/src/serenity/Userland/Libraries/LibAudio/Loader.h
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * Copyright (c) 2018-2022, the SerenityOS developers. |
3 | | * |
4 | | * SPDX-License-Identifier: BSD-2-Clause |
5 | | */ |
6 | | |
7 | | #pragma once |
8 | | |
9 | | #include <AK/Error.h> |
10 | | #include <AK/FixedArray.h> |
11 | | #include <AK/NonnullOwnPtr.h> |
12 | | #include <AK/NonnullRefPtr.h> |
13 | | #include <AK/RefCounted.h> |
14 | | #include <AK/RefPtr.h> |
15 | | #include <AK/Span.h> |
16 | | #include <AK/Stream.h> |
17 | | #include <AK/StringView.h> |
18 | | #include <AK/Try.h> |
19 | | #include <LibAudio/GenericTypes.h> |
20 | | #include <LibAudio/LoaderError.h> |
21 | | #include <LibAudio/Metadata.h> |
22 | | #include <LibAudio/Sample.h> |
23 | | #include <LibAudio/SampleFormats.h> |
24 | | |
25 | | namespace Audio { |
26 | | |
27 | | // Experimentally determined to be a decent buffer size on i686: |
28 | | // 4K (the default) is slightly worse, and 64K is much worse. |
29 | | // At sufficiently large buffer sizes, the advantage of infrequent read() calls is outweighed by the memmove() overhead. |
30 | | // There was no intensive fine-tuning done to determine this value, so improvements may definitely be possible. |
31 | | constexpr size_t const loader_buffer_size = 8 * KiB; |
32 | | |
33 | | // Two seek points should ideally not be farther apart than this. |
34 | | // This variable is a heuristic for seek table-constructing loaders. |
35 | | constexpr u64 const maximum_seekpoint_distance_ms = 1000; |
36 | | // Seeking should be at least as precise as this. |
37 | | // That means: The actual achieved seek position must not be more than this amount of time before the requested seek position. |
38 | | constexpr u64 const seek_tolerance_ms = 5000; |
39 | | |
40 | | using LoaderSamples = ErrorOr<FixedArray<Sample>, LoaderError>; |
41 | | using MaybeLoaderError = ErrorOr<void, LoaderError>; |
42 | | |
43 | | class LoaderPlugin { |
44 | | public: |
45 | | explicit LoaderPlugin(NonnullOwnPtr<SeekableStream> stream); |
46 | 3.69k | virtual ~LoaderPlugin() = default; |
47 | | |
48 | | // Load as many audio chunks as necessary to get up to the required samples. |
49 | | // A chunk can be anything that is convenient for the plugin to load in one go without requiring to move samples around different buffers. |
50 | | // For example: A FLAC, MP3 or QOA frame. |
51 | | // The chunks are returned in a vector, so the loader can simply add chunks until the requested sample amount is reached. |
52 | | // The sample count MAY be surpassed, but only as little as possible. It CAN be undershot when the end of the stream is reached. |
53 | | // If the loader has no chunking limitations (e.g. WAV), it may return a single exact-sized chunk. |
54 | | virtual ErrorOr<Vector<FixedArray<Sample>>, LoaderError> load_chunks(size_t samples_to_read_from_input) = 0; |
55 | | |
56 | | virtual MaybeLoaderError reset() = 0; |
57 | | |
58 | | virtual MaybeLoaderError seek(int const sample_index) = 0; |
59 | | |
60 | | // total_samples() and loaded_samples() should be independent |
61 | | // of the number of channels. |
62 | | // |
63 | | // For example, with a three-second-long, stereo, 44.1KHz audio file: |
64 | | // num_channels() should return 2 |
65 | | // sample_rate() should return 44100 (each channel is sampled at this rate) |
66 | | // total_samples() should return 132300 (sample_rate * three seconds) |
67 | | virtual int loaded_samples() = 0; |
68 | | virtual int total_samples() = 0; |
69 | | virtual u32 sample_rate() = 0; |
70 | | virtual u16 num_channels() = 0; |
71 | | |
72 | | // Human-readable name of the file format, of the form <full abbreviation> (.<ending>) |
73 | | virtual ByteString format_name() = 0; |
74 | | virtual PcmSampleFormat pcm_format() = 0; |
75 | | |
76 | 0 | Metadata const& metadata() const { return m_metadata; } |
77 | 0 | Vector<PictureData> const& pictures() const { return m_pictures; } |
78 | | |
79 | | protected: |
80 | | NonnullOwnPtr<SeekableStream> m_stream; |
81 | | |
82 | | Vector<PictureData> m_pictures; |
83 | | Metadata m_metadata; |
84 | | }; |
85 | | |
86 | | class Loader : public RefCounted<Loader> { |
87 | | public: |
88 | | static ErrorOr<NonnullRefPtr<Loader>, LoaderError> create(StringView path); |
89 | | static ErrorOr<NonnullRefPtr<Loader>, LoaderError> create(ReadonlyBytes buffer); |
90 | | |
91 | | // Will only read less samples if we're at the end of the stream. |
92 | | LoaderSamples get_more_samples(size_t samples_to_read_from_input = 128 * KiB); |
93 | | |
94 | | MaybeLoaderError reset() const |
95 | 0 | { |
96 | 0 | m_plugin_at_end_of_stream = false; |
97 | 0 | return m_plugin->reset(); |
98 | 0 | } |
99 | | MaybeLoaderError seek(int const position) const |
100 | 0 | { |
101 | 0 | m_buffer.clear_with_capacity(); |
102 | 0 | m_plugin_at_end_of_stream = false; |
103 | 0 | return m_plugin->seek(position); |
104 | 0 | } |
105 | | |
106 | 0 | int loaded_samples() const { return m_plugin->loaded_samples() - (int)m_buffer.size(); } |
107 | 0 | int total_samples() const { return m_plugin->total_samples(); } |
108 | 0 | u32 sample_rate() const { return m_plugin->sample_rate(); } |
109 | 0 | u16 num_channels() const { return m_plugin->num_channels(); } |
110 | 0 | ByteString format_name() const { return m_plugin->format_name(); } |
111 | 0 | u16 bits_per_sample() const { return pcm_bits_per_sample(m_plugin->pcm_format()); } |
112 | 0 | PcmSampleFormat pcm_format() const { return m_plugin->pcm_format(); } |
113 | 0 | Metadata const& metadata() const { return m_plugin->metadata(); } |
114 | 0 | Vector<PictureData> const& pictures() const { return m_plugin->pictures(); } |
115 | | |
116 | | private: |
117 | | static ErrorOr<NonnullOwnPtr<LoaderPlugin>, LoaderError> create_plugin(NonnullOwnPtr<SeekableStream> stream); |
118 | | |
119 | | explicit Loader(NonnullOwnPtr<LoaderPlugin>); |
120 | | |
121 | | mutable NonnullOwnPtr<LoaderPlugin> m_plugin; |
122 | | // The plugin can signal an end of stream by returning no (or only empty) chunks. |
123 | | mutable bool m_plugin_at_end_of_stream { false }; |
124 | | mutable Vector<Sample, loader_buffer_size> m_buffer; |
125 | | }; |
126 | | |
127 | | } |