Skip to content

Commit

Permalink
Merge pull request #12147 from unknownbrackets/audio2-src
Browse files Browse the repository at this point in the history
Handle audio SRC mixing more correctly
  • Loading branch information
hrydgard committed Jul 5, 2019
2 parents b0c4323 + 4546db5 commit accccba
Show file tree
Hide file tree
Showing 6 changed files with 94 additions and 24 deletions.
63 changes: 59 additions & 4 deletions Core/HLE/__sceAudio.cpp
Expand Up @@ -62,6 +62,7 @@ enum latency {
int eventAudioUpdate = -1; int eventAudioUpdate = -1;
int eventHostAudioUpdate = -1; int eventHostAudioUpdate = -1;
int mixFrequency = 44100; int mixFrequency = 44100;
int srcFrequency = 0;


const int hwSampleRate = 44100; const int hwSampleRate = 44100;


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


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


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


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


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


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

// Mix samples from the various audio channels into a single sample queue. // 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 // 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. // 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 // to the CPU. Much better to throttle the frame rate on frame display and just throw away audio
// if the buffer somehow gets full. // if the buffer somehow gets full.
bool firstChannel = true; bool firstChannel = true;
std::vector<int16_t> srcBuffer;


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


if (hwBlockSize * 2 > (int)chans[i].sampleQueue.size()) { bool needsResample = i == PSP_AUDIO_CHANNEL_SRC && srcFrequency != 0 && srcFrequency != mixFrequency;
ERROR_LOG(SCEAUDIO, "Channel %i buffer underrun at %i of %i", i, (int)chans[i].sampleQueue.size() / 2, hwBlockSize); 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; const s16 *buf1 = 0, *buf2 = 0;
size_t sz1, sz2; 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) { if (firstChannel) {
for (size_t s = 0; s < sz1; s++) for (size_t s = 0; s < sz1; s++)
Expand Down
1 change: 1 addition & 0 deletions Core/HLE/__sceAudio.h
Expand Up @@ -36,6 +36,7 @@ void __AudioDoState(PointerWrap &p);
void __AudioUpdate(bool resetRecording = false); void __AudioUpdate(bool resetRecording = false);
void __AudioShutdown(); void __AudioShutdown();
void __AudioSetOutputFrequency(int freq); void __AudioSetOutputFrequency(int freq);
void __AudioSetSRCFrequency(int freq);


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


static u32 sceAudioSetChannelDataLen(u32 chan, u32 len) { static u32 sceAudioSetChannelDataLen(u32 chan, u32 len) {
Expand Down Expand Up @@ -329,6 +329,7 @@ static u32 sceAudioOutput2Reserve(u32 sampleCount) {
chan.sampleCount = sampleCount; chan.sampleCount = sampleCount;
chan.format = PSP_AUDIO_FORMAT_STEREO; chan.format = PSP_AUDIO_FORMAT_STEREO;
chan.reserved = true; chan.reserved = true;
__AudioSetSRCFrequency(0);
return hleLogSuccessI(SCEAUDIO, 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]; 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.leftVolume = vol;
chan.rightVolume = vol; chan.rightVolume = vol;
chan.sampleAddress = dataPtr; chan.sampleAddress = dataPtr;
hleEatCycles(10000);


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


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


static u32 sceAudioOutput2GetRestSample() { static u32 sceAudioOutput2GetRestSample() {
if (!chans[PSP_AUDIO_CHANNEL_OUTPUT2].reserved) { auto &chan = chans[PSP_AUDIO_CHANNEL_OUTPUT2];
ERROR_LOG(SCEAUDIO, "sceAudioOutput2GetRestSample() - channel not reserved "); if (!chan.reserved) {
return SCE_ERROR_AUDIO_CHANNEL_NOT_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 hleLogSuccessI(SCEAUDIO, size);
return (u32) chans[PSP_AUDIO_CHANNEL_OUTPUT2].sampleQueue.size() / 2;
} }


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


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


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


auto &chan = chans[PSP_AUDIO_CHANNEL_SRC]; 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.leftVolume = vol;
chan.rightVolume = vol; chan.rightVolume = vol;
chan.sampleAddress = buf; chan.sampleAddress = buf;
Expand Down Expand Up @@ -511,7 +522,7 @@ const HLEFunction sceAudio[] =
{0X95FD0C2D, &WrapU_UU<sceAudioChangeChannelConfig>, "sceAudioChangeChannelConfig", 'x', "ii" }, {0X95FD0C2D, &WrapU_UU<sceAudioChangeChannelConfig>, "sceAudioChangeChannelConfig", 'x', "ii" },
{0XB7E1D8E7, &WrapU_UUU<sceAudioChangeChannelVolume>, "sceAudioChangeChannelVolume", 'x', "ixx" }, {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" }, {0X38553111, &WrapU_UUU<sceAudioSRCChReserve>, "sceAudioSRCChReserve", 'x', "iii" },
{0X5C37C0AE, &WrapU_V<sceAudioSRCChRelease>, "sceAudioSRCChRelease", 'x', "" }, {0X5C37C0AE, &WrapU_V<sceAudioSRCChRelease>, "sceAudioSRCChRelease", 'x', "" },
{0XE0727056, &WrapU_UU<sceAudioSRCOutputBlocking>, "sceAudioSRCOutputBlocking", 'x', "xx" }, {0XE0727056, &WrapU_UU<sceAudioSRCOutputBlocking>, "sceAudioSRCOutputBlocking", 'x', "xx" },
Expand Down
2 changes: 1 addition & 1 deletion Core/HLE/sceVaudio.cpp
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].leftVolume = 0;
chans[PSP_AUDIO_CHANNEL_VAUDIO].rightVolume = 0; chans[PSP_AUDIO_CHANNEL_VAUDIO].rightVolume = 0;
vaudioReserved = true; vaudioReserved = true;
__AudioSetOutputFrequency(freq); __AudioSetSRCFrequency(freq);
return 0; return 0;
} }


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


#ifdef _WIN32 #ifdef _WIN32
InitSysDirectories(); InitSysDirectories();
Expand Down

0 comments on commit accccba

Please sign in to comment.