diff --git a/src/openrct2-ui/audio/AudioChannel.cpp b/src/openrct2-ui/audio/AudioChannel.cpp index 51ea9d477b..e4f04b6a3f 100644 --- a/src/openrct2-ui/audio/AudioChannel.cpp +++ b/src/openrct2-ui/audio/AudioChannel.cpp @@ -9,6 +9,7 @@ #include "AudioContext.h" #include "AudioFormat.h" +#include "SDLAudioSource.h" #include #include @@ -18,7 +19,7 @@ namespace OpenRCT2::Audio { - template class AudioChannelImpl : public ISDLAudioChannel + template class AudioChannelImpl : public ISDLAudioChannel { static_assert(std::is_base_of_v); diff --git a/src/openrct2-ui/audio/AudioContext.cpp b/src/openrct2-ui/audio/AudioContext.cpp index a1b7697d1d..c192f044f8 100644 --- a/src/openrct2-ui/audio/AudioContext.cpp +++ b/src/openrct2-ui/audio/AudioContext.cpp @@ -11,6 +11,7 @@ #include "../SDLException.h" #include "AudioMixer.h" +#include "SDLAudioSource.h" #include #include @@ -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; 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 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 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 source) + IAudioSource* AddSource(std::unique_ptr source) { return _audioMixer->AddSource(std::move(source)); } diff --git a/src/openrct2-ui/audio/AudioContext.h b/src/openrct2-ui/audio/AudioContext.h index 92d0ad556d..325d61e97b 100644 --- a/src/openrct2-ui/audio/AudioContext.h +++ b/src/openrct2-ui/audio/AudioContext.h @@ -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 CreateMemoryFromCSS1( - const std::string& path, size_t index, const AudioFormat* targetFormat = nullptr); - std::unique_ptr CreateMemoryFromCSS1( - SDL_RWops* rw, size_t index, const AudioFormat* targetFormat = nullptr); - std::unique_ptr CreateMemoryFromWAV(SDL_RWops* rw, const AudioFormat* targetFormat = nullptr); - std::unique_ptr CreateStreamFromWAV(const std::string& path); - std::unique_ptr CreateStreamFromWAV(SDL_RWops* rw); - std::unique_ptr CreateStreamFromFlac(SDL_RWops* rw); - std::unique_ptr CreateStreamFromOgg(SDL_RWops* rw); - } // namespace AudioSource - namespace AudioChannel { ISDLAudioChannel* Create(); diff --git a/src/openrct2-ui/audio/AudioFormat.h b/src/openrct2-ui/audio/AudioFormat.h index 8f1a767edd..a3d1e11e73 100644 --- a/src/openrct2-ui/audio/AudioFormat.h +++ b/src/openrct2-ui/audio/AudioFormat.h @@ -10,7 +10,7 @@ #pragma once #include -#include +#include namespace OpenRCT2::Audio { diff --git a/src/openrct2-ui/audio/AudioMixer.cpp b/src/openrct2-ui/audio/AudioMixer.cpp index 0bf27de966..92cbf68c7a 100644 --- a/src/openrct2-ui/audio/AudioMixer.cpp +++ b/src/openrct2-ui/audio/AudioMixer.cpp @@ -105,7 +105,7 @@ void AudioMixer::SetVolume(float volume) _volume = volume; } -ISDLAudioSource* AudioMixer::AddSource(std::unique_ptr source) +SDLAudioSource* AudioMixer::AddSource(std::unique_ptr source) { std::lock_guard guard(_mutex); if (source != nullptr) @@ -122,7 +122,7 @@ void AudioMixer::RemoveReleasedSources() _sources.erase( std::remove_if( _sources.begin(), _sources.end(), - [](std::unique_ptr& source) { + [](std::unique_ptr& source) { { return source->IsReleased(); } diff --git a/src/openrct2-ui/audio/AudioMixer.h b/src/openrct2-ui/audio/AudioMixer.h index 4d011d7c8e..8ce6fabe0f 100644 --- a/src/openrct2-ui/audio/AudioMixer.h +++ b/src/openrct2-ui/audio/AudioMixer.h @@ -11,6 +11,7 @@ #include "AudioContext.h" #include "AudioFormat.h" +#include "SDLAudioSource.h" #include #include @@ -28,7 +29,7 @@ namespace OpenRCT2::Audio class AudioMixer final : public IAudioMixer { private: - std::vector> _sources; + std::vector> _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 source); + SDLAudioSource* AddSource(std::unique_ptr source); const AudioFormat& GetFormat() const; diff --git a/src/openrct2-ui/audio/FlacStream.cpp b/src/openrct2-ui/audio/FlacAudioSource.cpp similarity index 95% rename from src/openrct2-ui/audio/FlacStream.cpp rename to src/openrct2-ui/audio/FlacAudioSource.cpp index e6beed5e61..6b9e7827fd 100644 --- a/src/openrct2-ui/audio/FlacStream.cpp +++ b/src/openrct2-ui/audio/FlacAudioSource.cpp @@ -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 #include -#include -#include -#include +#include 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 AudioSource::CreateStreamFromFlac(SDL_RWops* rw) + std::unique_ptr CreateFlacAudioSource(SDL_RWops* rw) { auto source = std::make_unique(); if (!source->LoadFlac(rw)) diff --git a/src/openrct2-ui/audio/MemoryAudioSource.cpp b/src/openrct2-ui/audio/MemoryAudioSource.cpp index aee8322266..71e7ff2238 100644 --- a/src/openrct2-ui/audio/MemoryAudioSource.cpp +++ b/src/openrct2-ui/audio/MemoryAudioSource.cpp @@ -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 #include @@ -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 _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&& 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(std::min(len, _length - offset)); + bytesToRead = static_cast(std::min(len, _data.size() - offset)); - auto src = GetData(); + auto src = _data.data(); if (src != nullptr) { std::copy_n(src + offset, bytesToRead, reinterpret_cast(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(_length * cvt.len_mult); - std::copy_n(src, _length, cvtBuffer.data()); - cvt.len = static_cast(_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 AudioSource::CreateMemoryFromCSS1( - const std::string& path, size_t index, const AudioFormat* targetFormat) + static bool ConvertPcmData(const AudioFormat& target, const AudioFormat& src, std::vector& 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 AudioSource::CreateMemoryFromCSS1( - SDL_RWops* rw, size_t index, const AudioFormat* targetFormat) - { - auto source = std::make_unique(); - 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(srcLen * cvt.len_mult); + std::copy_n(srcData, srcLen, cvtBuffer.data()); + cvt.len = static_cast(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 AudioSource::CreateMemoryFromWAV(SDL_RWops* rw, const AudioFormat* targetFormat) + std::unique_ptr CreateMemoryAudioSource( + const AudioFormat& target, const AudioFormat& src, std::vector&& pcmData) { - auto source = std::make_unique(); - 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(target, std::move(pcmData)); } } // namespace OpenRCT2::Audio diff --git a/src/openrct2-ui/audio/OggStream.cpp b/src/openrct2-ui/audio/OggAudioSource.cpp similarity index 88% rename from src/openrct2-ui/audio/OggStream.cpp rename to src/openrct2-ui/audio/OggAudioSource.cpp index e7b0d0b9c5..48f5a3c42f 100644 --- a/src/openrct2-ui/audio/OggStream.cpp +++ b/src/openrct2-ui/audio/OggAudioSource.cpp @@ -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 -#include -#include -#include #include +#include #include 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 _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(datasource), ptr, size, nmemb); @@ -165,7 +148,7 @@ namespace OpenRCT2::Audio } }; - std::unique_ptr AudioSource::CreateStreamFromOgg(SDL_RWops* rw) + std::unique_ptr CreateOggAudioSource(SDL_RWops* rw) { auto source = std::make_unique(); if (!source->LoadOgg(rw)) diff --git a/src/openrct2-ui/audio/SDLAudioSource.cpp b/src/openrct2-ui/audio/SDLAudioSource.cpp new file mode 100644 index 0000000000..928ecd3219 --- /dev/null +++ b/src/openrct2-ui/audio/SDLAudioSource.cpp @@ -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::ToMemory(const AudioFormat& target) +{ + auto pcmLength = GetLength(); + + std::vector 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 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 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 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"); + } +} diff --git a/src/openrct2-ui/audio/SDLAudioSource.h b/src/openrct2-ui/audio/SDLAudioSource.h new file mode 100644 index 0000000000..3aefc3aaa3 --- /dev/null +++ b/src/openrct2-ui/audio/SDLAudioSource.h @@ -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 +#include +#include +#include + +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 ToMemory(const AudioFormat& target); + + virtual AudioFormat GetFormat() const = 0; + + protected: + virtual void Unload() = 0; + }; + + std::unique_ptr CreateAudioSource(SDL_RWops* rw); + std::unique_ptr CreateAudioSource(SDL_RWops* rw, uint32_t cssIndex); + std::unique_ptr CreateMemoryAudioSource( + const AudioFormat& target, const AudioFormat& src, std::vector&& pcmData); + std::unique_ptr CreateFlacAudioSource(SDL_RWops* rw); + std::unique_ptr CreateOggAudioSource(SDL_RWops* rw); + std::unique_ptr CreateWavAudioSource(SDL_RWops* rw); +} // namespace OpenRCT2::Audio diff --git a/src/openrct2-ui/audio/FileAudioSource.cpp b/src/openrct2-ui/audio/WavAudioSource.cpp similarity index 54% rename from src/openrct2-ui/audio/FileAudioSource.cpp rename to src/openrct2-ui/audio/WavAudioSource.cpp index 685efb7ec7..39a21082cb 100644 --- a/src/openrct2-ui/audio/FileAudioSource.cpp +++ b/src/openrct2-ui/audio/WavAudioSource.cpp @@ -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 -#include -#include -#include 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(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 AudioSource::CreateStreamFromWAV(const std::string& path) + std::unique_ptr CreateWavAudioSource(SDL_RWops* rw) { - auto* rw = SDL_RWFromFile(path.c_str(), "rb"); - if (rw != nullptr) - { - return AudioSource::CreateStreamFromWAV(rw); - } - return nullptr; - } - - std::unique_ptr AudioSource::CreateStreamFromWAV(SDL_RWops* rw) - { - auto source = std::make_unique(); - if (!source->LoadWAV(rw)) - { - source = nullptr; - } - return source; + return std::make_unique(rw); } } // namespace OpenRCT2::Audio diff --git a/src/openrct2/audio/AudioContext.h b/src/openrct2/audio/AudioContext.h index 5cd63b40b6..815cf5eb61 100644 --- a/src/openrct2/audio/AudioContext.h +++ b/src/openrct2/audio/AudioContext.h @@ -34,9 +34,7 @@ namespace OpenRCT2::Audio virtual std::vector 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 stream, uint32_t index) abstract; - virtual IAudioSource* CreateStreamFromWAV(const std::string& path) abstract; virtual IAudioSource* CreateStreamFromWAV(std::unique_ptr stream) abstract; virtual void StartTitleMusic() abstract; diff --git a/src/openrct2/audio/DummyAudioContext.cpp b/src/openrct2/audio/DummyAudioContext.cpp index 90795ba341..fe88cec760 100644 --- a/src/openrct2/audio/DummyAudioContext.cpp +++ b/src/openrct2/audio/DummyAudioContext.cpp @@ -26,21 +26,11 @@ namespace OpenRCT2::Audio { } - IAudioSource* CreateStreamFromCSS(const std::string& /* path */, uint32_t /* index */) override - { - return nullptr; - } - IAudioSource* CreateStreamFromCSS(std::unique_ptr /* stream */, uint32_t /* index */) override { return nullptr; } - IAudioSource* CreateStreamFromWAV(const std::string& /*path*/) override - { - return nullptr; - } - IAudioSource* CreateStreamFromWAV(std::unique_ptr) override { return nullptr; diff --git a/src/openrct2/drawing/Drawing.String.cpp b/src/openrct2/drawing/Drawing.String.cpp index 41165d0348..8eb14d3332 100644 --- a/src/openrct2/drawing/Drawing.String.cpp +++ b/src/openrct2/drawing/Drawing.String.cpp @@ -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 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 } diff --git a/src/openrct2/interface/Viewport.cpp b/src/openrct2/interface/Viewport.cpp index a996d7c587..75565a94c9 100644 --- a/src/openrct2/interface/Viewport.cpp +++ b/src/openrct2/interface/Viewport.cpp @@ -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; }