DSP HLE: Broken PCM16 and handle DSP voice status better

This commit is contained in:
wheremyfoodat 2024-04-28 16:58:42 +03:00
parent 8e303d8d08
commit 2fc9c0a573
3 changed files with 138 additions and 25 deletions

View file

@ -7,6 +7,8 @@
#include "services/dsp.hpp"
std::vector<Audio::HLE_DSP::Sample<s16, 2>> samplezorz = {};
namespace Audio {
namespace DSPPipeType {
enum : u32 {
@ -64,10 +66,6 @@ namespace Audio {
dspState = DSPState::Off;
loaded = false;
// Initialize these to some sane defaults
sampleFormat = SampleFormat::ADPCM;
sourceType = SourceType::Stereo;
for (auto& e : pipeData) {
e.clear();
}
@ -212,12 +210,16 @@ namespace Audio {
updateSourceConfig(source, config, read.adpcmCoefficients.coeff[i]);
// Generate audio
if (source.enabled && !source.buffers.empty()) {
const auto& buffer = source.buffers.top();
const u8* data = getPointerPhys<u8>(buffer.paddr);
if (source.enabled) {
generateFrame(source);
if (data != nullptr) {
// TODO
if (samplezorz.size() > 160 * 60 * 60 * 3) {
using namespace std;
ofstream fout("audio_data.bin", ios::out | ios::binary);
fout.write((char*)&samplezorz[0], samplezorz.size() * sizeof(Sample<s16, 2>));
fout.close();
Helpers::panic("Bwaa");
}
}
@ -225,6 +227,13 @@ namespace Audio {
auto& status = write.sourceStatuses.status[i];
status.isEnabled = source.enabled;
status.syncCount = source.syncCount;
status.currentBufferIDDirty = source.isBufferIDDirty ? 1 : 0;
status.currentBufferID = source.currentBufferID;
status.lastBufferID = source.previousBufferID;
// TODO: Properly update sample position
status.samplePosition = source.samplePosition;
source.isBufferIDDirty = false;
}
}
@ -265,11 +274,11 @@ namespace Audio {
// TODO: Should we check bufferQueueDirty here too?
if (config.formatDirty || config.embeddedBufferDirty) {
sampleFormat = config.format;
source.sampleFormat = config.format;
}
if (config.monoOrStereoDirty || config.embeddedBufferDirty) {
sourceType = config.monoOrStereo;
source.sourceType = config.monoOrStereo;
}
if (config.embeddedBufferDirty) {
@ -285,8 +294,8 @@ namespace Audio {
.looping = config.isLooping != 0,
.bufferID = config.bufferID,
.playPosition = config.playPosition,
.format = sampleFormat,
.sourceType = sourceType,
.format = source.sampleFormat,
.sourceType = source.sourceType,
.fromQueue = false,
.hasPlayedOnce = false,
};
@ -327,13 +336,95 @@ namespace Audio {
return;
}
switch (buffer.format) {
case SampleFormat::PCM8:
case SampleFormat::PCM16: Helpers::warn("Unimplemented sample format!"); break;
source.currentBufferID = buffer.bufferID;
source.previousBufferID = 0;
// For looping buffers, this is only set for the first time we play it. Loops do not set the dirty bit.
source.isBufferIDDirty = !buffer.hasPlayedOnce && buffer.fromQueue;
case SampleFormat::ADPCM: source.currentSamples = decodeADPCM(data, buffer.sampleCount, source); break;
default: Helpers::warn("Invalid DSP sample format"); break;
if (buffer.hasPlayedOnce) {
source.samplePosition = 0;
} else {
// Mark that the buffer has already been played once, needed for looping buffers
buffer.hasPlayedOnce = true;
// Play position is only used for the initial time the buffer is played. Loops will start from the beginning of the buffer.
source.samplePosition = buffer.playPosition;
}
switch (buffer.format) {
case SampleFormat::PCM8: Helpers::warn("Unimplemented sample format!"); break;
case SampleFormat::PCM16: source.currentSamples = decodePCM16(data, buffer.sampleCount, source); break;
case SampleFormat::ADPCM: source.currentSamples = decodeADPCM(data, buffer.sampleCount, source); break;
default:
Helpers::warn("Invalid DSP sample format");
source.currentSamples = {};
break;
}
// If the buffer is a looping buffer, re-push it
if (buffer.looping) {
source.pushBuffer(buffer);
}
}
void HLE_DSP::generateFrame(DSPSource& source) {
if (source.currentSamples.empty()) {
// There's no audio left to play, turn the voice off
if (source.buffers.empty()) {
source.enabled = false;
source.isBufferIDDirty = true;
source.previousBufferID = source.currentBufferID;
source.currentBufferID = 0;
return;
}
decodeBuffer(source);
} else {
constexpr uint maxSampleCount = Audio::samplesInFrame;
uint outputCount = 0;
while (outputCount < maxSampleCount) {
if (source.currentSamples.empty()) {
if (source.buffers.empty()) {
break;
} else {
decodeBuffer(source);
}
}
const uint sampleCount = std::min<s32>(maxSampleCount - outputCount, source.currentSamples.size());
samplezorz.insert(samplezorz.end(), source.currentSamples.begin(), source.currentSamples.begin() + sampleCount);
source.currentSamples.erase(source.currentSamples.begin(), source.currentSamples.begin() + sampleCount);
outputCount += sampleCount;
}
}
}
HLE_DSP::SampleBuffer HLE_DSP::decodePCM16(const u8* data, usize sampleCount, Source& source) {
SampleBuffer decodedSamples(sampleCount);
const s16* data16 = reinterpret_cast<const s16*>(data);
if (source.sourceType == SourceType::Stereo) {
for (usize i = 0; i < sampleCount; i++) {
s16 left = *data16++;
s16 right = *data16++;
if (left != 0 || right != 0) {
Helpers::panic("panda...");
}
decodedSamples[i] = {left, right};
}
} else {
// Mono
for (usize i = 0; i < sampleCount; i++) {
decodedSamples[i].fill(*data16++);
}
}
return decodedSamples;
}
HLE_DSP::SampleBuffer HLE_DSP::decodeADPCM(const u8* data, usize sampleCount, Source& source) {
@ -413,6 +504,15 @@ namespace Audio {
void DSPSource::reset() {
enabled = false;
isBufferIDDirty = false;
// Initialize these to some sane defaults
sampleFormat = SampleFormat::ADPCM;
sourceType = SourceType::Stereo;
samplePosition = 0;
previousBufferID = 0;
currentBufferID = 0;
syncCount = 0;
buffers = {};