1
0
mirror of https://github.com/OpenRCT2/OpenRCT2 synced 2025-12-10 09:32:29 +01:00

Refactor audio sources

This commit is contained in:
Ted John
2022-05-14 18:21:48 +01:00
parent 274bd921b3
commit 95661f4df2
16 changed files with 376 additions and 466 deletions

View File

@@ -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_>);

View File

@@ -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));
}

View File

@@ -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();

View File

@@ -10,7 +10,7 @@
#pragma once
#include <SDL.h>
#include <openrct2/common.h>
#include <cstdint>
namespace OpenRCT2::Audio
{

View File

@@ -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();
}

View File

@@ -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;

View File

@@ -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))

View File

@@ -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

View File

@@ -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))

View 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");
}
}

View 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

View File

@@ -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

View File

@@ -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;

View File

@@ -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;

View File

@@ -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
}

View File

@@ -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;
}