# ------------------------------------------------------------ # AAC Coder/Decoder - Huffman wrappers (Level 3) # # Multimedia course at Aristotle University of # Thessaloniki (AUTh) # # Author: # Christos Choutouridis (ΑΕΜ 8997) # cchoutou@ece.auth.gr # # Description: # Thin wrappers around the provided Huffman utilities (material/huff_utils.py) # so that the API matches the assignment text. # # Exposed API (assignment): # huff_sec, huff_codebook = aac_encode_huff(coeff_sec, huff_LUT_list, force_codebook) # dec_coeffs = aac_decode_huff(huff_sec, huff_codebook, huff_LUT_list) # # Notes: # - Huffman coding operates on tuples. Therefore, decode(encode(x)) may return # extra trailing symbols due to tuple padding. The AAC decoder knows the # true section length from side information (band limits) and truncates. # ------------------------------------------------------------ from __future__ import annotations from typing import Any import numpy as np from material.huff_utils import encode_huff, decode_huff def aac_encode_huff( coeff_sec: np.ndarray, huff_LUT_list: list[dict[str, Any]], force_codebook: int | None = None, ) -> tuple[str, int]: """ Huffman-encode a section of coefficients (MDCT symbols or scalefactors). Parameters ---------- coeff_sec : np.ndarray Coefficient section to be encoded. Any shape is accepted; the input is flattened and treated as a 1-D sequence of int64 symbols. huff_LUT_list : list[dict[str, Any]] List of Huffman Look-Up Tables (LUTs) as returned by material.load_LUT(). Index corresponds to codebook id (typically 1..11, with 0 reserved). force_codebook : int | None If provided, forces the use of this Huffman codebook. In the assignment, scalefactors are encoded with codebook 11. For MDCT coefficients, this argument is usually omitted (auto-selection). Returns ------- tuple[str, int] (huff_sec, huff_codebook) - huff_sec: bitstream as a string of '0'/'1' - huff_codebook: codebook id used by the encoder """ coeff_sec_arr = np.asarray(coeff_sec, dtype=np.int64).reshape(-1) if force_codebook is None: # Provided utility returns (bitstream, codebook) in the auto-selection case. huff_sec, huff_codebook = encode_huff(coeff_sec_arr, huff_LUT_list) return str(huff_sec), int(huff_codebook) # Provided utility returns ONLY the bitstream when force_codebook is set. cb = int(force_codebook) huff_sec = encode_huff(coeff_sec_arr, huff_LUT_list, force_codebook=cb) return str(huff_sec), cb def aac_decode_huff( huff_sec: str | np.ndarray, huff_codebook: int, huff_LUT: list[dict[str, Any]], ) -> np.ndarray: """ Huffman-decode a bitstream using the specified codebook. Parameters ---------- huff_sec : str | np.ndarray Huffman bitstream. Typically a string of '0'/'1'. If an array is provided, it is passed through to the provided decoder. huff_codebook : int Codebook id that was returned by aac_encode_huff. Codebook 0 represents an all-zero section. huff_LUT : list[dict[str, Any]] Huffman LUT list as returned by material.load_LUT(). Returns ------- np.ndarray Decoded coefficients as a 1-D np.int64 array. Note: Due to tuple coding, the decoded array may contain extra trailing padding symbols. The caller must truncate to the known section length. """ cb = int(huff_codebook) if cb == 0: # Codebook 0 represents an all-zero section. The decoded length is not # recoverable from the bitstream alone; the caller must expand/truncate. return np.zeros((0,), dtype=np.int64) if cb < 0 or cb >= len(huff_LUT): raise ValueError(f"Invalid Huffman codebook index: {cb}") lut = huff_LUT[cb] dec = decode_huff(huff_sec, lut) return np.asarray(dec, dtype=np.int64).reshape(-1)