/* * Copyright (c) 2023, kleines Filmröllchen * * SPDX-License-Identifier: BSD-2-Clause */ #pragma once #include #include #include #include #include namespace Audio::QOA { // 'qoaf' static constexpr u32 const magic = 0x716f6166; static constexpr size_t const header_size = sizeof(u64); struct FrameHeader { u8 num_channels; u32 sample_rate; // 24 bits u16 sample_count; // TODO: might be removed and/or replaced u16 frame_size; static ErrorOr read_from_stream(Stream& stream); }; static constexpr size_t const frame_header_size = sizeof(u64); // Least mean squares (LMS) predictor FIR filter size. static constexpr size_t const lms_history = 4; static constexpr size_t const lms_state_size = 2 * lms_history * sizeof(u16); // Only used for internal purposes; intermediate LMS states can be beyond 16 bits. struct LMSState { i32 history[lms_history] { 0, 0, 0, 0 }; i32 weights[lms_history] { 0, 0, 0, 0 }; LMSState() = default; LMSState(u64 history_packed, u64 weights_packed); i32 predict() const; void update(i32 sample, i32 residual); }; using PackedSlice = u64; // A QOA slice in a more directly readable format, unpacked from the stored 64-bit format. struct UnpackedSlice { size_t scale_factor_index; // 4 bits packed Array residuals; // 3 bits packed }; // Samples within a 64-bit slice. static constexpr size_t const slice_samples = 20; static constexpr size_t const max_slices_per_frame = 256; static constexpr size_t const max_frame_samples = slice_samples * max_slices_per_frame; // Defined as clamping limits by the spec. static constexpr i32 const sample_minimum = -32768; static constexpr i32 const sample_maximum = 32767; // Quantization and scale factor tables computed from formulas given in qoa.h constexpr Array generate_scale_factor_table() { Array scalefactor_table; for (size_t s = 0; s < 17; ++s) scalefactor_table[s] = static_cast(AK::round(AK::pow(static_cast(s + 1), 2.75))); return scalefactor_table; } // FIXME: Get rid of the literal table once Clang understands our constexpr pow() and round() implementations. #if defined(AK_COMPILER_CLANG) static constexpr Array scale_factor_table = { 1, 7, 21, 45, 84, 138, 211, 304, 421, 562, 731, 928, 1157, 1419, 1715, 2048 }; #else static constexpr Array scale_factor_table = generate_scale_factor_table(); #endif constexpr Array generate_reciprocal_table() { Array reciprocal_table; for (size_t s = 0; s < 17; ++s) { reciprocal_table[s] = ((1 << 16) + scale_factor_table[s] - 1) / scale_factor_table[s]; } return reciprocal_table; } constexpr Array, 16> generate_dequantization_table() { Array, 16> dequantization_table; constexpr Array float_dequantization_table = { 0.75, -0.75, 2.5, -2.5, 4.5, -4.5, 7, -7 }; for (size_t scale = 0; scale < 16; ++scale) { for (size_t quantization = 0; quantization < 8; ++quantization) dequantization_table[scale][quantization] = static_cast(AK::round( static_cast(scale_factor_table[scale]) * float_dequantization_table[quantization])); } return dequantization_table; } #if defined(AK_COMPILER_CLANG) static constexpr Array reciprocal_table = { 65536, 9363, 3121, 1457, 781, 475, 311, 216, 156, 117, 90, 71, 57, 47, 39, 32 }; static constexpr Array, 16> dequantization_table = { Array { 1, -1, 3, -3, 5, -5, 7, -7 }, { 5, -5, 18, -18, 32, -32, 49, -49 }, { 16, -16, 53, -53, 95, -95, 147, -147 }, { 34, -34, 113, -113, 203, -203, 315, -315 }, { 63, -63, 210, -210, 378, -378, 588, -588 }, { 104, -104, 345, -345, 621, -621, 966, -966 }, { 158, -158, 528, -528, 950, -950, 1477, -1477 }, { 228, -228, 760, -760, 1368, -1368, 2128, -2128 }, { 316, -316, 1053, -1053, 1895, -1895, 2947, -2947 }, { 422, -422, 1405, -1405, 2529, -2529, 3934, -3934 }, { 548, -548, 1828, -1828, 3290, -3290, 5117, -5117 }, { 696, -696, 2320, -2320, 4176, -4176, 6496, -6496 }, { 868, -868, 2893, -2893, 5207, -5207, 8099, -8099 }, { 1064, -1064, 3548, -3548, 6386, -6386, 9933, -9933 }, { 1286, -1286, 4288, -4288, 7718, -7718, 12005, -12005 }, { 1536, -1536, 5120, -5120, 9216, -9216, 14336, -14336 }, }; #else static constexpr Array reciprocal_table = generate_reciprocal_table(); static constexpr Array, 16> dequantization_table = generate_dequantization_table(); #endif static constexpr Array quantization_table = { 7, 7, 7, 5, 5, 3, 3, 1, // -8 ..-1 0, // 0 0, 2, 2, 4, 4, 6, 6, 6 // 1 .. 8 }; }