File Coverage

blib/lib/MIDI/Stream/Tables.pm
Criterion Covered Total %
statement 37 39 94.8
branch 20 24 83.3
condition 8 10 80.0
subroutine 14 16 87.5
pod 11 11 100.0
total 90 100 90.0


line stmt bran cond sub pod time code
1 13     13   82 use strict;
  13         25  
  13         461  
2 13     13   58 use warnings;
  13         23  
  13         1012  
3             package MIDI::Stream::Tables;
4              
5             # ABSTRACT: MIDI 1.0 look up tables and utility functions
6              
7              
8             our $VERSION = '0.005';
9              
10 13     13   5917 use parent 'Exporter';
  13         4222  
  13         74  
11              
12             my %status; my %fstatus;
13             BEGIN {
14 13     13   1460 %status = (
15             note_off => 0x80,
16             note_on => 0x90,
17             polytouch => 0xa0,
18             control_change => 0xb0,
19             program_change => 0xc0,
20             aftertouch => 0xd0,
21             pitch_bend => 0xe0,
22             );
23              
24 13         7154 %fstatus = (
25             sysex => 0xf0,
26             timecode => 0xf1,
27             song_position => 0xf2,
28             song_select => 0xf3,
29             tune_request => 0xf6,
30             eox => 0xf7,
31             clock => 0xf8,
32             start => 0xfa,
33             continue => 0xfb,
34             stop => 0xfc,
35             active_sensing => 0xfe,
36             system_reset => 0xff,
37             );
38             }
39              
40             my %name = reverse %status;
41             my %fname = reverse %fstatus;
42              
43             # Not exactly ecstatic about this pattern, but an alternative has yet to
44             # occur to me. One alternative I thought about was having objects push their
45             # ordered keys to an array in the top-level class, but this means you need
46             # an object instance to get the ordering:
47             # $class->from_hashref( $event )->as_arrayref seemed a little perverse.
48             my $event_keys = {
49             note_off => [qw/ channel note velocity /],
50             note_on => [qw/ channel note velocity /],
51             polytouch => [qw/ channel note pressure /],
52             control_change => [qw/ channel control value /],
53             program_change => [qw/ channel program /],
54             aftertouch => [qw/ channel pressure /],
55             pitch_bend => [qw/ channel value /],
56             song_position => [qw/ position /],
57             song_select => [qw/ song /],
58             timecode => [qw/ byte /],
59             sysex => [qw/ msg /],
60             };
61              
62              
63             sub keys_for {
64 235   100 235 1 1008 $event_keys->{ $_[0] } // [];
65             }
66              
67              
68             sub status_name {
69 165   66 165 1 967 $name{ $_[0] & 0xf0 } // $fname{ $_[0] };
70             }
71              
72              
73 136   66 136 1 675 sub status_byte { $status{ $_[0] } // $fstatus{ $_[0] } }
74              
75              
76 191     191 1 563 sub is_realtime { $_[0] > 0xf7 }
77              
78              
79 0     0 1 0 sub is_single_byte { $_[0] > 0xf5 }
80              
81              
82             sub message_length {
83 332     332 1 668 my ( $status ) = @_;
84              
85 332 100 100     1026 $status = status_byte( $status ) if ( length $status // 0 ) > 3;
86              
87 332 100       715 return 0 unless $status;
88              
89 329 50       683 return 0 if $status < 0x80;
90 329 100       994 return 3 if $status < 0xc0;
91 104 100       270 return 2 if $status < 0xe0;
92 74 100       213 return 3 if $status < 0xf0;
93              
94 28 100       69 return 0 if $status == 0xf0;
95 20 50       49 return 2 if $status == 0xf1;
96 20 100       75 return 3 if $status == 0xf2;
97 5 50       12 return 2 if $status == 0xf3;
98              
99 5 50       17 return 1 if $status > 0xf5;
100             }
101              
102              
103 0     0 1 0 sub is_status_byte { $_[0] & 0x80 }
104              
105              
106 179 100   179 1 669 sub has_channel { ( length $_[0] > 3 ? status_byte( $_[0] ) : $_[0] ) < 0xf0 }
107              
108              
109             sub is_cc {
110 171     171 1 747 ( $_[0] & 0xf0 ) == 0xb0;
111             }
112              
113              
114             sub combine_bytes {
115 31     31 1 106 my ( $lsb, $msb ) = @_;
116 31         213 $msb << 7 | $lsb & 0x7f;
117             }
118              
119              
120             sub split_bytes {
121 24     24 1 50 my ( $value ) = @_;
122 24         94 ( $value & 0x7f, $value >> 7 & 0x7f );
123             }
124              
125             use constant {
126 13         205 map { $_ => $_ } ( keys %fstatus, keys %status )
  247         4588  
127 13     13   148 };
  13         81  
128              
129             our @EXPORT_OK = qw/
130             keys_for
131             status_name
132             status_byte
133             status_chr
134             is_realtime
135             is_single_byte
136             message_length
137             is_status_byte
138             has_channel
139             is_cc
140             is_pitch_bend
141             combine_bytes
142             split_bytes
143             /;
144             push @EXPORT_OK, keys %status, keys %fstatus;
145             our %EXPORT_TAGS = ( all => \@EXPORT_OK );
146              
147             __END__