1
0
mirror of https://github.com/OpenRCT2/OpenRCT2 synced 2026-01-16 11:33:03 +01:00
Files
OpenRCT2/src/openrct2/audio/MemoryAudioSource.cpp
2017-01-04 17:44:12 +00:00

246 lines
6.5 KiB
C++

#pragma region Copyright (c) 2014-2016 OpenRCT2 Developers
/*****************************************************************************
* OpenRCT2, an open source clone of Roller Coaster Tycoon 2.
*
* OpenRCT2 is the work of many authors, a full list can be found in contributors.md
* For more information, visit https://github.com/OpenRCT2/OpenRCT2
*
* OpenRCT2 is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* A full copy of the GNU General Public License can be found in licence.txt
*****************************************************************************/
#pragma endregion
#include "../core/Math.hpp"
#include "../core/Memory.hpp"
#include "AudioMixer.h"
#include "AudioSource.h"
#pragma pack(push, 1)
struct WaveFormatEx
{
uint16 encoding;
uint16 channels;
uint32 frequency;
uint32 byterate;
uint16 blockalign;
uint16 bitspersample;
uint16 extrasize;
};
assert_struct_size(WaveFormatEx, 18);
#pragma pack(pop)
/**
* An audio source where raw PCM data is initially loaded into RAM from
* a file and then streamed.
*/
class MemoryAudioSource : public IAudioSource
{
private:
AudioFormat _format = { 0 };
uint8 * _data = nullptr;
size_t _length = 0;
bool _isSDLWav = false;
public:
~MemoryAudioSource()
{
Unload();
}
uint64 GetLength() override
{
return _length;
}
AudioFormat GetFormat() override
{
return _format;
}
size_t Read(void * dst, uint64 offset, size_t len) override
{
size_t bytesToRead = 0;
if (offset < _length)
{
bytesToRead = (size_t)Math::Min<uint64>(len, _length - offset);
Memory::Copy<void>(dst, _data + offset, bytesToRead);
}
return bytesToRead;
}
bool LoadWAV(const utf8 * path)
{
log_verbose("MemoryAudioSource::LoadWAV(%s)", path);
Unload();
bool result = false;
SDL_RWops * rw = SDL_RWFromFile(path, "rb");
if (rw != nullptr)
{
SDL_AudioSpec audiospec = { 0 };
uint32 audioLen;
SDL_AudioSpec * spec = SDL_LoadWAV_RW(rw, false, &audiospec, &_data, &audioLen);
if (spec != nullptr)
{
_format.freq = spec->freq;
_format.format = spec->format;
_format.channels = spec->channels;
_length = audioLen;
_isSDLWav = true;
result = true;
}
else
{
log_verbose("Error loading %s, unsupported WAV format", path);
}
SDL_RWclose(rw);
}
else
{
log_verbose("Error loading %s", path);
}
return result;
}
bool LoadCSS1(const utf8 * path, size_t index)
{
log_verbose("MemoryAudioSource::LoadCSS1(%s, %d)", path, index);
Unload();
bool result = false;
SDL_RWops * rw = SDL_RWFromFile(path, "rb");
if (rw != nullptr)
{
uint32 numSounds;
SDL_RWread(rw, &numSounds, sizeof(numSounds), 1);
if (index < numSounds)
{
SDL_RWseek(rw, index * 4, RW_SEEK_CUR);
uint32 pcmOffset;
SDL_RWread(rw, &pcmOffset, sizeof(pcmOffset), 1);
SDL_RWseek(rw, pcmOffset, RW_SEEK_SET);
uint32 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;
_data = new (std::nothrow) uint8[_length];
if (_data != nullptr)
{
SDL_RWread(rw, _data, _length, 1);
result = true;
}
else
{
log_verbose("Unable to allocate data");
}
}
SDL_RWclose(rw);
}
else
{
log_verbose("Unable to load %s", path);
}
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)
{
cvt.len = (int)_length;
cvt.buf = new uint8[cvt.len * cvt.len_mult];
Memory::Copy(cvt.buf, _data, _length);
if (SDL_ConvertAudio(&cvt) >= 0)
{
Unload();
_data = cvt.buf;
_length = cvt.len_cvt;
_format = *format;
return true;
}
else
{
delete[] cvt.buf;
}
}
}
return false;
}
private:
void Unload()
{
if (_data != nullptr)
{
if (_isSDLWav)
{
SDL_FreeWAV(_data);
}
else
{
delete[] _data;
}
_data = nullptr;
}
_isSDLWav = false;
_length = 0;
}
};
IAudioSource * AudioSource::CreateMemoryFromCSS1(const utf8 * path, size_t index, const AudioFormat * targetFormat)
{
auto source = new MemoryAudioSource();
if (source->LoadCSS1(path, index))
{
if (targetFormat != nullptr)
{
if (!source->Convert(targetFormat))
{
SafeDelete(source);
}
}
}
else
{
SafeDelete(source);
}
return source;
}
IAudioSource * AudioSource::CreateMemoryFromWAV(const utf8 * path, const AudioFormat * targetFormat)
{
auto source = new MemoryAudioSource();
if (source->LoadWAV(path))
{
if (targetFormat != nullptr)
{
if (!source->Convert(targetFormat))
{
SafeDelete(source);
}
}
}
else
{
SafeDelete(source);
}
return source;
}