145 lines
4.2 KiB
Python
145 lines
4.2 KiB
Python
# ------------------------------------------------------------
|
||
# AAC Coder/Decoder - Level 1 Wrappers + Demo
|
||
#
|
||
# Multimedia course at Aristotle University of
|
||
# Thessaloniki (AUTh)
|
||
#
|
||
# Author:
|
||
# Christos Choutouridis (ΑΕΜ 8997)
|
||
# cchoutou@ece.auth.gr
|
||
#
|
||
# Description:
|
||
# Level 1 wrapper module.
|
||
#
|
||
# This file provides:
|
||
# - Thin wrappers for Level 1 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 AACSeq1, StereoSignal
|
||
from core.aac_coder import aac_coder_1 as core_aac_coder_1
|
||
from core.aac_coder import aac_read_wav_stereo_48k
|
||
from core.aac_decoder import aac_decoder_1 as core_aac_decoder_1
|
||
from core.aac_snr_db import snr_db
|
||
|
||
|
||
# -----------------------------------------------------------------------------
|
||
# Public Level 1 API (wrappers)
|
||
# -----------------------------------------------------------------------------
|
||
|
||
def aac_coder_1(filename_in: Union[str, Path]) -> AACSeq1:
|
||
"""
|
||
Level-1 AAC encoder (wrapper).
|
||
|
||
Delegates to core implementation.
|
||
|
||
Parameters
|
||
----------
|
||
filename_in : Union[str, Path]
|
||
Input WAV filename.
|
||
Assumption: stereo audio, sampling rate 48 kHz.
|
||
|
||
Returns
|
||
-------
|
||
AACSeq1
|
||
List of encoded frames (Level 1 schema).
|
||
"""
|
||
return core_aac_coder_1(filename_in)
|
||
|
||
|
||
def aac_decoder_1(aac_seq_1: AACSeq1, filename_out: Union[str, Path]) -> StereoSignal:
|
||
"""
|
||
Level-1 AAC decoder (wrapper).
|
||
|
||
Delegates to core implementation.
|
||
|
||
Parameters
|
||
----------
|
||
aac_seq_1 : AACSeq1
|
||
Encoded sequence as produced by aac_coder_1().
|
||
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_1(aac_seq_1, filename_out)
|
||
|
||
|
||
# -----------------------------------------------------------------------------
|
||
# Demo (Level 1)
|
||
# -----------------------------------------------------------------------------
|
||
|
||
def demo_aac_1(filename_in: Union[str, Path], filename_out: Union[str, Path]) -> float:
|
||
"""
|
||
Demonstration for the Level-1 codec.
|
||
|
||
Runs:
|
||
- aac_coder_1(filename_in)
|
||
- aac_decoder_1(aac_seq_1, 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_1 = aac_coder_1(filename_in)
|
||
x_hat = aac_decoder_1(aac_seq_1, 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_1
|
||
# python -m level_1 input.wav output.wav
|
||
# or
|
||
# python -m level_1 material/LicorDeCalandraca.wav LicorDeCalandraca_out.wav
|
||
import sys
|
||
|
||
if len(sys.argv) != 3:
|
||
raise SystemExit("Usage: python -m level_1 <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_1(in_wav, out_wav)
|
||
print(f"SNR = {snr:.3f} dB")
|