mirror of
https://github.com/elasota/Aerofoil.git
synced 2025-12-14 12:09:36 +00:00
SDL audio driver
This commit is contained in:
@@ -448,6 +448,7 @@ int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine
|
|||||||
|
|
||||||
g_gpGlobalConfig.m_osGlobals = &g_gpWindowsGlobals;
|
g_gpGlobalConfig.m_osGlobals = &g_gpWindowsGlobals;
|
||||||
g_gpGlobalConfig.m_logger = logger;
|
g_gpGlobalConfig.m_logger = logger;
|
||||||
|
g_gpGlobalConfig.m_systemServices = GpSystemServices_Win32::GetInstance();
|
||||||
|
|
||||||
GpDisplayDriverFactory::RegisterDisplayDriverFactory(EGpDisplayDriverType_D3D11, GpDriver_CreateDisplayDriver_D3D11);
|
GpDisplayDriverFactory::RegisterDisplayDriverFactory(EGpDisplayDriverType_D3D11, GpDriver_CreateDisplayDriver_D3D11);
|
||||||
GpAudioDriverFactory::RegisterAudioDriverFactory(EGpAudioDriverType_XAudio2, GpDriver_CreateAudioDriver_XAudio2);
|
GpAudioDriverFactory::RegisterAudioDriverFactory(EGpAudioDriverType_XAudio2, GpDriver_CreateAudioDriver_XAudio2);
|
||||||
|
|||||||
@@ -56,6 +56,11 @@ PortabilityLayer::HostMutex *GpSystemServices_Win32::CreateMutex()
|
|||||||
return GpMutex_Win32::Create();
|
return GpMutex_Win32::Create();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
PortabilityLayer::HostMutex *GpSystemServices_Win32::CreateRecursiveMutex()
|
||||||
|
{
|
||||||
|
return GpMutex_Win32::Create();
|
||||||
|
}
|
||||||
|
|
||||||
PortabilityLayer::HostThreadEvent *GpSystemServices_Win32::CreateThreadEvent(bool autoReset, bool startSignaled)
|
PortabilityLayer::HostThreadEvent *GpSystemServices_Win32::CreateThreadEvent(bool autoReset, bool startSignaled)
|
||||||
{
|
{
|
||||||
return GpThreadEvent_Win32::Create(autoReset, startSignaled);
|
return GpThreadEvent_Win32::Create(autoReset, startSignaled);
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ public:
|
|||||||
int64_t GetTime() const override;
|
int64_t GetTime() const override;
|
||||||
void GetLocalDateTime(unsigned int &year, unsigned int &month, unsigned int &day, unsigned int &hour, unsigned int &minute, unsigned int &second) const override;
|
void GetLocalDateTime(unsigned int &year, unsigned int &month, unsigned int &day, unsigned int &hour, unsigned int &minute, unsigned int &second) const override;
|
||||||
PortabilityLayer::HostMutex *CreateMutex() override;
|
PortabilityLayer::HostMutex *CreateMutex() override;
|
||||||
|
PortabilityLayer::HostMutex *CreateRecursiveMutex() override;
|
||||||
PortabilityLayer::HostThreadEvent *CreateThreadEvent(bool autoReset, bool startSignaled) override;
|
PortabilityLayer::HostThreadEvent *CreateThreadEvent(bool autoReset, bool startSignaled) override;
|
||||||
uint64_t GetFreeMemoryCosmetic() const override;
|
uint64_t GetFreeMemoryCosmetic() const override;
|
||||||
void Beep() const override;
|
void Beep() const override;
|
||||||
|
|||||||
@@ -91,6 +91,7 @@
|
|||||||
<ClCompile Include="..\Aerofoil\GpMutex_Win32.cpp" />
|
<ClCompile Include="..\Aerofoil\GpMutex_Win32.cpp" />
|
||||||
<ClCompile Include="..\Aerofoil\GpSystemServices_Win32.cpp" />
|
<ClCompile Include="..\Aerofoil\GpSystemServices_Win32.cpp" />
|
||||||
<ClCompile Include="..\Aerofoil\GpThreadEvent_Win32.cpp" />
|
<ClCompile Include="..\Aerofoil\GpThreadEvent_Win32.cpp" />
|
||||||
|
<ClCompile Include="GpAudioDriver_SDL2.cpp" />
|
||||||
<ClCompile Include="GpDisplayDriver_SDL_GL2.cpp" />
|
<ClCompile Include="GpDisplayDriver_SDL_GL2.cpp" />
|
||||||
<ClCompile Include="GpFiberStarter_SDL.cpp" />
|
<ClCompile Include="GpFiberStarter_SDL.cpp" />
|
||||||
<ClCompile Include="GpFiber_SDL.cpp" />
|
<ClCompile Include="GpFiber_SDL.cpp" />
|
||||||
|
|||||||
@@ -60,6 +60,9 @@
|
|||||||
<ClCompile Include="GpDisplayDriver_SDL_GL2.cpp">
|
<ClCompile Include="GpDisplayDriver_SDL_GL2.cpp">
|
||||||
<Filter>Source Files</Filter>
|
<Filter>Source Files</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
|
<ClCompile Include="GpAudioDriver_SDL2.cpp">
|
||||||
|
<Filter>Source Files</Filter>
|
||||||
|
</ClCompile>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ClInclude Include="ShaderCode\Functions.h">
|
<ClInclude Include="ShaderCode\Functions.h">
|
||||||
|
|||||||
560
AerofoilSDL/GpAudioDriver_SDL2.cpp
Normal file
560
AerofoilSDL/GpAudioDriver_SDL2.cpp
Normal file
@@ -0,0 +1,560 @@
|
|||||||
|
#include "IGpAudioDriver.h"
|
||||||
|
#include "IGpAudioChannel.h"
|
||||||
|
#include "IGpAudioChannelCallbacks.h"
|
||||||
|
#include "IGpPrefsHandler.h"
|
||||||
|
#include "GpAudioDriverProperties.h"
|
||||||
|
#include "CoreDefs.h"
|
||||||
|
|
||||||
|
#include "HostMutex.h"
|
||||||
|
#include "HostSystemServices.h"
|
||||||
|
|
||||||
|
#include "SDL_audio.h"
|
||||||
|
#include "GpRingBuffer.h"
|
||||||
|
|
||||||
|
#include "SDL_atomic.h"
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <new>
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
class GpAudioDriver_SDL2;
|
||||||
|
|
||||||
|
static void *AlignedAlloc(size_t size, size_t alignment)
|
||||||
|
{
|
||||||
|
void *storage = malloc(size + alignment);
|
||||||
|
if (!storage)
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
uintptr_t alignedPtr = reinterpret_cast<uintptr_t>(storage);
|
||||||
|
size_t padding = alignment - static_cast<size_t>(alignedPtr % alignment);
|
||||||
|
|
||||||
|
uint8_t *storageLoc = static_cast<uint8_t*>(storage);
|
||||||
|
uint8_t *objectLoc = storageLoc + padding;
|
||||||
|
uint8_t *paddingSizeLoc = storageLoc + padding - 1;
|
||||||
|
|
||||||
|
*reinterpret_cast<uint8_t*>(paddingSizeLoc) = static_cast<uint8_t>(padding);
|
||||||
|
|
||||||
|
return objectLoc;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void AlignedFree(void *ptr)
|
||||||
|
{
|
||||||
|
size_t padding = static_cast<uint8_t*>(ptr)[-1];
|
||||||
|
void *storageLoc = static_cast<uint8_t*>(ptr) - padding;
|
||||||
|
|
||||||
|
free(storageLoc);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct GpAudioChannelBufferChain_SDL2 final
|
||||||
|
{
|
||||||
|
GpAudioChannelBufferChain_SDL2();
|
||||||
|
|
||||||
|
static GpAudioChannelBufferChain_SDL2 *Alloc();
|
||||||
|
void Release();
|
||||||
|
|
||||||
|
static const size_t kMaxCapacity = 65536;
|
||||||
|
|
||||||
|
size_t m_consumed;
|
||||||
|
size_t m_used;
|
||||||
|
uint8_t m_data[kMaxCapacity];
|
||||||
|
GpAudioChannelBufferChain_SDL2 *m_next;
|
||||||
|
bool m_hasTrigger;
|
||||||
|
};
|
||||||
|
|
||||||
|
GP_ALIGNED(GP_SYSTEM_MEMORY_ALIGNMENT) class GpAudioChannel_SDL2 final : public IGpAudioChannel
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
enum ChannelState
|
||||||
|
{
|
||||||
|
ChannelState_Idle,
|
||||||
|
ChannelState_Playing,
|
||||||
|
ChannelState_Stopped,
|
||||||
|
};
|
||||||
|
|
||||||
|
friend class GpAudioDriver_SDL2;
|
||||||
|
|
||||||
|
GpAudioChannel_SDL2();
|
||||||
|
~GpAudioChannel_SDL2();
|
||||||
|
|
||||||
|
void AddRef();
|
||||||
|
void Release();
|
||||||
|
|
||||||
|
void SetAudioChannelContext(IGpAudioChannelCallbacks *callbacks) override;
|
||||||
|
void PostBuffer(const void *buffer, size_t bufferSize) override;
|
||||||
|
void Stop() override;
|
||||||
|
void Destroy() override;
|
||||||
|
|
||||||
|
void Consume(uint8_t *output, size_t sz);
|
||||||
|
|
||||||
|
static GpAudioChannel_SDL2 *Alloc(GpAudioDriver_SDL2 *driver);
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool Init(GpAudioDriver_SDL2 *driver);
|
||||||
|
|
||||||
|
IGpAudioChannelCallbacks *m_callbacks;
|
||||||
|
PortabilityLayer::HostMutex *m_mutex;
|
||||||
|
GpAudioDriver_SDL2 *m_owner;
|
||||||
|
|
||||||
|
SDL_atomic_t m_refCount;
|
||||||
|
|
||||||
|
GpAudioChannelBufferChain_SDL2 *m_firstPendingBuffer;
|
||||||
|
GpAudioChannelBufferChain_SDL2 *m_lastPendingBuffer;
|
||||||
|
|
||||||
|
ChannelState m_channelState;
|
||||||
|
};
|
||||||
|
|
||||||
|
GP_ALIGNED(GP_SYSTEM_MEMORY_ALIGNMENT) class GpAudioDriver_SDL2 final : public IGpAudioDriver, public IGpPrefsHandler
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
friend class GpAudioChannel_SDL2;
|
||||||
|
|
||||||
|
explicit GpAudioDriver_SDL2(const GpAudioDriverProperties &properties);
|
||||||
|
~GpAudioDriver_SDL2();
|
||||||
|
|
||||||
|
IGpAudioChannel *CreateChannel() override;
|
||||||
|
void SetMasterVolume(uint32_t vol, uint32_t maxVolume) override;
|
||||||
|
void Shutdown() override;
|
||||||
|
IGpPrefsHandler *GetPrefsHandler() const override;
|
||||||
|
|
||||||
|
void ApplyPrefs(const void *identifier, size_t identifierSize, const void *contents, size_t contentsSize, uint32_t version) override;
|
||||||
|
bool SavePrefs(void *context, WritePrefsFunc_t writeFunc) override;
|
||||||
|
|
||||||
|
bool Init();
|
||||||
|
|
||||||
|
private:
|
||||||
|
void DetachAudioChannel(GpAudioChannel_SDL2 *channel);
|
||||||
|
|
||||||
|
static void SDLCALL StaticMixAudio(void *userdata, Uint8 *stream, int len);
|
||||||
|
|
||||||
|
void MixAudio(void *stream, size_t len);
|
||||||
|
void RefillMixChunk(GpAudioChannel_SDL2 *const*channels, size_t numChannels);
|
||||||
|
|
||||||
|
GpAudioDriverProperties m_properties;
|
||||||
|
PortabilityLayer::HostMutex *m_mutex;
|
||||||
|
PortabilityLayer::HostMutex *m_mixState;
|
||||||
|
|
||||||
|
static const size_t kMaxChannels = 16;
|
||||||
|
static const size_t kMixChunkSize = 256;
|
||||||
|
|
||||||
|
GpAudioChannel_SDL2 *m_channels[kMaxChannels];
|
||||||
|
size_t m_numChannels;
|
||||||
|
|
||||||
|
bool m_sdlAudioRunning;
|
||||||
|
|
||||||
|
GP_ALIGNED(GP_SYSTEM_MEMORY_ALIGNMENT) int16_t m_mixChunk[kMixChunkSize];
|
||||||
|
size_t m_mixChunkReadOffset;
|
||||||
|
};
|
||||||
|
|
||||||
|
GpAudioChannelBufferChain_SDL2::GpAudioChannelBufferChain_SDL2()
|
||||||
|
: m_used(0)
|
||||||
|
, m_consumed(0)
|
||||||
|
, m_next(nullptr)
|
||||||
|
, m_hasTrigger(false)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
GpAudioChannelBufferChain_SDL2 *GpAudioChannelBufferChain_SDL2::Alloc()
|
||||||
|
{
|
||||||
|
void *storage = AlignedAlloc(sizeof(GpAudioChannelBufferChain_SDL2), GP_SYSTEM_MEMORY_ALIGNMENT);
|
||||||
|
return new (storage) GpAudioChannelBufferChain_SDL2();
|
||||||
|
}
|
||||||
|
|
||||||
|
void GpAudioChannelBufferChain_SDL2::Release()
|
||||||
|
{
|
||||||
|
this->~GpAudioChannelBufferChain_SDL2();
|
||||||
|
AlignedFree(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
/////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// GpAudioChannel
|
||||||
|
|
||||||
|
GpAudioChannel_SDL2::GpAudioChannel_SDL2()
|
||||||
|
: m_callbacks(nullptr)
|
||||||
|
, m_mutex(nullptr)
|
||||||
|
, m_owner(nullptr)
|
||||||
|
, m_firstPendingBuffer(nullptr)
|
||||||
|
, m_lastPendingBuffer(nullptr)
|
||||||
|
{
|
||||||
|
SDL_AtomicSet(&m_refCount, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
GpAudioChannel_SDL2::~GpAudioChannel_SDL2()
|
||||||
|
{
|
||||||
|
Stop();
|
||||||
|
|
||||||
|
if (m_mutex)
|
||||||
|
m_mutex->Destroy();
|
||||||
|
|
||||||
|
while (m_firstPendingBuffer)
|
||||||
|
{
|
||||||
|
GpAudioChannelBufferChain_SDL2 *buffer = m_firstPendingBuffer;
|
||||||
|
m_firstPendingBuffer = buffer->m_next;
|
||||||
|
buffer->Release();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void GpAudioChannel_SDL2::AddRef()
|
||||||
|
{
|
||||||
|
SDL_AtomicIncRef(&m_refCount);
|
||||||
|
}
|
||||||
|
|
||||||
|
void GpAudioChannel_SDL2::Release()
|
||||||
|
{
|
||||||
|
if (SDL_AtomicDecRef(&m_refCount))
|
||||||
|
{
|
||||||
|
this->~GpAudioChannel_SDL2();
|
||||||
|
AlignedFree(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void GpAudioChannel_SDL2::SetAudioChannelContext(IGpAudioChannelCallbacks *callbacks)
|
||||||
|
{
|
||||||
|
m_callbacks = callbacks;
|
||||||
|
}
|
||||||
|
|
||||||
|
void GpAudioChannel_SDL2::PostBuffer(const void *buffer, size_t bufferSize)
|
||||||
|
{
|
||||||
|
m_mutex->Lock();
|
||||||
|
|
||||||
|
while (bufferSize > 0)
|
||||||
|
{
|
||||||
|
GpAudioChannelBufferChain_SDL2 *newBuffer = GpAudioChannelBufferChain_SDL2::Alloc();
|
||||||
|
if (newBuffer == nullptr)
|
||||||
|
break;
|
||||||
|
|
||||||
|
if (m_lastPendingBuffer == nullptr)
|
||||||
|
m_firstPendingBuffer = newBuffer;
|
||||||
|
else
|
||||||
|
m_lastPendingBuffer->m_next = newBuffer;
|
||||||
|
m_lastPendingBuffer = newBuffer;
|
||||||
|
|
||||||
|
size_t bufferable = newBuffer->kMaxCapacity;
|
||||||
|
if (bufferSize < bufferable)
|
||||||
|
bufferable = bufferSize;
|
||||||
|
|
||||||
|
memcpy(newBuffer->m_data, buffer, bufferable);
|
||||||
|
|
||||||
|
buffer = static_cast<const uint8_t*>(buffer) + bufferable;
|
||||||
|
bufferSize -= bufferable;
|
||||||
|
m_lastPendingBuffer->m_used = bufferable;
|
||||||
|
m_lastPendingBuffer->m_hasTrigger = (bufferSize == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
m_mutex->Unlock();
|
||||||
|
}
|
||||||
|
|
||||||
|
void GpAudioChannel_SDL2::Stop()
|
||||||
|
{
|
||||||
|
m_mutex->Lock();
|
||||||
|
|
||||||
|
GpAudioChannelBufferChain_SDL2 *buffer = m_firstPendingBuffer;
|
||||||
|
m_firstPendingBuffer = nullptr;
|
||||||
|
m_lastPendingBuffer = nullptr;
|
||||||
|
|
||||||
|
while (buffer)
|
||||||
|
{
|
||||||
|
if (buffer->m_hasTrigger && m_callbacks)
|
||||||
|
m_callbacks->NotifyBufferFinished();
|
||||||
|
|
||||||
|
GpAudioChannelBufferChain_SDL2 *nextBuffer = buffer->m_next;
|
||||||
|
buffer->Release();
|
||||||
|
|
||||||
|
buffer = nextBuffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_mutex->Unlock();
|
||||||
|
}
|
||||||
|
|
||||||
|
void GpAudioChannel_SDL2::Destroy()
|
||||||
|
{
|
||||||
|
if (m_owner)
|
||||||
|
m_owner->DetachAudioChannel(this);
|
||||||
|
|
||||||
|
this->Release();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool GpAudioChannel_SDL2::Init(GpAudioDriver_SDL2 *driver)
|
||||||
|
{
|
||||||
|
m_owner = driver;
|
||||||
|
m_mutex = driver->m_properties.m_systemServices->CreateRecursiveMutex();
|
||||||
|
if (!m_mutex)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void GpAudioChannel_SDL2::Consume(uint8_t *output, size_t sz)
|
||||||
|
{
|
||||||
|
m_mutex->Lock();
|
||||||
|
|
||||||
|
while (m_firstPendingBuffer != nullptr)
|
||||||
|
{
|
||||||
|
GpAudioChannelBufferChain_SDL2 *buffer = m_firstPendingBuffer;
|
||||||
|
const size_t available = (buffer->m_used - buffer->m_consumed);
|
||||||
|
if (available <= sz)
|
||||||
|
{
|
||||||
|
memcpy(output, buffer->m_data + buffer->m_consumed, available);
|
||||||
|
sz -= available;
|
||||||
|
output += available;
|
||||||
|
|
||||||
|
m_firstPendingBuffer = buffer->m_next;
|
||||||
|
if (m_firstPendingBuffer == nullptr)
|
||||||
|
m_lastPendingBuffer = nullptr;
|
||||||
|
|
||||||
|
if (buffer->m_hasTrigger && m_callbacks)
|
||||||
|
m_callbacks->NotifyBufferFinished();
|
||||||
|
|
||||||
|
buffer->Release();
|
||||||
|
|
||||||
|
if (sz == 0)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
memcpy(output, buffer->m_data + buffer->m_consumed, sz);
|
||||||
|
buffer->m_consumed += sz;
|
||||||
|
buffer += sz;
|
||||||
|
sz = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
m_mutex->Unlock();
|
||||||
|
|
||||||
|
memset(output, 0x80, sz);
|
||||||
|
}
|
||||||
|
|
||||||
|
GpAudioChannel_SDL2 *GpAudioChannel_SDL2::Alloc(GpAudioDriver_SDL2 *driver)
|
||||||
|
{
|
||||||
|
void *storage = AlignedAlloc(sizeof(GpAudioChannel_SDL2), GP_SYSTEM_MEMORY_ALIGNMENT);
|
||||||
|
if (!storage)
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
GpAudioChannel_SDL2 *channel = new (storage) GpAudioChannel_SDL2();
|
||||||
|
if (!channel->Init(driver))
|
||||||
|
{
|
||||||
|
channel->Destroy();
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
return channel;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// GpAudioDriver_SDL2
|
||||||
|
|
||||||
|
GpAudioDriver_SDL2::GpAudioDriver_SDL2(const GpAudioDriverProperties &properties)
|
||||||
|
: m_properties(properties)
|
||||||
|
, m_mutex(nullptr)
|
||||||
|
, m_numChannels(0)
|
||||||
|
, m_sdlAudioRunning(false)
|
||||||
|
, m_mixChunkReadOffset(kMixChunkSize)
|
||||||
|
|
||||||
|
{
|
||||||
|
for (size_t i = 0; i < kMaxChannels; i++)
|
||||||
|
m_channels[i] = nullptr;
|
||||||
|
|
||||||
|
for (size_t i = 0; i < kMixChunkSize; i++)
|
||||||
|
m_mixChunk[i] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
GpAudioDriver_SDL2::~GpAudioDriver_SDL2()
|
||||||
|
{
|
||||||
|
if (m_sdlAudioRunning)
|
||||||
|
SDL_CloseAudio();
|
||||||
|
|
||||||
|
if (m_mutex)
|
||||||
|
m_mutex->Destroy();
|
||||||
|
}
|
||||||
|
|
||||||
|
IGpAudioChannel *GpAudioDriver_SDL2::CreateChannel()
|
||||||
|
{
|
||||||
|
GpAudioChannel_SDL2 *newChannel = GpAudioChannel_SDL2::Alloc(this);
|
||||||
|
if (!newChannel)
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
m_mutex->Lock();
|
||||||
|
if (m_numChannels == kMaxChannels)
|
||||||
|
{
|
||||||
|
newChannel->Destroy();
|
||||||
|
m_mutex->Unlock();
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_channels[m_numChannels] = newChannel;
|
||||||
|
m_numChannels++;
|
||||||
|
|
||||||
|
m_mutex->Unlock();
|
||||||
|
|
||||||
|
return newChannel;
|
||||||
|
}
|
||||||
|
|
||||||
|
void GpAudioDriver_SDL2::SetMasterVolume(uint32_t vol, uint32_t maxVolume)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void GpAudioDriver_SDL2::Shutdown()
|
||||||
|
{
|
||||||
|
this->~GpAudioDriver_SDL2();
|
||||||
|
AlignedFree(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
IGpPrefsHandler *GpAudioDriver_SDL2::GetPrefsHandler() const
|
||||||
|
{
|
||||||
|
return const_cast<GpAudioDriver_SDL2*>(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
void GpAudioDriver_SDL2::ApplyPrefs(const void *identifier, size_t identifierSize, const void *contents, size_t contentsSize, uint32_t version)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
bool GpAudioDriver_SDL2::SavePrefs(void *context, WritePrefsFunc_t writeFunc)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool GpAudioDriver_SDL2::Init()
|
||||||
|
{
|
||||||
|
m_mutex = m_properties.m_systemServices->CreateMutex();
|
||||||
|
if (!m_mutex)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
SDL_AudioSpec requestedSpec;
|
||||||
|
memset(&requestedSpec, 0, sizeof(requestedSpec));
|
||||||
|
|
||||||
|
requestedSpec.callback = GpAudioDriver_SDL2::StaticMixAudio;
|
||||||
|
requestedSpec.channels = 1;
|
||||||
|
requestedSpec.format = AUDIO_S16;
|
||||||
|
requestedSpec.freq = m_properties.m_sampleRate;
|
||||||
|
requestedSpec.samples = 512;
|
||||||
|
requestedSpec.userdata = this;
|
||||||
|
|
||||||
|
if (SDL_OpenAudio(&requestedSpec, nullptr))
|
||||||
|
{
|
||||||
|
requestedSpec.freq = 22050;
|
||||||
|
if (SDL_OpenAudio(&requestedSpec, nullptr))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
SDL_PauseAudio(0);
|
||||||
|
|
||||||
|
m_sdlAudioRunning = true;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void GpAudioDriver_SDL2::DetachAudioChannel(GpAudioChannel_SDL2 *channel)
|
||||||
|
{
|
||||||
|
m_mutex->Lock();
|
||||||
|
const size_t numChannels = m_numChannels;
|
||||||
|
for (size_t i = 0; i < numChannels; i++)
|
||||||
|
{
|
||||||
|
if (m_channels[i] == channel)
|
||||||
|
{
|
||||||
|
m_numChannels = numChannels - 1;
|
||||||
|
m_channels[i] = m_channels[numChannels - 1];
|
||||||
|
m_channels[numChannels - 1] = nullptr;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
m_mutex->Unlock();
|
||||||
|
}
|
||||||
|
|
||||||
|
void GpAudioDriver_SDL2::StaticMixAudio(void *userdata, Uint8 *stream, int len)
|
||||||
|
{
|
||||||
|
static_cast<GpAudioDriver_SDL2*>(userdata)->MixAudio(stream, static_cast<size_t>(len));
|
||||||
|
}
|
||||||
|
|
||||||
|
void GpAudioDriver_SDL2::MixAudio(void *stream, size_t len)
|
||||||
|
{
|
||||||
|
GpAudioChannel_SDL2 *mixingChannels[kMaxChannels];
|
||||||
|
size_t numChannels = 0;
|
||||||
|
|
||||||
|
m_mutex->Lock();
|
||||||
|
numChannels = m_numChannels;
|
||||||
|
for (size_t i = 0; i < numChannels; i++)
|
||||||
|
{
|
||||||
|
GpAudioChannel_SDL2 *channel = m_channels[i];
|
||||||
|
channel->AddRef();
|
||||||
|
|
||||||
|
mixingChannels[i] = channel;
|
||||||
|
}
|
||||||
|
m_mutex->Unlock();
|
||||||
|
|
||||||
|
size_t samplesRemaining = len / sizeof(int16_t);
|
||||||
|
|
||||||
|
for (;;)
|
||||||
|
{
|
||||||
|
size_t availableInMixChunk = kMixChunkSize - m_mixChunkReadOffset;
|
||||||
|
|
||||||
|
if (availableInMixChunk > samplesRemaining)
|
||||||
|
{
|
||||||
|
m_mixChunkReadOffset += samplesRemaining;
|
||||||
|
memcpy(stream, m_mixChunk + m_mixChunkReadOffset, samplesRemaining * sizeof(int16_t));
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
memcpy(stream, m_mixChunk + m_mixChunkReadOffset, availableInMixChunk * sizeof(int16_t));
|
||||||
|
|
||||||
|
stream = static_cast<int16_t*>(stream) + availableInMixChunk;
|
||||||
|
samplesRemaining -= availableInMixChunk;
|
||||||
|
|
||||||
|
m_mixChunkReadOffset = 0;
|
||||||
|
RefillMixChunk(mixingChannels, numChannels);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (size_t i = 0; i < numChannels; i++)
|
||||||
|
mixingChannels[i]->Release();
|
||||||
|
}
|
||||||
|
|
||||||
|
void GpAudioDriver_SDL2::RefillMixChunk(GpAudioChannel_SDL2 *const*channels, size_t numChannels)
|
||||||
|
{
|
||||||
|
uint8_t audioMixBufferUnaligned[kMixChunkSize + GP_SYSTEM_MEMORY_ALIGNMENT];
|
||||||
|
uint8_t *audioMixBuffer = audioMixBufferUnaligned;
|
||||||
|
|
||||||
|
{
|
||||||
|
uintptr_t bufferPtr = reinterpret_cast<uintptr_t>(audioMixBuffer);
|
||||||
|
size_t alignPadding = GP_SYSTEM_MEMORY_ALIGNMENT - (bufferPtr % GP_SYSTEM_MEMORY_ALIGNMENT);
|
||||||
|
audioMixBuffer += alignPadding;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (size_t i = 0; i < numChannels; i++)
|
||||||
|
{
|
||||||
|
channels[i]->Consume(audioMixBuffer, kMixChunkSize);
|
||||||
|
|
||||||
|
if (i == 0)
|
||||||
|
{
|
||||||
|
for (size_t j = 0; j < kMixChunkSize; j++)
|
||||||
|
m_mixChunk[j] = audioMixBuffer[j] - 0x80;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
for (size_t j = 0; j < kMixChunkSize; j++)
|
||||||
|
m_mixChunk[j] += audioMixBuffer[j] - 0x80;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
IGpAudioDriver *GpDriver_CreateAudioDriver_SDL(const GpAudioDriverProperties &properties)
|
||||||
|
{
|
||||||
|
void *storage = AlignedAlloc(sizeof(GpAudioDriver_SDL2), GP_SYSTEM_MEMORY_ALIGNMENT);
|
||||||
|
if (!storage)
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
GpAudioDriver_SDL2 *driver = new (storage) GpAudioDriver_SDL2(properties);
|
||||||
|
if (!driver->Init())
|
||||||
|
{
|
||||||
|
driver->Shutdown();
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
return driver;
|
||||||
|
}
|
||||||
@@ -29,11 +29,11 @@
|
|||||||
|
|
||||||
GpWindowsGlobals g_gpWindowsGlobals;
|
GpWindowsGlobals g_gpWindowsGlobals;
|
||||||
|
|
||||||
extern "C" __declspec(dllimport) IGpAudioDriver *GpDriver_CreateAudioDriver_XAudio2(const GpAudioDriverProperties &properties);
|
|
||||||
extern "C" __declspec(dllimport) IGpInputDriver *GpDriver_CreateInputDriver_XInput(const GpInputDriverProperties &properties);
|
extern "C" __declspec(dllimport) IGpInputDriver *GpDriver_CreateInputDriver_XInput(const GpInputDriverProperties &properties);
|
||||||
extern "C" __declspec(dllimport) IGpFontHandler *GpDriver_CreateFontHandler_FreeType2(const GpFontHandlerProperties &properties);
|
extern "C" __declspec(dllimport) IGpFontHandler *GpDriver_CreateFontHandler_FreeType2(const GpFontHandlerProperties &properties);
|
||||||
|
|
||||||
IGpDisplayDriver *GpDriver_CreateDisplayDriver_SDL_GL2(const GpDisplayDriverProperties &properties);
|
IGpDisplayDriver *GpDriver_CreateDisplayDriver_SDL_GL2(const GpDisplayDriverProperties &properties);
|
||||||
|
IGpAudioDriver *GpDriver_CreateAudioDriver_SDL(const GpAudioDriverProperties &properties);
|
||||||
|
|
||||||
static void PostMouseEvent(IGpVOSEventQueue *eventQueue, GpMouseEventType_t eventType, GpMouseButton_t button, int32_t x, int32_t y, float pixelScaleX, float pixelScaleY)
|
static void PostMouseEvent(IGpVOSEventQueue *eventQueue, GpMouseEventType_t eventType, GpMouseButton_t button, int32_t x, int32_t y, float pixelScaleX, float pixelScaleY)
|
||||||
{
|
{
|
||||||
@@ -438,7 +438,7 @@ int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine
|
|||||||
|
|
||||||
g_gpGlobalConfig.m_displayDriverType = EGpDisplayDriverType_SDL_GL2;
|
g_gpGlobalConfig.m_displayDriverType = EGpDisplayDriverType_SDL_GL2;
|
||||||
|
|
||||||
g_gpGlobalConfig.m_audioDriverType = EGpAudioDriverType_XAudio2;
|
g_gpGlobalConfig.m_audioDriverType = EGpAudioDriverType_SDL2;
|
||||||
|
|
||||||
g_gpGlobalConfig.m_fontHandlerType = EGpFontHandlerType_FreeType2;
|
g_gpGlobalConfig.m_fontHandlerType = EGpFontHandlerType_FreeType2;
|
||||||
|
|
||||||
@@ -452,9 +452,10 @@ int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine
|
|||||||
|
|
||||||
g_gpGlobalConfig.m_osGlobals = &g_gpWindowsGlobals;
|
g_gpGlobalConfig.m_osGlobals = &g_gpWindowsGlobals;
|
||||||
g_gpGlobalConfig.m_logger = logger;
|
g_gpGlobalConfig.m_logger = logger;
|
||||||
|
g_gpGlobalConfig.m_systemServices = GpSystemServices_Win32::GetInstance();
|
||||||
|
|
||||||
GpDisplayDriverFactory::RegisterDisplayDriverFactory(EGpDisplayDriverType_SDL_GL2, GpDriver_CreateDisplayDriver_SDL_GL2);
|
GpDisplayDriverFactory::RegisterDisplayDriverFactory(EGpDisplayDriverType_SDL_GL2, GpDriver_CreateDisplayDriver_SDL_GL2);
|
||||||
GpAudioDriverFactory::RegisterAudioDriverFactory(EGpAudioDriverType_XAudio2, GpDriver_CreateAudioDriver_XAudio2);
|
GpAudioDriverFactory::RegisterAudioDriverFactory(EGpAudioDriverType_SDL2, GpDriver_CreateAudioDriver_SDL);
|
||||||
GpInputDriverFactory::RegisterInputDriverFactory(EGpInputDriverType_XInput, GpDriver_CreateInputDriver_XInput);
|
GpInputDriverFactory::RegisterInputDriverFactory(EGpInputDriverType_XInput, GpDriver_CreateInputDriver_XInput);
|
||||||
GpFontHandlerFactory::RegisterFontHandlerFactory(EGpFontHandlerType_FreeType2, GpDriver_CreateFontHandler_FreeType2);
|
GpFontHandlerFactory::RegisterFontHandlerFactory(EGpFontHandlerType_FreeType2, GpDriver_CreateFontHandler_FreeType2);
|
||||||
|
|
||||||
|
|||||||
@@ -6,6 +6,12 @@
|
|||||||
#define GP_IS_CPP11 0
|
#define GP_IS_CPP11 0
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef _MSC_VER
|
||||||
|
#define GP_ALIGNED(n) __declspec(align(n))
|
||||||
|
#else
|
||||||
|
#define GP_ALIGNED(n) __attribute__((aligned(n)))
|
||||||
|
#endif
|
||||||
|
|
||||||
#if GP_IS_CPP11
|
#if GP_IS_CPP11
|
||||||
#define GP_DELETED = delete
|
#define GP_DELETED = delete
|
||||||
#define GP_STATIC_ASSERT(n) static_assert((n), "Static assert failed: " #n)
|
#define GP_STATIC_ASSERT(n) static_assert((n), "Static assert failed: " #n)
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
enum EGpAudioDriverType
|
enum EGpAudioDriverType
|
||||||
{
|
{
|
||||||
EGpAudioDriverType_XAudio2,
|
EGpAudioDriverType_XAudio2,
|
||||||
|
EGpAudioDriverType_SDL2,
|
||||||
|
|
||||||
EGpAudioDriverType_Count,
|
EGpAudioDriverType_Count,
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -2,6 +2,11 @@
|
|||||||
|
|
||||||
#include "EGpAudioDriverType.h"
|
#include "EGpAudioDriverType.h"
|
||||||
|
|
||||||
|
namespace PortabilityLayer
|
||||||
|
{
|
||||||
|
class HostSystemServices;
|
||||||
|
}
|
||||||
|
|
||||||
struct IGpAudioDriver;
|
struct IGpAudioDriver;
|
||||||
struct IGpLogDriver;
|
struct IGpLogDriver;
|
||||||
|
|
||||||
@@ -13,4 +18,5 @@ struct GpAudioDriverProperties
|
|||||||
bool m_debug;
|
bool m_debug;
|
||||||
|
|
||||||
IGpLogDriver *m_logger;
|
IGpLogDriver *m_logger;
|
||||||
|
PortabilityLayer::HostSystemServices *m_systemServices;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -19,19 +19,43 @@ public:
|
|||||||
{
|
{
|
||||||
assert(index < m_Size);
|
assert(index < m_Size);
|
||||||
return m_Items[(m_Start + index) % TCapacity];
|
return m_Items[(m_Start + index) % TCapacity];
|
||||||
}
|
}
|
||||||
|
|
||||||
void RemoveFromStart()
|
void RemoveFromStart()
|
||||||
{
|
{
|
||||||
assert(m_Size >= 1);
|
assert(m_Size >= 1);
|
||||||
m_Start = (m_Start + 1) % TCapacity;
|
m_Start = (m_Start + 1) % TCapacity;
|
||||||
m_Size--;
|
m_Size--;
|
||||||
|
}
|
||||||
|
|
||||||
|
const uint8_t *GetContiguousAtStart(size_t count)
|
||||||
|
{
|
||||||
|
assert(m_Size >= count);
|
||||||
|
assert(m_Start + count <= TCapacity);
|
||||||
|
const uint8_t *ptr = m_Items + m_Start;
|
||||||
|
m_Start = (m_Start + count) % TCapacity;
|
||||||
|
m_Size -= count;
|
||||||
|
|
||||||
|
return ptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
void RemoveCountFromStart(size_t count)
|
||||||
|
{
|
||||||
|
assert(m_Size >= count);
|
||||||
|
m_Start = (m_Start + count) % TCapacity;
|
||||||
|
m_Size -= count;
|
||||||
}
|
}
|
||||||
|
|
||||||
void RemoveFromEnd()
|
void RemoveFromEnd()
|
||||||
{
|
{
|
||||||
assert(m_Size >= 1);
|
assert(m_Size >= 1);
|
||||||
m_Size--;
|
m_Size--;
|
||||||
|
}
|
||||||
|
|
||||||
|
void RemoveCountFromEnd(size_t count)
|
||||||
|
{
|
||||||
|
assert(m_Size >= count);
|
||||||
|
m_Size -= count;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Clear()
|
void Clear()
|
||||||
@@ -52,12 +76,54 @@ public:
|
|||||||
|
|
||||||
m_Size++;
|
m_Size++;
|
||||||
return &m_Items[(m_Start + (m_Size - 1)) % TCapacity];
|
return &m_Items[(m_Start + (m_Size - 1)) % TCapacity];
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t SizeContiguous() const
|
||||||
|
{
|
||||||
|
if (m_Size >= TCapacity - m_Start)
|
||||||
|
return TCapacity - m_Start;
|
||||||
|
else
|
||||||
|
return m_Size;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
size_t AppendMultiple(size_t quantity, TItem *& outPtr)
|
||||||
|
{
|
||||||
|
if (TCapacity - m_Start > m_Size)
|
||||||
|
{
|
||||||
|
const size_t available = TCapacity - (m_Start + m_Size);
|
||||||
|
outPtr = m_Items + m_Start + m_Size;
|
||||||
|
if (available < quantity)
|
||||||
|
{
|
||||||
|
m_Size += available;
|
||||||
|
return available;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
m_Size += quantity;
|
||||||
|
return quantity;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
const size_t available = TCapacity - m_Size;
|
||||||
|
outPtr = m_Items + ((m_Start + m_Size) % TCapacity);
|
||||||
|
if (available < quantity)
|
||||||
|
{
|
||||||
|
m_Size += available;
|
||||||
|
return available;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
m_Size += quantity;
|
||||||
|
return quantity;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static const size_t CAPACITY = TCapacity;
|
static const size_t CAPACITY = TCapacity;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
TItem m_Items[TCapacity];
|
TItem m_Items[TCapacity];
|
||||||
size_t m_Size;
|
size_t m_Size;
|
||||||
size_t m_Start;
|
size_t m_Start;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
struct IGpAudioChannelCallbacks;
|
struct IGpAudioChannelCallbacks;
|
||||||
|
|
||||||
struct IGpAudioChannel
|
struct IGpAudioChannel
|
||||||
{
|
{
|
||||||
virtual void SetAudioChannelContext(IGpAudioChannelCallbacks *callbacks) = 0;
|
virtual void SetAudioChannelContext(IGpAudioChannelCallbacks *callbacks) = 0;
|
||||||
virtual void PostBuffer(const void *buffer, size_t bufferSize) = 0;
|
virtual void PostBuffer(const void *buffer, size_t bufferSize) = 0;
|
||||||
virtual void Stop() = 0;
|
virtual void Stop() = 0;
|
||||||
virtual void Destroy() = 0;
|
virtual void Destroy() = 0;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -5,7 +5,12 @@
|
|||||||
#include "EGpFontHandlerType.h"
|
#include "EGpFontHandlerType.h"
|
||||||
#include "EGpInputDriverType.h"
|
#include "EGpInputDriverType.h"
|
||||||
|
|
||||||
struct IGpLogDriver;
|
struct IGpLogDriver;
|
||||||
|
|
||||||
|
namespace PortabilityLayer
|
||||||
|
{
|
||||||
|
class HostSystemServices;
|
||||||
|
}
|
||||||
|
|
||||||
struct GpGlobalConfig
|
struct GpGlobalConfig
|
||||||
{
|
{
|
||||||
@@ -17,6 +22,7 @@ struct GpGlobalConfig
|
|||||||
size_t m_numInputDrivers;
|
size_t m_numInputDrivers;
|
||||||
|
|
||||||
IGpLogDriver *m_logger;
|
IGpLogDriver *m_logger;
|
||||||
|
PortabilityLayer::HostSystemServices *m_systemServices;
|
||||||
void *m_osGlobals;
|
void *m_osGlobals;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -85,6 +85,7 @@ int GpMain::Run()
|
|||||||
adProps.m_debug = true;
|
adProps.m_debug = true;
|
||||||
#endif
|
#endif
|
||||||
adProps.m_logger = g_gpGlobalConfig.m_logger;
|
adProps.m_logger = g_gpGlobalConfig.m_logger;
|
||||||
|
adProps.m_systemServices = g_gpGlobalConfig.m_systemServices;
|
||||||
|
|
||||||
IGpInputDriver **inputDrivers = static_cast<IGpInputDriver**>(malloc(sizeof(IGpInputDriver*) * g_gpGlobalConfig.m_numInputDrivers));
|
IGpInputDriver **inputDrivers = static_cast<IGpInputDriver**>(malloc(sizeof(IGpInputDriver*) * g_gpGlobalConfig.m_numInputDrivers));
|
||||||
|
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ namespace PortabilityLayer
|
|||||||
virtual int64_t GetTime() const = 0;
|
virtual int64_t GetTime() const = 0;
|
||||||
virtual void GetLocalDateTime(unsigned int &year, unsigned int &month, unsigned int &day, unsigned int &hour, unsigned int &minute, unsigned int &second) const = 0;
|
virtual void GetLocalDateTime(unsigned int &year, unsigned int &month, unsigned int &day, unsigned int &hour, unsigned int &minute, unsigned int &second) const = 0;
|
||||||
virtual HostMutex *CreateMutex() = 0;
|
virtual HostMutex *CreateMutex() = 0;
|
||||||
|
virtual HostMutex *CreateRecursiveMutex() = 0;
|
||||||
virtual HostThreadEvent *CreateThreadEvent(bool autoReset, bool startSignaled) = 0;
|
virtual HostThreadEvent *CreateThreadEvent(bool autoReset, bool startSignaled) = 0;
|
||||||
virtual size_t GetFreeMemoryCosmetic() const = 0; // Returns free memory in bytes, does not have to be accurate
|
virtual size_t GetFreeMemoryCosmetic() const = 0; // Returns free memory in bytes, does not have to be accurate
|
||||||
virtual void Beep() const = 0;
|
virtual void Beep() const = 0;
|
||||||
|
|||||||
Reference in New Issue
Block a user