mirror of
https://github.com/OpenRCT2/OpenRCT2
synced 2025-12-10 09:32:29 +01:00
Refactor audio sources
This commit is contained in:
@@ -9,6 +9,7 @@
|
||||
|
||||
#include "AudioContext.h"
|
||||
#include "AudioFormat.h"
|
||||
#include "SDLAudioSource.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
@@ -18,7 +19,7 @@
|
||||
|
||||
namespace OpenRCT2::Audio
|
||||
{
|
||||
template<typename AudioSource_ = ISDLAudioSource> class AudioChannelImpl : public ISDLAudioChannel
|
||||
template<typename AudioSource_ = SDLAudioSource> class AudioChannelImpl : public ISDLAudioChannel
|
||||
{
|
||||
static_assert(std::is_base_of_v<IAudioSource, AudioSource_>);
|
||||
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
|
||||
#include "../SDLException.h"
|
||||
#include "AudioMixer.h"
|
||||
#include "SDLAudioSource.h"
|
||||
|
||||
#include <SDL.h>
|
||||
#include <memory>
|
||||
@@ -21,22 +22,11 @@
|
||||
|
||||
namespace OpenRCT2::Audio
|
||||
{
|
||||
enum class AudioCodecKind
|
||||
{
|
||||
Wav,
|
||||
Ogg,
|
||||
Flac,
|
||||
};
|
||||
|
||||
[[nodiscard]] int32_t ISDLAudioSource::GetBytesPerSecond() const
|
||||
{
|
||||
auto format = GetFormat();
|
||||
return format.GetBytesPerSecond();
|
||||
}
|
||||
|
||||
class AudioContext final : public IAudioContext
|
||||
{
|
||||
private:
|
||||
static constexpr size_t STREAM_MIN_SIZE = 2 * 1024 * 1024; // 2 MiB
|
||||
|
||||
std::unique_ptr<AudioMixer> _audioMixer;
|
||||
|
||||
public:
|
||||
@@ -80,17 +70,6 @@ namespace OpenRCT2::Audio
|
||||
_audioMixer->Init(szDeviceName);
|
||||
}
|
||||
|
||||
IAudioSource* CreateStreamFromCSS(const std::string& path, uint32_t index) override
|
||||
{
|
||||
auto& format = _audioMixer->GetFormat();
|
||||
return AddSource(AudioSource::CreateMemoryFromCSS1(path, index, &format));
|
||||
}
|
||||
|
||||
IAudioSource* CreateStreamFromWAV(const std::string& path) override
|
||||
{
|
||||
return AddSource(AudioSource::CreateStreamFromWAV(path));
|
||||
}
|
||||
|
||||
IAudioSource* CreateStreamFromCSS(std::unique_ptr<IStream> stream, uint32_t index) override
|
||||
{
|
||||
auto* rw = StreamToSDL2(std::move(stream));
|
||||
@@ -99,55 +78,49 @@ namespace OpenRCT2::Audio
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto& format = _audioMixer->GetFormat();
|
||||
return AddSource(AudioSource::CreateMemoryFromCSS1(rw, index, &format));
|
||||
}
|
||||
try
|
||||
{
|
||||
auto source = CreateAudioSource(rw, index);
|
||||
|
||||
static AudioCodecKind GetAudioCodec(SDL_RWops* rw)
|
||||
{
|
||||
constexpr uint32_t MAGIC_FLAC = 0x43614C66;
|
||||
constexpr uint32_t MAGIC_OGG = 0x5367674F;
|
||||
// Stream will already be in memory, so convert to target format
|
||||
auto& targetFormat = _audioMixer->GetFormat();
|
||||
source = source->ToMemory(targetFormat);
|
||||
|
||||
auto magic = SDL_ReadLE32(rw);
|
||||
SDL_RWseek(rw, -4, RW_SEEK_CUR);
|
||||
if (magic == MAGIC_FLAC)
|
||||
{
|
||||
return AudioCodecKind::Flac;
|
||||
return AddSource(std::move(source));
|
||||
}
|
||||
else if (magic == MAGIC_OGG)
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
return AudioCodecKind::Ogg;
|
||||
}
|
||||
else
|
||||
{
|
||||
return AudioCodecKind::Wav;
|
||||
log_verbose("Unable to create audio source: %s", e.what());
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
IAudioSource* CreateStreamFromWAV(std::unique_ptr<IStream> stream) override
|
||||
{
|
||||
constexpr size_t STREAM_MIN_SIZE = 2 * 1024 * 1024; // 2 MiB
|
||||
auto loadIntoRAM = stream->GetLength() < STREAM_MIN_SIZE;
|
||||
auto* rw = StreamToSDL2(std::move(stream));
|
||||
if (rw == nullptr)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto codec = GetAudioCodec(rw);
|
||||
switch (codec)
|
||||
try
|
||||
{
|
||||
case AudioCodecKind::Wav:
|
||||
return AddSource(
|
||||
loadIntoRAM ? AudioSource::CreateMemoryFromWAV(rw, &_audioMixer->GetFormat())
|
||||
: AudioSource::CreateStreamFromWAV(rw));
|
||||
case AudioCodecKind::Flac:
|
||||
return AddSource(AudioSource::CreateStreamFromFlac(rw));
|
||||
case AudioCodecKind::Ogg:
|
||||
return AddSource(AudioSource::CreateStreamFromOgg(rw));
|
||||
default:
|
||||
log_verbose("Unsupported audio codec");
|
||||
return nullptr;
|
||||
auto source = CreateAudioSource(rw);
|
||||
|
||||
// Load whole stream into memory if small enough
|
||||
auto dataLength = source->GetLength();
|
||||
if (dataLength < STREAM_MIN_SIZE)
|
||||
{
|
||||
auto& targetFormat = _audioMixer->GetFormat();
|
||||
source = source->ToMemory(targetFormat);
|
||||
}
|
||||
|
||||
return AddSource(std::move(source));
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
log_verbose("Unable to create audio source: %s", e.what());
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -198,7 +171,7 @@ namespace OpenRCT2::Audio
|
||||
}
|
||||
|
||||
private:
|
||||
IAudioSource* AddSource(std::unique_ptr<ISDLAudioSource> source)
|
||||
IAudioSource* AddSource(std::unique_ptr<SDLAudioSource> source)
|
||||
{
|
||||
return _audioMixer->AddSource(std::move(source));
|
||||
}
|
||||
|
||||
@@ -48,13 +48,6 @@ namespace OpenRCT2::Audio
|
||||
assert_struct_size(WaveFormatEx, 18);
|
||||
#pragma pack(pop)
|
||||
|
||||
struct ISDLAudioSource : public IAudioSource
|
||||
{
|
||||
[[nodiscard]] virtual AudioFormat GetFormat() const abstract;
|
||||
|
||||
[[nodiscard]] int32_t GetBytesPerSecond() const override;
|
||||
};
|
||||
|
||||
struct ISDLAudioChannel : public IAudioChannel
|
||||
{
|
||||
[[nodiscard]] virtual AudioFormat GetFormat() const abstract;
|
||||
@@ -62,19 +55,6 @@ namespace OpenRCT2::Audio
|
||||
virtual void SetResampler(SpeexResamplerState* value) abstract;
|
||||
};
|
||||
|
||||
namespace AudioSource
|
||||
{
|
||||
std::unique_ptr<ISDLAudioSource> CreateMemoryFromCSS1(
|
||||
const std::string& path, size_t index, const AudioFormat* targetFormat = nullptr);
|
||||
std::unique_ptr<ISDLAudioSource> CreateMemoryFromCSS1(
|
||||
SDL_RWops* rw, size_t index, const AudioFormat* targetFormat = nullptr);
|
||||
std::unique_ptr<ISDLAudioSource> CreateMemoryFromWAV(SDL_RWops* rw, const AudioFormat* targetFormat = nullptr);
|
||||
std::unique_ptr<ISDLAudioSource> CreateStreamFromWAV(const std::string& path);
|
||||
std::unique_ptr<ISDLAudioSource> CreateStreamFromWAV(SDL_RWops* rw);
|
||||
std::unique_ptr<ISDLAudioSource> CreateStreamFromFlac(SDL_RWops* rw);
|
||||
std::unique_ptr<ISDLAudioSource> CreateStreamFromOgg(SDL_RWops* rw);
|
||||
} // namespace AudioSource
|
||||
|
||||
namespace AudioChannel
|
||||
{
|
||||
ISDLAudioChannel* Create();
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <SDL.h>
|
||||
#include <openrct2/common.h>
|
||||
#include <cstdint>
|
||||
|
||||
namespace OpenRCT2::Audio
|
||||
{
|
||||
|
||||
@@ -105,7 +105,7 @@ void AudioMixer::SetVolume(float volume)
|
||||
_volume = volume;
|
||||
}
|
||||
|
||||
ISDLAudioSource* AudioMixer::AddSource(std::unique_ptr<ISDLAudioSource> source)
|
||||
SDLAudioSource* AudioMixer::AddSource(std::unique_ptr<SDLAudioSource> source)
|
||||
{
|
||||
std::lock_guard<std::mutex> guard(_mutex);
|
||||
if (source != nullptr)
|
||||
@@ -122,7 +122,7 @@ void AudioMixer::RemoveReleasedSources()
|
||||
_sources.erase(
|
||||
std::remove_if(
|
||||
_sources.begin(), _sources.end(),
|
||||
[](std::unique_ptr<ISDLAudioSource>& source) {
|
||||
[](std::unique_ptr<SDLAudioSource>& source) {
|
||||
{
|
||||
return source->IsReleased();
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
|
||||
#include "AudioContext.h"
|
||||
#include "AudioFormat.h"
|
||||
#include "SDLAudioSource.h"
|
||||
|
||||
#include <SDL.h>
|
||||
#include <cstdint>
|
||||
@@ -28,7 +29,7 @@ namespace OpenRCT2::Audio
|
||||
class AudioMixer final : public IAudioMixer
|
||||
{
|
||||
private:
|
||||
std::vector<std::unique_ptr<ISDLAudioSource>> _sources;
|
||||
std::vector<std::unique_ptr<SDLAudioSource>> _sources;
|
||||
|
||||
SDL_AudioDeviceID _deviceId = 0;
|
||||
AudioFormat _format = {};
|
||||
@@ -54,7 +55,7 @@ namespace OpenRCT2::Audio
|
||||
IAudioChannel* Play(IAudioSource* source, int32_t loop, bool deleteondone) override;
|
||||
void Stop(IAudioChannel* channel) override;
|
||||
void SetVolume(float volume) override;
|
||||
ISDLAudioSource* AddSource(std::unique_ptr<ISDLAudioSource> source);
|
||||
SDLAudioSource* AddSource(std::unique_ptr<SDLAudioSource> source);
|
||||
|
||||
const AudioFormat& GetFormat() const;
|
||||
|
||||
|
||||
@@ -7,21 +7,18 @@
|
||||
* OpenRCT2 is licensed under the GNU General Public License version 3.
|
||||
*****************************************************************************/
|
||||
|
||||
#include "AudioContext.h"
|
||||
#include "AudioFormat.h"
|
||||
#include "SDLAudioSource.h"
|
||||
|
||||
#include <FLAC/all.h>
|
||||
#include <SDL.h>
|
||||
#include <algorithm>
|
||||
#include <openrct2/audio/AudioSource.h>
|
||||
#include <openrct2/common.h>
|
||||
#include <vector>
|
||||
|
||||
namespace OpenRCT2::Audio
|
||||
{
|
||||
/**
|
||||
* An audio source which decodes a FLAC stream.
|
||||
*/
|
||||
class FlacAudioSource final : public ISDLAudioSource
|
||||
class FlacAudioSource final : public SDLAudioSource
|
||||
{
|
||||
private:
|
||||
AudioFormat _format = {};
|
||||
@@ -42,20 +39,6 @@ namespace OpenRCT2::Audio
|
||||
Release();
|
||||
}
|
||||
|
||||
bool IsReleased() const override
|
||||
{
|
||||
return _released;
|
||||
}
|
||||
|
||||
void Release() override
|
||||
{
|
||||
if (!_released)
|
||||
{
|
||||
Unload();
|
||||
_released = true;
|
||||
}
|
||||
}
|
||||
|
||||
[[nodiscard]] uint64_t GetLength() const override
|
||||
{
|
||||
return _dataLength;
|
||||
@@ -121,6 +104,20 @@ namespace OpenRCT2::Audio
|
||||
return bytesRead;
|
||||
}
|
||||
|
||||
protected:
|
||||
void Unload() override
|
||||
{
|
||||
if (_decoder != nullptr)
|
||||
{
|
||||
FLAC__stream_decoder_delete(_decoder);
|
||||
}
|
||||
if (_rw != nullptr)
|
||||
{
|
||||
SDL_RWclose(_rw);
|
||||
_rw = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
void Seek(uint64_t sampleIndex)
|
||||
{
|
||||
@@ -165,19 +162,6 @@ namespace OpenRCT2::Audio
|
||||
}
|
||||
}
|
||||
|
||||
void Unload()
|
||||
{
|
||||
if (_decoder != nullptr)
|
||||
{
|
||||
FLAC__stream_decoder_delete(_decoder);
|
||||
}
|
||||
if (_rw != nullptr)
|
||||
{
|
||||
SDL_RWclose(_rw);
|
||||
_rw = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
static FLAC__StreamDecoderReadStatus FlacCallbackRead(
|
||||
const FLAC__StreamDecoder* decoder, FLAC__byte buffer[], size_t* bytes, void* clientData)
|
||||
{
|
||||
@@ -328,7 +312,7 @@ namespace OpenRCT2::Audio
|
||||
}
|
||||
};
|
||||
|
||||
std::unique_ptr<ISDLAudioSource> AudioSource::CreateStreamFromFlac(SDL_RWops* rw)
|
||||
std::unique_ptr<SDLAudioSource> CreateFlacAudioSource(SDL_RWops* rw)
|
||||
{
|
||||
auto source = std::make_unique<FlacAudioSource>();
|
||||
if (!source->LoadFlac(rw))
|
||||
@@ -1,5 +1,5 @@
|
||||
/*****************************************************************************
|
||||
* Copyright (c) 2014-2020 OpenRCT2 developers
|
||||
* Copyright (c) 2014-2022 OpenRCT2 developers
|
||||
*
|
||||
* For a complete list of all authors, please refer to contributors.md
|
||||
* Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2
|
||||
@@ -9,6 +9,7 @@
|
||||
|
||||
#include "AudioContext.h"
|
||||
#include "AudioFormat.h"
|
||||
#include "SDLAudioSource.h"
|
||||
|
||||
#include <SDL.h>
|
||||
#include <algorithm>
|
||||
@@ -19,46 +20,19 @@
|
||||
namespace OpenRCT2::Audio
|
||||
{
|
||||
/**
|
||||
* An audio source where raw PCM data is initially loaded into RAM from
|
||||
* a file and then streamed.
|
||||
* An audio source where raw PCM data is stored in RAM.
|
||||
*/
|
||||
class MemoryAudioSource final : public ISDLAudioSource
|
||||
class MemoryAudioSource final : public SDLAudioSource
|
||||
{
|
||||
private:
|
||||
AudioFormat _format = {};
|
||||
std::vector<uint8_t> _data;
|
||||
uint8_t* _dataSDL{};
|
||||
size_t _length{};
|
||||
bool _released{};
|
||||
|
||||
const uint8_t* GetData()
|
||||
{
|
||||
return _dataSDL != nullptr ? _dataSDL : _data.data();
|
||||
}
|
||||
|
||||
public:
|
||||
~MemoryAudioSource() override
|
||||
MemoryAudioSource(const AudioFormat& format, std::vector<uint8_t>&& pcmData)
|
||||
: _format(format)
|
||||
, _data(pcmData)
|
||||
{
|
||||
Release();
|
||||
}
|
||||
|
||||
bool IsReleased() const override
|
||||
{
|
||||
return _released;
|
||||
}
|
||||
|
||||
void Release() override
|
||||
{
|
||||
if (!_released)
|
||||
{
|
||||
Unload();
|
||||
_released = true;
|
||||
}
|
||||
}
|
||||
|
||||
[[nodiscard]] uint64_t GetLength() const override
|
||||
{
|
||||
return _length;
|
||||
}
|
||||
|
||||
[[nodiscard]] AudioFormat GetFormat() const override
|
||||
@@ -66,14 +40,19 @@ namespace OpenRCT2::Audio
|
||||
return _format;
|
||||
}
|
||||
|
||||
[[nodiscard]] uint64_t GetLength() const override
|
||||
{
|
||||
return _data.size();
|
||||
}
|
||||
|
||||
size_t Read(void* dst, uint64_t offset, size_t len) override
|
||||
{
|
||||
size_t bytesToRead = 0;
|
||||
if (offset < _length)
|
||||
if (offset < _data.size())
|
||||
{
|
||||
bytesToRead = static_cast<size_t>(std::min<uint64_t>(len, _length - offset));
|
||||
bytesToRead = static_cast<size_t>(std::min<uint64_t>(len, _data.size() - offset));
|
||||
|
||||
auto src = GetData();
|
||||
auto src = _data.data();
|
||||
if (src != nullptr)
|
||||
{
|
||||
std::copy_n(src + offset, bytesToRead, reinterpret_cast<uint8_t*>(dst));
|
||||
@@ -82,164 +61,45 @@ namespace OpenRCT2::Audio
|
||||
return bytesToRead;
|
||||
}
|
||||
|
||||
bool LoadWAV(SDL_RWops* rw)
|
||||
protected:
|
||||
void Unload() override
|
||||
{
|
||||
Unload();
|
||||
|
||||
bool result = false;
|
||||
SDL_AudioSpec audiospec = {};
|
||||
uint32_t audioLen;
|
||||
SDL_AudioSpec* spec = SDL_LoadWAV_RW(rw, false, &audiospec, &_dataSDL, &audioLen);
|
||||
if (spec != nullptr)
|
||||
{
|
||||
_format.freq = spec->freq;
|
||||
_format.format = spec->format;
|
||||
_format.channels = spec->channels;
|
||||
_length = audioLen;
|
||||
result = true;
|
||||
}
|
||||
SDL_RWclose(rw);
|
||||
return result;
|
||||
}
|
||||
|
||||
bool LoadCSS1(SDL_RWops* rw, size_t index)
|
||||
{
|
||||
Unload();
|
||||
|
||||
bool result{};
|
||||
uint32_t numSounds{};
|
||||
SDL_RWread(rw, &numSounds, sizeof(numSounds), 1);
|
||||
if (index < numSounds)
|
||||
{
|
||||
SDL_RWseek(rw, index * 4, RW_SEEK_CUR);
|
||||
|
||||
uint32_t pcmOffset{};
|
||||
SDL_RWread(rw, &pcmOffset, sizeof(pcmOffset), 1);
|
||||
SDL_RWseek(rw, pcmOffset, RW_SEEK_SET);
|
||||
|
||||
uint32_t pcmSize{};
|
||||
SDL_RWread(rw, &pcmSize, sizeof(pcmSize), 1);
|
||||
_length = pcmSize;
|
||||
|
||||
WaveFormatEx waveFormat{};
|
||||
SDL_RWread(rw, &waveFormat, sizeof(waveFormat), 1);
|
||||
_format.freq = waveFormat.frequency;
|
||||
_format.format = AUDIO_S16LSB;
|
||||
_format.channels = waveFormat.channels;
|
||||
|
||||
try
|
||||
{
|
||||
_data.resize(_length);
|
||||
SDL_RWread(rw, _data.data(), _length, 1);
|
||||
result = true;
|
||||
}
|
||||
catch (const std::bad_alloc&)
|
||||
{
|
||||
log_verbose("Unable to allocate data");
|
||||
}
|
||||
}
|
||||
SDL_RWclose(rw);
|
||||
return result;
|
||||
}
|
||||
|
||||
bool Convert(const AudioFormat* format)
|
||||
{
|
||||
if (*format != _format)
|
||||
{
|
||||
SDL_AudioCVT cvt;
|
||||
if (SDL_BuildAudioCVT(
|
||||
&cvt, _format.format, _format.channels, _format.freq, format->format, format->channels, format->freq)
|
||||
>= 0)
|
||||
{
|
||||
auto src = GetData();
|
||||
auto cvtBuffer = std::vector<uint8_t>(_length * cvt.len_mult);
|
||||
std::copy_n(src, _length, cvtBuffer.data());
|
||||
cvt.len = static_cast<int32_t>(_length);
|
||||
cvt.buf = cvtBuffer.data();
|
||||
if (SDL_ConvertAudio(&cvt) >= 0)
|
||||
{
|
||||
cvtBuffer.resize(cvt.len_cvt);
|
||||
|
||||
Unload();
|
||||
_data = std::move(cvtBuffer);
|
||||
_length = cvt.len_cvt;
|
||||
_format = *format;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private:
|
||||
void Unload()
|
||||
{
|
||||
// Free our data
|
||||
_data.clear();
|
||||
_data.shrink_to_fit();
|
||||
|
||||
// Free SDL2's data
|
||||
SDL_FreeWAV(_dataSDL);
|
||||
_dataSDL = nullptr;
|
||||
|
||||
_length = 0;
|
||||
}
|
||||
};
|
||||
|
||||
std::unique_ptr<ISDLAudioSource> AudioSource::CreateMemoryFromCSS1(
|
||||
const std::string& path, size_t index, const AudioFormat* targetFormat)
|
||||
static bool ConvertPcmData(const AudioFormat& target, const AudioFormat& src, std::vector<uint8_t>& pcmData)
|
||||
{
|
||||
log_verbose("AudioSource::CreateMemoryFromCSS1(%s, %d)", path.c_str(), index);
|
||||
SDL_RWops* rw = SDL_RWFromFile(path.c_str(), "rb");
|
||||
if (rw != nullptr)
|
||||
if (target != src)
|
||||
{
|
||||
return CreateMemoryFromCSS1(rw, index, targetFormat);
|
||||
}
|
||||
else
|
||||
{
|
||||
log_verbose("Unable to load %s", path.c_str());
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::unique_ptr<ISDLAudioSource> AudioSource::CreateMemoryFromCSS1(
|
||||
SDL_RWops* rw, size_t index, const AudioFormat* targetFormat)
|
||||
{
|
||||
auto source = std::make_unique<MemoryAudioSource>();
|
||||
if (source->LoadCSS1(rw, index))
|
||||
{
|
||||
if (targetFormat != nullptr && source->GetFormat() != *targetFormat)
|
||||
SDL_AudioCVT cvt;
|
||||
if (SDL_BuildAudioCVT(&cvt, src.format, src.channels, src.freq, target.format, target.channels, target.freq) >= 0)
|
||||
{
|
||||
if (!source->Convert(targetFormat))
|
||||
auto srcData = pcmData.data();
|
||||
auto srcLen = pcmData.size();
|
||||
auto cvtBuffer = std::vector<uint8_t>(srcLen * cvt.len_mult);
|
||||
std::copy_n(srcData, srcLen, cvtBuffer.data());
|
||||
cvt.len = static_cast<int32_t>(srcLen);
|
||||
cvt.buf = cvtBuffer.data();
|
||||
if (SDL_ConvertAudio(&cvt) >= 0)
|
||||
{
|
||||
source = nullptr;
|
||||
cvtBuffer.resize(cvt.len_cvt);
|
||||
cvtBuffer.shrink_to_fit();
|
||||
pcmData = std::move(cvtBuffer);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
source = nullptr;
|
||||
}
|
||||
return source;
|
||||
return true;
|
||||
}
|
||||
|
||||
std::unique_ptr<ISDLAudioSource> AudioSource::CreateMemoryFromWAV(SDL_RWops* rw, const AudioFormat* targetFormat)
|
||||
std::unique_ptr<SDLAudioSource> CreateMemoryAudioSource(
|
||||
const AudioFormat& target, const AudioFormat& src, std::vector<uint8_t>&& pcmData)
|
||||
{
|
||||
auto source = std::make_unique<MemoryAudioSource>();
|
||||
if (source->LoadWAV(rw))
|
||||
{
|
||||
if (targetFormat != nullptr && source->GetFormat() != *targetFormat)
|
||||
{
|
||||
if (!source->Convert(targetFormat))
|
||||
{
|
||||
source = nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
source = nullptr;
|
||||
}
|
||||
return source;
|
||||
if (!ConvertPcmData(target, src, pcmData))
|
||||
throw std::runtime_error("Unable to convert PCM data to target format");
|
||||
|
||||
return std::make_unique<MemoryAudioSource>(target, std::move(pcmData));
|
||||
}
|
||||
} // namespace OpenRCT2::Audio
|
||||
|
||||
@@ -7,14 +7,11 @@
|
||||
* OpenRCT2 is licensed under the GNU General Public License version 3.
|
||||
*****************************************************************************/
|
||||
|
||||
#include "AudioContext.h"
|
||||
#include "AudioFormat.h"
|
||||
#include "SDLAudioSource.h"
|
||||
|
||||
#include <SDL.h>
|
||||
#include <algorithm>
|
||||
#include <openrct2/audio/AudioSource.h>
|
||||
#include <openrct2/common.h>
|
||||
#include <optional>
|
||||
#include <vector>
|
||||
#include <vorbis/vorbisfile.h>
|
||||
|
||||
namespace OpenRCT2::Audio
|
||||
@@ -22,12 +19,11 @@ namespace OpenRCT2::Audio
|
||||
/**
|
||||
* An audio source which decodes a OGG/Vorbis stream.
|
||||
*/
|
||||
class OggAudioSource final : public ISDLAudioSource
|
||||
class OggAudioSource final : public SDLAudioSource
|
||||
{
|
||||
private:
|
||||
AudioFormat _format = {};
|
||||
SDL_RWops* _rw = nullptr;
|
||||
bool _released{};
|
||||
|
||||
std::optional<OggVorbis_File> _file;
|
||||
uint64_t _dataLength{};
|
||||
@@ -43,20 +39,6 @@ namespace OpenRCT2::Audio
|
||||
Release();
|
||||
}
|
||||
|
||||
bool IsReleased() const override
|
||||
{
|
||||
return _released;
|
||||
}
|
||||
|
||||
void Release() override
|
||||
{
|
||||
if (!_released)
|
||||
{
|
||||
Unload();
|
||||
_released = true;
|
||||
}
|
||||
}
|
||||
|
||||
[[nodiscard]] uint64_t GetLength() const override
|
||||
{
|
||||
return _dataLength;
|
||||
@@ -135,8 +117,8 @@ namespace OpenRCT2::Audio
|
||||
return totalBytesRead;
|
||||
}
|
||||
|
||||
private:
|
||||
void Unload()
|
||||
protected:
|
||||
void Unload() override
|
||||
{
|
||||
if (_file)
|
||||
{
|
||||
@@ -149,6 +131,7 @@ namespace OpenRCT2::Audio
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
static size_t VorbisCallbackRead(void* ptr, size_t size, size_t nmemb, void* datasource)
|
||||
{
|
||||
return SDL_RWread(reinterpret_cast<SDL_RWops*>(datasource), ptr, size, nmemb);
|
||||
@@ -165,7 +148,7 @@ namespace OpenRCT2::Audio
|
||||
}
|
||||
};
|
||||
|
||||
std::unique_ptr<ISDLAudioSource> AudioSource::CreateStreamFromOgg(SDL_RWops* rw)
|
||||
std::unique_ptr<SDLAudioSource> CreateOggAudioSource(SDL_RWops* rw)
|
||||
{
|
||||
auto source = std::make_unique<OggAudioSource>();
|
||||
if (!source->LoadOgg(rw))
|
||||
128
src/openrct2-ui/audio/SDLAudioSource.cpp
Normal file
128
src/openrct2-ui/audio/SDLAudioSource.cpp
Normal file
@@ -0,0 +1,128 @@
|
||||
#include "SDLAudioSource.h"
|
||||
|
||||
using namespace OpenRCT2::Audio;
|
||||
|
||||
enum class AudioCodecKind
|
||||
{
|
||||
Unknown,
|
||||
Wav,
|
||||
Ogg,
|
||||
Flac,
|
||||
};
|
||||
|
||||
bool SDLAudioSource::IsReleased() const
|
||||
{
|
||||
return _released;
|
||||
}
|
||||
|
||||
void SDLAudioSource::Release()
|
||||
{
|
||||
if (!_released)
|
||||
{
|
||||
Unload();
|
||||
_released = true;
|
||||
}
|
||||
}
|
||||
|
||||
int32_t SDLAudioSource::GetBytesPerSecond() const
|
||||
{
|
||||
auto format = GetFormat();
|
||||
return format.GetBytesPerSecond();
|
||||
}
|
||||
|
||||
std::unique_ptr<SDLAudioSource> SDLAudioSource::ToMemory(const AudioFormat& target)
|
||||
{
|
||||
auto pcmLength = GetLength();
|
||||
|
||||
std::vector<uint8_t> pcmData;
|
||||
pcmData.resize(pcmLength);
|
||||
Read(pcmData.data(), 0, pcmLength);
|
||||
|
||||
auto srcFormat = GetFormat();
|
||||
return CreateMemoryAudioSource(target, srcFormat, std::move(pcmData));
|
||||
}
|
||||
|
||||
static AudioCodecKind GetAudioCodec(SDL_RWops* rw)
|
||||
{
|
||||
constexpr uint32_t MAGIC_FLAC = 0x43614C66;
|
||||
constexpr uint32_t MAGIC_OGG = 0x5367674F;
|
||||
constexpr uint32_t MAGIC_RIFF = 0x46464952;
|
||||
|
||||
auto originalPosition = SDL_RWtell(rw);
|
||||
auto magic = SDL_ReadLE32(rw);
|
||||
SDL_RWseek(rw, originalPosition, RW_SEEK_SET);
|
||||
switch (magic)
|
||||
{
|
||||
case MAGIC_FLAC:
|
||||
return AudioCodecKind::Flac;
|
||||
case MAGIC_OGG:
|
||||
return AudioCodecKind::Ogg;
|
||||
case MAGIC_RIFF:
|
||||
return AudioCodecKind::Wav;
|
||||
default:
|
||||
return AudioCodecKind::Unknown;
|
||||
}
|
||||
}
|
||||
|
||||
std::unique_ptr<SDLAudioSource> OpenRCT2::Audio::CreateAudioSource(SDL_RWops* rw)
|
||||
{
|
||||
auto codec = GetAudioCodec(rw);
|
||||
switch (codec)
|
||||
{
|
||||
case AudioCodecKind::Flac:
|
||||
return CreateFlacAudioSource(rw);
|
||||
case AudioCodecKind::Ogg:
|
||||
return CreateOggAudioSource(rw);
|
||||
case AudioCodecKind::Wav:
|
||||
return CreateWavAudioSource(rw);
|
||||
default:
|
||||
throw std::runtime_error("Unsupported audio codec");
|
||||
}
|
||||
}
|
||||
|
||||
std::unique_ptr<SDLAudioSource> OpenRCT2::Audio::CreateAudioSource(SDL_RWops* rw, uint32_t cssIndex)
|
||||
{
|
||||
auto numSounds = SDL_ReadLE32(rw);
|
||||
if (cssIndex < numSounds)
|
||||
{
|
||||
SDL_RWseek(rw, cssIndex * 4, RW_SEEK_CUR);
|
||||
|
||||
auto pcmOffset = SDL_ReadLE32(rw);
|
||||
SDL_RWseek(rw, pcmOffset, RW_SEEK_SET);
|
||||
|
||||
auto pcmLength = SDL_ReadLE32(rw);
|
||||
|
||||
AudioFormat format;
|
||||
[[maybe_unused]] auto encoding = SDL_ReadLE16(rw);
|
||||
format.channels = SDL_ReadLE16(rw);
|
||||
format.freq = SDL_ReadLE32(rw);
|
||||
[[maybe_unused]] auto byterate = SDL_ReadLE32(rw);
|
||||
[[maybe_unused]] auto blockalign = SDL_ReadLE16(rw);
|
||||
[[maybe_unused]] auto bitspersample = SDL_ReadLE16(rw);
|
||||
switch (bitspersample)
|
||||
{
|
||||
case 8:
|
||||
format.format = AUDIO_U8;
|
||||
break;
|
||||
case 16:
|
||||
format.format = AUDIO_S16LSB;
|
||||
break;
|
||||
default:
|
||||
SDL_RWclose(rw);
|
||||
throw std::runtime_error("Unsupported bits per sample");
|
||||
}
|
||||
[[maybe_unused]] auto extrasize = SDL_ReadLE16(rw);
|
||||
|
||||
std::vector<uint8_t> pcmData;
|
||||
pcmData.resize(pcmLength);
|
||||
SDL_RWread(rw, pcmData.data(), pcmLength, 1);
|
||||
|
||||
SDL_RWclose(rw);
|
||||
return CreateMemoryAudioSource(format, format, std::move(pcmData));
|
||||
}
|
||||
else
|
||||
{
|
||||
SDL_RWclose(rw);
|
||||
throw std::runtime_error("CSS does not contain required entry");
|
||||
}
|
||||
}
|
||||
45
src/openrct2-ui/audio/SDLAudioSource.h
Normal file
45
src/openrct2-ui/audio/SDLAudioSource.h
Normal file
@@ -0,0 +1,45 @@
|
||||
/*****************************************************************************
|
||||
* Copyright (c) 2014-2022 OpenRCT2 developers
|
||||
*
|
||||
* For a complete list of all authors, please refer to contributors.md
|
||||
* Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2
|
||||
*
|
||||
* OpenRCT2 is licensed under the GNU General Public License version 3.
|
||||
*****************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "AudioFormat.h"
|
||||
|
||||
#include <SDL.h>
|
||||
#include <memory>
|
||||
#include <openrct2/audio/AudioSource.h>
|
||||
#include <vector>
|
||||
|
||||
namespace OpenRCT2::Audio
|
||||
{
|
||||
class SDLAudioSource : public IAudioSource
|
||||
{
|
||||
private:
|
||||
bool _released{};
|
||||
|
||||
public:
|
||||
void Release() override;
|
||||
int32_t GetBytesPerSecond() const override;
|
||||
bool IsReleased() const;
|
||||
std::unique_ptr<SDLAudioSource> ToMemory(const AudioFormat& target);
|
||||
|
||||
virtual AudioFormat GetFormat() const = 0;
|
||||
|
||||
protected:
|
||||
virtual void Unload() = 0;
|
||||
};
|
||||
|
||||
std::unique_ptr<SDLAudioSource> CreateAudioSource(SDL_RWops* rw);
|
||||
std::unique_ptr<SDLAudioSource> CreateAudioSource(SDL_RWops* rw, uint32_t cssIndex);
|
||||
std::unique_ptr<SDLAudioSource> CreateMemoryAudioSource(
|
||||
const AudioFormat& target, const AudioFormat& src, std::vector<uint8_t>&& pcmData);
|
||||
std::unique_ptr<SDLAudioSource> CreateFlacAudioSource(SDL_RWops* rw);
|
||||
std::unique_ptr<SDLAudioSource> CreateOggAudioSource(SDL_RWops* rw);
|
||||
std::unique_ptr<SDLAudioSource> CreateWavAudioSource(SDL_RWops* rw);
|
||||
} // namespace OpenRCT2::Audio
|
||||
@@ -1,5 +1,5 @@
|
||||
/*****************************************************************************
|
||||
* Copyright (c) 2014-2020 OpenRCT2 developers
|
||||
* Copyright (c) 2014-2022 OpenRCT2 developers
|
||||
*
|
||||
* For a complete list of all authors, please refer to contributors.md
|
||||
* Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2
|
||||
@@ -7,59 +7,111 @@
|
||||
* OpenRCT2 is licensed under the GNU General Public License version 3.
|
||||
*****************************************************************************/
|
||||
|
||||
#include "AudioContext.h"
|
||||
#include "AudioFormat.h"
|
||||
#include "SDLAudioSource.h"
|
||||
|
||||
#include <SDL.h>
|
||||
#include <algorithm>
|
||||
#include <openrct2/audio/AudioSource.h>
|
||||
#include <openrct2/common.h>
|
||||
|
||||
namespace OpenRCT2::Audio
|
||||
{
|
||||
/**
|
||||
* An audio source where raw PCM data is streamed directly from
|
||||
* a file.
|
||||
* An audio source where raw PCM data is stored in RAM.
|
||||
*/
|
||||
class FileAudioSource final : public ISDLAudioSource
|
||||
class WavAudioSource final : public SDLAudioSource
|
||||
{
|
||||
private:
|
||||
static constexpr uint32_t DATA = 0x61746164;
|
||||
static constexpr uint32_t FMT = 0x20746D66;
|
||||
static constexpr uint32_t RIFF = 0x46464952;
|
||||
static constexpr uint32_t WAVE = 0x45564157;
|
||||
static constexpr uint16_t pcmformat = 0x0001;
|
||||
|
||||
SDL_RWops* _rw{};
|
||||
AudioFormat _format = {};
|
||||
SDL_RWops* _rw = nullptr;
|
||||
uint64_t _dataBegin = 0;
|
||||
uint64_t _dataLength = 0;
|
||||
bool _released{};
|
||||
uint64_t _dataBegin{};
|
||||
uint64_t _dataLength{};
|
||||
|
||||
public:
|
||||
~FileAudioSource() override
|
||||
WavAudioSource(SDL_RWops* rw)
|
||||
: _rw(rw)
|
||||
{
|
||||
auto chunkId = SDL_ReadLE32(rw);
|
||||
if (chunkId != RIFF)
|
||||
{
|
||||
SDL_RWclose(rw);
|
||||
throw std::runtime_error("Not a WAV file");
|
||||
}
|
||||
|
||||
// Read and discard chunk size
|
||||
SDL_ReadLE32(rw);
|
||||
auto chunkFormat = SDL_ReadLE32(rw);
|
||||
if (chunkFormat != WAVE)
|
||||
{
|
||||
SDL_RWclose(rw);
|
||||
throw std::runtime_error("Not in WAVE format");
|
||||
}
|
||||
|
||||
auto fmtChunkSize = FindChunk(rw, FMT);
|
||||
if (!fmtChunkSize)
|
||||
{
|
||||
SDL_RWclose(rw);
|
||||
throw std::runtime_error("Could not find FMT chunk");
|
||||
}
|
||||
|
||||
auto chunkStart = SDL_RWtell(rw);
|
||||
|
||||
auto encoding = SDL_ReadLE16(rw);
|
||||
if (encoding != pcmformat)
|
||||
{
|
||||
SDL_RWclose(rw);
|
||||
throw std::runtime_error("Not in PCM format");
|
||||
}
|
||||
|
||||
_format.channels = SDL_ReadLE16(rw);
|
||||
_format.freq = SDL_ReadLE32(rw);
|
||||
[[maybe_unused]] auto byterate = SDL_ReadLE32(rw);
|
||||
[[maybe_unused]] auto blockalign = SDL_ReadLE16(rw);
|
||||
[[maybe_unused]] auto bitspersample = SDL_ReadLE16(rw);
|
||||
switch (bitspersample)
|
||||
{
|
||||
case 8:
|
||||
_format.format = AUDIO_U8;
|
||||
break;
|
||||
case 16:
|
||||
_format.format = AUDIO_S16LSB;
|
||||
break;
|
||||
default:
|
||||
SDL_RWclose(rw);
|
||||
throw std::runtime_error("Unsupported bits per sample");
|
||||
}
|
||||
|
||||
SDL_RWseek(rw, chunkStart + fmtChunkSize, RW_SEEK_SET);
|
||||
|
||||
auto dataChunkSize = FindChunk(rw, DATA);
|
||||
if (dataChunkSize == 0)
|
||||
{
|
||||
SDL_RWclose(rw);
|
||||
throw std::runtime_error("Could not find DATA chunk");
|
||||
}
|
||||
|
||||
_dataLength = dataChunkSize;
|
||||
_dataBegin = static_cast<uint64_t>(SDL_RWtell(rw));
|
||||
}
|
||||
|
||||
~WavAudioSource() override
|
||||
{
|
||||
Release();
|
||||
}
|
||||
|
||||
bool IsReleased() const override
|
||||
{
|
||||
return _released;
|
||||
}
|
||||
|
||||
void Release() override
|
||||
{
|
||||
if (!_released)
|
||||
{
|
||||
Unload();
|
||||
_released = true;
|
||||
}
|
||||
}
|
||||
|
||||
[[nodiscard]] uint64_t GetLength() const override
|
||||
{
|
||||
return _dataLength;
|
||||
}
|
||||
|
||||
[[nodiscard]] AudioFormat GetFormat() const override
|
||||
{
|
||||
return _format;
|
||||
}
|
||||
|
||||
[[nodiscard]] uint64_t GetLength() const override
|
||||
{
|
||||
return _dataLength;
|
||||
}
|
||||
|
||||
size_t Read(void* dst, uint64_t offset, size_t len) override
|
||||
{
|
||||
size_t bytesRead = 0;
|
||||
@@ -81,81 +133,16 @@ namespace OpenRCT2::Audio
|
||||
return bytesRead;
|
||||
}
|
||||
|
||||
bool LoadWAV(SDL_RWops* rw)
|
||||
protected:
|
||||
void Unload() override
|
||||
{
|
||||
constexpr uint32_t DATA = 0x61746164;
|
||||
constexpr uint32_t FMT = 0x20746D66;
|
||||
constexpr uint32_t RIFF = 0x46464952;
|
||||
constexpr uint32_t WAVE = 0x45564157;
|
||||
constexpr uint16_t pcmformat = 0x0001;
|
||||
|
||||
Unload();
|
||||
|
||||
if (rw == nullptr)
|
||||
if (_rw != nullptr)
|
||||
{
|
||||
return false;
|
||||
SDL_RWclose(_rw);
|
||||
_rw = nullptr;
|
||||
}
|
||||
_rw = rw;
|
||||
|
||||
uint32_t chunkId = SDL_ReadLE32(rw);
|
||||
if (chunkId != RIFF)
|
||||
{
|
||||
log_verbose("Not a WAV file");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Read and discard chunk size
|
||||
SDL_ReadLE32(rw);
|
||||
uint32_t chunkFormat = SDL_ReadLE32(rw);
|
||||
if (chunkFormat != WAVE)
|
||||
{
|
||||
log_verbose("Not in WAVE format");
|
||||
return false;
|
||||
}
|
||||
|
||||
uint32_t fmtChunkSize = FindChunk(rw, FMT);
|
||||
if (!fmtChunkSize)
|
||||
{
|
||||
log_verbose("Could not find FMT chunk");
|
||||
return false;
|
||||
}
|
||||
|
||||
uint64_t chunkStart = SDL_RWtell(rw);
|
||||
|
||||
WaveFormat waveFormat{};
|
||||
SDL_RWread(rw, &waveFormat, sizeof(waveFormat), 1);
|
||||
SDL_RWseek(rw, chunkStart + fmtChunkSize, RW_SEEK_SET);
|
||||
if (waveFormat.encoding != pcmformat)
|
||||
{
|
||||
log_verbose("Not in proper format");
|
||||
return false;
|
||||
}
|
||||
|
||||
_format.freq = waveFormat.frequency;
|
||||
switch (waveFormat.bitspersample)
|
||||
{
|
||||
case 8:
|
||||
_format.format = AUDIO_U8;
|
||||
break;
|
||||
case 16:
|
||||
_format.format = AUDIO_S16LSB;
|
||||
break;
|
||||
default:
|
||||
log_verbose("Invalid bits per sample");
|
||||
return false;
|
||||
}
|
||||
_format.channels = waveFormat.channels;
|
||||
|
||||
uint32_t dataChunkSize = FindChunk(rw, DATA);
|
||||
if (dataChunkSize == 0)
|
||||
{
|
||||
log_verbose("Could not find DATA chunk");
|
||||
return false;
|
||||
}
|
||||
|
||||
_dataLength = dataChunkSize;
|
||||
_dataBegin = SDL_RWtell(rw);
|
||||
return true;
|
||||
_dataBegin = 0;
|
||||
_dataLength = 0;
|
||||
}
|
||||
|
||||
private:
|
||||
@@ -183,36 +170,10 @@ namespace OpenRCT2::Audio
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void Unload()
|
||||
{
|
||||
if (_rw != nullptr)
|
||||
{
|
||||
SDL_RWclose(_rw);
|
||||
_rw = nullptr;
|
||||
}
|
||||
_dataBegin = 0;
|
||||
_dataLength = 0;
|
||||
}
|
||||
};
|
||||
|
||||
std::unique_ptr<ISDLAudioSource> AudioSource::CreateStreamFromWAV(const std::string& path)
|
||||
std::unique_ptr<SDLAudioSource> CreateWavAudioSource(SDL_RWops* rw)
|
||||
{
|
||||
auto* rw = SDL_RWFromFile(path.c_str(), "rb");
|
||||
if (rw != nullptr)
|
||||
{
|
||||
return AudioSource::CreateStreamFromWAV(rw);
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::unique_ptr<ISDLAudioSource> AudioSource::CreateStreamFromWAV(SDL_RWops* rw)
|
||||
{
|
||||
auto source = std::make_unique<FileAudioSource>();
|
||||
if (!source->LoadWAV(rw))
|
||||
{
|
||||
source = nullptr;
|
||||
}
|
||||
return source;
|
||||
return std::make_unique<WavAudioSource>(rw);
|
||||
}
|
||||
} // namespace OpenRCT2::Audio
|
||||
@@ -34,9 +34,7 @@ namespace OpenRCT2::Audio
|
||||
virtual std::vector<std::string> GetOutputDevices() abstract;
|
||||
virtual void SetOutputDevice(const std::string& deviceName) abstract;
|
||||
|
||||
virtual IAudioSource* CreateStreamFromCSS(const std::string& path, uint32_t index) abstract;
|
||||
virtual IAudioSource* CreateStreamFromCSS(std::unique_ptr<IStream> stream, uint32_t index) abstract;
|
||||
virtual IAudioSource* CreateStreamFromWAV(const std::string& path) abstract;
|
||||
virtual IAudioSource* CreateStreamFromWAV(std::unique_ptr<IStream> stream) abstract;
|
||||
|
||||
virtual void StartTitleMusic() abstract;
|
||||
|
||||
@@ -26,21 +26,11 @@ namespace OpenRCT2::Audio
|
||||
{
|
||||
}
|
||||
|
||||
IAudioSource* CreateStreamFromCSS(const std::string& /* path */, uint32_t /* index */) override
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
IAudioSource* CreateStreamFromCSS(std::unique_ptr<IStream> /* stream */, uint32_t /* index */) override
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
IAudioSource* CreateStreamFromWAV(const std::string& /*path*/) override
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
IAudioSource* CreateStreamFromWAV(std::unique_ptr<IStream>) override
|
||||
{
|
||||
return nullptr;
|
||||
|
||||
@@ -826,6 +826,8 @@ static void ttf_process_string_literal(rct_drawpixelinfo* dpi, std::string_view
|
||||
#ifndef NO_TTF
|
||||
else
|
||||
{
|
||||
# pragma GCC diagnostic push
|
||||
# pragma GCC diagnostic ignored "-Wmaybe-uninitialized"
|
||||
CodepointView codepoints(text);
|
||||
std::optional<size_t> ttfRunIndex;
|
||||
for (auto it = codepoints.begin(); it != codepoints.end(); it++)
|
||||
@@ -859,6 +861,7 @@ static void ttf_process_string_literal(rct_drawpixelinfo* dpi, std::string_view
|
||||
auto len = text.size() - *ttfRunIndex;
|
||||
ttf_draw_string_raw_ttf(dpi, text.substr(ttfRunIndex.value(), len), info);
|
||||
}
|
||||
# pragma GCC diagnostic pop
|
||||
}
|
||||
#endif // NO_TTF
|
||||
}
|
||||
|
||||
@@ -1829,6 +1829,8 @@ InteractionInfo set_interaction_info_from_paint_session(paint_session* session,
|
||||
next_ps = ps->children;
|
||||
}
|
||||
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Wnull-dereference"
|
||||
for (attached_paint_struct* attached_ps = ps->attached_ps; attached_ps != nullptr; attached_ps = attached_ps->next)
|
||||
{
|
||||
if (is_sprite_interacted_with(dpi, attached_ps->image_id, { (attached_ps->x + ps->x), (attached_ps->y + ps->y) }))
|
||||
@@ -1839,6 +1841,7 @@ InteractionInfo set_interaction_info_from_paint_session(paint_session* session,
|
||||
}
|
||||
}
|
||||
}
|
||||
#pragma GCC diagnostic pop
|
||||
|
||||
ps = old_ps;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user