Page 1 of 2
Midi handling in OSX
Posted: Sun Mar 13, 2011 11:05 am
by psavolai
Hi,
I have several years of experience in handling midi under Windows in various languages, but not under OSX or Linux.
I would very much like to port some of my apps to OSX (and Linux) but have found really very little or no docs the subject of handling midi ports.
Anyone ?
cheers,
petri.
Re: Midi handling in OSX
Posted: Wed Jan 04, 2012 8:20 pm
by Khorus
I might be wrong but I think we need to write some sort of a library for us to access CoreMIDI's functions. PB doesn't support direct API calls. I'm investigating on this too.
Re: Midi handling in OSX
Posted: Wed Jan 04, 2012 9:32 pm
by wilbert
I have little knowledge about MIDI but this might get you going ...
http://developer.apple.com/library/mac/ ... IServices/
Example of importing functions
Code: Select all
Enumeration -10830 Step -1
#kMIDIInvalidClient
#kMIDIInvalidPort
#kMIDIWrongEndpointType
#kMIDINoConnection
#kMIDIUnknownEndpoint
#kMIDIUnknownProperty
#kMIDIWrongPropertyType
#kMIDINoCurrentSetup
#kMIDIMessageSendErr
#kMIDIServerStartErr
#kMIDISetupFormatErr
#kMIDIWrongThread
#kMIDIObjectNotFound
#kMIDIIDNotUnique
EndEnumeration
Structure MIDIPacket
timeStamp.q
length.u
pdata.b[256]
EndStructure
Structure MIDIPacketList
numPackets.l
packet.MIDIPacket[1]
EndStructure
ImportC "-framework CoreMIDI"
; Strings
CFRelease(cf)
CFSTR(cStr.p-ascii) As "___CFStringMakeConstantString"
CFStringCreateWithCharacters(allocator, chars.p-unicode, numChars)
CFStringGetCharacters(theString, loc, len, *buffer)
CFStringGetLength(theString)
; Clients
MIDIClientCreate(name, notifyProc, *notifyRefCon, *outClient)
MIDIClientDispose(client)
; Endpoints
MIDIDestinationCreate(client, name, readProc, *refCon, *outDest)
MIDIEndpointDispose(endpt)
MIDIGetDestination(destIndex0)
MIDIGetNumberOfDestinations()
MIDIGetNumberOfSources()
MIDIGetSource(sourceIndex0)
MIDISourceCreate(client, name, *outSrc)
; Packet
MIDIPacketListAdd(*pktlist, listSize, *curPacket, time.q, nData, *pdata)
MIDIPacketListInit(*pktlist)
MIDIPacketNext(*pkt)
; Ports
MIDIInputPortCreate(client, portName, readProc, *refCon, *outPort)
MIDIOutputPortCreate(client, portName, *outPort)
MIDIPortConnectSource(port, source, *connRefCon)
MIDIPortDisconnectSource(port, source)
MIDIPortDispose(port)
; I/O
MIDIFlushOutput(dest)
MIDIReceived(src, *pktlist)
MIDIRestart()
MIDISend(port, dest, *pktlist)
; Objects
MIDIObjectGetStringProperty(obj, propertyID, *str)
MIDIObjectSetStringProperty(obj, propertyID, str)
EndImport
Procedure CFStringCreate(s.s)
ProcedureReturn CFStringCreateWithCharacters(#Null, s, Len(s))
EndProcedure
Procedure.s StringFromCFString(CFString)
Protected len = CFStringGetLength(CFString)
Dim buffer.u(len)
CFStringGetCharacters(CFString, 0, len, @buffer())
ProcedureReturn PeekS(@buffer(), -1, #PB_Unicode)
EndProcedure
; test
MIDIClientCreate(CFSTR("midiClient"), #Null, #Null, @midiClient)
MIDIOutputPortCreate(midiClient, CFSTR("midiOutPort"), @midiOutPort)
Debug midiClient; midi client id
Debug midiOutPort; midi out port id
count = MIDIGetNumberOfDestinations()
For i = 0 To count - 1
dest = MIDIGetDestination(i)
MIDIObjectGetStringProperty(dest, CFSTR("name"), @pname)
Debug StringFromCFString(pname); debug name of destination
Next
Re: Midi handling in OSX
Posted: Thu Jan 05, 2012 2:08 pm
by Khorus
Awesome! THANKS a bunch for the source code! That's gold! Happy New Year!

Re: Midi handling in OSX
Posted: Thu Jan 05, 2012 5:39 pm
by wilbert
I updated the CStringFromCFString(CFString) procedure from the code above into StringFromCFString(CFString).
The difference is that it is able to return unicode characters while my first version wasn't.
CFSTR by the way may only be used with constants. If you want to convert a string variable into a CFString, you need to use the CFStringCreate procedure.
I would love to hear it if you get things working

Re: Midi handling in OSX
Posted: Fri Jan 06, 2012 6:16 am
by Khorus
Hello Wilbert,
I tried to run your source code on PureBasic 4.60. I'm running it on an old Leopard system (10.5). It should work, CoreMIDI has been around since 10.0.
The source code compiles and runs OK. I'm getting values for "midiClient" and "midiOutPort" but "MIDIGetNumberOfDestinations()" returns 0 always. I tried "MIDIGetNumberOfSources()" as well, no luck. I added a dummy MIDI device in the "Audio and MIDI Setup" just to make sure we get something listed here.
Thanks a bunch for all the info!
Marc aka Khorus
*EDIT*: OK, I added "MIDIGetNumberOfDevices" in the ImportC function, debugged it and it worked, I'm getting "2" as a result. It's a start!

Re: Midi handling in OSX
Posted: Fri Jan 06, 2012 8:30 am
by wilbert
Hi Marc,
I added some more imports and made it into an include file.
Code: Select all
; *** OSX_MIDI.pbi ***
; Error Constants
Enumeration -10830 Step -1
#kMIDIInvalidClient
#kMIDIInvalidPort
#kMIDIWrongEndpointType
#kMIDINoConnection
#kMIDIUnknownEndpoint
#kMIDIUnknownProperty
#kMIDIWrongPropertyType
#kMIDINoCurrentSetup
#kMIDIMessageSendErr
#kMIDIServerStartErr
#kMIDISetupFormatErr
#kMIDIWrongThread
#kMIDIObjectNotFound
#kMIDIIDNotUnique
EndEnumeration
; MIDIObjectType
Enumeration -1
#kMIDIObjectType_Other
#kMIDIObjectType_Device
#kMIDIObjectType_Entity
#kMIDIObjectType_Source
#kMIDIObjectType_Destination
#kMIDIObjectType_ExternalMask = $10
#kMIDIObjectType_ExternalDevice = #kMIDIObjectType_ExternalMask | #kMIDIObjectType_Device
#kMIDIObjectType_ExternalEntity = #kMIDIObjectType_ExternalMask | #kMIDIObjectType_Entity
#kMIDIObjectType_ExternalSource = #kMIDIObjectType_ExternalMask | #kMIDIObjectType_Source
#kMIDIObjectType_ExternalDestination = #kMIDIObjectType_ExternalMask | #kMIDIObjectType_Destination
EndEnumeration
; MIDIUniqueID
#kMIDIInvalidUniqueID = 0
; MIDINotificationMessageID
Enumeration 1
#kMIDIMsgSetupChanged
#kMIDIMsgObjectAdded
#kMIDIMsgObjectRemoved
#kMIDIMsgPropertyChanged
#kMIDIMsgThruConnectionsChanged
#kMIDIMsgSerialPortOwnerChanged
#kMIDIMsgIOError
EndEnumeration
Structure MIDIPacket
timeStamp.q
length.u
pdata.b[256]
EndStructure
Structure MIDIPacketList
numPackets.l
packet.MIDIPacket[1]
EndStructure
Structure MIDISysexSendRequest Align #PB_Structure_AlignC
destination.l
*pdata
bytesToSend.l
complete.b
reserved.b[3]
completionProc.i
*completionRefCon
EndStructure
ImportC "-framework CoreMIDI"
; Strings
CFRelease(cf)
CFSTR(cStr.p-ascii) As "___CFStringMakeConstantString"
CFStringCreateWithCharacters(allocator, chars.p-unicode, numChars)
CFStringGetCharacters(theString, loc, len, *buffer)
CFStringGetLength(theString)
; Clients
MIDIClientCreate(name, notifyProc, *notifyRefCon, *outClient)
MIDIClientDispose(client)
; Devices
MIDIDeviceGetEntity(device, entityIndex0)
MIDIDeviceGetNumberOfEntities(device)
MIDIGetDevice(deviceIndex0)
MIDIGetNumberOfDevices()
; Endpoints
MIDIDestinationCreate(client, name, readProc, *refCon, *outDest)
MIDIEndpointDispose(endpt)
MIDIEndpointGetEntity(inEndpoint, *outEntity)
MIDIGetDestination(destIndex0)
MIDIGetNumberOfDestinations()
MIDIGetNumberOfSources()
MIDIGetSource(sourceIndex0)
MIDISourceCreate(client, name, *outSrc)
; ENtities
MIDIEntityGetDestination(entity, destIndex0)
MIDIEntityGetDevice(inEntity, *outDevice)
MIDIEntityGetNumberOfDestinations(entity)
MIDIEntityGetNumberOfSources(entity)
MIDIEntityGetSource(entity, sourceIndex0)
; External
MIDIGetExternalDevice(deviceIndex0)
MIDIGetNumberOfExternalDevices()
; I/O
MIDIFlushOutput(dest)
MIDIReceived(src, *pktlist)
MIDIRestart()
MIDISend(port, dest, *pktlist)
; Objects
MIDIObjectFindByUniqueID(inUniqueID, *outObject, *outObjectType)
MIDIObjectGetIntegerProperty(obj, propertyID, *outValue)
MIDIObjectGetStringProperty(obj, propertyID, *str)
MIDIObjectSetIntegerProperty(obj, propertyID, value)
MIDIObjectSetStringProperty(obj, propertyID, str)
; Packet
MIDIPacketListAdd(*pktlist, listSize, *curPacket, time.q, nData, *pdata)
MIDIPacketListInit(*pktlist)
MIDIPacketNext(*pkt)
; Ports
MIDIInputPortCreate(client, portName, readProc, *refCon, *outPort)
MIDIOutputPortCreate(client, portName, *outPort)
MIDIPortConnectSource(port, source, *connRefCon)
MIDIPortDisconnectSource(port, source)
MIDIPortDispose(port)
EndImport
Procedure CFStringCreate(s.s)
ProcedureReturn CFStringCreateWithCharacters(#Null, s, Len(s))
EndProcedure
Procedure.s StringFromCFString(CFString)
Protected len = CFStringGetLength(CFString)
Dim buffer.u(len)
CFStringGetCharacters(CFString, 0, len, @buffer())
ProcedureReturn PeekS(@buffer(), -1, #PB_Unicode)
EndProcedure
_kMIDIPropertyName = CFSTR("name")
_kMIDIPropertyManufacturer = CFSTR("manufacturer")
_kMIDIPropertyModel = CFSTR("model")
_kMIDIPropertyUniqueID = CFSTR("uniqueID")
_kMIDIPropertyDeviceID = CFSTR("deviceID")
_kMIDIPropertyReceiveChannels = CFSTR("receiveChannels")
_kMIDIPropertyMaxSysExSpeed = CFSTR("maxSysExSpeed")
_kMIDIPropertyAdvanceScheduleTimeMuSec = CFSTR("scheduleAheadMuSec")
_kMIDIPropertyDriverOwner = CFSTR("driver")
_kMIDIPropertyIsEmbeddedEntity = CFSTR("embedded")
_kMIDIPropertyConnectionUniqueID = CFSTR("connUniqueID")
_kMIDIPropertyOffline = CFSTR("offline")
_kMIDIPropertyFactoryPatchNameFile = CFSTR("factoryPatchFile")
_kMIDIPropertyUserPatchNameFile = CFSTR("userPatchFile")
_kMIDIDriverPropertyUsesSerial = CFSTR("MIDIDriverUsesSerial")
Does this example report some names ?
Code: Select all
IncludeFile "OSX_MIDI.pbi"
count = MIDIGetNumberOfDevices()
For i = 0 To count - 1
dev = MIDIGetDevice(i)
MIDIObjectGetStringProperty(dev, _kMIDIPropertyName, @pname)
Debug StringFromCFString(pname); debug name of device
Next
I only have a simple usb keyboard so no MIDI out.
I had to connect it to get a destination. Devices shows also devices that can't be used.
Note: If a client iterates through the devices and entities in the system, it will not ever visit any virtual sources and destinations created by other clients. Also, a device iteration will return devices which are "offline" (were present in the past but are not currently present), while iterations through the system's sources and destinations will not include the endpoints of offline devices.
Thus clients should usually use MIDIGetNumberOfSources, MIDIGetSource, MIDIGetNumberOfDestinations and MIDIGetDestination, rather iterating through devices and entities to locate endpoints.
Re: Midi handling in OSX
Posted: Fri Jan 06, 2012 8:41 pm
by Khorus
Yes sir, the names are being reported good!

Thanks again for the include, looks like it is working great, I'll try to do a dummy Midi IN/OUT loop with the IAC Driver... See if I can talk to a DAW host directly. That's the goal of my project. Thank you!
Re: Midi handling in OSX
Posted: Tue Mar 13, 2012 7:13 pm
by J. Baker
Thanks link and code wilbert! Just curious, have you created any audio units in PB?
Re: Midi handling in OSX
Posted: Wed Mar 14, 2012 7:03 am
by wilbert
Hi Joe,
I haven't tried that.
Are you creating a midi application ?
Re: Midi handling in OSX
Posted: Wed Mar 14, 2012 7:15 am
by J. Baker
wilbert wrote:Hi Joe,
I haven't tried that.
Are you creating a midi application ?
I was recently playing with some synths in Garage Band and started thinking about what it would take to create an audio unit or if it was even possible in PB? So I started looking through the core audio sdk earlier. I still have some reading to do.
Re: Midi handling in OSX
Posted: Sun May 27, 2012 4:19 am
by Khorus
Sorry to revive an old thread, just wanted to know if you guys succeeded in sending and receiving MIDI messages?
I'm using Wilbert's Include file and it works for me so far for listing ports and such but I can't get my MIDI IN/OUT to work.
Any help greatly appreciated! Cheers!
-Khorus
Re: Midi handling in OSX
Posted: Thu Aug 16, 2012 2:57 pm
by wilbert
For midi out, the MusicPlayer might do
http://developer.apple.com/library/mac/ ... rence.html
Code: Select all
EnableExplicit
Structure MIDINoteMessage
Channel.a
Note.a
Velocity.a
ReleaseVelocity.a
Duration.f
EndStructure
ImportC "-framework AudioToolbox"
DisposeMusicPlayer(inPlayer)
DisposeMusicSequence(inSequence)
NewMusicPlayer(*outPlayer)
NewMusicSequence(*outSequence)
MusicPlayerSetSequence(inPlayer, inSequence)
MusicPlayerStart(inPlayer)
MusicPlayerStop(inPlayer)
MusicSequenceNewTrack(inSequence, *outTrack)
MusicTrackNewMIDINoteEvent(inTrack, inTimeStamp.d, *inMessage)
EndImport
Define Message.MIDINoteMessage
Define Player.i, Sequence.i, Track.i
NewMusicPlayer(@Player)
NewMusicSequence(@Sequence)
MusicPlayerSetSequence(Player, Sequence)
MusicSequenceNewTrack(Sequence, @Track)
Message\Channel = 0
Message\Note = 64
Message\Velocity = 64
Message\ReleaseVelocity = 64
Message\Duration = 1
MusicTrackNewMIDINoteEvent(Track, 0, @Message)
Message\Channel = 0
Message\Note = 66
Message\Velocity = 64
Message\ReleaseVelocity = 64
Message\Duration = 1
MusicTrackNewMIDINoteEvent(Track, 1, @Message)
MusicPlayerStart(Player)
MessageRequester ("", "Playing Midi")
MusicPlayerStop(Player)
DisposeMusicPlayer(Player)
DisposeMusicSequence(Sequence)
This small example plays only two notes on the built in software synth but there's also a function
MusicTrackSetDestMIDIEndpoint(inTrack, inEndpoint) that can connect a midi track to a midi endpoint.
Re: Midi handling in OSX
Posted: Sat Dec 29, 2012 8:03 am
by Khorus
Hello Wilbert,
I'm doing real time MIDI messages mostly, some sort of a MIDI translator. I need to read/write directly into the MIDI ports. Do you think you can give me a simple example on how to do this with your PB definitions? Thanks a bunch!
Re: Midi handling in OSX
Posted: Sat Dec 29, 2012 6:36 pm
by wilbert
Hello Khorus,
I'm not very familiar with Core Midi.
I noticed you also posted a thread about PortMIDI.
Since PortMIDI should also work on OS X, that might be the best solution if you are aiming for a cross platform solution.