Main Page · Modules · All Classes · Class Hierarchy
MASkit.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 "MASkit.hpp"
23 
24 #include "sound/MASoundData.hpp"
25 
26 #include <MCLog.hpp>
27 #include <MCThreadLocalData.hpp>
28 
29 #if defined(LIBARCHIVE_USE_INTERNAL)
30 #include <archive.h> //krazy:exclude=includes
31 #include <archive_entry.h> //krazy:exclude=includes
32 // Hack to include private headers from libarchive
33 #define __LIBARCHIVE_BUILD
34 #include <archive_private.h>
35 #else
36 #include <archive.h>
37 #include <archive_entry.h>
38 #endif
39 
40 #include <boost/algorithm/string/predicate.hpp>
41 #include <boost/scoped_ptr.hpp>
42 
43 namespace
44 {
45 // Temporary write buffer
46 MCThreadLocalData<MCBinaryData> WriteBuffer(true);
47 
48 void CheckStaticVariables()
49 {
50  if (unlikely(!WriteBuffer.get()))
51  WriteBuffer.reset(new MCBinaryData(20000));
52 }
53 }
54 
55 MASkit::MASkit() : AudioStart(0)
56 {
57 }
58 
59 
60 MASkit::MASkit(const std::string& name, MA::MotionSkitSPtr motion, MA::LedSkitSPtr led,
61  MC::BinaryDataSPtr sound) : Name(name), MotionSkit(motion), LedSkit(led),
62  SoundData(sound), AudioStart(0)
63 {
64 }
65 
66 
67 std::string MASkit::GetName() const
68 {
69  return Name;
70 }
71 
72 
74 {
75  return AudioStart;
76 }
77 
78 
80 {
81  return (!SoundData.get() ? 0 : SoundData->GetSize() / 16);
82 }
83 
84 
86 {
87  int Duration = 0;
88 
89  Duration = MCMax(Duration, GetAudioStart()+GetAudioDuration());
90  if (MotionSkit.get())
91  Duration = MCMax(Duration, MotionSkit->GetDuration());
92  if (LedSkit.get())
93  Duration = MCMax(Duration, LedSkit->GetDuration());
94 
95  return Duration;
96 }
97 
98 
99 void MASkit::Dump() const
100 {
101  MC_LOG("++ %s skit content ++", Name.c_str());
102  MC_LOG("++ Overall duration: %d msec ++", GetDuration());
103  // Dump motion skit
104  if (MotionSkit.get())
105  {
106  MotionSkit->Dump();
107  } else {
108  MC_LOG("No motion data");
109  }
110  // Dump LED skit
111  if (LedSkit.get())
112  {
113  LedSkit->Dump();
114  } else {
115  MC_LOG("No LED data");
116  }
117  // Dump sound skit
118  if (SoundData.get())
119  {
120  MC_LOG("-- %s sound --", Name.c_str());
121  MC_LOG("Start: %d msec", AudioStart);
122  MC_LOG("Duration: %d msec", GetAudioDuration());
123  } else {
124  MC_LOG("No sound data");
125  }
126 }
127 
128 
129 MA::MotionSkitSPtr MASkit::GetMotionSkit()
130 {
131  return MotionSkit;
132 }
133 
134 
135 MA::LedSkitSPtr MASkit::GetLedSkit()
136 {
137  return LedSkit;
138 }
139 
140 
141 MC::BinaryDataSPtr MASkit::GetSoundData()
142 {
143  return SoundData;
144 }
145 
146 
147 MA::SkitSPtr MASkit::CloneMirrored(const std::string& mirrored_name)
148 {
149  if (!MotionSkit.get())
150  {
151  MC_WARNING("This skit (%s) has no motions inside to be mirrored", Name.c_str());
152  return MA::SkitSPtr();
153  }
154  if (mirrored_name.empty())
155  {
156  MC_WARNING("Cannot create a noname mirrored skit from %s", Name.c_str());
157  return MA::SkitSPtr();
158  }
159  MA::MotionSkitSPtr NewMotion(MotionSkit->CloneMirrored());
160 
161  return MA::SkitSPtr(new MASkit(mirrored_name, NewMotion, LedSkit, SoundData));
162 }
163 
164 
166 {
167  boost::scoped_ptr<MAMotionSkit> MotionSkit(MAMotionSkit::DecodeMtnData(motion_data));
168 
169  if (!MotionSkit.get())
170  {
171  MC_WARNING("Invalid motion data cannot be converted to aib format");
172  return nullptr;
173  }
174  archive* Archive(archive_write_new());
175  archive_entry* ArchiveEntry = nullptr;
176  size_t FinalSize = 0;
177 
178  archive_write_set_format_zip(Archive);
179  archive_write_add_filter_none(Archive);
180  CheckStaticVariables();
181  archive_write_open_memory(Archive, WriteBuffer->GetData(), WriteBuffer->GetSize(),
182  &FinalSize);
183 
184  ArchiveEntry = archive_entry_new();
185  archive_entry_copy_pathname(ArchiveEntry,
186  std::string("Motion/"+MotionSkit->GetSkitName()+".mtn").c_str());
187  archive_entry_set_mode(ArchiveEntry, AE_IFREG | 0755);
188  archive_entry_set_size(ArchiveEntry, motion_data.GetSize());
189  archive_write_header(Archive, ArchiveEntry);
190  archive_entry_free(ArchiveEntry);
191  archive_write_data(Archive, motion_data.GetData(), motion_data.GetSize());
192 
193  archive_write_close(Archive);
194  archive_write_free(Archive);
195  // Get the final data out
196  MCBinaryData* FinalData = new MCBinaryData((int)FinalSize);
197 
198  memcpy(FinalData->GetData(), WriteBuffer->GetData(), FinalData->GetSize());
199  return FinalData;
200 }
201 
202 
204 {
205  MCBinaryData& BinaryData = const_cast<MCBinaryData&>(binary_data);
206  archive* Archive(archive_read_new());
207  archive_entry* ArchiveEntry = nullptr;
208 
209 #if __cplusplus >= 201103L
210 #pragma GCC diagnostic push
211 #pragma GCC diagnostic ignored "-Wdeprecated-declarations"
212 #endif
213  archive_read_support_format_zip(Archive);
214  archive_read_support_compression_gzip(Archive);
215  archive_read_support_compression_none(Archive);
216 #if __cplusplus >= 201103L
217 #pragma GCC diagnostic pop
218 #endif
219  int result = archive_read_open_memory(Archive, BinaryData.GetData(), BinaryData.GetSize());
220 
221  // Data can not be read as zip format
222  if (result != 0)
223  {
224  MC_WARNING("Binary data can not be read as aib (zip) format (error: %d)", result);
225  archive_read_free(Archive);
226  return nullptr;
227  }
228  MASkit* Skit = new MASkit;
229 
230  // Process the file headers inside the zip format
231  while (archive_read_next_header(Archive, &ArchiveEntry) == 0)
232  {
233  std::string FileName(archive_entry_pathname(ArchiveEntry));
234 
235 // MC_LOG("Extract %s", FileName.c_str());
236  if (Skit->Name.empty() &&
237  (boost::algorithm::iends_with(FileName, ".mtn") ||
238  boost::algorithm::iends_with(FileName, ".wav") ||
239  boost::algorithm::iends_with(FileName, ".mid") ||
240  boost::algorithm::iends_with(FileName, ".info") ||
241  boost::algorithm::iends_with(FileName, ".led")))
242  {
243  MC::StringList Strs;
244 
245  Skit->Name = FileName;
246  boost::split(Strs, Skit->Name, boost::is_any_of("/"));
247  if (Strs.size() == 2)
248  {
249  Skit->Name = Strs[1];
250  Skit->Name.erase(Skit->Name.size()-4, 4);
251  }
252  }
253  boost::scoped_ptr<MCBinaryData> FileData(new MCBinaryData((int)archive_entry_size(ArchiveEntry)));
254 
255  result = archive_read_data(Archive, FileData->GetData(), FileData->GetSize());
256  if (result != FileData->GetSize())
257  {
258  continue;
259  } else
260  if (boost::algorithm::iends_with(FileName, ".mtn"))
261  {
262  Skit->MotionSkit.reset(MAMotionSkit::DecodeMtnData(*FileData));
263  } else
264  if (boost::algorithm::iends_with(FileName, ".info"))
265  {
267  } else
268  if (boost::algorithm::iends_with(FileName, ".wav"))
269  {
270  // Convert the audio data from 8 bit, 8 kHz -> 16 bit, 16 kHz
271  MC::BinaryDataSPtr TempData(new MCBinaryData((FileData->GetSize()-44)*2, 0));
272  MC::DoubleList ResampleData, RightChannel;
273 
274  for (int i = 0; i < TempData->GetSize() / 2; ++i)
275  ((int16_t*)(void*)TempData->GetData())[i] = ((int16_t)FileData->GetData()[44+i]-128)*253;
276 
277  ResampleData = MASoundData::ConvertToDouble(*TempData);
278  ResampleData = MASoundData::Resample(ResampleData, 8000, MASoundData::SampleRateOnAIBO);
279  Skit->SoundData.reset(MASoundData::ConvertDoubleToRaw(ResampleData, RightChannel));
280  } else
281  if (boost::algorithm::iends_with(FileName, ".led"))
282  {
283  MC::StringList Strs;
284 
285  boost::split(Strs, FileName, boost::is_any_of("/"));
286  boost::split(Strs, Strs.back(), boost::is_any_of("."));
287  Skit->LedSkit.reset(MALedSkit::DecodeLedData(*FileData, Strs[0]));
288  }
289  }
290  archive_read_close(Archive);
291  archive_read_free(Archive);
292  if (!Skit->MotionSkit.get() && !Skit->SoundData.get() && !Skit->LedSkit.get())
293  {
294  MC_WARNING("No motion/LED/sound skit found in aib decoding");
295  delete Skit;
296  return nullptr;
297  }
298  MC_LOG("Decoded skit: %s", Skit->Name.c_str());
299  return Skit;
300 }
301 
302 
304 {
305  std::istringstream Stream;
306 
307  Stream.rdbuf()->pubsetbuf((char*)binary_data.GetData(), binary_data.GetSize());
308  while (Stream.good())
309  {
310  std::string Line;
311 
312  std::getline(Stream, Line);
313  if (boost::algorithm::istarts_with(Line, "SOUNDSTART"))
314  {
315  MC::StringList Strs;
316 
317  boost::split(Strs, Line, boost::is_any_of(" "));
318  if (Strs.size() >= 2)
319  {
320  return MCStrConvert<int>(Strs[1])*16;
321  }
322  break;
323  }
324  }
325  return -1;
326 }
static MCBinaryData * ConvertDoubleToRaw(MC::DoubleList &left_channel, MC::DoubleList &right_channel)
Convert double lists back to raw audio data.
static const int SampleRateOnAIBO
Sample rate for audio processing on AIBO.
MA::LedSkitSPtr LedSkit
Led skit.
Definition: MASkit.hpp:184
Binary data class.
void Dump() const
Print the skit content.
Definition: MASkit.cpp:99
std::string Name
Skit name.
Definition: MASkit.hpp:180
static MALedSkit * DecodeLedData(const MCBinaryData &binary_data, const std::string &name)
Load LED sequence from binary data.
Definition: MALedSkit.cpp:99
MA::MotionSkitSPtr MotionSkit
Motion skit.
Definition: MASkit.hpp:182
std::string Name
Motion name.
MA::LedSkitSPtr GetLedSkit()
Get the LED skit.
Definition: MASkit.cpp:135
#define MC_WARNING(...)
Warning macro.
Definition: MCLog.hpp:43
std::string GetName() const
Get the skit name.
Definition: MASkit.cpp:67
static MAMotionSkit * DecodeMtnData(const MCBinaryData &binary_data)
Load motion sequence from binary data.
MA::SkitSPtr CloneMirrored(const std::string &mirrored_name)
Creates a new skit with mirrored leg joint trajectories between left and right sides.
Definition: MASkit.cpp:147
static MCBinaryData * EncodeMtnToAibFormat(const MCBinaryData &motion_data)
Encode motion data (mtn) to aib format.
Definition: MASkit.cpp:165
int GetAudioStart() const
Get the audio effect start time.
Definition: MASkit.cpp:73
static MC::DoubleList ConvertToDouble(const MCBinaryData &raw_data)
Convert audio data to a double list.
int AudioStart
Audio start time in msec.
Definition: MASkit.hpp:188
static MC::DoubleList Resample(const MC::DoubleList &audio_data, int original_frequency, int new_frequency)
Resample audio data.
Skit class to decode aib format.
Definition: MASkit.hpp:44
MA::MotionSkitSPtr GetMotionSkit()
Get the motion skit.
Definition: MASkit.cpp:129
MC::BinaryDataSPtr SoundData
Sound skit binary data.
Definition: MASkit.hpp:186
static int GetAudioStartFromSkitterInfo(const MCBinaryData &binary_data)
Get the audio effect start from Skitter info format.
Definition: MASkit.cpp:303
int GetAudioDuration() const
Get the audio duration.
Definition: MASkit.cpp:79
int GetDuration() const
Get the skit duration.
Definition: MASkit.cpp:85
MC::BinaryDataSPtr GetSoundData()
Get the sound data.
Definition: MASkit.cpp:141
static MASkit * DecodeAibData(const MCBinaryData &binary_data)
Load a skit from binary data.
Definition: MASkit.cpp:203
unsigned char * GetData() const
Get direct access to the binary data.
A wrapper class to cover boost::thread_specific_ptr/folly::ThreadLocal API on certain targets...
const T MCMax(const U &container)
Get the maximal value of a container.
#define MC_LOG(...)
Debug macro.
Definition: MCLog.hpp:41
int GetSize() const
Get binary data size.