[XMA] Implement loop subframe precision

This commit is contained in:
Herman S.
2026-02-21 00:36:25 +09:00
parent 8586cc196d
commit 013320c051
2 changed files with 82 additions and 2 deletions

View File

@@ -229,6 +229,8 @@ void XmaContextNew::ClearLocked(XMA_CONTEXT_DATA* data) {
data->output_buffer_write_offset = 0;
current_frame_remaining_subframes_ = 0;
loop_frame_output_limit_ = 0;
loop_start_skip_pending_ = false;
}
void XmaContextNew::Disable() { set_is_enabled(false); }
@@ -266,10 +268,46 @@ void XmaContextNew::Consume(RingBuffer* XE_RESTRICT output_rb,
return;
}
const int8_t subframes_to_write =
// Check if the loop end truncation limit has been reached.
// Total subframes for this frame minus remaining gives how many have already
// been consumed. If that reaches the limit, discard the rest.
if (loop_frame_output_limit_ > 0) {
const uint8_t total_subframes =
(kBytesPerFrameChannel / kOutputBytesPerBlock) << data->is_stereo;
const uint8_t consumed =
total_subframes - current_frame_remaining_subframes_;
if (consumed >= loop_frame_output_limit_) {
// Charge headroom as if the frame completed normally so the Work()
// loop doesn't overestimate available output space.
XELOGAPU(
"XmaContext {}: Loop end truncation: discarding {} remaining "
"subframes (limit {})",
id(), current_frame_remaining_subframes_, loop_frame_output_limit_);
remaining_subframe_blocks_in_output_buffer_ -=
data->output_buffer_padding;
current_frame_remaining_subframes_ = 0;
loop_frame_output_limit_ = 0;
return;
}
}
int8_t subframes_to_write =
std::min((int8_t)current_frame_remaining_subframes_,
(int8_t)data->subframe_decode_count);
// Clamp to loop end limit if active.
if (loop_frame_output_limit_ > 0) {
const uint8_t total_subframes =
(kBytesPerFrameChannel / kOutputBytesPerBlock) << data->is_stereo;
const uint8_t consumed =
total_subframes - current_frame_remaining_subframes_;
const int8_t remaining_until_limit =
(int8_t)(loop_frame_output_limit_ - consumed);
if (subframes_to_write > remaining_until_limit) {
subframes_to_write = remaining_until_limit;
}
}
const int8_t raw_frame_read_offset =
((kBytesPerFrameChannel / kOutputBytesPerBlock) << data->is_stereo) -
current_frame_remaining_subframes_;
@@ -323,6 +361,14 @@ void XmaContextNew::Decode(XMA_CONTEXT_DATA* data) {
input_buffer_.fill(0);
// Detect if we're about to decode the loop end frame (before
// UpdateLoopStatus may reset the offset).
bool is_loop_end_frame = false;
if (data->loop_count > 0) {
const uint32_t loop_end = std::max(kBitsPerPacketHeader, data->loop_end);
is_loop_end_frame = (data->input_buffer_read_offset == loop_end);
}
UpdateLoopStatus(data);
if (!data->output_buffer_block_count) {
@@ -500,8 +546,33 @@ void XmaContextNew::Decode(XMA_CONTEXT_DATA* data) {
// dump_raw(av_frame_, id());
ConvertFrame(reinterpret_cast<const uint8_t**>(&av_frame_->data),
bool(data->is_stereo), raw_frame_.data());
// TODO: Be aware of subframe_skips & loops subframes skips
current_frame_remaining_subframes_ = 4 << data->is_stereo;
// Loop end: limit output to subframes 0..loop_subframe_end.
if (is_loop_end_frame) {
loop_frame_output_limit_ = (data->loop_subframe_end + 1)
<< data->is_stereo;
XELOGAPU(
"XmaContext {}: Loop end frame - limiting output to {} subframes "
"(loop_subframe_end={})",
id(), loop_frame_output_limit_, data->loop_subframe_end);
} else {
loop_frame_output_limit_ = 0;
}
// Loop start: skip leading subframes per loop_subframe_skip.
// Reducing remaining shifts the read offset forward in Consume().
if (loop_start_skip_pending_) {
const uint8_t skip = data->loop_subframe_skip << data->is_stereo;
if (skip < current_frame_remaining_subframes_) {
XELOGAPU(
"XmaContext {}: Loop start - skipping {} leading subframes "
"(loop_subframe_skip={})",
id(), skip, data->loop_subframe_skip);
current_frame_remaining_subframes_ -= skip;
}
loop_start_skip_pending_ = false;
}
}
// Compute where to go next.
@@ -576,6 +647,7 @@ void XmaContextNew::UpdateLoopStatus(XMA_CONTEXT_DATA* data) {
}
data->input_buffer_read_offset = loop_start;
loop_start_skip_pending_ = true;
if (data->loop_count != 255) {
data->loop_count--;

View File

@@ -112,6 +112,14 @@ class XmaContextNew : public XmaContext {
int32_t remaining_subframe_blocks_in_output_buffer_ = 0;
uint8_t current_frame_remaining_subframes_ = 0;
// Loop subframe precision state.
// Maximum subframe blocks to output from the current frame (loop end
// truncation). 0 means no limit.
uint8_t loop_frame_output_limit_ = 0;
// When true, the next decoded frame should skip leading subframes per
// loop_subframe_skip (loop start adjustment).
bool loop_start_skip_pending_ = false;
};
} // namespace apu