Add orx-midi

This commit is contained in:
Edwin Jakobs
2019-04-26 14:36:42 +02:00
parent ee9deb29c4
commit 9aa9049d33
6 changed files with 225 additions and 10 deletions

View File

@@ -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'
}
```

View File

@@ -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
View 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}")
}
```

View 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
}
}
}

View 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()
}

View File

@@ -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'