Protocol::OSC(3pm)

πŸ“¦
version 0.09 Β· 2 versions
, in debian bookworm, debian trixie, ubuntu jammy, ubuntu noble

πŸ’– Protocol::OSC β€” the Protocol::OSC(3pm) era πŸ’–

okay bestie, sit down, hydrate, because we're talking about Protocol::OSC β€” that's the Open Sound Control v1.1 implementation and she is literally iconic. this is the user-contributed Perl documentation page, and yes, we're reading it together, no cap. πŸŽ€


✨ NAME ✨

Protocol::OSC β€” Open Sound Control v1.1 implementation. that's it, that's the whole vibe. πŸ¦‹


🌸 SYNOPSIS 🌸

look at her. look at the lewk:

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

pack it, parse it, callback it, process it. the whole lifecycle. πŸ’…


πŸ’œ DESCRIPTION πŸ’œ

so this module does the (de)coding and the processing of OSC packets according to the specification. she's a pure Perl implementation, and β€” not to brag but she IS going to brag β€” she's faster than Net::LibLO and Net::OpenSoundControl (~2x). ate and left no crumbs, fr fr. πŸ”₯

she also gives you a connection agnostic interface plus path matching and type tagging according to the OSC v1 specification <http://opensoundcontrol.org/spec-1_0> (and v1.1 <http://opensoundcontrol.org/spec-1_1>). iykyk. πŸ‘‘


🌈 CONSTRUCTOR 🌈

🧚 new( ?actions => {}, ?scheduler => sub {...} )

Creates Protocol::Instance with an optional "actions" argument β€” that's a hashref of pairs: path => coderef β€” and an optional default scheduler for the "process" method. see below, bestie, we get there. 🫢


πŸ‘‘ METHODS πŸ‘‘

  • message($path, $typetag, ?@args) β€” Encodes a message to a packet. The typetag supports these OSC-types: ifstdbht. Everything else (like TFNI) will not affect packing of @args. Alias: msg. πŸ’Œ
  • bundle(undef || $unix_time, [@message_or_bundle], ...) β€” Encodes a bundle to a packet. Pack several OSC messages/bundles into a bundle. bundling bundle-ception, we love to see it. 🎁
  • parse($data) β€” Parses OSC packet data. Returns OSC message/bundle. An OSC-message is a blessed arrayref [$path, $type, @args] with corresponding methods path, type, args. An OSC-bundle is a blessed arrayref [$time, @packets] with corresponding methods time, packets. ✨
  • process($data, ?$scheduler_cb) β€” Parses OSC packet/data and processes messages in it. It will call matched actions through $scheduler_cb, which is just sub { $_[0]->(splice @_,1) } by default (or whatever you specified in the constructor). Arguments to the scheduler are $action_coderef, $time, $action_path, $osc_msg, ?@osc_bundles. πŸš€
  • actions β€” Returns hashref of actions: path => coderef pairs. One could modify this hashref or use the methods below. your call, bestie. πŸ™ˆ
  • set_cb($path, $cb) β€” Set coderef $cb to actions. πŸ’–
  • del_cb($path) β€” Remove coderef at $path from actions at $path. bye bestie πŸ‘‹
  • match($osc_path_pattern) β€” Returns matched actions in form of list of arrayrefs [$path, $coderef]. 🌸
  • time2tag($unix_time) β€” Converts (fractional) unix epoch time to NTP timestamp, which is a list of ($seconds_since_1900_01_01, $int32_fraction_parts_of_second). If $unix_time is undef then (0,1) is returned, which means immediate execution by OSC specs. ⏰
  • tag2time($ntp_time, $fraction_of_sec) β€” Reverse of previous. πŸ”„
  • to_stream($data) β€” Packs raw OSC data for (tcp) streaming. πŸ“¦
  • from_stream($buf) β€” Returns list of raw OSC data packets in $buf from stream buffer and residue of $buf. If $buf is incomplete β€” returns only $buf. 🧩

πŸŽ€ EXAMPLES πŸŽ€

πŸ’Œ Sending

make packet

my $data = $osc->message(my @specs = qw(/echo isf 3 ping 3.14));
# or
use Time::HiRes 'time';
my $data $osc->bundle(time, [@specs], [@specs2], ...);

via UDP

my $udp = IO::Socket::INET->new( PeerAddr => 'localhost', PeerPort => $port, Proto => 'udp', Type => SOCK_DGRAM) || die $!;
$udp->send($data);

via TCP

my $tcp = IO::Socket::INET->new( PeerAddr => 'localhost', PeerPort => $port, Proto => 'tcp', Type => SOCK_STREAM) || die $!;
$tcp->syswrite($osc->to_stream($data));

πŸ“₯ Receiving

UDP

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);

TCP

my $in = IO::Socket::INET->new( qw(LocalAddr localhost LocalPort), $port, qw(Proto tcp Type), SOCK_STREAM, qw(Listen 1 Reuse 1) ) || die $!;
while (my $sock = $in->accept) {
    my $tail;
    while ($sock->sysread(my $buf, $in->sockopt(SO_RCVBUF))) {
        my @packets = $p->from_stream(length($tail) ? $tail.$buf : $buf);
        $tail = pop @packets;
        $osc->parse($_) for @packets;
    }
}

πŸš€ Dispatching

$osc->set_cb('/echo', 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));

πŸ“ Ping-Pong using AnyEvent::Handle::UDP

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 '/ping';
    }
);
$udp_handle->push_send($osc->message(qw(/ping i 3)), [0, $port]);

πŸ“Š Benchmarks

encode

cmpthese -1, {
    'Net::LibLO::Message' => sub { Net::LibLO::Message->new(qw(isf 3 laaaa 3.0)) },
    'Protocol::OSC' => sub { $protocol->message(qw(/echo isf 3 laaaa 3.0)) },
    'Net::OpenSoundControl' => 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%            --

decode

cmpthese -1, {
    'Protocol::OSC' => sub { $protocol->parse($data) },
    'Net::OpenSoundControl' => sub { Net::OpenSoundControl::decode($data) }
};

                         Rate Net::OpenSoundControl         Protocol::OSC
Net::OpenSoundControl 1630/s                    --                  -65%
Protocol::OSC         4654/s                  186%                    --

⚠️ NB

No validation checks performed. bestie. just so you know. the module is NOT checking your work for you. πŸ’€

🫢 SUPPORT 🫢

  • GitHub

<http://github.com/vividsnow/Protocol-OSC>

  • Search MetaCPAN

<https://metacpan.org/module/Protocol::OSC>


πŸ‘Έ AUTHOR πŸ‘Έ

Yegor Korablev <egor@cpan.org> β€” the icon, the moment, the blueprint. 🌟


πŸ“œ 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. slay, legally. βš–οΈβœ¨


πŸ“ TODO πŸ“

more docs, examples and tests.. as usual ) β€” the eternal mood, fr. 😭


πŸ”­ SEE ALSO πŸ”­

Net::LibLO, Net::OpenSoundControl, AnyEvent::Handle::UDP β€” go read those too, bestie, expand the brain. πŸ§ πŸ’–


and that's the whole page, babes. this one shipped as perl v5.28.1 on 2018-12-21, and honestly? still giving main character energy. periodt. πŸ’…πŸŒˆπŸŽ€

πŸ“¦ where this page comes from