From 9465073add017c3419cf1d7a90e981aa2edcce25 Mon Sep 17 00:00:00 2001 From: Rix Date: Thu, 29 Oct 2026 17:14:01 -0700 Subject: [PATCH 1/3] SPUDUMP: 64-byte sample data, the loop point's loop, reference player limits. Sample data payloads must be a multiple of 64 bytes: the SPU DMA moves whole 64-byte blocks, so a player reads past a shorter payload. The loop point text now says when a pattern is set to loop: the order table loops from its last entry back to that same entry. A note lists the fixed table sizes of nugget's spdplayer. --- SPUDUMP.md | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/SPUDUMP.md b/SPUDUMP.md index 1499d3f..5343893 100644 --- a/SPUDUMP.md +++ b/SPUDUMP.md @@ -87,7 +87,7 @@ Each packet consists of a header and a payload. The header is 4 bytes long: - **End of pattern (0x02)**: The payload size is 0. Signals that the current pattern is complete. The player advances to the next entry in the current order table and begins processing the corresponding pattern. If the current order is the last entry, behavior depends on the order table's loop setting. -- **Loop point (0x03)**: The payload size is 0. Marks the current stream position as the loop restart point within the current pattern. When the player reaches the end of pattern (0x02) and the pattern is set to loop, it returns to this position. Only one loop point per pattern is valid; a second loop point overrides the first. +- **Loop point (0x03)**: The payload size is 0. Marks the current stream position as the loop restart point within the current pattern. When the player reaches the end of pattern (0x02) and the pattern is set to loop, that is the order table loops from its last entry back to that same entry, it returns to this position instead of the start of the pattern. Only one loop point per pattern is valid; a second loop point overrides the first. - **Trace begin (0x04)**: The payload size is 0. Indicates that all preceding packets were synthetically generated to restore SPU state, and that subsequent packets represent actual captured data. This is analogous to the GPU dump format's trace begin packet. For composition-driven files, this packet is not required. @@ -134,7 +134,7 @@ Each packet consists of a header and a payload. The header is 4 bytes long: The payload consists of: - 1 word: SPU RAM base address in 8-byte units. - - Remaining words: raw ADPCM data, to be DMA'd contiguously starting at the base address. + - Remaining words: raw ADPCM data, to be DMA'd contiguously starting at the base address. Its length must be a multiple of 64 bytes, padded with zeroes if needed: the SPU DMA moves whole 64-byte blocks, so a player may read up to 60 bytes past a shorter payload. ### Timing Packets @@ -179,6 +179,15 @@ A minimal player performs the following steps: Seeking to an arbitrary order position is accomplished by setting the current order index and jumping to the corresponding pattern's stream offset. Because each pattern begins with a full SPU state snapshot, no history is required. +### Player limits + +The reference player, nugget's `spdplayer`, has fixed-size tables. A file meant for it should stay within them; the player ignores what does not fit: + +- 256 entries per order table, and 16 order tables. +- 256 pattern headers. +- 128 samples in the sample directory. +- 256 macros (indices 0-255), of up to 8 register writes each. + ## Usage Notes - For composition-driven files, the SPU state snapshot at the head of each pattern should write all registers for every voice used by the song, including volume, pitch, ADSR, and sample address registers. Voices not used by the song should not be touched, leaving them available for sound effects. From ec324c048609897041f89d0dfc341be82dbb8694 Mon Sep 17 00:00:00 2001 From: Rix Date: Thu, 5 Nov 2026 17:03:00 -0800 Subject: [PATCH 2/3] SPUDUMP: state the loop point condition on the order table; drop the order table count limit --- SPUDUMP.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/SPUDUMP.md b/SPUDUMP.md index 5343893..e5603e2 100644 --- a/SPUDUMP.md +++ b/SPUDUMP.md @@ -87,7 +87,7 @@ Each packet consists of a header and a payload. The header is 4 bytes long: - **End of pattern (0x02)**: The payload size is 0. Signals that the current pattern is complete. The player advances to the next entry in the current order table and begins processing the corresponding pattern. If the current order is the last entry, behavior depends on the order table's loop setting. -- **Loop point (0x03)**: The payload size is 0. Marks the current stream position as the loop restart point within the current pattern. When the player reaches the end of pattern (0x02) and the pattern is set to loop, that is the order table loops from its last entry back to that same entry, it returns to this position instead of the start of the pattern. Only one loop point per pattern is valid; a second loop point overrides the first. +- **Loop point (0x03)**: The payload size is 0. Marks the current stream position as the loop restart point within the current pattern. When the player reaches the end of pattern (0x02) for the last entry in the current order table, and the order table loops from that entry back to itself, it returns to this position instead of the start of the pattern. Only one loop point per pattern is valid; a second loop point overrides the first. - **Trace begin (0x04)**: The payload size is 0. Indicates that all preceding packets were synthetically generated to restore SPU state, and that subsequent packets represent actual captured data. This is analogous to the GPU dump format's trace begin packet. For composition-driven files, this packet is not required. @@ -183,7 +183,7 @@ Seeking to an arbitrary order position is accomplished by setting the current or The reference player, nugget's `spdplayer`, has fixed-size tables. A file meant for it should stay within them; the player ignores what does not fit: -- 256 entries per order table, and 16 order tables. +- 256 entries per order table. Any number of order tables; the player loads one, the current subsong's, at a time. - 256 pattern headers. - 128 samples in the sample directory. - 256 macros (indices 0-255), of up to 8 register writes each. From 1f0fac5a5ae48b25f53cef018b20203d88dfaa8a Mon Sep 17 00:00:00 2001 From: Rix Date: Thu, 5 Nov 2026 17:08:44 -0800 Subject: [PATCH 3/3] SPUDUMP: sample padding is a writer rule, readers still take unpadded v1r1 payloads --- SPUDUMP.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/SPUDUMP.md b/SPUDUMP.md index e5603e2..76e15a0 100644 --- a/SPUDUMP.md +++ b/SPUDUMP.md @@ -134,7 +134,7 @@ Each packet consists of a header and a payload. The header is 4 bytes long: The payload consists of: - 1 word: SPU RAM base address in 8-byte units. - - Remaining words: raw ADPCM data, to be DMA'd contiguously starting at the base address. Its length must be a multiple of 64 bytes, padded with zeroes if needed: the SPU DMA moves whole 64-byte blocks, so a player may read up to 60 bytes past a shorter payload. + - Remaining words: raw ADPCM data, to be DMA'd contiguously starting at the base address. A writer should pad it with zeroes to a multiple of 64 bytes and keep the whole padded range inside SPU RAM and clear of every other sample's range, since a player that moves whole 64-byte DMA blocks uploads the padding too. A reader must still accept a payload whose length is not a multiple of 64 bytes, as earlier writers produced, and must not upload past its end. ### Timing Packets