Add orx-midi
This commit is contained in:
@@ -13,12 +13,13 @@ A growing library of assorted data structures, algorithms and utilities.
|
||||
- `orx-jumpflood`, a filter/shader based implementation of the jump flood algorithm for finding fast approximate (directional) distance fields
|
||||
- `orx-kdtree`, a kd-tree implementation for fast nearest point searches
|
||||
- [`orx-mesh-generators`](orx-mesh-generators/README.md), triangular mesh generators
|
||||
- [`orx-midi`](orx-midi/README.md), midi controller interface
|
||||
- [`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
|
||||
|
||||
## Usage
|
||||
ORX 0.0.23 is built against OPENRNDR 0.3.33-rc1, make sure you use this version in your project. Because OPENRNDR's API is pre 1.0 it tends to change from time to time.
|
||||
ORX 0.0.24 is built against OPENRNDR 0.3.33-rc2, make sure you use this version in your project. Because OPENRNDR's API is pre 1.0 it tends to change from time to time.
|
||||
|
||||
The easiest way to add ORX to your project is through the use of Jitpack. [Jitpack](http://jitpack.io) is a service that pulls Gradle based libraries from Github, builds them and serves the jar files.
|
||||
|
||||
@@ -32,13 +33,13 @@ repositories {
|
||||
You can then add any of the ORX artifacts to your `dependencies {}`:
|
||||
```
|
||||
dependencies {
|
||||
compile 'com.github.openrndr.orx:<orx-artifact>:v0.0.23'
|
||||
compile 'com.github.openrndr.orx:<orx-artifact>:v0.0.24'
|
||||
}
|
||||
```
|
||||
|
||||
For example if you want to use the `orx-no-clear` artifact one would use:
|
||||
```
|
||||
dependencies {
|
||||
compile 'com.github.openrndr.orx:orx-no-clear:v0.0.23'
|
||||
compile 'com.github.openrndr.orx:orx-no-clear:v0.0.24'
|
||||
}
|
||||
```
|
||||
@@ -4,7 +4,7 @@ plugins {
|
||||
|
||||
allprojects {
|
||||
group 'org.openrndr.extra'
|
||||
version '0.0.23'
|
||||
version '0.0.24'
|
||||
}
|
||||
|
||||
repositories {
|
||||
@@ -13,7 +13,7 @@ repositories {
|
||||
}
|
||||
|
||||
ext {
|
||||
openrndrVersion = "0.3.33-rc1"
|
||||
openrndrVersion = "0.3.33-rc2"
|
||||
}
|
||||
|
||||
subprojects {
|
||||
|
||||
19
orx-midi/README.md
Normal file
19
orx-midi/README.md
Normal file
@@ -0,0 +1,19 @@
|
||||
# orx-midi
|
||||
|
||||
A minimal and limited library for Midi controllers. Orx-midi is a wrapper around javax.midi.
|
||||
|
||||
## usage
|
||||
|
||||
```kotlin
|
||||
|
||||
// -- list all midi devices
|
||||
MidiDeviceDescription.list().forEach {
|
||||
println("${it.name}, ${it.vendor} r:${it.receive} t:${it.transmit}")
|
||||
}
|
||||
|
||||
// -- open a midi controller and listen for control changes
|
||||
val dev = MidiTransceiver.fromDeviceVendor("BCR2000 [hw:2,0,0]", "ALSA (http://www.alsa-project.org)")
|
||||
dev.controlChanged.listen {
|
||||
println("${it.channel} ${it.control} ${it.value}")
|
||||
}
|
||||
```
|
||||
47
orx-midi/src/main/kotlin/MidiEvent.kt
Normal file
47
orx-midi/src/main/kotlin/MidiEvent.kt
Normal file
@@ -0,0 +1,47 @@
|
||||
package org.openrndr.extra.midi
|
||||
|
||||
enum class MidiEventType {
|
||||
NOTE_ON,
|
||||
NOTE_OFF,
|
||||
CONTROL_CHANGED
|
||||
}
|
||||
|
||||
|
||||
class MidiEvent(val eventType: MidiEventType) {
|
||||
var origin = Origin.DEVICE
|
||||
var control: Int = 0
|
||||
var note: Int = 0
|
||||
var channel: Int = 0
|
||||
var value: Int = 0
|
||||
var velocity: Int = 0
|
||||
|
||||
enum class Origin {
|
||||
DEVICE,
|
||||
USER
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun noteOn(channel: Int, note: Int, velocity: Int): MidiEvent {
|
||||
val midiEvent = MidiEvent(MidiEventType.NOTE_ON)
|
||||
midiEvent.velocity = velocity
|
||||
midiEvent.note = note
|
||||
midiEvent.channel = channel
|
||||
return midiEvent
|
||||
}
|
||||
|
||||
fun noteOff(channel: Int, note: Int): MidiEvent {
|
||||
val midiEvent = MidiEvent(MidiEventType.NOTE_OFF)
|
||||
midiEvent.note = note
|
||||
midiEvent.channel = channel
|
||||
return midiEvent
|
||||
}
|
||||
|
||||
fun controlChange(channel:Int, control: Int, value: Int): MidiEvent {
|
||||
val midiEvent = MidiEvent(MidiEventType.CONTROL_CHANGED)
|
||||
midiEvent.channel = channel
|
||||
midiEvent.control = control
|
||||
midiEvent.value = value
|
||||
return midiEvent
|
||||
}
|
||||
}
|
||||
}
|
||||
151
orx-midi/src/main/kotlin/MidiTransceiver.kt
Normal file
151
orx-midi/src/main/kotlin/MidiTransceiver.kt
Normal file
@@ -0,0 +1,151 @@
|
||||
package org.openrndr.extra.midi
|
||||
|
||||
import org.openrndr.events.Event
|
||||
import javax.sound.midi.*
|
||||
|
||||
data class MidiDeviceName(val name: String, val vendor: String)
|
||||
class MidiDeviceCapabilities {
|
||||
var receive: Boolean = false
|
||||
var transmit: Boolean = false
|
||||
}
|
||||
|
||||
class MidiDeviceDescription(val name: String, val vendor: String, val receive: Boolean, val transmit: Boolean) {
|
||||
companion object {
|
||||
fun list(): List<MidiDeviceDescription> {
|
||||
val caps = mutableMapOf<MidiDeviceName, MidiDeviceCapabilities>()
|
||||
|
||||
val infos = MidiSystem.getMidiDeviceInfo()
|
||||
for (info in infos) {
|
||||
val device = MidiSystem.getMidiDevice(info)
|
||||
val name = MidiDeviceName(info.name, info.vendor)
|
||||
val deviceCaps = caps.getOrPut(name) { MidiDeviceCapabilities() }
|
||||
|
||||
if (device !is Sequencer && device !is Synthesizer) {
|
||||
if (device.maxReceivers != 0 && device.maxTransmitters == 0) {
|
||||
deviceCaps.receive = true
|
||||
}
|
||||
if (device.maxTransmitters != 0 && device.maxReceivers == 0) {
|
||||
deviceCaps.transmit = true
|
||||
}
|
||||
}
|
||||
}
|
||||
return caps.map {
|
||||
MidiDeviceDescription(it.key.name, it.key.vendor, it.value.receive, it.value.transmit)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class MidiTransceiver(val receiverDevice: MidiDevice, val transmitterDevicer: MidiDevice) {
|
||||
companion object {
|
||||
fun fromDeviceVendor(name: String, vendor: String): MidiTransceiver {
|
||||
val infos = MidiSystem.getMidiDeviceInfo()
|
||||
|
||||
var receiverDevice: MidiDevice? = null
|
||||
var transmitterDevice: MidiDevice? = null
|
||||
|
||||
for (info in infos) {
|
||||
try {
|
||||
val device = MidiSystem.getMidiDevice(info)
|
||||
if (device !is Sequencer && device !is Synthesizer) {
|
||||
if (info.vendor == vendor && info.name == name) {
|
||||
if (device.maxTransmitters != 0 && device.maxReceivers == 0) {
|
||||
transmitterDevice = device
|
||||
}
|
||||
|
||||
if (device.maxReceivers != 0 && device.maxTransmitters == 0) {
|
||||
receiverDevice = device
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
} catch (e: MidiUnavailableException) {
|
||||
throw IllegalStateException("no midi available")
|
||||
}
|
||||
}
|
||||
|
||||
if (receiverDevice != null && transmitterDevice != null) {
|
||||
receiverDevice.open()
|
||||
transmitterDevice.open()
|
||||
return MidiTransceiver(receiverDevice, transmitterDevice)
|
||||
} else {
|
||||
throw IllegalArgumentException("midi device not found ${name}:${vendor} ${receiverDevice} ${transmitterDevice}")
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
private val receiver = receiverDevice.receiver
|
||||
private val transmitter = transmitterDevicer.transmitter
|
||||
|
||||
init {
|
||||
transmitter.receiver = object : MidiDeviceReceiver {
|
||||
override fun getMidiDevice(): MidiDevice? {
|
||||
return null
|
||||
}
|
||||
|
||||
override fun send(message: MidiMessage, timeStamp: Long) {
|
||||
val cmd = message.message
|
||||
val channel = (cmd[0].toInt() and 0xff) and 0x0f
|
||||
val status = (cmd[0].toInt() and 0xff) and 0xf0
|
||||
when (status) {
|
||||
ShortMessage.NOTE_ON -> noteOn.trigger(MidiEvent.noteOn(channel, cmd[1].toInt() and 0xff, cmd[2].toInt() and 0xff))
|
||||
ShortMessage.NOTE_OFF -> noteOff.trigger(MidiEvent.noteOff(channel, cmd[1].toInt() and 0xff))
|
||||
ShortMessage.CONTROL_CHANGE -> controlChanged.trigger(MidiEvent.controlChange(channel,cmd[1].toInt() and 0xff, cmd[2].toInt() and 0xff))
|
||||
}
|
||||
}
|
||||
|
||||
override fun close() {
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val controlChanged = Event<MidiEvent>("midi-transceiver::controller-changed").signature(MidiEvent::class.java)
|
||||
val noteOn = Event<MidiEvent>("midi-transceiver::note-on").signature(MidiEvent::class.java)
|
||||
val noteOff = Event<MidiEvent>("midi-transceiver::note-off").signature(MidiEvent::class.java)
|
||||
|
||||
fun controlChange(channel: Int, control: Int, value: Int) {
|
||||
try {
|
||||
val msg = ShortMessage(ShortMessage.CONTROL_CHANGE, channel, control, value)
|
||||
if (receiverDevice != null) {
|
||||
val tc = receiverDevice!!.microsecondPosition
|
||||
receiver.send(msg, tc)
|
||||
}
|
||||
} catch (e: InvalidMidiDataException) {
|
||||
//
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
fun noteOn(channel: Int, key: Int, velocity: Int) {
|
||||
try {
|
||||
val msg = ShortMessage(ShortMessage.NOTE_ON, channel, key, velocity)
|
||||
if (receiverDevice != null) {
|
||||
val tc = receiverDevice!!.microsecondPosition
|
||||
receiver.send(msg, tc)
|
||||
}
|
||||
} catch (e: InvalidMidiDataException) {
|
||||
//
|
||||
}
|
||||
}
|
||||
|
||||
fun destroy() {
|
||||
receiverDevice.close()
|
||||
transmitterDevicer.close()
|
||||
}
|
||||
}
|
||||
|
||||
fun main() {
|
||||
MidiDeviceDescription.list().forEach {
|
||||
println("> ${it.name}, ${it.vendor} r:${it.receive} t:${it.transmit}")
|
||||
}
|
||||
|
||||
val dev = MidiTransceiver.fromDeviceVendor("BCR2000 [hw:2,0,0]", "ALSA (http://www.alsa-project.org)")
|
||||
dev.controlChanged.listen {
|
||||
println("${it.channel} ${it.control} ${it.value}")
|
||||
}
|
||||
|
||||
// dev.destroy()
|
||||
}
|
||||
@@ -9,10 +9,7 @@ include 'orx-camera',
|
||||
'orx-jumpflood',
|
||||
'orx-kdtree',
|
||||
'orx-mesh-generators',
|
||||
'orx-midi',
|
||||
'orx-no-clear',
|
||||
'orx-noise',
|
||||
'orx-obj-loader'
|
||||
|
||||
|
||||
|
||||
|
||||
'orx-obj-loader'
|
||||
Reference in New Issue
Block a user