Rewrite audio line output. Now deterministic (from the game's POV) and smoother.

This commit is contained in:
Henrik Rydgard 2012-11-17 14:20:04 +01:00
parent ba171c8360
commit 5a48578a12
11 changed files with 475 additions and 229 deletions

View File

@ -25,68 +25,83 @@
// Not fully featured, no safety checking yet. Add features as needed.
// TODO: "inline" storage?
template <class T, int N>
class FixedSizeQueue
{
T *storage;
int head;
int tail;
int count; // sacrifice 4 bytes for a simpler implementation. may optimize away in the future.
// Make copy constructor private for now.
FixedSizeQueue(FixedSizeQueue &other) { }
class FixedSizeQueue {
public:
FixedSizeQueue()
{
storage = new T[N];
FixedSizeQueue() {
storage_ = new T[N];
clear();
}
~FixedSizeQueue()
{
delete [] storage;
~FixedSizeQueue() {
delete [] storage_;
}
void clear() {
head = 0;
tail = 0;
count = 0;
head_ = 0;
tail_ = 0;
count_ = 0;
}
void push(T t) {
storage[tail] = t;
tail++;
if (tail == N)
tail = 0;
count++;
storage_[tail_] = t;
tail_++;
if (tail_ == N)
tail_ = 0;
count_++;
}
void push_array(const T *ptr, size_t num) {
// TODO: memcpy
for (size_t i = 0; i < num; i++) {
push(ptr[i]);
}
}
void pop() {
head++;
if (head == N)
head = 0;
count--;
head_++;
if (head_ == N)
head_ = 0;
count_--;
}
void pop_array(T *outptr, size_t num) {
for (size_t i = 0; i < num) {
outptr[i] = front();
pop();
}
}
T pop_front() {
const T &temp = storage[head];
const T &temp = storage_[head_];
pop();
return temp;
}
T &front() { return storage[head]; }
const T &front() const { return storage[head]; }
T &front() { return storage_[head_]; }
const T &front() const { return storage_[head_]; }
size_t size() const {
return count;
return count_;
}
int room() const {
return N - count_;
}
bool empty() {
return count;
return count_;
}
private:
T *storage_;
int head_;
int tail_;
int count_; // sacrifice 4 bytes for a simpler implementation. may optimize away in the future.
// Make copy constructor private for now.
FixedSizeQueue(FixedSizeQueue &other) { }
};
#endif // _FIXED_SIZE_QUEUE_H_

View File

@ -23,6 +23,7 @@
#include "StdMutex.h"
#include "CoreTiming.h"
#include "Core.h"
#include "HLE/sceKernelThread.h"
int CPU_HZ = 222000000;

View File

@ -178,6 +178,10 @@ template<u32 func(u32, u32, u32, u32, u32)> void WrapU_UUUUU() {
RETURN(retval);
}
template<void func(u32, u32, u32, u32, u32)> void WrapV_UUUUU() {
func(PARAM(0), PARAM(1), PARAM(2), PARAM(3), PARAM(4));
}
template<int func(const char *, int, u32, int, u32)> void WrapU_CIUIU() {
int retval = func(Memory::GetCharPointer(PARAM(0)), PARAM(1), PARAM(2), PARAM(3), PARAM(4));
RETURN(retval);

View File

@ -60,7 +60,7 @@ const HLEFunction FakeSysCalls[] =
{NID_THREADRETURN, __KernelReturnFromThread, "__KernelReturnFromThread"},
{NID_CALLBACKRETURN, __KernelReturnFromMipsCall, "__KernelReturnFromMipsCall"},
{NID_INTERRUPTRETURN, __KernelReturnFromInterrupt, "__KernelReturnFromInterrupt"},
{NID_IDLE, _sceKernelIdle, "_sceKernelIdle"},
{NID_IDLE, __KernelIdle, "_sceKernelIdle"},
};
const HLEFunction UtilsForUser[] =

View File

@ -24,6 +24,9 @@
#include "../CoreTiming.h"
#include "../MemMap.h"
#include "../Host.h"
#include "../Config.h"
#include "FixedSizeQueue.h"
#include "Common/Thread.h"
// While buffers == MAX_BUFFERS, block on blocking write
// non-blocking writes will return busy, I guess
@ -34,46 +37,52 @@
std::recursive_mutex section;
int eventAudioUpdate = -1;
int eventHostAudioUpdate = -1;
int mixFrequency = 44100;
const int hwSampleRate = 44100;
const int hwBlockSize = 480;
const int hostAttemptBlockSize = 64;
const int audioIntervalUs = (int)(1000000ULL * hwBlockSize / hwSampleRate);
const int audioHostIntervalUs = (int)(1000000ULL * hostAttemptBlockSize / hwSampleRate);
// High and low watermarks, basically.
const int chanQueueMaxSizeFactor = 4;
const int chanQueueMinSizeFactor = 2;
// A whole second, should be enough for anything.
FixedSizeQueue<s16, hwBlockSize * 8> outAudioQueue;
const int audioIntervalMs = 20;
void hleAudioUpdate(u64 userdata, int cyclesLate)
{
host->UpdateSound();
__AudioUpdate();
CoreTiming::ScheduleEvent(msToCycles(audioIntervalMs), eventAudioUpdate, 0);
CoreTiming::ScheduleEvent(usToCycles(audioIntervalUs), eventAudioUpdate, 0);
}
void hleHostAudioUpdate(u64 userdata, int cyclesLate)
{
host->UpdateSound();
CoreTiming::ScheduleEvent(usToCycles(audioHostIntervalUs), eventHostAudioUpdate, 0);
}
void __AudioInit()
{
eventAudioUpdate = CoreTiming::RegisterEvent("AudioUpdate", &hleAudioUpdate);
mixFrequency = 44100;
CoreTiming::ScheduleEvent(msToCycles(1), eventAudioUpdate, 0);
eventAudioUpdate = CoreTiming::RegisterEvent("AudioUpdate", &hleAudioUpdate);
eventHostAudioUpdate = CoreTiming::RegisterEvent("AudioUpdateHost", &hleHostAudioUpdate);
CoreTiming::ScheduleEvent(usToCycles(audioIntervalUs), eventAudioUpdate, 0);
CoreTiming::ScheduleEvent(usToCycles(audioHostIntervalUs), eventHostAudioUpdate, 0);
for (int i = 0; i < 8; i++)
chans[i].clear();
}
void __AudioShutdown()
{
}
void __AudioUpdate()
{
// DEBUG_LOG(HLE, "Updating audio");
section.lock();
for (int i = 0; i < MAX_CHANNEL; i++)
{
if (chans[i].triggered)
{
chans[i].triggered = false;
// Instead of looping through all threads, which this does, we should keep track of which threads are waiting and just
// resume them.
__KernelTriggerWait(WAITTYPE_AUDIOCHANNEL, (SceUID)i, true);
}
}
section.unlock();
for (int i = 0; i < 8; i++)
chans[i].clear();
}
u32 __AudioEnqueue(AudioChannel &chan, int chanNum, bool blocking)
@ -81,15 +90,18 @@ u32 __AudioEnqueue(AudioChannel &chan, int chanNum, bool blocking)
section.lock();
if (chan.sampleAddress == 0)
return SCE_ERROR_AUDIO_NOT_OUTPUT;
if (chan.sampleQueue.size() > chan.sampleCount*2) {
if (chan.sampleQueue.size() > chan.sampleCount*2*chanQueueMaxSizeFactor) {
// Block!
if (blocking) {
chan.waitingThread = __KernelGetCurThread();
// WARNING: This changes currentThread so must grab waitingThread before (line above).
__KernelWaitCurThread(WAITTYPE_AUDIOCHANNEL, (SceUID)chanNum, 0, 0, false);
section.unlock();
return 0;
}
else
{
chan.waitingThread = 0;
return SCE_ERROR_AUDIO_CHANNEL_BUSY;
}
}
@ -114,49 +126,122 @@ u32 __AudioEnqueue(AudioChannel &chan, int chanNum, bool blocking)
return 0;
}
int __AudioMix(short *outstereo, int numSamples)
// 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.
void __AudioUpdate()
{
memset(outstereo, 0, numSamples*sizeof(short)*2);
NOTICE_LOG(HLE, "__AudioUpdate");
#ifdef _WIN32
// HACK - TODO: Remove
bool noThrottle = GetAsyncKeyState(VK_TAB) != 0;
#else
bool noThrottle = false;
#endif
// Sleep here until the host audio hardware is ready to receive samples.
// This will effectively throttle the frame rate.
// Disable Audio for now.
// return numSamples;
if (!noThrottle) {
while (true)
{
if (outAudioQueue.size() < hwBlockSize * 4)
break; // room can only increase without us pushing, so there's no race condition between here and section.lock()
Common::SleepCurrentThread(0);
}
}
section.lock();
int numActiveChans = 0;
for (int j = 0; j < MAX_CHANNEL; j++)
{
numActiveChans += chans[j].running ? 1 : 0;
}
if (!numActiveChans)
{
section.unlock();
return numSamples;
}
s32 mixBuffer[hwBlockSize * 2];
memset(mixBuffer, 0, sizeof(mixBuffer));
for (int i = 0; i < MAX_CHANNEL; i++)
{
for (int s = 0; s < numSamples; s++)
if (!chans[i].reserved)
continue;
if (!chans[i].sampleQueue.size()) {
DEBUG_LOG(HLE, "No queued samples, skipping channel %i", i);
continue;
}
for (int s = 0; s < hwBlockSize; s++)
{
if (chans[i].sampleQueue.size() >= 2)
{
s16 sample1 = chans[i].sampleQueue.front();
s16 sampleL = chans[i].sampleQueue.front();
s16 sampleR = chans[i].sampleQueue.front();
chans[i].sampleQueue.pop();
s16 sample2 = chans[i].sampleQueue.front();
chans[i].sampleQueue.pop();
outstereo[s*2] += sample1 / 4;//(sample * chans[i].vol1) >> 8;
outstereo[s*2+1] += sample2 / 4;//.(sample * chans[i].vol2) >> 8;
mixBuffer[s * 2] += sampleL;
mixBuffer[s * 2 + 1] += sampleR;
}
else
{
ERROR_LOG(HLE, "channel %i buffer underrun at %i of %i", i, s, hwBlockSize);
break;
}
}
if (chans[i].sampleQueue.size() < chans[i].sampleCount)
if (chans[i].sampleQueue.size() < chans[i].sampleCount * 2 * chanQueueMinSizeFactor)
{
chans[i].triggered = true;
// Ask the thread to send more samples until next time, queue is being drained.
if (chans[i].waitingThread) {
SceUID waitingThread = chans[i].waitingThread;
chans[i].waitingThread = 0;
// DEBUG_LOG(HLE, "Woke thread %i for some buffer filling", waitingThread);
__KernelResumeThreadFromWait(waitingThread);
}
}
}
if (!noThrottle && g_Config.bEnableSound) {
// Push the mixed samples onto the output audio queue.
for (int i = 0; i < hwBlockSize; i++) {
s32 sampleL = mixBuffer[i * 2] >> 2; // TODO - what factor?
s32 sampleR = mixBuffer[i * 2 + 1] >> 2;
outAudioQueue.push((s16)sampleL);
outAudioQueue.push((s16)sampleR);
}
}
section.unlock();
return numSamples;
}
void __AudioSetOutputFrequency(int freq)
{
mixFrequency = freq;
}
// numFrames is number of stereo frames.
int __AudioMix(short *outstereo, int numFrames)
{
// TODO: if mixFrequency != the actual output frequency, resample!
section.lock();
int underrun = -1;
s16 sampleL = 0;
s16 sampleR = 0;
for (size_t i = 0; i < numFrames; i++) {
if (outAudioQueue.size() >= 2)
{
sampleL = outAudioQueue.front();
outAudioQueue.pop();
sampleR = outAudioQueue.front();
outAudioQueue.pop();
outstereo[i * 2] = sampleL;
outstereo[i * 2 + 1] = sampleR;
} else {
underrun = i;
outstereo[i * 2] = sampleL; // repeat last sample, can reduce clicking
outstereo[i * 2 + 1] = sampleR; // repeat last sample, can reduce clicking
}
}
if (underrun >= 0) {
ERROR_LOG(HLE, "audio out buffer UNDERRUN at %i of %i", underrun, numFrames);
} else {
// DEBUG_LOG(HLE, "No underrun, mixed %i samples fine", numFrames);
}
section.unlock();
return numFrames;
}

View File

@ -24,6 +24,7 @@
void __AudioInit();
void __AudioUpdate();
void __AudioShutdown();
void __AudioSetOutputFrequency(int freq);
// May return SCE_ERROR_AUDIO_CHANNEL_BUSY if buffer too large
u32 __AudioEnqueue(AudioChannel &chan, int chanNum, bool blocking);

View File

@ -35,6 +35,39 @@ AudioChannel chans[8];
// Not sure about the range of volume, I often see 0x800 so that might be either
// max or 50%?
void sceAudioOutputBlocking(u32 chan, u32 vol, u32 samplePtr)
{
if (samplePtr == 0)
{
ERROR_LOG(HLE, "sceAudioOutputBlocking - Sample pointer null");
RETURN(0);
}
if (chan < 0 || chan >= MAX_CHANNEL)
{
ERROR_LOG(HLE,"sceAudioOutputBlocking() - BAD CHANNEL");
RETURN(SCE_ERROR_AUDIO_INVALID_CHANNEL);
}
else if (!chans[chan].reserved)
{
ERROR_LOG(HLE,"sceAudioOutputBlocking() - channel not reserved");
RETURN(SCE_ERROR_AUDIO_CHANNEL_NOT_RESERVED);
}
else
{
DEBUG_LOG(HLE, "sceAudioOutputBlocking(%d, %d, %08x )",chan,vol,samplePtr);
chans[chan].leftVolume = vol;
chans[chan].rightVolume = vol;
chans[chan].sampleAddress = samplePtr;
RETURN(0);
int retval = __AudioEnqueue(chans[chan], chan, true);
if (retval != 0) {
// There was an error and didn't block (block always returns 0 here). Fine to RETURN.
RETURN(retval);
}
}
}
void sceAudioOutputPannedBlocking(u32 chan, u32 volume1, u32 volume2, u32 samplePtr)
{
if (samplePtr == 0)
@ -54,74 +87,77 @@ void sceAudioOutputPannedBlocking(u32 chan, u32 volume1, u32 volume2, u32 sample
}
else
{
DEBUG_LOG(HLE,"sceAudioOutputPannedBlocking(%d,%d,%d, %08x )", chan, volume1, volume2, samplePtr);
chans[chan].running = true;
DEBUG_LOG(HLE, "sceAudioOutputPannedBlocking(%d,%d,%d, %08x )", chan, volume1, volume2, samplePtr);
chans[chan].leftVolume = volume1;
chans[chan].rightVolume = volume2;
chans[chan].sampleAddress = samplePtr;
RETURN(0);
__AudioEnqueue(chans[chan], chan, true);
int retval = __AudioEnqueue(chans[chan], chan, true);
if (retval != 0) {
// There was an error and didn't block (block always returns 0 here). Fine to RETURN.
RETURN(retval);
}
}
}
void sceAudioGetChannelRestLen()
u32 sceAudioOutput(u32 chan, u32 vol, u32 samplePtr)
{
ERROR_LOG(HLE,"UNIMPL sceAudioGetChannelRestLen(%i)", PARAM(0));
// Remaining samples in that channel buffer.
RETURN(0);
if (chan < 0 || chan >= MAX_CHANNEL)
{
ERROR_LOG(HLE,"sceAudioOutput() - BAD CHANNEL");
return SCE_ERROR_AUDIO_INVALID_CHANNEL;
}
else if (!chans[chan].reserved)
{
ERROR_LOG(HLE,"sceAudioOutput(%d, %d, %08x) - channel not reserved", chan, vol, samplePtr);
return SCE_ERROR_AUDIO_CHANNEL_NOT_RESERVED;
}
else
{
chans[chan].leftVolume = vol;
chans[chan].rightVolume = vol;
chans[chan].sampleAddress = samplePtr;
u32 retval = __AudioEnqueue(chans[chan], chan, false);
DEBUG_LOG(HLE, "%08x=sceAudioOutputPanned(%d, %d, %08x)", retval, chan, vol, samplePtr);
return retval;
}
}
u32 sceAudioOutputPanned(u32 chan, u32 leftVol, u32 rightVol, u32 samplePtr)
{
if (chan < 0 || chan >= MAX_CHANNEL)
{
ERROR_LOG(HLE,"sceAudioOutputPanned() - BAD CHANNEL");
return SCE_ERROR_AUDIO_INVALID_CHANNEL;
}
else if (!chans[chan].reserved)
{
ERROR_LOG(HLE,"sceAudioOutputPanned(%d, %d, %d, %08x) - channel not reserved", chan,leftVol,rightVol,samplePtr);
return SCE_ERROR_AUDIO_CHANNEL_NOT_RESERVED;
}
else
{
chans[chan].running = true;
if (chan < 0 || chan >= MAX_CHANNEL)
{
ERROR_LOG(HLE,"sceAudioOutputPanned() - BAD CHANNEL");
return SCE_ERROR_AUDIO_INVALID_CHANNEL;
}
else if (!chans[chan].reserved)
{
ERROR_LOG(HLE,"sceAudioOutputPanned(%d, %d, %d, %08x) - channel not reserved", chan, leftVol, rightVol, samplePtr);
return SCE_ERROR_AUDIO_CHANNEL_NOT_RESERVED;
}
else
{
chans[chan].leftVolume = leftVol;
chans[chan].rightVolume = rightVol;
chans[chan].sampleAddress = samplePtr;
u32 retval = __AudioEnqueue(chans[chan], chan, false);
DEBUG_LOG(HLE,"%08x=sceAudioOutputPanned(%d, %d, %d, %08x)",retval,chan,leftVol,rightVol,samplePtr);
return retval;
u32 retval = __AudioEnqueue(chans[chan], chan, false);
DEBUG_LOG(HLE, "%08x=sceAudioOutputPanned(%d, %d, %d, %08x)", retval, chan, leftVol, rightVol, samplePtr);
return retval;
}
}
void sceAudioOutputBlocking(u32 chan, u32 vol, u32 samplePtr)
int sceAudioGetChannelRestLen(u32 chan)
{
if (samplePtr == 0)
{
ERROR_LOG(HLE, "sceAudioOutputBlocking - Sample pointer null");
RETURN(0);
}
if (chan < 0 || chan >= MAX_CHANNEL)
{
ERROR_LOG(HLE,"sceAudioOutputBlocking() - BAD CHANNEL");
RETURN(SCE_ERROR_AUDIO_INVALID_CHANNEL);
}
else if (!chans[chan].reserved)
{
ERROR_LOG(HLE,"sceAudioOutputBlocking() - channel not reserved");
RETURN(SCE_ERROR_AUDIO_CHANNEL_NOT_RESERVED);
}
else
{
DEBUG_LOG(HLE,"sceAudioOutputPanned(%d, %d, %08x )",chan,vol,samplePtr);
chans[chan].running = true;
chans[chan].leftVolume = vol;
chans[chan].rightVolume = vol;
chans[chan].sampleAddress = samplePtr;
RETURN(0);
__AudioEnqueue(chans[chan], chan, true);
}
int sz = (int)chans[chan].sampleQueue.size() / 2;
DEBUG_LOG(HLE,"UNTESTED %i = sceAudioGetChannelRestLen(%i)", PARAM(0));
return sz;
}
int sceAudioGetChannelRestLength(u32 chan)
{
int sz = (int)chans[chan].sampleQueue.size() / 2;
DEBUG_LOG(HLE,"UNTESTED %i = sceAudioGetChannelRestLen(%i)", PARAM(0));
return sz;
}
static int GetFreeChannel()
@ -158,7 +194,7 @@ u32 sceAudioChReserve(u32 channel, u32 sampleCount, u32 format) //.Allocate soun
{
WARN_LOG(HLE, "WARNING: Reserving already reserved channel. Error?");
}
DEBUG_LOG(HLE,"%i = sceAudioChReserve(%i, %i, %i)", channel, PARAM(0), sampleCount, format);
DEBUG_LOG(HLE, "%i = sceAudioChReserve(%i, %i, %i)", channel, PARAM(0), sampleCount, format);
chans[channel].sampleCount = sampleCount;
chans[channel].reserved = true;
@ -172,11 +208,9 @@ u32 sceAudioChRelease(u32 chan)
ERROR_LOG(HLE,"sceAudioChRelease(%i): channel not reserved", chan);
return SCE_ERROR_AUDIO_CHANNEL_NOT_RESERVED;
}
chans[chan].triggered = false;
chans[chan].running = false;
chans[chan].reserved = false;
DEBUG_LOG(HLE,"sceAudioChRelease(%i)", chan);
DEBUG_LOG(HLE, "sceAudioChRelease(%i)", chan);
return 1;
}
@ -194,8 +228,8 @@ u32 sceAudioSetChannelDataLen(u32 chan, u32 len)
}
else
{
DEBUG_LOG(HLE,"sceAudioSetChannelDataLen(%i, %i)", chan, len);
chans[chan].sampleCount = len;
DEBUG_LOG(HLE, "sceAudioSetChannelDataLen(%i, %i)", chan, len);
chans[chan].dataLen = len;
return 0;
}
}
@ -214,7 +248,7 @@ u32 sceAudioChangeChannelConfig(u32 chan, u32 format)
}
else
{
DEBUG_LOG(HLE,"sceAudioChangeChannelConfig(%i, %i)", chan, format);
DEBUG_LOG(HLE, "sceAudioChangeChannelConfig(%i, %i)", chan, format);
chans[chan].format = format;
return 0;
}
@ -234,7 +268,7 @@ u32 sceAudioChangeChannelVolume(u32 chan, u32 lvolume, u32 rvolume)
}
else
{
DEBUG_LOG(HLE,"sceAudioChangeChannelVolume(%i, %i, %i)", chan, lvolume, rvolume);
DEBUG_LOG(HLE, "sceAudioChangeChannelVolume(%i, %i, %i)", chan, lvolume, rvolume);
chans[chan].leftVolume = lvolume;
chans[chan].rightVolume = rvolume;
return 0;
@ -262,7 +296,6 @@ void sceAudioOutput2OutputBlocking()
u32 dataPtr = PARAM(1);
WARN_LOG(HLE,"FAKE sceAudioOutput2OutputBlocking(%i, %08x)", vol, dataPtr);
chans[0].running = true;
chans[0].leftVolume = vol;
chans[0].rightVolume = vol;
chans[0].sampleAddress = dataPtr;
@ -289,6 +322,17 @@ void sceAudioOutput2Release()
RETURN(0);
}
u32 sceAudioSetFrequency(u32 freq) {
if (freq == 44100 || freq == 48000) {
INFO_LOG(HLE, "sceAudioSetFrequency(%i)", freq);
__AudioSetOutputFrequency(freq);
return 0;
} else {
ERROR_LOG(HLE, "sceAudioSetFrequency(%i) - invalid frequency (must be 44.1 or 48 khz)", freq);
return -1;
}
}
const HLEFunction sceAudio[] =
{
{0x01562ba3, sceAudioOutput2Reserve, "sceAudioOutput2Reserve"}, // N+, Super Stardust Portable uses these
@ -302,29 +346,28 @@ const HLEFunction sceAudio[] =
{0x5C37C0AE, 0, "sceAudioSRCChRelease"},
{0x80F1F7E0, sceAudioInit, "sceAudioInit"},
{0x927AC32B, 0, "sceAudioSetVolumeOffset"},
{0xA2BEAA6C, 0, "sceAudioSetFrequency"},
{0xA633048E, 0, "sceAudioPollInputEnd"},
{0xB011922F, 0, "sceAudioGetChannelRestLength"},
{0xB61595C0, 0, "sceAudioLoopbackTest"},
{0xA2BEAA6C, WrapU_U<sceAudioSetFrequency>, "sceAudioSetFrequency"},
{0xE0727056, 0, "sceAudioSRCOutputBlocking"},
{0xE926D3FB, 0, "sceAudioInputInitEx"},
{0x8c1009b2, 0, "sceAudioOutput"},
{0x8c1009b2, WrapU_UUU<sceAudioOutput>, "sceAudioOutput"},
{0x136CAF51, WrapV_UUU<sceAudioOutputBlocking>, "sceAudioOutputBlocking"},
{0xE2D56B2D, WrapU_UUUU<sceAudioOutputPanned>, "sceAudioOutputPanned"},
{0x13F592BC, WrapV_UUUU<sceAudioOutputPannedBlocking>, "sceAudioOutputPannedBlocking"}, //(u32, u32, u32, void *)Output sound, blocking
{0x5EC81C55, WrapU_UUU<sceAudioChReserve>, "sceAudioChReserve"}, //(u32, u32 samplecount, u32) Initialize channel and allocate buffer long, long samplecount, long);//init buffer? returns handle, minus if error
{0x6FC46853, WrapU_U<sceAudioChRelease>, "sceAudioChRelease"}, //(long handle)Terminate channel and deallocate buffer //free buffer?
{0xE9D97901, sceAudioGetChannelRestLen, "sceAudioGetChannelRestLen"},
{0xCB2E439E, WrapU_UU<sceAudioSetChannelDataLen>, "sceAudioSetChannelDataLen"}, //(u32, u32)
{0xE9D97901, WrapI_U<sceAudioGetChannelRestLen>, "sceAudioGetChannelRestLen"},
{0xB011922F, WrapI_U<sceAudioGetChannelRestLen>, "sceAudioGetChannelRestLength"}, // Is there a difference between this and sceAudioGetChannelRestLen?
{0xCB2E439E, WrapU_UU<sceAudioSetChannelDataLen>, "sceAudioSetChannelDataLen"}, //(u32, u32)
{0x95FD0C2D, WrapU_UU<sceAudioChangeChannelConfig>, "sceAudioChangeChannelConfig"},
{0xB7E1D8E7, WrapU_UUU<sceAudioChangeChannelVolume>, "sceAudioChangeChannelVolume"},
{0x41efade7, 0, "sceAudioOneshotOutput"},
{0x086e5895, 0, "sceAudioInputBlocking"},
{0x6d4bec68, 0, "sceAudioInput"},
{0x41efade7, 0, "sceAudioOneshotOutput"},
{0xB61595C0, 0, "sceAudioLoopbackTest"},
{0x7de61688, 0, "sceAudioInputInit"},
{0xE926D3FB, 0, "sceAudioInputInitEx"},
{0x6d4bec68, 0, "sceAudioInput"},
{0x086e5895, 0, "sceAudioInputBlocking"},
{0xa708c6a6, 0, "sceAudioGetInputLength"},
{0x87b2e651, 0, "sceAudioWaitInputEnd"},
{0x7de61688, 0, "sceAudioInputInit"},
{0xb011922f, 0, "sceAudioGetChannelRestLength"},
{0xA633048E, 0, "sceAudioPollInputEnd"},
{0x87b2e651, 0, "sceAudioWaitInputEnd"},
};

