diff --git a/cmd/sendosc/sendosc.go b/cmd/sendosc/sendosc.go index c79690e..94a5f11 100644 --- a/cmd/sendosc/sendosc.go +++ b/cmd/sendosc/sendosc.go @@ -1,134 +1,16 @@ package main import ( - "bytes" - "encoding/binary" "encoding/hex" "fmt" "net" "strconv" - "strings" + + "github.com/jwetzell/osc-go/pkg/osc" "github.com/spf13/cobra" ) -type OSCArg struct { - Type string - Value any -} - -type OSCMessage struct { - Address string - Args []OSCArg -} - -func stringToOSCBytes(rawString string) []byte { - var sb strings.Builder - - sb.WriteString(rawString) - sb.WriteString("\u0000") - - padLength := 4 - (len(sb.String()) % 4) - if padLength < 4 { - for i := 0; i < padLength; i++ { - sb.WriteString("\u0000") - } - } - - return []byte(sb.String()) -} - -func integerToOSCBytes(number int32) []byte { - var buf bytes.Buffer - err := binary.Write(&buf, binary.BigEndian, number) - if err != nil { - panic(err) - } - return buf.Bytes() -} - -func floatToOSCBytes(number float32) []byte { - var buf bytes.Buffer - err := binary.Write(&buf, binary.BigEndian, number) - if err != nil { - panic(err) - } - return buf.Bytes() -} - -func byteArrayToOSCBytes(bytes []byte) []byte { - oscBytes := []byte{} - - bytesSize := len(bytes) - oscBytes = append(oscBytes, integerToOSCBytes(int32(bytesSize))...) - oscBytes = append(oscBytes, bytes...) - - padLength := 4 - (bytesSize % 4) - if padLength < 4 { - for i := 0; i < padLength; i++ { - oscBytes = append(oscBytes, 0) - } - } - - return oscBytes -} - -func argsToBuffer(args []OSCArg) []byte { - var argBuffers = []byte{} - - for _, arg := range args { - switch oscType := arg.Type; oscType { - case "s": - if value, ok := arg.Value.(string); ok { - argBuffers = append(argBuffers, stringToOSCBytes(value)...) - } else { - fmt.Println("OSC arg had string type but non-string value.") - } - case "i": - if value, ok := arg.Value.(int32); ok { - argBuffers = append(argBuffers, integerToOSCBytes(value)...) - } else { - fmt.Println("OSC arg had integer type but non-integer value.") - } - case "f": - if value, ok := arg.Value.(float32); ok { - argBuffers = append(argBuffers, floatToOSCBytes(value)...) - } else { - fmt.Println("OSC arg had float type but non-float value.") - } - case "b": - if value, ok := arg.Value.([]byte); ok { - argBuffers = append(argBuffers, byteArrayToOSCBytes(value)...) - } else { - fmt.Println("OSC arg had blob type but non-blob value.") - } - default: - fmt.Print("unhandled osc type: ") - fmt.Printf("%s.\n", oscType) - } - } - return argBuffers -} - -func messageToBuffer(message OSCMessage) []byte { - oscBuffer := []byte{} - - oscBuffer = append(oscBuffer, stringToOSCBytes(message.Address)...) - - var sb strings.Builder - - sb.WriteString(",") - - for _, arg := range message.Args { - sb.WriteString(arg.Type) - } - - oscBuffer = append(oscBuffer, stringToOSCBytes(sb.String())...) - oscBuffer = append(oscBuffer, argsToBuffer(message.Args)...) - - return oscBuffer -} - func main() { var Host string var Port int32 @@ -157,10 +39,10 @@ func main() { rootCmd.Execute() } -func argToTypedArg(rawArg string, oscType string) OSCArg { +func argToTypedArg(rawArg string, oscType string) osc.OSCArg { switch oscType { case "s": - return OSCArg{ + return osc.OSCArg{ Type: "s", Value: rawArg, } @@ -170,7 +52,7 @@ func argToTypedArg(rawArg string, oscType string) OSCArg { // ... handle error panic(err) } - return OSCArg{ + return osc.OSCArg{ Type: "i", Value: int32(number), } @@ -180,7 +62,7 @@ func argToTypedArg(rawArg string, oscType string) OSCArg { // ... handle error panic(err) } - return OSCArg{ + return osc.OSCArg{ Type: "f", Value: float32(number), } @@ -190,14 +72,14 @@ func argToTypedArg(rawArg string, oscType string) OSCArg { // ... handle error panic(err) } - return OSCArg{ + return osc.OSCArg{ Type: "b", Value: data, } default: fmt.Print("unhandled osc type: ") fmt.Printf("%s.\n", oscType) - return OSCArg{} + return osc.OSCArg{} } } @@ -225,7 +107,7 @@ func slipEncode(bytes []byte) []byte { func send(host string, port int32, address string, args []string, types []string, protocol string, slip bool) { - oscArgs := []OSCArg{} + oscArgs := []osc.OSCArg{} for index, arg := range args { oscType := "s" @@ -236,12 +118,12 @@ func send(host string, port int32, address string, args []string, types []string oscArgs = append(oscArgs, argToTypedArg(arg, oscType)) } - oscMessage := OSCMessage{ + oscMessage := osc.OSCMessage{ Address: address, Args: oscArgs, } - oscMessageBuffer := messageToBuffer(oscMessage) + oscMessageBuffer := osc.ToBuffer(oscMessage) if slip { oscMessageBuffer = slipEncode(oscMessageBuffer) diff --git a/go.mod b/go.mod index c32143f..5676d56 100644 --- a/go.mod +++ b/go.mod @@ -1,4 +1,4 @@ -module sendosc +module github.com/jwetzell/osc-go go 1.23.1 diff --git a/pkg/osc/osc.go b/pkg/osc/osc.go new file mode 100644 index 0000000..8c23b80 --- /dev/null +++ b/pkg/osc/osc.go @@ -0,0 +1,125 @@ +package osc + +import ( + "bytes" + "encoding/binary" + "fmt" + "strings" +) + +type OSCArg struct { + Type string + Value any +} + +type OSCMessage struct { + Address string + Args []OSCArg +} + +func stringToOSCBytes(rawString string) []byte { + var sb strings.Builder + + sb.WriteString(rawString) + sb.WriteString("\u0000") + + padLength := 4 - (len(sb.String()) % 4) + if padLength < 4 { + for i := 0; i < padLength; i++ { + sb.WriteString("\u0000") + } + } + + return []byte(sb.String()) +} + +func integerToOSCBytes(number int32) []byte { + var buf bytes.Buffer + err := binary.Write(&buf, binary.BigEndian, number) + if err != nil { + panic(err) + } + return buf.Bytes() +} + +func floatToOSCBytes(number float32) []byte { + var buf bytes.Buffer + err := binary.Write(&buf, binary.BigEndian, number) + if err != nil { + panic(err) + } + return buf.Bytes() +} + +func byteArrayToOSCBytes(bytes []byte) []byte { + oscBytes := []byte{} + + bytesSize := len(bytes) + oscBytes = append(oscBytes, integerToOSCBytes(int32(bytesSize))...) + oscBytes = append(oscBytes, bytes...) + + padLength := 4 - (bytesSize % 4) + if padLength < 4 { + for i := 0; i < padLength; i++ { + oscBytes = append(oscBytes, 0) + } + } + + return oscBytes +} + +func argsToBuffer(args []OSCArg) []byte { + var argBuffers = []byte{} + + for _, arg := range args { + switch oscType := arg.Type; oscType { + case "s": + if value, ok := arg.Value.(string); ok { + argBuffers = append(argBuffers, stringToOSCBytes(value)...) + } else { + fmt.Println("OSC arg had string type but non-string value.") + } + case "i": + if value, ok := arg.Value.(int32); ok { + argBuffers = append(argBuffers, integerToOSCBytes(value)...) + } else { + fmt.Println("OSC arg had integer type but non-integer value.") + } + case "f": + if value, ok := arg.Value.(float32); ok { + argBuffers = append(argBuffers, floatToOSCBytes(value)...) + } else { + fmt.Println("OSC arg had float type but non-float value.") + } + case "b": + if value, ok := arg.Value.([]byte); ok { + argBuffers = append(argBuffers, byteArrayToOSCBytes(value)...) + } else { + fmt.Println("OSC arg had blob type but non-blob value.") + } + default: + fmt.Print("unhandled osc type: ") + fmt.Printf("%s.\n", oscType) + } + } + return argBuffers +} + +func ToBuffer(message OSCMessage) []byte { + oscBuffer := []byte{} + + oscBuffer = append(oscBuffer, stringToOSCBytes(message.Address)...) + + var sb strings.Builder + + sb.WriteString(",") + + for _, arg := range message.Args { + sb.WriteString(arg.Type) + } + + oscBuffer = append(oscBuffer, stringToOSCBytes(sb.String())...) + oscBuffer = append(oscBuffer, argsToBuffer(message.Args)...) + + return oscBuffer +}