osc

package module
v0.0.0-...-2c021f8 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: May 23, 2024 License: MIT Imports: 15 Imported by: 0

README

osc

Open Sound Control 1.0 for Go.

Go Report Card

Install

go install github.com/scgolang/osc

Usage

See the ping pong example.

Contributing

This package aims to be high quality and completely compliant with the OSC 1.0 Spec.

If there is anything missing or this package does not satisfy your use case please open an issue with a detailed description of what you are doing and we will try to address it as quickly as possible.

If you wish to contribute code, please write tests that cover all your code.

Documentation

Overview

Package osc implements Open Sound Control 1.0

For more info about OSC, go to http://opensoundcontrol.org

Example (CustomDispatcher)
package main

import (
	"fmt"
	"log"
	"net"
	"time"
)

func main() {
	laddr, err := net.ResolveUDPAddr("udp", "127.0.0.1:0")
	if err != nil {
		log.Fatal(err)
	}
	server, err := ListenUDP("udp", laddr)
	if err != nil {
		log.Fatal(err)
	}
	defer server.Close()

	var (
		doneChan   = make(chan struct{})
		dispatcher = customDispatcher{done: doneChan}
		errChan    = make(chan error)
	)
	go func() {
		errChan <- server.Serve(1, dispatcher)
	}()

	// Send a message from the client.
	raddr, err := net.ResolveUDPAddr("udp", server.LocalAddr().String())
	if err != nil {
		log.Fatal(err)
	}
	client, err := DialUDP("udp", nil, raddr)
	if err != nil {
		log.Fatal(err)
	}
	if err := client.Send(Message{Address: "/foo"}); err != nil {
		log.Fatal(err)
	}
	select {
	case <-doneChan:
	case err := <-errChan:
		log.Fatal(err)
	case <-time.After(5 * time.Second):
		panic("timeout waiting for custom dispatcher example to finish")
	}
}

type customDispatcher struct {
	done chan struct{}
}

func (d customDispatcher) Dispatch(bundle Bundle, exactMatch bool) error {
	fmt.Println("bundle received by custom dispatcher")
	close(d.done)
	return nil
}

func (d customDispatcher) Invoke(msg Message, exactMatch bool) error {
	fmt.Printf("method %s received by custom dispatcher\n", msg.Address)
	close(d.done)
	return nil
}
Output:

method /foo received by custom dispatcher
Example (PingPong)
package main

import (
	"errors"
	"fmt"
	"log"
	"net"
	"time"
)

func main() {
	errChan := make(chan error)

	// Setup the server.
	laddr, err := net.ResolveUDPAddr("udp", "127.0.0.1:0")
	if err != nil {
		log.Fatal(err)
	}
	server, err := ListenUDP("udp", laddr)
	if err != nil {
		log.Fatal(err)
	}

	// Run the server in a new goroutine.
	go serverDispatch(server, errChan)

	// Setup the client.
	raddr, err := net.ResolveUDPAddr("udp", server.LocalAddr().String())
	if err != nil {
		log.Fatal(err)
	}
	client, err := DialUDP("udp", nil, raddr)
	if err != nil {
		log.Fatal(err)
	}

	var (
		pongChan        = make(chan struct{})
		clientCloseChan = make(chan struct{})
	)
	// Clients are also servers!
	go clientDispatch(client, errChan, pongChan, clientCloseChan)

	// Send the ping message, wait for the pong, then close both connections.
	if err := client.Send(Message{Address: "/ping"}); err != nil {
		log.Fatal(err)
	}
	if err := waitPong(pongChan, errChan); err != nil {
		log.Fatal(err)
	}

	if err := client.Send(Message{Address: "/close"}); err != nil {
		log.Fatal(err)
	}
	if err := waitErr(errChan); err != nil {
		log.Fatal(err)
	}
	if err := waitClose(clientCloseChan, errChan); err != nil {
		log.Fatal(err)
	}
}

func serverDispatch(server *UDPConn, errChan chan error) {
	errChan <- server.Serve(1, PatternMatching{
		"/ping": Method(func(msg Message) error {
			fmt.Println("Server received ping.")
			return server.SendTo(msg.Sender, Message{Address: "/pong"})
		}),
		"/close": Method(func(msg Message) error {
			if err := server.SendTo(msg.Sender, Message{Address: "/close"}); err != nil {
				_ = server.Close()
				return err
			}
			fmt.Println("Server closing.")
			return server.Close()
		}),
	})
}

func clientDispatch(client Conn, errChan chan error, pongChan chan struct{}, closeChan chan struct{}) {
	errChan <- client.Serve(1, PatternMatching{
		"/pong": Method(func(msg Message) error {
			fmt.Println("Client received pong.")
			close(pongChan)
			return nil
		}),
		"/close": Method(func(msg Message) error {
			fmt.Println("Client closing.")
			close(closeChan)
			return client.Close()
		}),
	})
}

func waitPong(pongChan chan struct{}, errChan chan error) error {
	select {
	case <-time.After(2 * time.Second):
		return errors.New("timeout")
	case err := <-errChan:
		if err != nil {
			return err
		}
	case <-pongChan:
	}
	return nil
}

func waitErr(errChan chan error) error {
	select {
	case <-time.After(2 * time.Second):
		return errors.New("timeout")
	case err := <-errChan:
		return err
	}
}

func waitClose(closeChan chan struct{}, errChan chan error) error {
	select {
	case <-time.After(2 * time.Second):
		return errors.New("timeout")
	case <-closeChan:
	}
	return nil
}
Output:

Server received ping.
Client received pong.
Server closing.
Client closing.

Index

Examples

Constants

View Source
const (
	TypetagPrefix byte = ','
	TypetagInt    byte = 'i'
	TypetagFloat  byte = 'f'
	TypetagString byte = 's'
	TypetagBlob   byte = 'b'
	TypetagFalse  byte = 'F'
	TypetagTrue   byte = 'T'
)

Typetag constants.

View Source
const (
	// SecondsFrom1900To1970 is exactly what it sounds like.
	SecondsFrom1900To1970 = 2208988800 // Source: RFC 868

	// TimetagSize is the number of 8-bit bytes in an OSC timetag.
	TimetagSize = 8

	// Immediately is a special timetag value that means "immediately".
	Immediately = Timetag(1)
)
View Source
const (
	// BundleTag is the tag on an OSC bundle message.
	BundleTag = "#bundle"
)
View Source
const (
	// MessageChar is the first character of any valid OSC message.
	MessageChar = '/'
)

Variables

View Source
var (
	ErrEarlyTimetag = errors.New("enclosing bundle's timetag was later than the nested bundle's")
	ErrEndOfPackets = errors.New("end of packets")
)

Common errors.

View Source
var (
	ErrNilDispatcher  = errors.New("nil dispatcher")
	ErrPrematureClose = errors.New("server cannot be closed before calling Listen")
)

Common errors.

View Source
var (
	ErrIndexOutOfBounds = errors.New("index out of bounds")
	ErrInvalidTypeTag   = errors.New("invalid type tag")
	ErrNilWriter        = errors.New("writer must not be nil")
	ErrParse            = errors.New("error parsing message")
)

Common errors.

View Source
var (
	ErrInvalidAddress = errors.New("invalid OSC address")
)

Common errors.

Functions

func GetRegex

func GetRegex(pattern string) (*regexp.Regexp, error)

GetRegex compiles and returns a regular expression object for the given address pattern.

func Pad

func Pad(b []byte) []byte

Pad pads a slice of bytes with null bytes so that it's length is a multiple of 4.

func ReadBlob

func ReadBlob(length int32, data []byte) ([]byte, int64)

ReadBlob reads a blob of the given length from the given slice of bytes.

func ReadString

func ReadString(data []byte) (string, int64)

ReadString reads a string from a byte slice. If the byte slice does not have any null bytes, then one is appended to the end. If the length of the byte slice is not a multiple of 4 we append as many null bytes as we need to make this true before converting to a string. What this means is that the second return value, which is the number of bytes that are consumed to create the string is always a multiple of 4. We also strip off any trailing null bytes in the returned string.

func TempSocket

func TempSocket() string

TempSocket creates an absolute path to a temporary socket file.

func ToBytes

func ToBytes(s string) []byte

ToBytes returns an OSC representation of the given string. This means that the returned byte slice is padded with null bytes so that it's length is a multiple of 4.

func ValidateAddress

func ValidateAddress(addr string) error

ValidateAddress returns an error if addr contains characters that are disallowed by the OSC spec.

func VerifyParts

func VerifyParts(m1, m2 string) bool

VerifyParts verifies that m1 and m2 have the same number of parts, where a part is a nonempty string between pairs of '/' or a nonempty string at the end.

Types

type Argument

type Argument interface {
	io.WriterTo

	Bytes() []byte
	Equal(Argument) bool
	ReadInt32() (int32, error)
	ReadFloat32() (float32, error)
	ReadBool() (bool, error)
	ReadString() (string, error)
	ReadBlob() ([]byte, error)
	String() string
	Typetag() byte
}

Argument represents an OSC argument. An OSC argument can have many different types, which is why we choose to represent them with an interface.

func ReadArgument

func ReadArgument(tt byte, data []byte) (Argument, int64, error)

ReadArgument parses an OSC message argument given a type tag and some data.

func ReadArguments

func ReadArguments(typetags, data []byte) ([]Argument, error)

ReadArguments reads all arguments from the reader and adds it to the OSC message.

func ReadBlobFrom

func ReadBlobFrom(data []byte) (Argument, int64, error)

ReadBlobFrom reads a binary blob from the provided data.

func ReadFloatFrom

func ReadFloatFrom(data []byte) (Argument, int64, error)

ReadFloatFrom reads a 32-bit float from a byte slice.

func ReadIntFrom

func ReadIntFrom(data []byte) (Argument, int64, error)

ReadIntFrom reads a 32-bit integer from a byte slice.

type Arguments

type Arguments []Argument

Arguments is a slice of Argument.

type Blob

type Blob []byte

Blob is a slice of bytes.

func (Blob) Bytes

func (b Blob) Bytes() []byte

Bytes converts the arg to a byte slice suitable for adding to the binary representation of an OSC message.

func (Blob) Equal

func (b Blob) Equal(other Argument) bool

Equal returns true if the argument equals the other one, false otherwise.

func (Blob) ReadBlob

func (b Blob) ReadBlob() ([]byte, error)

ReadBlob reads a slice of bytes from the arg.

func (Blob) ReadBool

func (b Blob) ReadBool() (bool, error)

ReadBool bool reads a boolean from the arg.

func (Blob) ReadFloat32

func (b Blob) ReadFloat32() (float32, error)

ReadFloat32 reads a 32-bit float from the arg.

func (Blob) ReadInt32

func (b Blob) ReadInt32() (int32, error)

ReadInt32 reads a 32-bit integer from the arg.

func (Blob) ReadString

func (b Blob) ReadString() (string, error)

ReadString string reads a string from the arg.

func (Blob) String

func (b Blob) String() string

String converts the arg to a string.

func (Blob) Typetag

func (b Blob) Typetag() byte

Typetag returns the argument's type tag.

func (Blob) WriteTo

func (b Blob) WriteTo(w io.Writer) (int64, error)

WriteTo writes the arg to an io.Writer.

type Bool

type Bool bool

Bool represents a boolean value.

func (Bool) Bytes

func (b Bool) Bytes() []byte

Bytes converts the arg to a byte slice suitable for adding to the binary representation of an OSC message.

func (Bool) Equal

func (b Bool) Equal(other Argument) bool

Equal returns true if the argument equals the other one, false otherwise.

func (Bool) ReadBlob

func (b Bool) ReadBlob() ([]byte, error)

ReadBlob reads a slice of bytes from the arg.

func (Bool) ReadBool

func (b Bool) ReadBool() (bool, error)

ReadBool bool reads a boolean from the arg.

func (Bool) ReadFloat32

func (b Bool) ReadFloat32() (float32, error)

ReadFloat32 reads a 32-bit float from the arg.

func (Bool) ReadInt32

func (b Bool) ReadInt32() (int32, error)

ReadInt32 reads a 32-bit integer from the arg.

func (Bool) ReadString

func (b Bool) ReadString() (string, error)

ReadString string reads a string from the arg.

func (Bool) String

func (b Bool) String() string

String converts the arg to a string.

func (Bool) Typetag

func (b Bool) Typetag() byte

Typetag returns the argument's type tag.

func (Bool) WriteTo

func (b Bool) WriteTo(w io.Writer) (int64, error)

WriteTo writes the arg to an io.Writer.

type Bundle

type Bundle struct {
	Timetag Timetag
	Packets []Packet
	Sender  net.Addr
}

Bundle is an OSC bundle. An OSC Bundle consists of the OSC-string "#bundle" followed by an OSC Time Tag, followed by zero or more bundle elements. The OSC-timetag is a 64-bit fixed point time tag. See http://opensoundcontrol.org/spec-1_0 for more information.

func ParseBundle

func ParseBundle(data []byte, sender net.Addr) (Bundle, error)

ParseBundle parses a bundle from a byte slice.

func (Bundle) Bytes

func (b Bundle) Bytes() []byte

Bytes returns the contents of the bundle as a slice of bytes.

func (Bundle) Equal

func (b Bundle) Equal(other Packet) bool

Equal returns true if one bundle equals another, and false otherwise.

type Conn

type Conn interface {
	net.Conn

	Context() context.Context
	Serve(int, Dispatcher) error
	Send(Packet) error
	SendTo(net.Addr, Packet) error
	SetExactMatch(bool)
}

Conn defines the methods

func DialUDP

func DialUDP(network string, laddr, raddr *net.UDPAddr) (Conn, error)

DialUDP creates a new OSC connection over UDP.

type Dispatcher

type Dispatcher interface {
	Dispatch(bundle Bundle, exactMatch bool) error
	Invoke(msg Message, exactMatch bool) error
}

Dispatcher dispatches OSC packets.

type Float

type Float float32

Float represents a 32-bit float.

func (Float) Bytes

func (f Float) Bytes() []byte

Bytes converts the arg to a byte slice suitable for adding to the binary representation of an OSC message.

func (Float) Equal

func (f Float) Equal(other Argument) bool

Equal returns true if the argument equals the other one, false otherwise.

func (Float) ReadBlob

func (f Float) ReadBlob() ([]byte, error)

ReadBlob reads a slice of bytes from the arg.

func (Float) ReadBool

func (f Float) ReadBool() (bool, error)

ReadBool bool reads a boolean from the arg.

func (Float) ReadFloat32

func (f Float) ReadFloat32() (float32, error)

ReadFloat32 reads a 32-bit float from the arg.

func (Float) ReadInt32

func (f Float) ReadInt32() (int32, error)

ReadInt32 reads a 32-bit integer from the arg.

func (Float) ReadString

func (f Float) ReadString() (string, error)

ReadString string reads a string from the arg.

func (Float) String

func (f Float) String() string

String converts the arg to a string.

func (Float) Typetag

func (f Float) Typetag() byte

Typetag returns the argument's type tag.

func (Float) WriteTo

func (f Float) WriteTo(w io.Writer) (int64, error)

WriteTo writes the arg to an io.Writer.

type Incoming

type Incoming struct {
	Data   []byte
	Sender net.Addr
}

Incoming represents incoming data.

type Int

type Int int32

Int represents a 32-bit integer.

func (Int) Bytes

func (i Int) Bytes() []byte

Bytes converts the arg to a byte slice suitable for adding to the binary representation of an OSC message.

func (Int) Equal

func (i Int) Equal(other Argument) bool

Equal returns true if the argument equals the other one, false otherwise.

func (Int) ReadBlob

func (i Int) ReadBlob() ([]byte, error)

ReadBlob reads a slice of bytes from the arg.

func (Int) ReadBool

func (i Int) ReadBool() (bool, error)

ReadBool bool reads a boolean from the arg.

func (Int) ReadFloat32

func (i Int) ReadFloat32() (float32, error)

ReadFloat32 reads a 32-bit float from the arg.

func (Int) ReadInt32

func (i Int) ReadInt32() (int32, error)

ReadInt32 reads a 32-bit integer from the arg.

func (Int) ReadString

func (i Int) ReadString() (string, error)

ReadString string reads a string from the arg.

func (Int) String

func (i Int) String() string

String converts the arg to a string.

func (Int) Typetag

func (i Int) Typetag() byte

Typetag returns the argument's type tag.

func (Int) WriteTo

func (i Int) WriteTo(w io.Writer) (int64, error)

WriteTo writes the arg to an io.Writer.

type Message

type Message struct {
	Address   string `json:"address"`
	Arguments []Argument
	Sender    net.Addr
}

