File Coverage

blib/lib/Net/OpenSoundControl.pm
Criterion Covered Total %
statement 128 139 92.0
branch 33 44 75.0
condition 3 6 50.0
subroutine 17 23 73.9
pod 8 8 100.0
total 189 220 85.9


line stmt bran cond sub pod time code
1             package Net::OpenSoundControl;
2              
3 5     5   176372 use 5.006;
  5         24  
  5         227  
4 5     5   31 use strict;
  5         10  
  5         276  
5 5     5   27 use warnings;
  5         32  
  5         628  
6              
7             our @ISA = qw();
8              
9             our $VERSION = '0.05';
10             our $NTP_ADJUSTMENT = 2208988800;
11             our $IMMEDIATE_FRACTION = '0' x 31 . '1';
12              
13 5     5   30 use Config;
  5         12  
  5         899  
14              
15             # initialize functions
16              
17             BEGIN {
18              
19             # Note: the little endian functions also work on a big endian
20             # platform, but they are less efficient.
21              
22 5 50   5   5317 if ($Config{byteorder} eq '1234') {
    50          
23 0         0 *toFloat = *_toFloat_littleEndian;
24 0         0 *fromFloat = *_fromFloat_littleEndian;
25             } elsif ($Config{byteorder} eq '12345678') {
26 5         22258 *toFloat = *_toFloat_64bit;
27 5         10070 *fromFloat = *_fromFloat_64bit;
28             } else {
29 0         0 *toFloat = *_toFloat_bigEndian;
30 0         0 *fromFloat = *_fromFloat_bigEndian;
31             }
32             }
33              
34             =head1 NAME
35              
36             Net::OpenSoundControl - OpenSound Control client and server implementation
37              
38             =head1 SYNOPSIS
39              
40             See L and L for the synopsis.
41              
42             =head1 DESCRIPTION
43              
44             OpenSound Control ("OSC") is a protocol for communication among computers,
45             sound synthesizers, and other multimedia devices that is optimized for
46             modern networking technology.
47              
48             http://www.cnmat.berkeley.edu/OpenSoundControl
49              
50             This suite of modules provides an implementation of the protocol in Perl,
51             according to version 1.0 (March 26, 2002) of the specification.
52              
53             To actually create an OSC client or server, take a look at L and L. This module only provides several helper functions. Normally, there shouldn't be a need for you to use this module directly.
54              
55             Please also see the F directory in this distribution, especially if
56             you are not very familiar with references.
57              
58             =head1 DATA FORMAT
59              
60             OSC data is represented in a form closely related to the original binary format.
61              
62             =head2 MESSAGES
63              
64             A message is an array reference containing an OSC address followed by zero or more pairs of type identifiers and data. Examples:
65              
66             ['/Fader', f, 0.2]
67              
68             ['/Synth/XY', i, 10, i, 200]
69              
70             =head2 BUNDLES
71              
72             A bundle is an array reference that contains the bundle identifier
73             C<#bundle> and a timestamp, followed by zero or more
74             messages. Examples:
75              
76             ['#bundle', time() + 0.1, ['/Pitch', 'f', rand(1)]
77              
78             ['#bundle', time() + 2, ['/Slider', 'f', $s],
79             ['/Synth/XY', 'i', $x, 'i', $y]
80              
81             Note that the time should be expressed in seconds since 1st January
82             1970, which is what time() returns on UNIX systems. You can pass it
83             a floating point time, the Time::HiRes module will help you find the
84             current time with sub-second accuracy.
85              
86             A timestamp with the value "0" carries the special meaning of "execute immediately". Example:
87              
88             ['#bundle', 0, ['/Pitch', 'f', rand(1)]
89              
90             =head1 FUNCTIONS
91              
92             =over
93              
94             =item protocol()
95              
96             Returns information about the version of the OSC protocol implemented. Currently C.
97              
98             =cut
99              
100             sub protocol {
101 0     0 1 0 return "OSC/1.0";
102             }
103              
104             =item types()
105              
106             Returns information about the data types implemented. Currently C (blobs, floats, ints and strings).
107              
108             =cut
109              
110             sub types {
111 0     0 1 0 return "bfis";
112             }
113              
114             our $_match_OSCString = <
115             (?:[^\0]{4})* # zero or more blocks of four non-ASCII-NULs
116             (?:
117             [^\0]{3}\0 | # block padded with ASCII-NULs
118             [^\0]{2}\0{2} |
119             [^\0]{1}\0{3} |
120             \0{4}
121             )
122             EOT
123              
124             =item decode($data)
125              
126             Decodes binary OSC message or bundle data into a Perl data structure
127              
128             =cut
129              
130             sub decode {
131 19     19 1 21 local $_;
132 19         32 my ($data) = @_;
133              
134 19 50       567 return undef unless $data;
135              
136 19 100       62 if ($data =~ /^\#bundle/) {
137 7         18 return _decode_bundle($data);
138             } else {
139 12         26 return _decode_message($data);
140             }
141             }
142              
143             # format: ['#bundle', timestamp, [element1...], [element2...], ...]
144             sub _decode_bundle {
145 7     7   11 my ($data) = @_;
146              
147 7         11 my $msg = [];
148              
149             # Get OSC target address
150 7 50       157 $data =~ /^($_match_OSCString)(.*)/x || return undef;
151 7         19 $data = $2; # discard '#bundle'
152 7         14 push @$msg, '#bundle';
153              
154 7         30 my ($secs, $frac) = unpack('NB32', $data);
155 7         17 substr($data, 0, 8) = '';
156 7 100 66     27 if ($secs eq 0 && $frac == $IMMEDIATE_FRACTION) {
157              
158             # 'immediately'
159 1         1 push @$msg, 0;
160             } else {
161 6         14 push @$msg, $secs - $NTP_ADJUSTMENT + _bin2frac($frac);
162             }
163              
164 7         20 while (length($data) > 0) {
165 7         14 my $len = unpack('N', $data);
166 7         9 substr($data, 0, 4) = '';
167 7         21 push @$msg, decode(substr($data, 0, $len));
168 7         25 substr($data, 0, $len) = '';
169             }
170              
171 7         24 return $msg;
172             }
173              
174             # format: [addr, type, data, type, data, ...]
175             sub _decode_message {
176 12     12   24 local $_;
177 12         18 my ($data) = @_;
178              
179 12         21 my $msg = [];
180              
181             # Get OSC target address
182 12 50       237 $data =~ /^($_match_OSCString)(.*)/x || return undef;
183 12         31 $data = $2;
184              
185 12         50 (my $addr = $1) =~ s/\0//g;
186 12         578 push @$msg, $addr;
187              
188             # Get type string
189 12 50       186 $data =~ /^($_match_OSCString)(.*)/x || return undef;
190 12         43 $data = $2;
191 12         67 (my $types = $1) =~ s/(^,|\0)//g;
192              
193 12         38 foreach (split //, $types) {
194              
195             # push type identifier
196 15         26 push @$msg, $_;
197              
198 15         24 SWITCH: for ($_) {
199 15 100       38 /i/ && do {
200 9         19 push @$msg, unpack('N', $data);
201              
202             # remove this integer from remaining data
203 9         14 substr($data, 0, 4) = '';
204 9         24 last SWITCH;
205             };
206 6 100       15 /f/ && do {
207 2         7 push @$msg, fromFloat($data);
208              
209             # push @$msg, unpack('f', $data);
210             # remove this float from remaining data
211 2         4 substr($data, 0, 4) = '';
212 2         5 last SWITCH;
213             };
214 4 100       12 /s/ && do {
215 2 50       95 $data =~ /^($_match_OSCString)(.*)/x || return undef;
216 2         6 $data = $2;
217 2         8 (my $s = $1) =~ s/\0//g;
218 2         5 push @$msg, $s;
219 2         6 last SWITCH;
220             };
221 2 50       6 /b/ && do {
222 2         7 my $len = unpack('N', $data);
223 2         5 substr($data, 0, 4) = '';
224              
225 2         5 push @$msg, substr($data, 0, $len);
226              
227             # blob is zero-padded
228 2         4 substr($data, 0, $len + (4 - $len % 4)) = '';
229              
230 2         5 last SWITCH;
231             };
232              
233 0         0 return undef;
234             }
235             }
236              
237 12         48 return $msg;
238             }
239              
240             =item encode($data)
241              
242             Encodes OSC messages or bundles into their binary representation
243              
244             =cut
245              
246             sub encode {
247 21     21 1 7606 local $_;
248 21         31 my ($data) = @_;
249 21         33 my $idx = 0;
250              
251 21 50 33     123 return undef unless $data && ref($data);
252              
253 21         25 my $msg;
254              
255 21 100       64 if ($data->[0] eq '#bundle') {
256 7         19 my $msg = toString($data->[$idx++]);
257              
258 7         21 $msg .= toTimetag($data->[$idx++]);
259              
260 7         24 while ($idx <= $#$data) {
261 7         27 my $e = encode($data->[$idx++]);
262 7         15 $msg .= toInt(length($e)) . $e;
263             }
264              
265 7         26 return $msg;
266             }
267              
268 14         47 $msg = toString($data->[$idx++]);
269 14         27 my ($types, $payload) = ('', '');
270              
271             # '<' because we need _two_ elements (type tag, data)
272 14         35 while ($idx < $#$data) {
273 21         241 my ($t, $d) = ($data->[$idx++], $data->[$idx++]);
274 21 100       49 $t eq 'i' && do { $types .= 'i'; $payload .= toInt($d) };
  11         19  
  11         24  
275 21 100       50 $t eq 'f' && do { $types .= 'f'; $payload .= toFloat($d) };
  5         8  
  5         13  
276 21 100       49 $t eq 's' && do { $types .= 's'; $payload .= toString($d) };
  3         5  
  3         8  
277 21 100       73 $t eq 'b' && do { $types .= 'b'; $payload .= toBlob($d) };
  2         3  
  2         7  
278             }
279              
280 14         39 return $msg . toString(",$types") . $payload;
281             }
282              
283             =item toInt($n)
284              
285             Returns the binary representation of an integer in OSC format
286              
287             =cut
288              
289             sub toInt {
290 22     22 1 91 return pack('N', $_[0]);
291             }
292              
293             =item fromFloat($n)
294              
295             Converts the binary representation of a floating point value in OSC format
296             into a Perl value. (There are no other "from..." functions since the code
297             for that is directly embedded into the decode functons for speed, but this
298             one is separate since it depends on the endianness of the system it's
299             running on).
300              
301             =cut
302              
303             sub _fromFloat_bigEndian {
304 0     0   0 return unpack('f', $_[0]);
305             }
306              
307             sub _fromFloat_littleEndian {
308 0     0   0 return unpack('f', pack('N', unpack('l', $_[0])));
309              
310             # return unpack('f', reverse $_[0]);
311             }
312              
313             sub _fromFloat_64bit {
314 5     5   18 my $t =
315             substr($_[0], 3, 1) . substr($_[0], 2, 1) . substr($_[0], 1, 1) .
316             substr($_[0], 0, 1);
317 5         27 return unpack('f', $t);
318             }
319              
320             =item toFloat($n)
321              
322             Returns the binary representation of a floating point value in OSC format
323              
324             =cut
325              
326             sub _toFloat_bigEndian {
327 0     0   0 return pack("f", $_[0]);
328             }
329              
330             sub _toFloat_littleEndian {
331              
332             # return reverse pack('f', $_[0]);
333 0     0   0 return pack('N', unpack('l', pack('f', $_[0])));
334             }
335              
336             sub _toFloat_64bit {
337 10     10   40 my $t = pack("f", $_[0]);
338             return
339 10         47 substr($t, 3, 1) . substr($t, 2, 1) . substr($t, 1, 1) . substr($t, 0, 1);
340             }
341              
342             =item toString($str)
343              
344             Returns the binary representation of a string in OSC format
345              
346             =cut
347              
348             sub toString {
349 40     40 1 53 my ($str) = @_;
350              
351 40 50       81 return undef unless defined $str;
352              
353             # use bytes for UNICODE compatibility
354 40         166 return $str . "\0" x (4 - length($str) % 4);
355             }
356              
357             =item toBlob($d)
358              
359             Returns the binary representation of a BLOB value in OSC format
360              
361             =cut
362              
363             sub toBlob {
364 2     2 1 4 my ($d) = @_;
365              
366 2 50       7 return undef unless defined $d;
367              
368 2         6 return toInt(length($d)) . toString($d);
369             }
370              
371             =item toTimetag($d)
372              
373             Returns the binary representation of a TIMETAG value in OSC format.
374              
375             =cut
376              
377             sub toTimetag {
378 7     7 1 9 my $timetag = shift;
379              
380 7 100       18 if ($timetag == 0) {
381              
382             # 'immediately'
383 1         8 return (pack("NB32", 0, $IMMEDIATE_FRACTION));
384             } else {
385 6         8 my $secs = int($timetag);
386 6         7 my $frac = $timetag - $secs;
387              
388 6         15 return (pack("NB32", $secs + $NTP_ADJUSTMENT, _frac2bin($frac)));
389             }
390             }
391              
392             # NTP conversion code, see e.g. Net::NTP
393             sub _frac2bin {
394 6     6   7 my $bin = '';
395 6         8 my $frac = shift;
396 6         12 while (length($bin) < 32) {
397 192         199 $bin = $bin . int($frac * 2);
398 192         3500 $frac = ($frac * 2) - (int($frac * 2));
399             }
400 6         40 return $bin;
401             }
402              
403             sub _bin2frac {
404 6     6   62 my @bin = split '', shift;
405 6         15 my $frac = 0;
406 6         16 while (@bin) {
407 192         411 $frac = ($frac + pop @bin) / 2;
408             }
409 6         15 return $frac;
410             }
411              
412             1;
413              
414             =back
415              
416              
417             =head1 BUGS
418              
419             Doesn't work with Unicode data. Remember to C if you use
420             Unicode Strings.
421              
422             =head1 SEE ALSO
423              
424             Hacking Perl in Nightclubs at L
425              
426             The OpenSoundControl website at L
427              
428             L
429              
430             L
431              
432             =head1 AUTHOR
433              
434             Christian Renz, Ecrenz @ web42.comE
435              
436             Timestamp code lifted from Net::NTP.
437              
438             Test against specification by Alex (yaxu.org).
439              
440             =head1 COPYRIGHT AND LICENSE
441              
442             Copyright 2004-2005 by Christian Renz Ecrenz @ web42.comE
443              
444             This library is free software; you can redistribute it and/or modify
445             it under the same terms as Perl itself.
446              
447             =cut
448