* new FPS limiter
* new audio output/sync method about fucking time
This commit is contained in:
parent
626a9c1385
commit
fbad8b0f43
47
src/SPU.cpp
47
src/SPU.cpp
|
@ -26,7 +26,6 @@
|
||||||
// * capture addition modes, overflow bugs
|
// * capture addition modes, overflow bugs
|
||||||
// * channel hold
|
// * channel hold
|
||||||
// * 'length less than 4' glitch
|
// * 'length less than 4' glitch
|
||||||
int brap = 0;
|
|
||||||
|
|
||||||
namespace SPU
|
namespace SPU
|
||||||
{
|
{
|
||||||
|
@ -63,10 +62,10 @@ const s16 PSGTable[8][8] =
|
||||||
|
|
||||||
const u32 kSamplesPerRun = 1;
|
const u32 kSamplesPerRun = 1;
|
||||||
|
|
||||||
const u32 OutputBufferSize = 2*2*1024;
|
const u32 OutputBufferSize = 2*1024;
|
||||||
s16 OutputBuffer[2 * OutputBufferSize];
|
s16 OutputBuffer[2 * OutputBufferSize];
|
||||||
u32 OutputReadOffset;
|
volatile u32 OutputReadOffset;
|
||||||
u32 OutputWriteOffset;
|
volatile u32 OutputWriteOffset;
|
||||||
|
|
||||||
|
|
||||||
u16 Cnt;
|
u16 Cnt;
|
||||||
|
@ -579,7 +578,7 @@ void CaptureUnit::Run(s32 sample)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int zog = 0, zig = 0;
|
|
||||||
void Mix(u32 samples)
|
void Mix(u32 samples)
|
||||||
{
|
{
|
||||||
s32 channelbuf[32];
|
s32 channelbuf[32];
|
||||||
|
@ -629,7 +628,7 @@ void Mix(u32 samples)
|
||||||
else if (val > 0x7FFF) val = 0x7FFF;
|
else if (val > 0x7FFF) val = 0x7FFF;
|
||||||
|
|
||||||
Capture[0]->Run(val);
|
Capture[0]->Run(val);
|
||||||
if (!((Capture[0]->Cnt & (1<<7)))) break;
|
if (!(Capture[0]->Cnt & (1<<7))) break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -644,7 +643,7 @@ void Mix(u32 samples)
|
||||||
else if (val > 0x7FFF) val = 0x7FFF;
|
else if (val > 0x7FFF) val = 0x7FFF;
|
||||||
|
|
||||||
Capture[1]->Run(val);
|
Capture[1]->Run(val);
|
||||||
if (!((Capture[1]->Cnt & (1<<7)))) break;
|
if (!(Capture[1]->Cnt & (1<<7))) break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -735,7 +734,6 @@ void Mix(u32 samples)
|
||||||
OutputWriteOffset += 2;
|
OutputWriteOffset += 2;
|
||||||
OutputWriteOffset &= ((2*OutputBufferSize)-1);
|
OutputWriteOffset &= ((2*OutputBufferSize)-1);
|
||||||
if (OutputWriteOffset == OutputReadOffset) printf("!! SOUND FIFO OVERFLOW %d\n", OutputWriteOffset>>1);
|
if (OutputWriteOffset == OutputReadOffset) printf("!! SOUND FIFO OVERFLOW %d\n", OutputWriteOffset>>1);
|
||||||
zog++; zig++; brap++;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
NDS::ScheduleEvent(NDS::Event_SPU, true, 1024*kSamplesPerRun, Mix, kSamplesPerRun);
|
NDS::ScheduleEvent(NDS::Event_SPU, true, 1024*kSamplesPerRun, Mix, kSamplesPerRun);
|
||||||
|
@ -744,13 +742,40 @@ void Mix(u32 samples)
|
||||||
|
|
||||||
int GetOutputSize()
|
int GetOutputSize()
|
||||||
{
|
{
|
||||||
return zog; // derp
|
int ret;
|
||||||
|
if (OutputWriteOffset >= OutputReadOffset)
|
||||||
|
ret = OutputWriteOffset - OutputReadOffset;
|
||||||
|
else
|
||||||
|
ret = (OutputBufferSize*2) - OutputReadOffset + OutputWriteOffset;
|
||||||
|
|
||||||
|
ret >>= 1;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Sync(bool wait)
|
||||||
|
{
|
||||||
|
// sync to audio output in case the core is running too fast
|
||||||
|
// * wait=true: wait until enough audio data has been played
|
||||||
|
// * wait=false: merely skip some audio data to avoid a FIFO overflow
|
||||||
|
|
||||||
|
const int halflimit = (OutputBufferSize / 2);
|
||||||
|
|
||||||
|
if (wait)
|
||||||
|
{
|
||||||
|
// TODO: less CPU-intensive wait?
|
||||||
|
while (GetOutputSize() > halflimit);
|
||||||
|
}
|
||||||
|
else if (GetOutputSize() > halflimit)
|
||||||
|
{
|
||||||
|
int readpos = OutputWriteOffset - (halflimit*2);
|
||||||
|
if (readpos < 0) readpos += (OutputBufferSize*2);
|
||||||
|
|
||||||
|
OutputReadOffset = readpos;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int ReadOutput(s16* data, int samples)
|
int ReadOutput(s16* data, int samples)
|
||||||
{
|
{
|
||||||
printf("ReadOutput(%d): wrote=%d level=%d ReadOffset=%d WriteOffset=%d\n", samples, zog, zig, OutputReadOffset>>1, OutputWriteOffset>>1);
|
|
||||||
zog = 0; zig -= (zig<samples ? zig : samples);
|
|
||||||
if (OutputReadOffset == OutputWriteOffset)
|
if (OutputReadOffset == OutputWriteOffset)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
|
|
@ -36,6 +36,7 @@ void SetBias(u16 bias);
|
||||||
void Mix(u32 samples);
|
void Mix(u32 samples);
|
||||||
|
|
||||||
int GetOutputSize();
|
int GetOutputSize();
|
||||||
|
void Sync(bool wait);
|
||||||
int ReadOutput(s16* data, int samples);
|
int ReadOutput(s16* data, int samples);
|
||||||
|
|
||||||
u8 Read8(u32 addr);
|
u8 Read8(u32 addr);
|
||||||
|
|
|
@ -158,6 +158,7 @@ bool LidStatus;
|
||||||
int JoystickID;
|
int JoystickID;
|
||||||
SDL_Joystick* Joystick;
|
SDL_Joystick* Joystick;
|
||||||
|
|
||||||
|
int AudioFreq;
|
||||||
SDL_AudioDeviceID AudioDevice, MicDevice;
|
SDL_AudioDeviceID AudioDevice, MicDevice;
|
||||||
|
|
||||||
u32 MicBufferLength = 2048;
|
u32 MicBufferLength = 2048;
|
||||||
|
@ -560,26 +561,28 @@ void MicLoadWav(char* name)
|
||||||
|
|
||||||
void AudioCallback(void* data, Uint8* stream, int len)
|
void AudioCallback(void* data, Uint8* stream, int len)
|
||||||
{
|
{
|
||||||
// resampling:
|
len /= (sizeof(s16) * 2);
|
||||||
// buffer length is 1024 samples
|
|
||||||
// which is 710 samples at the original sample rate
|
|
||||||
|
|
||||||
s16 buf_in[710*2];
|
// resample incoming audio to match the output sample rate
|
||||||
|
|
||||||
|
int len_in = (int)ceil((len * 32823.6328125) / (float)AudioFreq);
|
||||||
|
|
||||||
|
s16 buf_in[1024*2];
|
||||||
s16* buf_out = (s16*)stream;
|
s16* buf_out = (s16*)stream;
|
||||||
|
|
||||||
int num_in = SPU::ReadOutput(buf_in, 710);
|
int num_in = SPU::ReadOutput(buf_in, len_in);
|
||||||
int num_out = 1024;
|
int num_out = len;
|
||||||
printf("took %d/%d samples\n", num_in, 710);
|
|
||||||
int margin = 6;
|
int margin = 6;
|
||||||
if (num_in < 710-margin)
|
if (num_in < len_in-margin)
|
||||||
{
|
{
|
||||||
int last = num_in-1;
|
int last = num_in-1;
|
||||||
if (last < 0) last = 0;
|
if (last < 0) last = 0;
|
||||||
|
|
||||||
for (int i = num_in; i < 710-margin; i++)
|
for (int i = num_in; i < len_in-margin; i++)
|
||||||
((u32*)buf_in)[i] = ((u32*)buf_in)[last];
|
((u32*)buf_in)[i] = ((u32*)buf_in)[last];
|
||||||
|
|
||||||
num_in = 710-margin;
|
num_in = len_in-margin;
|
||||||
}
|
}
|
||||||
|
|
||||||
float res_incr = num_in / (float)num_out;
|
float res_incr = num_in / (float)num_out;
|
||||||
|
@ -590,10 +593,20 @@ printf("took %d/%d samples\n", num_in, 710);
|
||||||
|
|
||||||
for (int i = 0; i < 1024; i++)
|
for (int i = 0; i < 1024; i++)
|
||||||
{
|
{
|
||||||
// TODO: interp!!
|
|
||||||
buf_out[i*2 ] = (buf_in[res_pos*2 ] * volume) >> 8;
|
buf_out[i*2 ] = (buf_in[res_pos*2 ] * volume) >> 8;
|
||||||
buf_out[i*2+1] = (buf_in[res_pos*2+1] * volume) >> 8;
|
buf_out[i*2+1] = (buf_in[res_pos*2+1] * volume) >> 8;
|
||||||
|
|
||||||
|
/*s16 s_l = buf_in[res_pos*2 ];
|
||||||
|
s16 s_r = buf_in[res_pos*2+1];
|
||||||
|
|
||||||
|
float a = res_timer;
|
||||||
|
float b = 1.0 - a;
|
||||||
|
s_l = (s_l * a) + (buf_in[(res_pos-1)*2 ] * b);
|
||||||
|
s_r = (s_r * a) + (buf_in[(res_pos-1)*2+1] * b);
|
||||||
|
|
||||||
|
buf_out[i*2 ] = (s_l * volume) >> 8;
|
||||||
|
buf_out[i*2+1] = (s_r * volume) >> 8;*/
|
||||||
|
|
||||||
res_timer += res_incr;
|
res_timer += res_incr;
|
||||||
while (res_timer >= 1.0)
|
while (res_timer >= 1.0)
|
||||||
{
|
{
|
||||||
|
@ -838,6 +851,7 @@ bool JoyButtonHeld(int btnid, int njoybuttons, Uint8* joybuttons, Uint32 hat)
|
||||||
|
|
||||||
void UpdateWindowTitle(void* data)
|
void UpdateWindowTitle(void* data)
|
||||||
{
|
{
|
||||||
|
if (EmuStatus == 0) return;
|
||||||
uiWindowSetTitle(MainWindow, (const char*)data);
|
uiWindowSetTitle(MainWindow, (const char*)data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -880,6 +894,10 @@ int EmuThreadFunc(void* burp)
|
||||||
u32 lasttick = starttick;
|
u32 lasttick = starttick;
|
||||||
u32 lastmeasuretick = lasttick;
|
u32 lastmeasuretick = lasttick;
|
||||||
u32 fpslimitcount = 0;
|
u32 fpslimitcount = 0;
|
||||||
|
u64 perfcount = SDL_GetPerformanceCounter();
|
||||||
|
u64 perffreq = SDL_GetPerformanceFrequency();
|
||||||
|
float samplesleft = 0;
|
||||||
|
u32 nsamples = 0;
|
||||||
char melontitle[100];
|
char melontitle[100];
|
||||||
|
|
||||||
while (EmuRunning != 0)
|
while (EmuRunning != 0)
|
||||||
|
@ -959,43 +977,30 @@ int EmuThreadFunc(void* burp)
|
||||||
}
|
}
|
||||||
uiAreaQueueRedrawAll(MainDrawArea);
|
uiAreaQueueRedrawAll(MainDrawArea);
|
||||||
|
|
||||||
// framerate limiter based off SDL2_gfx
|
bool limitfps = Config::LimitFPS && !HotkeyDown(HK_FastForward);
|
||||||
|
SPU::Sync(limitfps);
|
||||||
|
|
||||||
float framerate = (1000.0f * nlines) / (60.0f * 263.0f);
|
float framerate = (1000.0f * nlines) / (60.0f * 263.0f);
|
||||||
|
|
||||||
/*fpslimitcount++;
|
|
||||||
u32 curtick = SDL_GetTicks();
|
|
||||||
u32 delay = curtick - lasttick;
|
|
||||||
lasttick = curtick;
|
|
||||||
|
|
||||||
bool limitfps = Config::LimitFPS && !HotkeyDown(HK_FastForward);
|
|
||||||
|
|
||||||
u32 wantedtick = starttick + (u32)((float)fpslimitcount * framerate);
|
|
||||||
if (curtick < wantedtick && limitfps)
|
|
||||||
{
|
|
||||||
SDL_Delay(wantedtick - curtick);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
fpslimitcount = 0;
|
|
||||||
starttick = curtick;
|
|
||||||
}*/
|
|
||||||
|
|
||||||
fpslimitcount++;
|
|
||||||
if (fpslimitcount >= 3)
|
|
||||||
{
|
{
|
||||||
u32 curtick = SDL_GetTicks();
|
u32 curtick = SDL_GetTicks();
|
||||||
u32 delay = curtick - lasttick;
|
u32 delay = curtick - lasttick;
|
||||||
|
|
||||||
bool limitfps = Config::LimitFPS && !HotkeyDown(HK_FastForward);
|
if (limitfps)
|
||||||
|
|
||||||
u32 wantedtick = lasttick + (u32)((float)fpslimitcount * framerate);
|
|
||||||
if (curtick < wantedtick && limitfps)
|
|
||||||
{
|
{
|
||||||
SDL_Delay(wantedtick - curtick);
|
float wantedtickF = starttick + (framerate * (fpslimitcount+1));
|
||||||
}
|
u32 wantedtick = (u32)ceil(wantedtickF);
|
||||||
|
if (curtick < wantedtick) SDL_Delay(wantedtick - curtick);
|
||||||
|
|
||||||
lasttick = SDL_GetTicks();
|
lasttick = SDL_GetTicks();
|
||||||
fpslimitcount = 0;
|
fpslimitcount++;
|
||||||
|
if ((abs(wantedtickF - (float)wantedtick) < 0.001312) || (fpslimitcount > 60))
|
||||||
|
{
|
||||||
|
fpslimitcount = 0;
|
||||||
|
nsamples = 0;
|
||||||
|
starttick = lasttick;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
nframes++;
|
nframes++;
|
||||||
|
@ -2649,20 +2654,23 @@ int main(int argc, char** argv)
|
||||||
uiMenuItemSetChecked(MenuItem_LimitFPS, Config::LimitFPS==1);
|
uiMenuItemSetChecked(MenuItem_LimitFPS, Config::LimitFPS==1);
|
||||||
uiMenuItemSetChecked(MenuItem_ShowOSD, Config::ShowOSD==1);
|
uiMenuItemSetChecked(MenuItem_ShowOSD, Config::ShowOSD==1);
|
||||||
|
|
||||||
|
AudioFreq = 48000; // TODO: make configurable?
|
||||||
SDL_AudioSpec whatIwant, whatIget;
|
SDL_AudioSpec whatIwant, whatIget;
|
||||||
memset(&whatIwant, 0, sizeof(SDL_AudioSpec));
|
memset(&whatIwant, 0, sizeof(SDL_AudioSpec));
|
||||||
whatIwant.freq = 47340;
|
whatIwant.freq = AudioFreq;
|
||||||
whatIwant.format = AUDIO_S16LSB;
|
whatIwant.format = AUDIO_S16LSB;
|
||||||
whatIwant.channels = 2;
|
whatIwant.channels = 2;
|
||||||
whatIwant.samples = 1024;
|
whatIwant.samples = 1024;
|
||||||
whatIwant.callback = AudioCallback;
|
whatIwant.callback = AudioCallback;
|
||||||
AudioDevice = SDL_OpenAudioDevice(NULL, 0, &whatIwant, &whatIget, 0);
|
AudioDevice = SDL_OpenAudioDevice(NULL, 0, &whatIwant, &whatIget, SDL_AUDIO_ALLOW_FREQUENCY_CHANGE);
|
||||||
if (!AudioDevice)
|
if (!AudioDevice)
|
||||||
{
|
{
|
||||||
printf("Audio init failed: %s\n", SDL_GetError());
|
printf("Audio init failed: %s\n", SDL_GetError());
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
AudioFreq = whatIget.freq;
|
||||||
|
printf("Audio output frequency: %d Hz\n", AudioFreq);
|
||||||
SDL_PauseAudioDevice(AudioDevice, 1);
|
SDL_PauseAudioDevice(AudioDevice, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue