mirror of
https://github.com/jwetzell/osc-go.git
synced 2026-08-17 12:43:24 +00:00
Compare commits
24 Commits
sendosc/v1.1.2
...
v0.2.1
| Author | SHA1 | Date | |
|---|---|---|---|
| 1130adf046 | |||
| ad3c449149 | |||
| fbd2bf3905 | |||
| e1c8563522 | |||
| e2a8daf013 | |||
| baa140297f | |||
| 8bb3f3fc97 | |||
| e70934e705 | |||
| 597b9ad053 | |||
| 6f141eb742 | |||
| 9bc51c787f | |||
| 37cc551793 | |||
| 67a1cc083b | |||
| 25c159013b | |||
| 5ac60b0685 | |||
| 6251acb852 | |||
| 5600ef75f5 | |||
| 77c92c5456 | |||
| 594c51d2a4 | |||
| d7c6da5c5b | |||
| 6f3a65322b | |||
| e34a589c5f | |||
| 41112d583b | |||
| 24a0501a4d |
@@ -0,0 +1,24 @@
|
|||||||
|
name: Test
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: [main]
|
||||||
|
pull_request:
|
||||||
|
branches: [main]
|
||||||
|
jobs:
|
||||||
|
test:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v6
|
||||||
|
- name: Set up Go
|
||||||
|
uses: actions/setup-go@v6
|
||||||
|
with:
|
||||||
|
go-version-file: go.mod
|
||||||
|
- name: Run tests
|
||||||
|
run: go test -v -coverprofile=coverage.txt .
|
||||||
|
|
||||||
|
- name: Upload coverage to Codecov
|
||||||
|
uses: codecov/codecov-action@v5
|
||||||
|
with:
|
||||||
|
token: ${{ secrets.CODECOV_TOKEN }}
|
||||||
|
files: coverage.txt
|
||||||
@@ -21,25 +21,29 @@ func (b *OSCBundle) ToBytes() []byte {
|
|||||||
return bytes
|
return bytes
|
||||||
}
|
}
|
||||||
|
|
||||||
func BundleFromBytes(bytes []byte) (OSCBundle, []byte, error) {
|
func BundleFromBytes(bytes []byte) (*OSCBundle, []byte, error) {
|
||||||
if len(bytes) < 20 {
|
if len(bytes) < 20 {
|
||||||
return OSCBundle{}, bytes, errors.New("bundle has to be at least 20 bytes")
|
return nil, bytes, errors.New("bundle has to be at least 20 bytes")
|
||||||
}
|
}
|
||||||
|
|
||||||
if bytes[0] != 35 {
|
if bytes[0] != 35 {
|
||||||
return OSCBundle{}, bytes, errors.New("bundle must start with a #")
|
return nil, bytes, errors.New("bundle must start with a #")
|
||||||
}
|
}
|
||||||
|
|
||||||
bundleHeader, bytesAfterBundleHeader := readOSCString(bytes)
|
bundleHeader, bytesAfterBundleHeader, err := readOSCString(bytes)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, bytes, err
|
||||||
|
}
|
||||||
|
|
||||||
if bundleHeader != "#bundle" {
|
if bundleHeader != "#bundle" {
|
||||||
return OSCBundle{}, bytesAfterBundleHeader, errors.New("bundle must start with #bundle string")
|
return nil, bytesAfterBundleHeader, errors.New("bundle must start with #bundle string")
|
||||||
}
|
}
|
||||||
|
|
||||||
timeTag, bytesAfterTimeTag, err := readOSCTimeTag(bytesAfterBundleHeader)
|
timeTag, bytesAfterTimeTag, err := readOSCTimeTag(bytesAfterBundleHeader)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return OSCBundle{}, bytesAfterBundleHeader, err
|
return nil, bytesAfterBundleHeader, err
|
||||||
}
|
}
|
||||||
|
|
||||||
bundleContents := []OSCPacket{}
|
bundleContents := []OSCPacket{}
|
||||||
@@ -52,13 +56,13 @@ func BundleFromBytes(bytes []byte) (OSCBundle, []byte, error) {
|
|||||||
contentSize, bytesAfterContentSize, err := readOSCInt32(remainingBytes)
|
contentSize, bytesAfterContentSize, err := readOSCInt32(remainingBytes)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return OSCBundle{}, remainingBytes, err
|
return nil, remainingBytes, err
|
||||||
}
|
}
|
||||||
|
|
||||||
remainingBytes = bytesAfterContentSize
|
remainingBytes = bytesAfterContentSize
|
||||||
|
|
||||||
if len(remainingBytes) < int(contentSize) {
|
if len(remainingBytes) < int(contentSize) {
|
||||||
return OSCBundle{}, remainingBytes, errors.New("bundle doesn't have enough bytes for the content size it specifies")
|
return nil, remainingBytes, errors.New("bundle doesn't have enough bytes for the content size it specifies")
|
||||||
}
|
}
|
||||||
|
|
||||||
bundleContentBytes := remainingBytes[0:contentSize]
|
bundleContentBytes := remainingBytes[0:contentSize]
|
||||||
@@ -66,17 +70,17 @@ func BundleFromBytes(bytes []byte) (OSCBundle, []byte, error) {
|
|||||||
if bundleContentBytes[0] == 35 {
|
if bundleContentBytes[0] == 35 {
|
||||||
content, _, err := BundleFromBytes(bundleContentBytes)
|
content, _, err := BundleFromBytes(bundleContentBytes)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return OSCBundle{}, remainingBytes, err
|
return nil, remainingBytes, err
|
||||||
}
|
}
|
||||||
bundleContents = append(bundleContents, &content)
|
bundleContents = append(bundleContents, content)
|
||||||
} else if bundleContentBytes[0] == 47 {
|
} else if bundleContentBytes[0] == 47 {
|
||||||
content, err := MessageFromBytes(bundleContentBytes)
|
content, err := MessageFromBytes(bundleContentBytes)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return OSCBundle{}, remainingBytes, err
|
return nil, remainingBytes, err
|
||||||
}
|
}
|
||||||
bundleContents = append(bundleContents, &content)
|
bundleContents = append(bundleContents, content)
|
||||||
} else {
|
} else {
|
||||||
return OSCBundle{}, remainingBytes, errors.New("bundle contents does not look a bundle or message")
|
return nil, remainingBytes, errors.New("bundle contents does not look a bundle or message")
|
||||||
}
|
}
|
||||||
remainingBytes = bytesAfterContentSize[contentSize:]
|
remainingBytes = bytesAfterContentSize[contentSize:]
|
||||||
if len(remainingBytes) == 0 {
|
if len(remainingBytes) == 0 {
|
||||||
@@ -85,7 +89,7 @@ func BundleFromBytes(bytes []byte) (OSCBundle, []byte, error) {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return OSCBundle{
|
return &OSCBundle{
|
||||||
TimeTag: timeTag,
|
TimeTag: timeTag,
|
||||||
Contents: bundleContents,
|
Contents: bundleContents,
|
||||||
},
|
},
|
||||||
|
|||||||
+4
-4
@@ -10,12 +10,12 @@ func TestOSCBundleEncoding(t *testing.T) {
|
|||||||
|
|
||||||
testCases := []struct {
|
testCases := []struct {
|
||||||
description string
|
description string
|
||||||
bundle OSCBundle
|
bundle *OSCBundle
|
||||||
expected []byte
|
expected []byte
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
"simple contents single message",
|
"simple contents single message",
|
||||||
OSCBundle{
|
&OSCBundle{
|
||||||
TimeTag: OSCTimeTag{
|
TimeTag: OSCTimeTag{
|
||||||
seconds: 32,
|
seconds: 32,
|
||||||
fractionalSeconds: 0,
|
fractionalSeconds: 0,
|
||||||
@@ -46,12 +46,12 @@ func TestOSCBundleEncoding(t *testing.T) {
|
|||||||
func TestOSCBundleDecoding(t *testing.T) {
|
func TestOSCBundleDecoding(t *testing.T) {
|
||||||
testCases := []struct {
|
testCases := []struct {
|
||||||
description string
|
description string
|
||||||
expected OSCBundle
|
expected *OSCBundle
|
||||||
bytes []byte
|
bytes []byte
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
"simple contents single message",
|
"simple contents single message",
|
||||||
OSCBundle{
|
&OSCBundle{
|
||||||
TimeTag: OSCTimeTag{
|
TimeTag: OSCTimeTag{
|
||||||
seconds: 32,
|
seconds: 32,
|
||||||
fractionalSeconds: 0,
|
fractionalSeconds: 0,
|
||||||
|
|||||||
@@ -12,30 +12,31 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
var Host string
|
var IP string
|
||||||
var Port int32
|
var Port int32
|
||||||
var Protocol string
|
var Protocol string
|
||||||
var Format string
|
var Format string
|
||||||
|
var Slip bool
|
||||||
|
|
||||||
cmd := &cli.Command{
|
cmd := &cli.Command{
|
||||||
Name: "receiveosc",
|
Name: "receiveosc",
|
||||||
Usage: "receive OSC messages via UDP or TCP",
|
Usage: "receive OSC messages via UDP or TCP",
|
||||||
Flags: []cli.Flag{
|
Flags: []cli.Flag{
|
||||||
&cli.StringFlag{
|
&cli.StringFlag{
|
||||||
Name: "host",
|
Name: "ip",
|
||||||
Usage: "host to send OSC message to",
|
Usage: "ip to receive OSC messages on",
|
||||||
Value: "127.0.0.1",
|
Value: "0.0.0.0",
|
||||||
Destination: &Host,
|
Destination: &IP,
|
||||||
},
|
},
|
||||||
&cli.Int32Flag{
|
&cli.Int32Flag{
|
||||||
Name: "port",
|
Name: "port",
|
||||||
Usage: "port to send OSC message to",
|
Usage: "port to receive OSC messages on",
|
||||||
Destination: &Port,
|
Destination: &Port,
|
||||||
Value: 8888,
|
Value: 8888,
|
||||||
},
|
},
|
||||||
&cli.StringFlag{
|
&cli.StringFlag{
|
||||||
Name: "protocol",
|
Name: "protocol",
|
||||||
Usage: "protocol to use to send (tcp or udp)",
|
Usage: "protocol to use to receive (tcp or udp)",
|
||||||
Value: "udp",
|
Value: "udp",
|
||||||
Destination: &Protocol,
|
Destination: &Protocol,
|
||||||
Validator: func(flag string) error {
|
Validator: func(flag string) error {
|
||||||
@@ -57,13 +58,23 @@ func main() {
|
|||||||
return nil
|
return nil
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
&cli.BoolFlag{
|
||||||
|
Name: "slip",
|
||||||
|
Value: false,
|
||||||
|
Usage: "whether to slip encode the OSC Message bytes",
|
||||||
|
Destination: &Slip,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
Action: func(ctx context.Context, cmd *cli.Command) error {
|
Action: func(ctx context.Context, cmd *cli.Command) error {
|
||||||
netAddress := fmt.Sprintf("%s:%d", Host, Port)
|
netAddress := fmt.Sprintf("%s:%d", IP, Port)
|
||||||
if Protocol == "udp" {
|
switch Protocol {
|
||||||
|
case "udp":
|
||||||
listenUDP(netAddress, Format)
|
listenUDP(netAddress, Format)
|
||||||
} else if Protocol == "tcp" {
|
case "tcp":
|
||||||
listenTCP(netAddress, Format)
|
if !Slip {
|
||||||
|
return fmt.Errorf("OSC 1.0 over TCP is not supported yet")
|
||||||
|
}
|
||||||
|
listenTCP(netAddress, Slip, Format)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
},
|
},
|
||||||
@@ -74,7 +85,7 @@ func main() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func listenTCP(netAddress string, format string) {
|
func listenTCP(netAddress string, useSLIP bool, format string) {
|
||||||
socket, err := net.Listen("tcp4", netAddress)
|
socket, err := net.Listen("tcp4", netAddress)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println(err)
|
fmt.Println(err)
|
||||||
@@ -89,13 +100,13 @@ func listenTCP(netAddress string, format string) {
|
|||||||
fmt.Println(err)
|
fmt.Println(err)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
go handleConnection(conn, format)
|
go handleTCPConnection(conn, useSLIP, format)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
type SLIP struct {
|
type SLIP struct {
|
||||||
pendingBytes []byte
|
pendingBytes []byte
|
||||||
Messages chan osc.OSCMessage
|
Packets chan osc.OSCPacket
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *SLIP) decode(bytes []byte) {
|
func (s *SLIP) decode(bytes []byte) {
|
||||||
@@ -121,14 +132,14 @@ func (s *SLIP) decode(bytes []byte) {
|
|||||||
escapeNext = false
|
escapeNext = false
|
||||||
} else if packetByte == END {
|
} else if packetByte == END {
|
||||||
if len(s.pendingBytes) == 0 {
|
if len(s.pendingBytes) == 0 {
|
||||||
// opening END byte, can discard
|
// probably opening END byte, can discard
|
||||||
continue
|
continue
|
||||||
} else {
|
} else {
|
||||||
message, err := osc.MessageFromBytes(s.pendingBytes)
|
oscPacket, _, err := osc.PacketFromBytes(s.pendingBytes)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println(err)
|
panic(err)
|
||||||
} else {
|
} else {
|
||||||
s.Messages <- message
|
s.Packets <- oscPacket
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
s.pendingBytes = []byte{}
|
s.pendingBytes = []byte{}
|
||||||
@@ -136,19 +147,18 @@ func (s *SLIP) decode(bytes []byte) {
|
|||||||
s.pendingBytes = append(s.pendingBytes, packetByte)
|
s.pendingBytes = append(s.pendingBytes, packetByte)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func handleSLIP(slip SLIP, format string) {
|
func handleSLIP(slip SLIP, format string) {
|
||||||
for message := range slip.Messages {
|
for message := range slip.Packets {
|
||||||
handleMessage(message, format)
|
handlePacket(message, format)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func handleConnection(conn net.Conn, format string) {
|
func handleTCPConnection(conn net.Conn, useSLIP bool, format string) {
|
||||||
slip := SLIP{
|
slip := SLIP{
|
||||||
pendingBytes: []byte{},
|
pendingBytes: []byte{},
|
||||||
Messages: make(chan osc.OSCMessage),
|
Packets: make(chan osc.OSCPacket),
|
||||||
}
|
}
|
||||||
go handleSLIP(slip, format)
|
go handleSLIP(slip, format)
|
||||||
|
|
||||||
@@ -161,12 +171,26 @@ func handleConnection(conn net.Conn, format string) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
if useSLIP {
|
||||||
|
slip.decode(buffer[0:bytesRead])
|
||||||
|
} else {
|
||||||
|
// TODO(jwetzell): handle non-SLIP TCP messages properly
|
||||||
|
}
|
||||||
|
|
||||||
slip.decode(buffer[0:bytesRead])
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func handleMessage(message osc.OSCMessage, format string) {
|
func handlePacket(message osc.OSCPacket, format string) {
|
||||||
|
if bundle, ok := message.(*osc.OSCBundle); ok {
|
||||||
|
handleBundle(bundle, format)
|
||||||
|
} else if msg, ok := message.(*osc.OSCMessage); ok {
|
||||||
|
handleMessage(msg, format)
|
||||||
|
} else {
|
||||||
|
fmt.Println("Received unknown OSC Packet type")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func handleMessage(message *osc.OSCMessage, format string) {
|
||||||
if format == "json" {
|
if format == "json" {
|
||||||
jsonData, _ := json.Marshal(message)
|
jsonData, _ := json.Marshal(message)
|
||||||
fmt.Println(string(jsonData))
|
fmt.Println(string(jsonData))
|
||||||
@@ -175,6 +199,12 @@ func handleMessage(message osc.OSCMessage, format string) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func handleBundle(bundle *osc.OSCBundle, format string) {
|
||||||
|
for _, packet := range bundle.Contents {
|
||||||
|
handlePacket(packet, format)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func listenUDP(netAddress string, format string) {
|
func listenUDP(netAddress string, format string) {
|
||||||
|
|
||||||
s, err := net.ResolveUDPAddr("udp4", netAddress)
|
s, err := net.ResolveUDPAddr("udp4", netAddress)
|
||||||
@@ -199,11 +229,11 @@ func listenUDP(netAddress string, format string) {
|
|||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
oscMessage, err := osc.MessageFromBytes(buffer[0:bytesRead])
|
oscPacket, _, err := osc.PacketFromBytes(buffer[0:bytesRead])
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
handleMessage(oscMessage, format)
|
handlePacket(oscPacket, format)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package main
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"encoding/binary"
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
"net"
|
||||||
@@ -211,6 +212,12 @@ func send(host string, port int32, address string, args []string, types []string
|
|||||||
|
|
||||||
if slip {
|
if slip {
|
||||||
oscMessageBuffer = slipEncode(oscMessageBuffer)
|
oscMessageBuffer = slipEncode(oscMessageBuffer)
|
||||||
|
} else if protocol == "tcp" {
|
||||||
|
// OSC 1.0 prepends a 4 byte size header for non-SLIP TCP messages
|
||||||
|
size := uint32(len(oscMessageBuffer))
|
||||||
|
sizeBytes := make([]byte, 4)
|
||||||
|
binary.BigEndian.PutUint32(sizeBytes, size)
|
||||||
|
oscMessageBuffer = append(sizeBytes, oscMessageBuffer...)
|
||||||
}
|
}
|
||||||
|
|
||||||
netAddress := fmt.Sprintf("%s:%d", host, port)
|
netAddress := fmt.Sprintf("%s:%d", host, port)
|
||||||
|
|||||||
@@ -2,4 +2,4 @@ module github.com/jwetzell/osc-go
|
|||||||
|
|
||||||
go 1.25.1
|
go 1.25.1
|
||||||
|
|
||||||
require github.com/urfave/cli/v3 v3.6.1
|
require github.com/urfave/cli/v3 v3.8.0
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb
|
|||||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
|
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
|
||||||
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
|
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
|
||||||
github.com/urfave/cli/v3 v3.6.1 h1:j8Qq8NyUawj/7rTYdBGrxcH7A/j7/G8Q5LhWEW4G3Mo=
|
github.com/urfave/cli/v3 v3.8.0 h1:XqKPrm0q4P0q5JpoclYoCAv0/MIvH/jZ2umzuf8pNTI=
|
||||||
github.com/urfave/cli/v3 v3.6.1/go.mod h1:ysVLtOEmg2tOy6PknnYVhDoouyC/6N42TMeoMzskhso=
|
github.com/urfave/cli/v3 v3.8.0/go.mod h1:ysVLtOEmg2tOy6PknnYVhDoouyC/6N42TMeoMzskhso=
|
||||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
|
|||||||
+26
-10
@@ -25,11 +25,18 @@ func (m *OSCMessage) ToBytes() []byte {
|
|||||||
return oscBuffer
|
return oscBuffer
|
||||||
}
|
}
|
||||||
|
|
||||||
func MessageFromBytes(bytes []byte) (OSCMessage, error) {
|
func MessageFromBytes(bytes []byte) (*OSCMessage, error) {
|
||||||
address, typeAndArgBytes := readOSCString(bytes)
|
if len(bytes) == 0 {
|
||||||
|
return nil, errors.New("cannot create OSC Message from empty byte array")
|
||||||
|
}
|
||||||
|
if bytes[0] != 47 {
|
||||||
|
return nil, errors.New("OSC Message must start with /")
|
||||||
|
}
|
||||||
|
|
||||||
if address[0] != 47 {
|
address, typeAndArgBytes, err := readOSCString(bytes)
|
||||||
return OSCMessage{}, errors.New("OSC Message address must start with /")
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
oscMessage := OSCMessage{
|
oscMessage := OSCMessage{
|
||||||
@@ -37,22 +44,31 @@ func MessageFromBytes(bytes []byte) (OSCMessage, error) {
|
|||||||
Args: []OSCArg{},
|
Args: []OSCArg{},
|
||||||
}
|
}
|
||||||
|
|
||||||
typeString, argBytes := readOSCString(typeAndArgBytes)
|
if len(typeAndArgBytes) == 0 {
|
||||||
|
// NOTE(jwetzell): no type string return early.
|
||||||
|
return &oscMessage, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
typeString, argBytes, err := readOSCString(typeAndArgBytes)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
for index, oscType := range typeString {
|
for index, oscType := range typeString {
|
||||||
if index == 0 {
|
if index == 0 {
|
||||||
if oscType != ',' {
|
if oscType != ',' {
|
||||||
return OSCMessage{}, errors.New("type string is malformed")
|
return nil, errors.New("type string is malformed")
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
oscArg, remainingBytes, error := readOSCArg(argBytes, string(oscType))
|
oscArg, remainingBytes, err := readOSCArg(argBytes, string(oscType))
|
||||||
if error != nil {
|
if err != nil {
|
||||||
return oscMessage, error
|
return nil, err
|
||||||
}
|
}
|
||||||
argBytes = remainingBytes
|
argBytes = remainingBytes
|
||||||
oscMessage.Args = append(oscMessage.Args, oscArg)
|
oscMessage.Args = append(oscMessage.Args, oscArg)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return oscMessage, nil
|
return &oscMessage, nil
|
||||||
}
|
}
|
||||||
|
|||||||
+164
-107
@@ -1,30 +1,29 @@
|
|||||||
package osc
|
package osc
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
|
||||||
"math"
|
"math"
|
||||||
"reflect"
|
"reflect"
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestOSCMessageEncoding(t *testing.T) {
|
func TestGoodOSCMessageEncoding(t *testing.T) {
|
||||||
|
|
||||||
testCases := []struct {
|
testCases := []struct {
|
||||||
description string
|
name string
|
||||||
message OSCMessage
|
message *OSCMessage
|
||||||
expected []byte
|
expected []byte
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
"simple hello",
|
name: "simple hello",
|
||||||
OSCMessage{
|
message: &OSCMessage{
|
||||||
Address: "/hello",
|
Address: "/hello",
|
||||||
Args: []OSCArg{},
|
Args: []OSCArg{},
|
||||||
},
|
},
|
||||||
[]byte{47, 104, 101, 108, 108, 111, 0, 0, 44, 0, 0, 0},
|
expected: []byte{47, 104, 101, 108, 108, 111, 0, 0, 44, 0, 0, 0},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"simple address string arg",
|
name: "simple address string arg",
|
||||||
OSCMessage{
|
message: &OSCMessage{
|
||||||
Address: "/hello",
|
Address: "/hello",
|
||||||
Args: []OSCArg{
|
Args: []OSCArg{
|
||||||
{
|
{
|
||||||
@@ -33,58 +32,58 @@ func TestOSCMessageEncoding(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
[]byte{47, 104, 101, 108, 108, 111, 0, 0, 44, 115, 0, 0, 97, 114, 103, 49, 0, 0, 0, 0},
|
expected: []byte{47, 104, 101, 108, 108, 111, 0, 0, 44, 115, 0, 0, 97, 114, 103, 49, 0, 0, 0, 0},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
description: "simple address integer arg",
|
name: "simple address integer arg",
|
||||||
message: OSCMessage{Address: "/hello", Args: []OSCArg{{Type: "i", Value: 35}}},
|
message: &OSCMessage{Address: "/hello", Args: []OSCArg{{Type: "i", Value: 35}}},
|
||||||
expected: []byte{47, 104, 101, 108, 108, 111, 0, 0, 44, 105, 0, 0, 0, 0, 0, 35},
|
expected: []byte{47, 104, 101, 108, 108, 111, 0, 0, 44, 105, 0, 0, 0, 0, 0, 35},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
description: "simple address float arg",
|
name: "simple address float arg",
|
||||||
message: OSCMessage{Address: "/hello", Args: []OSCArg{{Type: "f", Value: 34.5}}},
|
message: &OSCMessage{Address: "/hello", Args: []OSCArg{{Type: "f", Value: 34.5}}},
|
||||||
expected: []byte{47, 104, 101, 108, 108, 111, 0, 0, 44, 102, 0, 0, 66, 10, 0, 0},
|
expected: []byte{47, 104, 101, 108, 108, 111, 0, 0, 44, 102, 0, 0, 66, 10, 0, 0},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
description: "simple address blob arg",
|
name: "simple address blob arg",
|
||||||
message: OSCMessage{Address: "/hello", Args: []OSCArg{{Type: "b", Value: []byte{98, 108, 111, 98}}}},
|
message: &OSCMessage{Address: "/hello", Args: []OSCArg{{Type: "b", Value: []byte{98, 108, 111, 98}}}},
|
||||||
expected: []byte{47, 104, 101, 108, 108, 111, 0, 0, 44, 98, 0, 0, 0, 0, 0, 4, 98, 108, 111, 98},
|
expected: []byte{47, 104, 101, 108, 108, 111, 0, 0, 44, 98, 0, 0, 0, 0, 0, 4, 98, 108, 111, 98},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
description: "simple address True arg",
|
name: "simple address True arg",
|
||||||
message: OSCMessage{Address: "/hello", Args: []OSCArg{OSCArg{Type: "T", Value: true}}},
|
message: &OSCMessage{Address: "/hello", Args: []OSCArg{{Type: "T", Value: true}}},
|
||||||
expected: []byte{47, 104, 101, 108, 108, 111, 0, 0, 44, 84, 0, 0},
|
expected: []byte{47, 104, 101, 108, 108, 111, 0, 0, 44, 84, 0, 0},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
description: "simple address False arg",
|
name: "simple address False arg",
|
||||||
message: OSCMessage{Address: "/hello", Args: []OSCArg{{Type: "F", Value: false}}},
|
message: &OSCMessage{Address: "/hello", Args: []OSCArg{{Type: "F", Value: false}}},
|
||||||
expected: []byte{47, 104, 101, 108, 108, 111, 0, 0, 44, 70, 0, 0},
|
expected: []byte{47, 104, 101, 108, 108, 111, 0, 0, 44, 70, 0, 0},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
description: "simple address color arg",
|
name: "simple address color arg",
|
||||||
message: OSCMessage{Address: "/hello", Args: []OSCArg{{Type: "r", Value: OSCColor{r: 20, g: 21, b: 22, a: 10}}}},
|
message: &OSCMessage{Address: "/hello", Args: []OSCArg{{Type: "r", Value: OSCColor{r: 20, g: 21, b: 22, a: 10}}}},
|
||||||
expected: []byte{47, 104, 101, 108, 108, 111, 0, 0, 44, 114, 0, 0, 20, 21, 22, 10},
|
expected: []byte{47, 104, 101, 108, 108, 111, 0, 0, 44, 114, 0, 0, 20, 21, 22, 10},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
description: "simple address nil arg",
|
name: "simple address nil arg",
|
||||||
message: OSCMessage{Address: "/hello", Args: []OSCArg{{Type: "N", Value: nil}}},
|
message: &OSCMessage{Address: "/hello", Args: []OSCArg{{Type: "N", Value: nil}}},
|
||||||
expected: []byte{47, 104, 101, 108, 108, 111, 0, 0, 44, 78, 0, 0},
|
expected: []byte{47, 104, 101, 108, 108, 111, 0, 0, 44, 78, 0, 0},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
description: "simple address int64 arg",
|
name: "simple address int64 arg",
|
||||||
message: OSCMessage{Address: "/hello", Args: []OSCArg{{Type: "h", Value: 281474976710655}}},
|
message: &OSCMessage{Address: "/hello", Args: []OSCArg{{Type: "h", Value: 281474976710655}}},
|
||||||
expected: []byte{47, 104, 101, 108, 108, 111, 0, 0, 44, 104, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255},
|
expected: []byte{47, 104, 101, 108, 108, 111, 0, 0, 44, 104, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
description: "simple address float64 arg",
|
name: "simple address float64 arg",
|
||||||
message: OSCMessage{Address: "/hello", Args: []OSCArg{{Type: "d", Value: 12.7654763}}},
|
message: &OSCMessage{Address: "/hello", Args: []OSCArg{{Type: "d", Value: 12.7654763}}},
|
||||||
expected: []byte{
|
expected: []byte{
|
||||||
47, 104, 101, 108, 108, 111, 0, 0, 44, 100, 0, 0, 0x40, 0x29, 0x87, 0xec, 0x82, 0x74, 0xb9, 0xe6,
|
47, 104, 101, 108, 108, 111, 0, 0, 44, 100, 0, 0, 0x40, 0x29, 0x87, 0xec, 0x82, 0x74, 0xb9, 0xe6,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
// TODO(jwetzell): get array args working working
|
// TODO(jwetzell): get array args working working
|
||||||
// {
|
// {
|
||||||
// description: "simple address array arg",
|
// name: "simple address array arg",
|
||||||
// message: OSCMessage{
|
// message: OSCMessage{
|
||||||
// Address: "/hello",
|
// Address: "/hello",
|
||||||
// Args: []OSCArg{
|
// Args: []OSCArg{
|
||||||
@@ -100,16 +99,16 @@ func TestOSCMessageEncoding(t *testing.T) {
|
|||||||
// },
|
// },
|
||||||
// },
|
// },
|
||||||
{
|
{
|
||||||
description: "osc 1.0 spec example 1",
|
name: "osc 1.0 spec example 1",
|
||||||
message: OSCMessage{Address: "/oscillator/4/frequency", Args: []OSCArg{{Type: "f", Value: 440}}},
|
message: &OSCMessage{Address: "/oscillator/4/frequency", Args: []OSCArg{{Type: "f", Value: 440}}},
|
||||||
expected: []byte{
|
expected: []byte{
|
||||||
47, 111, 115, 99, 105, 108, 108, 97, 116, 111, 114, 47, 52, 47, 102, 114, 101, 113, 117, 101, 110, 99, 121, 0, 44,
|
47, 111, 115, 99, 105, 108, 108, 97, 116, 111, 114, 47, 52, 47, 102, 114, 101, 113, 117, 101, 110, 99, 121, 0, 44,
|
||||||
102, 0, 0, 67, 220, 0, 0,
|
102, 0, 0, 67, 220, 0, 0,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
description: "osc 1.0 spec example 2",
|
name: "osc 1.0 spec example 2",
|
||||||
message: OSCMessage{
|
message: &OSCMessage{
|
||||||
Address: "/foo",
|
Address: "/foo",
|
||||||
Args: []OSCArg{
|
Args: []OSCArg{
|
||||||
{Type: "i", Value: 1000},
|
{Type: "i", Value: 1000},
|
||||||
@@ -128,81 +127,80 @@ func TestOSCMessageEncoding(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for _, testCase := range testCases {
|
for _, testCase := range testCases {
|
||||||
|
t.Run(testCase.name, func(t *testing.T) {
|
||||||
|
actual := testCase.message.ToBytes()
|
||||||
|
|
||||||
actual := testCase.message.ToBytes()
|
if !reflect.DeepEqual(actual, testCase.expected) {
|
||||||
|
t.Fatalf("failed to encode properly got '%v', expected '%v'", actual, testCase.expected)
|
||||||
if !reflect.DeepEqual(actual, testCase.expected) {
|
}
|
||||||
t.Errorf("Test '%s' failed to encode properly", testCase.description)
|
})
|
||||||
fmt.Printf("expected: %v\n", testCase.expected)
|
|
||||||
fmt.Printf("actual: %v\n", actual)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestOSCMessageDecoding(t *testing.T) {
|
func TestGoodOSCMessageDecoding(t *testing.T) {
|
||||||
testCases := []struct {
|
testCases := []struct {
|
||||||
description string
|
name string
|
||||||
bytes []byte
|
bytes []byte
|
||||||
expected OSCMessage
|
expected OSCMessage
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
description: "simple address no args",
|
name: "simple address no args",
|
||||||
bytes: []byte{47, 104, 101, 108, 108, 111, 0, 0, 44, 0, 0, 0},
|
bytes: []byte{47, 104, 101, 108, 108, 111, 0, 0, 44, 0, 0, 0},
|
||||||
expected: OSCMessage{Address: "/hello", Args: []OSCArg{}},
|
expected: OSCMessage{Address: "/hello", Args: []OSCArg{}},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
description: "simple address string arg",
|
name: "simple address string arg",
|
||||||
bytes: []byte{47, 104, 101, 108, 108, 111, 0, 0, 44, 115, 0, 0, 97, 114, 103, 49, 0, 0, 0, 0},
|
bytes: []byte{47, 104, 101, 108, 108, 111, 0, 0, 44, 115, 0, 0, 97, 114, 103, 49, 0, 0, 0, 0},
|
||||||
expected: OSCMessage{Address: "/hello", Args: []OSCArg{{Type: "s", Value: "arg1"}}},
|
expected: OSCMessage{Address: "/hello", Args: []OSCArg{{Type: "s", Value: "arg1"}}},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
description: "simple address integer arg",
|
name: "simple address integer arg",
|
||||||
bytes: []byte{47, 104, 101, 108, 108, 111, 0, 0, 44, 105, 0, 0, 0, 0, 0, 35},
|
bytes: []byte{47, 104, 101, 108, 108, 111, 0, 0, 44, 105, 0, 0, 0, 0, 0, 35},
|
||||||
expected: OSCMessage{Address: "/hello", Args: []OSCArg{{Type: "i", Value: int32(35)}}},
|
expected: OSCMessage{Address: "/hello", Args: []OSCArg{{Type: "i", Value: int32(35)}}},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
description: "simple address float arg",
|
name: "simple address float arg",
|
||||||
bytes: []byte{47, 104, 101, 108, 108, 111, 0, 0, 44, 102, 0, 0, 66, 10, 0, 0},
|
bytes: []byte{47, 104, 101, 108, 108, 111, 0, 0, 44, 102, 0, 0, 66, 10, 0, 0},
|
||||||
expected: OSCMessage{Address: "/hello", Args: []OSCArg{{Type: "f", Value: float32(34.5)}}},
|
expected: OSCMessage{Address: "/hello", Args: []OSCArg{{Type: "f", Value: float32(34.5)}}},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
description: "simple address blob arg",
|
name: "simple address blob arg",
|
||||||
bytes: []byte{47, 104, 101, 108, 108, 111, 0, 0, 44, 98, 0, 0, 0, 0, 0, 4, 98, 108, 111, 98},
|
bytes: []byte{47, 104, 101, 108, 108, 111, 0, 0, 44, 98, 0, 0, 0, 0, 0, 4, 98, 108, 111, 98},
|
||||||
expected: OSCMessage{Address: "/hello", Args: []OSCArg{{Type: "b", Value: []byte{98, 108, 111, 98}}}},
|
expected: OSCMessage{Address: "/hello", Args: []OSCArg{{Type: "b", Value: []byte{98, 108, 111, 98}}}},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
description: "simple address True arg",
|
name: "simple address True arg",
|
||||||
bytes: []byte{47, 104, 101, 108, 108, 111, 0, 0, 44, 84, 0, 0},
|
bytes: []byte{47, 104, 101, 108, 108, 111, 0, 0, 44, 84, 0, 0},
|
||||||
expected: OSCMessage{Address: "/hello", Args: []OSCArg{{Type: "T", Value: true}}},
|
expected: OSCMessage{Address: "/hello", Args: []OSCArg{{Type: "T", Value: true}}},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
description: "simple address False arg",
|
name: "simple address False arg",
|
||||||
bytes: []byte{47, 104, 101, 108, 108, 111, 0, 0, 44, 70, 0, 0},
|
bytes: []byte{47, 104, 101, 108, 108, 111, 0, 0, 44, 70, 0, 0},
|
||||||
expected: OSCMessage{Address: "/hello", Args: []OSCArg{{Type: "F", Value: false}}},
|
expected: OSCMessage{Address: "/hello", Args: []OSCArg{{Type: "F", Value: false}}},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
description: "simple address color arg",
|
name: "simple address color arg",
|
||||||
bytes: []byte{47, 104, 101, 108, 108, 111, 0, 0, 44, 114, 0, 0, 20, 21, 22, 10},
|
bytes: []byte{47, 104, 101, 108, 108, 111, 0, 0, 44, 114, 0, 0, 20, 21, 22, 10},
|
||||||
expected: OSCMessage{Address: "/hello", Args: []OSCArg{{Type: "r", Value: OSCColor{r: 20, g: 21, b: 22, a: 10}}}},
|
expected: OSCMessage{Address: "/hello", Args: []OSCArg{{Type: "r", Value: OSCColor{r: 20, g: 21, b: 22, a: 10}}}},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
description: "simple address nil arg",
|
name: "simple address nil arg",
|
||||||
bytes: []byte{47, 104, 101, 108, 108, 111, 0, 0, 44, 78, 0, 0},
|
bytes: []byte{47, 104, 101, 108, 108, 111, 0, 0, 44, 78, 0, 0},
|
||||||
expected: OSCMessage{Address: "/hello", Args: []OSCArg{{Type: "N", Value: nil}}},
|
expected: OSCMessage{Address: "/hello", Args: []OSCArg{{Type: "N", Value: nil}}},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
description: "simple address Inifinitum arg",
|
name: "simple address Inifinitum arg",
|
||||||
bytes: []byte{47, 104, 101, 108, 108, 111, 0, 0, 44, 73, 0, 0},
|
bytes: []byte{47, 104, 101, 108, 108, 111, 0, 0, 44, 73, 0, 0},
|
||||||
expected: OSCMessage{Address: "/hello", Args: []OSCArg{{Type: "I", Value: math.MaxInt32}}},
|
expected: OSCMessage{Address: "/hello", Args: []OSCArg{{Type: "I", Value: math.MaxInt32}}},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
description: "simple address int64 arg",
|
name: "simple address int64 arg",
|
||||||
bytes: []byte{47, 104, 101, 108, 108, 111, 0, 0, 44, 104, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255},
|
bytes: []byte{47, 104, 101, 108, 108, 111, 0, 0, 44, 104, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255},
|
||||||
expected: OSCMessage{Address: "/hello", Args: []OSCArg{{Type: "h", Value: int64(281474976710655)}}},
|
expected: OSCMessage{Address: "/hello", Args: []OSCArg{{Type: "h", Value: int64(281474976710655)}}},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
description: "simple address float64 arg",
|
name: "simple address float64 arg",
|
||||||
bytes: []byte{
|
bytes: []byte{
|
||||||
47, 104, 101, 108, 108, 111, 0, 0, 44, 100, 0, 0, 0x40, 0x29, 0x87, 0xec, 0x82, 0x74, 0xb9, 0xe6,
|
47, 104, 101, 108, 108, 111, 0, 0, 44, 100, 0, 0, 0x40, 0x29, 0x87, 0xec, 0x82, 0x74, 0xb9, 0xe6,
|
||||||
},
|
},
|
||||||
@@ -210,7 +208,7 @@ func TestOSCMessageDecoding(t *testing.T) {
|
|||||||
},
|
},
|
||||||
// TODO(jwetzell): support OSC array
|
// TODO(jwetzell): support OSC array
|
||||||
// {
|
// {
|
||||||
// description: "simple address array arg",
|
// name: "simple address array arg",
|
||||||
// bytes: []byte{
|
// bytes: []byte{
|
||||||
// 47, 104, 101, 108, 108, 111, 0, 0, 44, 91, 100, 105, 93, 0, 0, 0, 0x40, 0x29, 0x87, 0xec, 0x82, 0x74, 0xb9, 0xe6,
|
// 47, 104, 101, 108, 108, 111, 0, 0, 44, 91, 100, 105, 93, 0, 0, 0, 0x40, 0x29, 0x87, 0xec, 0x82, 0x74, 0xb9, 0xe6,
|
||||||
// 0, 0, 3, 232,
|
// 0, 0, 3, 232,
|
||||||
@@ -226,15 +224,15 @@ func TestOSCMessageDecoding(t *testing.T) {
|
|||||||
// },
|
// },
|
||||||
// },
|
// },
|
||||||
{
|
{
|
||||||
description: "simple address no type string",
|
name: "simple address no type string",
|
||||||
bytes: []byte{47, 104, 101, 108, 108, 111, 0, 0},
|
bytes: []byte{47, 104, 101, 108, 108, 111, 0, 0},
|
||||||
expected: OSCMessage{
|
expected: OSCMessage{
|
||||||
Address: "/hello",
|
Address: "/hello",
|
||||||
Args: []OSCArg{},
|
Args: []OSCArg{},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
description: "osc 1.0 spec example 1",
|
name: "osc 1.0 spec example 1",
|
||||||
bytes: []byte{
|
bytes: []byte{
|
||||||
47, 111, 115, 99, 105, 108, 108, 97, 116, 111, 114, 47, 52, 47, 102, 114, 101, 113, 117, 101, 110, 99, 121, 0, 44,
|
47, 111, 115, 99, 105, 108, 108, 97, 116, 111, 114, 47, 52, 47, 102, 114, 101, 113, 117, 101, 110, 99, 121, 0, 44,
|
||||||
102, 0, 0, 67, 220, 0, 0,
|
102, 0, 0, 67, 220, 0, 0,
|
||||||
@@ -242,7 +240,7 @@ func TestOSCMessageDecoding(t *testing.T) {
|
|||||||
expected: OSCMessage{Address: "/oscillator/4/frequency", Args: []OSCArg{{Type: "f", Value: float32(440)}}},
|
expected: OSCMessage{Address: "/oscillator/4/frequency", Args: []OSCArg{{Type: "f", Value: float32(440)}}},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
description: "osc 1.0 spec example 2",
|
name: "osc 1.0 spec example 2",
|
||||||
bytes: []byte{
|
bytes: []byte{
|
||||||
47, 102, 111, 111, 0, 0, 0, 0, 44, 105, 105, 115, 102, 102, 0, 0, 0, 0, 3, 232, 255, 255, 255, 255, 104, 101, 108,
|
47, 102, 111, 111, 0, 0, 0, 0, 44, 105, 105, 115, 102, 102, 0, 0, 0, 0, 3, 232, 255, 255, 255, 255, 104, 101, 108,
|
||||||
108, 111, 0, 0, 0, 63, 157, 243, 182, 64, 181, 178, 45,
|
108, 111, 0, 0, 0, 63, 157, 243, 182, 64, 181, 178, 45,
|
||||||
@@ -262,24 +260,83 @@ func TestOSCMessageDecoding(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for _, testCase := range testCases {
|
for _, testCase := range testCases {
|
||||||
|
t.Run(testCase.name, func(t *testing.T) {
|
||||||
|
|
||||||
actual, error := MessageFromBytes(testCase.bytes)
|
actual, err := MessageFromBytes(testCase.bytes)
|
||||||
|
|
||||||
if error != nil {
|
if err != nil {
|
||||||
fmt.Println(error)
|
t.Fatalf("failed to encode properly: %s", err.Error())
|
||||||
t.Errorf("Test '%s' failed to encode properly", testCase.description)
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if !reflect.DeepEqual(actual.Address, testCase.expected.Address) {
|
if !reflect.DeepEqual(actual.Address, testCase.expected.Address) {
|
||||||
t.Errorf("Test '%s' failed to encode address properly", testCase.description)
|
t.Fatalf("failed to encode address propertly got '%s', expected '%s'", actual.Address, testCase.expected.Address)
|
||||||
fmt.Printf("expected: %v\n", testCase.expected.Address)
|
}
|
||||||
fmt.Printf("actual: %v\n", actual.Address)
|
|
||||||
}
|
|
||||||
|
|
||||||
if !reflect.DeepEqual(actual.Args, testCase.expected.Args) {
|
if !reflect.DeepEqual(actual.Args, testCase.expected.Args) {
|
||||||
t.Errorf("Test '%s' failed to encode args properly", testCase.description)
|
t.Fatalf("failed to encode args properly got '%+v', expected '%+v'", actual.Args, testCase.expected.Args)
|
||||||
fmt.Printf("expected: %v\n", testCase.expected.Args)
|
}
|
||||||
fmt.Printf("actual: %v\n", actual.Args)
|
})
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestBadOSCMessageDecoding(t *testing.T) {
|
||||||
|
testCases := []struct {
|
||||||
|
name string
|
||||||
|
bytes []byte
|
||||||
|
errorString string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "empty byte array",
|
||||||
|
bytes: []byte{},
|
||||||
|
errorString: "cannot create OSC Message from empty byte array",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "does not start with /",
|
||||||
|
bytes: []byte{0, 104, 101, 108, 108, 111, 0, 0},
|
||||||
|
errorString: "OSC Message must start with /",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "address string not padded",
|
||||||
|
bytes: []byte{47, 104, 101, 108, 108, 111, 0},
|
||||||
|
errorString: "OSC string is not properly padded",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "type string not padded",
|
||||||
|
bytes: []byte{47, 104, 101, 108, 108, 111, 0, 0, 44, 0},
|
||||||
|
errorString: "OSC string is not properly padded",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "type string does not start with ,",
|
||||||
|
bytes: []byte{47, 104, 101, 108, 108, 111, 0, 0, 45, 0, 0, 0},
|
||||||
|
errorString: "type string is malformed",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "string arg not null-terminated",
|
||||||
|
bytes: []byte{
|
||||||
|
47, 104, 101, 108, 108, 111, 0, 0, 44, 115, 0, 0, 97, 114, 103, 49,
|
||||||
|
},
|
||||||
|
errorString: "OSC string must be null-terminated",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "string arg not padded",
|
||||||
|
bytes: []byte{
|
||||||
|
47, 104, 101, 108, 108, 111, 0, 0, 44, 115, 0, 0, 104, 105, 0,
|
||||||
|
},
|
||||||
|
errorString: "OSC string is not properly padded",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, testCase := range testCases {
|
||||||
|
t.Run(testCase.name, func(t *testing.T) {
|
||||||
|
got, err := MessageFromBytes(testCase.bytes)
|
||||||
|
|
||||||
|
if err == nil {
|
||||||
|
t.Fatalf("MessageFromBytes expected to fail but got: %+v", got)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err.Error() != testCase.errorString {
|
||||||
|
t.Fatalf("MessageFromBytes got error '%s', expected '%s'", err.Error(), testCase.errorString)
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -171,32 +171,37 @@ func argsToBuffer(args []OSCArg) []byte {
|
|||||||
return argBuffers
|
return argBuffers
|
||||||
}
|
}
|
||||||
|
|
||||||
func readOSCString(bytes []byte) (string, []byte) {
|
func readOSCString(bytes []byte) (string, []byte, error) {
|
||||||
//TODO(jwetzell): add error handling
|
|
||||||
oscString := ""
|
oscString := ""
|
||||||
stringFinished := false
|
|
||||||
stringEndIndex := 0
|
stringEndIndex := 0
|
||||||
remainingBytes := []byte{}
|
|
||||||
|
|
||||||
|
nullByteFound := false
|
||||||
for index, byteIn := range bytes {
|
for index, byteIn := range bytes {
|
||||||
if !stringFinished {
|
if byteIn == 0 {
|
||||||
if byteIn == 0 {
|
nullByteFound = true
|
||||||
oscString = string(bytes[0:index])
|
oscString = string(bytes[0:index])
|
||||||
stringEndIndex = index + 1
|
stringEndIndex = index + 1
|
||||||
break
|
break
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if !nullByteFound {
|
||||||
|
return "", bytes, errors.New("OSC string must be null-terminated")
|
||||||
|
}
|
||||||
|
|
||||||
stringPadding := 4 - (stringEndIndex % 4)
|
stringPadding := 4 - (stringEndIndex % 4)
|
||||||
|
|
||||||
if stringPadding < 4 {
|
if stringPadding < 4 {
|
||||||
stringEndIndex = stringEndIndex + stringPadding
|
stringEndIndex = stringEndIndex + stringPadding
|
||||||
}
|
}
|
||||||
|
|
||||||
remainingBytes = bytes[stringEndIndex:]
|
if stringEndIndex > len(bytes) {
|
||||||
|
return "", bytes, errors.New("OSC string is not properly padded")
|
||||||
|
}
|
||||||
|
|
||||||
return oscString, remainingBytes
|
remainingBytes := bytes[stringEndIndex:]
|
||||||
|
|
||||||
|
return oscString, remainingBytes, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func readOSCInt32(bytes []byte) (int32, []byte, error) {
|
func readOSCInt32(bytes []byte) (int32, []byte, error) {
|
||||||
@@ -291,27 +296,30 @@ func readOSCArg(bytes []byte, oscType string) (OSCArg, []byte, error) {
|
|||||||
//TODO(jwetzell): add error handling
|
//TODO(jwetzell): add error handling
|
||||||
switch oscType {
|
switch oscType {
|
||||||
case "s":
|
case "s":
|
||||||
argString, bytesLeft := readOSCString(bytes)
|
argString, bytesLeft, err := readOSCString(bytes)
|
||||||
|
if err != nil {
|
||||||
|
return OSCArg{}, bytes, err
|
||||||
|
}
|
||||||
oscArg.Value = argString
|
oscArg.Value = argString
|
||||||
remainingBytes = bytesLeft
|
remainingBytes = bytesLeft
|
||||||
case "i":
|
case "i":
|
||||||
argInt, bytesLeft, error := readOSCInt32(bytes)
|
argInt, bytesLeft, err := readOSCInt32(bytes)
|
||||||
if error != nil {
|
if err != nil {
|
||||||
readArgError = error
|
readArgError = err
|
||||||
}
|
}
|
||||||
oscArg.Value = argInt
|
oscArg.Value = argInt
|
||||||
remainingBytes = bytesLeft
|
remainingBytes = bytesLeft
|
||||||
case "f":
|
case "f":
|
||||||
argFloat, bytesLeft, error := readOSCFloat32(bytes)
|
argFloat, bytesLeft, err := readOSCFloat32(bytes)
|
||||||
if error != nil {
|
if err != nil {
|
||||||
readArgError = error
|
readArgError = err
|
||||||
}
|
}
|
||||||
oscArg.Value = argFloat
|
oscArg.Value = argFloat
|
||||||
remainingBytes = bytesLeft
|
remainingBytes = bytesLeft
|
||||||
case "b":
|
case "b":
|
||||||
argBytes, bytesLeft, error := readOSCBlob(bytes)
|
argBytes, bytesLeft, err := readOSCBlob(bytes)
|
||||||
if error != nil {
|
if err != nil {
|
||||||
readArgError = error
|
readArgError = err
|
||||||
}
|
}
|
||||||
oscArg.Value = argBytes
|
oscArg.Value = argBytes
|
||||||
remainingBytes = bytesLeft
|
remainingBytes = bytesLeft
|
||||||
@@ -328,23 +336,23 @@ func readOSCArg(bytes []byte, oscType string) (OSCArg, []byte, error) {
|
|||||||
oscArg.Value = math.MaxInt32
|
oscArg.Value = math.MaxInt32
|
||||||
remainingBytes = bytes
|
remainingBytes = bytes
|
||||||
case "r":
|
case "r":
|
||||||
argColor, bytesLeft, error := readOSCColor(bytes)
|
argColor, bytesLeft, err := readOSCColor(bytes)
|
||||||
if error != nil {
|
if err != nil {
|
||||||
readArgError = error
|
readArgError = err
|
||||||
}
|
}
|
||||||
oscArg.Value = argColor
|
oscArg.Value = argColor
|
||||||
remainingBytes = bytesLeft
|
remainingBytes = bytesLeft
|
||||||
case "h":
|
case "h":
|
||||||
argInt, bytesLeft, error := readOSCInt64(bytes)
|
argInt, bytesLeft, err := readOSCInt64(bytes)
|
||||||
if error != nil {
|
if err != nil {
|
||||||
readArgError = error
|
readArgError = err
|
||||||
}
|
}
|
||||||
oscArg.Value = argInt
|
oscArg.Value = argInt
|
||||||
remainingBytes = bytesLeft
|
remainingBytes = bytesLeft
|
||||||
case "d":
|
case "d":
|
||||||
argFloat, bytesLeft, error := readOSCFloat64(bytes)
|
argFloat, bytesLeft, err := readOSCFloat64(bytes)
|
||||||
if error != nil {
|
if err != nil {
|
||||||
readArgError = error
|
readArgError = err
|
||||||
}
|
}
|
||||||
oscArg.Value = argFloat
|
oscArg.Value = argFloat
|
||||||
remainingBytes = bytesLeft
|
remainingBytes = bytesLeft
|
||||||
@@ -354,3 +362,26 @@ func readOSCArg(bytes []byte, oscType string) (OSCArg, []byte, error) {
|
|||||||
}
|
}
|
||||||
return oscArg, remainingBytes, readArgError
|
return oscArg, remainingBytes, readArgError
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func PacketFromBytes(bytes []byte) (OSCPacket, []byte, error) {
|
||||||
|
if len(bytes) == 0 {
|
||||||
|
return nil, bytes, errors.New("cannot create OSC Packet from empty byte array")
|
||||||
|
}
|
||||||
|
|
||||||
|
switch bytes[0] {
|
||||||
|
case '#':
|
||||||
|
bundle, remainingBytes, err := BundleFromBytes(bytes)
|
||||||
|
if err != nil {
|
||||||
|
return nil, bytes, err
|
||||||
|
}
|
||||||
|
return bundle, remainingBytes, nil
|
||||||
|
case '/':
|
||||||
|
message, err := MessageFromBytes(bytes)
|
||||||
|
if err != nil {
|
||||||
|
return nil, bytes, err
|
||||||
|
}
|
||||||
|
return message, []byte{}, nil
|
||||||
|
default:
|
||||||
|
return nil, bytes, errors.New("OSC Packet must start with # for bundle or / for message")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user