File Coverage

blib/lib/Time/Slideshow.pm
Criterion Covered Total %
statement 52 57 91.2
branch 4 6 66.6
condition 7 11 63.6
subroutine 12 14 85.7
pod 10 11 90.9
total 85 99 85.8


line stmt bran cond sub pod time code
1             package Time::Slideshow;
2 3     3   55916 use strict;
  3         7  
  3         457  
3 3     3   3427 use Time::HiRes qw(time);
  3         6281  
  3         16  
4 3     3   593 use vars qw($VERSION);
  3         16  
  3         2446  
5             $VERSION= '0.01';
6              
7             =head1 NAME
8              
9             Time::Slideshow - simple stateless slideshow with a fixed set of images
10              
11             =head1 SYNOPSIS
12              
13             my $slideshow= Time::Slideshow->new(
14             starttime => 0,
15             slides => [ 'picture1.jpg', 'picture2.jpg' ],
16             shuffle => 0, # pseudo-rng
17             display => 45, # show each slide for 45 seconds
18             );
19             my $current_slide= $slideshow->current_slide; # picture1.jpg
20             my $next_slide= $slideshow->next_slide; # picture2.jpg
21             sleep $slideshow->seconds_to_next_slide;
22              
23             =head1 OVERVIEW
24              
25             This module abstracts the inner workings of a slideshow, selecting the
26             next slide to display and calculating the time until the next picture. It
27             is possible to use this object in an asynchronous manner across machines
28             as the complete slide sequence is precalculated from the wallclock time.
29              
30             The module has no methods to advance the slideshow forwards or backwards
31             other than through the passage of time.
32              
33             =head1 METHODS
34              
35             =head2 C<< Time::Slideshow->new %options >>
36              
37             my $slideshow= Time::Slideshow->new(
38             slides => [ glob '~/backgrounds/*.jpg' ],
39             duration => 300, # five minutes per image
40             shuffle => 1, # permute the order of images
41             )
42              
43             Creates a new slideshow object and returns it. The options are
44              
45             =over 4
46              
47             =item B
48              
49             Array reference to the slides. These can be filenames
50             or URLs or whatever else helps your program to find and display
51             the appropriate image.
52              
53             =item B
54              
55             The time how long every image is displayed, in seconds. The default
56             value is 45.
57              
58             =item B
59              
60             If you want the order of the images to be shuffled, pass
61             a true value to this option. See the below discussion
62             on what different orders you can expect from shuffling.
63              
64             =item B
65              
66             If you want to set up a different offset of the start time,
67             pass this option with the epoch value of the start time. Usually,
68             you want to leave this parameter at its default of 0.
69              
70             =back
71              
72             =cut
73              
74             sub new {
75 19     19 1 119 my( $class, %options )= @_;
76 19   50     194 $options{ starttime }||= 0;
77 19   100     69 $options{ shuffle }||= 0;
78 19   50     50 $options{ duration }||= 45;
79 19   50     64 $options{ slides }||= [];
80            
81 19         65 my $self= bless \%options, $class;
82            
83 19         57 $self
84             }
85              
86 0     0 1 0 sub starttime { $_[0]->{ starttime }};
87 2639     2639 1 5835 sub duration { $_[0]->{ duration }};
88 1030     1030 1 4418 sub slides { $_[0]->{ slides }};
89              
90             =head2 C<< $show->add_slide( @slides ) >>
91              
92             $show->add_slide( glob '~/new_backgrounds/*' );
93              
94             If you want to add slides after object
95             creation, you can use this method.
96              
97             =cut
98              
99             sub add_slide {
100 0     0 1 0 my( $self, @slides )= @_;
101 0         0 push @{ $self->slides }, @slides
  0         0  
102             };
103              
104             =head2 C<< $show->current_slide >>
105              
106             print "Now displaying ", $show->current_slide, "\n";
107              
108             Returns the name of the slide that is currently displayed.
109              
110             This method is the central method for your application to
111             get the filename or URL of the image that your application
112             needs to display.
113              
114             =cut
115              
116             sub current_slide {
117 8     8 1 18 my( $self, $time )= @_;
118 8         24 $self->slide_at( $self->current_slide_index( $time ))
119             }
120              
121             =head2 C<< $show->next_slide >>
122              
123             print "Next up is ", $show->next_slide, "\n";
124              
125             Returns the name of the slide that will be displayed after the
126             current slide.
127              
128             You can use this method to preload the image data
129             for the upcoming slide.
130              
131             =cut
132              
133             sub next_slide {
134 2     2 1 5 my( $self, $time )= @_;
135 2         4377 $self->slide_at( $self->current_slide_index( $time ) +1 )
136             }
137              
138             =head2 C<< $show->seconds_to_next_slide >>
139              
140             my $wait= $show->seconds_to_next_slide;
141             sleep $wait;
142              
143             Returns the time remaining to the next slide
144             transition.
145              
146             You can use this method to pause your program or perform
147             other tasks until the next image
148             needs to be displayed.
149              
150             =cut
151              
152             sub seconds_to_next_slide {
153 3     3 1 13 my( $self, $time )= @_;
154 3 50       13 if( ! defined $time ) {
155 0         0 $time= Time::HiRes::time;
156             };
157 3         13 $time -= $self->{ starttime };
158             #warn sprintf "At %d, window %d, in %d", $time, $self->duration, $self->duration - ($time % $self->duration);
159 3         10 $self->duration - ($time % $self->duration)
160             };
161              
162             =head2 C<< $show->current_slide_index >>
163              
164             my $current_slide_index= $show->current_slide_index;
165              
166             This returns the index of the slide that is currently displayed.
167              
168             Most likely, you want to use C<< $show->current_slide >> instead.
169              
170             =cut
171              
172             sub current_slide_index {
173 874     874 1 3330 my( $self, $time )= @_;
174 874 50       1746 $time = time
175             unless defined $time;
176 874         1110 $time -= $self->{ starttime };
177            
178 874         806 my $items= 0+@{ $self->slides };
  874         1307  
179            
180 874         1517 my $index= int (($time % ($self->duration * $items)) / $self->duration);
181            
182 874 100 66     4065 if( $self->{ shuffle } and $items > 1) {
183             # Apply a permutation
184             # We don't want permutation 0 and 1 as 0 and 1 are identical
185             # Step is the distance between items
186             # Incr is the shift we need to apply per round
187             # 0 1 2 3 4 5
188             # Shuffle 1: Step = 2
189             # 0 1 2
190             # 3 4 5
191             # Shuffle 2: Step = 3
192             # 0 1
193             # 2 3
194             # 4 5
195             #
196             # For 10 items, Permutation 8 (Step size 3)
197             # 0 1 2 3 4 5 6 7 8 9
198             # 0 1 2 3
199             # 4 5 6
200             # 7 8 9
201            
202 869         1609 my $permutation= (int ($time / ($self->duration * $items)) % ($items-2));
203 869         943 my $step= $permutation;
204 869         939 $step++; # Increment between 1 and $items-1
205            
206 869         1215 my $gcd= gcd( $items, $step );
207 869         1308 my $round_size= int( $items / $gcd );
208              
209             # The round counts from $round_size towards 1 to shuffle the order of loops a bit
210 869         1172 my $round= $round_size - int($index / $round_size) +1;
211            
212 869         860 my $old_index= $index;
213 869         1407 $index= ((($old_index *$step) % $items) + $round ) % $items;
214              
215             #warn sprintf "Old Index % 2d Step: % 2d Round: % 2d Round size: % 2d Index: % 2d GCD: % 2d\n",
216             # $old_index, $step, $round, $round_size, $index, $gcd;
217             };
218            
219 874         2136 $index
220             };
221              
222             =head2 C<< $show->slide_at $index >>
223              
224             my $slide_count= @{ $show->slides };
225             for my $slide ( 0..$slide_count-1 ) {
226             print "Slide $slide: ", $show->slide_at( $slide ), "\n";
227             };
228              
229             Returns the name of the slide at the given index.
230              
231             =cut
232              
233             sub slide_at {
234 10     10 1 14 my( $self, $index )= @_;
235 10         15 $index %= 0+@{ $self->slides };
  10         16  
236 10         20 $self->slides->[ $index ]
237             }
238              
239             # Some math utilities to find the gcd, for the "random" shuffles
240             sub gcd {
241 869     869 0 996 my( $gcd )= shift;
242 869         1259 for my $d ( @_ ) {
243 869         2132 my $div= $d;
244 869         2037 while( $div ) {
245 1485         4607 ($gcd, $div) = ($div, $gcd % $div);
246             };
247             };
248 869         1491 $gcd
249             }
250              
251             1;
252              
253             __END__