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! :D

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! :D

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.