Level 3: First positive SNR version
This commit is contained in:
@@ -214,7 +214,10 @@ def aac_pack_frame_f_to_seq_channels(frame_type: FrameType, frame_f: FrameF) ->
|
||||
# Level 1 encoder
|
||||
# -----------------------------------------------------------------------------
|
||||
|
||||
def aac_coder_1(filename_in: Union[str, Path]) -> AACSeq1:
|
||||
def aac_coder_1(
|
||||
filename_in: Union[str, Path],
|
||||
verbose: bool = False
|
||||
) -> AACSeq1:
|
||||
"""
|
||||
Level-1 AAC encoder.
|
||||
|
||||
@@ -231,6 +234,8 @@ def aac_coder_1(filename_in: Union[str, Path]) -> AACSeq1:
|
||||
filename_in : Union[str, Path]
|
||||
Input WAV filename.
|
||||
Assumption: stereo audio, sampling rate 48 kHz.
|
||||
verbose : bool
|
||||
Optional argument to print encoding status
|
||||
|
||||
Returns
|
||||
-------
|
||||
@@ -257,8 +262,8 @@ def aac_coder_1(filename_in: Union[str, Path]) -> AACSeq1:
|
||||
aac_seq: AACSeq1 = []
|
||||
prev_frame_type: FrameType = "OLS"
|
||||
|
||||
win_type: WinType = WIN_TYPE
|
||||
|
||||
if verbose:
|
||||
print("Encoding ", end="", flush=True)
|
||||
for i in range(K):
|
||||
start = i * hop
|
||||
|
||||
@@ -275,23 +280,31 @@ def aac_coder_1(filename_in: Union[str, Path]) -> AACSeq1:
|
||||
next_t = np.vstack([next_t, tail])
|
||||
|
||||
frame_type = aac_ssc(frame_t, next_t, prev_frame_type)
|
||||
frame_f = aac_filter_bank(frame_t, frame_type, win_type)
|
||||
frame_f = aac_filter_bank(frame_t, frame_type, WIN_TYPE)
|
||||
|
||||
chl_f, chr_f = aac_pack_frame_f_to_seq_channels(frame_type, frame_f)
|
||||
|
||||
aac_seq.append({
|
||||
"frame_type": frame_type,
|
||||
"win_type": win_type,
|
||||
"win_type": WIN_TYPE,
|
||||
"chl": {"frame_F": chl_f},
|
||||
"chr": {"frame_F": chr_f},
|
||||
})
|
||||
|
||||
prev_frame_type = frame_type
|
||||
if verbose and (i % (K//20)) == 0:
|
||||
print(".", end="", flush=True)
|
||||
|
||||
if verbose:
|
||||
print(" done")
|
||||
|
||||
return aac_seq
|
||||
|
||||
|
||||
def aac_coder_2(filename_in: Union[str, Path]) -> AACSeq2:
|
||||
def aac_coder_2(
|
||||
filename_in: Union[str, Path],
|
||||
verbose: bool = False
|
||||
) -> AACSeq2:
|
||||
"""
|
||||
Level-2 AAC encoder (Level 1 + TNS).
|
||||
|
||||
@@ -299,6 +312,8 @@ def aac_coder_2(filename_in: Union[str, Path]) -> AACSeq2:
|
||||
----------
|
||||
filename_in : Union[str, Path]
|
||||
Input WAV filename (stereo, 48 kHz).
|
||||
verbose : bool
|
||||
Optional argument to print encoding status
|
||||
|
||||
Returns
|
||||
-------
|
||||
@@ -330,6 +345,8 @@ def aac_coder_2(filename_in: Union[str, Path]) -> AACSeq2:
|
||||
aac_seq: AACSeq2 = []
|
||||
prev_frame_type: FrameType = "OLS"
|
||||
|
||||
if verbose:
|
||||
print("Encoding ", end="", flush=True)
|
||||
for i in range(K):
|
||||
start = i * hop
|
||||
|
||||
@@ -347,16 +364,7 @@ def aac_coder_2(filename_in: Union[str, Path]) -> AACSeq2:
|
||||
# Level 1 analysis (packed stereo container)
|
||||
frame_f_stereo = aac_filter_bank(frame_t, frame_type, WIN_TYPE)
|
||||
|
||||
# Unpack to per-channel (as you already do in Level 1)
|
||||
if frame_type == "ESH":
|
||||
chl_f = np.empty((128, 8), dtype=np.float64)
|
||||
chr_f = np.empty((128, 8), dtype=np.float64)
|
||||
for j in range(8):
|
||||
chl_f[:, j] = frame_f_stereo[:, 2 * j + 0]
|
||||
chr_f[:, j] = frame_f_stereo[:, 2 * j + 1]
|
||||
else:
|
||||
chl_f = frame_f_stereo[:, 0:1].astype(np.float64, copy=False)
|
||||
chr_f = frame_f_stereo[:, 1:2].astype(np.float64, copy=False)
|
||||
chl_f, chr_f = aac_pack_frame_f_to_seq_channels(frame_type, frame_f_stereo)
|
||||
|
||||
# Level 2: apply TNS per channel
|
||||
chl_f_tns, chl_tns_coeffs = aac_tns(chl_f, frame_type)
|
||||
@@ -370,8 +378,12 @@ def aac_coder_2(filename_in: Union[str, Path]) -> AACSeq2:
|
||||
"chr": {"frame_F": chr_f_tns, "tns_coeffs": chr_tns_coeffs},
|
||||
}
|
||||
)
|
||||
|
||||
prev_frame_type = frame_type
|
||||
if verbose and (i % (K//20)) == 0:
|
||||
print(".", end="", flush=True)
|
||||
|
||||
if verbose:
|
||||
print(" done")
|
||||
|
||||
return aac_seq
|
||||
|
||||
@@ -379,6 +391,7 @@ def aac_coder_2(filename_in: Union[str, Path]) -> AACSeq2:
|
||||
def aac_coder_3(
|
||||
filename_in: Union[str, Path],
|
||||
filename_aac_coded: Union[str, Path] | None = None,
|
||||
verbose: bool = False,
|
||||
) -> AACSeq3:
|
||||
"""
|
||||
Level-3 AAC encoder (Level 2 + Psycho + Quantizer + Huffman).
|
||||
@@ -389,6 +402,8 @@ def aac_coder_3(
|
||||
Input WAV filename (stereo, 48 kHz).
|
||||
filename_aac_coded : Union[str, Path] | None
|
||||
Optional .mat filename to store aac_seq_3 (assignment convenience).
|
||||
verbose : bool
|
||||
Optional argument to print encoding status
|
||||
|
||||
Returns
|
||||
-------
|
||||
@@ -416,15 +431,14 @@ def aac_coder_3(
|
||||
aac_seq: AACSeq3 = []
|
||||
prev_frame_type: FrameType = "OLS"
|
||||
|
||||
# Pin win_type to the WinType literal for type checkers.
|
||||
win_type: WinType = WIN_TYPE
|
||||
|
||||
# Psycho model needs per-channel history (prev1, prev2) of 2048-sample frames.
|
||||
prev1_L = np.zeros((2048,), dtype=np.float64)
|
||||
prev2_L = np.zeros((2048,), dtype=np.float64)
|
||||
prev1_R = np.zeros((2048,), dtype=np.float64)
|
||||
prev2_R = np.zeros((2048,), dtype=np.float64)
|
||||
|
||||
if verbose:
|
||||
print("Encoding ", end="", flush=True)
|
||||
for i in range(K):
|
||||
start = i * hop
|
||||
|
||||
@@ -440,7 +454,7 @@ def aac_coder_3(
|
||||
frame_type = aac_ssc(frame_t, next_t, prev_frame_type)
|
||||
|
||||
# Analysis filterbank (stereo packed)
|
||||
frame_f_stereo = aac_filter_bank(frame_t, frame_type, win_type)
|
||||
frame_f_stereo = aac_filter_bank(frame_t, frame_type, WIN_TYPE)
|
||||
chl_f, chr_f = aac_pack_frame_f_to_seq_channels(frame_type, frame_f_stereo)
|
||||
|
||||
# TNS per channel
|
||||
@@ -474,32 +488,35 @@ def aac_coder_3(
|
||||
# Codebook 11:
|
||||
# maxAbsCodeVal = 16 is RESERVED for ESCAPE.
|
||||
# We must stay strictly within [-15, +15] to avoid escape decoding.
|
||||
sf_cb = 11
|
||||
sf_max_abs = int(huff_LUT_list[sf_cb]["maxAbsCodeVal"]) - 1 # -> 15
|
||||
# sf_cb = 11
|
||||
# sf_max_abs = int(huff_LUT_list[sf_cb]["maxAbsCodeVal"]) - 1 # -> 15
|
||||
#
|
||||
# sfc_L_dpcm = np.clip(
|
||||
# sfc_L_dpcm,
|
||||
# -sf_max_abs,
|
||||
# sf_max_abs,
|
||||
# ).astype(np.int64, copy=False)
|
||||
#
|
||||
# sfc_R_dpcm = np.clip(
|
||||
# sfc_R_dpcm,
|
||||
# -sf_max_abs,
|
||||
# sf_max_abs,
|
||||
# ).astype(np.int64, copy=False)
|
||||
|
||||
sfc_L_dpcm = np.clip(
|
||||
sfc_L_dpcm,
|
||||
-sf_max_abs,
|
||||
sf_max_abs,
|
||||
).astype(np.int64, copy=False)
|
||||
|
||||
sfc_R_dpcm = np.clip(
|
||||
sfc_R_dpcm,
|
||||
-sf_max_abs,
|
||||
sf_max_abs,
|
||||
).astype(np.int64, copy=False)
|
||||
|
||||
sfc_L_stream, _ = aac_encode_huff(
|
||||
sfc_L_stream, cb_sfc_L = aac_encode_huff(
|
||||
sfc_L_dpcm.reshape(-1, order="F"),
|
||||
huff_LUT_list,
|
||||
force_codebook=sf_cb,
|
||||
# force_codebook=11,
|
||||
)
|
||||
sfc_R_stream, _ = aac_encode_huff(
|
||||
sfc_R_stream, cb_sfc_R = aac_encode_huff(
|
||||
sfc_R_dpcm.reshape(-1, order="F"),
|
||||
huff_LUT_list,
|
||||
force_codebook=sf_cb,
|
||||
# force_codebook=11,
|
||||
)
|
||||
|
||||
if cb_sfc_L != 11 or cb_sfc_R != 11:
|
||||
print (f"frame: {i}: cb_sfc_l={cb_sfc_L}, cb_sfc_r={cb_sfc_R}")
|
||||
|
||||
mdct_L_stream, cb_L = aac_encode_huff(
|
||||
np.asarray(S_L, dtype=np.int64).reshape(-1),
|
||||
huff_LUT_list,
|
||||
@@ -512,7 +529,7 @@ def aac_coder_3(
|
||||
# Typed dict construction helps static analyzers validate the schema.
|
||||
frame_out: AACSeq3Frame = {
|
||||
"frame_type": frame_type,
|
||||
"win_type": win_type,
|
||||
"win_type": WIN_TYPE,
|
||||
"chl": {
|
||||
"tns_coeffs": np.asarray(chl_tns_coeffs, dtype=np.float64),
|
||||
"T": np.asarray(T_L, dtype=np.float64),
|
||||
@@ -539,6 +556,11 @@ def aac_coder_3(
|
||||
prev1_R = frame_R
|
||||
|
||||
prev_frame_type = frame_type
|
||||
if verbose and (i % (K//20)) == 0:
|
||||
print(".", end="", flush=True)
|
||||
|
||||
if verbose:
|
||||
print(" done")
|
||||
|
||||
# Optional: store to .mat for the assignment wrapper
|
||||
if filename_aac_coded is not None:
|
||||
@@ -548,6 +570,5 @@ def aac_coder_3(
|
||||
{"aac_seq_3": np.array(aac_seq, dtype=object)},
|
||||
do_compression=True,
|
||||
)
|
||||
|
||||
return aac_seq
|
||||
|
||||
|
||||
@@ -118,7 +118,11 @@ def aac_remove_padding(y_pad: StereoSignal, hop: int = 1024) -> StereoSignal:
|
||||
# Level 1 decoder
|
||||
# -----------------------------------------------------------------------------
|
||||
|
||||
def aac_decoder_1(aac_seq_1: AACSeq1, filename_out: Union[str, Path]) -> StereoSignal:
|
||||
def aac_decoder_1(
|
||||
aac_seq_1: AACSeq1,
|
||||
filename_out: Union[str, Path],
|
||||
verbose: bool = False
|
||||
) -> StereoSignal:
|
||||
"""
|
||||
Level-1 AAC decoder (inverse of aac_coder_1()).
|
||||
|
||||
@@ -134,6 +138,8 @@ def aac_decoder_1(aac_seq_1: AACSeq1, filename_out: Union[str, Path]) -> StereoS
|
||||
Encoded sequence as produced by aac_coder_1().
|
||||
filename_out : Union[str, Path]
|
||||
Output WAV filename. Assumption: 48 kHz, stereo.
|
||||
verbose : bool
|
||||
Optional argument to print encoding status
|
||||
|
||||
Returns
|
||||
-------
|
||||
@@ -152,6 +158,8 @@ def aac_decoder_1(aac_seq_1: AACSeq1, filename_out: Union[str, Path]) -> StereoS
|
||||
n_pad = (K - 1) * hop + win
|
||||
y_pad: StereoSignal = np.zeros((n_pad, 2), dtype=np.float64)
|
||||
|
||||
if verbose:
|
||||
print("Decoding ", end="", flush=True)
|
||||
for i, fr in enumerate(aac_seq_1):
|
||||
frame_type: FrameType = fr["frame_type"]
|
||||
win_type: WinType = fr["win_type"]
|
||||
@@ -164,12 +172,15 @@ def aac_decoder_1(aac_seq_1: AACSeq1, filename_out: Union[str, Path]) -> StereoS
|
||||
|
||||
start = i * hop
|
||||
y_pad[start:start + win, :] += frame_t_hat
|
||||
if verbose and (i % (K//20)) == 0:
|
||||
print(".", end="", flush=True)
|
||||
|
||||
y: StereoSignal = aac_remove_padding(y_pad, hop=hop)
|
||||
if verbose:
|
||||
print(" done")
|
||||
|
||||
# Level 1 assumption: 48 kHz output.
|
||||
sf.write(str(filename_out), y, 48000)
|
||||
|
||||
return y
|
||||
|
||||
|
||||
@@ -177,7 +188,11 @@ def aac_decoder_1(aac_seq_1: AACSeq1, filename_out: Union[str, Path]) -> StereoS
|
||||
# Level 2 decoder
|
||||
# -----------------------------------------------------------------------------
|
||||
|
||||
def aac_decoder_2(aac_seq_2: AACSeq2, filename_out: Union[str, Path]) -> StereoSignal:
|
||||
def aac_decoder_2(
|
||||
aac_seq_2: AACSeq2,
|
||||
filename_out: Union[str, Path],
|
||||
verbose: bool = False
|
||||
) -> StereoSignal:
|
||||
"""
|
||||
Level-2 AAC decoder (inverse of aac_coder_2).
|
||||
|
||||
@@ -195,6 +210,8 @@ def aac_decoder_2(aac_seq_2: AACSeq2, filename_out: Union[str, Path]) -> StereoS
|
||||
Encoded sequence as produced by aac_coder_2().
|
||||
filename_out : Union[str, Path]
|
||||
Output WAV filename.
|
||||
verbose : bool
|
||||
Optional argument to print encoding status
|
||||
|
||||
Returns
|
||||
-------
|
||||
@@ -213,6 +230,8 @@ def aac_decoder_2(aac_seq_2: AACSeq2, filename_out: Union[str, Path]) -> StereoS
|
||||
n_pad = (K - 1) * hop + win
|
||||
y_pad = np.zeros((n_pad, 2), dtype=np.float64)
|
||||
|
||||
if verbose:
|
||||
print("Decoding ", end="", flush=True)
|
||||
for i, fr in enumerate(aac_seq_2):
|
||||
frame_type: FrameType = fr["frame_type"]
|
||||
win_type: WinType = fr["win_type"]
|
||||
@@ -260,15 +279,23 @@ def aac_decoder_2(aac_seq_2: AACSeq2, filename_out: Union[str, Path]) -> StereoS
|
||||
|
||||
start = i * hop
|
||||
y_pad[start : start + win, :] += frame_t_hat
|
||||
if verbose and (i % (K//20)) == 0:
|
||||
print(".", end="", flush=True)
|
||||
|
||||
y = aac_remove_padding(y_pad, hop=hop)
|
||||
if verbose:
|
||||
print(" done")
|
||||
|
||||
sf.write(str(filename_out), y, 48000)
|
||||
return y
|
||||
|
||||
|
||||
|
||||
def aac_decoder_3(aac_seq_3: AACSeq3, filename_out: Union[str, Path]) -> StereoSignal:
|
||||
def aac_decoder_3(
|
||||
aac_seq_3: AACSeq3,
|
||||
filename_out: Union[str, Path],
|
||||
verbose: bool = False,
|
||||
) -> StereoSignal:
|
||||
"""
|
||||
Level-3 AAC decoder (inverse of aac_coder_3).
|
||||
|
||||
@@ -286,6 +313,8 @@ def aac_decoder_3(aac_seq_3: AACSeq3, filename_out: Union[str, Path]) -> StereoS
|
||||
Encoded sequence as produced by aac_coder_3.
|
||||
filename_out : Union[str, Path]
|
||||
Output WAV filename.
|
||||
verbose : bool
|
||||
Optional argument to print encoding status
|
||||
|
||||
Returns
|
||||
-------
|
||||
@@ -307,6 +336,9 @@ def aac_decoder_3(aac_seq_3: AACSeq3, filename_out: Union[str, Path]) -> StereoS
|
||||
n_pad = (K - 1) * hop + win
|
||||
y_pad = np.zeros((n_pad, 2), dtype=np.float64)
|
||||
|
||||
if verbose:
|
||||
print("Decoding ", end="", flush=True)
|
||||
|
||||
for i, fr in enumerate(aac_seq_3):
|
||||
frame_type: FrameType = fr["frame_type"]
|
||||
win_type: WinType = fr["win_type"]
|
||||
@@ -401,7 +433,12 @@ def aac_decoder_3(aac_seq_3: AACSeq3, filename_out: Union[str, Path]) -> StereoS
|
||||
start = i * hop
|
||||
y_pad[start : start + win, :] += frame_t_hat
|
||||
|
||||
if verbose and (i % (K//20)) == 0:
|
||||
print(".", end="", flush=True)
|
||||
|
||||
y = aac_remove_padding(y_pad, hop=hop)
|
||||
if verbose:
|
||||
print(" done")
|
||||
|
||||
sf.write(str(filename_out), y, 48000)
|
||||
return y
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from pathlib import Path
|
||||
from tabnanny import verbose
|
||||
from typing import Union
|
||||
|
||||
import soundfile as sf
|
||||
@@ -52,7 +53,7 @@ def aac_coder_1(filename_in: Union[str, Path]) -> AACSeq1:
|
||||
AACSeq1
|
||||
List of encoded frames (Level 1 schema).
|
||||
"""
|
||||
return core_aac_coder_1(filename_in)
|
||||
return core_aac_coder_1(filename_in, verbose=True)
|
||||
|
||||
|
||||
def aac_decoder_1(aac_seq_1: AACSeq1, filename_out: Union[str, Path]) -> StereoSignal:
|
||||
@@ -73,7 +74,7 @@ def aac_decoder_1(aac_seq_1: AACSeq1, filename_out: Union[str, Path]) -> StereoS
|
||||
StereoSignal
|
||||
Decoded audio samples (time-domain), stereo, shape (N, 2), dtype float64.
|
||||
"""
|
||||
return core_aac_decoder_1(aac_seq_1, filename_out)
|
||||
return core_aac_decoder_1(aac_seq_1, filename_out, verbose=True)
|
||||
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
|
||||
Reference in New Issue
Block a user