Using MIDIPacketList in swift

扶醉桌前 提交于 2019-12-22 05:23:37

问题


I am looking at some of the examples of midi output using core midi.

Specifically this question

and this

I have code that works based on these in objC and I now want to try to translate that to swift.

the line I least understand is this one: MIDIPacketList* pktList = (MIDIPacketList*) pktBuffer;

I read this as declaring a pointer pktlist of type MIDIPacketList and assigning it the value of pktBuffer, cast to the type MIDIPacketList

I'm new to C and objC and this makes no sense to me.

MIDIPacketList is a struct defined here:

in what way does it make sense to cast a bytearray to the struct type MIDIPacketList and what is this trying to achieve? I guess it is trying to define the size of the packet list, but why do we need to do that here, and how does this do it anyway? why is it not sufficient to do it with MIDIPacketListAdd as happens a few lines later?

here is my attempt at a midi output class in swift - can anyone see what is going wrong? this code gives no errors in Xcode until it is run. I have a working version of this in objC, but i can't get the size of the packet list defined in swift. (at least I think that's the problem)

import Foundation
import CoreMIDI

class MidiOutClass {

var midiClient = MIDIClientRef()
var midiSource = MIDIEndpointRef()


func openOutput() {
    MIDIClientCreate("MIDI client", nil, nil, &midiClient)
    MIDISourceCreate(midiClient, "MIDI Source",&midiSource)
    println("midi out opened")//should only do this if successful
}

func noteOn(channel: Int, note: Int, velocity:Int) {
    midisend((0x90+channel), note: note, value: velocity)
}

func polyAfter(channel: Int, note: Int, value:Int) {
    midisend((0xA0+channel), note: note, value: value)
}

func noteOff(channel: Int, note: Int) {
    midisend((0x90+channel), note: note, value: 0 )
}

func midisend(status:Int, note: Int, value:Int) {

    var packet: UnsafeMutablePointer<MIDIPacket> = nil
    //var buffer = [Byte](count:1024, repeatedValue: 0)
//this is the array I'm trying to use in a similar way to the obj C.

    var packetList: UnsafeMutablePointer<MIDIPacketList> = nil
    let midiDataToSend:[Byte] = [Byte(status), Byte(note), Byte(value)];
    packet = MIDIPacketListInit(packetList);
    packet = MIDIPacketListAdd(packetList, 1024, packet, 0, 3, midiDataToSend);

    if (packet == nil ) {
        println("failed to send the midi.")
    } else {
        MIDIReceived(midiSource, packetList)
        println("sent some stuff")
    }
}

}//end MidiOutClass

回答1:


I found an answer at this question

the byte array in the objC version is a nasty hack to allocate some memory to the packetList

here's my revised code, which now works.

    func midisend(status:Int, note: Int, value:Int) {

    var packet = UnsafeMutablePointer<MIDIPacket>.alloc(1)
    var packetList = UnsafeMutablePointer<MIDIPacketList>.alloc(1)
    let midiDataToSend:[Byte] = [Byte(status), Byte(note), Byte(value)];
    packet = MIDIPacketListInit(packetList);
    packet = MIDIPacketListAdd(packetList, 1024, packet, 0, 3, midiDataToSend);

    if (packet == nil ) {
        println("failed to send the midi.")
    } else {
        MIDIReceived(midiSource, packetList)
        println("sent some stuff")
    }
    packet.destroy()
    //packet.dealloc(1)
    packetList.destroy()
    packetList.dealloc(1)
}

However, the commented out dealloc seems to be unnecessary, I'm not exactly sure why yet. Does MIDIReceived take care of it?

If anyone has a better solution - maybe without using pointers at all, please post it!




回答2:


This is how I ended up going about it. I used the example of the byte buffer as found in various C / ObjC examples online, but I found I had to allocate a little bit more space for the MIDIPacketList struct itself.

A MidiEvent is just a UInt8 array. For usual MIDI events, it has a length of 3 bytes: (Status, Data, Data).

/// A UInt8 array, usually 3 bytes long
public typealias MidiEvent = [UInt8]

extension MIDIPacketList {
    init(midiEvents: [MidiEvent]) {

        let timestamp = MIDITimeStamp(0) // do it now
        let totalBytesInAllEvents = midiEvents.reduce(0) { total, event in
            return total + event.count
        }

        // Without this, we'd run out of space for the last few MidiEvents
        let listSize = MemoryLayout<MIDIPacketList>.size + totalBytesInAllEvents

        // CoreMIDI supports up to 65536 bytes, but in practical tests it seems
        // certain devices accept much less than that at a time. Unless you're
        // turning on / off ALL notes at once, 256 bytes should be plenty.
        assert(totalBytesInAllEvents < 256,
               "The packet list was too long! Split your data into multiple lists.")

        // Allocate space for a certain number of bytes
        let byteBuffer = UnsafeMutablePointer<UInt8>.allocate(capacity: listSize)

        // Use that space for our MIDIPacketList
        self = byteBuffer.withMemoryRebound(to: MIDIPacketList.self, capacity: 1) { packetList -> MIDIPacketList in
            var packet = MIDIPacketListInit(packetList)
            midiEvents.forEach { event in
                packet = MIDIPacketListAdd(packetList, listSize, packet, timestamp, event.count, event)
            }

            return packetList.pointee
        }

        byteBuffer.deallocate() // release the manually managed memory
    }
}

So if you're just sending one midi note, it'd look something like this. This example would send a NoteOn message to the Middle C on Channel 1 with a velocity of 100. You should use helper functions to make these MidiEvents though, rather than hard coding them ;)

var packets = MIDIPacketList(midiEvents: [[0x90, 60, 100]])
MIDISend(clientOutputPort, destination, &packetList)



回答3:


I have also used the above routine for sending midi commands from my app to internal synths running in the background like iMini, Z3TA+, Sunrizer, etc. but I couldn't get rid of memory leaks. This version has no unsafe pointers and allocs. It works without the dreaded memory leaks.

static var z: UInt8 = 0  // initalize tuple with 256 x this UInt8 value:

// Silly: why not an array instead of this.. a tuple is needed.. length must be exact 256..
// Don't know no other way to create a tuple with 256 elements...
  var midiDataTuple = (z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z, z)

// use the above tuple in MIDIPacket



func midiSend(status: Int, val1: Int, val2 :Int)
{
    var midipacket = MIDIPacket()

    midipacket.timeStamp = 0
    midipacket.length    = 3
    midipacket.data      = midiDataTuple  //<-=

    midipacket.data.0 = UInt8(status)
    midipacket.data.1 = UInt8(val1  )
    midipacket.data.2 = UInt8(val2  )

    var midipacketlist = MIDIPacketList(numPackets: 1, packet: midipacket)

    MIDIReceived(midiSource, &midipacketlist)
}




//////////////////////////////////////////////////////////////
func noteOn(soundNr: Int, note: Int, velocity:Int)
{
    let chan = midiChannelForSoundNr[soundNr]
    midiSend((0x90 + chan), val1: note, val2: velocity)
    notesPlaying[chan][note] = true
}

tested.



来源:https://stackoverflow.com/questions/26494434/using-midipacketlist-in-swift

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!