Skip to main content
Print

ATRAC Streaming State: Logical Stream Position, Ring Buffers, and Decode Contracts

Type: REFERENCE   Status: MAINTAINED   Scope: ATRAC3/ATRAC3+ stream state, logical versus physical positions, ring-buffer refill, decode outputs, loops, reset, and open priming questions.   Last reviewed: 2026-08-11

ATRAC integration fails when one cursor is asked to represent several coordinate systems. A correct model separates file byte offset, encoded-frame offset, logical decoded sample position, played sample after priming/skip, and physical position in a circular guest buffer.

Public API surface

The exact PSPSDK pspatrac3.h source exposes sceAtracSetData, sceAtracSetHalfwayBuffer, sceAtracSetSecondBuffer, sceAtracAddStreamData, sceAtracGetStreamDataInfo, sceAtracGetNextSample, sceAtracGetNextDecodePosition, sceAtracGetRemainFrame, sceAtracDecodeData, loop status, and reset-position functions. The declarations prove the observable seam; they do not settle every first-frame or loop edge case.

file offset -decode framing-> encoded frame offset<br>                                 |<br>guest ring: [physical read cursor ... wrap ...]<br>                                 | gather frame bytes safely<br>decoded sample position -skip/priming-> played sample position<br>                                 |<br>GetNextSample / DecodeData / remain-frame outputs
Source-owned coordinate map. Logical stream position is intentionally separate from the physical circular-buffer cursor.

Ring-buffer invariants

Track guest buffer address and size, valid byte range, logical encoded position, physical cursor, refill region, bytes added, second-buffer or loop-tail state, and EOF/last-frame status. A frame may straddle the physical end of the ring: gathering from dataByteOffset alone is insufficient. Copy or read the two physical segments while preserving one logical frame boundary.

sceAtracGetStreamDataInfo and sceAtracAddStreamData are producer/consumer operations. The bytes-available value, the amount added, and the next decode position should be kept as separate fields. Do not infer the next sample solely from the ring cursor.

Decode contract and partial frames

sceAtracDecodeData has multiple output pointers: samples, number of samples, end flag, and remaining-frame information. Validate every destination span before decoding and commit outputs atomically from the guest’s point of view. A normal frame size is not a promise that first, last, loop, or priming frames have the same count. The public API also exposes loop/reset and second-buffer state, which means an implementation must retain context beyond one call.

Nakagawa’s current HLE/bridge responsibilities are visible in hle.c and atrac3p_bridge.c. These links identify the implementation seam; private title streams, decoded PCM, paths, and captures are intentionally excluded.

Priming, seeking, and loops

Public emulator documentation and the PSPSDK API make it reasonable to distinguish encoded frame position from played sample position, especially after seeking or decoder warm-up. The exact first GetNextSample/DecodeData relationship, reset warm-up, all-data-loaded versus streamed behavior, and loop-tail accounting remain experiment targets. Keep synthetic tests separate from positive retail fixtures.

Safe test matrix

Use synthetic ATRAC-like frame metadata and source-owned ring buffers first: one frame wholly inside the ring, one straddling the boundary, exact-end refill, short final frame, second-buffer transition, reset, and loop. Assert logical offsets, physical reads, output counts, end flag, remaining-frame value, and error behavior independently. Add private hardware/title evidence only in the restricted acceptance route; publish the method and aggregate conclusion, not the artifacts.

Sources

Table of Contents