Main Page · Modules · All Classes · Class Hierarchy
MASpeakerController.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 "MASpeakerController.hpp"
23 
24 #include "types/MAAudio.hpp"
25 #include "types/MARobotState.hpp"
26 #include "sound/MASoundDatabase.hpp"
27 #include "MABehaviorManager.hpp"
28 
29 #include <MCLog.hpp>
30 #include <MCTimer.hpp>
31 
32 namespace MA
33 {
35 }
36 
37 namespace
38 {
40 typedef boost::unordered_map<std::string, MA::SoundBaseSPtr> SoundDataMap;
41 
43 typedef std::pair<std::string, int> FutureSoundDescriptor;
45 typedef boost::unordered_map<int, FutureSoundDescriptor> FutureSoundMap;
46 
47 // Global playback ID
48 MCThreadLocalData<int> PlaybackID(true);
49 // Loaded sounds
51 // Future sounds
52 MCThreadLocalData<FutureSoundMap> FutureSounds(true);
53 // Preloaded sounds
54 MCThreadLocalData<MC::StringList> PreloadedSounds(true);
55 
56 void CheckStaticSpeakerControllerVariables(bool reset_if_exists)
57 {
58  if (unlikely(!PlaybackID.get()))
59  {
60  PlaybackID.reset(new int);
61  *PlaybackID = 0;
62  Sounds.reset(new SoundDataMap);
63  FutureSounds.reset(new FutureSoundMap);
64  PreloadedSounds.reset(new MC::StringList);
65  } else
66  if (reset_if_exists)
67  {
68  *PlaybackID = 0;
69  Sounds->clear();
70  FutureSounds->clear();
71  PreloadedSounds->clear();
72  }
73 }
74 }
75 
76 MASpeakerController::MASpeakerController() : MAController(MA::SpeakerController), BufferSize(512)
77 {
78  CheckStaticSpeakerControllerVariables(true);
79  if (MA::Speaker.get())
80  {
81  MC_WARNING("The global speaker controller is overridden.");
82  }
83  MA::Speaker.reset(this);
84 }
85 
86 
87 MASpeakerController::~MASpeakerController()
88 {
89  if (MA::Speaker.get() == this)
90  {
91  MA::Speaker.release();
92  }
93  Playbacks.clear();
94  // Remove the non-cached sound files
95  for (auto iter = Sounds->begin(); iter != Sounds->end();)
96  {
97  MASoundBase* SoundData = iter->second.get();
98 
99  if (!SoundData->IsCached())
100  {
101  iter = Sounds->erase(iter);
102  // This safety check is required otherwise crash
103  if (iter == Sounds->end())
104  break;
105 
106  continue;
107  }
108  ++iter;
109  }
110 }
111 
112 
114 {
115  state.Audio->SpeakerVolume = (int)SpeakerDevice->GetVolume();
116  state.Audio->RemainingPlaybackTime = (int)GetRemainingPlaybackTime();
117  state.Audio->EffectsPlayed = (int)IsOnlyEffectsPlayed();
118 }
119 
120 
121 MA::DeviceList MASpeakerController::GetMotors() const
122 {
123  // Nothing to return
124  return MA::DeviceList();
125 }
126 
127 
128 void MASpeakerController::RegisterFutureSound(const int id, const std::string& name,
129  const int duration)
130 {
131  CheckStaticSpeakerControllerVariables(false);
132  FutureSounds->insert(std::make_pair(id, FutureSoundDescriptor(name, duration)));
133 }
134 
135 
137 {
138  return Playbacks.empty();
139 }
140 
141 
143 {
144  // Release the sounds
145  *PlaybackID = 0;
146  Sounds->clear();
147  // Switch profile
148  MA::SoundDatabase->SwitchProfile();
149  // Reload preloaded samples
150  for (unsigned int i = 0; i < PreloadedSounds->size(); ++i)
151  Preload((*PreloadedSounds)[i]);
152  SaveSoundProfile();
153 }
154 
155 
157 {
158  return MA::SoundDatabase->GetProfileName();
159 }
160 
161 
162 void MASpeakerController::SetSoundProfileName(const std::string& profile_name)
163 {
164  // Release the sounds
165  *PlaybackID = 0;
166  Sounds->clear();
167  // Set new profile
168  MA::SoundDatabase->SetProfileName(profile_name);
169  // Reload preloaded samples
170  for (unsigned int i = 0; i < PreloadedSounds->size(); ++i)
171  Preload((*PreloadedSounds)[i]);
172  SaveSoundProfile();
173 }
174 
175 
177 {
178 }
179 
180 
182 {
183 }
184 
185 
187 {
188 }
189 
190 
192 {
193 }
194 
195 
196 bool MASpeakerController::Preload(const std::string& name)
197 {
198  std::string FileName = MA::SoundDatabase->GetSoundFileName(name);
199 
200  if (FileName.empty())
201  {
202  MC_WARNING("File is not found: %s", name.c_str());
203  return false;
204  }
205  if (!(*Sounds)[FileName])
206  {
207  MA::SoundBaseSPtr NewSoundFile(MA::SoundDatabase->GetSoundFile(FileName, true));
208 
209  PreloadedSounds->push_back(name);
210  // The sound file is not valid (not found, read error or empty file)
211  if (!NewSoundFile->IsValid())
212  {
213  return false;
214  }
215  (*Sounds)[FileName] = NewSoundFile;
217  MC_LOG("Preload a sound file: %s", FileName.c_str());
218  }
219  return true;
220 }
221 
222 
223 void MASpeakerController::FreePreloadedSound(const std::string& name)
224 {
225  auto Iter = std::find(PreloadedSounds->begin(), PreloadedSounds->end(), name);
226 
227  if (Iter != PreloadedSounds->end())
228  {
229  PreloadedSounds->erase(Iter);
230  }
231  std::string FileName = MA::SoundDatabase->GetSoundFileName(name);
232  auto FileIter = Sounds->find(FileName);
233  MASoundBase* SoundDataPtr = nullptr;
234 
235  if (FileIter != Sounds->end())
236  {
237  SoundDataPtr = FileIter->second.get();
238  Sounds->erase(FileIter);
239  }
240  if (SoundDataPtr)
241  {
242  for (auto iter = Playbacks.begin(); iter != Playbacks.end(); ++iter)
243  {
244  if (iter->first.get() == SoundDataPtr)
245  {
246  Playbacks.erase(iter);
247  break;
248  }
249  }
250  }
251  MC_LOG("Preloaded sound freed: %s", name.c_str());
252 }
253 
254 
255 int MASpeakerController::Play(const std::string& name)
256 {
257  std::string FileName = MA::SoundDatabase->GetSoundFileName(name);
258 
259  if (FileName.empty())
260  {
261  MC_WARNING("Sound file is not found: %s", name.c_str());
262  return -1;
263  }
264  if (!(*Sounds)[FileName])
265  {
266  MA::SoundBaseSPtr NewSoundFile(MA::SoundDatabase->GetSoundFile(FileName, false));
267 
268  // The sound file is not valid (not found, read error or empty file)
269  if (!NewSoundFile->IsValid())
270  {
271  return -1;
272  }
273  (*Sounds)[FileName] = NewSoundFile;
274  }
275  return Play((*Sounds)[FileName]);
276 }
277 
278 
279 int MASpeakerController::Play(MA::SoundBaseSPtr sound)
280 {
281  (*PlaybackID)++;
282  // Overflow detection (not too practical error case)
283  if (*PlaybackID < 0)
284  {
285  *PlaybackID = 1;
286  }
287  MA::PlaybackDescriptor PlaybackDescriptor;
288  const auto Iter = FutureSounds->find(*PlaybackID);
289 
290  PlaybackDescriptor.get<0>() = *PlaybackID;
291  // Remember for the start time
292  PlaybackDescriptor.get<2>() = MCTimer::GetElapsedSystemTime() / 1000;
293  // Try to find in the future sounds
294  if (Iter != FutureSounds->end() && Iter->second.first == sound->GetDisplayName())
295  {
296  PlaybackDescriptor.get<3>() = Iter->second.second;
297  }
298  Playbacks.insert(std::make_pair(sound, PlaybackDescriptor));
300  {
301  MC_LOG("Play a sound file: %s (id: %d)", sound->GetDisplayName().c_str(), *PlaybackID);
302  }
303  return *PlaybackID;
304 }
305 
306 
307 bool MASpeakerController::IsPlayed(const int playback_id) const
308 {
309  if (Playbacks.empty())
310  return false;
311 
312  for (auto& playback : Playbacks)
313  {
314  const MA::PlaybackDescriptor& Descriptor = playback.second;
315 
316  if (Descriptor.get<0>() == playback_id)
317  return true;
318  }
319  return false;
320 }
321 
322 
323 bool MASpeakerController::IsPlayed(const std::string& sound_name) const
324 {
325  if (Playbacks.empty())
326  return false;
327 
328  for (auto& playback : Playbacks)
329  {
330  if (playback.first.get() && playback.first->GetDisplayName() == sound_name)
331  return true;
332  }
333  return false;
334 }
335 
336 
337 void MASpeakerController::SetVolume(const MA::VolumeType new_volume)
338 {
339  SpeakerDevice->SetVolumeState(new_volume);
340 }
341 
342 
343 void MASpeakerController::MixNextBuffer(int16_t* target_buffer, bool limit_non_cached)
344 {
345  int Counter = 0;
346  bool NonCachedSample = false;
347  int32_t TempBuffer[BufferSize];
348 
349  // Clear the buffers
350  memset(target_buffer, 0, BufferSize*sizeof(int16_t));
351  memset(&TempBuffer[0], 0, BufferSize*sizeof(int32_t));
352  // Mix the samples
353  for (auto iter = Playbacks.begin(); iter != Playbacks.end();)
354  {
355  MA::PlaybackDescriptor& Descriptor = iter->second;
356 
357  // Mixing if cached or the non-cached limit has not been reached
358  if (iter->first->IsCached() || !NonCachedSample)
359  iter->first->AddToBuffer(Descriptor.get<1>(), BufferSize, TempBuffer);
360  if (!iter->first->IsCached() && limit_non_cached)
361  NonCachedSample = true;
362 
363  Descriptor.get<1>() += BufferSize;
364  // Remove any finished sample
365  // Note: Erasing from multimap while iterating: see http://stackoverflow.com/questions/446205
366  if ((iter->first->GetSize() <= Descriptor.get<1>() &&
367  // Check if the sound has expected duration
368  (Descriptor.get<3>() <= 0 || Descriptor.get<3>() <=
369  (int)(MCTimer::GetElapsedSystemTime() / 1000-Descriptor.get<2>()))) ||
370  // Remove a non-cached sample if there is a non-cached sample already
371  (!iter->first->IsCached() && !NonCachedSample))
372  {
374  {
375  MC_LOG("Sound file ended: %s (id: %d, duration: %d)", iter->first->GetDisplayName().c_str(),
376  iter->second.get<0>(), (int)(MCTimer::GetElapsedSystemTime() / 1000-Descriptor.get<2>()));
377  }
378  Playbacks.erase(iter++);
379  } else {
380  ++iter;
381  }
382  Counter++;
383  }
384  // Final mixing into the target buffer
385  for (int i = 0; i < BufferSize; ++i)
386  target_buffer[i] = (int16_t)MCBound((int32_t)-32768, TempBuffer[i], (int32_t)32767);
387 }
388 
389 
390 unsigned int MASpeakerController::GetRemainingPlaybackTime() const
391 {
392  if (Playbacks.empty())
393  return 0;
394 
395  unsigned int RemainingData = 0;
396 
397  for (auto& playback : Playbacks)
398  {
399  const MA::PlaybackDescriptor& Descriptor = playback.second;
400  int SoundSize = playback.first->GetSize();
401  int CurrentPosition = Descriptor.get<1>();
402 
403  // Find the longest remaining duration from the playback list
404  if (CurrentPosition < SoundSize && (unsigned int)(SoundSize-CurrentPosition) > RemainingData)
405  RemainingData = (unsigned int)(SoundSize-CurrentPosition);
406  }
407  // Note: It is multiplied by 2 because RemainingData is based on int16_t size while
408  // GetMsecSampleSize() is based on byte size.
409  return RemainingData*2 / SpeakerDevice->GetMsecSampleSize();
410 }
411 
412 
413 bool MASpeakerController::IsOnlyEffectsPlayed() const
414 {
415  if (Playbacks.empty())
416  return false;
417 
418  for (auto& playback : Playbacks)
419  {
420  if (!playback.first->IsEffect())
421  return false;
422  }
423  return true;
424 }
425 
426 
427 MA::DeviceGeneratorsMap MASpeakerController::GetSkitTransitionGenerators(MA::SkitBaseSPtr)
428 {
429  return MA::DeviceGeneratorsMap();
430 }
virtual MA::DeviceList GetMotors() const override
Get the list of the motors.
bool IsFree() const
Check if any sound is being played back.
std::string GetSoundProfileName() const
Get sound profile name.
Base class for sounds.
Definition: MASoundBase.hpp:45
int Play(const std::string &name)
Play a sound by name.
void SetVolume(const MA::VolumeType new_volume)
Set a new volume level.
void FreePreloadedSound(const std::string &name)
Free a preloaded sound.
void SwitchSoundProfile()
Switch the sound profile.
#define MC_WARNING(...)
Warning macro.
Definition: MCLog.hpp:43
Base class for the controllers.
const T & MCBound(const T &min, const T &value, const T &max)
Check a value bound according to a range.
Definition: MCDefs.hpp:527
static bool IsVerbose()
Get the verbosity of the controller domain.
virtual void UpdateRobotState(MARobotState &state) override
Update the robot state.
virtual bool IsCached() const =0
Check if the sound data is cached.
virtual void LoadVolumeControl()
Load the volume control.
virtual MA::DeviceGeneratorsMap GetSkitTransitionGenerators(MA::SkitBaseSPtr skit) override
Get skit transition generators.
void SetSoundProfileName(const std::string &profile_name)
Set sound profile name.
bool IsPlayed(const int playback_id) const
Check if a playback ID is active.
static void RegisterFutureSound(const int id, const std::string &name, const int duration)
Register a sound for future playback.
bool Preload(const std::string &name)
Preload a sound.
virtual void SaveVolumeControl()
Save the volume control.
static int64_t GetElapsedSystemTime(bool relative_time=true)
Get the system time.
Definition: MCTimer.cpp:92
virtual void LoadSoundProfile()
Load the sound profile.
boost::scoped_ptr< MAAudio > Audio
Audio input/output.
A wrapper class to cover boost::thread_specific_ptr/folly::ThreadLocal API on certain targets...
#define MC_LOG(...)
Debug macro.
Definition: MCLog.hpp:41
Robot state.
virtual void SaveSoundProfile()
Save the sound profile.