.\" Automatically generated by Pod::Man 2.27 (Pod::Simple 3.28) .\" .\" Standard preamble: .\" ======================================================================== .de Sp \" Vertical space (when we can't use .PP) .if t .sp .5v .if n .sp .. .de Vb \" Begin verbatim text .ft CW .nf .ne \\$1 .. .de Ve \" End verbatim text .ft R .fi .. .\" Set up some character translations and predefined strings. \*(-- will .\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left .\" double quote, and \*(R" will give a right double quote. \*(C+ will .\" give a nicer C++. Capital omega is used to do unbreakable dashes and .\" therefore won't be available. \*(C` and \*(C' expand to `' in nroff, .\" nothing in troff, for use with C<>. .tr \(*W- .ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p' .ie n \{\ . ds -- \(*W- . ds PI pi . if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch . if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch . ds L" "" . ds R" "" . ds C` "" . ds C' "" 'br\} .el\{\ . ds -- \|\(em\| . ds PI \(*p . ds L" `` . ds R" '' . ds C` . ds C' 'br\} .\" .\" Escape single quotes in literal strings from groff's Unicode transform. .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" .\" If the F register is turned on, we'll generate index entries on stderr for .\" titles (.TH), headers (.SH), subsections (.SS), items (.Ip), and index .\" entries marked with X<> in POD. Of course, you'll have to process the .\" output yourself in some meaningful fashion. .\" .\" Avoid warning from groff about undefined register 'F'. .de IX .. .nr rF 0 .if \n(.g .if rF .nr rF 1 .if (\n(rF:(\n(.g==0)) \{ . if \nF \{ . de IX . tm Index:\\$1\t\\n%\t"\\$2" .. . if !\nF==2 \{ . nr % 0 . nr F 2 . \} . \} .\} .rr rF .\" .\" Accent mark definitions (@(#)ms.acc 1.5 88/02/08 SMI; from UCB 4.2). .\" Fear. Run. Save yourself. No user-serviceable parts. . \" fudge factors for nroff and troff .if n \{\ . ds #H 0 . ds #V .8m . ds #F .3m . ds #[ \f1 . ds #] \fP .\} .if t \{\ . ds #H ((1u-(\\\\n(.fu%2u))*.13m) . ds #V .6m . ds #F 0 . ds #[ \& . ds #] \& .\} . \" simple accents for nroff and troff .if n \{\ . ds ' \& . ds ` \& . ds ^ \& . ds , \& . ds ~ ~ . ds / .\} .if t \{\ . ds ' \\k:\h'-(\\n(.wu*8/10-\*(#H)'\'\h"|\\n:u" . ds ` \\k:\h'-(\\n(.wu*8/10-\*(#H)'\`\h'|\\n:u' . ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'^\h'|\\n:u' . ds , \\k:\h'-(\\n(.wu*8/10)',\h'|\\n:u' . ds ~ \\k:\h'-(\\n(.wu-\*(#H-.1m)'~\h'|\\n:u' . ds / \\k:\h'-(\\n(.wu*8/10-\*(#H)'\z\(sl\h'|\\n:u' .\} . \" troff and (daisy-wheel) nroff accents .ds : \\k:\h'-(\\n(.wu*8/10-\*(#H+.1m+\*(#F)'\v'-\*(#V'\z.\h'.2m+\*(#F'.\h'|\\n:u'\v'\*(#V' .ds 8 \h'\*(#H'\(*b\h'-\*(#H' .ds o \\k:\h'-(\\n(.wu+\w'\(de'u-\*(#H)/2u'\v'-.3n'\*(#[\z\(de\v'.3n'\h'|\\n:u'\*(#] .ds d- \h'\*(#H'\(pd\h'-\w'~'u'\v'-.25m'\f2\(hy\fP\v'.25m'\h'-\*(#H' .ds D- D\\k:\h'-\w'D'u'\v'-.11m'\z\(hy\v'.11m'\h'|\\n:u' .ds th \*(#[\v'.3m'\s+1I\s-1\v'-.3m'\h'-(\w'I'u*2/3)'\s-1o\s+1\*(#] .ds Th \*(#[\s+2I\s-2\h'-\w'I'u*3/5'\v'-.3m'o\v'.3m'\*(#] .ds ae a\h'-(\w'a'u*4/10)'e .ds Ae A\h'-(\w'A'u*4/10)'E . \" corrections for vroff .if v .ds ~ \\k:\h'-(\\n(.wu*9/10-\*(#H)'\s-2\u~\d\s+2\h'|\\n:u' .if v .ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'\v'-.4m'^\v'.4m'\h'|\\n:u' . \" for low resolution devices (crt and lpr) .if \n(.H>23 .if \n(.V>19 \ \{\ . ds : e . ds 8 ss . ds o a . ds d- d\h'-1'\(ga . ds D- D\h'-1'\(hy . ds th \o'bp' . ds Th \o'LP' . ds ae ae . ds Ae AE .\} .rm #[ #] #H #V #F C .\" ======================================================================== .\" .IX Title "Protocol::OSC 3pm" .TH Protocol::OSC 3pm "2014-04-28" "perl v5.18.2" "User Contributed Perl Documentation" .\" For nroff, turn off justification. Always turn off hyphenation; it makes .\" way too many mistakes in technical documents. .if n .ad l .nh .SH "NAME" Protocol::OSC \- Open Sound Control v1.1 implementation .SH "SYNOPSIS" .IX Header "SYNOPSIS" .Vb 3 \& my $osc = Protocol::OSC\->new; \& my $data = $osc\->message(qw(/echo isf 3 ping 3.14)); # pack \& my $packet = $osc\->parse($data); # parse \& \& $osc\->actions\->{$path} = $code_ref; # add callback \& $packet\->process($data, $scheduler_coderef); # parse and execute callbacks .Ve .SH "DESCRIPTION" .IX Header "DESCRIPTION" This module implements (de)coding and processing of \s-1OSC\s0 packets according the specification. It's pure Perl implementation, yet faster than Net::LibLO (~4x) and Net::OpenSoundControl (~2x). Also it provides connection agnostic interface and path matching and type tagging according \s-1OSC\s0 v1 specification ( and v1.1 ) .SH "CONSTRUCTOR" .IX Header "CONSTRUCTOR" .SS "new( ?actions => {}, ?scheduler => sub {...} )" .IX Subsection "new( ?actions => {}, ?scheduler => sub {...} )" Creates Protocol::Instance with optional \*(L"actions\*(R" argument which is hashref of pairs: \fIpath => coderef\fR and optional default scheduler for \*(L"process\*(R" method \- see below. .SH "METHODS" .IX Header "METHODS" .ie n .SS "message($path, $typetag, ?@args)" .el .SS "message($path, \f(CW$typetag\fP, ?@args)" .IX Subsection "message($path, $typetag, ?@args)" Encodes message to packet. Typetag supports these OSC-types: \fIifstdbht\fR. Everything else (like \fI\s-1TFNI\s0\fR) will not affect packing of \fB\f(CB@args\fB\fR. Alias: \fBmsg\fR .ie n .SS "bundle(undef || $unix_time, [@message_or_bundle], ...)" .el .SS "bundle(undef || \f(CW$unix_time\fP, [@message_or_bundle], ...)" .IX Subsection "bundle(undef || $unix_time, [@message_or_bundle], ...)" Encodes bundle to packet. Pack several \s-1OSC\s0 messages/bundles to a bundle. .SS "parse($data)" .IX Subsection "parse($data)" Parses \s-1OSC\s0 packet data. Returns \s-1OSC\s0 message/bundle. OSC-message is a blessed arrayref \fB[$path, \f(CB$type\fB, \f(CB@args\fB]\fR with corresponding methods \fBpath\fR, \fBtype\fR, \fBargs\fR. OSC-bundle is a blessed arrayref \fB[$time, \f(CB@packets\fB]\fR with corresponding methods \fBtime\fR, \fBpackets\fR .SS "process($data, ?$scheduler_cb)" .IX Subsection "process($data, ?$scheduler_cb)" Parses \s-1OSC\s0 packet/data and process messages in it. It will call matched actions through \fB\f(CB$scheduler_cb\fB\fR which is just \f(CW\*(C`sub { $_[0]\->(splice @_,1) }\*(C'\fR by default(or specified in constructor). Arguments to scheduler are \fB\f(CB$action_coderef\fB, \f(CB$time\fB, \f(CB$action_path\fB, \f(CB$osc_msg\fB, ?@osc_bundles\fR. .SS "actions" .IX Subsection "actions" Returns hashref of actions: \fBpath => coderef\fR pairs. One could modify this hashref or use methods below. .ie n .SS "set_cb($path, $cb)" .el .SS "set_cb($path, \f(CW$cb\fP)" .IX Subsection "set_cb($path, $cb)" Set coderef \fB\f(CB$cb\fB\fR to \fBactions\fR .SS "del_cb($path)" .IX Subsection "del_cb($path)" Remove coderef at \fB\f(CB$path\fB\fR from \fBactions\fR at \fB\f(CB$path\fB\fR .SS "match($osc_path_pattern)" .IX Subsection "match($osc_path_pattern)" Returns mathched actions in form of list of arrayrefs \fB[$path, \f(CB$coderef\fB]\fR .SS "time2tag($unix_time)" .IX Subsection "time2tag($unix_time)" Converts (fractional) unix epoch time to \s-1NTP\s0 timestamp, which is list of \fB($seconds_since_1900_01_01, \f(CB$int32_fraction_parts_of_second\fB)\fR. If \fB\f(CB$unix_time\fB\fR is undef then (0,1) is returned which means immediate execution by \s-1OSC\s0 specs. .ie n .SS "tag2time($ntp_time, $fraction_of_sec)" .el .SS "tag2time($ntp_time, \f(CW$fraction_of_sec\fP)" .IX Subsection "tag2time($ntp_time, $fraction_of_sec)" Reverse of previous. .SS "to_stream($data)" .IX Subsection "to_stream($data)" Packs raw \s-1OSC\s0 data for (tcp) streaming. .SS "from_steam($buf)" .IX Subsection "from_steam($buf)" Returns list of raw \s-1OSC\s0 data packets in \fB\f(CB$buf\fB\fR from stream buffer and residue of \fB\f(CB$buf\fB\fR. .SH "EXAMPLES" .IX Header "EXAMPLES" .SS "Sending" .IX Subsection "Sending" make packet .PP .Vb 4 \& my $data = $osc\->message(my @specs = qw(/echo isf 3 ping 3.14)); \& # or \& use Time::HiRes \*(Aqtime\*(Aq; \& my $data $osc\->bundle(time, [@specs], [@specs2], ...); .Ve .PP via \s-1UDP\s0 .PP .Vb 2 \& my $udp = IO::Socket::INET\->new( PeerAddr => \*(Aqlocalhost\*(Aq, PeerPort => \*(Aq57120\*(Aq, Proto => \*(Aqudp\*(Aq, Type => SOCK_DGRAM) || die $!; \& $udp\->send($data); .Ve .PP via \s-1TCP\s0 .PP .Vb 2 \& my $tcp = IO::Socket::INET\->new( PeerAddr => \*(Aqlocalhost\*(Aq, PeerPort => \*(Aq57120\*(Aq, Proto => \*(Aqtcp\*(Aq, Type => SOCK_STREAM) || die $!; \& $tcp\->send($osc\->to_stream($data)); .Ve .SS "Receiving" .IX Subsection "Receiving" \&\s-1UDP\s0 .PP .Vb 3 \& my $in = IO::Socket::INET\->new( qw(LocalAddr localhost LocalPort), $port, qw(Proto udp Type), SOCK_DGRAM ) || die $!; \& $in\->recv(my $packet, $in\->sockopt(SO_RCVBUF)); \& my $p = $osc\->parse($packet); .Ve .PP \&\s-1TCP\s0 .PP .Vb 3 \& $in = IO::Socket::INET\->new( qw(LocalAddr localhost LocalPort), $port, qw(Proto tcp Type), SOCK_STREAM, qw(Listen 1 Reuse 1) ) || die $!; \& $in\->accept\->recv(my $packet, $in\->sockopt(SO_RCVBUF)); \& my $p = $osc\->parse(($osc\->from_stream($packet))[0]); .Ve .SS "Dispatching" .IX Subsection "Dispatching" .Vb 10 \& $osc\->set_cb(\*(Aq/echo\*(Aq, sub { \& my ($at_time, $path, $msg, @maybe_bundles) = @_; \& say $at_time if $at_time; # time of parent bundle if message comes from bundle(s) \& say $path; # matched path \& say $msg\->path; # path pattern of OSC message \& say $msg\->type; # typetag \& say @{$msg\->args}; # message arguments \& map { # all bundles from which $msg comes (from inner to outer) \& say $_\->time; # time of bundle \& say $_\->packets; # array of messages/bundle in bundle \& } @maybe_bundles; \& }); \& ... \& $osc\->process($osc\->parse($data)); .Ve .SS "Ping-Pong using AnyEvent::Handle::UDP" .IX Subsection "Ping-Pong using AnyEvent::Handle::UDP" .Vb 11 \& use AnyEvent::Handle::UDP; \& my $udp_handle = AnyEvent::Handle::UDP\->new( \& bind => [0, $port], \& on_recv => sub { \& my ($data, $handle, $client_addr) = @_; \& my $msg = $osc\->parse($data); \& say $msg\->path; \& $handle\->push_send($osc\->message(qw(/pong i), ($msg\->args)[0]), $client_addr) if $msg\->path eq \*(Aq/ping\*(Aq; \& } \& ); \& $udp_handle\->push_send($osc\->message(qw(/ping i 3)), [0, $port]); .Ve .SS "Benchmarks" .IX Subsection "Benchmarks" encode .PP .Vb 5 \& cmpthese \-1, { \& \*(AqNet::LibLO::Message\*(Aq => sub { Net::LibLO::Message\->new(qw(isf 3 laaaa 3.0)) }, \& \*(AqProtocol::OSC\*(Aq => sub { $protocol\->message(qw(/echo isf 3 laaaa 3.0)) }, \& \*(AqNet::OpenSoundControl\*(Aq => sub { Net::OpenSoundControl::encode([qw(/echo i 3 s laaaa f 3.0)]) } \& }; \& \& ... \& \& Rate Net::LibLO::Message Net::OpenSoundControl Protocol::OSC \& Net::LibLO::Message 20479/s \-\- \-7% \-51% \& Net::OpenSoundControl 21920/s 7% \-\- \-48% \& Protocol::OSC 41754/s 104% 90% \-\- .Ve .PP decode .PP .Vb 4 \& cmpthese \-1, { \& \*(AqProtocol::OSC\*(Aq => sub { $protocol\->parse($data) }, \& \*(AqNet::OpenSoundControl\*(Aq => sub { Net::OpenSoundControl::decode($data) } \& }; \& \& Rate Net::OpenSoundControl Protocol::OSC \& Net::OpenSoundControl 1630/s \-\- \-65% \& Protocol::OSC 4654/s 186% \-\- .Ve .SH "SUPPORT" .IX Header "SUPPORT" .IP "\(bu" 4 GitHub .Sp .IP "\(bu" 4 Search MetaCPAN .Sp .SH "AUTHOR" .IX Header "AUTHOR" Yegor Korablev .SH "LICENSE" .IX Header "LICENSE" This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself. .SH "TODO" .IX Header "TODO" more docs, examples and tests.. as usual ) .SH "SEE ALSO" .IX Header "SEE ALSO" Net::LibLO, Net::OpenSoundControl, AnyEvent::Handle::UDP