mirror of
https://github.com/wheremyfoodat/Panda3DS.git
synced 2025-04-08 07:05:40 +12:00
DSP HLE: Broken PCM16 and handle DSP voice status better
This commit is contained in:
parent
8e303d8d08
commit
2fc9c0a573
3 changed files with 138 additions and 25 deletions
|
@ -297,7 +297,7 @@ namespace Audio::HLE {
|
||||||
u8 isEnabled; ///< Is this channel enabled? (Doesn't have to be playing anything.)
|
u8 isEnabled; ///< Is this channel enabled? (Doesn't have to be playing anything.)
|
||||||
u8 currentBufferIDDirty; ///< Non-zero when current_buffer_id changes
|
u8 currentBufferIDDirty; ///< Non-zero when current_buffer_id changes
|
||||||
u16_le syncCount; ///< Is set by the DSP to the value of SourceConfiguration::sync_count
|
u16_le syncCount; ///< Is set by the DSP to the value of SourceConfiguration::sync_count
|
||||||
u32_dsp bufferPosition; ///< Number of samples into the current buffer
|
u32_dsp samplePosition; ///< Number of samples into the current buffer
|
||||||
u16_le currentBufferID; ///< Updated when a buffer finishes playing
|
u16_le currentBufferID; ///< Updated when a buffer finishes playing
|
||||||
u16_le lastBufferID; ///< Updated when all buffers in the queue finish playing
|
u16_le lastBufferID; ///< Updated when all buffers in the queue finish playing
|
||||||
};
|
};
|
||||||
|
|
|
@ -47,9 +47,17 @@ namespace Audio {
|
||||||
using BufferQueue = std::priority_queue<Buffer>;
|
using BufferQueue = std::priority_queue<Buffer>;
|
||||||
BufferQueue buffers;
|
BufferQueue buffers;
|
||||||
|
|
||||||
|
SampleFormat sampleFormat = SampleFormat::ADPCM;
|
||||||
|
SourceType sourceType = SourceType::Stereo;
|
||||||
|
|
||||||
std::array<float, 3> gain0, gain1, gain2;
|
std::array<float, 3> gain0, gain1, gain2;
|
||||||
|
u32 samplePosition; // Sample number into the current audio buffer
|
||||||
u16 syncCount;
|
u16 syncCount;
|
||||||
|
u16 currentBufferID;
|
||||||
|
u16 previousBufferID;
|
||||||
|
|
||||||
bool enabled; // Is the source enabled?
|
bool enabled; // Is the source enabled?
|
||||||
|
bool isBufferIDDirty = false; // Did we change buffers?
|
||||||
|
|
||||||
// ADPCM decoding info:
|
// ADPCM decoding info:
|
||||||
// An array of fixed point S5.11 coefficients. These provide "weights" for the history samples
|
// An array of fixed point S5.11 coefficients. These provide "weights" for the history samples
|
||||||
|
@ -65,6 +73,10 @@ namespace Audio {
|
||||||
int index = 0; // Index of the voice in [0, 23] for debugging
|
int index = 0; // Index of the voice in [0, 23] for debugging
|
||||||
|
|
||||||
void reset();
|
void reset();
|
||||||
|
|
||||||
|
// Push a buffer to the buffer queue
|
||||||
|
void pushBuffer(const Buffer& buffer) { buffers.push(buffer); }
|
||||||
|
|
||||||
// Pop a buffer from the buffer queue and return it
|
// Pop a buffer from the buffer queue and return it
|
||||||
Buffer popBuffer() {
|
Buffer popBuffer() {
|
||||||
assert(!buffers.empty());
|
assert(!buffers.empty());
|
||||||
|
@ -114,9 +126,6 @@ namespace Audio {
|
||||||
std::array<Source, Audio::HLE::sourceCount> sources; // DSP voices
|
std::array<Source, Audio::HLE::sourceCount> sources; // DSP voices
|
||||||
Audio::HLE::DspMemory dspRam;
|
Audio::HLE::DspMemory dspRam;
|
||||||
|
|
||||||
SampleFormat sampleFormat = SampleFormat::ADPCM;
|
|
||||||
SourceType sourceType = SourceType::Stereo;
|
|
||||||
|
|
||||||
void resetAudioPipe();
|
void resetAudioPipe();
|
||||||
bool loaded = false; // Have we loaded a component?
|
bool loaded = false; // Have we loaded a component?
|
||||||
|
|
||||||
|
@ -159,9 +168,13 @@ namespace Audio {
|
||||||
|
|
||||||
void updateSourceConfig(Source& source, HLE::SourceConfiguration::Configuration& config, s16_le* adpcmCoefficients);
|
void updateSourceConfig(Source& source, HLE::SourceConfiguration::Configuration& config, s16_le* adpcmCoefficients);
|
||||||
void generateFrame(StereoFrame<s16>& frame);
|
void generateFrame(StereoFrame<s16>& frame);
|
||||||
|
void generateFrame(DSPSource& source);
|
||||||
void outputFrame();
|
void outputFrame();
|
||||||
|
|
||||||
// Decode an entire buffer worth of audio
|
// Decode an entire buffer worth of audio
|
||||||
void decodeBuffer(DSPSource& source);
|
void decodeBuffer(DSPSource& source);
|
||||||
|
|
||||||
|
SampleBuffer decodePCM16(const u8* data, usize sampleCount, Source& source);
|
||||||
SampleBuffer decodeADPCM(const u8* data, usize sampleCount, Source& source);
|
SampleBuffer decodeADPCM(const u8* data, usize sampleCount, Source& source);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
|
@ -7,6 +7,8 @@
|
||||||
|
|
||||||
#include "services/dsp.hpp"
|
#include "services/dsp.hpp"
|
||||||
|
|
||||||
|
std::vector<Audio::HLE_DSP::Sample<s16, 2>> samplezorz = {};
|
||||||
|
|
||||||
namespace Audio {
|
namespace Audio {
|
||||||
namespace DSPPipeType {
|
namespace DSPPipeType {
|
||||||
enum : u32 {
|
enum : u32 {
|
||||||
|
@ -64,10 +66,6 @@ namespace Audio {
|
||||||
dspState = DSPState::Off;
|
dspState = DSPState::Off;
|
||||||
loaded = false;
|
loaded = false;
|
||||||
|
|
||||||
// Initialize these to some sane defaults
|
|
||||||
sampleFormat = SampleFormat::ADPCM;
|
|
||||||
sourceType = SourceType::Stereo;
|
|
||||||
|
|
||||||
for (auto& e : pipeData) {
|
for (auto& e : pipeData) {
|
||||||
e.clear();
|
e.clear();
|
||||||
}
|
}
|
||||||
|
@ -212,12 +210,16 @@ namespace Audio {
|
||||||
updateSourceConfig(source, config, read.adpcmCoefficients.coeff[i]);
|
updateSourceConfig(source, config, read.adpcmCoefficients.coeff[i]);
|
||||||
|
|
||||||
// Generate audio
|
// Generate audio
|
||||||
if (source.enabled && !source.buffers.empty()) {
|
if (source.enabled) {
|
||||||
const auto& buffer = source.buffers.top();
|
generateFrame(source);
|
||||||
const u8* data = getPointerPhys<u8>(buffer.paddr);
|
|
||||||
|
|
||||||
if (data != nullptr) {
|
if (samplezorz.size() > 160 * 60 * 60 * 3) {
|
||||||
// TODO
|
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];
|
auto& status = write.sourceStatuses.status[i];
|
||||||
status.isEnabled = source.enabled;
|
status.isEnabled = source.enabled;
|
||||||
status.syncCount = source.syncCount;
|
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?
|
// TODO: Should we check bufferQueueDirty here too?
|
||||||
if (config.formatDirty || config.embeddedBufferDirty) {
|
if (config.formatDirty || config.embeddedBufferDirty) {
|
||||||
sampleFormat = config.format;
|
source.sampleFormat = config.format;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (config.monoOrStereoDirty || config.embeddedBufferDirty) {
|
if (config.monoOrStereoDirty || config.embeddedBufferDirty) {
|
||||||
sourceType = config.monoOrStereo;
|
source.sourceType = config.monoOrStereo;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (config.embeddedBufferDirty) {
|
if (config.embeddedBufferDirty) {
|
||||||
|
@ -285,8 +294,8 @@ namespace Audio {
|
||||||
.looping = config.isLooping != 0,
|
.looping = config.isLooping != 0,
|
||||||
.bufferID = config.bufferID,
|
.bufferID = config.bufferID,
|
||||||
.playPosition = config.playPosition,
|
.playPosition = config.playPosition,
|
||||||
.format = sampleFormat,
|
.format = source.sampleFormat,
|
||||||
.sourceType = sourceType,
|
.sourceType = source.sourceType,
|
||||||
.fromQueue = false,
|
.fromQueue = false,
|
||||||
.hasPlayedOnce = false,
|
.hasPlayedOnce = false,
|
||||||
};
|
};
|
||||||
|
@ -327,13 +336,95 @@ namespace Audio {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (buffer.format) {
|
source.currentBufferID = buffer.bufferID;
|
||||||
case SampleFormat::PCM8:
|
source.previousBufferID = 0;
|
||||||
case SampleFormat::PCM16: Helpers::warn("Unimplemented sample format!"); break;
|
// 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;
|
if (buffer.hasPlayedOnce) {
|
||||||
default: Helpers::warn("Invalid DSP sample format"); break;
|
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) {
|
HLE_DSP::SampleBuffer HLE_DSP::decodeADPCM(const u8* data, usize sampleCount, Source& source) {
|
||||||
|
@ -413,6 +504,15 @@ namespace Audio {
|
||||||
|
|
||||||
void DSPSource::reset() {
|
void DSPSource::reset() {
|
||||||
enabled = false;
|
enabled = false;
|
||||||
|
isBufferIDDirty = false;
|
||||||
|
|
||||||
|
// Initialize these to some sane defaults
|
||||||
|
sampleFormat = SampleFormat::ADPCM;
|
||||||
|
sourceType = SourceType::Stereo;
|
||||||
|
|
||||||
|
samplePosition = 0;
|
||||||
|
previousBufferID = 0;
|
||||||
|
currentBufferID = 0;
|
||||||
syncCount = 0;
|
syncCount = 0;
|
||||||
|
|
||||||
buffers = {};
|
buffers = {};
|
||||||
|
|
Loading…
Add table
Reference in a new issue