Midi handling in OSX

Mac OSX specific forum
Wolfram
Enthusiast
Enthusiast
Posts: 604
Joined: Thu May 30, 2013 4:39 pm

Re: Midi handling in OSX

Post by Wolfram »

Hi,
can anybody give me an example to send a message to an external midi device?

Thanks!
macOS Catalina 10.15.7
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 »

Midi in/out example

Code: Select all

Structure MIDIPacket 
  timeStamp.q 
  length.u 
  pdata.a[256] 
EndStructure

Structure MIDIPacketList
  numPackets.l 
  packet.MIDIPacket[1]
EndStructure

ImportC "-framework CoreMIDI"
  
  ; Strings
  CFRelease(cf)
  CFSTR(cStr.p-ascii) As "___CFStringMakeConstantString"
  
  ; Clients
  MIDIClientCreate(name, notifyProc, *notifyRefCon, *outClient)
  MIDIClientDispose(client)
  
  ; Endpoints
  MIDIGetDestination(destIndex0)
  MIDIGetNumberOfDestinations()
  MIDIGetNumberOfSources()
  MIDIGetSource(sourceIndex0)
  
  ; I/O
  MIDISend(port, dest, *pktlist)
  
  ; Objects
  MIDIObjectGetStringProperty(obj, propertyID, *str)
  
  ; Ports
  MIDIInputPortCreate(client, portName, readProc, *refCon, *outPort)
  MIDIOutputPortCreate(client, portName, *outPort)
  MIDIPortConnectSource(port, source, *connRefCon)
  MIDIPortDisconnectSource(port, source)
  MIDIPortDispose(port)
  
EndImport



