Main Page · Modules · All Classes · Class Hierarchy
MASoundDatabase.cpp
1 /*
2  * This file is part of the AiBO+ project
3  *
4  * Copyright (C) 2005-2016 Csaba Kertész (csaba.kertesz@gmail.com)
5  *
6  * AiBO+ is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * AiBO+ is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Street #330, Boston, MA 02111-1307, USA.
19  *
20  */
21 
22 #include "MASoundDatabase.hpp"
23 
24 #include "core/MACoreTypes.hpp"
25 #include "MASoundFile.hpp"
26 
27 #include <MCLog.hpp>
28 
29 #include <boost/algorithm/string/join.hpp>
30 #include <boost/algorithm/string/split.hpp>
31 
32 namespace MA
33 {
34 MCThreadLocalData<MASoundDatabase> SoundDatabase(true);
35 
36 std::string SoundProfileConfigName("SOUND_PROFILE");
37 }
38 
39 MASoundDatabase::MASoundDatabase() : DatabaseFileHandler(nullptr), SoundProfile("csaba")
40 {
41 }
42 
43 
44 MASoundDatabase::~MASoundDatabase()
45 {
46  if (DatabaseFileHandler)
47  {
48  fclose(DatabaseFileHandler);
49  DatabaseFileHandler = nullptr;
50  }
51 }
52 
53 
55 {
56  if (SoundProfile == "tts")
57  {
58  SoundProfile = "csaba";
59  } else
60  if (SoundProfile == "csaba")
61  {
62  SoundProfile = "marissa";
63  } else
64  if (SoundProfile == "marissa")
65  {
66  SoundProfile = "gillian";
67  } else
68  if (SoundProfile == "gillian")
69  {
70  SoundProfile = "tts";
71  }
72  MC_LOG("New sound profile: %s", SoundProfile.c_str());
73 }
74 
75 
77 {
78  return SoundProfile;
79 }
80 
81 
82 void MASoundDatabase::SetProfileName(const std::string& profile_name)
83 {
84  if (profile_name == "tts" || profile_name == "csaba" || profile_name == "marissa" ||
85  profile_name == "gillian")
86  {
87  SoundProfile = profile_name;
88  } else {
89  SoundProfile = "csaba";
90  }
91  MC_LOG("New sound profile: %s", SoundProfile.c_str());
92 }
93 
94 
95 std::string MASoundDatabase::GetSoundFileName(const std::string& sound_name)
96 {
97  std::string JokerName = sound_name+"_all.raw";
98  std::string ExpectedName = sound_name+'_'+SoundProfile+".raw";
99  std::string FallbackName = sound_name+"_tts.raw";
100 
101  if (Files.find(JokerName) != Files.end())
102  {
103  return JokerName;
104  } else
105  if (Files.find(ExpectedName) != Files.end())
106  {
107  return ExpectedName;
108  } else
109  if (Files.find(FallbackName) != Files.end())
110  {
111  return FallbackName;
112  }
113  return "";
114 }
115 
116 
117 MA::SoundBaseSPtr MASoundDatabase::GetSoundFile(const std::string& file_name, bool caching)
118 {
119  const auto Iter = Files.find(file_name);
120 
121  if (Iter != Files.end())
122  {
123  MASoundFile* SoundFile = nullptr;
124  MC::StringList StrParts;
125  std::string DisplayName;
126 
127  boost::split(StrParts, Iter->first, boost::is_any_of("_"));
128  StrParts.pop_back();
129  DisplayName = boost::algorithm::join(StrParts, "");
130  SoundFile = new MASoundFile(DisplayName, DatabaseFileHandler, Iter->second.first, Iter->second.second, caching);
131  return MA::SoundBaseSPtr(SoundFile);
132  }
133  return MA::SoundBaseSPtr();
134 }
135 
136 
137 bool MASoundDatabase::SaveToFile(const std::string& file_name, const MC::StringList& sound_file_names,
138  const MA::BinaryDataSPtrList& sounds)
139 {
140  // Safety checks
141  if (file_name.empty() || sound_file_names.size() != sounds.size() || sounds.size() == 0)
142  return false;
143 
144  for (unsigned int i = 0; i < sounds.size(); ++i)
145  {
146  if (sound_file_names[i].empty() || !sounds[i].get() || sounds[i]->GetSize() <= 0)
147  return false;
148  }
149  // Build the header
150  MC::BinaryDataSPtr HeaderData(new MCBinaryData(30000));
151  int HeaderSize = 0;
152 
153  HeaderData->IncrementPosition(4);
154  for (unsigned int i = 0; i < sounds.size(); ++i)
155  {
156  HeaderData->AddInt16(sound_file_names[i].size());
157  HeaderData->AddString(sound_file_names[i]);
158  HeaderData->AddInt32(sounds[i]->GetSize());
159  }
160  HeaderSize = HeaderData->GetPosition();
161  HeaderData->ResetPosition();
162  HeaderData->AddInt32(HeaderSize-4);
163  // Write the sound database to the file
164  FILE* FileHandler = fopen(file_name.c_str(), "wb+");
165 
166  fwrite(HeaderData->GetData(), HeaderSize, 1, FileHandler);
167  for (unsigned int i = 0; i < sounds.size(); ++i)
168  {
169  fwrite(sounds[i]->GetData(), sounds[i]->GetSize(), 1, FileHandler);
170  }
171  fclose(FileHandler);
172  return true;
173 }
174 
175 
176 MASoundDatabase* MASoundDatabase::LoadFromFile(const std::string& file_name)
177 {
178  // Load sound database from the file
179  FILE* FileHandler = fopen(file_name.c_str(), "rb");
180 
181  if (!FileHandler)
182  {
183  MC_WARNING("Unable to open a sound database file (%s)... :-|", file_name.c_str());
184  return nullptr;
185  }
186  MASoundDatabase* Database = new MASoundDatabase;
187  int FileSize = 0;
188  int HeaderSize = 0;
189  MC::BinaryDataSPtr FileData(new MCBinaryData(4));
190 
191  // Determinate the file size
192  fseek(FileHandler, 0, SEEK_END);
193  FileSize = (int)ftell(FileHandler);
194  // Empty file is an error
195  if (FileSize == 0)
196  {
197  delete Database;
198  Database = nullptr;
199  fclose(FileHandler);
200  FileHandler = nullptr;
201  MC_WARNING("Empty sound database file (%s)... :-|", file_name.c_str());
202  return nullptr;
203  }
204  // Go back to the beginning of the file
205  fseek(FileHandler, 0, SEEK_SET);
206  int Ret = fread(FileData->GetData(), 4, 1, FileHandler);
207 
208  HeaderSize = FileData->GetInt32();
209  if (HeaderSize > FileSize && Ret != 4)
210  {
211  delete Database;
212  Database = nullptr;
213  fclose(FileHandler);
214  FileHandler = nullptr;
215  MC_WARNING("Corrupt header in the sound database file (%s)... :-|", file_name.c_str());
216  return nullptr;
217  }
218  // Read the header
219  FileData->Allocate(HeaderSize);
220  Ret = fread(FileData->GetData(), HeaderSize, 1, FileHandler);
221  if (HeaderSize > FileSize && Ret != HeaderSize)
222  {
223  delete Database;
224  Database = nullptr;
225  fclose(FileHandler);
226  FileHandler = nullptr;
227  MC_WARNING("Can't read the header in the sound database file (%s)... :-|", file_name.c_str());
228  return nullptr;
229  }
230  while (!FileData->IsPositionAtEnd())
231  {
232  int StrSize = FileData->GetInt16();
233  int FileSize = 0;
234  std::string FileName;
235 
236  if (StrSize > 0 && StrSize < 100)
237  {
238  FileName = FileData->GetString(StrSize, "_.");
239  FileSize = FileData->GetInt32();
240  }
241  if (FileName.empty() || FileSize <= 0)
242  {
243  delete Database;
244  Database = nullptr;
245  fclose(FileHandler);
246  FileHandler = nullptr;
247  MC_WARNING("Corrupt header in the sound database file (%s)... :-|", file_name.c_str());
248  return nullptr;
249  }
250  Database->Files.insert(std::make_pair(FileName, MA::SoundFileInfo(0, FileSize)));
251  }
252  // Parse the rest of the database
253  for (auto& file : Database->Files)
254  {
255  file.second.first = (int)ftell(FileHandler);
256  if (fseek(FileHandler, file.second.second, SEEK_CUR) == 0 && (int)ftell(FileHandler) > FileSize)
257  {
258  delete Database;
259  Database = nullptr;
260  fclose(FileHandler);
261  FileHandler = nullptr;
262  MC_WARNING("Unexpected EOF in the sound database file (%s)... :-|", file_name.c_str());
263  return nullptr;
264  }
265  }
266  if ((int)ftell(FileHandler) < FileSize)
267  {
268  delete Database;
269  Database = nullptr;
270  fclose(FileHandler);
271  FileHandler = nullptr;
272  MC_WARNING("Too much data in the sound database file (%s)... :-|", file_name.c_str());
273  return nullptr;
274  }
275  Database->DatabaseFileHandler = FileHandler;
276  return Database;
277 }
std::string GetProfileName() const
Get sound profile name.
Sound database class.
Binary data class.
void SetProfileName(const std::string &profile_name)
Set sound profile name.
static MASoundDatabase * LoadFromFile(const std::string &file_name)
Load sound database from file.
#define MC_WARNING(...)
Warning macro.
Definition: MCLog.hpp:43
std::string GetSoundFileName(const std::string &sound_name)
Get an sound file name.
static bool SaveToFile(const std::string &file_name, const MC::StringList &sound_file_names, const MA::BinaryDataSPtrList &sounds)
Assembly an sound database file.
MA::SoundFileMap Files
Sound files.
MA::SoundBaseSPtr GetSoundFile(const std::string &sound_name, bool caching)
Get an sound file.
A wrapper class to cover boost::thread_specific_ptr/folly::ThreadLocal API on certain targets...
Sound file.
Definition: MASoundFile.hpp:37
#define MC_LOG(...)
Debug macro.
Definition: MCLog.hpp:41
FILE * DatabaseFileHandler
Sound database handler.
void SwitchProfile()
Switch sound profile.