1
0
mirror of https://github.com/OpenRCT2/OpenRCT2 synced 2026-01-18 04:23:20 +01:00
Files
OpenRCT2/src/openrct2-ui/audio/FileAudioSource.cpp
2021-07-30 21:31:10 +03:00

236 lines
7.1 KiB
C++

/*****************************************************************************
* Copyright (c) 2014-2020 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 "AudioContext.h"
#include "AudioFormat.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.
*/
class FileAudioSource final : public ISDLAudioSource
{
private:
AudioFormat _format = {};
SDL_RWops* _rw = nullptr;
uint64_t _dataBegin = 0;
uint64_t _dataLength = 0;
public:
~FileAudioSource() override
{
Unload();
}
[[nodiscard]] uint64_t GetLength() const override
{
return _dataLength;
}
[[nodiscard]] AudioFormat GetFormat() const override
{
return _format;
}
size_t Read(void* dst, uint64_t offset, size_t len) override
{
size_t bytesRead = 0;
int64_t currentPosition = SDL_RWtell(_rw);
if (currentPosition != -1)
{
size_t bytesToRead = static_cast<size_t>(std::min<uint64_t>(len, _dataLength - offset));
int64_t dataOffset = _dataBegin + offset;
if (currentPosition != dataOffset)
{
int64_t newPosition = SDL_RWseek(_rw, dataOffset, SEEK_SET);
if (newPosition == -1)
{
return 0;
}
}
bytesRead = SDL_RWread(_rw, dst, 1, bytesToRead);
}
return bytesRead;
}
bool LoadWAV(SDL_RWops* rw)
{
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)
{
return false;
}
_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;
}
private:
static uint32_t FindChunk(SDL_RWops* rw, uint32_t wantedId)
{
uint32_t subchunkId = SDL_ReadLE32(rw);
uint32_t subchunkSize = SDL_ReadLE32(rw);
if (subchunkId == wantedId)
{
return subchunkSize;
}
constexpr uint32_t FACT = 0x74636166;
constexpr uint32_t LIST = 0x5453494c;
constexpr uint32_t BEXT = 0x74786562;
constexpr uint32_t JUNK = 0x4B4E554A;
while (subchunkId == FACT || subchunkId == LIST || subchunkId == BEXT || subchunkId == JUNK)
{
SDL_RWseek(rw, subchunkSize, RW_SEEK_CUR);
subchunkId = SDL_ReadLE32(rw);
subchunkSize = SDL_ReadLE32(rw);
if (subchunkId == wantedId)
{
return subchunkSize;
}
}
return 0;
}
void Unload()
{
if (_rw != nullptr)
{
SDL_RWclose(_rw);
_rw = nullptr;
}
_dataBegin = 0;
_dataLength = 0;
}
};
IAudioSource* AudioSource::CreateStreamFromWAV(const std::string& path)
{
IAudioSource* source = nullptr;
SDL_RWops* rw = SDL_RWFromFile(path.c_str(), "rb");
if (rw != nullptr)
{
return AudioSource::CreateStreamFromWAV(rw);
}
return source;
}
IAudioSource* AudioSource::CreateStreamFromWAV(SDL_RWops* rw)
{
auto source = new FileAudioSource();
if (!source->LoadWAV(rw))
{
delete source;
source = nullptr;
}
return source;
}
IAudioSource* AudioSource::CreateStreamFromWAV(std::unique_ptr<IStream> stream)
{
auto rw = new SDL_RWops();
*rw = {};
rw->type = SDL_RWOPS_UNKNOWN;
rw->hidden.unknown.data1 = stream.release();
rw->seek = [](SDL_RWops* ctx, Sint64 offset, int whence) {
auto ptr = static_cast<IStream*>(ctx->hidden.unknown.data1);
ptr->Seek(offset, whence);
return static_cast<Sint64>(ptr->GetPosition());
};
rw->read = [](SDL_RWops* ctx, void* buf, size_t size, size_t maxnum) {
auto ptr = static_cast<IStream*>(ctx->hidden.unknown.data1);
return static_cast<size_t>(ptr->TryRead(buf, size * maxnum) / size);
};
rw->size = [](SDL_RWops* ctx) {
auto ptr = static_cast<IStream*>(ctx->hidden.unknown.data1);
return static_cast<Sint64>(ptr->GetLength());
};
rw->close = [](SDL_RWops* ctx) {
auto ptr = static_cast<IStream*>(ctx->hidden.unknown.data1);
delete ptr;
ctx->hidden.unknown.data1 = nullptr;
delete ctx;
return 0;
};
return CreateStreamFromWAV(rw);
}
} // namespace OpenRCT2::Audio