Main Page · Modules · All Classes · Class Hierarchy
MALedSkit.cpp
1 /*
2  * This file is part of the AiBO+ project
3  *
4  * Copyright (C) 2005-2015 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 "MALedSkit.hpp"
23 
24 #include "core/MADevice.hpp"
25 #include "core/MAValueGenerators.hpp"
26 #include "MASkitDatabase.hpp"
27 
28 #include <MCContainers.hpp>
29 
30 #include <boost/algorithm/string.hpp>
31 #include <boost/math/constants/constants.hpp>
32 
33 namespace
34 {
35 int GetTime(MCBinaryData& data, int& chunk_length)
36 {
37  int Time = 0;
38 
39  while (!data.IsPositionAtEnd() > 0 && chunk_length > 4)
40  {
41  unsigned char Char = data.GetUChar();
42 
43  chunk_length--;
44  if (Char < 128)
45  {
46  Time = (Time << 7)+(int)Char;
47  return Time;
48  }
49  Time = (Time << 7)+(int)Char % 128;
50  }
51  return Time;
52 }
53 }
54 
55 MALedSkit::MALedSkit() : MASkitBase(), SkitDuration(0)
56 {
57  Animations.resize(256);
58 }
59 
60 
61 std::string MALedSkit::GetName() const
62 {
63  return Name;
64 }
65 
66 
68 {
69  return SkitDuration;
70 }
71 
72 
73 void MALedSkit::Dump() const
74 {
75  MC_LOG("-- %s LED animations --", Name.c_str());
76  MC_LOG("Animation duration: %d msec", GetDuration());
77  foreach_i (i, animation, Animations)
78  {
79  if (!animation->empty())
80  {
81  printf("LED 0x%x: ", i);
82  }
83  std::string LogMessage;
84 
85  for (unsigned int i1 = 0; !Animations[i].empty() && i1 < Animations[i].size()-2; i1 += 3)
86  {
87  LogMessage += "start "+MCToStr(Animations[i][i1])+" msec - ";
88  LogMessage += "duration "+MCToStr(Animations[i][i1+1])+" msec - ";
89  LogMessage += "value "+MCToStr((float)Animations[i][i1+2] / 100);
90  if (i1 < Animations[i].size()-3)
91  LogMessage += " | ";
92  }
93  if (!LogMessage.empty())
94  MC_LOG("%s", LogMessage.c_str());
95  }
96 }
97 
98 
99 MALedSkit* MALedSkit::DecodeLedData(const MCBinaryData& binary_data, const std::string& name)
100 {
101  MCBinaryData& BinaryData = const_cast<MCBinaryData&>(binary_data);
102  int OldPosition = BinaryData.GetPosition();
103 
104  BinaryData.SetPosition(0);
105  // Test the file signature
106  if (BinaryData.GetString(4) != "MThd")
107  {
108  MC_WARNING("Wrong file signature, can't decode LED sequence");
109  BinaryData.SetPosition(OldPosition);
110  return nullptr;
111  }
112  /*
113  * Block 0
114  */
115  // Skip the chunk length (block 0)
116  BinaryData.GetInt32(true);
117  // Check the format 0 (block 0)
118  if (BinaryData.GetInt16(false) != 0)
119  {
120  MC_WARNING("Not format 0 file, can't decode the LED sequence");
121  BinaryData.SetPosition(OldPosition);
122  return nullptr;
123  }
124  // Check the track number (block 0)
125  if (BinaryData.GetInt16(false) != 1)
126  {
127  MC_WARNING("Not a file with one track, can't decode the LED sequence");
128  BinaryData.SetPosition(OldPosition);
129  return nullptr;
130  }
131  int TimeDelta = BinaryData.GetInt16(false);
132 
133  // Test the chunk signature
134  if (BinaryData.GetString(4) != "MTrk")
135  {
136  MC_WARNING("Wrong chunk signature, can't decode LED sequence");
137  BinaryData.SetPosition(OldPosition);
138  return nullptr;
139  }
140  // Get the chunk length (block 0)
141  int ChunkLength = BinaryData.GetInt32(false);
142 
143  // Test the model signature
144  if (BinaryData.GetUChar() != 0x00 || BinaryData.GetUChar() != 0xff || BinaryData.GetUChar() != 0x01 ||
145  BinaryData.GetUChar() != 9 || BinaryData.GetString(9, "= ") != "MODEL= 7")
146  {
147  MC_WARNING("Only Sony ERS-7 model is supported, can't decode LED sequence");
148  BinaryData.SetPosition(OldPosition);
149  return nullptr;
150  }
151  // Skip the string closing character (0x00)
152  BinaryData.GetUChar();
153  ChunkLength -= (4+9+1);
154  // Skip the track name
155  if (BinaryData.GetUChar() != 0xff || BinaryData.GetUChar() != 0x03)
156  {
157  MC_WARNING("Track name is not readable, can't decode LED sequence");
158  BinaryData.SetPosition(OldPosition);
159  return nullptr;
160  }
161  ChunkLength -= 2;
162  int StringLength = BinaryData.GetUChar();
163 
164  if (BinaryData.GetString(StringLength).empty())
165  {
166  MC_WARNING("Track name is not readable, can't decode LED sequence");
167  BinaryData.SetPosition(OldPosition);
168  return nullptr;
169  }
170  // Skip the string closing character (0x00)
171  BinaryData.GetUChar();
172  ChunkLength -= (1+StringLength+1);
173  // Check tempo identifier
174  if (BinaryData.GetUChar() != 0xff || BinaryData.GetUChar() != 0x51 || BinaryData.GetUChar() != 0x03)
175  {
176  MC_WARNING("Tempo identifier is invalid, can't decode LED sequence");
177  BinaryData.SetPosition(OldPosition);
178  return nullptr;
179  }
180  ChunkLength -= 3;
181  int KeyframeUsec = (int)BinaryData.GetUChar()*256*256+(int)BinaryData.GetUChar()*256+BinaryData.GetUChar();
182 
183  ChunkLength -= 3;
184  // Note: This calculation is a bit tricky and does not make sense to me, but
185  // that is the way how it should be done.
186  const float TimeConversionRate = (float)KeyframeUsec / ((float)TimeDelta*1000 / 1024) / 1000;
187  int StartTime = (int)((float)GetTime(BinaryData, ChunkLength)*TimeConversionRate);
188 
189  if (ChunkLength > 4)
190  {
191  if (BinaryData.GetUChar() != 0x90)
192  {
193  MC_WARNING("Status byte (0x90) is missing");
194  BinaryData.SetPosition(OldPosition);
195  return nullptr;
196  }
197  ChunkLength--;
198  }
199  /*
200  * Parse the LED animations
201  */
202  MC::IntList Devices, StartTimes, Intensities;
203  MALedSkit* LedSkit = new MALedSkit;
204 
205  LedSkit->Name = name;
206  while (ChunkLength > 4)
207  {
208  unsigned char DeviceChar = BinaryData.GetUChar();
209  unsigned char IntensityChar = BinaryData.GetUChar();
210 
211  // Correct the LED intensity values to range [0..100]
212  if (IntensityChar == 31)
213  IntensityChar = 25;
214  if (IntensityChar == 63)
215  IntensityChar = 50;
216  if (IntensityChar == 95)
217  IntensityChar = 75;
218  if (IntensityChar == 127)
219  IntensityChar = 100;
220  if (IntensityChar > 0)
221  {
222  Devices.push_back((int)DeviceChar);
223  Intensities.push_back((int)IntensityChar);
224  StartTimes.push_back(StartTime);
225  }
226  if (IntensityChar == 0)
227  {
228  foreach_i (i, device, Devices)
229  {
230  if (*device == DeviceChar)
231  {
232  LedSkit->Animations[(int)DeviceChar].push_back(StartTimes[i]);
233  LedSkit->Animations[(int)DeviceChar].push_back(StartTime-StartTimes[i]);
234  LedSkit->Animations[(int)DeviceChar].push_back(Intensities[i]);
235  Devices.erase(Devices.begin()+i);
236  Intensities.erase(Intensities.begin()+i);
237  StartTimes.erase(StartTimes.begin()+i);
238  break;
239  }
240  }
241  }
242  ChunkLength -= 2;
243  StartTime += (int)((float)GetTime(BinaryData, ChunkLength)*TimeConversionRate);
244  if (ChunkLength == 4)
245  {
246  LedSkit->SkitDuration = StartTime;
247  }
248  }
249  // cppcheck-suppress duplicateExpression
250  if (ChunkLength != 4 || BinaryData.GetUChar() != 0x00 || BinaryData.GetUChar() != 0xff ||
251  BinaryData.GetUChar() != 0x2f || BinaryData.GetUChar() != 0x00)
252  {
253  MC_WARNING("Invalid chunk ending, can't decode LED sequence");
254  BinaryData.SetPosition(OldPosition);
255  delete LedSkit;
256  return nullptr;
257  }
258  BinaryData.SetPosition(OldPosition);
259  MC_LOG("Decoded LED sequence: %s", name.c_str());
260  return LedSkit;
261 }
262 
263 
265 {
266  if (device > 255 || Animations[device].empty())
267  {
268  return nullptr;
269  }
270  MAGeneratorContainer* Container = new MAGeneratorContainer(*new MASimpleGenerator(0, 0, 0));
271  int Time = 0;
272 
273  for (unsigned int i = 0; i < Animations[device].size()-2; i += 3)
274  {
275  float TargetValue = (float)Animations[device][i+2] / 100;
276  bool GapBefore = false;
277 
278  // Add the initial delay properly
279  if ((i == 0 && Animations[device][i] != 0) ||
280  (i > 0 && Animations[device][i]-Time > 0))
281  {
282  // Note: We can use this calculation for both cases because the Time variable holds zero
283  // when i == 0.
284  Container->AddGenerator(*new MASimpleGenerator(0, 0, Animations[device][i]-Time));
285  Time += Animations[device][i]-Time;
286  GapBefore = true;
287  }
288  if (i+3 >= Animations[device].size() || Time+Animations[device][i+1] < Animations[device][i+3])
289  {
290  if (GapBefore)
291  {
292  // This is a special case: There are gaps before and after the LED value.
293  const int Duration = Animations[device][i+1] / 3;
294 
295  Container->AddGenerator(*new MAPeriodicGenerator(TargetValue,
296  0, Duration, Duration, Duration, 0, false));
297  Time += Animations[device][i+1];
298  continue;
299  } else {
300  // The LED value must be interpolated back to zero for the last period or
301  // there is an empty gap until the next period
302  TargetValue = 0;
303  }
304  }
305 
306  Container->AddGenerator(*new MASimpleGenerator(MCFloatInfinity(), TargetValue, Animations[device][i+1]));
307  Time += Animations[device][i+1];
308  }
309  if (SkitDuration-Time > 0)
310  Container->AddGenerator(*new MASimpleGenerator(0, 0, SkitDuration-Time));
311  else
312  Container->AddGenerator(*new MASimpleGenerator(0, 0, 0));
313 
314  return Container;
315 }
int16_t GetInt16(bool reverse_order=false)
Get a 16 bit integer from the current position.
Skit base class.
Definition: MASkitBase.hpp:40
std::string Name
LED name.
Definition: MALedSkit.hpp:142
void AddGenerator(MAGeneratorBase &generator, bool in_front=false)
Add a new generator after the last internal generator.
MC::IntTable Animations
LED animations (first columns - start time, second - duration, third - intensity...)
Definition: MALedSkit.hpp:144
Binary data class.
static MALedSkit * DecodeLedData(const MCBinaryData &binary_data, const std::string &name)
Load LED sequence from binary data.
Definition: MALedSkit.cpp:99
unsigned char GetUChar()
Get an unsigned char from the current position.
LED sequence (LED skit) class.
Definition: MALedSkit.hpp:87
void Dump() const
Print the LED skit information.
Definition: MALedSkit.cpp:73
#define MC_WARNING(...)
Warning macro.
Definition: MCLog.hpp:43
std::string MCToStr(const T value, bool hex_manipulator=false)
Convert an other type to string with std::stringstream.
Definition: MCDefs.hpp:360
std::string GetString(unsigned int length, const std::string &additional_chars="")
Get a string from the current position.
#define foreach_i(_i, _iter, _container)
For each cycle with auto keyword and index values.
Definition: MCDefs.hpp:63
int GetDuration() const
Get skit duration.
Definition: MALedSkit.cpp:67
Simple generator class.
MAGeneratorContainer * GetGeneratorContainer(unsigned int device) const
Get generator container for a device.
Definition: MALedSkit.cpp:264
int SkitDuration
Skit duration (in msec)
Definition: MALedSkit.hpp:146
void SetPosition(unsigned int position)
Set the cursor position.
MALedSkit()
Class constructor.
Definition: MALedSkit.cpp:55
Generator container class.
float MCFloatInfinity()
Get float infinity.
Definition: MCDefs.cpp:110
int32_t GetInt32(bool reverse_order=false)
Get a 32 bit integer from the current position.
Periodic generator class (waveform _/‾\_)
#define MC_LOG(...)
Debug macro.
Definition: MCLog.hpp:41
std::string GetName() const
Get the LED sequence name.
Definition: MALedSkit.cpp:61
int GetPosition() const
Get the current position in the binary data.
bool IsPositionAtEnd() const
Check if the current position is at the end of the binary data.