Midi handling in OSX

Mac OSX specific forum
psavolai
New User
New User
Posts: 5
Joined: Tue Feb 22, 2011 3:35 pm

Midi handling in OSX

Post 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.
Khorus
User
User
Posts: 25
Joined: Sat Nov 13, 2010 11:22 pm

Re: Midi handling in OSX

Post 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.
wilbert
PureBasic Expert
PureBasic Expert
Posts: 3942
Joined: Sun Aug 08, 2004 5:21 am
Location: Netherlands

Re: Midi handling in OSX

Post 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
Last edited by wilbert on Thu Mar 25, 2021 3:04 pm, edited 3 times in total.
Khorus
User
User
Posts: 25
Joined: Sat Nov 13, 2010 11:22 pm

Re: Midi handling in OSX

Post by Khorus »

Awesome! THANKS a bunch for the source code! That's gold! Happy New Year! :D
wilbert
PureBasic Expert
PureBasic Expert
Posts: 3942
Joined: Sun Aug 08, 2004 5:21 am
Location: Netherlands

Re: Midi handling in OSX

Post 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 :)
Khorus
User
User
Posts: 25
Joined: Sat Nov 13, 2010 11:22 pm

Re: Midi handling in OSX

Post 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
wilbert
PureBasic Expert
PureBasic Expert
Posts: 3942
Joined: Sun Aug 08, 2004 5:21 am
Location: Netherlands

Re: Midi handling in OSX

Post 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.
Last edited by wilbert on Thu Mar 25, 2021 3:05 pm, edited 3 times in total.
Khorus
User
User
Posts: 25
Joined: Sat Nov 13, 2010 11:22 pm

Re: Midi handling in OSX

Post 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!
User avatar
J. Baker
Addict
Addict
Posts: 2181
Joined: Sun Apr 27, 2003 8:12 am
Location: USA
Contact:

Re: Midi handling in OSX

Post by J. Baker »

Thanks link and code wilbert! Just curious, have you created any audio units in PB?
www.posemotion.com

PureBasic Tools for OS X: PureMonitor, plist Tool, Data Maker & App Chef


Even the vine knows it surroundings but the man with eyes does not.
wilbert
PureBasic Expert
PureBasic Expert
Posts: 3942
Joined: Sun Aug 08, 2004 5:21 am
Location: Netherlands

Re: Midi handling in OSX

Post by wilbert »

Hi Joe,
I haven't tried that.
Are you creating a midi application ?
User avatar
J. Baker
Addict
Addict
Posts: 2181
Joined: Sun Apr 27, 2003 8:12 am
Location: USA
Contact:

Re: Midi handling in OSX

Post 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.
www.posemotion.com

PureBasic Tools for OS X: PureMonitor, plist Tool, Data Maker & App Chef


Even the vine knows it surroundings but the man with eyes does not.
Khorus
User
User
Posts: 25
Joined: Sat Nov 13, 2010 11:22 pm

Re: Midi handling in OSX

Post 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
wilbert
PureBasic Expert
PureBasic Expert
Posts: 3942
Joined: Sun Aug 08, 2004 5:21 am
Location: Netherlands

Re: Midi handling in OSX

Post 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.
Last edited by wilbert on Thu Mar 25, 2021 3:06 pm, edited 1 time in total.
Khorus
User
User
Posts: 25
Joined: Sat Nov 13, 2010 11:22 pm

Re: Midi handling in OSX

Post 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!
wilbert
PureBasic Expert
PureBasic Expert
Posts: 3942
Joined: Sun Aug 08, 2004 5:21 am
Location: Netherlands

Re: Midi handling in OSX

Post 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.
Post Reply