File Coverage

blib/lib/Music/Factory.pm
Criterion Covered Total %
statement 47 47 100.0
branch 8 8 100.0
condition 3 3 100.0
subroutine 8 8 100.0
pod n/a
total 66 66 100.0


line stmt bran cond sub pod time code
1             # -*- Perl -*-
2             #
3             # some factory class for your music factory class
4              
5             package Music::Factory;
6             our $VERSION = '0.01';
7 3     3   885230 use 5.26.0;
  3         15  
8 3     3   22 use warnings;
  3         22  
  3         267  
9 3     3   2396 use Object::Pad 0.66;
  3         50696  
  3         244  
10              
11             # assembly line calls a generator object to fill an array of events up
12             # to a particular duration (musical section)
13             class Music::Factory::AssemblyLine {
14             field $events :param; # MIDI events (array ref)
15             field $gen :param; # generator object
16             field $maxlen :param; # maxiumum duration to generate
17              
18 7     7   1370 method update ( $length = $maxlen ) {
  7         22  
  7         13  
  7         46  
19 7         35 $gen->reset; # NOTE may be removed
20 7         20 my ( $epoch, $done, $overflow ) = ( 0, 0 );
21 7         12 while (1) {
22 9         35 my ( $dur, $elist ) = $gen->update( $epoch, $length );
23 8         65 $epoch += $dur;
24 8 100       31 if ( $epoch > $length ) {
    100          
25             # kick overflows back to the caller to figure out, if
26             # they care. generators can also do calculations to
27             # avoid this situation, if they care
28 1         3 $epoch -= $dur;
29 1         2 $overflow = [ $dur, $elist ];
30 1         3 last;
31             } elsif ( $epoch == $length ) {
32 5         8 $done = 1;
33             }
34 7 100 100     37 push $events->@*, $elist->@* if defined $elist and $elist->@*;
35 7 100       21 last if $done;
36             }
37 6         21 return $epoch, $events, $overflow;
38             }
39             }
40              
41             # generator objects are called by an assembly line and generate events
42             # (an array reference) of some length (hopefully of the same duration as
43             # the events generated)
44             class Music::Factory::Generator {
45             # NOTE reset() might be gotten rid of as a generator update() can
46             # probably run similar code when $epoch == 0 but that would make the
47             # update function a bit more complicated. if reset is common it
48             # probably should be a distinct method, otherwise not?
49 8     8   18 method reset () { }
  8         18  
50              
51 1     1   2 method update ( $epoch, $maxlen ) {
  1         2  
  1         2  
  1         1  
  1         19  
52 1         12 die "need a better implementation";
53             }
54             }
55              
56             # a note of a particular duration; the pitch and velocity are done by
57             # function callbacks
58             class Music::Factory::Note :isa(Music::Factory::Generator) {
59             field $chan :param = 0; # MIDI channel
60             field $pitch :param; # callback
61             field $velo :param; # callback
62             field $duration :param = 96;
63              
64 4     4   6 method update ( $epoch, $length ) {
  4         8  
  4         7  
  4         5  
  4         5  
65 4         5 my $dur = $duration;
66             # how to truncate to teh remaining length instead of
67             # overflowing. whether this makes sense depends on what a
68             # generator is doing. for example, if $dur is "too small" one
69             # may instead want to insert silence of that amount
70             #my $when = $epoch + $dur;
71             #if ( $when > $length ) {
72             # $dur -= $when - $length;
73             # return $dur, [ [ note_off => $dur, $chan, 0, 0 ] ];
74             #}
75 4         9 my $p = $pitch->($epoch);
76 4         17 return $duration,
77             [ [ note_on => 0, $chan, $p, $velo->($epoch) ],
78             [ note_off => $dur, $chan, $p, 0 ],
79             ];
80             }
81             }
82              
83             # silence to fill the maximum duration given
84             class Music::Factory::Rest :isa(Music::Factory::Generator) {
85              
86 2     2   2 method update ( $epoch, $length ) {
  2         5  
  2         2  
  2         3  
  2         2  
87 2         8 return $length, [ [ marker => $length, 'silence' ] ];
88             }
89             }
90              
91             1;
92             __END__