OpenRCT2/src/openrct2-ui/audio/SDLAudioSource.cpp

165 lines
4.3 KiB
C++

/*****************************************************************************
* Copyright (c) 2014-2024 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.
*****************************************************************************/
#include "SDLAudioSource.h"
#include <openrct2/Context.h>
#include <openrct2/audio/AudioContext.h>
#include <stdexcept>
using namespace OpenRCT2::Audio;
enum class AudioCodecKind
{
Unknown,
Wav,
Ogg,
Flac,
};
bool SDLAudioSource::IsReleased() const
{
return _released;
}
void SDLAudioSource::Release()
{
if (!_released)
{
// Lock the mixer to make sure we aren't mixing
// the source as we dispose it
auto mixer = GetMixer();
if (mixer != nullptr)
mixer->Lock();
Unload();
if (mixer != nullptr)
mixer->Unlock();
_released = true;
}
}
IAudioMixer* SDLAudioSource::GetMixer()
{
auto ctx = GetContext();
if (ctx == nullptr)
return nullptr;
auto audioContext = ctx->GetAudioContext();
if (audioContext == nullptr)
return nullptr;
return audioContext->GetMixer();
}
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");
}
}