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