Giter VIP home page Giter VIP logo

rosc's Introduction

rosc

Build Status license Crates.io rustdoc dependency status

rosc is an implementation of the OSC 1.0 protocol in pure Rust.

Usage

Add rosc to the dependencies section of your projects Cargo.toml:

[dependencies]
rosc = "~0.10"

Documentation

The package documentation can be found here

License

Licensed under either of

at your option.

Contribution

Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.

rosc's People

Contributors

ales-tsurko avatar barinzaya avatar bitzl avatar d1plo1d avatar drluke avatar eeeeeta avatar glindstedt avatar kisielk avatar klingtnet avatar maxnoel avatar ooesili avatar patrickod avatar rennesampaio avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar

rosc's Issues

Send Osc packet with TCP

Hello,

I am trying to use rosc from tokio. My first step is to decode and display incoming osc messages. I have made the server:

use rosc::decoder;
use rosc::{OscMessage, OscPacket};
use tokio::io::AsyncReadExt;
use tokio::net::TcpListener;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    println!("start");
    let listener = TcpListener::bind("0.0.0.0:9000").await?;
    println!("listening");
    loop {
        match listener.accept().await {
            Ok((mut socket, addr)) => {
                println!("new client: {:?}", addr);

                println!("ready");
                tokio::spawn(async move {
                    let mut buf = [0; 1024];

                    // In a loop, read data from the socket and write the data back.
                    loop {
                        let _ = match socket.read(&mut buf).await {
                            Ok(n) if n == 0 => {
                                eprintln!("end tran");
                                return;
                            }
                            Ok(n) => {
                                println!("{:?}", &buf[..n]);
                                println!("read {} bytes", n);
                                match decoder::decode_tcp(&buf[..n]) {
                                    Ok((_, payload)) => {
                                        println!("{:?}", payload);
                                        return;
                                    }
                                    _ => {
                                    eprintln!("decoding error");
                                    return;
                                    }
                                }
                            }
                            Err(e) => {
                                eprintln!("failed to read from socket; err = {:?}", e);
                                return;
                            }
                        };
                    }
                });
            }
            Err(e) => eprintln!("could not get client: {:?}", e),
        }
    }
}

and a simple client inspired from documentation:

use rosc::encoder;
use rosc::{OscMessage, OscPacket, OscType};
use tokio::io::{AsyncReadExt, AsyncWriteExt};
use tokio::net::TcpStream;
use tokio::time::{sleep, Duration};

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let addr = "127.0.0.1:9000";
    let mut stream = TcpStream::connect(addr).await?;
    let osc_packet = OscPacket::Message(OscMessage {
        addr: "/greet/me".to_string(),
        args: vec![OscType::String("hi!".to_string())],
    });
    if let Ok(buf) = encoder::encode(&osc_packet) {
        println!("send packet");
        println!("{:?}", buf);
        stream.write_all(&buf).await?;
        println!("transmitted");
    }
    Ok(())
}

When the server receives the packet it seems to fail decoding it, and put everything in the left first tuple element:

([47, 103, 114, 101, 101, 116, 47, 109, 101, 0, 0, 0, 44, 115, 0, 0, 104, 105, 33, 0], None)

I have made a little program to see what happens:

use rosc::{encoder,decoder};
use rosc::{OscMessage, OscPacket, OscType};

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let osc_packet = OscPacket::Message(OscMessage {
        addr: "/greet/me".to_string(),
        args: vec![OscType::String("hi!".to_string())],
    });
    if let Ok(buf) = encoder::encode(&osc_packet) {
        println!("{:?}", buf);
        let payload = decoder::decode_tcp(&buf)?;
        println!("{:?}", payload);
    }
    Ok(())
}

got this output:

mik@debian:~/hello$ cargo run  --example test_decode
    Finished dev [unoptimized + debuginfo] target(s) in 0.01s
     Running `target/debug/examples/test_decode`
[47, 103, 114, 101, 101, 116, 47, 109, 101, 0, 0, 0, 44, 115, 0, 0, 104, 105, 33, 0]
([47, 103, 114, 101, 101, 116, 47, 109, 101, 0, 0, 0, 44, 115, 0, 0, 104, 105, 33, 0], None)
mik@debian:~/hello$

I guess I missuse the api. I tried to find something to decode directly from a buffer in the API without succeed.

help with parsing fragmented packets?

Hi,

I'm a rust novice. 🦀🦀🦀 With a background in generative/interactive art.

I've been using the nannou library to explore this kickass language, and recently made up a personal learning project involving sending OSC data from TouchDesigner to a nannou app.

I started encountering an issue whereby the total channels i sent seem to be truncated based on packet size limitations. After some digging on the internet, I'm sort of coming to understand that the issue I'm having may be that I'm handling this as discrete packets rather than streams.

can you provide any guidance on how to handle this scenario using this crate?

thank you for all your fine work :)

Add TCP examples

At the moment there are only examples showing how to receive and send using a UDP socket. We should add related examples for sending and receiving OSC messsages over a TCP socket. Note that we must use decoder::decode_tcp then, encoding stays the same, though.

Remove `regex` crate dependency

With #21 most of the library does not depend on the std library anymore. If we want to remove the dependency on std completely we must get rid of regex that was introduced with #20 for OSC address pattern matching. Matching OSC address against OSC address patterns does not require to use a full blown regex library, but it was the most convenient option.

