mirror of
https://github.com/RexiMemo-org/switchnote-tools.git
synced 2026-10-30 00:19:12 +00:00
corrected switchnote speed
This commit is contained in:
1 parent
8f31bdd977
commit
fa99395580
6 files changed
+45
-16
No files matched your search
Binary file not shown.
Binary file not shown.
@@ -40,6 +40,7 @@ class AudioTrack:
|
||||
name: str
|
||||
frame_triggers: list[int]
|
||||
pcm: array.array
|
||||
reference_speed: int = 0
|
||||
|
||||
|
||||
@dataclass
|
||||
@@ -208,7 +209,7 @@ def read_sppm(source: Input, *, legacy_hmac_key: Optional[bytes] = None) -> Sppm
|
||||
frame_count, audio_count, reserved = r.u32(), r.u8(), r.u16()
|
||||
if (version not in (1, 2, 3) or header_size != HEADER_SIZE or total_size != len(body) or
|
||||
width != WIDTH or height != HEIGHT or layer_count != 4 or palette_count != 256 or
|
||||
not 1 <= fps <= 30 or not 1 <= frame_count <= 9999 or audio_count > 4 or reserved != 0 or
|
||||
not 1 <= fps <= 30 or not 1 <= frame_count <= 9999 or audio_count > 4 or (reserved != 0 if version < 3 else reserved > 8) or
|
||||
(flags & ~(3 if version == 1 else 15))):
|
||||
raise SppmError("unsupported SPPM header")
|
||||
if zlib.crc32(body[HEADER_SIZE:]) & 0xFFFFFFFF != expected_crc:
|
||||
@@ -228,7 +229,10 @@ def read_sppm(source: Input, *, legacy_hmac_key: Optional[bytes] = None) -> Sppm
|
||||
"authorFsid": r.id(), "originalAuthorFsid": r.id(), "lastEditorFsid": r.id(),
|
||||
"currentId": r.id(), "rootId": r.id(), "parentId": r.id(True),
|
||||
"createdUnixSeconds": r.u64(), "modifiedUnixSeconds": r.u64(), "coverFrame": r.u32(),
|
||||
"width": width, "height": height, "fps": fps, "locked": bool(flags & 1),
|
||||
"width": width, "height": height,
|
||||
"speed": reserved if version >= 3 else 0, "nominalFps": fps,
|
||||
"fps": (0.4985, 1.0, 2.0, 4.0, 5.9972, 11.986, 19.9459, 29.9275)[reserved - 1] if version >= 3 and reserved else fps,
|
||||
"locked": bool(flags & 1),
|
||||
"allowSpinoffs": bool(flags & 2), "bgmFollowsSpeed": version == 1 or bool(flags & 4),
|
||||
"loopPlayback": True if version == 1 else not bool(flags & 8),
|
||||
"frameCount": frame_count, "audioTrackCount": audio_count, "formatVersion": version,
|
||||
@@ -280,8 +284,12 @@ def read_sppm(source: Input, *, legacy_hmac_key: Optional[bytes] = None) -> Sppm
|
||||
pcm.frombytes(raw_pcm)
|
||||
if struct.pack("=H", 1) != b"\x01\x00":
|
||||
pcm.byteswap()
|
||||
tracks.append(AudioTrack(index, channels, fps if ti == 0 and version == 1 else reference_fps,
|
||||
sample_rate, name, cues, pcm))
|
||||
reference_speed = reference_fps if version >= 3 and reserved != 0 and ti == 0 else 0
|
||||
if version >= 3 and reserved != 0 and (reference_speed > 8 or (ti > 0 and reference_fps != 0)):
|
||||
raise SppmError("invalid audio speed metadata")
|
||||
legacy_reference_fps = (fps if ti == 0 else reference_fps) if version < 3 or reserved == 0 else 0
|
||||
tracks.append(AudioTrack(index, channels, legacy_reference_fps,
|
||||
sample_rate, name, cues, pcm, reference_speed))
|
||||
if r.at != body_end:
|
||||
raise SppmError("unexpected trailing bytes")
|
||||
return SppmNote(meta, palette, frames, tracks, signature)
|
||||
@@ -358,7 +366,9 @@ def _mix_wav(note: SppmNote, wav_path: str, sample_rate: int = 48000, chunk_seco
|
||||
times = (base + np.arange(count, dtype=np.float64)) / sample_rate
|
||||
for track, source in sources:
|
||||
if track.index == 0:
|
||||
rate = track.sample_rate * (fps / (track.reference_fps or fps) if note.metadata["bgmFollowsSpeed"] else 1.0)
|
||||
reference_fps = ((0.4985, 1.0, 2.0, 4.0, 5.9972, 11.986, 19.9459, 29.9275)[track.reference_speed - 1]
|
||||
if track.reference_speed else (track.reference_fps or fps))
|
||||
rate = track.sample_rate * (fps / reference_fps if note.metadata["bgmFollowsSpeed"] else 1.0)
|
||||
positions = times * rate
|
||||
valid = np.ones(count, dtype=bool) if note.metadata["loopPlayback"] else positions < len(source)
|
||||
if note.metadata["loopPlayback"]:
|
||||
|
||||
@@ -9,7 +9,7 @@ from pathlib import Path
|
||||
from sppm_tools import get_sppm_metadata, get_sppm_thumbnail, read_sppm, sppm_to_mp4
|
||||
|
||||
|
||||
def fixture() -> bytes:
|
||||
def fixture(version: int = 2, speed: int = 0) -> bytes:
|
||||
payload = bytearray()
|
||||
palette = bytearray(256 * 4)
|
||||
palette[4:8] = bytes((255, 0, 0, 255))
|
||||
@@ -37,15 +37,15 @@ def fixture() -> bytes:
|
||||
payload.extend(encoded)
|
||||
|
||||
pcm = struct.pack("<800h", *([1200, -1200] * 400))
|
||||
payload.extend(struct.pack("<BBB I", 0, 1, 12, 8000))
|
||||
payload.extend(struct.pack("<BBB I", 0, 1, (6 if version >= 3 and speed else 12), 8000))
|
||||
text("BGM")
|
||||
payload.extend(struct.pack("<HII", 0, 800, zlib.crc32(pcm) & 0xffffffff))
|
||||
payload.extend(pcm)
|
||||
|
||||
header = bytearray(b"SPPM")
|
||||
header.extend(struct.pack("<HHQ I HH B H B B I B H", 2, 36, 36 + len(payload),
|
||||
header.extend(struct.pack("<HHQ I HH B H B B I B H", version, 36, 36 + len(payload),
|
||||
zlib.crc32(payload) & 0xffffffff, 640, 360, 4, 256,
|
||||
12, 2, 1, 1, 0))
|
||||
12, 2, 1, 1, speed))
|
||||
assert len(header) == 36
|
||||
return bytes(header + payload)
|
||||
|
||||
@@ -75,6 +75,12 @@ class SppmToolTests(unittest.TestCase):
|
||||
with self.assertRaisesRegex(ValueError, "CRC"):
|
||||
read_sppm(broken)
|
||||
|
||||
def test_reads_dsi_speed_indices_using_measured_rates(self):
|
||||
note = read_sppm(fixture(version=3, speed=1))
|
||||
self.assertEqual(note.metadata["speed"], 1)
|
||||
self.assertEqual(note.metadata["fps"], 0.4985)
|
||||
self.assertEqual(note.audio_tracks[0].reference_speed, 6)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
Reference in new issue
Block a user
