Files
retroSoC/tests/test_apu_codecs.py
T

1283 lines
43 KiB
Python

"""APU-P5 codec profile, bundle, coefficient, and dependency tests."""
from __future__ import annotations
import hashlib
import json
import subprocess
import struct
import sys
from collections import Counter
from collections.abc import Callable
from functools import cache
from pathlib import Path
import pytest
ROOT = Path(__file__).resolve().parents[1]
sys.path.insert(0, str(ROOT / "scripts"))
from apu_codecs import ( # noqa: E402
AudioInfo,
CodecError,
DecodedAudio,
crc8,
crc16,
decode_flac,
decode_wav,
inspect_flac,
parse_wav,
process_pcm,
)
from apu_isa import ( # noqa: E402
APUMC_ABI_V1,
APUMC_ABI_V2,
APUMC_P5_CAPABILITY_MASK,
BitstreamOpcode,
ControlOpcode,
Entry,
Instruction,
InstructionClass,
LocalOpcode,
ScalarOpcode,
build_apumc,
parse_apumc,
validate_instruction,
)
from apu_mcasm import Assembly, assemble # noqa: E402
from apu_interpreter import Machine, TransportModel # noqa: E402
from apu_p5_coefficients import coefficient_bytes # noqa: E402
from apu_primitives import PrimitiveBam # noqa: E402
class _BitWriter:
def __init__(self) -> None:
self.bits: list[int] = []
def put(self, value: int, width: int) -> None:
self.bits.extend((value >> bit) & 1 for bit in range(width - 1, -1, -1))
def signed(self, value: int, width: int) -> None:
self.put(value & ((1 << width) - 1), width)
def rice0(self, value: int) -> None:
self.rice(value, 0)
def rice(self, value: int, parameter: int) -> None:
unsigned = (value << 1) if value >= 0 else ((-value << 1) - 1)
self.bits.extend([0] * (unsigned >> parameter))
self.bits.append(1)
if parameter:
self.put(unsigned & ((1 << parameter) - 1), parameter)
def bytes(self) -> bytes:
while len(self.bits) & 7:
self.bits.append(0)
result = bytearray(len(self.bits) // 8)
for index, bit in enumerate(self.bits):
result[index // 8] |= bit << (7 - (index & 7))
return bytes(result)
def _wav(payload: bytes, *, pad: int = 0) -> bytes:
fmt = struct.pack("<HHIIHH", 1, 2, 48000, 192000, 4, 16)
chunks = b"fmt " + struct.pack("<I", len(fmt)) + fmt
chunks += b"data" + struct.pack("<I", len(payload)) + payload
if len(payload) & 1:
chunks += bytes([pad])
return b"RIFF" + struct.pack("<I", len(chunks) + 4) + b"WAVE" + chunks
def _empty_flac() -> bytes:
streaminfo = bytearray(34)
streaminfo[0:2] = (16).to_bytes(2, "big")
streaminfo[2:4] = (16).to_bytes(2, "big")
packed = (48000 << 44) | (1 << 41) | (15 << 36)
streaminfo[10:18] = packed.to_bytes(8, "big")
return b"fLaC" + bytes([0x80]) + bytes([0, 0, 34]) + streaminfo
def _constant_flac(sample: int = -2) -> bytes:
streaminfo = bytearray(34)
streaminfo[0:2] = (16).to_bytes(2, "big")
streaminfo[2:4] = (16).to_bytes(2, "big")
packed = (48000 << 44) | (15 << 36) | 16
streaminfo[10:18] = packed.to_bytes(8, "big")
header = ((0x3FFE << 18) | (6 << 12) | (4 << 1)).to_bytes(4, "big")
header += b"\x00\x0f"
header += bytes([crc8(header)])
frame = header + b"\x00" + sample.to_bytes(2, "big", signed=True)
frame += crc16(frame).to_bytes(2, "big")
return b"fLaC" + bytes([0x80, 0, 0, 34]) + streaminfo + frame
def _framed_flac(
subframes: bytes,
assignment: int,
expected: tuple[tuple[int, ...], ...],
*,
bits: int = 16,
) -> bytes:
channels = 1 if assignment == 0 else 2
streaminfo = bytearray(34)
streaminfo[0:2] = (16).to_bytes(2, "big")
streaminfo[2:4] = (16).to_bytes(2, "big")
packed = (48000 << 44) | ((channels - 1) << 41) | ((bits - 1) << 36) | len(expected)
streaminfo[10:18] = packed.to_bytes(8, "big")
bit_code = {16: 4, 24: 6}[bits]
header = ((0x3FFE << 18) | (6 << 12) | (assignment << 4) | (bit_code << 1)).to_bytes(4, "big")
header += b"\x00" + bytes([len(expected) - 1])
header += bytes([crc8(header)])
frame = header + subframes
frame += crc16(frame).to_bytes(2, "big")
return b"fLaC" + bytes([0x80, 0, 0, 34]) + streaminfo + frame
def _wav_pcm(rate: int, channels: int, bits: int, payload: bytes) -> bytes:
alignment = channels * (bits // 8)
fmt = struct.pack("<HHIIHH", 1, channels, rate, rate * alignment, alignment, bits)
chunks = b"fmt " + struct.pack("<I", len(fmt)) + fmt
chunks += b"data" + struct.pack("<I", len(payload)) + payload
if len(payload) & 1:
chunks += b"\0"
return b"RIFF" + struct.pack("<I", len(chunks) + 4) + b"WAVE" + chunks
def _two_constant_flac(rate: int, first: int, second: int) -> bytes:
streaminfo = bytearray(34)
streaminfo[0:2] = (16).to_bytes(2, "big")
streaminfo[2:4] = (16).to_bytes(2, "big")
streaminfo[10:18] = ((rate << 44) | (15 << 36) | 32).to_bytes(8, "big")
def frame(number: int, sample: int) -> bytes:
header = ((0x3FFE << 18) | (6 << 12) | (4 << 1)).to_bytes(4, "big")
header += bytes([number, 15])
header += bytes([crc8(header)])
payload = header + b"\0" + sample.to_bytes(2, "big", signed=True)
return payload + crc16(payload).to_bytes(2, "big")
return b"fLaC" + bytes([0x80, 0, 0, 34]) + streaminfo + frame(0, first) + frame(1, second)
def _constant_frames_flac(samples: tuple[int, ...], *, total_samples: int | None = None) -> bytes:
streaminfo = bytearray(34)
streaminfo[0:2] = (16).to_bytes(2, "big")
streaminfo[2:4] = (16).to_bytes(2, "big")
advertised = len(samples) * 16 if total_samples is None else total_samples
streaminfo[10:18] = ((48000 << 44) | (15 << 36) | advertised).to_bytes(8, "big")
result = bytearray(b"fLaC" + bytes([0x80, 0, 0, 34]) + streaminfo)
for number, sample in enumerate(samples):
header = ((0x3FFE << 18) | (6 << 12) | (4 << 1)).to_bytes(4, "big")
header += bytes([number, 15])
header += bytes([crc8(header)])
frame = header + b"\0" + sample.to_bytes(2, "big", signed=True)
result.extend(frame + crc16(frame).to_bytes(2, "big"))
return bytes(result)
def _changed_geometry_header(
number: int, *, rate_code: int = 0, assignment: int = 0, bits_code: int = 4
) -> bytes:
header = (
(0x3FFE << 18) | (6 << 12) | (rate_code << 8) | (assignment << 4) | (bits_code << 1)
).to_bytes(4, "big")
header += bytes([number, 15])
return header + bytes([crc8(header)])
def _rice_flac(values: tuple[int, ...]) -> bytes:
writer = _BitWriter()
writer.put(8, 7)
writer.put(0, 1)
writer.put(0, 2)
writer.put(0, 4)
writer.put(0, 4)
for value in values:
writer.rice0(value)
image = bytearray(_framed_flac(writer.bytes(), 0, tuple((value,) for value in values)))
image[8:10] = len(values).to_bytes(2, "big")
image[10:12] = len(values).to_bytes(2, "big")
return bytes(image)
def _empty_flac_with_padding(padding_bytes: int) -> bytes:
image = bytearray(_empty_flac())
image[4] = 0
image.extend(bytes([0x81]))
image.extend(padding_bytes.to_bytes(3, "big"))
image.extend(bytes(padding_bytes))
return bytes(image)
@cache
def _release_assembly() -> Assembly:
source = (ROOT / "rtl/ip/multimedia/apu_p5_codecs.apus").read_text(encoding="utf-8")
return assemble(source, "p5", coefficient_bytes())
def _run_release_codec(
payload: bytes,
format_id: int,
*,
output_rate: int,
output_channels: int,
output_bits: int,
resample: bool = False,
downmix: bool = False,
capacity: int = 0x10000,
) -> dict[str, object]:
assembly = _release_assembly()
entry = assembly.entries[format_id]
primitives = PrimitiveBam()
primitives.memory.publish_table(coefficient_bytes())
frame_bytes = output_channels * (2 if output_bits == 16 else 4)
transport = TransportModel(
input_data=payload,
output_capacity=capacity,
output_frame_bytes=frame_bytes,
)
machine = Machine(
assembly.instructions,
entry,
target="p5",
primitives=primitives,
transport=transport,
fetch_no_retirement_cycles=0,
mc_abi=assembly.mc_abi,
)
control = (format_id << 4) | (int(downmix) << 10) | (int(resample) << 11)
pcm_format = 0 if output_bits == 16 else 1
output_config = output_rate | (output_channels << 17) | (pcm_format << 19)
context = [
control,
0,
output_config,
1,
0,
0,
0,
0,
len(payload),
capacity,
0,
0,
0,
0,
0,
0,
]
context_base = entry.scratch_base + entry.scratch_bytes - 64
primitives.memory.inject(
context_base,
b"".join(value.to_bytes(4, "little") for value in context),
)
return machine.run()
def test_p5_coefficient_payload_is_deterministic_and_phase_normalized() -> None:
payload = coefficient_bytes()
assert len(payload) == 1536 * 4
assert hashlib.sha256(payload).hexdigest() == (
"947f536d1e1f19f9199b5c756a1629402703a5e0d25dadede696314fd5e5c83f"
)
words = struct.unpack("<1536i", payload)
for phase in range(3 * 32):
assert sum(words[phase * 16 : (phase + 1) * 16]) == 1 << 30
def test_p5_release_bundle_has_frozen_targets_and_unsupported_mp3() -> None:
source = (ROOT / "rtl/ip/multimedia/apu_p5_codecs.apus").read_text(encoding="utf-8")
assembly = assemble(source, "p5", coefficient_bytes())
instructions, entries, header = parse_apumc(assembly.bundle, "p5")
assert 2048 < len(instructions) <= 4096
assert header[9] == entries[0].primitive_mask | entries[2].primitive_mask
assert header[9] & ~APUMC_P5_CAPABILITY_MASK == 0
assert entries[1].__dict__ == {
"format_id": 1,
"entry_pc": 0,
"first_pc": 0,
"last_pc": 0,
"max_loop_count": 1,
"max_retired": 1,
"scratch_base": 0,
"scratch_bytes": 0,
"primitive_mask": 0,
"table_offset": 0,
"table_bytes": 0,
}
assert instructions[0].instruction_class == InstructionClass.CONTROL
assert instructions[0].encode() == 0x0200000001000001
with pytest.raises(ValueError):
parse_apumc(assembly.bundle, "p4")
def test_release_flac_rice_parameter_survives_remainder_decode() -> None:
values = (32, -23, -50, 40, 22, -71, 86, -5, 14, -7, -10, 29, -38, 19, -14, -5)
fixed = _BitWriter()
fixed.put(8, 7)
fixed.put(0, 1)
fixed.put(0, 2)
fixed.put(0, 4)
fixed.put(5, 4)
for value in values:
fixed.rice(value, 5)
image = _framed_flac(fixed.bytes(), 0, tuple((value,) for value in values))
result = _run_release_codec(
image,
2,
output_rate=48000,
output_channels=1,
output_bits=16,
)
transport = result["transport"]
assert transport is not None
assert bytes.fromhex(transport["output"]) == struct.pack("<16h", *values)
assert transport["frames"] == len(values)
assert (transport["result_code"], transport["result_stage"]) == (0, 0)
def test_release_flac_partial_lpc_tail_does_not_execute_padding() -> None:
sample = 131081
coefficient = 16383
residual = sample - (coefficient * sample)
lpc = _BitWriter()
lpc.put(32, 7)
lpc.put(0, 1)
lpc.signed(0, 24)
lpc.put(14, 4)
lpc.signed(0, 5)
lpc.signed(coefficient, 15)
lpc.put(1, 2)
lpc.put(0, 4)
lpc.put(27, 5)
lpc.rice(sample, 27)
for _ in range(14):
lpc.rice(residual, 27)
expected_samples = ((0,),) + ((sample,),) * 15
image = _framed_flac(lpc.bytes(), 0, expected_samples, bits=24)
result = _run_release_codec(
image,
2,
output_rate=48000,
output_channels=1,
output_bits=24,
)
transport = result["transport"]
assert transport is not None
assert bytes.fromhex(transport["output"]) == struct.pack("<16i", *([0] + [sample] * 15))
assert transport["frames"] == 16
assert (transport["result_code"], transport["result_stage"]) == (0, 0)
@pytest.mark.parametrize("resample", [False, True])
def test_release_flac_refill_staging_preserves_pending_and_resampler_state(
resample: bool,
) -> None:
image = _constant_frames_flac(tuple(range(-16, 16)))
decoded = decode_flac(image)
output_rate = 96000 if resample else 48000
expected = process_pcm(
decoded,
output_rate=output_rate,
output_channels=1,
output_bits=16,
resample=resample,
)
result = _run_release_codec(
image,
2,
output_rate=output_rate,
output_channels=1,
output_bits=16,
resample=resample,
capacity=max(0x10000, len(expected.payload)),
)
transport = result["transport"]
assert transport is not None
assert bytes.fromhex(transport["output"]) == expected.payload
assert transport["frames"] == expected.frames
assert (transport["result_code"], transport["result_stage"]) == (0, 0)
def test_release_flac_sample_count_error_keeps_only_verified_prefix() -> None:
image = _constant_frames_flac((-3, 7, 11), total_samples=40)
with pytest.raises(CodecError) as caught:
decode_flac(image)
error = caught.value
assert (error.code, error.stage, error.reason) == (4, 4, 0x32)
assert error.partial_audio is not None
assert error.offset == 66
assert error.input_used == len(image)
assert error.partial_audio.samples == ((-3,),) * 16 + ((7,),) * 16
result = _run_release_codec(
image,
2,
output_rate=48000,
output_channels=1,
output_bits=16,
)
transport = result["transport"]
assert transport is not None
assert bytes.fromhex(transport["output"]) == struct.pack("<32h", *([-3] * 16 + [7] * 16))
assert transport["frames"] == 32
assert (
transport["result_code"],
transport["result_stage"],
transport["result_detail"],
) == (4, 4, 0x02010032)
@pytest.mark.parametrize(
"changed_header",
[
_changed_geometry_header(1, rate_code=1),
_changed_geometry_header(1, assignment=1),
_changed_geometry_header(1, bits_code=1),
],
)
def test_release_flac_midstream_geometry_error_keeps_verified_prefix(
changed_header: bytes,
) -> None:
first = _constant_frames_flac((23,), total_samples=0)
image = first + changed_header
with pytest.raises(CodecError) as caught:
decode_flac(image)
error = caught.value
assert (error.code, error.stage, error.reason) == (4, 4, 0x18)
assert error.partial_audio is not None
assert error.offset == len(first)
assert error.input_used == len(first) + len(changed_header) - 1
assert error.partial_audio.samples == ((23,),) * 16
result = _run_release_codec(
image,
2,
output_rate=48000,
output_channels=1,
output_bits=16,
)
transport = result["transport"]
assert transport is not None
assert bytes.fromhex(transport["output"]) == struct.pack("<16h", *([23] * 16))
assert transport["frames"] == 16
assert (
transport["result_code"],
transport["result_stage"],
transport["result_detail"],
) == (4, 4, 0x02010018)
@pytest.mark.parametrize(
("rate", "channels", "bits", "payload", "output_channels", "output_bits"),
[
(8000, 1, 8, bytes([0, 128, 255]), 1, 16),
(48000, 2, 16, struct.pack("<hhhh", -32768, 32767, -1, 1), 2, 16),
(44100, 1, 24, b"\0\0\x80\xff\xff\x7f", 2, 24),
(96000, 1, 24, b"\0\0\x80\xff\xff\x7f", 2, 24),
(96000, 2, 32, struct.pack("<ii", -2147483648, 2147483647), 2, 24),
],
)
def test_release_wav_microprogram_matches_bam_for_frozen_widths(
rate: int,
channels: int,
bits: int,
payload: bytes,
output_channels: int,
output_bits: int,
) -> None:
image = _wav_pcm(rate, channels, bits, payload)
decoded = decode_wav(image)
expected = process_pcm(
decoded,
output_rate=rate,
output_channels=output_channels,
output_bits=output_bits,
)
result = _run_release_codec(
image,
0,
output_rate=rate,
output_channels=output_channels,
output_bits=output_bits,
)
transport = result["transport"]
assert transport is not None
assert bytes.fromhex(transport["output"]) == expected.payload
assert transport["frames"] == expected.frames
assert (transport["result_code"], transport["result_stage"]) == (0, 0)
def test_release_flac_cross_frame_resampling_matches_integer_bam() -> None:
image = _two_constant_flac(44100, -2000, 2000)
source = DecodedAudio(
AudioInfo(44100, 1, 16, 32, 0, 0, len(image)),
((-2000,),) * 16 + ((2000,),) * 16,
)
expected = process_pcm(
source,
output_rate=48000,
output_channels=1,
output_bits=16,
resample=True,
)
result = _run_release_codec(
image,
2,
output_rate=48000,
output_channels=1,
output_bits=16,
resample=True,
)
transport = result["transport"]
assert transport is not None
assert bytes.fromhex(transport["output"]) == expected.payload
assert transport["frames"] == expected.frames == 35
assert transport["result_detail"] == 0x02010000
def test_release_wav_high_expansion_uses_only_complete_256_byte_commands() -> None:
image = _wav_pcm(8000, 1, 16, bytes(64))
decoded = decode_wav(image)
expected = process_pcm(
decoded,
output_rate=96000,
output_channels=2,
output_bits=24,
resample=True,
)
result = _run_release_codec(
image,
0,
output_rate=96000,
output_channels=2,
output_bits=24,
resample=True,
)
transport = result["transport"]
assert transport is not None
assert bytes.fromhex(transport["output"]) == expected.payload
assert transport["frames"] == expected.frames == 384
assert [command["bytes"] for command in transport["output_commands"]] == [256] * 12
assert (transport["result_code"], transport["result_stage"]) == (0, 0)
def test_release_flac_entropy_and_reconstruction_profiles_match_bam() -> None:
fixed_values = tuple(range(-4, 12))
fixed = _BitWriter()
fixed.put(8, 7)
fixed.put(0, 1)
fixed.put(0, 2)
fixed.put(0, 4)
fixed.put(0, 4)
for value in fixed_values:
fixed.rice0(value)
lpc = _BitWriter()
lpc.put(32, 7)
lpc.put(0, 1)
lpc.signed(7, 16)
lpc.put(1, 4)
lpc.signed(0, 5)
lpc.signed(1, 2)
lpc.put(0, 2)
lpc.put(0, 4)
lpc.put(0, 4)
for _ in range(15):
lpc.rice0(0)
left = _BitWriter()
left.put(0, 7)
left.put(0, 1)
left.signed(5, 16)
left.put(0, 7)
left.put(0, 1)
left.signed(2, 17)
cases = (
_framed_flac(fixed.bytes(), 0, tuple((value,) for value in fixed_values)),
_framed_flac(lpc.bytes(), 0, ((7,),) * 16),
_framed_flac(left.bytes(), 8, ((5, 3),) * 16),
)
for image in cases:
decoded = decode_flac(image)
expected = process_pcm(
decoded,
output_rate=48000,
output_channels=decoded.info.channels,
output_bits=16,
)
result = _run_release_codec(
image,
2,
output_rate=48000,
output_channels=decoded.info.channels,
output_bits=16,
)
transport = result["transport"]
assert transport is not None
assert bytes.fromhex(transport["output"]) == expected.payload
assert transport["frames"] == expected.frames
assert (transport["result_code"], transport["result_stage"]) == (0, 0)
@pytest.mark.parametrize(
"values",
[
(-32768,) + (0,) * 15,
(20,) * 64,
],
)
def test_release_flac_long_rice_profiles_fit_the_retirement_budget(
values: tuple[int, ...],
) -> None:
image = _rice_flac(values)
result = _run_release_codec(
image,
2,
output_rate=48000,
output_channels=1,
output_bits=16,
)
transport = result["transport"]
assert transport is not None
assert bytes.fromhex(transport["output"]) == struct.pack(f"<{len(values)}h", *values)
assert transport["frames"] == len(values)
assert result["retired"] < (1 << 24)
assert (transport["result_code"], transport["result_stage"]) == (0, 0)
def test_release_flac_rejects_a_rice_quotient_above_65535() -> None:
result = _run_release_codec(
_rice_flac((32768,) + (0,) * 15),
2,
output_rate=48000,
output_channels=1,
output_bits=16,
)
transport = result["transport"]
assert transport is not None
assert bytes.fromhex(transport["output"]) == b""
assert (transport["result_code"], transport["result_stage"]) == (3, 5)
assert transport["result_detail"] == 0x02010004
@pytest.mark.parametrize("padding_bytes", [65535, 65536, (1024 * 1024) - 34])
def test_release_flac_consumes_complete_opaque_metadata(padding_bytes: int) -> None:
image = _empty_flac_with_padding(padding_bytes)
decoded = decode_flac(image)
assert decoded.info.samples == 0
result = _run_release_codec(
image,
2,
output_rate=48000,
output_channels=2,
output_bits=16,
)
transport = result["transport"]
assert transport is not None
assert transport["input_used"] == len(image)
assert transport["frames"] == 0
assert transport["output_commands"] == []
assert result["retired"] < (1 << 24)
assert (transport["result_code"], transport["result_stage"]) == (0, 0)
@pytest.mark.parametrize(
("mutation", "expected"),
[
(lambda payload: b"x" + payload[1:], (4, 4, 0x02010010)),
(lambda payload: payload[:-1] + bytes([payload[-1] ^ 1]), (6, 4, 0x02010031)),
],
)
def test_release_flac_malformed_vectors_report_exact_tuple(
mutation: Callable[[bytes], bytes], expected: tuple[int, int, int]
) -> None:
image = mutation(_constant_flac())
result = _run_release_codec(
image,
2,
output_rate=48000,
output_channels=1,
output_bits=16,
)
transport = result["transport"]
assert transport is not None
assert bytes.fromhex(transport["output"]) == b""
assert (
transport["result_code"],
transport["result_stage"],
transport["result_detail"],
) == expected
def test_flac_metadata_structure_precedes_impossible_length() -> None:
image = bytearray(_empty_flac())
image[4] = 0
metadata_offset = len(image)
image.extend(b"\xff\xff\xff\xff")
with pytest.raises(CodecError) as caught:
inspect_flac(bytes(image))
error = caught.value
assert (error.code, error.stage, error.reason) == (4, 4, 0x16)
assert error.offset == metadata_offset
assert error.input_used == metadata_offset + 4
result = _run_release_codec(
bytes(image),
2,
output_rate=48000,
output_channels=1,
output_bits=16,
)
transport = result["transport"]
assert transport is not None
assert bytes.fromhex(transport["output"]) == b""
assert (
transport["result_code"],
transport["result_stage"],
transport["result_detail"],
) == (4, 4, 0x02010016)
def test_flac_invalid_first_streaminfo_size_reports_header_only_input() -> None:
image = bytearray(_empty_flac())
image[5:8] = (0xFFFFFF).to_bytes(3, "big")
with pytest.raises(CodecError) as caught:
inspect_flac(bytes(image))
error = caught.value
assert (error.code, error.stage, error.reason) == (4, 4, 0x15)
assert (error.offset, error.input_used) == (5, 8)
def test_flac_frame_number_precedes_crc8_failure() -> None:
image = bytearray(_constant_flac())
frame_offset = 42
image[frame_offset + 4] = 1
image[frame_offset + 6] ^= 1
with pytest.raises(CodecError) as caught:
decode_flac(bytes(image))
error = caught.value
assert (error.code, error.stage, error.reason) == (4, 4, 0x1A)
assert error.offset == frame_offset
assert error.input_used == frame_offset + 6
def test_release_flac_rejects_nonfinal_block_below_streaminfo_minimum() -> None:
streaminfo = bytearray(34)
streaminfo[0:2] = (16).to_bytes(2, "big")
streaminfo[2:4] = (16).to_bytes(2, "big")
streaminfo[10:18] = ((48000 << 44) | (15 << 36) | 24).to_bytes(8, "big")
def frame(number: int, count: int, sample: int) -> bytes:
header = ((0x3FFE << 18) | (6 << 12) | (4 << 1)).to_bytes(4, "big")
header += bytes([number, count - 1])
header += bytes([crc8(header)])
payload = header + b"\0" + sample.to_bytes(2, "big", signed=True)
return payload + crc16(payload).to_bytes(2, "big")
image = bytearray(b"fLaC" + bytes([0x80, 0, 0, 34]) + streaminfo)
image.extend(frame(0, 8, 3))
image.extend(frame(1, 16, 4))
with pytest.raises(CodecError) as caught:
decode_flac(bytes(image))
assert (caught.value.code, caught.value.stage, caught.value.reason) == (3, 4, 0x04)
result = _run_release_codec(
bytes(image),
2,
output_rate=48000,
output_channels=1,
output_bits=16,
)
transport = result["transport"]
assert transport is not None
assert bytes.fromhex(transport["output"]) == b""
assert (transport["result_code"], transport["result_stage"]) == (3, 4)
assert transport["result_detail"] == 0x02010004
@pytest.mark.parametrize("field_offset", [12, 15])
def test_release_flac_rejects_actual_frame_size_outside_hints(field_offset: int) -> None:
image = bytearray(_constant_flac())
actual_size = len(image) - 42
if field_offset == 12:
image[field_offset : field_offset + 3] = (actual_size + 1).to_bytes(3, "big")
else:
image[field_offset : field_offset + 3] = (actual_size - 1).to_bytes(3, "big")
result = _run_release_codec(
bytes(image),
2,
output_rate=48000,
output_channels=1,
output_bits=16,
)
transport = result["transport"]
assert transport is not None
assert bytes.fromhex(transport["output"]) == b""
assert (transport["result_code"], transport["result_stage"]) == (3, 4)
assert transport["result_detail"] == 0x02010004
@pytest.mark.parametrize(
("mutation", "expected"),
[
(lambda data: data.__setitem__(slice(8, 10), (1).to_bytes(2, "big")), (3, 4, 8)),
(lambda data: data.__setitem__(slice(10, 12), (4097).to_bytes(2, "big")), (3, 4, 10)),
(lambda data: data.__setitem__(slice(15, 18), (65537).to_bytes(3, "big")), (3, 4, 15)),
(
lambda data: data.__setitem__(
slice(18, 26),
((48000 << 44) | (2 << 41) | (15 << 36)).to_bytes(8, "big"),
),
(3, 2, 18),
),
],
)
def test_flac_streaminfo_uses_frozen_first_field_diagnostic(
mutation: Callable[[bytearray], None], expected: tuple[int, int, int]
) -> None:
image = bytearray(_empty_flac())
mutation(image)
with pytest.raises(CodecError) as caught:
inspect_flac(bytes(image))
error = caught.value
assert (error.code, error.reason, error.offset) == expected
assert error.stage == 4
assert error.input_used == 42
def test_pinned_flac_streaminfo_first_offenders_cover_all_62_cases() -> None:
corpus = ROOT / ".cache/retrosoc/sources/apu-flac-corpus"
if not corpus.is_dir():
raise RuntimeError("pinned APU FLAC corpus is required; run make setup-apu-reference")
observed: Counter[tuple[int, int, int]] = Counter()
for path in sorted(corpus.rglob("*.flac")):
data = path.read_bytes()
if len(data) < 42 or data[:5] not in (b"fLaC\x00", b"fLaC\x80"):
continue
minimum = int.from_bytes(data[8:10], "big")
maximum = int.from_bytes(data[10:12], "big")
min_frame = int.from_bytes(data[12:15], "big")
max_frame = int.from_bytes(data[15:18], "big")
packed = int.from_bytes(data[18:26], "big")
rate = (packed >> 44) & 0xFFFFF
channels = ((packed >> 41) & 7) + 1
bits = ((packed >> 36) & 0x1F) + 1
expected: tuple[int, int, int] | None = None
if minimum < 16:
expected = (3, 4, 8)
elif maximum < minimum:
expected = (4, 0x15, 10)
elif channels in (1, 2) and maximum > (4096 if channels == 1 else 2048):
expected = (3, 4, 10)
elif min_frame and max_frame and min_frame > max_frame:
expected = (4, 0x15, 15)
elif max_frame > 65536:
expected = (3, 4, 15)
elif channels not in (1, 2) or bits not in (16, 24) or not 8000 <= rate <= 96000:
expected = (3, 2, 18)
if expected is None:
continue
with pytest.raises(CodecError) as caught:
inspect_flac(data)
error = caught.value
assert (error.code, error.reason, error.offset) == expected, path
observed[expected] += 1
assert observed == Counter({(3, 4, 10): 50, (3, 2, 18): 9, (3, 4, 8): 2, (3, 4, 15): 1})
def test_pinned_faulty_total_samples_keeps_exact_nine_frame_prefix() -> None:
path = (
ROOT
/ ".cache/retrosoc/sources/apu-flac-corpus/faulty/05 - wrong total number of samples.flac"
)
if not path.is_file():
raise RuntimeError("pinned APU FLAC corpus is required; run make setup-apu-reference")
with pytest.raises(CodecError) as caught:
decode_flac(path.read_bytes())
error = caught.value
assert (error.code, error.stage, error.reason) == (4, 4, 0x32)
assert (error.offset, error.input_used) == (29914, 34352)
assert error.partial_audio is not None
assert len(error.partial_audio.samples) == 36864
pcm = process_pcm(
error.partial_audio,
output_rate=24000,
output_channels=1,
output_bits=16,
)
assert len(pcm.payload) == 73728
assert hashlib.sha256(pcm.payload).hexdigest() == (
"d597319c13a24e075e7aeb7ec33e007cae95a12323d11157bf9b13805c26f14e"
)
def test_release_wav_capacity_returns_only_complete_prefix() -> None:
image = _wav_pcm(48000, 2, 16, struct.pack("<hhhh", -32768, 32767, -1, 1))
result = _run_release_codec(
image,
0,
output_rate=48000,
output_channels=2,
output_bits=16,
capacity=6,
)
transport = result["transport"]
assert transport is not None
assert bytes.fromhex(transport["output"]) == struct.pack("<hh", -32768, 32767)
assert transport["frames"] == 1
assert (transport["result_code"], transport["result_stage"]) == (8, 8)
assert transport["result_detail"] == 0x00000051
def test_crc_primitives_do_not_prefill_or_consume_the_shared_fifo() -> None:
instructions = [
Instruction(InstructionClass.SCALAR, ScalarOpcode.MOVI, dst=1, immediate=0),
Instruction(InstructionClass.SCALAR, ScalarOpcode.MOVI, dst=2, immediate=0x12),
Instruction(InstructionClass.BITSTREAM, BitstreamOpcode.CRC8, dst=3, src0=1, src1=2),
Instruction(InstructionClass.LOCAL, LocalOpcode.FIFO_POP, dst=4),
Instruction(InstructionClass.CONTROL, ControlOpcode.END),
]
entry = Entry(0, 0, 0, 4, 1, 8)
primitives = PrimitiveBam()
machine = Machine(
instructions,
entry,
target="p5",
primitives=primitives,
fetch_no_retirement_cycles=0,
mc_abi=APUMC_ABI_V2,
)
primitives.input_fifo.push(0x0000010444332211)
result = machine.run()
assert result["registers"][4:6] == [0x44332211, 0x104]
def test_p5_release_microassembly_matches_the_deterministic_emitter() -> None:
subprocess.run(
[
sys.executable,
str(ROOT / "scripts/generate_apu_p5_microcode.py"),
"--output",
str(ROOT / "rtl/ip/multimedia/apu_p5_codecs.apus"),
"--check",
],
check=True,
)
def _capacity_program(count: int) -> tuple[list[Instruction], list[Entry]]:
instructions = [Instruction(InstructionClass.CONTROL, ControlOpcode.NOP)] * (count - 1)
instructions.append(Instruction(InstructionClass.CONTROL, ControlOpcode.END))
entries = [Entry(index, 0, 0, count - 1, 1, count) for index in range(3)]
return instructions, entries
@pytest.mark.parametrize(
("abi", "count"),
[(APUMC_ABI_V1, 2048), (APUMC_ABI_V2, 2049), (APUMC_ABI_V2, 2437), (APUMC_ABI_V2, 4096)],
)
def test_p5_apumc_capacity_boundaries_accept(abi: int, count: int) -> None:
instructions, entries = _capacity_program(count)
bundle = build_apumc(instructions, entries, target="p5", abi=abi)
parsed, parsed_entries, header = parse_apumc(bundle, "p5")
assert len(parsed) == count
assert parsed_entries == entries
assert header[1] == abi
@pytest.mark.parametrize(("abi", "count"), [(APUMC_ABI_V1, 2049), (APUMC_ABI_V2, 4097)])
def test_p5_apumc_capacity_boundaries_reject(abi: int, count: int) -> None:
instructions, entries = _capacity_program(count)
with pytest.raises(ValueError, match="instruction count"):
build_apumc(instructions, entries, target="p5", abi=abi)
def test_p5_apumc_v2_round_trips_high_pcs_and_branch_bit_11() -> None:
instructions = [Instruction(InstructionClass.CONTROL, ControlOpcode.NOP)] * 4096
instructions[2047] = Instruction(InstructionClass.CONTROL, ControlOpcode.END)
instructions[4095] = Instruction(InstructionClass.CONTROL, ControlOpcode.END)
entries = [
Entry(0, 0, 0, 2047, 1, 2048),
Entry(1, 2047, 2047, 2048, 1, 2),
Entry(2, 2048, 2048, 4095, 1, 4096),
]
bundle = build_apumc(instructions, entries, target="p5", abi=APUMC_ABI_V2)
_, parsed_entries, _ = parse_apumc(bundle, "p5")
assert parsed_entries == entries
wide_branch = Instruction(InstructionClass.CONTROL, ControlOpcode.JUMP_FWD, immediate=2048)
validate_instruction(wide_branch, "p5", APUMC_ABI_V2)
with pytest.raises(ValueError, match="image PC width"):
validate_instruction(wide_branch, "p5", APUMC_ABI_V1)
def test_p5_interpreter_executes_v2_branch_bit_11() -> None:
instructions = [Instruction(InstructionClass.CONTROL, ControlOpcode.NOP)] * 2050
instructions[0] = Instruction(InstructionClass.CONTROL, ControlOpcode.JUMP_FWD, immediate=2048)
instructions[2049] = Instruction(InstructionClass.CONTROL, ControlOpcode.END)
entry = Entry(0, 0, 0, 2049, 1, 4)
result = Machine(instructions, entry, target="p5", mc_abi=APUMC_ABI_V2).run()
assert (result["pc"], result["retired"]) == (2049, 2)
def test_p5_release_reports_hashes_and_storage_high_water(tmp_path: Path) -> None:
subprocess.run(
[
sys.executable,
str(ROOT / "scripts/build_apu_p5_bundle.py"),
"--output-dir",
str(tmp_path),
],
check=True,
)
manifest = json.loads((tmp_path / "manifest.json").read_text(encoding="utf-8"))
high_water = json.loads((tmp_path / "high-water.json").read_text(encoding="utf-8"))
cfg_report = json.loads((tmp_path / "cfg-report.json").read_text(encoding="utf-8"))
primitive_manifest = json.loads(
(tmp_path / "primitive-manifest.json").read_text(encoding="utf-8")
)
assert (
manifest["sha256"]["bundle"]
== hashlib.sha256((tmp_path / "apu-p5.apumc").read_bytes()).hexdigest()
)
assert (
manifest["sha256"]["coefficients"]
== hashlib.sha256((tmp_path / "apu-p5-coefficients.bin").read_bytes()).hexdigest()
)
assert high_water["instruction_words"] == manifest["instruction_count"]
assert manifest["abi"] == "2.0"
assert manifest["mc_abi"] == "0x00020000"
assert high_water["apumc_version"] == "0x00020000"
assert high_water["maximum_instruction_words"] == 4096
assert high_water["free_instruction_words"] == 4096 - high_water["instruction_words"]
assert high_water["control_store_bytes"] <= 32 * 1024
assert high_water["table_bytes"] <= 6144
assert high_water["maximum_scratch_end"] <= 0x6000
proof_workspace = high_water["loader_proof_workspace"]
assert proof_workspace == {
"memo_entries": 8192,
"memo_entry_bits": 65,
"memo_storage_bytes_ceiling": 66560,
"pending_path_records_v1": 2048,
"pending_path_records_v2": 4096,
"pending_record_bits": 64,
"pending_storage_bytes_v1": 16384,
"pending_storage_bytes_v2": 32768,
"traversal_limit_v1": 131072,
"traversal_limit_v2": 262144,
}
assert len(cfg_report["control_flow"]) == 3
assert primitive_manifest["implemented_mask"] == 0x001FFFFF
assert primitive_manifest["required_mask"] == 0x001FF83B
for name, digest in manifest["sha256"]["reports"].items():
assert digest == hashlib.sha256((tmp_path / name).read_bytes()).hexdigest()
def test_wav_profile_accepts_pcm_and_enforces_padding_and_extent() -> None:
image = _wav(b"\x00\x80\x00\x7f")
info = parse_wav(image)
assert (info.rate, info.channels, info.bits, info.samples) == (48000, 2, 16, 1)
assert info.input_used == len(image)
malformed = bytearray(image)
malformed[4:8] = UINT32_MAX = (0xFFFFFFFF).to_bytes(4, "little")
assert malformed[4:8] == UINT32_MAX
with pytest.raises(CodecError) as caught:
parse_wav(bytes(malformed))
assert (caught.value.code, caught.value.reason, caught.value.offset) == (4, 0x11, 4)
odd = _wav(b"\x00", pad=0xA5)
with pytest.raises(CodecError) as caught:
parse_wav(odd)
assert caught.value.reason == 0x13
@pytest.mark.parametrize(
("bits", "encoded", "expected"),
[
(8, bytes([0, 128, 255]), (-128, 0, 127)),
(16, struct.pack("<hhh", -32768, 0, 32767), (-32768, 0, 32767)),
(
24,
b"\x00\x00\x80\x00\x00\x00\xff\xff\x7f",
(-8388608, 0, 8388607),
),
(
32,
struct.pack("<iii", -2147483648, 0, 2147483647),
(-2147483648, 0, 2147483647),
),
],
)
def test_wav_decode_all_frozen_integer_widths(
bits: int, encoded: bytes, expected: tuple[int, ...]
) -> None:
alignment = bits // 8
fmt = struct.pack("<HHIIHH", 1, 1, 8000, 8000 * alignment, alignment, bits)
chunks = b"fmt " + struct.pack("<I", len(fmt)) + fmt
chunks += b"data" + struct.pack("<I", len(encoded)) + encoded
if len(encoded) & 1:
chunks += b"\x00"
image = b"RIFF" + struct.pack("<I", len(chunks) + 4) + b"WAVE" + chunks
decoded = decode_wav(image)
assert tuple(frame[0] for frame in decoded.samples) == expected
def test_flac_streaminfo_profile_and_crc_helpers() -> None:
info = inspect_flac(_empty_flac())
assert (info.rate, info.channels, info.bits, info.samples, info.warnings) == (
48000,
2,
16,
None,
1,
)
assert crc8(b"123456789") == 0xF4
assert crc16(b"123456789") == 0xFEE8
decoded = decode_flac(_empty_flac())
assert decoded.samples == ()
assert decoded.info.input_used == len(_empty_flac())
def test_native_flac_constant_frame_decodes_before_publication() -> None:
image = _constant_flac()
decoded = decode_flac(image)
assert decoded.info.samples == 16
assert decoded.samples == ((-2,),) * 16
damaged = bytearray(image)
damaged[-1] ^= 1
with pytest.raises(CodecError) as caught:
decode_flac(bytes(damaged))
assert (caught.value.code, caught.value.stage, caught.value.reason) == (6, 4, 0x31)
def test_native_flac_fixed_lpc_and_side_channel_reconstruction() -> None:
fixed_values = tuple(range(-4, 12))
fixed = _BitWriter()
fixed.put(8, 7)
fixed.put(0, 1)
fixed.put(0, 2)
fixed.put(0, 4)
fixed.put(0, 4)
for value in fixed_values:
fixed.rice0(value)
fixed_image = _framed_flac(fixed.bytes(), 0, tuple((value,) for value in fixed_values))
assert decode_flac(fixed_image).samples == tuple((value,) for value in fixed_values)
lpc = _BitWriter()
lpc.put(32, 7)
lpc.put(0, 1)
lpc.signed(7, 16)
lpc.put(1, 4)
lpc.signed(0, 5)
lpc.signed(1, 2)
lpc.put(0, 2)
lpc.put(0, 4)
lpc.put(0, 4)
for _ in range(15):
lpc.rice0(0)
lpc_expected = ((7,),) * 16
assert decode_flac(_framed_flac(lpc.bytes(), 0, lpc_expected)).samples == lpc_expected
left = _BitWriter()
left.put(0, 7)
left.put(0, 1)
left.signed(5, 16)
left.put(0, 7)
left.put(0, 1)
left.signed(2, 17)
stereo_expected = ((5, 3),) * 16
assert decode_flac(_framed_flac(left.bytes(), 8, stereo_expected)).samples == stereo_expected
def test_integer_postprocessing_downmix_resample_and_pack() -> None:
stereo = DecodedAudio(
AudioInfo(48000, 2, 16, 2, 0, 8, 8),
((32767, -32768), (1, 0)),
)
passthrough = process_pcm(stereo, output_channels=2, output_bits=16)
assert passthrough.payload == struct.pack("<hhhh", 32767, -32768, 1, 0)
downmixed = process_pcm(stereo, output_channels=1, output_bits=16, downmix=True)
assert struct.unpack("<hh", downmixed.payload) == (-1, 0)
mono = DecodedAudio(
AudioInfo(44100, 1, 16, 147, 0, 294, 294),
tuple((index - 73,) for index in range(147)),
)
converted = process_pcm(
mono,
output_rate=48000,
output_channels=1,
output_bits=24,
resample=True,
i2s=True,
)
assert converted.rate == 48000
assert converted.channels == 2
assert converted.frames == 160
words = struct.unpack(f"<{converted.frames * 2}i", converted.payload)
assert all(words[index] == words[index + 1] for index in range(0, len(words), 2))
def test_p5_corpus_qualifier_emits_per_file_agreement(tmp_path: Path) -> None:
corpus = tmp_path / "corpus"
corpus.mkdir()
(corpus / "LICENSE.txt").write_text("CC0\n", encoding="utf-8")
(corpus / "constant.flac").write_bytes(_constant_flac())
reference = tmp_path / "flac"
reference.write_text(
"#!/usr/bin/env python3\nimport sys\nsys.stdout.buffer.write(b'\\xfe\\xff' * 16)\n",
encoding="utf-8",
)
reference.chmod(0o755)
manifest_path = tmp_path / "corpus.json"
subprocess.run(
[
sys.executable,
str(ROOT / "scripts/qualify_apu_p5_corpus.py"),
"--flac",
str(reference),
"--corpus",
str(corpus),
"--output",
str(manifest_path),
],
check=True,
)
manifest = json.loads(manifest_path.read_text(encoding="utf-8"))
assert manifest["file_count"] == 1
assert manifest["counts"] == {"malformed": 0, "supported": 1, "unsupported": 0}
assert manifest["files"][0]["model_reference_agreement"] is True
truth = manifest["files"][0]["production_truth"]
assert truth == {
"code": 0,
"error_offset": 0,
"frames": 16,
"input_used": len(_constant_flac()),
"input_used_policy": "exact",
"output_bytes": 32,
"pcm_sha256": hashlib.sha256(b"\xfe\xff" * 16).hexdigest(),
"reason": 0,
"source_info": 48000 | (1 << 17) | (16 << 19),
"stage": 0,
"status": "success",
"warnings": 1,
}
def test_p5_reference_inputs_match_frozen_lock() -> None:
lock = json.loads((ROOT / "dependencies/dependencies.lock.json").read_text(encoding="utf-8"))
assert lock["sources"]["apu_libflac"]["revision"] == (
"1507800de4b70e21be71f38caa0d9079d0bc6e45"
)
assert lock["sources"]["apu_flac_corpus"]["revision"] == (
"aa7b0c6cf32994c106ae517a08134c28a96ff5b2"
)
assert lock["archives"]["apu_libflac"]["sha256"] == (
"d80ef5facdb21972efe91774da03d6b9abf216aa17093d740fcc411cd8afbb41"
)
assert lock["archives"]["apu_flac_corpus"]["sha256"] == (
"36de2310155b4084011fbd56f24603dfef91a26c5433e3f92bd21995b45089c3"
)