/src/serenity/Userland/Libraries/LibAudio/QOALoader.h
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * Copyright (c) 2023, kleines Filmröllchen <filmroellchen@serenityos.org> |
3 | | * |
4 | | * SPDX-License-Identifier: BSD-2-Clause |
5 | | */ |
6 | | |
7 | | #pragma once |
8 | | |
9 | | #include <AK/Error.h> |
10 | | #include <AK/NonnullOwnPtr.h> |
11 | | #include <AK/Span.h> |
12 | | #include <AK/Types.h> |
13 | | #include <LibAudio/Loader.h> |
14 | | #include <LibAudio/QOATypes.h> |
15 | | #include <LibAudio/SampleFormats.h> |
16 | | |
17 | | namespace Audio { |
18 | | |
19 | | // Decoder for the Quite Okay Audio (QOA) format. |
20 | | // NOTE: The QOA format is not finalized yet and this decoder might not be fully spec-compliant as of 2023-02-02. |
21 | | // |
22 | | // https://github.com/phoboslab/qoa/blob/master/qoa.h |
23 | | class QOALoaderPlugin : public LoaderPlugin { |
24 | | public: |
25 | | explicit QOALoaderPlugin(NonnullOwnPtr<AK::SeekableStream> stream); |
26 | | virtual ~QOALoaderPlugin() override = default; |
27 | | |
28 | | static bool sniff(SeekableStream& stream); |
29 | | static ErrorOr<NonnullOwnPtr<LoaderPlugin>, LoaderError> create(NonnullOwnPtr<SeekableStream>); |
30 | | |
31 | | virtual ErrorOr<Vector<FixedArray<Sample>>, LoaderError> load_chunks(size_t samples_to_read_from_input) override; |
32 | | |
33 | | virtual MaybeLoaderError reset() override; |
34 | | virtual MaybeLoaderError seek(int sample_index) override; |
35 | | |
36 | 0 | virtual int loaded_samples() override { return static_cast<int>(m_loaded_samples); } |
37 | 0 | virtual int total_samples() override { return static_cast<int>(m_total_samples); } |
38 | 0 | virtual u32 sample_rate() override { return m_sample_rate; } |
39 | 0 | virtual u16 num_channels() override { return m_num_channels; } |
40 | 0 | virtual ByteString format_name() override { return "Quite Okay Audio (.qoa)"; } |
41 | 0 | virtual PcmSampleFormat pcm_format() override { return PcmSampleFormat::Int16; } |
42 | | |
43 | | private: |
44 | | enum class IsFirstFrame : bool { |
45 | | Yes = true, |
46 | | No = false, |
47 | | }; |
48 | | |
49 | | MaybeLoaderError initialize(); |
50 | | MaybeLoaderError parse_header(); |
51 | | |
52 | | MaybeLoaderError load_one_frame(Span<Sample>& target, IsFirstFrame is_first_frame = IsFirstFrame::No); |
53 | | // Updates predictor values in lms_state so the next slice can reuse the same state. |
54 | | MaybeLoaderError read_one_slice(QOA::LMSState& lms_state, Span<i16>& samples); |
55 | | static ALWAYS_INLINE QOA::UnpackedSlice unpack_slice(QOA::PackedSlice packed_slice); |
56 | | |
57 | | // QOA's division routine for scaling residuals before final quantization. |
58 | | static ALWAYS_INLINE i16 qoa_divide(i16 value, i16 scale_factor); |
59 | | |
60 | | // Because QOA has dynamic sample rate and channel count, we only use the sample rate and channel count from the first frame. |
61 | | u32 m_sample_rate { 0 }; |
62 | | u8 m_num_channels { 0 }; |
63 | | // If this is the case (the reference encoder even enforces it at the moment) |
64 | | bool m_has_uniform_channel_count { true }; |
65 | | |
66 | | size_t m_loaded_samples { 0 }; |
67 | | size_t m_total_samples { 0 }; |
68 | | }; |
69 | | |
70 | | } |