Coverage Report

Created: 2025-09-05 06:52

/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
}