File Coverage

blib/lib/Audio/Xmpcr/Serial.pm
Criterion Covered Total %
statement 9 97 9.2
branch 0 46 0.0
condition 0 13 0.0
subroutine 3 16 18.7
pod 0 10 0.0
total 12 182 6.5


line stmt bran cond sub pod time code
1             # -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
2             # Audio::Xmpcr::Serial
3             # Copyright Paul Bournival 2003
4             # -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
5              
6             package Audio::Xmpcr::Serial;
7              
8             $VERSION="1.02";
9              
10 1     1   4 use strict;
  1         2  
  1         31  
11 1     1   1369 use Device::SerialPort;
  1         67133  
  1         69  
12 1     1   1176 use bytes;
  1         10  
  1         6  
13              
14              
15             sub new {
16 0     0 0   my($class,$port)=@_;
17 0           my $self={};
18 0           $self->{port}=$port;
19 0   0       $self->{sdev} = new Device::SerialPort ("$self->{port}")
20             || die "Can't open USB Port! ($self->{port} $!\n";
21 0           $self->{sdev}->baudrate(9600);
22 0           $self->{sdev}->parity('none');
23 0           $self->{sdev}->databits(8);
24 0           $self->{sdev}->stopbits(1);
25              
26 0           $self->{_state}={
27             power => 0,
28             channel => 0,
29             radioId => "",
30             channels => [],
31             };
32              
33 0           bless $self,$class;
34             }
35              
36             # -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
37             # a general send/receive method.
38             # if called in a scalar context, returns STATUS: undef=success || errmsg=failed
39             # if called in an array context, returns (STATUS (above),PORTREADSTR)
40             # -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
41             sub _doop {
42 0     0     my($self,$op,$cmd,$wcnt,$rcnt)=@_;
43 0           my($readstr,$retval,$cnt)=("",undef,0);
44 0 0 0       return("$op: Power isn't on!")
45             if $cmd ne "5AA500050010101001EDED" and ! $self->{_state}{power};
46 0           $self->{sdev}->write(pack("H*",$cmd));
47 0 0         $self->{sdev}->read_const_time($wcnt) if defined $wcnt;
48 0 0         if ($rcnt) {
49 0           while($cnt<$rcnt) {
50 0           ($cnt,$readstr)=$self->{sdev}->read($rcnt);
51 0           $readstr=join("",unpack("H*",$readstr));
52             }
53 0 0         $retval=substr($readstr,0,6) eq "5aa500" ? undef : "$op failed";
54 0 0         $self->{_state}{radioId}=pack("H*",substr($readstr, 46, 16))
55             if $cmd eq "5AA500050010101001EDED";
56             }
57 0 0         wantarray ? ($retval,$readstr) : $retval;
58             }
59              
60             # -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
61             # turn on/off power
62             # -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
63             sub power {
64 0     0 0   my($self,$status)=@_;
65 0 0         defined($status) || die "power called improperly\n";
66 0 0         my $res=$status eq "on" ?
67             $self->_doop("power on","5AA500050010101001EDED",100,40) :
68             $self->_doop("power off","5AA500020100EDED",0,0);
69              
70 0 0         $self->{_state}{power}=($status eq "on" ? 1 : 0) if ! $res;
    0          
71              
72             # if powering up, load the channels from the device.
73 0 0 0       if ($status eq "on" and ! $res) {
74 0           sleep(8);
75 0           $self->_buildChannelList;
76 0           $self->setchannel(1);
77             }
78 0           $res;
79             }
80              
81             # -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
82             # turn on/off mute
83             # -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
84             sub mute {
85 0     0 0   my($self,$status)=@_;
86 0 0         defined($status) || die "mute called improperly\n";
87 0 0         $self->_doop("mute $status",$status eq "on" ?
88             "5AA500021301EDED" : "5AA500021300EDED", 0,10);
89             }
90              
91             # -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
92             # change channel
93             # -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
94             sub setchannel {
95 0     0 0   my($self,$chan)=@_;
96 0 0         defined($chan) || die "setchannel called improperly\n";
97 0           $self->{_state}{channel}=$chan;
98 0           $self->_doop("setchannel $chan",
99 0           "5AA500061002@{[sprintf('%02X',$chan)]}000001EDED",3000,12);
100             }
101              
102             # -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
103             # list 1 or all channels
104             # -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
105             sub list {
106 0     0 0   my($self,$chan)=@_;
107 0           my(@ret,$err,$res);
108 0 0         my @ch=$chan ? ($chan) : @{ $self->{_state}{channels} };
  0            
109 0           for my $ch (@ch) {
110 0           ($err,$res)=$self->_doop("channel $ch info",
111 0           "5AA500042508@{[sprintf('%02X',$ch)]}00EDED",100,83);
112 0 0         last if $err;
113 0           push(@ret,{
114             NUM => $ch,
115             NAME => $self->_prune(pack("H*", substr($res, 20, 32))),
116             CAT => $self->_prune(pack("H*", substr($res, 52, 32))),
117             ARTIST => $self->_prune(pack("H*", substr($res, 88, 32))),
118             SONG => $self->_prune(pack("H*", substr($res, 122, 32))),
119             });
120             }
121 0 0         $chan ? $ret[0] : @ret;
122             }
123              
124             # -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
125             # remove extra spaces and control characters
126             # -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
127             sub _prune {
128 0     0     my($self,$str)=@_;
129 0           $str =~ s/[^[:graph:] ]//gs;
130 0           $str =~ s/^\s+//;
131 0           $str =~ s/\s+$//;
132 0           $str =~ s#/#-#g; # embedded forward slashes - yuk!
133 0           $str;
134             }
135              
136             # -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
137             # builds a list of channels on the radio
138             # this should probably write the list to a file somewhere...
139             # -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
140             # to be used at power up only!!!
141             sub _buildChannelList {
142 0     0     my($self)=@_;
143 0           my($ch,$lasterr,$res)=("00",undef);
144             # NOTE: PAULB GET RID OF ME LATER! - for debugging only!!!!!!!!!!!!
145             # $self->{_state}{channels}=[1,4,5,6,7,8,9,10,11,12,13,14,15,20,21,22,23,24,25,26,27,28,29,30,31,32,40,41,42,43,44,45,46,47,48,50,51,52,60,61,62,63,64,65,66,67,70,71,72,73,74,75,76,80,81,82,83,90,91,92,93,94,100,101,102,103,104,110,112,113,115,116,121,122,123,124,125,127,129,130,131,132,134,140,141,142,143,144,150,151,152,161,162,163,164,165,166,168,169,170,171];
146             #return;
147 0           $self->{_state}{channels}=[];
148              
149             # build a cache file if none is present
150 0 0         if (! -f "$ENV{HOME}/.xmpcrd-cache") {
151 0 0         open(F,">$ENV{HOME}/.xmpcrd-cache") or die "Can't write cache file: $!";
152 0           while(1) {
153 0           ($lasterr,$res)=$self->_doop("channel $ch info",
154             "5AA500042509${ch}00EDED",100,83);
155 0           $ch=substr($res,14,2);
156 0 0 0       last if $ch eq "00" or $lasterr;
157 0           print F hex($ch) . "\n";
158             }
159 0           close(F);
160             }
161              
162 0           my($line);
163 0 0         open(F,"$ENV{HOME}/.xmpcrd-cache") or die "Can't read cache file: $!";
164 0           while($line=) {
165 0           chop $line;
166 0           push(@{ $self->{_state}{channels} },$line);
  0            
167             }
168 0           close(F);
169            
170 0           $lasterr;
171             }
172              
173             # -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
174             # obtain general radio status
175             # -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
176             sub status {
177 0     0 0   my($self)=@_;
178            
179 0           my %cur;
180 0 0         if ($self->{_state}{power}) {
181 0           %cur=%{ $self->list($self->{_state}{channel}) };
  0            
182 0           $cur{RADIOID}= $self->{_state}{radioId};
183 0           my($err,$ti)=$self->_doop("tech info","5AA5000143EDED",100,32);
184 0   0       $cur{ANTENNA}=int(1+(substr($ti, 16,2) || 0)*33.3);
185             }
186 0 0         $cur{POWER}=$self->{_state}{power} ? "on" : "off";
187 0           %cur;
188             }
189              
190             # -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
191             # event support (i.e., song changing)
192             # -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
193             sub events {
194 0     0 0   die "Whoops! events aren't supported on the serial interface!\n";
195             }
196             sub processEvents {
197 0     0 0   die "Whoops! events aren't supported on the serial interface!\n";
198             }
199             sub eventFd {
200 0     0 0   die "Whoops! events aren't supported on the serial interface!\n";
201             }
202             sub forcelock {
203 0     0 0   die "Whoops! locks aren't supported on the serial interface!\n";
204             }
205              
206              
207              
208             1;