Message is an OSC message. An OSC message consists of an OSC address pattern and zero or more arguments.

func ParseMessage

func ParseMessage(data []byte, sender net.Addr) (Message, error)

ParseMessage parses an OSC message from a slice of bytes.

func (Message) Bytes

func (msg Message) Bytes() []byte

Bytes returns the contents of the message as a slice of bytes.

func (Message) Equal

func (msg Message) Equal(other Packet) bool

Equal returns true if the messages are equal, false otherwise.

func (Message) Match

func (msg Message) Match(address string, exactMatch bool) (bool, error)

Match returns true if the address of the OSC Message matches the given address.

func (Message) Typetags

func (msg Message) Typetags() []byte

Typetags returns a padded byte slice of the message's type tags.

func (Message) WriteTo

func (msg Message) WriteTo(w io.Writer) (int64, error)

WriteTo writes the Message to an io.Writer.

type MessageHandler

type MessageHandler interface {
	Handle(Message) error
}

MessageHandler is any type that can handle an OSC message.

type Method

type Method func(msg Message) error

Method is an OSC method

func (Method) Handle

func (method Method) Handle(m Message) error

Handle handles an OSC message.

type Packet

type Packet interface {
	Bytes() []byte
	Equal(other Packet) bool
}

Packet is an OSC packet. An OSC packet consists of its contents, a contiguous block of binary data, and its size, the number of 8-bit bytes that comprise the contents. The size of an OSC packet is always a multiple of 4.

type PatternMatching

type PatternMatching map[string]MessageHandler

PatternMatching is a dispatcher that implements OSC 1.0 pattern matching. See http://opensoundcontrol.org/spec-1_0 "OSC Message Dispatching and Pattern Matching"

func (PatternMatching) Dispatch

func (h PatternMatching) Dispatch(b Bundle, exactMatch bool) error

Dispatch invokes an OSC bundle's messages.

func (PatternMatching) Invoke

func (h PatternMatching) Invoke(msg Message, exactMatch bool) error

Invoke invokes an OSC message.

type String

type String string

String is a string.

func (String) Bytes

func (s String) Bytes() []byte

Bytes converts the arg to a byte slice suitable for adding to the binary representation of an OSC message.

func (String) Equal

func (s String) Equal(other Argument) bool

Equal returns true if the argument equals the other one, false otherwise.

func (String) ReadBlob

func (s String) ReadBlob() ([]byte, error)

ReadBlob reads a slice of bytes from the arg.

func (String) ReadBool

func (s String) ReadBool() (bool, error)

ReadBool bool reads a boolean from the arg.

func (String) ReadFloat32

func (s String) ReadFloat32() (float32, error)

ReadFloat32 reads a 32-bit float from the arg.

func (String) ReadInt32

func (s String) ReadInt32() (int32, error)

ReadInt32 reads a 32-bit integer from the arg.

func (String) ReadString

func (s String) ReadString() (string, error)

ReadString string reads a string from the arg.

func (String) String

func (s String) String() string

String converts the arg to a string.

func (String) Typetag

func (s String) Typetag() byte

Typetag returns the argument's type tag.

func (String) WriteTo

func (s String) WriteTo(w io.Writer) (int64, error)

WriteTo writes the arg to an io.Writer.

type Timetag

type Timetag uint64

Timetag represents an OSC Time Tag. An OSC Time Tag is defined as follows: Time tags are represented by a 64 bit fixed point number. The first 32 bits specify the number of seconds since midnight on January 1, 1900, and the last 32 bits specify fractional parts of a second to a precision of about 200 picoseconds. This is the representation used by Internet NTP timestamps. The time tag value consisting of 63 zero bits followed by a one in the least significant bit is a special case meaning "immediately."

func FromTime

func FromTime(t time.Time) Timetag

FromTime converts the given time to an OSC timetag.

func ReadTimetag

func ReadTimetag(data []byte) (Timetag, error)

ReadTimetag parses a timetag from a byte slice.

func (Timetag) Bytes

func (tt Timetag) Bytes() []byte

Bytes converts the timetag to a slice of bytes.

func (Timetag) String

func (tt Timetag) String() string

func (Timetag) Time

func (tt Timetag) Time() time.Time

