Skip to content
Permalink
Browse files

Merge pull request #12147 from unknownbrackets/audio2-src

Handle audio SRC mixing more correctly
  • Loading branch information...
hrydgard committed Jul 5, 2019
2 parents b0c4323 + 4546db5 commit accccba11b7a764146fa66ec3bf937816a5bd1b4
Showing with 94 additions and 24 deletions.
  1. +59 −4 Core/HLE/__sceAudio.cpp
  2. +1 −0 Core/HLE/__sceAudio.h
  3. +30 −19 Core/HLE/sceAudio.cpp
  4. +1 −1 Core/HLE/sceVaudio.cpp
  5. +2 −0 Core/HW/StereoResampler.cpp
  6. +1 −0 headless/Headless.cpp
@@ -62,6 +62,7 @@ enum latency {
int eventAudioUpdate = -1;
int eventHostAudioUpdate = -1;
int mixFrequency = 44100;
int srcFrequency = 0;

const int hwSampleRate = 44100;

@@ -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:
@@ -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;

@@ -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) {
@@ -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)
@@ -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.
@@ -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)
@@ -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++)
@@ -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);
@@ -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) {
@@ -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);
}

@@ -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() {
@@ -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);
@@ -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);
}

@@ -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;
@@ -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" },
@@ -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;
}

@@ -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();
}
@@ -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();

0 comments on commit accccba

Please sign in to comment.
You can’t perform that action at this time.