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:
Andrea Odetti 2020-12-26 19:28:51 +00:00
parent 1a5ec9ec0f
commit 175491f250
8 changed files with 82 additions and 47 deletions

View file

@ -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``

View file

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

View file

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

View file

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

View file

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

View file

@ -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)

View file

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

View file

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