File Coverage

blib/lib/Time/Slideshow.pm
Criterion Covered Total %
statement 53 58 91.3
branch 5 8 62.5
condition 7 11 63.6
subroutine 12 14 85.7
pod 10 11 90.9
total 87 102 85.2


line stmt bran cond sub pod time code
1             package Time::Slideshow;
2 2     2   53596 use strict;
  2         4  
  2         126  
3 2     2   1702 use Time::HiRes qw(time);
  2         4102  
  2         16  
4 2     2   431 use vars qw($VERSION);
  2         13  
  2         1792  
5             $VERSION= '0.02';
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             duration => 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 76 my( $class, %options )= @_;
76 19   50     89 $options{ starttime }||= 0;
77 19   100     40 $options{ shuffle }||= 0;
78 19   50     34 $options{ duration }||= 45;
79 19   50     40 $options{ slides }||= [];
80              
81 19         30 my $self= bless \%options, $class;
82              
83 19         34 $self
84             }
85              
86 0     0 1 0 sub starttime { $_[0]->{ starttime }};
87 2639     2639 1 3329 sub duration { $_[0]->{ duration }};
88 1030     1030 1 2148 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 13 my( $self, $time )= @_;
118 8         18 $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 3 my( $self, $time )= @_;
135 2         8 $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 10 my( $self, $time )= @_;
154 3 50       7 if( ! defined $time ) {
155 0         0 $time= Time::HiRes::time;
156             };
157 3         6 $time -= $self->{ starttime };
158             #warn sprintf "At %d, window %d, in %d", $time, $self->duration, $self->duration - ($time % $self->duration);
159 3         7 $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 2179 my( $self, $time )= @_;
174 874 50       1280 $time = time
175             unless defined $time;
176 874         758 $time -= $self->{ starttime };
177              
178 874         642 my $items= 0+@{ $self->slides };
  874         995  
179 874 50       1310 return undef unless $items;
180              
181 874         951 my $index= int (($time % ($self->duration * $items)) / $self->duration);
182              
183 874 100 66     2866 if( $self->{ shuffle } and $items > 1) {
184             # Apply a permutation
185             # We don't want permutation 0 and 1 as 0 and 1 are identical
186             # Step is the distance between items
187             # Incr is the shift we need to apply per round
188             # 0 1 2 3 4 5
189             # Shuffle 1: Step = 2
190             # 0 1 2
191             # 3 4 5
192             # Shuffle 2: Step = 3
193             # 0 1
194             # 2 3
195             # 4 5
196             #
197             # For 10 items, Permutation 8 (Step size 3)
198             # 0 1 2 3 4 5 6 7 8 9
199             # 0 1 2 3
200             # 4 5 6
201             # 7 8 9
202              
203 869         1026 my $permutation= (int ($time / ($self->duration * $items)) % ($items-2));
204 869         699 my $step= $permutation;
205 869         609 $step++; # Increment between 1 and $items-1
206              
207 869         922 my $gcd= gcd( $items, $step );
208 869         803 my $round_size= int( $items / $gcd );
209              
210             # The round counts from $round_size towards 1 to shuffle the order of loops a bit
211 869         881 my $round= $round_size - int($index / $round_size) +1;
212              
213 869         685 my $old_index= $index;
214 869         923 $index= ((($old_index *$step) % $items) + $round ) % $items;
215              
216             #warn sprintf "Old Index % 2d Step: % 2d Round: % 2d Round size: % 2d Index: % 2d GCD: % 2d\n",
217             # $old_index, $step, $round, $round_size, $index, $gcd;
218             };
219              
220 874         1219 $index
221             };
222              
223             =head2 C<< $show->slide_at $index >>
224              
225             my $slide_count= @{ $show->slides };
226             for my $slide ( 0..$slide_count-1 ) {
227             print "Slide $slide: ", $show->slide_at( $slide ), "\n";
228             };
229              
230             Returns the name of the slide at the given index.
231              
232             =cut
233              
234             sub slide_at {
235 10     10 1 11 my( $self, $index )= @_;
236 10         9 $index %= 0+@{ $self->slides };
  10         12  
237 10         13 $self->slides->[ $index ]
238             }
239              
240             # Some math utilities to find the gcd, for the "random" shuffles
241             sub gcd {
242 869     869 0 768 my( $gcd )= shift;
243 869         914 for my $d ( @_ ) {
244 869         665 my $div= $d;
245 869         1191 while( $div ) {
246 1485         2653 ($gcd, $div) = ($div, $gcd % $div);
247             };
248             };
249 869         858 $gcd
250             }
251              
252             1;
253              
254             __END__