sciss / scalaosc

Contributors Wanted GNU Library General Public License v2.1 only GitHub

OpenSoundControl (OSC) library for the Scala programming language. Mirror of https://codeberg.org/sciss/ScalaOSC

ScalaOSC

Gitter Build Status Maven Central Donate using Liberapay

statement

ScalaOSC is an OpenSoundControl (OSC) library for the Scala programming language. It is (C)opyright 2008–2021 by Hanns Holger Rutz. All rights reserved. ScalaOSC is released under the GNU Lesser General Public License v2.1+ and comes with absolutely no warranties. To contact the author, send an e-mail to contact at sciss.de.

Please consider supporting this project through Liberapay (see badge above) – thank you!

requirements / installation

ScalaOSC builds with sbt against Scala 2.13, 2.12, Dotty (JVM). The last version to support Scala 2.11 was 1.2.0.

To link to ScalaOSC:

libraryDependencies += "de.sciss" %% "scalaosc" % v

The current version v is "1.3.1"

N.B.: A version is published for Scala 2.13 on Scala.js, but due to type-safety issues it does not maintain the contract of the library under JVM. The artefact is published solely for the purpose of experimenting with Scala.js. To make ScalaOSC work correctly under Scala.js, a major rewrite will be necessary. You can use ScalaOSC under Scala.js, but numeric arguments may be misleadingly encoded; for example the floating-point number 1.0f passed to osc.Message will be encoded with an integer 'i' type-tag instead of the expected float 'f' type-tag.

contributing

Please see the file CONTRIBUTING.md

overview

OpenSoundControl (OSC) is a protocol to exchange messages between systems, typically over a network using UDP or TCP, and typically to control sound or multimedia applications.

OSC can be used to control sound software – for example SuperCollider Server was one of the first systems to use OSC –, but also to communicate with hardware controllers.

OSC is a generic protocol and not restricted to sound applications: For example, SwingOSC uses OSC to provide network access to the Java Virtual Machine.

For more information, known implementations and the protocol standard, visit opensoundcontrol.org.

implementation

ScalaOSC currently provides single ended channels (Transmitter to send messages and Receiver to run a receiving loop), as well as single sockets in bidirectional mode (Client and Server). The supported transports are UDP and TCP (Server obviously requires TCP).

ScalaOSC comes with a codec conforming with the strict OSC 1.0 specification. It can be configured to use the types h (64-bit integer), d (64-bit floating point), to encode OSC packets themselves as b blobs (as used by SuperCollider), to support the boolean tags T and F and many other optional types of OSC 1.1, including array wrapping with [ and ]. The API also allows to extend the codec with custom types.

documentation and examples

An example of setting up a client that talks to the SuperCollider server running on UDP port 57110:

    
import de.sciss.osc._
import Implicits._      // simply socket address construction

// create explicit config, because we want to customize it
val cfg = UDP.Config()  
// while SuperCollider uses only OSC 1.0 syntax, we want to
// be able to use doubles and booleans, by making them fall
// back to floats and 0/1 integers
cfg.codec = PacketCodec().doublesAsFloats().booleansAsInts()

// create a client talking to localhost port 57110. the client
// picks a random free port for itself, unless you set it
// explicitly through cfg.localPort = ...
val c = UDP.Client(localhost -> 57110, cfg)

// the following command actually establishes the connection
c.connect()

// now send out a few messages, step-by-step:
c ! Message("/s_new", "default", 1000)
c ! Message("/n_set", 1000, "freq", 666.6)
c ! Message("/n_run", 1000, false)
c ! Message("/n_run", 1000, true)
c ! Message("/n_free", 1000)

// finally shut down the client
c.close()
    

Another very brief example, showing two UDP clients playing ping-pong:

    
import de.sciss.osc
// a sender, no target
val pingT = osc.UDP.Transmitter()
// a receiver, no target, but same channel as sender
val pingR = osc.UDP.Receiver(pingT.channel)
// a bidirectional client, targeted at ping's socket
val pong = osc.UDP.Client(pingT.localSocketAddress)
pingT.connect() // connect all channels
pingR.connect()
pong.connect()

val t = new java.util.Timer()
def delay(code: => Unit): Unit =
  t.schedule(new java.util.TimerTask {
    def run(): Unit = code }, 500)

// unbound channels action takes packet and sender
pingR.action = {
  // match against a particular message
  case (m @ osc.Message("/ping", c: Int), s) =>
    println(s"Ping received $m")
    delay {
      pingT.send(osc.Message("/pong", c), s)
    }
  case _ => // ignore any other message
}

// bound channels action takes just packet
var cnt = 0
pong.action = packet => {
  println(s"Pong received $packet")
  cnt += 1
  if (cnt <= 10) {
    // bound channels send via !(packet)
    delay { pong ! osc.Message("/ping", cnt) }
  } else {
    pingR.close()
    pingT.close()
    pong.close()
    sys.exit()
  }
}

// unbound channels send via send(packet, addr)
pingT.send(osc.Message("/start"),
  pong.localSocketAddress)

Another example, building a TCP echo server:

import de.sciss.osc._

val server = TCP.Server()
server.action = {
  case (Message(name, args @ _*), from) =>
    from ! Message("/pong", args: _*)
}
server.connect()

val client = TCP.Client(server.localSocketAddress)
client.dump()
client.connect()
client ! Message("/ping", 1, 2.3f)

Further examples can be found in the headers of the API docs, e.g. by looking up the documentation for UDP.Client.

ScalaOSC is used in the ScalaCollider project, so you may take a look at its usage there.

todo

The TCP server uses one thread per connection right now. This is fine for most scenarios were only one or two clients are connected. A future version might use asynchronous I/O with thread pooling. Also a file protocol conforming to SuperCollider's binary OSC file format is planned.

download

The current version can be downloaded from git.iem.at/sciss/ScalaOSC.