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