144 lines
4.2 KiB
Python
144 lines
4.2 KiB
Python
# ------------------------------------------------------------
|
||
# AAC Coder/Decoder - Level 2 Wrappers + Demo
|
||
#
|
||
# Multimedia course at Aristotle University of
|
||
# Thessaloniki (AUTh)
|
||
#
|
||
# Author:
|
||
# Christos Choutouridis (ΑΕΜ 8997)
|
||
# cchoutou@ece.auth.gr
|
||
#
|
||
# Description:
|
||
# Level 2 wrapper module.
|
||
#
|
||
# This file provides:
|
||
# - Thin wrappers for Level 2 API functions (encode/decode) that delegate
|
||
# to the corresponding core implementations.
|
||
# - A demo function that runs end-to-end and computes SNR.
|
||
# - A small CLI entrypoint for convenience.
|
||
# ------------------------------------------------------------
|
||
from __future__ import annotations
|
||
|
||
from pathlib import Path
|
||
from typing import Union
|
||
|
||
import soundfile as sf
|
||
|
||
from core.aac_types import AACSeq2, StereoSignal
|
||
from core.aac_coder import aac_coder_2 as core_aac_coder_2
|
||
from core.aac_coder import aac_read_wav_stereo_48k
|
||
from core.aac_decoder import aac_decoder_2 as core_aac_decoder_2
|
||
from core.aac_utils import snr_db
|
||
|
||
# -----------------------------------------------------------------------------
|
||
# Public Level 2 API (wrappers)
|
||
# -----------------------------------------------------------------------------
|
||
|
||
def aac_coder_2(filename_in: Union[str, Path]) -> AACSeq2:
|
||
"""
|
||
Level-2 AAC encoder (wrapper).
|
||
|
||
Delegates to core implementation.
|
||
|
||
Parameters
|
||
----------
|
||
filename_in : Union[str, Path]
|
||
Input WAV filename.
|
||
Assumption: stereo audio, sampling rate 48 kHz.
|
||
|
||
Returns
|
||
-------
|
||
AACSeq2
|
||
List of encoded frames (Level 2 schema).
|
||
"""
|
||
return core_aac_coder_2(filename_in)
|
||
|
||
|
||
def i_aac_coder_2(aac_seq_2: AACSeq2, filename_out: Union[str, Path]) -> StereoSignal:
|
||
"""
|
||
Level-2 AAC decoder (wrapper).
|
||
|
||
Delegates to core implementation.
|
||
|
||
Parameters
|
||
----------
|
||
aac_seq_2 : AACSeq2
|
||
Encoded sequence as produced by aac_coder_2().
|
||
filename_out : Union[str, Path]
|
||
Output WAV filename. Assumption: 48 kHz, stereo.
|
||
|
||
Returns
|
||
-------
|
||
StereoSignal
|
||
Decoded audio samples (time-domain), stereo, shape (N, 2), dtype float64.
|
||
"""
|
||
return core_aac_decoder_2(aac_seq_2, filename_out)
|
||
|
||
|
||
# -----------------------------------------------------------------------------
|
||
# Demo (Level 2)
|
||
# -----------------------------------------------------------------------------
|
||
|
||
def demo_aac_2(filename_in: Union[str, Path], filename_out: Union[str, Path]) -> float:
|
||
"""
|
||
Demonstration for the Level-2 codec.
|
||
|
||
Runs:
|
||
- aac_coder_2(filename_in)
|
||
- aac_decoder_2(aac_seq_2, filename_out)
|
||
and computes total SNR between original and decoded audio.
|
||
|
||
Parameters
|
||
----------
|
||
filename_in : Union[str, Path]
|
||
Input WAV filename (stereo, 48 kHz).
|
||
filename_out : Union[str, Path]
|
||
Output WAV filename (stereo, 48 kHz).
|
||
|
||
Returns
|
||
-------
|
||
float
|
||
Overall SNR in dB.
|
||
"""
|
||
filename_in = Path(filename_in)
|
||
filename_out = Path(filename_out)
|
||
|
||
# Read original audio (reference) with the same validation as the codec.
|
||
x_ref, fs_ref = aac_read_wav_stereo_48k(filename_in)
|
||
if int(fs_ref) != 48000:
|
||
raise ValueError("Input sampling rate must be 48 kHz.")
|
||
|
||
# Encode / decode
|
||
aac_seq_2 = aac_coder_2(filename_in)
|
||
x_hat = i_aac_coder_2(aac_seq_2, filename_out)
|
||
|
||
# Optional sanity: ensure output file exists and is readable
|
||
_, fs_hat = sf.read(str(filename_out), always_2d=True)
|
||
if int(fs_hat) != 48000:
|
||
raise ValueError("Decoded output sampling rate must be 48 kHz.")
|
||
|
||
return snr_db(x_ref, x_hat)
|
||
|
||
|
||
# -----------------------------------------------------------------------------
|
||
# CLI
|
||
# -----------------------------------------------------------------------------
|
||
|
||
if __name__ == "__main__":
|
||
# Example:
|
||
# cd level_2
|
||
# python -m level_2 input.wav output.wav
|
||
# or
|
||
# python -m level_2 material/LicorDeCalandraca.wav LicorDeCalandraca_out_l2.wav
|
||
import sys
|
||
|
||
if len(sys.argv) != 3:
|
||
raise SystemExit("Usage: python -m level_2 <input.wav> <output.wav>")
|
||
|
||
in_wav = Path(sys.argv[1])
|
||
out_wav = Path(sys.argv[2])
|
||
|
||
print(f"Encoding/Decoding {in_wav} to {out_wav}")
|
||
snr = demo_aac_2(in_wav, out_wav)
|
||
print(f"SNR = {snr:.3f} dB")
|