Time converts an OSC timetag to a time.Time.

type UDPConn

type UDPConn struct {
	// contains filtered or unexported fields
}

UDPConn is an OSC connection over UDP.

func DialUDPContext

func DialUDPContext(ctx context.Context, network string, laddr, raddr *net.UDPAddr) (*UDPConn, error)

DialUDPContext returns a new OSC connection over UDP that can be canceled with the provided context.

func ListenUDP

func ListenUDP(network string, laddr *net.UDPAddr) (*UDPConn, error)

ListenUDP creates a new UDP server.

func ListenUDPContext

func ListenUDPContext(ctx context.Context, network string, laddr *net.UDPAddr) (*UDPConn, error)

ListenUDPContext creates a UDP listener that can be canceled with the provided context.

func (*UDPConn) Close

func (conn *UDPConn) Close() error

Close closes the udp conn.

func (*UDPConn) CloseChan

func (conn *UDPConn) CloseChan() <-chan struct{}

CloseChan returns a channel that is closed when the connection gets closed.

func (*UDPConn) Context

func (conn *UDPConn) Context() context.Context

Context returns the context associated with the conn.

func (*UDPConn) Send

func (conn *UDPConn) Send(p Packet) error

Send sends an OSC message over UDP.

func (*UDPConn) SendTo

func (conn *UDPConn) SendTo(addr net.Addr, p Packet) error

SendTo sends a packet to the given address.

func (*UDPConn) Serve

func (conn *UDPConn) Serve(numWorkers int, dispatcher Dispatcher) error

Serve starts dispatching OSC. Any errors returned from a dispatched method will be returned. Note that this means that errors returned from a dispatcher method will kill your server. If context.Canceled or context.DeadlineExceeded are encountered they will be returned directly.

func (*UDPConn) SetContext

func (conn *UDPConn) SetContext(ctx context.Context)

SetContext sets the context associated with the conn.

func (*UDPConn) SetExactMatch

func (conn *UDPConn) SetExactMatch(value bool)

SetExactMatch changes the behavior of the Serve method so that messages will only be dispatched to methods whose addresses match the message's address exactly. This should provide some performance improvement.

type UnixConn

type UnixConn struct {
	// contains filtered or unexported fields
}

UnixConn handles OSC over a unix socket.

func DialUnix

func DialUnix(network string, laddr, raddr *net.UnixAddr) (*UnixConn, error)

DialUnix opens a unix socket for OSC communication.

func DialUnixContext

func DialUnixContext(ctx context.Context, network string, laddr, raddr *net.UnixAddr) (*UnixConn, error)

DialUnixContext creates a new UnixConn.

func ListenUnix

func ListenUnix(network string, laddr *net.UnixAddr) (*UnixConn, error)

ListenUnix creates a Unix listener that can be canceled with the provided context.

func ListenUnixContext

func ListenUnixContext(ctx context.Context, network string, laddr *net.UnixAddr) (*UnixConn, error)

ListenUnixContext creates a Unix listener that can be canceled with the provided context.

func (*UnixConn) Close

func (conn *UnixConn) Close() error

Close closes the connection.

func (*UnixConn) CloseChan

func (conn *UnixConn) CloseChan() <-chan struct{}

CloseChan returns a channel that is closed when the connection gets closed.

func (*UnixConn) Context

func (conn *UnixConn) Context() context.Context

Context returns the context for the unix conn.

func (*UnixConn) Send

func (conn *UnixConn) Send(p Packet) error

Send sends a Packet.

func (*UnixConn) SendTo

func (conn *UnixConn) SendTo(addr net.Addr, p Packet) error

SendTo sends a Packet to the provided net.Addr.

func (*UnixConn) Serve

func (conn *UnixConn) Serve(numWorkers int, dispatcher Dispatcher) error

Serve starts dispatching OSC. Any errors returned from a dispatched method will be returned. Note that this means that errors returned from a dispatcher method will kill your server. If context.Canceled or context.DeadlineExceeded are encountered they will be returned directly.

func (*UnixConn) SetExactMatch

func (conn *UnixConn) SetExactMatch(value bool)

SetExactMatch changes the behavior of the Serve method so that messages will only be dispatched to methods whose addresses match the message's address exactly. This should provide some performance improvement.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL