All Classes Namespaces Functions Variables Enumerations Properties Pages
playbackmanager.cpp
1 /*
2 
3 Pencil2D - Traditional Animation Software
4 Copyright (C) 2005-2007 Patrick Corrieri & Pascal Naidon
5 Copyright (C) 2012-2020 Matthew Chiawen Chang
6 
7 This program is free software; you can redistribute it and/or
8 modify it under the terms of the GNU General Public License
9 as published by the Free Software Foundation; version 2 of the License.
10 
11 This program 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 */
17 
18 #include "playbackmanager.h"
19 
20 #include <QTimer>
21 #include <QElapsedTimer>
22 #include <QDebug>
23 #include <QSettings>
24 #include "object.h"
25 #include "editor.h"
26 #include "layersound.h"
27 #include "layermanager.h"
28 #include "soundclip.h"
29 #include "toolmanager.h"
30 
31 
32 PlaybackManager::PlaybackManager(Editor* editor) : BaseManager(editor, __FUNCTION__)
33 {
34 }
35 
36 PlaybackManager::~PlaybackManager()
37 {
38  delete mElapsedTimer;
39 }
40 
41 bool PlaybackManager::init()
42 {
43  mTimer = new QTimer(this);
45 
46  mFlipTimer = new QTimer(this);
47  mFlipTimer->setTimerType(Qt::PreciseTimer);
48 
49  mScrubTimer = new QTimer(this);
50  mScrubTimer->setTimerType(Qt::PreciseTimer);
51  mSoundclipsToPLay.clear();
52 
53  QSettings settings (PENCIL2D, PENCIL2D);
54  mFps = settings.value(SETTING_FPS).toInt();
55  mMsecSoundScrub = settings.value(SETTING_SOUND_SCRUB_MSEC).toInt();
56  if (mMsecSoundScrub == 0) { mMsecSoundScrub = 100; }
57  mSoundScrub = settings.value(SETTING_SOUND_SCRUB_ACTIVE).toBool();
58 
59  mElapsedTimer = new QElapsedTimer;
60  connect(mTimer, &QTimer::timeout, this, &PlaybackManager::timerTick);
61  connect(mFlipTimer, &QTimer::timeout, this, &PlaybackManager::flipTimerTick);
62  return true;
63 }
64 
65 Status PlaybackManager::load(Object* o)
66 {
67  const ObjectData* data = o->data();
68 
69  mIsLooping = data->isLooping();
70  mIsRangedPlayback = data->isRangedPlayback();
71  mMarkInFrame = data->getMarkInFrameNumber();
72  mMarkOutFrame = data->getMarkOutFrameNumber();
73  mFps = data->getFrameRate();
74 
75  updateStartFrame();
76  updateEndFrame();
77 
78  return Status::OK;
79 }
80 
81 Status PlaybackManager::save(Object* o)
82 {
83  ObjectData* data = o->data();
84  data->setLooping(mIsLooping);
85  data->setRangedPlayback(mIsRangedPlayback);
86  data->setMarkInFrameNumber(mMarkInFrame);
87  data->setMarkOutFrameNumber(mMarkOutFrame);
88  data->setFrameRate(mFps);
89  data->setCurrentFrame(editor()->currentFrame());
90  return Status::OK;
91 }
92 
93 bool PlaybackManager::isPlaying()
94 {
95  return (mTimer->isActive() || mFlipTimer->isActive());
96 }
97 
98 void PlaybackManager::play()
99 {
100  updateStartFrame();
101  updateEndFrame();
102 
103  // This is probably not the right place or function to be calling this, but it's the easiest thing to do right now that works
104  // TODO make a new tool function to handle playing (or perhaps generic scrubbing)
105  bool switchLayer = editor()->tools()->currentTool()->switchingLayer();
106  if (!switchLayer) return;
107 
108  int frame = editor()->currentFrame();
109  if (frame >= mEndFrame || frame < mStartFrame)
110  {
111  editor()->scrubTo(mStartFrame);
112  frame = editor()->currentFrame();
113  }
114 
115  mListOfActiveSoundFrames.clear();
116  // Check for any sounds we should start playing part-way through.
117  mCheckForSoundsHalfway = true;
118  playSounds(frame);
119 
120  mTimer->setInterval(static_cast<int>(1000.f / mFps));
121  mTimer->start();
122 
123  // for error correction, please ref skipFrame()
124  mPlayingFrameCounter = 1;
125  mElapsedTimer->start();
126 
127  emit playStateChanged(true);
128 }
129 
130 void PlaybackManager::stop()
131 {
132  mTimer->stop();
133  stopSounds();
134  emit playStateChanged(false);
135 }
136 
137 void PlaybackManager::playFlipRoll()
138 {
139  if (isPlaying()) { return; }
140 
141  int start = editor()->currentFrame();
142  int tmp = start;
143  mFlipList.clear();
144  QSettings settings(PENCIL2D, PENCIL2D);
145  mFlipRollMax = settings.value(SETTING_FLIP_ROLL_DRAWINGS).toInt();
146  for (int i = 0; i < mFlipRollMax; i++)
147  {
148  int prev = editor()->layers()->currentLayer()->getPreviousKeyFramePosition(tmp);
149  if (prev < tmp)
150  {
151  mFlipList.prepend(prev);
152  tmp = prev;
153  }
154  }
155  if (mFlipList.isEmpty()) { return; }
156 
157  // run the roll...
158  mFlipRollInterval = settings.value(SETTING_FLIP_ROLL_MSEC).toInt();
159  mFlipList.append(start);
160  mFlipTimer->setInterval(mFlipRollInterval);
161 
162  editor()->scrubTo(mFlipList[0]);
163  mFlipTimer->start();
164  emit playStateChanged(true);
165 }
166 
167 void PlaybackManager::playFlipInBetween()
168 {
169  if (isPlaying()) { return; }
170 
171  LayerManager* layerMgr = editor()->layers();
172  int start = editor()->currentFrame();
173 
174  int prev = layerMgr->currentLayer()->getPreviousKeyFramePosition(start);
175  int next = layerMgr->currentLayer()->getNextKeyFramePosition(start);
176 
177  if (prev < start && next > start &&
178  layerMgr->currentLayer()->keyExists(prev) &&
179  layerMgr->currentLayer()->keyExists(next))
180  {
181  mFlipList.clear();
182  mFlipList.append(prev);
183  mFlipList.append(prev);
184  mFlipList.append(start);
185  mFlipList.append(next);
186  mFlipList.append(next);
187  mFlipList.append(start);
188  }
189  else
190  {
191  return;
192  }
193  // run the flip in-between...
194  QSettings settings(PENCIL2D, PENCIL2D);
195  mFlipInbetweenInterval = settings.value(SETTING_FLIP_INBETWEEN_MSEC).toInt();
196 
197  mFlipTimer->setInterval(mFlipInbetweenInterval);
198  editor()->scrubTo(mFlipList[0]);
199  mFlipTimer->start();
200  emit playStateChanged(true);
201 }
202 
203 void PlaybackManager::playScrub(int frame)
204 {
205  if (!mSoundScrub || !mSoundclipsToPLay.isEmpty()) {return; }
206 
207  auto layerMan = editor()->layers();
208  for (int i = 0; i < layerMan->count(); i++)
209  {
210  Layer* layer = layerMan->getLayer(i);
211  if (layer->type() == Layer::SOUND && layer->visible())
212  {
213  KeyFrame* key = layer->getKeyFrameWhichCovers(frame);
214  if (key != nullptr)
215  {
216  SoundClip* clip = static_cast<SoundClip*>(key);
217  mSoundclipsToPLay.append(clip);
218  }
219  }
220  }
221 
222  if (mSoundclipsToPLay.isEmpty()) { return; }
223 
224  mScrubTimer->singleShot(mMsecSoundScrub, this, &PlaybackManager::stopScrubPlayback);
225  for (int i = 0; i < mSoundclipsToPLay.count(); i++)
226  {
227  mSoundclipsToPLay.at(i)->playFromPosition(frame, mFps);
228  }
229 }
230 
231 void PlaybackManager::setFps(int fps)
232 {
233  if (mFps != fps)
234  {
235  mFps = fps;
236  QSettings settings (PENCIL2D, PENCIL2D);
237  settings.setValue(SETTING_FPS, fps);
238  emit fpsChanged(mFps);
239 
240  // Update key-frame lengths of sound layers,
241  // since the length depends on fps.
242  for (int i = 0; i < object()->getLayerCount(); ++i)
243  {
244  Layer* layer = object()->getLayer(i);
245  if (layer->type() == Layer::SOUND)
246  {
247  auto soundLayer = dynamic_cast<LayerSound *>(layer);
248  soundLayer->updateFrameLengths(mFps);
249  }
250  }
251  }
252 }
253 
254 void PlaybackManager::playSounds(int frame)
255 {
256  // If sound is turned off, don't play anything.
257  if (!mIsPlaySound)
258  {
259  return;
260  }
261 
262  std::vector< LayerSound* > kSoundLayers;
263  for (int i = 0; i < object()->getLayerCount(); ++i)
264  {
265  Layer* layer = object()->getLayer(i);
266  if (layer->type() == Layer::SOUND)
267  {
268  kSoundLayers.push_back(static_cast<LayerSound*>(layer));
269  }
270  }
271 
272  for (LayerSound* layer : kSoundLayers)
273  {
274  KeyFrame* key = layer->getLastKeyFrameAtPosition(frame);
275 
276  if (!layer->visible())
277  {
278  continue;
279  }
280 
281  if (key != nullptr)
282  {
283  // add keyframe position to list
284  if (key->pos() + key->length() >= frame)
285  {
286  if (!mListOfActiveSoundFrames.contains(key->pos()))
287  {
288  mListOfActiveSoundFrames.append(key->pos());
289  }
290  }
291  }
292 
293  if (mCheckForSoundsHalfway)
294  {
295  // Check for sounds which we should start playing from part-way through.
296  for (int i = 0; i < mListOfActiveSoundFrames.count(); i++)
297  {
298  int listPosition = mListOfActiveSoundFrames.at(i);
299  if (layer->keyExistsWhichCovers(listPosition))
300  {
301  key = layer->getKeyFrameWhichCovers(listPosition);
302  SoundClip* clip = static_cast<SoundClip*>(key);
303  clip->playFromPosition(frame, mFps);
304  }
305  }
306  }
307  else if (layer->keyExists(frame))
308  {
309  key = layer->getKeyFrameAt(frame);
310  SoundClip* clip = static_cast<SoundClip*>(key);
311 
312  clip->play();
313 
314  // save the position of our active sound frame
315  mActiveSoundFrame = frame;
316  }
317 
318  if (frame >= mEndFrame)
319  {
320  if (layer->keyExists(mActiveSoundFrame))
321  {
322  key = layer->getKeyFrameWhichCovers(mActiveSoundFrame);
323  SoundClip* clip = static_cast<SoundClip*>(key);
324  clip->stop();
325 
326  // make sure list is cleared on end
327  if (!mListOfActiveSoundFrames.isEmpty())
328  mListOfActiveSoundFrames.clear();
329  }
330  }
331  }
332 
333  // Set flag to false, since this check should only be done when
334  // starting play-back, or when looping.
335  mCheckForSoundsHalfway = false;
336 }
337 
344 {
345  // uncomment these debug outputs to see what happens
346  //float expectedTime = (mPlayingFrameCounter) * (1000.f / mFps);
347  //qDebug("Expected: %.2f ms", expectedTime);
348  //qDebug("Actual: %d ms", mElapsedTimer->elapsed());
349 
350  int t = qRound((mPlayingFrameCounter - 1) * (1000.f / mFps));
351  if (mElapsedTimer->elapsed() < t)
352  {
353  qDebug() << "skip";
354  return true;
355  }
356 
357  ++mPlayingFrameCounter;
358  return false;
359 }
360 
361 void PlaybackManager::stopSounds()
362 {
363  std::vector<LayerSound*> kSoundLayers;
364 
365  for (int i = 0; i < object()->getLayerCount(); ++i)
366  {
367  Layer* layer = object()->getLayer(i);
368  if (layer->type() == Layer::SOUND)
369  {
370  kSoundLayers.push_back(static_cast<LayerSound*>(layer));
371  }
372  }
373 
374  for (LayerSound* layer : kSoundLayers)
375  {
376  layer->foreachKeyFrame([](KeyFrame* key)
377  {
378  SoundClip* clip = static_cast<SoundClip*>(key);
379  clip->stop();
380  });
381  }
382 }
383 
384 void PlaybackManager::stopScrubPlayback()
385 {
386  for (int i = 0; i < mSoundclipsToPLay.count(); i++)
387  {
388  mSoundclipsToPLay.at(i)->pause();
389  }
390  mSoundclipsToPLay.clear();
391 }
392 
393 void PlaybackManager::timerTick()
394 {
395  int currentFrame = editor()->currentFrame();
396 
397  // reach the end
398  if (currentFrame >= mEndFrame)
399  {
400  if (mIsLooping)
401  {
402  editor()->scrubTo(mStartFrame);
403  mCheckForSoundsHalfway = true;
404  }
405  else
406  {
407  stop();
408  }
409  return;
410  }
411 
412  if (skipFrame())
413  return;
414 
415  // keep going
416  editor()->scrubForward();
417 
418  int newFrame = editor()->currentFrame();
419  playSounds(newFrame);
420 }
421 
422 void PlaybackManager::flipTimerTick()
423 {
424  if (mFlipList.count() < 2 || editor()->currentFrame() != mFlipList[0])
425  {
426  mFlipTimer->stop();
427  editor()->scrubTo(mFlipList.last());
428  emit playStateChanged(false);
429  }
430  else
431  {
432  editor()->scrubTo(mFlipList[1]);
433  mFlipList.removeFirst();
434  }
435 }
436 
437 void PlaybackManager::setLooping(bool isLoop)
438 {
439  if (mIsLooping != isLoop)
440  {
441  mIsLooping = isLoop;
442  emit loopStateChanged(mIsLooping);
443  }
444 }
445 
446 void PlaybackManager::enableRangedPlayback(bool b)
447 {
448  if (mIsRangedPlayback != b)
449  {
450  mIsRangedPlayback = b;
451 
452  updateStartFrame();
453  updateEndFrame();
454 
455  emit rangedPlaybackStateChanged(mIsRangedPlayback);
456  }
457 }
458 
459 void PlaybackManager::setRangedStartFrame(int frame)
460 {
461  mMarkInFrame = frame;
462  updateStartFrame();
463 }
464 
465 void PlaybackManager::setRangedEndFrame(int frame)
466 {
467  mMarkOutFrame = frame;
468  updateEndFrame();
469 }
470 
471 void PlaybackManager::updateStartFrame()
472 {
473  mStartFrame = (mIsRangedPlayback) ? mMarkInFrame : 1;
474 }
475 
476 void PlaybackManager::updateEndFrame()
477 {
478  int projectLength = editor()->layers()->animationLength();
479  mEndFrame = (mIsRangedPlayback) ? mMarkOutFrame : projectLength;
480 }
481 
482 void PlaybackManager::enableSound(bool b)
483 {
484  mIsPlaySound = b;
485 
486  if (!mIsPlaySound)
487  {
488  stopSounds();
489 
490  // If, during playback, the sound is turned on again,
491  // check for sounds partway through.
492  mCheckForSoundsHalfway = true;
493  }
494 }
void setInterval(int msec)
void append(const T &value)
T & last()
bool isActive() const const
void timeout()
void clear()
bool contains(const T &value) const const
Definition: layer.h:38
void stop()
void removeFirst()
bool skipFrame()
PlaybackManager::skipFrame() Small errors accumulate while playing animation If the error is greater ...
const T & at(int i) const const
bool isEmpty() const const
void prepend(T &&value)
int count(const T &value) const const
PreciseTimer
void start(int msec)
Definition: object.h:41
void setTimerType(Qt::TimerType atype)
Definition: editor.h:55
int animationLength(bool includeSounds=true)
Get the length of current project.
QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type)
qint64 elapsed() const const