[orx-midi] Add pitch bend and pressure (#265)

This commit is contained in:
Abe Pazos
2022-08-22 15:27:12 +00:00
committed by GitHub
parent 39ef11ee19
commit 775abf35f9
2 changed files with 131 additions and 30 deletions

View File

@@ -4,7 +4,9 @@ enum class MidiEventType {
NOTE_ON,
NOTE_OFF,
CONTROL_CHANGED,
PROGRAM_CHANGE
PROGRAM_CHANGE,
CHANNEL_PRESSURE,
PITCH_BEND
}
class MidiEvent(val eventType: MidiEventType) {
@@ -13,6 +15,8 @@ class MidiEvent(val eventType: MidiEventType) {
var program: Int = 0
var note: Int = 0
var channel: Int = 0
var pitchBend: Int = 0
var pressure: Int = 0
var value: Int = 0
var velocity: Int = 0
@@ -51,9 +55,32 @@ class MidiEvent(val eventType: MidiEventType) {
midiEvent.program = program
return midiEvent
}
fun channelPressure(channel:Int, pressure: Int): MidiEvent {
val midiEvent = MidiEvent(MidiEventType.CHANNEL_PRESSURE)
midiEvent.channel = channel
midiEvent.pressure = pressure
return midiEvent
}
fun pitchBend(channel:Int, pitchBend: Int): MidiEvent {
val midiEvent = MidiEvent(MidiEventType.PITCH_BEND)
midiEvent.channel = channel
midiEvent.pitchBend = pitchBend
return midiEvent
}
}
override fun toString(): String {
return "MidiEvent(eventType=$eventType, origin=$origin, program=$program, control=$control, note=$note, channel=$channel, value=$value, velocity=$velocity)"
return "MidiEvent(eventType=$eventType, " +
"origin=$origin, " +
"program=$program, " +
"control=$control, " +
"note=$note, " +
"channel=$channel, " +
"pitchBend=$pitchBend, " +
"pressure=$pressure, " +
"value=$value, " +
"velocity=$velocity)"
}
}

View File

@@ -13,7 +13,12 @@ class MidiDeviceCapabilities {
}
}
data class MidiDeviceDescription(val name: String, val vendor: String, val receive: Boolean, val transmit: Boolean) {
data 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>()
@@ -22,7 +27,8 @@ data class MidiDeviceDescription(val name: String, val vendor: String, val recei
for (info in infos) {
val device = MidiSystem.getMidiDevice(info)
val name = MidiDeviceName(info.name, info.vendor)
val deviceCaps = caps.getOrPut(name) { MidiDeviceCapabilities() }
val deviceCaps =
caps.getOrPut(name) { MidiDeviceCapabilities() }
if (device !is Sequencer && device !is Synthesizer) {
if (device.maxReceivers != 0 && device.maxTransmitters == 0) {
@@ -34,7 +40,12 @@ data class MidiDeviceDescription(val name: String, val vendor: String, val recei
}
}
return caps.map {
MidiDeviceDescription(it.key.name, it.key.vendor, it.value.receive, it.value.transmit)
MidiDeviceDescription(
it.key.name,
it.key.vendor,
it.value.receive,
it.value.transmit
)
}
}
}
@@ -86,6 +97,7 @@ class MidiTransceiver(val receiverDevice: MidiDevice, val transmitterDevicer: Mi
private val receiver = receiverDevice.receiver
private val transmitter = transmitterDevicer.transmitter
private inner class Destroyer : Thread() {
override fun run() {
destroy()
@@ -103,10 +115,56 @@ class MidiTransceiver(val receiverDevice: MidiDevice, val transmitterDevicer: Mi
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))
ShortMessage.PROGRAM_CHANGE -> programChanged.trigger(MidiEvent.programChange(channel,cmd[1].toInt() and 0xff))
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
)
)
ShortMessage.PROGRAM_CHANGE -> programChanged.trigger(
MidiEvent.programChange(
channel,
cmd[1].toInt() and 0xff
)
)
ShortMessage.CHANNEL_PRESSURE -> channelPressure.trigger(
MidiEvent.channelPressure(
channel,
cmd[1].toInt() and 0xff
)
)
// https://sites.uci.edu/camp2014/2014/04/30/managing-midi-pitchbend-messages/
// The next operation to combine two 7bit values
// was verified to give the same results as the Linux
// `midisnoop` program while using an `Alesis Vortex
// Wireless 2` device. This MIDI device does not provide a
// full range 14 bit pitch-bend resolution though, so
// a different device is needed to confirm the pitch bend
// values slide as expected from -8192 to +8191.
ShortMessage.PITCH_BEND -> pitchBend.trigger(
MidiEvent.pitchBend(
channel,
(cmd[2].toInt() shl 25 shr 18) + cmd[1].toInt()
)
)
}
}
override fun close() {
@@ -121,14 +179,13 @@ class MidiTransceiver(val receiverDevice: MidiDevice, val transmitterDevicer: Mi
val programChanged = Event<MidiEvent>("midi-transceiver::program-changed")
val noteOn = Event<MidiEvent>("midi-transceiver::note-on")
val noteOff = Event<MidiEvent>("midi-transceiver::note-off")
val channelPressure = Event<MidiEvent>("midi-transceiver::channel-pressure")
val pitchBend = Event<MidiEvent>("midi-transceiver::pitch-bend")
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)
}
receiver.send(msg, receiverDevice.microsecondPosition)
} catch (e: InvalidMidiDataException) {
//
}
@@ -137,10 +194,7 @@ class MidiTransceiver(val receiverDevice: MidiDevice, val transmitterDevicer: Mi
fun programChange(channel: Int, program: Int) {
try {
val msg = ShortMessage(ShortMessage.PROGRAM_CHANGE, channel, program)
if (receiverDevice != null) {
val tc = receiverDevice.microsecondPosition
receiver.send(msg, tc)
}
receiver.send(msg, receiverDevice.microsecondPosition)
} catch (e: InvalidMidiDataException) {
//
}
@@ -149,10 +203,25 @@ class MidiTransceiver(val receiverDevice: MidiDevice, val transmitterDevicer: Mi
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)
receiver.send(msg, receiverDevice.microsecondPosition)
} catch (e: InvalidMidiDataException) {
//
}
}
fun channelPressure(channel: Int, value: Int) {
try {
val msg = ShortMessage(ShortMessage.CHANNEL_PRESSURE, channel, value)
receiver.send(msg, receiverDevice.microsecondPosition)
} catch (e: InvalidMidiDataException) {
//
}
}
fun pitchBend(channel: Int, value: Int) {
try {
val msg = ShortMessage(ShortMessage.PITCH_BEND, channel, value)
receiver.send(msg, receiverDevice.microsecondPosition)
} catch (e: InvalidMidiDataException) {
//
}
@@ -165,11 +234,16 @@ class MidiTransceiver(val receiverDevice: MidiDevice, val transmitterDevicer: Mi
}
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}")
val deviceName = "BCR2000"
MidiDeviceDescription.list().forEach(::println)
MidiDeviceDescription.list().firstOrNull { it.name.contains(deviceName) }
?.run {
val controller = MidiTransceiver.fromDeviceVendor(name, vendor)
controller.controlChanged.listen { println(it) }
controller.programChanged.listen { println(it) }
controller.noteOn.listen { println(it) }
controller.noteOff.listen { println(it) }
controller.channelPressure.listen { println(it) }
controller.pitchBend.listen { println(it) }
}
}