1 /***************************************************************************** 2 * Copyright (c) 2014-2020 OpenRCT2 developers 3 * 4 * For a complete list of all authors, please refer to contributors.md 5 * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2 6 * 7 * OpenRCT2 is licensed under the GNU General Public License version 3. 8 *****************************************************************************/ 9 10 #include "AudioContext.h" 11 #include "AudioFormat.h" 12 13 #include <SDL.h> 14 #include <algorithm> 15 #include <openrct2/audio/AudioSource.h> 16 #include <openrct2/common.h> 17 #include <vector> 18 19 namespace OpenRCT2::Audio 20 { 21 /** 22 * An audio source where raw PCM data is initially loaded into RAM from 23 * a file and then streamed. 24 */ 25 class MemoryAudioSource final : public ISDLAudioSource 26 { 27 private: 28 AudioFormat _format = {}; 29 std::vector<uint8_t> _data; 30 uint8_t* _dataSDL = nullptr; 31 size_t _length = 0; 32 GetData()33 const uint8_t* GetData() 34 { 35 return _dataSDL != nullptr ? _dataSDL : _data.data(); 36 } 37 38 public: ~MemoryAudioSource()39 ~MemoryAudioSource() override 40 { 41 Unload(); 42 } 43 GetLength() const44 [[nodiscard]] uint64_t GetLength() const override 45 { 46 return _length; 47 } 48 GetFormat() const49 [[nodiscard]] AudioFormat GetFormat() const override 50 { 51 return _format; 52 } 53 Read(void * dst,uint64_t offset,size_t len)54 size_t Read(void* dst, uint64_t offset, size_t len) override 55 { 56 size_t bytesToRead = 0; 57 if (offset < _length) 58 { 59 bytesToRead = static_cast<size_t>(std::min<uint64_t>(len, _length - offset)); 60 61 auto src = GetData(); 62 if (src != nullptr) 63 { 64 std::copy_n(src + offset, bytesToRead, reinterpret_cast<uint8_t*>(dst)); 65 } 66 } 67 return bytesToRead; 68 } 69 LoadWAV(const utf8 * path)70 bool LoadWAV(const utf8* path) 71 { 72 log_verbose("MemoryAudioSource::LoadWAV(%s)", path); 73 74 Unload(); 75 76 bool result = false; 77 SDL_RWops* rw = SDL_RWFromFile(path, "rb"); 78 if (rw != nullptr) 79 { 80 SDL_AudioSpec audiospec = {}; 81 uint32_t audioLen; 82 SDL_AudioSpec* spec = SDL_LoadWAV_RW(rw, false, &audiospec, &_dataSDL, &audioLen); 83 if (spec != nullptr) 84 { 85 _format.freq = spec->freq; 86 _format.format = spec->format; 87 _format.channels = spec->channels; 88 _length = audioLen; 89 result = true; 90 } 91 else 92 { 93 log_verbose("Error loading %s, unsupported WAV format", path); 94 } 95 SDL_RWclose(rw); 96 } 97 else 98 { 99 log_verbose("Error loading %s", path); 100 } 101 return result; 102 } 103 LoadCSS1(const utf8 * path,size_t index)104 bool LoadCSS1(const utf8* path, size_t index) 105 { 106 log_verbose("MemoryAudioSource::LoadCSS1(%s, %d)", path, index); 107 108 Unload(); 109 110 bool result = false; 111 SDL_RWops* rw = SDL_RWFromFile(path, "rb"); 112 if (rw != nullptr) 113 { 114 uint32_t numSounds{}; 115 SDL_RWread(rw, &numSounds, sizeof(numSounds), 1); 116 if (index < numSounds) 117 { 118 SDL_RWseek(rw, index * 4, RW_SEEK_CUR); 119 120 uint32_t pcmOffset{}; 121 SDL_RWread(rw, &pcmOffset, sizeof(pcmOffset), 1); 122 SDL_RWseek(rw, pcmOffset, RW_SEEK_SET); 123 124 uint32_t pcmSize{}; 125 SDL_RWread(rw, &pcmSize, sizeof(pcmSize), 1); 126 _length = pcmSize; 127 128 WaveFormatEx waveFormat{}; 129 SDL_RWread(rw, &waveFormat, sizeof(waveFormat), 1); 130 _format.freq = waveFormat.frequency; 131 _format.format = AUDIO_S16LSB; 132 _format.channels = waveFormat.channels; 133 134 try 135 { 136 _data.resize(_length); 137 SDL_RWread(rw, _data.data(), _length, 1); 138 result = true; 139 } 140 catch (const std::bad_alloc&) 141 { 142 log_verbose("Unable to allocate data"); 143 } 144 } 145 SDL_RWclose(rw); 146 } 147 else 148 { 149 log_verbose("Unable to load %s", path); 150 } 151 return result; 152 } 153 Convert(const AudioFormat * format)154 bool Convert(const AudioFormat* format) 155 { 156 if (*format != _format) 157 { 158 SDL_AudioCVT cvt; 159 if (SDL_BuildAudioCVT( 160 &cvt, _format.format, _format.channels, _format.freq, format->format, format->channels, format->freq) 161 >= 0) 162 { 163 auto src = GetData(); 164 auto cvtBuffer = std::vector<uint8_t>(_length * cvt.len_mult); 165 std::copy_n(src, _length, cvtBuffer.data()); 166 cvt.len = static_cast<int32_t>(_length); 167 cvt.buf = cvtBuffer.data(); 168 if (SDL_ConvertAudio(&cvt) >= 0) 169 { 170 cvtBuffer.resize(cvt.len_cvt); 171 172 Unload(); 173 _data = std::move(cvtBuffer); 174 _length = cvt.len_cvt; 175 _format = *format; 176 return true; 177 } 178 } 179 } 180 return false; 181 } 182 183 private: Unload()184 void Unload() 185 { 186 // Free our data 187 _data.clear(); 188 _data.shrink_to_fit(); 189 190 // Free SDL2's data 191 SDL_FreeWAV(_dataSDL); 192 _dataSDL = nullptr; 193 194 _length = 0; 195 } 196 }; 197 CreateMemoryFromCSS1(const std::string & path,size_t index,const AudioFormat * targetFormat)198 IAudioSource* AudioSource::CreateMemoryFromCSS1(const std::string& path, size_t index, const AudioFormat* targetFormat) 199 { 200 auto source = new MemoryAudioSource(); 201 if (source->LoadCSS1(path.c_str(), index)) 202 { 203 if (targetFormat != nullptr && source->GetFormat() != *targetFormat) 204 { 205 if (!source->Convert(targetFormat)) 206 { 207 delete source; 208 source = nullptr; 209 } 210 } 211 } 212 else 213 { 214 delete source; 215 source = nullptr; 216 } 217 return source; 218 } 219 CreateMemoryFromWAV(const std::string & path,const AudioFormat * targetFormat)220 IAudioSource* AudioSource::CreateMemoryFromWAV(const std::string& path, const AudioFormat* targetFormat) 221 { 222 auto source = new MemoryAudioSource(); 223 if (source->LoadWAV(path.c_str())) 224 { 225 if (targetFormat != nullptr && source->GetFormat() != *targetFormat) 226 { 227 if (!source->Convert(targetFormat)) 228 { 229 SafeDelete(source); 230 } 231 } 232 } 233 else 234 { 235 delete source; 236 source = nullptr; 237 } 238 return source; 239 } 240 } // namespace OpenRCT2::Audio 241