diff --git a/Core/stdafx.h b/Core/stdafx.h index 4f90fc2a..29c4c2e5 100644 --- a/Core/stdafx.h +++ b/Core/stdafx.h @@ -20,7 +20,7 @@ #include "../Utilities/UTF8Util.h" #ifdef __GNUC__ -#define __forceinline inline +#define __forceinline #endif using std::vector; diff --git a/GUI.NET/Forms/frmMain.cs b/GUI.NET/Forms/frmMain.cs index 36b5af22..5ca57fb0 100644 --- a/GUI.NET/Forms/frmMain.cs +++ b/GUI.NET/Forms/frmMain.cs @@ -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(); } } diff --git a/InteropDLL/ConsoleWrapper.cpp b/InteropDLL/ConsoleWrapper.cpp index 8eb9e18c..e4790193 100644 --- a/InteropDLL/ConsoleWrapper.cpp +++ b/InteropDLL/ConsoleWrapper.cpp @@ -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); diff --git a/Linux/LinuxKeyManager.cpp b/Linux/LinuxKeyManager.cpp new file mode 100755 index 00000000..d77d5823 --- /dev/null +++ b/Linux/LinuxKeyManager.cpp @@ -0,0 +1,304 @@ +#include "LinuxKeyManager.h" +#include "../Core/ControlManager.h" + +static vector _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)); +} diff --git a/Linux/LinuxKeyManager.h b/Linux/LinuxKeyManager.h new file mode 100755 index 00000000..1cca37d8 --- /dev/null +++ b/Linux/LinuxKeyManager.h @@ -0,0 +1,34 @@ +#pragma once +#include +#include "../Core/IKeyManager.h" + +struct KeyDefinition { + string name; + uint32_t keyCode; + string description; + string extDescription; +}; + +class LinuxKeyManager : public IKeyManager +{ +private: + int _numKeys; + vector _keyState; + std::unordered_map _keyNames; + std::unordered_map _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(); +}; diff --git a/Linux/SdlRenderer.cpp b/Linux/SdlRenderer.cpp new file mode 100755 index 00000000..fe6c3c9f --- /dev/null +++ b/Linux/SdlRenderer.cpp @@ -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); +} diff --git a/Linux/SdlRenderer.h b/Linux/SdlRenderer.h new file mode 100755 index 00000000..1f56c474 --- /dev/null +++ b/Linux/SdlRenderer.h @@ -0,0 +1,56 @@ +#pragma once +#include +#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(); +}; \ No newline at end of file diff --git a/Linux/SdlSoundManager.cpp b/Linux/SdlSoundManager.cpp new file mode 100755 index 00000000..e58932be --- /dev/null +++ b/Linux/SdlSoundManager.cpp @@ -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 SdlSoundManager::GetAvailableDeviceInfo() +{ + vector 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; +} diff --git a/Linux/SdlSoundManager.h b/Linux/SdlSoundManager.h new file mode 100755 index 00000000..d0f69133 --- /dev/null +++ b/Linux/SdlSoundManager.h @@ -0,0 +1,40 @@ +#pragma once +#include +#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 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; +};