136 lines
13 KiB
Python
136 lines
13 KiB
Python
"""Deterministic media assets and hosted codec fixture contracts."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import base64
|
|
from dataclasses import dataclass
|
|
import io
|
|
import math
|
|
import struct
|
|
import wave
|
|
|
|
|
|
@dataclass(frozen=True)
|
|
class MediaFixtureSpec:
|
|
id: str
|
|
title: str
|
|
filename: str
|
|
overview: str
|
|
video_codec: str
|
|
width: int
|
|
height: int
|
|
size_bytes: int
|
|
sha256: str
|
|
|
|
|
|
MEDIA_FIXTURE_SPECS = (
|
|
MediaFixtureSpec(
|
|
id="codec-dv-uhd",
|
|
title="Codec DV UHD TrueHD PGS",
|
|
filename="dv8_hdr10plus_truehd_pgs.mkv",
|
|
overview="Dolby Vision profile 8 with an HDR fallback, TrueHD Atmos, AC-3, and PGS subtitles.",
|
|
video_codec="hevc",
|
|
width=3840,
|
|
height=1604,
|
|
size_bytes=154537349,
|
|
sha256="1feac8feb3bf31323a8da16a9db22549e1acfa7d9469b4b85deaa2bd145ec84c",
|
|
),
|
|
MediaFixtureSpec(
|
|
id="codec-dv-web",
|
|
title="Codec DV Web EAC3 SRT",
|
|
filename="dv8_hdr_web_eac3_atmos_srt.mkv",
|
|
overview="Dolby Vision profile 8 with an HDR fallback, E-AC-3 Atmos, and multilingual SRT subtitles.",
|
|
video_codec="hevc",
|
|
width=3840,
|
|
height=1606,
|
|
size_bytes=120581060,
|
|
sha256="9bc5e1f90c20de9317800f51424f11366bb16861f10853e93625bb649ea34343",
|
|
),
|
|
MediaFixtureSpec(
|
|
id="codec-av1",
|
|
title="Codec AV1 Opus ASS",
|
|
filename="av1_opus_ass_srt.mkv",
|
|
overview="AV1 video with multilingual Opus audio, styled ASS subtitles, SRT subtitles, and embedded fonts.",
|
|
video_codec="av1",
|
|
width=1916,
|
|
height=1072,
|
|
size_bytes=6744111,
|
|
sha256="15a825c0d2d70c7f593cde1d86c1044c440b6745735a4e53c212878fd365fccd",
|
|
),
|
|
MediaFixtureSpec(
|
|
id="codec-h264-high10",
|
|
title="Codec H264 High10 DTS-HD ASS",
|
|
filename="h264_high10_dts_hd_ass.mkv",
|
|
overview="H.264 High 10 video with dual DTS-HD MA audio, styled ASS subtitles, and embedded fonts.",
|
|
video_codec="h264",
|
|
width=1920,
|
|
height=1080,
|
|
size_bytes=57191760,
|
|
sha256="0fb22b97f3542e228d55b80aa2893c3d92934192c908e8f96e696dbaaafcfe86",
|
|
),
|
|
MediaFixtureSpec(
|
|
id="codec-h264-eac3",
|
|
title="Codec H264 EAC3 Multisub",
|
|
filename="h264_eac3_multisub.mkv",
|
|
overview="H.264 video with three E-AC-3 audio tracks and a broad multilingual SRT subtitle set.",
|
|
video_codec="h264",
|
|
width=1920,
|
|
height=1080,
|
|
size_bytes=26793597,
|
|
sha256="759cc11148b4358611f6bbc1c9a6db014ecc6dce9b907993c8e51bd885f94d18",
|
|
),
|
|
MediaFixtureSpec(
|
|
id="codec-hevc-flac",
|
|
title="Codec HEVC10 FLAC ASS",
|
|
filename="hevc10_flac_ass.mkv",
|
|
overview="HEVC Main 10 video with dual FLAC 5.1 audio, styled ASS subtitles, and embedded fonts.",
|
|
video_codec="hevc",
|
|
width=1920,
|
|
height=1080,
|
|
size_bytes=45578288,
|
|
sha256="af8950e3c5a487e39b2f23df371583fa7469003da72df723be3d0f0071cf3414",
|
|
),
|
|
)
|
|
|
|
# Thirty-second 160x90 H.264 baseline MP4 generated once with ffmpeg. Embedding the
|
|
# payload keeps CI independent of ffmpeg and gives the real player a valid range-
|
|
# requestable stream long enough for chrome auto-hide and remote-key regressions.
|
|
_VIDEO = base64.b64decode(
|
|
"AAAAIGZ0eXBpc29tAAACAGlzb21pc28yYXZjMW1wNDEAAAjJbW9vdgAAAGxtdmhkAAAAAAAAAAAAAAAAAAAD6AAAdTAAAQAAAQAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgAAB/N0cmFrAAAAXHRraGQAAAADAAAAAAAAAAAAAAABAAAAAAAAdTAAAAAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAABAAAAAAKAAAABaAAAAAAAkZWR0cwAAABxlbHN0AAAAAAAAAAEAAHUwAAAAAAABAAAAAAdrbWRpYQAAACBtZGhkAAAAAAAAAAAAAAAAAAAwAAAFoABVxAAAAAAALWhkbHIAAAAAAAAAAHZpZGUAAAAAAAAAAAAAAABWaWRlb0hhbmRsZXIAAAAHFm1pbmYAAAAUdm1oZAAAAAEAAAAAAAAAAAAAACRkaW5mAAAAHGRyZWYAAAAAAAAAAQAAAAx1cmwgAAAAAQAABtZzdGJsAAAAunN0c2QAAAAAAAAAAQAAAKphdmMxAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAKAAWgBIAAAASAAAAAAAAAABFUxhdmM2Mi4yOC4xMDIgbGlieDI2NAAAAAAAAAAAAAAAGP//AAAAMGF2Y0MBQsAe/+EAGGdCwB7ZAo35MBEAAAMAAQAAAwAYDxYuSAEABWjLg8sgAAAAEHBhc3AAAAABAAAAAQAAABRidHJ0AAAAAAAABIMAAAAAAAAAGHN0dHMAAAAAAAAAAQAAAWgAAAQAAAAAGHN0c3MAAAAAAAAAAgAAAAEAAAD7AAAAHHN0c2MAAAAAAAAAAQAAAAEAAAFoAAAAAQAABbRzdHN6AAAAAAAAAAAAAAFoAAACsgAAAAoAAAALAAAACgAAAAoAAAAKAAAACgAAAAoAAAAKAAAACgAAAAoAAAAKAAAACgAAAAoAAAAKAAAACgAAAAoAAAAKAAAACgAAAAoAAAAKAAAACgAAAAoAAAAKAAAACgAAAAoAAAAKAAAACgAAAAoAAAAKAAAACgAAAAoAAAAKAAAACgAAAAoAAAAKAAAACgAAAAoAAAAKAAAACgAAAAoAAAAKAAAACgAAAAoAAAAKAAAACgAAAAoAAAAKAAAACgAAAAoAAAAKAAAACgAAAAoAAAAKAAAACgAAAAoAAAAKAAAACgAAAAoAAAAKAAAACgAAAAoAAAAKAAAACgAAAAoAAAAKAAAACgAAAAoAAAAKAAAACgAAAAoAAAAKAAAACgAAAAoAAAAKAAAACgAAAAoAAAAKAAAACgAAAAoAAAAKAAAACgAAAAoAAAAKAAAACgAAAAoAAAAKAAAACgAAAAoAAAAKAAAACgAAAAoAAAAKAAAACgAAAAoAAAAKAAAACgAAAAoAAAAKAAAACgAAAAoAAAAKAAAACgAAAAoAAAAKAAAACgAAAAoAAAAKAAAACgAAAAoAAAAKAAAACgAAAAoAAAAKAAAACgAAAAoAAAAKAAAACgAAAAoAAAAKAAAACgAAAAoAAAAKAAAACgAAAAoAAAAKAAAACgAAAAoAAAAKAAAACgAAAAoAAAAKAAAACgAAAAoAAAAKAAAACgAAAAoAAAAKAAAACgAAAAoAAAAKAAAACgAAAAoAAAAKAAAACgAAAAoAAAAKAAAACgAAAAoAAAAKAAAACgAAAAoAAAAKAAAACgAAAAoAAAAKAAAACgAAAAoAAAAKAAAACgAAAAoAAAAKAAAACgAAAAoAAAAKAAAACgAAAAoAAAAKAAAACgAAAAoAAAAKAAAACgAAAAoAAAAKAAAACgAAAAoAAAAKAAAACgAAAAoAAAAKAAAACgAAAAoAAAAKAAAACgAAAAoAAAAKAAAACgAAAAoAAAAKAAAACgAAAAoAAAAKAAAACgAAAAoAAAAKAAAACgAAAAoAAAAKAAAACgAAAAoAAAAKAAAACgAAAAoAAAAKAAAACgAAAAoAAAAKAAAACgAAAAoAAAAKAAAACgAAAAoAAAAKAAAACgAAAAoAAAAKAAAACgAAAAoAAAAKAAAACgAAAAoAAAAKAAAACgAAAAoAAAAKAAAACgAAAAoAAAAKAAAACgAAAAoAAAAKAAAACgAAAAoAAAAKAAAACgAAAAoAAAAKAAAACgAAAAoAAAAKAAAACgAAAAoAAAAKAAAACgAAAAoAAAAKAAAACgAAAAoAAAAKAAAACgAAAD0AAAAKAAAACwAAAAoAAAAKAAAACgAAAAoAAAAKAAAACgAAAAoAAAAKAAAACgAAAAoAAAAKAAAACgAAAAoAAAAKAAAACgAAAAoAAAAKAAAACgAAAAoAAAAKAAAACgAAAAoAAAAKAAAACgAAAAoAAAAKAAAACgAAAAoAAAAKAAAACgAAAAoAAAAKAAAACgAAAAoAAAAKAAAACgAAAAoAAAAKAAAACgAAAAoAAAAKAAAACgAAAAoAAAAKAAAACgAAAAoAAAAKAAAACgAAAAoAAAAKAAAACgAAAAoAAAAKAAAACgAAAAoAAAAKAAAACgAAAAoAAAAKAAAACgAAAAoAAAAKAAAACgAAAAoAAAAKAAAACgAAAAoAAAAKAAAACgAAAAoAAAAKAAAACgAAAAoAAAAKAAAACgAAAAoAAAAKAAAACgAAAAoAAAAKAAAACgAAAAoAAAAKAAAACgAAAAoAAAAKAAAACgAAAAoAAAAKAAAACgAAAAoAAAAKAAAACgAAAAoAAAAKAAAACgAAAAoAAAAKAAAACgAAAAoAAAAKAAAACgAAAAoAAAAKAAAACgAAAAoAAAAKAAAAFHN0Y28AAAAAAAAAAQAACPkAAABidWR0YQAAAFptZXRhAAAAAAAAACFoZGxyAAAAAAAAAABtZGlyYXBwbAAAAAAAAAAAAAAAAC1pbHN0AAAAJal0b28AAAAdZGF0YQAAAAEAAAAATGF2ZjYyLjEyLjEwMgAAAAhmcmVlAAAQ9W1kYXQAAAJxBgX//23cRem95tlIt5Ys2CDZI+7veDI2NCAtIGNvcmUgMTY1IHIzMjIyIGIzNTYwNWEgLSBILjI2NC9NUEVHLTQgQVZDIGNvZGVjIC0gQ29weWxlZnQgMjAwMy0yMDI1IC0gaHR0cDovL3d3dy52aWRlb2xhbi5vcmcveDI2NC5odG1sIC0gb3B0aW9uczogY2FiYWM9MCByZWY9MyBkZWJsb2NrPTE6MDowIGFuYWx5c2U9MHgxOjB4MTExIG1lPWhleCBzdWJtZT03IHBzeT0xIHBzeV9yZD0xLjAwOjAuMDAgbWl4ZWRfcmVmPTEgbWVfcmFuZ2U9MTYgY2hyb21hX21lPTEgdHJlbGxpcz0xIDh4OGRjdD0wIGNxbT0wIGRlYWR6b25lPTIxLDExIGZhc3RfcHNraXA9MSBjaHJvbWFfcXBfb2Zmc2V0PS0yIHRocmVhZHM9MyBsb29rYWhlYWRfdGhyZWFkcz0xIHNsaWNlZF90aHJlYWRzPTAgbnI9MCBkZWNpbWF0ZT0xIGludGVybGFjZWQ9MCBibHVyYXlfY29tcGF0PTAgY29uc3RyYWluZWRfaW50cmE9MCBiZnJhbWVzPTAgd2VpZ2h0cD0wIGtleWludD0yNTAga2V5aW50X21pbj0xMiBzY2VuZWN1dD00MCBpbnRyYV9yZWZyZXNoPTAgcmNfbG9va2FoZWFkPTQwIHJjPWNyZiBtYnRyZWU9MSBjcmY9MjMuMCBxY29tcD0wLjYwIHFwbWluPTAgcXBtYXg9NjkgcXBzdGVwPTQgaXBfcmF0aW89MS40MCBhcT0xOjEuMDAAgAAAADlliIQO8mKAAL78nJycnJycnJyddddddddddddddddddddddddddddddddddddddddddddddddddeAAAAAGQZo4HeD2AAAAB0GaVAd4PYAAAAAGQZpgO8HsAAAABkGagDvB7AAAAAZBmqA7wewAAAAGQZrAO8HsAAAABkGa4DvB7AAAAAZBmwA7wewAAAAGQZsgO8HsAAAABkGbQDvB7AAAAAZBm2A7wewAAAAGQZuAO8HsAAAABkGboDvB7AAAAAZBm8A7wewAAAAGQZvgO8HsAAAABkGaADvB7AAAAAZBmiA7wewAAAAGQZpAO8HsAAAABkGaYDvB7AAAAAZBmoA7wewAAAAGQZqgO8HsAAAABkGawDvB7AAAAAZBmuA7wewAAAAGQZsAO8HsAAAABkGbIDvB7AAAAAZBm0A7wewAAAAGQZtgO8HsAAAABkGbgDvB7AAAAAZBm6A7wewAAAAGQZvAO8HsAAAABkGb4DvB7AAAAAZBmgA7wewAAAAGQZogO8HsAAAABkGaQDvB7AAAAAZBmmA7wewAAAAGQZqAO8HsAAAABkGaoDvB7AAAAAZBmsA7wewAAAAGQZrgO8HsAAAABkGbADvB7AAAAAZBmyA7wewAAAAGQZtAO8HsAAAABkGbYDvB7AAAAAZBm4A7wewAAAAGQZugO8HsAAAABkGbwDvB7AAAAAZBm+A7wewAAAAGQZoAO8HsAAAABkGaIDvB7AAAAAZBmkA7wewAAAAGQZpgO8HsAAAABkGagDvB7AAAAAZBmqA7wewAAAAGQZrAO8HsAAAABkGa4DvB7AAAAAZBmwA7wewAAAAGQZsgO8HsAAAABkGbQDvB7AAAAAZBm2A7wewAAAAGQZuAO8HsAAAABkGboDvB7AAAAAZBm8A7wewAAAAGQZvgO8HsAAAABkGaADvB7AAAAAZBmiA7wewAAAAGQZpAO8HsAAAABkGaYDvB7AAAAAZBmoA7wewAAAAGQZqgO8HsAAAABkGawDvB7AAAAAZBmuA7wewAAAAGQZsAO8HsAAAABkGbIDvB7AAAAAZBm0A7wewAAAAGQZtgO8HsAAAABkGbgDvB7AAAAAZBm6A7wewAAAAGQZvAO8HsAAAABkGb4DvB7AAAAAZBmgA7wewAAAAGQZogO8HsAAAABkGaQDvB7AAAAAZBmmA7wewAAAAGQZqAO8HsAAAABkGaoDvB7AAAAAZBmsA7wewAAAAGQZrgO8HsAAAABkGbADvB7AAAAAZBmyA7wewAAAAGQZtAO8HsAAAABkGbYDvB7AAAAAZBm4A7wewAAAAGQZugO8HsAAAABkGbwDvB7AAAAAZBm+A7wewAAAAGQZoAO8HsAAAABkGaIDvB7AAAAAZBmkA7wewAAAAGQZpgO8HsAAAABkGagDvB7AAAAAZBmqA7wewAAAAGQZrAO8HsAAAABkGa4DvB7AAAAAZBmwA7wewAAAAGQZsgO8HsAAAABkGbQDvB7AAAAAZBm2A7wewAAAAGQZuAO8HsAAAABkGboDvB7AAAAAZBm8A7wewAAAAGQZvgO8HsAAAABkGaADvB7AAAAAZBmiA7wewAAAAGQZpAO8HsAAAABkGaYDvB7AAAAAZBmoA7wewAAAAGQZqgO8HsAAAABkGawDvB7AAAAAZBmuA7wewAAAAGQZsAO8HsAAAABkGbIDvB7AAAAAZBm0A7wewAAAAGQZtgO8HsAAAABkGbgDvB7AAAAAZBm6A7wewAAAAGQZvAO8HsAAAABkGb4DvB7AAAAAZBmgA7wewAAAAGQZogO8HsAAAABkGaQDvB7AAAAAZBmmA7wewAAAAGQZqAO8HsAAAABkGaoDvB7AAAAAZBmsA7wewAAAAGQZrgO8HsAAAABkGbADvB7AAAAAZBmyA7wewAAAAGQZtAO8HsAAAABkGbYDvB7AAAAAZBm4A7wewAAAAGQZugO8HsAAAABkGbwDvB7AAAAAZBm+A7wewAAAAGQZoAO8HsAAAABkGaIDvB7AAAAAZBmkA7wewAAAAGQZpgO8HsAAAABkGagDvB7AAAAAZBmqA7wewAAAAGQZrAO8HsAAAABkGa4DvB7AAAAAZBmwA7wewAAAAGQZsgO8HsAAAABkGbQDvB7AAAAAZBm2A7wewAAAAGQZuAO8HsAAAABkGboDvB7AAAAAZBm8A7wewAAAAGQZvgO8HsAAAABkGaADvB7AAAAAZBmiA7wewAAAAGQZpAO8HsAAAABkGaYDvB7AAAAAZBmoA7wewAAAAGQZqgO8HsAAAABkGawDvB7AAAAAZBmuA7wewAAAAGQZsAO8HsAAAABkGbIDvB7AAAAAZBm0A7wewAAAAGQZtgO8HsAAAABkGbgDvB7AAAAAZBm6A7wewAAAAGQZvAO8HsAAAABkGb4DvB7AAAAAZBmgA7wewAAAAGQZogO8HsAAAABkGaQDvB7AAAAAZBmmA7wewAAAAGQZqAO8HsAAAABkGaoDvB7AAAAAZBmsA7wewAAAAGQZrgO8HsAAAABkGbADvB7AAAAAZBmyA7wewAAAAGQZtAO8HsAAAABkGbYDvB7AAAAAZBm4A7wewAAAAGQZugO8HsAAAABkGbwDvB7AAAAAZBm+A7wewAAAAGQZoAO8HsAAAABkGaIDvB7AAAAAZBmkA7wewAAAAGQZpgO8HsAAAABkGagDvB7AAAAAZBmqA7wewAAAAGQZrAO8HsAAAABkGa4DvB7AAAAAZBmwA7wewAAAAGQZsgO8HsAAAABkGbQDvB7AAAAAZBm2A7wewAAAAGQZuAO8HsAAAABkGboDvB7AAAAAZBm8A7wewAAAAGQZvgO8HsAAAABkGaADvB7AAAAAZBmiA7wewAAAAGQZpAO8HsAAAABkGaYDvB7AAAAAZBmoA7wewAAAAGQZqgO8HsAAAABkGawDvB7AAAAAZBmuA7wewAAAAGQZsAO8HsAAAABkGbIDvB7AAAAAZBm0A7wewAAAAGQZtgO8HsAAAABkGbgDvB7AAAAAZBm6A7wewAAAAGQZvAO8HsAAAABkGb4DvB7AAAAAZBmgA7wewAAAAGQZogO8HsAAAABkGaQDvB7AAAAAZBmmA7wewAAAAGQZqAO8HsAAAABkGaoDvB7AAAAAZBmsA7wewAAAAGQZrgO8HsAAAABkGbADvB7AAAAAZBmyA7wewAAAAGQZtAO8HsAAAABkGbYDvB7AAAAAZBm4A7wewAAAAGQZugO8HsAAAABkGbwDvB7AAAAAZBm+A7wewAAAAGQZoAO8HsAAAABkGaIDvB7AAAAAZBmkA7wewAAAAGQZpgO8HsAAAABkGagDvB7AAAAAZBmqA7wewAAAAGQZrAO8HsAAAABkGa4DvB7AAAAAZBmwA7wewAAAAGQZsgO8HsAAAAOWWIggEfJigADYjJycnJycnJycnXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXgAAAAZBmjgd4PYAAAAHQZpUB3g9gAAAAAZBmmA7wewAAAAGQZqAO8HsAAAABkGaoDvB7AAAAAZBmsA7wewAAAAGQZrgO8HsAAAABkGbADvB7AAAAAZBmyA7wewAAAAGQZtAO8HsAAAABkGbYDvB7AAAAAZBm4A7wewAAAAGQZugO8HsAAAABkGbwDvB7AAAAAZBm+A7wewAAAAGQZoAO8HsAAAABkGaIDvB7AAAAAZBmkA7wewAAAAGQZpgO8HsAAAABkGagDvB7AAAAAZBmqA7wewAAAAGQZrAO8HsAAAABkGa4DvB7AAAAAZBmwA7wewAAAAGQZsgO8HsAAAABkGbQDvB7AAAAAZBm2A7wewAAAAGQZuAO8HsAAAABkGboDvB7AAAAAZBm8A7wewAAAAGQZvgO8HsAAAABkGaADvB7AAAAAZBmiA7wewAAAAGQZpAO8HsAAAABkGaYDvB7AAAAAZBmoA7wewAAAAGQZqgO8HsAAAABkGawDvB7AAAAAZBmuA7wewAAAAGQZsAO8HsAAAABkGbIDvB7AAAAAZBm0A7wewAAAAGQZtgO8HsAAAABkGbgDvB7AAAAAZBm6A7wewAAAAGQZvAO8HsAAAABkGb4DvB7AAAAAZBmgA7wewAAAAGQZogO8HsAAAABkGaQDvB7AAAAAZBmmA7wewAAAAGQZqAO8HsAAAABkGaoDvB7AAAAAZBmsA7wewAAAAGQZrgO8HsAAAABkGbADvB7AAAAAZBmyA7wewAAAAGQZtAO8HsAAAABkGbYDvB7AAAAAZBm4A7wewAAAAGQZugO8HsAAAABkGbwDvB7AAAAAZBm+A7wewAAAAGQZoAO8HsAAAABkGaIDvB7AAAAAZBmkA7wewAAAAGQZpgO8HsAAAABkGagDvB7AAAAAZBmqA7wewAAAAGQZrAO8HsAAAABkGa4DvB7AAAAAZBmwA7wewAAAAGQZsgO8HsAAAABkGbQDvB7AAAAAZBm2A7wewAAAAGQZuAO8HsAAAABkGboDvB7AAAAAZBm8A7wewAAAAGQZvgO8HsAAAABkGaADvB7AAAAAZBmiA7wewAAAAGQZpAO8HsAAAABkGaYDvB7AAAAAZBmoA7wewAAAAGQZqgO8HsAAAABkGawDvB7AAAAAZBmuA7wewAAAAGQZsAO8HsAAAABkGbIDvB7AAAAAZBm0A7wewAAAAGQZtgO8HsAAAABkGbgDvB7AAAAAZBm6A7wewAAAAGQZvAO8HsAAAABkGb4DvB7AAAAAZBmgA7wewAAAAGQZogO8HsAAAABkGaQDvB7AAAAAZBmmA7wewAAAAGQZqAO8HsAAAABkGaoDvB7AAAAAZBmsA7wewAAAAGQZrgO8HsAAAABkGbADvB7AAAAAZBmyA7wewAAAAGQZtAO8HsAAAABkGbYDvB7AAAAAZBm4A3wewAAAAGQZugM8Hs"
|
|
)
|
|
|
|
def _build_wav() -> bytes:
|
|
sample_rate = 8_000
|
|
frame_count = sample_rate * 2
|
|
pcm = bytearray(frame_count * 2)
|
|
for index in range(frame_count):
|
|
sample = int(8_000 * math.sin(2 * math.pi * 440 * index / sample_rate))
|
|
struct.pack_into("<h", pcm, index * 2, sample)
|
|
output = io.BytesIO()
|
|
with wave.open(output, "wb") as wav:
|
|
wav.setnchannels(1)
|
|
wav.setsampwidth(2)
|
|
wav.setframerate(sample_rate)
|
|
wav.writeframes(pcm)
|
|
|
|
payload = bytearray(output.getvalue())
|
|
info_chunks = []
|
|
for code, value in {
|
|
"INAM": "Resilient Track",
|
|
"IART": "Maestro Artist",
|
|
"IPRD": "Regression Album",
|
|
"ITRK": "1",
|
|
"ICRD": "2026",
|
|
}.items():
|
|
encoded = value.encode("utf-8") + b"\0"
|
|
padded = encoded if len(encoded) % 2 == 0 else encoded + b"\0"
|
|
info_chunks.append(code.encode("ascii") + struct.pack("<I", len(encoded)) + padded)
|
|
info = b"INFO" + b"".join(info_chunks)
|
|
payload.extend(b"LIST" + struct.pack("<I", len(info)) + info)
|
|
struct.pack_into("<I", payload, 4, len(payload) - 8)
|
|
return bytes(payload)
|
|
|
|
|
|
_AUDIO = _build_wav()
|
|
|