libretro: play speaker audio (with some glitches).
Signed-off-by: Andrea Odetti <mariofutire@gmail.com>
This commit is contained in:
parent
9b90335c63
commit
1a5ec9ec0f
5 changed files with 196 additions and 5 deletions
3
linux.md
3
linux.md
|
@ -23,6 +23,7 @@ There are 4 projects
|
|||
* applen: a frontend based on ncurses
|
||||
* qapple: Qt frontend
|
||||
* sa2: SDL2 frontend
|
||||
* libra2: a libretro core
|
||||
|
||||
The libapple interface is a *link time* interface: some functions are not defined and must be provided in order to properly link
|
||||
the application. These functions are listed in [interface.h](source/linux/interface.h).
|
||||
|
@ -107,6 +108,8 @@ 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.
|
||||
|
||||
Must be manually configured:
|
||||
``cmake -DLIBRETRO_PATH=/path/to/libretro-common``
|
||||
|
||||
|
|
|
@ -15,6 +15,7 @@ if (EXISTS ${LIBRETRO_PATH}/include/libretro.h)
|
|||
joypadbase.cpp
|
||||
joypad.cpp
|
||||
analog.cpp
|
||||
rdirectsound.cpp
|
||||
)
|
||||
|
||||
target_include_directories(ra2 PRIVATE
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
|
||||
#include "StdAfx.h"
|
||||
#include "Frame.h"
|
||||
#include "Common.h"
|
||||
|
||||
#include "linux/version.h"
|
||||
#include "linux/paddle.h"
|
||||
|
@ -12,6 +13,7 @@
|
|||
#include "frontends/libretro/environment.h"
|
||||
#include "frontends/libretro/joypad.h"
|
||||
#include "frontends/libretro/analog.h"
|
||||
#include "frontends/libretro/rdirectsound.h"
|
||||
|
||||
namespace
|
||||
{
|
||||
|
@ -90,7 +92,7 @@ void retro_get_system_av_info(retro_system_av_info *info)
|
|||
info->geometry.aspect_ratio = 0;
|
||||
|
||||
info->timing.fps = 60;
|
||||
info->timing.sample_rate = 0;
|
||||
info->timing.sample_rate = SPKR_SAMPLE_RATE;
|
||||
}
|
||||
|
||||
void retro_set_environment(retro_environment_t cb)
|
||||
|
@ -117,8 +119,11 @@ void retro_set_environment(retro_environment_t cb)
|
|||
|
||||
cb(RETRO_ENVIRONMENT_SET_CONTROLLER_INFO, (void*)ports);
|
||||
|
||||
retro_keyboard_callback callback = {&Game::keyboardCallback};
|
||||
cb(RETRO_ENVIRONMENT_SET_KEYBOARD_CALLBACK, &callback);
|
||||
retro_keyboard_callback keyboardCallback = {&Game::keyboardCallback};
|
||||
cb(RETRO_ENVIRONMENT_SET_KEYBOARD_CALLBACK, &keyboardCallback);
|
||||
|
||||
retro_audio_buffer_status_callback audioCallback = {&RDirectSound::bufferStatusCallback};
|
||||
cb(RETRO_ENVIRONMENT_SET_AUDIO_BUFFER_STATUS_CALLBACK, &audioCallback);
|
||||
}
|
||||
|
||||
void retro_set_audio_sample(retro_audio_sample_t cb)
|
||||
|
@ -156,6 +161,8 @@ void retro_run(void)
|
|||
game->processInputEvents();
|
||||
game->executeOneFrame();
|
||||
game->drawVideoBuffer();
|
||||
const size_t ms = (1000 + 60 - 1) / 60; // round up
|
||||
RDirectSound::writeAudio(ms);
|
||||
}
|
||||
|
||||
bool retro_load_game(const retro_game_info *info)
|
||||
|
|
|
@ -1,10 +1,181 @@
|
|||
#include <linux/interface.h>
|
||||
#include "frontends/libretro/rdirectsound.h"
|
||||
#include "frontends/libretro/environment.h"
|
||||
|
||||
#include "linux/windows/dsound.h"
|
||||
|
||||
#include <unordered_map>
|
||||
#include <memory>
|
||||
#include <iostream>
|
||||
#include <iomanip>
|
||||
#include <cmath>
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
class DirectSoundGenerator
|
||||
{
|
||||
public:
|
||||
DirectSoundGenerator(IDirectSoundBuffer * buffer);
|
||||
|
||||
void writeAudio(const size_t ms);
|
||||
void playSilence(const size_t ms);
|
||||
|
||||
private:
|
||||
IDirectSoundBuffer * myBuffer;
|
||||
|
||||
std::vector<int16_t> myMixerBuffer;
|
||||
|
||||
size_t myBytesPerSecond;
|
||||
|
||||
bool isRunning() const;
|
||||
|
||||
void mixBuffer(const void * ptr, const size_t size);
|
||||
};
|
||||
|
||||
|
||||
std::unordered_map<IDirectSoundBuffer *, std::shared_ptr<DirectSoundGenerator>> activeSoundGenerators;
|
||||
|
||||
DirectSoundGenerator::DirectSoundGenerator(IDirectSoundBuffer * buffer)
|
||||
: myBuffer(buffer)
|
||||
{
|
||||
myBytesPerSecond = myBuffer->channels * myBuffer->sampleRate * sizeof(int16_t);
|
||||
}
|
||||
|
||||
bool DirectSoundGenerator::isRunning() const
|
||||
{
|
||||
DWORD dwStatus;
|
||||
myBuffer->GetStatus(&dwStatus);
|
||||
if (!(dwStatus & DSBSTATUS_PLAYING))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
// for now, just play speaker audio
|
||||
return myBuffer->channels == 1;
|
||||
}
|
||||
}
|
||||
|
||||
void DirectSoundGenerator::mixBuffer(const void * ptr, const size_t size)
|
||||
{
|
||||
const int16_t frames = size / (sizeof(int16_t) * myBuffer->channels);
|
||||
const int16_t * data = static_cast<const int16_t *>(ptr);
|
||||
|
||||
if (myBuffer->channels == 2)
|
||||
{
|
||||
myMixerBuffer.assign(data, data + frames * myBuffer->channels);
|
||||
}
|
||||
else
|
||||
{
|
||||
myMixerBuffer.resize(2 * size);
|
||||
for (int16_t i = 0; i < frames; ++i)
|
||||
{
|
||||
myMixerBuffer[i * 2] = data[i];
|
||||
myMixerBuffer[i * 2 + 1] = data[i];
|
||||
}
|
||||
}
|
||||
|
||||
const double logVolume = myBuffer->GetLogarithmicVolume();
|
||||
// same formula as QAudio::convertVolume()
|
||||
const double linVolume = logVolume > 0.99 ? 1.0 : -std::log(1.0 - logVolume) / std::log(100.0);
|
||||
const int16_t rvolume = int16_t(linVolume * 128);
|
||||
|
||||
for (int16_t & sample : myMixerBuffer)
|
||||
{
|
||||
sample = (sample * rvolume) / 128;
|
||||
}
|
||||
|
||||
audio_batch_cb(myMixerBuffer.data(), frames);
|
||||
}
|
||||
|
||||
void DirectSoundGenerator::playSilence(const size_t ms)
|
||||
{
|
||||
if (!isRunning())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
const size_t frames = ms * myBuffer->sampleRate / 1000;
|
||||
const size_t bytesToWrite = frames * sizeof(int16_t) * 2;
|
||||
myMixerBuffer.resize(bytesToWrite);
|
||||
std::fill(myMixerBuffer.begin(), myMixerBuffer.end(), 0);
|
||||
audio_batch_cb(myMixerBuffer.data(), frames);
|
||||
}
|
||||
|
||||
void DirectSoundGenerator::writeAudio(const size_t ms)
|
||||
{
|
||||
// this is autostart as we only do for the palying buffers
|
||||
// and AW might activate one later
|
||||
if (!isRunning())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
const size_t bytesToRead = ms * myBytesPerSecond / 1000;
|
||||
|
||||
LPVOID lpvAudioPtr1, lpvAudioPtr2;
|
||||
DWORD dwAudioBytes1, dwAudioBytes2;
|
||||
myBuffer->Read(bytesToRead, &lpvAudioPtr1, &dwAudioBytes1, &lpvAudioPtr2, &dwAudioBytes2);
|
||||
|
||||
if (lpvAudioPtr1 && dwAudioBytes1)
|
||||
{
|
||||
mixBuffer(lpvAudioPtr1, dwAudioBytes1);
|
||||
}
|
||||
if (lpvAudioPtr2 && dwAudioBytes2)
|
||||
{
|
||||
mixBuffer(lpvAudioPtr2, dwAudioBytes2);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Mockingboard
|
||||
void registerSoundBuffer(IDirectSoundBuffer * buffer)
|
||||
{
|
||||
const std::shared_ptr<DirectSoundGenerator> generator = std::make_shared<DirectSoundGenerator>(buffer);
|
||||
activeSoundGenerators[buffer] = generator;
|
||||
}
|
||||
|
||||
void unregisterSoundBuffer(IDirectSoundBuffer * buffer)
|
||||
{
|
||||
const auto it = activeSoundGenerators.find(buffer);
|
||||
if (it != activeSoundGenerators.end())
|
||||
{
|
||||
activeSoundGenerators.erase(it);
|
||||
}
|
||||
}
|
||||
|
||||
namespace RDirectSound
|
||||
{
|
||||
|
||||
void writeAudio(const size_t ms)
|
||||
{
|
||||
for (auto & it : activeSoundGenerators)
|
||||
{
|
||||
const std::shared_ptr<DirectSoundGenerator> & generator = it.second;
|
||||
generator->writeAudio(ms);
|
||||
}
|
||||
}
|
||||
|
||||
void playSilence(const size_t ms)
|
||||
{
|
||||
for (auto & it : activeSoundGenerators)
|
||||
{
|
||||
const std::shared_ptr<DirectSoundGenerator> & generator = it.second;
|
||||
generator->playSilence(ms);
|
||||
}
|
||||
}
|
||||
|
||||
void bufferStatusCallback(bool active, unsigned occupancy, bool underrun_likely)
|
||||
{
|
||||
if (active)
|
||||
{
|
||||
// I am not sure this is any useful
|
||||
if (underrun_likely || occupancy < 40)
|
||||
{
|
||||
log_cb(RETRO_LOG_INFO, "RA2: %s occupancy = %d, underrun_likely = %d\n", __FUNCTION__, occupancy, underrun_likely);
|
||||
playSilence(10);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
9
source/frontends/libretro/rdirectsound.h
Normal file
9
source/frontends/libretro/rdirectsound.h
Normal file
|
@ -0,0 +1,9 @@
|
|||
#pragma once
|
||||
|
||||
#include <cstdlib>
|
||||
|
||||
namespace RDirectSound
|
||||
{
|
||||
void writeAudio(const size_t ms);
|
||||
void bufferStatusCallback(bool active, unsigned occupancy, bool underrun_likely);
|
||||
}
|
Loading…
Add table
Reference in a new issue