xref: /haiku/src/apps/mediaplayer/media_node_framework/PlaybackManager.cpp (revision 4cf03112fa7d2fbd98d3248e2c8afd898c596665)
1 // PlaybackManager.cpp
2 
3 #include <algorithm>
4 #include <stdio.h>
5 
6 #include <Message.h>
7 #include <OS.h>
8 #include <Window.h>
9 
10 #include "EventQueue.h"
11 #include "MessageEvent.h"
12 #include "PlaybackListener.h"
13 
14 #include "PlaybackManager.h"
15 
16 using namespace std;
17 
18 //#define TRACE_NODE_MANAGER
19 #ifdef TRACE_NODE_MANAGER
20 # define TRACE(x...)	printf(x)
21 # define ERROR(x...)	fprintf(stderr, x)
22 #else
23 # define TRACE(x...)
24 # define ERROR(x...)	fprintf(stderr, x)
25 #endif
26 
27 void
28 tdebug(const char* str)
29 {
30 	TRACE("[%lx, %lld] ", find_thread(NULL), system_time());
31 	TRACE(str);
32 }
33 
34 
35 struct PlaybackManager::PlayingState {
36 	int64		start_frame;
37 	int64		end_frame;
38 	int64		frame_count;
39 	int64		first_visible_frame;
40 	int64		last_visible_frame;
41 	int64		max_frame_count;
42 	int32		play_mode;
43 	int32		loop_mode;
44 	bool		looping_enabled;
45 	int64		current_frame;			// Playlist frame
46 	int64		range_index;			// playing range index of current_frame
47 	int64		activation_frame;		// absolute video frame
48 };
49 
50 
51 struct PlaybackManager::SpeedInfo {
52 	int64		activation_frame;		// absolute video frame
53 	bigtime_t	activation_time;		// performance time
54 	float		speed;					// speed to be used for calculations,
55 										// is 1.0 if not playing
56 	float		set_speed;				// speed set by the user
57 };
58 
59 
60 // #pragma mark - PlaybackManager
61 
62 
63 PlaybackManager::PlaybackManager()
64 	: BLooper("playback manager"),
65 	  fStates(10),
66 	  fSpeeds(10),
67 	  fCurrentAudioTime(0),
68 	  fCurrentVideoTime(0),
69 	  fPerformanceTime(0),
70 	  fFrameRate(1.0),
71 	  fStopPlayingFrame(-1),
72 	  fListeners(),
73 	  fNoAudio(false)
74 {
75 	Run();
76 }
77 
78 
79 PlaybackManager::~PlaybackManager()
80 {
81 	Cleanup();
82 }
83 
84 
85 void
86 PlaybackManager::Init(float frameRate, int32 loopingMode, bool loopingEnabled,
87 	float playbackSpeed, int32 playMode, int32 currentFrame)
88 {
89 	// cleanup first
90 	Cleanup();
91 
92 	// set the new frame rate
93 	fFrameRate = frameRate;
94 	fCurrentAudioTime = 0;
95 	fCurrentVideoTime = 0;
96 	fPerformanceTime = 0;
97 	fStopPlayingFrame = -1;
98 
99 	// set up the initial speed
100 	SpeedInfo* speed = new SpeedInfo;
101 	speed->activation_frame = 0;
102 	speed->activation_time = 0;
103 	speed->speed = playbackSpeed;
104 	speed->set_speed = playbackSpeed;
105 	_PushSpeedInfo(speed);
106 
107 	// set up the initial state
108 	PlayingState* state = new PlayingState;
109 	state->frame_count = Duration();
110 	state->start_frame = 0;
111 	state->end_frame = 0;
112 	state->first_visible_frame = 0;
113 	state->last_visible_frame = 0;
114 	state->max_frame_count = state->frame_count;
115 
116 	state->play_mode = MODE_PLAYING_PAUSED_FORWARD;
117 	state->loop_mode = loopingMode;
118 	state->looping_enabled = loopingEnabled;
119 	state->current_frame = currentFrame;
120 	state->activation_frame = 0;
121 	fStates.AddItem(state);
122 
123 	TRACE("initial state pushed: state count: %ld\n", fStates.CountItems());
124 
125 	// notify the listeners
126 	NotifyPlayModeChanged(PlayMode());
127 	NotifyLoopModeChanged(LoopMode());
128 	NotifyLoopingEnabledChanged(IsLoopingEnabled());
129 	NotifyVideoBoundsChanged(VideoBounds());
130 	NotifyFPSChanged(FramesPerSecond());
131 	NotifySpeedChanged(Speed());
132 	NotifyCurrentFrameChanged(CurrentFrame());
133 	SetPlayMode(playMode);
134 }
135 
136 
137 void
138 PlaybackManager::Cleanup()
139 {
140 	// delete states
141 	for (int32 i = 0; PlayingState* state = _StateAt(i); i++)
142 		delete state;
143 	fStates.MakeEmpty();
144 
145 	// delete speed infos
146 	for (int32 i = 0; SpeedInfo* speed = _SpeedInfoAt(i); i++)
147 		delete speed;
148 	fSpeeds.MakeEmpty();
149 }
150 
151 
152 void
153 PlaybackManager::MessageReceived(BMessage* message)
154 {
155 	switch (message->what) {
156 		case MSG_EVENT:
157 			SetPerformanceTime(TimeForRealTime(system_time()));
158 //TRACE("MSG_EVENT: rt: %lld, pt: %lld\n", system_time(), fPerformanceTime);
159 			break;
160 
161 		case MSG_PLAYBACK_FORCE_UPDATE:
162 		{
163 			int64 frame;
164 			if (message->FindInt64("frame", &frame) < B_OK)
165 				frame = CurrentFrame();
166 			SetCurrentFrame(frame);
167 			break;
168 		}
169 
170 		case MSG_PLAYBACK_SET_RANGE:
171 		{
172 			int64 startFrame = _LastState()->start_frame;
173 			int64 endFrame = _LastState()->end_frame;
174 			message->FindInt64("start frame", &startFrame);
175 			message->FindInt64("end frame", &endFrame);
176 			if (startFrame != _LastState()->start_frame
177 				|| endFrame != _LastState()->end_frame) {
178 				PlayingState* state = new PlayingState(*_LastState());
179 				state->start_frame = startFrame;
180 				state->end_frame = endFrame;
181 				_PushState(state, true);
182 			}
183 			break;
184 		}
185 
186 		case MSG_PLAYBACK_SET_VISIBLE:
187 		{
188 			int64 startFrame = _LastState()->first_visible_frame;
189 			int64 endFrame = _LastState()->last_visible_frame;
190 			message->FindInt64("start frame", &startFrame);
191 			message->FindInt64("end frame", &endFrame);
192 			if (startFrame != _LastState()->first_visible_frame
193 				|| endFrame != _LastState()->last_visible_frame) {
194 				PlayingState* state = new PlayingState(*_LastState());
195 				state->first_visible_frame = startFrame;
196 				state->last_visible_frame = endFrame;
197 				_PushState(state, true);
198 			}
199 			break;
200 		}
201 
202 		case MSG_PLAYBACK_SET_LOOP_MODE:
203 		{
204 			int32 loopMode = _LastState()->loop_mode;
205 			message->FindInt32("mode", &loopMode);
206 			if (loopMode != _LastState()->loop_mode) {
207 				PlayingState* state = new PlayingState(*_LastState());
208 				state->loop_mode = loopMode;
209 				_PushState(state, true);
210 			}
211 			break;
212 		}
213 
214 		// other messages
215 		default:
216 			BLooper::MessageReceived(message);
217 			break;
218 	}
219 }
220 
221 // #pragma mark - playback control
222 
223 
224 void
225 PlaybackManager::StartPlaying(bool atBeginning)
226 {
227 	TRACE("PlaybackManager::StartPlaying()\n");
228 	int32 playMode = PlayMode();
229 	if (playMode == MODE_PLAYING_PAUSED_FORWARD)
230 		SetPlayMode(MODE_PLAYING_FORWARD, !atBeginning);
231 	if (playMode == MODE_PLAYING_PAUSED_BACKWARD)
232 		SetPlayMode(MODE_PLAYING_BACKWARD, !atBeginning);
233 }
234 
235 
236 void
237 PlaybackManager::StopPlaying()
238 {
239 	TRACE("PlaybackManager::StopPlaying()\n");
240 	int32 playMode = PlayMode();
241 	if (playMode == MODE_PLAYING_FORWARD)
242 		SetPlayMode(MODE_PLAYING_PAUSED_FORWARD, true);
243 	if (playMode == MODE_PLAYING_BACKWARD)
244 		SetPlayMode(MODE_PLAYING_PAUSED_BACKWARD, true);
245 }
246 
247 
248 void
249 PlaybackManager::TogglePlaying(bool atBeginning)
250 {
251 	// playmodes (paused <-> playing) are the negative of each other
252 	SetPlayMode(-PlayMode(), !atBeginning);
253 }
254 
255 
256 void
257 PlaybackManager::PausePlaying()
258 {
259 	if (PlayMode() > 0)
260 		TogglePlaying();
261 }
262 
263 
264 bool
265 PlaybackManager::IsPlaying() const
266 {
267 	return (PlayMode() > 0);
268 }
269 
270 
271 int32
272 PlaybackManager::PlayMode() const
273 {
274 	if (!_LastState())
275 		return MODE_PLAYING_PAUSED_FORWARD;
276 	return _LastState()->play_mode;
277 }
278 
279 
280 int32
281 PlaybackManager::LoopMode() const
282 {
283 	if (!_LastState())
284 		return LOOPING_ALL;
285 	return _LastState()->loop_mode;
286 }
287 
288 
289 bool
290 PlaybackManager::IsLoopingEnabled() const
291 {
292 	if (!_LastState())
293 		return true;
294 	return _LastState()->looping_enabled;
295 }
296 
297 
298 int32
299 PlaybackManager::CurrentFrame() const
300 {
301 	return PlaylistFrameAtFrame(FrameForTime(fPerformanceTime));
302 }
303 
304 
305 float
306 PlaybackManager::Speed() const
307 {
308 	if (!_LastState())
309 		return 1.0;
310 	return _LastSpeedInfo()->set_speed;
311 }
312 
313 
314 void
315 PlaybackManager::SetFramesPerSecond(float framesPerSecond)
316 {
317 	if (framesPerSecond != fFrameRate) {
318 		fFrameRate = framesPerSecond;
319 		NotifyFPSChanged(fFrameRate);
320 	}
321 }
322 
323 
324 float
325 PlaybackManager::FramesPerSecond() const
326 {
327 	return fFrameRate;
328 }
329 
330 
331 void
332 PlaybackManager::FrameDropped() const
333 {
334 	NotifyFrameDropped();
335 }
336 
337 
338 void
339 PlaybackManager::DurationChanged()
340 {
341 	if (!_LastState())
342 		return;
343 	int32 frameCount = Duration();
344 	if (frameCount != _LastState()->frame_count) {
345 		PlayingState* state = new PlayingState(*_LastState());
346 		state->frame_count = frameCount;
347 		state->max_frame_count = frameCount;
348 
349 		_PushState(state, true);
350 	}
351 }
352 
353 /*! Creates a new playing state that equals the previous one aside from
354 	its current frame which is set to /frame/.
355 	The new state will be activated as soon as possible. */
356 void
357 PlaybackManager::SetCurrentFrame(int64 frame)
358 {
359 	if (_LastState()->current_frame == frame)
360 		return;
361 	PlayingState* newState = new PlayingState(*_LastState());
362 	newState->current_frame = frame;
363 	_PushState(newState, false);
364 }
365 
366 
367 /*!	Creates a new playing state that equals the previous one aside from
368 	its playing mode which is set to /mode/.
369 	The new state will be activated as soon as possible.
370 	If /continuePlaying/ is true and the new state is a `not stopped'
371 	state, playing continues at the frame the last state reaches when the
372 	new state becomes active (or the next frame in the playing range).
373 	If /continuePlaying/ is false, playing starts at the beginning of the
374 	playing range (taking the playing direction into consideration). */
375 void
376 PlaybackManager::SetPlayMode(int32 mode, bool continuePlaying)
377 {
378 //printf("PlaybackManager::SetPlayMode(%ld, %d)\n", mode, continuePlaying);
379 	PlayingState* newState = new PlayingState(*_LastState());
380 	newState->play_mode = mode;
381 	// Jump to the playing start frame if we should not continue, where we
382 	// stop.
383 	if (!continuePlaying && !(_PlayingDirectionFor(newState) == 0))
384 		newState->current_frame = _PlayingStartFrameFor(newState);
385 	_PushState(newState, continuePlaying);
386 	NotifyPlayModeChanged(mode);
387 }
388 
389 
390 /*!	Creates a new playing state that equals the previous one aside from
391 	its loop mode which is set to /mode/.
392 	The new state will be activated as soon as possible.
393 	If /continuePlaying/ is true and the new state is a `not stopped'
394 	state, playing continues at the frame the last state reaches when the
395 	new state becomes active (or the next frame in the playing range).
396 	If /continuePlaying/ is false, playing starts at the beginning of the
397 	playing range (taking the playing direction into consideration). */
398 void
399 PlaybackManager::SetLoopMode(int32 mode, bool continuePlaying)
400 {
401 	PlayingState* newState = new PlayingState(*_LastState());
402 	newState->loop_mode = mode;
403 	// Jump to the playing start frame if we should not continue, where we
404 	// stop.
405 	if (!continuePlaying && !(_PlayingDirectionFor(newState) == 0))
406 		newState->current_frame = _PlayingStartFrameFor(newState);
407 	_PushState(newState, continuePlaying);
408 	NotifyLoopModeChanged(mode);
409 }
410 
411 
412 /*	Creates a new playing state that equals the previous one aside from
413 	its looping enabled flag which is set to /enabled/.
414 	The new state will be activated as soon as possible.
415 	If /continuePlaying/ is true and the new state is a `not stopped'
416 	state, playing continues at the frame the last state reaches when the
417 	new state becomes active (or the next frame in the playing range).
418 	If /continuePlaying/ is false, playing starts at the beginning of the
419 	playing range (taking the playing direction into consideration). */
420 void
421 PlaybackManager::SetLoopingEnabled(bool enabled, bool continuePlaying)
422 {
423 	PlayingState* newState = new PlayingState(*_LastState());
424 	newState->looping_enabled = enabled;
425 	// Jump to the playing start frame if we should not continue, where we
426 	// stop.
427 	if (!continuePlaying && !(_PlayingDirectionFor(newState) == 0))
428 		newState->current_frame = _PlayingStartFrameFor(newState);
429 	_PushState(newState, continuePlaying);
430 	NotifyLoopingEnabledChanged(enabled);
431 }
432 
433 
434 void
435 PlaybackManager::SetSpeed(float speed)
436 {
437 	SpeedInfo* lastSpeed = _LastSpeedInfo();
438 	if (speed != lastSpeed->set_speed) {
439 		SpeedInfo* info = new SpeedInfo(*lastSpeed);
440 		info->activation_frame = NextFrame();
441 		info->set_speed = speed;
442 		if (_PlayingDirectionFor(_StateAtFrame(info->activation_frame)) != 0)
443 			info->speed = info->set_speed;
444 		else
445 			info->speed = 1.0;
446 		_PushSpeedInfo(info);
447 	}
448 }
449 
450 
451 // #pragma mark -
452 
453 
454 /*!	Returns the first frame at which a new playing state could become active,
455 	that is the first frame for that neither the audio nor the video producer
456 	have fetched data.*/
457 int64
458 PlaybackManager::NextFrame() const
459 {
460 	if (fNoAudio)
461 		return FrameForTime(fCurrentVideoTime - 1) + 1;
462 
463 	return FrameForTime(max((bigtime_t)fCurrentAudioTime,
464 		(bigtime_t)fCurrentVideoTime) - 1) + 1;
465 }
466 
467 
468 //! Returns the Playlist frame for NextFrame().
469 int64
470 PlaybackManager::NextPlaylistFrame() const
471 {
472 	return PlaylistFrameAtFrame(NextFrame());
473 }
474 
475 
476 int64
477 PlaybackManager::FirstPlaybackRangeFrame()
478 {
479 	PlayingState* state = _StateAtFrame(CurrentFrame());
480 	switch (state->loop_mode) {
481 		case LOOPING_RANGE:
482 			return state->start_frame;
483 		case LOOPING_SELECTION:
484 			// TODO: ...
485 			return 0;
486 		case LOOPING_VISIBLE:
487 			return state->first_visible_frame;
488 
489 		case LOOPING_ALL:
490 		default:
491 			return 0;
492 	}
493 }
494 
495 
496 int64
497 PlaybackManager::LastPlaybackRangeFrame()
498 {
499 	PlayingState* state = _StateAtFrame(CurrentFrame());
500 	switch (state->loop_mode) {
501 		case LOOPING_RANGE:
502 			return state->end_frame;
503 		case LOOPING_SELECTION:
504 			// TODO: ...
505 			return state->frame_count - 1;
506 		case LOOPING_VISIBLE:
507 			return state->last_visible_frame;
508 
509 		case LOOPING_ALL:
510 		default:
511 			return state->frame_count - 1;
512 	}
513 }
514 
515 
516 // #pragma mark -
517 
518 
519 int64
520 PlaybackManager::StartFrameAtFrame(int64 frame)
521 {
522 	return _StateAtFrame(frame)->start_frame;
523 }
524 
525 
526 int64
527 PlaybackManager::StartFrameAtTime(bigtime_t time)
528 {
529 	return _StateAtTime(time)->start_frame;
530 }
531 
532 
533 int64
534 PlaybackManager::EndFrameAtFrame(int64 frame)
535 {
536 	return _StateAtTime(frame)->end_frame;
537 }
538 
539 
540 int64
541 PlaybackManager::EndFrameAtTime(bigtime_t time)
542 {
543 	return _StateAtTime(time)->end_frame;
544 }
545 
546 
547 int64
548 PlaybackManager::FrameCountAtFrame(int64 frame)
549 {
550 	return _StateAtTime(frame)->frame_count;
551 }
552 
553 
554 int64
555 PlaybackManager::FrameCountAtTime(bigtime_t time)
556 {
557 	return _StateAtTime(time)->frame_count;
558 }
559 
560 
561 int32
562 PlaybackManager::PlayModeAtFrame(int64 frame)
563 {
564 	return _StateAtTime(frame)->play_mode;
565 }
566 
567 
568 int32
569 PlaybackManager::PlayModeAtTime(bigtime_t time)
570 {
571 	return _StateAtTime(time)->play_mode;
572 }
573 
574 
575 int32
576 PlaybackManager::LoopModeAtFrame(int64 frame)
577 {
578 	return _StateAtTime(frame)->loop_mode;
579 }
580 
581 
582 int32
583 PlaybackManager::LoopModeAtTime(bigtime_t time)
584 {
585 	return _StateAtTime(time)->loop_mode;
586 }
587 
588 
589 /*!	Returns which Playlist frame should be displayed at (performance) video
590 	frame /frame/. Additionally the playing direction (0, 1, -1) is returned,
591 	as well as if a new playing state becomes active with this frame.
592 	A new playing state is installed, if either some data directly concerning
593 	the playing (play mode, loop mode, playing ranges, selection...) or the
594 	Playlist has changed. */
595 int64
596 PlaybackManager::PlaylistFrameAtFrame(int64 frame, int32& playingDirection,
597 	bool& newState) const
598 {
599 //TRACE("PlaybackManager::PlaylistFrameAtFrame(%lld)\n", frame);
600 	PlayingState* state = _StateAtFrame(frame);
601 	newState = (state->activation_frame == frame);
602 	playingDirection = _PlayingDirectionFor(state);
603 //TRACE("playing state: activation frame: %lld, current frame: %lld, dir: %ld\n",
604 //state->activation_frame, state->current_frame, state->play_mode);
605 	// The first part of the index calculation is invariable for a state. We
606 	// could add it to the state data.
607 	int64 result = state->current_frame;
608 	if (playingDirection != 0) {
609 //		int64 index = _RangeFrameForFrame(state, state->current_frame)
610 		int64 index = state->range_index
611 			+ (frame - state->activation_frame) * playingDirection;
612 		result = _FrameForRangeFrame(state, index);
613 	}
614 //TRACE("PlaybackManager::PlaylistFrameAtFrame() done: %lld\n", result);
615 //printf("PlaybackManager::PlaylistFrameAtFrame(%lld): %lld, direction: %ld\n",
616 //	frame, result, playingDirection);
617 	return result;
618 }
619 
620 
621 int64
622 PlaybackManager::PlaylistFrameAtFrame(int64 frame, int32& playingDirection) const
623 {
624 	bool newState;
625 	return PlaylistFrameAtFrame(frame, playingDirection, newState);
626 }
627 
628 
629 int64
630 PlaybackManager::PlaylistFrameAtFrame(int64 frame) const
631 {
632 	bool newState;
633 	int32 playingDirection;
634 	return PlaylistFrameAtFrame(frame, playingDirection, newState);
635 }
636 
637 
638 /*!	Returns the index of the next frame after /startFrame/ at which a
639 	playing state or speed change occurs or /endFrame/, if this happens to be
640 	earlier. */
641 int64
642 PlaybackManager::NextChangeFrame(int64 startFrame, int64 endFrame) const
643 {
644 	int32 startIndex = _IndexForFrame(startFrame);
645 	int32 endIndex = _IndexForFrame(endFrame);
646 	if (startIndex < endIndex)
647 		endFrame = _StateAt(startIndex + 1)->activation_frame;
648 	startIndex = _SpeedInfoIndexForFrame(startFrame);
649 	endIndex = _SpeedInfoIndexForFrame(endFrame);
650 	if (startIndex < endIndex)
651 		endFrame = _SpeedInfoAt(startIndex + 1)->activation_frame;
652 	return endFrame;
653 }
654 
655 
656 /*!	Returns the next time after /startTime/ at which a playing state or
657 	speed change occurs or /endTime/, if this happens to be earlier. */
658 bigtime_t
659 PlaybackManager::NextChangeTime(bigtime_t startTime, bigtime_t endTime) const
660 {
661 	int32 startIndex = _IndexForTime(startTime);
662 	int32 endIndex = _IndexForTime(endTime);
663 	if (startIndex < endIndex)
664 		endTime = TimeForFrame(_StateAt(startIndex + 1)->activation_frame);
665 	startIndex = _SpeedInfoIndexForTime(startTime);
666 	endIndex = _SpeedInfoIndexForTime(endTime);
667 	if (startIndex < endIndex)
668 		endTime = TimeForFrame(_SpeedInfoAt(startIndex + 1)->activation_frame);
669 	return endTime;
670 }
671 
672 
673 /*!	Returns a contiguous Playlist frame interval for a given frame interval.
674 	The returned interval may be smaller than the supplied one. Therefore
675 	the supplied /endFrame/ is adjusted.
676 	The value written to /xEndFrame/ is the first frame that doesn't belong
677 	to the interval. /playingDirection/ may either be -1 for backward,
678 	1 for forward or 0 for not playing. */
679 void
680 PlaybackManager::GetPlaylistFrameInterval(int64 startFrame, int64& endFrame,
681 	int64& xStartFrame, int64& xEndFrame, int32& playingDirection) const
682 {
683 	// limit the interval to one state and speed info
684 	endFrame = NextChangeFrame(startFrame, endFrame);
685 	// init return values
686 	xStartFrame = PlaylistFrameAtFrame(startFrame);
687 	xEndFrame = xStartFrame;
688 	playingDirection = _PlayingDirectionFor(_StateAtFrame(startFrame));
689 	// Step through the interval and check whether the respective Playlist
690 	// frames belong to the Playlist interval.
691 	bool endOfInterval = false;
692 	int64 intervalLength = 0;
693 	while (startFrame < endFrame && !endOfInterval) {
694 		intervalLength++;
695 		startFrame++;
696 		xEndFrame += playingDirection;
697 		endOfInterval = (PlaylistFrameAtFrame(startFrame) != xEndFrame);
698 	}
699 	// order the interval bounds, if playing backwards
700 	if (xStartFrame > xEndFrame) {
701 		swap(xStartFrame, xEndFrame);
702 		xStartFrame++;
703 		xEndFrame++;
704 	}
705 }
706 
707 
708 /*!	The same as GetPlaylistFrameInterval() just for time instead of frame
709 	intervals. Note, that /startTime/ and /endTime/ measure
710 	performance times whereas /xStartTime/ and /xEndTime/ specifies an
711 	Playlist time interval. In general it does not hold
712 	xEndTime - xStartTime == endTime - startTime, even if playing (think
713 	of a playing speed != 1.0). */
714 void
715 PlaybackManager::GetPlaylistTimeInterval(bigtime_t startTime,
716 	bigtime_t& endTime, bigtime_t& xStartTime, bigtime_t& xEndTime,
717 	float& playingSpeed) const
718 {
719 	// Get the frames that bound the given time interval. The end frame might
720 	// be greater than necessary, but that doesn't harm.
721 	int64 startFrame = FrameForTime(startTime);
722 	int64 endFrame = FrameForTime(endTime) + 1;
723 	SpeedInfo* info = _SpeedInfoForFrame(startFrame);
724 	// Get the Playlist frame interval that belongs to the frame interval.
725 	int64 xStartFrame;
726 	int64 xEndFrame;
727 	int32 playingDirection;
728 	GetPlaylistFrameInterval(startFrame, endFrame, xStartFrame, xEndFrame,
729 		playingDirection);
730 	// Calculate the performance time interval end/length.
731 	bigtime_t endTimeForFrame = TimeForFrame(endFrame);
732 	endTime = min(endTime, endTimeForFrame);
733 	bigtime_t intervalLength = endTime - startTime;
734 
735 	// Finally determine the time bounds for the Playlist interval (depending
736 	// on the playing direction).
737 	switch (playingDirection) {
738 		// forward
739 		case 1:
740 		{
741 //			xStartTime = PlaylistTimeForFrame(xStartFrame)
742 //						 + startTime - TimeForFrame(startFrame);
743 //			xEndTime = xStartTime + intervalLength;
744 
745 // TODO: The current method does not handle the times the same way.
746 //       It may happen, that for the same performance time different
747 //       Playlist times (within a frame) are returned when passing it
748 //       one time as a start time and another time as an end time.
749 			xStartTime = PlaylistTimeForFrame(xStartFrame)
750 				+ bigtime_t(double(startTime - TimeForFrame(startFrame))
751 							* info->speed);
752 			xEndTime = xStartTime
753 					   + bigtime_t((double)intervalLength * info->speed);
754 			break;
755 		}
756 		// backward
757 		case -1:
758 		{
759 //			xEndTime = PlaylistTimeForFrame(xEndFrame)
760 //					   - startTime + TimeForFrame(startFrame);
761 //			xStartTime = xEndTime - intervalLength;
762 
763 			xEndTime = PlaylistTimeForFrame(xEndFrame)
764 				- bigtime_t(double(startTime - TimeForFrame(startFrame))
765 							* info->speed);
766 			xStartTime = xEndTime
767 					   - bigtime_t((double)intervalLength * info->speed);
768 			break;
769 		}
770 		// not playing
771 		case 0:
772 		default:
773 			xStartTime = PlaylistTimeForFrame(xStartFrame);
774 			xEndTime = xStartTime;
775 			break;
776 	}
777 	playingSpeed = (float)playingDirection * info->speed;
778 }
779 
780 
781 /*!	Returns the frame that is being performed at the supplied time.
782 	It holds TimeForFrame(frame) <= time < TimeForFrame(frame + 1). */
783 int64
784 PlaybackManager::FrameForTime(bigtime_t time) const
785 {
786 //TRACE("PlaybackManager::FrameForTime(%lld)\n", time);
787 //	return (int64)((double)time * (double)fFrameRate / 1000000.0);
788 	// In order to avoid problems caused by rounding errors, we check
789 	// if for the resulting frame holds
790 	// TimeForFrame(frame) <= time < TimeForFrame(frame + 1).
791 //	int64 frame = (int64)((double)time * (double)fFrameRate / 1000000.0);
792 	SpeedInfo* info = _SpeedInfoForTime(time);
793 if (!info) {
794 	fprintf(stderr, "PlaybackManager::FrameForTime() - no SpeedInfo!\n");
795 	return 0;
796 }
797 	int64 frame = (int64)(((double)time - info->activation_time)
798 		* (double)fFrameRate * info->speed / 1000000.0)
799 		+ info->activation_frame;
800 	if (TimeForFrame(frame) > time)
801 		frame--;
802 	else if (TimeForFrame(frame + 1) <= time)
803 		frame++;
804 //TRACE("PlaybackManager::FrameForTime() done: %lld\n", frame);
805 //printf("PlaybackManager::FrameForTime(%lld): %lld\n", time, frame);
806 	return frame;
807 }
808 
809 
810 /*!	Returns the time at which the supplied frame should be performed (started
811 	to be performed). */
812 bigtime_t
813 PlaybackManager::TimeForFrame(int64 frame) const
814 {
815 //	return (bigtime_t)((double)frame * 1000000.0 / (double)fFrameRate);
816 	SpeedInfo* info = _SpeedInfoForFrame(frame);
817 if (!info) {
818 	fprintf(stderr, "PlaybackManager::TimeForFrame() - no SpeedInfo!\n");
819 	return 0;
820 }
821 //	return (bigtime_t)((double)(frame - info->activation_frame) * 1000000.0
822 //					   / ((double)fFrameRate * info->speed))
823 // 		   + info->activation_time;
824 bigtime_t result = (bigtime_t)((double)(frame - info->activation_frame) * 1000000.0
825 / ((double)fFrameRate * info->speed)) + info->activation_time;
826 //fprintf(stderr, "PlaybackManager::TimeForFrame(%lld): %lld\n", frame, result);
827 return result;
828 }
829 
830 
831 /*!	Returns the Playlist frame for an Playlist time.
832 	It holds PlaylistTimeForFrame(frame) <= time <
833 	PlaylistTimeForFrame(frame + 1). */
834 int64
835 PlaybackManager::PlaylistFrameForTime(bigtime_t time) const
836 {
837 	// In order to avoid problems caused by rounding errors, we check
838 	// if for the resulting frame holds
839 	// PlaylistTimeForFrame(frame) <= time < PlaylistTimeForFrame(frame + 1).
840 	int64 frame = (int64)((double)time * (double)fFrameRate / 1000000.0);
841 	if (TimeForFrame(frame) > time)
842 		frame--;
843 	else if (TimeForFrame(frame + 1) <= time)
844 		frame++;
845 	return frame;
846 }
847 
848 
849 //! Returns the Playlist start time for an Playlist frame.
850 bigtime_t
851 PlaybackManager::PlaylistTimeForFrame(int64 frame) const
852 {
853 	return (bigtime_t)((double)frame * 1000000.0 / (double)fFrameRate);
854 }
855 
856 
857 //! To be called when done with all activities concerning audio before /time/.
858 void
859 PlaybackManager::SetCurrentAudioTime(bigtime_t time)
860 {
861 TRACE("PlaybackManager::SetCurrentAudioTime(%lld)\n", time);
862 	bigtime_t lastFrameTime = _TimeForLastFrame();
863 	fCurrentAudioTime = time;
864 //	_UpdateStates();
865 	bigtime_t newLastFrameTime = _TimeForLastFrame();
866 	if (lastFrameTime != newLastFrameTime) {
867 		bigtime_t eventTime = RealTimeForTime(newLastFrameTime);
868 		EventQueue::Default().AddEvent(new MessageEvent(eventTime, this));
869 		_CheckStopPlaying();
870 	}
871 }
872 
873 
874 //! To be called when done with all activities concerning video before /frame/.
875 void
876 PlaybackManager::SetCurrentVideoFrame(int64 frame)
877 {
878 	SetCurrentVideoTime(TimeForFrame(frame));
879 }
880 
881 
882 //! To be called when done with all activities concerning video before /time/.
883 void
884 PlaybackManager::SetCurrentVideoTime(bigtime_t time)
885 {
886 TRACE("PlaybackManager::SetCurrentVideoTime(%lld)\n", time);
887 	bigtime_t lastFrameTime = _TimeForLastFrame();
888 	fCurrentVideoTime = time;
889 //	_UpdateStates();
890 	bigtime_t newLastFrameTime = _TimeForLastFrame();
891 	if (lastFrameTime != newLastFrameTime) {
892 		bigtime_t eventTime = RealTimeForTime(newLastFrameTime);
893 		EventQueue::Default().AddEvent(new MessageEvent(eventTime, this));
894 		_CheckStopPlaying();
895 	}
896 }
897 
898 
899 //! To be called as soon as video frame /frame/ is being performed.
900 void
901 PlaybackManager::SetPerformanceFrame(int64 frame)
902 {
903 	SetPerformanceFrame(TimeForFrame(frame));
904 }
905 
906 
907 /*!	Similar to SetPerformanceTime() just with a time instead of a frame
908 	argument. */
909 void
910 PlaybackManager::SetPerformanceTime(bigtime_t time)
911 {
912 	int32 oldCurrentFrame = CurrentFrame();
913 	fPerformanceTime = time;
914 	_UpdateStates();
915 	_UpdateSpeedInfos();
916 	int32 currentFrame = CurrentFrame();
917 
918 //printf("PlaybackManager::SetPerformanceTime(%lld): %ld -> %ld\n",
919 //	time, oldCurrentFrame, currentFrame);
920 
921 	if (currentFrame != oldCurrentFrame)
922 		NotifyCurrentFrameChanged(currentFrame);
923 }
924 
925 
926 // #pragma mark - Listeners
927 
928 
929 void
930 PlaybackManager::AddListener(PlaybackListener* listener)
931 {
932 	if (!listener)
933 		return;
934 
935 	if (!Lock())
936 		return;
937 
938 	if (!fListeners.HasItem(listener) && fListeners.AddItem(listener)) {
939 		// bring listener up2date, if we have been initialized
940 		if (_LastState()) {
941 			listener->PlayModeChanged(PlayMode());
942 			listener->LoopModeChanged(LoopMode());
943 			listener->LoopingEnabledChanged(IsLoopingEnabled());
944 			listener->VideoBoundsChanged(VideoBounds());
945 			listener->FramesPerSecondChanged(FramesPerSecond());
946 			listener->SpeedChanged(Speed());
947 			listener->CurrentFrameChanged(CurrentFrame());
948 		}
949 	}
950 
951 	Unlock();
952 }
953 
954 
955 void
956 PlaybackManager::RemoveListener(PlaybackListener* listener)
957 {
958 	if (listener && Lock()) {
959 		fListeners.RemoveItem(listener);
960 		Unlock();
961 	}
962 }
963 
964 
965 void
966 PlaybackManager::NotifyPlayModeChanged(int32 mode) const
967 {
968 	for (int32 i = 0;
969 		 PlaybackListener* listener = (PlaybackListener*)fListeners.ItemAt(i);
970 		 i++) {
971 		listener->PlayModeChanged(mode);
972 	}
973 }
974 
975 
976 void
977 PlaybackManager::NotifyLoopModeChanged(int32 mode) const
978 {
979 	for (int32 i = 0;
980 		 PlaybackListener* listener = (PlaybackListener*)fListeners.ItemAt(i);
981 		 i++) {
982 		listener->LoopModeChanged(mode);
983 	}
984 }
985 
986 
987 void
988 PlaybackManager::NotifyLoopingEnabledChanged(bool enabled) const
989 {
990 	for (int32 i = 0;
991 		 PlaybackListener* listener = (PlaybackListener*)fListeners.ItemAt(i);
992 		 i++) {
993 		listener->LoopingEnabledChanged(enabled);
994 	}
995 }
996 
997 
998 void
999 PlaybackManager::NotifyVideoBoundsChanged(BRect bounds) const
1000 {
1001 	for (int32 i = 0;
1002 		 PlaybackListener* listener = (PlaybackListener*)fListeners.ItemAt(i);
1003 		 i++) {
1004 		listener->VideoBoundsChanged(bounds);
1005 	}
1006 }
1007 
1008 
1009 void
1010 PlaybackManager::NotifyFPSChanged(float fps) const
1011 {
1012 	for (int32 i = 0;
1013 		 PlaybackListener* listener = (PlaybackListener*)fListeners.ItemAt(i);
1014 		 i++) {
1015 		listener->FramesPerSecondChanged(fps);
1016 	}
1017 }
1018 
1019 
1020 void
1021 PlaybackManager::NotifyCurrentFrameChanged(int32 frame) const
1022 {
1023 	for (int32 i = 0;
1024 		 PlaybackListener* listener = (PlaybackListener*)fListeners.ItemAt(i);
1025 		 i++) {
1026 		listener->CurrentFrameChanged(frame);
1027 	}
1028 }
1029 
1030 
1031 void
1032 PlaybackManager::NotifySpeedChanged(float speed) const
1033 {
1034 	for (int32 i = 0;
1035 		 PlaybackListener* listener = (PlaybackListener*)fListeners.ItemAt(i);
1036 		 i++) {
1037 		listener->SpeedChanged(speed);
1038 	}
1039 }
1040 
1041 
1042 void
1043 PlaybackManager::NotifyFrameDropped() const
1044 {
1045 	for (int32 i = 0;
1046 		 PlaybackListener* listener = (PlaybackListener*)fListeners.ItemAt(i);
1047 		 i++) {
1048 		listener->FrameDropped();
1049 	}
1050 }
1051 
1052 
1053 void
1054 PlaybackManager::NotifyStopFrameReached() const
1055 {
1056 	// not currently implemented in PlaybackListener interface
1057 }
1058 
1059 
1060 void
1061 PlaybackManager::PrintState(PlayingState* state)
1062 {
1063 	TRACE("state: activation frame: %lld, current frame: %lld, "
1064 		   "start frame: %lld, end frame: %lld, frame count: %lld, "
1065 		   "first visible: %lld, last visible: %lld, selection (...), "
1066 		   "play mode: %ld, loop mode: %ld\n",
1067 			state->activation_frame,
1068 			state->current_frame,
1069 			state->start_frame,
1070 			state->end_frame,
1071 			state->frame_count,
1072 			state->first_visible_frame,
1073 			state->last_visible_frame,
1074 //			state->selection,
1075 			state->play_mode,
1076 			state->loop_mode);
1077 }
1078 
1079 
1080 void
1081 PlaybackManager::PrintStateAtFrame(int64 frame)
1082 {
1083 	TRACE("frame %lld: ", frame);
1084 	PrintState(_StateAtFrame(frame));
1085 }
1086 
1087 
1088 /*!	Appends the supplied state to the list of states. If the state would
1089 	become active at the same time as _LastState() the latter is removed
1090 	and deleted. However, the activation time for the new state is adjusted,
1091 	so that it is >= that of the last state and >= the current audio and
1092 	video time. If the additional parameter /adjustCurrentFrame/ is true,
1093 	the new state's current frame is tried to be set to the frame that is
1094 	reached at the time the state will become active. In every case
1095 	it is ensured that the current frame lies within the playing range
1096 	(if playing). */
1097 void
1098 PlaybackManager::_PushState(PlayingState* state, bool adjustCurrentFrame)
1099 {
1100 //	if (!_LastState())
1101 //		debugger("PlaybackManager::_PushState() used before Init()\n");
1102 
1103 TRACE("PlaybackManager::_PushState()\n");
1104 	if (state) {
1105 		// unset fStopPlayingFrame
1106 		int64 oldStopPlayingFrame = fStopPlayingFrame;
1107 		fStopPlayingFrame = -1;
1108 		// get last state
1109 		PlayingState* lastState = _LastState();
1110 		int64 activationFrame = max(max(state->activation_frame,
1111 										lastState->activation_frame),
1112 									NextFrame());
1113 TRACE("  state activation frame: %lld, last state activation frame: %lld, "
1114 "NextFrame(): %lld\n", state->activation_frame, lastState->activation_frame,
1115 NextFrame());
1116 
1117 		int64 currentFrame = 0;
1118 		// remember the current frame, if necessary
1119 		if (adjustCurrentFrame)
1120 			currentFrame = PlaylistFrameAtFrame(activationFrame);
1121 		// check whether it is active
1122 		// (NOTE: We may want to keep the last state, if it is not active,
1123 		//  but then the new state should become active after the last one.
1124 		//  Thus we had to replace `NextFrame()' with `activationFrame'.)
1125 		if (lastState->activation_frame >= NextFrame()) {
1126 			// it isn't -- remove it
1127 			fStates.RemoveItem(fStates.CountItems() - 1);
1128 TRACE("deleting last \n");
1129 PrintState(lastState);
1130 			delete lastState;
1131 		} else {
1132 			// it is -- keep it
1133 		}
1134 		// adjust the new state's current frame and activation frame
1135 		if (adjustCurrentFrame)
1136 			state->current_frame = currentFrame;
1137 		int32 playingDirection = _PlayingDirectionFor(state);
1138 		if (playingDirection != 0) {
1139 			state->current_frame
1140 				= _NextFrameInRange(state, state->current_frame);
1141 		} else {
1142 			// If not playing, we check at least, if the current frame lies
1143 			// within the interval [0, max_frame_count).
1144 			if (state->current_frame >= state->max_frame_count)
1145 				state->current_frame = state->max_frame_count - 1;
1146 			if (state->current_frame < 0)
1147 				state->current_frame = 0;
1148 		}
1149 		state->range_index = _RangeFrameForFrame(state, state->current_frame);
1150 		state->activation_frame = activationFrame;
1151 		fStates.AddItem(state);
1152 PrintState(state);
1153 TRACE("_PushState: state count: %ld\n", fStates.CountItems());
1154 		// push a new speed info
1155 		SpeedInfo* speedInfo = new SpeedInfo(*_LastSpeedInfo());
1156 		if (playingDirection == 0)
1157 			speedInfo->speed = 1.0;
1158 		else
1159 			speedInfo->speed = speedInfo->set_speed;
1160 		speedInfo->activation_frame = state->activation_frame;
1161 		_PushSpeedInfo(speedInfo);
1162 		// If the new state is a playing state and looping is turned off,
1163 		// determine when playing shall stop.
1164 		if (playingDirection != 0 && !state->looping_enabled) {
1165 			int64 startFrame, endFrame, frameCount;
1166 			_GetPlayingBoundsFor(state, startFrame, endFrame, frameCount);
1167 			if (playingDirection == -1)
1168 				swap(startFrame, endFrame);
1169 			// If we shall stop at the frame we start, set the current frame
1170 			// to the beginning of the range.
1171 			// We have to take care, since this state may equal the one
1172 			// before (or probably differs in just one (unimportant)
1173 			// parameter). This happens for instance, if the user changes the
1174 			// data or start/end frame... while playing. In this case setting
1175 			// the current frame to the start frame is unwanted. Therefore
1176 			// we check whether the previous state was intended to stop
1177 			// at the activation frame of this state.
1178 			if (oldStopPlayingFrame != state->activation_frame
1179 				&& state->current_frame == endFrame && frameCount > 1) {
1180 				state->current_frame = startFrame;
1181 				state->range_index
1182 					= _RangeFrameForFrame(state, state->current_frame);
1183 			}
1184 			if (playingDirection == 1) {	// forward
1185 				fStopPlayingFrame = state->activation_frame
1186 									+ frameCount - state->range_index - 1;
1187 			} else {						// backwards
1188 				fStopPlayingFrame = state->activation_frame
1189 									+ state->range_index;
1190 			}
1191 			_CheckStopPlaying();
1192 		}
1193 	}
1194 TRACE("PlaybackManager::_PushState() done\n");
1195 }
1196 
1197 
1198 /*!	Removes and deletes all states that are obsolete, that is which stopped
1199 	being active at or before the current audio and video time. */
1200 void
1201 PlaybackManager::_UpdateStates()
1202 {
1203 //	int32 firstActive = min(_IndexForTime(fCurrentAudioTime),
1204 //							_IndexForTime(fCurrentVideoTime));
1205 	// Performance time should always be the least one.
1206 	int32 firstActive = _IndexForTime(fPerformanceTime);
1207 //TRACE("firstActive: %ld, numStates: %ld\n", firstActive, fStates.CountItems());
1208 	for (int32 i = 0; i < firstActive; i++)
1209 		delete _StateAt(i);
1210 	if (firstActive > 0)
1211 {
1212 		fStates.RemoveItems(0, firstActive);
1213 TRACE("_UpdateStates: states removed: %ld, state count: %ld\n",
1214 firstActive, fStates.CountItems());
1215 }
1216 }
1217 
1218 
1219 /*!	Returns the index of the state, that is active at frame /frame/.
1220 	The index of the first frame (0) is returned, if /frame/ is even less
1221 	than the frame at which this state becomes active. */
1222 int32
1223 PlaybackManager::_IndexForFrame(int64 frame) const
1224 {
1225 	int32 index = 0;
1226 	PlayingState* state;
1227 	while (((state = _StateAt(index + 1))) && state->activation_frame <= frame)
1228 		index++;
1229 	return index;
1230 }
1231 
1232 /*!	Returns the index of the state, that is active at time /time/.
1233 	The index of the first frame (0) is returned, if /time/ is even less
1234 	than the time at which this state becomes active. */
1235 int32
1236 PlaybackManager::_IndexForTime(bigtime_t time) const
1237 {
1238 	return _IndexForFrame(FrameForTime(time));
1239 }
1240 
1241 
1242 //! Returns the state that reflects the most recent changes.
1243 PlaybackManager::PlayingState*
1244 PlaybackManager::_LastState() const
1245 {
1246 	return _StateAt(fStates.CountItems() - 1);
1247 }
1248 
1249 
1250 PlaybackManager::PlayingState*
1251 PlaybackManager::_StateAt(int32 index) const
1252 {
1253 	return (PlayingState*)fStates.ItemAt(index);
1254 }
1255 
1256 
1257 PlaybackManager::PlayingState*
1258 PlaybackManager::_StateAtFrame(int64 frame) const
1259 {
1260 	return _StateAt(_IndexForFrame(frame));
1261 }
1262 
1263 
1264 PlaybackManager::PlayingState*
1265 PlaybackManager::_StateAtTime(bigtime_t time) const
1266 {
1267 	return _StateAt(_IndexForTime(time));
1268 }
1269 
1270 
1271 int32
1272 PlaybackManager::_PlayingDirectionFor(int32 playingMode)
1273 {
1274 	int32 direction = 0;
1275 	switch (playingMode) {
1276 		case MODE_PLAYING_FORWARD:
1277 			direction = 1;
1278 			break;
1279 		case MODE_PLAYING_BACKWARD:
1280 			direction = -1;
1281 			break;
1282 		case MODE_PLAYING_PAUSED_FORWARD:
1283 		case MODE_PLAYING_PAUSED_BACKWARD:
1284 			break;
1285 	}
1286 	return direction;
1287 }
1288 
1289 
1290 int32
1291 PlaybackManager::_PlayingDirectionFor(PlayingState* state)
1292 {
1293 	return _PlayingDirectionFor(state->play_mode);
1294 }
1295 
1296 
1297 /*!	Returns the Playlist frame range that bounds the playing range of a given
1298 	state.
1299 	\a startFrame is the lower and \a endFrame the upper bound of the range,
1300 	and \a frameCount the number of frames in the range; it is guaranteed to
1301 	be >= 1, even for an empty selection. */
1302 void
1303 PlaybackManager::_GetPlayingBoundsFor(PlayingState* state, int64& startFrame,
1304 	int64& endFrame, int64& frameCount)
1305 {
1306 	switch (state->loop_mode) {
1307 		case LOOPING_ALL:
1308 			startFrame = 0;
1309 			endFrame = max(startFrame, state->frame_count - 1);
1310 			frameCount = endFrame - startFrame + 1;
1311 			break;
1312 		case LOOPING_RANGE:
1313 			startFrame = state->start_frame;
1314 			endFrame = state->end_frame;
1315 			frameCount = endFrame - startFrame + 1;
1316 			break;
1317 //		case LOOPING_SELECTION:
1318 //			if (!state->selection.IsEmpty()) {
1319 //				startFrame = state->selection.FirstIndex();
1320 //				endFrame = state->selection.LastIndex();
1321 //				frameCount = state->selection.CountIndices();
1322 //TRACE("  LOOPING_SELECTION: %lld - %lld (%lld)\n", startFrame, endFrame,
1323 //frameCount);
1324 //			} else {
1325 //				startFrame = state->current_frame;
1326 //				endFrame = state->current_frame;
1327 //				frameCount = 1;
1328 //			}
1329 //			break;
1330 		case LOOPING_VISIBLE:
1331 			startFrame = state->first_visible_frame;
1332 			endFrame = state->last_visible_frame;
1333 			frameCount = endFrame - startFrame + 1;
1334 			break;
1335 	}
1336 }
1337 
1338 
1339 /*!	Returns the frame at which playing would start for a given playing
1340 	state. If the playing mode for the supplied state specifies a stopped
1341 	or undefined mode, the result is the state's current frame. */
1342 int64
1343 PlaybackManager::_PlayingStartFrameFor(PlayingState* state)
1344 {
1345 	int64 startFrame, endFrame, frameCount, frame = 0;
1346 	_GetPlayingBoundsFor(state, startFrame, endFrame, frameCount);
1347 	switch (_PlayingDirectionFor(state)) {
1348 		case -1:
1349 			frame = endFrame;
1350 			break;
1351 		case 1:
1352 			frame = startFrame;
1353 			break;
1354 		default:
1355 			frame = state->current_frame;
1356 			break;
1357 	}
1358 	return frame;
1359 }
1360 
1361 
1362 /*!	Returns the frame at which playing would end for a given playing
1363 	state. If the playing mode for the supplied state specifies a stopped
1364 	or undefined mode, the result is the state's current frame. */
1365 int64
1366 PlaybackManager::_PlayingEndFrameFor(PlayingState* state)
1367 {
1368 	int64 startFrame, endFrame, frameCount, frame = 0;
1369 	_GetPlayingBoundsFor(state, startFrame, endFrame, frameCount);
1370 	switch (_PlayingDirectionFor(state)) {
1371 		case -1:
1372 			frame = startFrame;
1373 			break;
1374 		case 1:
1375 			frame = endFrame;
1376 			break;
1377 		default:
1378 			frame = state->current_frame;
1379 			break;
1380 	}
1381 	return frame;
1382 }
1383 
1384 
1385 /*!	Returns the index that the supplied frame has within a playing range.
1386 	If the state specifies a not-playing mode, 0 is returned. The supplied
1387 	frame has to lie within the bounds of the playing range, but it doesn't
1388 	need to be contained, e.g. if the range is not contiguous. In this case
1389 	the index of the next frame within the range (in playing direction) is
1390 	returned. */
1391 int64
1392 PlaybackManager::_RangeFrameForFrame(PlayingState* state, int64 frame)
1393 {
1394 TRACE("PlaybackManager::_RangeFrameForFrame(%lld)\n", frame);
1395 	int64 startFrame, endFrame, frameCount;
1396 	int64 index = 0;
1397 	_GetPlayingBoundsFor(state, startFrame, endFrame, frameCount);
1398 TRACE("  start frame: %lld, end frame: %lld, frame count: %lld\n",
1399 startFrame, endFrame, frameCount);
1400 	switch (state->loop_mode) {
1401 		case LOOPING_ALL:
1402 		case LOOPING_RANGE:
1403 		case LOOPING_VISIBLE:
1404 			index = frame - startFrame;
1405 			break;
1406 	}
1407 TRACE("PlaybackManager::_RangeFrameForFrame() done: %lld\n", index);
1408 	return index;
1409 }
1410 
1411 
1412 /*!	Returns the Playlist frame for a playing range index. /index/ doesn't need
1413 	to be in the playing range -- it is mapped into. */
1414 int64
1415 PlaybackManager::_FrameForRangeFrame(PlayingState* state, int64 index)
1416 {
1417 TRACE("PlaybackManager::_FrameForRangeFrame(%lld)\n", index);
1418 	int64 startFrame, endFrame, frameCount;
1419 	_GetPlayingBoundsFor(state, startFrame, endFrame, frameCount);
1420 TRACE("  frame range: %lld - %lld, count: %lld\n", startFrame, endFrame,
1421 frameCount);
1422 	// map the index into the index interval of the playing range
1423 	if (frameCount > 1)
1424 		index = (index % frameCount + frameCount) % frameCount;
1425 
1426 	// get the frame for the index
1427 	int32 frame = startFrame;
1428 	switch (state->loop_mode) {
1429 		case LOOPING_ALL:
1430 		case LOOPING_RANGE:
1431 		case LOOPING_VISIBLE:
1432 			frame = startFrame + index;
1433 			break;
1434 	}
1435 TRACE("PlaybackManager::_FrameForRangeFrame() done: %ld\n", frame);
1436 	return frame;
1437 }
1438 
1439 
1440 /*!	Given an arbitrary Playlist frame this function returns the next frame within
1441 	the playing range for the supplied playing state. */
1442 int64
1443 PlaybackManager::_NextFrameInRange(PlayingState* state, int64 frame)
1444 {
1445 	int64 newFrame = frame;
1446 	int64 startFrame, endFrame, frameCount;
1447 	_GetPlayingBoundsFor(state, startFrame, endFrame, frameCount);
1448 	if (frame < startFrame || frame > endFrame)
1449 		newFrame = _PlayingStartFrameFor(state);
1450 	else {
1451 		int64 index = _RangeFrameForFrame(state, frame);
1452 		newFrame = _FrameForRangeFrame(state, index);
1453 		if (newFrame > frame && _PlayingDirectionFor(state) == -1)
1454 			newFrame = _FrameForRangeFrame(state, index - 1);
1455 	}
1456 	return newFrame;
1457 }
1458 
1459 
1460 void
1461 PlaybackManager::_PushSpeedInfo(SpeedInfo* info)
1462 {
1463 	if (info) {
1464 		// check the last state
1465 		if (SpeedInfo* lastSpeed = _LastSpeedInfo()) {
1466 			// set the activation time
1467 			info->activation_time = TimeForFrame(info->activation_frame);
1468 			// Remove the last state, if it won't be activated.
1469 			if (lastSpeed->activation_frame == info->activation_frame) {
1470 //fprintf(stderr, "  replacing last speed info\n");
1471 				fSpeeds.RemoveItem(lastSpeed);
1472 				delete lastSpeed;
1473 			}
1474 		}
1475 		fSpeeds.AddItem(info);
1476 //fprintf(stderr, "  speed info pushed: activation frame: %lld, "
1477 //"activation time: %lld, speed: %f\n", info->activation_frame,
1478 //info->activation_time, info->speed);
1479 		// ...
1480 	}
1481 }
1482 
1483 
1484 PlaybackManager::SpeedInfo*
1485 PlaybackManager::_LastSpeedInfo() const
1486 {
1487 	return (SpeedInfo*)fSpeeds.ItemAt(fSpeeds.CountItems() - 1);
1488 }
1489 
1490 
1491 PlaybackManager::SpeedInfo*
1492 PlaybackManager::_SpeedInfoAt(int32 index) const
1493 {
1494 	return (SpeedInfo*)fSpeeds.ItemAt(index);
1495 }
1496 
1497 
1498 int32
1499 PlaybackManager::_SpeedInfoIndexForFrame(int64 frame) const
1500 {
1501 	int32 index = 0;
1502 	SpeedInfo* info;
1503 	while (((info = _SpeedInfoAt(index + 1)))
1504 		   && info->activation_frame <= frame) {
1505 		index++;
1506 	}
1507 //fprintf(stderr, "PlaybackManager::_SpeedInfoIndexForFrame(%lld): %ld\n",
1508 //frame, index);
1509 	return index;
1510 }
1511 
1512 
1513 int32
1514 PlaybackManager::_SpeedInfoIndexForTime(bigtime_t time) const
1515 {
1516 	int32 index = 0;
1517 	SpeedInfo* info;
1518 	while (((info = _SpeedInfoAt(index + 1)))
1519 		   && info->activation_time <= time) {
1520 		index++;
1521 	}
1522 //fprintf(stderr, "PlaybackManager::_SpeedInfoIndexForTime(%lld): %ld\n",
1523 //time, index);
1524 	return index;
1525 }
1526 
1527 
1528 PlaybackManager::SpeedInfo*
1529 PlaybackManager::_SpeedInfoForFrame(int64 frame) const
1530 {
1531 	return _SpeedInfoAt(_SpeedInfoIndexForFrame(frame));
1532 }
1533 
1534 
1535 PlaybackManager::SpeedInfo*
1536 PlaybackManager::_SpeedInfoForTime(bigtime_t time) const
1537 {
1538 	return _SpeedInfoAt(_SpeedInfoIndexForTime(time));
1539 }
1540 
1541 
1542 void
1543 PlaybackManager::_UpdateSpeedInfos()
1544 {
1545 	int32 firstActive = _SpeedInfoIndexForTime(fPerformanceTime);
1546 	for (int32 i = 0; i < firstActive; i++)
1547 		delete _SpeedInfoAt(i);
1548 	if (firstActive > 0)
1549 		fSpeeds.RemoveItems(0, firstActive);
1550 //fprintf(stderr, "  speed infos 0 - %ld removed\n", firstActive);
1551 }
1552 
1553 
1554 /*!	Returns the end time for the last video frame that the video and the
1555 	audio producer both have completely processed. */
1556 bigtime_t
1557 PlaybackManager::_TimeForLastFrame() const
1558 {
1559 	if (fNoAudio)
1560 		return TimeForFrame(FrameForTime(fCurrentVideoTime));
1561 
1562 	return TimeForFrame(FrameForTime(min((bigtime_t)fCurrentAudioTime,
1563 										 (bigtime_t)fCurrentVideoTime)));
1564 }
1565 
1566 
1567 /*!	Returns the start time for the first video frame for which
1568 	neither the video nor the audio producer have fetched data. */
1569 bigtime_t
1570 PlaybackManager::_TimeForNextFrame() const
1571 {
1572 	return TimeForFrame(NextFrame());
1573 }
1574 
1575 
1576 void
1577 PlaybackManager::_CheckStopPlaying()
1578 {
1579 //printf("_CheckStopPlaying() - %lld, next: %lld\n", fStopPlayingFrame, NextFrame());
1580 	if (fStopPlayingFrame > 0 && fStopPlayingFrame <= NextFrame()) {
1581 		StopPlaying();
1582 		NotifyStopFrameReached();
1583 	}
1584 }
1585 
1586