Linux: SDL video/audio + linux key manager

This commit is contained in:
Souryo 2016-12-11 16:39:11 -05:00
parent efca5284b1
commit 9ca1bf263c
9 changed files with 722 additions and 12 deletions

View file

@ -20,7 +20,7 @@
#include "../Utilities/UTF8Util.h"
#ifdef __GNUC__
#define __forceinline inline
#define __forceinline
#endif
using std::vector;

View file

@ -9,7 +9,6 @@ using System.Net;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Automation;
using System.Windows.Forms;
using System.Xml;
using Mesen.GUI.Config;
@ -699,7 +698,6 @@ namespace Mesen.GUI.Forms
bool IMessageFilter.PreFilterMessage(ref Message m)
{
if(m.Msg == WM_KEYUP) {
int virtualKeyCode = (Int32)m.WParam;
int scanCode = (Int32)(((Int64)m.LParam & 0x1FF0000) >> 16);
InteropEmu.SetKeyState(scanCode, false);
}
@ -710,7 +708,6 @@ namespace Mesen.GUI.Forms
protected override bool ProcessCmdKey(ref Message msg, Keys keyData)
{
if(msg.Msg == WM_KEYDOWN) {
int virtualKeyCode = (Int32)((Int64)msg.WParam & 0xFF);
int scanCode = (Int32)(((Int64)msg.LParam & 0x1FF0000) >> 16);
InteropEmu.SetKeyState(scanCode, true);
}
@ -1346,10 +1343,7 @@ namespace Mesen.GUI.Forms
string[] files = (string[])e.Data.GetData(DataFormats.FileDrop);
if(File.Exists(files[0])) {
LoadFile(files[0]);
AutomationElement element = AutomationElement.FromHandle(System.Diagnostics.Process.GetCurrentProcess().MainWindowHandle);
if(element != null) {
element.SetFocus();
}
this.Activate();
}
}

View file

@ -23,9 +23,9 @@
#include "../Windows/SoundManager.h"
#include "../Windows/WindowsKeyManager.h"
#else
#include "../SDL/SdlRenderer.h"
#include "../SDL/SdlSoundManager.h"
#include "../SDL/SdlKeyManager.h"
#include "../Linux/SdlRenderer.h"
#include "../Linux/SdlSoundManager.h"
#include "../Linux/LinuxKeyManager.h"
#endif
IRenderingDevice *_renderer = nullptr;
@ -98,7 +98,7 @@ namespace InteropEmu {
#ifdef _WIN32
_keyManager = new WindowsKeyManager((HWND)_windowHandle);
#else
_keyManager = new SdlKeyManager();
_keyManager = new LinuxKeyManager();
#endif
ControlManager::RegisterKeyManager(_keyManager);

304
Linux/LinuxKeyManager.cpp Executable file
View file

@ -0,0 +1,304 @@
#include "LinuxKeyManager.h"
#include "../Core/ControlManager.h"
static vector<KeyDefinition> _keyDefinitions = {
{ "", 9, "Escape", "" },
{ "", 10, "1", "" },
{ "", 11, "2", "" },
{ "", 12, "3", "" },
{ "", 13, "4", "" },
{ "", 14, "5", "" },
{ "", 15, "6", "" },
{ "", 16, "7", "" },
{ "", 17, "8", "" },
{ "", 18, "9", "" },
{ "", 19, "0", "" },
{ "", 20, "Minus", "" },
{ "", 21, "Equal", "" },
{ "", 22, "BackSpace", "" },
{ "", 23, "Tab", "" },
{ "", 24, "Q", "" },
{ "", 25, "W", "" },
{ "", 26, "E", "" },
{ "", 27, "R", "" },
{ "", 28, "T", "" },
{ "", 29, "Y", "" },
{ "", 30, "U", "" },
{ "", 31, "I", "" },
{ "", 32, "O", "" },
{ "", 33, "P", "" },
{ "", 34, "Left Bracket", "" },
{ "", 35, "Right Bracket", "" },
{ "", 36, "Return", "" },
{ "", 37, "Left Control", "" },
{ "", 38, "A", "" },
{ "", 39, "S", "" },
{ "", 40, "D", "" },
{ "", 41, "F", "" },
{ "", 42, "G", "" },
{ "", 43, "H", "" },
{ "", 44, "J", "" },
{ "", 45, "K", "" },
{ "", 46, "L", "" },
{ "", 47, "Semicolor", "" },
{ "", 48, "Apostrophe", "" },
{ "", 49, "Grave", "" },
{ "", 50, "Left Shift", "" },
{ "", 51, "\\", "" },
{ "", 52, "Z", "" },
{ "", 53, "X", "" },
{ "", 54, "C", "" },
{ "", 55, "V", "" },
{ "", 56, "B", "" },
{ "", 57, "N", "" },
{ "", 58, "M", "" },
{ "", 59, ",", "" },
{ "", 60, ".", "" },
{ "", 61, "/", "" },
{ "", 62, "Right Shift", "" },
{ "", 63, "KP Multiply", "" },
{ "", 64, "Left Alt", "" },
{ "", 65, "Space", "" },
{ "", 66, "Caps Lock", "" },
{ "", 67, "F1", "" },
{ "", 68, "F2", "" },
{ "", 69, "F3", "" },
{ "", 70, "F4", "" },
{ "", 71, "F5", "" },
{ "", 72, "F6", "" },
{ "", 73, "F7", "" },
{ "", 74, "F8", "" },
{ "", 75, "F9", "" },
{ "", 76, "F10", "" },
{ "", 77, "Num Lock", "" },
{ "", 78, "Scroll Lock", "" },
{ "", 79, "KP Home", "" },
{ "", 80, "KP Up", "" },
{ "", 81, "KP Prior", "" },
{ "", 82, "KP Subtract", "" },
{ "", 83, "KP Left", "" },
{ "", 84, "KP Begin", "" },
{ "", 85, "KP Right", "" },
{ "", 86, "KP Add", "" },
{ "", 87, "KP End", "" },
{ "", 88, "KP Down", "" },
{ "", 89, "KP Next", "" },
{ "", 90, "KP Insert", "" },
{ "", 91, "KP Delete", "" },
{ "", 92, "ISO_Level3_Shift", "" },
{ "", 94, "Less", "" },
{ "", 95, "F11", "" },
{ "", 96, "F12", "" },
{ "", 98, "Katakana", "" },
{ "", 99, "Hiragana", "" },
{ "", 100, "Henkan_Mode", "" },
{ "", 101, "Hiragana_Katakana", "" },
{ "", 102, "Muhenkan", "" },
{ "", 104, "KP_Enter", "" },
{ "", 105, "Control_R", "" },
{ "", 106, "KP_Divide", "" },
{ "", 107, "Print", "" },
{ "", 108, "Right Alt", "" },
{ "", 109, "Linefeed", "" },
{ "", 110, "Home", "" },
{ "", 111, "Up Arrow", "" },
{ "", 112, "Prior", "" },
{ "", 113, "Left Arrow", "" },
{ "", 114, "Right Arrow", "" },
{ "", 115, "End", "" },
{ "", 116, "Down Arrow", "" },
{ "", 117, "Next", "" },
{ "", 118, "Insert", "" },
{ "", 119, "Delete", "" },
{ "", 121, "XF86AudioMute", "" },
{ "", 122, "XF86AudioLowerVolume", "" },
{ "", 123, "XF86AudioRaiseVolume", "" },
{ "", 124, "XF86PowerOff", "" },
{ "", 125, "KP_Equal", "" },
{ "", 126, "PlusMinus", "" },
{ "", 127, "Pause", "" },
{ "", 128, "XF86LaunchA", "" },
{ "", 129, "KP_Decimal", "" },
{ "", 130, "Hangul", "" },
{ "", 131, "Hangul_Hanja", "" },
{ "", 133, "Super_L", "" },
{ "", 134, "Super_R", "" },
{ "", 135, "Menu", "" },
{ "", 136, "Cancel", "" },
{ "", 137, "Redo", "" },
{ "", 138, "SunProps", "" },
{ "", 139, "Undo", "" },
{ "", 140, "SunFront", "" },
{ "", 141, "XF86Copy", "" },
{ "", 142, "XF86Open", "" },
{ "", 143, "XF86Paste", "" },
{ "", 144, "Find", "" },
{ "", 145, "XF86Cut", "" },
{ "", 146, "Help", "" },
{ "", 147, "XF86MenuKB", "" },
{ "", 148, "XF86Calculator", "" },
{ "", 150, "XF86Sleep", "" },
{ "", 151, "XF86WakeUp", "" },
{ "", 152, "XF86Explorer", "" },
{ "", 153, "XF86Send", "" },
{ "", 155, "XF86Xfer", "" },
{ "", 156, "XF86Launch1", "" },
{ "", 157, "XF86Launch2", "" },
{ "", 158, "XF86WWW", "" },
{ "", 159, "XF86DOS", "" },
{ "", 160, "XF86ScreenSaver", "" },
{ "", 161, "XF86RotateWindows", "" },
{ "", 162, "XF86TaskPane", "" },
{ "", 163, "XF86Mail", "" },
{ "", 164, "XF86Favorites", "" },
{ "", 165, "XF86MyComputer", "" },
{ "", 166, "XF86Back", "" },
{ "", 167, "XF86Forward", "" },
{ "", 169, "XF86Eject", "" },
{ "", 170, "XF86Eject", "" },
{ "", 171, "XF86AudioNext", "" },
{ "", 172, "XF86AudioPlay", "" },
{ "", 173, "XF86AudioPrev", "" },
{ "", 174, "XF86AudioStop", "" },
{ "", 175, "XF86AudioRecord", "" },
{ "", 176, "XF86AudioRewind", "" },
{ "", 177, "XF86Phone", "" },
{ "", 179, "XF86Tools", "" },
{ "", 180, "XF86HomePage", "" },
{ "", 181, "XF86Reload", "" },
{ "", 182, "XF86Close", "" },
{ "", 185, "XF86ScrollUp", "" },
{ "", 186, "XF86ScrollDown", "" },
{ "", 187, "Paren Left", "" },
{ "", 188, "Paren Right", "" },
{ "", 189, "XF86New", "" },
{ "", 190, "Redo", "" },
{ "", 191, "XF86Tools", "" },
{ "", 192, "XF86Launch5", "" },
{ "", 193, "XF86Launch6", "" },
{ "", 194, "XF86Launch7", "" },
{ "", 195, "XF86Launch8", "" },
{ "", 196, "XF86Launch9", "" },
{ "", 198, "XF86AudioMicMute", "" },
{ "", 199, "XF86TouchpadToggle", "" },
{ "", 200, "XF86TouchpadOn", "" },
{ "", 201, "XF86TouchpadOff", "" },
{ "", 203, "Mode_switch", "" },
{ "", 204, "NoSymbol", "" },
{ "", 205, "NoSymbol", "" },
{ "", 206, "NoSymbol", "" },
{ "", 207, "NoSymbol", "" },
{ "", 208, "XF86AudioPlay", "" },
{ "", 209, "XF86AudioPause", "" },
{ "", 210, "XF86Launch3", "" },
{ "", 211, "XF86Launch4", "" },
{ "", 212, "XF86LaunchB", "" },
{ "", 213, "XF86Suspend", "" },
{ "", 214, "XF86Close", "" },
{ "", 215, "XF86AudioPlay", "" },
{ "", 216, "XF86AudioForward", "" },
{ "", 218, "Print", "" },
{ "", 220, "XF86WebCam", "" },
{ "", 223, "XF86Mail", "" },
{ "", 224, "XF86Messenger", "" },
{ "", 225, "XF86Search", "" },
{ "", 226, "XF86Go", "" },
{ "", 227, "XF86Finance", "" },
{ "", 228, "XF86Game", "" },
{ "", 229, "XF86Shop", "" },
{ "", 231, "Cancel", "" },
{ "", 232, "XF86MonBrightnessDown", "" },
{ "", 233, "XF86MonBrightnessUp", "" },
{ "", 234, "XF86AudioMedia", "" },
{ "", 235, "XF86Display", "" },
{ "", 236, "XF86KbdLightOnOff", "" },
{ "", 237, "XF86KbdBrightnessDown", "" },
{ "", 238, "XF86KbdBrightnessUp", "" },
{ "", 239, "XF86Send", "" },
{ "", 240, "XF86Reply", "" },
{ "", 241, "XF86MailForward", "" },
{ "", 242, "XF86Save", "" },
{ "", 243, "XF86Documents", "" },
{ "", 244, "XF86Battery", "" },
{ "", 245, "XF86Bluetooth", "" },
{ "", 246, "XF86WLAN", "" },
};
LinuxKeyManager::LinuxKeyManager()
{
_keyState.insert(_keyState.end(), 0x200, 0);
for(KeyDefinition &keyDef : _keyDefinitions) {
_keyNames[keyDef.keyCode] = keyDef.description;
_keyCodes[keyDef.description] = keyDef.keyCode;
}
}
LinuxKeyManager::~LinuxKeyManager()
{
}
void LinuxKeyManager::RefreshState()
{
//TODO: NOT IMPLEMENTED YET;
//Only needed to detect poll controller input
}
bool LinuxKeyManager::IsKeyPressed(uint32_t key)
{
if(key < 0x200) {
return _keyState[key & 0xFF] != 0;
}
return false;
}
bool LinuxKeyManager::IsMouseButtonPressed(MouseButton button)
{
//TODO: NOT IMPLEMENTED YET
//Only needed for zapper/etc
return false;
}
uint32_t LinuxKeyManager::GetPressedKey()
{
for(int i = 0; i < 0x200; i++) {
if(_keyState[i]) {
return i;
}
}
return 0;
}
string LinuxKeyManager::GetKeyName(uint32_t key)
{
auto keyDef = _keyNames.find(key & 0xFF);
if(keyDef != _keyNames.end()) {
return keyDef->second;
}
return "";
}
uint32_t LinuxKeyManager::GetKeyCode(string keyName)
{
auto keyDef = _keyCodes.find(keyName);
if(keyDef != _keyCodes.end()) {
return keyDef->second;
}
return 0;
}
void LinuxKeyManager::UpdateDevices()
{
//TODO: NOT IMPLEMENTED YET
//Only needed to detect newly plugged in devices
}
void LinuxKeyManager::SetKeyState(uint16_t scanCode, bool state)
{
_keyState[scanCode & 0xFF] = state ? 1 : 0;
}
void LinuxKeyManager::ResetKeyState()
{
memset(_keyState.data(), 0, 0x200 * sizeof(uint32_t));
}

34
Linux/LinuxKeyManager.h Executable file
View file

@ -0,0 +1,34 @@
#pragma once
#include <unordered_map>
#include "../Core/IKeyManager.h"
struct KeyDefinition {
string name;
uint32_t keyCode;
string description;
string extDescription;
};
class LinuxKeyManager : public IKeyManager
{
private:
int _numKeys;
vector<uint32_t> _keyState;
std::unordered_map<uint32_t, string> _keyNames;
std::unordered_map<string, uint32_t> _keyCodes;
public:
LinuxKeyManager();
virtual ~LinuxKeyManager();
void RefreshState();
bool IsKeyPressed(uint32_t key);
bool IsMouseButtonPressed(MouseButton button);
uint32_t GetPressedKey();
string GetKeyName(uint32_t key);
uint32_t GetKeyCode(string keyName);
void UpdateDevices();
void SetKeyState(uint16_t scanCode, bool state);
void ResetKeyState();
};

114
Linux/SdlRenderer.cpp Executable file
View file

@ -0,0 +1,114 @@
#include "SdlRenderer.h"
#include "../Core/VideoRenderer.h"
#include "../Core/VideoDecoder.h"
#include "../Core/EmulationSettings.h"
SdlRenderer::SdlRenderer(void* windowHandle) : _windowHandle(windowHandle)
{
_frameBuffer = nullptr;
SetScreenSize(256,240);
VideoRenderer::GetInstance()->RegisterRenderingDevice(this);
}
SdlRenderer::~SdlRenderer()
{
VideoRenderer::GetInstance()->UnregisterRenderingDevice(this);
Cleanup();
}
void SdlRenderer::Init()
{
SDL_InitSubSystem(SDL_INIT_VIDEO);
_sdlWindow = SDL_CreateWindowFrom(_windowHandle);
//Hack to make this work properly - otherwise SDL_CreateRenderer never returns
_sdlWindow->flags |= SDL_WINDOW_OPENGL;
SDL_GL_LoadLibrary(NULL);
_sdlRenderer = SDL_CreateRenderer(_sdlWindow, -1, SDL_RENDERER_ACCELERATED);
_sdlTexture = SDL_CreateTexture(_sdlRenderer, SDL_PIXELFORMAT_ARGB8888, SDL_TEXTUREACCESS_STREAMING, _nesFrameWidth, _nesFrameHeight);
_frameBuffer = new uint8_t[_nesFrameHeight*_nesFrameWidth*4];
memset(_frameBuffer, 0, _nesFrameHeight*_nesFrameWidth*4);
}
void SdlRenderer::Cleanup()
{
if(_sdlTexture) {
SDL_DestroyTexture(_sdlTexture);
_sdlTexture = nullptr;
}
if(_sdlRenderer) {
SDL_DestroyRenderer(_sdlRenderer);
_sdlRenderer = nullptr;
}
if(_frameBuffer) {
delete[] _frameBuffer;
_frameBuffer = nullptr;
}
}
void SdlRenderer::Reset()
{
_frameLock.Acquire();
Cleanup();
Init();
_frameLock.Release();
}
void SdlRenderer::SetScreenSize(uint32_t width, uint32_t height)
{
ScreenSize screenSize;
VideoDecoder::GetInstance()->GetScreenSize(screenSize, true);
double scale = EmulationSettings::GetVideoScale();
if(_scale != scale || _screenHeight != (uint32_t)screenSize.Height || _screenWidth != (uint32_t)screenSize.Width || _nesFrameHeight != height || _nesFrameWidth != width || _resizeFilter != EmulationSettings::GetVideoResizeFilter()) {
_frameLock.Acquire();
_nesFrameHeight = height;
_nesFrameWidth = width;
_newFrameBufferSize = width*height;
_screenHeight = screenSize.Height;
_screenWidth = screenSize.Width;
_resizeFilter = EmulationSettings::GetVideoResizeFilter();
_scale = scale;
SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, _resizeFilter == VideoResizeFilter::Bilinear ? "1" : "0");
SDL_RenderSetLogicalSize(_sdlRenderer, _nesFrameWidth, _nesFrameHeight);
_screenBufferSize = _screenHeight*_screenWidth;
Reset();
_frameLock.Release();
}
}
void SdlRenderer::UpdateFrame(void *frameBuffer, uint32_t width, uint32_t height)
{
_frameLock.Acquire();
SetScreenSize(width, height);
memcpy(_frameBuffer, frameBuffer, width*height*4);
_frameLock.Release();
}
void SdlRenderer::Render()
{
auto lock = _frameLock.AcquireSafe();
SDL_RenderClear(_sdlRenderer);
uint8_t *textureBuffer;
int rowPitch;
SDL_LockTexture(_sdlTexture, nullptr, (void**)&textureBuffer, &rowPitch);
uint32_t* ppuFrameBuffer = (uint32_t*)_frameBuffer;
for(uint32_t i = 0, iMax = _nesFrameHeight; i < iMax; i++) {
memcpy(textureBuffer, ppuFrameBuffer, _nesFrameWidth*4);
ppuFrameBuffer += _nesFrameWidth;
textureBuffer += rowPitch;
}
SDL_UnlockTexture(_sdlTexture);
SDL_RenderCopy(_sdlRenderer, _sdlTexture, nullptr, nullptr);
SDL_RenderPresent(_sdlRenderer);
}

56
Linux/SdlRenderer.h Executable file
View file

@ -0,0 +1,56 @@
#pragma once
#include <SDL2/SDL.h>
#include "../Core/IRenderingDevice.h"
#include "../Utilities/SimpleLock.h"
#include "../Core/EmulationSettings.h"
#include "../Core/VideoRenderer.h"
struct SDL_Window
{
const void *magic;
Uint32 id;
char *title;
SDL_Surface *icon;
int x, y;
int w, h;
int min_w, min_h;
int max_w, max_h;
Uint32 flags;
};
typedef struct SDL_Window SDL_Window;
class SdlRenderer : public IRenderingDevice
{
private:
void* _windowHandle;
SDL_Window* _sdlWindow = nullptr;
SDL_Renderer *_sdlRenderer = nullptr;
SDL_Texture *_sdlTexture = nullptr;
VideoResizeFilter _resizeFilter = VideoResizeFilter::NearestNeighbor;
SimpleLock _frameLock;
uint8_t* _frameBuffer;
const uint32_t _bytesPerPixel = 4;
uint32_t _screenWidth = 0;
uint32_t _screenHeight = 0;
uint32_t _screenBufferSize = 0;
double _scale = 0;
uint32_t _nesFrameHeight = 0;
uint32_t _nesFrameWidth = 0;
uint32_t _newFrameBufferSize = 0;
void Init();
void Cleanup();
void SetScreenSize(uint32_t width, uint32_t height);
public:
SdlRenderer(void* windowHandle);
virtual ~SdlRenderer();
void UpdateFrame(void *frameBuffer, uint32_t width, uint32_t height);
void Render();
void Reset();
};

168
Linux/SdlSoundManager.cpp Executable file
View file

@ -0,0 +1,168 @@
#include "SdlSoundManager.h"
#include "../Core/EmulationSettings.h"
#include "../Core/SoundMixer.h"
SdlSoundManager::SdlSoundManager()
{
if(InitializeAudio(44100, false)) {
SoundMixer::RegisterAudioDevice(this);
_buffer = new uint8_t[0xFFFF];
}
}
SdlSoundManager::~SdlSoundManager()
{
if(_buffer) {
delete[] _buffer;
}
}
void SdlSoundManager::FillAudioBuffer(void *userData, uint8_t *stream, int len)
{
SdlSoundManager* soundManager = (SdlSoundManager*)userData;
soundManager->ReadFromBuffer(stream, len);
}
bool SdlSoundManager::InitializeAudio(uint32_t sampleRate, bool isStereo)
{
if(SDL_InitSubSystem(SDL_INIT_AUDIO) != 0) {
return false;
}
int isCapture = 0;
_sampleRate = sampleRate;
_isStereo = isStereo;
SDL_AudioSpec audioSpec;
SDL_memset(&audioSpec, 0, sizeof(audioSpec));
audioSpec.freq = sampleRate;
audioSpec.format = AUDIO_S16SYS; //16-bit samples
audioSpec.channels = isStereo ? 2 : 1;
audioSpec.samples = 1024;
audioSpec.callback = &SdlSoundManager::FillAudioBuffer;
audioSpec.userdata = this;
SDL_AudioSpec obtainedSpec;
_audioDeviceID = SDL_OpenAudioDevice(_deviceName.empty() ? nullptr : _deviceName.c_str(), isCapture, &audioSpec, &obtainedSpec, 0);
_writePosition = 0;
_readPosition = 0;
_needReset = false;
return _audioDeviceID != 0;
}
string SdlSoundManager::GetAvailableDevices()
{
string deviceString;
for(string device : GetAvailableDeviceInfo()) {
deviceString += device + std::string("||");
}
return deviceString;
}
vector<string> SdlSoundManager::GetAvailableDeviceInfo()
{
vector<string> deviceList;
int isCapture = 0;
int deviceCount = SDL_GetNumAudioDevices(isCapture);
if(deviceCount == -1) {
//No devices found
} else {
for(int i = 0; i < deviceCount; i++) {
deviceList.push_back(SDL_GetAudioDeviceName(i, isCapture));
}
}
return deviceList;
}
void SdlSoundManager::SetAudioDevice(string deviceName)
{
if(deviceName.compare(_deviceName) != 0) {
_deviceName = deviceName;
_needReset = true;
}
}
void SdlSoundManager::ReadFromBuffer(uint8_t* output, uint32_t len)
{
if(_readPosition + len < 65536) {
memcpy(output, _buffer+_readPosition, len);
_readPosition += len;
} else {
int remainingBytes = (65536 - _readPosition);
memcpy(output, _buffer+_readPosition, remainingBytes);
memcpy(output+remainingBytes, _buffer, len - remainingBytes);
_readPosition = len - remainingBytes;
}
}
void SdlSoundManager::WriteToBuffer(uint8_t* input, uint32_t len)
{
if(_writePosition + len < 65536) {
memcpy(_buffer+_writePosition, input, len);
_writePosition += len;
} else {
int remainingBytes = 65536 - _writePosition;
memcpy(_buffer+_writePosition, input, remainingBytes);
memcpy(_buffer, ((uint8_t*)input)+remainingBytes, len - remainingBytes);
_writePosition = len - remainingBytes;
}
}
void SdlSoundManager::PlayBuffer(int16_t *soundBuffer, uint32_t sampleCount, uint32_t sampleRate, bool isStereo)
{
uint32_t bytesPerSample = (SoundMixer::BitsPerSample / 8);
if(_sampleRate != sampleRate || _isStereo != isStereo || _needReset) {
Stop();
InitializeAudio(sampleRate, isStereo);
}
if(isStereo) {
bytesPerSample *= 2;
}
int32_t byteLatency = (int32_t)((float)(sampleRate * EmulationSettings::GetAudioLatency()) / 1000.0f * bytesPerSample);
if(byteLatency != _previousLatency) {
Stop();
_previousLatency = byteLatency;
}
WriteToBuffer((uint8_t*)soundBuffer, sampleCount * bytesPerSample);
int32_t playWriteByteLatency = _writePosition - _readPosition;
if(playWriteByteLatency < 0) {
playWriteByteLatency = 0xFFFF - _readPosition + _writePosition;
}
if(playWriteByteLatency > byteLatency * 3) {
//Out of sync, resync
Stop();
WriteToBuffer((uint8_t*)soundBuffer, sampleCount * bytesPerSample);
playWriteByteLatency = _writePosition - _readPosition;
}
if(playWriteByteLatency > byteLatency) {
//Start playing
SDL_PauseAudioDevice(_audioDeviceID, 0);
}
}
void SdlSoundManager::Pause()
{
SDL_PauseAudioDevice(_audioDeviceID, 1);
}
void SdlSoundManager::Stop()
{
Pause();
_readPosition = 0;
_writePosition = 0;
}

40
Linux/SdlSoundManager.h Executable file
View file

@ -0,0 +1,40 @@
#pragma once
#include <SDL2/SDL.h>
#include "../Core/IAudioDevice.h"
class SdlSoundManager : public IAudioDevice
{
public:
SdlSoundManager();
~SdlSoundManager();
void PlayBuffer(int16_t *soundBuffer, uint32_t bufferSize, uint32_t sampleRate, bool isStereo);
void Pause();
void Stop();
string GetAvailableDevices();
void SetAudioDevice(string deviceName);
private:
vector<string> GetAvailableDeviceInfo();
bool InitializeAudio(uint32_t sampleRate, bool isStereo);
static void FillAudioBuffer(void *userData, uint8_t *stream, int len);
void ReadFromBuffer(uint8_t* output, uint32_t len);
void WriteToBuffer(uint8_t* output, uint32_t len);
private:
SDL_AudioDeviceID _audioDeviceID;
string _deviceName;
bool _needReset = false;
uint16_t _lastWriteOffset = 0;
uint16_t _previousLatency = 0;
uint32_t _sampleRate = 0;
bool _isStereo = false;
uint8_t* _buffer = nullptr;
uint32_t _writePosition = 0;
uint32_t _readPosition = 0;
};