diff --git a/README.md b/README.md index 164cce9a..0b2d8a04 100644 --- a/README.md +++ b/README.md @@ -20,7 +20,8 @@ A growing library of assorted data structures, algorithms and utilities. - [`orx-noise`](orx-noise/README.md), library for random number generation and noise - [`orx-no-clear`](orx-no-clear/README.md), a simple extension that provides drawing without clearing the background - [`orx-obj-loader`](orx-obj-loader/README.md), simple Wavefront .obj mesh loader -- [`orx-olive`](orx-olive/README.md), extensions that turns OPENRNDR in to a live coding environment +- [`orx-olive`](orx-olive/README.md), extensions that turns OPENRNDR in to a live coding environment +- [`orx-osc`](orx-osc/README.md), open sound control interface # Developer notes diff --git a/orx-osc/README.md b/orx-osc/README.md new file mode 100644 index 00000000..1d0d6feb --- /dev/null +++ b/orx-osc/README.md @@ -0,0 +1,16 @@ +# orx-osc + +Orx-osc is a wrapper around javaOSC + +## Usage + +```kotlin +// PORT IN and OUT: 57110 +val osc = OSC() + +osc.listen("/live/track2") { + // do something +} + +osc.send("/maxmsp/filter", 500, "hz") +``` diff --git a/orx-osc/build.gradle b/orx-osc/build.gradle new file mode 100644 index 00000000..9f2daaa2 --- /dev/null +++ b/orx-osc/build.gradle @@ -0,0 +1,5 @@ +dependencies { + def withoutSlf4j = { exclude group: 'org.slf4j' } + + compile "com.illposed.osc:javaosc-core:0.6", withoutSlf4j +} \ No newline at end of file diff --git a/orx-osc/src/main/kotlin/OSC.kt b/orx-osc/src/main/kotlin/OSC.kt new file mode 100644 index 00000000..6dd393de --- /dev/null +++ b/orx-osc/src/main/kotlin/OSC.kt @@ -0,0 +1,70 @@ +package org.openrndr.extra.osc + +import com.illposed.osc.OSCMessage +import com.illposed.osc.OSCMessageListener +import com.illposed.osc.messageselector.OSCPatternAddressMessageSelector +import com.illposed.osc.transport.udp.OSCPort +import com.illposed.osc.transport.udp.OSCPortIn +import com.illposed.osc.transport.udp.OSCPortOut +import mu.KotlinLogging +import java.net.InetAddress +import java.net.PortUnreachableException + +private typealias OSCListener = Pair + +private val logger = KotlinLogging.logger {} + +class OSC ( + val address: InetAddress = InetAddress.getLocalHost(), + val portIn: Int = OSCPort.DEFAULT_SC_OSC_PORT, + val portOut: Int = portIn +) { + private val receiver: OSCPortIn = OSCPortIn(portIn) + private val sender: OSCPortOut = OSCPortOut(address, portOut) + private val listeners: MutableMap = mutableMapOf() + + fun send(channel: String, vararg message: T) { + if (!sender.isConnected) sender.connect() + + val msg = OSCMessage(channel, message.toList()) + + try { + sender.send(msg) + } catch (ex: PortUnreachableException) { + logger.error(ex) { "Error: Could not connect to OUT port" } + } catch (ex: IllegalStateException) { + logger.error(ex) { "Error: Couldn't send message to channel: $channel" } + } + } + + fun listen(channel: String, callback: (List) -> Unit) { + val selector = OSCPatternAddressMessageSelector(channel); + + val cb = OSCMessageListener { + callback(it.message.arguments) + } + + receiver.dispatcher.addListener(selector, cb) + + listeners[channel] = Pair(selector, cb) + + if (!receiver.isListening) this.startListening() + } + + // Cannot be called inside a listener's callback + fun removeListener(channel: String?) { + val listener = listeners[channel] + + if (listener != null) { + receiver.dispatcher.removeListener(listener.first, listener.second) + } + } + + private fun startListening() { + receiver.dispatcher.isAlwaysDispatchingImmediately = true; + + receiver.startListening() + + if (receiver.isListening) logger.info("OSC is listening on port: $portIn") + } +} diff --git a/settings.gradle b/settings.gradle index bf88a7f5..96fe2a44 100644 --- a/settings.gradle +++ b/settings.gradle @@ -16,6 +16,7 @@ include 'orx-camera', 'orx-noise', 'orx-obj-loader', 'orx-olive', + 'orx-osc', 'orx-poisson-fill', 'orx-shader-phrases', 'orx-kinect-common',