Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
commonMain.dev.atsushieno.ktmidi.ci.CIFactory.kt Maven / Gradle / Ivy
package dev.atsushieno.ktmidi.ci
import kotlin.experimental.and
object CIFactory {
// Assumes the input value is already 7-bit encoded if required.
fun midiCiDirectInt16At(dst: MutableList, offset: Int, v: Short) {
dst[offset] = (v.toInt() and 0x7F).toByte()
dst[offset + 1] = (v.toInt() shr 8 and 0x7F).toByte()
}
// Assumes the input value is already 7-bit encoded if required.
fun midiCiDirectUint32At(dst: MutableList, offset: Int, v: Int) {
dst[offset] = (v and 0xFF).toByte()
dst[offset + 1] = ((v shr 8) and 0xFF).toByte()
dst[offset + 2] = ((v shr 16) and 0xFF).toByte()
dst[offset + 3] = ((v shr 24) and 0xFF).toByte()
}
fun midiCI7bitInt14At(dst: MutableList, offset: Int, v: Short) {
dst[offset] = (v.toInt() and 0x7F).toByte()
dst[offset + 1] = (v.toInt() shr 7 and 0x7F).toByte()
}
fun midiCI7bitInt21At(dst: MutableList, offset: Int, v: Int) {
dst[offset] = (v and 0x7F).toByte()
dst[offset + 1] = ((v shr 7) and 0x7F).toByte()
dst[offset + 2] = ((v shr 14) and 0x7F).toByte()
}
fun midiCI7bitInt28At(dst: MutableList, offset: Int, v: Int) {
dst[offset] = (v and 0x7F).toByte()
dst[offset + 1] = ((v shr 7) and 0x7F).toByte()
dst[offset + 2] = ((v shr 14) and 0x7F).toByte()
dst[offset + 3] = ((v shr 21) and 0x7F).toByte()
}
fun midiCIMessageCommon(
dst: MutableList,
address: Byte, sysexSubId2: Byte, versionAndFormat: Byte, sourceMUID: Int, destinationMUID: Int
) {
dst[0] = MidiCIConstants.UNIVERSAL_SYSEX
dst[1] = address
dst[2] = MidiCIConstants.SYSEX_SUB_ID_MIDI_CI
dst[3] = sysexSubId2
dst[4] = versionAndFormat
midiCiDirectUint32At(dst, 5, sourceMUID)
midiCiDirectUint32At(dst, 9, destinationMUID)
}
// Discovery
fun midiCIDiscoveryCommon(
dst: MutableList, sysexSubId2: Byte,
versionAndFormat: Byte, sourceMUID: Int, destinationMUID: Int,
deviceManufacturer3Bytes: Int, deviceFamily: Short, deviceFamilyModelNumber: Short,
softwareRevisionLevel: Int, ciCategorySupported: Byte, receivableMaxSysExSize: Int,
initiatorOutputPathId: Byte
) {
midiCIMessageCommon(dst, MidiCIConstants.WHOLE_FUNCTION_BLOCK, sysexSubId2, versionAndFormat, sourceMUID, destinationMUID)
midiCiDirectUint32At(
dst, 13,
deviceManufacturer3Bytes
) // the last byte is extraneous, but will be overwritten next.
midiCiDirectInt16At(dst, 16, deviceFamily)
midiCiDirectInt16At(dst, 18, deviceFamilyModelNumber)
// LAMESPEC: Software Revision Level does not mention in which endianness this field is stored.
midiCiDirectUint32At(dst, 20, softwareRevisionLevel)
dst[24] = ciCategorySupported
midiCiDirectUint32At(dst, 25, receivableMaxSysExSize)
dst[29] = initiatorOutputPathId
}
fun midiCIDiscovery(
dst: MutableList,
versionAndFormat: Byte, sourceMUID: Int,
deviceManufacturer: Int, deviceFamily: Short, deviceFamilyModelNumber: Short,
softwareRevisionLevel: Int, ciCategorySupported: Byte, receivableMaxSysExSize: Int,
initiatorOutputPathId: Byte
) : List {
midiCIDiscoveryCommon(
dst, CISubId2.DISCOVERY_INQUIRY,
versionAndFormat, sourceMUID, 0x7F7F7F7F,
deviceManufacturer, deviceFamily, deviceFamilyModelNumber,
softwareRevisionLevel, ciCategorySupported, receivableMaxSysExSize,
initiatorOutputPathId
)
return dst.take(30)
}
fun midiCIDiscoveryReply(
dst: MutableList,
versionAndFormat: Byte, sourceMUID: Int, destinationMUID: Int,
deviceManufacturer: Int, deviceFamily: Short, deviceFamilyModelNumber: Short,
softwareRevisionLevel: Int, ciCategorySupported: Byte, receivableMaxSysExSize: Int,
initiatorOutputPathId: Byte, functionBlock: Byte
) : List {
midiCIDiscoveryCommon(
dst, CISubId2.DISCOVERY_REPLY,
versionAndFormat, sourceMUID, destinationMUID,
deviceManufacturer, deviceFamily, deviceFamilyModelNumber,
softwareRevisionLevel, ciCategorySupported, receivableMaxSysExSize,
initiatorOutputPathId
)
dst[30] = functionBlock
return dst.take(31)
}
fun midiCIEndpointMessage(dst: MutableList, versionAndFormat: Byte, sourceMUID: Int, destinationMUID: Int, status: Byte
) : List {
midiCIMessageCommon(dst, MidiCIConstants.WHOLE_FUNCTION_BLOCK, CISubId2.ENDPOINT_MESSAGE_INQUIRY, versionAndFormat, sourceMUID, destinationMUID)
dst[13] = status
return dst.take(14)
}
fun midiCIEndpointMessageReply(dst: MutableList, versionAndFormat: Byte, sourceMUID: Int, destinationMUID: Int, status: Byte, informationData: List
) : List {
midiCIMessageCommon(dst, MidiCIConstants.WHOLE_FUNCTION_BLOCK, CISubId2.ENDPOINT_MESSAGE_REPLY, versionAndFormat, sourceMUID, destinationMUID)
dst[13] = status
midiCI7bitInt14At(dst, 14, informationData.size.toShort())
memcpy(dst, 16, informationData, informationData.size)
return dst.take(16 + informationData.size)
}
fun midiCIInvalidateMuid(
dst: MutableList,
versionAndFormat: Byte, sourceMUID: Int, targetMUID: Int
) : List {
midiCIMessageCommon(dst, MidiCIConstants.WHOLE_FUNCTION_BLOCK, CISubId2.INVALIDATE_MUID, versionAndFormat, sourceMUID, MidiCIConstants.BROADCAST_MUID_32)
midiCiDirectUint32At(dst, 13, targetMUID)
return dst.take(17)
}
fun midiCIDiscoveryNak(
dst: MutableList, address: Byte,
versionAndFormat: Byte, sourceMUID: Int, destinationMUID: Int
) : List {
midiCIMessageCommon(dst, address, 0x7F, versionAndFormat, sourceMUID, destinationMUID)
return dst.take(13)
}
// Profile Configuration
fun midiCIProfile(dst: MutableList, offset: Int, info: MidiCIProfileId) {
memcpy(dst, offset, info.bytes, 5)
}
fun midiCIProfileInquiry(
dst: MutableList, address: Byte,
sourceMUID: Int, destinationMUID: Int
) : List {
midiCIMessageCommon(
dst, address,
CISubId2.PROFILE_INQUIRY,
MidiCIConstants.CI_VERSION_AND_FORMAT, sourceMUID, destinationMUID
)
return dst.take(13)
}
fun midiCIProfileInquiryReply(
dst: MutableList, address: Byte,
sourceMUID: Int, destinationMUID: Int,
enabledProfiles: List,
disabledProfiles: List
) : List {
midiCIMessageCommon(
dst, address,
CISubId2.PROFILE_INQUIRY_REPLY,
MidiCIConstants.CI_VERSION_AND_FORMAT, sourceMUID, destinationMUID
)
dst[13] = (enabledProfiles.size and 0x7F).toByte()
dst[14] = ((enabledProfiles.size shr 7) and 0x7F).toByte()
enabledProfiles.forEachIndexed { i, p ->
midiCIProfile(dst, 15 + i * 5, p)
}
var pos: Int = 15 + enabledProfiles.size * 5
dst[pos++] = (disabledProfiles.size and 0x7F).toByte()
dst[pos++] = ((disabledProfiles.size shr 7) and 0x7F).toByte()
disabledProfiles.forEachIndexed { i, p ->
midiCIProfile(dst, pos + i * 5, p)
}
pos += disabledProfiles.size * 5
return dst.take(pos)
}
fun midiCIProfileSet(
dst: MutableList, address: Byte, turnOn: Boolean,
sourceMUID: Int, destinationMUID: Int, profile: MidiCIProfileId,
numChannelsRequested: Short
) : List {
midiCIMessageCommon(
dst, address,
if (turnOn) CISubId2.SET_PROFILE_ON else CISubId2.SET_PROFILE_OFF,
MidiCIConstants.CI_VERSION_AND_FORMAT, sourceMUID, destinationMUID
)
midiCIProfile(dst, 13, profile)
// new field in MIDI-CI v1.2
midiCI7bitInt14At(dst, 18, numChannelsRequested)
return dst.take(20)
}
fun midiCIProfileAddedRemoved(
dst: MutableList, address: Byte, isRemoved: Boolean,
sourceMUID: Int, profile: MidiCIProfileId
) : List {
midiCIMessageCommon(
dst, address,
if (isRemoved) CISubId2.PROFILE_REMOVED_REPORT else CISubId2.PROFILE_ADDED_REPORT,
MidiCIConstants.CI_VERSION_AND_FORMAT, sourceMUID, 0x7F7F7F7F
)
midiCIProfile(dst, 13, profile)
return dst.take(18)
}
fun midiCIProfileReport(
dst: MutableList, address: Byte, isEnabledReport: Boolean,
sourceMUID: Int, profile: MidiCIProfileId,
numChannelsAffected: Short
) : List {
midiCIMessageCommon(
dst, address,
if (isEnabledReport) CISubId2.PROFILE_ENABLED_REPORT else CISubId2.PROFILE_DISABLED_REPORT,
MidiCIConstants.CI_VERSION_AND_FORMAT, sourceMUID, 0x7F7F7F7F
)
midiCIProfile(dst, 13, profile)
midiCI7bitInt14At(dst, 18, numChannelsAffected)
return dst.take(20)
}
private fun midiCIProfileDetailsCommon(
isReply: Boolean,
dst: MutableList, address: Byte,
sourceMUID: Int, destinationMUID: Int,
profile: MidiCIProfileId, target: Byte) {
midiCIMessageCommon(
dst, address,
if (isReply) CISubId2.PROFILE_DETAILS_REPLY else CISubId2.PROFILE_DETAILS_INQUIRY,
MidiCIConstants.CI_VERSION_AND_FORMAT, sourceMUID, destinationMUID
)
midiCIProfile(dst, 13, profile)
dst[18] = target
}
fun midiCIProfileDetails(
dst: MutableList, address: Byte,
sourceMUID: Int, destinationMUID: Int,
profile: MidiCIProfileId, target: Byte): List {
midiCIProfileDetailsCommon(false, dst, address, sourceMUID, destinationMUID, profile, target)
return dst.take(19)
}
fun midiCIProfileDetailsReply(dst: MutableList, address: Byte, sourceMUID: Int, destinationMUID: Int, profile: MidiCIProfileId, target: Byte, data: List): List {
midiCIProfileDetailsCommon(true, dst, address, sourceMUID, destinationMUID, profile, target)
midiCI7bitInt14At(dst, 19, data.size.toShort())
memcpy(dst, 21, data, data.size)
return dst.take(21 + data.size)
}
fun midiCIProfileSpecificData(
dst: MutableList, address: Byte,
sourceMUID: Int, destinationMUID: Int, profile: MidiCIProfileId, data: List
) : List {
midiCIMessageCommon(
dst, address,
CISubId2.PROFILE_SPECIFIC_DATA,
MidiCIConstants.CI_VERSION_AND_FORMAT, sourceMUID, destinationMUID
)
midiCIProfile(dst, 13, profile)
midiCiDirectUint32At(dst, 18, data.size)
memcpy(dst, 22, data, data.size)
return dst.take(22 + data.size)
}
// Property Exchange
fun midiCIPropertyGetCapabilities(
dst: MutableList, address: Byte, isReply: Boolean,
sourceMUID: Int, destinationMUID: Int, maxSimulutaneousRequests: Byte
) : List {
midiCIMessageCommon(
dst, address,
if (isReply) CISubId2.PROPERTY_CAPABILITIES_REPLY else CISubId2.PROPERTY_CAPABILITIES_INQUIRY,
MidiCIConstants.CI_VERSION_AND_FORMAT, sourceMUID, destinationMUID
)
dst[13] = maxSimulutaneousRequests
// since MIDI-CI 1.2
dst[14] = MidiCIConstants.PROPERTY_EXCHANGE_MAJOR_VERSION
dst[15] = MidiCIConstants.PROPERTY_EXCHANGE_MINOR_VERSION
return dst.take(16)
}
// common to all of: has data & reply, get data & reply, set data & reply, subscribe & reply, notify
fun midiCIPropertyCommon(
dst: MutableList, address: Byte, messageTypeSubId2: Byte,
sourceMUID: Int, destinationMUID: Int,
requestId: Byte, header: List,
numChunks: Short, chunkIndex: Short, data: List
) {
midiCIMessageCommon(dst, address, messageTypeSubId2,
MidiCIConstants.CI_VERSION_AND_FORMAT, sourceMUID, destinationMUID)
dst[13] = requestId
midiCI7bitInt14At(dst, 14, header.size.toShort())
memcpy(dst, 16, header, header.size)
midiCI7bitInt14At(dst, 16 + header.size, numChunks)
midiCI7bitInt14At(dst, 18 + header.size, chunkIndex)
midiCI7bitInt14At(dst, 20 + header.size, data.size.toShort())
memcpy(dst, 22 + header.size, data, data.size)
}
private fun memcpy(dst: MutableList, dstOffset: Int, src: List, size: Int) {
for (i in 0 until size)
dst[i + dstOffset] = src[i]
}
private fun midiCIPropertyPacketCommon(dst: MutableList, subId: Byte, sourceMUID: Int, destinationMUID: Int,
requestId: Byte, header: List,
numChunks: Short, chunkIndex1Based: Short,
data: List) : List {
midiCIPropertyCommon(dst, MidiCIConstants.WHOLE_FUNCTION_BLOCK, subId,
sourceMUID, destinationMUID, requestId, header, numChunks, chunkIndex1Based, data)
return dst.take(16 + header.size + 6 + data.size)
}
fun midiCIPropertyChunks(dst: MutableList, maxDataLengthInPacket: Int, subId: Byte, sourceMUID: Int, destinationMUID: Int,
requestId: Byte, header: List, data: List) : List> {
if (data.isEmpty())
return listOf(midiCIPropertyPacketCommon(dst, subId, sourceMUID, destinationMUID, requestId, header,
1, 1, data))
val chunks = data.chunked(maxDataLengthInPacket)
return chunks.mapIndexed { index, packetData ->
midiCIPropertyPacketCommon(dst, subId, sourceMUID, destinationMUID, requestId, header,
chunks.size.toShort(), (index + 1).toShort(), packetData)
}
}
// Process Inquiry
fun midiCIProcessInquiryCapabilities(
dst: MutableList, sourceMUID: Int, destinationMUID: Int
) : List {
midiCIMessageCommon(
dst, MidiCIConstants.ADDRESS_FUNCTION_BLOCK, CISubId2.PROCESS_INQUIRY_CAPABILITIES,
MidiCIConstants.CI_VERSION_AND_FORMAT, sourceMUID, destinationMUID
)
return dst.take(13)
}
fun midiCIProcessInquiryCapabilitiesReply(
dst: MutableList, sourceMUID: Int, destinationMUID: Int, features: Byte
) : List {
midiCIMessageCommon(
dst, MidiCIConstants.ADDRESS_FUNCTION_BLOCK, CISubId2.PROCESS_INQUIRY_CAPABILITIES_REPLY,
MidiCIConstants.CI_VERSION_AND_FORMAT, sourceMUID, destinationMUID
)
dst[13] = features
return dst.take(14)
}
fun midiCIMidiMessageReport(
dst: MutableList, address: Byte, sourceMUID: Int, destinationMUID: Int,
messageDataControl: Byte,
systemMessages: Byte,
channelControllerMessages: Byte,
noteDataMessages: Byte
) : List {
midiCIMessageCommon(
dst, address, CISubId2.PROCESS_INQUIRY_MIDI_MESSAGE_REPORT,
MidiCIConstants.CI_VERSION_AND_FORMAT, sourceMUID, destinationMUID
)
dst[13] = messageDataControl
dst[14] = systemMessages
dst[15] = 0 // reserved for other System Messages
dst[16] = channelControllerMessages
dst[17] = noteDataMessages
return dst.take(18)
}
fun midiCIMidiMessageReportReply(
dst: MutableList, address: Byte, sourceMUID: Int, destinationMUID: Int,
systemMessages: Byte,
channelControllerMessages: Byte,
noteDataMessages: Byte
) : List {
midiCIMessageCommon(
dst, address, CISubId2.PROCESS_INQUIRY_MIDI_MESSAGE_REPORT_REPLY,
MidiCIConstants.CI_VERSION_AND_FORMAT, sourceMUID, destinationMUID
)
dst[13] = systemMessages
dst[14] = 0 // reserved for other System Messages
dst[15] = channelControllerMessages
dst[16] = noteDataMessages
return dst.take(17)
}
fun midiCIEndOfMidiMessage(
dst: MutableList, address: Byte, sourceMUID: Int, destinationMUID: Int
) : List {
midiCIMessageCommon(dst, address, CISubId2.PROCESS_INQUIRY_END_OF_MIDI_MESSAGE,
MidiCIConstants.CI_VERSION_AND_FORMAT, sourceMUID, destinationMUID)
return dst.take(13)
}
// ACK/NAK
fun midiCIAckNak(
dst: MutableList,
isNak: Boolean,
address: Byte,
versionAndFormat: Byte,
sourceMUID: Int,
destinationMUID: Int,
originalSubId: Byte,
statusCode: Byte,
statusData: Byte,
nakDetails: List,
messageTextData: List
): List {
midiCIMessageCommon(
dst, address, if (isNak) CISubId2.NAK else CISubId2.ACK,
versionAndFormat, sourceMUID, destinationMUID)
dst[13] = originalSubId
dst[14] = statusCode
dst[15] = statusData
if (nakDetails.size == 5)
memcpy(dst, 16, nakDetails, 5)
dst[21] = (messageTextData.size % 0x80).toByte()
dst[22] = (messageTextData.size / 0x80).toByte()
if (messageTextData.isNotEmpty())
memcpy(dst, 23, messageTextData, messageTextData.size)
return dst.take(23 + messageTextData.size)
}
fun midiCI32to28(i: Int): Int =
((i shr 24) shl 21) +
(((i shr 16) and 0x7F) shl 14) +
(((i shr 8) and 0x7F) shl 7) +
(i and 0x7F)
}