What we need to do is to remove the usage of regex in src/address.rs by parsing the OSC address patterns using nom and implement a custom matcher that can handle the small regular expression subset the OSC spec defines for address matching.

Possible small bug?

Hello,

I've found an error while compiling after having upgraded to rosc 0.9.1 from version 0.8.

Compiling rosc v0.9.1
error[E0449]: unnecessary visibility qualifier
   --> /Users/<my-username>/.cargo/registry/src/github.com-1ecc6299db9ec823/rosc-0.9.1/src/types.rs:106:5
    |
106 |     pub fn from(time: (u32, u32)) -> OscTime {
    |     ^^^ `pub` not permitted here because it's implied

I've temporarily solved it by following the suggested solution and removing the pub modifier.

Draft: Only match addresses once

We need a struct for addresses so we only have to validate them once instead of every time match_address is invoked

This needs a proper description, just see this issue as a reminder for now.

This is a follow-up of #26 .

Should decoder discard messages with invalid address?

I've been trying to implement a dispatcher and noticed that the current matcher is a bit unergonomic to use. The OscMessage's address is only checked for validity in the matcher. This results in having to check for address errors during address matching, which seems like the wrong place to do that, as a dispatcher will attempt to match with every OSC method, but every single one will fail due to the address being invalid, so the same work is done repeatedly.

I propose that we instead discard incoming OscMessages in the decoder, as an OscMessage without a valid address can not ever be matched anyway, and as a consequence always know that an OscMessage must have a valid address.

I wanted to gather thoughts on this before implementing it.

Support OSC array type

The OSC specification allows to send arrays of values. The tag [ marks the start of an array type definition followed by the type tags of each value until it is closed by a ] tag.

Why is this useful? In my case, I'd need to send a list of frequencies (float values) to a Supercollider synth. The number of frequencies can vary, so a fixed number of fields would be an ugly workaround.

Note that nesting isn't exactly specified (it's "the tags following are for the data", but also "until a close brace tag is reached"), so I assume it's fine to go with whatever is easier to implement.

I'm happy to help. A recursive type would look nice, but I'm not sure if that's the best way to go:

pub enum OscType {
    // ...
    Array(Vec<OscType>),
    // ...
}

Decoder methods wrap OscError in nom Err enum

I've just noticed that the decode_udp method straight up returns the nom Err wrapping the OscError.

This means that you have to add nom as a dependency to your project if you want to get the OscError (example).

I think this is an unnecessary leaky abstraction, we should instead return a Result<(&[u8], OscPacket), OscError> on the decode methods.

Converting between OscTime and SystemTime

Hello. I wrote this function in some code that used this library and thought that it would be useful for other people. I could not find an easy way to create an OscTime from SystemTime in this library.

use rosc::OscTime;
use std::time::{Duration, SystemTime};

fn system_time_to_osc(time: SystemTime) -> OscTime {
    const UNIX_OFFSET: u64 = 2_208_988_800; // From RFC 5905
    const TWO_POW_32: f64 = 4294967296.0;

    let unix_time = time.duration_since(SystemTime::UNIX_EPOCH).unwrap(); // Safe absent time machines
    let unix_offset = Duration::new(UNIX_OFFSET, 0);
    let epoch_time = unix_offset + unix_time;
    let seconds = epoch_time.as_secs() as u32;
    let nanos = epoch_time.subsec_nanos() as f64;
    let fractional = ((nanos * TWO_POW_32) / 1.0e9).round() as u32;
    (seconds, fractional)
}

I based the function from some code I found in Cloudflare's cfnts project which uses NTP timestamps which appear to be the same format used in OSC.

I was going to make a PR to add it but there appeared to be a few different ways to go about implementing it and I wanted some input on the approach. I thought of three alternatives. Let me know if this is something you want to include in the library and if you have a preference between one of these.

1. Add this function as-is to the crate

This is the easiest option and is the only non-breaking change. Unfortunately, because OscTime is currently a type alias to (u8, u8), we can not add implementations to OscTime, such as impl From<SystemTime> for OscTime, which would provide the nicest experience to performing this conversion in my opinion.

2. Change OscTime into a struct

If we changed OscTime into a struct we could implement the conversion from SystemTime like this:

#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct OscTime {
    pub seconds: u32,
    pub fractional: u32,
}

// Now we can do this.
impl From<SystemTime> for OscTime {
    fn from(time: SystemTime) -> OscTime {
        // ...
    }
}

This provides a better experience in my opinion, but would be a breaking change. Than the previous one.

3. Completely Replace OscTime with SystemTime

I haven't quite thought though the ramifications of this one. SystemTime does appear able to represent times as far back as 1900-01-01 00:00:00 UTC so in theory we should be able to represent every OSC time as a SystemTime.

Support TCP

When working with TCP, the OSC spec mentions:

In a stream-based protocol such as TCP, the stream should begin with an int32 giving the size of the first packet, followed by the contents of the first packet, followed by the size of the second packet, etc.

I've had much trouble with rosc to use TCP, reading this little line in the spec I finally understand. Is it true that rosc doesn't implement this right now, and if so, would this be a good idea to have?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.