Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Handle audio SRC mixing more correctly #12147

Merged
merged 4 commits into from
Jul 5, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
63 changes: 59 additions & 4 deletions Core/HLE/__sceAudio.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ enum latency {
int eventAudioUpdate = -1;
int eventHostAudioUpdate = -1;
int mixFrequency = 44100;
int srcFrequency = 0;

const int hwSampleRate = 44100;

Expand Down Expand Up @@ -107,6 +108,7 @@ static void __AudioCPUMHzChange() {
void __AudioInit() {
memset(&g_AudioDebugStats, 0, sizeof(g_AudioDebugStats));
mixFrequency = 44100;
srcFrequency = 0;

switch (g_Config.iAudioLatency) {
case LOW_LATENCY:
Expand Down Expand Up @@ -149,7 +151,7 @@ void __AudioInit() {
}

void __AudioDoState(PointerWrap &p) {
auto s = p.Section("sceAudio", 1);
auto s = p.Section("sceAudio", 1, 2);
if (!s)
return;

Expand All @@ -159,6 +161,13 @@ void __AudioDoState(PointerWrap &p) {
CoreTiming::RestoreRegisterEvent(eventHostAudioUpdate, "AudioUpdateHost", &hleHostAudioUpdate);

p.Do(mixFrequency);
if (s >= 2) {
p.Do(srcFrequency);
} else {
// Assume that it was actually the SRC channel frequency.
srcFrequency = mixFrequency;
mixFrequency = 44100;
}

// TODO: This never happens because maxVer=1.
if (s >= 2) {
Expand All @@ -176,6 +185,7 @@ void __AudioDoState(PointerWrap &p) {
if (chanCount != ARRAY_SIZE(chans))
{
ERROR_LOG(SCEAUDIO, "Savestate failure: different number of audio channels.");
p.SetError(p.ERROR_FAILURE);
return;
}
for (int i = 0; i < chanCount; ++i)
Expand Down Expand Up @@ -329,6 +339,10 @@ void __AudioSetOutputFrequency(int freq) {
mixFrequency = freq;
}

void __AudioSetSRCFrequency(int freq) {
srcFrequency = freq;
}

// Mix samples from the various audio channels into a single sample queue.
// This single sample queue is where __AudioMix should read from. If the sample queue is full, we should
// just sleep the main emulator thread a little.
Expand All @@ -337,6 +351,7 @@ void __AudioUpdate(bool resetRecording) {
// to the CPU. Much better to throttle the frame rate on frame display and just throw away audio
// if the buffer somehow gets full.
bool firstChannel = true;
std::vector<int16_t> srcBuffer;

for (u32 i = 0; i < PSP_AUDIO_CHANNEL_MAX + 1; i++) {
if (!chans[i].reserved)
Expand All @@ -348,14 +363,54 @@ void __AudioUpdate(bool resetRecording) {
continue;
}

if (hwBlockSize * 2 > (int)chans[i].sampleQueue.size()) {
ERROR_LOG(SCEAUDIO, "Channel %i buffer underrun at %i of %i", i, (int)chans[i].sampleQueue.size() / 2, hwBlockSize);
bool needsResample = i == PSP_AUDIO_CHANNEL_SRC && srcFrequency != 0 && srcFrequency != mixFrequency;
size_t sz = needsResample ? (hwBlockSize * 2 * srcFrequency) / mixFrequency : hwBlockSize * 2;
if (sz > chans[i].sampleQueue.size()) {
ERROR_LOG(SCEAUDIO, "Channel %i buffer underrun at %i of %i", i, (int)chans[i].sampleQueue.size() / 2, (int)sz / 2);
}

const s16 *buf1 = 0, *buf2 = 0;
size_t sz1, sz2;

chans[i].sampleQueue.popPointers(hwBlockSize * 2, &buf1, &sz1, &buf2, &sz2);
chans[i].sampleQueue.popPointers(sz, &buf1, &sz1, &buf2, &sz2);

if (needsResample) {
auto read = [&](size_t i) {
if (i < sz1)
return buf1[i];
if (i < sz1 + sz2)
return buf2[i - sz1];
if (buf2)
return buf2[sz2 - 1];
return buf1[sz1 - 1];
};

srcBuffer.resize(hwBlockSize * 2);

// TODO: This is terrible, since it's doing it by small chunk and discarding frac.
const uint32_t ratio = (uint32_t)(65536.0 * srcFrequency / (double)mixFrequency);
uint32_t frac = 0;
size_t readIndex = 0;
for (size_t outIndex = 0; readIndex < sz && outIndex < srcBuffer.size(); outIndex += 2) {
size_t readIndex2 = readIndex + 2;
int16_t l1 = read(readIndex);
int16_t r1 = read(readIndex + 1);
int16_t l2 = read(readIndex2);
int16_t r2 = read(readIndex2 + 1);
int sampleL = ((l1 << 16) + (l2 - l1) * (uint16_t)frac) >> 16;
int sampleR = ((r1 << 16) + (r2 - r1) * (uint16_t)frac) >> 16;
srcBuffer[outIndex] = sampleL;
srcBuffer[outIndex + 1] = sampleR;
frac += ratio;
readIndex += 2 * (uint16_t)(frac >> 16);
frac &= 0xffff;
}

buf1 = srcBuffer.data();
sz1 = srcBuffer.size();
buf2 = nullptr;
sz2 = 0;
}

if (firstChannel) {
for (size_t s = 0; s < sz1; s++)
Expand Down
1 change: 1 addition & 0 deletions Core/HLE/__sceAudio.h
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ void __AudioDoState(PointerWrap &p);
void __AudioUpdate(bool resetRecording = false);
void __AudioShutdown();
void __AudioSetOutputFrequency(int freq);
void __AudioSetSRCFrequency(int freq);

// May return SCE_ERROR_AUDIO_CHANNEL_BUSY if buffer too large
u32 __AudioEnqueue(AudioChannel &chan, int chanNum, bool blocking);
Expand Down
49 changes: 30 additions & 19 deletions Core/HLE/sceAudio.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -248,10 +248,10 @@ static u32 sceAudioChRelease(u32 chan) {
ERROR_LOG(SCEAUDIO, "sceAudioChRelease(%i) - channel not reserved", chan);
return SCE_ERROR_AUDIO_CHANNEL_NOT_RESERVED;
}
DEBUG_LOG(SCEAUDIO, "sceAudioChRelease(%i)", chan);
// TODO: Does this error if busy?
chans[chan].reset();
chans[chan].reserved = false;
return 1;
return hleLogSuccessI(SCEAUDIO, 0);
}

static u32 sceAudioSetChannelDataLen(u32 chan, u32 len) {
Expand Down Expand Up @@ -329,6 +329,7 @@ static u32 sceAudioOutput2Reserve(u32 sampleCount) {
chan.sampleCount = sampleCount;
chan.format = PSP_AUDIO_FORMAT_STEREO;
chan.reserved = true;
__AudioSetSRCFrequency(0);
return hleLogSuccessI(SCEAUDIO, 0);
}

Expand All @@ -339,34 +340,41 @@ static u32 sceAudioOutput2OutputBlocking(u32 vol, u32 dataPtr) {
}

auto &chan = chans[PSP_AUDIO_CHANNEL_OUTPUT2];
if (!chan.reserved) {
return hleLogError(SCEAUDIO, SCE_ERROR_AUDIO_CHANNEL_NOT_RESERVED, "channel not reserved");
}

chan.leftVolume = vol;
chan.rightVolume = vol;
chan.sampleAddress = dataPtr;
hleEatCycles(10000);

hleEatCycles(10000);
int result = __AudioEnqueue(chan, PSP_AUDIO_CHANNEL_OUTPUT2, true);
if (result < 0)
return hleLogError(SCEAUDIO, result);
return hleLogSuccessI(SCEAUDIO, result);
}

static u32 sceAudioOutput2ChangeLength(u32 sampleCount) {
if (!chans[PSP_AUDIO_CHANNEL_OUTPUT2].reserved) {
ERROR_LOG(SCEAUDIO, "sceAudioOutput2ChangeLength(%08x) - channel not reserved ", sampleCount);
return SCE_ERROR_AUDIO_CHANNEL_NOT_RESERVED;
auto &chan = chans[PSP_AUDIO_CHANNEL_OUTPUT2];
if (!chan.reserved) {
return hleLogError(SCEAUDIO, SCE_ERROR_AUDIO_CHANNEL_NOT_RESERVED, "channel not reserved");
}
DEBUG_LOG(SCEAUDIO, "sceAudioOutput2ChangeLength(%08x)", sampleCount);
chans[PSP_AUDIO_CHANNEL_OUTPUT2].sampleCount = sampleCount;
return 0;
chan.sampleCount = sampleCount;
return hleLogSuccessI(SCEAUDIO, 0);
}

static u32 sceAudioOutput2GetRestSample() {
if (!chans[PSP_AUDIO_CHANNEL_OUTPUT2].reserved) {
ERROR_LOG(SCEAUDIO, "sceAudioOutput2GetRestSample() - channel not reserved ");
return SCE_ERROR_AUDIO_CHANNEL_NOT_RESERVED;
auto &chan = chans[PSP_AUDIO_CHANNEL_OUTPUT2];
if (!chan.reserved) {
return hleLogError(SCEAUDIO, SCE_ERROR_AUDIO_CHANNEL_NOT_RESERVED, "channel not reserved");
}
u32 size = (u32)chan.sampleQueue.size() / 2;
if (size > chan.sampleCount) {
// If ChangeLength reduces the size, it still gets output but this return is clamped.
size = chan.sampleCount;
}
DEBUG_LOG(SCEAUDIO, "sceAudioOutput2GetRestSample()");
return (u32) chans[PSP_AUDIO_CHANNEL_OUTPUT2].sampleQueue.size() / 2;
return hleLogSuccessI(SCEAUDIO, size);
}

static u32 sceAudioOutput2Release() {
Expand All @@ -382,6 +390,7 @@ static u32 sceAudioOutput2Release() {
}

static u32 sceAudioSetFrequency(u32 freq) {
// TODO: Not available from user code.
if (freq == 44100 || freq == 48000) {
INFO_LOG(SCEAUDIO, "sceAudioSetFrequency(%08x)", freq);
__AudioSetOutputFrequency(freq);
Expand Down Expand Up @@ -424,10 +433,8 @@ static u32 sceAudioSRCChReserve(u32 sampleCount, u32 freq, u32 format) {
chan.reserved = true;
chan.sampleCount = sampleCount;
chan.format = format == 2 ? PSP_AUDIO_FORMAT_STEREO : PSP_AUDIO_FORMAT_MONO;
// TODO: Zero probably means don't change? Or it means default?
if (freq != 0) {
__AudioSetOutputFrequency(freq);
}
// Zero means default to 44.1kHz.
__AudioSetSRCFrequency(freq);
return hleLogSuccessI(SCEAUDIO, 0);
}

Expand All @@ -449,6 +456,10 @@ static u32 sceAudioSRCOutputBlocking(u32 vol, u32 buf) {
}

auto &chan = chans[PSP_AUDIO_CHANNEL_SRC];
if (!chan.reserved) {
return hleLogError(SCEAUDIO, SCE_ERROR_AUDIO_CHANNEL_NOT_RESERVED, "channel not reserved");
}

chan.leftVolume = vol;
chan.rightVolume = vol;
chan.sampleAddress = buf;
Expand Down Expand Up @@ -511,7 +522,7 @@ const HLEFunction sceAudio[] =
{0X95FD0C2D, &WrapU_UU<sceAudioChangeChannelConfig>, "sceAudioChangeChannelConfig", 'x', "ii" },
{0XB7E1D8E7, &WrapU_UUU<sceAudioChangeChannelVolume>, "sceAudioChangeChannelVolume", 'x', "ixx" },

// Not sure about the point of these, maybe like traditional but with ability to do sample rate conversion?
// Like Output2, but with ability to do sample rate conversion.
{0X38553111, &WrapU_UUU<sceAudioSRCChReserve>, "sceAudioSRCChReserve", 'x', "iii" },
{0X5C37C0AE, &WrapU_V<sceAudioSRCChRelease>, "sceAudioSRCChRelease", 'x', "" },
{0XE0727056, &WrapU_UU<sceAudioSRCOutputBlocking>, "sceAudioSRCOutputBlocking", 'x', "xx" },
Expand Down
2 changes: 1 addition & 1 deletion Core/HLE/sceVaudio.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ static u32 sceVaudioChReserve(int sampleCount, int freq, int format) {
chans[PSP_AUDIO_CHANNEL_VAUDIO].leftVolume = 0;
chans[PSP_AUDIO_CHANNEL_VAUDIO].rightVolume = 0;
vaudioReserved = true;
__AudioSetOutputFrequency(freq);
__AudioSetSRCFrequency(freq);
return 0;
}

Expand Down
2 changes: 2 additions & 0 deletions Core/HW/StereoResampler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -293,4 +293,6 @@ void StereoResampler::DoState(PointerWrap &p) {
auto s = p.Section("resampler", 1);
if (!s)
return;
if (p.mode == p.MODE_READ)
Clear();
}
1 change: 1 addition & 0 deletions headless/Headless.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -377,6 +377,7 @@ int main(int argc, const char* argv[])
g_Config.bHighQualityDepth = true;
g_Config.bMemStickInserted = true;
g_Config.bFragmentTestCache = true;
g_Config.iAudioLatency = 1;

#ifdef _WIN32
InitSysDirectories();
Expand Down