View File

@ -20,6 +20,7 @@
#include <queue>
#include "CommonTypes.h"
#include "sceKernel.h"
#include "FixedSizeQueue.h"
enum PspAudioFormats { PSP_AUDIO_FORMAT_STEREO = 0, PSP_AUDIO_FORMAT_MONO = 0x10 };
@ -39,7 +40,9 @@ enum PspAudioFrequencies { PSP_AUDIO_FREQ_44K = 44100, PSP_AUDIO_FREQ_48K = 48
struct AudioChannel
{
AudioChannel() {clear();}
AudioChannel() {
clear();
}
// PSP side
@ -48,26 +51,30 @@ struct AudioChannel
// last sample address
u32 sampleAddress;
u32 sampleCount; // Number of samples written in each OutputBlocking
int running;
int triggered;
int dataLen; // Probably internal queue size. Not currently used.
int leftVolume;
int rightVolume;
int format;
SceUID waitingThread;
// PC side - should probably split out
// We copy samples as they are written into this simple ring buffer.
// Might try something more efficient later.
FixedSizeQueue<s16, 16384> sampleQueue;
FixedSizeQueue<s16, 32768> sampleQueue;
void clear() {
reserved = false;
running = false;
waitingThread = 0;
leftVolume = 0;
rightVolume = 0;
format = 0;
sampleAddress = 0;
sampleCount = 0;
sampleQueue.clear();
}
};

View File

@ -19,7 +19,7 @@
#include <algorithm>
#include "HLE.h"
#include "Common/Action.h"
#include "Common/FileUtil.h"
#include "../Host.h"
#include "../MIPS/MIPS.h"
@ -57,37 +57,71 @@ static const char *blacklistedModules[] = {
"sceNet_Library",
};
struct Module : public KernelObject
struct NativeModule {
u32 next;
u16 attribute;
u8 version[2];
char name[28];
u32 status;
u32 unk1;
u32 usermod_thid;
u32 memid;
u32 mpidtext;
u32 mpiddata;
u32 ent_top;
u32 ent_size;
u32 stub_top;
u32 stub_size;
u32 module_start_func;
u32 module_stop_func;
u32 module_bootstart_func;
u32 module_reboot_before_func;
u32 module_reboot_phase_func;
u32 entry_addr;
u32 gp_value;
u32 text_addr;
u32 text_size;
u32 data_size;
u32 bss_size;
u32 nsegment;
u32 segmentaddr[4];
u32 segmentsize[4];
u32 module_start_thread_priority;
u32 module_start_thread_stacksize;
u32 module_start_thread_attr;
u32 module_stop_thread_priority;
u32 module_stop_thread_stacksize;
u32 module_stop_thread_attr;
u32 module_reboot_before_thread_priority;
u32 module_reboot_before_thread_stacksize;
u32 module_reboot_before_thread_attr;
};
class Module : public KernelObject
{
const char *GetName() {return name;}
public:
Module() : memoryBlockAddr(0) {}
~Module() {
if (memoryBlockAddr) {
userMemory.Free(memoryBlockAddr);
}
}
const char *GetName() {return nm.name;}
const char *GetTypeName() {return "Module";}
void GetQuickInfo(char *ptr, int size)
{
// ignore size
sprintf(ptr, "name=%s gp=%08x entry=%08x",
name,
gp_value,
entry_addr);
nm.name,
nm.gp_value,
nm.entry_addr);
}
static u32 GetMissingErrorCode() { return SCE_KERNEL_ERROR_UNKNOWN_MODULE; }
int GetIDType() const { return 0; }
SceSize size;
char nsegment;
char reserved[3];
int segmentaddr[4];
int segmentsize[4];
unsigned int entry_addr;
unsigned int gp_value;
unsigned int text_addr;
unsigned int text_size;
unsigned int data_size;
unsigned int bss_size;
// The following is only available in the v1.5 firmware and above,
// but as sceKernelQueryModuleInfo is broken in v1.0 it doesn't matter.
unsigned short attribute;
unsigned char version[2];
char name[28];
NativeModule nm;
u32 memoryBlockAddr;
};
//////////////////////////////////////////////////////////////////////////
@ -183,6 +217,7 @@ Module *__KernelLoadELFFromPtr(const u8 *ptr, u32 loadAddress, std::string *erro
kernelObjects.Destroy<Module>(module->GetUID());
return 0;
}
module->memoryBlockAddr = reader.GetVaddr();
struct libent
{
@ -256,8 +291,8 @@ Module *__KernelLoadELFFromPtr(const u8 *ptr, u32 loadAddress, std::string *erro
}
}
module->gp_value = modinfo->gp;
strncpy(module->name, modinfo->name, 28);
module->nm.gp_value = modinfo->gp;
strncpy(module->nm.name, modinfo->name, 28);
INFO_LOG(LOADER,"Module %s: %08x %08x %08x", modinfo->name, modinfo->gp, modinfo->libent,modinfo->libstub);
@ -341,7 +376,7 @@ Module *__KernelLoadELFFromPtr(const u8 *ptr, u32 loadAddress, std::string *erro
if (ent->name == 0)
{
// ?
name = module->name;
name = module->nm.name;
}
else
{
@ -368,7 +403,7 @@ Module *__KernelLoadELFFromPtr(const u8 *ptr, u32 loadAddress, std::string *erro
}
}
module->entry_addr = reader.GetEntryPoint();
module->nm.entry_addr = reader.GetEntryPoint();
if (newptr)
{
@ -418,7 +453,7 @@ bool __KernelLoadPBP(const char *filename, std::string *error_string)
Module *module = __KernelLoadELFFromPtr(temp, PSP_GetDefaultLoadAddress(), error_string);
if (!module)
return false;
mipsr4k.pc = module->entry_addr;
mipsr4k.pc = module->nm.entry_addr;
delete [] temp;
}
in.close();
@ -460,13 +495,13 @@ void __KernelStartModule(Module *m, int args, const char *argp, SceKernelSMOptio
}
u32 __KernelGetModuleGP(SceUID module)
u32 __KernelGetModuleGP(SceUID uid)
{
u32 error;
Module *m = kernelObjects.Get<Module>(module,error);
if (m)
Module *module = kernelObjects.Get<Module>(uid, error);
if (module)
{
return m->gp_value;
return module->nm.gp_value;
}
else
{
@ -490,14 +525,14 @@ bool __KernelLoadExec(const char *filename, SceKernelLoadExecParam *param, std::
pspFileSystem.ReadFile(handle, temp, (size_t)info.size);
Module *m = __KernelLoadModule(temp, 0, error_string);
Module *module = __KernelLoadModule(temp, 0, error_string);
if (!m) {
if (!module) {
ERROR_LOG(LOADER, "Failed to load module %s", filename);
return false;
}
mipsr4k.pc = m->entry_addr;
mipsr4k.pc = module->nm.entry_addr;
INFO_LOG(LOADER, "Module entry: %08x", mipsr4k.pc);
@ -512,7 +547,7 @@ bool __KernelLoadExec(const char *filename, SceKernelLoadExecParam *param, std::
option.priority = 0x20;
option.stacksize = 0x40000; // crazy? but seems to be the truth
__KernelStartModule(m, (u32)strlen(filename) + 1, filename, &option);
__KernelStartModule(module, (u32)strlen(filename) + 1, filename, &option);
__KernelStartIdleThreads();
return true;
@ -578,6 +613,7 @@ u32 sceKernelLoadModule(const char *name, u32 flags)
if (PARAM(2))
{
SceKernelLMOption *lmoption = (SceKernelLMOption *)Memory::GetPointer(PARAM(2));
}
Module *module = 0;
@ -608,23 +644,66 @@ u32 sceKernelLoadModule(const char *name, u32 flags)
return module->GetUID();
}
void sceKernelStartModule()
{
int id = PARAM(0);
int argsize = PARAM(1);
u32 argptr = PARAM(2);
u32 ptrReturn = PARAM(3);
if (PARAM(4)) {
SceKernelSMOption *smoption = (SceKernelSMOption*)Memory::GetPointer(PARAM(4));
}
ERROR_LOG(HLE,"UNIMPL sceKernelStartModule(%d,asize=%08x,aptr=%08x,retptr=%08x,...)",
id,argsize,argptr,ptrReturn);
RETURN(0);
class AfterModuleEntryCall : public Action {
public:
AfterModuleEntryCall() {}
Module *module_;
u32 retValAddr;
virtual void run();
};
void AfterModuleEntryCall::run() {
Memory::Write_U32(retValAddr, currentMIPS->r[2]);
}
void sceKernelStopModule()
void sceKernelStartModule(u32 moduleId, u32 argsize, u32 argAddr, u32 returnValueAddr, u32 optionAddr)
{
ERROR_LOG(HLE,"UNIMPL sceKernelStopModule");
// Dunno what these three defaults should be...
u32 priority = 0x20;
u32 stacksize = 0x40000;
u32 attr = 0;
int stackPartition = 0;
if (optionAddr) {
SceKernelSMOption smoption;
Memory::ReadStruct(optionAddr, &smoption);;
priority = smoption.priority;
attr = smoption.attribute;
stacksize = smoption.stacksize;
stackPartition = smoption.mpidstack;
}
u32 error;
Module *module = kernelObjects.Get<Module>(moduleId, error);
if (!module) {
// TODO: Try not to lie so much.
/*
RETURN(error);
return;
*/
} else {
u32 entryAddr = module->nm.entry_addr;
if (entryAddr == -1) {
entryAddr = module->nm.module_start_func;
// attr = module->nm
}
}
//SceUID threadId;
//__KernelCreateThread(threadId, moduleId, module->nm.name, module->nm.entry_addr, priority, stacksize, attr);
ERROR_LOG(HLE,"UNIMPL sceKernelStartModule(%d,asize=%08x,aptr=%08x,retptr=%08x,%08x)",
moduleId,argsize,argAddr,returnValueAddr,optionAddr);
// Apparently, we need to call the entry point directly and insert the return value afterwards. This calls
// for a MipsCall and an Action. TODO
RETURN(0); // TODO: Delete
}
void sceKernelStopModule(u32 moduleId, u32 argSize, u32 argAddr, u32 returnValueAddr, u32 optionAddr)
{
ERROR_LOG(HLE,"UNIMPL sceKernelStopModule(%i, %i, %08x, %08x, %08x)",
moduleId, argSize, argAddr, returnValueAddr, optionAddr);
// We should call the "stop" entry point and return the value in returnValueAddr. See StartModule.
RETURN(0);
}
@ -655,8 +734,8 @@ void sceKernelGetModuleIdByAddress()
void sceKernelGetModuleId()
{
ERROR_LOG(HLE,"UNIMPL sceKernelGetModuleId");
RETURN(0);
ERROR_LOG(HLE,"sceKernelGetModuleId()");
RETURN(__KernelGetCurThreadModuleId());
}
void sceKernelFindModuleByName()
@ -665,15 +744,19 @@ void sceKernelFindModuleByName()
RETURN(1);
}
u32 sceKernelLoadModuleByID(u32 id) {
ERROR_LOG(HLE,"UNIMPL sceKernelLoadModuleById(%08x)", id);
// Apparenty, ID is a sceIo File UID. So this shouldn't be too hard when needed.
return 0;
}
const HLEFunction ModuleMgrForUser[] =
{
{0x977DE386,&WrapU_CU<sceKernelLoadModule>,"sceKernelLoadModule"},
{0xb7f46618,0,"sceKernelLoadModuleByID"},
{0x50F0C1EC,&sceKernelStartModule,"sceKernelStartModule"},
{0xb7f46618,&WrapU_U<sceKernelLoadModuleByID>,"sceKernelLoadModuleByID"},
{0x50F0C1EC,&WrapV_UUUUU<sceKernelStartModule>,"sceKernelStartModule"},
{0xD675EBB8,&sceKernelExitGame,"sceKernelSelfStopUnloadModule"}, //HACK
{0xd1ff982a,&sceKernelStopModule,"sceKernelStopModule"},
{0xd1ff982a,&WrapV_UUUUU<sceKernelStopModule>,"sceKernelStopModule"},
{0x2e0911aa,&sceKernelUnloadModule,"sceKernelUnloadModule"},
{0x710F61B5,0,"sceKernelLoadModuleMs"},
{0xF9275D98,0,"sceKernelLoadModuleBufferUsbWlan"}, ///???

View File

@ -242,6 +242,7 @@ public:
ThreadWaitInfo waitInfo;
bool sleeping;
SceUID moduleId;
bool isProcessingCallbacks;
@ -354,12 +355,11 @@ void __KernelStartIdleThreads()
}
}
void _sceKernelIdle()
void __KernelIdle()
{
CoreTiming::Idle();
// Advance must happen between Idle and Reschedule, so that threads that were waiting for something
// that was triggered at the end of the Idle period must get a chance to be scheduled.
// get a chance to be rescheduled.
CoreTiming::Advance();
// In Advance, we might trigger an interrupt such as vblank.
@ -749,7 +749,7 @@ void ThreadContext::reset()
lo = 0;
}
Thread *__KernelCreateThread(SceUID &id, SceUID moduleID, const char *name, u32 entryPoint, u32 priority, int stacksize, u32 attr)
Thread *__KernelCreateThread(SceUID &id, SceUID moduleId, const char *name, u32 entryPoint, u32 priority, int stacksize, u32 attr)
{
Thread *t = new Thread;
id = kernelObjects.Create(t);
@ -780,10 +780,11 @@ Thread *__KernelCreateThread(SceUID &id, SceUID moduleID, const char *name, u32
t->nt.runForClocks.hi = 0;
t->nt.wakeupCount = 0;
t->isProcessingCallbacks = false;
if (moduleID)
t->nt.gpreg = __KernelGetModuleGP(moduleID);
if (moduleId)
t->nt.gpreg = __KernelGetModuleGP(moduleId);
else
t->nt.gpreg = 0; // sceKernelStartThread will take care of this.
t->moduleId = moduleId;
strncpy(t->nt.name, name, 32);
t->context.r[MIPS_REG_RA] = threadReturnHackAddr; //hack! TODO fix
@ -806,8 +807,8 @@ void __KernelSetupRootThread(SceUID moduleID, int args, const char *argp, int pr
mipsr4k.r[MIPS_REG_SP] -= 256;
u32 location = mipsr4k.r[MIPS_REG_SP];
mipsr4k.r[MIPS_REG_A1] = location;
for (int i=0; i<args; i++)
Memory::Write_U8(argp[i], location+i);
for (int i = 0; i < args; i++)
Memory::Write_U8(argp[i], location + i);
}
@ -1072,6 +1073,12 @@ SceUID __KernelGetCurThread()
return currentThread->GetUID();
}
SceUID __KernelGetCurThreadModuleId()
{
return currentThread->moduleId;
}
void sceKernelGetThreadId()
{
u32 retVal = currentThread->GetUID();

View File

@ -102,7 +102,7 @@ void __KernelLoadContext(ThreadContext *ctx);
// TODO: Replace this with __KernelResumeThread over time as it's misguided.
bool __KernelTriggerWait(WaitType type, int id, bool dontSwitch = false);
u32 __KernelResumeThread(SceUID threadID); // can return an error value
u32 __KernelResumeThreadFromWait(SceUID threadID); // can return an error value
u32 __KernelGetWaitValue(SceUID threadID, u32 &error);
void __KernelWaitCurThread(WaitType type, SceUID waitId, u32 waitValue, int timeout, bool processCallbacks);
@ -131,12 +131,13 @@ u32 __KernelUnregisterCallback(RegisteredCallbackType type, SceUID cbId);
u32 __KernelNotifyCallbackType(RegisteredCallbackType type, SceUID cbId, int notifyArg);
SceUID __KernelGetCurThread();
SceUID __KernelGetCurThreadModuleId();
void __KernelSetupRootThread(SceUID moduleId, int args, const char *argp, int prio, int stacksize, int attr); //represents the real PSP elf loader, run before execution
void __KernelStartIdleThreads();
void __KernelReturnFromThread(); // Called as HLE function
u32 __KernelGetThreadPrio(SceUID id);
void _sceKernelIdle();
void __KernelIdle();
u32 __KernelMipsCallReturnAddress();
u32 __KernelInterruptReturnAddress(); // TODO: remove
@ -159,7 +160,6 @@ bool __KernelInCallback();
bool __KernelCheckCallbacks();
class Thread;
void __KernelSwitchContext(Thread *target, const char *reason);
u32 __KernelResumeThreadFromWait(SceUID threadID);
bool __KernelExecutePendingMipsCalls();
void __KernelNotifyCallback(RegisteredCallbackType type, SceUID threadId, SceUID cbId, int notifyArg);