Initial implementation of partial_embedded_buffer_dirty handling (#5548)
* Initial implementation of partial_embedded_buffer_dirty handling * Apply suggestions from code review Co-authored-by: Marshall Mohror <mohror64@gmail.com> * Serialize physical address, fix LOG_TRACE * Add bracket * Avoid crash in partial update behavior Co-authored-by: Marshall Mohror <mohror64@gmail.com>
This commit is contained in:
parent
02d6032afb
commit
1aaec7938f
|
@ -169,6 +169,71 @@ void Source::ParseConfig(SourceConfiguration::Configuration& config,
|
||||||
// This will be the starting sample for the first time the buffer is played.
|
// This will be the starting sample for the first time the buffer is played.
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO(xperia64): Is this in the correct spot in terms of the bit handling order?
|
||||||
|
if (config.partial_embedded_buffer_dirty) {
|
||||||
|
config.partial_embedded_buffer_dirty.Assign(0);
|
||||||
|
|
||||||
|
// As this bit is set by the game, three config options are also updated:
|
||||||
|
// buffer_id (after a check comparing the buffer_id to something, probably to make sure it's
|
||||||
|
// the same buffer?), flags2_raw.is_looping, and length.
|
||||||
|
|
||||||
|
// A quick and dirty way of extending the current buffer is to just read the whole thing
|
||||||
|
// again with the new length. Note that this uses the latched physical address instead of
|
||||||
|
// whatever is in config, because that may be invalid.
|
||||||
|
const u8* const memory =
|
||||||
|
memory_system->GetPhysicalPointer(state.current_buffer_physical_address & 0xFFFFFFFC);
|
||||||
|
|
||||||
|
// TODO(xperia64): This could potentially be optimized by only decoding the new data and
|
||||||
|
// appending that to the buffer.
|
||||||
|
if (memory) {
|
||||||
|
const unsigned num_channels = state.mono_or_stereo == MonoOrStereo::Stereo ? 2 : 1;
|
||||||
|
bool valid = false;
|
||||||
|
switch (state.format) {
|
||||||
|
case Format::PCM8:
|
||||||
|
// TODO(xperia64): This may just work fine like PCM16, but I haven't tested and
|
||||||
|
// couldn't find any test case games
|
||||||
|
UNIMPLEMENTED_MSG("{} not handled for partial buffer updates", "PCM8");
|
||||||
|
// state.current_buffer = Codec::DecodePCM8(num_channels, memory, config.length);
|
||||||
|
break;
|
||||||
|
case Format::PCM16:
|
||||||
|
state.current_buffer = Codec::DecodePCM16(num_channels, memory, config.length);
|
||||||
|
valid = true;
|
||||||
|
break;
|
||||||
|
case Format::ADPCM:
|
||||||
|
// TODO(xperia64): Are partial embedded buffer updates even valid for ADPCM? What
|
||||||
|
// about the adpcm state?
|
||||||
|
UNIMPLEMENTED_MSG("{} not handled for partial buffer updates", "ADPCM");
|
||||||
|
/* state.current_buffer =
|
||||||
|
Codec::DecodeADPCM(memory, config.length, state.adpcm_coeffs,
|
||||||
|
state.adpcm_state); */
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
UNIMPLEMENTED();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Again, because our interpolation consumes samples instead of using an index, let's
|
||||||
|
// just re-consume the samples up to the current sample number. There may be some
|
||||||
|
// imprecision here with the current sample number, as Detective Pikachu sounds a little
|
||||||
|
// rough at times.
|
||||||
|
if (valid) {
|
||||||
|
|
||||||
|
// TODO(xperia64): Tomodachi life apparently can decrease config.length when the
|
||||||
|
// user skips dialog. I don't know the correct behavior, but to avoid crashing, just
|
||||||
|
// reset the current sample number to 0 and don't try to truncate the buffer
|
||||||
|
if (state.current_buffer.size() < state.current_sample_number) {
|
||||||
|
state.current_sample_number = 0;
|
||||||
|
} else {
|
||||||
|
state.current_buffer.erase(
|
||||||
|
state.current_buffer.begin(),
|
||||||
|
std::next(state.current_buffer.begin(), state.current_sample_number));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
LOG_TRACE(Audio_DSP, "partially updating embedded buffer addr={:#010x} len={} id={}",
|
||||||
|
state.current_buffer_physical_address, config.length, config.buffer_id);
|
||||||
|
}
|
||||||
|
|
||||||
if (config.embedded_buffer_dirty) {
|
if (config.embedded_buffer_dirty) {
|
||||||
config.embedded_buffer_dirty.Assign(0);
|
config.embedded_buffer_dirty.Assign(0);
|
||||||
// HACK
|
// HACK
|
||||||
|
@ -343,6 +408,7 @@ bool Source::DequeueBuffer() {
|
||||||
// the first playthrough starts at play_position, loops start at the beginning of the buffer
|
// the first playthrough starts at play_position, loops start at the beginning of the buffer
|
||||||
state.current_sample_number = (!buf.has_played) ? buf.play_position : 0;
|
state.current_sample_number = (!buf.has_played) ? buf.play_position : 0;
|
||||||
state.next_sample_number = state.current_sample_number;
|
state.next_sample_number = state.current_sample_number;
|
||||||
|
state.current_buffer_physical_address = buf.physical_address;
|
||||||
state.current_buffer_id = buf.buffer_id;
|
state.current_buffer_id = buf.buffer_id;
|
||||||
state.buffer_update = buf.from_queue && !buf.has_played;
|
state.buffer_update = buf.from_queue && !buf.has_played;
|
||||||
|
|
||||||
|
|
|
@ -137,6 +137,7 @@ private:
|
||||||
|
|
||||||
u32 current_sample_number = 0;
|
u32 current_sample_number = 0;
|
||||||
u32 next_sample_number = 0;
|
u32 next_sample_number = 0;
|
||||||
|
PAddr current_buffer_physical_address = 0;
|
||||||
AudioInterp::StereoBuffer16 current_buffer = {};
|
AudioInterp::StereoBuffer16 current_buffer = {};
|
||||||
|
|
||||||
// buffer_id state
|
// buffer_id state
|
||||||
|
@ -170,6 +171,7 @@ private:
|
||||||
ar& format;
|
ar& format;
|
||||||
ar& current_sample_number;
|
ar& current_sample_number;
|
||||||
ar& next_sample_number;
|
ar& next_sample_number;
|
||||||
|
ar& current_buffer_physical_address;
|
||||||
ar& current_buffer;
|
ar& current_buffer;
|
||||||
ar& buffer_update;
|
ar& buffer_update;
|
||||||
ar& current_buffer_id;
|
ar& current_buffer_id;
|
||||||
|
|
Reference in New Issue