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