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