libretro core: fix (most?) audio glitches.
It was reading a number of bytes not multiple of "2 * channels". Signed-off-by: Andrea Odetti <mariofutire@gmail.com>
This commit is contained in:
parent
1a5ec9ec0f
commit
175491f250
8 changed files with 82 additions and 47 deletions
2
linux.md
2
linux.md
|
@ -108,7 +108,7 @@ In order to have a better experience with the keyboard, one should probably enab
|
|||
|
||||
Video works, but the vertical flip is done in software.
|
||||
|
||||
Audio (speaker) works with some glitches.
|
||||
Audio (speaker) works.
|
||||
|
||||
Must be manually configured:
|
||||
``cmake -DLIBRETRO_PATH=/path/to/libretro-common``
|
||||
|
|
|
@ -16,11 +16,11 @@ void Speed::reset()
|
|||
myStartCycles = g_nCumulativeCycles;
|
||||
}
|
||||
|
||||
size_t Speed::getCyclesTillNext(const size_t milliseconds) const
|
||||
size_t Speed::getCyclesTillNext(const size_t microseconds) const
|
||||
{
|
||||
if (myFixedSpeed)
|
||||
{
|
||||
const size_t cycles = static_cast<uint64_t>(milliseconds * g_fCurrentCLK6502 * 1.0e-3);
|
||||
const size_t cycles = static_cast<uint64_t>(microseconds * g_fCurrentCLK6502 * 1.0e-6);
|
||||
return cycles;
|
||||
}
|
||||
else
|
||||
|
@ -28,11 +28,11 @@ size_t Speed::getCyclesTillNext(const size_t milliseconds) const
|
|||
const uint64_t currentCycles = g_nCumulativeCycles;
|
||||
const auto currentTime = std::chrono::steady_clock::now();
|
||||
|
||||
const auto currentDelta = std::chrono::duration_cast<std::chrono::milliseconds>(currentTime - myStartTime).count();
|
||||
const auto currentDelta = std::chrono::duration_cast<std::chrono::microseconds>(currentTime - myStartTime).count();
|
||||
// target the next time we will be called
|
||||
const auto targetDeltaInMillis = currentDelta + milliseconds;
|
||||
const auto targetDeltaInMicros = currentDelta + microseconds;
|
||||
|
||||
const uint64_t targetCycles = static_cast<uint64_t>(targetDeltaInMillis * g_fCurrentCLK6502 * 1.0e-3) + myStartCycles;
|
||||
const uint64_t targetCycles = static_cast<uint64_t>(targetDeltaInMicros * g_fCurrentCLK6502 * 1.0e-6) + myStartCycles;
|
||||
if (targetCycles > currentCycles)
|
||||
{
|
||||
// number of cycles to fill this period
|
||||
|
|
|
@ -10,8 +10,8 @@ public:
|
|||
void reset();
|
||||
|
||||
// calculate the number of cycles to execute in the current period
|
||||
// assuming the next call will happen in x milliseconds
|
||||
size_t getCyclesTillNext(const size_t milliseconds) const;
|
||||
// assuming the next call will happen in x microseconds
|
||||
size_t getCyclesTillNext(const size_t microseconds) const;
|
||||
|
||||
private:
|
||||
|
||||
|
|
|
@ -26,24 +26,6 @@
|
|||
namespace
|
||||
{
|
||||
|
||||
void runOneFrame(Speed & speed)
|
||||
{
|
||||
if (g_nAppMode == MODE_RUNNING)
|
||||
{
|
||||
const size_t cyclesToExecute = speed.getCyclesTillNext(16);
|
||||
|
||||
const bool bVideoUpdate = true;
|
||||
const UINT dwClksPerFrame = NTSC_GetCyclesPerFrame();
|
||||
|
||||
const DWORD executedCycles = CpuExecute(cyclesToExecute, bVideoUpdate);
|
||||
|
||||
g_dwCyclesThisFrame = (g_dwCyclesThisFrame + executedCycles) % dwClksPerFrame;
|
||||
GetCardMgr().GetDisk2CardMgr().UpdateDriveState(executedCycles);
|
||||
MB_PeriodicUpdate(executedCycles);
|
||||
SpkrUpdate(executedCycles);
|
||||
}
|
||||
}
|
||||
|
||||
void updateWindowTitle()
|
||||
{
|
||||
GetAppleWindowTitle();
|
||||
|
@ -88,9 +70,23 @@ Game::~Game()
|
|||
uninitialiseEmulator();
|
||||
}
|
||||
|
||||
retro_usec_t Game::ourFrameTime = 0;
|
||||
|
||||
void Game::executeOneFrame()
|
||||
{
|
||||
runOneFrame(mySpeed);
|
||||
const size_t cyclesToExecute = mySpeed.getCyclesTillNext(ourFrameTime);
|
||||
if (g_nAppMode == MODE_RUNNING)
|
||||
{
|
||||
const bool bVideoUpdate = true;
|
||||
const UINT dwClksPerFrame = NTSC_GetCyclesPerFrame();
|
||||
|
||||
const DWORD executedCycles = CpuExecute(cyclesToExecute, bVideoUpdate);
|
||||
|
||||
g_dwCyclesThisFrame = (g_dwCyclesThisFrame + executedCycles) % dwClksPerFrame;
|
||||
GetCardMgr().GetDisk2CardMgr().UpdateDriveState(executedCycles);
|
||||
MB_PeriodicUpdate(executedCycles);
|
||||
SpkrUpdate(executedCycles);
|
||||
}
|
||||
}
|
||||
|
||||
void Game::processInputEvents()
|
||||
|
@ -111,6 +107,11 @@ void Game::keyboardCallback(bool down, unsigned keycode, uint32_t character, uin
|
|||
}
|
||||
}
|
||||
|
||||
void Game::frameTimeCallback(retro_usec_t usec)
|
||||
{
|
||||
ourFrameTime = usec;
|
||||
}
|
||||
|
||||
void Game::processKeyDown(unsigned keycode, uint32_t character, uint16_t key_modifiers)
|
||||
{
|
||||
BYTE ch = 0;
|
||||
|
|
|
@ -18,7 +18,10 @@ public:
|
|||
|
||||
static void keyboardCallback(bool down, unsigned keycode, uint32_t character, uint16_t key_modifiers);
|
||||
|
||||
static void frameTimeCallback(retro_usec_t usec);
|
||||
static constexpr size_t FPS = 60;
|
||||
static unsigned input_devices[MAX_PADS];
|
||||
static retro_usec_t ourFrameTime;
|
||||
|
||||
private:
|
||||
Speed mySpeed; // fixed speed
|
||||
|
|
|
@ -91,7 +91,7 @@ void retro_get_system_av_info(retro_system_av_info *info)
|
|||
info->geometry.max_height = GetFrameBufferBorderlessHeight();
|
||||
info->geometry.aspect_ratio = 0;
|
||||
|
||||
info->timing.fps = 60;
|
||||
info->timing.fps = Game::FPS;
|
||||
info->timing.sample_rate = SPKR_SAMPLE_RATE;
|
||||
}
|
||||
|
||||
|
@ -124,6 +124,9 @@ void retro_set_environment(retro_environment_t cb)
|
|||
|
||||
retro_audio_buffer_status_callback audioCallback = {&RDirectSound::bufferStatusCallback};
|
||||
cb(RETRO_ENVIRONMENT_SET_AUDIO_BUFFER_STATUS_CALLBACK, &audioCallback);
|
||||
|
||||
retro_frame_time_callback timeCallback = {&Game::frameTimeCallback, 1000000 / Game::FPS};
|
||||
cb(RETRO_ENVIRONMENT_SET_FRAME_TIME_CALLBACK, &timeCallback);
|
||||
}
|
||||
|
||||
void retro_set_audio_sample(retro_audio_sample_t cb)
|
||||
|
|
|
@ -12,6 +12,10 @@
|
|||
namespace
|
||||
{
|
||||
|
||||
// we can only run 1 generator at a time
|
||||
// 1 is for speaker (2 would be Mockingboard)
|
||||
const size_t ourChannels = 1;
|
||||
|
||||
class DirectSoundGenerator
|
||||
{
|
||||
public:
|
||||
|
@ -20,6 +24,9 @@ namespace
|
|||
void writeAudio(const size_t ms);
|
||||
void playSilence(const size_t ms);
|
||||
|
||||
bool isRunning() const;
|
||||
size_t getNumberOfChannels() const;
|
||||
|
||||
private:
|
||||
IDirectSoundBuffer * myBuffer;
|
||||
|
||||
|
@ -27,14 +34,24 @@ namespace
|
|||
|
||||
size_t myBytesPerSecond;
|
||||
|
||||
bool isRunning() const;
|
||||
|
||||
void mixBuffer(const void * ptr, const size_t size);
|
||||
};
|
||||
|
||||
|
||||
std::unordered_map<IDirectSoundBuffer *, std::shared_ptr<DirectSoundGenerator>> activeSoundGenerators;
|
||||
|
||||
std::shared_ptr<DirectSoundGenerator> findRunningGenerator(const size_t channels)
|
||||
{
|
||||
for (auto & it : activeSoundGenerators)
|
||||
{
|
||||
const std::shared_ptr<DirectSoundGenerator> & generator = it.second;
|
||||
if (generator->isRunning() && generator->getNumberOfChannels() == channels)
|
||||
{
|
||||
return generator;
|
||||
}
|
||||
}
|
||||
return std::shared_ptr<DirectSoundGenerator>();
|
||||
}
|
||||
|
||||
DirectSoundGenerator::DirectSoundGenerator(IDirectSoundBuffer * buffer)
|
||||
: myBuffer(buffer)
|
||||
{
|
||||
|
@ -45,17 +62,21 @@ namespace
|
|||
{
|
||||
DWORD dwStatus;
|
||||
myBuffer->GetStatus(&dwStatus);
|
||||
if (!(dwStatus & DSBSTATUS_PLAYING))
|
||||
if (dwStatus & DSBSTATUS_PLAYING)
|
||||
{
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
// for now, just play speaker audio
|
||||
return myBuffer->channels == 1;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
size_t DirectSoundGenerator::getNumberOfChannels() const
|
||||
{
|
||||
return myBuffer->channels;
|
||||
}
|
||||
|
||||
void DirectSoundGenerator::mixBuffer(const void * ptr, const size_t size)
|
||||
{
|
||||
const int16_t frames = size / (sizeof(int16_t) * myBuffer->channels);
|
||||
|
@ -67,7 +88,7 @@ namespace
|
|||
}
|
||||
else
|
||||
{
|
||||
myMixerBuffer.resize(2 * size);
|
||||
myMixerBuffer.resize(2 * frames);
|
||||
for (int16_t i = 0; i < frames; ++i)
|
||||
{
|
||||
myMixerBuffer[i * 2] = data[i];
|
||||
|
@ -96,8 +117,7 @@ namespace
|
|||
}
|
||||
|
||||
const size_t frames = ms * myBuffer->sampleRate / 1000;
|
||||
const size_t bytesToWrite = frames * sizeof(int16_t) * 2;
|
||||
myMixerBuffer.resize(bytesToWrite);
|
||||
myMixerBuffer.resize(2 * frames);
|
||||
std::fill(myMixerBuffer.begin(), myMixerBuffer.end(), 0);
|
||||
audio_batch_cb(myMixerBuffer.data(), frames);
|
||||
}
|
||||
|
@ -111,7 +131,8 @@ namespace
|
|||
return;
|
||||
}
|
||||
|
||||
const size_t bytesToRead = ms * myBytesPerSecond / 1000;
|
||||
const size_t frames = ms * myBuffer->sampleRate / 1000;
|
||||
const size_t bytesToRead = frames * myBuffer->channels * sizeof(int16_t);
|
||||
|
||||
LPVOID lpvAudioPtr1, lpvAudioPtr2;
|
||||
DWORD dwAudioBytes1, dwAudioBytes2;
|
||||
|
@ -149,18 +170,18 @@ namespace RDirectSound
|
|||
|
||||
void writeAudio(const size_t ms)
|
||||
{
|
||||
for (auto & it : activeSoundGenerators)
|
||||
const auto generator = findRunningGenerator(ourChannels);
|
||||
if (generator)
|
||||
{
|
||||
const std::shared_ptr<DirectSoundGenerator> & generator = it.second;
|
||||
generator->writeAudio(ms);
|
||||
}
|
||||
}
|
||||
|
||||
void playSilence(const size_t ms)
|
||||
{
|
||||
for (auto & it : activeSoundGenerators)
|
||||
const auto generator = findRunningGenerator(ourChannels);
|
||||
if (generator)
|
||||
{
|
||||
const std::shared_ptr<DirectSoundGenerator> & generator = it.second;
|
||||
generator->playSilence(ms);
|
||||
}
|
||||
}
|
||||
|
@ -170,10 +191,17 @@ namespace RDirectSound
|
|||
if (active)
|
||||
{
|
||||
// I am not sure this is any useful
|
||||
if (underrun_likely || occupancy < 40)
|
||||
if (underrun_likely)
|
||||
{
|
||||
log_cb(RETRO_LOG_INFO, "RA2: %s occupancy = %d, underrun_likely = %d\n", __FUNCTION__, occupancy, underrun_likely);
|
||||
playSilence(10);
|
||||
if (occupancy < 20)
|
||||
{
|
||||
log_cb(RETRO_LOG_INFO, "RA2: %s occupancy = %d, underrun_likely = %d\n", __FUNCTION__, occupancy, underrun_likely);
|
||||
playSilence(10);
|
||||
}
|
||||
if (occupancy > 80)
|
||||
{
|
||||
log_cb(RETRO_LOG_INFO, "RA2: %s occupancy = %d, underrun_likely = %d\n", __FUNCTION__, occupancy, underrun_likely);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -168,7 +168,7 @@ void Emulator::execute(const size_t next)
|
|||
{
|
||||
if (g_nAppMode == MODE_RUNNING)
|
||||
{
|
||||
const size_t cyclesToExecute = mySpeed.getCyclesTillNext(next);
|
||||
const size_t cyclesToExecute = mySpeed.getCyclesTillNext(next * 1000);
|
||||
|
||||
const bool bVideoUpdate = true;
|
||||
const UINT dwClksPerFrame = NTSC_GetCyclesPerFrame();
|
||||
|
|
Loading…
Add table
Reference in a new issue