Procedure.i FindSource(Name.s)
  Protected dname.s, d.i, i.i = MIDIGetNumberOfSources()
  While i
    i - 1
    d = MIDIGetSource(i)
    MIDIObjectGetStringProperty(d, CFSTR("name"), @str)
    If str
      dname = PeekS(CocoaMessage(0, str, "UTF8String"), -1, #PB_UTF8)
      CFRelease(str)
      ; Debug dname
      If dname = Name
        Break
      EndIf
    EndIf
  Wend
  ProcedureReturn d
EndProcedure

Procedure.i FindDestination(Name.s)
  Protected dname.s, d.i, i.i = MIDIGetNumberOfDestinations()
  While i
    i - 1
    d = MIDIGetDestination(i)
    MIDIObjectGetStringProperty(d, CFSTR("name"), @str)
    If str
      dname = PeekS(CocoaMessage(0, str, "UTF8String"), -1, #PB_UTF8)
      CFRelease(str)
      ; Debug dname
      If dname = Name
        Break
      EndIf
    EndIf
  Wend
  ProcedureReturn d
EndProcedure



ProcedureC midiInputCallback(*pktList.MIDIPacketList, *procRef, *srcRef)
  Protected i.i, timeStamp.q, length.u, pdata.s, numPackets.l = *pktList\numPackets
  Protected *pkt.MIDIPacket = @*pktList\packet[0]
  While numPackets
    timeStamp = *pkt\timeStamp
    length = *pkt\length
    pdata = ""
    For i = 0 To length - 1
      pdata + Hex(*pkt\pdata[i], #PB_Ascii) + " "
    Next
    
    Debug "timeStamp : " + Str(timeStamp)
    Debug "length    : " + Str(length)
    Debug "data      : " + pdata
    
    *pkt + length + 10 
    numPackets - 1 
  Wend
EndProcedure



If OpenWindow(0, 0, 0, 200, 80, "Mac MIDI", #PB_Window_SystemMenu | #PB_Window_ScreenCentered)
  
  MIDIClientCreate(CFSTR("PureMIDI"), #Null, #Null, @midiClient)
  
  MIDIInputPortCreate(midiClient, CFSTR("PureMIDIin"), @midiInputCallback(), #Null, @inPort)
  
  src = FindSource("Keystation Mini 32"); ** change the name to a device you have **
  
  MIDIPortConnectSource(inPort, src, #Null)
  
  MIDIOutputPortCreate(midiClient, CFSTR("PureMIDIout"), @outPort)
  
  dest = FindDestination("Bus 1"); ** change the name to a device you have **
  
  
  
  ; create a packet and send it
  
  *pktList.MIDIPacketList = AllocateMemory(SizeOf(MIDIPacketList))
  *pktlist\numPackets = 1
  *pktlist\packet[0]\timeStamp = 0; send immediately
  *pktlist\packet[0]\length = 3; 3 bytes to send
  *pktlist\packet[0]\pdata[0] = $91
  *pktlist\packet[0]\pdata[1] = $3C
  *pktlist\packet[0]\pdata[2] = $40
  
  MIDISend(outPort, dest, *pktList)
  
  
  
  Repeat
    Event = WaitWindowEvent()
  Until Event = #PB_Event_CloseWindow
  
EndIf


; clean up

MIDIPortDisconnectSource(inPort, src)
MIDIPortDispose(outPort)
MIDIPortDispose(inPort)
MIDIClientDispose(midiClient)
Last edited by wilbert on Thu Mar 25, 2021 3:04 pm, edited 1 time in total.
Windows (x64)
Raspberry Pi OS (Arm64)
Wolfram
Enthusiast
Enthusiast
Posts: 604
Joined: Thu May 30, 2013 4:39 pm

Re: Midi handling in OSX

Post by Wolfram »

Hello!

I try to read some SysEx Data and have a strange problem.
If I work with Debugger I can receive the MIDI bytes but on exit the Program frees.
This is not happened if I turn of Thread safe.



I'm on PB 5.31 x86 and OSX 10.7.5
macOS Catalina 10.15.7
Wolfram
Enthusiast
Enthusiast
Posts: 604
Joined: Thu May 30, 2013 4:39 pm

Re: Midi handling in OSX

Post by Wolfram »

Here another way to show all MIDI devices and ports

Code: Select all

EnableExplicit

;GUI
Global Window_Main, Combo_IN, Combo_Out, Text_in, Text_Out
Define event


Structure PortName
  ShowName.s
  Name.s
EndStructure

Global NewList OutPort.PortName(), NewList InPort.PortName() 


 ;-ImportC
 ImportC "/System/Library/Frameworks/CoreMIDI.framework/CoreMIDI"
  
  ; Strings
  CFSTR(cStr.p-ascii) As "___CFStringMakeConstantString"

  
  ; Objects
  MIDIObjectGetStringProperty(obj, propertyID, *str)  
  MIDIObjectGetIntegerProperty(obj, propertyID, *outValue )
  
  
  ;Device
  MIDIDeviceGetEntity(device, entityIndex0)
  MIDIEntityGetNumberOfSources(entity)
  MIDIEntityGetNumberOfDestinations(entity)
  MIDIEntityGetSource(entity, sourceIndex0)
  MIDIDeviceGetNumberOfEntities(device)
  MIDIGetDevice(deviceIndex0)
  MIDIGetNumberOfDevices()
  MIDIEntityGetDestination(entity, destIndex0)

EndImport





Procedure.s GetName(object)
  Protected name, _kMIDIPropertyName = CFSTR("name")
  
  If MIDIObjectGetStringProperty(object, _kMIDIPropertyName, @name)
    Debug "Error"
    ProcedureReturn ""
  EndIf
  
    
  ProcedureReturn PeekS(CocoaMessage(0, name, "UTF8String"), -1, #PB_UTF8)
  
EndProcedure


Procedure FindPorts()
  Protected PortName.s, P, entityCount, E, entity, sourceCount, destCount 
  Protected deviceCount, device, DeviceName.s, D, isOffline.a, source, dest
  Protected _kMIDIPropertyOffline = CFSTR("offline")
  
  
  deviceCount = MIDIGetNumberOfDevices()
  
  
  For D = 0 To deviceCount -1
    
    
    device = MIDIGetDevice(D)
    DeviceName = GetName(device) + " "
    
    
    isOffline = #False
    MIDIObjectGetIntegerProperty(device, _kMIDIPropertyOffline, @isOffline)
    
    If isOffline = #True
      DeviceName = Chr(126) +DeviceName
      Debug DeviceName + " isOffline"
    EndIf
    
    entityCount = MIDIDeviceGetNumberOfEntities(device)
    
    For  E = 0 To entityCount -1
      
      entity = MIDIDeviceGetEntity(device, E)
           
      sourceCount = MIDIEntityGetNumberOfSources(entity)
      
      For P = 0 To sourceCount-1
        
        source = MIDIEntityGetSource(entity, P)
        PortName = GetName(source)        
        AddElement(InPort())
        InPort()\ShowName = DeviceName + PortName
        InPort()\Name = PortName
      Next
      
      
      destCount = MIDIEntityGetNumberOfDestinations(entity)
      
      For P = 0 To destCount -1
     
        dest = MIDIEntityGetDestination(entity, P)
        PortName = GetName(dest)
        AddElement(outPort())
        OutPort()\ShowName = DeviceName + PortName
        OutPort()\Name = PortName      
      Next
      
    Next
    
    Debug "------Next Device"
    
  Next
  
  ;Fill Gadget
  For P =0 To ListSize(InPort()) -1
    SelectElement(InPort(), P)
    AddGadgetItem(Combo_IN, -1, InPort()\ShowName)   
  Next
  SetGadgetState(Combo_IN, 0)
  
  
  For P =0 To ListSize(OutPort()) -1
    SelectElement(OutPort(), P)
    AddGadgetItem(Combo_Out, -1, OutPort()\ShowName)    
  Next
  SetGadgetState(Combo_Out, 0)

EndProcedure


Procedure Window_Main_Events(event)
  Select event
    Case #PB_Event_CloseWindow
      ProcedureReturn #False

    Case #PB_Event_Gadget
      Select EventGadget()
        Case Combo_IN
          SelectElement(InPort(), GetGadgetState(Combo_IN))
          Debug InPort()\Name
        Case Combo_Out
          SelectElement(OutPort(), GetGadgetState(Combo_Out))
          Debug OutPort()\Name
      EndSelect
  EndSelect
  
  ProcedureReturn #True
EndProcedure


  Window_Main = OpenWindow(#PB_Any, 0, 0, 470, 130, "MIDI I/O", #PB_Window_SystemMenu | #PB_Window_ScreenCentered)

  Combo_IN = ComboBoxGadget(#PB_Any, 50, 60, 160, 20)
  Text_in = TextGadget(#PB_Any, 50, 40, 110, 20, "MIDI IN")
  Combo_Out = ComboBoxGadget(#PB_Any, 250, 60, 160, 20) 
  Text_Out = TextGadget(#PB_Any, 250, 40, 110, 20, "MIDI Out")

  FindPorts()


Repeat
  event = WaitWindowEvent()
Until Window_Main_Events(event) = #False

End
macOS Catalina 10.15.7
Wolfram
Enthusiast
Enthusiast
Posts: 604
Joined: Thu May 30, 2013 4:39 pm

Re: Midi handling in OSX

Post by Wolfram »

And this is the eases way to get a overview about you MIDI I/Os.

Code: Select all

EnableExplicit

;GUI
Global Window_Main, Combo_IN, Combo_Out, Text_in, Text_Out

Define event


Structure PortName
  ShowName.s
  Objekt.l
EndStructure

Global Dim OutPort.PortName(0), Dim InPort.PortName(0) 


 ;-ImportC
 ImportC "/System/Library/Frameworks/CoreMIDI.framework/CoreMIDI"
  
  ; Strings
  CFSTR(cStr.p-ascii) As "___CFStringMakeConstantString"

  
  ; Endpoints
  MIDIGetNumberOfSources()
  MIDIGetSource(sourceIndex0)
  MIDIGetNumberOfDestinations()
  MIDIGetDestination(destIndex0)

  
  ; Objects
  MIDIObjectGetStringProperty(obj, propertyID, *str)
  
EndImport


Global _kMIDIPropertyDisplayName = CFSTR("displayName")


Procedure.s GetDisplayName(object)
  Protected name
  
  If MIDIObjectGetStringProperty(object, _kMIDIPropertyDisplayName, @name)
    Debug "Error"
    ProcedureReturn ""
  EndIf
  
    
  ProcedureReturn PeekS(CocoaMessage(0, name, "UTF8String"), -1, #PB_UTF8)
  
EndProcedure


Procedure FindPorts()
  Protected D, sourceCount.a, destCount.a, source.l, dest.l
  
  
  sourceCount = MIDIGetNumberOfSources()
  
  ReDim InPort(sourceCount -1)
  
  For D = 0 To sourceCount -1
    
    source = MIDIGetSource(D)
    
    If source
      InPort(D)\ShowName = GetDisplayName(source)
      InPort(D)\Objekt = source
    EndIf
    
  Next
  
  
  
  destCount = MIDIGetNumberOfDestinations()
  
  ReDim OutPort(destCount -1)
  
  For D = 0 To destCount -1
    
    dest = MIDIGetDestination(D)
    
    If dest
      OutPort(D)\ShowName = GetDisplayName(dest)
      OutPort(D)\Objekt = dest
    EndIf
    
  Next
  
  
  
  ;Fill Gadget
  For D =0 To sourceCount -1
    AddGadgetItem(Combo_IN, -1, InPort(D)\ShowName)

  Next
  SetGadgetState(Combo_IN, 0)
  
  
  For D =0 To destCount -1
    AddGadgetItem(Combo_Out, -1, OutPort(D)\ShowName)
    
  Next
  SetGadgetState(Combo_Out, 0)

EndProcedure


Procedure Window_Main_Events(event)
  Select event
    Case #PB_Event_CloseWindow
      ProcedureReturn #False

    Case #PB_Event_Gadget
      Select EventGadget()
        Case Combo_IN
          Debug InPort(GetGadgetState(Combo_IN))\ShowName
        Case Combo_Out

          Debug OutPort(GetGadgetState(Combo_Out))\ShowName
        Case Combo_Out
      EndSelect
  EndSelect
  
  ProcedureReturn #True
EndProcedure


  Window_Main = OpenWindow(#PB_Any, 0, 0, 470, 130, "MIDI I/O", #PB_Window_SystemMenu | #PB_Window_ScreenCentered)

  Combo_IN = ComboBoxGadget(#PB_Any, 50, 60, 160, 20)
  Text_in = TextGadget(#PB_Any, 50, 40, 110, 20, "MIDI IN")
  Combo_Out = ComboBoxGadget(#PB_Any, 250, 60, 160, 20) 
  Text_Out = TextGadget(#PB_Any, 250, 40, 110, 20, "MIDI Out")

  FindPorts()
    

Repeat
  event = WaitWindowEvent()
Until Window_Main_Events(event) = #False

End
macOS Catalina 10.15.7
Post Reply