File Coverage

blib/lib/Audio/MadJACK.pm
Criterion Covered Total %
statement 4 6 66.6
branch n/a
condition n/a
subroutine 2 2 100.0
pod n/a
total 6 8 75.0


line stmt bran cond sub pod time code
1             package Audio::MadJACK;
2              
3             ################
4             #
5             # MadJACK: perl control interface
6             #
7             # Copyright 2005 Nicholas J. Humfrey
8             #
9              
10 1     1   26460 use Carp;
  1         3  
  1         99  
11              
12 1     1   3783 use Net::LibLO;
  0            
  0            
13             use strict;
14              
15             use vars qw/$VERSION $ATTEMPTS/;
16              
17             $VERSION="0.04";
18             $ATTEMPTS=5;
19              
20              
21             sub new {
22             my $class = shift;
23            
24             croak( "Missing MadJACK server port or URL" ) if (scalar(@_)<1);
25              
26             # Bless the hash into an object
27             my $self = {
28             pong => 0,
29             state => undef,
30             version => undef,
31             error => undef,
32             duration => undef,
33             position => undef,
34             filepath => undef
35             };
36             bless $self, $class;
37              
38             # Create address of MadJACK server
39             $self->{addr} = new Net::LibLO::Address( @_ );
40             if (!defined $self->{addr}) {
41             carp("Error creating Net::LibLO::Address");
42             return undef;
43             }
44            
45             # Create new LibLO instance
46             $self->{lo} = new Net::LibLO();
47             if (!defined $self->{lo}) {
48             carp("Error creating Net::LibLO");
49             return undef;
50             }
51            
52             # Add reply handlers
53             $self->{lo}->add_method( '/deck/state', 's', \&_state_handler, $self );
54             $self->{lo}->add_method( '/deck/duration', 'd', \&_duration_handler, $self );
55             $self->{lo}->add_method( '/deck/position', 'd', \&_position_handler, $self );
56             $self->{lo}->add_method( '/deck/filepath', 's', \&_filepath_handler, $self );
57             $self->{lo}->add_method( '/version', 'ss', \&_version_handler, $self );
58             $self->{lo}->add_method( '/error', 's', \&_error_handler, $self );
59             $self->{lo}->add_method( '/pong', '', \&_pong_handler, $self );
60            
61             # Check MadJACK server is there
62             if (!$self->ping()) {
63             carp("MadJACK server is not responding");
64             return undef;
65             }
66              
67             return $self;
68             }
69              
70             sub load {
71             my $self=shift;
72             my ($filepath) = @_;
73             return $self->_send( '/deck/load', 'LOADING|READY|ERROR', 's', $filepath);
74             }
75              
76              
77             sub play {
78             my $self=shift;
79             return $self->_send( '/deck/play', 'PLAYING');
80             }
81              
82             sub pause {
83             my $self=shift;
84             return $self->_send( '/deck/pause', 'PAUSED');
85             }
86              
87             sub stop {
88             my $self=shift;
89             return $self->_send( '/deck/stop', 'STOPPED');
90             }
91              
92             sub cue {
93             my $self=shift;
94             my ($cuepoint) = @_;
95             if (defined $cuepoint) {
96             return $self->_send( '/deck/cue', 'LOADING|READY', 'd', $cuepoint);
97             } else {
98             return $self->_send( '/deck/cue', 'LOADING|READY');
99             }
100             }
101              
102             sub eject {
103             my $self=shift;
104             return $self->_send( '/deck/eject', 'EMPTY');
105             }
106              
107              
108             sub get_state {
109             my $self=shift;
110             $self->{state} = undef;
111             $self->_wait_reply( '/deck/get_state' );
112             return $self->{state};
113             }
114              
115             sub _state_handler {
116             my ($serv, $mesg, $path, $typespec, $userdata, @params) = @_;
117             $userdata->{state}=$params[0];
118             return 0; # Success
119             }
120              
121             sub get_duration {
122             my $self=shift;
123             $self->{duration} = undef;
124             $self->_wait_reply( '/deck/get_duration' );
125             return $self->{duration};
126             }
127              
128             sub _duration_handler {
129             my ($serv, $mesg, $path, $typespec, $userdata, @params) = @_;
130             $userdata->{duration}=$params[0];
131             return 0; # Success
132             }
133              
134             sub get_position {
135             my $self=shift;
136             $self->{postion} = undef;
137             $self->_wait_reply( '/deck/get_position' );
138             return $self->{position};
139             }
140              
141             sub _position_handler {
142             my ($serv, $mesg, $path, $typespec, $userdata, @params) = @_;
143             $userdata->{position}=$params[0];
144             return 0; # Success
145             }
146              
147             sub get_filepath {
148             my $self=shift;
149             $self->{filepath} = undef;
150             $self->_wait_reply( '/deck/get_filepath' );
151             return $self->{filepath};
152             }
153              
154             sub _filepath_handler {
155             my ($serv, $mesg, $path, $typespec, $userdata, @params) = @_;
156             $userdata->{filepath}=$params[0];
157             return 0; # Success
158             }
159              
160             sub get_version {
161             my $self=shift;
162             $self->{version} = undef;
163             $self->_wait_reply( '/get_version' );
164             return $self->{version};
165             }
166              
167             sub _version_handler {
168             my ($serv, $mesg, $path, $typespec, $userdata, @params) = @_;
169             $userdata->{version}=$params[0].'/'.$params[1];
170             return 0; # Success
171             }
172              
173              
174             sub get_error {
175             my $self=shift;
176             $self->{error} = undef;
177             $self->_wait_reply( '/get_error' );
178             return $self->{error};
179             }
180              
181             sub _error_handler {
182             my ($serv, $mesg, $path, $typespec, $userdata, @params) = @_;
183             $userdata->{error}=$params[0];
184             return 0; # Success
185             }
186              
187             sub ping {
188             my $self=shift;
189             $self->{pong} = 0;
190             $self->_wait_reply( '/ping' );
191             return $self->{pong};
192             }
193              
194             sub _pong_handler {
195             my ($serv, $mesg, $path, $typespec, $userdata, @params) = @_;
196             $userdata->{pong}++;
197             return 0; # Success
198             }
199              
200             sub get_url {
201             my $self=shift;
202             return $self->{addr}->get_url();
203             }
204              
205              
206             sub _send {
207             my $self=shift;
208             my ($path, $desired, $typespec, @params) = @_;
209             my $state = undef;
210            
211             # Empty typespec if non specified
212             $typespec = '' unless (defined $typespec);
213            
214             # Try a few times
215             for(1..$ATTEMPTS) {
216             my $result = $self->{lo}->send( $self->{addr}, $path, $typespec, @params );
217             warn "Warning: failed to send '$path' OSC message.\n" if ($result<1);
218              
219             # Check what state the player is in now
220             if (defined $desired) {
221             $state = $self->get_state();
222             last if ($state =~ /^$desired$/i);
223             } else {
224             return 0;
225             }
226             }
227            
228             # Finally return true if we are in desired state
229             if ($state =~ /^$desired$/i) { return 1 }
230             else { return 0 }
231             }
232              
233              
234             sub _wait_reply {
235             my $self=shift;
236             my ($path) = @_;
237             my $bytes = 0;
238            
239             # Throw away any old incoming messages
240             for(1..$ATTEMPTS) { $self->{lo}->recv_noblock( 0 ); }
241              
242             # Try a few times
243             for(1..$ATTEMPTS) {
244            
245             # Send Query
246             my $result = $self->{lo}->send( $self->{addr}, $path, '' );
247             if ($result<1) {
248             warn "Failed to send message ($path): ".$self->{addr}->errstr()."\n";
249             sleep(1);
250             next;
251             }
252              
253             # Wait for reply within one second
254             $bytes = $self->{lo}->recv_noblock( 1000 );
255             if ($bytes<1) {
256             warn "Timed out waiting for reply after one second.\n";
257             } else { last; }
258             }
259            
260             # Failed to get reply ?
261             if ($bytes<1) {
262             warn "Failed to get reply from MadJACK server after $ATTEMPTS attempts.\n";
263             }
264            
265             return $bytes;
266             }
267              
268              
269             1;
270              
271             __END__