mirror of
https://github.com/elasota/Aerofoil.git
synced 2026-02-04 10:38:52 +00:00
Merge branch 'master' into mac
# Conflicts: # AerofoilX/GpMain_SDL_X.cpp
This commit is contained in:
@@ -84,6 +84,7 @@
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="..\AerofoilPortable\GpAllocator_C.cpp" />
|
||||
<ClCompile Include="..\Aerofoil\GpColorCursor_Win32.cpp" />
|
||||
<ClCompile Include="..\Aerofoil\GpFileStream_Win32.cpp" />
|
||||
<ClCompile Include="..\Aerofoil\GpFileSystem_Win32.cpp" />
|
||||
|
||||
@@ -66,6 +66,9 @@
|
||||
<ClCompile Include="GpInputDriver_SDL_Gamepad.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\AerofoilPortable\GpAllocator_C.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="ShaderCode\Functions.h">
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#include "CoreDefs.h"
|
||||
#include "IGpAudioBuffer.h"
|
||||
#include "IGpAudioDriver.h"
|
||||
#include "IGpAudioChannel.h"
|
||||
#include "IGpAudioChannelCallbacks.h"
|
||||
@@ -13,13 +14,21 @@
|
||||
|
||||
#include "SDL_atomic.h"
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <new>
|
||||
#include <stdio.h>
|
||||
#include <chrono>
|
||||
|
||||
class GpAudioDriver_SDL2;
|
||||
|
||||
typedef std::chrono::high_resolution_clock::duration GpAudioDriver_SDL2_Duration_t;
|
||||
typedef std::chrono::high_resolution_clock::time_point GpAudioDriver_SDL2_TimePoint_t;
|
||||
typedef std::chrono::high_resolution_clock::period GpAudioDriver_SDL2_Period_t;
|
||||
typedef std::chrono::high_resolution_clock::rep GpAudioDriver_SDL2_Rep_t;
|
||||
|
||||
static void *AlignedAlloc(size_t size, size_t alignment)
|
||||
{
|
||||
void *storage = malloc(size + alignment);
|
||||
@@ -46,65 +55,130 @@ static void AlignedFree(void *ptr)
|
||||
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;
|
||||
};
|
||||
|
||||
class GP_ALIGNED(GP_SYSTEM_MEMORY_ALIGNMENT) GpAudioChannel_SDL2 final : public IGpAudioChannel
|
||||
class GpAudioBuffer_SDL2 final : public IGpAudioBuffer
|
||||
{
|
||||
public:
|
||||
enum ChannelState
|
||||
{
|
||||
ChannelState_Idle,
|
||||
ChannelState_Playing,
|
||||
ChannelState_Stopped,
|
||||
};
|
||||
static GpAudioBuffer_SDL2 *Create(const void *data, size_t size);
|
||||
|
||||
void AddRef() override;
|
||||
void Release() override;
|
||||
|
||||
const void *GetData() const;
|
||||
size_t GetSize() const;
|
||||
|
||||
private:
|
||||
GpAudioBuffer_SDL2(const void *data, size_t size);
|
||||
~GpAudioBuffer_SDL2();
|
||||
|
||||
void Destroy();
|
||||
|
||||
const void *m_data;
|
||||
size_t m_size;
|
||||
SDL_atomic_t m_count;
|
||||
};
|
||||
|
||||
GpAudioBuffer_SDL2 *GpAudioBuffer_SDL2::Create(const void *data, size_t size)
|
||||
{
|
||||
void *storage = malloc(size + sizeof(GpAudioBuffer_SDL2));
|
||||
if (!storage)
|
||||
return nullptr;
|
||||
|
||||
void *dataPos = static_cast<uint8_t*>(storage) + sizeof(GpAudioBuffer_SDL2);
|
||||
memcpy(dataPos, data, size);
|
||||
|
||||
return new (storage) GpAudioBuffer_SDL2(dataPos, size);
|
||||
}
|
||||
|
||||
|
||||
void GpAudioBuffer_SDL2::AddRef()
|
||||
{
|
||||
SDL_AtomicAdd(&m_count, 1);
|
||||
}
|
||||
|
||||
void GpAudioBuffer_SDL2::Release()
|
||||
{
|
||||
int prevCount = SDL_AtomicAdd(&m_count, -1);
|
||||
if (prevCount == 1)
|
||||
this->Destroy();
|
||||
}
|
||||
|
||||
const void *GpAudioBuffer_SDL2::GetData() const
|
||||
{
|
||||
return m_data;
|
||||
}
|
||||
|
||||
size_t GpAudioBuffer_SDL2::GetSize() const
|
||||
{
|
||||
return m_size;
|
||||
}
|
||||
|
||||
|
||||
GpAudioBuffer_SDL2::GpAudioBuffer_SDL2(const void *data, size_t size)
|
||||
: m_data(data)
|
||||
, m_size(size)
|
||||
{
|
||||
SDL_AtomicSet(&m_count, 1);
|
||||
}
|
||||
|
||||
GpAudioBuffer_SDL2::~GpAudioBuffer_SDL2()
|
||||
{
|
||||
}
|
||||
|
||||
void GpAudioBuffer_SDL2::Destroy()
|
||||
{
|
||||
this->~GpAudioBuffer_SDL2();
|
||||
free(this);
|
||||
}
|
||||
|
||||
|
||||
class GpAudioChannel_SDL2 final : public IGpAudioChannel
|
||||
{
|
||||
public:
|
||||
friend class GpAudioDriver_SDL2;
|
||||
|
||||
GpAudioChannel_SDL2();
|
||||
GpAudioChannel_SDL2(GpAudioDriver_SDL2_Duration_t latency, GpAudioDriver_SDL2_Duration_t bufferTime, size_t bufferSamplesMax, uint16_t sampleRate);
|
||||
~GpAudioChannel_SDL2();
|
||||
|
||||
void AddRef();
|
||||
void Release();
|
||||
|
||||
void SetAudioChannelContext(IGpAudioChannelCallbacks *callbacks) override;
|
||||
void PostBuffer(const void *buffer, size_t bufferSize) override;
|
||||
bool PostBuffer(IGpAudioBuffer *buffer) override;
|
||||
void Stop() override;
|
||||
void Destroy() override;
|
||||
|
||||
void Consume(uint8_t *output, size_t sz);
|
||||
void Consume(uint8_t *output, size_t sz, GpAudioDriver_SDL2_TimePoint_t mixStartTime, GpAudioDriver_SDL2_TimePoint_t mixEndTime);
|
||||
|
||||
static GpAudioChannel_SDL2 *Alloc(GpAudioDriver_SDL2 *driver);
|
||||
static GpAudioChannel_SDL2 *Alloc(GpAudioDriver_SDL2 *driver, GpAudioDriver_SDL2_Duration_t latency, GpAudioDriver_SDL2_Duration_t bufferTime, size_t bufferSamplesMax, uint16_t sampleRate);
|
||||
|
||||
private:
|
||||
bool Init(GpAudioDriver_SDL2 *driver);
|
||||
|
||||
static const size_t kMaxBuffers = 16;
|
||||
|
||||
IGpAudioChannelCallbacks *m_callbacks;
|
||||
IGpMutex *m_mutex;
|
||||
GpAudioDriver_SDL2 *m_owner;
|
||||
|
||||
SDL_atomic_t m_refCount;
|
||||
|
||||
GpAudioChannelBufferChain_SDL2 *m_firstPendingBuffer;
|
||||
GpAudioChannelBufferChain_SDL2 *m_lastPendingBuffer;
|
||||
GpAudioBuffer_SDL2 *m_pendingBuffers[kMaxBuffers];
|
||||
size_t m_nextPendingBufferConsumePos;
|
||||
size_t m_nextPendingBufferInsertionPos;
|
||||
size_t m_numQueuedBuffers;
|
||||
size_t m_firstBufferSamplesConsumed;
|
||||
|
||||
ChannelState m_channelState;
|
||||
GpAudioDriver_SDL2_TimePoint_t m_timestamp; // Time that audio will be consumed if posted to the channel, if m_hasTimestamp is true.
|
||||
GpAudioDriver_SDL2_Duration_t m_latency;
|
||||
GpAudioDriver_SDL2_Duration_t m_bufferTime;
|
||||
size_t m_bufferSamplesMax;
|
||||
size_t m_leadingSilence;
|
||||
uint16_t m_sampleRate;
|
||||
bool m_isMixing;
|
||||
bool m_hasTimestamp;
|
||||
};
|
||||
|
||||
class GP_ALIGNED(GP_SYSTEM_MEMORY_ALIGNMENT) GpAudioDriver_SDL2 final : public IGpAudioDriver, public IGpPrefsHandler
|
||||
class GpAudioDriver_SDL2 final : public IGpAudioDriver, public IGpPrefsHandler
|
||||
{
|
||||
public:
|
||||
friend class GpAudioChannel_SDL2;
|
||||
@@ -112,6 +186,7 @@ public:
|
||||
explicit GpAudioDriver_SDL2(const GpAudioDriverProperties &properties);
|
||||
~GpAudioDriver_SDL2();
|
||||
|
||||
IGpAudioBuffer *CreateBuffer(const void *data, size_t size) override;
|
||||
IGpAudioChannel *CreateChannel() override;
|
||||
void SetMasterVolume(uint32_t vol, uint32_t maxVolume) override;
|
||||
void Shutdown() override;
|
||||
@@ -122,25 +197,32 @@ public:
|
||||
|
||||
bool Init();
|
||||
|
||||
static GpAudioDriver_SDL2_TimePoint_t GetCurrentTime();
|
||||
|
||||
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);
|
||||
void RefillMixChunk(GpAudioChannel_SDL2 *const*channels, size_t numChannels, size_t maxSamplesToFill, GpAudioDriver_SDL2_TimePoint_t mixStartTime, GpAudioDriver_SDL2_TimePoint_t mixEndTime);
|
||||
|
||||
GpAudioDriverProperties m_properties;
|
||||
IGpMutex *m_mutex;
|
||||
IGpMutex *m_mixState;
|
||||
|
||||
static const size_t kMaxChannels = 16;
|
||||
static const size_t kMixChunkSize = 256;
|
||||
static const int16_t kMaxAudioVolumeScale = 25;
|
||||
static const size_t kMixChunkSize = 512;
|
||||
static const int16_t kMaxAudioVolumeScale = 64;
|
||||
|
||||
GpAudioChannel_SDL2 *m_channels[kMaxChannels];
|
||||
size_t m_numChannels;
|
||||
|
||||
unsigned int m_sampleRate;
|
||||
GpAudioDriver_SDL2_Duration_t m_latency;
|
||||
GpAudioDriver_SDL2_Duration_t m_bufferTime;
|
||||
size_t m_bufferSamples;
|
||||
|
||||
bool m_sdlAudioRunning;
|
||||
|
||||
GP_ALIGNED(GP_SYSTEM_MEMORY_ALIGNMENT) int16_t m_mixChunk[kMixChunkSize];
|
||||
@@ -149,36 +231,24 @@ private:
|
||||
int16_t m_audioVolumeScale;
|
||||
};
|
||||
|
||||
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()
|
||||
GpAudioChannel_SDL2::GpAudioChannel_SDL2(GpAudioDriver_SDL2_Duration_t latency, GpAudioDriver_SDL2_Duration_t bufferTime, size_t bufferSamplesMax, uint16_t sampleRate)
|
||||
: m_callbacks(nullptr)
|
||||
, m_mutex(nullptr)
|
||||
, m_owner(nullptr)
|
||||
, m_firstPendingBuffer(nullptr)
|
||||
, m_lastPendingBuffer(nullptr)
|
||||
, m_latency(latency)
|
||||
, m_bufferTime(bufferTime)
|
||||
, m_bufferSamplesMax(bufferSamplesMax)
|
||||
, m_leadingSilence(0)
|
||||
, m_sampleRate(sampleRate)
|
||||
, m_isMixing(false)
|
||||
, m_hasTimestamp(false)
|
||||
, m_nextPendingBufferConsumePos(0)
|
||||
, m_nextPendingBufferInsertionPos(0)
|
||||
, m_numQueuedBuffers(0)
|
||||
, m_firstBufferSamplesConsumed(0)
|
||||
{
|
||||
SDL_AtomicSet(&m_refCount, 1);
|
||||
}
|
||||
@@ -190,12 +260,7 @@ GpAudioChannel_SDL2::~GpAudioChannel_SDL2()
|
||||
if (m_mutex)
|
||||
m_mutex->Destroy();
|
||||
|
||||
while (m_firstPendingBuffer)
|
||||
{
|
||||
GpAudioChannelBufferChain_SDL2 *buffer = m_firstPendingBuffer;
|
||||
m_firstPendingBuffer = buffer->m_next;
|
||||
buffer->Release();
|
||||
}
|
||||
assert(m_numQueuedBuffers == 0);
|
||||
}
|
||||
|
||||
void GpAudioChannel_SDL2::AddRef()
|
||||
@@ -218,54 +283,70 @@ void GpAudioChannel_SDL2::SetAudioChannelContext(IGpAudioChannelCallbacks *callb
|
||||
m_callbacks = callbacks;
|
||||
}
|
||||
|
||||
void GpAudioChannel_SDL2::PostBuffer(const void *buffer, size_t bufferSize)
|
||||
bool GpAudioChannel_SDL2::PostBuffer(IGpAudioBuffer *buffer)
|
||||
{
|
||||
buffer->AddRef();
|
||||
|
||||
m_mutex->Lock();
|
||||
|
||||
while (bufferSize > 0)
|
||||
if (m_numQueuedBuffers == kMaxBuffers)
|
||||
{
|
||||
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();
|
||||
buffer->Release();
|
||||
return false;
|
||||
}
|
||||
|
||||
size_t leadingSilence = 0;
|
||||
if (m_numQueuedBuffers == 0 && m_hasTimestamp && !m_isMixing)
|
||||
{
|
||||
GpAudioDriver_SDL2_TimePoint_t queueTime = GpAudioDriver_SDL2::GetCurrentTime() + m_latency;
|
||||
if (queueTime > m_timestamp)
|
||||
{
|
||||
const GpAudioDriver_SDL2_Duration_t leadTime = queueTime - m_timestamp;
|
||||
|
||||
if (leadTime > m_bufferTime)
|
||||
leadingSilence = m_bufferSamplesMax;
|
||||
else
|
||||
{
|
||||
const GpAudioDriver_SDL2_Rep_t leadTimeRep = leadTime.count();
|
||||
leadingSilence = leadTimeRep * static_cast<GpAudioDriver_SDL2_Rep_t>(m_sampleRate) * GpAudioDriver_SDL2_Period_t::num / GpAudioDriver_SDL2_Period_t::den;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
m_leadingSilence = leadingSilence;
|
||||
|
||||
m_pendingBuffers[m_nextPendingBufferInsertionPos++] = static_cast<GpAudioBuffer_SDL2*>(buffer);
|
||||
m_numQueuedBuffers++;
|
||||
|
||||
m_nextPendingBufferInsertionPos = m_nextPendingBufferInsertionPos % kMaxBuffers;
|
||||
|
||||
m_mutex->Unlock();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void GpAudioChannel_SDL2::Stop()
|
||||
{
|
||||
m_mutex->Lock();
|
||||
|
||||
GpAudioChannelBufferChain_SDL2 *buffer = m_firstPendingBuffer;
|
||||
m_firstPendingBuffer = nullptr;
|
||||
m_lastPendingBuffer = nullptr;
|
||||
m_leadingSilence = 0;
|
||||
|
||||
while (buffer)
|
||||
size_t numBuffersToDischarge = m_numQueuedBuffers;
|
||||
|
||||
for (size_t i = 0; i < numBuffersToDischarge; i++)
|
||||
{
|
||||
if (buffer->m_hasTrigger && m_callbacks)
|
||||
GpAudioBuffer_SDL2 *buffer = m_pendingBuffers[m_nextPendingBufferConsumePos];
|
||||
|
||||
m_nextPendingBufferConsumePos = (m_nextPendingBufferConsumePos + 1) % kMaxBuffers;
|
||||
m_numQueuedBuffers--;
|
||||
|
||||
m_firstBufferSamplesConsumed = 0;
|
||||
|
||||
if (m_callbacks)
|
||||
m_callbacks->NotifyBufferFinished();
|
||||
|
||||
GpAudioChannelBufferChain_SDL2 *nextBuffer = buffer->m_next;
|
||||
buffer->Release();
|
||||
|
||||
buffer = nextBuffer;
|
||||
}
|
||||
|
||||
m_mutex->Unlock();
|
||||
@@ -289,25 +370,57 @@ bool GpAudioChannel_SDL2::Init(GpAudioDriver_SDL2 *driver)
|
||||
return true;
|
||||
}
|
||||
|
||||
void GpAudioChannel_SDL2::Consume(uint8_t *output, size_t sz)
|
||||
void GpAudioChannel_SDL2::Consume(uint8_t *output, size_t sz, GpAudioDriver_SDL2_TimePoint_t mixStartTime, GpAudioDriver_SDL2_TimePoint_t mixEndTime)
|
||||
{
|
||||
m_mutex->Lock();
|
||||
|
||||
while (m_firstPendingBuffer != nullptr)
|
||||
m_isMixing = true;
|
||||
m_hasTimestamp = true;
|
||||
m_timestamp = mixEndTime;
|
||||
|
||||
if (sz <= m_leadingSilence)
|
||||
{
|
||||
GpAudioChannelBufferChain_SDL2 *buffer = m_firstPendingBuffer;
|
||||
const size_t available = (buffer->m_used - buffer->m_consumed);
|
||||
memset(output, 0x80, sz);
|
||||
m_leadingSilence -= sz;
|
||||
|
||||
m_isMixing = false;
|
||||
m_mutex->Unlock();
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
size_t leadingSilence = m_leadingSilence;
|
||||
if (leadingSilence > 0)
|
||||
{
|
||||
memset(output, 0x80, leadingSilence);
|
||||
output += leadingSilence;
|
||||
sz -= leadingSilence;
|
||||
|
||||
m_leadingSilence = 0;
|
||||
}
|
||||
}
|
||||
|
||||
while (m_numQueuedBuffers > 0)
|
||||
{
|
||||
GpAudioBuffer_SDL2 *buffer = m_pendingBuffers[m_nextPendingBufferConsumePos];
|
||||
const void *bufferData = buffer->GetData();
|
||||
const size_t bufferSize = buffer->GetSize();
|
||||
|
||||
assert(m_firstBufferSamplesConsumed < bufferSize);
|
||||
|
||||
const size_t available = (bufferSize - m_firstBufferSamplesConsumed);
|
||||
if (available <= sz)
|
||||
{
|
||||
memcpy(output, buffer->m_data + buffer->m_consumed, available);
|
||||
memcpy(output, static_cast<const uint8_t*>(bufferData) + m_firstBufferSamplesConsumed, available);
|
||||
sz -= available;
|
||||
output += available;
|
||||
|
||||
m_firstPendingBuffer = buffer->m_next;
|
||||
if (m_firstPendingBuffer == nullptr)
|
||||
m_lastPendingBuffer = nullptr;
|
||||
m_nextPendingBufferConsumePos = (m_nextPendingBufferConsumePos + 1) % kMaxBuffers;
|
||||
m_numQueuedBuffers--;
|
||||
|
||||
if (buffer->m_hasTrigger && m_callbacks)
|
||||
m_firstBufferSamplesConsumed = 0;
|
||||
|
||||
if (m_callbacks)
|
||||
m_callbacks->NotifyBufferFinished();
|
||||
|
||||
buffer->Release();
|
||||
@@ -317,26 +430,28 @@ void GpAudioChannel_SDL2::Consume(uint8_t *output, size_t sz)
|
||||
}
|
||||
else
|
||||
{
|
||||
memcpy(output, buffer->m_data + buffer->m_consumed, sz);
|
||||
buffer->m_consumed += sz;
|
||||
buffer += sz;
|
||||
memcpy(output, static_cast<const uint8_t*>(bufferData) + m_firstBufferSamplesConsumed, sz);
|
||||
m_firstBufferSamplesConsumed += sz;
|
||||
output += sz;
|
||||
sz = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
m_isMixing = false;
|
||||
|
||||
m_mutex->Unlock();
|
||||
|
||||
memset(output, 0x80, sz);
|
||||
}
|
||||
|
||||
GpAudioChannel_SDL2 *GpAudioChannel_SDL2::Alloc(GpAudioDriver_SDL2 *driver)
|
||||
GpAudioChannel_SDL2 *GpAudioChannel_SDL2::Alloc(GpAudioDriver_SDL2 *driver, GpAudioDriver_SDL2_Duration_t latency, GpAudioDriver_SDL2_Duration_t bufferTime, size_t bufferSamplesMax, uint16_t sampleRate)
|
||||
{
|
||||
void *storage = AlignedAlloc(sizeof(GpAudioChannel_SDL2), GP_SYSTEM_MEMORY_ALIGNMENT);
|
||||
if (!storage)
|
||||
return nullptr;
|
||||
|
||||
GpAudioChannel_SDL2 *channel = new (storage) GpAudioChannel_SDL2();
|
||||
GpAudioChannel_SDL2 *channel = new (storage) GpAudioChannel_SDL2(latency, bufferTime, bufferSamplesMax, sampleRate);
|
||||
if (!channel->Init(driver))
|
||||
{
|
||||
channel->Destroy();
|
||||
@@ -354,6 +469,9 @@ GpAudioDriver_SDL2::GpAudioDriver_SDL2(const GpAudioDriverProperties &properties
|
||||
: m_properties(properties)
|
||||
, m_mutex(nullptr)
|
||||
, m_numChannels(0)
|
||||
, m_sampleRate(0)
|
||||
, m_latency(GpAudioDriver_SDL2_Duration_t::zero())
|
||||
, m_bufferTime(GpAudioDriver_SDL2_Duration_t::zero())
|
||||
, m_sdlAudioRunning(false)
|
||||
, m_mixChunkReadOffset(kMixChunkSize)
|
||||
, m_audioVolumeScale(kMaxAudioVolumeScale)
|
||||
@@ -375,9 +493,14 @@ GpAudioDriver_SDL2::~GpAudioDriver_SDL2()
|
||||
m_mutex->Destroy();
|
||||
}
|
||||
|
||||
IGpAudioBuffer *GpAudioDriver_SDL2::CreateBuffer(const void *data, size_t size)
|
||||
{
|
||||
return GpAudioBuffer_SDL2::Create(data, size);
|
||||
}
|
||||
|
||||
IGpAudioChannel *GpAudioDriver_SDL2::CreateChannel()
|
||||
{
|
||||
GpAudioChannel_SDL2 *newChannel = GpAudioChannel_SDL2::Alloc(this);
|
||||
GpAudioChannel_SDL2 *newChannel = GpAudioChannel_SDL2::Alloc(this, m_latency, m_bufferTime, m_bufferSamples, m_sampleRate);
|
||||
if (!newChannel)
|
||||
return nullptr;
|
||||
|
||||
@@ -437,7 +560,7 @@ bool GpAudioDriver_SDL2::Init()
|
||||
requestedSpec.channels = 1;
|
||||
requestedSpec.format = AUDIO_S16;
|
||||
requestedSpec.freq = m_properties.m_sampleRate;
|
||||
requestedSpec.samples = 1024;
|
||||
requestedSpec.samples = kMixChunkSize;
|
||||
requestedSpec.userdata = this;
|
||||
|
||||
if (SDL_OpenAudio(&requestedSpec, nullptr))
|
||||
@@ -450,6 +573,10 @@ bool GpAudioDriver_SDL2::Init()
|
||||
SDL_PauseAudio(0);
|
||||
|
||||
m_sdlAudioRunning = true;
|
||||
m_sampleRate = requestedSpec.freq;
|
||||
m_latency = GpAudioDriver_SDL2_Duration_t(static_cast<GpAudioDriver_SDL2_Rep_t>(GpAudioDriver_SDL2_Period_t::den * requestedSpec.samples / GpAudioDriver_SDL2_Period_t::num / m_sampleRate));
|
||||
m_bufferTime = GpAudioDriver_SDL2_Duration_t(static_cast<GpAudioDriver_SDL2_Rep_t>(GpAudioDriver_SDL2_Period_t::den * requestedSpec.samples / GpAudioDriver_SDL2_Period_t::num / m_sampleRate));
|
||||
m_bufferSamples = requestedSpec.samples;
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -492,8 +619,13 @@ void GpAudioDriver_SDL2::MixAudio(void *stream, size_t len)
|
||||
}
|
||||
m_mutex->Unlock();
|
||||
|
||||
size_t samplesRemaining = len / sizeof(int16_t);
|
||||
const size_t totalSamples = len / sizeof(int16_t);
|
||||
size_t samplesRemaining = totalSamples;
|
||||
|
||||
GpAudioDriver_SDL2_TimePoint_t audioMixStartTime = GpAudioDriver_SDL2::GetCurrentTime();
|
||||
GpAudioDriver_SDL2_TimePoint_t audioMixBlockStartTime = audioMixStartTime;
|
||||
|
||||
size_t samplesSinceStart = 0;
|
||||
for (;;)
|
||||
{
|
||||
size_t availableInMixChunk = kMixChunkSize - m_mixChunkReadOffset;
|
||||
@@ -510,10 +642,17 @@ void GpAudioDriver_SDL2::MixAudio(void *stream, size_t len)
|
||||
memcpy(stream, m_mixChunk + m_mixChunkReadOffset, availableInMixChunk * sizeof(int16_t));
|
||||
|
||||
stream = static_cast<int16_t*>(stream) + availableInMixChunk;
|
||||
samplesRemaining -= availableInMixChunk;
|
||||
|
||||
samplesSinceStart += availableInMixChunk;
|
||||
|
||||
GpAudioDriver_SDL2_Duration_t audioMixDurationSinceStart = GpAudioDriver_SDL2_Duration_t(static_cast<GpAudioDriver_SDL2_Rep_t>(GpAudioDriver_SDL2_Period_t::den * samplesSinceStart / GpAudioDriver_SDL2_Period_t::num / m_sampleRate));
|
||||
GpAudioDriver_SDL2_TimePoint_t audioMixBlockEndTime = audioMixStartTime + audioMixDurationSinceStart;
|
||||
|
||||
m_mixChunkReadOffset = 0;
|
||||
RefillMixChunk(mixingChannels, numChannels);
|
||||
RefillMixChunk(mixingChannels, numChannels, samplesRemaining, audioMixBlockStartTime, audioMixBlockEndTime);
|
||||
audioMixBlockStartTime = audioMixBlockEndTime;
|
||||
|
||||
samplesRemaining -= availableInMixChunk;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -521,7 +660,7 @@ void GpAudioDriver_SDL2::MixAudio(void *stream, size_t len)
|
||||
mixingChannels[i]->Release();
|
||||
}
|
||||
|
||||
void GpAudioDriver_SDL2::RefillMixChunk(GpAudioChannel_SDL2 *const*channels, size_t numChannels)
|
||||
void GpAudioDriver_SDL2::RefillMixChunk(GpAudioChannel_SDL2 *const*channels, size_t numChannels, size_t maxSamplesToFill, GpAudioDriver_SDL2_TimePoint_t mixStartTime, GpAudioDriver_SDL2_TimePoint_t mixEndTime)
|
||||
{
|
||||
uint8_t audioMixBufferUnaligned[kMixChunkSize + GP_SYSTEM_MEMORY_ALIGNMENT];
|
||||
uint8_t *audioMixBuffer = audioMixBufferUnaligned;
|
||||
@@ -536,28 +675,52 @@ void GpAudioDriver_SDL2::RefillMixChunk(GpAudioChannel_SDL2 *const*channels, siz
|
||||
|
||||
const int16_t audioVolumeScale = m_audioVolumeScale;
|
||||
|
||||
size_t samplesToFill = kMixChunkSize;
|
||||
if (samplesToFill > maxSamplesToFill)
|
||||
{
|
||||
m_mixChunkReadOffset += samplesToFill - maxSamplesToFill;
|
||||
samplesToFill = maxSamplesToFill;
|
||||
}
|
||||
else
|
||||
m_mixChunkReadOffset = 0;
|
||||
|
||||
int16_t *mixChunkStart = m_mixChunk + m_mixChunkReadOffset;
|
||||
int16_t audioNormalizeShift = 0;
|
||||
|
||||
for (size_t i = 0; i < numChannels; i++)
|
||||
{
|
||||
channels[i]->Consume(audioMixBuffer, kMixChunkSize);
|
||||
channels[i]->Consume(audioMixBuffer, samplesToFill, mixStartTime, mixEndTime);
|
||||
|
||||
if (i == 0)
|
||||
{
|
||||
noAudio = false;
|
||||
for (size_t j = 0; j < kMixChunkSize; j++)
|
||||
m_mixChunk[j] = (static_cast<int16_t>(audioMixBuffer[j]) - 0x80) * audioVolumeScale;
|
||||
audioNormalizeShift = 0x80;
|
||||
for (size_t j = 0; j < samplesToFill; j++)
|
||||
mixChunkStart[j] = static_cast<int16_t>(audioMixBuffer[j]);
|
||||
}
|
||||
else
|
||||
{
|
||||
for (size_t j = 0; j < kMixChunkSize; j++)
|
||||
m_mixChunk[j] += (static_cast<int16_t>(audioMixBuffer[j]) - 0x80) * audioVolumeScale;
|
||||
audioNormalizeShift += 0x80;
|
||||
for (size_t j = 0; j < samplesToFill; j++)
|
||||
mixChunkStart[j] += static_cast<int16_t>(audioMixBuffer[j]);
|
||||
}
|
||||
}
|
||||
|
||||
if (noAudio)
|
||||
memset(m_mixChunk, 0, kMixChunkSize * sizeof(m_mixChunk[0]));
|
||||
memset(mixChunkStart, 0, samplesToFill * sizeof(mixChunkStart[0]));
|
||||
else
|
||||
{
|
||||
for (size_t i = 0; i < samplesToFill; i++)
|
||||
mixChunkStart[i] = (mixChunkStart[i] - audioNormalizeShift) * audioVolumeScale;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
GpAudioDriver_SDL2_TimePoint_t GpAudioDriver_SDL2::GetCurrentTime()
|
||||
{
|
||||
return std::chrono::high_resolution_clock::now();
|
||||
}
|
||||
|
||||
IGpAudioDriver *GpDriver_CreateAudioDriver_SDL(const GpAudioDriverProperties &properties)
|
||||
{
|
||||
void *storage = AlignedAlloc(sizeof(GpAudioDriver_SDL2), GP_SYSTEM_MEMORY_ALIGNMENT);
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
#include "GpRingBuffer.h"
|
||||
#include "GpInputDriver_SDL_Gamepad.h"
|
||||
#include "GpSDL.h"
|
||||
#include "GpUnicode.h"
|
||||
#include "IGpCursor.h"
|
||||
#include "IGpDisplayDriverSurface.h"
|
||||
#include "IGpLogDriver.h"
|
||||
@@ -52,68 +53,6 @@ struct GpDisplayDriver_SDL_GL2_Prefs
|
||||
bool m_isFullScreen;
|
||||
};
|
||||
|
||||
namespace DeleteMe
|
||||
{
|
||||
bool DecodeCodePoint(const uint8_t *characters, size_t availableCharacters, size_t &outCharactersDigested, uint32_t &outCodePoint)
|
||||
{
|
||||
if (availableCharacters <= 0)
|
||||
return false;
|
||||
|
||||
if ((characters[0] & 0x80) == 0x00)
|
||||
{
|
||||
outCharactersDigested = 1;
|
||||
outCodePoint = characters[0];
|
||||
return true;
|
||||
}
|
||||
|
||||
size_t sz = 0;
|
||||
uint32_t codePoint = 0;
|
||||
uint32_t minCodePoint = 0;
|
||||
if ((characters[0] & 0xe0) == 0xc0)
|
||||
{
|
||||
sz = 2;
|
||||
minCodePoint = 0x80;
|
||||
codePoint = (characters[0] & 0x1f);
|
||||
}
|
||||
else if ((characters[0] & 0xf0) == 0xe0)
|
||||
{
|
||||
sz = 3;
|
||||
minCodePoint = 0x800;
|
||||
codePoint = (characters[0] & 0x0f);
|
||||
}
|
||||
else if ((characters[0] & 0xf8) == 0xf0)
|
||||
{
|
||||
sz = 4;
|
||||
minCodePoint = 0x10000;
|
||||
codePoint = (characters[0] & 0x07);
|
||||
}
|
||||
else
|
||||
return false;
|
||||
|
||||
if (availableCharacters < sz)
|
||||
return false;
|
||||
|
||||
for (size_t auxByte = 1; auxByte < sz; auxByte++)
|
||||
{
|
||||
if ((characters[auxByte] & 0xc0) != 0x80)
|
||||
return false;
|
||||
|
||||
codePoint = (codePoint << 6) | (characters[auxByte] & 0x3f);
|
||||
}
|
||||
|
||||
if (codePoint < minCodePoint || codePoint > 0x10ffff)
|
||||
return false;
|
||||
|
||||
if (codePoint >= 0xd800 && codePoint <= 0xdfff)
|
||||
return false;
|
||||
|
||||
outCodePoint = codePoint;
|
||||
outCharactersDigested = sz;
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
namespace GpBinarizedShaders
|
||||
{
|
||||
extern const char *g_drawQuadV_GL2;
|
||||
@@ -846,9 +785,6 @@ private:
|
||||
GpComPtr<GpGLRenderTargetView> m_upscaleTextureRTV;
|
||||
GpComPtr<GpGLTexture> m_upscaleTexture;
|
||||
|
||||
uint32_t m_upscaleTextureWidth;
|
||||
uint32_t m_upscaleTextureHeight;
|
||||
|
||||
GpComPtr<GpGLVertexArray> m_quadVertexArray;
|
||||
GpComPtr<GpGLBuffer> m_quadVertexBufferKeepalive;
|
||||
GpComPtr<GpGLBuffer> m_quadIndexBuffer;
|
||||
@@ -905,7 +841,10 @@ private:
|
||||
uint32_t m_initialHeightVirtual;
|
||||
float m_pixelScaleX;
|
||||
float m_pixelScaleY;
|
||||
|
||||
bool m_useUpscaleFilter;
|
||||
uint32_t m_upscaleTextureWidth;
|
||||
uint32_t m_upscaleTextureHeight;
|
||||
|
||||
GpCursor_SDL2 *m_activeCursor;
|
||||
GpCursor_SDL2 *m_pendingCursor;
|
||||
@@ -1197,6 +1136,8 @@ GpDisplayDriver_SDL_GL2::GpDisplayDriver_SDL_GL2(const GpDisplayDriverProperties
|
||||
, m_pixelScaleX(1.0f)
|
||||
, m_pixelScaleY(1.0f)
|
||||
, m_useUpscaleFilter(false)
|
||||
, m_upscaleTextureWidth(0)
|
||||
, m_upscaleTextureHeight(0)
|
||||
, m_pendingCursor(nullptr)
|
||||
, m_activeCursor(nullptr)
|
||||
, m_currentStandardCursor(EGpStandardCursors::kArrow)
|
||||
@@ -1531,8 +1472,7 @@ void GpDisplayDriver_SDL_GL2::ServeTicks(int ticks)
|
||||
logger->Printf(IGpLogDriver::Category_Information, "Resetting OpenGL context. Physical: %i x %i Virtual %i x %i", static_cast<int>(m_windowWidthPhysical), static_cast<int>(m_windowHeightPhysical), static_cast<int>(m_windowWidthVirtual), static_cast<int>(m_windowHeightVirtual));
|
||||
|
||||
// Drop everything and reset
|
||||
m_res.~InstancedResources();
|
||||
new (&m_res) InstancedResources();
|
||||
m_res = InstancedResources();
|
||||
|
||||
if (m_firstSurface)
|
||||
m_firstSurface->DestroyAll();
|
||||
@@ -2040,7 +1980,7 @@ void GpDisplayDriver_SDL_GL2::TranslateSDLMessage(const SDL_Event *msg, IGpVOSEv
|
||||
{
|
||||
uint32_t codePoint;
|
||||
size_t numDigested;
|
||||
DeleteMe::DecodeCodePoint(reinterpret_cast<const uint8_t*>(teEvt->text) + parseOffset, lenUTF8 - parseOffset, numDigested, codePoint);
|
||||
GpUnicode::UTF8::Decode(reinterpret_cast<const uint8_t*>(teEvt->text) + parseOffset, lenUTF8 - parseOffset, numDigested, codePoint);
|
||||
|
||||
parseOffset += numDigested;
|
||||
|
||||
@@ -2458,11 +2398,6 @@ bool GpDisplayDriver_SDL_GL2::InitResources(uint32_t physicalWidth, uint32_t phy
|
||||
if (logger)
|
||||
logger->Printf(IGpLogDriver::Category_Information, "GpDisplayDriver_SDL_GL2::InitResources");
|
||||
|
||||
if ((m_pixelScaleX < 2.0f && m_pixelScaleX > 1.0f) || (m_pixelScaleY < 2.0f && m_pixelScaleY > 1.0f))
|
||||
m_useUpscaleFilter = true;
|
||||
else
|
||||
m_useUpscaleFilter = false;
|
||||
|
||||
CheckGLError(m_gl, logger);
|
||||
|
||||
if (!InitBackBuffer(virtualWidth, virtualHeight))
|
||||
@@ -2776,8 +2711,9 @@ bool GpDisplayDriver_SDL_GL2::InitBackBuffer(uint32_t width, uint32_t height)
|
||||
m_gl.BindFramebuffer(GL_FRAMEBUFFER, 0);
|
||||
}
|
||||
|
||||
m_useUpscaleFilter = ((m_pixelScaleX < 2.0f && m_pixelScaleX > 1.0f) || (m_pixelScaleY < 2.0f && m_pixelScaleY > 1.0f));
|
||||
|
||||
if (m_pixelScaleX != floor(m_pixelScaleX) || m_pixelScaleY != floor(m_pixelScaleY))
|
||||
if (m_useUpscaleFilter)
|
||||
{
|
||||
uint32_t upscaleX = ceil(m_pixelScaleX);
|
||||
uint32_t upscaleY = ceil(m_pixelScaleY);
|
||||
@@ -2792,8 +2728,8 @@ bool GpDisplayDriver_SDL_GL2::InitBackBuffer(uint32_t width, uint32_t height)
|
||||
return false;
|
||||
}
|
||||
|
||||
m_res.m_upscaleTextureWidth = width * upscaleX;
|
||||
m_res.m_upscaleTextureHeight = height * upscaleY;
|
||||
m_upscaleTextureWidth = width * upscaleX;
|
||||
m_upscaleTextureHeight = height * upscaleY;
|
||||
|
||||
GLenum internalFormat = SupportsSizedFormats() ? GL_RGBA8 : GL_RGBA;
|
||||
|
||||
@@ -2803,7 +2739,7 @@ bool GpDisplayDriver_SDL_GL2::InitBackBuffer(uint32_t width, uint32_t height)
|
||||
m_gl.TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||
m_gl.TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
||||
m_gl.TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
||||
m_gl.TexImage2D(GL_TEXTURE_2D, 0, internalFormat, m_res.m_upscaleTextureWidth, m_res.m_upscaleTextureHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
|
||||
m_gl.TexImage2D(GL_TEXTURE_2D, 0, internalFormat, m_upscaleTextureWidth, m_upscaleTextureHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
|
||||
m_gl.BindTexture(GL_TEXTURE_2D, 0);
|
||||
|
||||
CheckGLError(m_gl, logger);
|
||||
@@ -2848,7 +2784,7 @@ void GpDisplayDriver_SDL_GL2::ScaleVirtualScreen()
|
||||
{
|
||||
m_gl.BindFramebuffer(GL_FRAMEBUFFER, m_res.m_upscaleTextureRTV->GetID());
|
||||
|
||||
m_gl.Viewport(0, 0, m_res.m_upscaleTextureWidth, m_res.m_upscaleTextureHeight);
|
||||
m_gl.Viewport(0, 0, m_upscaleTextureWidth, m_upscaleTextureHeight);
|
||||
|
||||
const BlitQuadProgram &program = m_res.m_scaleQuadProgram;
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#include "SDL.h"
|
||||
|
||||
#include "GpMain.h"
|
||||
#include "GpAllocator_C.h"
|
||||
#include "GpAudioDriverFactory.h"
|
||||
#include "GpDisplayDriverFactory.h"
|
||||
#include "GpGlobalConfig.h"
|
||||
@@ -59,6 +60,7 @@ int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine
|
||||
drivers->SetDriver<GpDriverIDs::kFileSystem>(GpFileSystem_Win32::GetInstance());
|
||||
drivers->SetDriver<GpDriverIDs::kSystemServices>(GpSystemServices_Win32::GetInstance());
|
||||
drivers->SetDriver<GpDriverIDs::kLog>(GpLogDriver_Win32::GetInstance());
|
||||
drivers->SetDriver<GpDriverIDs::kAlloc>(GpAllocator_C::GetInstance());
|
||||
|
||||
g_gpWindowsGlobals.m_hInstance = hInstance;
|
||||
g_gpWindowsGlobals.m_hPrevInstance = hPrevInstance;
|
||||
@@ -86,6 +88,7 @@ int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine
|
||||
g_gpGlobalConfig.m_osGlobals = &g_gpWindowsGlobals;
|
||||
g_gpGlobalConfig.m_logger = logger;
|
||||
g_gpGlobalConfig.m_systemServices = GpSystemServices_Win32::GetInstance();
|
||||
g_gpGlobalConfig.m_allocator = GpAllocator_C::GetInstance();
|
||||
|
||||
GpDisplayDriverFactory::RegisterDisplayDriverFactory(EGpDisplayDriverType_SDL_GL2, GpDriver_CreateDisplayDriver_SDL_GL2);
|
||||
GpAudioDriverFactory::RegisterAudioDriverFactory(EGpAudioDriverType_SDL2, GpDriver_CreateAudioDriver_SDL);
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
" vec4 resultColor = vec4(SamplePixel(texCoord.xy), 1.0);\n"\
|
||||
" resultColor *= constants_Modulation;\n"\
|
||||
"#ifdef ENABLE_FLICKER\n"\
|
||||
" resultColor = ApplyFlicker(constants_FlickerAxis, texCoord.zw, constants_FlickerStartThreshold, constants_FlickerEndThreshold, resultColor * constants_Modulation);\n"\
|
||||
" resultColor = ApplyFlicker(constants_FlickerAxis, texCoord.zw, constants_FlickerStartThreshold, constants_FlickerEndThreshold, resultColor);\n"\
|
||||
" if (resultColor.a < 1.0)\n"\
|
||||
" discard;\n"\
|
||||
"#endif\n"\
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
" vec4 resultColor = vec4(SamplePixel(texCoord.xy), 1.0);\n"\
|
||||
" resultColor *= constants_Modulation;\n"\
|
||||
"#ifdef ENABLE_FLICKER\n"\
|
||||
" resultColor = ApplyFlicker(constants_FlickerAxis, texCoord.zw, constants_FlickerStartThreshold, constants_FlickerEndThreshold, resultColor * constants_Modulation);\n"\
|
||||
" resultColor = ApplyFlicker(constants_FlickerAxis, texCoord.zw, constants_FlickerStartThreshold, constants_FlickerEndThreshold, resultColor);\n"\
|
||||
" if (resultColor.a < 1.0)\n"\
|
||||
" discard;\n"\
|
||||
"#endif\n"\
|
||||
|
||||
Reference in New Issue
Block a user