Cytopia  0.3
A city building simulation game
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
ResourceManager.cxx
Go to the documentation of this file.
1 #include "ResourceManager.hxx"
2 
3 #ifdef USE_AUDIO
4 #include "AL/al.h"
5 #include "AL/alc.h"
6 
7 //#include "ogg/ogg.h"
8 #include "vorbis/codec.h"
9 #include "vorbis/vorbisfile.h"
10 #include "vorbis/vorbisenc.h"
11 
12 #include <cstdio>
13 #include <cstdlib>
14 #include <cmath>
15 
16 //windows vorbisfile audio decoding,
17 //need to declare these libraries so that we can set stdin/stdout to binary
18 //for audio decoding on windows
19 #ifdef _WIN32
20 #include <io.h>
21 #include <fcntl.h>
22 #endif
23 
24 #endif
25 
26 #include "../util/Exception.hxx"
27 #include "../util/LOG.hxx"
28 
30 #include <fstream>
31 #include "Filesystem.hxx"
32 
34 using nlohmann::json;
35 
36 ResourceManager::ResourceManager() : m_Age(0), m_CacheSize(0)
37 {
38 #ifdef USE_AUDIO
39  std::string jsonFileContent = fs::readFileAsString(Settings::instance().audioConfigJSONFile.get());
40  json config_json = json::parse(jsonFileContent, nullptr, false);
41 
42  // check if json file can be parsed
43  if (config_json.is_discarded())
44  throw ConfigurationError(TRACE_INFO "Error parsing JSON File " + Settings::instance().audioConfigJSONFile.get());
45 
46  m_audioConfig = config_json;
47 #endif // USE_AUDIO
48 }
49 
50 #ifdef USE_AUDIO
51 int ResourceManager::LoadAudioWithOggVorbis(std::string path, DecodedAudioData &dAudioBuffer)
52 {
53 
54  OggVorbis_File vf;
55  bool eof = false;
56  int current_section;
57 
58  //small buffer to store small quantities of data
59  char pcmout[4096];
60 
61 #ifdef _WIN32
62  _setmode(_fileno(stdin), _O_BINARY);
63  _setmode(_fileno(stdout), _O_BINARY);
64 #endif
65 
66  //open file
67  ifstream file(path, std::ifstream::in | std::ifstream::binary);
68 
69  //check if file is an vorbis ogg file
70  if (ov_fopen(path.c_str(), &vf) < 0)
71  {
72  LOG(LOG_ERROR) << "Input does not appear to be an Ogg bitstream. \n" << stderr;
73  return -1;
74  }
75 
76  //readwhile end of file has not been reached yet
77  while (!eof)
78  {
79 
80  //read content from file
81  // length of buffer
82  // bingendianp bit packing = 0 = little endian
83  // word size = 2 = 16-bit
84  // sgned = 1 = signed
85 
86  //read to character buffer
87  //number of bytes returned
88  long ret = ov_read(&vf, pcmout, 4096, 0, 2, 1, &current_section);
89 
90  //check for errors
91  //insert char data into audio buffer if there are no errors
92  switch (ret)
93  {
94  case 0:
95  eof = true;
96  break;
97  case OV_HOLE:
98  LOG(LOG_ERROR) << "ERROR: OV_HOLE found in initial read of buffer\n";
99  return -1;
100  break;
101  case OV_EBADLINK:
102  LOG(LOG_ERROR) << "ERROR: OV_EBADLINK found in initial read of buffer\n";
103  return -1;
104  break;
105  case OV_EINVAL:
106  LOG(LOG_ERROR) << "ERROR: OV_EINVAL found in initial read of buffer\n";
107  return -1;
108  break;
109  default:
110  dAudioBuffer.char_data_vec.insert(dAudioBuffer.char_data_vec.end(), pcmout, pcmout + ret);
111  break;
112  }
113  }
114 
115  //set number of bytes in buffer
116  dAudioBuffer.nBytes = dAudioBuffer.char_data_vec.size() * sizeof(char);
117 
118  //get sample rate
119  vorbis_info *vorbisInfo = ov_info(&vf, -1);
120  dAudioBuffer.data_sample_rate = vorbisInfo->rate;
121 
122  //clear data
123  ov_clear(&vf);
124 
125  LOG(LOG_DEBUG) << "Audio data read successful! Loaded into decoded audio buffer.\n";
126  return 0;
127 }
128 
129 void ResourceManager::fetch(SoundtrackID id)
130 {
131  if (m_soundtracks.count(id) > 0)
132  {
133  m_soundtracks[id].age = m_Age++;
134  return;
135  }
136  string filepath;
137  bool isMusic = false;
139  if (m_audioConfig.Music.count(id.get()) > 0)
140  {
141  config = &m_audioConfig.Music.at(id.get());
142  isMusic = true;
143  }
144  else if (m_audioConfig.Sound.count(id.get()) > 0)
145  config = &m_audioConfig.Sound.at(id.get());
146  if (Settings::instance().audio3DStatus)
147  filepath = fs::getBasePath() + config->monoFilePath;
148  else
149  filepath = fs::getBasePath() + config->stereoFilePath;
150  LOG(LOG_INFO) << "Fetching " << id.get() << " at " << filepath;
151 
152  DecodedAudioData dataBuffer;
153  if (LoadAudioWithOggVorbis(filepath.c_str(), dataBuffer) == -1)
154  {
155  throw AudioError(TRACE_INFO "Failed to read sound file with libvorbis.\n ");
156  }
157 
158  if (dataBuffer.char_data_vec.empty())
159  throw AudioError(TRACE_INFO "Could not read sound file: It is empty");
160 
161  m_CacheSize += sizeof(dataBuffer) + sizeof(Soundtrack) + sizeof(SoundtrackResource) + dataBuffer.nBytes;
162  auto soundtrack = new Soundtrack{id, ChannelID{-1}, &dataBuffer, RepeatCount{0}, isMusic, false, true, true};
163  m_soundtracks[id] = SoundtrackResource{SoundtrackUPtr{soundtrack}, m_Age++};
164 
165  LOG(LOG_INFO) << "Resource cache is now at " << (m_CacheSize / 1000000) << "MB";
166  if (m_CacheSize > MAX_RESOURCE_BYTES::value)
167  prune();
168 }
169 #endif // USE_AUDIO
170 
172 {
173 #ifdef USE_AUDIO
174  size_t total_size = 0;
175  total_size += m_soundtracks.size();
176  /* We evict a half of kept resources */
177  total_size /= 2;
178  std::vector<uint32_t> all_ages;
180  all_ages.insert<AgeIt>(all_ages.cend(), AgeIterator{m_soundtracks.begin()}, AgeIterator{m_soundtracks.end()});
181  std::nth_element(all_ages.begin(), all_ages.begin() + total_size, all_ages.end());
182  uint32_t median = all_ages[total_size];
183  for (auto it = m_soundtracks.begin(); it != m_soundtracks.end();)
184  {
185  if (it->second.age < median and !it->second.resource->isPlaying)
186  {
187  int sizeBytes = 0;
188  alGetBufferi(it->second.resource->buffer, AL_SIZE, &sizeBytes);
189  m_CacheSize -= sizeof(DecodedAudioData) + sizeof(Soundtrack) + sizeof(SoundtrackUPtr) + sizeBytes;
190  it = m_soundtracks.erase(it);
191  }
192  else
193  {
194  it->second.age = 0;
195  it++;
196  }
197  }
198 #endif // USE_AUDIO
199  m_Age = 0;
200  LOG(LOG_INFO) << "After eviction, cache is " << (m_CacheSize / 1000000) << "MB";
201 }
TRACE_INFO
#define TRACE_INFO
Definition: Exception.hxx:12
ConfigurationError
A configuration error.
Definition: Exception.hxx:36
StrongType< string, struct SoundtrackIDTag >
ResourceManager::prune
void prune()
Cleans up the cache of unused resources.
Definition: ResourceManager.cxx:171
DecodedAudioData::data_sample_rate
int data_sample_rate
Definition: Soundtrack.hxx:29
LOG
Definition: LOG.hxx:32
LOG_INFO
@ LOG_INFO
Definition: LOG.hxx:25
AudioConfig::SoundtrackConfiguration::stereoFilePath
string stereoFilePath
Definition: AudioConfig.hxx:26
ResourceManager::ResourceManager
ResourceManager()
Creates the ResourceManager.
Definition: ResourceManager.cxx:36
SoundtrackUPtr
std::unique_ptr< Soundtrack > SoundtrackUPtr
Definition: Soundtrack.hxx:98
ResourceManager::AgeIterator
Definition: ResourceManager.hxx:38
LOG_ERROR
@ LOG_ERROR
Definition: LOG.hxx:28
ResourceManager::m_Age
uint32_t m_Age
Definition: ResourceManager.hxx:32
ResourceManager::m_CacheSize
uint32_t m_CacheSize
Definition: ResourceManager.hxx:33
LOG_DEBUG
@ LOG_DEBUG
Definition: LOG.hxx:26
readFileAsString
std::string readFileAsString(const std::string &fileName, bool binaryMode)
Read contents from a file as string.
Definition: Filesystem.cxx:12
ifstream
std::ifstream ifstream
Definition: AudioMixer.cxx:14
getBasePath
std::string getBasePath()
Get base path (where Cytopia is being run)
Definition: Filesystem.cxx:77
Filesystem.hxx
DecodedAudioData::char_data_vec
std::vector< char > char_data_vec
pcm audio data
Definition: Soundtrack.hxx:27
DecodedAudioData
Container for raw pcm data that read from .ogg sound file.
Definition: Soundtrack.hxx:25
ifstream
std::ifstream ifstream
Definition: ResourceManager.cxx:33
JsonSerialization.hxx
AudioConfig::SoundtrackConfiguration
Definition: AudioConfig.hxx:24
DecodedAudioData::nBytes
long nBytes
number of bytes in decoded audio data
Definition: Soundtrack.hxx:28
Singleton< Settings >::instance
static Settings & instance(void)
Get an instance of the singleton.
Definition: Singleton.hxx:15
ResourceManager.hxx
Soundtrack
Definition: Soundtrack.hxx:32
json
nlohmann::json json
Definition: Settings.hxx:12
ResourceManager::get
resource_type< ResourceID >::type get(const ResourceID &)
Fetches and return a Soundtrack.
Definition: ResourceManager.inl.hxx:14
string
std::string string
Definition: AudioConfig.hxx:14
AudioConfig::SoundtrackConfiguration::monoFilePath
string monoFilePath
Definition: AudioConfig.hxx:27
AudioError
An audio-related error occured.
Definition: Exception.hxx:52